Powered by AppSignal & Oban Pro

AoC - Day5

2022/elixir/day5.livemd

AoC - Day5

Mix.install([
  {:kino_aoc, git: "https://github.com/ljgago/kino_aoc"}
])

Setup

{:ok, data} = KinoAOC.download_puzzle("2022", "5", System.fetch_env!("LB_AOC_SECRET"))

Solve

defmodule Day5 do
  def solve(data) do
    [crates, moves] = String.split(data, "\n\n")

    cr = parse_crates(crates)

    moves
    |> parse_moves()
    |> Enum.reduce([cr, cr], fn move, [cr1, cr2] ->
      [task1(cr1, move), task2(cr2, move)]
    end)
    |> Enum.map(&get_res/1)
  end

  def parse_crates(crates) do
    crates
    |> String.split("\n")
    |> Enum.reverse()
    |> then(fn [_ind | crates] -> crates end)
    |> Enum.flat_map(&clean_columns/1)
    |> Enum.group_by(fn {_c, ind} -> ind end)
    |> Enum.into(%{}, fn {ind, el} -> {ind, parse_item(el)} end)
  end

  def clean_columns(row) do
    row
    |> String.codepoints()
    |> Enum.chunk_every(4)
    |> Enum.map(&Enum.at(&1, 1))
    |> Enum.with_index(1)
  end

  def parse_item(box) do
    box |> Enum.map(&elem(&1, 0)) |> Enum.reverse() |> Enum.reject(&(&1 == " "))
  end

  def parse_moves(moves) do
    moves
    |> String.trim()
    |> String.split("\n", trim: true)
    |> Enum.map(&String.split/1)
    |> Enum.map(fn [_, cnt, _, f, _, t] -> {to_i(cnt), to_i(f), to_i(t)} end)
  end

  def to_i(str) when is_binary(str), do: String.to_integer(str)

  def task1(cr, {cnt, f, t}) do
    Enum.reduce(1..cnt, cr, fn _i, acc ->
      [el | ff_rest] = acc[f]

      acc
      |> Map.put(f, ff_rest)
      |> Map.put(t, [el | acc[t]])
    end)
  end

  def task2(cr, {cnt, f, t}) do
    {buf, f_rest} = Enum.split(cr[f], cnt)
    t_new = Enum.concat(buf, cr[t])

    cr |> Map.put(f, f_rest) |> Map.put(t, t_new)
  end

  def get_res(cr) do
    cr |> Map.values() |> Enum.map(fn [c | _] -> c end) |> Enum.join()
  end
end

# ["QNHWJVJZW", "BPCZJLFJW"]
Day5.solve(data)

Check

ExUnit.start(autorun: false)

defmodule AoCTest do
  use ExUnit.Case, async: false

  @testd """
      [D]    
  [N] [C]    
  [Z] [M] [P]
  1   2   3 

  move 1 from 2 to 1
  move 3 from 1 to 3
  move 2 from 2 to 1
  move 1 from 1 to 2
  """

  test "solves test cases" do
    assert ["CMZ", "MCD"] = Day5.solve(@testd)
  end
end

ExUnit.run()