272 lines
5.7 KiB
Go
272 lines
5.7 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
func main() {
|
|
{
|
|
exampleFloor := readFloor("example_map.txt")
|
|
exampleMoves, _ := os.ReadFile("example_moves.txt")
|
|
fmt.Println(part1(exampleFloor, exampleMoves), 10092)
|
|
}
|
|
|
|
{
|
|
dataFloor := readFloor("data_map.txt")
|
|
dataMoves, _ := os.ReadFile("data_moves.txt")
|
|
fmt.Println(part1(dataFloor, dataMoves), 1492518)
|
|
}
|
|
|
|
// {
|
|
// var simpleFloor1 [][]byte
|
|
// simpleFloor1 = append(simpleFloor1, []byte("##########"))
|
|
// simpleFloor1 = append(simpleFloor1, []byte("#........#"))
|
|
// simpleFloor1 = append(simpleFloor1, []byte("#........#"))
|
|
// simpleFloor1 = append(simpleFloor1, []byte("#.....O..#"))
|
|
// simpleFloor1 = append(simpleFloor1, []byte("#.....O..#"))
|
|
// simpleFloor1 = append(simpleFloor1, []byte("#.....O..#"))
|
|
// simpleFloor1 = append(simpleFloor1, []byte("#.....@..#"))
|
|
// simpleFloor1 = append(simpleFloor1, []byte("#........#"))
|
|
// simpleFloor1 = append(simpleFloor1, []byte("#........#"))
|
|
// simpleFloor1 = append(simpleFloor1, []byte("##########"))
|
|
|
|
// simpleFloor := makeWide(simpleFloor1)
|
|
// simpleMoves := []byte("^^")
|
|
// part2(simpleFloor, simpleMoves)
|
|
// }
|
|
|
|
{
|
|
exampleFloor := makeWide(readFloor("example_map.txt"))
|
|
exampleMoves, _ := os.ReadFile("example_moves.txt")
|
|
fmt.Println(part2(exampleFloor, exampleMoves), 9021)
|
|
}
|
|
|
|
{
|
|
dataFloor := makeWide(readFloor("data_map.txt"))
|
|
dataMoves, _ := os.ReadFile("data_moves.txt")
|
|
fmt.Println(part2(dataFloor, dataMoves))
|
|
}
|
|
}
|
|
|
|
func part1(floor [][]byte, moves []byte) (result int) {
|
|
botx, boty := findInitialPosition(floor)
|
|
|
|
doMoves(moves, botx, boty, floor, doMovePart1)
|
|
|
|
for y, line := range floor {
|
|
for x, c := range line {
|
|
if c == 'O' {
|
|
result += y*100 + x
|
|
}
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func doMovePart1(floor [][]byte, startx, starty int, dx, dy int) (endx, endy int) {
|
|
x := startx
|
|
y := starty
|
|
moved := 0
|
|
for {
|
|
moved += 1
|
|
x += dx
|
|
y += dy
|
|
|
|
if floor[y][x] == '#' {
|
|
return startx, starty
|
|
}
|
|
|
|
if floor[y][x] == '.' {
|
|
if moved > 1 {
|
|
floor[y][x] = 'O'
|
|
}
|
|
floor[starty+dy][startx+dx] = '@'
|
|
floor[starty][startx] = '.'
|
|
return startx + dx, starty + dy
|
|
}
|
|
}
|
|
}
|
|
|
|
func doMoves(moves []byte, botx int, boty int, floor [][]byte, doMoveFn func([][]byte, int, int, int, int) (int, int)) {
|
|
for _, move := range moves {
|
|
switch move {
|
|
case '^':
|
|
botx, boty = doMoveFn(floor, botx, boty, 0, -1)
|
|
case 'v':
|
|
botx, boty = doMoveFn(floor, botx, boty, 0, 1)
|
|
case '<':
|
|
botx, boty = doMoveFn(floor, botx, boty, -1, 0)
|
|
case '>':
|
|
botx, boty = doMoveFn(floor, botx, boty, 1, 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
func findInitialPosition(floor [][]byte) (int, int) {
|
|
var botx, boty int
|
|
for y, line := range floor {
|
|
idx := strings.IndexByte(string(line), '@')
|
|
if idx >= 0 {
|
|
botx = idx
|
|
boty = y
|
|
break
|
|
}
|
|
}
|
|
return botx, boty
|
|
}
|
|
|
|
type QueuedMove struct {
|
|
x, y int
|
|
c byte
|
|
}
|
|
|
|
func part2(floor [][]byte, moves []byte) (result int) {
|
|
var botx, boty int
|
|
for y, line := range floor {
|
|
idx := strings.IndexByte(string(line), '@')
|
|
if idx >= 0 {
|
|
botx = idx
|
|
boty = y
|
|
break
|
|
}
|
|
}
|
|
|
|
for _, move := range moves {
|
|
var queued []QueuedMove
|
|
var dx, dy int
|
|
switch move {
|
|
case '^':
|
|
dx, dy = 0, -1
|
|
case 'v':
|
|
dx, dy = 0, 1
|
|
case '<':
|
|
dx, dy = -1, 0
|
|
case '>':
|
|
dx, dy = 1, 0
|
|
default:
|
|
continue
|
|
}
|
|
|
|
if doMovePart2(floor, botx+dx, boty+dy, dx, dy, &queued) {
|
|
hasMadeMove := make(map[QueuedMove]bool)
|
|
for i := range queued {
|
|
m := queued[len(queued)-1-i]
|
|
_, alreadyMade := hasMadeMove[m]
|
|
if !alreadyMade {
|
|
floor[m.y][m.x] = m.c
|
|
floor[m.y-dy][m.x-dx] = '.'
|
|
hasMadeMove[m] = true
|
|
}
|
|
}
|
|
|
|
floor[boty][botx] = '.'
|
|
botx += dx
|
|
boty += dy
|
|
floor[boty][botx] = '@'
|
|
}
|
|
|
|
if !checkIntegrity(floor) {
|
|
panic("no")
|
|
}
|
|
}
|
|
|
|
for y, line := range floor {
|
|
for x, c := range line {
|
|
if c == '[' {
|
|
result += y*100 + x
|
|
}
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func printMap(floor [][]byte) {
|
|
for _, line := range floor {
|
|
fmt.Println(string(line))
|
|
}
|
|
}
|
|
|
|
func checkIntegrity(floor [][]byte) bool {
|
|
for _, line := range floor {
|
|
for x, c := range line {
|
|
if c == '[' && line[x+1] != ']' {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func enqueueMove(x, y int, c byte, queued *[]QueuedMove) {
|
|
*queued = append(*queued, QueuedMove{x, y, c})
|
|
}
|
|
|
|
func doMovePart2(floor [][]byte, x, y int, dx, dy int, queued *[]QueuedMove) bool {
|
|
switch floor[y][x] {
|
|
case '.':
|
|
return true
|
|
case '#':
|
|
return false
|
|
case '[':
|
|
if dx == 0 {
|
|
enqueueMove(x, y+dy, '[', queued)
|
|
enqueueMove(x+1, y+dy, ']', queued)
|
|
return doMovePart2(floor, x, y+dy, dx, dy, queued) && doMovePart2(floor, x+1, y+dy, dx, dy, queued)
|
|
} else if dx > 0 {
|
|
enqueueMove(x+1, y, '[', queued)
|
|
enqueueMove(x+2, y, ']', queued)
|
|
return doMovePart2(floor, x+2, y, dx, dy, queued)
|
|
}
|
|
case ']':
|
|
if dx == 0 {
|
|
enqueueMove(x-1, y+dy, '[', queued)
|
|
enqueueMove(x, y+dy, ']', queued)
|
|
return doMovePart2(floor, x-1, y+dy, dx, dy, queued) && doMovePart2(floor, x, y+dy, dx, dy, queued)
|
|
} else if dx < 0 {
|
|
enqueueMove(x-1, y, ']', queued)
|
|
enqueueMove(x-2, y, '[', queued)
|
|
return doMovePart2(floor, x-2, y, dx, dy, queued)
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func makeWide(floor [][]byte) (wide [][]byte) {
|
|
wide = make([][]byte, len(floor))
|
|
for y, line := range floor {
|
|
w := make([]byte, len(line)*2)
|
|
for i, c := range line {
|
|
switch c {
|
|
case '#':
|
|
w[i*2+0] = '#'
|
|
w[i*2+1] = '#'
|
|
case '.':
|
|
w[i*2+0] = '.'
|
|
w[i*2+1] = '.'
|
|
case 'O':
|
|
w[i*2+0] = '['
|
|
w[i*2+1] = ']'
|
|
case '@':
|
|
w[i*2+0] = '@'
|
|
w[i*2+1] = '.'
|
|
}
|
|
}
|
|
wide[y] = w
|
|
}
|
|
return
|
|
}
|
|
|
|
func readFloor(fileName string) (floor [][]byte) {
|
|
fp, _ := os.Open(fileName)
|
|
scanner := bufio.NewScanner(fp)
|
|
for scanner.Scan() {
|
|
floor = append(floor, []byte(strings.TrimSpace(scanner.Text())))
|
|
}
|
|
return
|
|
}
|