optimize day7 with cache on hand_type

This commit is contained in:
Caleb Webber 2023-12-07 10:32:48 -05:00
parent 8c60dd6550
commit 23f8a28bbd
2 changed files with 50 additions and 34 deletions

8
bench/day7_bench.exs Normal file
View file

@ -0,0 +1,8 @@
{_, content} = File.read("./input/2023_7.txt")
Benchee.run(
%{
"day7_part2" => fn -> content |> String.split("\n") |> Stream.map(&String.trim/1) |> Mix.Tasks.Day7.solve() end
}
)

View file

@ -58,53 +58,61 @@ defmodule Mix.Tasks.Day7 do
end
defp brute_force_best_hand(h1) do
cache = Process.get(:key_cache, %{})
cond do
cache |> Map.has_key?(h1) -> Map.get(cache, h1) |> hand_type(:part2)
true -> best = generate_all_hands(h1)
|> Enum.sort(fn cand1, cand2 ->
best = generate_all_hands(h1)
|> Enum.sort(fn cand1, cand2 ->
hand_type(cand1, :part2) > hand_type(cand2, :part2)
end)
|> Enum.at(0)
Process.put(:key_cache, cache |> Map.put(h1, best))
best |> hand_type(:part2)
end
hand_type(cand1, :part2) > hand_type(cand2, :part2)
end)
|> Enum.at(0)
best |> hand_type(:part2)
end
defp hand_type(h1, part) do
values = h1 |> String.graphemes() |> Enum.frequencies() |> Map.values()
cond do
part === :part2 and h1 |> String.contains?("J") -> brute_force_best_hand(h1)
# five of kind
values |> Enum.any?(&(&1 === 5)) -> 7
# four of kind
values |> Enum.any?(&(&1 === 4)) -> 6
# full house
values |> Enum.any?(&(&1 === 3)) &&
values |> Enum.any?(&(&1 === 2)) -> 5
# three
values |> Enum.any?(&(&1 === 3)) -> 4
# two pair
values |> Enum.filter(&(&1 === 2)) |> Enum.count() === 2 -> 3
# one
values |> Enum.filter(&(&1 === 2)) |> Enum.count() === 1 -> 2
# high card
values |> Enum.all?(&(&1 === 1)) -> 1
cache = Process.get(:cache_key, %{})
store_and_return = fn i ->
if part === :part2 do
Process.put(:cache_key, cache |> Map.put(h1, i))
end
i
end
if part === :part2 and cache |> Map.has_key?(h1) do
Map.get(cache, h1)
else
values = h1 |> String.graphemes() |> Enum.frequencies() |> Map.values()
cond do
part === :part2 and h1 |> String.contains?("J") -> store_and_return.(brute_force_best_hand(h1))
# five of kind
values |> Enum.any?(&(&1 === 5)) -> store_and_return.(7)
# four of kind
values |> Enum.any?(&(&1 === 4)) -> store_and_return.(6)
# full house
values |> Enum.any?(&(&1 === 3)) &&
values |> Enum.any?(&(&1 === 2)) -> store_and_return.(5)
# three
values |> Enum.any?(&(&1 === 3)) -> store_and_return.(4)
# two pair
values |> Enum.filter(&(&1 === 2)) |> Enum.count() === 2 -> store_and_return.(3)
# one
values |> Enum.filter(&(&1 === 2)) |> Enum.count() === 1 -> store_and_return.(2)
# high card
values |> Enum.all?(&(&1 === 1)) -> store_and_return.(1)
end
end
end
def solve(input) do
part = :part1
part = :part2
Process.delete(:key_cache)
input
|> Enum.map(fn line -> line |> String.trim() |> String.split(" ") |> List.to_tuple() end)
|> Stream.reject(&(&1 === ""))
|> Stream.map(fn line ->
line |> String.trim() |> String.split(" ") |> List.to_tuple()
end)
|> Enum.sort(fn {h1, _}, {h2, _} -> sort_by_hand_ascending(h1, h2, part) end)
|> Enum.with_index()
|> Stream.with_index()
|> Enum.reduce(
0,
fn {{hand, bid}, i}, acc ->
IO.puts(hand)
fn {{_, bid}, i}, acc ->
bid = String.to_integer(bid)
acc + bid * (i+1)
end