Skip to content

Commit 303089a

Browse files
committed
Add utils
1 parent 3108256 commit 303089a

11 files changed

+271
-12
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
.venv
1+
.venv
2+
__pycache__

.vscode/launch.json

+41-3
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,50 @@
55
"version": "0.2.0",
66
"configurations": [
77
{
8-
"name": "Python: Current File",
9-
"type": "python",
8+
"name": "Part A Example",
9+
"type": "debugpy",
1010
"request": "launch",
1111
"program": "${file}",
1212
"console": "internalConsole",
13-
"justMyCode": true
13+
"env": {
14+
"MODE": "part_a_example",
15+
"PYTHONPATH": "${workspaceFolder}:${env:PYTHONPATH}"
16+
}
17+
},
18+
{
19+
"name": "Part A",
20+
"type": "debugpy",
21+
"request": "launch",
22+
"program": "${file}",
23+
"console": "internalConsole",
24+
"justMyCode": true,
25+
"env": {
26+
"MODE": "part_a",
27+
"PYTHONPATH": "${workspaceFolder}:${env:PYTHONPATH}"
28+
}
29+
},
30+
{
31+
"name": "Part B Example",
32+
"type": "debugpy",
33+
"request": "launch",
34+
"program": "${file}",
35+
"console": "internalConsole",
36+
"justMyCode": true,
37+
"env": {
38+
"MODE": "part_b_example",
39+
"PYTHONPATH": "${workspaceFolder}:${env:PYTHONPATH}"
40+
}
41+
},
42+
{
43+
"name": "Part B",
44+
"type": "debugpy",
45+
"request": "launch",
46+
"program": "${file}",
47+
"console": "internalConsole",
48+
"env": {
49+
"MODE": "part_b",
50+
"PYTHONPATH": "${workspaceFolder}:${env:PYTHONPATH}"
51+
}
1452
}
1553
]
1654
}

.vscode/python.code-snippets

+18-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"prefix": "aoc",
44
"body": [
55
"from aocd.models import Puzzle",
6+
"import os",
67
"",
78
"puzzle = Puzzle(year=2024, day=${1:day})",
89
"input_data = puzzle.input_data.splitlines()",
@@ -18,12 +19,23 @@
1819
"",
1920
"",
2021
"if __name__ == '__main__':",
21-
" answer_a = part_a(example_input_data)",
22-
" print(f\"Answer A: {answer_a}\")",
23-
" # puzzle.answer_a = answer_a",
24-
" # answer_b = part_b(example_input_data)",
25-
" # print(f\"Answer B: {answer_b}\")",
26-
" # puzzle.answer_b = answer_b"
22+
" match os.environ.get('MODE'):",
23+
" case 'part_a_example':",
24+
" answer_a = part_a(example_input_data)",
25+
" print(f\"Answer A: {answer_a}\")",
26+
" case 'part_a':",
27+
" answer_a = part_a(input_data)",
28+
" print(f\"Answer A: {answer_a}\")",
29+
" puzzle.answer_a = answer_a",
30+
" case 'part_b_example':",
31+
" answer_b = part_b(example_input_data)",
32+
" print(f\"Answer B: {answer_b}\")",
33+
" case 'part_b':",
34+
" answer_b = part_b(input_data)",
35+
" print(f\"Answer B: {answer_b}\")",
36+
" puzzle.answer_b = answer_b",
37+
" case _:",
38+
" print('No MODE set')",
2739
],
2840
"description": "Advent of code 2023",
2941
"isFileTemplate": true

.vscode/settings.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"vscodeGitCommit.template": ["add solution for 2023/"],
2+
"vscodeGitCommit.template": ["add solution for 2024/"],
33
"vscodeGitCommit.insertMode": "Concatenate"
44
}

