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 }