Skip to content

Commit 5a0f703

Browse files
committed
22nd day
1 parent aeb6d7b commit 5a0f703

File tree

3 files changed

+445
-0
lines changed

3 files changed

+445
-0
lines changed

day22/day22.go

+212
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"strings"
7+
8+
. "majcn.si/advent-of-code-2022/util"
9+
)
10+
11+
type Command struct {
12+
Move int
13+
ChangeDirection byte
14+
}
15+
16+
type Direction Location
17+
18+
type DataType struct {
19+
StartLocation Location
20+
StartDirection Direction
21+
Grid Set[Location]
22+
GridSize Location
23+
Wall Set[Location]
24+
Commands []Command
25+
}
26+
27+
func parseData(data string) DataType {
28+
dataSplit := strings.Split(data, "\n")
29+
30+
grid := make(Set[Location])
31+
wall := make(Set[Location])
32+
startLocation := Location{X: -1, Y: -1}
33+
for y, line := range dataSplit[:len(dataSplit)-2] {
34+
for x, v := range line {
35+
switch v {
36+
case '.':
37+
grid.Add(Location{X: x, Y: y})
38+
if startLocation.X < 0 {
39+
startLocation = Location{X: x, Y: y}
40+
}
41+
case '#':
42+
wall.Add(Location{X: x, Y: y})
43+
}
44+
}
45+
}
46+
47+
commandLine := dataSplit[len(dataSplit)-1]
48+
commandMatches := regexp.MustCompile(`(\d+)([LR])`).FindAllStringSubmatch(commandLine, -1)
49+
commands := make([]Command, len(commandMatches)*2+1)
50+
for i, match := range commandMatches {
51+
commands[i*2] = Command{Move: ParseInt(match[1])}
52+
commands[i*2+1] = Command{ChangeDirection: match[2][0]}
53+
}
54+
commands[len(commands)-1] = Command{
55+
Move: ParseInt(regexp.MustCompile(`\d+$`).FindString(commandLine)),
56+
}
57+
58+
return DataType{
59+
StartLocation: startLocation,
60+
StartDirection: directionRight,
61+
Grid: grid,
62+
GridSize: Location{X: len(dataSplit[0]), Y: len(dataSplit) - 2},
63+
Wall: wall,
64+
Commands: commands,
65+
}
66+
}
67+
68+
type NextDirectionMapKey struct {
69+
FromDirection Direction
70+
Instruction byte
71+
}
72+
73+
var directionLeft, directionRight, directionUp, directionDown Direction
74+
var nextDirectionMap map[NextDirectionMapKey]Direction
75+
76+
func init() {
77+
directionLeft = Direction{X: -1, Y: 0}
78+
directionRight = Direction{X: 1, Y: 0}
79+
directionUp = Direction{X: 0, Y: -1}
80+
directionDown = Direction{X: 0, Y: 1}
81+
82+
nextDirectionMap = map[NextDirectionMapKey]Direction{
83+
{FromDirection: directionLeft, Instruction: 'L'}: directionDown,
84+
{FromDirection: directionLeft, Instruction: 'R'}: directionUp,
85+
86+
{FromDirection: directionRight, Instruction: 'L'}: directionUp,
87+
{FromDirection: directionRight, Instruction: 'R'}: directionDown,
88+
89+
{FromDirection: directionUp, Instruction: 'L'}: directionLeft,
90+
{FromDirection: directionUp, Instruction: 'R'}: directionRight,
91+
92+
{FromDirection: directionDown, Instruction: 'L'}: directionRight,
93+
{FromDirection: directionDown, Instruction: 'R'}: directionLeft,
94+
}
95+
}
96+
97+
func calculateScore(location Location, direction Direction) int {
98+
var facingScore int
99+
switch direction {
100+
case directionRight:
101+
facingScore = 0
102+
case directionDown:
103+
facingScore = 1
104+
case directionLeft:
105+
facingScore = 2
106+
case directionUp:
107+
facingScore = 3
108+
}
109+
110+
return 4*(location.X+1) + 1000*(location.Y+1) + facingScore
111+
}
112+
113+
func solvePartX(data DataType, nextStateF func(location Location, direction Direction) (Location, Direction)) int {
114+
startLocation, startDirection, grid, commands := data.StartLocation, data.StartDirection, data.Grid, data.Commands
115+
116+
myLocation := startLocation
117+
myDirection := startDirection
118+
for _, c := range commands {
119+
if c.ChangeDirection == 'L' || c.ChangeDirection == 'R' {
120+
myDirection = nextDirectionMap[NextDirectionMapKey{FromDirection: myDirection, Instruction: c.ChangeDirection}]
121+
continue
122+
}
123+
124+
for i := 0; i < c.Move; i++ {
125+
newLocation, newDirection := nextStateF(myLocation, myDirection)
126+
if !grid.Contains(newLocation) {
127+
break
128+
}
129+
130+
myLocation, myDirection = newLocation, newDirection
131+
}
132+
}
133+
134+
return calculateScore(myLocation, myDirection)
135+
}
136+
137+
func nextStatePart1(grid Set[Location], gridSize Location, wall Set[Location], location Location, direction Direction) (Location, Direction) {
138+
result := Location{X: location.X + direction.X, Y: location.Y + direction.Y}
139+
140+
for !wall.Contains(result) && !grid.Contains(result) {
141+
result.X += direction.X
142+
result.Y += direction.Y
143+
144+
switch {
145+
case result.X >= gridSize.X:
146+
result.X = 0
147+
case result.X < 0:
148+
result.X = gridSize.X - 1
149+
case result.Y >= gridSize.Y:
150+
result.Y = 0
151+
case result.Y < 0:
152+
result.Y = gridSize.Y - 1
153+
}
154+
}
155+
156+
return result, direction
157+
}
158+
159+
func solvePart1(data DataType) (rc int) {
160+
grid, gridSize, wall := data.Grid, data.GridSize, data.Wall
161+
162+
return solvePartX(data, func(location Location, direction Direction) (Location, Direction) {
163+
return nextStatePart1(grid, gridSize, wall, location, direction)
164+
})
165+
}
166+
167+
func nextStatePart2(location Location, direction Direction) (Location, Direction) {
168+
switch {
169+
case location.X == 50 && 0 <= location.Y && location.Y <= 49 && direction == directionLeft:
170+
return Location{X: 0, Y: -location.Y + 149}, directionRight
171+
case location.Y == 0 && 50 <= location.X && location.X <= 99 && direction == directionUp:
172+
return Location{X: 0, Y: location.X + 100}, directionRight
173+
case location.Y == 0 && 100 <= location.X && location.X <= 149 && direction == directionUp:
174+
return Location{X: location.X - 100, Y: 199}, directionUp
175+
case location.X == 149 && 0 <= location.Y && location.Y <= 49 && direction == directionRight:
176+
return Location{X: 99, Y: 149 - location.Y}, directionLeft
177+
case location.Y == 49 && 100 <= location.X && location.X <= 149 && direction == directionDown:
178+
return Location{X: 99, Y: location.X - 50}, directionLeft
179+
case location.X == 99 && 50 <= location.Y && location.Y <= 99 && direction == directionRight:
180+
return Location{X: location.Y + 50, Y: 49}, directionUp
181+
case location.X == 50 && 50 <= location.Y && location.Y <= 99 && direction == directionLeft:
182+
return Location{X: location.Y - 50, Y: 100}, directionDown
183+
case location.X == 99 && 100 <= location.Y && location.Y <= 149 && direction == directionRight:
184+
return Location{X: 149, Y: -location.Y + 149}, directionLeft
185+
case location.Y == 149 && 50 <= location.X && location.X <= 99 && direction == directionDown:
186+
return Location{X: 49, Y: location.X + 100}, directionLeft
187+
case location.X == 49 && 150 <= location.Y && location.Y <= 199 && direction == directionRight:
188+
return Location{X: location.Y - 100, Y: 149}, directionUp
189+
case location.Y == 199 && 0 <= location.X && location.X <= 49 && direction == directionDown:
190+
return Location{X: location.X + 100, Y: 0}, directionDown
191+
case location.X == 0 && 150 <= location.Y && location.Y <= 199 && direction == directionLeft:
192+
return Location{X: location.Y - 100, Y: 0}, directionDown
193+
case location.X == 0 && 100 <= location.Y && location.Y <= 149 && direction == directionLeft:
194+
return Location{X: 50, Y: -location.Y + 149}, directionRight
195+
case location.Y == 100 && 0 <= location.X && location.X <= 49 && direction == directionUp:
196+
return Location{X: 50, Y: location.X + 50}, directionRight
197+
default:
198+
return Location{X: location.X + direction.X, Y: location.Y + direction.Y}, direction
199+
}
200+
}
201+
202+
func solvePart2(data DataType) (rc int) {
203+
return solvePartX(data, func(location Location, direction Direction) (Location, Direction) {
204+
return nextStatePart2(location, direction)
205+
})
206+
}
207+
208+
func main() {
209+
data := parseData(FetchInputData(22))
210+
fmt.Println(solvePart1(data))
211+
fmt.Println(solvePart2(data))
212+
}

day22/day22_test.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
. "majcn.si/advent-of-code-2022/util"
8+
)
9+
10+
func ExpectedPart1() int {
11+
return 135107
12+
}
13+
14+
func ExpectedPart2() int {
15+
return 27279
16+
}
17+
18+
var data DataType
19+
20+
func init() {
21+
input, _ := os.ReadFile("../examples/22.txt")
22+
data = parseData(string(input))
23+
}
24+
25+
func TestPart1(t *testing.T) {
26+
AssertTestPartX(t, ExpectedPart1(), solvePart1(data))
27+
}
28+
29+
func TestPart2(t *testing.T) {
30+
AssertTestPartX(t, ExpectedPart2(), solvePart2(data))
31+
}

0 commit comments

Comments
 (0)