Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 4: Giant Squid

2021/day04.livemd

Day 4: Giant Squid

Section

defmodule Setup do
  def get_input(prompt) do
    case IO.gets(prompt) do
      :eof -> ""
      line -> line <> get_input(prompt)
    end
  end

  def parse_input(lines) do
    [numbers | boards] = String.split(lines, "\n", trim: true)

    numbers = String.split(numbers, ",", trim: true) |> Enum.map(&amp;String.to_integer/1)

    boards =
      boards
      |> Enum.map(fn board_input ->
        String.split(board_input, " ", trim: true)
        |> Enum.map(&amp;String.to_integer/1)
      end)
      |> Enum.chunk_every(5)

    {numbers,
     Enum.zip(boards, Enum.map(boards, &amp;transpose/1)) |> Enum.map(fn {b1, b2} -> b1 ++ b2 end)}
  end

  def transpose(board) do
    board
    |> Enum.zip()
    |> Enum.map(&amp;Tuple.to_list/1)
  end
end

{numbers, boards} = Setup.get_input("Input") |> Setup.parse_input()
defmodule Bingo do
  def winner(numbers, boards) do
    numbers
    |> Enum.reduce_while(boards, fn n, boards ->
      boards = Enum.map(boards, &amp;play_board(&amp;1, n))

      if board = Enum.find(boards, &amp;wins?/1) do
        {:halt, score(board, n)}
      else
        {:cont, boards}
      end
    end)
  end

  def last_winner(numbers, boards) do
    numbers
    |> Enum.reduce_while(boards, fn n, boards ->
      boards = Enum.map(boards, &amp;play_board(&amp;1, n))

      case Enum.reject(boards, &amp;wins?/1) do
        [] ->
          [board] = boards

          {:halt, score(board, n)}

        boards ->
          {:cont, boards}
      end
    end)
  end

  def play_board(board, number), do: Enum.map(board, &amp;List.delete(&amp;1, number))

  def wins?(board), do: [] in board

  def score(board, number), do: div(Enum.sum(List.flatten(board)), 2) * number
end
Bingo.winner(numbers, boards)
Bingo.last_winner(numbers, boards)

Processes

{:ok, _} = Registry.start_link(keys: :duplicate, name: BingoMachine)

defmodule BingoRow do
  use Agent

  def start_link(numbers) do
    Agent.start_link(fn -> numbers end)
  end

  def stamp(pid, number) do
    Agent.update(fn 
      [] -> )
  end
end
ExUnit.start()

defmodule Test do
  use ExUnit.Case

  @test_input """
  7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1

  22 13 17 11  0
   8  2 23  4 24
  21  9 14 16  7
   6 10  3 18  5
   1 12 20 15 19

   3 15  0  2 22
   9 18 13 17  5
  19  8  7 25 23
  20 11 10 24  4
  14 21 16 12  6

  14 21 17 24  4
  10 16 15  9 19
  18  8 23 26 20
  22 11 13  6  5
   2  0 12  3  7
  """

  setup do
    {:ok, input: Setup.parse_input(@test_input)}
  end

  test "first winner", %{input: {number, boards}} do
    assert Bingo.winner(number, boards) == 4512
  end

  test "last winner", %{input: {number, boards}} do
    assert Bingo.last_winner(number, boards) == 1924
  end
end

ExUnit.run()