Skip to content

Commit b661de7

Browse files
committed
Add visualisation for day 4
1 parent 846f8e7 commit b661de7

File tree

7 files changed

+139
-4
lines changed

7 files changed

+139
-4
lines changed

challenges/2024/04-ceresSearch/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,10 @@
22

33
Part two is:
44
* less than 1976
5-
* also, somewhat obviously, less than 1978
5+
* also, somewhat obviously, less than 1978
6+
7+
Heatmap of the most overlapped locations
8+
9+
| Part 1 | Part 2 |
10+
|--------|--------|
11+
| ![heatmap 1](heatmap-1.png) | ![heatmap 2](heatmap-2.png) |
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env bash
2+
3+
set -ex
4+
5+
cat input.txt | PYTHONPATH=../../../ python3 vis.py
13.1 KB
Loading
11.9 KB
Loading

challenges/2024/04-ceresSearch/vis.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import sys
2+
import gridutil.grid as grid
3+
import gridutil.coord as coord
4+
from collections import defaultdict
5+
import os
6+
from pathlib import Path
7+
from PIL import Image
8+
from tqdm import tqdm
9+
10+
11+
def parse(instr: str) -> grid.Grid:
12+
return grid.parse(instr.upper())
13+
14+
def one(instr: str):
15+
wordsearch = parse(instr)
16+
17+
seq_starts = list(
18+
map(lambda x: x[0], filter(lambda x: x[1] == "X", wordsearch.items()))
19+
)
20+
detected_sequences = set()
21+
22+
for start_pos in seq_starts:
23+
for xdir in [-1, 0, 1]:
24+
for ydir in [-1, 0, 1]:
25+
26+
if xdir == 0 and ydir == 0:
27+
continue
28+
29+
delta = coord.Coordinate(xdir, ydir)
30+
31+
ok = True
32+
b = []
33+
for i, v in enumerate("XMAS"):
34+
if not ok:
35+
break
36+
37+
x = coord.add(start_pos, coord.mult(delta, i))
38+
g = wordsearch.get(x, "-")
39+
ok = g == v
40+
b.append(x)
41+
42+
if ok:
43+
detected_sequences.add(tuple(b))
44+
45+
return detected_sequences
46+
47+
48+
def check_cross_adjacents(s: str) -> bool:
49+
return s == "SM" or s == "MS"
50+
51+
52+
def two(instr: str):
53+
wordsearch = parse(instr)
54+
55+
seq_starts = list(
56+
map(lambda x: x[0], filter(lambda x: x[1] == "A", wordsearch.items()))
57+
)
58+
detected_sequences = set()
59+
60+
for start_pos in seq_starts:
61+
62+
a = wordsearch.get(coord.add(start_pos, (-1, -1)), "") + wordsearch.get(
63+
coord.add(start_pos, (1, 1)), ""
64+
)
65+
b = wordsearch.get(coord.add(start_pos, (-1, 1)), "") + wordsearch.get(
66+
coord.add(start_pos, (1, -1)), ""
67+
)
68+
69+
if check_cross_adjacents(a) and check_cross_adjacents(b):
70+
detected_sequences.add(start_pos)
71+
72+
return detected_sequences
73+
74+
75+
lowest_colour = (255, 245, 237)
76+
highest_colour = (255, 159, 45)
77+
colour_diffs = tuple(map(lambda x: x[1] - x[0], zip(highest_colour, lowest_colour)))
78+
79+
80+
def get_colour_for(n):
81+
return tuple(
82+
map(int, map(lambda x: x[0] - x[1], zip(lowest_colour, map(lambda x: x * n, colour_diffs))))
83+
)
84+
85+
86+
scale_factor = 4
87+
88+
def generate_frame(path: str, wordsearch: grid.Grid, counts: dict[coord.Coordinate, int]):
89+
max_val = max(counts.values())
90+
91+
maxx, maxy = grid.get_max_x(wordsearch), grid.get_max_y(wordsearch)
92+
93+
img = Image.new("RGB", (maxx+1, maxy+1))
94+
95+
for x in range(maxx+1):
96+
for y in range(maxy+1):
97+
img.putpixel((x, y), get_colour_for(counts[(x, y)]/max_val))
98+
99+
img = img.resize((maxx * scale_factor, maxy * scale_factor), resample=Image.NEAREST)
100+
img.save(path)
101+
102+
103+
def main():
104+
inp = sys.stdin.read().strip()
105+
wordsearch = parse(inp)
106+
107+
j = defaultdict(lambda: 0)
108+
for state in one(inp):
109+
for s in state:
110+
j[s] = j[s] + 1
111+
generate_frame("heatmap-1.png", wordsearch, j)
112+
113+
j = defaultdict(lambda: 0)
114+
for state in two(inp):
115+
for dir in [(0, 0), (-1, -1), (-1, 1), (1, 1), (1, -1)]:
116+
s = coord.add(state, dir)
117+
j[s] = j[s] + 1
118+
generate_frame("heatmap-2.png", wordsearch, j)
119+
120+
121+
if __name__ == "__main__":
122+
main()

challenges/2024/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ Total stars: **8 ★**
1010

1111
<!-- ★ ☆ ✗ -->
1212

13-
A day denoted with a star means it has a visualisation.
13+
A day denoted with an asterisk means it has a visualisation.
1414

1515
| Day | Status | Solutions | Notes |
1616
|-------------------------------------|--------|----------------------|-------|
1717
| 01 - Historian Hysteria | ★ ★ | Python | The reading comprehension was the hardest part of this. |
1818
| 02 - Red-Nosed Reindeer | ★ ★ | Python ||
1919
| 03 - Mull It Over | ★ ★ | Python | The first instance of Advent of Parsing this year! |
20-
| 04 - Ceres Search | ★ ★ | Python | When it says a cross, it does not mean a plus. |
20+
| 04* - Ceres Search | ★ ★ | Python | When it says a cross, it does not mean a plus. |

gridutil/coord.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,6 @@ def __eq__(self, x):
8080
return self.value == x.value
8181

8282
def __hash__(self):
83-
return hash(self.value)
83+
return hash(self.value)
84+
85+
directions = [Direction.Up, Direction.Down, Direction.Left, Direction.Right]

0 commit comments

Comments
 (0)