package main import ( "bufio" "errors" "fmt" "io" "log" "os" "strings" ) func trim(bs []byte) []byte { if len(bs) > 0 && bs[len(bs)-1] == '\n' { return bs[:len(bs)-1] } return bs } func coordOf(i, w int) (x, y int) { y = int(float64(i) / float64(w)) x = i - y*w return } func indexOf(x, y, w int) int { return x + y*w } type World struct { wmap []byte width, height, robot int } func (w *World) String() string { var b strings.Builder var n int var err error for i := 0; i < w.height; i++ { idx := i * w.width n, err = b.Write(w.wmap[idx : idx+w.width]) if err != nil { panic(fmt.Sprintf("While writing bytes to string: %v", err)) } if n != w.width { panic("Wrote fewer bytes to string than expected") } n, err = b.Write([]byte{'\n'}) if err != nil { panic(fmt.Sprintf("While writing bytes to string: %v", err)) } if n != 1 { panic("Wrote fewer bytes to string than expected") } } return b.String() } func (w *World) Robot(move byte) bool { x, y := coordOf(w.robot, w.width) var x2, y2 int switch move { case '<': x2, y2 = x-1, y case '>': x2, y2 = x+1, y case '^': x2, y2 = x, y-1 case 'v': x2, y2 = x, y+1 default: log.Fatalf("Unknown robot instruction %q", move) } // Test for world escape. if x2 < 0 || w.width <= x2 || y2 < 0 || w.height <= y2 { return false } next := indexOf(x2, y2, w.width) delta := next - w.robot idx := next for w.wmap[idx] != '.' && w.wmap[idx] != '#' { idx += delta } if w.wmap[idx] == '#' { // Walls are immovable. return false } for idx != w.robot { // Move everything over. w.wmap[idx] = w.wmap[idx-delta] idx -= delta } w.robot = next w.wmap[idx] = '.' // Replace robot with empty space. return true } func main() { world := &World{ wmap: make([]byte, 0, 512), } r := bufio.NewReader(os.Stdin) // Read map lastLinefeed := 0 i := 0 for { i++ col := i - lastLinefeed b, err := r.ReadByte() if errors.Is(err, io.EOF) { log.Fatal("Unexpected end of file") } if err != nil { line := 1 if world.width > 0 { line = i / world.width } log.Fatalf("While reading input at line %d, column %d: %v", line, col, err) } if b == '\n' { if col == 1 { break } if world.height == 0 { world.width = len(world.wmap) } lastLinefeed = i world.height++ continue } switch b { case '@': world.robot = len(world.wmap) case '.', 'O', '#': default: line := 1 if world.width > 0 { line = i / world.width } log.Fatalf("Unknown or unexpected map glyph %q at line %d, column %d", b, line, col) } world.wmap = append(world.wmap, b) } // Read movements i = 0 for { i++ col := i - lastLinefeed move, err := r.ReadByte() if errors.Is(err, io.EOF) { break } if err != nil { log.Fatalf("While reading movements at line %d, column %d: %v", i, col, err) } switch move { case '\n': continue case '<', '>', '^', 'v': world.Robot(move) } } //fmt.Printf("%s\n", world) // Calculate sum of "GPS" coordinates sum := 0 for i, b := range world.wmap { if b == 'O' { x, y := coordOf(i, world.width) sum += 100*y + x } } fmt.Println(sum) }