day17, example

This commit is contained in:
efim 2023-12-17 10:43:12 +00:00
parent 08c20ea6e0
commit 41e32d405b
1 changed files with 114 additions and 70 deletions

View File

@ -5,24 +5,42 @@ import (
"log" "log"
"math" "math"
"os" "os"
"slices"
"strings" "strings"
// "time" // "time"
) )
const ExampleResult string = ">>v>>>^>>>vv>>vv>vvv>vvv<vv>"
func Run() int { func Run() int {
fmt.Println("hello from day 17") fmt.Println("hello from day 17")
filename := "day17/example" filename := "day17/example"
field := NewField(filename) field := NewField(filename)
fmt.Printf("%+v\n", field) fmt.Printf("%+v\n", field)
field.RunDijkstra() startSegment := PathSegmentEnd{
endsAt: Coord{0, 0},
totalLength: 0,
lastSteps: make(map[Direction]int),
done: true,
lastDirection: Downward, // fake, caution
}
end := Coord{field.Height - 1, field.Width - 1} end := Coord{field.Height - 1, field.Width - 1}
lenToEnd := field.Paths[end].totalLength field.runSearch(startSegment, end)
pathsFoundToEnd := field.Paths[end]
fmt.Println("check visually:") fmt.Println("check visually:")
// fmt.Println(field.Paths[end].stringPathSoFar) // fmt.Println(field.Paths[end].stringPathSoFar)
fmt.Println(field.printLastDirection()) minimal := math.MaxInt
return lenToEnd for _, path := range pathsFoundToEnd {
fmt.Printf("%+v ; len is %d\n", path, path.totalLength)
if path.totalLength < minimal {
minimal = path.totalLength
}
}
// fmt.Printf("i'm looking for %s\n", ExampleResult)
return minimal
} }
// let's do dijkstra. it also needs a priority queue // let's do dijkstra. it also needs a priority queue
@ -116,6 +134,7 @@ func (d Direction) GetPerpendicular() (directions []Direction) {
} }
type PathSegmentEnd struct { type PathSegmentEnd struct {
endsAt Coord
totalLength int totalLength int
lastSteps map[Direction]int lastSteps map[Direction]int
lastDirection Direction lastDirection Direction
@ -123,6 +142,10 @@ type PathSegmentEnd struct {
done bool done bool
} }
func (p *PathSegmentEnd) IsExamplePathPrefix() bool {
return strings.HasPrefix(ExampleResult, p.stringPathSoFar)
}
func (p *PathSegmentEnd) NextDirections() (next []Direction) { func (p *PathSegmentEnd) NextDirections() (next []Direction) {
next = append(next, p.lastDirection.GetPerpendicular()...) next = append(next, p.lastDirection.GetPerpendicular()...)
@ -132,12 +155,40 @@ func (p *PathSegmentEnd) NextDirections() (next []Direction) {
next = append(next, p.lastDirection) next = append(next, p.lastDirection)
} }
log.Printf("getting directions from %+v they are %+v", p, next) // if p.IsExamplePathPrefix() {
// log.Printf("getting directions from %+v they are %+v", p, next)
// }
// log.Printf("getting directions from %+v they are %+v", p, next)
return return
} }
func (p *PathSegmentEnd) isDominating(other PathSegmentEnd) bool {
if p.endsAt != other.endsAt {
panic(fmt.Sprintf("comparing domination of paths on different ells: %+v %+v",
p, other))
}
var thisDirection Direction
var thisSteps int
for thisDirection, thisSteps = range p.lastSteps {
break
}
var otherDirection Direction
var otherSteps int
for otherDirection, otherSteps = range other.lastSteps {
break
}
sameDirection := thisDirection == otherDirection
// if other has less steps, then current total length is not important
// other can still be more efficient in the future
lessOrSameLastSteps := thisSteps <= otherSteps
return sameDirection && lessOrSameLastSteps && p.totalLength < other.totalLength
}
type Field struct { type Field struct {
Paths map[Coord]*PathSegmentEnd Paths map[Coord][]*PathSegmentEnd
Costs [][]int Costs [][]int
Height, Width int Height, Width int
Start Coord Start Coord
@ -146,13 +197,14 @@ type Field struct {
func NewField(filename string) Field { func NewField(filename string) Field {
enterCosts := ReadEnterCosts(filename) enterCosts := ReadEnterCosts(filename)
startSegment := PathSegmentEnd{ startSegment := PathSegmentEnd{
endsAt: Coord{0, 0},
totalLength: 0, totalLength: 0,
lastSteps: make(map[Direction]int), lastSteps: make(map[Direction]int),
done: true, done: true,
lastDirection: Downward, // fake, need to init direct neighbors also lastDirection: Downward, // fake, caution
} }
initialPaths := make(map[Coord]*PathSegmentEnd) initialPaths := make(map[Coord][]*PathSegmentEnd)
initialPaths[Coord{0, 0}] = &startSegment initialPaths[Coord{0, 0}] = []*PathSegmentEnd{&startSegment}
return Field{ return Field{
Paths: initialPaths, Paths: initialPaths,
@ -168,9 +220,8 @@ func (f *Field) isValid(c Coord) bool {
} }
// presupposes that direction is valid // presupposes that direction is valid
func (f *Field) continuePathInDirection(from Coord, d Direction) (result PathSegmentEnd) { func (f *Field) continuePathInDirection(curPath PathSegmentEnd, d Direction, finish Coord) (result PathSegmentEnd) {
curPath := f.Paths[from] nextCoord := curPath.endsAt.applyDirection(d)
nextCoord := from.applyDirection(d)
moveCost := f.Costs[nextCoord.Row][nextCoord.Col] moveCost := f.Costs[nextCoord.Row][nextCoord.Col]
newCost := curPath.totalLength + moveCost newCost := curPath.totalLength + moveCost
lastSteps := make(map[Direction]int) lastSteps := make(map[Direction]int)
@ -183,73 +234,66 @@ func (f *Field) continuePathInDirection(from Coord, d Direction) (result PathSeg
} }
return PathSegmentEnd{ return PathSegmentEnd{
endsAt: nextCoord,
totalLength: newCost, totalLength: newCost,
lastDirection: d, lastDirection: d,
lastSteps: lastSteps, lastSteps: lastSteps,
stringPathSoFar: curPath.stringPathSoFar + d.AsSymbol(), stringPathSoFar: curPath.stringPathSoFar + d.AsSymbol(),
done: nextCoord == finish,
} }
} }
func (f *Field) RunDijkstra() { func (f *Field) runSearch(curPath PathSegmentEnd, finish Coord) {
checking := make(map[Coord]any, 0)
checking[f.Start] = struct{}{} // if len(curPath.stringPathSoFar) > f.Height+f.Width {
// if curPath.IsExamplePathPrefix() {
for len(checking) > 0 { // log.Printf(">> CUTOFF %+v\n", curPath)
var currentCoord Coord // }
for key := range checking { // return
currentCoord = key // }
break // if start is also finish : this path is done
} // record the length, for the coord?
currentPath := f.Paths[currentCoord] // get directions,
directions := currentPath.NextDirections() // if no directions : this path is done
// get neighbords,
for _, direction := range directions { // for each neighbor calc what whould be path if we went to the neighbor
log.Printf("from %+v will examine in direction %s", currentCoord, direction) // if neighbor already known path which DOMINATES current - do not continue
neighborCoord := currentCoord.applyDirection(direction) //
if !f.isValid(neighborCoord) { // in the end for each vertice there will be map[direction][]PathSegmentEnd
continue // prevent going off the grid current := curPath.endsAt
} if current == finish {
neighborPathSoFar, found := f.Paths[neighborCoord] if curPath.IsExamplePathPrefix() {
if !found { log.Printf(">> reached end with %+v\n", curPath)
neighborPathSoFar = &PathSegmentEnd{
totalLength: math.MaxInt,
}
f.Paths[neighborCoord] = neighborPathSoFar
}
// log.Printf("about to check done on neighbor %+v\n", neighborPathSoFar)
if neighborPathSoFar.done {
continue // already found optimal
}
pathIfWeGoFromCurrent := f.continuePathInDirection(currentCoord, direction)
if pathIfWeGoFromCurrent.totalLength < neighborPathSoFar.totalLength {
f.Paths[neighborCoord] = &pathIfWeGoFromCurrent
}
checking[neighborCoord] = struct{}{}
}
f.Paths[currentCoord].done = true
delete(checking, currentCoord)
// fmt.Print(f.printLastDirection())
// time.Sleep(time.Second)
}
}
func (f *Field) printLastDirection() (result string) {
result += "\n"
for rowNum := 0; rowNum < f.Height; rowNum++ {
for colNum := 0; colNum < f.Width; colNum++ {
path, found := f.Paths[Coord{rowNum, colNum}]
if !found {
result += "."
} else {
result += path.lastDirection.AsSymbol()
}
}
result += "\n"
} }
return return
} }
directions := curPath.NextDirections()
if len(directions) == 0 {
return
}
checkingNeighbors:
for _, d := range directions {
nextCoord := curPath.endsAt.applyDirection(d)
if !f.isValid(nextCoord) {
continue
}
ponentialPath := f.continuePathInDirection(curPath, d, finish)
knownPathsToNeighbor := f.Paths[ponentialPath.endsAt]
for _, knownPath := range knownPathsToNeighbor {
if knownPath.isDominating(ponentialPath) {
continue checkingNeighbors
}
// if our potential path is not dominated, then save as potential path
// and would be nice to remove all known paths which are dominated by this potential
}
filteredKnownPaths := slices.DeleteFunc(knownPathsToNeighbor, func(previous *PathSegmentEnd) bool {
return ponentialPath.isDominating(*previous)
})
filteredKnownPaths = append(filteredKnownPaths, &ponentialPath)
f.Paths[ponentialPath.endsAt] = filteredKnownPaths
f.runSearch(ponentialPath, finish)
}
}