Compare commits

...

5 Commits

Author SHA1 Message Date
953716b95a
day 18 puzzle 2 2024-12-18 21:07:30 +01:00
43c6d9e325
day 18 puzzle 1 2024-12-18 20:42:18 +01:00
927feb071e
started day 17 puzzle 2 2024-12-18 14:32:38 +01:00
5107cf7f82
started day 16 puzzle 1 2024-12-18 14:31:57 +01:00
9a7eef5186
day 17 puzzle 1 2024-12-18 08:12:37 +01:00
14 changed files with 595 additions and 0 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 129 KiB

View File

@ -28,3 +28,9 @@
stars: 2
15:
stars: 2
16:
stars: 0
17:
stars: 1
18:
stars: 2

15
res/examples/day16_1.txt Normal file
View File

@ -0,0 +1,15 @@
###############
#.......#....E#
#.#.###.#.###.#
#.....#.#...#.#
#.###.#####.#.#
#.#.#.......#.#
#.#.#####.###.#
#...........#.#
###.#.#####.#.#
#...#.....#.#.#
#.#.#.###.#.#.#
#.....#...#.#.#
#.###.#.#.#.#.#
#S..#.....#...#
###############

17
res/examples/day16_2.txt Normal file
View File

@ -0,0 +1,17 @@
#################
#...#...#...#..E#
#.#.#.#.#.#.#.#.#
#.#.#.#...#...#.#
#.#.#.#.###.#.#.#
#...#.#.#.....#.#
#.#.#.#.#.#####.#
#.#...#.#.#.....#
#.#.#####.#.###.#
#.#.#.......#...#
#.#.###.#####.###
#.#.#...#.....#.#
#.#.#.#####.###.#
#.#.#.........#.#
#.#.#.#########.#
#S#.............#
#################

5
res/examples/day17_1.txt Normal file
View File

@ -0,0 +1,5 @@
Register A: 729
Register B: 0
Register C: 0
Program: 0,1,5,4,3,0

5
res/examples/day17_2.txt Normal file
View File

@ -0,0 +1,5 @@
Register A: 2024
Register B: 0
Register C: 0
Program: 0,3,5,4,3,0

25
res/examples/day18.txt Normal file
View File

@ -0,0 +1,25 @@
5,4
4,2
4,5
3,0
2,1
6,3
2,4
1,5
0,6
3,3
2,6
5,1
1,2
5,5
2,5
6,5
1,4
0,4
6,4
1,1
6,1
1,0
0,5
1,6
2,0

67
src/day16/puzzle1.typ Normal file
View File

@ -0,0 +1,67 @@
#import "/src/utils.typ": *
#let START = "S"
#let END = "E"
#let WALL = "#"
#let EMPTY = "."
#let offsets = (
(1, 0),
(0, 1),
(-1, 0),
(0, -1)
)
#let solve(input) = {
let grid = input.split("\n").map(l => l.clusters())
let w = grid.first().len()
let h = grid.len()
let (sx, sy) = (0, 0)
let (ex, ey) = (0, 0)
for y in range(h) {
for x in range(w) {
let c = grid.at(y).at(x)
if c == START {
(sx, sy) = (x, y)
} else if c == END {
(ex, ey) = (x, y)
}
}
}
let choices = ((sx, sy, 0, 0),)
let (x, y, dir, score) = (0, 0, 0, 0)
while choices.len() != 0 {
let min-score = calc.min(..choices.map(c => c.last()))
let i = choices.position(c => c.last() == min-score)
(x, y, dir, score) = choices.remove(i)
for (d, (dx, dy)) in offsets.enumerate() {
// Ignore backflips
if calc.abs(d - dir) == 2 {
continue
}
let (x2, y2) = (x + dx, y + dy)
let c = grid.at(y2).at(x2)
if c == WALL {
continue
}
let score2 = score + 1 + if d != dir {1000} else {0}
if c == END {
return score2
}
choices.push((x2, y2, d, score2))
}
break
}
return 0
}
#show-puzzle(
16, 1,
solve,
example: (
"1": 7036,
"2": 11048
)
)

0
src/day16/puzzle2.typ Normal file
View File

87
src/day17/puzzle1.typ Normal file
View File

