bjorng

bjorng

Erlang Core Team

This topic is about Day 2 of the Advent of Code 2021.

We have a private leaderboard (shared with users of Erlang Forums):

https://adventofcode.com/2021/leaderboard/private/view/370884

The entry code is:
370884-a6a71927

Showing Posts 1 to 10

APB9785

APB9785

Creator of ECSx
code-shoily

code-shoily

This one wasn’t as fun as the day 1 one for me.

defmodule AdventOfCode.Y2021.Day02 do
  @moduledoc """
  --- Day 2: Dive! ---
  Problem Link: https://adventofcode.com/2021/day/2
  """
  use AdventOfCode.Helpers.InputReader, year: 2021, day: 2

  def run_1, do: input!() |> parse() |> track_positions() |> then(& &1.depth * &1.horizontal)
  def run_2, do: input!() |> parse() |> track_aims() |> then(& &1.depth * &1.horizontal)

  def parse(data) do
    data
    |> String.split("\n")
    |> Enum.map(fn line ->
      [direction, value] = String.split(line, " ")
      {String.to_existing_atom(direction), String.to_integer(value)}
    end)
  end

  defp track_positions(directions) do
    directions
    |> Enum.reduce(%{horizontal: 0, depth: 0}, fn
      {:forward, v}, %{horizontal: horizontal} = acc -> %{acc | horizontal: horizontal + v}
      {:backward, v}, %{horizontal: horizontal} = acc -> %{acc | horizontal: horizontal - v}
      {:up, v}, %{depth: depth} = acc -> %{acc | depth: depth - v}
      {:down, v}, %{depth: depth} = acc -> %{acc | depth: depth + v}
    end)
  end

  defp track_aims(directions) do
    directions
    |> Enum.reduce(%{horizontal: 0, depth: 0, aim: 0}, fn
      {:forward, v}, %{horizontal: horizontal, depth: depth, aim: aim} = acc ->
        %{acc | horizontal: horizontal + v, depth: depth + aim * v}

      {:backward, v}, %{horizontal: horizontal} = acc ->
        %{acc | horizontal: horizontal - v}

      {:up, v}, %{aim: aim} = acc ->
        %{acc | aim: aim - v}

      {:down, v}, %{aim: aim} = acc ->
        %{acc | aim: aim + v}
    end)
  end
end

Aetherus

Aetherus

As usual, my solution is extremely scripting style:

Part 1

#!/usr/bin/env elixir

File.stream!("input.txt")
|> Stream.map(&String.split(&1, ~r/\s+/, trim: true))
|> Stream.map(fn
  ["forward", amount] -> {String.to_integer(amount), 0}
  ["down", amount] -> {0, String.to_integer(amount)}
  ["up", amount] -> {0, -String.to_integer(amount)}
end)
|> Enum.reduce({0, 0}, fn {dx, dy}, {x, y} ->
  {x + dx, y + dy}
end)
|> then(fn {x, y} -> x * y end)
|> IO.inspect()

Part 2

#!/usr/bin/env elixir

File.stream!("input.txt")
|> Stream.map(&String.split(&1, ~r/\s+/, trim: true))
|> Stream.map(fn [command, steps] ->
  {command, String.to_integer(steps)}
end)
|> Enum.reduce({0, 0, 0}, fn
  {"down", amount}, {x, y, aim} -> {x, y, aim + amount}
  {"up", amount}, {x, y, aim} -> {x, y, aim - amount}
  {"forward", amount}, {x, y, aim} -> {x + amount, y + aim * amount, aim}
end)
|> then(fn {x, y, _} -> x * y end)
|> IO.inspect()
code-shoily

code-shoily

In Part 1, you could’ve used Tuple.product and get rid of that then ? I was contemplating on representing my data structure as tuple only so that I could use Tuple.product - one of my favourite functions :smiley: but since I was in a noisy room, I made the representation more verbose so I don’t mentally keep track of the which position means what.

Clever way to represent the positions though!

Aetherus

Aetherus

Exactly. But the first few days should be a warmup, right?

To make things a little bit more fun, I introduced some sort of sigil.

defmodule Point2D do
  defstruct x: 0, y: 0

  def new(x, y) when is_integer(x) and is_integer(y), do:
    %__MODULE__{x: x, y: y}

  def sigil_p(string, _) do
    ~r/-?\d+/
    |> Regex.scan(string)
    |> List.flatten
    |> Enum.map(&String.to_integer/1)
    |> then(&apply(__MODULE__, :new, &1))
  end

  def add(%__MODULE__{x: x1, y: y1}, %__MODULE__{x: x2, y: y2}) do
    new(x1 + x2, y1 + y2)
  end
end

and then rewrote Part 1:

#!/usr/bin/env elixir

import Point2D, only: [sigil_p: 2]

File.stream!("input.txt")
|> Stream.map(&String.split(&1, ~r/\s+/, trim: true))
|> Stream.map(fn
  ["forward", amount] -> ~p(#{amount} 0)
  ["down", amount] -> ~p(0 #{amount})
  ["up", amount] -> ~p(0 -#{amount})
end)
|> Enum.reduce(~p(0 0), &Point2D.add/2)
|> then(fn %{x: x, y: y} -> x * y end)
|> IO.inspect()
code-shoily

code-shoily

niiice. I will freeze my Elixir refactor attempts and get an F# solution out tomorrow. There is an opportunity of using a syntax gimmick there which I’d very much like to try out.

You planning on running Benchee for your two solutions?

wasi0013

wasi0013

Here’s mine:

defmodule Aoc.Y2021.Day02 do
  @moduledoc false
  import Aoc.Helper.IO

  def run_part1(), do: get_input() |> solve_part1()
  def run_part2(), do: get_input() |> solve_part2()

  def solve_part1(data), do: data |> plan_course(0, 0)
  def plan_course([], width, depth), do: width * depth
  def plan_course([["forward", value] | rest], width, depth), do: plan_course(rest, width + value, depth)
  def plan_course([["down", value] | rest], width, depth), do: plan_course(rest, width, depth + value)
  def plan_course([["up", value] | rest], width, depth), do: plan_course(rest, width, depth - value)

  def solve_part2(data), do: data |> process_aim(0, 0, 0)

  def process_aim([], width, depth, _aim), do: width * depth

  def process_aim([["forward", value] | rest], width, depth, aim),
    do: process_aim(rest, width + value, depth + aim * value, aim)

  def process_aim([["down", value] | rest], width, depth, aim), do: process_aim(rest, width, depth, aim + value)
  def process_aim([["up", value] | rest], width, depth, aim), do: process_aim(rest, width, depth, aim - value)

  defp get_input(),
    do:
      get_string_input("2021", "02")
      |> String.split("\n")
      |> Enum.map(&String.split(&1, " "))
      |> Enum.map(fn [ins, value] -> [ins, String.to_integer(value)] end)
end
Aetherus

Aetherus

No, but I guess the first solution should be faster since it doesn’t use regular expressions. However, I can make that sigil_p a macro (the interpolation part can be tricky to implement), and put some of the parsing jobs to the compile-time, but I feel it does not worth the effort because it’s an Elixir script anyway, not a never-should-die service.

Where Next? Top

Trending in Challenges Top

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews