aoc: solve day 7
This commit is contained in:
parent
cc2f49a3b9
commit
27892ff860
9 changed files with 1359 additions and 0 deletions
4
advent-of-code-2023/elixir/day_07/.formatter.exs
Normal file
4
advent-of-code-2023/elixir/day_07/.formatter.exs
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
26
advent-of-code-2023/elixir/day_07/.gitignore
vendored
Normal file
26
advent-of-code-2023/elixir/day_07/.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").
|
||||
day_07-*.tar
|
||||
|
||||
# Temporary files, for example, from tests.
|
||||
/tmp/
|
104
advent-of-code-2023/elixir/day_07/README.md
Normal file
104
advent-of-code-2023/elixir/day_07/README.md
Normal file
|
@ -0,0 +1,104 @@
|
|||
# Day 7: Camel Cards
|
||||
|
||||
Your all-expenses-paid trip turns out to be a one-way, five-minute ride in an airship.
|
||||
(At least it's a cool airship!) It drops you off at the edge of a vast desert and descends back to Island Island.
|
||||
|
||||
"Did you bring the parts?"
|
||||
|
||||
You turn around to see an Elf completely covered in white clothing, wearing goggles, and riding a large camel.
|
||||
|
||||
"Did you bring the parts?" she asks again, louder this time. You aren't sure what parts she's looking for;
|
||||
you're here to figure out why the sand stopped.
|
||||
|
||||
"The parts! For the sand, yes! Come with me; I will show you." She beckons you onto the camel.
|
||||
|
||||
After riding a bit across the sands of Desert Island, you can see what look like very large rocks covering half of the horizon.
|
||||
The Elf explains that the rocks are all along the part of Desert Island that is directly above Island Island,
|
||||
making it hard to even get there. Normally, they use big machines to move the rocks and filter the sand,
|
||||
but the machines have broken down because Desert Island recently stopped receiving the parts they need to fix the machines.
|
||||
|
||||
You've already assumed it'll be your job to figure out why the parts stopped when she asks if you can help. You agree automatically.
|
||||
|
||||
Because the journey will take a few days, she offers to teach you the game of Camel Cards. Camel Cards is sort of similar to poker except it's designed to be easier to play while riding a camel.
|
||||
|
||||
In Camel Cards, you get a list of hands, and your goal is to order them based on the strength of each hand.
|
||||
A hand consists of five cards labeled one of `A`, `K`, `Q`, `J`, `T`, `9`, `8`, `7`, `6`, `5`, `4`, `3`, or `2`.
|
||||
The relative strength of each card follows this order, where `A` is the highest and `2` is the lowest.
|
||||
|
||||
Every hand is exactly one type. From strongest to weakest, they are:
|
||||
|
||||
- Five of a kind, where all five cards have the same label: AAAAA
|
||||
- Four of a kind, where four cards have the same label and one card has a different label: AA8AA
|
||||
- Full house, where three cards have the same label, and the remaining two cards share a different label: 23332
|
||||
- Three of a kind, where three cards have the same label, and the remaining two cards are each different from any other card in the hand: TTT98
|
||||
- Two pair, where two cards share one label, two other cards share a second label, and the remaining card has a third label: 23432
|
||||
- One pair, where two cards share one label, and the other three cards have a different label from the pair and each other: A23A4
|
||||
- High card, where all cards' labels are distinct: 23456
|
||||
|
||||
Hands are primarily ordered based on type; for example, every full house is stronger than any three of a kind.
|
||||
|
||||
If two hands have the same type, a second ordering rule takes effect.
|
||||
Start by comparing the first card in each hand. If these cards are different, the hand with the stronger first card is considered stronger.
|
||||
If the first card in each hand have the same label, however, then move on to considering the second card in each hand.
|
||||
If they differ, the hand with the higher second card wins;
|
||||
otherwise, continue with the third card in each hand, then the fourth, then the fifth.
|
||||
|
||||
So, `33332` and `2AAAA` are both four of a kind hands, but `33332` is stronger because its first card is stronger.
|
||||
Similarly, `77888` and `77788` are both a full house, but `77888` is stronger because its third card is stronger
|
||||
(and both hands have the same first and second card).
|
||||
|
||||
To play Camel Cards, you are given a list of hands and their corresponding bid (your puzzle input). For example:
|
||||
|
||||
```
|
||||
32T3K 765
|
||||
T55J5 684
|
||||
KK677 28
|
||||
KTJJT 220
|
||||
QQQJA 483
|
||||
```
|
||||
|
||||
This example shows five hands; each hand is followed by its bid amount.
|
||||
Each hand wins an amount equal to its bid multiplied by its rank, where the weakest hand gets rank 1,
|
||||
the second-weakest hand gets rank 2, and so on up to the strongest hand.
|
||||
Because there are five hands in this example, the strongest hand will have rank 5 and its bid will be multiplied by 5.
|
||||
|
||||
So, the first step is to put the hands in order of strength:
|
||||
|
||||
- `32T3K` is the only one pair and the other hands are all a stronger type, so it gets rank 1.
|
||||
- `KK677` and `KTJJT` are both two pair. Their first cards both have the same label, but the second card of `KK677` is stronger (K vs T), so `KTJJT` gets rank 2 and `KK677` gets rank 3.
|
||||
- `T55J5` and `QQQJA` are both three of a kind. `QQQJA` has a stronger first card, so it gets rank 5 and `T55J5` gets rank 4.
|
||||
|
||||
Now, you can determine the total winnings of this set of hands by adding up the result of multiplying each hand's bid with its rank (765 * 1 + 220 * 2 + 28 * 3 + 684 * 4 + 483 * 5). So the total winnings in this example are 6440.
|
||||
|
||||
Find the rank of every hand in your set. What are the total winnings?
|
||||
|
||||
## Part Two
|
||||
|
||||
To make things a little more interesting, the Elf introduces one additional rule.
|
||||
Now, J cards are jokers - wildcards that can act like whatever card would make the hand the strongest type possible.
|
||||
|
||||
To balance this, `J` cards are now the weakest individual cards, weaker even than `2`.
|
||||
The other cards stay in the same order: `A`, `K`, `Q`, `T`, `9`, `8`, `7`, `6`, `5`, `4`, `3`, `2`, `J`.
|
||||
|
||||
`J` cards can pretend to be whatever card is best for the purpose of determining hand type;
|
||||
for example, `QJJQ2` is now considered four of a kind.
|
||||
However, for the purpose of breaking ties between two hands of the same type, `J` is always treated as `J`,
|
||||
not the card it's pretending to be: `JKKK2` is weaker than `QQQQ2` because `J` is weaker than `Q`.
|
||||
|
||||
Now, the above example goes very differently:
|
||||
|
||||
```
|
||||
32T3K 765
|
||||
T55J5 684
|
||||
KK677 28
|
||||
KTJJT 220
|
||||
QQQJA 483
|
||||
```
|
||||
|
||||
- 32T3K is still the only one pair; it doesn't contain any jokers, so its strength doesn't increase.
|
||||
- KK677 is now the only two pair, making it the second-weakest hand.
|
||||
- T55J5, KTJJT, and QQQJA are now all four of a kind! T55J5 gets rank 3, QQQJA gets rank 4, and KTJJT gets rank 5.
|
||||
|
||||
With the new joker rule, the total winnings in this example are 5905.
|
||||
|
||||
Using the new joker rule, find the rank of every hand in your set. What are the new total winnings?
|
169
advent-of-code-2023/elixir/day_07/lib/solution.ex
Normal file
169
advent-of-code-2023/elixir/day_07/lib/solution.ex
Normal file
|
@ -0,0 +1,169 @@
|
|||
defmodule Solution do
|
||||
@spec get_total_winnings(String.t()) :: integer()
|
||||
def get_total_winnings(filename, with_jokers \\ false) do
|
||||
hands =
|
||||
filename
|
||||
|> File.stream!()
|
||||
|> Enum.map(&parse_line/1)
|
||||
|> Map.new()
|
||||
|
||||
sorted_cards =
|
||||
hands
|
||||
|> Map.keys()
|
||||
|> Enum.sort_by(fn hand -> get_sorting_order(hand, with_jokers) end)
|
||||
|
||||
sorted_cards
|
||||
|> Enum.with_index(1)
|
||||
|> Enum.reduce(0, fn {el, idx}, acc -> acc + idx * hands[el] end)
|
||||
end
|
||||
|
||||
defp parse_line(line) do
|
||||
[hand, bid] =
|
||||
line
|
||||
|> String.trim()
|
||||
|> String.split(" ")
|
||||
|
||||
{hand, String.to_integer(bid)}
|
||||
end
|
||||
|
||||
# Map a hand to a list that may be used for sorting.
|
||||
defp get_sorting_order(hand, with_jokers) do
|
||||
# Map cards to corresponding power,
|
||||
# e.g. "57TA" -> [5, 7, 10, 14]
|
||||
powers =
|
||||
hand
|
||||
|> String.graphemes()
|
||||
|> Enum.map(fn card -> get_card_power(card, with_jokers) end)
|
||||
|
||||
# Prepend hand rank.
|
||||
[get_rank(hand, with_jokers) | powers]
|
||||
end
|
||||
|
||||
@doc """
|
||||
Converts a card symbol to a number.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Solution.get_card_power("J")
|
||||
11
|
||||
|
||||
iex> Solution.get_card_power("J", true)
|
||||
1
|
||||
|
||||
iex> Solution.get_card_power("5")
|
||||
5
|
||||
"""
|
||||
def get_card_power(card, with_jokers \\ false) do
|
||||
case card do
|
||||
"A" ->
|
||||
14
|
||||
|
||||
"K" ->
|
||||
13
|
||||
|
||||
"Q" ->
|
||||
12
|
||||
|
||||
"J" ->
|
||||
if with_jokers, do: 1, else: 11
|
||||
|
||||
"T" ->
|
||||
10
|
||||
|
||||
num ->
|
||||
String.to_integer(num)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the rank of a hand based on the number of identical cards.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Solution.get_rank("AAAAA")
|
||||
7
|
||||
|
||||
iex> Solution.get_rank("AA8AA")
|
||||
6
|
||||
|
||||
iex> Solution.get_rank("23332")
|
||||
5
|
||||
|
||||
iex> Solution.get_rank("TTT98")
|
||||
4
|
||||
|
||||
iex> Solution.get_rank("23432")
|
||||
3
|
||||
|
||||
iex> Solution.get_rank("A23A4")
|
||||
2
|
||||
|
||||
iex> Solution.get_rank("23456")
|
||||
1
|
||||
"""
|
||||
def get_rank(hand, with_jokers \\ false) do
|
||||
hand = if with_jokers, do: replace_joker(hand), else: hand
|
||||
|
||||
freq =
|
||||
hand
|
||||
|> String.graphemes()
|
||||
|> Enum.frequencies()
|
||||
|> Map.values()
|
||||
|> Enum.sort()
|
||||
|
||||
case freq do
|
||||
# five of a kind
|
||||
[5] -> 7
|
||||
# four of a kind
|
||||
[1, 4] -> 6
|
||||
# full house
|
||||
[2, 3] -> 5
|
||||
# three of a kind
|
||||
[1, 1, 3] -> 4
|
||||
# two pair
|
||||
[1, 2, 2] -> 3
|
||||
# one pair
|
||||
[1, 1, 1, 2] -> 2
|
||||
# high card
|
||||
_ -> 1
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Replaces all J cards in hand with the card that will result in the best rank.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Solution.replace_joker("T55J5")
|
||||
"T5555"
|
||||
|
||||
iex> Solution.replace_joker("KTJJT")
|
||||
"KTTTT"
|
||||
|
||||
iex> Solution.replace_joker("JJJJJ")
|
||||
"AAAAA"
|
||||
"""
|
||||
def replace_joker(hand) do
|
||||
freq =
|
||||
hand
|
||||
|> String.replace("J", "")
|
||||
|> String.graphemes()
|
||||
|> Enum.frequencies()
|
||||
|
||||
if Enum.empty?(Map.keys(freq)) do
|
||||
String.replace(hand, "J", "A")
|
||||
else
|
||||
max_count =
|
||||
freq
|
||||
|> Map.values()
|
||||
|> Enum.max()
|
||||
|
||||
{most_used_symbol, _} =
|
||||
freq
|
||||
|> Map.to_list()
|
||||
|> Enum.find(fn {_, count} -> count == max_count end)
|
||||
|
||||
String.replace(hand, "J", most_used_symbol)
|
||||
end
|
||||
end
|
||||
end
|
28
advent-of-code-2023/elixir/day_07/mix.exs
Normal file
28
advent-of-code-2023/elixir/day_07/mix.exs
Normal file
|
@ -0,0 +1,28 @@
|
|||
defmodule Day07.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :day_07,
|
||||
version: "0.1.0",
|
||||
elixir: "~> 1.16",
|
||||
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
|
5
advent-of-code-2023/elixir/day_07/test/example.txt
Normal file
5
advent-of-code-2023/elixir/day_07/test/example.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
32T3K 765
|
||||
T55J5 684
|
||||
KK677 28
|
||||
KTJJT 220
|
||||
QQQJA 483
|
1000
advent-of-code-2023/elixir/day_07/test/input.txt
Normal file
1000
advent-of-code-2023/elixir/day_07/test/input.txt
Normal file
File diff suppressed because it is too large
Load diff
22
advent-of-code-2023/elixir/day_07/test/solution_test.exs
Normal file
22
advent-of-code-2023/elixir/day_07/test/solution_test.exs
Normal file
|
@ -0,0 +1,22 @@
|
|||
defmodule SolutionTest do
|
||||
use ExUnit.Case
|
||||
doctest Solution
|
||||
|
||||
test "example 1" do
|
||||
assert Solution.get_total_winnings("test/example.txt") == 6440
|
||||
end
|
||||
|
||||
test "part 1" do
|
||||
res = Solution.get_total_winnings("test/input.txt")
|
||||
IO.puts("First answer: #{res}")
|
||||
end
|
||||
|
||||
test "example 2" do
|
||||
assert Solution.get_total_winnings("test/example.txt", true) == 5905
|
||||
end
|
||||
|
||||
test "part 2" do
|
||||
res = Solution.get_total_winnings("test/input.txt", true)
|
||||
IO.puts("Second answer: #{res}")
|
||||
end
|
||||
end
|
1
advent-of-code-2023/elixir/day_07/test/test_helper.exs
Normal file
1
advent-of-code-2023/elixir/day_07/test/test_helper.exs
Normal file
|
@ -0,0 +1 @@
|
|||
ExUnit.start()
|
Loading…
Reference in a new issue