day16 still working:

This commit is contained in:
Tyrel Souza 2022-12-16 22:07:17 -05:00
parent 139b6d7e12
commit d7a1aac178
7 changed files with 247 additions and 179 deletions

View File

@ -8,11 +8,11 @@ def part1(rows):
pass pass
# @shared.profile # @shared.profile
def part2(rows): def part2(rows):
pass pass
def main(): def main():
rows = [row for row in shared.load_rows(15)] rows = [row for row in shared.load_rows(15)]
with shared.elapsed_timer() as elapsed: with shared.elapsed_timer() as elapsed:

View File

@ -66,15 +66,18 @@ def check_group(left, right):
elif left == right: elif left == right:
return None return None
def part2(rows): def part2(rows):
two, six = 0, 0 two, six = 0, 0
rows = [eval(r) for r in rows if r] rows = [eval(r) for r in rows if r]
rows.append([[2]]) rows.append([[2]])
rows.append([[6]]) rows.append([[6]])
def cmp(x, y): def cmp(x, y):
return {True: 1,False: -1,}[check_group(x, y)] return {
True: 1,
False: -1,
}[check_group(x, y)]
idx = 0 # ew 1 based lists, jeeeze AoC idx = 0 # ew 1 based lists, jeeeze AoC
for r in sorted(rows, key=functools.cmp_to_key(cmp), reverse=True): for r in sorted(rows, key=functools.cmp_to_key(cmp), reverse=True):
@ -88,8 +91,6 @@ def part2(rows):
print(two * six) print(two * six)
def main(): def main():
rows = shared.load_rows(13) rows = shared.load_rows(13)
with shared.elapsed_timer() as elapsed: with shared.elapsed_timer() as elapsed:

View File

@ -23,7 +23,14 @@ def part1(rows):
d = sand[1] d = sand[1]
dr = sand[1] + 1 dr = sand[1] + 1
return next_y, (dl, d, dr) return next_y, (dl, d, dr)
lines = [[list(reversed(list(map(int, point.split(","))))) for point in row.split(" -> ")] for row in rows]
lines = [
[
list(reversed(list(map(int, point.split(",")))))
for point in row.split(" -> ")
]
for row in rows
]
(minY, maxY), (minX, maxX) = find_bounds(lines) (minY, maxY), (minX, maxX) = find_bounds(lines)
mx = matrix.matrix_of_size(maxX + 2, maxY + 2) mx = matrix.matrix_of_size(maxX + 2, maxY + 2)
walls = [] walls = []
@ -75,7 +82,13 @@ def part1(rows):
@shared.profile @shared.profile
def part2(rows): def part2(rows):
lines = [[list(reversed(list(map(int, point.split(","))))) for point in row.split(" -> ")] for row in rows] lines = [
[
list(reversed(list(map(int, point.split(",")))))
for point in row.split(" -> ")
]
for row in rows
]
(minY, maxY), (minX, maxX) = find_bounds(lines) (minY, maxY), (minX, maxX) = find_bounds(lines)
walls = {} walls = {}
_between_points = matrix.coords_between_points _between_points = matrix.coords_between_points
@ -120,6 +133,7 @@ def part2(rows):
walls[tuple(sand)] = "#" walls[tuple(sand)] = "#"
break break
def main(): def main():
rows = [row for row in shared.load_rows(14)] rows = [row for row in shared.load_rows(14)]
with shared.elapsed_timer() as elapsed: with shared.elapsed_timer() as elapsed:

View File

