From 405f8bf53325643d81cc94a00ed04d54a73420d4 Mon Sep 17 00:00:00 2001 From: Jeremy Kaplan Date: Fri, 31 Jul 2026 16:05:30 -0400 Subject: [PATCH 1/4] 2017 Day 12 Part 1 --- 2017/day12.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 2017/day12.py diff --git a/2017/day12.py b/2017/day12.py new file mode 100644 index 0000000..dd62e4f --- /dev/null +++ b/2017/day12.py @@ -0,0 +1,28 @@ +from collections import defaultdict + +with open("input/day12.txt") as f: + input = f.read() + +neighbors = defaultdict(set) + +for line in input.splitlines(): + a, bs = line.split(" <-> ") + for b in bs.split(", "): + neighbors[a].add(b) + neighbors[b].add(a) + + +def connected(start, neighbors) -> int: + queue = [start] + seen = set() + while queue: + node = queue.pop(0) + if node in seen: + continue + + seen.add(node) + queue.extend(neighbors[node]) + return len(seen) + + +print(connected("0", neighbors)) From c282317273743088c81a122989e124a66c152b4d Mon Sep 17 00:00:00 2001 From: Jeremy Kaplan Date: Fri, 31 Jul 2026 16:09:49 -0400 Subject: [PATCH 2/4] 2017 Day 12 Part 2 --- 2017/day12.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/2017/day12.py b/2017/day12.py index dd62e4f..83d9493 100644 --- a/2017/day12.py +++ b/2017/day12.py @@ -12,7 +12,7 @@ for line in input.splitlines(): neighbors[b].add(a) -def connected(start, neighbors) -> int: +def connected(start, neighbors): queue = [start] seen = set() while queue: @@ -22,7 +22,21 @@ def connected(start, neighbors) -> int: seen.add(node) queue.extend(neighbors[node]) - return len(seen) + return seen -print(connected("0", neighbors)) +def groups(neighbors) -> int: + seeds = set(neighbors.keys()) + count = 0 + + while seeds: + start = seeds.pop() + seen = connected(start, neighbors) + seeds.difference_update(seen) + count += 1 + + return count + + +print(len(connected("0", neighbors))) +print(groups(neighbors)) From 3421534adb589e04e55c033e0d57ef6a2863f554 Mon Sep 17 00:00:00 2001 From: Jeremy Kaplan Date: Fri, 31 Jul 2026 16:28:58 -0400 Subject: [PATCH 3/4] 2017 Day 13 Part 1 --- 2017/day13.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 2017/day13.py diff --git a/2017/day13.py b/2017/day13.py new file mode 100644 index 0000000..de669c9 --- /dev/null +++ b/2017/day13.py @@ -0,0 +1,29 @@ +with open("input/day13.txt") as f: + input = f.read() + +scanners = {} + +for line in input.splitlines(): + parts = line.split(": ") + d, r = int(parts[0]), int(parts[1]) + + assert d not in scanners, "no overlapping scanners" + scanners[d] = r + + +def part1(scanners): + severity = 0 + for d in range(max(scanners.keys()) + 1): + r = scanners.get(d) + if r is None: + continue + + assert r > 1, r + cycle = (r - 1) * 2 + pos = d % cycle + if pos == 0: + severity += d * r + return severity + + +print(part1(scanners)) From db081bed303bc2d55e184ceaab47f8c56ea0eb64 Mon Sep 17 00:00:00 2001 From: Jeremy Kaplan Date: Sat, 8 Aug 2026 14:08:51 -0400 Subject: [PATCH 4/4] 2017 Day 13 Part 2 --- 2017/day13.py | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/2017/day13.py b/2017/day13.py index de669c9..4a88f1d 100644 --- a/2017/day13.py +++ b/2017/day13.py @@ -1,7 +1,10 @@ +from math import lcm +from dataclasses import dataclass + with open("input/day13.txt") as f: input = f.read() -scanners = {} +scanners: dict[int, int] = {} for line in input.splitlines(): parts = line.split(": ") @@ -26,4 +29,71 @@ def part1(scanners): return severity +def is_safe(scanners, delay): + for depth in range(max(scanners.keys()) + 1): + r = scanners.get(depth) + if r is None: + continue + + assert r > 1, r + cycle = (r - 1) * 2 + pos = (delay + depth) % cycle + if pos == 0: + return False + return True + + +@dataclass +class Scanner: + depth: int + cycle: int + + +@dataclass(order=True) +class Candidate: + delay: int + increment: int + + def __init__(self, delay: int, increment: int): + self.delay = delay % increment + self.increment = increment + + def __hash__(self): + return hash((self.delay % self.increment, self.increment)) + + def scale(self, scalar: int) -> set["Candidate"]: + res = set() + increment = self.increment * scalar + for i in range(scalar + 1): + res.add(Candidate(self.delay + i * self.increment, increment)) + return res + + def __str__(self): + return f"{self.delay} + k * {self.increment}" + + +def part2(scanner_map: dict[int, int]): + scanners: list[Scanner] = [] + for depth in range(max(scanner_map.keys()) + 1): + r = scanner_map.get(depth) + if r is None: + continue + cycle = (r - 1) * 2 + scanners.append(Scanner(depth, cycle)) + + candidates = {Candidate(0, 1)} + for s in scanners: + next: set[Candidate] = set() + for c in candidates: + scalar = lcm(c.increment, s.cycle) // c.increment + for n in c.scale(scalar): + if (n.delay + s.depth) % s.cycle: + next.add(n) + + candidates = next + + return min(c.delay for c in candidates) + + print(part1(scanners)) +print(part2(scanners))