Watch
1
0
Fork
You've already forked advent-of-code
0

2021 Day 23 Part 2

This commit is contained in:
Jeremy Kaplan 2021-12-24 03:28:12 -08:00
commit fde48e8321

View file

@ -9,13 +9,9 @@ import (
) )
func main() { func main() {
startText := aoc.Input().ReadFile("day23.txt") startText := aoc.Input().ReadFile("day23-part2.txt")
goalText := aoc.Input().ReadFile("day23-goal.txt")
w, start := parseInput(startText) w, start := parseInput(startText)
_, goal := parseInput(goalText) fmt.Println(part1(w, start))
fmt.Println(part1(w, start, goal))
} }
func parseInput(text string) (Walls, State) { func parseInput(text string) (Walls, State) {
@ -38,20 +34,33 @@ func parseInput(text string) (Walls, State) {
bs := pods[Bronze] bs := pods[Bronze]
cs := pods[Copper] cs := pods[Copper]
ds := pods[Desert] ds := pods[Desert]
if len(as) != podCount {
panic(as)
}
if len(bs) != podCount {
panic(bs)
}
if len(cs) != podCount {
panic(cs)
}
if len(ds) != podCount {
panic(ds)
}
s := State{ s := State{
A: [2]RC{as[0], as[1]}, A: [podCount]RC{as[0], as[1], as[2], as[3]},
B: [2]RC{bs[0], bs[1]}, B: [podCount]RC{bs[0], bs[1], bs[2], bs[3]},
C: [2]RC{cs[0], cs[1]}, C: [podCount]RC{cs[0], cs[1], cs[2], cs[3]},
D: [2]RC{ds[0], ds[1]}, D: [podCount]RC{ds[0], ds[1], ds[2], ds[3]},
} }
return w, s return w, s
} }
func part1(w Walls, start, goal State) int { func part1(w Walls, start State) int {
successors := func(s State) map[State]int { successors := func(s State) map[State]int {
fmt.Println(s.Debug(w))
return s.Successors(w) return s.Successors(w)
} }
return search(start, goal, successors) return search(start, State.Goal, successors, State.Wrong)
} }
type Object rune type Object rune
@ -95,10 +104,14 @@ func (rc RC) Neighbors() []RC {
} }
} }
const podCount = 4
const rHallway = 1
const rMax = rHallway + podCount
type Walls map[RC]bool type Walls map[RC]bool
func (w Walls) IsDoor(rc RC) bool { func (w Walls) IsDoor(rc RC) bool {
if rc.r != 1 { if rc.r != rHallway {
return false return false
} }
switch rc.c { switch rc.c {
@ -110,29 +123,34 @@ func (w Walls) IsDoor(rc RC) bool {
func (w Walls) Blocked(rc RC) bool { func (w Walls) Blocked(rc RC) bool {
r, c := rc.r, rc.c r, c := rc.r, rc.c
ok := 1 <= r && r <= 3 && 1 <= c && c <= 9 ok := rHallway <= r && r <= rMax && 1 <= c && c <= 9
wall := w[rc] wall := w[rc]
return ok && !wall return ok && !wall
} }
type State struct{ A, B, C, D [2]RC } type State struct{ A, B, C, D [podCount]RC }
func (s State) pods() map[RC]Object { func (s State) pods() map[RC]Object {
return map[RC]Object{ p := make(map[RC]Object)
s.A[0]: Amber, for _, a := range s.A {
s.A[1]: Amber, p[a] = Amber
s.B[0]: Bronze,
s.B[1]: Bronze,
s.C[0]: Copper,
s.C[1]: Copper,
s.D[0]: Desert,
s.D[1]: Desert,
} }
for _, b := range s.B {
p[b] = Bronze
} }
for _, c := range s.C {
p[c] = Copper
}
for _, d := range s.D {
p[d] = Desert
}
return p
}
func (s State) Debug(w Walls) string { func (s State) Debug(w Walls) string {
pods := s.pods() pods := s.pods()
var sb strings.Builder var sb strings.Builder
for r := 0; r <= 4; r++ { for r := 0; r <= rMax+1; r++ {
for c := 0; c <= 12; c++ { for c := 0; c <= 12; c++ {
rc := RC{r, c} rc := RC{r, c}
if w[rc] { if w[rc] {
@ -197,6 +215,54 @@ func (s State) Successors(w Walls) map[State]int {
return next return next
} }
func (s State) Goal() bool {
for _, a := range s.A {
if a.c != 3 {
return false
}
}
for _, b := range s.B {
if b.c != 5 {
return false
}
}
for _, c := range s.C {
if c.c != 7 {
return false
}
}
for _, d := range s.D {
if d.c != 9 {
return false
}
}
return true
}
func (s State) Wrong() (cost int) {
for _, a := range s.A {
if a.c != 3 {
cost += 2
}
}
for _, b := range s.B {
if b.c != 5 {
cost += 20
}
}
for _, c := range s.C {
if c.c != 7 {
cost += 200
}
}
for _, d := range s.D {
if d.c != 9 {
cost += 2000
}
}
return
}
func podMoves(w Walls, s State, start RC, kind Object) (costs map[RC]int) { func podMoves(w Walls, s State, start RC, kind Object) (costs map[RC]int) {
var targetC, cost int var targetC, cost int
switch kind { switch kind {
@ -218,33 +284,38 @@ func podMoves(w Walls, s State, start RC, kind Object) (costs map[RC]int) {
type Mode string type Mode string
const ( const (
Error Mode = "error"
Exit Mode = "exit" Exit Mode = "exit"
Wait Mode = "wait" Wait Mode = "wait"
Park Mode = "park"
Done Mode = "done" Done Mode = "done"
) )
func podMode(w Walls, s State, rc RC, kind Object, targetC int) Mode { func podMode(w Walls, s State, rc RC, kind Object, targetC int) Mode {
pods := s.pods() pods := s.pods()
if rc.r == 1 { if rc.r == rHallway {
// In the hallway. // In the hallway. Would another pod need to leave?
for r := rc.r + 1; r <= rMax; r++ {
inner := RC{r, targetC}
if k, ok := pods[inner]; ok && k != kind {
// Need to let the other pod out.
return Wait return Wait
} }
}
// Nope, get moving!
return Park
}
if rc.c == targetC { if rc.c == targetC {
// In correct column. // In correct column. Does another pod want to leave?
inner := RC{3, targetC} for r := rc.r + 1; r <= rMax; r++ {
if rc == inner { inner := RC{r, targetC}
// No reason to leave! if k, ok := pods[inner]; ok && k != kind {
return Done
}
// Double-parked. Does the other pod want to leave?
if pods[inner] == kind {
// Nope, all good!
return Done
}
// Need to let the other pod out. // Need to let the other pod out.
return Exit return Exit
} }
}
// No reason to leave!
return Done
}
return Exit return Exit
} }
@ -254,12 +325,15 @@ func podPath(w Walls, s State, rc RC, mode Mode, targetC int, cost int) (costs m
// Any walkable space in the hallway that's not the door. // Any walkable space in the hallway that's not the door.
costs = make(map[RC]int) costs = make(map[RC]int)
for _, spot := range walk(w, s, rc) { for _, spot := range walk(w, s, rc) {
if spot.r == 1 && !w.IsDoor(spot) { if spot.r == rHallway && !w.IsDoor(spot) {
costs[spot] = cost * manhattan(rc, spot) costs[spot] = cost * manhattan(rc, spot)
} }
} }
return costs return costs
case Wait: case Wait:
// Do nothing
return nil
case Park:
// Only the correct parking spot // Only the correct parking spot
var bestSpot RC var bestSpot RC
for _, spot := range walk(w, s, rc) { for _, spot := range walk(w, s, rc) {
@ -324,13 +398,20 @@ func walk(w Walls, s State, from RC) (spots []RC) {
} }
func search( func search(
start, goal State, start State,
isGoal func(State) bool,
successors func(State) map[State]int, successors func(State) map[State]int,
heuristic func(State) int,
) (totalCost int) { ) (totalCost int) {
if heuristic == nil {
heuristic = func(State) int { return 0 }
}
pq := make(PriorityQueue, 1) pq := make(PriorityQueue, 1)
pq[0] = &Node{ pq[0] = &Node{
s: start, s: start,
cost: 0, cost: 0,
heur: 0,
idx: 0, idx: 0,
} }
heap.Init(&pq) heap.Init(&pq)
@ -338,14 +419,14 @@ func search(
seen := make(map[State]bool) seen := make(map[State]bool)
for len(pq) > 0 { for len(pq) > 0 {
state, cost := pq.Next() state, cost := pq.Next()
if state == goal { if isGoal(state) {
return cost return cost
} }
if seen[state] { if seen[state] {
continue continue
} }
for s, c := range successors(state) { for s, c := range successors(state) {
pq.Insert(s, cost+c) pq.Insert(s, cost+c, heuristic(state))
} }
seen[state] = true seen[state] = true
} }
@ -355,6 +436,7 @@ func search(
type Node struct { type Node struct {
s State s State
cost int cost int
heur int
idx int idx int
} }
@ -365,8 +447,8 @@ func (pq *PriorityQueue) Next() (s State, cost int) {
return n.s, n.cost return n.s, n.cost
} }
func (pq *PriorityQueue) Insert(s State, cost int) { func (pq *PriorityQueue) Insert(s State, cost int, heur int) {
heap.Push(pq, &Node{s: s, cost: cost}) heap.Push(pq, &Node{s: s, cost: cost, heur: heur})
} }
func (pq PriorityQueue) Len() int { func (pq PriorityQueue) Len() int {
@ -374,7 +456,9 @@ func (pq PriorityQueue) Len() int {
} }
func (pq PriorityQueue) Less(i, j int) bool { func (pq PriorityQueue) Less(i, j int) bool {
return pq[i].cost < pq[j].cost ii := pq[i].cost + pq[i].heur
jj := pq[j].cost + pq[j].heur
return ii < jj
} }
func (p PriorityQueue) Swap(i, j int) { func (p PriorityQueue) Swap(i, j int) {