Aetherus

Aetherus

This topic is about the Advent of Code 2021 - Day 4.

Thanks to @bjorng , we now have a new Private Leaderboard.

The entry code is:
370884-a6a71927

Showing Posts 1 to 10

bjorng

bjorng

Erlang Core Team
Aetherus

Aetherus OP

Today I tried LiveBook on the new Elixir 1.13.0, and accidentally deleted my part 1 code, so I just post my part 2:

[moves | boards] =
  File.read!("input.txt")
  |> String.split("\n\n", trim: true)

# [integer]
moves =
  moves
  |> String.split(~r/\D/, trim: true)
  |> Enum.map(&String.to_integer/1)

# %{val => {row, col}}
boards =
  boards
  |> Enum.map(&String.split/1)
  |> Enum.map(&Enum.chunk_every(&1, 5))
  |> Enum.map(fn board ->
    for {row, i} <- Enum.with_index(board), {num, j} <- Enum.with_index(row), into: %{} do
      {String.to_integer(num), {i, j}}
    end
  end)

{_, {move, last_winning_board}} = 
  for move <- moves, reduce: {boards, nil} do
    {boards, last_win} ->
      boards = Enum.map(boards, fn board ->
        Map.delete(board, move)
      end)
      {wins, boards} = Enum.split_with(boards, fn board ->
        Enum.any?(0..4, fn i ->
          Enum.all?(board, &not match?({_, {^i, _}}, &1))
        end) or
        Enum.any?(0..4, fn j ->
          Enum.all?(board, &not match?({_, {_, ^j}}, &1))
        end)
      end)

      case wins do
        [] -> {boards, last_win}
        _ -> {boards, {move, List.last(wins)}}
      end
  end

last_winning_board
|> Map.keys()
|> Enum.sum()
|> Kernel.*(move)
|> IO.inspect()

It feels a little bit tricky implementing a modification-based algorithm in an immutable way.

Aetherus

Aetherus OP

Sorry for posting twice. There was a network issue when I tried to edit my answer, and I don’t know how to delete one.

code-shoily

code-shoily

It’s starting to get difficult already, at least from the perspective of Elixir. It’s difficult to come up with a solution fast that is mutation and early return friendly on a matrix like data.

Aetherus

Aetherus OP

I usually use %{{x, y} => val} to represent a matrix, but for today’s challenge, I recognized that I need to look up {x, y} by values, so I swapped the keys and the values.

code-shoily

code-shoily

Here I am thinking the opposite direction, I am thinking %{value => {row, col}} where row = div(idx, 5) and col = rem(idx, 5). That way I can just remove the value into a bucket of rowlist, collist as I move forward. But it’s feeling slightly weird and verbose.

Update: Seems like you’re doing the same thing. Sorry, brain’s not keeping up with eyes lol.

code-shoily

code-shoily

Ah, finally got the first one right, also, I got to use the new Map.map :smiley:

UPDATE: Did both parts, Part 2 was quicker to implement, but I don’t have a good feeling about this code for some reason.

