This commit is contained in:
akp 2023-12-17 21:31:15 +00:00
parent fbac277c8a
commit 8efe5a209f
No known key found for this signature in database
GPG key ID: CF8D58F3DEB20755
5 changed files with 117 additions and 35 deletions

View file

@ -1,6 +1,9 @@
import sys
import gridutil.grid as gu
import gridutil.coord as cu
from collections.abc import Callable, Iterator
from collections import namedtuple
from queue import PriorityQueue
def parse(instr: str) -> gu.Grid:
@ -8,63 +11,116 @@ def parse(instr: str) -> gu.Grid:
return {k: int(g[k]) for k in g}
def get_shortest_path(grid: gu.Grid, start: cu.Coordinate, end: cu.Coordinate) -> list[cu.Coordinate]:
d = {start: (0, 0, cu.Direction.Right)}
State = namedtuple("State", ["pos", "steps_taken", "direction"])
def djikstra(
start: State,
end: Callable[[State], bool],
neighbours: Callable[[State], Iterator[State, None, None]],
cost: Callable[[State, State], int],
) -> list[State]:
dq = PriorityQueue()
dq.put((0, start))
d = {start: 0}
p = {}
f = {}
while end not in d:
w = min(filter(lambda x: x[0] not in f, d.items()), key=lambda x: x[1][0])[0]
f[w] = None
endState = None
while endState is None:
w = dq.get()[1]
dist_w, prev_steps, prev_direction = d[w]
for direction in cu.Direction:
if (direction == prev_direction and prev_steps == 3) or direction == prev_direction.opposite():
continue
u = cu.add(w, direction.delta())
if u not in grid:
continue
if u not in d or dist_w + grid[u] < d[u][0]:
d[u] = (dist_w + grid[u], prev_steps + 1 if direction == prev_direction else 1, direction)
for u in neighbours(w):
c = d[w] + cost(w, u)
if u not in d or c < d[u]:
d[u] = c
dq.put((c, u))
p[u] = w
if u == end:
if end(u):
endState = u
break
# raise SystemExit(14)
res = [end]
cursor = p[end]
while cursor != start:
res = [endState]
cursor = p[endState]
while cursor is not None:
res.append(cursor)
cursor = p[cursor]
cursor = p.get(cursor)
return list(reversed(res))
def one(instr: str):
grid = parse(instr)
end = (gu.get_max_x(grid), gu.get_max_y(grid))
path = get_shortest_path(grid, (0, 0), end)
gu.print_grid(grid, file=sys.stderr)
_debug()
def neighbours(node: State) -> Iterator[State, None, None]:
for direction in cu.Direction:
if (
direction == node.direction and node.steps_taken == 3
) or node.direction == direction.opposite():
continue
g = grid.copy()
for p in path:
g[p] = ""
nc = cu.add(node.pos, direction.delta())
if nc not in grid:
continue
gu.print_grid(g, file=sys.stderr)
yield State(
nc,
(node.steps_taken + 1) if direction == node.direction else 1,
direction,
)
path = djikstra(
State((0, 0), 0, cu.Direction.Right),
lambda x: x.pos == end,
neighbours,
lambda _, x: grid[x.pos],
)[1:]
acc = 0
for node in path:
acc += grid[node]
acc += grid[node.pos]
return acc
def two(instr: str):
return
grid = parse(instr)
end = (gu.get_max_x(grid), gu.get_max_y(grid))
for x in range(end[0] - 3, end[0]):
for y in range(end[1] - 3, end[1]):
del grid[(x, y)]
def neighbours(node: State) -> Iterator[State, None, None]:
for direction in cu.Direction:
if 0 < node.steps_taken < 4 and direction != node.direction:
continue
if (
direction == node.direction and node.steps_taken == 10
) or node.direction == direction.opposite():
continue
nc = cu.add(node.pos, direction.delta())
if nc not in grid:
continue
yield State(
nc,
(node.steps_taken + 1) if direction == node.direction else 1,
direction,
)
path = djikstra(
State((0, 0), 0, cu.Direction.Right),
lambda x: x.pos == end,
neighbours,
lambda _, x: grid[x.pos],
)[1:]
acc = 0
for node in path:
acc += grid[node.pos]
return acc
def _debug(*args, **kwargs):
@ -80,4 +136,4 @@ if __name__ == "__main__":
if sys.argv[1] == "1":
print(one(inp))
else:
print(two(inp))
print(two(inp))

View file

@ -4,5 +4,15 @@
"is": "102",
"input": "2413432311323\n3215453535623\n3255245654254\n3446585845452\n4546657867536\n1438598798454\n4457876987766\n3637877979653\n4654967986887\n4564679986453\n1224686865563\n2546548887735\n4322674655533\n\n"
}
],
"2": [
{
"is": "94",
"input": "2413432311323\n3215453535623\n3255245654254\n3446585845452\n4546657867536\n1438598798454\n4457876987766\n3637877979653\n4654967986887\n4564679986453\n1224686865563\n2546548887735\n4322674655533\n\n"
},
{
"is": "71",
"input": "111111111111\n999999999991\n999999999991\n999999999991\n999999999991\n\n"
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Before After
Before After

View file

@ -29,3 +29,5 @@
{"day": 15, "part": 2, "runner": "go", "min": 0.0012998580932617188, "max": 0.0028612613677978516, "avg": 0.0014359498023986817, "n": 100}
{"day": 16, "part": 1, "runner": "py", "min": 0.030805587768554688, "max": 0.03831839561462402, "avg": 0.03250444730122884, "n": 15}
{"day": 16, "part": 2, "runner": "py", "min": 2.7863943576812744, "max": 4.14529013633728, "avg": 3.1346225261688234, "n": 15}
{"day": 17, "part": 1, "runner": "py", "min": 5.36311674118042, "max": 5.36311674118042, "avg": 5.36311674118042, "n": 1}
{"day": 17, "part": 2, "runner": "py", "min": 26.201914072036743, "max": 26.201914072036743, "avg": 26.201914072036743, "n": 1}

View file

@ -16,6 +16,11 @@ def sub(a: Coordinate, b: Coordinate) -> Coordinate:
return xa - xb, ya - yb
def manhattan_dist(a: Coordinate, b: Coordinate) -> Coordinate:
x, y = sub(b, a)
return abs(x) + abs(y)
class Direction(Enum):
Up = auto()
Down = auto()
@ -42,4 +47,13 @@ class Direction(Enum):
case Direction.Left:
return Direction.Right
case Direction.Right:
return Direction.Left
return Direction.Left
def __lt__(self, x):
return False
def __eq__(self, x):
return self.value == x.value
def __hash__(self):
return hash(self.value)