def Movement(self): if self == Direction.NORTH: return linalg.Point(row=-1, col=0) elif self == Direction.EAST: return linalg.Point(row=0, col=1) elif self == Direction.SOUTH: return linalg.Point(row=1, col=0) elif self == Direction.WEST: return linalg.Point(row=0, col=-1) else: raise ValueError(f"Invalid direction {self}")
def test_Part2(self): state = problem12.WaypointShip() state = state.ApplyInputs(EXAMPLE_1.splitlines()) self.assertEqual( state, problem12.WaypointShip( position=linalg.Point(row=72, col=214), waypoint=linalg.Point(row=10, col=4), ), ) self.assertEqual( abs(state.position.row) + abs(state.position.col), 286)
def __str__(self): output = "" for row in range(0, self.height + 1): for col in range(0, self.width + 1): output += self.data.get(linalg.Point(row=row, col=col), " ") output += "\n" return output
class WaypointShip(object): waypoint: linalg.Point = linalg.Point(row=-1, col=10) position: linalg.Point = linalg.Point(0, 0) def TranslateWaypoint(self, pt: linalg.Point) -> WaypointShip: return WaypointShip( waypoint=self.waypoint + pt, position=self.position, ) def Command(self, c: str) -> WaypointShip: match = INPUT_REGEX.match(c.strip()) if not match: raise ValueError("Could not parse") command = match.group("command") value = int(match.group("value")) if command == "N": return self.TranslateWaypoint(Direction.NORTH.Movement() * value) elif command == "E": return self.TranslateWaypoint(Direction.EAST.Movement() * value) elif command == "S": return self.TranslateWaypoint(Direction.SOUTH.Movement() * value) elif command == "W": return self.TranslateWaypoint(Direction.WEST.Movement() * value) elif command == "L": return WaypointShip( position=self.position, waypoint=self.waypoint.RotateClockwise(-value), ) elif command == "R": return WaypointShip( position=self.position, waypoint=self.waypoint.RotateClockwise(value), ) elif command == "F": return WaypointShip( waypoint=self.waypoint, position=self.position + self.waypoint * value, ) else: raise ValueError(f"Invalid command: {command}, {value}") def ApplyInputs(self, inputs: Iterable[str]) -> WaypointShip: state = self for line in inputs: state = state.Command(line.strip()) return state
def CountTrees(hill: grid.Grid, slope: linalg.Slope) -> int: trees_hit = 0 pt = linalg.Point(0, 0) while pt.row < hill.rows: if hill.Get(pt) == grid.Tile.TREE: trees_hit += 1 pt += slope return trees_hit
def test_Part1(self): state = problem12.Ship() state = state.ApplyInputs(EXAMPLE_1.splitlines()) self.assertEqual( state, problem12.Ship( position=linalg.Point(row=8, col=17), direction=problem12.Direction.SOUTH, ), ) self.assertEqual(abs(state.position.row) + abs(state.position.col), 25)
def FromString( cls, inp: str, bounds: Mapping[Tuple[Axis, Direction], BoundaryBehavior] = {}, ) -> Grid: data = dict() for row_idx, line in enumerate(inp.splitlines()): for col_idx, char in enumerate(line): pt = linalg.Point(row=row_idx, col=col_idx) data[pt] = Tile.FromChar(char) return cls(tiles=data, boundary_behaviors=bounds)
def Neighbors(pt: linalg.Point) -> Iterator[linalg.Point]: yield pt + linalg.Point(row=0, col=1) yield pt + linalg.Point(row=0, col=-1) yield pt + linalg.Point(row=1, col=0) yield pt + linalg.Point(row=1, col=-1) yield pt + linalg.Point(row=-1, col=1) yield pt + linalg.Point(row=-1, col=0)
class Ship(object): direction: Direction = Direction.EAST position: linalg.Point = linalg.Point(0, 0) def Translate(self, pt: linalg.Point) -> Ship: return Ship( direction=self.direction, position=self.position + pt, ) def Command(self, c: str) -> Ship: match = INPUT_REGEX.match(c.strip()) if not match: raise ValueError("Could not parse") command = match.group("command") value = int(match.group("value")) if command == "N": return self.Translate(Direction.NORTH.Movement() * value) elif command == "E": return self.Translate(Direction.EAST.Movement() * value) elif command == "S": return self.Translate(Direction.SOUTH.Movement() * value) elif command == "W": return self.Translate(Direction.WEST.Movement() * value) elif command == "L": state = self for _ in range(0, value, 90): state = Ship( direction=state.direction.Left(), position=state.position, ) return state elif command == "R": state = self for _ in range(0, value, 90): state = Ship( direction=state.direction.Right(), position=state.position, ) return state elif command == "F": return self.Translate(self.direction.Movement() * value) raise ValueError(f"Invalid command: {command}, {value}") def ApplyInputs(self, inputs: Iterable[str]) -> Ship: state = self for line in inputs: state = state.Command(line) return state
def Movement(instructions: str) -> linalg.Point: match = _MOVE_RE.fullmatch(instructions.strip()) position = linalg.Point(0, 0) for move in match.captures("move"): if move == "e": position += linalg.Point(row=0, col=1) elif move == "w": position += linalg.Point(row=0, col=-1) elif move == "se": position += linalg.Point(row=1, col=0) elif move == "sw": position += linalg.Point(row=1, col=-1) elif move == "ne": position += linalg.Point(row=-1, col=1) elif move == "nw": position += linalg.Point(row=-1, col=0) return position
return nextgrid else: g = nextgrid def Visible(self, pt: linalg.Point, direction: linalg.Point) -> Optional[str]: next_point = pt while True: next_point += direction next_cell = self.data.get(next_point) if next_cell != ".": return next_cell _DIRECTIONS = [ linalg.Point(0, 1), linalg.Point(0, -1), linalg.Point(1, 1), linalg.Point(1, 0), linalg.Point(1, -1), linalg.Point(-1, 1), linalg.Point(-1, 0), linalg.Point(-1, -1), ] def ParseInput(inp: str) -> Grid: result = {} for row_idx, row in enumerate(inp.splitlines()): for col_idx, char in enumerate(row.strip()): result[linalg.Point(row=row_idx, col=col_idx)] = char
def test_multiplication(self): self.assertEqual(linalg.Point(1, 2) * 2, linalg.Point(2, 4)) self.assertEqual(linalg.Point(1, 2) * -2, linalg.Point(-2, -4))
def test_subtraction(self): self.assertEqual( linalg.Point(1, 2) - linalg.Point(3, 4), linalg.Point(-2, -2)) self.assertEqual( linalg.Point(5, 10) - linalg.Point(3, 2), linalg.Point(2, 8))
def Get(self, coord: linalg.Point) -> Tile: normalized = linalg.Point( row=self._Normalize(Axis.ROW, coord.row), col=self._Normalize(Axis.COL, coord.col), ) return self.tiles[normalized]
def test_Rotate(self): self.assertEqual( linalg.Point(1, 2).RotateClockwise(90), linalg.Point(2, -1)) self.assertEqual( linalg.Point(1, 2).RotateClockwise(-90), linalg.Point(-2, 1))
def test_CanAddToPoint(self): self.assertEqual( linalg.Point(row=1, col=2) + linalg.Slope(over=10, down=30), linalg.Point(row=31, col=12), )
def test_addition(self): self.assertEqual( linalg.Point(1, 2) + linalg.Point(3, 4), linalg.Point(4, 6))
def ParseInput(inp: str) -> Grid: result = {} for row_idx, row in enumerate(inp.splitlines()): for col_idx, char in enumerate(row.strip()): result[linalg.Point(row=row_idx, col=col_idx)] = char return Grid(result)