Skip to content

Commit 05cbe57

Browse files
committed
Day 17
1 parent 2bb3ce4 commit 05cbe57

File tree

4 files changed

+211
-2
lines changed

4 files changed

+211
-2
lines changed

src/aoc/runner/year2024.gleam

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import aoc/year2024/day13
1414
import aoc/year2024/day14
1515
import aoc/year2024/day15
1616
import aoc/year2024/day16
17+
import aoc/year2024/day17
1718
import gleam/string
1819

1920
pub fn run(input: String, day: Int, part: Int) {
@@ -50,6 +51,8 @@ pub fn run(input: String, day: Int, part: Int) {
5051
15, 2 -> input |> day15.part2 |> string.inspect
5152
16, 1 -> input |> day16.part1 |> string.inspect
5253
16, 2 -> input |> day16.part2 |> string.inspect
54+
17, 1 -> input |> day17.part1
55+
17, 2 -> input |> day17.part2 |> string.inspect
5356
_, _ ->
5457
"Unknown day and part for 2024: day "
5558
<> string.inspect(day)

src/aoc/year2024/day16.gleam

+1-2
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,7 @@ fn neighbors(
8282
]
8383
}
8484

85-
candidates
86-
|> list.filter(fn(node) {
85+
list.filter(candidates, fn(node) {
8786
!set.contains(seen, #(node.1, node.2))
8887
&& case dict.get(grid, node.1) {
8988
Ok(v) -> v != "#"

src/aoc/year2024/day17.gleam

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import aoc/util/parser.{type Parser}
2+
import gleam/dict.{type Dict}
3+
import gleam/float
4+
import gleam/int
5+
import gleam/list
6+
import gleam/result
7+
import gleam/string
8+
import gleam/yielder.{type Yielder, Next}
9+
import party
10+
11+
type Vm {
12+
Vm(a: Int, b: Int, c: Int, pos: Int, prog: Dict(Int, Int))
13+
}
14+
15+
fn reg_p(name: String) -> Parser(Int) {
16+
use <- party.drop(party.string("Register " <> name <> ": "))
17+
use v <- party.do(parser.int())
18+
use <- party.drop(party.string("\n"))
19+
party.return(v)
20+
}
21+
22+
fn vm_p() -> Parser(Vm) {
23+
use a <- party.do(reg_p("A"))
24+
use b <- party.do(reg_p("B"))
25+
use c <- party.do(reg_p("C"))
26+
use <- party.drop(party.string("\nProgram: "))
27+
use ops <- party.map(party.sep1(parser.int(), party.string(",")))
28+
let prog = ops |> list.index_map(fn(o, i) { #(i, o) }) |> dict.from_list
29+
Vm(a:, b:, c:, pos: 0, prog:)
30+
}
31+
32+
fn combo(vm: Vm, i: Int) -> Int {
33+
case i {
34+
4 -> vm.a
35+
5 -> vm.b
36+
6 -> vm.c
37+
i -> i
38+
}
39+
}
40+
41+
fn pow(a: Int, b: Int) -> Int {
42+
a
43+
|> int.power(int.to_float(b))
44+
|> result.unwrap(0.0)
45+
|> float.floor
46+
|> float.round
47+
}
48+
49+
fn opcode(vm: Vm) -> Result(Int, Nil) {
50+
dict.get(vm.prog, vm.pos)
51+
}
52+
53+
fn operand(vm: Vm) -> Result(Int, Nil) {
54+
dict.get(vm.prog, vm.pos + 1)
55+
}
56+
57+
fn run(vm: Vm, output: List(Int)) -> List(Int) {
58+
let res = {
59+
use op <- result.try(opcode(vm))
60+
case op {
61+
0 -> {
62+
use arg <- result.map(operand(vm))
63+
let res = vm.a / pow(2, combo(vm, arg))
64+
#(Vm(..vm, a: res, pos: vm.pos + 2), output)
65+
}
66+
1 -> {
67+
use arg <- result.map(operand(vm))
68+
let res = int.bitwise_exclusive_or(vm.b, arg)
69+
#(Vm(..vm, b: res, pos: vm.pos + 2), output)
70+
}
71+
2 -> {
72+
use arg <- result.map(operand(vm))
73+
let res = arg |> combo(vm, _) |> int.modulo(8) |> result.unwrap(0)
74+
#(Vm(..vm, b: res, pos: vm.pos + 2), output)
75+
}
76+
3 -> {
77+
case vm.a == 0 {
78+
True -> Ok(#(Vm(..vm, pos: vm.pos + 2), output))
79+
False -> {
80+
use arg <- result.map(operand(vm))
81+
#(Vm(..vm, pos: arg), output)
82+
}
83+
}
84+
}
85+
4 -> {
86+
let res = int.bitwise_exclusive_or(vm.b, vm.c)
87+
Ok(#(Vm(..vm, b: res, pos: vm.pos + 2), output))
88+
}
89+
5 -> {
90+
use arg <- result.map(operand(vm))
91+
let res = arg |> combo(vm, _) |> int.modulo(8) |> result.unwrap(0)
92+
#(Vm(..vm, pos: vm.pos + 2), [res, ..output])
93+
}
94+
6 -> {
95+
use arg <- result.map(operand(vm))
96+
let res = vm.a / pow(2, combo(vm, arg))
97+
#(Vm(..vm, b: res, pos: vm.pos + 2), output)
98+
}
99+
_ -> {
100+
use arg <- result.map(operand(vm))
101+
let res = vm.a / pow(2, combo(vm, arg))
102+
#(Vm(..vm, c: res, pos: vm.pos + 2), output)
103+
}
104+
}
105+
}
106+
107+
case res {
108+
Error(_) -> list.reverse(output)
109+
Ok(#(vm, output)) -> run(vm, output)
110+
}
111+
}
112+
113+
fn ints(start: Int) -> Yielder(Int) {
114+
yielder.unfold(start, fn(n) { Next(n, n + 1) })
115+
}
116+
117+
fn search(vm: Vm, n: Int, d: Int, goal: List(Int)) -> Int {
118+
case d > list.length(goal) {
119+
True -> n / 8
120+
False -> {
121+
let cur_goal = list.take(goal, d)
122+
let assert Ok(next) =
123+
n
124+
|> ints
125+
|> yielder.find(fn(i) {
126+
let actual =
127+
run(Vm(..vm, a: i), [])
128+
|> list.reverse
129+
|> list.take(d)
130+
131+
actual == cur_goal
132+
})
133+
134+
search(vm, next * 8, d + 1, goal)
135+
}
136+
}
137+
}
138+
139+
pub fn part1(input: String) -> String {
140+
input
141+
|> parser.go(vm_p())
142+
|> run([])
143+
|> list.map(int.to_string)
144+
|> string.join(",")
145+
}
146+
147+
pub fn part2(input: String) -> Int {
148+
let vm = parser.go(input, vm_p())
149+
150+
let goal =
151+
vm.prog
152+
|> dict.to_list
153+
|> list.sort(fn(a, b) { int.compare(b.0, a.0) })
154+
|> list.map(fn(p) { p.1 })
155+
156+
search(vm, 0, 1, goal)
157+
}

test/aoc/year2024/day17_test.gleam

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import aoc/year2024/day17
2+
import glacier/should
3+
4+
const input = "Register A: 729
5+
Register B: 0
6+
Register C: 0
7+
8+
Program: 0,1,5,4,3,0
9+
"
10+
11+
const input1 = "Register A: 10
12+
Register B: 0
13+
Register C: 0
14+
15+
Program: 5,0,5,1,5,4
16+
"
17+
18+
const input2 = "Register A: 2024
19+
Register B: 0
20+
Register C: 0
21+
22+
Program: 0,1,5,4,3,0
23+
"
24+
25+
const input3 = "Register A: 2024
26+
Register B: 0
27+
Register C: 0
28+
29+
Program: 0,3,5,4,3,0
30+
"
31+
32+
pub fn part1_test() {
33+
input
34+
|> day17.part1
35+
|> should.equal("4,6,3,5,6,3,5,2,1,0")
36+
37+
input1
38+
|> day17.part1
39+
|> should.equal("0,1,2")
40+
41+
input2
42+
|> day17.part1
43+
|> should.equal("4,2,5,6,7,7,7,7,3,1,0")
44+
}
45+
46+
pub fn part2_test() {
47+
input3
48+
|> day17.part2
49+
|> should.equal(117_440)
50+
}

0 commit comments

Comments
 (0)