@ -8,9 +8,11 @@ from typing import Optional, List, Tuple
from dataclasses import dataclass from dataclasses import dataclass
from collections import defaultdict from collections import defaultdict
def cityblock(y1, x1, y2, x2): def cityblock(y1, x1, y2, x2):
return abs(y2 - y1) + abs(x2 - x1) return abs(y2 - y1) + abs(x2 - x1)
@dataclass @dataclass
class Sensor: class Sensor:
sX: int sX: int
@ -23,8 +25,10 @@ class Sensor:
_border: List[Tuple[int, int]] = None _border: List[Tuple[int, int]] = None
def __str__(self): def __str__(self):
return (f"Sensor(sX={self.sX}, sY={self.sY}, bX={self.bX}," return (
f"bY={self.bY}, d={self._d}, edges={len(self._edges)}, borders={len(self._borders)})") f"Sensor(sX={self.sX}, sY={self.sY}, bX={self.bX},"
f"bY={self.bY}, d={self._d}, edges={len(self._edges)}, borders={len(self._borders)})"
)
@property @property
def s(self): def s(self):
@ -62,7 +66,6 @@ class Sensor:
return False return False
return True return True
def in_diamond(self): def in_diamond(self):
sX, sY = self.sX, self.sY sX, sY = self.sX, self.sY
up_lim = sY - self.distance up_lim = sY - self.distance
@ -150,7 +153,9 @@ def part1(rows, sample=False):
xSet = set() xSet = set()
ySet = set() ySet = set()
for row in rows: for row in rows:
x,y,bx,by = scanf("Sensor at x=%d, y=%d: closest beacon is at x=%d, y=%d", row) x, y, bx, by = scanf(
"Sensor at x=%d, y=%d: closest beacon is at x=%d, y=%d", row
)
xSet.add(x) xSet.add(x)
xSet.add(bx) xSet.add(bx)
ySet.add(y) ySet.add(y)
@ -205,8 +210,10 @@ def part1(rows, sample=False):
mx[y][x] = "S" mx[y][x] = "S"
print(matrix.ppmx(mx, pad=False, space=True)) print(matrix.ppmx(mx, pad=False, space=True))
tuning = lambda y, x: y + (4000000 * x) tuning = lambda y, x: y + (4000000 * x)
def part2(rows, sample=False): def part2(rows, sample=False):
sensors = [] sensors = []
sensor_points = [] sensor_points = []
@ -215,7 +222,9 @@ def part2(rows, sample=False):
xSet = set() xSet = set()
ySet = set() ySet = set()
for row in rows: for row in rows:
x,y,bx,by = scanf("Sensor at x=%d, y=%d: closest beacon is at x=%d, y=%d", row) x, y, bx, by = scanf(
"Sensor at x=%d, y=%d: closest beacon is at x=%d, y=%d", row
)
xSet.add(x) xSet.add(x)
xSet.add(bx) xSet.add(bx)
ySet.add(y) ySet.add(y)
@ -281,11 +290,13 @@ def part2(rows, sample=False):
if y <= maxY and x <= maxX: if y <= maxY and x <= maxX:
mx[y][x] = "S" mx[y][x] = "S"
mx[TARGET[0]][TARGET[1]] = "!" mx[TARGET[0]][TARGET[1]] = "!"
matrix.highlight(mx, blink_green=[TARGET,]) matrix.highlight(
mx,
blink_green=[
TARGET,
],
)
def main(): def main():

View File

