Finished day 5
This commit is contained in:
parent
a426ff5a7c
commit
c2fa707d2b
1 changed files with 235 additions and 0 deletions
235
day5.livemd
Normal file
235
day5.livemd
Normal file
|
@ -0,0 +1,235 @@
|
|||
# AOC 2022 - Day 5
|
||||
|
||||
```elixir
|
||||
Mix.install([
|
||||
{:req, "~> 0.3.3"},
|
||||
{:nimble_parsec, "~> 1.2.3"}
|
||||
])
|
||||
```
|
||||
|
||||
## Puzzle description
|
||||
|
||||
[Day 5: Supply Stacks](https://adventofcode.com/2022/day/5).
|
||||
|
||||
## Input
|
||||
|
||||
```elixir
|
||||
defmodule Load do
|
||||
def input do
|
||||
aoc_session = System.fetch_env!("LB_AOC_SESSION")
|
||||
input_url = "https://adventofcode.com/2022/day/5/input"
|
||||
Req.get!(input_url, headers: [cookie: "session=#{aoc_session}"]).body
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```elixir
|
||||
defmodule Parser do
|
||||
import NimbleParsec
|
||||
|
||||
crate =
|
||||
ignore(string("["))
|
||||
|> utf8_char([?A..?Z])
|
||||
|> ignore(string("]"))
|
||||
|
||||
empty_slot = string(" ") |> replace(:empty)
|
||||
|
||||
slot = choice([crate, empty_slot])
|
||||
|
||||
row =
|
||||
repeat_while(
|
||||
concat(
|
||||
slot,
|
||||
optional(ignore(string(" ")))
|
||||
),
|
||||
:not_newline
|
||||
)
|
||||
|> ignore(string("\n"))
|
||||
|> tag(:row)
|
||||
|
||||
rows =
|
||||
repeat_while(row, :not_index_row)
|
||||
|> tag(:rows)
|
||||
|
||||
indices =
|
||||
repeat_while(
|
||||
ignore(string(" "))
|
||||
|> integer(min: 1)
|
||||
|> ignore(string(" "))
|
||||
|> optional(ignore(string(" "))),
|
||||
:not_newline
|
||||
)
|
||||
|> ignore(string("\n"))
|
||||
|> tag(:indices)
|
||||
|
||||
instruction =
|
||||
ignore(string("move "))
|
||||
|> unwrap_and_tag(integer(min: 1), :amount)
|
||||
|> ignore(string(" from "))
|
||||
|> unwrap_and_tag(integer(min: 1), :from)
|
||||
|> ignore(string(" to "))
|
||||
|> unwrap_and_tag(integer(min: 1), :to)
|
||||
|> ignore(optional(string("\n")))
|
||||
|> tag(:instruction)
|
||||
|
||||
instructions = instruction |> repeat() |> tag(:instructions) |> eos()
|
||||
|
||||
defparsec(
|
||||
:crates,
|
||||
rows
|
||||
|> concat(indices)
|
||||
|> concat(ignore(string("\n")))
|
||||
|> concat(instructions)
|
||||
)
|
||||
|
||||
defp not_newline(<<?\n, _::binary>>, context, _, _), do: {:halt, context}
|
||||
defp not_newline(_, context, _, _), do: {:cont, context}
|
||||
|
||||
defp not_index_row(<<" 1", _::binary>>, context, _, _), do: {:halt, context}
|
||||
defp not_index_row(_, context, _, _), do: {:cont, context}
|
||||
end
|
||||
|
||||
# Parser.rows(~s([S] [C]
|
||||
# [P] [M] [Z]
|
||||
# 1 2 3)) |> IO.inspect()
|
||||
|
||||
# Parser.slot(" ") |> IO.inspect()
|
||||
# Parser.slot("brt") |> IO.inspect()
|
||||
```
|
||||
|
||||
```elixir
|
||||
|
||||
```
|
||||
|
||||
```elixir
|
||||
defmodule Part1 do
|
||||
def run(input) do
|
||||
{:ok, data, _, _, _, _} =
|
||||
input
|
||||
|> Parser.crates()
|
||||
|
||||
stacks =
|
||||
data
|
||||
|> Keyword.get(:rows)
|
||||
|> Keyword.values()
|
||||
|> List.zip()
|
||||
|> Enum.map(&Tuple.to_list/1)
|
||||
|> Enum.map(
|
||||
&Enum.reject(&1, fn
|
||||
:empty -> true
|
||||
_ -> false
|
||||
end)
|
||||
)
|
||||
|
||||
instructions =
|
||||
data
|
||||
|> Keyword.get(:instructions)
|
||||
|> Keyword.values()
|
||||
|
||||
instructions
|
||||
|> Enum.reduce(stacks, fn [amount: amount, from: from, to: to], acc ->
|
||||
from = from - 1
|
||||
to = to - 1
|
||||
|
||||
{transfer, from_stack} =
|
||||
Enum.at(acc, from)
|
||||
|> Enum.split(amount)
|
||||
|
||||
to_stack =
|
||||
transfer
|
||||
|> Enum.reverse()
|
||||
|> Enum.concat(Enum.at(acc, to))
|
||||
|
||||
List.replace_at(acc, from, from_stack)
|
||||
|> List.replace_at(to, to_stack)
|
||||
end)
|
||||
|> Enum.map(&Enum.take(&1, 1))
|
||||
|> Enum.join()
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Part2 do
|
||||
def run(input) do
|
||||
{:ok, data, _, _, _, _} =
|
||||
input
|
||||
|> Parser.crates()
|
||||
|
||||
stacks =
|
||||
data
|
||||
|> Keyword.get(:rows)
|
||||
|> Keyword.values()
|
||||
|> List.zip()
|
||||
|> Enum.map(&Tuple.to_list/1)
|
||||
|> Enum.map(
|
||||
&Enum.reject(&1, fn
|
||||
:empty -> true
|
||||
_ -> false
|
||||
end)
|
||||
)
|
||||
|
||||
instructions =
|
||||
data
|
||||
|> Keyword.get(:instructions)
|
||||
|> Keyword.values()
|
||||
|
||||
instructions
|
||||
|> Enum.reduce(stacks, fn [amount: amount, from: from, to: to], acc ->
|
||||
from = from - 1
|
||||
to = to - 1
|
||||
|
||||
{transfer, from_stack} =
|
||||
Enum.at(acc, from)
|
||||
|> Enum.split(amount)
|
||||
|
||||
to_stack =
|
||||
transfer
|
||||
|> Enum.concat(Enum.at(acc, to))
|
||||
|
||||
List.replace_at(acc, from, from_stack)
|
||||
|> List.replace_at(to, to_stack)
|
||||
end)
|
||||
|> Enum.map(&Enum.take(&1, 1))
|
||||
|> Enum.join()
|
||||
end
|
||||
end
|
||||
|
||||
ExUnit.start(autorun: false)
|
||||
|
||||
defmodule Test do
|
||||
use ExUnit.Case, async: true
|
||||
@example_input ~s( [D]
|
||||
[N] [C]
|
||||
[Z] [M] [P]
|
||||
1 2 3
|
||||
|
||||
move 1 from 2 to 1
|
||||
move 3 from 1 to 3
|
||||
move 2 from 2 to 1
|
||||
move 1 from 1 to 2)
|
||||
@input Load.input()
|
||||
|
||||
test "it loads the input" do
|
||||
assert String.length(@input) > 0
|
||||
end
|
||||
|
||||
test "part 1 example" do
|
||||
assert Part1.run(@example_input) === "CMZ"
|
||||
end
|
||||
|
||||
test "part 1" do
|
||||
assert Part1.run(@input) === "GFTNRBZPF"
|
||||
end
|
||||
|
||||
test "part 2 example" do
|
||||
assert Part2.run(@example_input) === "MCD"
|
||||
end
|
||||
|
||||
test "part 2" do
|
||||
assert Part2.run(@input) === "VRQWPDSGP"
|
||||
end
|
||||
end
|
||||
|
||||
ExUnit.run()
|
||||
```
|
Loading…
Reference in a new issue