adventofcode/2024/day15/main.go

230 lines
4.4 KiB
Go
Raw Normal View History

2024-12-15 15:12:24 +00:00
package main
import (
"fmt"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
func main() {
inp := h.StdinToStringSlice()
part1(inp)
fmt.Println()
part2(inp)
}
func part1(inp []string) {
fmt.Println("# Part 1")
m, dirs := parseInput(inp)
for i := range dirs {
bot, _ := m.FindFirst('@')
move(m, bot, dirs[i])
}
crates := m.FindAll('O')
var total int
for i := range crates {
add := (crates[i].Y*100 + crates[i].X)
total = total + add
}
fmt.Println("GPS Sum:", total)
}
func part2(inp []string) {
fmt.Println("# Part 2")
m, dirs := parseInput2(inp)
for i := range dirs {
bot, _ := m.FindFirst('@')
move(m, bot, dirs[i])
}
crates := m.FindAll('[')
var total int
for i := range crates {
add := (crates[i].Y*100 + crates[i].X)
total = total + add
}
fmt.Println("GPS Sum:", total)
}
func move(m h.CoordByteMap, orig h.Coordinate, dir byte) bool {
if !canMove(m, orig, dir) {
return false
}
oB := m.Get(orig)
if dir == '^' || dir == 'v' {
switch oB {
case ']':
return moveCrate(m, orig.West(), dir)
case '[':
return moveCrate(m, orig, dir)
}
}
var moveTo h.Coordinate
switch dir {
case '^':
moveTo = orig.North()
case '>':
moveTo = orig.East()
case 'v':
moveTo = orig.South()
case '<':
moveTo = orig.West()
}
dest := m.Get(moveTo)
if dest == '#' {
// A wall, can't move
return false
} else if dest != '.' {
// Not a wall, but not empty, can we push it?
if !move(m, moveTo, dir) {
return false
}
// Fall through
}
m.Put(moveTo, oB)
m.Put(orig, '.')
return true
}
func moveCrate(m h.CoordByteMap, orig h.Coordinate, dir byte) bool {
if !canMoveCrate(m, orig, dir) {
return false
}
var moveTo h.Coordinate
switch dir {
case '^':
moveTo = orig.North()
case 'v':
moveTo = orig.South()
}
destW, destE := m.Get(moveTo), m.Get(moveTo.East())
if destW == '#' || destE == '#' {
// A wall, can't move
return false
} else {
if destW != '.' && !move(m, moveTo, dir) {
return false
}
// Check other half of crate
if (destE != '.' && destE != ']') && !move(m, moveTo.East(), dir) {
return false
}
// Fall through
}
m.Put(moveTo, '[')
m.Put(moveTo.East(), ']')
m.Put(orig, '.')
m.Put(orig.East(), '.')
return true
}
func canMove(m h.CoordByteMap, orig h.Coordinate, dir byte) bool {
oB := m.Get(orig)
if dir == '^' || dir == 'v' {
switch oB {
case ']':
return canMoveCrate(m, orig.West(), dir)
case '[':
return canMoveCrate(m, orig, dir)
}
}
var moveTo h.Coordinate
switch dir {
case '^':
moveTo = orig.North()
case '>':
moveTo = orig.East()
case 'v':
moveTo = orig.South()
case '<':
moveTo = orig.West()
}
dest := m.Get(moveTo)
if dest == '#' {
// A wall, can't move
return false
} else if dest != '.' {
// Not a wall, but not empty, can we push it?
if !canMove(m, moveTo, dir) {
return false
}
// Fall through
}
// can move
return true
}
func canMoveCrate(m h.CoordByteMap, orig h.Coordinate, dir byte) bool {
var moveTo h.Coordinate
switch dir {
case '^':
moveTo = orig.North()
case 'v':
moveTo = orig.South()
}
destW, destE := m.Get(moveTo), m.Get(moveTo.East())
if destW == '#' || destE == '#' {
// A wall, can't move
return false
} else {
if destW != '.' && !canMove(m, moveTo, dir) {
return false
}
// Check other half of crate
if destE != '.' && !canMove(m, moveTo.East(), dir) {
return false
}
// Fall through
}
// can move
return true
}
func parseInput(inp []string) (h.CoordByteMap, []byte) {
var m h.CoordByteMap
var dirs []byte
var inDirs bool
for i := range inp {
if inDirs {
dirs = append(dirs, []byte(inp[i])...)
} else {
if inp[i] == "" {
m = h.StringSliceToCoordByteMap(inp[:i])
inDirs = true
}
}
}
return m, dirs
}
func parseInput2(inp []string) (h.CoordByteMap, []byte) {
// First modify the input
var m h.CoordByteMap
var newMap []string
var inDirs bool
var dirs []byte
for i := range inp {
if inDirs {
dirs = append(dirs, []byte(inp[i])...)
} else {
if inp[i] == "" {
m = h.StringSliceToCoordByteMap(newMap)
inDirs = true
}
var newLine []byte
for j := range inp[i] {
switch inp[i][j] {
case 'O':
newLine = append(newLine, []byte{'[', ']'}...)
case '@':
newLine = append(newLine, []byte{'@', '.'}...)
default:
newLine = append(newLine, []byte{inp[i][j], inp[i][j]}...)
}
}
newMap = append(newMap, string(newLine))
}
}
return m, dirs
}