Skip to content

Commit 4511704

Browse files
committed
Solution for 2024, day 15
1 parent 87d85c3 commit 4511704

File tree

4 files changed

+235
-0
lines changed

4 files changed

+235
-0
lines changed

2024/15/Makefile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
input:
2+
http "https://adventofcode.com/2024/day/15/input" "Cookie:session=${AOC_SESSION};" >input
3+
4+
main1:
5+
go build -o main1 main1.go common.go
6+
7+
main2:
8+
go build -o main2 main2.go common.go
9+
10+
.PHONY: run1 run2 clean
11+
12+
run1: main1 input
13+
./main1 <input
14+
15+
run2: main2 input
16+
./main2 <input
17+
18+
clean:
19+
rm -f main1 main2 input

2024/15/common.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"errors"
6+
"os"
7+
)
8+
9+
var (
10+
Width, Height = 50, 50
11+
NotFound = errors.New("not found")
12+
)
13+
14+
func parseInput() (map[P]byte, []byte, P) {
15+
var robot P
16+
m := map[P]byte{}
17+
18+
scanner := bufio.NewScanner(os.Stdin)
19+
20+
for i := 0; scanner.Scan(); i++ {
21+
line := scanner.Text()
22+
23+
if line == "" {
24+
break
25+
}
26+
27+
for j, c := range line {
28+
if c == '@' {
29+
robot = P{i, j}
30+
m[P{i, j}] = '.'
31+
} else {
32+
m[P{i, j}] = byte(c)
33+
}
34+
}
35+
}
36+
37+
moves := []byte{}
38+
for scanner.Scan() {
39+
moves = append(moves, []byte(scanner.Text())...)
40+
}
41+
return m, moves, robot
42+
}
43+
44+
type P struct{ x, y int }
45+
46+
func getDelta(move byte) P {
47+
switch move {
48+
case '<':
49+
return P{0, -1}
50+
case '>':
51+
return P{0, 1}
52+
case '^':
53+
return P{-1, 0}
54+
case 'v':
55+
return P{1, 0}
56+
}
57+
panic("invalid move")
58+
}
59+
60+
func findNextEmpty(m map[P]byte, p, delta P) (P, error) {
61+
for {
62+
p = P{p.x + delta.x, p.y + delta.y}
63+
switch m[p] {
64+
case '.':
65+
return p, nil
66+
case '#':
67+
return P{}, NotFound
68+
}
69+
}
70+
}
71+
72+
func gps(m map[P]byte, char byte) int {
73+
sum := 0
74+
for i := 0; i < Height; i++ {
75+
for j := 0; j < Width; j++ {
76+
if m[P{i, j}] == char {
77+
sum += 100*i + j
78+
}
79+
}
80+
}
81+
return sum
82+
}

2024/15/main1.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package main
2+
3+
import "fmt"
4+
5+
func main() {
6+
m, moves, robot := parseInput()
7+
for _, move := range moves {
8+
robot = apply(m, move, robot)
9+
}
10+
fmt.Println(gps(m, 'O'))
11+
}
12+
13+
func apply(m map[P]byte, move byte, robot P) P {
14+
delta := getDelta(move)
15+
16+
nextEmpty, err := findNextEmpty(m, robot, delta)
17+
if err != nil {
18+
return robot
19+
}
20+
21+
closest := P{robot.x + delta.x, robot.y + delta.y}
22+
if m[closest] == 'O' {
23+
m[closest] = '.'
24+
m[nextEmpty] = 'O'
25+
}
26+
return closest
27+
}

2024/15/main2.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package main
2+
3+
import "fmt"
4+
5+
func main() {
6+
m, moves, robot := parseInput()
7+
m, robot = grow(m, robot)
8+
for _, move := range moves {
9+
robot = apply(m, move, robot)
10+
}
11+
fmt.Println(gps(m, '['))
12+
}
13+
14+
func grow(m map[P]byte, robot P) (map[P]byte, P) {
15+
newM := make(map[P]byte, len(m)*2)
16+
for i := 0; i < Height; i++ {
17+
for j := 0; j < Width; j++ {
18+
p := P{i, j}
19+
switch m[p] {
20+
case '#':
21+
newM[P{p.x, 2 * p.y}] = '#'
22+
newM[P{p.x, 2*p.y + 1}] = '#'
23+
case 'O':
24+
newM[P{p.x, 2 * p.y}] = '['
25+
newM[P{p.x, 2*p.y + 1}] = ']'
26+
case '.':
27+
newM[P{p.x, 2 * p.y}] = '.'
28+
newM[P{p.x, 2*p.y + 1}] = '.'
29+
case '@':
30+
newM[P{p.x, 2 * p.y}] = '@'
31+
newM[P{p.x, 2*p.y + 1}] = '.'
32+
}
33+
}
34+
}
35+
Width *= 2
36+
return newM, P{robot.x, 2 * robot.y}
37+
}
38+
39+
func apply(m map[P]byte, move byte, robot P) P {
40+
delta := getDelta(move)
41+
42+
nextEmpty, err := findNextEmpty(m, robot, delta)
43+
if err != nil {
44+
return robot
45+
}
46+
47+
if move == '<' || move == '>' {
48+
for curr := nextEmpty; curr != robot; {
49+
closest := P{curr.x, curr.y - delta.y}
50+
m[curr], m[closest] = m[closest], m[curr]
51+
curr = closest
52+
}
53+
return P{robot.x, robot.y + delta.y}
54+
}
55+
56+
if move == '^' || move == 'v' {
57+
affected, maxLevel, err := affectedVertically(m, robot, delta.x)
58+
if err != nil {
59+
return robot
60+
}
61+
for x := maxLevel; x != robot.x; x -= delta.x {
62+
for col := range affected[x] {
63+
m[P{x + delta.x, col}], m[P{x, col}] = m[P{x, col}], m[P{x + delta.x, col}]
64+
}
65+
}
66+
67+
return P{robot.x + delta.x, robot.y}
68+
}
69+
70+
panic("unreachable")
71+
}
72+
73+
func affectedVertically(m map[P]byte, robot P, deltaX int) (map[int]map[int]struct{}, int, error) {
74+
affected := map[int]map[int]struct{}{
75+
robot.x: {robot.y: {}},
76+
}
77+
78+
for currX := robot.x; ; currX += deltaX {
79+
newCols, err := newColumns(m, currX+deltaX, affected[currX])
80+
if err != nil {
81+
return nil, 0, err
82+
}
83+
84+
if len(newCols) == 0 {
85+
return affected, currX, nil
86+
}
87+
88+
affected[currX+deltaX] = newCols
89+
}
90+
}
91+
92+
func newColumns(m map[P]byte, nextX int, columns map[int]struct{}) (map[int]struct{}, error) {
93+
newCols := map[int]struct{}{}
94+
for col := range columns {
95+
switch m[P{nextX, col}] {
96+
case '#':
97+
return nil, NotFound
98+
case '[':
99+
newCols[col] = struct{}{}
100+
newCols[col+1] = struct{}{}
101+
case ']':
102+
newCols[col] = struct{}{}
103+
newCols[col-1] = struct{}{}
104+
}
105+
}
106+
return newCols, nil
107+
}

0 commit comments

Comments
 (0)