@ -3,9 +3,12 @@ import matrix
import shared import shared
import scanf import scanf
from dataclasses import dataclass from dataclasses import dataclass
from collections import defaultdict
from typing import List, Dict from typing import List, Dict
from pprint import pprint from pprint import pprint
import networkx as nx
from IPython.display import Image, display
@dataclass @dataclass
class Valve: class Valve:
@ -23,6 +26,7 @@ class Valve:
def highest_potential(self): def highest_potential(self):
return max(self.potential, key=self.potential.get) return max(self.potential, key=self.potential.get)
def parse(rows): def parse(rows):
valves = {} valves = {}
for row in rows: for row in rows:
@ -47,60 +51,91 @@ class Part1:
self.rows = rows self.rows = rows
self.sample = sample self.sample = sample
self.valves = parse(rows) self.valves = parse(rows)
self.nonzero = {v.label: v for _, v in self.valves.items() if v.rate > 0}
self.cur = self.valves["AA"] self.cur = self.valves["AA"]
self.tick = 1 self.tick = 1
self.minutes = minutes self.minutes = minutes
self.g = nx.DiGraph()
self.path_distances = defaultdict(dict)
self.set_up_graph()
def draw(self):
pdot = nx.drawing.nx_pydot.to_pydot(self.g)
pdot.write_png("15.png")
def set_up_graph(self):
for lbl, v in self.valves.items():
for t in v.tunnels:
# self.g.add_edge(lbl, t, {'weight':self.valves[t].rate})
self.g.add_edge(lbl, t, weight=self.valves[t].rate)
all_keys = self.valves.keys()
for lbl, _ in self.valves.items():
for other in all_keys:
if other == lbl:
continue
self.path_distances[lbl][other] = min([len(x) for x in nx.shortest_simple_paths(self.g, lbl, other)])
def do_tick(self, minute): def do_tick(self, minute):
pressure = 0 pressure = 0
opened = [] opened = []
for _, valve in self.valves.items(): for _, valve in self.valves.items():
if valve.opened_at > 0: if valve.opened_at > 0:
continue
pressure += valve.rate pressure += valve.rate
print(f"== Minute {minute} == [ {self.cur.label} ]") opened.append(valve.label)
print(f"\tValves {', '.join(opened)} are open, releasing {pressure} pressure") print(f"== Min {minute}:: Valves {', '.join(opened)} are open, releasing {pressure} pressure")
def open_or_move(self):
#print(self.cur)
hi = self.cur.highest_potential()
potential_elsewhere = self.cur.potential
pe = potential_elsewhere[hi]
if pe > self.cur.rate:
#print(f"should move to {hi}")
self.move(hi)
elif self.cur.rate > pe:
print(f"should open {self.cur.label}")
self.open()
def move(self, where):
self.tick += 1
self.cur = self.valves[where]
print("\tMove to valve", where)
def open(self):
if self.cur.opened_at >= 0 :
raise Exception("tried to open valve already opened")
self.tick += 1
self.valves[self.cur.label].opened_at = self.tick
self.cur = self.valves[self.cur.label]
print(self.cur)
def calculate_total_flow(self): def calculate_total_flow(self):
total = 0 total = 0
for label, valve in self.valves.items(): for label, valve in self.valves.items():
if valve.is_open: if valve.opened_at > 0:
total += valve.rate * (30 - valve.opened_at) total += valve.rate * (30 - valve.opened_at)
return total return total
def run(self): def run(self):
for minute in range(1, self.minutes+1): # Construct the graph with vertices & edges from the input
self.do_tick(minute) # Call a function to compute the distances between every pair of vertices
self.open_or_move() # Create a closed set containing all the valves with non-zero rates
# At each step, iterate over the remaining set of closed, non-zero valves
# - Subtract the distance from remaining minutes
# - Calculate the flow (rate * remaining minutes)
# - Remove the recently opened valve from the closed set (functionally), so the deeper levels won't consider it
print("total flow:") def priority(remaining):
print(self.calculate_total_flow()) _pris = []
for _,n in self.nonzero.items():
# (time_remaining - distance_to_valve - 1) * flow rate
pri = (remaining - self.path_distances[self.cur.label][n.label] - 1) * n.rate
_pris.append((n.label, pri))
_pris = list(sorted(_pris, key=lambda x: x[1], reverse=True))
return _pris
remaining = self.minutes
open_order = []
while len(self.nonzero):
if remaining <= 0:
print("ran out of time")
break
self.do_tick(31-remaining)
pris = priority(remaining)
print(pris)
_pri, _ = pris.pop(0)
n = self.nonzero[_pri]
del self.nonzero[_pri]
open_order.append(n.label)
print("\tMoving to", n.label)
print("\tOpening ", n.label)
distance = self.path_distances[self.cur.label][n.label]
self.cur = n
self.cur.opened_at = self.minutes - (remaining + 1)
remaining -= distance # Move
remaining -= 1 # open
print(remaining)
print(open_order)
print("sample: 1651")
print("total flow:", self.calculate_total_flow())

View File

