day24
This commit is contained in:
6
inputs/example.24
Normal file
6
inputs/example.24
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#.######
|
||||||
|
#>>.<^<#
|
||||||
|
#.<..<<#
|
||||||
|
#>v.><>#
|
||||||
|
#<^v^^>#
|
||||||
|
######.#
|
||||||
27
inputs/input.24
Normal file
27
inputs/input.24
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#.########################################################################################################################
|
||||||
|
#<^>^<^^<v><vv^v^<^vv>^<^v>vv^v<<v<>^>^v<>v<<<.^<>^>v^><v<^...>>>>.^>>>v<.^^^^^>.v.v<v.><^<v<<v<>.<v.v<><vv.v>v>v<<<<<.<<#
|
||||||
|
#<>^^^v<.v<<v.v.vv.v^v>v<>.>vv>^>vv>^^><<<v>><v>><>.v.v>><><<>>vv>^^v<v^v><^<.><vv<.v<v>^^<v>^>^>^<v<>.<^.^<.>^v<<<<v^^.>#
|
||||||
|
#<v..^^>><<vvv><v^><>.>^^^><<>^><>>>^v<>><>^<v<<>>>v^v>.^v<v^>><vvv^<<.><v<.v<^>.v<..>^^^>><^v<v<v>v><>>>vvv^<<^v^>v.^v^<#
|
||||||
|
#<.^vv<^<.>>>>v<v^vv^v.v.v>>>^v<<<v^^<v<^<>.v<><v.v^<<^><><v^<<v.>><v><vv>^.<^<^v.v>>vv<>>vv^.^.><vv<^^^^<v^v<v>vv.v.><v>#
|
||||||
|
#<><^>v.^<v^<vv>vvv>>^v<v..vv.><>^>^v<<v.<v^...vv>>><^<^<^>v^^^><v<^<v<^.^<.^v><<v<.<>^<>v^.<>>.><^<^><><vv>vv^vv>><<><.<#
|
||||||
|
#.^^^^^^v^v.<<><>vv<>.>^v<v>^>.<>v.^^<><>^<>^v^^.<v<vv>>v<^<^><v<>.<v>^<^vvv<>^<^v^^.>.^.><<>v>v^>^^>vv^v>v.^vv<v^>>^^v><#
|
||||||
|
#<<^<<v>^<v^>>>v><^.^<.<>^.><v>vv<<.>v<^^v>vv>>>^<<>^<v<v<^<^.^^<><<<<^>v^>vv>^vv^v<.>v^^vv^<v^<v><>><v^<>^v<v><vv>^.<^v<#
|
||||||
|
#<<>.v^<v>^<>>v><<><vvvvv^><<<<>^<.<<>><^<v>>^<<^.v<<.>>><v<^.v.v<<v>>^><^v<>><^>>v>>>.^>>v^<^.<v>vv..v^^vvv^vv<>^<v<<>.>#
|
||||||
|
#><.v.^><vv.>>>>><.>.vv^<<>v><^<<v.<><>.v<v><<.>>v>>v<^.^^^<^^<v>^^<<>>><^>><^>^<.<v^v<v^><v<.<^^.>>^^<v.v<><v<>^^<.^<>v>#
|
||||||
|
#<<>v<^<.<<.>vv<.><>.vvvv<^.<<>><<v>>^^>v>^v>v<^^v.<^v<>vv^<><<>vv<v<^^<<><v>^<vv.^<>v<>.><vv>>vv^.>.>v>v>^v>^.><v<.v><^>#
|
||||||
|
#>.>>vv>><<>^v^^v>^<<.^^>>><v<v>>><<^^^<^vv<>v<v^>.<^<^<>v<^><>v.v^.>>v^^^^>.v.><v^^^vv<^>v^.^>v<>v.^^.^^v^v<v>>.^><<.^<>#
|
||||||
|
#<>v.^>>>>^^^>v<^<>v>^>v>vv^^>v.v<>>.>^><<>v^v.<v>^..v.>vv.^>^^v<v^^v>vvvvv>>^<^v.<>><<.v^>vv>>>vv><^.><.<^^^v^>^v<>v<v^>#
|
||||||
|
#<^<v^.>>.^<^^v^v.<v<<vv^.^.><vv^vv<>v^>.<.<<^<vv^>><<^^v^v^<v^vv.^^^>vv^<>v^>v^vv<>v.^v.<>^>vv>.^<^.v.>v>v>>v<v<^v>><v<<#
|
||||||
|
#>.<<>^^vvv>>v^^>^<^>.<<.v^>vv>>v<vv<<^v.<.v^^^.^>v>>.>>.^<<^v>.vvv><<<>^.^v>vvv^^^^<vv.><v<>v<<<><^^<v<.><<^><<>><><v^<<#
|
||||||
|
#<<<<..^v>v^>v^>^.<<^<>>v.vv>>>^^v^.vv^>><<<<.<<>^>^v>><>.>><>^><v^<v<^^v^>^>^^>>v>^>v.<v>>^<<^><<v>^v^<>v<>^^>>v<vv^vv>.#
|
||||||
|
#..^>^v.>.<^>^>.v>>.<<..<<>v^vvv<<vvv<.<>v><.<^^>^<^^<^>><>^<.vv<<<>^^v^><><^<^vv<<.<<v^^v^<<vv^<.v>^>v.v<^^<>>>.>^^v<^v>#
|
||||||
|
#<.>vv<.<>^<<.>.v<<><vvv^<<v>><<.>.vv^^<>vv^v..^<>>.^<>^>^^<^^^<v^>>>><><>^^.vv<<>v>^.v.^^<<^v<>.^>>^^>v>v^^^>v^>^>><v>><#
|
||||||
|
#<>v>vv<<<><<v><<v>v>>^.<vvvv<^^<^<>^<<^>>^^>^v.<vv^vv<>v^<>^^<>>^v.>.<.>>>.<<^><.<^^>^>>.><^>.^>v<^>^>v>.^^v>.v^>>>>>v>>#
|
||||||
|
#<.<<^>><<^>.v>v^^.v^>v<^<vv^>>>^.>>>^>^v<^v<v>.>^^<<<>v.^.v^v.^>.<v<<v^^^<<><>^><<<<>v^^><v^<>..>v.><^^.<<<^.>^<>>>vv><>#
|
||||||
|
#>^v.>vvv^^.^<>^><vv>^>v^<>.^.v<v>.^v<<v^^>^v><^v<>^>>v.^<^><<>^>>>><^^<v<<><<>^^><<<^vvv>><.<v>^>>v^^<^><>.<v^>>..^^v>v>#
|
||||||
|
#.<^^><<<^^^v<>^.>v>v^><v<>v^^.v^.^<<<^^<vvv^<^^v.<vvv<>v.v^<^>.<>><<><^v^v.^^<>^v^vvvv><<^^><<>vv<v^v^.^v<<<^v<<<^<v>v^>#
|
||||||
|
#.<^..v^^^>>^v^^.><v><>v^.>v^^<<><^^<.<>>v^>.<>>.^>vv>v>^v.<^^^<<^<vv>.^>^.<<<.^^>.^^^.v<<>v<.vv^^<>v>>>><>^>^^.<<^^<>><<#
|
||||||
|
#<^<^^>>^.<v>^^^>v><^><.v>>v^>vv>v.>^>^<^^^^v<<<^>.>v^<v.<v^^^^>v^..<<<<>^<<<>^<v^^>^vv^v>.<^v<><>^<<>^<>>v>><<>^>vvvv<>>#
|
||||||
|
#>>^.^^<v<<vvv^^<<vv>>^^<<>^^vv>>><v>^>v<v>v.><><^^v<<<.<vv>v<^^>><^v>^<>^<v<vv<v^>v<^.<<>^v<.^<.v.><.<^>^vvvvvv^v^^<^.<>#
|
||||||
|
#<><<^<.<^>^^^<v>.^v>>vv.vv<v..<^vv<>.^vv^.^<vv<>^vvv<v^>v.<v<v<.^>.<.v^>^>>>vv<>^>.>>^^^<<^v<^.vv>>v.v<v><<<^^^v^vvv..^>#
|
||||||
|
########################################################################################################################.#
|
||||||
3
main.go
3
main.go
@@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/acw/advent2022/solutions/day21"
|
"github.com/acw/advent2022/solutions/day21"
|
||||||
"github.com/acw/advent2022/solutions/day22"
|
"github.com/acw/advent2022/solutions/day22"
|
||||||
"github.com/acw/advent2022/solutions/day23"
|
"github.com/acw/advent2022/solutions/day23"
|
||||||
|
"github.com/acw/advent2022/solutions/day24"
|
||||||
"github.com/acw/advent2022/solutions/day3"
|
"github.com/acw/advent2022/solutions/day3"
|
||||||
"github.com/acw/advent2022/solutions/day4"
|
"github.com/acw/advent2022/solutions/day4"
|
||||||
"github.com/acw/advent2022/solutions/day5"
|
"github.com/acw/advent2022/solutions/day5"
|
||||||
@@ -82,6 +83,8 @@ func main() {
|
|||||||
day22.Run(os.Args[2])
|
day22.Run(os.Args[2])
|
||||||
case "day23":
|
case "day23":
|
||||||
day23.Run(os.Args[2])
|
day23.Run(os.Args[2])
|
||||||
|
case "day24":
|
||||||
|
day24.Run(os.Args[2])
|
||||||
default:
|
default:
|
||||||
fmt.Println("PANIC! Unknown day.")
|
fmt.Println("PANIC! Unknown day.")
|
||||||
}
|
}
|
||||||
|
|||||||
313
solutions/day24/day24.go
Normal file
313
solutions/day24/day24.go
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
package day24
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Direction int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Up Direction = iota
|
||||||
|
Down
|
||||||
|
Left
|
||||||
|
Right
|
||||||
|
Stay
|
||||||
|
)
|
||||||
|
|
||||||
|
type Point struct {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Directions []Direction
|
||||||
|
|
||||||
|
func listContains(dirs Directions, x Direction) bool {
|
||||||
|
for _, v := range dirs {
|
||||||
|
if x == v {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func sameBoard(board1 map[Point]Directions, board2 map[Point]Directions) bool {
|
||||||
|
if len(board1) != len(board2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for point, vals1 := range board1 {
|
||||||
|
vals2 := board2[point]
|
||||||
|
|
||||||
|
if len(vals1) != len(vals2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range vals1 {
|
||||||
|
if !listContains(vals2, v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type Map struct {
|
||||||
|
minute int
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
elf Point
|
||||||
|
entrance Point
|
||||||
|
target Point
|
||||||
|
}
|
||||||
|
|
||||||
|
var boardStates []map[Point]Directions
|
||||||
|
|
||||||
|
func printState(state Map) {
|
||||||
|
board := boardStates[state.minute%len(boardStates)]
|
||||||
|
|
||||||
|
for y := 0; y < state.height; y++ {
|
||||||
|
for x := 0; x < state.width; x++ {
|
||||||
|
if x == state.elf.x && y == state.elf.y {
|
||||||
|
fmt.Printf("E")
|
||||||
|
} else if x == state.target.x && y == state.target.y {
|
||||||
|
fmt.Printf(".")
|
||||||
|
} else if x == state.entrance.x && y == state.entrance.y {
|
||||||
|
fmt.Printf(".")
|
||||||
|
} else if y == 0 {
|
||||||
|
fmt.Printf("#")
|
||||||
|
} else if y == state.height-1 {
|
||||||
|
fmt.Printf("#")
|
||||||
|
} else if x == 0 || x == state.width-1 {
|
||||||
|
fmt.Printf("#")
|
||||||
|
} else {
|
||||||
|
dir, exists := board[Point{x, y}]
|
||||||
|
if exists && len(dir) == 1 {
|
||||||
|
switch dir[0] {
|
||||||
|
case Left:
|
||||||
|
fmt.Printf("<")
|
||||||
|
case Right:
|
||||||
|
fmt.Printf(">")
|
||||||
|
case Up:
|
||||||
|
fmt.Printf("^")
|
||||||
|
case Down:
|
||||||
|
fmt.Printf("v")
|
||||||
|
default:
|
||||||
|
fmt.Printf("?")
|
||||||
|
}
|
||||||
|
} else if exists {
|
||||||
|
fmt.Printf("%d", len(dir))
|
||||||
|
} else {
|
||||||
|
fmt.Printf(".")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func okMove(state Map) bool {
|
||||||
|
if state.elf.x == state.target.x && state.elf.y == state.target.y {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.elf.x <= 0 || state.elf.y <= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.elf.x == state.width-1 || state.elf.y == state.height-1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
board := boardStates[state.minute%len(boardStates)]
|
||||||
|
_, exists := board[Point{state.elf.x, state.elf.y}]
|
||||||
|
return !exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextBoard(width int, height int, board map[Point]Directions) map[Point]Directions {
|
||||||
|
newBoard := map[Point]Directions{}
|
||||||
|
|
||||||
|
// start with the snow move
|
||||||
|
for point, dirs := range board {
|
||||||
|
for _, dir := range dirs {
|
||||||
|
switch dir {
|
||||||
|
case Left:
|
||||||
|
newX := point.x - 1
|
||||||
|
if newX == 0 {
|
||||||
|
newX = width - 2
|
||||||
|
}
|
||||||
|
newPoint := Point{newX, point.y}
|
||||||
|
newBoard[newPoint] = append(newBoard[newPoint], Left)
|
||||||
|
|
||||||
|
case Right:
|
||||||
|
newX := point.x + 1
|
||||||
|
if newX == width-1 {
|
||||||
|
newX = 1
|
||||||
|
}
|
||||||
|
newPoint := Point{newX, point.y}
|
||||||
|
newBoard[newPoint] = append(newBoard[newPoint], Right)
|
||||||
|
|
||||||
|
case Up:
|
||||||
|
newY := point.y - 1
|
||||||
|
if newY == 0 {
|
||||||
|
newY = height - 2
|
||||||
|
}
|
||||||
|
newPoint := Point{point.x, newY}
|
||||||
|
newBoard[newPoint] = append(newBoard[newPoint], Up)
|
||||||
|
|
||||||
|
case Down:
|
||||||
|
newY := point.y + 1
|
||||||
|
if newY == height-1 {
|
||||||
|
newY = 1
|
||||||
|
}
|
||||||
|
newPoint := Point{point.x, newY}
|
||||||
|
newBoard[newPoint] = append(newBoard[newPoint], Down)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newBoard
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextStates(state Map) []Map {
|
||||||
|
retval := []Map{}
|
||||||
|
newMinute := state.minute + 1
|
||||||
|
|
||||||
|
stay := Map{newMinute, state.width, state.height, state.elf, state.entrance, state.target}
|
||||||
|
if okMove(stay) {
|
||||||
|
retval = append(retval, stay)
|
||||||
|
}
|
||||||
|
|
||||||
|
left := Map{newMinute, state.width, state.height, Point{state.elf.x - 1, state.elf.y}, state.entrance, state.target}
|
||||||
|
if okMove(left) {
|
||||||
|
retval = append(retval, left)
|
||||||
|
}
|
||||||
|
|
||||||
|
right := Map{newMinute, state.width, state.height, Point{state.elf.x + 1, state.elf.y}, state.entrance, state.target}
|
||||||
|
if okMove(right) {
|
||||||
|
retval = append(retval, right)
|
||||||
|
}
|
||||||
|
|
||||||
|
up := Map{newMinute, state.width, state.height, Point{state.elf.x, state.elf.y - 1}, state.entrance, state.target}
|
||||||
|
if okMove(up) {
|
||||||
|
retval = append(retval, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
down := Map{newMinute, state.width, state.height, Point{state.elf.x, state.elf.y + 1}, state.entrance, state.target}
|
||||||
|
if okMove(down) {
|
||||||
|
retval = append(retval, down)
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval
|
||||||
|
}
|
||||||
|
|
||||||
|
func search(initialState Map) int {
|
||||||
|
queue := []Map{initialState}
|
||||||
|
minute := -1
|
||||||
|
|
||||||
|
for len(queue) > 0 {
|
||||||
|
workState := queue[0]
|
||||||
|
|
||||||
|
if workState.minute > minute {
|
||||||
|
minute = workState.minute
|
||||||
|
filteredStates := []Map{}
|
||||||
|
pointsFound := map[Point]bool{}
|
||||||
|
|
||||||
|
for _, state := range queue {
|
||||||
|
if state.minute != minute {
|
||||||
|
fmt.Println("PANIC: WEIRD")
|
||||||
|
}
|
||||||
|
_, exists := pointsFound[state.elf]
|
||||||
|
if !exists {
|
||||||
|
filteredStates = append(filteredStates, state)
|
||||||
|
pointsFound[state.elf] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queue = filteredStates
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if workState.elf.x == workState.target.x && workState.elf.y == workState.target.y {
|
||||||
|
return workState.minute
|
||||||
|
}
|
||||||
|
|
||||||
|
queue = append(queue[1:], nextStates(workState)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
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 entrance, exit Point
|
||||||
|
board := map[Point]Directions{}
|
||||||
|
width := 0
|
||||||
|
y := 0
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := []rune(scanner.Text())
|
||||||
|
|
||||||
|
if line[3] == '#' {
|
||||||
|
for x, r := range line {
|
||||||
|
if r == '.' {
|
||||||
|
if y == 0 {
|
||||||
|
entrance = Point{x, 0}
|
||||||
|
} else {
|
||||||
|
exit = Point{x, y}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for x, r := range line {
|
||||||
|
switch r {
|
||||||
|
case '>':
|
||||||
|
board[Point{x, y}] = Directions{Right}
|
||||||
|
case '<':
|
||||||
|
board[Point{x, y}] = Directions{Left}
|
||||||
|
case '^':
|
||||||
|
board[Point{x, y}] = Directions{Up}
|
||||||
|
case 'v':
|
||||||
|
board[Point{x, y}] = Directions{Down}
|
||||||
|
case '.', '#':
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
fmt.Println("PANIC: Unexpected character", string(r))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if width == 0 {
|
||||||
|
width = len(line)
|
||||||
|
}
|
||||||
|
y += 1
|
||||||
|
}
|
||||||
|
file.Close()
|
||||||
|
|
||||||
|
boardStates = []map[Point]Directions{board}
|
||||||
|
for {
|
||||||
|
nextBoard := nextBoard(width, y, boardStates[len(boardStates)-1])
|
||||||
|
if sameBoard(nextBoard, boardStates[0]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
boardStates = append(boardStates, nextBoard)
|
||||||
|
}
|
||||||
|
fmt.Println("Pre-computed", len(boardStates), "boards.")
|
||||||
|
|
||||||
|
initialState := Map{0, width, y, entrance, entrance, exit}
|
||||||
|
printState(initialState)
|
||||||
|
foundExit := search(initialState)
|
||||||
|
fmt.Println("Found exit after", foundExit, "minutes")
|
||||||
|
backToEntrance := search(Map{foundExit, initialState.width, initialState.height, initialState.target, initialState.entrance, initialState.entrance})
|
||||||
|
fmt.Println("Made it back to the exit at", backToEntrance, "minutes")
|
||||||
|
backToExit := search(Map{backToEntrance, initialState.width, initialState.height, initialState.elf, initialState.entrance, initialState.target})
|
||||||
|
fmt.Println("And then back to the exit at", backToExit, "minutes")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user