def main(data: Iterable[str]): ship_pos = Coord2D(0, 0) waypoint = Coord2D(10, 1) for cmd, amt in comandorate(data): print("Currently at", ship_pos, "waypoint is at", waypoint) if cmd == Command.Forward: print("Moving to waypoint", amt, "times") ship_pos += waypoint * amt continue elif cmd == Command.Left or cmd == Command.Right: print("Rotating waypoint", cmd.name, amt, "degrees") waypoint = rotate(waypoint, cmd, amt) continue if cmd == Command.North: move_dir = Direction.North elif cmd == Command.South: move_dir = Direction.South elif cmd == Command.East: move_dir = Direction.East elif cmd == Command.West: move_dir = Direction.West else: assert False print("Moving waypoint", move_dir.name, amt, "units") waypoint += move_dir.value * amt print("end pos", ship_pos) print(abs(ship_pos.x) + abs(ship_pos.y))
def read_data(self, data: Iterable[str]): y = 0 for line in data: for x in range(len(line)): self[Coord2D(x, y)] = GridCell(line[x]) y += 1 gridmax = Coord2D(-1, -1) for coord in self.keys(): gridmax = Coord2D(x=max(gridmax.x, coord.x), y=max(gridmax.y, coord.y)) self._max = Coord2D(gridmax.x + 1, gridmax.y + 1)
def a_star(self) -> List[Coord2D]: # self.print() start = Coord2D(0, 0) self[start] = self[start]._replace(cheapest_cost=0) working_set = WorkingSet[int, Coord2D]() working_set.add(start, self._heuristic(start)) while working_set: coord = working_set.pop() if coord == self._goal: path = self._get_path() self.print(path) return path _, cost, _ = self[coord] for neighbour in self._get_neighbours(coord, diagonal=False): neighbour_risk, neighbour_cost, _ = self[neighbour] new_cost = cost + neighbour_risk if new_cost < neighbour_cost: self[neighbour] = Chiton(neighbour_risk, new_cost, coord) working_set.add_or_replace( neighbour, new_cost + self._heuristic(neighbour), ) raise RuntimeError("Could not find goal?!")
def main(data: Iterable[str]) -> None: vents = [Vent.parse(line) for line in data] gridmax = Coord2D(0, 0) for vent in vents: gridmax = gridmax.get_max(vent.get_max()) grid = pd.DataFrame([ [0 for x in range(gridmax.x + 1)] # for y in range(gridmax.y + 1) ]) for vent in vents: for coord in vent: grid[coord.x][coord.y] += 1 # print(grid.replace(0, ".").to_string(header=False, index=False)) overlaps = 0 # gnee for col_id in grid.columns: for value in grid[col_id]: if value > 1: overlaps += 1 print(overlaps)
class Grid(Dict[Coord2D, GridCell]): _max: Coord2D(-1, -1) def read_data(self, data: Iterable[str]): y = 0 for line in data: for x in range(len(line)): self[Coord2D(x, y)] = GridCell(line[x]) y += 1 gridmax = Coord2D(-1, -1) for coord in self.keys(): gridmax = Coord2D(x=max(gridmax.x, coord.x), y=max(gridmax.y, coord.y)) self._max = Coord2D(gridmax.x + 1, gridmax.y + 1) def get_neighbours(self, coord: Coord2D) -> Iterable[GridCell]: for dir in DIRECTIONS: try: yield self[coord + dir] except KeyError: pass def get_visible_seats(self, coord: Coord2D) -> Iterable[GridCell]: for dir in DIRECTIONS: target = coord + dir try: while (cell := self[target]) == GridCell.Floor: target += dir yield cell except KeyError: pass
def __str__(self): ret = "" for y in range(self._max.y): for x in range(self._max.x): ret += self[Coord2D(x, y)].value ret += "\n" return ret.strip()
def print(self, path: Optional[Iterable[Coord2D]] = None) -> None: if path: pathset = set(path) else: pathset = set((Coord2D(0, 0), self._goal)) for y in range(self.height): for x in range(self.width): coord = Coord2D(x, y) risk, _, _ = self[coord] if coord in pathset: cprint(str(risk), attrs=["bold"], end="") else: cprint(str(risk), attrs=["dark"], end="") print() print()
def __str__(self) -> str: ret = "" for y in range(self.height): for x in range(self.width): if self.get(Coord2D(x, y)): ret += "#" else: ret += "." ret += "\n" return ret
def print(self) -> None: for y in range(self.height): for x in range(self.width): energy = self[Coord2D(x, y)] if energy == 0: cprint(str(energy), attrs=["bold"], end=" ") else: cprint(str(energy), attrs=["dark"], end=" ") print() print()
def __iter__(self) -> Iterator[Coord2D]: start = self.start end = self.end step = Coord2D(_steppe(start.x, end.x), _steppe(start.y, end.y)) pos = start yield pos # probably fine while True: pos += step yield pos if pos == end: break
def parse(cls, data: Iterator[str]) -> Grid: ret = Transparency() for line in data: if not line: break coord = Coord2D.parse(line) ret.width = max(ret.width, coord.x + 1) ret.height = max(ret.height, coord.y + 1) ret[coord] = True return ret
def main(data: Iterator[str]) -> None: grid = ChitonCave.parse(data) path = grid.a_star() total_risk = 0 start = Coord2D(0, 0) for coord in path: if coord != start: risk, _, _ = grid[coord] total_risk += risk print(total_risk)
def rotate(pos: Coord2D, direction: Command, amount: int): assert amount % 90 == 0 amount //= 90 assert amount <= 4 # Always rotate right if direction == Command.Left: amount = 4 - amount for _ in range(amount): x, y = pos pos = Coord2D(x=y, y=-x) return pos
def main(data: Iterable[str]): coords = sorted(_get_coords(data), key=lambda c: c[0] * c[1]) gridmin = Coord2D(math.inf, math.inf) gridmax = Coord2D(-math.inf, -math.inf) for coord in coords: gridmin = Coord2D(x=min(gridmin.x, coord.x), y=min(gridmin.y, coord.y)) gridmax = Coord2D(x=max(gridmax.x, coord.x), y=max(gridmax.y, coord.y)) print(gridmin.x, gridmin.y, gridmax.x, gridmax.y) MAX_DISTANCE = 10000 size = 0 for x in range(gridmin.x, gridmax.x + 1): for y in range(gridmin.y, gridmax.y + 1): to_check = Coord2D(x, y) distance = sum(coord.distance(to_check) for coord in coords) if distance < MAX_DISTANCE: size += 1 print(size)
def parse(cls, data: Iterator[str]): ret = cls() ret._fill(((Chiton( risk=int(risk), cheapest_cost=sys.maxsize, cheapest_neighbour=None, ) for risk in line) for line in data)) for y in range(6): for x in range(6): if x == 0 and y == 0: continue for coord in ret._iterate_grid(): newcoord = Coord2D( x=coord.x + ret.width * x, y=coord.y + ret.height * y, ) existing = ret[coord] ret[newcoord] = existing._replace( risk=((existing.risk + x + y) % 9) or 9) ret.width *= 5 ret.height *= 5 ret._goal = Coord2D(ret.width - 1, ret.height - 1) return ret
def main(data: Iterable[str]): grid = HeightGrid.parse(data) ret = 0 lowspots: List[Coord2D] = [] for y in range(grid.height): for x in range(grid.width): coord = Coord2D(x, y) pos = grid[coord] if grid.check_lowspot(coord): lowspots.append(coord) cprint(str(pos), color="red", attrs=["bold"], end="") elif pos < 9: cprint(str(pos), end="") else: cprint(str(pos), attrs=["concealed"], end="") print() basins = [grid.count_floodfill(coord) for coord in lowspots] biggest = sorted(basins, reverse=True)[:3] ret = biggest[0] * biggest[1] * biggest[2] print(ret)
def parse(cls, data: str) -> Vent: start, end = data.split(" -> ") return Vent(start=Coord2D.parse(start), end=Coord2D.parse(end))
from enum import Enum from typing import Dict, Iterable from aoc.utils import Coord2D class GridCell(Enum): Floor = "." Chair = "L" OccupiedChair = "#" DIRECTIONS = [ Coord2D(-1, -1), Coord2D(0, -1), Coord2D(1, -1), Coord2D(-1, 0), Coord2D(1, 0), Coord2D(-1, 1), Coord2D(0, 1), Coord2D(1, 1), ] class Grid(Dict[Coord2D, GridCell]): _max: Coord2D(-1, -1) def read_data(self, data: Iterable[str]): y = 0 for line in data: for x in range(len(line)):
def _heuristic(self, coord: Coord2D) -> int: return coord.distance(self._goal)
def _get_coords(data: Iterable[str]) -> Iterable[Coord2D]: for line in data: x, y = line.split(",") yield Coord2D(int(x), int(y))