Beispiel #1
0
    def build_cycle(self, env: Environment) -> Optional[List[Vector]]:
        # Attempt to build the list of next actions
        head = env.snake.head()
        tail = env.snake.tail()
        # We build a cycle by building the longest path from the snakes
        # head to it's tail. If the snake is only 1 tile long, then we
        # make an 'adjustment' and choose a tile next to the head
        # as the target, essentially faking a tail.
        # This is necessary as our algorithm won't return any actions
        # if the from tile is the same as the target tile.
        if head == tail:
            if tail.x > 1:
                adjustment = Vector(-1, 0)
            else:
                adjustment = Vector(1, 0)
            tail = tail + adjustment
        built = self._bfsl.longest_path(env, head, tail,
                                        env.snake.action.vector)
        if built is None:
            return None

        # We've built the longest path from head to tail, but we need to
        # check that it covers all vectors.
        if len(built) != env.available_tiles_count():
            return None
        built.append(head)
        return built
Beispiel #2
0
 def prepare_training_environment(self,
                                  horizontal_pixels=Constants.ENV_WIDTH,
                                  vertical_pixels=Constants.ENV_HEIGHT):
     environment = Environment(width=horizontal_pixels,
                               height=vertical_pixels)
     environment.set_wall()
     environment.set_fruit()
     environment.set_snake()
     return environment
 def _should_search_vector(self, env: Environment, vector: Vector):
     t = env.tile_at(vector)
     # Snake can't move to a vector that would kill it
     if t == tile.WALL:
         return False
     if t == tile.SNAKE:
         return False
     return True
Beispiel #4
0
    def __init__(self, game_model: AbstractModel, fps: int,
                 horizontal_tiles: int, vertical_tiles: int, screen_width: int,
                 screen_height: int, score_logger: ScoreLogger, font: str,
                 screen_depth: int):
        self.clock = pygame.time.Clock()
        self.model = game_model
        self.fps = fps
        self.screen = pygame.display.set_mode((
            screen_width,
            screen_height,
        ), 0, screen_depth)
        self.surface = pygame.Surface(self.screen.get_size())
        self.screen_width = screen_width
        self.screen_height = screen_height
        self.horizontal_tiles = horizontal_tiles
        self.vertical_tiles = vertical_tiles
        self.tile_width = int(screen_width / horizontal_tiles)
        self.tile_height = int(screen_height / vertical_tiles)
        self._score_logger = score_logger
        self._font = font

        self.environment = Environment(width=self.horizontal_tiles,
                                       height=self.vertical_tiles)
        self.environment.init_wall()
        self.environment.init_fruit()
        self.environment.init_snake()

        self.screen_objects = []

        normalised_wall_vectors = self._normalise_vectors(
            self.environment.wall.get_vectors())
        self.wall = WallScreenObject(self, normalised_wall_vectors)
        self.screen_objects.append(self.wall)

        normalised_fruit_vectors = self._normalise_vectors(
            self.environment.fruit.get_vectors())
        self.fruit = FruitScreenObject(self, normalised_fruit_vectors)
        self.screen_objects.append(self.fruit)

        normalised_snake_vectors = self._normalise_vectors(
            self.environment.snake.get_vectors())
        self.snake = SnakeScreenObject(self, normalised_snake_vectors)
        self.screen_objects.append(self.snake)
 def _can_populate_vector(self, env: Environment, vector: Vector,
                          path: List[Vector], goal: Vector) -> bool:
     t = env.tile_at(vector)
     if t == tile.WALL:
         return False
     if t == tile.SNAKE:
         return False
     if vector in path:
         return False
     if vector == goal:
         return False
     return True
Beispiel #6
0
    def next_action(self, environment: Environment) -> act.Action:
        # We calculate the cycle one and iterate over it
        if not self._actions:
            cycle_vectors = self.build_cycle(environment)
            if not cycle_vectors:
                # If we're not able to build them, it usually means there
                # is no path to the fruit. Continue to any available position.
                if environment.tile_at(
                        environment.snake.head() +
                        environment.snake.action.vector) != tile.EMPTY:
                    return environment.random_action()
                return environment.snake.action
            cycle_action_vectors = to_direction_vectors(cycle_vectors)
            self._actions = act.vectors_to_action(cycle_action_vectors)
            self._i = 0

        # Keep looping over our list of actions
        next_action = self._actions[self._i]
        self._i += 1
        if self._i == len(self._actions):
            self._i = 0
        return next_action
