package main import ( "errors" "fmt" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) func main() { inp := h.StdinToStringSlice() part1(inp) // fmt.Println() // part2(inp) } var ( N = h.Coordinate{X: 0, Y: -1} E = h.Coordinate{X: 1, Y: 0} S = h.Coordinate{X: 0, Y: 1} W = h.Coordinate{X: -1, Y: 0} ) var ( scoreTrack map[h.Coordinate]int bestCostToEnd map[h.Coordinate]int ) func part1(inpS []string) { inp := h.StringSliceToCoordByteMap(inpS) scoreTrack = make(map[h.Coordinate]int) bestCostToEnd = make(map[h.Coordinate]int) start, _ := inp.FindFirst('S') end, _ := inp.FindFirst('E') inp.Put(start, '.') bestCostToEnd[end] = 0 cd := CD{c: start, d: E} solve(inp, cd, end, 0, []h.Coordinate{cd.c}) best, ok := scoreTrack[end] if !ok { panic(errors.New("no path to end found")) } fmt.Println("Best Score:", best) bestSeats := make(map[h.Coordinate]byte) for i := range bestPaths { for j := range bestPaths[i] { bestSeats[bestPaths[i][j]] = byte('0' + i) } } printBestSeats(inp, bestSeats) fmt.Println("Seats:", len(bestSeats)) } var bestPaths [][]h.Coordinate func solve(inp h.CoordByteMap, start CD, end h.Coordinate, score int, currPath []h.Coordinate) int { // tryMove takes the cd that we're moving to and the cost to move there // and returns the cost to get from that cd to the end tryMove := func(c CD, cost int) int { if inp.ContainsCoord(c.c) && inp.Get(c.c) != '#' { // printPathMap(inp, c, end, currPath) // poi := h.Coordinate{X: 3, Y: 10} // time.Sleep(time.Second / 20) nextScore := score + cost v, ok := bestCostToEnd[c.c] //if c.c.Equals(poi) { // fmt.Println("Current Scoretrack[poi] =", v) // fmt.Println("Next Score =", nextScore) // time.Sleep(time.Second * 5) //} if !ok || v >= nextScore { nxtPath := make([]h.Coordinate, len(currPath)) copy(nxtPath, currPath) nxtPath = append(nxtPath, c.c) // Check end conditions if c.c.Equals(end) { if !ok || v > nextScore { // New Best fmt.Println("New Best Path") scoreTrack[end] = nextScore bestPaths = [][]h.Coordinate{nxtPath} } else if v == nextScore { // Another Best Path fmt.Println("Adding path to best paths") bestPaths = append(bestPaths, nxtPath) } return cost } // Not the end, but keep going scoreTrack[c.c] = nextScore solve(inp, c, end, nextScore, nxtPath) } } return h.MAX_INT } // Test forward cost := tryMove(start.move(), 1) // Test CW if wrk := tryMove(start.turnCW().move(), 1001); wrk < cost { cost = wrk } // Test CCW if wrk := tryMove(start.turnCCW().move(), 1001); wrk < cost { cost = wrk } bestCostToEnd[start.c] = cost return cost } func printPathMap(inp h.CoordByteMap, loc CD, end h.Coordinate, path []h.Coordinate) { fmt.Print(h.CLEAR_SCREEN) wrk := inp.Copy() wrk.ReplaceAll('.', ' ') for i := range path { wrk.Put(path[i], 'o') } wrk.Put(loc.c, loc.Byte()) fmt.Println(wrk) if v, ok := scoreTrack[end]; ok { fmt.Printf("Best Score: %v (Paths: %d)\n", v, len(bestPaths)) } else { fmt.Println("Waiting on best score...") } } func printBestSeats(inp h.CoordByteMap, seats map[h.Coordinate]byte) { fmt.Print(h.CLEAR_SCREEN) wrk := inp.Copy() wrk.ReplaceAll('.', ' ') for k, v := range seats { wrk.Put(k, byte(0+v)) } fmt.Println(wrk) } /* func part2(inpS []string) { inp := h.StringSliceToCoordByteMap(inpS) scoreTrack = make(map[h.Coordinate]int) start, _ := inp.FindFirst('S') end, _ := inp.FindFirst('E') inp.Put(start, '.') cd := CD{c: start, d: E} lookForSeats(inp, cd, end, 0, []h.Coordinate{cd.c}) _, ok := scoreTrack[end] if !ok { panic(errors.New("no path to end found")) } bestSeats := make(map[h.Coordinate]int) for i := range bestPaths { for j := range bestPaths[i] { bestSeats[bestPaths[i][j]] = i } } fmt.Println("Number of Seats:", len(bestSeats)) } func coordInPath(c h.Coordinate, path []h.Coordinate) bool { for i := range path { if path[i].Equals(c) { return true } } return false } func lookForSeats(inp h.CoordByteMap, start CD, end h.Coordinate, score int, path []h.Coordinate) int { // Check for finished: if start.c.Equals(end) { v, ok := scoreTrack[end] if !ok || v > score { // We have a new best path bestPaths = [][]h.Coordinate{path} scoreTrack[end] = score } else if scoreTrack[end] == score { bestPaths = append(bestPaths, path) } return score } // Helper func canMoveTo := func(p h.Coordinate) bool { return inp.ContainsCoord(p) && inp.Get(p) != '#' } // Keep moving: tryMove := func(c CD, cost int) { if canMoveTo(c.c) { // Check if this path has alread gone through this spot nxtPath := make([]h.Coordinate, len(path)) copy(nxtPath, path) if coordInPath(c.c, nxtPath) { return } // Ok, now what's the best score we end up if we make this move? nextScore := score + cost n, ok := scoreTrack[c.c] if !ok || n > nextScore { lookForSeats(inp, c, end, nextScore, append(nxtPath, c.c)) } } } // Test forward tryMove(start.move(), 1) // Test CW tryMove(start.turnCW().move(), 1001) // Test CCW tryMove(start.turnCCW().move(), 1001) return scoreTrack[start.c] } */ type CD struct { c h.Coordinate d h.Coordinate } func (cd CD) move() CD { return CD{ c: cd.c.Add(cd.d), d: cd.d, } } func (cd CD) turnCW() CD { var newD h.Coordinate switch cd.d { case N: newD = E case E: newD = S case S: newD = W case W: newD = N } return CD{ c: cd.c, d: newD, } } func (cd CD) turnCCW() CD { var newD h.Coordinate switch cd.d { case N: newD = W case E: newD = N case S: newD = E case W: newD = S } return CD{ c: cd.c, d: newD, } } func (cd CD) Byte() byte { switch cd.d { case N: return '^' case E: return '>' case S: return 'v' case W: return '<' } return '?' }