@ -8,9 +8,11 @@ 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_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] split_line_to_int_list = lambda y: [int(w) for w in y.split(" ") if w]
def split_x_out(l): def split_x_out(l):
return [x for _, x in l] return [x for _, x in l]
def split_y_out(l): def split_y_out(l):
return [y for y, _ in l] return [y for y, _ in l]
@ -154,7 +156,9 @@ def get_neighbor_coords(matrix, c, r, diagonals=True):
return neighbors return neighbors
def line_of_sight_coords(matrix, row, col, distance=None) -> Dict[str, List[Tuple[int, int]]]: def line_of_sight_coords(
matrix, row, col, distance=None
) -> Dict[str, List[Tuple[int, int]]]:
""" """
Takes a matrix, a row, and a column Takes a matrix, a row, and a column
calculates the coordinates to the edge for all four cardinal directions calculates the coordinates to the edge for all four cardinal directions
@ -168,8 +172,14 @@ def line_of_sight_coords(matrix, row, col, distance=None) -> Dict[str, List[Tupl
row_ids = list(range(0, width)) row_ids = list(range(0, width))
if distance: if distance:
up_ids, down_ids = list(reversed(col_ids[:col])), col_ids[col + 1 :col+distance+1] up_ids, down_ids = (
left_ids, right_ids = list(reversed(row_ids[:row])), row_ids[row + 1 :row+distance+1] list(reversed(col_ids[:col])),
col_ids[col + 1 : col + distance + 1],
)
left_ids, right_ids = (
list(reversed(row_ids[:row])),
row_ids[row + 1 : row + distance + 1],
)
else: else:
up_ids, down_ids = list(reversed(col_ids[:col])), col_ids[col + 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_ids, right_ids = list(reversed(row_ids[:row])), row_ids[row + 1 :]
@ -226,8 +236,6 @@ def coords_between_points(point1, point2):
return coords return coords
def get_size(matrix): def get_size(matrix):
height = len(matrix) height = len(matrix)
width = len(matrix[0]) width = len(matrix[0])
@ -303,14 +311,13 @@ def ppmx(*matrices, pad=True, space=True, zero="."):
out.append("".join([f(x) for x in c])) out.append("".join([f(x) for x in c]))
return "\n".join(out) return "\n".join(out)
def view_matrix(matrix, y1, x1, y2, x2): def view_matrix(matrix, y1, x1, y2, x2):
lines = ppmx(matrix, pad=0, space=0).split("\n") lines = ppmx(matrix, pad=0, space=0).split("\n")
for line in lines[y1 : y2 + 1]: for line in lines[y1 : y2 + 1]:
print(line[x1:x2]) print(line[x1:x2])
def highlight(matrix, red=[], green=[], blue=[], blink_green=[]): def highlight(matrix, red=[], green=[], blue=[], blink_green=[]):
""" """
print a matrix of anything, Falsy values turns to `.` for clarity print a matrix of anything, Falsy values turns to `.` for clarity
@ -335,5 +342,3 @@ def highlight(matrix, red=[], green=[], blue=[], blink_green=[]):
new = f"{colors.BLINK}{colors.GREEN}{mx[y][x]}{colors.ENDC}" new = f"{colors.BLINK}{colors.GREEN}{mx[y][x]}{colors.ENDC}"
mx[y][x] = new mx[y][x] = new
print(ppmx(mx, pad=False, space=True, zero="0")) print(ppmx(mx, pad=False, space=True, zero="0"))

View File

@ -5,8 +5,8 @@ import cProfile
import functools import functools
import pstats import pstats
def profile(func):
def profile(func):
@functools.wraps(func) @functools.wraps(func)
def inner(*args, **kwargs): def inner(*args, **kwargs):
profiler = cProfile.Profile() profiler = cProfile.Profile()
@ -15,19 +15,21 @@ def profile(func):
retval = func(*args, **kwargs) retval = func(*args, **kwargs)
finally: finally:
profiler.disable() profiler.disable()
with open('profile.out', 'w') as profile_file: with open("profile.out", "w") as profile_file:
stats = pstats.Stats(profiler, stream=profile_file) stats = pstats.Stats(profiler, stream=profile_file)
stats.print_stats() stats.print_stats()
return retval return retval
return inner return inner
spl = lambda y: [int(w) for w in y] spl = lambda y: [int(w) for w in y]
def minmax(l): def minmax(l):
return min(l), max(l) return min(l), max(l)
def load_rows(day): def load_rows(day):
return [row for row in load(day)] return [row for row in load(day)]