Day 23 part 1 done
This commit is contained in:
parent
86efa964b2
commit
dd0da19680
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue