package main import ( "bufio" "fmt" "os" "slices" "strings" ) func main() { fmt.Println(part1(readData("example.txt"))) fmt.Println(part1(readData("data.txt"))) fmt.Println(part2(readData("example.txt"))) fmt.Println(part2(readData("data.txt"))) } func part1(tiles [][]byte, x, y int) (visited int) { directions := [4][2]int{ {0, -1}, {1, 0}, {0, 1}, {-1, 0}, } dir := 0 dx := directions[dir][0] dy := directions[dir][1] for { if tiles[y][x] != 'X' { tiles[y][x] = 'X' visited++ } if y+dy < 0 || y+dy >= len(tiles) { return } if x+dx < 0 || x+dx >= len(tiles[y+dy]) { return } if tiles[y+dy][x+dx] == '#' { dir = (dir + 1) % 4 dx = directions[dir][0] dy = directions[dir][1] } else { x += dx y += dy } } } type State struct { x, y int dir int } func (state *State) GetDirection() (dx, dy int) { directions := [4][2]int{ {0, -1}, {1, 0}, {0, 1}, {-1, 0}, } dx = directions[state.dir][0] dy = directions[state.dir][1] return } func (state *State) GetNextPosition() (x, y int) { dx, dy := state.GetDirection() x = state.x + dx y = state.y + dy return } func (state *State) IsAboutToExit(tiles [][]byte) bool { x, y := state.GetNextPosition() if y < 0 || y >= len(tiles) { return true } if x < 0 || x >= len(tiles[y]) { return true } return false } func (state *State) HasObstacleInFront(tiles [][]byte) bool { x, y := state.GetNextPosition() return tiles[y][x] == '#' } func (state *State) Clone() State { return State{ state.x, state.y, state.dir, } } func (state *State) Rotate() { state.dir = (state.dir + 1) % 4 } func copyVisited(visited [][]byte) (visitedCopy [][]byte) { visitedCopy = make([][]byte, len(visited)) for i, sublist := range visited { visitedCopy[i] = make([]byte, len(sublist)) copy(visitedCopy[i], sublist) } return } func canReachExit(tiles [][]byte, visited [][]byte, state State) bool { for !state.IsAboutToExit(tiles) { visitedBit := byte(1) << state.dir if visited[state.y][state.x]&visitedBit != 0 { return false } visited[state.y][state.x] |= visitedBit if state.HasObstacleInFront(tiles) { state.Rotate() continue } nextX, nextY := state.GetNextPosition() state.x = nextX state.y = nextY } return true } func part2(tiles [][]byte, startX, startY int) (positionsLooping int) { state := State{startX, startY, 0} visited := make([][]byte, len(tiles)) for i := range visited { visited[i] = make([]byte, len(tiles[i])) } for !state.IsAboutToExit(tiles) { visitedBit := byte(1) << state.dir if visited[state.y][state.x]&visitedBit != 0 { panic("Already visited???") } visited[state.y][state.x] |= visitedBit if state.HasObstacleInFront(tiles) { state.Rotate() continue } nextX, nextY := state.GetNextPosition() if visited[nextY][nextX] == 0 { tiles[nextY][nextX] = '#' stateCopy := state.Clone() stateCopy.Rotate() if !canReachExit(tiles, copyVisited(visited), stateCopy) { positionsLooping++ } tiles[nextY][nextX] = '.' } state.x = nextX state.y = nextY } return } func readData(fileName string) (tiles [][]byte, startX, startY int) { fp, err := os.Open(fileName) if err != nil { panic(err) } scanner := bufio.NewScanner(fp) y := 0 for scanner.Scan() { line := []byte(strings.TrimSpace(scanner.Text())) x := slices.Index(line, '^') if x != -1 { startX = x startY = y } tiles = append(tiles, line) y += 1 } return }