README.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
# Advent of Code
2-
These are my solutions of the [Advent of Code](https://adventofcode.com/) puzzles.
2+
3+
These are my solutions of the [Advent of Code](https://adventofcode.com/) puzzles.
4+
5+
## Credits
6+
7+
Thanks to [Defelo](https://github.com/Defelo) for the utils functions from his [AoC repo](https://github.com/Defelo/AdventOfCode).

utils/__init__.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from .graph import *
2+
from .grid import *
3+
from .lists import *
4+
from .math_utils import *
5+
from .parsing import *

utils/graph.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
class UnionFind:
2+
def __init__(self, n):
3+
self.parents = [*range(n)]
4+
5+
def find(self, x):
6+
if self.parents[x] == x:
7+
return x
8+
self.parents[x] = self.find(self.parents[x])
9+
return self.parents[x]
10+
11+
def merge(self, x, y):
12+
self.parents[self.find(x)] = self.find(y)
13+
14+
def connected(self, x, y):
15+
return self.find(x) == self.find(y)
16+
17+
def count(self):
18+
return len(set(map(self.find, range(len(self.parents)))))
19+
20+
def components(self):
21+
out = {}
22+
for parent, i in zip(map(self.find, range(len(self.parents))), range(len(self.parents))):
23+
out.setdefault(parent, []).append(i)
24+
25+
return out

utils/grid.py

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
NEIGH_DICT = {
2+
"N": (0, -1),
3+
"E": (1, 0),
4+
"S": (0, 1),
5+
"W": (-1, 0),
6+
"U": (0, -1),
7+
"D": (0, 1),
8+
"L": (-1, 0),
9+
"R": (1, 0),
10+
}
11+
12+
NEIGH_DIRECT = [*{*NEIGH_DICT.values()}]
13+
NEIGH_DIAG = [tuple(NEIGH_DICT[a][i] + NEIGH_DICT[b][i] for i in range(2)) for a, b in zip("NESW", "ESWN")]
14+
15+
16+
def get_neighbors(ox=0, oy=0, w=None, h=None, diag=False, include_self=False):
17+
return [
18+
(x, y)
19+
for dx, dy in NEIGH_DIRECT + (NEIGH_DIAG * diag) + ([(0, 0)] * include_self)
20+
if ((x := ox + dx) or 1)
21+
and (w is None or x in range(w))
22+
and ((y := oy + dy) or 1)
23+
and (h is None or y in range(h))
24+
]
25+
26+
27+
def rotate_left(x, y):
28+
return y, -x
29+
30+
31+
def rotate_right(x, y):
32+
return -y, x
33+
34+
35+
def rotate_matrix_left(matrix):
36+
return [*zip(*matrix)][::-1]
37+
38+
39+
def rotate_matrix_right(matrix):
40+
return [*zip(*matrix[::-1])]
41+
42+
43+
def iter_line(x1, y1, x2, y2):
44+
xr = range(min(x1, x2), max(x1, x2) + 1)
45+
if x1 > x2:
46+
xr = xr[::-1]
47+
48+
yr = range(min(y1, y2), max(y1, y2) + 1)
49+
if y1 > y2:
50+
yr = yr[::-1]
51+
52+
if x1 == x2:
53+
xr = [x1] * len(yr)
54+
if y1 == y2:
55+
yr = [y1] * len(xr)
56+
57+
for x, y in zip(xr, yr):
58+
yield x, y
59+
60+
61+
def transpose(matrix):
62+
return [*zip(*matrix)]

utils/lists.py

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
def most_common(lst, values=None):
2+
return max(values or set(lst), key=lst.count)
3+
4+
5+
def least_common(lst, values=None):
6+
return min(values or set(lst), key=lst.count)
7+
8+
9+
def minmax(lst, key=None):
10+
return min(lst, key=key), max(lst, key=key)
11+
12+
13+
def irange(start=0, step=1):
14+
i = start
15+
while True:
16+
yield i
17+
i += step
18+
19+
20+
def sliding_window(seq, n=2):
21+
lst = []
22+
for elem in seq:
23+
lst.append(elem)
24+
if len(lst) == n:
25+
yield tuple(lst)
26+
lst.pop(0)

utils/math_utils.py

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from functools import reduce
2+
import operator
3+
4+
5+
def product(it):
6+
return reduce(operator.mul, it)
7+
8+
9+
def extended_gcd(a, b):
10+
if a == 0:
11+
return b, 0, 1
12+
13+
gcd, x1, y1 = extended_gcd(b % a, a)
14+
15+
x = y1 - (b // a) * x1
16+
y = x1
17+
18+
return gcd, x, y
19+
20+
21+
def chinese_remainder(n, a):
22+
s = 0
23+
prod = reduce(int.__mul__, n)
24+
for n_i, a_i in zip(n, a):
25+
p = prod // n_i
26+
s += a_i * extended_gcd(p, n_i)[1] * p
27+
return s % prod
28+
29+
30+
def is_prime(n):
31+
if n < 5:
32+
return n in [2, 3]
33+
if n % 6 not in [1, 5]:
34+
return False
35+
return all(n % i for i in range(2, int(n**0.5) + 1))

utils/parsing.py

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import re
2+
3+
4+
def ints(inp):
5+
return [int(x[0]) for x in re.finditer(r"[+-]?\d+", inp)]
6+
7+
8+
def pints(inp):
9+
return [int(x[0]) for x in re.finditer(r"\d+", inp)]
10+
11+
12+
def floats(inp):
13+
return [float(x[0]) for x in re.finditer(r"[+-]?(\d*\.)?\d+", inp)]
14+
15+
16+
def pfloats(inp):
17+
return [float(x[0]) for x in re.finditer(r"(\d*\.)?\d+", inp)]
18+
19+
20+
def parse_ascii(dots: set[tuple[int, int]]) -> str:
21+
out = ""
22+
n = 0
23+
while True:
24+
k = 0
25+
for j in range(5 * n, 5 * n + 4):
26+
for i in range(6):
27+
k <<= 1
28+
k |= (i, j) in dots
29+
if not k:
30+
break
31+
out += {
32+
0b011111100100100100011111: "A",
33+
0b111111101001101001010110: "B",
34+
0b011110100001100001010010: "C",
35+
0b111111101001101001100001: "E",
36+
0b111111101000101000100000: "F",
37+
0b011110100001100101010111: "G",
38+
0b111111001000001000111111: "H",
39+
0b000010000001100001111111: "J",
40+
0b000010000001100001111110: "J",
41+
0b111111001000010110100001: "K",
42+
0b111111000001000001000001: "L",
43+
0b111111100100100100011000: "P",
44+
0b111111100100100110011001: "R",
45+
0b111110000001000001111110: "U",
46+
0b100011100101101001110001: "Z",
47+
}.get(k, "?")
48+
n += 1
49+
50+
return out

0 commit comments

Comments
 (0)