Skip to content

Commit 10ef23e

Browse files
committed
Day 9
1 parent 19c81c6 commit 10ef23e

3 files changed

Lines changed: 123 additions & 8 deletions

File tree

aoc/2025/09/09.vn.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Advent of code Day 09
2+
# https://adventofcode.com/2025/day/9
3+
# 09/12/2025
4+
5+
E = 10 ** -3
6+
7+
class Line:
8+
def __init__(self, p1, p2):
9+
x1, y1 = p1
10+
x2, y2 = p2
11+
if x1 == x2:
12+
self.a, self.b, self.c = 1, 0, x1
13+
else:
14+
a = (y1-y2)/(x1-x2)
15+
b = (x2*y1-x1*y2)/(x2-x1)
16+
self.a, self.b, self.c = a, -1, -b
17+
self.line = self
18+
19+
def has(self, p):
20+
if not p: return False
21+
x, y = p
22+
return abs(x*self.a+y*self.b-self.c) <= E
23+
24+
def inter(self, other):
25+
a, b, p = self.a, self.b, self.c
26+
c, d, q = other.a, other.b, other.c
27+
28+
det = a*d-b*c
29+
if abs(det) <= E: return None
30+
a, b, c, d = d, -b, -c, a
31+
x = a*p+b*q
32+
y = c*p+d*q
33+
return (x/det, y/det)
34+
35+
36+
class Ray:
37+
def __init__(self, line, condition):
38+
self.line = line
39+
self.condition = condition
40+
41+
def has(self, p):
42+
if not p: return False
43+
if not self.line.has(p): return False
44+
return self.condition(p)
45+
46+
def inter(self, ray):
47+
inter = self.line.inter(ray.line)
48+
return self.has(inter) and ray.has(inter)
49+
50+
with open("input.txt") as file:
51+
inp = list(map(lambda s: tuple(map(int, s.strip().split(","))), file.readlines()))
52+
53+
from tqdm.auto import tqdm
54+
from functools import cache
55+
from itertools import combinations
56+
57+
58+
xs = sorted(set(x for x, y in inp))
59+
ys = sorted(set(y for x, y in inp))
60+
61+
original = inp
62+
inp = [(xs.index(x), ys.index(y)) for x, y in inp]
63+
64+
edges = []
65+
for i, (x1, y1) in enumerate(inp):
66+
x2, y2 = inp[(i+1) % len(inp)]
67+
68+
line = Line((x1, y1), (x2, y2))
69+
if x1 == x2:
70+
def hits(p, Y1=y1, Y2=y2):
71+
_, y = p
72+
return min(Y1, Y2) <= y <= max(Y1, Y2)
73+
elif y1 == y2:
74+
def hits(p, X1=x1, X2=x2):
75+
x, _ = p
76+
return min(X1, X2) <= x <= max(X1, X2)
77+
78+
edges.append(Ray(line, hits))
79+
80+
@cache
81+
def inside(p):
82+
# Evil ray algorithm
83+
# To be inside, the four rays must cross
84+
# something
85+
if p in inp: return True
86+
x, y = p
87+
88+
rays = [
89+
Ray(Line(p, (x-10, y)), lambda p1, X=x: p1[0] <= X),
90+
Ray(Line(p, (x, y+10)), lambda p1, Y=y: p1[1] >= Y),
91+
Ray(Line(p, (x+10, y)), lambda p1, X=x: p1[0] >= X),
92+
Ray(Line(p, (x, y-10)), lambda p1, Y=y: p1[1] <= Y)
93+
]
94+
95+
hits = [0, 0, 0, 0]
96+
for edge in edges:
97+
for i, ray in enumerate(rays):
98+
if ray.inter(edge):
99+
hits[i] = 1
100+
if sum(hits) == 4: return True
101+
return sum(hits) == 4
102+
103+
res1, res2 = 0, 0
104+
105+
for (x1, y1), (x2, y2) in tqdm(list(combinations(inp, 2))):
106+
# area computed with real coords
107+
area = (abs(xs[x2] - xs[x1]) + 1) * (abs(ys[y2] - ys[y1]) + 1)
108+
res1 = max(res1, area)
109+
110+
# Checks on reduced coords
111+
allIn = True
112+
for x in range(min(x1, x2), max(x1, x2) + 1):
113+
for y in range(min(y1, y2), max(y1, y2) + 1):
114+
if not inside((x, y)):
115+
allIn = False
116+
break
117+
if not allIn:
118+
break
119+
120+
if allIn:
121+
res2 = max(res2, area)
122+
123+
print(res1, res2)

aoc/2025/09/animated/09_animated.vn.py

Lines changed: 0 additions & 4 deletions
This file was deleted.

aoc/2025/09/normal/09_normal.vn.py

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)