Start fixing 2022 for new API.
This commit is contained in:
		| @@ -1,7 +1,12 @@ | |||||||
| import sys | from typing import Any, Iterator | ||||||
|  |  | ||||||
| blocks = sys.stdin.read().split("\n\n") | from ..base import BaseSolver | ||||||
| values = sorted(sum(map(int, block.split())) for block in blocks) |  | ||||||
|  |  | ||||||
| print(f"answer 1 is {values[-1]}") |  | ||||||
| print(f"answer 2 is {sum(values[-3:])}") | class Solver(BaseSolver): | ||||||
|  |     def solve(self, input: str) -> Iterator[Any]: | ||||||
|  |         blocks = input.split("\n\n") | ||||||
|  |         values = sorted(sum(map(int, block.split())) for block in blocks) | ||||||
|  |  | ||||||
|  |         yield values[-1] | ||||||
|  |         yield sum(values[-3:]) | ||||||
|   | |||||||
| @@ -1,13 +1,16 @@ | |||||||
| import sys | from typing import Any, Iterator | ||||||
|  |  | ||||||
| lines = sys.stdin.read().splitlines() | from ..base import BaseSolver | ||||||
|  |  | ||||||
| cycle = 1 |  | ||||||
| x = 1 |  | ||||||
|  |  | ||||||
| values = {cycle: x} | class Solver(BaseSolver): | ||||||
|  |     def solve(self, input: str) -> Iterator[Any]: | ||||||
|  |         lines = [line.strip() for line in input.splitlines()] | ||||||
|  |  | ||||||
| for line in lines: |         cycle, x = 1, 1 | ||||||
|  |         values = {cycle: x} | ||||||
|  |  | ||||||
|  |         for line in lines: | ||||||
|             cycle += 1 |             cycle += 1 | ||||||
|  |  | ||||||
|             if line == "noop": |             if line == "noop": | ||||||
| @@ -22,17 +25,19 @@ for line in lines: | |||||||
|  |  | ||||||
|             values[cycle] = x |             values[cycle] = x | ||||||
|  |  | ||||||
| answer_1 = sum(c * values[c] for c in range(20, max(values.keys()) + 1, 40)) |         answer_1 = sum(c * values[c] for c in range(20, max(values.keys()) + 1, 40)) | ||||||
| print(f"answer 1 is {answer_1}") |         yield answer_1 | ||||||
|  |  | ||||||
|  |         yield ( | ||||||
| for i in range(6): |             "\n" | ||||||
|     for j in range(40): |             + "\n".join( | ||||||
|         v = values[1 + i * 40 + j] |                 "".join( | ||||||
|  |                     "#" | ||||||
|         if j >= v - 1 and j <= v + 1: |                     if j >= (v := values[1 + i * 40 + j]) - 1 and j <= v + 1 | ||||||
|             print("#", end="") |                     else "." | ||||||
|         else: |                     for j in range(40) | ||||||
|             print(".", end="") |                 ) | ||||||
|  |                 for i in range(6) | ||||||
|     print() |             ) | ||||||
|  |             + "\n" | ||||||
|  |         ) | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| import copy | import copy | ||||||
| import sys |  | ||||||
| from functools import reduce | from functools import reduce | ||||||
| from typing import Callable, Final, Mapping, Sequence | from typing import Any, Callable, Final, Iterator, Mapping, Sequence | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
|  |  | ||||||
| class Monkey: | class Monkey: | ||||||
| @@ -119,24 +120,28 @@ def monkey_business(inspects: dict[Monkey, int]) -> int: | |||||||
|     return sorted_levels[-2] * sorted_levels[-1] |     return sorted_levels[-2] * sorted_levels[-1] | ||||||
|  |  | ||||||
|  |  | ||||||
| monkeys = [parse_monkey(block.splitlines()) for block in sys.stdin.read().split("\n\n")] | class Solver(BaseSolver): | ||||||
|  |     def solve(self, input: str) -> Iterator[Any]: | ||||||
|  |         monkeys = [parse_monkey(block.splitlines()) for block in input.split("\n\n")] | ||||||
|  |  | ||||||
| # case 1: we simply divide the worry by 3 after applying the monkey worry operation |         # case 1: we simply divide the worry by 3 after applying the monkey worry operation | ||||||
| answer_1 = monkey_business( |         yield monkey_business( | ||||||
|             run(copy.deepcopy(monkeys), 20, me_worry_fn=lambda w: w // 3) |             run(copy.deepcopy(monkeys), 20, me_worry_fn=lambda w: w // 3) | ||||||
| ) |         ) | ||||||
| print(f"answer 1 is {answer_1}") |  | ||||||
|  |  | ||||||
| # case 2: to keep reasonable level values, we can use a modulo operation, we need to |         # case 2: to keep reasonable level values, we can use a modulo operation, we need to | ||||||
| # use the product of all "divisible by" test so that the test remains valid |         # use the product of all "divisible by" test so that the test remains valid | ||||||
| # |         # | ||||||
| # (a + b) % c == ((a % c) + (b % c)) % c --- this would work for a single test value |         # (a + b) % c == ((a % c) + (b % c)) % c --- this would work for a single test value | ||||||
| # |         # | ||||||
| # (a + b) % c == ((a % d) + (b % d)) % c --- if d is a multiple of c, which is why here |         # (a + b) % c == ((a % d) + (b % d)) % c --- if d is a multiple of c, which is why here | ||||||
| # we use the product of all test value |         # we use the product of all test value | ||||||
| # |         # | ||||||
| total_test_value = reduce(lambda w, m: w * m.test_value, monkeys, 1) |         total_test_value = reduce(lambda w, m: w * m.test_value, monkeys, 1) | ||||||
| answer_2 = monkey_business( |         yield monkey_business( | ||||||
|     run(copy.deepcopy(monkeys), 10_000, me_worry_fn=lambda w: w % total_test_value) |             run( | ||||||
| ) |                 copy.deepcopy(monkeys), | ||||||
| print(f"answer 2 is {answer_2}") |                 10_000, | ||||||
|  |                 me_worry_fn=lambda w: w % total_test_value, | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import heapq | import heapq | ||||||
| import sys | from typing import Any, Callable, Iterator, TypeVar | ||||||
| from typing import Callable, Iterator, TypeVar |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
| Node = TypeVar("Node") | Node = TypeVar("Node") | ||||||
|  |  | ||||||
| @@ -68,30 +69,6 @@ def make_path(parents: dict[Node, Node], start: Node, end: Node) -> list[Node] | | |||||||
|     return list(reversed(path)) |     return list(reversed(path)) | ||||||
|  |  | ||||||
|  |  | ||||||
| def print_path(path: list[tuple[int, int]], n_rows: int, n_cols: int) -> None: |  | ||||||
|     end = path[-1] |  | ||||||
|  |  | ||||||
|     graph = [["." for _c in range(n_cols)] for _r in range(n_rows)] |  | ||||||
|     graph[end[0]][end[1]] = "E" |  | ||||||
|  |  | ||||||
|     for i in range(0, len(path) - 1): |  | ||||||
|         cr, cc = path[i] |  | ||||||
|         nr, nc = path[i + 1] |  | ||||||
|  |  | ||||||
|         if cr == nr and nc == cc - 1: |  | ||||||
|             graph[cr][cc] = "<" |  | ||||||
|         elif cr == nr and nc == cc + 1: |  | ||||||
|             graph[cr][cc] = ">" |  | ||||||
|         elif cr == nr - 1 and nc == cc: |  | ||||||
|             graph[cr][cc] = "v" |  | ||||||
|         elif cr == nr + 1 and nc == cc: |  | ||||||
|             graph[cr][cc] = "^" |  | ||||||
|         else: |  | ||||||
|             assert False, "{} -> {} infeasible".format(path[i], path[i + 1]) |  | ||||||
|  |  | ||||||
|     print("\n".join("".join(row) for row in graph)) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def neighbors( | def neighbors( | ||||||
|     grid: list[list[int]], node: tuple[int, int], up: bool |     grid: list[list[int]], node: tuple[int, int], up: bool | ||||||
| ) -> Iterator[tuple[int, int]]: | ) -> Iterator[tuple[int, int]]: | ||||||
| @@ -118,17 +95,44 @@ def neighbors( | |||||||
|  |  | ||||||
| # === main code === | # === main code === | ||||||
|  |  | ||||||
| lines = sys.stdin.read().splitlines() |  | ||||||
|  |  | ||||||
| grid = [[ord(cell) - ord("a") for cell in line] for line in lines] | class Solver(BaseSolver): | ||||||
|  |     def print_path(self, path: list[tuple[int, int]], n_rows: int, n_cols: int) -> None: | ||||||
|  |         end = path[-1] | ||||||
|  |  | ||||||
| start: tuple[int, int] | None = None |         graph = [["." for _c in range(n_cols)] for _r in range(n_rows)] | ||||||
| end: tuple[int, int] | None = None |         graph[end[0]][end[1]] = "E" | ||||||
|  |  | ||||||
| # for part 2 |         for i in range(0, len(path) - 1): | ||||||
| start_s: list[tuple[int, int]] = [] |             cr, cc = path[i] | ||||||
|  |             nr, nc = path[i + 1] | ||||||
|  |  | ||||||
| for i_row, row in enumerate(grid): |             if cr == nr and nc == cc - 1: | ||||||
|  |                 graph[cr][cc] = "<" | ||||||
|  |             elif cr == nr and nc == cc + 1: | ||||||
|  |                 graph[cr][cc] = ">" | ||||||
|  |             elif cr == nr - 1 and nc == cc: | ||||||
|  |                 graph[cr][cc] = "v" | ||||||
|  |             elif cr == nr + 1 and nc == cc: | ||||||
|  |                 graph[cr][cc] = "^" | ||||||
|  |             else: | ||||||
|  |                 assert False, "{} -> {} infeasible".format(path[i], path[i + 1]) | ||||||
|  |  | ||||||
|  |         for row in graph: | ||||||
|  |             self.logger.info("".join(row)) | ||||||
|  |  | ||||||
|  |     def solve(self, input: str) -> Iterator[Any]: | ||||||
|  |         lines = input.splitlines() | ||||||
|  |  | ||||||
|  |         grid = [[ord(cell) - ord("a") for cell in line] for line in lines] | ||||||
|  |  | ||||||
|  |         start: tuple[int, int] | None = None | ||||||
|  |         end: tuple[int, int] | None = None | ||||||
|  |  | ||||||
|  |         # for part 2 | ||||||
|  |         start_s: list[tuple[int, int]] = [] | ||||||
|  |  | ||||||
|  |         for i_row, row in enumerate(grid): | ||||||
|             for i_col, col in enumerate(row): |             for i_col, col in enumerate(row): | ||||||
|                 if chr(col + ord("a")) == "S": |                 if chr(col + ord("a")) == "S": | ||||||
|                     start = (i_row, i_col) |                     start = (i_row, i_col) | ||||||
| @@ -138,26 +142,27 @@ for i_row, row in enumerate(grid): | |||||||
|                 elif col == 0: |                 elif col == 0: | ||||||
|                     start_s.append((i_row, i_col)) |                     start_s.append((i_row, i_col)) | ||||||
|  |  | ||||||
| assert start is not None |         assert start is not None | ||||||
| assert end is not None |         assert end is not None | ||||||
|  |  | ||||||
| # fix values |         # fix values | ||||||
| grid[start[0]][start[1]] = 0 |         grid[start[0]][start[1]] = 0 | ||||||
| grid[end[0]][end[1]] = ord("z") - ord("a") |         grid[end[0]][end[1]] = ord("z") - ord("a") | ||||||
|  |  | ||||||
|  |         lengths_1, parents_1 = dijkstra( | ||||||
|  |             start=start, | ||||||
|  |             neighbors=lambda n: neighbors(grid, n, True), | ||||||
|  |             cost=lambda lhs, rhs: 1, | ||||||
|  |         ) | ||||||
|  |         path_1 = make_path(parents_1, start, end) | ||||||
|  |         assert path_1 is not None | ||||||
|  |  | ||||||
| lengths_1, parents_1 = dijkstra( |         self.print_path(path_1, n_rows=len(grid), n_cols=len(grid[0])) | ||||||
|     start=start, neighbors=lambda n: neighbors(grid, n, True), cost=lambda lhs, rhs: 1 |         yield lengths_1[end] - 1 | ||||||
| ) |  | ||||||
| path_1 = make_path(parents_1, start, end) |  | ||||||
| assert path_1 is not None |  | ||||||
|  |  | ||||||
| print_path(path_1, n_rows=len(grid), n_cols=len(grid[0])) |         lengths_2, _ = dijkstra( | ||||||
|  |             start=end, | ||||||
| print(f"answer 1 is {lengths_1[end] - 1}") |             neighbors=lambda n: neighbors(grid, n, False), | ||||||
|  |             cost=lambda lhs, rhs: 1, | ||||||
| lengths_2, parents_2 = dijkstra( |         ) | ||||||
|     start=end, neighbors=lambda n: neighbors(grid, n, False), cost=lambda lhs, rhs: 1 |         yield min(lengths_2.get(start, float("inf")) for start in start_s) | ||||||
| ) |  | ||||||
| answer_2 = min(lengths_2.get(start, float("inf")) for start in start_s) |  | ||||||
| print(f"answer 2 is {answer_2}") |  | ||||||
|   | |||||||
| @@ -1,11 +1,8 @@ | |||||||
| import json | import json | ||||||
| import sys |  | ||||||
| from functools import cmp_to_key | from functools import cmp_to_key | ||||||
| from typing import TypeAlias, cast | from typing import Any, Iterator, TypeAlias, cast | ||||||
|  |  | ||||||
| blocks = sys.stdin.read().strip().split("\n\n") | from ..base import BaseSolver | ||||||
|  |  | ||||||
| pairs = [tuple(json.loads(p) for p in block.split("\n")) for block in blocks] |  | ||||||
|  |  | ||||||
| Packet: TypeAlias = list[int | list["Packet"]] | Packet: TypeAlias = list[int | list["Packet"]] | ||||||
|  |  | ||||||
| @@ -28,14 +25,18 @@ def compare(lhs: Packet, rhs: Packet) -> int: | |||||||
|     return len(rhs) - len(lhs) |     return len(rhs) - len(lhs) | ||||||
|  |  | ||||||
|  |  | ||||||
| answer_1 = sum(i + 1 for i, (lhs, rhs) in enumerate(pairs) if compare(lhs, rhs) > 0) | class Solver(BaseSolver): | ||||||
| print(f"answer_1 is {answer_1}") |     def solve(self, input: str) -> Iterator[Any]: | ||||||
|  |         blocks = input.split("\n\n") | ||||||
|  |         pairs = [tuple(json.loads(p) for p in block.split("\n")) for block in blocks] | ||||||
|  |  | ||||||
| dividers = [[[2]], [[6]]] |         yield sum(i + 1 for i, (lhs, rhs) in enumerate(pairs) if compare(lhs, rhs) > 0) | ||||||
|  |  | ||||||
| packets = [packet for packets in pairs for packet in packets] |         dividers = [[[2]], [[6]]] | ||||||
| packets.extend(dividers) |  | ||||||
| packets = list(reversed(sorted(packets, key=cmp_to_key(compare)))) |  | ||||||
|  |  | ||||||
| d_index = [packets.index(d) + 1 for d in dividers] |         packets = [packet for packets in pairs for packet in packets] | ||||||
| print(f"answer 2 is {d_index[0] * d_index[1]}") |         packets.extend(dividers) | ||||||
|  |         packets = list(reversed(sorted(packets, key=cmp_to_key(compare)))) | ||||||
|  |  | ||||||
|  |         d_index = [packets.index(d) + 1 for d in dividers] | ||||||
|  |         yield d_index[0] * d_index[1] | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import sys |  | ||||||
| from enum import Enum, auto | from enum import Enum, auto | ||||||
| from typing import Callable, cast | from typing import Any, Callable, Iterator, cast | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
|  |  | ||||||
| class Cell(Enum): | class Cell(Enum): | ||||||
| @@ -12,26 +13,6 @@ class Cell(Enum): | |||||||
|         return {Cell.AIR: ".", Cell.ROCK: "#", Cell.SAND: "O"}[self] |         return {Cell.AIR: ".", Cell.ROCK: "#", Cell.SAND: "O"}[self] | ||||||
|  |  | ||||||
|  |  | ||||||
| def print_blocks(blocks: dict[tuple[int, int], Cell]): |  | ||||||
|     """ |  | ||||||
|     Print the given set of blocks on a grid. |  | ||||||
|  |  | ||||||
|     Args: |  | ||||||
|         blocks: Set of blocks to print. |  | ||||||
|     """ |  | ||||||
|     x_min, y_min, x_max, y_max = ( |  | ||||||
|         min(x for x, _ in blocks), |  | ||||||
|         0, |  | ||||||
|         max(x for x, _ in blocks), |  | ||||||
|         max(y for _, y in blocks), |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     for y in range(y_min, y_max + 1): |  | ||||||
|         print( |  | ||||||
|             "".join(str(blocks.get((x, y), Cell.AIR)) for x in range(x_min, x_max + 1)) |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def flow( | def flow( | ||||||
|     blocks: dict[tuple[int, int], Cell], |     blocks: dict[tuple[int, int], Cell], | ||||||
|     stop_fn: Callable[[int, int], bool], |     stop_fn: Callable[[int, int], bool], | ||||||
| @@ -84,21 +65,46 @@ def flow( | |||||||
|  |  | ||||||
| # === inputs === | # === inputs === | ||||||
|  |  | ||||||
| lines = sys.stdin.read().splitlines() |  | ||||||
|  |  | ||||||
| paths: list[list[tuple[int, int]]] = [] | class Solver(BaseSolver): | ||||||
| for line in lines: |     def print_blocks(self, blocks: dict[tuple[int, int], Cell]): | ||||||
|  |         """ | ||||||
|  |         Print the given set of blocks on a grid. | ||||||
|  |  | ||||||
|  |         Args: | ||||||
|  |             blocks: Set of blocks to print. | ||||||
|  |         """ | ||||||
|  |         x_min, y_min, x_max, y_max = ( | ||||||
|  |             min(x for x, _ in blocks), | ||||||
|  |             0, | ||||||
|  |             max(x for x, _ in blocks), | ||||||
|  |             max(y for _, y in blocks), | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         for y in range(y_min, y_max + 1): | ||||||
|  |             self.logger.info( | ||||||
|  |                 "".join( | ||||||
|  |                     str(blocks.get((x, y), Cell.AIR)) for x in range(x_min, x_max + 1) | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |     def solve(self, input: str) -> Iterator[Any]: | ||||||
|  |         lines = [line.strip() for line in input.splitlines()] | ||||||
|  |  | ||||||
|  |         paths: list[list[tuple[int, int]]] = [] | ||||||
|  |         for line in lines: | ||||||
|             parts = line.split(" -> ") |             parts = line.split(" -> ") | ||||||
|             paths.append( |             paths.append( | ||||||
|                 [ |                 [ | ||||||
|             cast(tuple[int, int], tuple(int(c.strip()) for c in part.split(","))) |                     cast( | ||||||
|  |                         tuple[int, int], tuple(int(c.strip()) for c in part.split(",")) | ||||||
|  |                     ) | ||||||
|                     for part in parts |                     for part in parts | ||||||
|                 ] |                 ] | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|  |         blocks: dict[tuple[int, int], Cell] = {} | ||||||
| blocks: dict[tuple[int, int], Cell] = {} |         for path in paths: | ||||||
| for path in paths: |  | ||||||
|             for start, end in zip(path[:-1], path[1:]): |             for start, end in zip(path[:-1], path[1:]): | ||||||
|                 x_start = min(start[0], end[0]) |                 x_start = min(start[0], end[0]) | ||||||
|                 x_end = max(start[0], end[0]) + 1 |                 x_end = max(start[0], end[0]) + 1 | ||||||
| @@ -109,32 +115,25 @@ for path in paths: | |||||||
|                     for y in range(y_start, y_end): |                     for y in range(y_start, y_end): | ||||||
|                         blocks[x, y] = Cell.ROCK |                         blocks[x, y] = Cell.ROCK | ||||||
|  |  | ||||||
| print_blocks(blocks) |         self.print_blocks(blocks) | ||||||
| print() |  | ||||||
|  |  | ||||||
| x_min, y_min, x_max, y_max = ( |         y_max = max(y for _, y in blocks) | ||||||
|     min(x for x, _ in blocks), |  | ||||||
|     0, |  | ||||||
|     max(x for x, _ in blocks), |  | ||||||
|     max(y for _, y in blocks), |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| # === part 1 === |         # === part 1 === | ||||||
|  |  | ||||||
| blocks_1 = flow( |         blocks_1 = flow( | ||||||
|             blocks.copy(), stop_fn=lambda x, y: y > y_max, fill_fn=lambda x, y: Cell.AIR |             blocks.copy(), stop_fn=lambda x, y: y > y_max, fill_fn=lambda x, y: Cell.AIR | ||||||
| ) |         ) | ||||||
| print_blocks(blocks_1) |         self.print_blocks(blocks_1) | ||||||
| print(f"answer 1 is {sum(v == Cell.SAND for v in blocks_1.values())}") |         yield sum(v == Cell.SAND for v in blocks_1.values()) | ||||||
| print() |  | ||||||
|  |  | ||||||
| # === part 2 === |         # === part 2 === | ||||||
|  |  | ||||||
| blocks_2 = flow( |         blocks_2 = flow( | ||||||
|             blocks.copy(), |             blocks.copy(), | ||||||
|             stop_fn=lambda x, y: x == 500 and y == 0, |             stop_fn=lambda x, y: x == 500 and y == 0, | ||||||
|             fill_fn=lambda x, y: Cell.AIR if y < y_max + 2 else Cell.ROCK, |             fill_fn=lambda x, y: Cell.AIR if y < y_max + 2 else Cell.ROCK, | ||||||
| ) |         ) | ||||||
| blocks_2[500, 0] = Cell.SAND |         blocks_2[500, 0] = Cell.SAND | ||||||
| print_blocks(blocks_2) |         self.print_blocks(blocks_2) | ||||||
| print(f"answer 2 is {sum(v == Cell.SAND for v in blocks_2.values())}") |         yield sum(v == Cell.SAND for v in blocks_2.values()) | ||||||
|   | |||||||
| @@ -1,4 +1,3 @@ | |||||||
| import sys |  | ||||||
| from typing import Any, Iterator | from typing import Any, Iterator | ||||||
|  |  | ||||||
| import numpy as np | import numpy as np | ||||||
| @@ -62,8 +61,9 @@ class Solver(BaseSolver): | |||||||
|         for (sx, sy), (bx, by) in sensor_to_beacon.items(): |         for (sx, sy), (bx, by) in sensor_to_beacon.items(): | ||||||
|             d = abs(sx - bx) + abs(sy - by) |             d = abs(sx - bx) + abs(sy - by) | ||||||
|             m.add_constraint( |             m.add_constraint( | ||||||
|                 m.abs(x - sx) + m.abs(y - sy) >= d + 1, ctname=f"ct_{sx}_{sy}" |                 m.abs(x - sx) + m.abs(y - sy) >= d + 1,  # type: ignore | ||||||
|             )  # type: ignore |                 ctname=f"ct_{sx}_{sy}", | ||||||
|  |             ) | ||||||
|  |  | ||||||
|         m.set_objective("min", x + y) |         m.set_objective("min", x + y) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,10 +5,12 @@ import itertools | |||||||
| import re | import re | ||||||
| import sys | import sys | ||||||
| from collections import defaultdict | from collections import defaultdict | ||||||
| from typing import FrozenSet, NamedTuple | from typing import Any, FrozenSet, Iterator, NamedTuple | ||||||
|  |  | ||||||
| from tqdm import tqdm | from tqdm import tqdm | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
|  |  | ||||||
| class Pipe(NamedTuple): | class Pipe(NamedTuple): | ||||||
|     name: str |     name: str | ||||||
|   | |||||||
| @@ -1,8 +1,10 @@ | |||||||
| import sys | import sys | ||||||
| from typing import Sequence, TypeVar | from typing import Any, Iterator, Sequence, TypeVar | ||||||
|  |  | ||||||
| import numpy as np | import numpy as np | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
| T = TypeVar("T") | T = TypeVar("T") | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,10 @@ | |||||||
| import sys | import sys | ||||||
|  | from typing import Any, Iterator | ||||||
|  |  | ||||||
| import numpy as np | import numpy as np | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
| xyz = np.asarray( | xyz = np.asarray( | ||||||
|     [ |     [ | ||||||
|         tuple(int(x) for x in row.split(","))  # type: ignore |         tuple(int(x) for x in row.split(","))  # type: ignore | ||||||
|   | |||||||
| @@ -1,10 +1,12 @@ | |||||||
| import sys | import sys | ||||||
| from typing import Any, Literal | from typing import Any, Iterator, Literal | ||||||
|  |  | ||||||
| import numpy as np | import numpy as np | ||||||
| import parse  # pyright: ignore[reportMissingTypeStubs] | import parse  # pyright: ignore[reportMissingTypeStubs] | ||||||
| from numpy.typing import NDArray | from numpy.typing import NDArray | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
| Reagent = Literal["ore", "clay", "obsidian", "geode"] | Reagent = Literal["ore", "clay", "obsidian", "geode"] | ||||||
| REAGENTS: tuple[Reagent, ...] = ( | REAGENTS: tuple[Reagent, ...] = ( | ||||||
|     "ore", |     "ore", | ||||||
|   | |||||||
| @@ -1,4 +1,6 @@ | |||||||
| import sys | from typing import Any, Iterator | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
|  |  | ||||||
| def score_1(ux: int, vx: int) -> int: | def score_1(ux: int, vx: int) -> int: | ||||||
| @@ -33,21 +35,23 @@ def score_2(ux: int, vx: int) -> int: | |||||||
|     return (ux + vx - 1) % 3 + 1 + vx * 3 |     return (ux + vx - 1) % 3 + 1 + vx * 3 | ||||||
|  |  | ||||||
|  |  | ||||||
| lines = sys.stdin.readlines() | class Solver(BaseSolver): | ||||||
|  |     def solve(self, input: str) -> Iterator[Any]: | ||||||
|  |         lines = input.splitlines() | ||||||
|  |  | ||||||
| # the solution relies on replacing rock / paper / scissor by values 0 / 1 / 2 and using |         # the solution relies on replacing rock / paper / scissor by values 0 / 1 / 2 and using | ||||||
| # modulo-3 arithmetic |         # modulo-3 arithmetic | ||||||
| # |         # | ||||||
| # in modulo-3 arithmetic, the winning move is 1 + the opponent move (e.g., winning move |         # in modulo-3 arithmetic, the winning move is 1 + the opponent move (e.g., winning move | ||||||
| # if opponent plays 0 is 1, or 0 if opponent plays 2 (0 = (2 + 1 % 3))) |         # if opponent plays 0 is 1, or 0 if opponent plays 2 (0 = (2 + 1 % 3))) | ||||||
| # |         # | ||||||
|  |  | ||||||
| # we read the lines in a Nx2 in array with value 0/1/2 instead of A/B/C or X/Y/Z for |         # we read the lines in a Nx2 in array with value 0/1/2 instead of A/B/C or X/Y/Z for | ||||||
| # easier manipulation |         # easier manipulation | ||||||
| values = [(ord(row[0]) - ord("A"), ord(row[2]) - ord("X")) for row in lines] |         values = [(ord(row[0]) - ord("A"), ord(row[2]) - ord("X")) for row in lines] | ||||||
|  |  | ||||||
| # part 1 - 13526 |         # part 1 - 13526 | ||||||
| print(f"answer 1 is {sum(score_1(*v) for v in values)}") |         yield sum(score_1(*v) for v in values) | ||||||
|  |  | ||||||
| # part 2 - 14204 |         # part 2 - 14204 | ||||||
| print(f"answer 2 is {sum(score_2(*v) for v in values)}") |         yield sum(score_2(*v) for v in values) | ||||||
|   | |||||||
| @@ -1,6 +1,9 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| import sys | import sys | ||||||
|  | from typing import Any, Iterator | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
|  |  | ||||||
| class Number: | class Number: | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| import operator | import operator | ||||||
| import sys | import sys | ||||||
| from typing import Callable | from typing import Any, Callable, Iterator | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
|  |  | ||||||
| def compute(monkeys: dict[str, int | tuple[str, str, str]], monkey: str) -> int: | def compute(monkeys: dict[str, int | tuple[str, str, str]], monkey: str) -> int: | ||||||
|   | |||||||
| @@ -1,9 +1,11 @@ | |||||||
| import re | import re | ||||||
| import sys | import sys | ||||||
| from typing import Callable | from typing import Any, Callable, Iterator | ||||||
|  |  | ||||||
| import numpy as np | import numpy as np | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
| VOID, EMPTY, WALL = 0, 1, 2 | VOID, EMPTY, WALL = 0, 1, 2 | ||||||
| TILE_FROM_CHAR = {" ": VOID, ".": EMPTY, "#": WALL} | TILE_FROM_CHAR = {" ": VOID, ".": EMPTY, "#": WALL} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,9 @@ | |||||||
| import itertools | import itertools | ||||||
| import sys | import sys | ||||||
| from collections import defaultdict | from collections import defaultdict | ||||||
|  | from typing import Any, Iterator | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
| Directions = list[ | Directions = list[ | ||||||
|     tuple[ |     tuple[ | ||||||
|   | |||||||
| @@ -2,6 +2,9 @@ import heapq | |||||||
| import math | import math | ||||||
| import sys | import sys | ||||||
| from collections import defaultdict | from collections import defaultdict | ||||||
|  | from typing import Any, Iterator | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
| lines = sys.stdin.read().splitlines() | lines = sys.stdin.read().splitlines() | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,7 @@ | |||||||
| import sys | import sys | ||||||
|  | from typing import Any, Iterator | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
| lines = sys.stdin.read().splitlines() | lines = sys.stdin.read().splitlines() | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,23 +1,28 @@ | |||||||
| import string | import string | ||||||
| import sys | from typing import Any, Iterator | ||||||
|  |  | ||||||
| lines = [line.strip() for line in sys.stdin.readlines()] | from ..base import BaseSolver | ||||||
|  |  | ||||||
| # extract content of each part |  | ||||||
| parts = [(set(line[: len(line) // 2]), set(line[len(line) // 2 :])) for line in lines] |  | ||||||
|  |  | ||||||
| # priorities | class Solver(BaseSolver): | ||||||
| priorities = {c: i + 1 for i, c in enumerate(string.ascii_letters)} |     def solve(self, input: str) -> Iterator[Any]: | ||||||
|  |         lines = [line.strip() for line in input.splitlines()] | ||||||
|  |  | ||||||
| # part 1 |         # extract content of each part | ||||||
| part1 = sum(priorities[c] for p1, p2 in parts for c in p1.intersection(p2)) |         parts = [ | ||||||
| print(f"answer 1 is {part1}") |             (set(line[: len(line) // 2]), set(line[len(line) // 2 :])) for line in lines | ||||||
|  |         ] | ||||||
|  |  | ||||||
| # part 2 |         # priorities | ||||||
| n_per_group = 3 |         priorities = {c: i + 1 for i, c in enumerate(string.ascii_letters)} | ||||||
| part2 = sum( |  | ||||||
|  |         # part 1 | ||||||
|  |         yield sum(priorities[c] for p1, p2 in parts for c in p1.intersection(p2)) | ||||||
|  |  | ||||||
|  |         # part 2 | ||||||
|  |         n_per_group = 3 | ||||||
|  |         yield sum( | ||||||
|             priorities[c] |             priorities[c] | ||||||
|             for i in range(0, len(lines), n_per_group) |             for i in range(0, len(lines), n_per_group) | ||||||
|             for c in set(lines[i]).intersection(*lines[i + 1 : i + n_per_group]) |             for c in set(lines[i]).intersection(*lines[i + 1 : i + n_per_group]) | ||||||
| ) |         ) | ||||||
| print(f"answer 2 is {part2}") |  | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import sys | from typing import Any, Iterator | ||||||
|  |  | ||||||
| lines = [line.strip() for line in sys.stdin.readlines()] | from ..base import BaseSolver | ||||||
|  |  | ||||||
|  |  | ||||||
| def make_range(value: str) -> set[int]: | def make_range(value: str) -> set[int]: | ||||||
| @@ -8,10 +8,13 @@ def make_range(value: str) -> set[int]: | |||||||
|     return set(range(int(parts[0]), int(parts[1]) + 1)) |     return set(range(int(parts[0]), int(parts[1]) + 1)) | ||||||
|  |  | ||||||
|  |  | ||||||
| sections = [tuple(make_range(part) for part in line.split(",")) for line in lines] | class Solver(BaseSolver): | ||||||
|  |     def solve(self, input: str) -> Iterator[Any]: | ||||||
|  |         lines = [line.strip() for line in input.splitlines()] | ||||||
|  |  | ||||||
| answer_1 = sum(s1.issubset(s2) or s2.issubset(s1) for s1, s2 in sections) |         sections = [ | ||||||
| print(f"answer 1 is {answer_1}") |             tuple(make_range(part) for part in line.split(",")) for line in lines | ||||||
|  |         ] | ||||||
|  |  | ||||||
| answer_2 = sum(bool(s1.intersection(s2)) for s1, s2 in sections) |         yield sum(s1.issubset(s2) or s2.issubset(s1) for s1, s2 in sections) | ||||||
| print(f"answer 1 is {answer_2}") |         yield sum(bool(s1.intersection(s2)) for s1, s2 in sections) | ||||||
|   | |||||||
| @@ -1,41 +1,43 @@ | |||||||
| import copy | import copy | ||||||
| import sys | from typing import Any, Iterator | ||||||
|  |  | ||||||
| blocks_s, moves_s = (part.splitlines() for part in sys.stdin.read().split("\n\n")) | from ..base import BaseSolver | ||||||
|  |  | ||||||
| blocks: dict[str, list[str]] = {stack: [] for stack in blocks_s[-1].split()} |  | ||||||
|  |  | ||||||
| # this codes assumes that the lines are regular, i.e., 4 characters per "crate" in the | class Solver(BaseSolver): | ||||||
| # form of '[X] ' (including the trailing space) |     def solve(self, input: str) -> Iterator[Any]: | ||||||
| # |         blocks_s, moves_s = (part.splitlines() for part in input.split("\n\n")) | ||||||
| for block in blocks_s[-2::-1]: |  | ||||||
|  |         blocks: dict[str, list[str]] = {stack: [] for stack in blocks_s[-1].split()} | ||||||
|  |  | ||||||
|  |         # this codes assumes that the lines are regular, i.e., 4 characters per "crate" in the | ||||||
|  |         # form of '[X] ' (including the trailing space) | ||||||
|  |         # | ||||||
|  |         for block in blocks_s[-2::-1]: | ||||||
|             for stack, index in zip(blocks, range(0, len(block), 4)): |             for stack, index in zip(blocks, range(0, len(block), 4)): | ||||||
|                 crate = block[index + 1 : index + 2].strip() |                 crate = block[index + 1 : index + 2].strip() | ||||||
|  |  | ||||||
|                 if crate: |                 if crate: | ||||||
|                     blocks[stack].append(crate) |                     blocks[stack].append(crate) | ||||||
|  |  | ||||||
| # part 1 - deep copy for part 2 |         # part 1 - deep copy for part 2 | ||||||
| blocks_1 = copy.deepcopy(blocks) |         blocks_1 = copy.deepcopy(blocks) | ||||||
|  |  | ||||||
| for move in moves_s: |         for move in moves_s: | ||||||
|             _, count_s, _, from_, _, to_ = move.strip().split() |             _, count_s, _, from_, _, to_ = move.strip().split() | ||||||
|  |  | ||||||
|             for _i in range(int(count_s)): |             for _i in range(int(count_s)): | ||||||
|                 blocks_1[to_].append(blocks_1[from_].pop()) |                 blocks_1[to_].append(blocks_1[from_].pop()) | ||||||
|  |  | ||||||
| # part 2 |         # part 2 | ||||||
| blocks_2 = copy.deepcopy(blocks) |         blocks_2 = copy.deepcopy(blocks) | ||||||
|  |  | ||||||
| for move in moves_s: |         for move in moves_s: | ||||||
|             _, count_s, _, from_, _, to_ = move.strip().split() |             _, count_s, _, from_, _, to_ = move.strip().split() | ||||||
|             count = int(count_s) |             count = int(count_s) | ||||||
|  |  | ||||||
|             blocks_2[to_].extend(blocks_2[from_][-count:]) |             blocks_2[to_].extend(blocks_2[from_][-count:]) | ||||||
|             del blocks_2[from_][-count:] |             del blocks_2[from_][-count:] | ||||||
|  |  | ||||||
| answer_1 = "".join(s[-1] for s in blocks_1.values()) |         yield "".join(s[-1] for s in blocks_1.values()) | ||||||
| print(f"answer 1 is {answer_1}") |         yield "".join(s[-1] for s in blocks_2.values()) | ||||||
|  |  | ||||||
| answer_2 = "".join(s[-1] for s in blocks_2.values()) |  | ||||||
| print(f"answer 2 is {answer_2}") |  | ||||||
|   | |||||||
| @@ -1,4 +1,6 @@ | |||||||
| import sys | from typing import Any, Iterator | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
|  |  | ||||||
| def index_of_first_n_differents(data: str, n: int) -> int: | def index_of_first_n_differents(data: str, n: int) -> int: | ||||||
| @@ -8,8 +10,7 @@ def index_of_first_n_differents(data: str, n: int) -> int: | |||||||
|     return -1 |     return -1 | ||||||
|  |  | ||||||
|  |  | ||||||
| data = sys.stdin.read().strip() | class Solver(BaseSolver): | ||||||
|  |     def solve(self, input: str) -> Iterator[Any]: | ||||||
|  |         yield index_of_first_n_differents(input, 4) | ||||||
| print(f"answer 1 is {index_of_first_n_differents(data, 4)}") |         yield index_of_first_n_differents(input, 14) | ||||||
| print(f"answer 2 is {index_of_first_n_differents(data, 14)}") |  | ||||||
|   | |||||||
| @@ -1,30 +1,35 @@ | |||||||
| import sys |  | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
|  | from typing import Any, Iterator | ||||||
|  |  | ||||||
| lines = sys.stdin.read().splitlines() | from ..base import BaseSolver | ||||||
|  |  | ||||||
| # we are going to use Path to create path and go up/down in the file tree since it |  | ||||||
| # implements everything we need |  | ||||||
| # |  | ||||||
| # we can use .resolve() to get normalized path, although this will add C:\ to all paths |  | ||||||
| # on Windows but that is not an issue since only the sizes matter |  | ||||||
| # |  | ||||||
|  |  | ||||||
| # mapping from path to list of files or directories | class Solver(BaseSolver): | ||||||
| trees: dict[Path, list[Path]] = {} |     def solve(self, input: str) -> Iterator[Any]: | ||||||
|  |         lines = [line.strip() for line in input.splitlines()] | ||||||
|  |  | ||||||
| # mapping from paths to either size (for file) or -1 for directory |         # we are going to use Path to create path and go up/down in the file tree since it | ||||||
| sizes: dict[Path, int] = {} |         # implements everything we need | ||||||
|  |         # | ||||||
|  |         # we can use .resolve() to get normalized path, although this will add C:\ to all paths | ||||||
|  |         # on Windows but that is not an issue since only the sizes matter | ||||||
|  |         # | ||||||
|  |  | ||||||
| # first line must be a cd otherwise we have no idea where we are |         # mapping from path to list of files or directories | ||||||
| assert lines[0].startswith("$ cd") |         trees: dict[Path, list[Path]] = {} | ||||||
| base_path = Path(lines[0].strip("$").split()[1]).resolve() |  | ||||||
| cur_path = base_path |  | ||||||
|  |  | ||||||
| trees[cur_path] = [] |         # mapping from paths to either size (for file) or -1 for directory | ||||||
| sizes[cur_path] = -1 |         sizes: dict[Path, int] = {} | ||||||
|  |  | ||||||
| for line in lines[1:]: |         # first line must be a cd otherwise we have no idea where we are | ||||||
|  |         assert lines[0].startswith("$ cd") | ||||||
|  |         base_path = Path(lines[0].strip("$").split()[1]).resolve() | ||||||
|  |         cur_path = base_path | ||||||
|  |  | ||||||
|  |         trees[cur_path] = [] | ||||||
|  |         sizes[cur_path] = -1 | ||||||
|  |  | ||||||
|  |         for line in lines[1:]: | ||||||
|             # command |             # command | ||||||
|             if line.startswith("$"): |             if line.startswith("$"): | ||||||
|                 parts = line.strip("$").strip().split() |                 parts = line.strip("$").strip().split() | ||||||
| @@ -53,8 +58,7 @@ for line in lines[1:]: | |||||||
|                 trees[cur_path].append(path) |                 trees[cur_path].append(path) | ||||||
|                 sizes[path] = size |                 sizes[path] = size | ||||||
|  |  | ||||||
|  |         def compute_size(path: Path) -> int: | ||||||
| def compute_size(path: Path) -> int: |  | ||||||
|             size = sizes[path] |             size = sizes[path] | ||||||
|  |  | ||||||
|             if size >= 0: |             if size >= 0: | ||||||
| @@ -62,19 +66,16 @@ def compute_size(path: Path) -> int: | |||||||
|  |  | ||||||
|             return sum(compute_size(sub) for sub in trees[path]) |             return sum(compute_size(sub) for sub in trees[path]) | ||||||
|  |  | ||||||
|  |         acc_sizes = {path: compute_size(path) for path in trees} | ||||||
|  |  | ||||||
| acc_sizes = {path: compute_size(path) for path in trees} |         # part 1 | ||||||
|  |         yield sum(size for size in acc_sizes.values() if size <= 100_000) | ||||||
|  |  | ||||||
| # part 1 |         # part 2 | ||||||
| answer_1 = sum(size for size in acc_sizes.values() if size <= 100_000) |         total_space = 70_000_000 | ||||||
| print(f"answer 1 is {answer_1}") |         update_space = 30_000_000 | ||||||
|  |         free_space = total_space - acc_sizes[base_path] | ||||||
|  |  | ||||||
| # part 2 |         to_free_space = update_space - free_space | ||||||
| total_space = 70_000_000 |  | ||||||
| update_space = 30_000_000 |  | ||||||
| free_space = total_space - acc_sizes[base_path] |  | ||||||
|  |  | ||||||
| to_free_space = update_space - free_space |         yield min(size for size in acc_sizes.values() if size >= to_free_space) | ||||||
|  |  | ||||||
| answer_2 = min(size for size in acc_sizes.values() if size >= to_free_space) |  | ||||||
| print(f"answer 2 is {answer_2}") |  | ||||||
|   | |||||||
| @@ -1,15 +1,20 @@ | |||||||
| import sys | from typing import Any, Iterator | ||||||
|  |  | ||||||
| import numpy as np | import numpy as np | ||||||
| from numpy.typing import NDArray | from numpy.typing import NDArray | ||||||
|  |  | ||||||
| lines = sys.stdin.read().splitlines() | from ..base import BaseSolver | ||||||
|  |  | ||||||
| trees = np.array([[int(x) for x in row] for row in lines]) |  | ||||||
|  |  | ||||||
| # answer 1 | class Solver(BaseSolver): | ||||||
| highest_trees = np.ones(trees.shape + (4,), dtype=int) * -1 |     def solve(self, input: str) -> Iterator[Any]: | ||||||
| highest_trees[1:-1, 1:-1] = [ |         lines = [line.strip() for line in input.splitlines()] | ||||||
|  |  | ||||||
|  |         trees = np.array([[int(x) for x in row] for row in lines]) | ||||||
|  |  | ||||||
|  |         # answer 1 | ||||||
|  |         highest_trees = np.ones(trees.shape + (4,), dtype=int) * -1 | ||||||
|  |         highest_trees[1:-1, 1:-1] = [ | ||||||
|             [ |             [ | ||||||
|                 [ |                 [ | ||||||
|                     trees[:i, j].max(), |                     trees[:i, j].max(), | ||||||
| @@ -20,13 +25,11 @@ highest_trees[1:-1, 1:-1] = [ | |||||||
|                 for j in range(1, trees.shape[1] - 1) |                 for j in range(1, trees.shape[1] - 1) | ||||||
|             ] |             ] | ||||||
|             for i in range(1, trees.shape[0] - 1) |             for i in range(1, trees.shape[0] - 1) | ||||||
| ] |         ] | ||||||
|  |  | ||||||
| answer_1 = (highest_trees.min(axis=2) < trees).sum() |         yield (highest_trees.min(axis=2) < trees).sum() | ||||||
| print(f"answer 1 is {answer_1}") |  | ||||||
|  |  | ||||||
|  |         def viewing_distance(row_of_trees: NDArray[np.int_], value: int) -> int: | ||||||
| def viewing_distance(row_of_trees: NDArray[np.int_], value: int) -> int: |  | ||||||
|             w = np.where(row_of_trees >= value)[0] |             w = np.where(row_of_trees >= value)[0] | ||||||
|  |  | ||||||
|             if not w.size: |             if not w.size: | ||||||
| @@ -34,10 +37,9 @@ def viewing_distance(row_of_trees: NDArray[np.int_], value: int) -> int: | |||||||
|  |  | ||||||
|             return w[0] + 1 |             return w[0] + 1 | ||||||
|  |  | ||||||
|  |         # answer 2 | ||||||
| # answer 2 |         v_distances = np.zeros(trees.shape + (4,), dtype=int) | ||||||
| v_distances = np.zeros(trees.shape + (4,), dtype=int) |         v_distances[1:-1, 1:-1, :] = [ | ||||||
| v_distances[1:-1, 1:-1, :] = [ |  | ||||||
|             [ |             [ | ||||||
|                 [ |                 [ | ||||||
|                     viewing_distance(trees[i - 1 :: -1, j], trees[i, j]), |                     viewing_distance(trees[i - 1 :: -1, j], trees[i, j]), | ||||||
| @@ -48,6 +50,5 @@ v_distances[1:-1, 1:-1, :] = [ | |||||||
|                 for j in range(1, trees.shape[1] - 1) |                 for j in range(1, trees.shape[1] - 1) | ||||||
|             ] |             ] | ||||||
|             for i in range(1, trees.shape[0] - 1) |             for i in range(1, trees.shape[0] - 1) | ||||||
| ] |         ] | ||||||
| answer_2 = np.prod(v_distances, axis=2).max() |         yield np.prod(v_distances, axis=2).max() | ||||||
| print(f"answer 2 is {answer_2}") |  | ||||||
|   | |||||||
| @@ -1,7 +1,10 @@ | |||||||
| import sys | import itertools as it | ||||||
|  | from typing import Any, Iterator | ||||||
|  |  | ||||||
| import numpy as np | import numpy as np | ||||||
|  |  | ||||||
|  | from ..base import BaseSolver | ||||||
|  |  | ||||||
|  |  | ||||||
| def move(head: tuple[int, int], command: str) -> tuple[int, int]: | def move(head: tuple[int, int], command: str) -> tuple[int, int]: | ||||||
|     h_col, h_row = head |     h_col, h_row = head | ||||||
| @@ -43,17 +46,14 @@ def run(commands: list[str], n_blocks: int) -> list[tuple[int, int]]: | |||||||
|     return visited |     return visited | ||||||
|  |  | ||||||
|  |  | ||||||
| lines = sys.stdin.read().splitlines() | class Solver(BaseSolver): | ||||||
|  |     def solve(self, input: str) -> Iterator[Any]: | ||||||
|  |         lines = [line.strip() for line in input.splitlines()] | ||||||
|  |  | ||||||
| # flatten the commands |         # flatten the commands | ||||||
| commands: list[str] = [] |         commands = list( | ||||||
| for line in lines: |             it.chain(*(p[0] * int(p[1]) for line in lines if (p := line.split()))) | ||||||
|     d, c = line.split() |         ) | ||||||
|     commands.extend(d * int(c)) |  | ||||||
|  |  | ||||||
|  |         yield len(set(run(commands, n_blocks=2))) | ||||||
| visited_1 = run(commands, n_blocks=2) |         yield len(set(run(commands, n_blocks=10))) | ||||||
| print(f"answer 1 is {len(set(visited_1))}") |  | ||||||
|  |  | ||||||
| visited_2 = run(commands, n_blocks=10) |  | ||||||
| print(f"answer 2 is {len(set(visited_2))}") |  | ||||||
|   | |||||||
| @@ -1,3 +1,5 @@ | |||||||
|  | # pyright: reportUnknownMemberType=false | ||||||
|  |  | ||||||
| from typing import Any, Iterator | from typing import Any, Iterator | ||||||
|  |  | ||||||
| import networkx as nx | import networkx as nx | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user