Last active
December 12, 2022 18:49
-
-
Save tnlogy/97a84616820febc1d09cb084d28ec92d to your computer and use it in GitHub Desktop.
advent of code 2022 - don't look unless you have completed it :)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
def one(part=2): | |
"""Find the maximum sum, or the three maximum sums for rows of values""" | |
with open('input.txt', 'r') as f: | |
data = [np.sum(np.fromstring(d, sep="\n", dtype=int)) for d in f.read().split("\n\n")] | |
if part == 2: | |
print("one:2:", np.sum(np.sort(data)[-3:])) | |
else: | |
print("one:", np.sum(np.sort(data)[-1])) | |
def two(part=2): | |
"""Calculate the score for Rock, Paper, Scissors game, according to wanted move or wanted outcome""" | |
score = dict( | |
A=dict(X=3, Y=6, Z=0), | |
B=dict(X=0, Y=3, Z=6), | |
C=dict(X=6, Y=0, Z=3) | |
) | |
shape_score = dict(X=1, Y=2, Z=3) | |
wanted_outcome = dict(X=0, Y=3, Z=6) | |
s = 0 | |
for a, b in np.genfromtxt("input2.txt", dtype=(str, str)): | |
if part == 2: | |
o = wanted_outcome[b] | |
move = next(move for move, outcome in score[a].items() if outcome == o) # reverse lookup | |
s += score[a][move] + shape_score[move] | |
else: | |
s += score[a][b] + shape_score[b] | |
print(f"two:{part}:", s) | |
def three(): | |
"""Find the common items in the rucksacks, and sum the priority values""" | |
keys = {k: v+1 for v, k in enumerate("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")} | |
with open('input3.txt', 'r') as f: | |
s = 0 | |
for r in f: | |
h = int(len(r)/2) | |
a, b = r[:h], r[h:] | |
d = set.intersection(set(a), set(b)).pop() | |
s += keys[d] | |
print("three:", s) | |
def three2(): | |
"""Find the common items in the rucksacks for three rows, and sum the priority values""" | |
keys = {k: v+1 for v, k in enumerate("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")} | |
with open('input3.txt', 'r') as f: | |
s = 0 | |
rows = f.read().splitlines() | |
for a, b, c in zip(rows[::3], rows[1::3], rows[2::3]): | |
d = set.intersection(set(a), set(b), set(c)).pop() | |
s += keys[d] | |
print("three:2:", s) | |
def four(part=2): | |
"""Find number of pairs that are subsets, or not disjoint""" | |
with open('input4.txt', 'r') as f: | |
s = 0 | |
for r in f: | |
a, b = r.split(",") | |
a_min, a_max = a.split("-") | |
b_min, b_max = b.split("-") | |
ar, br = range(int(a_min), int(a_max) +1), range(int(b_min), int(b_max) + 1) | |
a_s, b_s = set(ar), set(br) | |
if part == 2: | |
if not a_s.isdisjoint(b_s): | |
s += 1 | |
else: | |
if a_s.issubset(b_s) or b_s.issubset(a_s): | |
s += 1 | |
print(f"four:{part}", s) | |
def five(part=2): | |
"""Print top rows of crates after N move operations""" | |
with open('input5.txt', 'r') as f: | |
stacks, code = f.read().split("\n\n") | |
# parse stacks | |
stacks = stacks.splitlines()[:-1] | |
d = np.array([[k for k in row] for row in stacks]) | |
stacks = [np.flip(r[r != ' ']).tolist() for r in d[:, 1::4].T] | |
for r in code.split("\n"): | |
x = r[5:].split(" from ") | |
fr, to = x[1].split(" to ") | |
fr, to = int(fr), int(to) | |
count = int(x[0]) | |
if part == 2: | |
stacks[to-1] += stacks[fr-1][-count:] | |
stacks[fr-1] = stacks[fr-1][:-count] | |
else: | |
for c in range(0, count): | |
stacks[to-1].append(stacks[fr-1].pop()) | |
print(f"five:{part}: ", end="") | |
for s in stacks: | |
print(s[-1], end="") | |
print() | |
def six(part=1): | |
"""Print position in file after first number of distinct markers""" | |
with open('input6.txt', 'r') as f: | |
data = f.read() | |
n_distinct_markers = 4 if part == 1 else 14 | |
# loop over a window (win) of length n_distinct_markers to check when all chars are distinct | |
for p in range(0, len(data)): | |
win = data[p:p+n_distinct_markers] | |
if len(set(win)) == n_distinct_markers: | |
print(f"six:{part}:", p+n_distinct_markers) | |
return | |
def seven(): | |
"""Parse directory listing and sum size of folders""" | |
class Dir: | |
def __init__(self, name): | |
self.name, self.files, self._size = name, [], None | |
def add(self, file): | |
self.files.append(file) | |
def get_dir(self, name): | |
return next(file for file in self.files if file.name == name) | |
@property | |
def size(self): | |
if not self._size: | |
self._size = sum(file.size for file in self.files) | |
return self._size | |
def get_dirs(self, cond, dirs=None): | |
dirs = [] if dirs is None else dirs | |
if cond(self): | |
dirs.append(self) | |
for file in self.files: | |
file.get_dirs(cond, dirs) | |
return dirs | |
class File: | |
def __init__(self, name, size): | |
self.name, self.size = name, size | |
def get_size(self): | |
return self.size | |
def get_dirs(self, cond, res): | |
pass | |
with open('input7.txt', 'r') as f: | |
root = Dir("/") | |
cwd = [root] | |
for line in f.read().splitlines()[1:]: | |
if line.startswith("$"): | |
cmd = line[2:].split(" ") | |
if cmd[0] == "cd": | |
if cmd[1] == "..": | |
cwd.pop() | |
else: | |
cwd.append(cwd[-1].get_dir(cmd[1])) | |
else: # ls result | |
if line.startswith("dir "): | |
_, name = line.split(" ") | |
cwd[-1].add(Dir(name)) | |
else: | |
size, name = line.split(" ") | |
cwd[-1].add(File(name, int(size))) | |
print("seven:1:", sum(file.size for file in root.get_dirs(lambda x: x.size <= 100000))) | |
needed = 30000000 - (70000000 - root.size) | |
print("seven:2:", min(file.size for file in root.get_dirs(lambda x: x.size > needed))) | |
def eight(part=2): | |
def los(d, v): | |
"""line of sight: how far can you see in the array?""" | |
return next((i+1 for i, x in enumerate(d) if x >= v), len(list(d))) | |
with open('input8.txt', 'r') as f: | |
d = np.array([[int(k) for k in row] for row in f.read().splitlines()]) # make a 2D array | |
s = 0 | |
for i, j in np.ndindex(d.shape): | |
if i == 0 or j == 0 or (d.shape[1] - 1) == i or (d.shape[0] - 1) == j: | |
if part == 1: | |
s += 1 | |
else: | |
v = d[j][i] | |
if part == 1: | |
if max(d[0:j, i]) < v or max(d[j+1:, i]) < v or max(d[j, 0:i]) < v or max(d[j, i+1:]) < v: | |
s += 1 | |
if part == 2: # multiply line of sight in 4 directions | |
s = max(los(np.flip(d[0:j, i]), v) * los(d[j+1:, i], v) * | |
los(np.flip(d[j, 0:i]), v) * los(d[j, i+1:], v), s) | |
print(f"eight:{part}:", s) | |
def nine(part=2): | |
"""How many positions does the tail of the rope visit at least once?""" | |
Pos = namedtuple("Pos", ["x", "y"]) | |
def update_tail(head, tail): | |
new_pos = Pos(tail.x + np.sign(head.x - tail.x), tail.y + np.sign(head.y - tail.y)) | |
if head == tail or head == new_pos or (abs(head.x - tail.x) == 1 and abs(head.y - tail.y) == 1): | |
return tail | |
else: | |
return new_pos | |
path = [] | |
H, TL = Pos(0, 0), [Pos(0, 0) for _ in range(0, 1 if part == 1 else 9)] | |
dir_map = dict(R=Pos(1, 0), L=Pos(-1, 0), U=Pos(0, 1), D=Pos(0, -1)) | |
for d, l in np.genfromtxt("input9.txt", dtype="U1,int"): | |
direction = dir_map[d] | |
for i in range(0, l): | |
H = Pos(H.x + direction.x, H.y + direction.y) | |
PH = H | |
for j in range(0, len(TL)): | |
PH = update_tail(PH, TL[j]) | |
TL[j] = PH | |
path.append(TL[-1]) | |
print(f"nine:{part}:", len(set(path))) | |
def ten(part=1): | |
with open('input10.txt', 'r') as f: | |
cycles = [1] | |
X = 1 | |
for row in f.read().splitlines(): | |
if row == "noop": | |
cycles.append(X) | |
else: | |
_, v = row.split(" ") | |
cycles.append(X) | |
cycles.append(X) | |
X += int(v) | |
cs = [20, 60, 100, 140, 180, 220] | |
print([cycles[c] for c in cs]) | |
res = [c * cycles[c] for c in cs] | |
print(res) | |
print(sum(res)) | |
screen = "" | |
for y in range(0, 6): | |
for x in range(0, 40): | |
if abs(cycles[x + y*40 +1] - x) < 2: | |
screen += "#" | |
else: | |
screen += "." | |
screen += "\n" | |
print(screen) | |
pass | |
def eleven(part=1): | |
"""To slow for part 2 so far...""" | |
class Monkey: | |
def __init__(self, *args): | |
self.items, self.expr, self.div_by, self.true, self.false = args | |
self.inspect_count = 0 | |
with open('input11.txt', 'r') as f: | |
data = f.read().split("\n\n") | |
monkeys = [] | |
for m in data: | |
rows = m.split("\n") | |
# eval(expr, dict(old=3)) | |
monkeys.append(Monkey([int(v) for v in rows[1][18:].split(",")], rows[2].split("=")[1], | |
int(rows[3][21:]), int(rows[4][29:]), int(rows[5][30:]))) | |
for rounds in range(20 if part == 1 else 10000): | |
for m in monkeys: | |
for i in m.items: | |
v = eval(m.expr, dict(old=i)) | |
if part == 1: | |
v = math.floor(v/3) | |
m.inspect_count += 1 | |
if v % m.div_by == 0: | |
monkeys[m.true].items.append(v) | |
else: | |
monkeys[m.false].items.append(v) | |
m.items = [] | |
if (rounds % 100) == 0: | |
print("round", rounds) | |
two_highest = np.sort([m.inspect_count for m in monkeys])[-2:] | |
print(two_highest) | |
print(np.multiply(*two_highest)) | |
def twelve(part=1): | |
import astar as astar | |
Node = namedtuple("Node", "x y") | |
with open('input12.txt', 'r') as f: | |
maze = np.array([[k for k in row] for row in f.read().splitlines()]) # make a 2D array | |
S, E = Node(*np.argwhere(maze == 'S')[0]), Node(*np.argwhere(maze == 'E')[0]) | |
maze[S.x, S.y], maze[E.x, E.y] = 'a', 'z' | |
def neighbors(n): | |
def valid_path(x, y): | |
return 0 <= x < maze.shape[0] and 0 <= y < maze.shape[1] and ord(maze[x, y]) - 1 <= ord(v) | |
v = maze[n.x, n.y] | |
for delta in [Node(-1, 0), Node(1, 0), Node(0, -1), Node(0, 1)]: | |
if valid_path(n.x + delta.x, n.y + delta.y): | |
yield Node(n.x + delta.x, n.y + delta.y) | |
def manhattan_distance(a, b): | |
return abs(a.x - b.x) + abs(a.y - b.y) | |
if part == 1: | |
path = list(astar.find_path(S, E, neighbors_fnct=neighbors, | |
heuristic_cost_estimate_fnct=manhattan_distance)) | |
print("twelve:1:", len(path) - 1) | |
else: # Quite slow, but works. Should be able to store previous results from 'a'-nodes to speed up. | |
res = 100000 | |
for S in np.argwhere(maze == 'a'): | |
path = astar.find_path(Node(*S), E, neighbors_fnct=neighbors, | |
heuristic_cost_estimate_fnct=manhattan_distance) | |
if path: | |
l = len(list(path)) - 1 | |
if l < res: | |
res = l | |
print("twelve:2:", res) | |
if __name__ == '__main__': | |
one(1) | |
one(2) | |
two(1) | |
two(2) | |
three() | |
three2() | |
four(1) | |
four(2) | |
five(1) | |
five(2) | |
six(1) | |
six(2) | |
seven() | |
eight(1) | |
eight(2) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment