Solve 2021:12 "Passage Pathing"

I struggled a lot with the direction, but realised way too much later
that the mappings are bidirectional.

Part 2 was exceptionally hard, but I realised early that the order did
not matter and that a simple boolean lock was all that was needed. The
challenge was to know when to watch for duplicates.

The seen was initially a set, but since new sets must be sent instead of
references, a list is used instead to be able to use the seen + [] hack.
This commit is contained in:
Anders Englöf Ytterström 2021-12-12 14:28:36 +01:00
parent b53c50addc
commit c0d352cdf1
2 changed files with 129 additions and 0 deletions

View file

@ -0,0 +1,54 @@
from solutions import BaseSolution
from collections import defaultdict, deque, Counter
class Solution(BaseSolution):
input_file = "12.txt"
def __str__(self):
return "Day 12: Passage Pathing"
def parse_input(self, data):
return [l.split("-") for l in data.split()]
def solve(self, puzzle_input):
return self._solve(puzzle_input, lambda seen: True)
def solve_again(self, puzzle_input):
return self._solve(
puzzle_input,
lambda seen: any(
[
k.islower() and k != "start" and v > 1
for k, v in Counter(seen).items()
]
),
)
def _solve(self, puzzle_input, isdup):
ft = defaultdict(lambda: [])
for f, t in puzzle_input:
ft[f].append(t)
for f, t in puzzle_input:
ft[t].append(f)
queue = deque([("start", list(), False)])
s2e = 0
while queue:
pos, seen, dup = queue.pop()
seen.append(pos)
dup = isdup(seen)
for t in ft[pos]:
if t == "end":
s2e += 1
continue
elif t == "start":
continue
elif dup and t.islower() and t in seen:
continue
queue.append((t, seen + [], dup))
return s2e
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -0,0 +1,75 @@
import unittest
from solutions.day_12 import Solution
class Day12TestCase(unittest.TestCase):
def setUp(self):
self.solution = Solution()
self.puzzle_input = self.solution.parse_input(
"""
start-A
start-b
A-c
A-b
b-d
A-end
b-end
"""
)
def test_parse_puzzle_input(self):
data = """
a-b
q1w2-e3r4
"""
assert self.solution.parse_input(data) == [["a", "b"], ["q1w2", "e3r4"]]
def test_solve_first_part(self):
assert self.solution.solve(self.puzzle_input) == 10
def test_solve_second_part(self):
assert self.solution.solve_again(self.puzzle_input) == 36
if __name__ == "__main__":
unittest.main()
"""
start,A,b,A,b,A,c,A,end
start,A,b,A,b,A,end
start,A,b,A,b,end
start,A,b,A,c,A,b,A,end
start,A,b,A,c,A,b,end
start,A,b,A,c,A,c,A,end
start,A,b,A,c,A,end
start,A,b,A,end
start,A,b,d,b,A,c,A,end
start,A,b,d,b,A,end
start,A,b,d,b,end
start,A,b,end
start,A,c,A,b,A,b,A,end
start,A,c,A,b,A,b,end
start,A,c,A,b,A,c,A,end
start,A,c,A,b,A,end
start,A,c,A,b,d,b,A,end
start,A,c,A,b,d,b,end
start,A,c,A,b,end
start,A,c,A,c,A,b,A,end
start,A,c,A,c,A,b,end
start,A,c,A,c,A,end
start,A,c,A,end
start,A,end
start,b,A,b,A,c,A,end
start,b,A,b,A,end
start,b,A,b,end
start,b,A,c,A,b,A,end
start,b,A,c,A,b,end
start,b,A,c,A,c,A,end
start,b,A,c,A,end
start,b,A,end
start,b,d,b,A,c,A,end
start,b,d,b,A,end
start,b,d,b,end
start,b,end
"""