def part2(): cur_pos = Coord(0, 0) cur_waypoint = Coord(10, 1) def _rotate_waypoint(diff): num_times = units // 90 if dir == 'L': num_times = (4 - num_times) % 4 for _ in range(num_times): diff = Coord(diff.y, -diff.x) return diff for dir, units in map(Inst.from_str, data): if dir in 'LR': cur_waypoint = cur_pos + _rotate_waypoint(cur_waypoint - cur_pos) elif dir == 'F': waypoint_diff = cur_waypoint - cur_pos cur_pos += waypoint_diff * units cur_waypoint = cur_pos + waypoint_diff else: cur_waypoint += Dir[dir].value * units return cur_pos.manhattan
def board_dimensions(self) -> Dimensions: xs = {c.x for c in self.board} ys = {c.y for c in self.board} min_x, max_x = juxt(min, max)(xs) min_y, max_y = juxt(min, max)(ys) return Dimensions(Coord(min_x, min_y), Coord(max_x, max_y))
class Dir(Enum): n = Coord(0, 1) e = Coord(1, 0) s = Coord(0, -1) w = Coord(-1, 0) def from_offset(self, offset: int): return rot_map[(rot_map[self] + offset) % 4]
def to_coord(cmd, val): match cmd: case 'forward': return Coord(val, 0) case 'down': return Coord(0, val) case 'up': return Coord(0, -val)
class Dir(Enum): N = Coord(0, 1) E = Coord(1, 0) S = Coord(0, -1) W = Coord(-1, 0) def rotate(self, dir, units): mult = 1 if dir == 'R' else -1 return _list_dirs[(_list_dirs.index(self) + mult * units // 90) % len(_list_dirs)]
def _update_board(self, x, y, tile_id): if (x, y) == (-1, 0): self.score = tile_id return t = Tile(tile_id) if t is Tile.ball: self.ball_pos = Coord(x, y) return if t is Tile.paddle: self.paddle_pos = Coord(x, y) super()._update_board(x, y, tile_id)
def _gen_coords(): """ create coords in the order they would appear in the data structure in the docstring in aoc03_a coords: 0, 0 1, 0 1, 1 0, 1 -1, 1 -1, 0 -1, -1, 0, -1 1, -1 """ for i, size in zip(count(1), count(3, 2)): cur = Coord( i, 1 - i) # the start of the next outer square (bottom right corner) cur_offset = Coord(0, 1) offset_dict = { Coord(i, i): Coord(-1, 0), Coord(-i, i): Coord(0, -1), Coord(-i, -i): Coord(1, 0), } yield cur n_elements = size**2 - (size - 2)**2 for _ in range(n_elements - 1): cur += cur_offset yield cur cur_offset = offset_dict.get(cur, cur_offset)
def aoc03_b(value): """generate square with neighbors to figure out first > value occurrence represent square as dict of {coord: value} """ offsets = {Coord(x, y) for x, y in product(range(-1, 2), range(-1, 2))} grid = {Coord(0, 0): 1} def _get_neighbor_vals(_c): return sum(grid.get(_c + o, 0) for o in offsets) for c in _gen_coords(): v = grid[c] = _get_neighbor_vals(c) if v > value: return v
class Direction(Enum): up = Coord(0, -1) right = Coord(1, 0) down = Coord(0, 1) left = Coord(-1, 0) def rotated(self, val): """ 0: rotate 90 deg counter-clockwise 1: rotate 90 deg clockwise """ dirs = list(Direction) val = 2 * val - 1 new_idx = (dirs.index(self) + val) % len(dirs) return dirs[new_idx]
def parse_file(fn): rows = U.read_file(fn, 2017, do_strip=False) return { Coord(c_num, r_num): v for r_num, r in enumerate(rows) for c_num, v in enumerate(r) if v not in set(' \n') }
def __init__(self, program: Program): self._cur_pos = Coord(0, 0) self._map = Map() self._map[self._cur_pos] = Tile.empty self._prog = self._init_program(program) self._last_dir = None self._dirs = []
def __init__(self, init_color=0): """colors are 0 (black) or 1 (white)""" self.whites: Set[Coord] = set() self._cur_pos = Coord(0, 0) if init_color: self.whites.add(self._cur_pos) self._cur_dir = Direction.up self._painted: Set[Coord] = set()
def aoc01_a(insts: List[Inst]): cur = Coord(0, 0) dir = Direction.up for i in insts: dir = turns[i.direction](dir) cur += dir.value * i.steps return cur.manhattan
def _rotate_waypoint(diff): num_times = units // 90 if dir == 'L': num_times = (4 - num_times) % 4 for _ in range(num_times): diff = Coord(diff.y, -diff.x) return diff
def traversed_coords(self, include_diag=False) -> set[Coord]: if not include_diag and self.is_diag: return set() (x1, y1), (x2, y2) = self if x1 == x2: # vertical return {Coord(x1, v) for v in range(min(y1, y2), max(y1, y2) + 1)} if y1 == y2: # horizontal return {Coord(v, y1) for v in range(min(x1, x2), max(x1, x2) + 1)} # diagonal x_off = 1 if x2 > x1 else -1 y_off = 1 if y2 > y1 else -1 return { self.c1 + m * Coord(x_off, y_off) for m in range(abs(x2 - x1) + 1) }
def aoc03_a(location): """only works where the location doesn't 'wrap around' from the bottom of the square e.g., based on this input: 17 16 15 14 13 18 5 4 3 12 19 6 1 2 11 20 7 8 9 10 21 22 23---> ... [21, 25], [7, 9], and [1] would work, but 17, e.g, would not """ side = get_square_side(location) end = side**2 ec = side // 2 end_coord = Coord(ec, ec) end_coord -= Coord(end - location, 0) return end_coord.manhattan
def _add_to_grid(cell: Cell, coord: Coord = Coord(0, 0), rot: Dir = Dir.n): if cell in processed: return processed.add(cell) grid[coord] = rot, cell for cur_to_other, (other_to_cur, other_tile) in cell.conns.items(): abs_dir_to_other = _get_dir_to_other(cell.tile, other_tile, rot) other_rot = _get_other_rotation(rot, cur_to_other, other_to_cur) _add_to_grid(Cell(other_tile), coord + abs_dir_to_other.value, other_rot)
def aoc01_b(insts: List[Inst]): cur = Coord(0, 0) dir = Direction.up seen = {cur} for i in insts: dir = turns[i.direction](dir) for _ in range(i.steps): cur += dir.value if cur in seen: return cur.manhattan seen.add(cur)
class HexOffset(Enum): e = Coord(1, 0) se = Coord(0, 1) sw = Coord(-1, 1) w = Coord(-1, 0) nw = Coord(0, -1) ne = Coord(1, -1)
class Offset(Enum): n = Coord(0, 1) ne = Coord(1, 0) se = Coord(1, -1) s = Coord(0, -1) sw = Coord(-1, 0) nw = Coord(-1, 1)
def parse_map(filename=13): track = {Coord(c_num, r_num): v for r_num, row in enumerate(read_file(filename, 2018, do_strip=False)) for c_num, v in enumerate(row) if v != ' '} carts = {} for coord, v in track.items(): if v in set('<>^v'): track[coord] = '|' if v in set('v^') else '-' carts[coord] = Cart(v) return carts, track
def part1(): cur_dir = Dir.E cur_pos = Coord(0, 0) for dir, units in map(Inst.from_str, data): if dir in 'LR': cur_dir = cur_dir.rotate(dir, units) elif dir == 'F': cur_pos += cur_dir.value * units else: cur_pos += Dir[dir].value * units return cur_pos.manhattan
def _add_to_grid(cur_cell: Cell, cur_coord: Coord = Coord(0, 0), cur_rot: Dir = Dir.n): if cur_cell in processed: return processed.add(cur_cell) if cur_coord in grid: a = 4 grid[cur_coord] = cur_rot, cur_cell for dir, (other_dir, other_cell) in cur_cell.conns.items(): mod_rot = rot_map[(rot_map[dir] + rot_map[cur_rot]) % 4] print(cur_cell.tile.id, dir, cur_rot) other_coord = cur_coord + mod_rot.value _add_to_grid(other_cell, other_coord, _get_other_rotation(cur_rot, dir, other_dir))
def draw(self): min_x = min(c.x for c in self.whites) min_y = min(c.y for c in self.whites) offset = Coord(-min_x, -min_y) coords = {c + offset for c in self.whites} max_x = max(c.x for c in coords) max_y = max(c.y for c in coords) canvas = [[' '] * (max_y + 1) for _ in range(max_x + 1)] for c in coords: canvas[c.x][c.y] = 'X' for l in reversed(canvas): print(''.join(l))
def draw(self, t: Tile, around=8): res = [[None] * around * 2 for _ in range(around * 2)] cp = self._cur_pos coords = (Coord(x, y) for x in range(cp.x - around, cp.x + around) for y in range(cp.y - around, cp.y + around)) res_rcs = ((r, c) for c in range(2 * around) for r in range(2 * around)) for coord, (r, c) in zip(coords, res_rcs): res[r][c] = disp_map[self._map.get(coord)] res[around][around] = 'D' s = f'{Direction(self._last_dir)} -> {self._cur_pos} {t}' print(len(s) * '-') print(s) print(2 * around * '_') for l in reversed(res): print(''.join(l) + '|') print(2 * around * '-') print()
class Dir(Enum): up = Coord(0, -1) right = Coord(1, 0) down = Coord(0, 1) left = Coord(-1, 0)
def from_str(cls, s: str): c1, c2 = s.split(' -> ') return cls(Coord(*eval(c1)), Coord(*eval(c2)))
def _create_connections(coords: Set[Coord]) -> Dict[Coord, Set[Coord]]: res = {} offsets = Coord(0, 0), Coord(1, 0), Coord(-1, 0), Coord(0, 1), Coord(0, -1) for c in coords: res[c] = {new_coord for o in offsets if (new_coord := c + o) in coords} return res
def _get_occupied_mem_coords() -> Set[Coord]: return {Coord(r, c) for r in range(128) for c, v in enumerate(get_hash(r)) if v == '1'}
def _update_board(self, x, y, tile_id): self.board[Coord(x, y)] = Tile(tile_id)