Beispiel #7
0
class Game:
    def __init__(self, game_model: AbstractModel, fps: int,
                 horizontal_tiles: int, vertical_tiles: int, screen_width: int,
                 screen_height: int, score_logger: ScoreLogger, font: str,
                 screen_depth: int):
        self.clock = pygame.time.Clock()
        self.model = game_model
        self.fps = fps
        self.screen = pygame.display.set_mode((
            screen_width,
            screen_height,
        ), 0, screen_depth)
        self.surface = pygame.Surface(self.screen.get_size())
        self.screen_width = screen_width
        self.screen_height = screen_height
        self.horizontal_tiles = horizontal_tiles
        self.vertical_tiles = vertical_tiles
        self.tile_width = int(screen_width / horizontal_tiles)
        self.tile_height = int(screen_height / vertical_tiles)
        self._score_logger = score_logger
        self._font = font

        self.environment = Environment(width=self.horizontal_tiles,
                                       height=self.vertical_tiles)
        self.environment.init_wall()
        self.environment.init_fruit()
        self.environment.init_snake()

        self.screen_objects = []

        normalised_wall_vectors = self._normalise_vectors(
            self.environment.wall.get_vectors())
        self.wall = WallScreenObject(self, normalised_wall_vectors)
        self.screen_objects.append(self.wall)

        normalised_fruit_vectors = self._normalise_vectors(
            self.environment.fruit.get_vectors())
        self.fruit = FruitScreenObject(self, normalised_fruit_vectors)
        self.screen_objects.append(self.fruit)

        normalised_snake_vectors = self._normalise_vectors(
            self.environment.snake.get_vectors())
        self.snake = SnakeScreenObject(self, normalised_snake_vectors)
        self.screen_objects.append(self.snake)

    def tick(self) -> bool:
        continue_game = self._handle_user_input()
        if not continue_game:
            return False
        action = self.model.next_action(self.environment)
        reason = self.environment.step(action)
        if reason:
            print(f'died: {reason.reason}')
            self.snake_died()
        elif self.environment.won():
            print('won')
            self.snake_died()
        self._sync_screen_with_environment()
        self._draw_screen()
        self._display()

        self.clock.tick(self.fps)
        return True

    def snake_died(self):
        self._score_logger.log_score(self.model.short_name,
                                     self.environment.reward())
        self.model.reset()
        self.environment.init_snake()

    def draw_tile(self, surface: Surface, col: colour.Colour, vector: Vector):
        rect = pygame.Rect((vector.x, vector.y),
                           (self.tile_width, self.tile_height))
        pygame.draw.rect(surface, col, rect)

    def _draw_screen(self):
        self.surface.fill(colour.WHITE)
        for game_object in self.screen_objects:
            game_object.draw(self.surface)

        font = pygame.font.SysFont(self._font, int(self.tile_height / 1.3))
        score_text = font.render(str(self.environment.reward()), 1,
                                 colour.WHITE)
        score_text_rect = score_text.get_rect()
        score_text_rect.center = (self.screen_width / 2, self.tile_height / 2)
        self.surface.blit(score_text, score_text_rect)

        self.screen.blit(self.surface, (0, 0))

    def _sync_screen_with_environment(self):
        self.fruit.set_vectors(
            self._normalise_vectors(self.environment.fruit.get_vectors()))
        self.snake.set_vectors(
            self._normalise_vectors(self.environment.snake.get_vectors()))

    def _display(self):
        pygame.display.flip()
        pygame.display.update()

    def _normalise_vectors(self, vectors: List[Vector]) -> List[Vector]:
        return list(map(lambda x: self._normalise_vector(x), vectors))

    def _normalise_vector(self, vector: Vector) -> Vector:
        return Vector(vector.x * self.tile_width, vector.y * self.tile_height)

    def _handle_user_input(self) -> bool:
        for event in pygame.event.get():
            if event.type == QUIT:
                return False
            elif event.type == KEYDOWN:
                self.model.user_input(event)
        return True