From 491530d85720eb9be1acd023fb803a348800f39d Mon Sep 17 00:00:00 2001 From: Tyrel Souza Date: Mon, 12 Dec 2022 02:41:14 -0500 Subject: [PATCH] day12 not pretty at alllll --- 2022/python/_sample.py | 2 + 2022/python/anim.py | 13 +++- 2022/python/day01.py | 11 ++- 2022/python/day02.py | 41 +++++------ 2022/python/day02_golf.py | 21 ++++-- 2022/python/day03.py | 6 +- 2022/python/day04.py | 16 ++-- 2022/python/day05.psg.py | 150 ++++++++++++++++++++++++++++++++++++++ 2022/python/day05.py | 29 ++++---- 2022/python/day06.py | 9 ++- 2022/python/day07.py | 59 ++++++++------- 2022/python/day08.py | 62 ++++++++-------- 2022/python/day09.py | 92 +++++++++++------------ 2022/python/day10.py | 26 +++---- 2022/python/day11.py | 21 ++---- 2022/python/day12.py | 116 +++++++++++++++++++++++++++++ 2022/python/matrix.py | 137 ++++++++++++++++++++++++---------- 2022/python/shared.py | 13 +++- 18 files changed, 588 insertions(+), 236 deletions(-) create mode 100644 2022/python/day05.psg.py create mode 100644 2022/python/day12.py diff --git a/2022/python/_sample.py b/2022/python/_sample.py index 3b20b90..f63bfff 100644 --- a/2022/python/_sample.py +++ b/2022/python/_sample.py @@ -1,9 +1,11 @@ import shared from pprint import pprint as pp + def run(SOMETHING): pp(SOMETHING) + def main(): spl = lambda y: [int(w) for w in y] rows = [row.rstrip().split() for row in shared.load(3)] diff --git a/2022/python/anim.py b/2022/python/anim.py index f1cb688..a1a1def 100644 --- a/2022/python/anim.py +++ b/2022/python/anim.py @@ -3,6 +3,7 @@ import imageio import matplotlib.pyplot as plt import numpy as np + class Animate: def __init__(self, mx, day="CHANGEME"): self.mx = mx @@ -14,7 +15,7 @@ class Animate: def add_frame(self, frame): self.f_count += 1 - self.write_frame(frame) + self.write_frame(frame) def write_frame(self, frame): current = np.zeros_like(self.mx) @@ -24,13 +25,17 @@ class Animate: fig, ax = plt.subplots() ax.imshow(current, cmap=plt.cm.gray) ax.axis("off") - _figpath = (f"gif-{self.day}/{self.f_count:05}.png") + _figpath = f"gif-{self.day}/{self.f_count:05}.png" plt.savefig(_figpath) plt.close() def animate(self, frameskip=1): - with imageio.get_writer(f"gif-{self.day}/day{self.day}.gif", mode="I") as writer: - names = [f"gif-{self.day}/{x:05}.png" for x in range(0,self.f_count, frameskip)] + with imageio.get_writer( + f"gif-{self.day}/day{self.day}.gif", mode="I" + ) as writer: + names = [ + f"gif-{self.day}/{x:05}.png" for x in range(0, self.f_count, frameskip) + ] print(names) for filename in names: try: diff --git a/2022/python/day01.py b/2022/python/day01.py index 5ce2f92..d93a151 100644 --- a/2022/python/day01.py +++ b/2022/python/day01.py @@ -6,7 +6,7 @@ def run(rows): elves = [] current_elf = 0 for row in rows: - if row == '': + if row == "": elves.append(current_elf) current_elf = 0 continue @@ -15,14 +15,13 @@ def run(rows): three = 0 for x in range(3): - most = max(elves) - idx = elves.index(most) - elves.pop(idx) - three += most + most = max(elves) + idx = elves.index(most) + elves.pop(idx) + three += most print(three) - def main(): with open(shared.get_fname(1), "r") as f: rows = [x.rstrip() for x in f.readlines()] diff --git a/2022/python/day02.py b/2022/python/day02.py index f371bce..250344f 100644 --- a/2022/python/day02.py +++ b/2022/python/day02.py @@ -10,31 +10,18 @@ tie = "Y" win = "Z" Moves = { - "A":rock, - "B":paper, - "C":scissors, - - "X":rock, - "Y":paper, - "Z":scissors, + "A": rock, + "B": paper, + "C": scissors, + "X": rock, + "Y": paper, + "Z": scissors, } -Scores = { - rock: 1, - paper: 2, - scissors: 3 -} +Scores = {rock: 1, paper: 2, scissors: 3} -LosesTo = { - rock: paper, - paper: scissors, - scissors: rock -} -WinsTo = { - paper: rock, - scissors: paper, - rock: scissors -} +LosesTo = {rock: paper, paper: scissors, scissors: rock} +WinsTo = {paper: rock, scissors: paper, rock: scissors} def winner(opponent, me): @@ -42,11 +29,16 @@ def winner(opponent, me): return 3 + Scores[me] # wins - if (opponent == rock and me == paper) or (opponent == paper and me == scissors) or (opponent == scissors and me == rock): + if ( + (opponent == rock and me == paper) + or (opponent == paper and me == scissors) + or (opponent == scissors and me == rock) + ): return 6 + Scores[me] return 0 + Scores[me] + def which_move(opponent, me): if me == lose: return WinsTo[Moves[opponent]] @@ -61,11 +53,12 @@ def run(moves): for move in moves: opponent, me = move my_move = which_move(opponent, me) - o_move = Moves[opponent] + o_move = Moves[opponent] pts = winner(o_move, my_move) score += pts print(score) + def main(): rows = [row.rstrip().split() for row in shared.load(2)] run(rows) diff --git a/2022/python/day02_golf.py b/2022/python/day02_golf.py index a114691..08a026e 100644 --- a/2022/python/day02_golf.py +++ b/2022/python/day02_golf.py @@ -4,26 +4,33 @@ R = "A" P = "B" S = "C" -X = "X" # lose -Y = "Y" # tie -Z = "Z" # win +X = "X" # lose +Y = "Y" # tie +Z = "Z" # win Moves = { - R: {X:S, Y:R, Z:P}, - P: {X:R, Y:P, Z:S}, - S: {X:P, Y:S, Z:R}, + R: {X: S, Y: R, Z: P}, + P: {X: R, Y: P, Z: S}, + S: {X: P, Y: S, Z: R}, } Scores = { - R: 1, P: 2, S: 3, X: 0, Y: 3, Z: 6, + R: 1, + P: 2, + S: 3, + X: 0, + Y: 3, + Z: 6, } + def run(moves): score = 0 for o, a in moves: score += Scores[Moves[o][a]] + Scores[a] print(score) + def main(): rows = [row.rstrip().split() for row in shared.load(2)] run(rows) diff --git a/2022/python/day03.py b/2022/python/day03.py index 8b561e2..4b536d8 100644 --- a/2022/python/day03.py +++ b/2022/python/day03.py @@ -4,17 +4,18 @@ import string def part1(rucksacks): total = 0 - for comp1,comp2 in [(r[:len(r)//2], r[len(r)//2:]) for r in rucksacks]: + for comp1, comp2 in [(r[: len(r) // 2], r[len(r) // 2 :]) for r in rucksacks]: c1 = set(x for x in comp1) c2 = set(x for x in comp2) match = list(c1.intersection(c2))[0] total += string.ascii_letters.index(match) + 1 print(total) + def part2(rucksacks): total = 0 for idx in range(0, len(rucksacks), 3): - r1,r2,r3 = rucksacks[idx:idx+3] + r1, r2, r3 = rucksacks[idx : idx + 3] c1 = set(x for x in r1) c2 = set(x for x in r2) c3 = set(x for x in r3) @@ -28,5 +29,6 @@ def main(): part1(rows) part2(rows) + if __name__ == "__main__": main() diff --git a/2022/python/day04.py b/2022/python/day04.py index acae2fb..96f5400 100644 --- a/2022/python/day04.py +++ b/2022/python/day04.py @@ -4,27 +4,27 @@ import shared def part1(x): internal = 0 for pair in x: - l,r = pair.split(",") - l1,l2 = map(int, l.split("-")) - r1,r2 = map(int, r.split("-")) + l, r = pair.split(",") + l1, l2 = map(int, l.split("-")) + r1, r2 = map(int, r.split("-")) if l1 <= r1 and l2 >= r2: internal += 1 elif r1 <= l1 and r2 >= l2: internal += 1 print(internal) - + def part2(x): internal = 0 for pair in x: - l,r = pair.split(",") + l, r = pair.split(",") ll = list(sorted(map(int, l.split("-")))) rl = list(sorted(map(int, r.split("-")))) - set1 = set(range(ll[0], ll[1]+1)) - set2 = set(range(rl[0], rl[1]+1)) + set1 = set(range(ll[0], ll[1] + 1)) + set2 = set(range(rl[0], rl[1] + 1)) if bool(set1 & set2): - internal +=1 + internal += 1 print(internal) diff --git a/2022/python/day05.psg.py b/2022/python/day05.psg.py new file mode 100644 index 0000000..e3e2a1a --- /dev/null +++ b/2022/python/day05.psg.py @@ -0,0 +1,150 @@ +from pprint import pprint as pp +import PySimpleGUI as sg +from shared import load_rows +from scanf import scanf +from string import ascii_letters +import re + +WATCHERS = {} +OUTPUT = [] + + +def load_crates(x): + done_crates = False + crates = [] + instructions = [] + for row in x: + if done_crates: + instructions.append(row) + else: + if row == "": + done_crates = True + else: + crates.append(row) + crates.pop() + return crates, instructions + + +def to_lists(crates): + parsed = [] + reg = re.compile(r"[\([{})\]]") + for row in crates: + parsed.append([x for x in reg.sub(" ", row)]) + parsed = list(zip(*parsed[::-1])) + parsed = list(zip(*parsed[::-1])) + parsed = list(zip(*parsed[::-1])) + cleaned1 = [[x for x in y if x.strip()] for y in parsed if y] + cleaned2 = [x for x in cleaned1 if x != []][::-1] + return cleaned2 + + +def parse_instructions(crates, instructions): + for instruction in instructions: + count, _from, _to = scanf("move %d from %d to %d", instruction) + update_watchers("count", count) + update_output(" ".join(map(str, (count, _from, _to)))) + _from -= 1 # 1 based yo + _to -= 1 + for x in range(count): + value = crates[_from].pop(0) + crates[_to].insert(0, value) + return crates + + +def parse_instructions_pt2(crates, instructions): + for instruction in instructions: + count, _from, _to = scanf("move %d from %d to %d", instruction) + _from -= 1 # 1 based yo + _to -= 1 + moving = crates[_from][:count] + update_output(" ".join(map(str, (instruction, moving)))) + for x in range(count): + crates[_from].pop(0) + for x in reversed(moving): + crates[_to].insert(0, x) + + return crates + + +def part1(x): + crates, instructions = load_crates(x) + crates = to_lists(crates) + crates = parse_instructions(crates, instructions) + update_output("".join([c[0] for c in crates])) + + +def part2(x): + crates, instructions = load_crates(x) + crates = parse_instructions_pt2(crates, instructions) + update_output("".join([c[0] for c in crates])) + + +def main(): + rows = load_rows(5) + sg_window(rows) + + +sg.Window("Day5") +sg.theme("DarkAmber") +watch_column = [ + [sg.Text("Watch Column")], + [sg.Text("------------")], + [sg.Text("", key="-WATCH-")], +] +output_column = [ + [sg.Text("Output Column")], + [sg.Text("------------")], + [sg.Text("", key="-OUTPUT-")], +] + +layout = [ + [sg.Button("Part1"), sg.Button("Part2"), sg.Button("X")], + [ + sg.Column(watch_column), + sg.VSeperator(), + sg.Column(output_column), + ], +] + +window = sg.Window("App", layout) + + +def update_output(new): + new = new.split("\n") + global OUTPUT + OUTPUT.extend(new) + if len(OUTPUT) > 40: + OUTPUT = OUTPUT[len(new) :] + window["-OUTPUT-"].update("\n".join(OUTPUT)) + + +def watcher_to_string(): + s = "" + for k, v in WATCHERS.items(): + s += f"{k}:{v}\n" + return s + + +def update_watchers(k, v): + WATCHERS[k] = v + window["-WATCH-"].update(watcher_to_string()) + + +def sg_window(rows): + while True: + event, values = window.Read() + if event == "X" or event == sg.WIN_CLOSED: + break + if event == "Part1": + part1(rows) + if event == "Part2": + part2(rows) + + window.Close() + + # part1(rows) + # part2(rows) + + +if __name__ == "__main__": + main() diff --git a/2022/python/day05.py b/2022/python/day05.py index 413d98b..d415344 100644 --- a/2022/python/day05.py +++ b/2022/python/day05.py @@ -13,41 +13,44 @@ def load_crates(x): if done_crates: instructions.append(row) else: - if row == '': + if row == "": done_crates = True else: crates.append(row) crates.pop() return crates, instructions + def to_lists(crates): parsed = [] reg = re.compile(r"[\([{})\]]") for row in crates: - parsed.append([x for x in reg.sub(" ",row)]) + parsed.append([x for x in reg.sub(" ", row)]) parsed = list(zip(*parsed[::-1])) parsed = list(zip(*parsed[::-1])) parsed = list(zip(*parsed[::-1])) - cleaned1 = [[x for x in y if x.strip()] for y in parsed if y] + cleaned1 = [[x for x in y if x.strip()] for y in parsed if y] cleaned2 = [x for x in cleaned1 if x != []][::-1] return cleaned2 + def parse_instructions(crates, instructions): for instruction in instructions: - count,_from,_to = scanf("move %d from %d to %d", instruction) - print(count,_from,_to) - _from -=1 # 1 based yo - _to -=1 + count, _from, _to = scanf("move %d from %d to %d", instruction) + print(count, _from, _to) + _from -= 1 # 1 based yo + _to -= 1 for x in range(count): value = crates[_from].pop(0) crates[_to].insert(0, value) return crates + def parse_instructions_pt2(crates, instructions): for instruction in instructions: - count,_from,_to = scanf("move %d from %d to %d", instruction) - _from -=1 # 1 based yo - _to -=1 + count, _from, _to = scanf("move %d from %d to %d", instruction) + _from -= 1 # 1 based yo + _to -= 1 moving = crates[_from][:count] print(instruction, moving) for x in range(count): @@ -56,7 +59,7 @@ def parse_instructions_pt2(crates, instructions): crates[_to].insert(0, x) return crates - + def part1(x): crates, instructions = load_crates(x) @@ -64,7 +67,7 @@ def part1(x): crates = parse_instructions(crates, instructions) print([c[0] for c in crates]) - + def part2(x): crates, instructions = load_crates(x) crates = to_lists(crates) @@ -74,7 +77,7 @@ def part2(x): def main(): rows = load_rows(5) - #part1(rows) + # part1(rows) part2(rows) diff --git a/2022/python/day06.py b/2022/python/day06.py index 0994c38..8fbcb62 100644 --- a/2022/python/day06.py +++ b/2022/python/day06.py @@ -1,14 +1,17 @@ from shared import load_rows + def part1(row, group_size=4): for x in range(len(row)): - if (len(set(row[x:x+group_size])) == group_size): - print(x+group_size) + if len(set(row[x : x + group_size])) == group_size: + print(x + group_size) break - + + def part2(row): part1(row, 14) + def main(): rows = load_rows(6) for row in rows: diff --git a/2022/python/day07.py b/2022/python/day07.py index a75fc56..a7e594b 100644 --- a/2022/python/day07.py +++ b/2022/python/day07.py @@ -8,77 +8,80 @@ from functools import reduce import operator PWD = [] -FS = { "/": {".":{"size":0, "files":[]}} } +FS = {"/": {".": {"size": 0, "files": []}}} + def get_keys(input_dict): for key, value in input_dict.items(): if isinstance(value, dict): for subkey in get_keys(value): - yield key + ',' + subkey + yield key + "," + subkey else: if key in ("files"): continue yield f"{value}" + def getFromDict(mapList): return reduce(operator.getitem, mapList, FS) + def setInDict(mapList, value): getFromDict(mapList[:-1])[mapList[-1]] = value + def addInDict(mapList, filename, value): try: v = getFromDict(mapList[:-1])[mapList[-1]] v += value getFromDict(mapList[:-1])[mapList[-1]] = v except KeyError: - getFromDict(mapList[:-1])["."] = {"size": 0, "files":[]} + getFromDict(mapList[:-1])["."] = {"size": 0, "files": []} v = getFromDict(mapList[:-1])[mapList[-1]] - v["size"] += 0 # Value + v["size"] += 0 # Value v["files"].append(f"{filename}:{value}") getFromDict(mapList[:-1])[mapList[-1]] = v except TypeError: v = getFromDict(mapList[:-1])["."] - v["size"] += 0 # value + v["size"] += 0 # value v["files"].append(f"{filename}:{value}") getFromDict(mapList[:-1])[mapList[-1]] = v - - def part1(rows): build_fs(rows) - #calculate_directories() + # calculate_directories() + def calculate_directories(): keys = list(get_keys(FS)) new_FS = {} for key in keys: pwd = key.split(",") - size = int(pwd.pop()) # split out size - pwd.pop() # remove . + size = int(pwd.pop()) # split out size + pwd.pop() # remove . pwd_key = ",".join(pwd) parent = ",".join(pwd[:-1]) - new_FS[pwd_key] = {"size":size, "full_size":size, "parent":parent} + new_FS[pwd_key] = {"size": size, "full_size": size, "parent": parent} print("made") - #pp(new_FS) - #print("----") - #for pwd, directory in new_FS.items(): + # pp(new_FS) + # print("----") + # for pwd, directory in new_FS.items(): # print(pwd, "\t", directory['parent']) print(new_FS.keys()) print("keys^") for pwd, directory in new_FS.items(): - parent = directory['parent'] + parent = directory["parent"] if parent: print(parent) - #print(f"{pwd}:{parent} Adding {directory['size']} to fullsize {new_FS[parent]['full_size']}") + # print(f"{pwd}:{parent} Adding {directory['size']} to fullsize {new_FS[parent]['full_size']}") new_FS[parent]["size"] += directory["size"] print("added sizes") sizes = [] - for k,v in new_FS.items(): - sizes.append(v['size']) + for k, v in new_FS.items(): + sizes.append(v["size"]) total = 0 for size in sizes: @@ -86,7 +89,8 @@ def calculate_directories(): print("+", size) total += size - print("=" ,total) + print("=", total) + def build_fs(rows): LS_ING = False @@ -102,7 +106,7 @@ def build_fs(rows): PWD.pop() else: PWD.append(parts[2]) - #print(PWD) + # print(PWD) elif parts[1] == "ls": LS_ING = True continue @@ -113,6 +117,7 @@ def build_fs(rows): add_file(parts[1], int(parts[0])) jp(FS) + def jp(d): output = json.dumps(FS, indent=4) output2 = re.sub(r'": \[\s+', '": [', output) @@ -123,26 +128,28 @@ def jp(d): def add_directory(dirname): temp_new_path = PWD + [dirname] - setInDict(temp_new_path, {".":{"size":0, "files":[]}}) + setInDict(temp_new_path, {".": {"size": 0, "files": []}}) + def add_file(filename, size): - #print(".", PWD, filename, size) + # print(".", PWD, filename, size) mapList = PWD + ["."] - addInDict(mapList, filename, size) + addInDict(mapList, filename, size) curr = getFromDict(mapList[:-1]) print(curr) - #print(curr) + # print(curr) s = curr["."] s["size"] += size - print(size,s) + print(size, s) tmp = mapList + ["size"] setInDict(mapList, s) print("-----") - + def part2(row): pass + def main(): rows = load_rows(7) part1(rows) diff --git a/2022/python/day08.py b/2022/python/day08.py index 6d1f43b..4f06ce5 100644 --- a/2022/python/day08.py +++ b/2022/python/day08.py @@ -3,16 +3,15 @@ import shared import matrix - def part1(mx): - SIZE=len(mx) - MAX_IDX = SIZE -1 + SIZE = len(mx) + MAX_IDX = SIZE - 1 data = matrix.matrix_of_size(SIZE, SIZE) data = matrix.set_matrix_dict(data) for row in range(SIZE): - for col in range(SIZE): - data[row][col] = { "l": True, "r": True, "u": True, "d": True } + for col in range(SIZE): + data[row][col] = {"l": True, "r": True, "u": True, "d": True} for row, _row in enumerate(mx): # Skip outer edges @@ -28,7 +27,7 @@ def part1(mx): trees_u_indexes = list(range(0, row)) trees_l_indexes = list(range(0, col)) trees_d_indexes = list(range(MAX_IDX, row, -1)) - trees_r_indexes = list(range(col+1, SIZE)) + trees_r_indexes = list(range(col + 1, SIZE)) # Get the trees in the line trees_u = [mx[y][col] for y in trees_u_indexes] @@ -45,50 +44,50 @@ def part1(mx): # if there's anything as tall, or taller than this tree in that line # then it's not visible if highest_u >= _val: - data[row][col]["u"] = False + data[row][col]["u"] = False if highest_l >= _val: - data[row][col]["l"] = False + data[row][col]["l"] = False if highest_d >= _val: - data[row][col]["d"] = False + data[row][col]["d"] = False if highest_r >= _val: - data[row][col]["r"] = False + data[row][col]["r"] = False # count visible count = 0 for row in range(SIZE): - for col in range(SIZE): + for col in range(SIZE): u = data[row][col]["u"] l = data[row][col]["l"] d = data[row][col]["d"] r = data[row][col]["r"] - if any((r,l,u,d)): + if any((r, l, u, d)): count += 1 print(count) def part2(mx): - SIZE=len(mx) - MAX_IDX = SIZE -1 + SIZE = len(mx) + MAX_IDX = SIZE - 1 data = matrix.matrix_of_size(SIZE, SIZE) data = matrix.set_matrix_dict(data) high_score = 0 for row in range(SIZE): - for col in range(SIZE): - data[row][col] = { "value": mx[row][col]} + for col in range(SIZE): + data[row][col] = {"value": mx[row][col]} for row, _row in enumerate(mx): for col, _val in enumerate(mx[row]): # Get a list of indexes in the current direction to check # TODO: slice trees_u_indexes = list(range(0, row)) - trees_l_indexes = list(range(0,col)) + trees_l_indexes = list(range(0, col)) trees_d_indexes = list(range(MAX_IDX, row, -1)) - trees_r_indexes = list(range(col+1, SIZE)) + trees_r_indexes = list(range(col + 1, SIZE)) # Grab the trees potentially in sight trees_u = [mx[y][col] for y in trees_u_indexes] @@ -103,17 +102,17 @@ def part2(mx): data[row][col]["r"] = trees_r # Calculate score - score = 1 # identity yoooo + score = 1 # identity yoooo cell = data[row][col] - val = cell['value'] + val = cell["value"] # Get the score of visible trees in each direction - for direction in ("u","l","d","r"): + for direction in ("u", "l", "d", "r"): in_line = cell[direction] if not in_line: # we're on an edge score = 0 break - + line_score = 0 for idx, tree in enumerate(in_line): # for every tree, check if its as tall or taller, @@ -126,17 +125,18 @@ def part2(mx): high_score = score print(high_score) + def part2_with_fixes(mx): - SIZE=len(mx) - MAX_IDX = SIZE -1 + SIZE = len(mx) + MAX_IDX = SIZE - 1 data = matrix.matrix_of_size(SIZE, SIZE) data = matrix.set_matrix_dict(data) high_score = 0 for row in range(SIZE): - for col in range(SIZE): - data[row][col] = { "value": mx[row][col]} + for col in range(SIZE): + data[row][col] = {"value": mx[row][col]} for row, _row in enumerate(mx): for col, _val in enumerate(mx[row]): @@ -148,16 +148,16 @@ def part2_with_fixes(mx): data[row][col]["r"] = lineofsight["R"] # Calculate score - score = 1 # identity yoooo - val = data[row][col]['value'] + score = 1 # identity yoooo + val = data[row][col]["value"] # Get the score of visible trees in each direction - for direction in ("u","l","d","r"): + for direction in ("u", "l", "d", "r"): in_line = data[row][col][direction] if not in_line: # we're on an edge score = 0 break - + line_score = 0 for idx, tree in enumerate(in_line): # for every tree, check if its as tall or taller, @@ -170,6 +170,7 @@ def part2_with_fixes(mx): high_score = score print(high_score) + def main(): mx = matrix.load_matrix_file(shared.get_fname(8)) with shared.elapsed_timer() as elapsed: @@ -183,6 +184,5 @@ def main(): print(elapsed()) - if __name__ == "__main__": main() diff --git a/2022/python/day09.py b/2022/python/day09.py index 6e62fe0..ad77369 100644 --- a/2022/python/day09.py +++ b/2022/python/day09.py @@ -5,28 +5,28 @@ import shared import matrix MATRIX_SIZE = 400 -ATRIX_SIZE = 40 # sample +ATRIX_SIZE = 40 # sample HALF = MATRIX_SIZE // 2 -H = [HALF,HALF] -T = [HALF,HALF] +H = [HALF, HALF] +T = [HALF, HALF] # returns (x,y) - so remember to take dx,dy lower DIRS = { - 'U': matrix.M_U, # (0, -1) - 'D': matrix.M_D, - 'L': matrix.M_L, - 'R': matrix.M_R, - - 'UR': matrix.M_UR, # (+1, -1) - 'DR': matrix.M_DR, - 'UL': matrix.M_UL, - 'DL': matrix.M_DL, + "U": matrix.M_U, # (0, -1) + "D": matrix.M_D, + "L": matrix.M_L, + "R": matrix.M_R, + "UR": matrix.M_UR, # (+1, -1) + "DR": matrix.M_DR, + "UL": matrix.M_UL, + "DL": matrix.M_DL, } + def part1(steps): - field = matrix.matrix_of_size(MATRIX_SIZE,MATRIX_SIZE) + field = matrix.matrix_of_size(MATRIX_SIZE, MATRIX_SIZE) anim = Animate(field, day="09") for direction, amount in steps: @@ -34,21 +34,21 @@ def part1(steps): amount = int(amount) for x in range(amount): - dx,dy = DIRS[direction] - H[0] += dy + dx, dy = DIRS[direction] + H[0] += dy H[1] += dx - HY,bX = H + HY, bX = H # head is in new position - kY,kX = T - field[kY][kX] = 1 # 'T' + kY, kX = T + field[kY][kX] = 1 # 'T' # Check if same if kY == HY and kX == bX: continue touching = False for _, d in DIRS.items(): - dx,dy = d - t_y, t_x = H[0]+dy, H[1]+dx + dx, dy = d + t_y, t_x = H[0] + dy, H[1] + dx if kY == t_y and kX == t_x: touching = True break @@ -58,66 +58,67 @@ def part1(steps): # assume the same row/column, no movement needed tY = 0 tX = 0 - + # if Head X is less than Tail X, move Tail X 1 left if bX < kX: tX = -1 # if Head X is greater than Tail X, move Tail X 1 left elif bX > kX: tX = 1 - - + # if Head Y is less than Tail Y, move Tail Y 1 up if HY < kY: tY = -1 # if Head Y is greater than Tail Y, move Tail Y 1 down elif HY > kY: tY = 1 - + T[0] += tY T[1] += tX anim.add_frame(field) - field[T[0]][T[1]] = 1 # mark visited tails + field[T[0]][T[1]] = 1 # mark visited tails print(matrix.sum_matrix(field)) anim.animate() - + def part2(steps): - field = matrix.matrix_of_size(MATRIX_SIZE,MATRIX_SIZE) + field = matrix.matrix_of_size(MATRIX_SIZE, MATRIX_SIZE) anim = Animate(field, day="09") - S = [[HALF,HALF],] # HEAD ONLY + S = [ + [HALF, HALF], + ] # HEAD ONLY _cur_frame = 0 for x in range(9): - S.append([HALF,HALF]) + S.append([HALF, HALF]) for direction, amount in steps: # move HEAD amount = int(amount) for x in range(amount): - HEAD_dx,HEAD_dy = DIRS[direction] - S[0][0] += HEAD_dy + HEAD_dx, HEAD_dy = DIRS[direction] + S[0][0] += HEAD_dy S[0][1] += HEAD_dx - #print(HEAD_dy,HEAD_dx, S) + # print(HEAD_dy,HEAD_dx, S) # HEAD MOVED one step - - for knot_idx, _ in enumerate(S): + + for knot_idx, _ in enumerate(S): if knot_idx == 0: # Skip Head continue knot = S[knot_idx] - kY,kX = knot + kY, kX = knot - bY,bX = S[knot_idx-1] + bY, bX = S[knot_idx - 1] - # Check if same + # Check if same if kY == bY and kX == bX: continue touching = False for _, d in DIRS.items(): - dx,dy = d - t_y, t_x = S[knot_idx-1][0]+dy, S[knot_idx-1][1]+dx + dx, dy = d + t_y, t_x = S[knot_idx - 1][0] + dy, S[knot_idx - 1][1] + dx if kY == t_y and kX == t_x: touching = True break @@ -125,36 +126,35 @@ def part2(steps): continue # assume the same row/column, no movement needed - tY, tX = 0,0 - + tY, tX = 0, 0 + # if Head X is less than Tail X, move Tail X 1 left if bX < kX: tX = -1 # if Head X is greater than Tail X, move Tail X 1 left elif bX > kX: tX = 1 - + # if Head Y is less than Tail Y, move Tail Y 1 up if bY < kY: tY = -1 # if Head Y is greater than Tail Y, move Tail Y 1 down elif bY > kY: tY = 1 - + S[knot_idx][0] += tY S[knot_idx][1] += tX if _cur_frame % 25 == 0: anim.add_frame(field) _cur_frame += 1 - field[S[-1][0]][S[-1][1]] = 1 # mark visited tails + field[S[-1][0]][S[-1][1]] = 1 # mark visited tails print(matrix.sum_matrix(field)) anim.animate() - def main(): rows = [x.split(" ") for x in shared.load_rows(9)] - #part1(rows) + # part1(rows) part2(rows) diff --git a/2022/python/day10.py b/2022/python/day10.py index 8f4ba6b..6fccd7f 100644 --- a/2022/python/day10.py +++ b/2022/python/day10.py @@ -3,9 +3,10 @@ from anim import Animate import matrix cycles = { - "noop": 1, - "addx": 2, - } + "noop": 1, + "addx": 2, +} + def part1(x): X = 1 @@ -22,23 +23,21 @@ def part1(x): for x in range(cycles[opcode]): cycle_count += 1 - if cycle_count == 20 or ((cycle_count -20) % 40 == 0): - signal_strength.append(cycle_count*X) + if cycle_count == 20 or ((cycle_count - 20) % 40 == 0): + signal_strength.append(cycle_count * X) if opcode == "addx": X += val print(sum(signal_strength)) - - def part2(x): def cycle_to_xy(cycle): x = cycle % 40 y = cycle // 40 - return x,y + return x, y - screen = matrix.matrix_of_size(40,6) + screen = matrix.matrix_of_size(40, 6) X = 1 cycle_count = 0 for step, instruction in enumerate(x): @@ -50,8 +49,8 @@ def part2(x): opcode = instruction for x in range(cycles[opcode]): - _x,y = cycle_to_xy(cycle_count) - criteria = (X-1, X, X+1) + _x, y = cycle_to_xy(cycle_count) + criteria = (X - 1, X, X + 1) cycle_count += 1 if _x in criteria: screen[y][_x] = 1 @@ -59,15 +58,16 @@ def part2(x): if opcode == "addx": X += val - matrix.ppmx(screen,pad=False, space=False) + matrix.ppmx(screen, pad=False, space=False) anim = Animate(screen, day="10") anim.add_frame(screen) print("wrote gif-10/day10.gif") + def main(): rows = [row for row in shared.load(10)] part1(rows) - print("%"*40) + print("%" * 40) part2(rows) diff --git a/2022/python/day11.py b/2022/python/day11.py index 886a35b..7e21a05 100644 --- a/2022/python/day11.py +++ b/2022/python/day11.py @@ -29,10 +29,10 @@ def load_monkeys(lines): try: by = int(line.split(" ")[-1]) except ValueError: - by = None # know None means by self - if '*' in op: + by = None # know None means by self + if "*" in op: operand = operator.mul - elif '+' in op: + elif "+" in op: operand = operator.add elif line.lstrip().startswith("Test"): div = int(line.split(" ")[-1]) @@ -41,19 +41,14 @@ def load_monkeys(lines): elif line.lstrip().startswith("If false"): false = int(line.split(" ")[-1]) monkey = Monkey( - number=count, - items=items, - op=operand, - by=by, - div=div, - t=true, - f=false - ) + number=count, items=items, op=operand, by=by, div=div, t=true, f=false + ) monkeys.append(monkey) - count+=1 + count += 1 lcm = math.lcm(*[m.div for m in monkeys]) return monkeys, lcm + def part1(lines): monkeys, _ = load_monkeys(lines) @@ -78,7 +73,7 @@ def part1(lines): total = counts[-1] * counts[-2] print(total) - + def part2(lines): monkeys, lcm = load_monkeys(lines) diff --git a/2022/python/day12.py b/2022/python/day12.py new file mode 100644 index 0000000..b1a7c4e --- /dev/null +++ b/2022/python/day12.py @@ -0,0 +1,116 @@ +import shared +import matrix +from dijkstar import Graph, find_path, algorithm + + +#def dj_walk(mx, start, end): +# def next_cost(c, val): +# if val > c+1: +# return 9999 +# else: +# return val +# +# start = start[1], start[0] +# end = end[1], end[0] +# +# # Dijkstra from RedBlobGames +# frontier = PriorityQueue() +# frontier.put(start, 0) +# came_from = dict() +# cost_so_far = dict() +# came_from[start] = None +# cost_so_far[start] = 0 +# last = None +# while not frontier.empty(): +# x, y = frontier.get() +# cur = (x, y) +# if cur == end: +# break +# for _n in matrix.get_neighbors(mx, x=x, y=y, _dict=True): +# nxt = (_n["x"], _n["y"]) +# v = _n["value"] +# +# new_cost = next_cost(cost_so_far[cur], v) +# if nxt not in cost_so_far or new_cost < cost_so_far[nxt]: +# cost_so_far[nxt] = new_cost +# priority = new_cost +# frontier.put(nxt, priority) +# came_from[nxt] = cur +# last = cur +# print(len(cost_so_far)) + +criteria = lambda _cur, _neighbor: _neighbor - _cur <= 1 +def build_graph(mx): + graph = Graph() + for y, row in enumerate(mx): + for x, _ in enumerate(row): + neighbors = matrix.valid_neighbors(mx, x=x, y=y, criteria=criteria) + for neighbor in neighbors: + graph.add_edge((y, x), (neighbor['y'],neighbor['x']), 1) + return graph + + + +#SEEN = [] +#def walk(mx, y, x, seen=[]): +# print(len(seen)) +# valid_next = matrix.valid_neighbors(mx, y, x, criteria=criteria) +# print(valid_next) +# matrix.highlight( mx, red=seen, green=[ *valid_next, ], blue=[ END, ],) +# for next_yx in valid_next: +# if next_yx not in seen: +# seen.append(next_yx) +# _y, _x = next_yx +# walk(mx, _y, _x, seen) +# else: +# seen.append(next_yx) + +def elev(yx): + y,x = yx + return mx[y][x] + +def part1(mx): + start = matrix.find_in_matrix(mx, "S") + end = matrix.find_in_matrix(mx, "E") + mx[start[0]][start[1]] = "a" + mx[end[0]][end[1]] = "z" + matrix.apply_to_all(mx, lambda x: ord(x) - ord('a')) + + #walk(mx, cur[0],cur[0], seen=[(cur[0],cur[1])]) + #dj_walk(mx, cur, END) + graph = build_graph(mx) + path = find_path(graph, start, end) + print(len(path.nodes)) + + +def part2(mx): + end = matrix.find_in_matrix(mx, "E") + s = matrix.find_in_matrix(mx, "S") + mx[s[0]][s[1]] = "a" + mx[end[0]][end[1]] = "z" + + starts = matrix.find_in_matrix(mx, 'a', one=False) + matrix.apply_to_all(mx, lambda x: ord(x) - ord('a')) + graph = build_graph(mx) + + n_counts = [] + for start in starts: + #walk(mx, cur[0],cur[0], seen=[(cur[0],cur[1])]) + #dj_walk(mx, cur, END) + try: + path = find_path(graph, start, end) + n_counts.append(len(path.nodes)-1) + except algorithm.NoPathError: + pass + print(n_counts) + print(min(n_counts)) + + +def main(): + mx = matrix.load_matrix_file(shared.get_fname(12), matrix.split_word_to_chr_list) + #part1(mx) + part2(mx) + + +if __name__ == "__main__": + main() diff --git a/2022/python/matrix.py b/2022/python/matrix.py index 6efd5a5..c9f5ff9 100644 --- a/2022/python/matrix.py +++ b/2022/python/matrix.py @@ -1,10 +1,28 @@ +from copy import deepcopy from collections import defaultdict from typing import List, Dict, Tuple + +split_word_to_chr_list = lambda y: [w for w in y] split_word_to_int_list = lambda y: [int(w) for w in y] split_line_to_int_list = lambda y: [int(w) for w in y.split(" ") if w] +class colors: + # HEADER = '\033[95m' + BLUE = "\033[94m" + GREEN = "\033[92m" + YELLOW = "\033[93m" + RED = "\033[91m" + ENDC = "\033[0m" + + +def apply_to_all(mx, func): + for row_num, row in enumerate(mx): + for col_num, val in enumerate(row): + mx[row_num][col_num] = func(val) + + def rotate(m, right=True): # -90 """ Takes a matrix, and rotates all of the values 90 degrees to the left @@ -17,7 +35,7 @@ def rotate(m, right=True): # -90 def load_matrix_file(name, func=None): """ - Open a file and split all space separated word lists to integers as a matrix + Open a file and split all space separated word lists to integers as a matrix """ with open(name, "r") as f: my_file = [] @@ -28,13 +46,26 @@ def load_matrix_file(name, func=None): return [split_word_to_int_list(x) for x in my_file] +def find_in_matrix(mx, what, one=True): + coords = [] + for row_num, row in enumerate(mx): + for col_num, val in enumerate(row): + if val == what: + coord = (row_num, col_num) + if one is True: + return coord + else: + coords.append(coord) + return coords + + def get_neighbors(matrix, x, y, _dict=False): neighbors = [] # left try: if x - 1 >= 0: if _dict: - neighbors.append({'x':x - 1,'y':y,'value':matrix[y][x - 1]}) + neighbors.append({"x": x - 1, "y": y, "value": matrix[y][x - 1]}) else: neighbors.append([(x - 1, y), matrix[y][x - 1]]) except IndexError: @@ -42,7 +73,7 @@ def get_neighbors(matrix, x, y, _dict=False): # right try: if _dict: - neighbors.append({'x':x + 1,'y':y,'value':matrix[y][x + 1]}) + neighbors.append({"x": x + 1, "y": y, "value": matrix[y][x + 1]}) else: neighbors.append([(x + 1, y), matrix[y][x + 1]]) except IndexError: @@ -52,7 +83,7 @@ def get_neighbors(matrix, x, y, _dict=False): try: if y - 1 >= 0: if _dict: - neighbors.append({'x':x,'y':y-1,'value':matrix[y-1][x]}) + neighbors.append({"x": x, "y": y - 1, "value": matrix[y - 1][x]}) else: neighbors.append([(x, y - 1), matrix[y - 1][x]]) except IndexError: @@ -61,7 +92,7 @@ def get_neighbors(matrix, x, y, _dict=False): # down try: if _dict: - neighbors.append({'x':x,'y':y+1,'value':matrix[y+1][x]}) + neighbors.append({"x": x, "y": y + 1, "value": matrix[y + 1][x]}) else: neighbors.append([(x, y + 1), matrix[y + 1][x]]) except IndexError: @@ -69,6 +100,19 @@ def get_neighbors(matrix, x, y, _dict=False): return neighbors + +def valid_neighbors(matrix, x, y, criteria=None): + if criteria is None: + raise Exception("Please pass in a lambda for criteria") + cur = matrix[y][x] + neighbors = get_neighbors(matrix, x, y, _dict=True) + valid = [] + for neighbor in neighbors: + if criteria(cur, neighbor['value']): + valid.append(neighbor) + return valid + + def sum_matrix(mtx): total = 0 for row in mtx: @@ -80,27 +124,20 @@ M_UL, M_U, M_UR = (-1, -1), (0, -1), (1, -1) M_L, M_R = (-1, 0), (1, 0) M_DL, M_D, M_DR = (-1, 1), (0, 1), (1, 1) + def get_neighbor_coords(matrix, c, r, diagonals=True): height = len(matrix) width = len(matrix[0]) if diagonals: - coords = ( - M_UL, M_U, M_UR, - M_L, M_R, - M_DL, M_D, M_DR - ) + coords = (M_UL, M_U, M_UR, M_L, M_R, M_DL, M_D, M_DR) else: - coords = ( - M_U, - M_L,M_R, - M_D - ) + coords = (M_U, M_L, M_R, M_D) neighbors = [] for _c, _r in coords: try: value = matrix[r + _r][c + _c] # Try to get a value error - if (r+_r>=0 and c+_c >= 0): + if r + _r >= 0 and c + _c >= 0: neighbors.append( [{"c": c + _c, "r": r + _r}, value] ) # woo, no error, this coord is valid @@ -108,37 +145,39 @@ def get_neighbor_coords(matrix, c, r, diagonals=True): pass # okay we out of bounds boizzzz return neighbors -def line_of_sight_coords(matrix, row,col) -> Dict[str,List[Tuple[int,int]]]: - """ - Takes a matrix, a row, and a column - calculates the coordinates to the edge for all four cardinal directions - returns a dict with a list of tuple coordes TRAVELING AWAY from the - requested coordinate +def line_of_sight_coords(matrix, row, col) -> Dict[str, List[Tuple[int, int]]]: + """ + Takes a matrix, a row, and a column + calculates the coordinates to the edge for all four cardinal directions + + returns a dict with a list of tuple coordes TRAVELING AWAY from the + requested coordinate """ height, width = get_size(matrix) col_ids = list(range(0, height)) row_ids = list(range(0, width)) - up_ids,down_ids = list(reversed(col_ids[:col])), col_ids[col+1:] - left_ids,right_ids = list(reversed(row_ids[:row])), row_ids[row+1:] + up_ids, down_ids = list(reversed(col_ids[:col])), col_ids[col + 1 :] + left_ids, right_ids = list(reversed(row_ids[:row])), row_ids[row + 1 :] - left = [(r,col) for r in left_ids] - right = [(r,col) for r in right_ids] - up = [(row,c) for c in up_ids] - down = [(row,c) for c in down_ids] + left = [(r, col) for r in left_ids] + right = [(r, col) for r in right_ids] + up = [(row, c) for c in up_ids] + down = [(row, c) for c in down_ids] return { - 'U':up, - 'L':left, - 'D':down, - 'R':right, + "U": up, + "L": left, + "D": down, + "R": right, } + def line_of_sight(mx, row, col): """ - renders a line of sight coord calculation, into the values + renders a line of sight coord calculation, into the values """ coords = line_of_sight_coords(mx, row, col) los = defaultdict(list) @@ -148,22 +187,23 @@ def line_of_sight(mx, row, col): return los - def get_size(matrix): height = len(matrix) width = len(matrix[0]) return height, width + def row_col_from_int(matrix, x): - h,w = get_size(matrix) + h, w = get_size(matrix) col = x % w row = x // h - return row,col + return row, col def matrix_of_size(width, height, default=0): return [[default] * width for x in range(height)] + def set_matrix_dict(m): for x in range(len(m)): for y in range(len(m[x])): @@ -173,7 +213,7 @@ def set_matrix_dict(m): def pmx(*matrices, pad=True, space=True): """ - print a matrix of integers, zero turns to `.` for clarity + print a matrix of integers, zero turns to `.` for clarity """ if len(matrices) > 1: matrices = list(zip(*matrices)) @@ -195,9 +235,10 @@ def pmx(*matrices, pad=True, space=True): f = lambda x: f"{int(x)or '.'} " print("".join([f(x) for x in c])) + def ppmx(*matrices, pad=True, space=True): """ - print a matrix of anything, Falsy values turns to `.` for clarity + print a matrix of anything, Falsy values turns to `.` for clarity """ if len(matrices) > 1: matrices = list(zip(*matrices)) @@ -218,3 +259,23 @@ def ppmx(*matrices, pad=True, space=True): if space: f = lambda x: f"{x or '.'} " print("".join([f(x) for x in c])) + + +def highlight(matrix, red=[], green=[], blue=[]): + """ + print a matrix of anything, Falsy values turns to `.` for clarity + """ + mx = deepcopy(matrix) + for (y, x) in red: + try: + new = f"{colors.RED}{mx[y][x]}{colors.ENDC}" + mx[y][x] = new + except IndexError: + breakpoint() + for (y, x) in green: + new = f"{colors.GREEN}{mx[y][x]}{colors.ENDC}" + mx[y][x] = new + for (y, x) in blue: + new = f"{colors.BLUE}{mx[y][x]}{colors.ENDC}" + mx[y][x] = new + ppmx(mx, pad=False, space=False) diff --git a/2022/python/shared.py b/2022/python/shared.py index 06fe888..63c2197 100644 --- a/2022/python/shared.py +++ b/2022/python/shared.py @@ -4,13 +4,16 @@ from pathlib import Path spl = lambda y: [int(w) for w in y] + def load_rows(day): return [row for row in load(day)] + def load(day): path = Path(get_fname(day)) return path.read_text().rstrip().split("\n") + def get_fname(day: int) -> str: import sys @@ -27,6 +30,7 @@ def load_char_matrix(f): my_file.append(line.rstrip()) return [list(x) for x in my_file] + def load_file_char_matrix(name): with open(name, "r") as f: return load_char_matrix(f) @@ -38,21 +42,27 @@ def load_int_matrix(f): my_file.append(line.rstrip()) return [list(map(int, x)) for x in my_file] + def load_file_int_matrix(name): with open(name, "r") as f: return load_int_matrix(f) + def load_word_matrix(f): my_file = [] for line in f: my_file.append(line.rstrip()) return [x.split(" ") for x in my_file] + def load_file_word_matrix(name): with open(name, "r") as f: return load_word_matrix(f) + + ############# + def rotate(WHAT, times=1): what = WHAT for x in range(times): @@ -60,11 +70,10 @@ def rotate(WHAT, times=1): return what - @contextmanager def elapsed_timer(): start = default_timer() elapser = lambda: default_timer() - start yield lambda: elapser() end = default_timer() - elapser = lambda: end-start + elapser = lambda: end - start