alexandrajay2002

Advent of Code 2024 day 18 part 1

Dec 18th, 2024
36
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.39 KB | Source Code | 0 0
  1. from dataclasses import dataclass
  2. from collections import defaultdict
  3. from math import inf
  4. from argparse import ArgumentParser, FileType
  5.  
  6.  
  7. @dataclass(frozen=True, slots=True)
  8. class Coord:
  9.     x: int
  10.     y: int
  11.     z: int = 0
  12.  
  13.     def taxicab_distance(self, other):
  14.         return abs(self.x - other.x) + abs(self.y - other.y) \
  15.             + abs(self.z - other.z)
  16.  
  17.     def to_3d(self, z):
  18.         return Coord(self.x, self.y, self.z)
  19.  
  20.     def on_grid(self, max_x, max_y):
  21.         return 0 <= self.y <= max_x and 0 <= self.x <= max_y
  22.  
  23.     def move(self, dx=0, dy=0, dz=0):
  24.         return Coord(self.x + dx, self.y + dy, self.z + dz)
  25.  
  26.     @property
  27.     def neighbours(self):
  28.         yield Coord(self.x, self.y - 1)
  29.         yield Coord(self.x + 1, self.y)
  30.         yield Coord(self.x, self.y + 1)
  31.         yield Coord(self.x - 1, self.y)
  32.  
  33.  
  34. def parse_byte(line):
  35.     x, y = line.split(',')
  36.     return Coord(int(x), int(y))
  37.  
  38.  
  39. def parse(src, byte_count):
  40.     return {parse_byte(next(src)) for _ in range(byte_count)}
  41.  
  42.  
  43. def print_grid(walls, path, max_x, max_y):
  44.     output = ''
  45.     for y in range(max_y+1):
  46.         for x in range(max_x+1):
  47.             coord = Coord(x, y)
  48.             if coord in path:
  49.                 output += 'O'
  50.             elif coord in walls:
  51.                 output += '#'
  52.             else:
  53.                 output += '.'
  54.         output += '\n'
  55.     print(output)
  56.  
  57.  
  58. def ScoreMap(mapping):
  59.     return defaultdict(lambda: inf, mapping)
  60.  
  61.  
  62. def get_nodes(prev, end):
  63.     nodes = set()
  64.     next_node = end
  65.     while next_node is not None:
  66.         nodes.add(next_node)
  67.         next_node = prev.get(next_node)
  68.     return nodes
  69.  
  70.  
  71. def a_star(walls, max_x, max_y):
  72.     start = Coord(0, 0)
  73.     end = Coord(max_x, max_y)
  74.  
  75.     discovered = set((start,))
  76.     g_score = ScoreMap({start: 0})
  77.     f_score = ScoreMap({start: start.taxicab_distance(end)})
  78.     prev = dict()
  79.  
  80.     while len(discovered) > 0:
  81.         current_node = min(discovered, key=lambda x: f_score[x])
  82.  
  83.         if current_node == end:
  84.             return get_nodes(prev, end)
  85.  
  86.         discovered.remove(current_node)
  87.  
  88.         for neighbour in current_node.neighbours:
  89.             if not neighbour.on_grid(max_x, max_y) or neighbour in walls:
  90.                 continue
  91.  
  92.             candidate_score = g_score[current_node] + 1
  93.             if candidate_score < g_score[neighbour]:
  94.                 g_score[neighbour] = candidate_score
  95.                 f_score[neighbour] = candidate_score \
  96.                     + neighbour.taxicab_distance(end)
  97.  
  98.                 discovered.add(neighbour)
  99.                 prev[neighbour] = current_node
  100.  
  101.     return None  # unexpanded nodes exhausted but goal not reached
  102.  
  103.  
  104. def main(walls, max_x, max_y, verbose):
  105.     path = a_star(walls, max_x, max_y)
  106.     if verbose:
  107.         print_grid(walls, path, max_x, max_y)
  108.     return len(path) - 1
  109.  
  110.  
  111. arg_parser = ArgumentParser()
  112. arg_parser.add_argument('src', type=FileType('r'))
  113. arg_parser.add_argument('max_x', nargs='?', type=int, default=70)
  114. arg_parser.add_argument('max_y', nargs='?', type=int, default=70)
  115. arg_parser.add_argument('byte_count', nargs='?', type=int, default=1024)
  116. arg_parser.add_argument('-v', '--verbose', action='store_true')
  117.  
  118. if __name__ == '__main__':
  119.     args = arg_parser.parse_args()
  120.     walls = parse(args.src, args.byte_count)
  121.     print(main(walls, args.max_x, args.max_y, args.verbose))
  122.  
Add Comment
Please, Sign In to add comment