Skip to content

Commit

Permalink
Merge branch 'main' into y2022d16
Browse files Browse the repository at this point in the history
  • Loading branch information
cj81499 authored Dec 23, 2022
2 parents 5a9fa50 + 34677b4 commit c834086
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 6 deletions.
6 changes: 5 additions & 1 deletion src/aoc_cj/aoc2022/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
| 20 | [Grove Positioning System](https://adventofcode.com/2022/day/20) | :x: | :x: | :x: |
| 21 | [Monkey Math](https://adventofcode.com/2022/day/21) | :star: | :star: | :white_check_mark: |
| 22 | [Monkey Map](https://adventofcode.com/2022/day/22) | :star: | :x: | :white_check_mark: |
| 23 | [?](https://adventofcode.com/2022/day/23) | :x: | :x: | :x: |
| 23 | [Unstable Diffusion](https://adventofcode.com/2022/day/23) | :star: | :x: | :white_check_mark: |
| 24 | [?](https://adventofcode.com/2022/day/24) | :x: | :x: | :x: |
| 25 | [?](https://adventofcode.com/2022/day/25) | :x: | :x: | :x: |

Expand All @@ -33,9 +33,13 @@
```text
--------Part 1-------- --------Part 2--------
Day Time Rank Score Time Rank Score
23 00:44:15 948 0 00:46:30 813 0
22 00:35:52 555 0 - - -
21 00:22:07 2285 0 23:39:04 14617 0
20 01:11:32 1938 0 - - -
18 00:08:16 1039 0 00:23:34 629 0
17 00:53:01 1088 0 - - -
16 >24h 14398 0 - - -
15 00:49:02 3216 0 01:13:55 1758 0
14 00:20:30 676 0 00:25:16 705 0
13 00:24:30 1427 0 00:32:16 1338 0
Expand Down
8 changes: 4 additions & 4 deletions src/aoc_cj/aoc2022/day21.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import abc
import enum
from typing import Callable, Union
from typing import Callable, Optional, Union

IntFn = Callable[[int, int], int]

Expand Down Expand Up @@ -92,7 +92,7 @@ def __init__(self, name: str, left: str, op: Operation, right: str) -> None:
self.op = op
self.right = right

self._resolved: ResolvedMonkey | None = None
self._resolved: Optional[ResolvedMonkey] = None

def evaluate(self, monkeys: dict[str, Monkey]) -> int:
return self._resolve(monkeys).evaluate(monkeys)
Expand Down Expand Up @@ -130,8 +130,8 @@ def __init__(self, name: str, left: Monkey, op: Operation, right: Monkey) -> Non
self.op = op
self.right = right

self._result: int | None = None
self._contains_humn: bool | None = None
self._result: Optional[int] = None
self._contains_humn: Optional[bool] = None

def evaluate(self, monkeys: dict[str, Monkey]) -> int:
if self._result is None:
Expand Down
3 changes: 2 additions & 1 deletion src/aoc_cj/aoc2022/day22.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections.abc import Generator
from typing import Union

import more_itertools as mi

Expand All @@ -13,7 +14,7 @@
FACING_TO_INT = {RIGHT: 0, DOWN: 1, LEFT: 2, UP: 3}


def parse_map_path(path: str) -> Generator[int | str, None, None]:
def parse_map_path(path: str) -> Generator[Union[int, str], None, None]:
for chunk in mi.split_when(path, lambda x, y: x.isnumeric() != y.isnumeric()):
joined = "".join(chunk)
yield int(joined) if joined.isnumeric() else joined
Expand Down
116 changes: 116 additions & 0 deletions src/aoc_cj/aoc2022/day23.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from collections import defaultdict, deque
from collections.abc import Iterable
from typing import DefaultDict, Deque, Literal

CardinalDirection = Literal["N", "W", "E", "S"]
Direction = Literal[CardinalDirection, "NW", "NE", "SW", "SE"]

NORTH = complex(0, -1)
SOUTH = complex(0, 1)
WEST = complex(-1, 0)
EAST = complex(1, 0)


def adj(p: complex) -> dict[Direction, complex]:
return {
"NW": p + NORTH + WEST,
"N": p + NORTH,
"NE": p + NORTH + EAST,
"W": p + WEST,
"E": p + EAST,
"SW": p + SOUTH + WEST,
"S": p + SOUTH,
"SE": p + SOUTH + EAST,
}


class Simulation:
def __init__(self, elf_positions: set[complex]) -> None:
self._elves = elf_positions
self.is_complete = False
self.round = 0
self._check_order: Deque[CardinalDirection] = deque(("N", "S", "W", "E"))

def simulate_round(self) -> None:
if self.is_complete:
raise AssertionError("simulation is already complete")
self.round += 1

proposed_moves = self._get_proposed_moves() # first half of round
self._move_elves(proposed_moves) # second half of round

def _get_proposed_moves(self) -> defaultdict[complex, set[complex]]:
# map position to position of elves that want to move there
proposed_moves: DefaultDict[complex, set[complex]] = defaultdict(set)

for elf_pos in self._elves:
adjacent = adj(elf_pos)

# if there are no adjacent elves, the elf does nothing
if not self.any_elf(adjacent.values()):
continue

for check_direction in self._check_order:
# if none of the spaces to the `check_direction` of are elves, an elf proposes a move in that direction
if not self.any_elf(adj_p for direction, adj_p in adjacent.items() if check_direction in direction):
proposed_moves[adjacent[check_direction]].add(elf_pos)
break # an elf can only propose 1 move per turn

return proposed_moves

def _move_elves(self, proposed_moves: dict[complex, set[complex]]) -> None:
accepted_moves = {srcs.pop(): dest for dest, srcs in proposed_moves.items() if len(srcs) == 1}

if len(accepted_moves) == 0:
self.is_complete = True
return

# move the elves
self._elves.difference_update(accepted_moves) # remove the old positions
self._elves.update(accepted_moves.values()) # add the new positions

self._check_order.rotate(-1)

def covered_ground(self) -> int:
min_x = min(int(p.real) for p in self._elves)
max_x = max(int(p.real) for p in self._elves)
min_y = min(int(p.imag) for p in self._elves)
max_y = max(int(p.imag) for p in self._elves)

dx = max_x - min_x + 1
dy = max_y - min_y + 1

area = dx * dy

return area - len(self._elves)

@staticmethod
def parse(initial_state: str) -> "Simulation":
elf_positions = {
complex(x, y) for y, line in enumerate(initial_state.splitlines()) for x, c in enumerate(line) if c == "#"
}
return Simulation(elf_positions)

def any_elf(self, to_check: Iterable[complex]) -> bool:
return any(p in self._elves for p in to_check)


def parta(txt: str) -> int:
s = Simulation.parse(txt)
for _round in range(10):
s.simulate_round()
return s.covered_ground()


def partb(txt: str) -> int:
s = Simulation.parse(txt)
while not s.is_complete:
s.simulate_round()
return s.round


if __name__ == "__main__":
from aocd import data

print(f"parta: {parta(data)}")
print(f"partb: {partb(data)}")
19 changes: 19 additions & 0 deletions tests/aoc2022/y2022d23_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import aoc_cj.aoc2022.day23 as d

EXAMPLE_INPUT = """
....#..
..###.#
#...#.#
.#...##
#.###..
##.#.##
.#..#..
""".strip()


def test_a() -> None:
assert d.parta(EXAMPLE_INPUT) == 110


def test_b() -> None:
assert d.partb(EXAMPLE_INPUT) == 20

0 comments on commit c834086

Please sign in to comment.