defmodule AdventOfCode.Y2021.Day04 do
  @moduledoc """
  --- Day 4: Giant Squid ---
  Problem Link: https://adventofcode.com/2021/day/4
  """
  use AdventOfCode.Helpers.InputReader, year: 2021, day: 4

  def run_1, do: input!() |> parse() |> play_1()
  def run_2, do: input!() |> parse() |> play_2()

  def parse(data) do
    [order_data | board_data] = String.split(data, "\n\n", trim: true)
    {get_orders(order_data), get_boards(board_data)}
  end

  defp get_orders(data), do: data |> String.split(",") |> Enum.map(&String.to_integer/1)

  defp get_boards(data), do: data |> Enum.with_index() |> Enum.map(&get_board/1)

  defp get_board({data, idx}) do
    data
    |> String.split("\n")
    |> Enum.flat_map(fn line ->
      line
      |> String.split(" ", trim: true)
      |> Enum.map(&String.to_integer/1)
    end)
    |> Enum.with_index()
    |> Enum.into(%{})
    |> Map.map(fn {_, idx} -> {div(idx, 5), rem(idx, 5)} end)
    |> then(&board_state(&1, idx))
  end

  defp board_state(board, idx), do: {idx, board, [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]}

  def play_1({orders, board_states}) do
    orders
    |> Enum.reduce_while(board_states, fn order, acc ->
      acc = Enum.map(acc, &mark_board(&1, order))

      case get_winner(acc) do
        nil -> {:cont, acc}
        board -> {:halt, {score(board), order}}
      end
    end)
    |> Tuple.product()
  end

  def score(board), do: board |> Map.keys() |> Enum.sum()

  defp mark_board({idx, board, rows, cols} = state, order) do
    case Map.pop(board, order) do
      {{row, col}, board} ->
        {idx, board, List.update_at(rows, row, &(&1 + 1)), List.update_at(cols, col, &(&1 + 1))}

      {nil, _} ->
        state
    end
  end

  defp get_winner(board_states) do
    Enum.reduce_while(board_states, nil, fn {_, board, rows, cols}, _ ->
      5 in rows or (5 in cols && {:halt, board}) || {:cont, nil}
    end)
  end

  def play_2({orders, board_states}) do
    orders
    |> Enum.reduce({board_states, []}, fn order, {acc, winners} ->
      acc = Enum.map(acc, &mark_board(&1, order))

      {acc,
       [Enum.map(get_winners(acc), fn {idx, board} -> {idx, score(board), order} end) | winners]}
    end)
    |> elem(1)
    |> Enum.reverse()
    |> Enum.drop_while(&Enum.empty?/1)
    |> Enum.flat_map(&Function.identity/1)
    |> last_winner(Enum.count(board_states), MapSet.new())
    |> Tuple.product()
  end

  defp last_winner([{idx, score, order} | rest], size, map_set) do
    set = MapSet.put(map_set, idx)
    (Enum.count(set) == size && {score, order}) || last_winner(rest, size, set)
  end

  defp get_winners(board_states) do
    Enum.reduce(board_states, [], fn {idx, board, rows, cols}, acc ->
      ((5 in rows or 5 in cols) && [{idx, board} | acc]) || acc
    end)
  end
end
wasi0013

wasi0013

My solution: y2021/day_04.ex

Klohto

Klohto

I’m already ashamed by posting this horrible Part 1.
At this point, I’ll have to switch to normal function definitions because it’s becoming a spaghetti mess with anons and it’s just Day 4.

Don’t ask how I did Part 2, I have to clean both solutions up first.
It’s a challenge trying to do this in an immutable way.

# Day 4

## Input

<!-- livebook:{"livebook_object":"cell_input","name":"input","type":"text","value":"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"} -->

```elixir
# Livebook Input (atleast the default one) doesn't support multiline
[winning_nums | boards] = IO.gets(:input) |> String.split(~r{\s}, trim: true)

winning_nums =
  winning_nums
  |> String.split(",")
  |> Enum.map(&String.to_integer/1)

boards =
  boards
  |> Enum.map(&String.to_integer/1)
  |> Enum.chunk_every(5)
  |> Enum.chunk_every(5)
```

## Part 1

```elixir
calculate_card = fn bingo_card, last_number ->
  List.flatten(bingo_card)
  |> Enum.reject(&(&1 == -1))
  |> Enum.sum()
  |> Kernel.*(last_number)
  |> IO.inspect()

  # exit after first find
  Kernel.exit(:ok)
end

# mark all matching numbers inside boards
mark_boards = fn boards, n ->
  List.flatten(boards)
  |> Enum.map(&if(&1 == n, do: -1, else: &1))
  # convert back to 5x5 boards
  |> Enum.chunk_every(5)
  |> Enum.chunk_every(5)
end

check_boards = fn boards, last_n ->
  Enum.each(boards, fn b ->
    for i <- 0..4 do
      # horizontal || vertical
      if Enum.sum(Enum.at(b, i)) == -5 || Enum.sum(Enum.map(b, &Enum.at(&1, i))) == -5 do
        calculate_card.(b, last_n)
      end
    end
  end)

  boards
end

Enum.reduce(winning_nums, boards, fn n, b -> mark_boards.(b, n) |> check_boards.(n) end)
```

## Part 2

```elixir
# So ugly I had to hide it
```

Where Next? Top

Trending in Challenges Top

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
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
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews