my advent of code 2021 solutions
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

138 lines
5.2 KiB

from queue import PriorityQueue
import sys
import copy
class grid:
def __init__(self):
self.y = []
self.attempts = 0
self.solutions = 0
self.current_smallest_solution_level = None
self.point_costs = {(0, 0): 0}
self.next_point_queue = PriorityQueue()
self.next_point_queue.put((0, (0, 0)))
self.visited = {}
def addline(self, line):
self.y.append([int(i) for i in line])
self.maxy = len(self.y) - 1
self.maxx = len(self.y[0]) - 1
# print(self.maxx)
# print(self.maxy)
def largegrid(self):
for f in self.y:
temp = copy.copy(f)
for i in range(1, 5):
for t in range(len(temp)):
temp[t] += 1
if temp[t] > 9:
temp[t] -= 9
f.extend(temp)
temp2 = copy.copy(self.y)
for i in range(1, 5):
temp3 = copy.copy(temp2)
for f in temp3:
self.y.append([(g+i-1) % 9 + 1 for g in f])
self.maxy = len(self.y) - 1
self.maxx = len(self.y[0]) - 1
# def initialpath(self):
def findpath(self):
while not self.next_point_queue.empty():
level, poi = self.next_point_queue.get()
if self.visited.get(poi, False):
continue
self.visited[poi] = True
self.attempts += 1
if self.attempts % 100000 == 0:
print('Attempt %d, solutions: %d, cost: %d, visited: %d, queued: %d' % (
self.attempts, self.solutions, self.current_smallest_solution_level or -1,
len(self.visited), self.next_point_queue.qsize()))
neighbors = self.get_neighbors(poi, level)
for i in neighbors:
next_level = level + int(self.y[i[0]][i[1]])
if (self.current_smallest_solution_level is not None and
next_level > self.current_smallest_solution_level):
# We already found a solution that was overall less risky
# than the current path, so who cares.
continue
if i == (self.maxy, self.maxx):
self.solutions += 1
if (self.current_smallest_solution_level is None or
next_level < self.current_smallest_solution_level):
self.current_smallest_solution_level = next_level
# print('Found solution: ', i, ', orig level ', level)
continue
elif self.visited.get(i, False) and (i in self.point_costs and self.point_costs[i] <= next_level):
# We already visited the point on a cheaper path.
continue
else:
# print('Path: %s, next: %s' % (repr(path), i))
self.point_costs[i] = next_level
self.next_point_queue.put((next_level, i))
# Try to find a path from the current point to the end by going down and right
# alternatingly, so we can restrict the search to paths less costly than the
# current one.
next_level = level
while poi != (self.maxx, self.maxy):
if poi[0] < self.maxx:
poi = (poi[0] + 1, poi[1])
next_level += int(self.y[poi[0]][poi[1]])
if poi in self.point_costs and next_level > self.point_costs[poi]:
break
self.point_costs[poi] = next_level
if poi[1] < self.maxy:
poi = (poi[0], poi[1] + 1)
next_level += int(self.y[poi[0]][poi[1]])
if poi in self.point_costs and next_level > self.point_costs[poi]:
break
self.point_costs[poi] = next_level
if poi == (self.maxx, self.maxy):
self.solutions += 1
if (self.current_smallest_solution_level is None or
next_level < self.current_smallest_solution_level):
self.current_smallest_solution_level = next_level
def get_neighbors(self, poi, level):
nex = {}
rv = []
if poi[0] != self.maxy:
nex.setdefault(self.y[poi[0] + 1][poi[1]], list()).append((poi[0] + 1, poi[1]))
if poi[1] != self.maxx:
nex.setdefault(self.y[poi[0]][poi[1] + 1], list()).append((poi[0], poi[1] + 1))
if poi[0] != 0:
nex.setdefault(self.y[poi[0] - 1][poi[1]], list()).append((poi[0] - 1, poi[1]))
if poi[1] != 0:
nex.setdefault(self.y[poi[0]][poi[1] - 1], list()).append((poi[0], poi[1] - 1))
order = sorted(nex.keys())
for j in order:
for p in nex[j]:
if (p not in self.visited and
(p not in self.point_costs or self.point_costs[p] >= level + j)):
rv.append(p)
return rv
gri = grid()
with open(sys.argv[1], 'r') as f:
for i in f.readlines():
gri.addline(i.strip())
gri.largegrid()
#for f in gri.y:
# print("".join(map(str, f)))
gri.findpath()
print(gri.current_smallest_solution_level)