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

Advent of Code 2023 - Day 07

2023/07.livemd

Advent of Code 2023 - Day 07

Mix.install([
  {:kino, "~> 0.11.0"},
  {:kino_aoc, github: "ljgago/kino_aoc"},
  {:explorer, "~> 0.7.0"},
  {:kino_explorer, "~> 0.1.10"}
])

Input

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2023", "7", System.fetch_env!("LB_AOC_SESSION"))
require Explorer.DataFrame, as: DF
alias Explorer.Series

{hands, bids} =
  puzzle_input
  |> String.split(["\n", " "])
  |> Enum.chunk_every(2)
  |> Enum.map(&List.to_tuple/1)
  |> Enum.unzip()

init_df =
  DF.new(hand: hands, bid: bids)
  |> DF.mutate(bid: Series.cast(bid, :integer))
hand_to_cards = fn hand, j_value ->
  hand
  |> String.split("", trim: true)
  |> Enum.map(fn
    "A" -> 14
    "K" -> 13
    "Q" -> 12
    "J" -> j_value
    "T" -> 10
    x -> String.to_integer(x)
  end)
end

put_cards = fn df, j_value ->
  [c1, c2, c3, c4, c5] =
    df["hand"]
    |> Series.to_list()
    |> Stream.map(&hand_to_cards.(&1, j_value))
    |> Enum.zip_with(&Function.identity/1)

  df
  |> DF.put(:c1, Series.from_list(c1))
  |> DF.put(:c2, Series.from_list(c2))
  |> DF.put(:c3, Series.from_list(c3))
  |> DF.put(:c4, Series.from_list(c4))
  |> DF.put(:c5, Series.from_list(c5))
end

put_strength = fn df, mapper ->
  {strengths, types} =
    df
    |> DF.select(["c1", "c2", "c3", "c4", "c5"])
    |> DF.to_columns()
    |> Map.values()
    |> Stream.zip_with(&Function.identity/1)
    |> Stream.map(&Enum.sort(&1, :desc))
    |> Stream.map(mapper)
    |> Enum.unzip()

  df
  |> DF.put(:type, Series.from_list(types))
  |> DF.put(:strength, Series.from_list(strengths))
end

put_rank_and_winnings = fn df ->
  total = Series.size(df["hand"])
  ranks = Enum.to_list(total..1)

  df
  |> DF.arrange(desc: strength, desc: c1, desc: c2, desc: c3, desc: c4, desc: c5)
  |> DF.put(:rank, Series.from_list(ranks))
  |> DF.mutate(winnings: multiply(rank, bid))
end

Part 1

results =
  init_df
  |> put_cards.(11)
  |> put_strength.(fn
    [a, a, a, a, a] -> {6, "five_of_a_kind"}
    [a, a, a, a, _] -> {5, "four_of_a_kind"}
    [_, a, a, a, a] -> {5, "four_of_a_kind"}
    [a, a, a, b, b] -> {4, "full_house"}
    [a, a, b, b, b] -> {4, "full_house"}
    [a, a, a, _, _] -> {3, "three_of_a_kind"}
    [_, a, a, a, _] -> {3, "three_of_a_kind"}
    [_, _, a, a, a] -> {3, "three_of_a_kind"}
    [a, a, b, b, _] -> {2, "two_pair"}
    [a, a, _, b, b] -> {2, "two_pair"}
    [_, a, a, b, b] -> {2, "two_pair"}
    [a, a, _, _, _] -> {1, "one_pair"}
    [_, a, a, _, _] -> {1, "one_pair"}
    [_, _, a, a, _] -> {1, "one_pair"}
    [_, _, _, a, a] -> {1, "one_pair"}
    [_, _, _, _, _] -> {0, "high_card"}
  end)
  |> put_rank_and_winnings.()
  |> DF.select(["hand", "type", "rank", "bid", "winnings"])
DF.summarise(results, total_winnings: sum(winnings))

Part 2

results =
  init_df
  |> put_cards.(1)
  |> put_strength.(fn
    [a, a, a, a, a] -> {6, "five_of_a_kind"}
    [a, a, a, a, 1] -> {6, "five_of_a_kind"}
    [a, a, a, 1, 1] -> {6, "five_of_a_kind"}
    [a, a, 1, 1, 1] -> {6, "five_of_a_kind"}
    [_, 1, 1, 1, 1] -> {6, "five_of_a_kind"}
    [a, a, a, a, _] -> {5, "four_of_a_kind"}
    [_, a, a, a, a] -> {5, "four_of_a_kind"}
    [a, a, a, _, 1] -> {5, "four_of_a_kind"}
    [_, a, a, a, 1] -> {5, "four_of_a_kind"}
    [a, a, _, 1, 1] -> {5, "four_of_a_kind"}
    [_, a, a, 1, 1] -> {5, "four_of_a_kind"}
    [_, _, 1, 1, 1] -> {5, "four_of_a_kind"}
    [a, a, a, b, b] -> {4, "full_house"}
    [a, a, b, b, b] -> {4, "full_house"}
    [a, a, a, _, 1] -> {4, "full_house"}
    [a, a, b, b, 1] -> {4, "full_house"}
    [a, a, b, b, 1] -> {4, "full_house"}
    [a, a, _, 1, 1] -> {4, "full_house"}
    [a, a, a, _, _] -> {3, "three_of_a_kind"}
    [_, a, a, a, _] -> {3, "three_of_a_kind"}
    [_, _, a, a, a] -> {3, "three_of_a_kind"}
    [a, a, _, _, 1] -> {3, "three_of_a_kind"}
    [_, a, a, _, 1] -> {3, "three_of_a_kind"}
    [_, _, a, a, 1] -> {3, "three_of_a_kind"}
    [_, _, _, 1, 1] -> {3, "three_of_a_kind"}
    [a, a, b, b, _] -> {2, "two_pair"}
    [a, a, _, b, b] -> {2, "two_pair"}
    [_, a, a, b, b] -> {2, "two_pair"}
    [a, a, _, _, 1] -> {2, "two_pair"}
    [a, a, _, _, _] -> {1, "one_pair"}
    [_, a, a, _, _] -> {1, "one_pair"}
    [_, _, a, a, _] -> {1, "one_pair"}
    [_, _, _, a, a] -> {1, "one_pair"}
    [_, _, _, _, 1] -> {1, "one_pair"}
    [_, _, _, _, _] -> {0, "high_card"}
  end)
  |> put_rank_and_winnings.()
  |> DF.select(["hand", "type", "rank", "bid", "winnings"])
DF.summarise(results, total_winnings: sum(winnings))

Run in Livebook