day16 still working:
This commit is contained in:
parent
139b6d7e12
commit
d7a1aac178
@ -3,16 +3,16 @@ import shared
|
||||
import itertools
|
||||
import functools
|
||||
|
||||
#@shared.profile
|
||||
# @shared.profile
|
||||
def part1(rows):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
#@shared.profile
|
||||
# @shared.profile
|
||||
def part2(rows):
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
rows = [row for row in shared.load_rows(15)]
|
||||
with shared.elapsed_timer() as elapsed:
|
||||
|
@ -14,7 +14,7 @@ def part1(rows):
|
||||
# print(f"== Pair {idx+1} ==")
|
||||
passes = check_group(left, right)
|
||||
if passes:
|
||||
indexes.append(idx+1)
|
||||
indexes.append(idx + 1)
|
||||
# if passes is True:
|
||||
# print(f"{idx+1} {matrix.colors.GREEN}{passes}{matrix.colors.ENDC}")
|
||||
# elif passes is False:
|
||||
@ -26,7 +26,7 @@ def part1(rows):
|
||||
|
||||
def check_group(left, right):
|
||||
if isinstance(left, list) and isinstance(right, list):
|
||||
# if both items are lists, loop over both
|
||||
# if both items are lists, loop over both
|
||||
for idx in range(min(len(left), len(right))):
|
||||
check = check_group(left[idx], right[idx])
|
||||
# bubble up the int comparisons, or list length comparisons
|
||||
@ -62,42 +62,43 @@ def check_group(left, right):
|
||||
# If the left integer is higher than the right integer, the inputs are not in the right order.
|
||||
elif left > right:
|
||||
return False
|
||||
#Otherwise, the inputs are the same integer; continue checking the next part of the input.
|
||||
# Otherwise, the inputs are the same integer; continue checking the next part of the input.
|
||||
elif left == right:
|
||||
return None
|
||||
|
||||
|
||||
def part2(rows):
|
||||
two, six = 0, 0
|
||||
rows = [eval(r) for r in rows if r]
|
||||
rows.append([[2]])
|
||||
rows.append([[6]])
|
||||
|
||||
|
||||
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):
|
||||
idx += 1
|
||||
if r == [[2]]:
|
||||
two = idx
|
||||
if r == [[6]]:
|
||||
six = idx
|
||||
break # will come 2nd logically, so we can break here to save time
|
||||
break # will come 2nd logically, so we can break here to save time
|
||||
|
||||
print(two * six)
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
rows = shared.load_rows(13)
|
||||
with shared.elapsed_timer() as elapsed:
|
||||
part1(rows)
|
||||
print("🕒", elapsed())
|
||||
with shared.elapsed_timer() as elapsed:
|
||||
part2(rows)
|
||||
print("🕒", elapsed())
|
||||
part2(rows)
|
||||
print("🕒", elapsed())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -4,7 +4,7 @@ import itertools
|
||||
import functools
|
||||
|
||||
|
||||
SAND_START = [0,500]
|
||||
SAND_START = [0, 500]
|
||||
|
||||
|
||||
def find_bounds(lines):
|
||||
@ -13,97 +13,110 @@ def find_bounds(lines):
|
||||
for line in lines:
|
||||
x.extend(matrix.split_x_out(line))
|
||||
y.extend(matrix.split_y_out(line))
|
||||
return shared.minmax(y),shared.minmax(x)
|
||||
return shared.minmax(y), shared.minmax(x)
|
||||
|
||||
|
||||
|
||||
def part1(rows):
|
||||
def get_next(sand):
|
||||
next_y = sand[0]+1
|
||||
dl = sand[1]-1
|
||||
next_y = sand[0] + 1
|
||||
dl = sand[1] - 1
|
||||
d = sand[1]
|
||||
dr = sand[1]+1
|
||||
return next_y, (dl,d,dr)
|
||||
lines = [[list(reversed(list(map(int, point.split(","))))) for point in row.split(" -> ")] for row in rows]
|
||||
(minY,maxY), (minX,maxX) = find_bounds(lines)
|
||||
mx = matrix.matrix_of_size(maxX+2,maxY+2)
|
||||
dr = sand[1] + 1
|
||||
return next_y, (dl, d, dr)
|
||||
|
||||
lines = [
|
||||
[
|
||||
list(reversed(list(map(int, point.split(",")))))
|
||||
for point in row.split(" -> ")
|
||||
]
|
||||
for row in rows
|
||||
]
|
||||
(minY, maxY), (minX, maxX) = find_bounds(lines)
|
||||
mx = matrix.matrix_of_size(maxX + 2, maxY + 2)
|
||||
walls = []
|
||||
for line in lines:
|
||||
for x in range(len(line)-1):
|
||||
coords = matrix.coords_between_points(line[x], line[x+1])
|
||||
for x in range(len(line) - 1):
|
||||
coords = matrix.coords_between_points(line[x], line[x + 1])
|
||||
walls.extend(coords)
|
||||
for y,x in walls:
|
||||
for y, x in walls:
|
||||
mx[y][x] = "#"
|
||||
mx[SAND_START[0]][SAND_START[1]] = "+"
|
||||
|
||||
sands = []
|
||||
|
||||
try:
|
||||
for sand_grains in range(900): # produce Sand one unit at a time
|
||||
for sand_grains in range(900): # produce Sand one unit at a time
|
||||
last_grain = []
|
||||
sand = SAND_START[:]
|
||||
#print("starting sand", sand_grains+1, sand)
|
||||
# print("starting sand", sand_grains+1, sand)
|
||||
while True:
|
||||
last_grain.append(sand[:])
|
||||
next_y, (dl, d, dr) = get_next(sand)
|
||||
if mx[next_y][d] == 0:
|
||||
#print(mx[next_y][d],"moving_down", end=' ')
|
||||
#print("moving down")
|
||||
# print(mx[next_y][d],"moving_down", end=' ')
|
||||
# print("moving down")
|
||||
sand[0] = next_y
|
||||
elif mx[next_y][dl] == 0:
|
||||
#print(mx[next_y][dl], "moving left", end=' ')
|
||||
#print("moving left")
|
||||
# print(mx[next_y][dl], "moving left", end=' ')
|
||||
# print("moving left")
|
||||
sand[0] = next_y
|
||||
sand[1] = dl
|
||||
elif mx[next_y][dr] == 0:
|
||||
#print(mx[next_y][dr], "moving right", end=' ')
|
||||
#print("moving right")
|
||||
# print(mx[next_y][dr], "moving right", end=' ')
|
||||
# print("moving right")
|
||||
sand[0] = next_y
|
||||
sand[1] = dr
|
||||
else:
|
||||
mx[sand[0]][sand[1]] = "o"
|
||||
#print(mx[next_y][dl], mx[next_y][d], mx[next_y][dr])
|
||||
#print("STOPPING", end=' ')
|
||||
# print(mx[next_y][dl], mx[next_y][d], mx[next_y][dr])
|
||||
# print("STOPPING", end=' ')
|
||||
break
|
||||
#matrix.view_matrix(mx, 0,minX-1, maxY+1,maxX+2)
|
||||
# matrix.view_matrix(mx, 0,minX-1, maxY+1,maxX+2)
|
||||
except IndexError:
|
||||
sands.append(last_grain)
|
||||
for y,x in sands[-1]:
|
||||
for y, x in sands[-1]:
|
||||
mx[y][x] = "~"
|
||||
#matrix.view_matrix(mx, 0,minX-1, maxY+1,maxX+2)
|
||||
# matrix.view_matrix(mx, 0,minX-1, maxY+1,maxX+2)
|
||||
print("done", sand_grains)
|
||||
|
||||
|
||||
@shared.profile
|
||||
def part2(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)
|
||||
lines = [
|
||||
[
|
||||
list(reversed(list(map(int, point.split(",")))))
|
||||
for point in row.split(" -> ")
|
||||
]
|
||||
for row in rows
|
||||
]
|
||||
(minY, maxY), (minX, maxX) = find_bounds(lines)
|
||||
walls = {}
|
||||
_between_points = matrix.coords_between_points
|
||||
for line in lines:
|
||||
for x in range(len(line)-1):
|
||||
coords = matrix.coords_between_points(line[x], line[x+1])
|
||||
for x in range(len(line) - 1):
|
||||
coords = matrix.coords_between_points(line[x], line[x + 1])
|
||||
for c in coords:
|
||||
walls[c] = "#"
|
||||
for y,x in walls:
|
||||
walls[(y,x)] = "#"# .add((y,x))
|
||||
for y, x in walls:
|
||||
walls[(y, x)] = "#" # .add((y,x))
|
||||
floor = maxY + 2
|
||||
|
||||
sand_grains = -1
|
||||
while True:
|
||||
sand_grains += 1
|
||||
sand = [0,500]
|
||||
sand = [0, 500]
|
||||
|
||||
if tuple(sand) in walls:
|
||||
print("done", sand_grains)
|
||||
break
|
||||
|
||||
while True:
|
||||
next_y = sand[0]+1
|
||||
dl = sand[1]-1
|
||||
dr = sand[1]+1
|
||||
next_y = sand[0] + 1
|
||||
dl = sand[1] - 1
|
||||
dr = sand[1] + 1
|
||||
if next_y == floor:
|
||||
# hit the floor
|
||||
walls[tuple(sand)] = "#" # Draw stop
|
||||
walls[tuple(sand)] = "#" # Draw stop
|
||||
break
|
||||
|
||||
elif (next_y, sand[1]) not in walls:
|
||||
@ -120,6 +133,7 @@ def part2(rows):
|
||||
walls[tuple(sand)] = "#"
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
rows = [row for row in shared.load_rows(14)]
|
||||
with shared.elapsed_timer() as elapsed:
|
||||
|
@ -8,8 +8,10 @@ from typing import Optional, List, Tuple
|
||||
from dataclasses import dataclass
|
||||
from collections import defaultdict
|
||||
|
||||
def cityblock(y1,x1, y2,x2):
|
||||
return abs(y2-y1) + abs(x2-x1)
|
||||
|
||||
def cityblock(y1, x1, y2, x2):
|
||||
return abs(y2 - y1) + abs(x2 - x1)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Sensor:
|
||||
@ -17,14 +19,16 @@ class Sensor:
|
||||
sY: int
|
||||
bX: int
|
||||
bY: int
|
||||
limit: Tuple[int,int] = (0,0)
|
||||
limit: Tuple[int, int] = (0, 0)
|
||||
_d: int = None
|
||||
_edges: List[Tuple[int,int]]= None
|
||||
_border: List[Tuple[int,int]]= None
|
||||
_edges: List[Tuple[int, int]] = None
|
||||
_border: List[Tuple[int, int]] = None
|
||||
|
||||
def __str__(self):
|
||||
return (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)})")
|
||||
return (
|
||||
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
|
||||
def s(self):
|
||||
@ -37,15 +41,15 @@ class Sensor:
|
||||
@property
|
||||
def distance(self):
|
||||
if self._d is None:
|
||||
self._d = cityblock(self.sY,self.sX, self.bY, self.bX)
|
||||
self._d = cityblock(self.sY, self.sX, self.bY, self.bX)
|
||||
return self._d
|
||||
|
||||
def distance_to(self, bY, bX):
|
||||
return cityblock(self.sY,self.sX, bY, bX)
|
||||
return cityblock(self.sY, self.sX, bY, bX)
|
||||
|
||||
def on_line(self, y):
|
||||
midpoint = (y,self.s[1])
|
||||
d = self.distance_to(*midpoint)
|
||||
midpoint = (y, self.s[1])
|
||||
d = self.distance_to(*midpoint)
|
||||
if d > self.distance:
|
||||
return []
|
||||
|
||||
@ -54,17 +58,16 @@ class Sensor:
|
||||
start = (y, midpoint[1] - need)
|
||||
end = (y, midpoint[1] + need)
|
||||
|
||||
return list(range(start[1],end[1]+1))
|
||||
return list(range(start[1], end[1] + 1))
|
||||
|
||||
def in_range(self, bY,bX):
|
||||
d = cityblock(self.sY,self.sX, bY, bX)
|
||||
def in_range(self, bY, bX):
|
||||
d = cityblock(self.sY, self.sX, bY, bX)
|
||||
if self.d < d:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def in_diamond(self):
|
||||
sX,sY = self.sX, self.sY
|
||||
sX, sY = self.sX, self.sY
|
||||
up_lim = sY - self.distance
|
||||
dn_lim = sY + self.distance
|
||||
|
||||
@ -78,17 +81,17 @@ class Sensor:
|
||||
|
||||
infliction = 1
|
||||
height = -1
|
||||
for idx, x in enumerate(range(l[1],r[1]+1)):
|
||||
for idx, x in enumerate(range(l[1], r[1] + 1)):
|
||||
height += infliction
|
||||
if (sY, x) == self.s:
|
||||
infliction = -1
|
||||
for y in range(sY-height, sY+height+1):
|
||||
yield (y,x)
|
||||
for y in range(sY - height, sY + height + 1):
|
||||
yield (y, x)
|
||||
|
||||
def edges(self):
|
||||
if self._edges:
|
||||
return self._edges
|
||||
sX,sY = self.sX, self.sY
|
||||
sX, sY = self.sX, self.sY
|
||||
up_lim = sY - self.distance
|
||||
dn_lim = sY + self.distance
|
||||
|
||||
@ -104,19 +107,19 @@ class Sensor:
|
||||
height = -1
|
||||
edges = set()
|
||||
# to left -1 and right + 1
|
||||
for idx, x in enumerate(range(l[1],r[1]+1)):
|
||||
for idx, x in enumerate(range(l[1], r[1] + 1)):
|
||||
height += infliction
|
||||
if (sY, x) == self.s:
|
||||
infliction = -1
|
||||
edges.add((sY-height,x))
|
||||
edges.add((sY+height,x))
|
||||
edges.add((sY - height, x))
|
||||
edges.add((sY + height, x))
|
||||
self._edges = edges
|
||||
return self._edges
|
||||
|
||||
def border(self):
|
||||
if self._border:
|
||||
return self._border
|
||||
sX,sY = self.sX, self.sY
|
||||
sX, sY = self.sX, self.sY
|
||||
up_lim = sY - self.distance
|
||||
dn_lim = sY + self.distance
|
||||
|
||||
@ -132,12 +135,12 @@ class Sensor:
|
||||
height = -1
|
||||
border = set()
|
||||
# to left -1 and right + 1
|
||||
for idx, x in enumerate(range(l[1]-1,r[1]+2)):
|
||||
for idx, x in enumerate(range(l[1] - 1, r[1] + 2)):
|
||||
height += infliction
|
||||
if (sY, x) == self.s:
|
||||
infliction = -1
|
||||
border.add((sY-height,x))
|
||||
border.add((sY+height,x))
|
||||
border.add((sY - height, x))
|
||||
border.add((sY + height, x))
|
||||
self._border = border
|
||||
return self._border
|
||||
|
||||
@ -150,19 +153,21 @@ def part1(rows, sample=False):
|
||||
xSet = set()
|
||||
ySet = set()
|
||||
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(bx)
|
||||
ySet.add(y)
|
||||
ySet.add(by)
|
||||
sensors.append(Sensor(sX=x,sY=y,bX=bx,bY=by,limit=(0,0)))
|
||||
minX, maxX = min(xSet),max(xSet)
|
||||
minY, maxY = min(ySet),max(ySet)
|
||||
limLo = min(minX,minY)
|
||||
limHi = max(maxX,maxY)
|
||||
sensors.append(Sensor(sX=x, sY=y, bX=bx, bY=by, limit=(0, 0)))
|
||||
minX, maxX = min(xSet), max(xSet)
|
||||
minY, maxY = min(ySet), max(ySet)
|
||||
limLo = min(minX, minY)
|
||||
limHi = max(maxX, maxY)
|
||||
|
||||
for sensor in sensors:
|
||||
sensor.limit = (limLo,limHi)
|
||||
sensor.limit = (limLo, limHi)
|
||||
sensor_points.append(sensor.s)
|
||||
beacon_points.append(sensor.b)
|
||||
if sample:
|
||||
@ -177,7 +182,7 @@ def part1(rows, sample=False):
|
||||
for s in sensors:
|
||||
coll = s.on_line(CHECK_ROW)
|
||||
ineligible.update(coll)
|
||||
|
||||
|
||||
count_ignoring_current_beacons = 0
|
||||
for i in ineligible:
|
||||
if (CHECK_ROW, i) not in beacon_points:
|
||||
@ -187,25 +192,27 @@ def part1(rows, sample=False):
|
||||
if not sample:
|
||||
return
|
||||
|
||||
mx = matrix.matrix_of_size(maxX+1, maxY+1)
|
||||
mx = matrix.matrix_of_size(maxX + 1, maxY + 1)
|
||||
for yx in ineligible_points:
|
||||
y,x = yx
|
||||
y, x = yx
|
||||
if y >= 0 and x >= 0:
|
||||
if y <= maxY and x <= maxX:
|
||||
mx[y][x] = "#"
|
||||
for yx in beacon_points:
|
||||
y,x = yx
|
||||
y, x = yx
|
||||
if y >= 0 and x >= 0:
|
||||
if y <= maxY and x <= maxX:
|
||||
mx[y][x] = "B"
|
||||
for yx in sensor_points:
|
||||
y,x = yx
|
||||
y, x = yx
|
||||
if y >= 0 and x >= 0:
|
||||
if y <= maxY and x <= maxX:
|
||||
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):
|
||||
sensors = []
|
||||
@ -215,14 +222,16 @@ def part2(rows, sample=False):
|
||||
xSet = set()
|
||||
ySet = set()
|
||||
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(bx)
|
||||
ySet.add(y)
|
||||
ySet.add(by)
|
||||
sensors.append(Sensor(sX=x,sY=y,bX=bx,bY=by))
|
||||
minX, maxX = min(xSet),max(xSet)
|
||||
minY, maxY = min(ySet),max(ySet)
|
||||
sensors.append(Sensor(sX=x, sY=y, bX=bx, bY=by))
|
||||
minX, maxX = min(xSet), max(xSet)
|
||||
minY, maxY = min(ySet), max(ySet)
|
||||
|
||||
for sensor in sensors:
|
||||
_ = sensor.edges()
|
||||
@ -240,21 +249,21 @@ def part2(rows, sample=False):
|
||||
borders = defaultdict(int)
|
||||
for s in sensors:
|
||||
for yx in s.border():
|
||||
y,x = yx
|
||||
y, x = yx
|
||||
if y > 0 and y <= L and x > 0 and x <= L:
|
||||
borders[yx] += 1
|
||||
|
||||
TARGET = None
|
||||
for (eY,eX) in borders.keys():
|
||||
#print("checking:",(eY,ex))
|
||||
for (eY, eX) in borders.keys():
|
||||
# print("checking:",(eY,ex))
|
||||
away_from = []
|
||||
for idx, s in enumerate(sensors):
|
||||
d = s.distance_to(eY,eX)
|
||||
d = s.distance_to(eY, eX)
|
||||
if d > s.distance:
|
||||
away_from.append(s.s)
|
||||
if len(away_from) == len(sensors):
|
||||
TARGET = (eY,eX)
|
||||
print(TARGET, tuning(eY,eX))
|
||||
TARGET = (eY, eX)
|
||||
print(TARGET, tuning(eY, eX))
|
||||
break
|
||||
|
||||
if not sample:
|
||||
@ -262,30 +271,32 @@ def part2(rows, sample=False):
|
||||
|
||||
# """ PRINT OUTPUT """
|
||||
|
||||
mx = matrix.matrix_of_size(maxX+1, maxY+1)
|
||||
mx = matrix.matrix_of_size(maxX + 1, maxY + 1)
|
||||
for yx in ineligible_points:
|
||||
y,x = yx
|
||||
y, x = yx
|
||||
if y >= 0 and x >= 0:
|
||||
if y <= maxY and x <= maxX:
|
||||
mx[y][x] = "#"
|
||||
|
||||
for yx in beacon_points:
|
||||
y,x = yx
|
||||
y, x = yx
|
||||
if y >= 0 and x >= 0:
|
||||
if y <= maxY and x <= maxX:
|
||||
mx[y][x] = "B"
|
||||
|
||||
for yx in sensor_points:
|
||||
y,x = yx
|
||||
y, x = yx
|
||||
if y >= 0 and x >= 0:
|
||||
if y <= maxY and x <= maxX:
|
||||
mx[y][x] = "S"
|
||||
|
||||
|
||||
mx[TARGET[0]][TARGET[1]] = "!"
|
||||
matrix.highlight(mx, blink_green=[TARGET,])
|
||||
|
||||
|
||||
matrix.highlight(
|
||||
mx,
|
||||
blink_green=[
|
||||
TARGET,
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -3,9 +3,12 @@ import matrix
|
||||
import shared
|
||||
import scanf
|
||||
from dataclasses import dataclass
|
||||
from collections import defaultdict
|
||||
from typing import List, Dict
|
||||
from pprint import pprint
|
||||
import networkx as nx
|
||||
|
||||
from IPython.display import Image, display
|
||||
|
||||
@dataclass
|
||||
class Valve:
|
||||
@ -13,7 +16,7 @@ class Valve:
|
||||
rate: int
|
||||
tunnels: List[str]
|
||||
opened_at: int = -1
|
||||
potential: Dict[str,int] = None
|
||||
potential: Dict[str, int] = None
|
||||
|
||||
def set_potential(self, valves):
|
||||
self.potential = {}
|
||||
@ -23,6 +26,7 @@ class Valve:
|
||||
def highest_potential(self):
|
||||
return max(self.potential, key=self.potential.get)
|
||||
|
||||
|
||||
def parse(rows):
|
||||
valves = {}
|
||||
for row in rows:
|
||||
@ -30,15 +34,15 @@ def parse(rows):
|
||||
right = right.replace("s ", "").lstrip()
|
||||
valve, rate = scanf.scanf("Valve %s has flow rate=%d; %*s %*s to", left)
|
||||
tunnels = right.split(", ")
|
||||
valves[valve] = Valve(label=valve,rate=rate,tunnels=tunnels)
|
||||
valves[valve] = Valve(label=valve, rate=rate, tunnels=tunnels)
|
||||
|
||||
for _,v in valves.items():
|
||||
for _, v in valves.items():
|
||||
v.set_potential(valves)
|
||||
return valves
|
||||
|
||||
|
||||
def part1(rows, sample=False):
|
||||
p1 = Part1(rows,sample, 30)
|
||||
p1 = Part1(rows, sample, 30)
|
||||
p1.run()
|
||||
|
||||
|
||||
@ -47,62 +51,93 @@ class Part1:
|
||||
self.rows = rows
|
||||
self.sample = sample
|
||||
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.tick = 1
|
||||
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):
|
||||
pressure = 0
|
||||
pressure = 0
|
||||
opened = []
|
||||
for _, valve in self.valves.items():
|
||||
if valve.opened_at > 0:
|
||||
continue
|
||||
pressure += valve.rate
|
||||
print(f"== Minute {minute} == [ {self.cur.label} ]")
|
||||
print(f"\tValves {', '.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)
|
||||
pressure += valve.rate
|
||||
opened.append(valve.label)
|
||||
print(f"== Min {minute}:: Valves {', '.join(opened)} are open, releasing {pressure} pressure")
|
||||
|
||||
def calculate_total_flow(self):
|
||||
total = 0
|
||||
for label, valve in self.valves.items():
|
||||
if valve.is_open:
|
||||
if valve.opened_at > 0:
|
||||
total += valve.rate * (30 - valve.opened_at)
|
||||
return total
|
||||
|
||||
def run(self):
|
||||
for minute in range(1, self.minutes+1):
|
||||
self.do_tick(minute)
|
||||
self.open_or_move()
|
||||
# Construct the graph with vertices & edges from the input
|
||||
# Call a function to compute the distances between every pair of vertices
|
||||
# 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:")
|
||||
print(self.calculate_total_flow())
|
||||
def priority(remaining):
|
||||
_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())
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
sample = False
|
||||
@ -114,7 +149,7 @@ def main():
|
||||
part1(rows, sample)
|
||||
print("🕒", elapsed())
|
||||
|
||||
#with shared.elapsed_timer() as elapsed:
|
||||
# with shared.elapsed_timer() as elapsed:
|
||||
# part2(rows, sample)
|
||||
# print("🕒", elapsed())
|
||||
|
||||
|
@ -8,11 +8,13 @@ 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]
|
||||
|
||||
|
||||
def split_x_out(l):
|
||||
return [x for _,x in l]
|
||||
return [x for _, x in l]
|
||||
|
||||
|
||||
def split_y_out(l):
|
||||
return [y for y,_ in l]
|
||||
return [y for y, _ in l]
|
||||
|
||||
|
||||
class colors:
|
||||
@ -154,7 +156,9 @@ def get_neighbor_coords(matrix, c, r, diagonals=True):
|
||||
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
|
||||
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))
|
||||
|
||||
if distance:
|
||||
up_ids, down_ids = 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]
|
||||
up_ids, down_ids = (
|
||||
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:
|
||||
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 :]
|
||||
@ -200,8 +210,8 @@ def line_of_sight(mx, row, col, distance=None):
|
||||
|
||||
|
||||
def coords_between_points(point1, point2):
|
||||
y1,x1 = point1
|
||||
y2,x2 = point2
|
||||
y1, x1 = point1
|
||||
y2, x2 = point2
|
||||
|
||||
coords = []
|
||||
x = 0
|
||||
@ -209,23 +219,21 @@ def coords_between_points(point1, point2):
|
||||
|
||||
if x2 < x1:
|
||||
y = point1[0]
|
||||
for _x in range(x2, x1+1):
|
||||
coords.append((y,_x))
|
||||
for _x in range(x2, x1 + 1):
|
||||
coords.append((y, _x))
|
||||
elif x1 < x2:
|
||||
y = point1[0]
|
||||
for _x in range(x1, x2+1):
|
||||
coords.append((y,_x))
|
||||
for _x in range(x1, x2 + 1):
|
||||
coords.append((y, _x))
|
||||
elif y2 < y1:
|
||||
x = point1[1]
|
||||
for _y in range(y2, y1+1):
|
||||
coords.append((_y,x))
|
||||
for _y in range(y2, y1 + 1):
|
||||
coords.append((_y, x))
|
||||
elif y1 < y2:
|
||||
x = point1[1]
|
||||
for _y in range(y1, y2+1):
|
||||
coords.append((_y,x))
|
||||
for _y in range(y1, y2 + 1):
|
||||
coords.append((_y, x))
|
||||
return coords
|
||||
|
||||
|
||||
|
||||
|
||||
def get_size(matrix):
|
||||
@ -303,14 +311,13 @@ def ppmx(*matrices, pad=True, space=True, zero="."):
|
||||
out.append("".join([f(x) for x in c]))
|
||||
return "\n".join(out)
|
||||
|
||||
def view_matrix(matrix, y1,x1, y2,x2):
|
||||
lines = ppmx(matrix, pad=0,space=0).split("\n")
|
||||
for line in lines[y1:y2+1]:
|
||||
|
||||
def view_matrix(matrix, y1, x1, y2, x2):
|
||||
lines = ppmx(matrix, pad=0, space=0).split("\n")
|
||||
for line in lines[y1 : y2 + 1]:
|
||||
print(line[x1:x2])
|
||||
|
||||
|
||||
|
||||
|
||||
def highlight(matrix, red=[], green=[], blue=[], blink_green=[]):
|
||||
"""
|
||||
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}"
|
||||
mx[y][x] = new
|
||||
print(ppmx(mx, pad=False, space=True, zero="0"))
|
||||
|
||||
|
||||
|
@ -5,8 +5,8 @@ import cProfile
|
||||
import functools
|
||||
import pstats
|
||||
|
||||
def profile(func):
|
||||
|
||||
def profile(func):
|
||||
@functools.wraps(func)
|
||||
def inner(*args, **kwargs):
|
||||
profiler = cProfile.Profile()
|
||||
@ -15,18 +15,20 @@ def profile(func):
|
||||
retval = func(*args, **kwargs)
|
||||
finally:
|
||||
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.print_stats()
|
||||
return retval
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
spl = lambda y: [int(w) for w in y]
|
||||
|
||||
|
||||
def minmax(l):
|
||||
return min(l),max(l)
|
||||
return min(l), max(l)
|
||||
|
||||
|
||||
def load_rows(day):
|
||||
return [row for row in load(day)]
|
||||
|
Loading…
Reference in New Issue
Block a user