day17, example
This commit is contained in:
parent
08c20ea6e0
commit
41e32d405b
|
@ -5,24 +5,42 @@ import (
|
|||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
// "time"
|
||||
)
|
||||
|
||||
const ExampleResult string = ">>v>>>^>>>vv>>vv>vvv>vvv<vv>"
|
||||
|
||||
func Run() int {
|
||||
fmt.Println("hello from day 17")
|
||||
filename := "day17/example"
|
||||
field := NewField(filename)
|
||||
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}
|
||||
lenToEnd := field.Paths[end].totalLength
|
||||
field.runSearch(startSegment, end)
|
||||
|
||||
pathsFoundToEnd := field.Paths[end]
|
||||
fmt.Println("check visually:")
|
||||
// fmt.Println(field.Paths[end].stringPathSoFar)
|
||||
fmt.Println(field.printLastDirection())
|
||||
return lenToEnd
|
||||
minimal := math.MaxInt
|
||||
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
|
||||
|
@ -116,6 +134,7 @@ func (d Direction) GetPerpendicular() (directions []Direction) {
|
|||
}
|
||||
|
||||
type PathSegmentEnd struct {
|
||||
endsAt Coord
|
||||
totalLength int
|
||||
lastSteps map[Direction]int
|
||||
lastDirection Direction
|
||||
|
@ -123,6 +142,10 @@ type PathSegmentEnd struct {
|
|||
done bool
|
||||
}
|
||||
|
||||
func (p *PathSegmentEnd) IsExamplePathPrefix() bool {
|
||||
return strings.HasPrefix(ExampleResult, p.stringPathSoFar)
|
||||
}
|
||||
|
||||
func (p *PathSegmentEnd) NextDirections() (next []Direction) {
|
||||
next = append(next, p.lastDirection.GetPerpendicular()...)
|
||||
|
||||
|
@ -132,12 +155,40 @@ func (p *PathSegmentEnd) NextDirections() (next []Direction) {
|
|||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
Paths map[Coord]*PathSegmentEnd
|
||||
Paths map[Coord][]*PathSegmentEnd
|
||||
Costs [][]int
|
||||
Height, Width int
|
||||
Start Coord
|
||||
|
@ -146,13 +197,14 @@ type Field struct {
|
|||
func NewField(filename string) Field {
|
||||
enterCosts := ReadEnterCosts(filename)
|
||||
startSegment := PathSegmentEnd{
|
||||
endsAt: Coord{0, 0},
|
||||
totalLength: 0,
|
||||
lastSteps: make(map[Direction]int),
|
||||
done: true,
|
||||
lastDirection: Downward, // fake, need to init direct neighbors also
|
||||
lastDirection: Downward, // fake, caution
|
||||
}
|
||||
initialPaths := make(map[Coord]*PathSegmentEnd)
|
||||
initialPaths[Coord{0, 0}] = &startSegment
|
||||
initialPaths := make(map[Coord][]*PathSegmentEnd)
|
||||
initialPaths[Coord{0, 0}] = []*PathSegmentEnd{&startSegment}
|
||||
|
||||
return Field{
|
||||
Paths: initialPaths,
|
||||
|
@ -168,9 +220,8 @@ func (f *Field) isValid(c Coord) bool {
|
|||
}
|
||||
|
||||
// presupposes that direction is valid
|
||||
func (f *Field) continuePathInDirection(from Coord, d Direction) (result PathSegmentEnd) {
|
||||
curPath := f.Paths[from]
|
||||
nextCoord := from.applyDirection(d)
|
||||
func (f *Field) continuePathInDirection(curPath PathSegmentEnd, d Direction, finish Coord) (result PathSegmentEnd) {
|
||||
nextCoord := curPath.endsAt.applyDirection(d)
|
||||
moveCost := f.Costs[nextCoord.Row][nextCoord.Col]
|
||||
newCost := curPath.totalLength + moveCost
|
||||
lastSteps := make(map[Direction]int)
|
||||
|
@ -183,73 +234,66 @@ func (f *Field) continuePathInDirection(from Coord, d Direction) (result PathSeg
|
|||
}
|
||||
|
||||
return PathSegmentEnd{
|
||||
endsAt: nextCoord,
|
||||
totalLength: newCost,
|
||||
lastDirection: d,
|
||||
lastSteps: lastSteps,
|
||||
stringPathSoFar: curPath.stringPathSoFar + d.AsSymbol(),
|
||||
done: nextCoord == finish,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Field) RunDijkstra() {
|
||||
checking := make(map[Coord]any, 0)
|
||||
func (f *Field) runSearch(curPath PathSegmentEnd, finish Coord) {
|
||||
|
||||
checking[f.Start] = struct{}{}
|
||||
|
||||
for len(checking) > 0 {
|
||||
var currentCoord Coord
|
||||
for key := range checking {
|
||||
currentCoord = key
|
||||
break
|
||||
}
|
||||
currentPath := f.Paths[currentCoord]
|
||||
directions := currentPath.NextDirections()
|
||||
|
||||
for _, direction := range directions {
|
||||
log.Printf("from %+v will examine in direction %s", currentCoord, direction)
|
||||
neighborCoord := currentCoord.applyDirection(direction)
|
||||
if !f.isValid(neighborCoord) {
|
||||
continue // prevent going off the grid
|
||||
}
|
||||
neighborPathSoFar, found := f.Paths[neighborCoord]
|
||||
if !found {
|
||||
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"
|
||||
// if len(curPath.stringPathSoFar) > f.Height+f.Width {
|
||||
// if curPath.IsExamplePathPrefix() {
|
||||
// log.Printf(">> CUTOFF %+v\n", curPath)
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
// if start is also finish : this path is done
|
||||
// record the length, for the coord?
|
||||
// get directions,
|
||||
// if no directions : this path is done
|
||||
// get neighbords,
|
||||
// for each neighbor calc what whould be path if we went to the neighbor
|
||||
// if neighbor already known path which DOMINATES current - do not continue
|
||||
//
|
||||
// in the end for each vertice there will be map[direction][]PathSegmentEnd
|
||||
current := curPath.endsAt
|
||||
if current == finish {
|
||||
if curPath.IsExamplePathPrefix() {
|
||||
log.Printf(">> reached end with %+v\n", curPath)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue