Day 23 part 1 done

This commit is contained in:
Daniel Flanagan 2021-12-24 15:37:03 -06:00
parent 86efa964b2
commit dd0da19680
Signed by: lytedev
GPG key ID: 5B2020A0F9921EF4
2 changed files with 42 additions and 44 deletions

View file

@ -47,17 +47,17 @@ proc doDay*[T, X](
expectedPart1: X, expectedPart1: X,
expectedPart2: X): void = expectedPart2: X): void =
when not defined(release): # when not defined(release):
var p1 = testInput.part1() var p1 = testInput.part1()
echo "Day ", day, " Part 1: ", p1, " (Expected: ", expectedPart1, ")" echo "Day ", day, " Part 1: ", p1, " (Expected: ", expectedPart1, ")"
doAssert p1 == expectedPart1 doAssert p1 == expectedPart1
time(&"Day {day} Part 1"): echo day.inputLoader().part1() time(&"Day {day} Part 1"): echo day.inputLoader().part1()
when not defined(release): # when not defined(release):
var p2 = testInput.part2() var p2 = testInput.part2()
echo "Day ", day, " Part 2: ", p2, " (Expected: ", expectedPart2, ")" echo "Day ", day, " Part 2: ", p2, " (Expected: ", expectedPart2, ")"
doAssert p2 == expectedPart2 doAssert p2 == expectedPart2
time(&"Day {day} Part 2"): echo day.inputLoader().part2() time(&"Day {day} Part 2"): echo day.inputLoader().part2()

View file

