🌅
This commit is contained in:
635
solutions/day22/day22.go
Normal file
635
solutions/day22/day22.go
Normal file
@@ -0,0 +1,635 @@
|
||||
package day22
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Command int
|
||||
type Tile int
|
||||
type Direction int
|
||||
type Face int
|
||||
type Board [][]Tile
|
||||
type FaceMap map[Face]Board
|
||||
|
||||
const (
|
||||
TurnRight Command = -1
|
||||
TurnLeft = -2
|
||||
)
|
||||
|
||||
const (
|
||||
Void Tile = iota
|
||||
Empty
|
||||
Wall
|
||||
)
|
||||
|
||||
const (
|
||||
Right Direction = 0
|
||||
Down = 1
|
||||
Left = 2
|
||||
Up = 3
|
||||
)
|
||||
|
||||
const (
|
||||
TopFace Face = iota
|
||||
BottomFace
|
||||
LeftFace
|
||||
RightFace
|
||||
FrontFace
|
||||
BackFace
|
||||
)
|
||||
|
||||
func printBoard(board Board) {
|
||||
for _, row := range board {
|
||||
for _, tile := range row {
|
||||
switch tile {
|
||||
case Void:
|
||||
fmt.Printf(" ")
|
||||
case Empty:
|
||||
fmt.Printf(".")
|
||||
case Wall:
|
||||
fmt.Printf("#")
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
func parseCommands(runes []rune) []Command {
|
||||
result := []Command{}
|
||||
accum := 0
|
||||
|
||||
for _, r := range runes {
|
||||
if r >= '0' && r <= '9' {
|
||||
accum = (accum * 10) + int(r-'0')
|
||||
} else {
|
||||
result = append(result, Command(accum))
|
||||
accum = 0
|
||||
if r == 'R' {
|
||||
result = append(result, TurnRight)
|
||||
} else {
|
||||
result = append(result, TurnLeft)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if accum > 0 {
|
||||
result = append(result, Command(accum))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func parseBoardLine(runes []rune) []Tile {
|
||||
result := []Tile{}
|
||||
|
||||
for _, r := range runes {
|
||||
switch r {
|
||||
case ' ':
|
||||
result = append(result, Void)
|
||||
case '.':
|
||||
result = append(result, Empty)
|
||||
case '#':
|
||||
result = append(result, Wall)
|
||||
default:
|
||||
fmt.Println("PANIC: Illegal character in board map", string(r))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func initialPosition(board Board) (x int, y int) {
|
||||
for idx, tile := range board[0] {
|
||||
if tile == Empty {
|
||||
x = idx
|
||||
y = 0
|
||||
return
|
||||
}
|
||||
}
|
||||
fmt.Println("PANIC: Couldn't figure out initial position")
|
||||
x = 0
|
||||
y = 0
|
||||
return
|
||||
}
|
||||
|
||||
func turn(direction Direction, turn Command) Direction {
|
||||
switch direction {
|
||||
case Up:
|
||||
switch turn {
|
||||
case TurnLeft:
|
||||
return Left
|
||||
case TurnRight:
|
||||
return Right
|
||||
}
|
||||
case Right:
|
||||
switch turn {
|
||||
case TurnLeft:
|
||||
return Up
|
||||
case TurnRight:
|
||||
return Down
|
||||
}
|
||||
case Down:
|
||||
switch turn {
|
||||
case TurnLeft:
|
||||
return Right
|
||||
case TurnRight:
|
||||
return Left
|
||||
}
|
||||
case Left:
|
||||
switch turn {
|
||||
case TurnLeft:
|
||||
return Down
|
||||
case TurnRight:
|
||||
return Up
|
||||
}
|
||||
}
|
||||
fmt.Println("PANIC: turn default WTF")
|
||||
return Down
|
||||
}
|
||||
|
||||
type State2D struct {
|
||||
x int
|
||||
y int
|
||||
direction Direction
|
||||
board Board
|
||||
}
|
||||
|
||||
func step2d(count int, state *State2D) {
|
||||
if count == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
keepGoing := true
|
||||
|
||||
switch state.direction {
|
||||
case Right:
|
||||
currentRow := state.board[state.y]
|
||||
tester := (state.x + 1) % len(currentRow)
|
||||
for {
|
||||
if currentRow[tester] == Void {
|
||||
tester = (tester + 1) % len(currentRow)
|
||||
continue
|
||||
} else if currentRow[tester] == Wall {
|
||||
keepGoing = false
|
||||
break
|
||||
} else {
|
||||
(*state).x = tester
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
case Left:
|
||||
currentRow := state.board[state.y]
|
||||
tester := state.x - 1
|
||||
for {
|
||||
if tester < 0 {
|
||||
tester = len(currentRow) - 1
|
||||
}
|
||||
if currentRow[tester] == Void {
|
||||
tester -= 1
|
||||
continue
|
||||
} else if currentRow[tester] == Wall {
|
||||
keepGoing = false
|
||||
break
|
||||
} else {
|
||||
(*state).x = tester
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
case Up:
|
||||
tester := state.y - 1
|
||||
for {
|
||||
if tester < 0 {
|
||||
tester = len(state.board) - 1
|
||||
}
|
||||
if state.board[tester][state.x] == Void {
|
||||
tester -= 1
|
||||
continue
|
||||
} else if state.board[tester][state.x] == Wall {
|
||||
keepGoing = false
|
||||
break
|
||||
} else {
|
||||
(*state).y = tester
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
case Down:
|
||||
fmt.Println("Moving down count is", count, "y value", state.y)
|
||||
tester := (state.y + 1) % len(state.board)
|
||||
for {
|
||||
if state.board[tester][state.x] == Void {
|
||||
tester = (tester + 1) % len(state.board)
|
||||
continue
|
||||
} else if state.board[tester][state.x] == Wall {
|
||||
fmt.Println("Hit wall at tester", tester)
|
||||
keepGoing = false
|
||||
break
|
||||
} else {
|
||||
(*state).y = tester
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
fmt.Println("BAD")
|
||||
}
|
||||
|
||||
if keepGoing {
|
||||
step2d(count-1, state)
|
||||
}
|
||||
}
|
||||
|
||||
func copyFace(startX int, startY int, faceSize int, board Board) Board {
|
||||
retval := Board{}
|
||||
|
||||
for y := 0; y < faceSize; y++ {
|
||||
newRow := []Tile{}
|
||||
|
||||
for x := 0; x < faceSize; x++ {
|
||||
newRow = append(newRow, board[y+startY][x+startX])
|
||||
}
|
||||
retval = append(retval, newRow)
|
||||
}
|
||||
|
||||
return retval
|
||||
}
|
||||
|
||||
func validPoint(x int, y int, board Board) bool {
|
||||
return (x >= 0) && (y >= 0) && (x < len(board[0])) && (y < len(board))
|
||||
}
|
||||
|
||||
var faceMoves map[Face]map[Direction]Face = map[Face]map[Direction]Face{
|
||||
TopFace: map[Direction]Face{
|
||||
Up: BackFace,
|
||||
Left: LeftFace,
|
||||
Right: RightFace,
|
||||
Down: FrontFace,
|
||||
},
|
||||
BottomFace: map[Direction]Face{
|
||||
Up: FrontFace,
|
||||
Left: LeftFace,
|
||||
Right: RightFace,
|
||||
Down: BackFace,
|
||||
},
|
||||
LeftFace: map[Direction]Face{
|
||||
Up: TopFace,
|
||||
Left: BackFace,
|
||||
Right: FrontFace,
|
||||
Down: BottomFace,
|
||||
},
|
||||
RightFace: map[Direction]Face{
|
||||
Up: TopFace,
|
||||
Left: FrontFace,
|
||||
Right: BackFace,
|
||||
Down: BottomFace,
|
||||
},
|
||||
FrontFace: map[Direction]Face{
|
||||
Up: TopFace,
|
||||
Left: LeftFace,
|
||||
Right: RightFace,
|
||||
Down: BottomFace,
|
||||
},
|
||||
BackFace: map[Direction]Face{
|
||||
Up: BackFace,
|
||||
Left: RightFace,
|
||||
Right: FrontFace,
|
||||
Down: FrontFace,
|
||||
},
|
||||
}
|
||||
|
||||
var faceName map[Face]string = map[Face]string{
|
||||
TopFace: "top",
|
||||
BottomFace: "bottom",
|
||||
LeftFace: "left",
|
||||
RightFace: "right",
|
||||
FrontFace: "front",
|
||||
BackFace: "back",
|
||||
}
|
||||
|
||||
var dirName map[Direction]string = map[Direction]string{
|
||||
Up: "up",
|
||||
Down: "down",
|
||||
Left: "left",
|
||||
Right: "right",
|
||||
}
|
||||
|
||||
var tileName map[Tile]string = map[Tile]string{
|
||||
Void: "void",
|
||||
Empty: "empty",
|
||||
Wall: "wall",
|
||||
}
|
||||
|
||||
type State3D struct {
|
||||
x int
|
||||
y int
|
||||
face Face
|
||||
direction Direction
|
||||
board FaceMap
|
||||
}
|
||||
|
||||
func translate(x int, y int, faceSize int, face Face, dir Direction) (resx int, resy int, resdir Direction) {
|
||||
switch face {
|
||||
case TopFace:
|
||||
resdir = Down
|
||||
switch dir {
|
||||
case Right, Left:
|
||||
resy = 0
|
||||
resx = y
|
||||
case Up, Down:
|
||||
resy = 0
|
||||
resx = x
|
||||
}
|
||||
case FrontFace:
|
||||
resdir = dir
|
||||
switch dir {
|
||||
case Up:
|
||||
resy = faceSize - 1
|
||||
resx = x
|
||||
case Down:
|
||||
resy = 0
|
||||
resx = x
|
||||
case Left:
|
||||
resy = y
|
||||
resx = faceSize - 1
|
||||
case Right:
|
||||
resy = y
|
||||
resx = 0
|
||||
}
|
||||
case LeftFace:
|
||||
switch dir {
|
||||
case Up:
|
||||
resx = 0
|
||||
resy = x
|
||||
resdir = Right
|
||||
case Down:
|
||||
resx = 0
|
||||
resy = x
|
||||
resdir = Right
|
||||
case Left:
|
||||
resy = y
|
||||
resx = faceSize - 1
|
||||
resdir = Left
|
||||
case Right:
|
||||
resy = y
|
||||
resx = 0
|
||||
resdir = Right
|
||||
}
|
||||
case RightFace:
|
||||
switch dir {
|
||||
case Up:
|
||||
resx = faceSize - 1
|
||||
resy = x
|
||||
resdir = Left
|
||||
case Down:
|
||||
resx = faceSize - 1
|
||||
resy = x
|
||||
resdir = Left
|
||||
case Left:
|
||||
resx = faceSize - 1
|
||||
resy = y
|
||||
resdir = Left
|
||||
case Right:
|
||||
resy = y
|
||||
resx = 0
|
||||
resdir = Right
|
||||
}
|
||||
case BackFace:
|
||||
switch dir {
|
||||
case Up:
|
||||
resx = x
|
||||
resy = 0
|
||||
resdir = Down
|
||||
case Down:
|
||||
resx = x
|
||||
resy = faceSize - 1
|
||||
resdir = Up
|
||||
case Left:
|
||||
resx = faceSize - 1
|
||||
resy = y
|
||||
resdir = Right
|
||||
case Right:
|
||||
resy = y
|
||||
resx = 0
|
||||
resdir = Left
|
||||
}
|
||||
case BottomFace:
|
||||
switch dir {
|
||||
case Up:
|
||||
resx = x
|
||||
resy = faceSize - 1
|
||||
resdir = Up
|
||||
case Down:
|
||||
resx = x
|
||||
resy = faceSize - 1
|
||||
resdir = Up
|
||||
case Left:
|
||||
resx = y
|
||||
resy = faceSize - 1
|
||||
resdir = Up
|
||||
case Right:
|
||||
resy = faceSize - 1
|
||||
resx = y
|
||||
resdir = Up
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func step3d(count int, state *State3D) {
|
||||
if count == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
newX := state.x
|
||||
newY := state.y
|
||||
newFace := state.face
|
||||
newDirection := state.direction
|
||||
|
||||
switch state.direction {
|
||||
case Up:
|
||||
newY -= 1
|
||||
case Down:
|
||||
newY += 1
|
||||
case Left:
|
||||
newX -= 1
|
||||
case Right:
|
||||
newX += 1
|
||||
}
|
||||
|
||||
if !validPoint(newX, newY, state.board[state.face]) {
|
||||
newFace = faceMoves[state.face][state.direction]
|
||||
newX, newY, newDirection = translate(newX, newY, len(state.board[state.face]), state.face, state.direction)
|
||||
}
|
||||
fmt.Printf("(%d, %d) going %s on %s ---> (%d, %d) going %s on %s [%s]\n", state.x, state.y, dirName[state.direction], faceName[state.face], newX, newY, dirName[newDirection], faceName[newFace], tileName[state.board[newFace][newY][state.x]])
|
||||
|
||||
if state.board[newFace][newY][newX] == Wall {
|
||||
return
|
||||
}
|
||||
|
||||
(*state).x = newX
|
||||
(*state).y = newY
|
||||
(*state).face = newFace
|
||||
(*state).direction = newDirection
|
||||
}
|
||||
}
|
||||
|
||||
func Run(filename string) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
fmt.Println("Error opening file:", err)
|
||||
return
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
var commands []Command
|
||||
initialBoard := Board{}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
runes := []rune(line)
|
||||
|
||||
if len(runes) > 0 {
|
||||
if runes[0] >= '0' && runes[0] <= '9' {
|
||||
commands = parseCommands(runes)
|
||||
} else {
|
||||
initialBoard = append(initialBoard, parseBoardLine(runes))
|
||||
}
|
||||
}
|
||||
}
|
||||
file.Close()
|
||||
|
||||
maximumRowLength := 0
|
||||
for _, row := range initialBoard {
|
||||
if len(row) > maximumRowLength {
|
||||
maximumRowLength = len(row)
|
||||
}
|
||||
}
|
||||
|
||||
board := Board{}
|
||||
|
||||
for _, row := range initialBoard {
|
||||
for len(row) < maximumRowLength {
|
||||
row = append(row, Void)
|
||||
}
|
||||
board = append(board, row)
|
||||
}
|
||||
|
||||
printBoard(board)
|
||||
|
||||
startX, startY := initialPosition(board)
|
||||
x, y := startX, startY
|
||||
fmt.Println("initial position is", x, y)
|
||||
state := State2D{x, y, Right, board}
|
||||
for _, command := range commands {
|
||||
fmt.Println("x", state.x, "y", state.y, "direction", dirName[state.direction])
|
||||
fmt.Println("Running", command)
|
||||
switch command {
|
||||
case TurnRight:
|
||||
state.direction = turn(state.direction, TurnRight)
|
||||
case TurnLeft:
|
||||
state.direction = turn(state.direction, TurnLeft)
|
||||
default:
|
||||
step2d(int(command), &state)
|
||||
}
|
||||
}
|
||||
|
||||
finalRow := state.y + 1
|
||||
finalColumn := state.x + 1
|
||||
finalPassword := 1000*finalRow + 4*finalColumn + int(state.direction)
|
||||
fmt.Println("Final row is", finalRow, "final column is", finalColumn, "final password is", finalPassword)
|
||||
|
||||
narrowestSection := len(board[0])
|
||||
for _, row := range board {
|
||||
startIdx := -1
|
||||
endIdx := -1
|
||||
|
||||
for idx, tile := range row {
|
||||
if tile != Void && startIdx == -1 {
|
||||
startIdx = idx
|
||||
}
|
||||
|
||||
if tile == Void && (startIdx != -1) && (endIdx == -1) {
|
||||
endIdx = idx
|
||||
}
|
||||
}
|
||||
|
||||
if endIdx == -1 {
|
||||
endIdx = len(row) - 1
|
||||
}
|
||||
|
||||
if (endIdx - startIdx) < narrowestSection {
|
||||
narrowestSection = endIdx - startIdx
|
||||
}
|
||||
}
|
||||
|
||||
cubeFaceSize := narrowestSection
|
||||
fmt.Println("Cube face size is", cubeFaceSize)
|
||||
|
||||
faceMap := FaceMap{}
|
||||
currentFace := TopFace
|
||||
currentSpineX := startX
|
||||
currentSpineY := startY
|
||||
|
||||
for validPoint(currentSpineX, currentSpineY, board) {
|
||||
faceMap[currentFace] = copyFace(currentSpineX, currentSpineY, cubeFaceSize, board)
|
||||
fmt.Printf("Found %s face at (%d,%d)\n", faceName[currentFace], currentSpineX, currentSpineY)
|
||||
|
||||
leftwardsFace := faceMoves[currentFace][Left]
|
||||
for leftX := currentSpineX - cubeFaceSize; validPoint(leftX, currentSpineY, board) && board[currentSpineY][leftX] != Void; leftX -= cubeFaceSize {
|
||||
faceMap[leftwardsFace] = copyFace(leftX, currentSpineY, cubeFaceSize, board)
|
||||
fmt.Printf("Found %s face at (%d,%d)\n", faceName[leftwardsFace], leftX, currentSpineY)
|
||||
fmt.Println("Moving face", faceName[leftwardsFace], "left to", faceName[faceMoves[leftwardsFace][Left]])
|
||||
leftwardsFace = faceMoves[leftwardsFace][Left]
|
||||
}
|
||||
|
||||
rightwardsFace := faceMoves[currentFace][Right]
|
||||
for rightX := currentSpineX + cubeFaceSize; validPoint(rightX, currentSpineY, board) && board[currentSpineY][rightX] != Void; rightX += cubeFaceSize {
|
||||
faceMap[rightwardsFace] = copyFace(rightX, currentSpineY, cubeFaceSize, board)
|
||||
fmt.Printf("Found %s face at (%d,%d)\n", faceName[rightwardsFace], rightX, currentSpineY)
|
||||
fmt.Println("Moving face", faceName[rightwardsFace], "right to", faceName[faceMoves[rightwardsFace][Left]])
|
||||
rightwardsFace = faceMoves[rightwardsFace][Right]
|
||||
}
|
||||
|
||||
currentSpineY += cubeFaceSize
|
||||
fmt.Println("Moving face", faceName[currentFace], "down to", faceName[faceMoves[currentFace][Down]])
|
||||
currentFace = faceMoves[currentFace][Down]
|
||||
}
|
||||
|
||||
if len(faceMap) != 6 {
|
||||
fmt.Println("COULD NOT FIND ALL SIX FACES")
|
||||
return
|
||||
}
|
||||
|
||||
for face, board := range faceMap {
|
||||
fmt.Println("Board", faceName[face])
|
||||
printBoard(board)
|
||||
}
|
||||
|
||||
fmt.Println("initial position is", x, y)
|
||||
state3 := State3D{0, 0, TopFace, Right, faceMap}
|
||||
for _, command := range commands {
|
||||
fmt.Println("x", state3.x, "y", state3.y, "face", faceName[state3.face], "direction", dirName[state3.direction])
|
||||
fmt.Println("Running", command)
|
||||
switch command {
|
||||
case TurnRight:
|
||||
state3.direction = turn(state3.direction, TurnRight)
|
||||
case TurnLeft:
|
||||
state3.direction = turn(state3.direction, TurnLeft)
|
||||
default:
|
||||
step3d(int(command), &state3)
|
||||
}
|
||||
}
|
||||
|
||||
finalRow = state3.y + 1
|
||||
finalColumn = state3.x + 1
|
||||
finalPassword = 1000*finalRow + 4*finalColumn + int(state.direction)
|
||||
fmt.Println("Final row is", finalRow, "final column is", finalColumn, "final password is", finalPassword)
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user