@ -0,0 +1,87 @@
#import "/src/utils.typ": *
#let ADV = 0
#let BXL = 1
#let BST = 2
#let JNZ = 3
#let BXC = 4
#let OUT = 5
#let BDV = 6
#let CDV = 7
#let get-combo(regs, value) = {
if value >= 7 {
panic()
}
if value <= 3 {
return value
}
return regs.at(value - 4)
}
#let solve(input) = {
let (registers, program) = input.split("\n\n")
let regs = ()
for line in registers.split("\n") {
regs.push(
int(
line.split(": ")
.last()
)
)
}
program = program.split(": ")
.last()
.split(",")
.map(int)
let out = ()
let pc = 0
while pc < program.len() {
let op = program.at(pc)
let val = program.at(pc + 1)
if op == ADV {
let num = regs.at(0)
let den = get-combo(regs, val)
let res = num.bit-rshift(den)
regs.at(0) = res
} else if op == BXL {
regs.at(1) = regs.at(1).bit-xor(val)
} else if op == BST {
regs.at(1) = get-combo(regs, val).bit-and(0b111)
} else if op == JNZ {
if regs.at(0) != 0 {
pc = val
continue
}
} else if op == BXC {
regs.at(1) = regs.at(1).bit-xor(regs.at(2))
} else if op == OUT {
out.push(get-combo(regs, val).bit-and(0b111))
} else if op == BDV {
let num = regs.at(0)
let den = get-combo(regs, val)
let res = num.bit-rshift(den)
regs.at(1) = res
} else if op == CDV {
let num = regs.at(0)
let den = get-combo(regs, val)
let res = num.bit-rshift(den)
regs.at(2) = res
} else {
panic("Unknown instruction " + str(op))
}
pc += 2
}
return out.map(str).join(",")
}
#show-puzzle(
17, 1,
solve,
example: (
"1": "4,6,3,5,6,3,5,2,1,0"
)
)

108
src/day17/puzzle2.typ Normal file
View File

@ -0,0 +1,108 @@
#import "/src/utils.typ": *
#let ADV = 0
#let BXL = 1
#let BST = 2
#let JNZ = 3
#let BXC = 4
#let OUT = 5
#let BDV = 6
#let CDV = 7
#let ops = ("ADV", "BXL", "BST", "JNZ", "BXC", "OUT", "BDV", "CDV")
#let get-combo(regs, value) = {
if value >= 7 {
panic()
}
if value <= 3 {
return value
}
return regs.at(value - 4)
}
#let describe-combo(value) = {
if value == 7 {
return "<invalid>"
}
if value <= 3 {
return str(value)
}
return "ABC".at(value - 4)
}
#let describe(program) = {
let res = ""
res += program.map(str).join(",")
for i in range(0, program.len(), step: 2) {
res += "\n"
let op = program.at(i)
let val = program.at(i + 1)
let combo = describe-combo(val)
res += ops.at(op) + ": "
if op == ADV {
res += "A >>= " + combo
} else if op == BXL {
res += "B ^= " + str(val)
} else if op == BST {
res += "B = " + combo + " & 0b111"
} else if op == JNZ {
res += "IF A != 0 {PC = " + str(val) + "}"
} else if op == BXC {
res += "B ^= C"
} else if op == OUT {
res += "OUT(" + combo + " & 0b111)"
} else if op == BDV {
res += "B = A >> " + combo
} else if op == CDV {
res += "C = A >> " + combo
}
}
return res
}
#let solve(input) = {
let (registers, program) = input.split("\n\n")
let regs = ()
for line in registers.split("\n") {
regs.push(
int(
line.split(": ")
.last()
)
)
}
program = program.split(": ")
.last()
.split(",")
.map(int)
let out = ()
let pc = 0
regs.first() = 0
for n in program {
let b = n
}
return raw(block: true, describe(program))
}
Example:
#solve(read(get-example-path(17, suffix: "2")))
Input:
#solve(get-input(17))
/*
#show-puzzle(
17, 2,
solve,
example: (
"2": 117440
)
)*/

143
src/day18/puzzle1.typ Normal file
View File

