2024 Day 17 Part 1 + not working Part 2
This commit is contained in:
parent
e8a87d4a9c
commit
0bc03b7157
1 changed files with 145 additions and 0 deletions
145
2024/day17.exs
Executable file
145
2024/day17.exs
Executable file
|
@ -0,0 +1,145 @@
|
|||
#!/usr/bin/env elixir
|
||||
defmodule Day17 do
|
||||
defmodule Computer do
|
||||
defstruct a: 0, b: 0, c: 0, output: [], program: []
|
||||
end
|
||||
|
||||
def part1(computer) do
|
||||
process(computer.program, computer)
|
||||
|> Map.fetch!(:output)
|
||||
|> Enum.reverse()
|
||||
|> Enum.join(",")
|
||||
end
|
||||
|
||||
def process([], computer), do: computer
|
||||
|
||||
# adv div(A, 2^ combo operand) -> A
|
||||
def process([0, operand | program], computer) do
|
||||
value = div(computer.a, 2 ** combo(operand, computer))
|
||||
computer = %Computer{computer | a: value}
|
||||
process(program, computer)
|
||||
end
|
||||
|
||||
# bxl B XOR literal operand -> B
|
||||
def process([1, operand | program], computer) do
|
||||
value = Bitwise.bxor(computer.b, operand)
|
||||
computer = %Computer{computer | b: value}
|
||||
process(program, computer)
|
||||
end
|
||||
|
||||
# bst combo operand mod 8 -> B (only 3 lowest bits)
|
||||
def process([2, operand | program], computer) do
|
||||
value = rem(combo(operand, computer), 8)
|
||||
computer = %Computer{computer | b: value}
|
||||
process(program, computer)
|
||||
end
|
||||
|
||||
# jnz noting if A is 0, otherwise set instruction to literal operand - don't inc by 2 if jump
|
||||
def process([3, operand | program], computer) do
|
||||
if computer.a == 0 do
|
||||
process(program, computer)
|
||||
else
|
||||
program = Enum.drop(computer.program, operand)
|
||||
process(program, computer)
|
||||
end
|
||||
end
|
||||
|
||||
# bxc B XOR C -> B (ignore operand)
|
||||
def process([4, _operand | program], computer) do
|
||||
value = Bitwise.bxor(computer.b, computer.c)
|
||||
computer = %Computer{computer | b: value}
|
||||
process(program, computer)
|
||||
end
|
||||
|
||||
# out combo operand mod 8 + output (CSV)
|
||||
def process([5, operand | program], computer) do
|
||||
value = rem(combo(operand, computer), 8)
|
||||
computer = %Computer{computer | output: [value | computer.output]}
|
||||
process(program, computer)
|
||||
end
|
||||
|
||||
# bdv same as adv but store in B
|
||||
def process([6, operand | program], computer) do
|
||||
value = div(computer.a, 2 ** combo(operand, computer))
|
||||
computer = %Computer{computer | b: value}
|
||||
process(program, computer)
|
||||
end
|
||||
|
||||
# cdv same as adv but store in C
|
||||
def process([7, operand | program], computer) do
|
||||
value = div(computer.a, 2 ** combo(operand, computer))
|
||||
computer = %Computer{computer | c: value}
|
||||
process(program, computer)
|
||||
end
|
||||
|
||||
def combo(literal, _computer) when literal in [0, 1, 2, 3], do: literal
|
||||
def combo(4, computer), do: computer.a
|
||||
def combo(5, computer), do: computer.b
|
||||
def combo(6, computer), do: computer.c
|
||||
|
||||
def part2(computer) do
|
||||
# TODO runs forever. Probably need to detect loops?
|
||||
search_for_self(computer)
|
||||
end
|
||||
|
||||
def search_for_self(computer, a \\ 0) do
|
||||
computer = %Computer{computer | a: a}
|
||||
output = process(computer.program, computer).output |> Enum.reverse()
|
||||
|
||||
if output == computer.program do
|
||||
a
|
||||
else
|
||||
search_for_self(computer, a + 1)
|
||||
end
|
||||
end
|
||||
|
||||
def input do
|
||||
with [input_filename] <- System.argv(),
|
||||
{:ok, input} <- File.read(input_filename) do
|
||||
"Register A: " <> input = input
|
||||
{a, input} = Integer.parse(input)
|
||||
"\nRegister B: " <> input = input
|
||||
{b, input} = Integer.parse(input)
|
||||
"\nRegister C: " <> input = input
|
||||
{c, input} = Integer.parse(input)
|
||||
"\n\nProgram: " <> input = input
|
||||
program = input |> String.split([",", "\n"], trim: true) |> Enum.map(&String.to_integer/1)
|
||||
|
||||
%Computer{a: a, b: b, c: c, program: program}
|
||||
else
|
||||
_ -> :error
|
||||
end
|
||||
end
|
||||
|
||||
#######################
|
||||
# HERE BE BOILERPLATE #
|
||||
#######################
|
||||
|
||||
def run do
|
||||
case input() do
|
||||
:error -> print_usage()
|
||||
input -> run_parts_with_timer(input)
|
||||
end
|
||||
end
|
||||
|
||||
defp run_parts_with_timer(input) do
|
||||
run_with_timer(1, fn -> part1(input) end)
|
||||
run_with_timer(2, fn -> part2(input) end)
|
||||
end
|
||||
|
||||
defp run_with_timer(part, fun) do
|
||||
{time, result} = :timer.tc(fun)
|
||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
||||
IO.puts("#{inspect(result)}\n")
|
||||
end
|
||||
|
||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
||||
|
||||
defp print_usage do
|
||||
IO.puts("Usage: elixir day17.exs input_filename")
|
||||
end
|
||||
end
|
||||
|
||||
Day17.run()
|
Loading…
Reference in a new issue