day17, dijkstra doesn't work
right? just because if found a shorter path, doesn't mean i don't need to check other directions. i have, because maybe they are not blocked and will provide better solution. so, yikes
This commit is contained in:
parent
ed4abd2d7e
commit
08c20ea6e0
|
@ -0,0 +1,255 @@
|
|||
package day17
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
// "time"
|
||||
)
|
||||
|
||||
func Run() int {
|
||||
fmt.Println("hello from day 17")
|
||||
filename := "day17/example"
|
||||
field := NewField(filename)
|
||||
fmt.Printf("%+v\n", field)
|
||||
|
||||
field.RunDijkstra()
|
||||
|
||||
end := Coord{field.Height - 1, field.Width - 1}
|
||||
lenToEnd := field.Paths[end].totalLength
|
||||
fmt.Println("check visually:")
|
||||
// fmt.Println(field.Paths[end].stringPathSoFar)
|
||||
fmt.Println(field.printLastDirection())
|
||||
return lenToEnd
|
||||
}
|
||||
|
||||
// let's do dijkstra. it also needs a priority queue
|
||||
|
||||
// priority queue would be over vertice. and would have to have enough information to
|
||||
// calc the distance from neighbors.
|
||||
// how to check condition of max 3 in one row?
|
||||
// with each vertice store [horizontal:n|vertical:n] and if it's 3 just dont consider?
|
||||
|
||||
// so in iteration, i have some vertice, with horizontal:2 for example,
|
||||
// i check all neighbors, if path through 'this' is shorter, set that as path,
|
||||
// but also mark the path with len of straight.
|
||||
//
|
||||
// so priority queue is with 'path to next'
|
||||
// or rather 'path to i,j'
|
||||
// then check for neighbors (non finished), calc distance to them through this
|
||||
// checking neighbors via 'path get directions' 'path get geighbors from directions'
|
||||
// if shorter - update
|
||||
// mark current as 'finished'
|
||||
// so, i'll be checking cost to enter directly from this table,
|
||||
// but check path len
|
||||
|
||||
func ReadEnterCosts(filename string) [][]int {
|
||||
bytes, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
panic(fmt.Sprint("error reading file ", filename))
|
||||
}
|
||||
text := strings.TrimSpace(string(bytes))
|
||||
result := make([][]int, 0)
|
||||
for _, line := range strings.Split(text, "\n") {
|
||||
numbers := make([]int, 0)
|
||||
for _, digit := range line {
|
||||
num := int(digit - '0')
|
||||
numbers = append(numbers, num)
|
||||
}
|
||||
result = append(result, numbers)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type Coord struct {
|
||||
Row, Col int
|
||||
}
|
||||
|
||||
func (c Coord) applyDirection(d Direction) (result Coord) {
|
||||
result = c
|
||||
switch d {
|
||||
case Upward:
|
||||
result.Row -= 1
|
||||
case Downward:
|
||||
result.Row += 1
|
||||
case Leftward:
|
||||
result.Col -= 1
|
||||
case Rightward:
|
||||
result.Col += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type Direction int
|
||||
|
||||
const (
|
||||
Upward Direction = iota
|
||||
Downward
|
||||
Leftward
|
||||
Rightward
|
||||
)
|
||||
|
||||
func (d Direction) String() string {
|
||||
strings := []string{"Up", "Down", "Left", "Right"}
|
||||
return strings[d]
|
||||
}
|
||||
|
||||
func (d Direction) AsSymbol() string {
|
||||
strings := []string{"^", "v", "<", ">"}
|
||||
return strings[d]
|
||||
}
|
||||
|
||||
func (d Direction) GetPerpendicular() (directions []Direction) {
|
||||
switch d {
|
||||
case Upward:
|
||||
directions = []Direction{Leftward, Rightward}
|
||||
case Downward:
|
||||
directions = []Direction{Leftward, Rightward}
|
||||
case Leftward:
|
||||
directions = []Direction{Upward, Downward}
|
||||
case Rightward:
|
||||
directions = []Direction{Upward, Downward}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type PathSegmentEnd struct {
|
||||
totalLength int
|
||||
lastSteps map[Direction]int
|
||||
lastDirection Direction
|
||||
stringPathSoFar string
|
||||
done bool
|
||||
}
|
||||
|
||||
func (p *PathSegmentEnd) NextDirections() (next []Direction) {
|
||||
next = append(next, p.lastDirection.GetPerpendicular()...)
|
||||
|
||||
// last steps of 2 is max allowed 3 tiles in row
|
||||
lastSteps := p.lastSteps[p.lastDirection]
|
||||
if lastSteps < 3 {
|
||||
next = append(next, p.lastDirection)
|
||||
}
|
||||
|
||||
log.Printf("getting directions from %+v they are %+v", p, next)
|
||||
return
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
Paths map[Coord]*PathSegmentEnd
|
||||
Costs [][]int
|
||||
Height, Width int
|
||||
Start Coord
|
||||
}
|
||||
|
||||
func NewField(filename string) Field {
|
||||
enterCosts := ReadEnterCosts(filename)
|
||||
startSegment := PathSegmentEnd{
|
||||
totalLength: 0,
|
||||
lastSteps: make(map[Direction]int),
|
||||
done: true,
|
||||
lastDirection: Downward, // fake, need to init direct neighbors also
|
||||
}
|
||||
initialPaths := make(map[Coord]*PathSegmentEnd)
|
||||
initialPaths[Coord{0, 0}] = &startSegment
|
||||
|
||||
return Field{
|
||||
Paths: initialPaths,
|
||||
Costs: enterCosts,
|
||||
Height: len(enterCosts),
|
||||
Width: len(enterCosts[0]),
|
||||
Start: Coord{0, 0},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Field) isValid(c Coord) bool {
|
||||
return c.Col >= 0 && c.Row >= 0 && c.Row < f.Height && c.Col < f.Width
|
||||
}
|
||||
|
||||
// presupposes that direction is valid
|
||||
func (f *Field) continuePathInDirection(from Coord, d Direction) (result PathSegmentEnd) {
|
||||
curPath := f.Paths[from]
|
||||
nextCoord := from.applyDirection(d)
|
||||
moveCost := f.Costs[nextCoord.Row][nextCoord.Col]
|
||||
newCost := curPath.totalLength + moveCost
|
||||
lastSteps := make(map[Direction]int)
|
||||
|
||||
curPathStepsIntoThisDirection, found := curPath.lastSteps[d]
|
||||
if !found {
|
||||
lastSteps[d] = 1
|
||||
} else {
|
||||
lastSteps[d] = curPathStepsIntoThisDirection + 1
|
||||
}
|
||||
|
||||
return PathSegmentEnd{
|
||||
totalLength: newCost,
|
||||
lastDirection: d,
|
||||
lastSteps: lastSteps,
|
||||
stringPathSoFar: curPath.stringPathSoFar + d.AsSymbol(),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Field) RunDijkstra() {
|
||||
checking := make(map[Coord]any, 0)
|
||||
|
||||
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"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
2413432311323
|
||||
3215453535623
|
||||
3255245654254
|
||||
3446585845452
|
||||
4546657867536
|
||||
1438598798454
|
||||
4457876987766
|
||||
3637877979653
|
||||
4654967986887
|
||||
4564679986453
|
||||
1224686865563
|
||||
2546548887735
|
||||
4322674655533
|
6
main.go
6
main.go
|
@ -3,12 +3,12 @@ package main
|
|||
import (
|
||||
"log"
|
||||
|
||||
"sunshine.industries/aoc2023/day16"
|
||||
"sunshine.industries/aoc2023/day17"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Print("> starting run:")
|
||||
|
||||
result := day16.Run()
|
||||
log.Printf("\n\nday16 result: %d\n****\n", result)
|
||||
result := day17.Run()
|
||||
log.Printf("\n\nday17 result: %d\n****\n", result)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue