From 4511704db801797f8cc63584bddb96395bb79909 Mon Sep 17 00:00:00 2001 From: Luciano Quercia Date: Sun, 15 Dec 2024 11:26:25 +0000 Subject: [PATCH] Solution for 2024, day 15 --- 2024/15/Makefile | 19 ++++++++ 2024/15/common.go | 82 +++++++++++++++++++++++++++++++++++ 2024/15/main1.go | 27 ++++++++++++ 2024/15/main2.go | 107 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 2024/15/Makefile create mode 100644 2024/15/common.go create mode 100644 2024/15/main1.go create mode 100644 2024/15/main2.go diff --git a/2024/15/Makefile b/2024/15/Makefile new file mode 100644 index 0000000..6a3b383 --- /dev/null +++ b/2024/15/Makefile @@ -0,0 +1,19 @@ +input: + http "https://adventofcode.com/2024/day/15/input" "Cookie:session=${AOC_SESSION};" >input + +main1: + go build -o main1 main1.go common.go + +main2: + go build -o main2 main2.go common.go + +.PHONY: run1 run2 clean + +run1: main1 input + ./main1 ': + return P{0, 1} + case '^': + return P{-1, 0} + case 'v': + return P{1, 0} + } + panic("invalid move") +} + +func findNextEmpty(m map[P]byte, p, delta P) (P, error) { + for { + p = P{p.x + delta.x, p.y + delta.y} + switch m[p] { + case '.': + return p, nil + case '#': + return P{}, NotFound + } + } +} + +func gps(m map[P]byte, char byte) int { + sum := 0 + for i := 0; i < Height; i++ { + for j := 0; j < Width; j++ { + if m[P{i, j}] == char { + sum += 100*i + j + } + } + } + return sum +} diff --git a/2024/15/main1.go b/2024/15/main1.go new file mode 100644 index 0000000..0f7bd9a --- /dev/null +++ b/2024/15/main1.go @@ -0,0 +1,27 @@ +package main + +import "fmt" + +func main() { + m, moves, robot := parseInput() + for _, move := range moves { + robot = apply(m, move, robot) + } + fmt.Println(gps(m, 'O')) +} + +func apply(m map[P]byte, move byte, robot P) P { + delta := getDelta(move) + + nextEmpty, err := findNextEmpty(m, robot, delta) + if err != nil { + return robot + } + + closest := P{robot.x + delta.x, robot.y + delta.y} + if m[closest] == 'O' { + m[closest] = '.' + m[nextEmpty] = 'O' + } + return closest +} diff --git a/2024/15/main2.go b/2024/15/main2.go new file mode 100644 index 0000000..05f1ef9 --- /dev/null +++ b/2024/15/main2.go @@ -0,0 +1,107 @@ +package main + +import "fmt" + +func main() { + m, moves, robot := parseInput() + m, robot = grow(m, robot) + for _, move := range moves { + robot = apply(m, move, robot) + } + fmt.Println(gps(m, '[')) +} + +func grow(m map[P]byte, robot P) (map[P]byte, P) { + newM := make(map[P]byte, len(m)*2) + for i := 0; i < Height; i++ { + for j := 0; j < Width; j++ { + p := P{i, j} + switch m[p] { + case '#': + newM[P{p.x, 2 * p.y}] = '#' + newM[P{p.x, 2*p.y + 1}] = '#' + case 'O': + newM[P{p.x, 2 * p.y}] = '[' + newM[P{p.x, 2*p.y + 1}] = ']' + case '.': + newM[P{p.x, 2 * p.y}] = '.' + newM[P{p.x, 2*p.y + 1}] = '.' + case '@': + newM[P{p.x, 2 * p.y}] = '@' + newM[P{p.x, 2*p.y + 1}] = '.' + } + } + } + Width *= 2 + return newM, P{robot.x, 2 * robot.y} +} + +func apply(m map[P]byte, move byte, robot P) P { + delta := getDelta(move) + + nextEmpty, err := findNextEmpty(m, robot, delta) + if err != nil { + return robot + } + + if move == '<' || move == '>' { + for curr := nextEmpty; curr != robot; { + closest := P{curr.x, curr.y - delta.y} + m[curr], m[closest] = m[closest], m[curr] + curr = closest + } + return P{robot.x, robot.y + delta.y} + } + + if move == '^' || move == 'v' { + affected, maxLevel, err := affectedVertically(m, robot, delta.x) + if err != nil { + return robot + } + for x := maxLevel; x != robot.x; x -= delta.x { + for col := range affected[x] { + m[P{x + delta.x, col}], m[P{x, col}] = m[P{x, col}], m[P{x + delta.x, col}] + } + } + + return P{robot.x + delta.x, robot.y} + } + + panic("unreachable") +} + +func affectedVertically(m map[P]byte, robot P, deltaX int) (map[int]map[int]struct{}, int, error) { + affected := map[int]map[int]struct{}{ + robot.x: {robot.y: {}}, + } + + for currX := robot.x; ; currX += deltaX { + newCols, err := newColumns(m, currX+deltaX, affected[currX]) + if err != nil { + return nil, 0, err + } + + if len(newCols) == 0 { + return affected, currX, nil + } + + affected[currX+deltaX] = newCols + } +} + +func newColumns(m map[P]byte, nextX int, columns map[int]struct{}) (map[int]struct{}, error) { + newCols := map[int]struct{}{} + for col := range columns { + switch m[P{nextX, col}] { + case '#': + return nil, NotFound + case '[': + newCols[col] = struct{}{} + newCols[col+1] = struct{}{} + case ']': + newCols[col] = struct{}{} + newCols[col-1] = struct{}{} + } + } + return newCols, nil +}