Skip to content

Commit 11aca1b

Browse files
committed
Everybody Codes: tidy 2025/14
1 parent 178bf4f commit 11aca1b

1 file changed

Lines changed: 48 additions & 89 deletions

File tree

everybody_codes/event2025/quest_14.py

Lines changed: 48 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,77 @@
11
"""Everyone Codes Day N."""
22

3-
import logging
43
from lib import helpers
54
from lib import parsers
65

7-
log = logging.info
8-
DIAGS = [1+1j, 1-1j,-1+1j,-1-1j]
6+
DIAGONALS = [(x, y) for x in [-1, 1] for y in [-1, 1]]
97

10-
def p3(data):
11-
all_coords = [complex(x, y) for x in range(34) for y in range(34)]
12-
active = set()
138

14-
def neighbors(i):
15-
for d in DIAGS:
16-
if i + d in all_coords:
17-
yield i + d
18-
19-
def print_active():
20-
for y in range(34):
21-
print("".join(
22-
"#" if complex(x,y) in active else "."
23-
for x in range(34)
24-
))
25-
26-
want_steps = 1000000000
27-
assert data.max_x == data.max_y == 7
28-
want_active = {i + 13 + 13j for i in data.all_coords if i in data.coords["#"]}
29-
want_off = {i + 13 + 13j for i in data.all_coords if i not in data.coords["#"]}
30-
# print(f"{len(want_active)=}")
31-
# print(f"{len(want_off)=}")
32-
33-
if False:
34-
for y in range(13, 13+9):
35-
print("".join(
36-
"#" if complex(x,y) in want_active else "."
37-
for x in range(13, 13+9)
38-
))
39-
print()
40-
41-
for y in range(13, 13+9):
42-
print("".join(
43-
"#" if complex(x,y) in want_off else "."
44-
for x in range(13, 13+9)
45-
))
46-
print()
9+
def solve(part: int, data: str) -> int:
10+
"""Solve the parts."""
11+
lines = data.splitlines()
12+
input_width = len(lines[0])
13+
input_height = len(lines)
14+
assert input_width == input_height
15+
board_size = input_width if part in [1, 2] else 34
16+
17+
offset = 0 if part in [1, 2] else (board_size - input_width) // 2
18+
input_active = {
19+
(x + offset, y + offset)
20+
for y, line in enumerate(lines)
21+
for x, char in enumerate(line)
22+
if char == "#"
23+
}
24+
# Used for part 3.
25+
input_inactive = {
26+
(x + offset, y + offset)
27+
for y, line in enumerate(lines)
28+
for x, char in enumerate(line)
29+
if char != "#"
30+
}
31+
32+
active = frozenset() if part == 3 else frozenset(input_active)
33+
34+
def neighbors(x, y):
35+
return [(x + dx, y + dy) for dx, dy in DIAGONALS if 0 <= x + dx < board_size and 0 <= y + dy < board_size]
36+
37+
want_steps = [10, 2025, 1000000000][part - 1]
4738

4839
total = 0
4940
seen_at = {}
50-
seen = {}
41+
seen: dict[frozenset[tuple[int, int]], int] = {}
5142

5243
for step in range(5000):
5344
active = frozenset({
54-
c for c in all_coords
45+
(x, y) for x in range(board_size) for y in range(board_size)
5546
if (
56-
(c in active and sum(n in active for n in neighbors(c)) % 2 == 1)
57-
or (c not in active and sum(n in active for n in neighbors(c)) % 2 == 0)
47+
((x, y) in active and sum(n in active for n in neighbors(x, y)) % 2 == 1)
48+
or ((x, y) not in active and sum(n in active for n in neighbors(x, y)) % 2 == 0)
5849
)
5950
})
60-
if want_active.issubset(active) and want_off.isdisjoint(active):
61-
print(step)
51+
52+
if part != 3:
53+
total += len(active)
54+
if step + 1 == want_steps:
55+
return total
56+
elif input_active.issubset(active) and input_inactive.isdisjoint(active):
6257
seen_at[step] = len(active)
6358
if active in seen:
6459
cycle_start = seen[active]
6560
cycle = step - cycle_start
66-
print(f"{seen_at=}")
67-
print(f"{cycle_start=}")
68-
print(f"{cycle=}")
69-
spots = set()
61+
spots: set[int] = set()
7062
for prior in seen_at:
7163
if prior >= cycle_start:
72-
spots.update(range(cycle_start+(prior-cycle_start), want_steps, cycle))
73-
total = 0
74-
for spot in spots:
75-
total += seen_at[((spot - cycle_start) % cycle) + cycle_start]
76-
return total
64+
spots.update(range(cycle_start + (prior - cycle_start), want_steps, cycle))
65+
return sum(
66+
seen_at[((spot - cycle_start) % cycle) + cycle_start]
67+
for spot in spots
68+
)
7769

7870
seen[active] = step
79-
raise RuntimeError("Nope")
80-
81-
def solve(part: int, data: str) -> int:
82-
"""Solve the parts."""
83-
if part == 3:
84-
return p3(data)
85-
active = data.coords["#"]
86-
total = 0
87-
88-
def neighbors(i):
89-
for d in DIAGS:
90-
if i + d in data.all_coords:
91-
yield i + d
92-
93-
def print_active():
94-
for y in range(data.max_y+1):
95-
print("".join(
96-
"#" if complex(x,y) in active else "."
97-
for x in range(data.max_x+1)
98-
))
99-
100-
for _ in range(10 if part == 1 else 2025):
101-
#print_active()
102-
#print()
103-
active = {
104-
c for c in data.all_coords
105-
if (
106-
(c in active and sum(n in active for n in neighbors(c)) % 2 == 1)
107-
or (c not in active and sum(n in active for n in neighbors(c)) % 2 == 0)
108-
)
109-
}
110-
# print( len(active))
111-
total += len(active)
112-
return total
71+
raise RuntimeError("No solution found.")
11372

11473

115-
PARSER = parsers.CoordinatesParser()
74+
PARSER = parsers.parse_one_str
11675
TEST_DATA = [
11776
"""\
11877
.#.##.

0 commit comments

Comments
 (0)