Add day 17 part 2

master
Nick Krichevsky 2023-12-18 19:29:35 -05:00
parent 95f7c6203e
commit 0cf36dc66e
1 changed files with 39 additions and 18 deletions

View File

@ -18,13 +18,18 @@ const (
DirectionWest DirectionWest
) )
type Coordinate struct { type Location struct {
Row int Row int
Col int Col int
FromDirection Direction FromDirection Direction
NumMovesInDirection int NumMovesInDirection int
} }
type VisitedNode struct {
Coordinate Location
LeftInDirection Direction
}
type Heap[T comparable] struct { type Heap[T comparable] struct {
compare func(T, T) int compare func(T, T) int
data []T data []T
@ -137,31 +142,48 @@ func main() {
} }
fmt.Printf("Part 1: %d\n", part1(grid)) fmt.Printf("Part 1: %d\n", part1(grid))
fmt.Printf("Part 2: %d\n", part2(grid))
} }
func part1(grid [][]int) int { func part1(grid [][]int) int {
heuristic := func(pos Coordinate) int { return doAStar(grid, func(_startPos Location, _direction Direction, neighbor Location) bool {
return neighbor.NumMovesInDirection > 2
})
}
func part2(grid [][]int) int {
return doAStar(grid, func(startPos Location, direction Direction, neighbor Location) bool {
// < 3 because we are considering the node we started with, rather than are moving to.
// In other words, if we have moved fewer than three times on the node we started at, we can't change directions
return (startPos.FromDirection.Opposite() != direction && startPos.NumMovesInDirection < 3) || (neighbor.FromDirection.Opposite() == direction && neighbor.NumMovesInDirection > 9)
})
}
// This is a really messy A* implementation I mostly lifted from wikipedia and adapted
// After writing it, I learned of some clearer ways to write A*, but I stuck with this
func doAStar(grid [][]int, skipNeighbor func(Location, Direction, Location) bool) int {
heuristic := func(pos Location) int {
endRow := len(grid) endRow := len(grid)
endCol := len(grid[0]) endCol := len(grid[0])
return abs(endRow-pos.Row) + abs(endCol-pos.Col) return abs(endRow-pos.Row) + abs(endCol-pos.Col)
} }
startingLocation := Coordinate{ // Each location must also consider the direction it was approached from and how many steps lead up to it
startingLocation := Location{
Row: 0, Row: 0,
Col: 0, Col: 0,
FromDirection: DirectionWest, FromDirection: DirectionWest,
NumMovesInDirection: 0, NumMovesInDirection: 0,
} }
estimatedDistances := map[Coordinate]int{ estimatedDistances := map[Location]int{
startingLocation: heuristic(startingLocation), startingLocation: heuristic(startingLocation),
} }
shortestDistances := map[Coordinate]int{ shortestDistances := map[Location]int{
startingLocation: 0, startingLocation: 0,
} }
toVisit := NewHeap[Coordinate](func(c1, c2 Coordinate) int { toVisit := NewHeap[Location](func(c1, c2 Location) int {
estimatedC1Distance, haveC1Estimate := estimatedDistances[c1] estimatedC1Distance, haveC1Estimate := estimatedDistances[c1]
estimatedC2Distance, haveC2Estimate := estimatedDistances[c2] estimatedC2Distance, haveC2Estimate := estimatedDistances[c2]
if !haveC1Estimate && !haveC2Estimate { if !haveC1Estimate && !haveC2Estimate {
@ -177,7 +199,6 @@ func part1(grid [][]int) int {
}) })
toVisit.PushItem(startingLocation) toVisit.PushItem(startingLocation)
parents := make(map[Coordinate]VisitedNode)
firstVisit := true firstVisit := true
for toVisit.Len() > 0 { for toVisit.Len() > 0 {
@ -197,7 +218,7 @@ func part1(grid [][]int) int {
} else if searchPos.FromDirection == direction { } else if searchPos.FromDirection == direction {
// can't turn around // can't turn around
continue continue
} else if neighborPos.NumMovesInDirection > 2 { } else if skipNeighbor(searchPos, direction, neighborPos) {
// can't go straight too much // can't go straight too much
continue continue
} }
@ -221,17 +242,17 @@ func part1(grid [][]int) int {
panic("search failed to find an element") panic("search failed to find an element")
} }
func neighbors(coordinate Coordinate) map[Direction]Coordinate { func neighbors(loc Location) map[Direction]Location {
res := map[Direction]Coordinate{ res := map[Direction]Location{
DirectionNorth: {Row: coordinate.Row - 1, Col: coordinate.Col, FromDirection: DirectionSouth}, DirectionNorth: {Row: loc.Row - 1, Col: loc.Col, FromDirection: DirectionSouth},
DirectionSouth: {Row: coordinate.Row + 1, Col: coordinate.Col, FromDirection: DirectionNorth}, DirectionSouth: {Row: loc.Row + 1, Col: loc.Col, FromDirection: DirectionNorth},
DirectionWest: {Row: coordinate.Row, Col: coordinate.Col - 1, FromDirection: DirectionEast}, DirectionWest: {Row: loc.Row, Col: loc.Col - 1, FromDirection: DirectionEast},
DirectionEast: {Row: coordinate.Row, Col: coordinate.Col + 1, FromDirection: DirectionWest}, DirectionEast: {Row: loc.Row, Col: loc.Col + 1, FromDirection: DirectionWest},
} }
inLastDirection := res[coordinate.FromDirection.Opposite()] inLastDirection := res[loc.FromDirection.Opposite()]
inLastDirection.NumMovesInDirection = coordinate.NumMovesInDirection + 1 inLastDirection.NumMovesInDirection = loc.NumMovesInDirection + 1
res[coordinate.FromDirection.Opposite()] = inLastDirection res[loc.FromDirection.Opposite()] = inLastDirection
return res return res
} }