@ -0,0 +1,143 @@
#import "/src/utils.typ": *
#let offsets = (
(1, 0),
(0, 1),
(-1, 0),
(0, -1)
)
#let get-lowest(nodes, scores) = {
let lowest-score = none
let lowest-i = none
for (i, (x, y)) in nodes.enumerate() {
let score = scores.at(y).at(x)
if lowest-i == none or score < lowest-score {
lowest-score = score
lowest-i = i
}
}
if lowest-i == none {
panic()
}
return lowest-i
}
#let make-path(parents, end) = {
let path = (end,)
let (x, y) = end
let pos = parents.at(y).at(x)
while pos != none {
(x, y) = pos
path.insert(0, pos)
pos = parents.at(y).at(x)
}
return path
}
#let find-path(w, h, grid) = {
let end = (w - 1, h - 1)
let (x, y) = (0, 0)
let open = ((x, y),)
let closed = ()
let g-scores = ((0,) * w,) * h
let f-scores = ((calc.inf,) * w,) * h
let parents = ((none,)*w,)*h
while open.len() != 0 {
let cur = open.remove(get-lowest(open, f-scores))
if cur == end {
return make-path(parents, cur)
}
let g-score = g-scores.at(y).at(x)
let f-score = f-scores.at(y).at(x)
let (x, y) = cur
for (dx, dy) in offsets {
let (x2, y2) = (x + dx, y + dy)
if x2 < 0 or x2 >= w or y2 < 0 or y2 >= h {
continue
}
if grid.at(y2).at(x2) {
continue
}
if (x2, y2) in closed {
continue
}
let g = x2 + y2
let h = calc.abs(end.first() - x2) + calc.abs(end.last() - y2)
let f = g + h
if f < f-scores.at(y2).at(x2) {
g-scores.at(y2).at(x2) = g
f-scores.at(y2).at(x2) = f
parents.at(y2).at(x2) = (x, y)
open.push((x2, y2))
}
}
closed.push(cur)
}
panic("No path was found")
}
#let solve(input, w: 0, h: 0, n-bytes: 0) = {
assert(w != 0, message: "Width cannot be 0")
assert(h != 0, message: "Height cannot be 0")
let grid = ((false,) * w,) * h
let obstacles = input.split("\n")
for obs in obstacles.slice(0, n-bytes) {
let (x, y) = obs.split(",").map(int)
grid.at(y).at(x) = true
}
let path = find-path(w, h, grid)
return path.len() - 1
}
#let visualize(input, w: 0, h: 0, n-bytes: 0, s: 2em) = {
let grid_ = ((false,) * w,) * h
let obstacles = input.split("\n")
for obs in obstacles.slice(0, n-bytes) {
let (x, y) = obs.split(",").map(int)
grid_.at(y).at(x) = true
}
let path = find-path(w, h, grid_)
for (x, y) in path {
grid_.at(y).at(x) = grid.cell(fill: green.lighten(60%))[O]
}
let cells = grid_.flatten().map(c => {
if c == false []
else if c == true {grid.cell(fill: red.lighten(60%))[\#]}
else {c}
})
grid(
columns: (s,) * w,
rows: (s,) * h,
align: center + horizon,
stroke: black,
..cells
)
}
#show-puzzle(
18, 1,
solve.with(w: 71, h: 71, n-bytes: 1024),
example: (
(result: 22, args: (w: 7, h: 7, n-bytes: 12)),
),
visualize: visualize.with(w: 7, h: 7, n-bytes: 12)
)
/*
#pagebreak()
#set page(width: auto, height: auto)
#visualize(get-input(18), w: 71, h: 71, n-bytes: 1024, s: 1em)
*/

117
src/day18/puzzle2.typ Normal file
View File

@ -0,0 +1,117 @@
#import "/src/utils.typ": *
#let offsets = (
(1, 0),
(0, 1),
(-1, 0),
(0, -1)
)
#let get-lowest(nodes, scores) = {
let lowest-score = none
let lowest-i = none
for (i, (x, y)) in nodes.enumerate() {
let score = scores.at(y).at(x)
if lowest-i == none or score < lowest-score {
lowest-score = score
lowest-i = i
}
}
if lowest-i == none {
panic()
}
return lowest-i
}
#let make-path(parents, end) = {
let path = (end,)
let (x, y) = end
let pos = parents.at(y).at(x)
while pos != none {
(x, y) = pos
path.insert(0, pos)
pos = parents.at(y).at(x)
}
return path
}
#let find-path(w, h, grid, start: (0, 0), end: auto) = {
let end = if end == auto {(w - 1, h - 1)} else {end}
let (x, y) = start
let open = ((x, y),)
let closed = ()
let g-scores = ((0,) * w,) * h
let f-scores = ((calc.inf,) * w,) * h
let parents = ((none,)*w,)*h
while open.len() != 0 {
let cur = open.remove(get-lowest(open, f-scores))
if cur == end {
return true
}
let g-score = g-scores.at(y).at(x)
let f-score = f-scores.at(y).at(x)
let (x, y) = cur
for (dx, dy) in offsets {
let (x2, y2) = (x + dx, y + dy)
if x2 < 0 or x2 >= w or y2 < 0 or y2 >= h {
continue
}
if grid.at(y2).at(x2) {
continue
}
if (x2, y2) in closed {
continue
}
let g = calc.abs(x2 - start.first()) + calc.abs(y2 - start.last())
let h = calc.abs(end.first() - x2) + calc.abs(end.last() - y2)
let f = g + h
if f < f-scores.at(y2).at(x2) {
g-scores.at(y2).at(x2) = g
f-scores.at(y2).at(x2) = f
parents.at(y2).at(x2) = (x, y)
open.push((x2, y2))
}
}
closed.push(cur)
}
return false
}
#let solve(input, w: 0, h: 0, n-bytes: 0) = {
assert(w != 0, message: "Width cannot be 0")
assert(h != 0, message: "Height cannot be 0")
let grid = ((false,) * w,) * h
let obstacles = input.split("\n")
let a = 0
let b = obstacles.len()
while b - a > 1 {
let m = calc.div-euclid(a + b, 2)
let grid2 = grid
for obs in obstacles.slice(0, m) {
let (x, y) = obs.split(",").map(int)
grid2.at(y).at(x) = true
}
if find-path(w, h, grid2) {
a = m
} else {
b = m
}
}
return obstacles.at(a)
}
#show-puzzle(
18, 2,
solve.with(w: 71, h: 71),
example: (
(result: "6,1", args: (w: 7, h: 7)),
)
)

Binary file not shown.