add day7 with elixir
This commit is contained in:
parent
37165b60fd
commit
136802984d
11 changed files with 1311 additions and 0 deletions
4
23/elixir/.formatter.exs
Normal file
4
23/elixir/.formatter.exs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Used by "mix format"
|
||||||
|
[
|
||||||
|
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||||
|
]
|
26
23/elixir/.gitignore
vendored
Normal file
26
23/elixir/.gitignore
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# The directory Mix will write compiled artifacts to.
|
||||||
|
/_build/
|
||||||
|
|
||||||
|
# If you run "mix test --cover", coverage assets end up here.
|
||||||
|
/cover/
|
||||||
|
|
||||||
|
# The directory Mix downloads your dependencies sources to.
|
||||||
|
/deps/
|
||||||
|
|
||||||
|
# Where third-party dependencies like ExDoc output generated docs.
|
||||||
|
/doc/
|
||||||
|
|
||||||
|
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||||
|
/.fetch
|
||||||
|
|
||||||
|
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||||
|
erl_crash.dump
|
||||||
|
|
||||||
|
# Also ignore archive artifacts (built via "mix archive.build").
|
||||||
|
*.ez
|
||||||
|
|
||||||
|
# Ignore package tarball (built via "mix hex.build").
|
||||||
|
aoc-*.tar
|
||||||
|
|
||||||
|
# Temporary files, for example, from tests.
|
||||||
|
/tmp/
|
2
23/elixir/Makefile
Normal file
2
23/elixir/Makefile
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
day%:
|
||||||
|
mix run -e "AOC.Day$*.solve()"
|
21
23/elixir/README.md
Normal file
21
23/elixir/README.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Aoc
|
||||||
|
|
||||||
|
**TODO: Add description**
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
||||||
|
by adding `aoc` to your list of dependencies in `mix.exs`:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
def deps do
|
||||||
|
[
|
||||||
|
{:aoc, "~> 0.1.0"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
||||||
|
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
||||||
|
be found at <https://hexdocs.pm/aoc>.
|
||||||
|
|
1000
23/elixir/inputs/day7.txt
Normal file
1000
23/elixir/inputs/day7.txt
Normal file
File diff suppressed because it is too large
Load diff
5
23/elixir/inputs/day7_example.txt
Normal file
5
23/elixir/inputs/day7_example.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
32T3K 765
|
||||||
|
T55J5 684
|
||||||
|
KK677 28
|
||||||
|
KTJJT 220
|
||||||
|
QQQJA 483
|
46
23/elixir/lib/day.ex
Normal file
46
23/elixir/lib/day.ex
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
defmodule AOC.Day do
|
||||||
|
defmacro __using__(opts) do
|
||||||
|
day = Keyword.get(opts, :day)
|
||||||
|
debug = Keyword.get(opts, :debug, false)
|
||||||
|
trim = Keyword.get(opts, :trim, true)
|
||||||
|
input_file = Keyword.get(opts, :input, nil)
|
||||||
|
|
||||||
|
quote do
|
||||||
|
def solve do
|
||||||
|
input =
|
||||||
|
AOC.Day.input_lines(unquote(day), unquote(trim), unquote(input_file))
|
||||||
|
|> parse_input()
|
||||||
|
|
||||||
|
if unquote(debug) do
|
||||||
|
IO.inspect(input)
|
||||||
|
end
|
||||||
|
|
||||||
|
part1_solution = part1(input)
|
||||||
|
IO.puts("Part 1: #{part1_solution}")
|
||||||
|
|
||||||
|
part2_solution = part2(input)
|
||||||
|
IO.puts("Part 2: #{part2_solution}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def input_lines(day, trim, input_file) do
|
||||||
|
lines =
|
||||||
|
input_file_name(day, input_file)
|
||||||
|
|> File.stream!()
|
||||||
|
|
||||||
|
if trim do
|
||||||
|
Enum.map(lines, &String.trim/1)
|
||||||
|
else
|
||||||
|
lines
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def input_file_name(day, input_file) do
|
||||||
|
if input_file do
|
||||||
|
Path.join([File.cwd!(), "inputs", "day#{day}_#{input_file}.txt"])
|
||||||
|
else
|
||||||
|
Path.join([File.cwd!(), "inputs", "day#{day}.txt"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
170
23/elixir/lib/days/day7.ex
Normal file
170
23/elixir/lib/days/day7.ex
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
defmodule AOC.Day7 do
|
||||||
|
# use AOC.Day, day: 7, input: "example"
|
||||||
|
use AOC.Day, day: 7
|
||||||
|
|
||||||
|
@type_to_power %{
|
||||||
|
five_of_a_kind: 6,
|
||||||
|
four_of_a_kind: 5,
|
||||||
|
full_house: 4,
|
||||||
|
three_of_a_kind: 3,
|
||||||
|
two_pair: 2,
|
||||||
|
one_pair: 1,
|
||||||
|
high_card: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@card_to_power %{
|
||||||
|
?A => 14,
|
||||||
|
?K => 13,
|
||||||
|
?Q => 12,
|
||||||
|
?J => 11,
|
||||||
|
?T => 10,
|
||||||
|
?9 => 9,
|
||||||
|
?8 => 8,
|
||||||
|
?7 => 7,
|
||||||
|
?6 => 6,
|
||||||
|
?5 => 5,
|
||||||
|
?4 => 4,
|
||||||
|
?3 => 3,
|
||||||
|
?2 => 2
|
||||||
|
}
|
||||||
|
|
||||||
|
def parse_input(lines) do
|
||||||
|
lines
|
||||||
|
|> Enum.map(fn line ->
|
||||||
|
[hand, bid] = String.split(line, " ")
|
||||||
|
|
||||||
|
{hand, String.to_integer(bid)}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def replace_jokers(hand, card) do
|
||||||
|
Enum.map(hand, fn
|
||||||
|
?J -> card
|
||||||
|
c -> c
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def best_joker_replacement(hand) do
|
||||||
|
freqs = Enum.frequencies(hand)
|
||||||
|
|> Enum.reject(fn {card, count} -> card == ?J end)
|
||||||
|
|
||||||
|
cond do
|
||||||
|
Enum.any?(freqs, fn {card, count} -> count == 4 end) ->
|
||||||
|
{card, _} = Enum.find(freqs, fn {card, count} -> count == 4 end)
|
||||||
|
card
|
||||||
|
Enum.any?(freqs, fn {card, count} -> count == 3 end) ->
|
||||||
|
{card, _} = Enum.find(freqs, fn {card, count} -> count == 3 end)
|
||||||
|
card
|
||||||
|
Enum.any?(freqs, fn {card, count} -> count == 2 end) ->
|
||||||
|
doubles = Enum.filter(freqs, fn {card, count} -> count == 2 end)
|
||||||
|
if length(doubles) == 1 do
|
||||||
|
hd(doubles) |> elem(0)
|
||||||
|
else
|
||||||
|
[{double1, _}, {double2, _}] = doubles
|
||||||
|
if Map.fetch!(@card_to_power, double1) >= Map.fetch!(@card_to_power, double2) do
|
||||||
|
double1
|
||||||
|
else
|
||||||
|
double2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
true ->
|
||||||
|
freqs
|
||||||
|
|> Enum.map(fn {card, count} -> card end)
|
||||||
|
|> Enum.sort(fn card1, card2 ->
|
||||||
|
Map.fetch!(@card_to_power, card1) >= Map.fetch!(@card_to_power, card2)
|
||||||
|
end)
|
||||||
|
|> hd()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_best_joker_configuration(hand) do
|
||||||
|
freqs = Enum.frequencies(hand)
|
||||||
|
joker_count = Map.get(freqs, ?J, 0)
|
||||||
|
|
||||||
|
case joker_count do
|
||||||
|
0 -> hand
|
||||||
|
5 -> 'AAAAA'
|
||||||
|
_ ->
|
||||||
|
joker_replacement = best_joker_replacement(hand)
|
||||||
|
|
||||||
|
replace_jokers(hand, joker_replacement)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def hand_to_type(hand, joker_rule) do
|
||||||
|
freqs = hand
|
||||||
|
|> String.to_charlist()
|
||||||
|
|> then(fn hand ->
|
||||||
|
if joker_rule do
|
||||||
|
find_best_joker_configuration(hand)
|
||||||
|
else
|
||||||
|
hand
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> Enum.frequencies()
|
||||||
|
|> Enum.map(fn {_k, v} -> v end)
|
||||||
|
|
||||||
|
cond do
|
||||||
|
Enum.member?(freqs, 5) -> :five_of_a_kind
|
||||||
|
Enum.member?(freqs, 4) -> :four_of_a_kind
|
||||||
|
Enum.member?(freqs, 3) and Enum.member?(freqs, 2) -> :full_house
|
||||||
|
Enum.member?(freqs, 3) -> :three_of_a_kind
|
||||||
|
Enum.count(freqs, & &1==2) == 2 -> :two_pair
|
||||||
|
Enum.member?(freqs, 2) -> :one_pair
|
||||||
|
true -> :high_card
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def compare_same_type_hands(hand1, hand2, joker_rule) do
|
||||||
|
hand1 = String.to_charlist(hand1)
|
||||||
|
hand2 = String.to_charlist(hand2)
|
||||||
|
|
||||||
|
Enum.zip(hand1, hand2)
|
||||||
|
|> compare_same_type_hands2(joker_rule)
|
||||||
|
end
|
||||||
|
|
||||||
|
def compare_same_type_hands2([], _), do: true
|
||||||
|
|
||||||
|
def compare_same_type_hands2([{card1, card2} | tl], joker_rule) do
|
||||||
|
if card1 == card2 do
|
||||||
|
compare_same_type_hands2(tl, joker_rule)
|
||||||
|
else
|
||||||
|
cond do
|
||||||
|
joker_rule and card1 == ?J -> false
|
||||||
|
joker_rule and card2 == ?J -> true
|
||||||
|
true -> Map.fetch!(@card_to_power, card1) >= Map.fetch!(@card_to_power, card2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def sort_hands(hands, joker_rule \\ false) do
|
||||||
|
Enum.sort(hands, fn {hand1, _bid1}, {hand2, _bid2} ->
|
||||||
|
type1 = hand_to_type(hand1, joker_rule)
|
||||||
|
type2 = hand_to_type(hand2, joker_rule)
|
||||||
|
|
||||||
|
if type1 == type2 do
|
||||||
|
compare_same_type_hands(hand1, hand2, joker_rule)
|
||||||
|
else
|
||||||
|
Map.fetch!(@type_to_power, type1) >= Map.fetch!(@type_to_power, type2)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1(input) do
|
||||||
|
input
|
||||||
|
|> sort_hands()
|
||||||
|
|> Enum.reverse()
|
||||||
|
|> Enum.with_index(1)
|
||||||
|
|> Enum.map(fn {{_card, bid}, rank} -> rank * bid end)
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2(input) do
|
||||||
|
input
|
||||||
|
|> sort_hands(true)
|
||||||
|
|> Enum.reverse()
|
||||||
|
|> Enum.with_index(1)
|
||||||
|
|> Enum.map(fn {{_card, bid}, rank} -> rank * bid end)
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
end
|
28
23/elixir/mix.exs
Normal file
28
23/elixir/mix.exs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
defmodule AOC.MixProject do
|
||||||
|
use Mix.Project
|
||||||
|
|
||||||
|
def project do
|
||||||
|
[
|
||||||
|
app: :aoc,
|
||||||
|
version: "0.1.0",
|
||||||
|
elixir: "~> 1.15",
|
||||||
|
start_permanent: Mix.env() == :prod,
|
||||||
|
deps: deps()
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help compile.app" to learn about applications.
|
||||||
|
def application do
|
||||||
|
[
|
||||||
|
extra_applications: [:logger]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help deps" to learn about dependencies.
|
||||||
|
defp deps do
|
||||||
|
[
|
||||||
|
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||||
|
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
8
23/elixir/test/aoc_test.exs
Normal file
8
23/elixir/test/aoc_test.exs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
defmodule AocTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
doctest Aoc
|
||||||
|
|
||||||
|
test "greets the world" do
|
||||||
|
assert Aoc.hello() == :world
|
||||||
|
end
|
||||||
|
end
|
1
23/elixir/test/test_helper.exs
Normal file
1
23/elixir/test/test_helper.exs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ExUnit.start()
|
Loading…
Reference in a new issue