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 areas(draw, max_modulus=50, min_width=0, max_width=None, min_height=0, max_height=None): lower = draw( st.builds( Point, st.integers(min_value=-max_modulus, max_value=max_modulus - min_width), st.integers(min_value=-max_modulus, max_value=max_modulus - min_height), )) max_x = max_y = max_modulus if max_width is not None: max_x = min(max_x, lower.x + max_width) if max_height is not None: max_y = min(max_y, lower.y + max_height) upper = draw( st.builds( Point, st.integers(min_value=lower.x + min_width, max_value=max_x), st.integers(min_value=lower.y + min_height, max_value=max_y), )) return Area(lower, upper)
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 non_overlapping_areas(draw): """Produce a pair of areas with no points in common. This works by producing the first area, then producing another area in one of the four overlapping areas of space that won't intersect with it. There's probably a neater way to do this, but this does the trick. """ lower = draw(points()) upper = draw(points()) area = Area(lower, upper) left = st.builds(Point, x=st.integers(max_value=lower.x), y=st.integers()) right = st.builds(Point, x=st.integers(min_value=upper.x + 1), y=st.integers()) down = st.builds(Point, x=st.integers(), y=st.integers(max_value=lower.y)) up = st.builds(Point, x=st.integers(), y=st.integers(min_value=upper.y + 1)) second_area = draw( st.one_of( st.builds(Area, lower=left, upper=left), st.builds(Area, lower=right, upper=right), st.builds(Area, lower=down, upper=down), st.builds(Area, lower=up, upper=up), )) return (area, second_area)
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 test_population(self, population): world_area = Area.from_zero(5, 5) builder = Builder(world_area, population) roster = builder.roster assert roster.width == 5 assert roster.height == 5 total_characters = 0 for position, character in roster.positions: assert position in world_area assert character in population total_characters = total_characters + 1 assert total_characters == len( [c for c in population if c is not None])
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_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 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 test_width(self): lower = Point(0, 0) upper = Point(2, 2) area = Area(lower, upper) assert area.width == 2
def test_contains_lower_bound(self, points): lower, upper = points assert lower in Area(lower, upper)
def test_construction_from_zero(self, width, height): assert Area.from_zero(width, height) == Area(Point(0, 0), Point(width, height))
def test_two_point_constructor(self, point_a, point_b): Area(point_a, point_b)
def test_from_origin_type(self, lower, upper, origin): area = Area(lower, upper) assert isinstance(area.from_origin(origin), BoundingBox)
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_no_nearest_character(self, character): roster = Roster.for_mapping( {Point(1, 1): character}, area=Area(Point(0, 0), Point(2, 2)), ) assert roster.nearest_to(Point(1, 1), key=()) is None
def test_empty_roster(self): characters: Mapping[Point, Any] = {} roster = Roster.for_mapping(characters, Area(Point(0, 0), Point(5, 5))) assert not roster
def test_empty_viewpoint(self): characters: Mapping[Point, Any] = {} roster = Roster.for_mapping(characters, area=Area(Point(0, 0), Point(2, 2))) viewpoint = Viewpoint(Point(1, 1), roster) assert viewpoint.occupied_points_in(BoundingBox.range(2)) == set()
yield sleep_time = max(next_tick - current_time(), 0) sleep(sleep_time) def clear() -> None: print("\033[H\033[J", end="") if __name__ == "__main__": population = Population[Character]( (DENSITY * (1 - ZOMBIE_CHANCE), default_human), (DENSITY * ZOMBIE_CHANCE, default_zombie), ) world_area = Area.from_zero(world_width, world_height) barriers = random_barriers(range(BARRIERS), world_area) empty = RenderEmpty.SPACE if world_size_auto and barriers else RenderEmpty.DOT builder = Builder(world_area, population, barriers) roster = builder.roster renderer = Renderer(roster, barriers, empty=empty) ticks = islice(each_interval(TICK), MAX_AGE) tracing_context = ExitStack() trace_file_name = environ.get("TRACEFILE") if trace_file_name: trace_file = open(trace_file_name, mode="w")
def test_viewpoint_single_character(self, character): roster = Roster.for_mapping({Point(1, 1): character}, area=Area(Point(0, 0), Point(2, 2))) viewpoint = Viewpoint(Point(1, 1), roster) assert viewpoint.occupied_points_in( BoundingBox.range(2)) == {Vector.ZERO}
def test_height(self): lower = Point(0, 0) upper = Point(2, 2) area = Area(lower, upper) assert area.height == 2
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_excludes_upper_bound(self, lower, upper): assert upper not in Area(lower, upper)
def test_no_arg_constructor(self): with pytest.raises(TypeError) as exc: Area() # type: ignore
def test_excludes_on_y_coordinate(self): area = Area(Point(0, 0), Point(3, 3)) assert Point(1, 4) not in area
def test_single_arg_constructor(self): with pytest.raises(TypeError): Area(Point(1, 3)) # type: ignore
@given(areas().flatmap(lambda a: st.tuples(st.just(a), points_in(a)))) def test_no_nearest(area_and_point): area, point = area_and_point tree = SpaceTree.build(area=area, positions={point: object()}) assert tree.nearest_to(point) is None @settings(max_examples=25) @given(areas().flatmap(lambda a: st.tuples( st.just(a), st.lists(points_in(a), min_size=2, unique=True).flatmap(list_and_element), ))) @example( area_and_points=( Area(Point(x=-257, y=0), Point(x=0, y=3)), ( [ Point(x=-1, y=0), Point(x=-3, y=0), Point(x=-1, y=1), Point(x=-1, y=2), Point(x=-2, y=1), Point(x=-5, y=0), Point(x=-6, y=0), Point(x=-7, y=0), Point(x=-2, y=0), ], Point(x=-3, y=0), ), ), )
def test_fails_if_instigator_not_in_roster(self, instigator, target): roster = Roster.for_mapping({Point(0, 0): target}, Area(Point(0, 0), Point(5, 5))) action = ChangeCharacter(instigator, target, paint_blue) with pytest.raises(ValueError): action.next_roster(roster)