diff --git a/2021/nim/common.nim b/2021/nim/common.nim index 09e990d..069dde6 100644 --- a/2021/nim/common.nim +++ b/2021/nim/common.nim @@ -47,17 +47,17 @@ proc doDay*[T, X]( expectedPart1: X, expectedPart2: X): void = - when not defined(release): - var p1 = testInput.part1() - echo "Day ", day, " Part 1: ", p1, " (Expected: ", expectedPart1, ")" - doAssert p1 == expectedPart1 + # when not defined(release): + var p1 = testInput.part1() + echo "Day ", day, " Part 1: ", p1, " (Expected: ", expectedPart1, ")" + doAssert p1 == expectedPart1 time(&"Day {day} Part 1"): echo day.inputLoader().part1() - when not defined(release): - var p2 = testInput.part2() - echo "Day ", day, " Part 2: ", p2, " (Expected: ", expectedPart2, ")" - doAssert p2 == expectedPart2 + # when not defined(release): + var p2 = testInput.part2() + echo "Day ", day, " Part 2: ", p2, " (Expected: ", expectedPart2, ")" + doAssert p2 == expectedPart2 time(&"Day {day} Part 2"): echo day.inputLoader().part2() diff --git a/2021/nim/day23.nim b/2021/nim/day23.nim index 7151338..5badacf 100644 --- a/2021/nim/day23.nim +++ b/2021/nim/day23.nim @@ -53,7 +53,6 @@ proc path(s: AmphipodCaveState, ai: int, goal: Vec2): seq[Vec2] = proc move(s: AmphipodCaveState, ai: int, p: Vec2): (AmphipodCaveState, uint64) = var newState = s 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) 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 = let a = s[ai] 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 - # no blocking + # echo " -> no blocking" 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 - # 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 - # can't enter home if occupied by another breed - if goal.y == 1: + # echo " -> can't enter home if occupied by another breed" + if goal.y > 0: for sib in s: + if sib.breed == a.breed: continue 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 - # can't move through any occupied positions + # echo " -> can't move through any occupied positions" let path = s.path(ai, goal) if path.anyIt(s.spaceOccupied(it)): return false + # echo " -> Allowed!" true proc isWin(s: AmphipodCaveState): bool = result = s.allIt(it.pos.x == it.breed.home and it.pos.y > 0) - if result: - echo s - echo "I WON" + # if result: + # echo s + # echo "I WON" proc possibleMoves(s: AmphipodCaveState, ai: int): HashSet[Vec2] = 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 isHome(a: Amphipod): bool = a.pos.x == a.breed.home and a.pos.y > 0 + proc doneMoving(s: AmphipodCaveState, ai: int): bool = let a = s[ai] 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 -var cheapestWins = newTable[AmphipodCaveState, uint64]() +var cheapestWins = newTable[AmphipodCaveState, (uint64, uint64)]() proc cheapestWin(s: AmphipodCaveState, cost: uint64, depth = 0): uint64 = result = uint64.high - echo &"cheapestWin: {cost} {depth}\n{s}" - if cheapestWins.hasKey s: return cheapestWins[s] - if s.isWin or cost > 50000: return cost + # echo &"cheapestWin: {cost} {depth}\n{s}" + if cheapestWins.hasKey s: + # 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]() for ai,a in s: if s.doneMoving(ai): continue for p in s.possibleMoves ai: 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) costs.incl newState.cheapestWin(cost + moveCost, depth + 1) for c in costs: result = min(result, c) - cheapestWins[s] = result + cheapestWins[s] = (cost, result) proc testMoves(state: AmphipodCaveState, moves: seq[(int, Vec2)]) = var s = state @@ -126,6 +132,7 @@ proc testMoves(state: AmphipodCaveState, moves: seq[(int, Vec2)]) = for m in moves: echo s let (ai, goal) = m + let doneMoving = s.doneMoving(ai) let suggestions = s.possibleMoves(ai) echo suggestions var sugs = @["...........", " . . . .", " . . . .", ""] @@ -136,35 +143,26 @@ proc testMoves(state: AmphipodCaveState, moves: seq[(int, Vec2)]) = echo sugs.join("\n") let allowed = s.canMoveTo(ai, 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) cost += moveCost s = newState echo &"Did we win? {s.isWin} -- Cost so far: {cost}" 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) +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 = (""" ############# #...........# ###B#C#B#D### #A#D#C#A# ######### -""".strip().split('\n'), 12521'u64, 0'u64) -doDayX 23, (n: int) => n.loadInput, p1, p1, rt +""".strip().split('\n'), 12521'u64, 44169'u64) +doDayX 23, (n: int) => n.loadInput, p1, p2, rt