main.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package main
  2. import (
  3. "bufio"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "log"
  8. "os"
  9. "strings"
  10. )
  11. func trim(bs []byte) []byte {
  12. if len(bs) > 0 && bs[len(bs)-1] == '\n' {
  13. return bs[:len(bs)-1]
  14. }
  15. return bs
  16. }
  17. func coordOf(i, w int) (x, y int) {
  18. y = int(float64(i) / float64(w))
  19. x = i - y*w
  20. return
  21. }
  22. func indexOf(x, y, w int) int {
  23. return x + y*w
  24. }
  25. type World struct {
  26. wmap []byte
  27. width, height, robot int
  28. }
  29. func (w *World) String() string {
  30. var b strings.Builder
  31. var n int
  32. var err error
  33. for i := 0; i < w.height; i++ {
  34. idx := i * w.width
  35. n, err = b.Write(w.wmap[idx : idx+w.width])
  36. if err != nil {
  37. panic(fmt.Sprintf("While writing bytes to string: %v", err))
  38. }
  39. if n != w.width {
  40. panic("Wrote fewer bytes to string than expected")
  41. }
  42. n, err = b.Write([]byte{'\n'})
  43. if err != nil {
  44. panic(fmt.Sprintf("While writing bytes to string: %v", err))
  45. }
  46. if n != 1 {
  47. panic("Wrote fewer bytes to string than expected")
  48. }
  49. }
  50. return b.String()
  51. }
  52. func (w *World) Robot(move byte) bool {
  53. x, y := coordOf(w.robot, w.width)
  54. var x2, y2 int
  55. switch move {
  56. case '<':
  57. x2, y2 = x-1, y
  58. case '>':
  59. x2, y2 = x+1, y
  60. case '^':
  61. x2, y2 = x, y-1
  62. case 'v':
  63. x2, y2 = x, y+1
  64. default:
  65. log.Fatalf("Unknown robot instruction %q", move)
  66. }
  67. // Test for world escape.
  68. if x2 < 0 || w.width <= x2 ||
  69. y2 < 0 || w.height <= y2 {
  70. return false
  71. }
  72. next := indexOf(x2, y2, w.width)
  73. delta := next - w.robot
  74. idx := next
  75. for w.wmap[idx] != '.' && w.wmap[idx] != '#' {
  76. idx += delta
  77. }
  78. if w.wmap[idx] == '#' { // Walls are immovable.
  79. return false
  80. }
  81. for idx != w.robot { // Move everything over.
  82. w.wmap[idx] = w.wmap[idx-delta]
  83. idx -= delta
  84. }
  85. w.robot = next
  86. w.wmap[idx] = '.' // Replace robot with empty space.
  87. return true
  88. }
  89. func main() {
  90. world := &World{
  91. wmap: make([]byte, 0, 512),
  92. }
  93. r := bufio.NewReader(os.Stdin)
  94. // Read map
  95. lastLinefeed := 0
  96. i := 0
  97. for {
  98. i++
  99. col := i - lastLinefeed
  100. b, err := r.ReadByte()
  101. if errors.Is(err, io.EOF) {
  102. log.Fatal("Unexpected end of file")
  103. }
  104. if err != nil {
  105. line := 1
  106. if world.width > 0 {
  107. line = i / world.width
  108. }
  109. log.Fatalf("While reading input at line %d, column %d: %v", line, col, err)
  110. }
  111. if b == '\n' {
  112. if col == 1 {
  113. break
  114. }
  115. if world.height == 0 {
  116. world.width = len(world.wmap)
  117. }
  118. lastLinefeed = i
  119. world.height++
  120. continue
  121. }
  122. switch b {
  123. case '@':
  124. world.robot = len(world.wmap)
  125. case '.', 'O', '#':
  126. default:
  127. line := 1
  128. if world.width > 0 {
  129. line = i / world.width
  130. }
  131. log.Fatalf("Unknown or unexpected map glyph %q at line %d, column %d", b, line, col)
  132. }
  133. world.wmap = append(world.wmap, b)
  134. }
  135. // Read movements
  136. i = 0
  137. for {
  138. i++
  139. col := i - lastLinefeed
  140. move, err := r.ReadByte()
  141. if errors.Is(err, io.EOF) {
  142. break
  143. }
  144. if err != nil {
  145. log.Fatalf("While reading movements at line %d, column %d: %v", i, col, err)
  146. }
  147. switch move {
  148. case '\n':
  149. continue
  150. case '<', '>', '^', 'v':
  151. world.Robot(move)
  152. }
  153. }
  154. //fmt.Printf("%s\n", world)
  155. // Calculate sum of "GPS" coordinates
  156. sum := 0
  157. for i, b := range world.wmap {
  158. if b == 'O' {
  159. x, y := coordOf(i, world.width)
  160. sum += 100*y + x
  161. }
  162. }
  163. fmt.Println(sum)
  164. }