def visible_neighbors(seats: SeatMap, origin: Point) -> typing.List[SeatStatus]: directions = Point.directions8 statuses = [] for x, y in directions: for magnitude in itertools.count(1): dx, dy = x * magnitude, y * magnitude p = origin.displace(dx, dy) if p not in seats: break if seats[p] != SeatStatus.Floor: statuses.append(seats[p]) break return statuses
class Explorer: def __init__(self, tape): self.tape = tape self.position = Point(0, 0) self.graph = collections.defaultdict(set) self.oxygen = None def explore(self): places_to_visit = collections.deque([self.position]) visited = {self.position} while places_to_visit: next_point = places_to_visit.pop() self.move_to(next_point) unvisited_neighbors = set(self.position.neighbors()) - visited for neighbor in unvisited_neighbors: status = self.explore_adjacent_point(neighbor) if status == StatusCode.Wall: visited.add(neighbor) if status in [StatusCode.Step, StatusCode.Oxygen]: self.graph[self.position].add(neighbor) self.graph[neighbor].add(self.position) places_to_visit.append(neighbor) if status == StatusCode.Oxygen: self.oxygen = neighbor visited.add(self.position) def find_path(self, start, end): import heapq as h distances = {start: 0} heap = [(0, start)] prev_node = dict() while end not in prev_node: weight, node = h.heappop(heap) unvisited_neighbors = self.graph[node] - distances.keys() for neighbor in unvisited_neighbors: prev_node[neighbor] = node distance = weight + 1 distances[neighbor] = weight h.heappush(heap, (distance, neighbor)) cur_node = end path = [end] while cur_node != start: previous_node = prev_node[cur_node] path.append(previous_node) cur_node = previous_node return list(reversed(path))[1:] def distances_from(self, start): import heapq as h distances = {start: 0} heap = [(0, start)] while heap: weight, node = h.heappop(heap) unvisited_neighbors = self.graph[node] - distances.keys() for neighbor in unvisited_neighbors: distance = weight + 1 distances[neighbor] = weight h.heappush(heap, (distance, neighbor)) return distances def move_direction(self, direction): input_code = direction_to_int(direction) status_code = self.tape.run_until_output(provide_input=input_code)[0] status = StatusCode(status_code) if status != StatusCode.Wall: self.position = self.position.displace(*direction.value) return status def move_to(self, destination): if self.position == destination: return for point in self.find_path(self.position, destination): self.move_direction(self.position.direction_to(point)) def explore_adjacent_point(self, point): direction = self.position.direction_to(point) status = self.move_direction(direction) if status != StatusCode.Wall: self.move_direction(direction.inverse_direction()) return status