package common import ( "bufio" "strconv" ) type Tile byte const ( None Tile = iota Empty Wall ) type Direction byte const ( East Direction = iota West North South ) type Traveler struct { facing Direction x int y int } type Map struct { tiles [][]Tile traveler Traveler isCubic bool } func (tileMap *Map) GetNextTile() (int, int, Direction) { nextX := tileMap.traveler.x nextY := tileMap.traveler.y nextDirection := tileMap.traveler.facing switch nextDirection { case East: if nextX + 1 < len(tileMap.tiles[nextY]) && tileMap.tiles[nextY][nextX+1] == Empty { nextX++ } else if nextX + 1 == len(tileMap.tiles[nextY]) || tileMap.tiles[nextY][nextX+1] == None { if tileMap.isCubic { if nextX == 149 { if tileMap.tiles[149-nextY][99] == Empty { nextX = 99 nextY = 149 - nextY nextDirection = West } } else if nextX == 99 && nextY < 100 { if tileMap.tiles[49][nextY+50] == Empty { nextX = nextY + 50 nextY = 49 nextDirection = North } } else if nextX == 99 { if tileMap.tiles[149-nextY][149] == Empty { nextX = 149 nextY = 149 - nextY nextDirection = West } } else { if tileMap.tiles[149][nextY-100] == Empty { nextX = nextY - 100 nextY = 149 nextDirection = North } } } else { for i := 0; i < len(tileMap.tiles[nextY]); i++ { if tileMap.tiles[nextY][i] != None { if tileMap.tiles[nextY][i] == Empty { nextX = i } break } } } } case West: if nextX - 1 >= 0 && tileMap.tiles[nextY][nextX-1] == Empty { nextX-- } else if nextX - 1 < 0 || tileMap.tiles[nextY][nextX-1] == None { if tileMap.isCubic { if nextX == 50 && nextY < 50 { if tileMap.tiles[149-nextY][0] == Empty { nextX = 0 nextY = 149 - nextY nextDirection = East } } else if nextX == 50 { if tileMap.tiles[100][nextY-50] == Empty { nextX = nextY - 50 nextY = 100 nextDirection = South } } else if nextY < 150 { if tileMap.tiles[149-nextY][50] == Empty { nextX = 50 nextY = 149 - nextY nextDirection = East } } else { if tileMap.tiles[0][nextY-100] == Empty { nextX = nextY - 100 nextY = 0 nextDirection = South } } } else { for i := len(tileMap.tiles[nextY]) - 1; i >= 0; i-- { if tileMap.tiles[nextY][i] != None { if tileMap.tiles[nextY][i] == Empty { nextX = i } break } } } } case North: if nextY - 1 >= 0 && tileMap.tiles[nextY-1][nextX] == Empty { nextY-- } else if nextY - 1 < 0 || tileMap.tiles[nextY-1][nextX] == None { if tileMap.isCubic { if nextX < 50 { if tileMap.tiles[nextX+50][50] == Empty { nextY = nextX + 50 nextX = 50 nextDirection = East } } else if nextX < 100 { if tileMap.tiles[nextX+100][0] == Empty { nextY = nextX + 100 nextX = 0 nextDirection = East } } else { if tileMap.tiles[199][nextX-100] == Empty { nextX = nextX - 100 nextY = 199 } } } else { for i := len(tileMap.tiles) - 1; i >= 0; i-- { if tileMap.tiles[i][nextX] != None { if tileMap.tiles[i][nextX] == Empty { nextY = i } break } } } } case South: if nextY + 1 < len(tileMap.tiles) && tileMap.tiles[nextY+1][nextX] == Empty { nextY++ } else if nextY + 1 == len(tileMap.tiles) || tileMap.tiles[nextY+1][nextX] == None { if tileMap.isCubic { if nextX < 50 { if tileMap.tiles[0][nextX+100] == Empty { nextX = nextX + 100 nextY = 0 } } else if nextX < 100 { if tileMap.tiles[nextX+100][49] == Empty { nextY = nextX + 100 nextX = 49 nextDirection = West } } else { if tileMap.tiles[nextX-50][99] == Empty { nextY = nextX - 50 nextX = 99 nextDirection = West } } } else { for i := 0; i < len(tileMap.tiles); i++ { if tileMap.tiles[i][nextX] != None { if tileMap.tiles[i][nextX] == Empty { nextY = i } break } } } } } return nextX, nextY, nextDirection } func (tileMap *Map) GetPassword() int { password := 1000 * (tileMap.traveler.y + 1) + 4 * (tileMap.traveler.x + 1) switch tileMap.traveler.facing { case South: password += 1 case West: password += 2 case North: password += 3 } return password } type Instruction interface { Apply(*Map) } type Move struct { nTiles int } func (instruction Move) Apply(tileMap *Map) { for i := 0; i < instruction.nTiles; i++ { nextX, nextY, nextDirection := tileMap.GetNextTile() tileMap.traveler.x = nextX tileMap.traveler.y = nextY tileMap.traveler.facing = nextDirection } } type RotateClockwise struct {} func (instruction RotateClockwise) Apply(tileMap *Map) { switch tileMap.traveler.facing { case East: tileMap.traveler.facing = South case South: tileMap.traveler.facing = West case West: tileMap.traveler.facing = North case North: tileMap.traveler.facing = East } } type RotateCounterclockwise struct {} func (instruction RotateCounterclockwise) Apply(tileMap *Map) { switch tileMap.traveler.facing { case East: tileMap.traveler.facing = North case South: tileMap.traveler.facing = East case West: tileMap.traveler.facing = South case North: tileMap.traveler.facing = West } } func Parse(scanner bufio.Scanner, isCubic bool) (Map, []Instruction) { tileMap := Map{} instructions := []Instruction{} longestRow := 0 for scanner.Scan() { line := scanner.Text() if line == "" { scanner.Scan() line := scanner.Text() for i := 0; i < len(line); i++ { if line[i] >= '0' && line[i] <= '9' { j := i for j < len(line) && line[j] >= '0' && line[j] <= '9' { j++ } nTiles, _ := strconv.Atoi(line[i:j]) instructions = append(instructions, Move{nTiles}) i = j - 1 } else if line[i] == 'L' { instructions = append(instructions, RotateCounterclockwise{}) } else if line[i] == 'R' { instructions = append(instructions, RotateClockwise{}) } } } else { if len(line) > longestRow { longestRow = len(line) } tileRow := make([]Tile, len(line)) for i := range line { switch line[i] { case ' ': tileRow[i] = None case '.': tileRow[i] = Empty case '#': tileRow[i] = Wall } } tileMap.tiles = append(tileMap.tiles, tileRow) } } for i := range tileMap.tiles { for len(tileMap.tiles[i]) < longestRow { tileMap.tiles[i] = append(tileMap.tiles[i], None) } } for i := range tileMap.tiles[0] { if tileMap.tiles[0][i] == Empty { tileMap.traveler = Traveler{East, i, 0} break } } tileMap.isCubic = isCubic return tileMap, instructions }