def part_2(values: Iterable[int]) -> int: """ Considering every single measurement isn't as useful as you expected: there's just too much noise in the data. Instead, consider sums of a **three-measurement sliding window**. Again considering the above example: >>> measurements = [199, 200, 208, 210, 200, 207, 240, 269, 260, 263] >>> [sum(w) for w in slidingw(measurements, 3)] [607, 618, 618, 617, 647, 716, 769, 792] In this example, there are **5** sums that are larger than the previous sum. >>> count_increases(_) 5 Consider sums of a three-measurement sliding window. **How many sums are larger than the previous sum?** >>> part_2(measurements) part 2: 5 increases 5 """ triples = (sum(w) for w in slidingw(values, 3)) result = count_increases(triples) print(f"part 2: {result} increases") return result
def shortest_path(self, start_at: str = '0') -> tuple[int, Path]: distances = self.calculate_distances() result_path, distance = mink( self.paths(start_at), key=lambda path: sum(distances[(a, b)] for a, b in slidingw(path, 2))) return distance, result_path
def draw_arrangement(arrangement: list[str], rules: Rules) -> None: assert len(arrangement) > 1 seats = arrangement[-1:] + arrangement[:-1] for neighbor_1, seat, neighbor_2 in slidingw(seats, 3, wrap=True): happines_1 = rules[seat, neighbor_1] happines_2 = rules[seat, neighbor_2] print(f"{happines_1:+3} {seat:5} {happines_2:+3}")
def shortest_closed_path(self, start_at: str = '0') -> tuple[int, Path]: distances = self.calculate_distances() result_path, (distance, _) = mink( self.closed_paths(start_at), # path is included in key in order to make the calculation deterministic # because closed path can be reversed (0-1-2-0 and 0-2-1-0) key=lambda path: (sum(distances[(a, b)] for a, b in slidingw(path, 2)), path)) return distance, result_path
def grow(polymer: str, rules: Rules, steps: int, log: bool = False) -> str: assert len(polymer) >= 2 if log: print(f'Template: {polymer}') for step in range(steps): polymer = ''.join( e for i0, i1 in slidingw(polymer, 2) for e in (i0, rules[(i0, i1)]) ) + polymer[-1] if log: print(f'After step {step + 1}: {polymer}') return polymer
def _parse_line_fixes(line: str, *fixes: str) -> tuple[str, ...]: assert len(fixes) >= 2 results = [] for fix1, fix2 in slidingw(fixes, 2): assert line.startswith(fix1), (line, fix1) line = line[len(fix1):] if fix2: pos2 = line.index(fix2) results.append(line[:pos2]) line = line[pos2:] else: results.append(line) line = '' assert line == fixes[-1], f"{line!r} != {fixes[-1]!r}" return tuple(results)
def grow_optimized(polymer: str, rules: Rules, steps: int) -> Counter[str]: assert len(polymer) >= 2 # instead of keeping track of the polymer itself, we'll watch only counts of its pairs pairs = Counter(slidingw(polymer, 2)) for _ in range(steps): pairs = _total_counts( (new_pair, count) for (i0, i1), count in pairs.items() if (out := rules[(i0, i1)]) for new_pair in [(i0, out), (out, i1)] ) return _total_counts( chain( # sum of pair quantities -> only first element (otherwise result would be ~doubled) ((e0, count) for (e0, _), count in pairs.items()), # plus 1 for the last element (which remains unchanged throughout the process) [(polymer[-1], 1)] ) )
def count_increases(values: Iterable[int]) -> int: return sum(v1 > v0 for v0, v1 in slidingw(values, 2))
def captcha_1(digits: str) -> int: return sum( int(a) for a, b in slidingw(digits, 2, wrap=True) if a == b )