Esempio n. 1
0
class TestSimulation(unittest.TestCase):
    """Tests for the Simulation class
    """

    class mock_ai(object):
        """A mock AI callable for testing
        """

        def __init__(self, ):
            self.calls = 0
            self.cat = []
            self.goal = []
            self.dogs = []

        def __call__(self, current, cat, dogs, goal):
            """AI call. Stores data about the call and returns a default direction.

            Arguments:
            - `cat`: The cat position
            - `dogs`: The dog positions
            - `goal`: The goal position
            """
            self.dogs.append(dogs)
            self.cat.append(cat)
            self.goal.append(goal)
            self.calls += 1
            return "left"

    def setUp(self, ):
        """Set up a default starting state
        """
        self.cat_ai = self.mock_ai()
        self.dog_ai = self.mock_ai()
        self.DOGS = 5
        self.FIELDSIZE = (16,16)
        self.sim = Simulation(self.cat_ai, self.dog_ai,
                              field_size=self.FIELDSIZE, num_dogs=self.DOGS)

    def _all_in_stage(self, state):
        self.assertTrue(self._in_stage(state["cat"], self.sim.CAT_SIZE))
        for dog in state["dogs"]:
            self.assertTrue(self._in_stage(dog, (self.sim.DOG_RADIUS*2,
                                                 self.sim.DOG_RADIUS*2)))
        self.assertTrue(self._in_stage(state["goal"], self.sim.GOAL_SIZE))

    def _in_stage(self, x, size):
        return (x[0]-(size[0]*0.5) >= 0 and
                x[0]+(size[0]*0.5) <= self.FIELDSIZE[0] and
                x[1]-(size[1]*0.5) >= 0 and
                x[1]+(size[1]*0.5) <= self.FIELDSIZE[1])


    def testGetState(self, ):
        """Tests the getState function.

        Checks that the correct members exist in the state, and that they are of
        correct type.
        """

        state = self.sim.getState()
        self.assertItemsEqual(state.keys(), ["cat", "dogs", "gameover", "win", "updated", "goal"])
        self.assertTrue(isinstance(state["cat"], tuple))
        self.assertTrue(isinstance(state["dogs"], list))
        for dog in state["dogs"]:
            self.assertTrue(isinstance(dog, tuple))
        self.assertTrue(isinstance(state["goal"], tuple))
        self.assertTrue(isinstance(state["gameover"], bool))
        self.assertTrue(isinstance(state["win"], bool))
        self.assertTrue(isinstance(state["updated"], datetime))


    def testInit(self, ):
        """Test that the simulation is initialized correctly
        """

        state = self.sim.getState()
        self.assertEqual(len(state["dogs"]), self.DOGS)
        self.assertEqual(state["gameover"], False)
        self.assertEqual(state["win"], False)
        self._all_in_stage(state)


    def testSimtickWin(self, ):
        """Test the simtick method for a winning move
        """
        # Move cat to the right of the goal (so the ai moving left will win)
        goalright = self.sim._goal.getPosition()[0]+self.sim.GOAL_SIZE[0]*0.5
        self.sim._cat.setPosition([goalright+self.sim.CAT_SIZE[0]*0.5,0])
        old_state = self.sim.getState()
        new_state = self.sim.simtick()
        self.assertGreater(new_state["updated"], old_state["updated"])
        self.assertTrue(new_state["gameover"])
        self.assertTrue(new_state["win"])

        # Make sure it stops ticking after game is over
        done_state = self.sim.simtick()
        self.assertEqual(new_state, done_state)


    def testSimtickLose(self, ):
        """Test the simtick method for a losing move
        """
        # Move the cat inbetween the dogs and the left wall, to ensure collision

        dog_pos = self.sim._dogs[0].getPosition()
        dog_pos = (self.sim.CAT_SIZE[0]+self.sim.DOG_RADIUS, dog_pos[1])
        self.sim._dogs[0].setPosition(dog_pos)
        self.sim._cat.setPosition([0, dog_pos[1]])
        state = self.sim.simtick()
        self.assertTrue(state["gameover"])
        self.assertFalse(state["win"])


    def testSimtickWinOrLose(self, ):
        """Test that the simtick method resolves loss properly.

        In a simultaneous loss and win situation, the loss should win out.
        """
        # Move cat so it will hit the goal, then move a dog on top of the cat,
        # so it will kill the cat in the same turn.
        goalright = self.sim._goal.getPosition()[0]+self.sim.GOAL_SIZE[0]*0.5
        newpos = [goalright+self.sim.CAT_SIZE[0]*0.5,0]
        self.sim._cat.setPosition(newpos)
        self.sim._dogs[0].setPosition(newpos)
        state = self.sim.simtick()
        self.assertTrue(state["gameover"])
        self.assertFalse(state["win"])


    def testAiStep(self, ):
        """Test that _aiStep works properly.
        """
        pre_state = self.sim.getState()
        moves = self.sim._aiStep()
        post_state = self.sim.getState()

        # Check that AIs have been called the right amount of times
        self.assertEqual(self.cat_ai.calls, 1)
        self.assertEqual(self.dog_ai.calls, self.DOGS)

        # Check correct return value
        self.assertEqual(moves, ["left"]*(self.DOGS+1))

        # Assert that state has not changed anywhere during the call
        self.assertEqual(pre_state["cat"], self.cat_ai.cat[0])
        self.assertEqual(pre_state["dogs"], self.cat_ai.dogs[0])
        self.assertEqual(pre_state["goal"], self.cat_ai.goal[0])
        for i in range(5):
            self.assertItemsEqual(self.cat_ai.cat[0], self.dog_ai.cat[i])
            self.assertItemsEqual(self.cat_ai.dogs[0], self.dog_ai.dogs[i])
            self.assertItemsEqual(self.cat_ai.goal[0], self.dog_ai.goal[i])
        self.assertEqual(pre_state, post_state)


    def testUpdateState(self, ):
        """Test that _updateState works.
        """
        # Basic sanity check
        expected_state = self.sim.getState()
        expected_state["cat"] = (expected_state["cat"][0], expected_state["cat"][1]-self.sim.CAT_SPEED)
        expected_state["dogs"] = [(x[0], x[1]-self.sim.DOG_SPEED) for x in expected_state["dogs"]]
        moves = ["up"]*(self.DOGS+1)
        self.sim._updateState(moves)
        post_state = self.sim.getState()
        self.assertEqual(expected_state, post_state)
        self._all_in_stage(post_state)

        # Now check that attempting to move outside the stage will move the
        # entity back in flush with the edge
        old_pos = self.sim._dogs[1].getPosition()
        self.sim._dogs[1].setPosition((old_pos[0], self.sim.DOG_RADIUS))
        self.sim._updateState(moves)
        post_state = self.sim.getState()
        self._all_in_stage(post_state)
        self.assertEqual(self.sim._dogs[1]._position[1], self.sim.DOG_RADIUS)

    def testEnsureInside(self, ):
        """Tests that _ensureInside works.
        """
        # Check that ensure inside moves entity back inside, and only touches
        # the proper coordinate, for all sides
        move_base = self.sim.CAT_SIZE[0]*0.5
        field = self.sim._field_size
        for i in range(4):
            # sides in order: left, right, top, bottom
            if i == 0:
                coord = 0
                setto = 0
                movement = move_base
            elif i == 1:
                coord = 0
                setto = field[0]
                movement = -move_base
            elif i == 2:
                coord = 1
                setto = 0
                movement = move_base
            elif i == 3:
                coord = 1
                setto = field[1]
                movement = -move_base

            old_pos = list(self.sim._cat.getPosition())
            old_pos[coord] = setto
            self.sim._cat.setPosition(old_pos)
            self.sim._ensureInside(self.sim._cat, self.sim.CAT_SIZE)
            new_pos = self.sim._cat.getPosition()
            self._all_in_stage(self.sim.getState())
            expected_pos = [old_pos[0], old_pos[1]]
            expected_pos[coord] = setto+movement
            self.assertEqual(tuple(expected_pos), new_pos)

        # Check that entities that are inside are NOT moved
        old_pos = self.sim._goal.getPosition()
        self.sim._ensureInside(self.sim._goal, self.sim.GOAL_SIZE)
        new_pos = self.sim._goal.getPosition()
        self.assertEqual(old_pos, new_pos)


    def testCheckCollisions(self, ):
        """Test that _checkCollisions works
        """
        # Shouldn't collide when starting, returns empty list, but tests False
        self.assertFalse(self.sim._checkCollisions())

        # Move dog onto cat, should collide and return ["dog"]
        pos = self.sim._dogs[0]._position
        pos[0] = self.sim._cat._position[0]
        pos[1] = self.sim._cat._position[1]
        self.assertEqual(self.sim._checkCollisions(), ["dog"])

        # Move goal onto cat, should collide and return ["dog", "goal"]
        pos = self.sim._goal._position
        pos[0] = self.sim._cat._position[0]
        pos[1] = self.sim._cat._position[1]
        self.assertEqual(self.sim._checkCollisions(), ["dog", "goal"])

        # Move the last dog onto the goal, should collide and return
        # ["dog", "dog", "goal"]
        pos = self.sim._dogs[-1]._position
        pos[0] = self.sim._cat._position[0]
        pos[1] = self.sim._cat._position[1]
        self.assertEqual(self.sim._checkCollisions(), ["dog", "dog", "goal"])


    def testCollideRectWithRect(self, ):
        """Test that the rect to rect collision works
        """
        rect = (5, 5, 10, 10)
        overlapping = (12, 12, 4, 4)
        inside = (5, 5, 2, 2)
        outside = (15, 15, 5, 5)

        self.assertTrue(self.sim._collideRectWithRect(rect, rect))
        self.assertTrue(self.sim._collideRectWithRect(rect, overlapping))
        self.assertTrue(self.sim._collideRectWithRect(rect, inside))
        self.assertFalse(self.sim._collideRectWithRect(rect, outside))

    def testCollideCircleWithRect(self, ):
        """Test that the rect to circle collision works
        """
        rect = (5, 5, 10, 10)
        encompasses = (5, 5, 10)
        inside = (5, 5, 2)
        outside = (12, 12, 1)
        corner = (0, 0, 1)
        edgecase = (5, 1, 2)
        self.assertTrue(self.sim._collideCircleWithRect(encompasses, rect))
        self.assertTrue(self.sim._collideCircleWithRect(inside, rect))
        self.assertTrue(self.sim._collideCircleWithRect(corner, rect))
        self.assertTrue(self.sim._collideCircleWithRect(edgecase, rect))
        self.assertFalse(self.sim._collideCircleWithRect(outside, rect))