@ -53,7 +53,6 @@ proc path(s: AmphipodCaveState, ai: int, goal: Vec2): seq[Vec2] =
proc move(s: AmphipodCaveState, ai: int, p: Vec2): (AmphipodCaveState, uint64) = proc move(s: AmphipodCaveState, ai: int, p: Vec2): (AmphipodCaveState, uint64) =
var newState = s var newState = s
newState[ai].pos = p newState[ai].pos = p
echo &"Moving {ai} from {s[ai].pos} to {p}"
(newState, s.path(ai, p).len.uint64 * s[ai].breed.weight.uint64) (newState, s.path(ai, p).len.uint64 * s[ai].breed.weight.uint64)
proc spaceOccupied(s: AmphipodCaveState, p: Vec2): bool = proc spaceOccupied(s: AmphipodCaveState, p: Vec2): bool =
@ -62,30 +61,32 @@ proc spaceOccupied(s: AmphipodCaveState, p: Vec2): bool =
proc canMoveTo(s: AmphipodCaveState, ai: int, goal: Vec2): bool = proc canMoveTo(s: AmphipodCaveState, ai: int, goal: Vec2): bool =
let a = s[ai] let a = s[ai]
let pos = s[ai].pos let pos = s[ai].pos
# can't move anywhere but home once we've left # echo " -> once in the hallway, we can't move somewhere else in the hallway"
if pos.y == 0 and goal.y == 0: return false if pos.y == 0 and goal.y == 0: return false
# no blocking # echo " -> no blocking"
if goal.y == 0 and homeSet.contains(goal.x): return false if goal.y == 0 and homeSet.contains(goal.x): return false
# non-positions # echo " -> non-positions"
if goal.y > 0 and not homeSet.contains(goal.x): return false if goal.y > 0 and not homeSet.contains(goal.x): return false
# can't leave empty spots below when going home # echo " -> can't leave empty spots below when going home"
if goal.y == 1 and not s.spaceOccupied(v2(goal.x, 2)): return false if goal.y == 1 and not s.spaceOccupied(v2(goal.x, 2)): return false
# can't enter home if occupied by another breed # echo " -> can't enter home if occupied by another breed"
if goal.y == 1: if goal.y > 0:
for sib in s: for sib in s:
if sib.breed == a.breed: continue
if sib.pos.x == goal.x and sib.pos.y > goal.y: return false if sib.pos.x == goal.x and sib.pos.y > goal.y: return false
# can't enter somebody else's home # echo " -> can't enter somebody else's home"
if goal.y > 0 and goal.x != a.breed.home: return false if goal.y > 0 and goal.x != a.breed.home: return false
# can't move through any occupied positions # echo " -> can't move through any occupied positions"
let path = s.path(ai, goal) let path = s.path(ai, goal)
if path.anyIt(s.spaceOccupied(it)): return false if path.anyIt(s.spaceOccupied(it)): return false
# echo " -> Allowed!"
true true
proc isWin(s: AmphipodCaveState): bool = proc isWin(s: AmphipodCaveState): bool =
result = s.allIt(it.pos.x == it.breed.home and it.pos.y > 0) result = s.allIt(it.pos.x == it.breed.home and it.pos.y > 0)
if result: # if result:
echo s # echo s
echo "I WON" # echo "I WON"
proc possibleMoves(s: AmphipodCaveState, ai: int): HashSet[Vec2] = proc possibleMoves(s: AmphipodCaveState, ai: int): HashSet[Vec2] =
let a = s[ai] let a = s[ai]
@ -98,27 +99,32 @@ proc possibleMoves(s: AmphipodCaveState, ai: int): HashSet[Vec2] =
proc getByPos(s: AmphipodCaveState, pos: Vec2): Option[Amphipod] = s.findFirstO(a => a.pos == pos) proc getByPos(s: AmphipodCaveState, pos: Vec2): Option[Amphipod] = s.findFirstO(a => a.pos == pos)
proc isHome(a: Amphipod): bool = a.pos.x == a.breed.home and a.pos.y > 0 proc isHome(a: Amphipod): bool = a.pos.x == a.breed.home and a.pos.y > 0
proc doneMoving(s: AmphipodCaveState, ai: int): bool = proc doneMoving(s: AmphipodCaveState, ai: int): bool =
let a = s[ai] let a = s[ai]
if a.isHome and a.pos.y >= 2: return true if a.isHome and a.pos.y >= 2: return true
let below = s.getByPos((x: a.pos.x, y: 1)) let below = s.getByPos((x: a.pos.x, y: 2))
if below.isSome: return a.isHome and below.get.isHome if below.isSome: return a.isHome and below.get.isHome
var cheapestWins = newTable[AmphipodCaveState, uint64]() var cheapestWins = newTable[AmphipodCaveState, (uint64, uint64)]()
proc cheapestWin(s: AmphipodCaveState, cost: uint64, depth = 0): uint64 = proc cheapestWin(s: AmphipodCaveState, cost: uint64, depth = 0): uint64 =
result = uint64.high result = uint64.high
echo &"cheapestWin: {cost} {depth}\n{s}" # echo &"cheapestWin: {cost} {depth}\n{s}"
if cheapestWins.hasKey s: return cheapestWins[s] if cheapestWins.hasKey s:
if s.isWin or cost > 50000: return cost # echo &"cache hit for {s}: {cheapestWins[s]}"
if cheapestWins[s][0] <= cost:
return cheapestWins[s][1]
if s.isWin or cost > 400000: return cost
var costs = initHashSet[uint64]() var costs = initHashSet[uint64]()
for ai,a in s: for ai,a in s:
if s.doneMoving(ai): continue if s.doneMoving(ai): continue
for p in s.possibleMoves ai: for p in s.possibleMoves ai:
if not s.canMoveTo(ai, p): continue if not s.canMoveTo(ai, p): continue
# echo &"Moving {ai} from {a.pos} to {p} at turn {depth}"
let (newState, moveCost) = s.move(ai, p) let (newState, moveCost) = s.move(ai, p)
costs.incl newState.cheapestWin(cost + moveCost, depth + 1) costs.incl newState.cheapestWin(cost + moveCost, depth + 1)
for c in costs: result = min(result, c) for c in costs: result = min(result, c)
cheapestWins[s] = result cheapestWins[s] = (cost, result)
proc testMoves(state: AmphipodCaveState, moves: seq[(int, Vec2)]) = proc testMoves(state: AmphipodCaveState, moves: seq[(int, Vec2)]) =
var s = state var s = state
@ -126,6 +132,7 @@ proc testMoves(state: AmphipodCaveState, moves: seq[(int, Vec2)]) =
for m in moves: for m in moves:
echo s echo s
let (ai, goal) = m let (ai, goal) = m
let doneMoving = s.doneMoving(ai)
let suggestions = s.possibleMoves(ai) let suggestions = s.possibleMoves(ai)
echo suggestions echo suggestions
var sugs = @["...........", " . . . .", " . . . .", ""] var sugs = @["...........", " . . . .", " . . . .", ""]
@ -136,35 +143,26 @@ proc testMoves(state: AmphipodCaveState, moves: seq[(int, Vec2)]) =
echo sugs.join("\n") echo sugs.join("\n")
let allowed = s.canMoveTo(ai, goal) let allowed = s.canMoveTo(ai, goal)
let suggested = suggestions.contains goal let suggested = suggestions.contains goal
echo &"Can move {ai} from {s[ai].pos} to {goal}: allowed: {allowed}, suggested: {suggested}" echo &"Can move {ai} from {s[ai].pos} to {goal}: allowed: {allowed}, suggested: {suggested}, doneMoving: {doneMoving}"
var (newState, moveCost) = s.move(ai, goal) var (newState, moveCost) = s.move(ai, goal)
cost += moveCost cost += moveCost
s = newState s = newState
echo &"Did we win? {s.isWin} -- Cost so far: {cost}" echo &"Did we win? {s.isWin} -- Cost so far: {cost}"
proc p1(input: Lines): uint64 = proc p1(input: Lines): uint64 =
echo input.parseCaveState
var s = input.parseCaveState
s.testMoves(@[
(2, (x: 3, y: 0)),
(1, (x: 6, y: 1)),
(5, (x: 5, y: 0)),
(2, v2(4, 2)),
(0, v2(4, 1)),
(3, v2(7, 0)),
(7, v2(9, 0)),
(3, v2(8, 2)),
(5, v2(8, 1)),
(7, v2(2, 1)),
])
echo "Crunching..."
input.parseCaveState.cheapestWin(0) input.parseCaveState.cheapestWin(0)
proc p2(input: Lines): uint64 =
var unfoldedInput = input
unfoldedInput.insert(" #D#C#B#A#", 1)
unfoldedInput.insert(" #D#C#B#A#", 2)
unfoldedInput.parseCaveState.cheapestWin(0)
const rt = (""" const rt = ("""
############# #############
#...........# #...........#
###B#C#B#D### ###B#C#B#D###
#A#D#C#A# #A#D#C#A#
######### #########
""".strip().split('\n'), 12521'u64, 0'u64) """.strip().split('\n'), 12521'u64, 44169'u64)
doDayX 23, (n: int) => n.loadInput, p1, p1, rt doDayX 23, (n: int) => n.loadInput, p1, p2, rt