Skip to content

Commit b685188

Browse files
committed
calendars gif
1 parent f43e3e0 commit b685188

File tree

3 files changed

+197
-46
lines changed

3 files changed

+197
-46
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
![Rust: 262](https://img.shields.io/badge/Rust-262-cyan?logo=Rust)
55
![Python: 127](https://img.shields.io/badge/Python-127-cyan?logo=Python)
66

7-
<img src="./scripts/assets/christmas_ferris_2015_2024.png" alt="Christmas Ferris" width="164" />
7+
![calendars](./scripts/assets/calendars.gif) <img src="./scripts/assets/christmas_ferris_2015_2024.png" alt="Christmas Ferris" width="164" />
88

99
*Complete* solutions of [Advent of Code](https://adventofcode.com/) in [Rust](https://www.rust-lang.org), and sometimes in [Python](https://www.python.org/) 3.10+ and other languages 🎄✨.
1010

@@ -35,12 +35,12 @@ Calendar | Solutions | Stars | Rust | Python | 🎁
3535
[Advent of Code 2024](https://adventofcode.com/2024) | [Solutions](src/year2024/README.md) | 50⭐ | 25 | 11 | 3
3636
[Advent of Code 2023](https://adventofcode.com/2023) | [Solutions](src/year2023/README.md) | 50⭐ | 25 | 10 | 2
3737
[Advent of Code 2022](https://adventofcode.com/2022) | [Solutions](src/year2022/README.md) | 50⭐ | 25 | 18 | 1
38-
[Advent of Code 2021](https://adventofcode.com/2021) | [Solutions](src/year2021/README.md) | 50⭐ | 25 | 11 |
39-
[Advent of Code 2020](https://adventofcode.com/2020) | [Solutions](src/year2020/README.md) | 50⭐ | 25 | 23 |
38+
[Advent of Code 2021](https://adventofcode.com/2021) | [Solutions](src/year2021/README.md) | 50⭐ | 25 | 11 |
39+
[Advent of Code 2020](https://adventofcode.com/2020) | [Solutions](src/year2020/README.md) | 50⭐ | 25 | 23 |
4040
[Advent of Code 2019](https://adventofcode.com/2019) | [Solutions](src/year2019/README.md) | 50⭐ | 25 | 23 | 2
4141
[Advent of Code 2018](https://adventofcode.com/2018) | [Solutions](src/year2018/README.md) | 50⭐ | 25 | 4 | 1
42-
[Advent of Code 2017](https://adventofcode.com/2017) | [Solutions](src/year2017/README.md) | 50⭐ | 25 | 17 |
43-
[Advent of Code 2016](https://adventofcode.com/2016) | [Solutions](src/year2016/README.md) | 50⭐ | 25 | 0 |
42+
[Advent of Code 2017](https://adventofcode.com/2017) | [Solutions](src/year2017/README.md) | 50⭐ | 25 | 17 |
43+
[Advent of Code 2016](https://adventofcode.com/2016) | [Solutions](src/year2016/README.md) | 50⭐ | 25 | 0 |
4444
[Advent of Code 2015](https://adventofcode.com/2015) | [Solutions](src/year2015/README.md) | 50⭐ | 25 | 5 | 1
4545

4646
## Bonus 🎁

scripts/assets/calendars.gif

89.8 KB
Loading

scripts/extract_drawing.py

Lines changed: 192 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,34 @@
55
# curl -H "Cookie: session=$(cat ~/.adventofcode.session)" https://adventofcode.com/2024 -o 2024.html
66

77

8+
import os
89
import re
910
import sys
11+
import time
1012
from pathlib import Path
1113

1214
import requests
1315

14-
if len(sys.argv) != 2:
15-
print(f"Usage: {sys.argv[0]} <file>")
16+
17+
def get_cookie():
18+
for line in Path("~/.adventofcode.session").expanduser().read_text().splitlines():
19+
line = line.strip()
20+
if len(line) == 128 and line.startswith("53"):
21+
return line
22+
print("session cookie not found")
1623
exit(1)
1724

18-
f = sys.argv[1]
19-
if Path(f).is_file():
20-
calendar = Path(f).read_text()
21-
elif f.isdigit():
22-
s = Path("~/.adventofcode.session").expanduser()
23-
if s.is_file():
24-
session = s.read_text().strip()
25+
26+
def get_calendar(f):
27+
f = str(f)
28+
if Path(f).is_file():
29+
calendar = Path(f).read_text()
30+
31+
elif Path(f).with_suffix(".html").is_file():
32+
calendar = Path(f).with_suffix(".html").read_text()
33+
34+
elif f.isdigit():
35+
session = get_cookie()
2536
content = requests.get(
2637
f"https://adventofcode.com/{f}",
2738
headers={
@@ -34,6 +45,12 @@
3445

3546
calendar = content.decode()
3647

48+
else:
49+
print(f"Calendar not found: {f}")
50+
exit()
51+
52+
return calendar
53+
3754

3855
def rgb(s: str) -> str:
3956
if "color:" not in s:
@@ -44,13 +61,7 @@ def rgb(s: str) -> str:
4461
print("color problem:", s)
4562
exit(2)
4663
if len(rgb) == 3:
47-
if rgb == "ccc":
48-
rgb = "cccccc"
49-
elif rgb == "333":
50-
rgb = "333333"
51-
else:
52-
print(f"Unknown color: {rgb}")
53-
exit()
64+
rgb = rgb[0] + rgb[0] + rgb[1] + rgb[1] + rgb[2] + rgb[2]
5465
elif len(rgb) == 6:
5566
pass
5667
else:
@@ -64,43 +75,183 @@ def rgb(s: str) -> str:
6475
return f"\033[38;2;{r};{g};{b}m"
6576

6677

67-
colors = {}
68-
colors[None] = rgb("#606060;") # "\033[0m"
78+
def remove_between(line, sa, sb):
79+
while True:
80+
a = line.find(sa)
81+
if a == -1:
82+
break
83+
b = line.find(sb, a)
84+
if a == -1:
85+
break
86+
87+
line = line[:a] + line[b + len(sb) :]
88+
return line
89+
90+
91+
def sunbeam_2019(line):
92+
sa = '<span class="sunbeam"'
93+
sb = ">"
94+
color = rgb("color: #ffff66;")
95+
while True:
96+
a = line.find(sa)
97+
if a == -1:
98+
break
99+
b = line.find(sb, a)
100+
if a == -1:
101+
break
102+
103+
line = line[:a] + color + line[b + len(sb) :]
104+
color = ""
105+
106+
line = remove_between(line, '<span style="animation-delay:', ">")
107+
line = line.replace("*</span></span>", "")
108+
return line
109+
110+
111+
def text_shadow_2019(line):
112+
sa = '<span style="text-shadow:'
113+
sb = ">"
114+
while True:
115+
a = line.find(sa)
116+
if a == -1:
117+
break
118+
b = line.find(sb, a)
119+
if a == -1:
120+
break
121+
122+
color = ""
123+
text_shadow = line[a : b + len(sb)]
124+
for s in text_shadow.split():
125+
if s.startswith("#"):
126+
color = rgb("color: " + s.split(",")[0] + ";")
127+
break
128+
129+
line = line[:a] + color + line[b + len(sb) :]
130+
return line
131+
69132

133+
def ascii_calendar(calendar):
134+
colors = {}
135+
colors[None] = rgb("color: #606060;") # "\033[0m"
70136

71-
for line in calendar.splitlines():
137+
for line in calendar.splitlines():
138+
if line.startswith(".calendar .calendar-color-"):
139+
line = line.split(maxsplit=2)
140+
color = line[1][1:]
141+
code = rgb(line[2])
72142

73-
if line.startswith(".calendar .calendar-color-"):
143+
colors[color] = code
74144

75-
line = line.split(maxsplit=2)
76-
color = line[1][1:]
77-
code = rgb(line[2])
145+
year = int(re.search(r"<title>Advent of Code (\d+)</title>", calendar).group(1))
78146

79-
colors[color] = code
147+
if year == 2015:
148+
colors[None] = rgb("color: #009900;")
149+
colors["calendar-ornament0"] = rgb("color: #0066ff;")
150+
colors["calendar-ornament1"] = rgb("color: #ff9900;")
151+
colors["calendar-ornament2"] = rgb("color: #ff0000;")
152+
colors["calendar-ornament3"] = rgb("color: #ffff66;")
153+
colors["calendar-lightbeam"] = rgb("color: #ffff66;")
154+
colors["calendar-trunk"] = rgb("color: #cccccc;")
80155

156+
if year == 2016:
157+
colors["calendar-streets"] = rgb("color:#666666;")
158+
colors["calendar-window-blue"] = rgb("color:#0066ff;")
159+
colors["calendar-window-yellow"] = rgb("color:#ffff66;")
160+
colors["calendar-window-green"] = rgb("color:#009900;")
161+
colors["calendar-window-red"] = rgb("color:#ff0000;")
162+
colors["calendar-window-dark"] = rgb("color:#333333;")
163+
colors["calendar-window-brown"] = rgb("color:#553322;")
164+
colors["calendar-antenna-star"] = rgb("color: #ffff66;")
165+
colors["calendar-antenna-signal0"] = rgb("color: #ffff66;")
166+
colors["calendar-antenna-signal1"] = rgb("color: #ffff66;")
167+
colors["calendar-antenna-signal2"] = rgb("color: #ffff66;")
168+
colors["calendar-antenna-signal3"] = rgb("color: #ffff66;")
169+
colors["calendar-antenna-signal4"] = rgb("color: #ffff66;")
170+
colors["calendar-antenna-signal5"] = rgb("color: #ffff66;")
81171

82-
calendar = re.search(r'<pre class="calendar.*?">(.+?)</pre>', calendar, re.DOTALL).group(1)
83-
for line in calendar.splitlines():
172+
if year == 2017:
173+
colors["calendar-ornament0"] = rgb("color:#ff9900;")
174+
colors["calendar-ornament5"] = rgb("color:#990099;")
175+
colors["calendar-verycomplete"] = rgb("color:#ffff66;")
176+
colors["calendar-ornament2"] = rgb("color:#aaaaaa;")
177+
colors["calendar-ornament3"] = rgb("color:#ff0000;")
178+
colors["calendar-ornament1"] = rgb("color:#0066ff;")
179+
colors["calendar-ornament4"] = rgb("color:#009900;")
180+
181+
if year == 2019:
182+
colors["calendar-s"] = rgb("color: #333;")
183+
184+
calendar = re.search(r'<pre class="calendar.*?">(.+?)</pre>', calendar, re.DOTALL).group(1)
185+
for line in calendar.splitlines():
186+
# if "calendar-verycomplete" not in line and "calendar-mark-verycomplete" not in line:
187+
# print(line)
188+
# continue
189+
190+
line = remove_between(line, "<a ", ">")
191+
line = remove_between(line, '<span class="calendar-day">', "</a>")
192+
193+
if year == 2017:
194+
if 'span class="calendar-print-text' in line:
195+
continue
196+
line = line.replace("</i>", "")
197+
line = line.replace("<i>", "")
198+
line = remove_between(line, '<span style="animation-delay:', ">")
199+
200+
if year == 2019:
201+
line = remove_between(line, '<span style="position:relative;', ">")
202+
# line = remove_between(line, '<span style="text-shadow', ">")
203+
line = text_shadow_2019(line)
204+
line = sunbeam_2019(line)
205+
206+
if year == 2024:
207+
line = line.replace("</i>", "")
208+
line = line.replace("<i>", "")
209+
line = line.replace('<span id="calendar-monorail-8">', "")
210+
line = line.replace('<span id="calendar-monorail-9">', "")
211+
line = line.replace('<span class="calendar-9-path">.</span>', "")
212+
213+
line = re.sub(r'<span class="(.+?)">', lambda x: colors.get(x.group(1), ""), line)
214+
215+
# line = re.sub(r"(<span.+?>)(.+?)(</span>)", lambda m: f"{rgb(m[1])}{m[2]}\033[0m", line)
216+
line = re.sub(r"(<span.+?>)(.+?)(</span>)", lambda m: "", line)
217+
218+
line = line.replace("</span>", colors[None])
219+
220+
line = line.replace("&gt;", ">")
221+
line = line.replace("&lt;", "<")
222+
line = line.replace("&quot;", "'")
223+
line = line.replace("&amp;", "&")
84224

85-
if "calendar-verycomplete" not in line:
86225
print(line)
87-
continue
88226

89-
a = line.index(">") + 1
90-
b = line.find('<span class="calendar-day">')
91-
line = line[a:b]
227+
print("\033[0m")
228+
92229

93-
line = re.sub(r'<span class="(.+?)">', lambda x: colors.get(x.group(1), ""), line)
230+
def main():
231+
if len(sys.argv) >= 2 and sys.argv[1] == "rec":
232+
if len(sys.argv) == 2:
233+
os.system(f"asciinema rec --overwrite calendars.cast --command '{__file__} rec years'")
234+
os.system("agg calendars.cast calendars.gif --rows 37 --cols 56 --font-size 6")
235+
else:
94236

95-
# line = re.sub(r"(<span.+?>)(.+?)(</span>)", lambda m: f"{rgb(m[1])}{m[2]}\033[0m", line)
96-
line = re.sub(r"(<span.+?>)(.+?)(</span>)", lambda m: "", line)
237+
for year in range(2015, 2026):
238+
print(f"\033[?25l\033[H\033[2J\033[1;32m{year}\033[0m")
239+
ascii_calendar(get_calendar(year))
240+
time.sleep(2)
97241

98-
line = line.replace("</span>", colors[None])
99-
line = line.replace("</i>", colors[None])
100-
line = line.replace("<i>", "\033[2m")
242+
print("\033[25h")
243+
244+
exit()
101245

102-
line = line.replace("&gt;", ">")
103-
line = line.replace("&lt;", "<")
104-
line = line.replace("&quot;", "'")
246+
if len(sys.argv) != 2:
247+
print(f"Usage: {sys.argv[0]} [<file> | <year> | rec]")
248+
exit(1)
105249

106-
print(line + "\033[0m")
250+
ascii_calendar(get_calendar(sys.argv[1]))
251+
252+
253+
if __name__ == "__main__":
254+
try:
255+
main()
256+
except KeyboardInterrupt:
257+
pass

0 commit comments

Comments
 (0)