2021 Day 23 Part 2
This commit is contained in:
parent
c8cc841fd2
commit
fde48e8321
1 changed files with 135 additions and 51 deletions
|
|
@ -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) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue