230 lines
4.4 KiB
Go
230 lines
4.4 KiB
Go
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
|
|
}
|