day 18 part 1

main
magical 2024-12-18 05:19:25 +00:00
parent d1a2b1eeb1
commit 16637ab722
6 changed files with 3641 additions and 0 deletions

105
day18/astar.py 100644
View File

@ -0,0 +1,105 @@
from heapq import heappush, heappop
def search(start, is_goal, neighbors, heuristic=None, worst=None):
if heuristic == None:
def heuristic(x):
return 0
if not callable(is_goal):
goal = is_goal
def is_goal(this):
return this == goal
if not isinstance(start, list):
start = [start]
i = 0 # tiebreaker
q = []
for s in start:
i += 1
heappush(q, (heuristic(s), i, s, None))
done = {}
if worst:
best_worst = min(worst(s) for s in start)
while q:
z, _, this, prev = heappop(q)
if this in done:
continue
cost_so_far = z - heuristic(this)
#print(this,z, cost_so_far)
done[this] = (cost_so_far, prev)
if is_goal(this):
print("astar: visited", len(done), "nodes")
print("astar: pending", len(q), "nodes")
# reconsruct the path
n = this
path = []
while n is not None:
path.append(n)
_, n = done[n]
path.reverse()
return cost_so_far, this, path
for c, n in neighbors(this):
c = cost_so_far + c
if n not in done or c < done[n][0]:
h = heuristic(n)
if worst:
if c+h > best_worst:
# if the best possible cost for this node
# is worse than the lowest worst-case cost we've seen
# then don't even bother exploring it
continue
w = worst(n)
if c+w < best_worst:
best_worst = c+w
i += 1
heappush(q, (c+h, i, n, this))
return float('inf'), None, []
def test():
data = [
"aabqponm",
"abcryxxl",
"accszzxk",
"acctuvwj",
"abdefghi",
]
#data = [
# "aabbaaba",
# "abcbaaba",
# "accabcbb",
# "accbbbba",
# "abbbabab",
#]
start = (0,0)
goal = (5,2)
def get(x,y):
return ord(data[y][x])
def neighbors(src):
x,y = src
here = get(x,y)
n = []
def push(x,y):
if 0 <= y < len(data):
if 0 <= x < len(data[y]):
if get(x,y) <= here+1:
n.append((1, (x,y)))
push(x-1, y)
push(x+1,y)
push(x, y-1)
push(x, y+1)
return n
def heuristic(n):
return abs(goal[0] - n[0]) + abs(goal[1] - n[1])
c, _, path = search(start, goal, neighbors, heuristic)
print(*data, sep="\n")
print(*path, sep="\n")
assert c == 31, c
if __name__ == '__main__':
test()

3450
day18/input 100644

File diff suppressed because it is too large Load Diff

25
day18/sample1.in 100644
View File

@ -0,0 +1,25 @@
5,4
4,2
4,5
3,0
2,1
6,3
2,4
1,5
0,6
3,3
2,6
5,1
1,2
5,5
2,5
6,5
1,4
0,4
6,4
1,1
6,1
1,0
0,5
1,6
2,0

7
day18/sample2.in 100644
View File

@ -0,0 +1,7 @@
...#...
..#..#.
....#..
...#..#
..#..#.
.#..#..
#.#....

7
day18/sample3.in 100644
View File

@ -0,0 +1,7 @@
<em>O</em><em>O</em>.#<em>O</em><em>O</em><em>O</em>
.<em>O</em>#<em>O</em><em>O</em>#<em>O</em>
.<em>O</em><em>O</em><em>O</em>#<em>O</em><em>O</em>
...#<em>O</em><em>O</em>#
..#<em>O</em><em>O</em>#.
.#.<em>O</em>#..
#.#<em>O</em><em>O</em><em>O</em><em>O</em>

47
day18/sol.py 100644
View File

@ -0,0 +1,47 @@
from collections import defaultdict
import astar
def parse(file):
return [
tuple([int(x) for x in line.strip().split(',')])
for line in file
]
def solve(file, N=70, T=1024):
falling = parse(file)
start = (0,0,0) # x,y,t
map = [[0]*N for _ in range(N)]
inf = float('inf')
map = defaultdict(lambda: inf)
for t,p in enumerate(falling):
map[p] = t
def neighbors(n):
x,y,t = n
#if T < map[x,y]:
# can't move out of a blocked space
# return []
def push(x,y,t):
if 0 <= x < N and 0 <= y < N:
if T <= map[x,y]:
out.append((1,(x,y,t)))
out = []
push(x+1,y,t+1)
push(x,y+1,t+1)
push(x-1,y,t+1)
push(x,y-1,t+1)
return out
end = N-1, N-1
def isgoal(n):
x,y,_ = n
return (x,y) == end
sol = astar.search(start, isgoal, neighbors)
print(sol)
print(len(sol[-1])-1)
solve(open("sample1.in"), N=7, T=12)
solve(open("input"), N=71, T=1024)