def test_move_of_non_existent_character(self, character, non_existent_character): roster = Roster.for_mapping({Point(0, 0): character}, Area(Point(0, 0), Point(5, 5))) move = Move(non_existent_character, Point(1, 1), Point(2, 1)) with pytest.raises(ValueError): move.next_roster(roster)
def test_move_to_occupied_position(self, mover, obstacle): positions = {Point(0, 0): mover, Point(1, 1): obstacle} roster = Roster.for_mapping(positions, area_containing(positions)) move = Move(mover, Point(0, 0), Point(1, 1)) with pytest.raises(ValueError): move.next_roster(roster)
def test_preserves_instigator(self, instigator, target): positions = {Point(0, 0): instigator, Point(1, 1): target} roster = Roster.for_mapping(positions, area_containing(positions)) paint = ChangeCharacter(instigator, Point(1, 1), paint_blue) new_roster = paint.next_roster(roster) assert new_roster.character_at(Point(0, 0)) is instigator
def area_containing(points): min_x = min((p.x for p in points), default=0) min_y = min((p.y for p in points), default=0) max_x = max((p.x for p in points), default=0) max_y = max((p.y for p in points), default=0) return Area(lower=Point(min_x, min_y), upper=Point(max_x + 1, max_y + 1))
def turn(self, turn): a, b = self.velocity if turn == Robot.LEFT: return Point((-b, a)) elif turn == Robot.RIGHT: return Point((b, -a)) raise "Invalid turn"
def travel(stretch, origin): vector = switch(lambda direction, length: direction, { 'U': lambda direction, length: Point((0, length)), 'R': lambda direction, length: Point((length, 0)), 'D': lambda direction, length: Point((0, -length)), 'L': lambda direction, length: Point((-length, 0)), }) return Point(origin) + vector(**stretch)
def positions(self) -> Generator[Tuple[Point, BarrierPoint], None, None]: for point in self.points: bp = BarrierPoint( above=self.occupied(Point(point.x, point.y - 1)), below=self.occupied(Point(point.x, point.y + 1)), left=self.occupied(Point(point.x - 1, point.y)), right=self.occupied(Point(point.x + 1, point.y)), ) yield (point, bp)
def test_changes_target(self, instigator): target = Character(colour="red") positions = {Point(0, 0): instigator, Point(1, 1): target} roster = Roster.for_mapping(positions, area_containing(positions)) paint = ChangeCharacter(instigator, Point(1, 1), paint_blue) new_roster = paint.next_roster(roster) new_character = new_roster.character_at(Point(1, 1)) assert new_character is not None assert new_character.colour == "blue"
def test_viewpoint_multiple_characters(self, char1, char2): roster = Roster.for_mapping( { Point(1, 1): char1, Point(2, 0): char2 }, area=Area(Point(0, 0), Point(3, 3)), ) viewpoint = Viewpoint(Point(0, 1), roster) assert viewpoint.occupied_points_in( BoundingBox.range(1)) == {Vector(1, 0)} assert viewpoint.occupied_points_in(BoundingBox.range(5)) == { Vector(1, 0), Vector(2, -1), }
def rosters(draw, inhabitants=st.one_of(st.builds(default_human), st.builds(default_zombie))): width, height = draw(world_dimensions), draw(world_dimensions) assume(width > 0) assume(height > 0) area = Area(Point(0, 0), Point(width, height)) points = st.builds( Point, st.integers(min_value=0, max_value=width - 1), st.integers(min_value=0, max_value=height - 1), ) characters = draw(st.dictionaries(points, inhabitants)) return Roster.partitioned(characters, area=area, partition_func=LifeState.for_character)
def from_string(self, string): self.update({ Point((i, j)): char for j, line in enumerate(string.split('\n')) for i, char in enumerate(list(line)) }) return self
def test_dead_human(self): dead_human = Character(LifeState.DEAD) world = World( width=3, height=1, positions=[(Point(1, 0), dead_human)], ) renderer = Renderer(world) assert renderer.lines == [". \U0001F480 . "]
def _split(self) -> Tuple[Area, Area, LowerFunc]: if self._area.width >= self._area.height: # Split horizontally midpoint_x = (self._area._lower.x + self._area._upper.x) // 2 lower_area = Area(self._area._lower, Point(midpoint_x, self._area._upper.y)) upper_area = Area(Point(midpoint_x, self._area._lower.y), self._area._upper) lower_func = lambda point: point.x < midpoint_x return (lower_area, upper_area, lower_func) else: # Split vertically midpoint_y = (self._area._lower.y + self._area._upper.y) // 2 lower_area = Area(self._area._lower, Point(self._area._upper.x, midpoint_y)) upper_area = Area(Point(self._area._lower.x, midpoint_y), self._area._upper) lower_func = lambda point: point.y < midpoint_y return (lower_area, upper_area, lower_func)
def random_barriers(counter: Iterable[Any], area: Area) -> Barriers: def x_coord() -> int: return random.randint(area._lower.x, area._upper.x - 1) def y_coord() -> int: return random.randint(area._lower.y, area._upper.y - 1) barrier_areas = set() for _ in counter: if random.choice([True, False]): # Vertical barrier x1 = x_coord() x2 = x1 + 1 y1, y2 = sorted([y_coord(), y_coord()]) else: # Horizontal barrier x1, x2 = sorted([x_coord(), x_coord()]) y1 = y_coord() y2 = y1 + 1 barrier_areas.add(Area(Point(x1, y1), Point(x2, y2))) return Barriers.for_areas(barrier_areas)
def find_path(grid): robot = next(coords for coords in grid if grid[coords] in '<^>v') path = [Point(robot)] while True: adjacent = [ p for p in path[-1].adjacent(1) if walkable(p, grid) and (len(path) < 2 or p != path[-2]) ] go_straight = lambda position: -1 if len(path) < 2 or (path[-1] - path[ -2]) == (position - path[-1]) else 1 move = min(adjacent, key=go_straight, default=None) # final position reached; exit if move is None: break path.append(move) return path
def trace(asteroids): for i in range(len(asteroids)): asteroid = asteroids[i] for j in range(i + 1, len(asteroids)): other = asteroids[j] x, y = (a - b for a, b in zip(asteroid.coords, other.coords)) slope = (y / x) if x != 0 else infinity positive = other.coords > asteroid.coords asteroid.traces[(slope, positive)].append(other) other.traces[(slope, not positive)].append(asteroid) # sort by distance for asteroid in asteroids: sortByDistance = compose(Point(asteroid.coords).manhattan, Point, attr('coords')) for slope in asteroid.traces: asteroid.traces[slope] = sorted(asteroid.traces[slope], key=sortByDistance) return asteroids
def test_zombie_approaches_human(self): zombie = default_zombie() human = default_human() characters = {Point(0, 0): zombie, Point(2, 2): human} area = Area(Point(0, 0), Point(3, 3)) roster = Roster.partitioned(characters, area=area, partition_func=LifeState.for_character) roster = Tick(roster).next() assert sorted(roster.positions) == [(Point(1, 1), zombie), (Point(2, 2), human)]
def test_height(self): lower = Point(0, 0) upper = Point(2, 2) area = Area(lower, upper) assert area.height == 2
def test_width(self): lower = Point(0, 0) upper = Point(2, 2) area = Area(lower, upper) assert area.width == 2
def test_construction_from_zero(self, width, height): assert Area.from_zero(width, height) == Area(Point(0, 0), Point(width, height))
def test_single_arg_constructor(self): with pytest.raises(TypeError): Area(Point(1, 3)) # type: ignore
class TestArea: def test_no_arg_constructor(self): with pytest.raises(TypeError) as exc: Area() # type: ignore def test_single_arg_constructor(self): with pytest.raises(TypeError): Area(Point(1, 3)) # type: ignore @given(points(), points()) def test_two_point_constructor(self, point_a, point_b): Area(point_a, point_b) @given(st.integers(), st.integers()) def test_construction_from_zero(self, width, height): assert Area.from_zero(width, height) == Area(Point(0, 0), Point(width, height)) @given(ordered_points()) def test_contains_lower_bound(self, points): lower, upper = points assert lower in Area(lower, upper) def test_width(self): lower = Point(0, 0) upper = Point(2, 2) area = Area(lower, upper) assert area.width == 2 def test_height(self): lower = Point(0, 0) upper = Point(2, 2) area = Area(lower, upper) assert area.height == 2 @given(points(), points()) def test_excludes_upper_bound(self, lower, upper): assert upper not in Area(lower, upper) @given(ordered_points()) def test_includes_midpoint(self, points): lower, upper = points midpoint = Point((lower.x + upper.x) // 2, (lower.y + upper.y) // 2) assert midpoint in Area(lower, upper) def test_excludes_on_x_coordinate(self): area = Area(Point(0, 0), Point(3, 3)) assert Point(4, 1) not in area def test_excludes_on_y_coordinate(self): area = Area(Point(0, 0), Point(3, 3)) assert Point(1, 4) not in area @given( st.builds(Area, points(bound=ITERATION_BOUND), points(bound=ITERATION_BOUND))) def test_iteration_covers_area(self, area): area_points = list(area) for point in area: assert point in area_points @given( area=st.builds(Area, points(bound=ITERATION_BOUND), points(bound=ITERATION_BOUND)), point=points(), ) @example(Area(Point(-2, -2), Point(3, 3)), Point(2, 3)) def test_iteration_is_limited_to_area(self, area, point): assume(point not in area) assert point not in list(area) @given(points(), points(), points()) def test_from_origin_type(self, lower, upper, origin): area = Area(lower, upper) assert isinstance(area.from_origin(origin), BoundingBox) @given(st.builds(Area, points(), points()), points(), points()) @example(Area(Point(0, 0), Point(2, 2)), Point(0, 0), Point(1, 1)) def test_from_origin_containment(self, area, origin, point): from_origin = area.from_origin(origin) assert (point in area) == ((point - origin) in from_origin) @given(st.builds(Area, points(), points()), points()) def test_areas_and_boxes(self, area, origin): assert area.from_origin(origin).to_area(origin) == area @given(overlapping_areas()) def test_overlapping_areas(self, areas): area_a, area_b = areas assert area_a.intersects_with(area_b) assert area_b.intersects_with(area_a) @given(non_overlapping_areas()) @example([Area(Point(0, 0), Point(2, 2)), Area(Point(2, 0), Point(4, 2))]) @example([Area(Point(0, 0), Point(2, 2)), Area(Point(0, 2), Point(2, 4))]) def test_non_overlapping_areas(self, areas): area_a, area_b = areas assert not area_a.intersects_with(area_b) assert not area_b.intersects_with(area_a) @given( areas=st.lists(st.builds(Area, points(), points()), min_size=2), point=points(), ) def test_point_outside_intersection(self, areas, point): assume(any(point not in area for area in areas)) intersection = reduce(lambda a, b: a.intersect(b), areas) assert point not in intersection @given(points_and_containing_areas()) def test_point_inside_intersection(self, point_and_areas): point, areas = point_and_areas intersection = reduce(lambda a, b: a.intersect(b), areas) assert point in intersection
def test_point_addition_fails(self): # Adding a point to a point doesn't make sense with pytest.raises(AttributeError): Point(2, 5) + Point(3, 2) # type: ignore
def test_no_arg_constructor(self): with pytest.raises(TypeError) as exc: Point() # type: ignore
def test_zombie(self): zombie = Character(LifeState.UNDEAD) world = World(width=3, height=1, positions=[(Point(1, 0), zombie)]) renderer = Renderer(world) assert renderer.lines == [". \U0001F9DF . "]
def test_human(self): human = Character(LifeState.LIVING) world = World(width=3, height=1, positions=[(Point(1, 0), human)]) renderer = Renderer(world) assert renderer.lines == [". \U0001F9D1 . "]
def add_portal(first_letter, second_letter, position): name = grid[first_letter] + grid[second_letter] portals[name].append(Point(position))
def test_includes_midpoint(self, points): lower, upper = points midpoint = Point((lower.x + upper.x) // 2, (lower.y + upper.y) // 2) assert midpoint in Area(lower, upper)
def test_excludes_on_y_coordinate(self): area = Area(Point(0, 0), Point(3, 3)) assert Point(1, 4) not in area
def test_accepts_two_coordinates(self, x, y): point = Point(x, y) assert point.x == x assert point.y == y