Day 07
Mix.install([
{:flow, "~> 1.2"},
{:kino, "~> 0.11"},
{:nimble_parsec, "~> 1.4"}
])
example_input =
Kino.Input.textarea("example input:")
|> Kino.render()
real_input = Kino.Input.textarea("real input:")
Common
defmodule Parser do
import NimbleParsec
hand = ascii_string([], 5) |> post_traverse(:to_hand)
bid = integer(min: 1)
hand_and_bid =
hand
|> ignore(string(" "))
|> concat(bid)
|> wrap()
|> ignore(optional(ascii_char([?\n])))
list_of_hands =
hand_and_bid
|> times(min: 1)
defparsec(:parse, list_of_hands |> eos())
defp to_hand(rest, [args], context, _line, _offset) do
{rest, [context.to_hand.(args)], context}
end
end
Part 1
defmodule Hand do
defstruct [:card_strength, :hand_strength]
@card_strengths %{
?2 => 1,
?3 => 2,
?4 => 3,
?5 => 4,
?6 => 5,
?7 => 6,
?8 => 7,
?9 => 8,
?T => 9,
?J => 10,
?Q => 11,
?K => 12,
?A => 13
}
def compare(a, b) when a.hand_strength > b.hand_strength, do: :gt
def compare(a, b) when a.hand_strength < b.hand_strength, do: :lt
def compare(a, b) when a.card_strength == b.card_strength, do: :eq
def compare(a, b) when a.card_strength > b.card_strength, do: :gt
def compare(a, b) when a.card_strength < b.card_strength, do: :ld
def new(string) do
chars = to_charlist(string)
sorted = Enum.sort_by(chars, &@card_strengths[&1])
%__MODULE__{
card_strength: card_strength(chars),
hand_strength: hand_strength(sorted)
}
end
defp card_strength(chars), do: Enum.map(chars, &@card_strengths[&1])
defp hand_strength([a, a, a, a, a]), do: 6
defp hand_strength([a, a, a, a, _]), do: 5
defp hand_strength([_, a, a, a, a]), do: 5
defp hand_strength([a, a, a, b, b]), do: 4
defp hand_strength([b, b, a, a, a]), do: 4
defp hand_strength([a, a, a, _, _]), do: 3
defp hand_strength([_, a, a, a, _]), do: 3
defp hand_strength([_, _, a, a, a]), do: 3
defp hand_strength([a, a, b, b, _]), do: 2
defp hand_strength([a, a, _, b, b]), do: 2
defp hand_strength([_, a, a, b, b]), do: 2
defp hand_strength([a, a, _, _, _]), do: 1
defp hand_strength([_, a, a, _, _]), do: 1
defp hand_strength([_, _, a, a, _]), do: 1
defp hand_strength([_, _, _, a, a]), do: 1
defp hand_strength([_, _, _, _, _]), do: 0
end
ExUnit.start(autorun: false)
defmodule HandTest do
use ExUnit.Case, async: true
describe "compare/2" do
test "returns :gt when first hand is stronger" do
assert :gt = Hand.compare(Hand.new("QQQJA"), Hand.new("T55J5"))
end
test "returns :lt when first hand is weaker" do
assert :lt = Hand.compare(Hand.new("KK677"), Hand.new("T55J5"))
end
test "returns :eq when hands are equal" do
assert :eq = Hand.compare(Hand.new("KK677"), Hand.new("KK677"))
end
end
end
ExUnit.run()
hands =
real_input
|> Kino.Input.read()
|> Parser.parse(context: %{to_hand: &Hand.new/1})
|> case do
{:ok, acc, "", _, _, _} -> acc
end
|> Enum.sort_by(&hd/1, Hand)
|> Enum.with_index(1)
|> Enum.reduce(0, fn {[_, bid], idx}, acc -> acc + bid * idx end)
Part 2
defmodule Hand2 do
defstruct [:card_strength, :hand_strength]
@card_strengths %{
?J => 0,
?2 => 1,
?3 => 2,
?4 => 3,
?5 => 4,
?6 => 5,
?7 => 6,
?8 => 7,
?9 => 8,
?T => 9,
?Q => 11,
?K => 12,
?A => 13
}
def compare(a, b) when a.hand_strength > b.hand_strength, do: :gt
def compare(a, b) when a.hand_strength < b.hand_strength, do: :lt
def compare(a, b) when a.card_strength == b.card_strength, do: :eq
def compare(a, b) when a.card_strength > b.card_strength, do: :gt
def compare(a, b) when a.card_strength < b.card_strength, do: :lt
def new(string) do
chars = to_charlist(string)
sorted = Enum.sort_by(chars, &@card_strengths[&1], :desc) |> replace_jokers()
%__MODULE__{
card_strength: card_strength(chars),
hand_strength: hand_strength(sorted)
}
end
defp card_strength(chars), do: Enum.map(chars, &@card_strengths[&1])
defp hand_strength([a, a, a, a, a]), do: 6
defp hand_strength([a, a, a, a, _]), do: 5
defp hand_strength([_, a, a, a, a]), do: 5
defp hand_strength([a, a, a, b, b]), do: 4
defp hand_strength([b, b, a, a, a]), do: 4
defp hand_strength([a, a, a, _, _]), do: 3
defp hand_strength([_, a, a, a, _]), do: 3
defp hand_strength([_, _, a, a, a]), do: 3
defp hand_strength([a, a, b, b, _]), do: 2
defp hand_strength([a, a, _, b, b]), do: 2
defp hand_strength([_, a, a, b, b]), do: 2
defp hand_strength([a, a, _, _, _]), do: 1
defp hand_strength([_, a, a, _, _]), do: 1
defp hand_strength([_, _, a, a, _]), do: 1
defp hand_strength([_, _, _, a, a]), do: 1
defp hand_strength([_, _, _, _, _]), do: 0
defp replace_jokers([a, a, a, a, ?J]), do: [a, a, a, a, a]
defp replace_jokers([a, a, a, ?J, ?J]), do: [a, a, a, a, a]
defp replace_jokers([a, a, ?J, ?J, ?J]), do: [a, a, a, a, a]
defp replace_jokers([a, ?J, ?J, ?J, ?J]), do: [a, a, a, a, a]
defp replace_jokers([a, a, a, b, ?J]), do: [a, a, a, a, b]
defp replace_jokers([a, a, b, ?J, ?J]), do: [a, a, a, a, b]
defp replace_jokers([a, b, ?J, ?J, ?J]), do: [a, a, a, a, b]
defp replace_jokers([a, b, b, b, ?J]), do: [a, b, b, b, b]
defp replace_jokers([a, b, b, ?J, ?J]), do: [a, b, b, b, b]
defp replace_jokers([a, a, b, b, ?J]), do: [a, a, a, b, b]
defp replace_jokers([a, a, b, c, ?J]), do: [a, a, a, b, c]
defp replace_jokers([a, b, c, ?J, ?J]), do: [a, a, a, b, c]
defp replace_jokers([a, b, b, c, ?J]), do: [a, b, b, b, c]
defp replace_jokers([a, b, c, c, ?J]), do: [a, b, c, c, c]
defp replace_jokers([a, b, c, d, ?J]), do: [a, a, b, c, d]
defp replace_jokers(h), do: h
end
ExUnit.start(autorun: false)
defmodule Hand2Test do
use ExUnit.Case, async: true
describe "compare/2" do
test "returns :gt when first hand is stronger because of jokers" do
assert :gt = Hand2.compare(Hand2.new("KTJJT"), Hand2.new("QQQJA"))
end
test "returns :gt when first hand is stronger" do
assert :gt = Hand2.compare(Hand2.new("QQQJA"), Hand2.new("T55J5"))
end
test "returns :lt when first hand is weaker" do
assert :lt = Hand2.compare(Hand2.new("KK677"), Hand2.new("T55J5"))
end
test "returns :eq when hands are equal" do
assert :eq = Hand2.compare(Hand2.new("KK677"), Hand2.new("KK677"))
end
end
end
ExUnit.run()
hands =
real_input
|> Kino.Input.read()
|> Parser.parse(context: %{to_hand: &Hand2.new/1})
|> case do
{:ok, acc, "", _, _, _} -> acc
end
|> Enum.sort_by(&hd/1, Hand2)
|> Enum.with_index(1)
|> Enum.reduce(0, fn {[_, bid], idx}, acc -> acc + bid * idx end)