mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-06-07 12:45:10 +02:00
2021 day23, its slow... but it works :) lots of small bugs that got hunted via tests
This commit is contained in:
@@ -3,3 +3,6 @@ input*.txt
|
|||||||
!scripts/skeleton/tmpls/input.txt
|
!scripts/skeleton/tmpls/input.txt
|
||||||
prompt.md
|
prompt.md
|
||||||
prompt.txt
|
prompt.txt
|
||||||
|
|
||||||
|
// ignore files that are prefixed with an underscore
|
||||||
|
_*
|
||||||
+85
-107
@@ -28,20 +28,40 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
fmt.Println("Running part", part)
|
fmt.Println("Running part", part)
|
||||||
|
|
||||||
if part == 1 {
|
ans := amphipodDay23(input, part)
|
||||||
ans := part1(input)
|
|
||||||
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
||||||
fmt.Println("Output:", ans)
|
fmt.Println("Output:", ans)
|
||||||
} else {
|
|
||||||
ans := part2(input)
|
|
||||||
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
|
||||||
fmt.Println("Output:", ans)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func part1(input string) int {
|
var roomCoordToWantCharPart1 = map[[2]int]string{
|
||||||
|
{2, 3}: "A", {3, 3}: "A",
|
||||||
|
{2, 5}: "B", {3, 5}: "B",
|
||||||
|
{2, 7}: "C", {3, 7}: "C",
|
||||||
|
{2, 9}: "D", {3, 9}: "D",
|
||||||
|
}
|
||||||
|
var roomCoordToWantCharPart2 = map[[2]int]string{
|
||||||
|
{2, 3}: "A", {3, 3}: "A", {4, 3}: "A", {5, 3}: "A",
|
||||||
|
{2, 5}: "B", {3, 5}: "B", {4, 5}: "B", {5, 5}: "B",
|
||||||
|
{2, 7}: "C", {3, 7}: "C", {4, 7}: "C", {5, 7}: "C",
|
||||||
|
{2, 9}: "D", {3, 9}: "D", {4, 9}: "D", {5, 9}: "D",
|
||||||
|
}
|
||||||
|
|
||||||
|
func amphipodDay23(input string, part int) int {
|
||||||
start := parseInput(input)
|
start := parseInput(input)
|
||||||
|
|
||||||
|
roomCoordToWantChar := roomCoordToWantCharPart1
|
||||||
|
if part == 2 {
|
||||||
|
roomCoordToWantChar = roomCoordToWantCharPart2
|
||||||
|
|
||||||
|
// update the grid with the 2 new rows, move old ones down
|
||||||
|
start.grid = append(start.grid, nil, nil)
|
||||||
|
start.grid[6] = start.grid[4]
|
||||||
|
start.grid[5] = start.grid[3]
|
||||||
|
|
||||||
|
start.grid[3] = strings.Split(" #D#C#B#A# ", "")
|
||||||
|
start.grid[4] = strings.Split(" #D#B#A#C# ", "")
|
||||||
|
}
|
||||||
|
|
||||||
minHeap := heap.NewMinHeap()
|
minHeap := heap.NewMinHeap()
|
||||||
|
|
||||||
minHeap.Add(start)
|
minHeap.Add(start)
|
||||||
@@ -53,22 +73,16 @@ func part1(input string) int {
|
|||||||
if seenGrids[key] {
|
if seenGrids[key] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Println(minHeap.Length(), "\n", front)
|
|
||||||
seenGrids[key] = true
|
seenGrids[key] = true
|
||||||
|
|
||||||
if front.allDone() {
|
if front.allDone(roomCoordToWantChar) {
|
||||||
return front.energyUsed
|
return front.energyUsed
|
||||||
}
|
}
|
||||||
|
|
||||||
unsettledCoords := front.getUnsettledCoords()
|
unsettledCoords := front.getUnsettledCoords(roomCoordToWantChar)
|
||||||
for _, unsettledCoord := range unsettledCoords {
|
for _, unsettledCoord := range unsettledCoords {
|
||||||
// // do not try to move the last one that was moved, otherwise it'll infinite loop
|
|
||||||
// if front.coordOfLastMoved == unsettledCoord {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
|
|
||||||
ur, uc := unsettledCoord[0], unsettledCoord[1]
|
ur, uc := unsettledCoord[0], unsettledCoord[1]
|
||||||
nextMoves := front.getNextPossibleMoves(unsettledCoord)
|
nextMoves := front.getNextPossibleMoves(unsettledCoord, roomCoordToWantChar)
|
||||||
for _, nextCoord := range nextMoves {
|
for _, nextCoord := range nextMoves {
|
||||||
nr, nc := nextCoord[0], nextCoord[1]
|
nr, nc := nextCoord[0], nextCoord[1]
|
||||||
if front.grid[nr][nc] != "." {
|
if front.grid[nr][nc] != "." {
|
||||||
@@ -80,7 +94,6 @@ func part1(input string) int {
|
|||||||
cp.energyUsed += calcEnergy(cp.grid[ur][uc], unsettledCoord, nextCoord)
|
cp.energyUsed += calcEnergy(cp.grid[ur][uc], unsettledCoord, nextCoord)
|
||||||
cp.path += fmt.Sprintf("%s%v->%v{%d},", front.grid[ur][uc], unsettledCoord, nextCoord, cp.energyUsed)
|
cp.path += fmt.Sprintf("%s%v->%v{%d},", front.grid[ur][uc], unsettledCoord, nextCoord, cp.energyUsed)
|
||||||
cp.grid[nr][nc], cp.grid[ur][uc] = cp.grid[ur][uc], cp.grid[nr][nc]
|
cp.grid[nr][nc], cp.grid[ur][uc] = cp.grid[ur][uc], cp.grid[nr][nc]
|
||||||
cp.coordOfLastMoved = nextCoord
|
|
||||||
|
|
||||||
// add it to the min heap
|
// add it to the min heap
|
||||||
minHeap.Add(cp)
|
minHeap.Add(cp)
|
||||||
@@ -88,23 +101,26 @@ func part1(input string) int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10901 TOO LOW
|
|
||||||
|
|
||||||
panic("should return from loop")
|
panic("should return from loop")
|
||||||
}
|
}
|
||||||
|
|
||||||
func part2(input string) int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type state struct {
|
type state struct {
|
||||||
grid [][]string
|
grid [][]string
|
||||||
coordOfLastMoved [2]int // store so you don't try to move the same one twice in a row
|
|
||||||
energyUsed int
|
energyUsed int
|
||||||
path string
|
path string // for debugging
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value is to implement the heap.Node interface so I can dump states into a Min Heap
|
func parseInput(input string) *state {
|
||||||
|
grid := [][]string{}
|
||||||
|
for _, line := range strings.Split(input, "\n") {
|
||||||
|
grid = append(grid, strings.Split(line, ""))
|
||||||
|
}
|
||||||
|
return &state{
|
||||||
|
grid: grid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value is to implement the heap.heapNode interface so I can dump states into a Min Heap
|
||||||
func (s *state) Value() int {
|
func (s *state) Value() int {
|
||||||
return s.energyUsed
|
return s.energyUsed
|
||||||
}
|
}
|
||||||
@@ -118,7 +134,7 @@ func (s *state) String() string {
|
|||||||
sb.WriteRune('\n')
|
sb.WriteRune('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.WriteString(fmt.Sprintf("nrg: %d, last_moved: %v, path: %s\n", s.energyUsed, s.coordOfLastMoved, s.path))
|
sb.WriteString(fmt.Sprintf("nrg: %d, ,path: %s\n", s.energyUsed, s.path))
|
||||||
|
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
@@ -127,7 +143,6 @@ func (s *state) String() string {
|
|||||||
func (s *state) copy() *state {
|
func (s *state) copy() *state {
|
||||||
cp := state{
|
cp := state{
|
||||||
grid: make([][]string, len(s.grid)),
|
grid: make([][]string, len(s.grid)),
|
||||||
coordOfLastMoved: s.coordOfLastMoved,
|
|
||||||
energyUsed: s.energyUsed,
|
energyUsed: s.energyUsed,
|
||||||
path: s.path,
|
path: s.path,
|
||||||
}
|
}
|
||||||
@@ -141,14 +156,7 @@ func (s *state) copy() *state {
|
|||||||
return &cp
|
return &cp
|
||||||
}
|
}
|
||||||
|
|
||||||
var roomCoordToWantChar = map[[2]int]string{
|
func (s *state) allDone(roomCoordToWantChar map[[2]int]string) bool {
|
||||||
{2, 3}: "A", {3, 3}: "A",
|
|
||||||
{2, 5}: "B", {3, 5}: "B",
|
|
||||||
{2, 7}: "C", {3, 7}: "C",
|
|
||||||
{2, 9}: "D", {3, 9}: "D",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) allDone() bool {
|
|
||||||
for coord, want := range roomCoordToWantChar {
|
for coord, want := range roomCoordToWantChar {
|
||||||
if s.grid[coord[0]][coord[1]] != want {
|
if s.grid[coord[0]][coord[1]] != want {
|
||||||
return false
|
return false
|
||||||
@@ -157,45 +165,36 @@ func (s *state) allDone() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state) getUnsettledCoords() [][2]int {
|
func (s *state) getUnsettledCoords(roomCoordToWantChar map[[2]int]string) [][2]int {
|
||||||
var unsettled [][2]int
|
var unsettled [][2]int
|
||||||
for r, row := range s.grid {
|
// check entire hallway
|
||||||
for c, v := range row {
|
for col := 1; col < len(s.grid[0]); col++ {
|
||||||
// iterate through the entire grid, for every letter "/[A-D]/"
|
if strings.Contains("ABCD", s.grid[1][col]) {
|
||||||
if strings.Contains("ABCD", v) {
|
unsettled = append(unsettled, [2]int{1, col})
|
||||||
// add it to the unsettled list
|
|
||||||
coord := [2]int{r, c}
|
|
||||||
// IF not in coords map
|
|
||||||
if want, ok := roomCoordToWantChar[coord]; !ok {
|
|
||||||
unsettled = append(unsettled, coord)
|
|
||||||
continue // these are all probably unnecessary but helpful for my brain
|
|
||||||
} else {
|
|
||||||
// IF in coords map but not matching the wantChar
|
|
||||||
if want != v {
|
|
||||||
unsettled = append(unsettled, coord)
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
// IF it matches wantChar but the cell below is
|
|
||||||
// ALSO in coords->want map AND it is the wrong want unsettledChar
|
|
||||||
// this means that it is in the right place but needs to get out
|
|
||||||
// of the way for a cell below
|
|
||||||
below := [2]int{r + 1, c}
|
|
||||||
wantBelow, ok := roomCoordToWantChar[below]
|
|
||||||
// ok means that it is a "room" cell, not wall
|
|
||||||
if ok && wantBelow != s.grid[r+1][c] {
|
|
||||||
unsettled = append(unsettled, coord)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, col := range []int{3, 5, 7, 9} {
|
||||||
|
roomFullFromBack := true
|
||||||
|
for row := len(s.grid) - 2; row >= 2; row-- {
|
||||||
|
coord := [2]int{row, col}
|
||||||
|
wantChar := roomCoordToWantChar[coord]
|
||||||
|
gotChar := s.grid[row][col]
|
||||||
|
if gotChar != "." {
|
||||||
|
if gotChar != wantChar {
|
||||||
|
roomFullFromBack = false
|
||||||
|
unsettled = append(unsettled, coord)
|
||||||
|
} else if gotChar == wantChar && !roomFullFromBack {
|
||||||
|
// need to get out of the way of someone in the wrong room
|
||||||
|
unsettled = append(unsettled, coord)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return unsettled
|
return unsettled
|
||||||
}
|
}
|
||||||
|
|
||||||
// cannot stop in front of a room
|
// cannot stop in front of a room, still applicable for part2
|
||||||
var coordsInFrontOfRooms = map[[2]int]bool{
|
var coordsInFrontOfRooms = map[[2]int]bool{
|
||||||
{1, 3}: true,
|
{1, 3}: true,
|
||||||
{1, 5}: true,
|
{1, 5}: true,
|
||||||
@@ -207,7 +206,7 @@ func isInHallway(coord [2]int) bool {
|
|||||||
return coord[0] == 1
|
return coord[0] == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state) getNextPossibleMoves(unsettledCoord [2]int) [][2]int {
|
func (s *state) getNextPossibleMoves(unsettledCoord [2]int, roomCoordToWantChar map[[2]int]string) [][2]int {
|
||||||
// get all the eligible locations for this coord to go to
|
// get all the eligible locations for this coord to go to
|
||||||
unsettledChar := s.grid[unsettledCoord[0]][unsettledCoord[1]]
|
unsettledChar := s.grid[unsettledCoord[0]][unsettledCoord[1]]
|
||||||
|
|
||||||
@@ -215,10 +214,10 @@ func (s *state) getNextPossibleMoves(unsettledCoord [2]int) [][2]int {
|
|||||||
panic("unexpected character to get next moves for " + unsettledChar)
|
panic("unexpected character to get next moves for " + unsettledChar)
|
||||||
}
|
}
|
||||||
|
|
||||||
startedInHallway := isInHallway(unsettledCoord)
|
|
||||||
// fmt.Println(unsettledChar, unsettledCoord, "in hallway", startedInHallway)
|
|
||||||
var possible [][2]int
|
var possible [][2]int
|
||||||
|
|
||||||
|
startedInHallway := isInHallway(unsettledCoord)
|
||||||
|
|
||||||
queue := [][2]int{unsettledCoord}
|
queue := [][2]int{unsettledCoord}
|
||||||
seen := map[[2]int]bool{}
|
seen := map[[2]int]bool{}
|
||||||
for len(queue) > 0 {
|
for len(queue) > 0 {
|
||||||
@@ -243,26 +242,26 @@ func (s *state) getNextPossibleMoves(unsettledCoord [2]int) [][2]int {
|
|||||||
} else if wantChar == unsettledChar {
|
} else if wantChar == unsettledChar {
|
||||||
// found the correct room
|
// found the correct room
|
||||||
// check if there is a deeper part of the room (aka lower)
|
// check if there is a deeper part of the room (aka lower)
|
||||||
maybeLower := [2]int{front[0] + 1, front[1]}
|
|
||||||
if _, ok := roomCoordToWantChar[maybeLower]; ok {
|
// if there is a "stuck" amphipod deeper in the room, cannot stop here
|
||||||
lowerChar := s.grid[maybeLower[0]][maybeLower[1]]
|
// if not deepest empty coord, cannot stop here
|
||||||
if lowerChar == "." {
|
// in both cases walking further is handles all cases, whether that's
|
||||||
possible = append(possible, maybeLower)
|
// to walk further in or out of the room
|
||||||
// can only go deeper into the room so just kill the traverse here
|
isStuckAmphipod := false
|
||||||
continue
|
roomHasDeeperOpenSpaces := false
|
||||||
|
for r := front[0] + 1; r < len(s.grid)-1; r++ {
|
||||||
|
char := s.grid[r][front[1]]
|
||||||
|
if char == "." {
|
||||||
|
roomHasDeeperOpenSpaces = true
|
||||||
}
|
}
|
||||||
// if lower char is the same, then can move into the front of the room
|
if char != "." && char != unsettledChar {
|
||||||
if lowerChar == unsettledChar {
|
isStuckAmphipod = true
|
||||||
possible = append(possible, front)
|
break
|
||||||
// no where else to go, so just continue and end this iteration
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// otherwise already deep part of the room, append it
|
|
||||||
// ?probably unreachable code
|
if !roomHasDeeperOpenSpaces && !isStuckAmphipod {
|
||||||
fmt.Println("unreachable code in else block?")
|
|
||||||
possible = append(possible, front)
|
possible = append(possible, front)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,24 +305,3 @@ func calcEnergy(char string, start, end [2]int) int {
|
|||||||
}
|
}
|
||||||
return energyPerType[char] * dist
|
return energyPerType[char] * dist
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseInput(input string) *state {
|
|
||||||
grid := [][]string{}
|
|
||||||
for _, line := range strings.Split(input, "\n") {
|
|
||||||
grid = append(grid, strings.Split(line, ""))
|
|
||||||
}
|
|
||||||
|
|
||||||
// // uncomment to check if coordToWantChars are correct...
|
|
||||||
// for c, unsettledChar := range roomCoordToWantChar {
|
|
||||||
// grid[c[0]][c[1]] = unsettledChar
|
|
||||||
// }
|
|
||||||
// st := state{grid: grid}
|
|
||||||
// fmt.Println(st.String())
|
|
||||||
// if !st.allDone() {
|
|
||||||
// panic("state.allDone() should be true ")
|
|
||||||
// }
|
|
||||||
|
|
||||||
return &state{
|
|
||||||
grid: grid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+138
-37
@@ -2,7 +2,9 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,96 +20,94 @@ var doneInput = `#############
|
|||||||
#A#B#C#D#
|
#A#B#C#D#
|
||||||
######### `
|
######### `
|
||||||
|
|
||||||
func Test_part1(t *testing.T) {
|
func Test_amphipodDay23(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
input string
|
input string
|
||||||
|
part int
|
||||||
want int
|
want int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "example",
|
name: "part1 example",
|
||||||
input: example,
|
input: example,
|
||||||
|
part: 1,
|
||||||
want: 12521,
|
want: 12521,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "simple",
|
name: "part1 simple",
|
||||||
input: `#############
|
input: `#############
|
||||||
#.A.........#
|
#.A.........#
|
||||||
###.#B#C#D###
|
###.#B#C#D###
|
||||||
#A#B#C#D#
|
#A#B#C#D#
|
||||||
######### `,
|
######### `,
|
||||||
|
part: 1,
|
||||||
want: 2,
|
want: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "simple: A then B",
|
name: "part1 simple: A then B",
|
||||||
input: `#############
|
input: `#############
|
||||||
#BA.........#
|
#BA.........#
|
||||||
###.#.#C#D###
|
###.#.#C#D###
|
||||||
#A#B#C#D#
|
#A#B#C#D#
|
||||||
######### `,
|
######### `,
|
||||||
|
part: 1,
|
||||||
want: 52,
|
want: 52,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// NOTE found a bug! A moving from a deep room to another room is calculating energy
|
// NOTE found a bug! A moving from a deep room to another room is calculating energy
|
||||||
// NOTE as if it is walking through the wall
|
// NOTE as if it is walking through the wall
|
||||||
name: "reversed B room", // B has to get out of A's way first
|
name: "part1 reversed B room", // B has to get out of A's way first
|
||||||
input: `#############
|
input: `#############
|
||||||
#.B.........#
|
#.B.........#
|
||||||
###.#B#C#D###
|
###.#B#C#D###
|
||||||
#A#A#C#D#
|
#A#A#C#D#
|
||||||
######### `,
|
######### `,
|
||||||
|
part: 1,
|
||||||
want: 95,
|
want: 95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "some shuffling",
|
name: "part1 some shuffling",
|
||||||
input: `#############
|
input: `#############
|
||||||
#...........#
|
#...........#
|
||||||
###A#B#C#D###
|
###A#B#C#D###
|
||||||
#A#C#B#D#
|
#A#C#B#D#
|
||||||
######### `,
|
######### `,
|
||||||
|
part: 1,
|
||||||
want: 1120,
|
want: 1120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "doneInput",
|
name: "part1 doneInput",
|
||||||
input: doneInput,
|
input: doneInput,
|
||||||
|
part: 1,
|
||||||
want: 0,
|
want: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "actual",
|
name: "part1 actual",
|
||||||
input: input,
|
input: input,
|
||||||
|
part: 1,
|
||||||
want: 15299,
|
want: 15299,
|
||||||
},
|
},
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := part1(tt.input); got != tt.want {
|
|
||||||
t.Errorf("part1() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_part2(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
want int
|
|
||||||
}{
|
|
||||||
{
|
{
|
||||||
name: "example",
|
name: "example part 2",
|
||||||
input: example,
|
input: example,
|
||||||
|
part: 2,
|
||||||
want: 44169,
|
want: 44169,
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// name: "actual",
|
name: "part2 actual",
|
||||||
// input: input,
|
input: input,
|
||||||
// want: 0,
|
part: 2,
|
||||||
// },
|
want: 47193,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if got := part2(tt.input); got != tt.want {
|
if testing.Short() && strings.Contains(tt.name, "actual") {
|
||||||
t.Errorf("part2() = %v, want %v", got, tt.want)
|
t.Skip(fmt.Sprintf("skipping %q in -short mode", tt.name))
|
||||||
|
}
|
||||||
|
if got := amphipodDay23(tt.input, tt.part); got != tt.want {
|
||||||
|
t.Errorf("amphipodDay23() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -117,11 +117,13 @@ func Test_state_getUnsettledCoords(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
input string
|
input string
|
||||||
|
roomCoordToWantChar map[[2]int]string
|
||||||
want [][2]int
|
want [][2]int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "already done",
|
name: "already done",
|
||||||
input: doneInput,
|
input: doneInput,
|
||||||
|
roomCoordToWantChar: roomCoordToWantCharPart1,
|
||||||
want: nil,
|
want: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -134,22 +136,41 @@ func Test_state_getUnsettledCoords(t *testing.T) {
|
|||||||
#A#D#C#A#
|
#A#D#C#A#
|
||||||
#########
|
#########
|
||||||
*/
|
*/
|
||||||
want: [][2]int{{2, 3}, {2, 5}, {2, 7}, {2, 9}, {3, 5}, {3, 9}},
|
roomCoordToWantChar: roomCoordToWantCharPart1,
|
||||||
|
want: [][2]int{{2, 3}, {3, 5}, {2, 5}, {2, 7}, {3, 9}, {2, 9}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "four unsettled coords",
|
name: "four unsettled coords",
|
||||||
input: ` #############
|
input: `#############
|
||||||
#AB.....D..D#
|
#AB.....D..D#
|
||||||
###.#.#C#.###
|
###.#.#C#.###
|
||||||
#A#B#C#.#
|
#A#B#C#.#
|
||||||
######### `,
|
######### `,
|
||||||
|
roomCoordToWantChar: roomCoordToWantCharPart1,
|
||||||
want: [][2]int{{1, 1}, {1, 2}, {1, 8}, {1, 11}},
|
want: [][2]int{{1, 1}, {1, 2}, {1, 8}, {1, 11}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "part2 test",
|
||||||
|
input: `#############
|
||||||
|
#AB.....D..D#
|
||||||
|
###.#.#C#.###
|
||||||
|
#A#A#C#.#
|
||||||
|
#B#B#C#D#
|
||||||
|
#A#B#C#D#
|
||||||
|
######### `,
|
||||||
|
roomCoordToWantChar: roomCoordToWantCharPart2,
|
||||||
|
want: [][2]int{
|
||||||
|
// hallway
|
||||||
|
{1, 1}, {1, 2}, {1, 8}, {1, 11},
|
||||||
|
{4, 3}, {3, 3}, // A room
|
||||||
|
{3, 5}, // B room
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
s := parseInput(tt.input)
|
s := parseInput(tt.input)
|
||||||
if got := s.getUnsettledCoords(); !reflect.DeepEqual(got, tt.want) {
|
if got := s.getUnsettledCoords(tt.roomCoordToWantChar); !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("state.getUnsettledCoords() = %v, want %v", got, tt.want)
|
t.Errorf("state.getUnsettledCoords() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -161,6 +182,7 @@ func Test_state_getNextPossibleMoves(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
input string
|
input string
|
||||||
unsettledCoord [2]int
|
unsettledCoord [2]int
|
||||||
|
roomCoordToWantChar map[[2]int]string
|
||||||
want [][2]int
|
want [][2]int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@@ -174,45 +196,115 @@ func Test_state_getNextPossibleMoves(t *testing.T) {
|
|||||||
#########
|
#########
|
||||||
*/
|
*/
|
||||||
unsettledCoord: [2]int{3, 3}, // DEEP room in A slot
|
unsettledCoord: [2]int{3, 3}, // DEEP room in A slot
|
||||||
|
roomCoordToWantChar: roomCoordToWantCharPart1,
|
||||||
want: nil,
|
want: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "example-frontA",
|
name: "example-frontA",
|
||||||
input: example,
|
input: example,
|
||||||
unsettledCoord: [2]int{2, 3}, // FRONT room in A slot
|
unsettledCoord: [2]int{2, 3}, // FRONT room in A slot
|
||||||
|
roomCoordToWantChar: roomCoordToWantCharPart1,
|
||||||
want: [][2]int{{1, 2}, {1, 4}, {1, 1}, {1, 6}, {1, 8}, {1, 10}, {1, 11}},
|
want: [][2]int{{1, 2}, {1, 4}, {1, 1}, {1, 6}, {1, 8}, {1, 10}, {1, 11}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "example-deepA-should be stuck",
|
name: "example-deepA-should be stuck",
|
||||||
input: example,
|
input: example,
|
||||||
unsettledCoord: [2]int{3, 3}, // FRONT room in A slot
|
unsettledCoord: [2]int{3, 3}, // FRONT room in A slot
|
||||||
|
roomCoordToWantChar: roomCoordToWantCharPart1,
|
||||||
want: nil,
|
want: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "hallways to rooms ONLY",
|
name: "hallways to rooms ONLY",
|
||||||
input: ` #############
|
input: `#############
|
||||||
#AB.....D..D#
|
#AB.....D..D#
|
||||||
###.#.#C#.###
|
###.#.#C#.###
|
||||||
#A#B#C#.#
|
#A#B#C#.#
|
||||||
######### `,
|
######### `,
|
||||||
unsettledCoord: [2]int{1, 2},
|
unsettledCoord: [2]int{1, 2},
|
||||||
|
roomCoordToWantChar: roomCoordToWantCharPart1,
|
||||||
want: [][2]int{{2, 5}},
|
want: [][2]int{{2, 5}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "hallway to DEEP room",
|
name: "hallway to DEEP room",
|
||||||
input: ` #############
|
input: `#############
|
||||||
#AB.....D..D#
|
#AB.....D..D#
|
||||||
###.#.#C#.###
|
###.#.#C#.###
|
||||||
#A#B#C#.#
|
#A#B#C#.#
|
||||||
######### `,
|
######### `,
|
||||||
unsettledCoord: [2]int{1, 8},
|
unsettledCoord: [2]int{1, 8},
|
||||||
|
roomCoordToWantChar: roomCoordToWantCharPart1,
|
||||||
want: [][2]int{{3, 9}},
|
want: [][2]int{{3, 9}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "part2 simple",
|
||||||
|
input: `#############
|
||||||
|
#B..........#
|
||||||
|
###A#.#C#D###
|
||||||
|
#A#B#C#D#
|
||||||
|
#A#B#C#D#
|
||||||
|
#A#B#C#D#
|
||||||
|
######### `,
|
||||||
|
unsettledCoord: [2]int{1, 1},
|
||||||
|
roomCoordToWantChar: roomCoordToWantCharPart2,
|
||||||
|
want: [][2]int{{2, 5}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "part2 back of room",
|
||||||
|
input: `#############
|
||||||
|
#B......B.BB#
|
||||||
|
###A#.#C#D###
|
||||||
|
#A#.#C#D#
|
||||||
|
#A#.#C#D#
|
||||||
|
#A#.#C#D#
|
||||||
|
######### `,
|
||||||
|
unsettledCoord: [2]int{1, 1},
|
||||||
|
roomCoordToWantChar: roomCoordToWantCharPart2,
|
||||||
|
want: [][2]int{{5, 5}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "part2 back of room",
|
||||||
|
input: `#############
|
||||||
|
#B......B..B#
|
||||||
|
###A#.#C#D###
|
||||||
|
#A#.#C#D#
|
||||||
|
#A#.#C#D#
|
||||||
|
#A#B#C#D#
|
||||||
|
######### `,
|
||||||
|
unsettledCoord: [2]int{1, 8},
|
||||||
|
roomCoordToWantChar: roomCoordToWantCharPart2,
|
||||||
|
want: [][2]int{{4, 5}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "part2 bug moving C from B room to C room",
|
||||||
|
input: `#############
|
||||||
|
#AA.....B.BD#
|
||||||
|
###B#.#.#.###
|
||||||
|
#D#C#.#.#
|
||||||
|
#D#B#C#C#
|
||||||
|
#A#D#C#A#
|
||||||
|
######### `,
|
||||||
|
unsettledCoord: [2]int{3, 5},
|
||||||
|
roomCoordToWantChar: roomCoordToWantCharPart2,
|
||||||
|
want: [][2]int{{1, 4}, {1, 6}, {3, 7}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "part2 bug moving B out of B room bc of a blocked D",
|
||||||
|
input: `#############
|
||||||
|
#AA.....B.BD#
|
||||||
|
###B#.#.#.###
|
||||||
|
#D#.#C#.#
|
||||||
|
#D#B#C#C#
|
||||||
|
#A#D#C#A#
|
||||||
|
######### `,
|
||||||
|
unsettledCoord: [2]int{4, 5},
|
||||||
|
roomCoordToWantChar: roomCoordToWantCharPart2,
|
||||||
|
want: [][2]int{{1, 4}, {1, 6}},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
s := parseInput(tt.input)
|
s := parseInput(tt.input)
|
||||||
if got := s.getNextPossibleMoves(tt.unsettledCoord); !reflect.DeepEqual(got, tt.want) {
|
if got := s.getNextPossibleMoves(tt.unsettledCoord, tt.roomCoordToWantChar); !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("state.getNextPossibleMoves() = %v, want %v", got, tt.want)
|
t.Errorf("state.getNextPossibleMoves() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -266,6 +358,15 @@ func Test_calcEnergy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
want: 600,
|
want: 600,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "part2 C",
|
||||||
|
args: args{
|
||||||
|
char: "C",
|
||||||
|
start: [2]int{5, 11},
|
||||||
|
end: [2]int{3, 7},
|
||||||
|
},
|
||||||
|
want: 1000,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user