Skip to content

Commit 8f7d4b9

Browse files
committed
2024.08 visualisation
1 parent 1656243 commit 8f7d4b9

File tree

5 files changed

+116
-2
lines changed

5 files changed

+116
-2
lines changed

challenges/2024/08-resonantCollinearity/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@
33
Part 1 is:
44
* less than 298
55

6-
This is not a fully generic solution as, in a situation where the differences between x and y dimensions of pairs of coordinates are not coprime, this implementation would skip steps.
6+
This is not a fully generic solution as, in a situation where the differences between x and y dimensions of pairs of coordinates are not coprime, this implementation would skip steps.
7+
8+
Visualisation exists as `out.mp4`
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
3+
set -ex
4+
5+
cat input.txt | PYTHONPATH=../../../ python3 vis.py
6+
ffmpeg -y -framerate 30 -pattern_type glob -i 'frames/*.png' -c:v libx264 -pix_fmt yuv420p out.mp4
Binary file not shown.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import sys
2+
from gridutil import grid, coord
3+
from collections import defaultdict
4+
import itertools
5+
from fractions import Fraction
6+
import os
7+
from pathlib import Path
8+
from PIL import Image
9+
from tqdm import tqdm
10+
from colorsys import hsv_to_rgb
11+
12+
13+
def parse(instr: str) -> tuple[dict[str, list[coord.Coordinate]], tuple[int, int]]:
14+
g = grid.parse(instr)
15+
antenna_by_type = defaultdict(list)
16+
for key in g:
17+
if g[key] == ".":
18+
continue
19+
antenna_by_type[g[key]].append(key)
20+
return antenna_by_type, (grid.get_max_x(g), grid.get_max_y(g))
21+
22+
23+
lowest_colour = (255, 245, 237)
24+
highest_colour = (255, 159, 45)
25+
highlight_colour = (255, 47, 47)
26+
shadow_colour = (226, 218, 211)
27+
colour_diffs = tuple(map(lambda x: x[1] - x[0], zip(highest_colour, lowest_colour)))
28+
29+
30+
def get_colour_for(n):
31+
return tuple(
32+
map(int, map(lambda x: x[0] - x[1], zip(lowest_colour, map(lambda x: x * n, colour_diffs))))
33+
)
34+
35+
scale_factor = 8
36+
37+
38+
def get_highlight_for(n):
39+
return tuple(map(lambda x: int(x * 255), hsv_to_rgb(n, 0.82, 1)))
40+
41+
42+
def generate_frame(i, base_img, highlight_locs, hc, sequence) -> int:
43+
for n in range(len(sequence)):
44+
s = sequence[:n+1]
45+
img = base_img.copy()
46+
47+
sl = len(s) + 1
48+
for j, p in enumerate(s):
49+
img.putpixel(p, get_colour_for((j + 1) / sl))
50+
51+
for h in highlight_locs:
52+
img.putpixel(h, hc)
53+
54+
maxx, maxy = img.size
55+
img = img.resize((maxx * scale_factor, maxy * scale_factor), resample=Image.NEAREST)
56+
img.save(f"frames/{str(i).zfill(5)}.png")
57+
i += 1
58+
return i
59+
60+
61+
def update_base(base_img, add):
62+
for v in add:
63+
base_img.putpixel(v, shadow_colour)
64+
65+
66+
if __name__ == "__main__":
67+
inp = sys.stdin.read().strip()
68+
(antenna_by_type, (max_x, max_y)) = parse(inp)
69+
70+
ns = list(sorted(antenna_by_type.keys()))
71+
nns = len(ns)
72+
73+
try:
74+
os.makedirs("frames")
75+
except FileExistsError:
76+
pass
77+
78+
base_img = Image.new("RGB", (max_x+1, max_y+1), color=lowest_colour)
79+
80+
i = 0
81+
for antenna_type in tqdm(antenna_by_type):
82+
for (a, b) in itertools.permutations(antenna_by_type[antenna_type], 2):
83+
if (
84+
a.x > b.x
85+
): # filter out (most) duplicate pairs eg ((1, 2), (2, 1)) will only be calculated as ((2, 1), (1, 2)) will be filtered. This also prevents diff.x from being negative (useful for the mod operation)
86+
continue
87+
88+
this_iter = []
89+
90+
diff = coord.sub(b, a)
91+
92+
m = Fraction(
93+
diff.y, diff.x
94+
) # equiv of diff.y / diff.x but without the 26.9999999999996 issue
95+
c = a.y - (m * a.x)
96+
97+
x_cursor = a.x % diff.x
98+
y_cursor = int((x_cursor * m) + c)
99+
while x_cursor <= max_x:
100+
if 0 <= y_cursor <= max_y:
101+
this_iter.append((x_cursor, y_cursor))
102+
x_cursor += diff.x
103+
y_cursor += diff.y
104+
105+
i = generate_frame(i, base_img, (a, b), get_highlight_for(ns.index(antenna_type) / nns), this_iter)
106+
update_base(base_img, this_iter)

challenges/2024/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ A day denoted with an asterisk means it has a visualisation.
2121
| 05 - Print Queue | ★ ★ | Python | Before you dismiss and idea as being "too simple", make sure you check that it doesn't work. |
2222
| 06 - Guard Gallivant | ★ ★ | Python | oh dear runtime (also I knew what I wanted to do for so long it just took me 3 hours to implement it properly) |
2323
| 07 - Bridge Repair | ★ ★ | Python | Maths? Backwards?? |
24-
| 08 - Resonant Collinearity | ★ ★ | Python | `Fraction` saving us all from the curse of a computer's inability to do floating point arithmetic |
24+
| 08* - Resonant Collinearity | ★ ★ | Python | `Fraction` saving us all from the curse of a computer's inability to do floating point arithmetic |

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy