add 2024 day 17

This commit is contained in:
Caleb Webber 2024-12-19 11:09:32 -05:00
parent 1e77d59ed1
commit 9b9b00ed61
2 changed files with 107 additions and 91 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.DS_Store

View file

@ -46,6 +46,32 @@ If register B contains 29, the program 1,7 would set register B to 26.
If register B contains 2024 and register C contains 43690, the program 4,0 would set register B to 44354.
```
```elixir
e1 = """
Register A: 729
Register B: 0
Register C: 0
Program: 0,1,5,4,3,0
"""
s1 = "4,6,3,5,6,3,5,2,1,0"
e2 = """
Register A: 2024
Register B: 0
Register C: 0
Program: 0,3,5,4,3,0
"""
input = """
Register A: 17323786
Register B: 0
Register C: 0
Program: 2,4,1,1,7,5,1,5,4,1,5,5,0,3,3,0
"""
```
```elixir
defmodule ChronospatialComputer do
defstruct a: nil, b: nil, c: nil, program: nil, pc: nil, output: <<>>
@ -55,7 +81,12 @@ defmodule ChronospatialComputer do
[a,b,c] = for register <- registers |> String.split("\n") do
register |> String.split(": ") |> Enum.at(1) |> String.to_integer
end
pc = program |> String.split(": ") |> Enum.at(1) |> String.split(",", trim: true) |> Enum.map(&(String.to_integer/1)) |> :binary.list_to_bin()
pc = program
|> String.split(": ")
|> Enum.at(1)
|> String.split(",", trim: true)
|> Enum.map(&(String.to_integer/1))
|> :binary.list_to_bin()
init(a,b,c,pc)
end
@ -151,106 +182,90 @@ defmodule ChronospatialComputer do
7 -> throw "received combo op 7 at #{computer.pc + 1}"
end
end
# Runs my AOC input written as Elixir with `a` in register A.
# I could use the code that solves p1, but this runs 10x faster.
def run_with_register_a(a), do: run_with_register_a(a, <<>>)
def run_with_register_a(0, o), do: o
def run_with_register_a(a, o) do
import Bitwise
# 0 to 7
# 0 <= len(b) <= 3
b = (a &&& 7)|> Bitwise.bxor(1)
# a - 7 <= len(c) <= a
c = a >>> b
# 0 <= len(b) <= 3
b = b |> Bitwise.bxor(5) |> Bitwise.bxor(c)
o = o <> <<b &&& 7>>
run_with_register_a(a >>> 3, o)
end
# finds valid values of the `A` register that will cause `run_with_register_a` to return
# an output equal to the program
def quine(program) do
quine(program, 0, 10, 0)
|> Enum.sort()
end
def quine(<<>>, bits, _, _), do: [bits]
# bits is the integer so far
# len is the number of bits to generate
# z is the number of bits in `bits` expressed as 7 + 3*z
def quine(<< first, rest::binary >>, bits, len, z) do
import Bitwise
for i <- 0..((2**len)-1), # first iter - generate 10-bit sequences, do 3-bits on all subsequent loops
# noop on first iter - after, shifts over 7 bits
i = i <<< (10 - len),
# `bits` is always 7+3z bits long, so this will return 7 bits from `bits` and
# adds the three bits from `i` at the beginning
# so j is << i_0, i_1, i_2, b_0, b_1, b_2, b_3, b_4, b_5, b_6 >>
j = (bits >>> 3*z) + i,
# run the program with `j` as input
o = ChronospatialComputer.run_with_register_a(j),
o |> byte_size() > 0,
# take only the sequences that generate the next output we're looking for
# if we're on the last iteration we want sequences that don't generate
# more outputs than we need.
# if we're on any other iteration, we take those outputs because they
# will be combined with more bits on the next loop.
(rest != <<>> and o |> :binary.at(0) == first) or o == << first >> do
# takes the `len` (either 3 or 10 - if it's the first loop) new bits from
# `i` and prepends them to `bits`
(i <<< 3*z) + bits
end |> Enum.flat_map(
fn b ->
# generate the next three bits in the sequence
# we do 3 at a time because the program always shifts a 3 bits after writing
# to the output.
quine(rest, b, 3, z+1)
end
)
end
end
```
```elixir
<<12>> <> <<12>>
Digging deeper in the device's manual, you discover the problem: this program is supposed to output another copy of the program! Unfortunately, the value in register A seems to have been corrupted. You'll need to find a new value to which you can initialize register A so that the program's output instructions produce an exact copy of the program itself.
For example:
```
```elixir
e1 = """
Register A: 729
Register B: 0
Register C: 0
Program: 0,1,5,4,3,0
"""
s1 = "4,6,3,5,6,3,5,2,1,0"
e2 = """
Register A: 2024
Register B: 0
Register C: 0
Program: 0,3,5,4,3,0
"""
input = """
"""
```
This program outputs a copy of itself if register A is instead initialized to 117440. (The original initial value of register A, 2024, is ignored.)
What is the lowest positive initial value for register A that causes the program to output a copy of itself?
```elixir
[p2answer, _] = ChronospatialComputer.quine(<<2,4,1,1,7,5,1,5,4,1,5,5,0,3,3,0>>)
```
```elixir
comp = input |> ChronospatialComputer.parse()
```
```elixir
Stream.iterate(0, &(&1 + 1))
|> Enum.reduce_while(
nil,
fn i, _ ->
case (%ChronospatialComputer{comp | a: i}
|> ChronospatialComputer.run_while(fn c ->
o = c.output
match?(^o <> _, c.program)
end)) do
{:halt, %ChronospatialComputer{program: p, output: p}} -> {:halt, i}
_ -> {:cont, nil}
end
end
)
```
```elixir
"""
Register A: 0
Register B: 0
Register C: 9
Program: 2,6
"""
|> ChronospatialComputer.parse() |> ChronospatialComputer.run()
```
```elixir
"""
Register A: 10
Register B: 0
Register C: 0
Program: 5,0,5,1,5,4
"""
|> ChronospatialComputer.parse() |> ChronospatialComputer.run()
```
```elixir
"""
Register A: 2024
Register B: 0
Register C: 0
Program: 0,1,5,4,3,0
"""
|> ChronospatialComputer.parse() |> ChronospatialComputer.run()
```
```elixir
"""
Register A: 0
Register B: 29
Register C: 0
Program: 1,7
"""
|> ChronospatialComputer.parse() |> ChronospatialComputer.run()
```
```elixir
"""
Register A: 0
Register B: 2024
Register C: 43690
Program: 4,0
"""
|> ChronospatialComputer.parse() |> ChronospatialComputer.run()
ChronospatialComputer.run_with_register_a(p2answer)
```