示例#1
0
class Undead:

    life_state = LifeState.UNDEAD
    movement_range = BoundingBox.range(1)
    attack_range = BoundingBox.range(1)
    next_state = None

    def attack(self, target_vectors: SupportsNearestHuman) -> Optional[Vector]:
        nearest_human = target_vectors.nearest_human
        if nearest_human is not None and nearest_human in self.attack_range:
            return nearest_human
        else:
            return None

    def best_move(
        self, target_vectors: SupportsNearestHuman, available_moves: Iterable[Vector]
    ) -> Vector:
        nearest_human = target_vectors.nearest_human
        if nearest_human:

            def move_rank(move: Vector) -> Tuple[float, float]:
                assert nearest_human is not None
                return ((nearest_human - move).distance, move.distance)

            return min(available_moves, key=move_rank)
        else:
            return shortest(available_moves)

    def __eq__(self, other: object) -> bool:
        return isinstance(other, Undead)
示例#2
0
 def test_runs_away_from_zombie(self, human, environment_and_limits):
     environment, limits = environment_and_limits
     # Make sure there's actually room to run away
     assume(limits.intersect(BoundingBox.range(1)) == BoundingBox.range(1))
     move = human.move(FakeViewpoint(environment), limits)
     zombie_vector = environment[0][0]
     assert (zombie_vector - move).distance > zombie_vector.distance
 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),
     }
示例#4
0
    def test_state_change_action(self):
        character = Character(state=Dead(age=20))
        viewpoint = FakeViewpoint([])
        next_action = character.next_action(viewpoint, BoundingBox.range(5),
                                            FakeActions())

        assert next_action == StateChange(Undead())
示例#5
0
 def test_attack_action(self):
     character = Character(state=Undead())
     target = default_human()
     next_action = character.next_action(
         FakeViewpoint([(Vector(1, 1), target)]), BoundingBox.range(5),
         FakeActions())
     assert next_action == Attack(Vector(1, 1))
示例#6
0
 def test_move_action(self):
     character = Character(state=Undead())
     environment = FakeViewpoint([(Vector(3, 3), default_human())])
     next_action: Action = character.next_action(environment,
                                                 BoundingBox.range(5),
                                                 FakeActions())
     assert next_action == Move(Vector(1, 1))
示例#7
0
class Dead:
    def __init__(self, age: int = 0):
        self._age = age

    life_state = LifeState.DEAD
    movement_range = BoundingBox.range(0)

    _resurrection_age: ClassVar[int] = 20

    def attack(self, target_vectors: TargetVectors) -> Optional[Vector]:
        return None

    def best_move(
        self, target_vectors: TargetVectors, available_moves: Iterable[Vector]
    ) -> Vector:
        if Vector.ZERO not in available_moves:
            raise ValueError("Zero move unavailable for dead character")
        return Vector.ZERO

    @property
    def next_state(self) -> State:
        if self._age >= self._resurrection_age:
            return Undead()
        else:
            return Dead(self._age + 1)

    def __eq__(self, other: object) -> bool:
        return isinstance(other, Dead) and self._age == other._age
示例#8
0
 def test_alternate_path(self, zombie):
     environment = [
         (Vector(2, 2), default_human()),
         (Vector(1, 1), default_zombie()),
         (Vector(1, 0), default_zombie()),
     ]
     limits = BoundingBox(Vector(-100, -100), Vector(100, 100))
     assert zombie.move(FakeViewpoint(environment), limits) == Vector(0, 1)
示例#9
0
    def test_nearest_human(self, zombie):
        environment = [
            (Vector(3, -3), default_human()),
            (Vector(2, 2), default_human()),
            (Vector(-3, 3), default_human()),
        ]
        limits = BoundingBox(Vector(-100, -100), Vector(100, 100))

        assert zombie.move(FakeViewpoint(environment), limits) == Vector(1, 1)
示例#10
0
 def occupied_points_in(self, box: BoundingBox) -> Set[Vector]:
     area = box.to_area(self._origin)
     occupied_by_character = {
         m.position - self._origin for m in self._roster.characters_in(area)
     }
     occupied_by_barrier = {
         p - self._origin for p in self._barriers.occupied_points_in(area)
     }
     return occupied_by_character | occupied_by_barrier
示例#11
0
class Living:

    life_state = LifeState.LIVING
    movement_range = BoundingBox.range(2)
    next_state = None

    def attack(self, target_vectors: TargetVectors) -> Optional[Vector]:
        return None

    def best_move(
        self, target_vectors: TargetVectors, available_moves: Iterable[Vector]
    ) -> Vector:
        def nearest_zombie(move: Vector) -> Optional[Vector]:
            if (nearest := target_vectors.nearest_zombie_to(move)) is not None:
                return nearest - move
            else:
示例#12
0
    def test_all_paths_blocked(self, zombie):
        """Test that zombies stay still when surrounded by other zombies.

        This effectively functions as a last check that zombies always have
        their own position as a fall-back, and don't register as blocking their
        own non-movement.
        """
        def env_contents(vector):
            return default_zombie() if vector else zombie

        vectors = [Vector(dx, dy) for dx in [-1, 0, 1] for dy in [-1, 0, 1]]
        distant_human = [(Vector(2, 2), default_human())]
        zombies_all_around = [(v, env_contents(v)) for v in vectors]

        viewpoint = FakeViewpoint(distant_human + zombies_all_around)
        limits = BoundingBox(Vector(-100, -100), Vector(100, 100))

        assert zombie.move(viewpoint, limits) == Vector.ZERO
