423 lines
8.0 KiB
Go
423 lines
8.0 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
|
|
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
|
)
|
|
|
|
func main() {
|
|
inp := h.StdinToStringSlice()
|
|
//part1(inp)
|
|
part2(inp)
|
|
}
|
|
func part1(inp []string) {
|
|
board, inst := inp[:len(inp)-2], inp[len(inp)-1]
|
|
b := BuildBoard(board)
|
|
var count int
|
|
var turn byte
|
|
fmt.Println(b)
|
|
fmt.Println(b.pos)
|
|
for len(inst) > 0 {
|
|
count, turn, inst = getNextInstructions(inst)
|
|
b.Move(count)
|
|
if turn == 'L' || turn == 'R' {
|
|
b.Turn(turn)
|
|
}
|
|
}
|
|
fmt.Println("Password:", b.Password())
|
|
}
|
|
|
|
func part2(inp []string) {
|
|
board, inst := inp[:len(inp)-2], inp[len(inp)-1]
|
|
b := BuildCubeBoard(board)
|
|
var count int
|
|
var turn byte
|
|
fmt.Println(b)
|
|
fmt.Println(b.pos)
|
|
for len(inst) > 0 {
|
|
count, turn, inst = getNextInstructions(inst)
|
|
b.Move(count)
|
|
if turn == 'L' || turn == 'R' {
|
|
b.Turn(turn)
|
|
}
|
|
}
|
|
fmt.Println("Password:", b.Password())
|
|
}
|
|
|
|
func getNextInstructions(inst string) (int, byte, string) {
|
|
for i := range inst {
|
|
if inst[i] == 'L' || inst[i] == 'R' {
|
|
return h.Atoi(inst[:i]), inst[i], inst[i+1:]
|
|
}
|
|
}
|
|
return h.Atoi(inst), 0, ""
|
|
}
|
|
|
|
const (
|
|
dirR = iota
|
|
dirD
|
|
dirL
|
|
dirU
|
|
dirErr
|
|
|
|
sideTop = iota
|
|
sideBottom
|
|
sideRight
|
|
sideLeft
|
|
sideFront
|
|
sideBack
|
|
)
|
|
|
|
type CubeBoard struct {
|
|
Top *CubeBoardSide
|
|
Bottom *CubeBoardSide
|
|
Left *CubeBoardSide
|
|
Right *CubeBoardSide
|
|
Front *CubeBoardSide
|
|
Back *CubeBoardSide
|
|
|
|
// Pos now holds:
|
|
// y, x, dir, side
|
|
pos []int
|
|
}
|
|
|
|
// For the purposes of directions, we treat the board like this:
|
|
// [T]
|
|
// [B][L][F][R]
|
|
// [B]
|
|
// So up from left, front, back, and right are all top
|
|
// Up from bottom is front
|
|
// Down from bottom is back
|
|
// Up from top is back
|
|
// Down from top is front
|
|
// So you can continually go left on the main level and circumnavigate the cube
|
|
// Continually going up from anywhere will eventually get you bouncing between top and back
|
|
// Continually going down from anywhere will eventually get you bouncing between bottom and back
|
|
func BuildCubeBoard(inp []string) *CubeBoard {
|
|
cb := CubeBoard{}
|
|
var side string
|
|
for i := 0; i < len(inp); i++ {
|
|
if len(inp) == 0 {
|
|
break
|
|
}
|
|
side = inp[i]
|
|
i++
|
|
cbs := BuildCubeBoardSide(inp[i : i+len(inp[i])])
|
|
switch side {
|
|
case "Top":
|
|
cb.Top = cbs
|
|
case "Bottom":
|
|
cb.Bottom = cbs
|
|
case "Left":
|
|
cb.Left = cbs
|
|
case "Right":
|
|
cb.Right = cbs
|
|
case "Front":
|
|
cb.Front = cbs
|
|
case "Back":
|
|
cb.Back = cbs
|
|
}
|
|
i = i + cbs.Size() - 1
|
|
}
|
|
cb.pos = []int{0, 0, dirR, sideTop}
|
|
return &cb
|
|
}
|
|
func (cb *CubeBoard) Size() int { return cb.Top.Size() }
|
|
func (cb *CubeBoard) Move(count int) {
|
|
|
|
}
|
|
func (cb *CubeBoard) GetPosUp() []int {
|
|
y := cb.pos[0]
|
|
x := cb.pos[1]
|
|
dir := cb.pos[2]
|
|
side := cb.pos[3]
|
|
if y == 0 {
|
|
// Going to another side
|
|
switch side {
|
|
case sideTop:
|
|
side = sideBack
|
|
case sideBottom:
|
|
side = sideFront
|
|
case sideFront:
|
|
side = sideTop
|
|
case sideBack:
|
|
side = sideTop
|
|
case sideLeft:
|
|
side = sideTop
|
|
case sideRight:
|
|
side = sideTop
|
|
}
|
|
}
|
|
return []int{y, x, dir, side}
|
|
}
|
|
func (cb *CubeBoard) GetPosDown() []int {
|
|
y := cb.pos[0]
|
|
x := cb.pos[1]
|
|
dir := cb.pos[2]
|
|
side := cb.pos[3]
|
|
if y == cb.Size()-1 {
|
|
// Going to another side
|
|
switch side {
|
|
case sideTop:
|
|
side = sideFront
|
|
case sideBottom:
|
|
side = sideBack
|
|
case sideFront:
|
|
side = sideBottom
|
|
case sideBack:
|
|
side = sideBottom
|
|
case sideLeft:
|
|
side = sideBottom
|
|
case sideRight:
|
|
side = sideBottom
|
|
}
|
|
} else {
|
|
y = y + 1
|
|
}
|
|
return []int{y, x, dir, side}
|
|
}
|
|
func (cb *CubeBoard) GetPosRight() []int {
|
|
y := cb.pos[0]
|
|
x := cb.pos[1]
|
|
dir := cb.pos[2]
|
|
side := cb.pos[3]
|
|
if x == cb.Size()-1 {
|
|
// Going to another side
|
|
switch side {
|
|
case sideTop:
|
|
side = sideRight
|
|
case sideBottom:
|
|
side = sideRight
|
|
case sideFront:
|
|
side = sideRight
|
|
case sideBack:
|
|
side = sideLeft
|
|
case sideLeft:
|
|
side = sideFront
|
|
case sideRight:
|
|
side = sideBack
|
|
}
|
|
} else {
|
|
y = y + 1
|
|
}
|
|
return []int{y, x, dir, side}
|
|
}
|
|
|
|
// Get the current pos' side
|
|
func (cb *CubeBoard) Side() *CubeBoardSide {
|
|
switch cb.pos[3] {
|
|
case sideTop:
|
|
return cb.Top
|
|
case sideBottom:
|
|
return cb.Bottom
|
|
case sideFront:
|
|
return cb.Front
|
|
case sideBack:
|
|
return cb.Back
|
|
case sideLeft:
|
|
return cb.Left
|
|
case sideRight:
|
|
return cb.Right
|
|
}
|
|
return nil
|
|
}
|
|
func (cb *CubeBoard) Turn(dir byte) {
|
|
if dir == 'R' {
|
|
cb.pos[2] = (cb.pos[2] + 1) % dirErr
|
|
} else if dir == 'L' {
|
|
cb.pos[2] = (cb.pos[2] - 1 + dirErr) % dirErr
|
|
}
|
|
}
|
|
func (cb *CubeBoard) Tile(y, x, side int) byte {
|
|
switch side {
|
|
case sideTop:
|
|
return cb.Top.Tile(y, x)
|
|
case sideBottom:
|
|
return cb.Bottom.Tile(y, x)
|
|
case sideLeft:
|
|
return cb.Left.Tile(y, x)
|
|
case sideRight:
|
|
return cb.Right.Tile(y, x)
|
|
case sideFront:
|
|
return cb.Front.Tile(y, x)
|
|
case sideBack:
|
|
return cb.Back.Tile(y, x)
|
|
}
|
|
return 0
|
|
}
|
|
func (cb *CubeBoard) Password() int {
|
|
return 0
|
|
}
|
|
|
|
type CubeBoardSide struct {
|
|
spots [][]byte
|
|
}
|
|
|
|
func BuildCubeBoardSide(inp []string) *CubeBoardSide {
|
|
cbs := CubeBoardSide{}
|
|
fmt.Println("Building CBS from:", inp)
|
|
cbs.spots = make([][]byte, len(inp))
|
|
for y := range inp {
|
|
row := make([]byte, len(inp[y]))
|
|
for x := range inp[y] {
|
|
row[x] = inp[y][x]
|
|
}
|
|
cbs.spots[y] = row
|
|
}
|
|
return &cbs
|
|
}
|
|
func (cbs *CubeBoardSide) Size() int {
|
|
return len(cbs.spots)
|
|
}
|
|
func (cbs *CubeBoardSide) Tile(y, x int) byte {
|
|
if len(cbs.spots) < y && len(cbs.spots[y]) < x {
|
|
return cbs.spots[y][x]
|
|
}
|
|
return 0
|
|
}
|
|
|
|
type Board struct {
|
|
spots [][]byte
|
|
// pos holds
|
|
// y, x, dir
|
|
pos []int
|
|
}
|
|
|
|
func BuildBoard(inp []string) *Board {
|
|
maxX := math.MinInt
|
|
for y := range inp {
|
|
maxX = h.Max(maxX, len(inp[y]))
|
|
}
|
|
b := Board{
|
|
spots: make([][]byte, len(inp)),
|
|
}
|
|
for y := range inp {
|
|
sl := make([]byte, maxX)
|
|
for x := range inp[y] {
|
|
sl[x] = inp[y][x]
|
|
}
|
|
b.spots[y] = sl
|
|
}
|
|
|
|
b.pos = make([]int, 3)
|
|
for x := range b.spots[0] {
|
|
if b.spots[0][x] == '.' {
|
|
b.pos = []int{0, x, dirR}
|
|
break
|
|
}
|
|
}
|
|
return &b
|
|
}
|
|
func (b *Board) Password() int {
|
|
return ((b.pos[0] + 1) * 1000) + ((b.pos[1] + 1) * 4) + b.pos[2]
|
|
}
|
|
func (b *Board) Tile(y, x int) byte {
|
|
if len(b.spots) > y && len(b.spots[y]) > x {
|
|
return b.spots[y][x]
|
|
}
|
|
return 0
|
|
}
|
|
func (b *Board) GetNextPos() []int {
|
|
switch b.pos[2] {
|
|
case dirU:
|
|
return b.GetPosUp()
|
|
case dirR:
|
|
return b.GetPosRight()
|
|
case dirD:
|
|
return b.GetPosDown()
|
|
case dirL:
|
|
return b.GetPosLeft()
|
|
}
|
|
return []int{-1, -1}
|
|
}
|
|
func (b *Board) GetPosUp() []int {
|
|
x := b.pos[1]
|
|
y := ((b.pos[0] - 1) + len(b.spots)) % len(b.spots)
|
|
for (b.spots[y][x] == 0 || b.spots[y][x] == 32) && y != b.pos[0] {
|
|
y = ((y - 1) + len(b.spots)) % len(b.spots)
|
|
}
|
|
return []int{y, x}
|
|
}
|
|
func (b *Board) GetPosRight() []int {
|
|
y := b.pos[0]
|
|
x := (b.pos[1] + 1) % len(b.spots[y])
|
|
for (b.spots[y][x] == 0 || b.spots[y][x] == 32) && x != b.pos[1] {
|
|
x = (x + 1) % len(b.spots[y])
|
|
}
|
|
return []int{y, x}
|
|
}
|
|
func (b *Board) GetPosDown() []int {
|
|
x := b.pos[1]
|
|
y := (b.pos[0] + 1) % len(b.spots)
|
|
for (b.spots[y][x] == 0 || b.spots[y][x] == 32) && y != b.pos[0] {
|
|
y = (y + 1) % len(b.spots)
|
|
}
|
|
return []int{y, x}
|
|
}
|
|
func (b *Board) GetPosLeft() []int {
|
|
y := b.pos[0]
|
|
x := ((b.pos[1] - 1) + len(b.spots[y])) % len(b.spots[y])
|
|
for (b.spots[y][x] == 0 || b.spots[y][x] == 32) && x != b.pos[1] {
|
|
x = ((x - 1) + len(b.spots[y])) % len(b.spots[y])
|
|
}
|
|
return []int{y, x}
|
|
}
|
|
|
|
func (b *Board) Move(count int) {
|
|
var to []int
|
|
for count > 0 {
|
|
switch b.pos[2] {
|
|
case dirU:
|
|
to = b.GetPosUp()
|
|
case dirR:
|
|
to = b.GetPosRight()
|
|
case dirD:
|
|
to = b.GetPosDown()
|
|
case dirL:
|
|
to = b.GetPosLeft()
|
|
}
|
|
if b.Tile(to[0], to[1]) == '.' {
|
|
count--
|
|
b.pos[0], b.pos[1] = to[0], to[1]
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *Board) Turn(dir byte) {
|
|
if dir == 'R' {
|
|
b.pos[2] = (b.pos[2] + 1) % dirErr
|
|
} else if dir == 'L' {
|
|
b.pos[2] = (b.pos[2] - 1 + dirErr) % dirErr
|
|
}
|
|
}
|
|
|
|
func (b Board) String() string {
|
|
var ret string
|
|
for y := range b.spots {
|
|
for x := range b.spots[y] {
|
|
bt := b.spots[y][x]
|
|
if b.pos[1] == x && b.pos[0] == y {
|
|
switch b.pos[2] {
|
|
case dirU:
|
|
bt = '^'
|
|
case dirR:
|
|
bt = '>'
|
|
case dirD:
|
|
bt = 'v'
|
|
case dirL:
|
|
bt = '<'
|
|
default:
|
|
bt = '!'
|
|
}
|
|
}
|
|
ret = ret + fmt.Sprint(string(bt))
|
|
}
|
|
ret = ret + "\n"
|
|
}
|
|
return ret
|
|
}
|