2024 Day 16 Complete!

This commit is contained in:
Brian Buller 2024-12-17 12:47:16 -06:00
parent 5cfe4e1b65
commit 5ae4d0ca9b
2 changed files with 367 additions and 243 deletions

View File

@ -1,276 +1,124 @@
package main
import (
"errors"
"fmt"
"math"
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)
inp := h.StdinToCoordMap()
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))
score, seats := bfs(inp, start, end)
fmt.Println("# Part 1")
fmt.Println("Minimum Score:", score)
fmt.Println("# Part 2")
fmt.Println("Best Seats:", seats)
}
var bestPaths [][]h.Coordinate
func bfs(inp h.CoordByteMap, s, e h.Coordinate) (int, int) {
start := CD{c: s, d: E}
minScore := math.MaxInt
queue := []State{
{
pos: start,
path: []h.Coordinate{},
},
}
visited := make(map[CD]int)
sizeToC := make(map[int][]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)
for len(queue) > 0 {
currState := queue[0]
queue = queue[1:]
// 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
if currState.score > minScore {
continue
}
if currState.pos.c.Equals(e) {
if currState.score <= minScore {
minScore = currState.score
sizeToC[minScore] = append(sizeToC[minScore], currState.path...)
}
continue
}
for _, n := range currState.pos.GetNeighbors() {
if inp.Get(n.c) == '#' {
continue
}
score := currState.score + 1
if currState.pos.d != n.d {
score += 1000
}
if prev, ok := visited[n]; ok {
if prev < score {
continue
}
// 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
}
visited[n] = score
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
np := make([]h.Coordinate, len(currState.path))
copy(np, currState.path)
queue = append(queue, State{
pos: n,
path: append(np, n.c),
score: score,
})
}
}
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
}
seatMap := make(map[h.Coordinate]bool)
for _, p := range sizeToC[minScore] {
seatMap[p] = 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))
/*
for y := inp.TLY; y <= inp.BRY; y++ {
for x := inp.TLX; x <= inp.BRX; x++ {
c := h.Coordinate{X: x, Y: y}
if _, ok := seatMap[c]; ok {
fmt.Print("O")
} else {
fmt.Print(string(inp.Get(c)))
}
}
fmt.Println()
}
}
// Test forward
tryMove(start.move(), 1)
// Test CW
tryMove(start.turnCW().move(), 1001)
// Test CCW
tryMove(start.turnCCW().move(), 1001)
return scoreTrack[start.c]
*/
return minScore, len(seatMap) + 1
}
*/
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}
Dirs = []h.Coordinate{N, E, S, W}
)
type CD struct {
c h.Coordinate
d h.Coordinate
c, d h.Coordinate
}
func (cd CD) move() CD {
return CD{
c: cd.c.Add(cd.d),
d: cd.d,
func (cd CD) GetNeighbors() []CD {
var ret []CD
opp := h.Coordinate{X: -cd.d.X, Y: -cd.d.Y}
for _, dir := range Dirs {
if !dir.Equals(opp) {
ret = append(ret, CD{
c: cd.c.Add(dir),
d: dir,
})
}
}
return ret
}
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 '?'
type State struct {
pos CD
path []h.Coordinate
score int
}

276
2024/day16/main.go.bk Normal file
View File

@ -0,0 +1,276 @@
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 '?'
}