示例#13
0
    def test_partial_barrier(self):
        # +---+---+---+
        # | m | m |   |
        # +---+---+---+
        # | m | x |   |
        # +---+---+---+
        # | 0 | x |   |
        # +---+---+---+

        character_range = BoundingBox(Vector(0, 0), Vector(3, 3))
        obstacles = {Vector(1, 1), Vector(1, 0)}
        available = available_moves(character_range, obstacles)
        assert available == {
            Vector(0, 0),
            Vector(0, 1),
            Vector(0, 2),
            Vector(1, 2)
        }
示例#14
0
 def test_invalid_range(self):
     with pytest.raises(ValueError):
         BoundingBox.range(-1)
示例#15
0
 def test_range(self, radius, vector):
     bounding_box = BoundingBox.range(radius)
     if abs(vector.dx) <= radius and abs(vector.dy) <= radius:
         assert vector in bounding_box
     else:
         assert vector not in bounding_box
示例#16
0
 def test_vector_containment(self, vector):
     box = BoundingBox(Vector.ZERO, Vector(1, 1))
     assert (vector in box) == (vector == vector.ZERO)
示例#17
0
 def test_negative_box(self):
     box = BoundingBox(Vector.ZERO, Vector(-1, -1))
     assert Vector.ZERO not in box
示例#18
0
 def test_empty_box(self):
     box = BoundingBox(Vector.ZERO, Vector.ZERO)
     assert Vector(1, 1) not in box
示例#19
0
 def test_takes_two_vectors(self):
     BoundingBox(Vector.ZERO, Vector(1, 1))
示例#20
0
class TestBoundingBox:
    def test_takes_two_vectors(self):
        BoundingBox(Vector.ZERO, Vector(1, 1))

    def test_empty_box(self):
        box = BoundingBox(Vector.ZERO, Vector.ZERO)
        assert Vector(1, 1) not in box

    def test_negative_box(self):
        box = BoundingBox(Vector.ZERO, Vector(-1, -1))
        assert Vector.ZERO not in box

    @given(vectors())
    def test_vector_containment(self, vector):
        box = BoundingBox(Vector.ZERO, Vector(1, 1))
        assert (vector in box) == (vector == vector.ZERO)

    @given(
        st.builds(BoundingBox, vectors(bound=ITERATION_BOUND),
                  vectors(bound=ITERATION_BOUND)))
    def test_iteration_covers_box(self, box):
        box_vectors = list(box)
        for vector in box:
            assert vector in box_vectors

    @given(
        box=st.builds(BoundingBox, vectors(bound=ITERATION_BOUND),
                      vectors(bound=ITERATION_BOUND)),
        vector=vectors(),
    )
    @example(BoundingBox(Vector(-2, -2), Vector(3, 3)), Vector(2, 3))
    def test_iteration_is_limited_to_box(self, box, vector):
        assume(vector not in box)
        assert vector not in list(box)

    @given(
        boxes=st.lists(st.builds(BoundingBox, vectors(), vectors()),
                       min_size=2),
        vector=vectors(),
    )
    def test_vector_outside_intersection(self, boxes, vector):
        assume(any(vector not in box for box in boxes))
        intersection = reduce(lambda a, b: a.intersect(b), boxes)
        assert vector not in intersection

    @given(vectors_and_containing_boxes())
    def test_vector_inside_intersection(self, vector_and_boxes):
        vector, boxes = vector_and_boxes
        intersection = reduce(lambda a, b: a.intersect(b), boxes)
        assert vector in intersection

    @given(st.integers(min_value=0), vectors())
    @example(radius=10, vector=Vector(10, 10))
    @example(radius=0, vector=Vector(0, 0))
    def test_range(self, radius, vector):
        bounding_box = BoundingBox.range(radius)
        if abs(vector.dx) <= radius and abs(vector.dy) <= radius:
            assert vector in bounding_box
        else:
            assert vector not in bounding_box

    def test_invalid_range(self):
        with pytest.raises(ValueError):
            BoundingBox.range(-1)
示例#21
0
 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()
示例#22
0
 def test_total_barrier(self):
     obstacles = {Vector(1, y) for y in range(-2, 3)}
     available = available_moves(BoundingBox.range(2), obstacles)
     assert available == set(BoundingBox(Vector(-2, -2), Vector(1, 3)))
示例#23
0
 def test_does_not_obstruct_self(self, human):
     environment = FakeViewpoint([(Vector.ZERO, human)])
     limits = BoundingBox(Vector(-100, -100), Vector(100, 100))
     assert human.move(environment, limits) == Vector.ZERO
示例#24
0
 def test_does_not_move_in_empty_environment(self, human):
     limits = BoundingBox(Vector(-100, -100), Vector(100, 100))
     assert human.move(FakeViewpoint([]), limits) == Vector.ZERO
示例#25
0
 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}
示例#26
0
    def test_targets_out_of_range(self, zombie, environment):
        biting_range = BoundingBox(Vector(-1, -1), Vector(2, 2))
        assume(all(e[0] not in biting_range for e in environment))

        assert zombie.attack(FakeViewpoint(environment)) is None