def test_add_snake_collision(self): self.board.snakes = [] self.board.add_object("snake", Point(x=20, y=20)) self.board.add_object("snake", Point(x=20, y=20)) snake_1 = self.board.objects['snake'].pop() snake_2 = self.board.objects['snake'].pop() self.assertEqual(False, snake_1 in snake_2, "These are the same snakes")
def test_snake_eat_apple(self): self.board = Board(width=50, height=50) self.board.add_object("snake", Point(x=20, y=33)) self.board.add_object("apple", Point(x=20, y=32)) snake = self.board.objects['snake'][0] _, reward, _, _ = self.board.step(constants.GET_ACTION_MEANING.index("UP")) self.assertEqual(constants.DEFAULT_REWARD_PER_APPLE + constants.DEFAULT_REWARD_PER_STEP, reward, "Snake eating apple incorrect points awarded") self.assertEqual(LEN_SNAKE_START+1, len(snake), "Snake length is not increased after eating apple")
def obs(self): """ Generates the output array. The output will be a (24,) numpy array, with 3 times 8 directions. wall distance, snake distance, food distance ["UP", "DOWN", "LEFT", "LEFT UP", "LEFT DOWN", "RIGHT", "RIGHT UP", "RIGHT DOWN"] """ object_types = [ v for k, v in self.board.object_types.items() if k != "ground" ] obs_directions = [x for x in itertools.product([0, 1, -1], repeat=2)][1:] obs_out = np.zeros((len(object_types), len(obs_directions)), dtype=np.int) snake = self.board.objects['snake'][0] for idx_direction, direction in enumerate(obs_directions): scan_direction = Point(*direction) object_found = False scan_counter = 1 while not object_found: scan_x = snake.position.x + scan_direction.x * scan_counter scan_y = snake.position.y + scan_direction.y * scan_counter for idx_object, object_type in enumerate(object_types): if isinstance(self.board.board[scan_x, scan_y], object_type): obs_out[idx_object, idx_direction] = scan_counter object_found = True scan_counter += 1 return obs_out.flatten()
def test_collide_body(self): collide = self.snake.collide( type("", (), dict(position=Point(43, 150)))) self.assertEqual(False, collide, "Collide body wrongly detected") collide = self.snake.collide( type("", (), dict(position=Point(50, 150)))) self.assertEqual(False, collide, "Collide body wrongly detected") collide = self.snake.collide( type("", (), dict(position=Point(80, 120)))) self.assertEqual(False, collide, "Collide body wrongly detected") collide = self.snake.collide( type("", (), dict(position=self.pos_start))) self.assertEqual(True, collide, "Collide body not detected")
def add_object(self, name, position=None): """ Add an object to the board :param name: str Name of the object you want to add, valid options are currently 'wall', 'snake', 'ground', 'apple' :param position: Point Defines the position of the object on the board, if none is provided it will pick a random position on the board """ start_pos = position if position is not None else self._random_point() if self.object_types.get(name.lower().strip(), None): new_object = self.object_types.get(name.lower().strip())(start_pos) if not self._collision_new_object(new_object): self.objects[name].append(new_object) self.board[start_pos.x, start_pos.y] = new_object else: options = self._locate_object(Ground) if not options: raise ValueError("No space to put the new object.") new_pos = np.random.choice(range(len(options))) self.add_object(name, position=Point(*options[new_pos])) else: raise ValueError(f"Object '{name}' not familiar")
def _create_board(width, height): """ Creates a numpy array with only ground objects, can be populated by other Objects """ board = np.zeros((width, height), dtype=np.object) for x in range(width): for y in range(height): border_row = (y % (height - 1)) == 0 border_col = (x % (width - 1)) == 0 if border_row or border_col: board[x, y] = Wall(Point(x, y)) else: board[x, y] = Ground(Point(x, y)) return board
def test_add_snake_specific(self): self.board.snakes = [] self.board.add_object("snake", Point(x=10, y=15)) snake = self.board.objects['snake'].pop() self.assertEqual(10, snake.get_head().x, "x position not set correctly") self.assertEqual(15, snake.get_head().y, "y position not set correctly") self.assertEqual(constants.LEN_SNAKE_START, len(snake), "Snake is not start length")
def test_snake_wall(self): self.board = Board(width=50, height=50) self.board.add_object("snake", Point(x=1, y=30)) snake = self.board.objects['snake'][0] _, _, done, _ = self.board.step(constants.GET_ACTION_MEANING.index("LEFT")) self.board.step(1) self.assertEqual(True, done, "Game over is not detected upon dying") self.assertEqual(False, snake.alive, "Snake didn't die upon hitting the wall")
def setUp(self) -> None: self.objects_name = ["wall", "ground", "apple", "snake"] self.wall = Wall(Point(0, 0)) self.ground = Ground(Point(10, 10)) self.apple = Apple(Point(20, 20)) self.snake = Snake(Point(30, 30)) self.wall_same = Wall(Point(0, 0)) self.ground_same = Ground(Point(0, 0)) self.apple_same = Apple(Point(0, 0)) self.snake_same = Snake(Point(0, 0)) self.objects_diff = [getattr(self, name) for name in self.objects_name] self.objects_same = [getattr(self, name + "_same") for name in self.objects_name]
def _random_point(self, min_distance=constants.MIN_SPAWN_WALL_DISTANCE): """ Returns a random point on the board at least min_distance away from the sides. :param min_distance: int The minimum distance to the borders of the snake :return Point A position on the board at least min_distance away from the boarder """ return Point(x=np.random.randint(min_distance, self.width - min_distance), y=np.random.randint(min_distance, self.height - min_distance))
def test_contains(self): # Please note that we can't swap 'UP' and 'DOWN' or 'LEFT' and 'RIGHT' # Due to the restriction that the snake can not turn 180 degrees from the previous direction. snake_diff = Snake(Point(self.pos_start.x, self.pos_start.y + 5)) snake_diff.direction = self.start_direction for _ in range(4): snake_diff.step() self.assertEqual(False, self.snake in snake_diff, "A Faulty collision detected") snake_diff.step() self.assertEqual(True, self.snake in snake_diff, "Collision not detected") self.assertEqual(True, self.snake in self.snake, "Snake doesn't contain itself")
def setUp(self) -> None: self.pos_start = Point(50, 120) self.pos_diff = Point(50, 115) self.start_direction = "UP" self.snake = Snake(self.pos_start)