Пример #1
0
    def cross_over(self, agent):
        """
        "Breed" with another agent to produce a "child"

        :param agent: the other parent agent
        :return: "child" agent
        """
        child = GeneticAgentComplete()
        # Choose weight randomly from the parents
        child.weight_height = self.weight_height if random.getrandbits(
            1) else agent.weight_height
        child.weight_holes = self.weight_holes if random.getrandbits(
            1) else agent.weight_holes
        child.weight_bumpiness = self.weight_bumpiness if random.getrandbits(
            1) else agent.weight_bumpiness
        child.weight_line_clear = self.weight_line_clear if random.getrandbits(
            1) else agent.weight_line_clear

        # Randomly mutate weights
        if random.random() < MUTATION_RATE:
            child.weight_height = TUtils.random_weight()
        if random.random() < MUTATION_RATE:
            child.weight_holes = TUtils.random_weight()
        if random.random() < MUTATION_RATE:
            child.weight_bumpiness = TUtils.random_weight()
        if random.random() < MUTATION_RATE:
            child.weight_line_clear = TUtils.random_weight()

        # Return completed child model
        return child
Пример #2
0
 def draw_tiles(self, matrix, offsets=(0, 0), outline_only=False):
     for y, row in enumerate(matrix):
         for x, val in enumerate(row):
             if val == 0:
                 continue
             coord_x = (offsets[0] + x) * self.grid_size
             coord_y = (offsets[1] + y) * self.grid_size
             # Draw rectangle
             if not outline_only:
                 pygame.draw.rect(self.screen,
                                  TUtils.get_color_tuple(COLORS.get("TILE_" + TILES[val - 1])),
                                  (coord_x, coord_y, self.grid_size, self.grid_size))
                 pygame.draw.rect(self.screen,
                                  TUtils.get_color_tuple(COLORS.get("BACKGROUND_BLACK")),
                                  (coord_x, coord_y, self.grid_size, self.grid_size), 1)
                 # Draw highlight triangle
                 offset = int(self.grid_size / 10)
                 pygame.draw.polygon(self.screen, TUtils.get_color_tuple(COLORS.get("TRIANGLE_GRAY")),
                                     ((coord_x + offset, coord_y + offset),
                                      (coord_x + 3 * offset, coord_y + offset),
                                      (coord_x + offset, coord_y + 3 * offset)))
             else:
                 # Outline-only for prediction location
                 pygame.draw.rect(self.screen,
                                  TUtils.get_color_tuple(COLORS.get("TILE_" + TILES[val - 1])),
                                  (coord_x + 1, coord_y + 1, self.grid_size - 2, self.grid_size - 2), 1)
Пример #3
0
    def calculate_actions(self, board, current_tile, next_tile,
                          offsets) -> List[int]:
        """
        Calculate action sequence based on the agent's prediction

        :param board: the current Tetris board
        :param current_tile: the current Tetris tile
        :param next_tile: the next Tetris tile (swappable)
        :param offsets: the current Tetris tile's coordinates
        :return: list of actions (integers) that should be executed in order
        """
        best_fitness = -9999
        best_tile_index = -1
        best_rotation = -1
        best_x = -1

        tiles = [current_tile, next_tile]
        # 2 tiles: current and next (swappable)
        for tile_index in range(len(tiles)):
            tile = tiles[tile_index]
            # Rotation: 0-3 times (4x is the same as 0x)
            for rotation_count in range(0, 4):
                # X movement
                for x in range(0, GRID_COL_COUNT - len(tile[0]) + 1):
                    new_board = TUtils.get_future_board_with_tile(
                        board, tile, (x, offsets[1]), True)
                    fitness = self.get_fitness(new_board)
                    if fitness > best_fitness:
                        best_fitness = fitness
                        best_tile_index = tile_index
                        best_rotation = rotation_count
                        best_x = x
                # Rotate tile (prep for next iteration)
                tile = TUtils.get_rotated_tile(tile)

        ##################################################################################
        # Obtained best stats, now convert them into sequences of actions
        # Action = index of { NOTHING, L, R, 2L, 2R, ROTATE, SWAP, FAST_FALL, INSTA_FALL }
        actions = []
        if tiles[best_tile_index] != current_tile:
            actions.append(ACTIONS.index("SWAP"))
        for _ in range(best_rotation):
            actions.append(ACTIONS.index("ROTATE"))
        temp_x = offsets[0]
        while temp_x != best_x:
            direction = 1 if temp_x < best_x else -1
            magnitude = 1 if abs(temp_x - best_x) == 1 else 2
            temp_x += direction * magnitude
            actions.append(
                ACTIONS.index(("" if magnitude == 1 else "2") +
                              ("R" if direction == 1 else "L")))
        actions.append(ACTIONS.index("INSTA_FALL"))
        return actions
Пример #4
0
 def rotate_tile(self, pseudo=False):
     if not self.active or self.paused:
         return False, self.tile_x, self.tile_shape
     new_shape = TUtils.get_rotated_tile(self.tile_shape)
     temp_x = self.tile_x
     # Out of range detection
     if self.tile_x + len(new_shape[0]) > GRID_COL_COUNT:
         temp_x = GRID_COL_COUNT - len(new_shape[0])
     # If collide, disallow rotation
     if TUtils.check_collision(self.board, new_shape, (temp_x, self.tile_y)):
         return False, self.tile_x, self.tile_shape
     if not pseudo:
         self.tile_x = temp_x
         self.tile_shape = new_shape
     return True, temp_x, new_shape
Пример #5
0
    def rotate_tile(self):
        """ Rotate current tile by 90 degrees """
        new_tile_shape = TUtils.get_rotated_tile(self.tile_shape)
        new_x = self.tile_x
        # Out of range detection
        if self.tile_x + len(new_tile_shape[0]) > GRID_COL_COUNT:
            new_x = GRID_COL_COUNT - len(new_tile_shape[0])

        # If collide, disallow rotation
        if TUtils.check_collision(self.board, new_tile_shape,
                                  (new_x, self.tile_y)):
            return
        # Apply tile properties
        self.tile_x = new_x
        self.tile_shape = new_tile_shape
Пример #6
0
 def get_fitness(self, board):
     """ Utility method to calculate fitness score """
     score = 0
     # Check if the board has any completed rows
     future_board, clear_count = TUtils.get_board_and_lines_cleared(board)
     # Calculate the line-clear score and apply weights
     score += self.weight_line_clear * clear_count
     # Calculate the aggregate height of future board and apply weights
     score += self.weight_height * sum(TUtils.get_col_heights(future_board))
     # Calculate the holes score and apply weights
     score += self.weight_holes * TUtils.get_hole_count(future_board)
     # Calculate the "smoothness" score and apply weights
     score += self.weight_bumpiness * TUtils.get_bumpiness(future_board)
     # Return the final score
     return score
Пример #7
0
    def swap_tile(self, pseudo=False):
        if not self.active or self.paused:
            return False, (self.tile_x, self.tile_y), self.tile_shape
        new_tile = self.get_next_tile(False)
        new_tile_shape = TILE_SHAPES.get(new_tile)[:]
        temp_x, temp_y = self.tile_x, self.tile_y

        # Out of range detection
        if temp_x + len(self.tile_shape[0]) > GRID_COL_COUNT:
            temp_x = GRID_COL_COUNT - len(self.tile_shape[0])
        if temp_y + len(self.tile_shape) > GRID_ROW_COUNT:
            temp_y = GRID_ROW_COUNT - len(self.tile_shape)

        # If collide, disallow swapping
        if TUtils.check_collision(self.board, new_tile_shape, (temp_x, temp_y)):
            return False, (self.tile_x, self.tile_y), self.tile_shape

        if not pseudo:
            # Swap next tile with current tile
            self.get_next_tile(True)
            self.tile_bank.insert(0, self.tile)
            # Apply stats
            self.tile = new_tile
            self.tile_shape = new_tile_shape
            self.tile_x, self.tile_y = temp_x, temp_y
        return True, (temp_x, temp_y), new_tile_shape
Пример #8
0
    def calculate_scores(self):
        score_count = 0
        row = 0
        while True:
            if row >= len(self.board):
                break
            if 0 in self.board[row]:
                row += 1
                continue
            # Delete the "filled" row
            del self.board[row]
            # Insert empty row at top
            self.board.insert(0, [0] * GRID_COL_COUNT)
            score_count += 1
        # Calculate fitness score
        self.fitness = TUtils.get_fitness_score(self.board)
        # If cleared nothing, early return
        if score_count == 0:
            return
        # Calculate total score based on algorithm
        total_score = MULTI_SCORE_ALGORITHM(score_count)
        # Callback
        for callback in self.on_score_changed_callbacks:
            callback(self.score, self.score + total_score)

        self.score += total_score
        self.lines += score_count
        self.log("Cleared " + str(score_count) + " rows with score " + str(total_score), 3)
        # Calculate game speed
        pygame.time.set_timer(pygame.USEREVENT + 1, SPEED_DEFAULT if not SPEED_SCALE_ENABLED else int(
            max(50, SPEED_DEFAULT - self.score * SPEED_SCALE)))
Пример #9
0
 def step(self, action=0, use_fitness=False):
     # Update UI
     if HAS_DISPLAY:
         pygame.event.get()
     # Obtain previous score
     previous_fitness = self.fitness
     previous_score = self.score
     # Move action
     if action in [1, 2, 3, 4]:
         self.move_tile((-1 if action in [1, 3] else 1) * (1 if action in [1, 2] else 2))
     # Rotate
     elif action == 5:
         self.rotate_tile()
     # Swap
     elif action == 6:
         self.swap_tile()
     # Fast fall / Insta-fall
     elif action in [7, 8]:
         self.drop(instant=(action == 8))
     # Continue by 1 step
     self.drop()
     # >> Returns: board matrix (state), score change (reward), is-game-over (done), next piece (extras)
     measurement = self.score - previous_score
     if use_fitness:
         measurement = self.fitness - previous_fitness
     board = TUtils.get_board_with_tile(self.board, self.tile_shape, (self.tile_x, self.tile_y), True)
     return board, measurement, not self.active, self.get_next_tile()
Пример #10
0
def draw_tiles(screen,
               matrix,
               offsets=(0, 0),
               global_offsets=(0, 0),
               outline_only=False):
    """
    Draw tiles from a matrix (utility method)

    :param screen: the screen to draw on
    :param matrix: the matrix to draw
    :param offsets: matrix index offsets
    :param global_offsets: global pixel offsets
    :param outline_only: draw prediction outline only?
    """
    for y, row in enumerate(matrix):
        for x, val in enumerate(row):
            if val == 0:
                continue
            coord_x = global_offsets[0] + (offsets[0] + x) * GAME_GRID_SIZE
            coord_y = global_offsets[1] + (offsets[1] + y) * GAME_GRID_SIZE
            # Draw rectangle
            if not outline_only:
                pygame.draw.rect(
                    screen,
                    TUtils.get_color_tuple(COLORS.get("TILE_" +
                                                      TILES[val - 1])),
                    (coord_x, coord_y, GAME_GRID_SIZE, GAME_GRID_SIZE))
                pygame.draw.rect(
                    screen,
                    TUtils.get_color_tuple(COLORS.get("BACKGROUND_BLACK")),
                    (coord_x, coord_y, GAME_GRID_SIZE, GAME_GRID_SIZE), 1)
                # Draw highlight triangle
                offset = int(GAME_GRID_SIZE / 10)
                pygame.draw.polygon(
                    screen,
                    TUtils.get_color_tuple(COLORS.get("TRIANGLE_GRAY")),
                    ((coord_x + offset, coord_y + offset),
                     (coord_x + 3 * offset, coord_y + offset),
                     (coord_x + offset, coord_y + 3 * offset)))
            else:
                # Outline-only for prediction location
                pygame.draw.rect(
                    screen,
                    TUtils.get_color_tuple(COLORS.get("TILE_" +
                                                      TILES[val - 1])),
                    (coord_x + 1, coord_y + 1, GAME_GRID_SIZE - 2,
                     GAME_GRID_SIZE - 2), 1)
Пример #11
0
 def draw_next_tile(self, offsets):
     size = int(self.grid_size * 0.75)
     for y, row in enumerate(TILE_SHAPES.get(self.get_next_tile())):
         for x, val in enumerate(row):
             if val == 0:
                 continue
             coord_x = offsets[0] + x * size
             coord_y = offsets[1] + y * size
             # Draw rectangle
             pygame.draw.rect(self.screen, TUtils.get_color_tuple(COLORS.get("TILE_" + TILES[val - 1])), (coord_x, coord_y, size, size))
             pygame.draw.rect(self.screen, TUtils.get_color_tuple(COLORS.get("BACKGROUND_BLACK")), (coord_x, coord_y, size, size), 1)
             # Draw highlight triangle
             offset = int(size / 10)
             pygame.draw.polygon(self.screen, TUtils.get_color_tuple(COLORS.get("TRIANGLE_GRAY")),
                                 ((coord_x + offset, coord_y + offset),
                                  (coord_x + 3 * offset, coord_y + offset),
                                  (coord_x + offset, coord_y + 3 * offset)))
Пример #12
0
    def spawn_tile(self):
        self.tile = self.get_next_tile(pop=True)
        self.tile_shape = TILE_SHAPES.get(self.tile)[:]
        self.tile_x = int(GRID_COL_COUNT / 2 - len(self.tile_shape[0]) / 2)
        self.tile_y = 0

        self.log("Spawning a new " + self.tile + " tile!", 1)
        if TUtils.check_collision(self.board, self.tile_shape, (self.tile_x, self.tile_y)):
            self.active = False
            self.paused = True
Пример #13
0
    def drop(self, instant=False):
        if not self.active or self.paused:
            return
        # Drop the tile
        if instant:
            destination = TUtils.get_effective_height(self.board, self.tile_shape, (self.tile_x, self.tile_y))
            self.score += PER_STEP_SCORE_GAIN * (destination - self.tile_y)
            self.tile_y = destination + 1
        else:
            self.tile_y += 1
            self.score += PER_STEP_SCORE_GAIN

        # If no collision happen, skip
        if not TUtils.check_collision(self.board, self.tile_shape, (self.tile_x, self.tile_y)) and not instant:
            return
        # Collided! Add tile to board, spawn new tile, and calculate scores
        self.add_tile_to_board()
        self.calculate_scores()
        self.spawn_tile()
Пример #14
0
 def move_tile(self, delta):
     if not self.active or self.paused:
         return
     new_x = self.tile_x + delta
     # Clamping
     new_x = max(0, min(new_x, GRID_COL_COUNT - len(self.tile_shape[0])))
     # Cannot "override" blocks AKA cannot move when it is blocked
     if TUtils.check_collision(self.board, self.tile_shape, (new_x, self.tile_y)):
         return
     self.tile_x = new_x
Пример #15
0
def draw_board(screen, tetris: Tetris, x_offset: int, y_offset: int):
    """
    Draws one Tetris board with offsets, called by draw() multiple times per frame

    :param screen: the screen to draw on
    :param tetris: Tetris instance
    :param x_offset: X offset (starting X)
    :param y_offset: Y offset (starting Y)
    """
    # [0] Striped background layer
    for a in range(GRID_COL_COUNT):
        color = TUtils.get_color_tuple(
            COLORS.get("BACKGROUND_DARK" if a %
                       2 == 0 else "BACKGROUND_LIGHT"))
        pygame.draw.rect(screen, color,
                         (x_offset + a * GAME_GRID_SIZE, y_offset,
                          GAME_GRID_SIZE, GAME_HEIGHT))

    # [1] Board tiles
    draw_tiles(screen, tetris.board, global_offsets=(x_offset, y_offset))
    # [1] Current tile
    draw_tiles(screen,
               tetris.tile_shape,
               offsets=(tetris.tile_x, tetris.tile_y),
               global_offsets=(x_offset, y_offset))

    # [2] Game over graphics
    if tetris.game_over:
        color = TUtils.get_color_tuple(COLORS.get("BACKGROUND_BLACK"))
        ratio = 0.9
        pygame.draw.rect(screen, color,
                         (x_offset, y_offset + (GAME_HEIGHT * ratio) / 2,
                          GAME_WIDTH, GAME_HEIGHT * (1 - ratio)))

        message = "GAME OVER"
        color = TUtils.get_color_tuple(COLORS.get("RED"))
        text_image = pygame.font.SysFont(FONT_NAME, GAME_WIDTH // 6).render(
            message, False, color)
        rect = text_image.get_rect()
        screen.blit(text_image,
                    (x_offset + (GAME_WIDTH - rect.width) / 2, y_offset +
                     (GAME_HEIGHT - rect.height) / 2))
Пример #16
0
 def pseudo_step(self):
     scores = [0] * 9
     curr_score = TUtils.get_fitness_score(TUtils.get_future_board_with_tile(self.board, self.tile, self.tile_x))
     # Wait action
     for action in [0, 7, 8]:
         scores[action] = curr_score
     # Move left once
     scores[1] = curr_score if TUtils.check_collision(self.board, self.tile_shape, (self.tile_x - 1, self.tile_y)) else TUtils.get_fitness_score(TUtils.get_future_board_with_tile(self.board, self.tile, self.tile_x - 1))
     # Move right once
     scores[2] = curr_score if TUtils.check_collision(self.board, self.tile_shape, (self.tile_x + 1, self.tile_y)) else TUtils.get_fitness_score(TUtils.get_future_board_with_tile(self.board, self.tile, self.tile_x + 1))
     # Move left twice
     scores[3] = scores[1] if TUtils.check_collision(self.board, self.tile_shape, (self.tile_x - 2, self.tile_y)) else TUtils.get_fitness_score(TUtils.get_future_board_with_tile(self.board, self.tile, self.tile_x - 2))
     # Move right twice
     scores[4] = scores[2] if TUtils.check_collision(self.board, self.tile_shape, (self.tile_x + 2, self.tile_y)) else TUtils.get_fitness_score(TUtils.get_future_board_with_tile(self.board, self.tile, self.tile_x + 2))
     # Rotate
     success, offset_x, tile_shape = self.rotate_tile(pseudo=True)
     scores[5] = curr_score if not success else TUtils.get_fitness_score(TUtils.get_future_board_with_tile(self.board, tile_shape, offset_x))
     # Swap
     success, offsets, tile_shape = self.swap_tile(pseudo=True)
     scores[6] = curr_score if not success else TUtils.get_fitness_score(TUtils.get_future_board_with_tile(self.board, tile_shape, offsets[0]))
     return scores
Пример #17
0
    def spawn_tile(self) -> bool:
        """
        Spawns a new tile from the tile pool
        :return: whether the game is over
        """
        self.current_tile = self.get_next_tile(pop=True)
        self.tile_shape = TILE_SHAPES[self.current_tile][:]
        self.tile_x = int(GRID_COL_COUNT / 2 - len(self.tile_shape[0]) / 2)
        self.tile_y = 0

        # Game over check: game over if new tile collides with existing blocks
        return TUtils.check_collision(self.board, self.tile_shape,
                                      (self.tile_x, self.tile_y))
Пример #18
0
    def drop_tile(self, instant=False):
        """
        Drop the current tile by 1 grid; if <INSTANT>, then drop all the way

        :param instant: whether to drop all the way
        """
        if instant:
            # Drop the tile until it collides with existing block(s)
            new_y = TUtils.get_effective_height(self.board, self.tile_shape,
                                                (self.tile_x, self.tile_y))
            self.tile_y = new_y + 1
            self.score += PER_STEP_SCORE_GAIN * (new_y - self.tile_y)
        else:
            # Drop the tile by 1 grid
            self.tile_y += 1
            self.score += PER_STEP_SCORE_GAIN

        # If doesn't collide, skip next step
        if not instant and not TUtils.check_collision(
                self.board, self.tile_shape, (self.tile_x, self.tile_y)):
            return
        # Trigger collision event
        self.on_tile_collision()
Пример #19
0
    def move_tile(self, delta: int):
        """
        Move current tile to the right by <DISTANCE> grids

        :param delta: one of [-2, -1, 1, 2]; negative values will move to the left
        """
        assert delta in [-2, -1, 1, 2], "Invalid move distance"
        new_x = self.tile_x + delta
        new_x = max(0,
                    min(new_x,
                        GRID_COL_COUNT - len(self.tile_shape[0])))  # clamping

        # Cannot "override" blocks AKA cannot clip into existing blocks
        if TUtils.check_collision(self.board, self.tile_shape,
                                  (new_x, self.tile_y)):
            return
        # Apply tile properties
        self.tile_x = new_x
Пример #20
0
def highlight(screen, index: int, mode: int):
    """
    Highlight a certain Tetris grid

    :param screen: the screen to draw on
    :param index: index of Tetris grid to highlight
    :param mode: 0/1, 0 = best, 1 = previous best
    """
    game_x = index % COL_COUNT
    game_y = index // COL_COUNT
    color = TUtils.get_color_tuple(
        COLORS.get("HIGHLIGHT_GREEN" if mode == 0 else "HIGHLIGHT_RED"))

    if mode == 1:
        # Draw previous best (thick border)
        temp_x = (GAME_WIDTH + PADDING) * game_x
        temp_y = (GAME_HEIGHT + PADDING) * game_y
        pygame.draw.rect(screen, color,
                         (temp_x, temp_y, GAME_WIDTH + PADDING * 2, PADDING))
        pygame.draw.rect(screen, color,
                         (temp_x, temp_y, PADDING, GAME_HEIGHT + PADDING * 2))
        temp_x = (GAME_WIDTH + PADDING) * (game_x + 1) + PADDING - 1
        temp_y = (GAME_HEIGHT + PADDING) * (game_y + 1) + PADDING - 1
        pygame.draw.rect(screen, color,
                         (temp_x, temp_y, -GAME_WIDTH - PADDING, -PADDING))
        pygame.draw.rect(screen, color,
                         (temp_x, temp_y, -PADDING, -GAME_HEIGHT - PADDING))
    elif mode == 0:
        # Draw current best (thin border)
        temp_x = (GAME_WIDTH + PADDING) * game_x + PADDING / 2
        temp_y = (GAME_HEIGHT + PADDING) * game_y + PADDING / 2
        pygame.draw.rect(screen, color,
                         (temp_x, temp_y, GAME_WIDTH + PADDING, PADDING / 2))
        pygame.draw.rect(screen, color,
                         (temp_x, temp_y, PADDING / 2, GAME_HEIGHT + PADDING))
        temp_x = (GAME_WIDTH + PADDING) * (game_x + 1) + PADDING / 2 - 1
        temp_y = (GAME_HEIGHT + PADDING) * (game_y + 1) + PADDING / 2 - 1
        pygame.draw.rect(
            screen, color,
            (temp_x, temp_y, -GAME_WIDTH - PADDING + 2, -PADDING / 2))
        pygame.draw.rect(
            screen, color,
            (temp_x, temp_y, -PADDING / 2, -GAME_HEIGHT - PADDING + 2))
Пример #21
0
    def get_fitness(self, board):
        """ Utility method to calculate fitness score """
        score = 0
        # Check if the board has any completed rows
        future_board, rows_cleared = TUtils.get_board_and_lines_cleared(board)
        # TODO: calculate fitness score for the board
        # TODO: calculate the aggregate height of the board and apply weights
        pass

        # TODO: count the holes and apply weights
        pass

        # TODO: calculate the "bumpiness" score and apply weights
        pass

        # TODO: Calculate the line-clear score and apply weights
        pass

        # Return the final score
        return score
Пример #22
0
    def swap_tile(self):
        """ Swaps current tile with the future one """
        # Get next tile without popping (swapping could fail)
        new_tile = self.get_next_tile(pop=False)
        new_tile_shape = TILE_SHAPES[new_tile][:]  # clone tile shape
        temp_x, temp_y = self.tile_x, self.tile_y

        # Out of range detection
        if temp_x + len(self.tile_shape[0]) > GRID_COL_COUNT:
            temp_x = GRID_COL_COUNT - len(self.tile_shape[0])
        if temp_y + len(self.tile_shape) > GRID_ROW_COUNT:
            temp_y = GRID_ROW_COUNT - len(self.tile_shape)

        # If collide, disallow swapping
        if TUtils.check_collision(self.board, new_tile_shape,
                                  (temp_x, temp_y)):
            return
        # Put current tile as the next tile
        self.tile_pool[0] = self.current_tile
        # Apply tile properties
        self.current_tile = new_tile
        self.tile_shape = new_tile_shape
        self.tile_x, self.tile_y = temp_x, temp_y
Пример #23
0
    # Utility Functions #
    #####################
    def get_next_tile(self, pop=False):
        """ Obtains the next tile from the tile pool """
        if not self.tile_pool:
            self.generate_tile_pool()
        return self.tile_pool[0] if not pop else self.tile_pool.pop(0)


# Test game board using step action
if __name__ == "__main__":
    tetris = Tetris()
    while True:
        if not tetris.game_over:
            # Print game board
            TUtils.print_board(
                TUtils.get_board_with_tile(tetris.board, tetris.tile_shape,
                                           (tetris.tile_x, tetris.tile_y)))

        # Get user input (q = quit, r = reset)
        # 0-8 = actions
        message = input("Next action (0-8): ")
        if message == "q":
            break
        elif message == "r":
            tetris.reset_game()
            continue

        # Step
        tetris.step(int(message))
Пример #24
0
def draw(screen):
    """ Called by the update() function every frame, draws the PyGame GUI """
    # Background layer
    screen.fill(TUtils.get_color_tuple(COLORS.get("BACKGROUND_BLACK")))

    # Draw Tetris boards
    curr_x, curr_y = PADDING, PADDING
    for x in range(ROW_COUNT):
        for y in range(COL_COUNT):
            draw_board(screen, TETRIS_GAMES[x * COL_COUNT + y], curr_x, curr_y)
            curr_x += GAME_WIDTH + PADDING
        curr_x = PADDING
        curr_y += GAME_HEIGHT + PADDING

    # Draw statistics
    # Realign starting point to statistics bar
    curr_x, curr_y = GAME_WIDTH * COL_COUNT + PADDING * (COL_COUNT +
                                                         1), PADDING

    # Draw title
    draw_text("Tetris", screen, (curr_x, curr_y), font_size=48)
    curr_y += 60
    # Draw statistics
    best_indexes, best_score = get_high_score()
    draw_text(f"High Score: {best_score:.1f}", screen, (curr_x, curr_y))
    curr_y += 20
    draw_text(f"Best Agent: {SEP.join(map(str, best_indexes))}", screen,
              (curr_x, curr_y))
    curr_y += 20

    # Draw genetics
    if gen_generation > -1:
        curr_y += 20
        draw_text(f"Generation #{gen_generation}",
                  screen, (curr_x, curr_y),
                  font_size=24)
        curr_y += 35
        draw_text(f"Time Limit: {time_elapsed}/{time_limit}", screen,
                  (curr_x, curr_y))
        curr_y += 20

        survivor = len([a for a in TETRIS_GAMES if not a.game_over])
        draw_text(
            f"Survivors: {survivor}/{GAME_COUNT} ({survivor / GAME_COUNT * 100:.1f}%)",
            screen, (curr_x, curr_y))
        curr_y += 20
        draw_text(f"Prev H.Score: {gen_previous_best_score:.1f}", screen,
                  (curr_x, curr_y))
        curr_y += 20
        draw_text(f"All Time H.S: {gen_top_score:.1f}", screen,
                  (curr_x, curr_y))
        curr_y += 40

        # Display selected agent
        mouse_x, mouse_y = pygame.mouse.get_pos()
        grid_x, grid_y = mouse_x // (GAME_WIDTH + PADDING), mouse_y // (
            GAME_HEIGHT + PADDING)
        selected = grid_y * COL_COUNT + grid_x

        agent_index = -1
        highlight_selected = False
        if selected < GAME_COUNT and mouse_x < (GAME_WIDTH +
                                                PADDING) * COL_COUNT:
            agent_index = selected
            highlight_selected = True
        elif len(best_indexes) > 0:
            agent_index = best_indexes[0]

        if agent_index != -1:
            draw_text(f"Agent #{agent_index}:",
                      screen, (curr_x, curr_y),
                      font_size=24)
            curr_y += 35
            draw_text(
                f">> Agg Height: {AGENTS[agent_index].weight_height:.1f}",
                screen, (curr_x, curr_y))
            curr_y += 20
            draw_text(f">> Hole Count: {AGENTS[agent_index].weight_holes:.1f}",
                      screen, (curr_x, curr_y))
            curr_y += 20
            draw_text(
                f">> Bumpiness:  {AGENTS[agent_index].weight_bumpiness:.1f}",
                screen, (curr_x, curr_y))
            curr_y += 20
            draw_text(
                f">> Line Clear: {AGENTS[agent_index].weight_line_clear:.1f}",
                screen, (curr_x, curr_y))
            curr_y += 20
            if highlight_selected:
                highlight(screen, selected, mode=1)
            else:
                # Highlight current best(s)
                for a in best_indexes:
                    highlight(screen, a, mode=0)

        # Update display
        pygame.display.update()
Пример #25
0
 def print_board(self, flattened=False):
     TUtils.print_board(
         TUtils.get_board_with_tile(self.board, self.tile_shape, (self.tile_x, self.tile_y), flattened))
Пример #26
0
def draw_text(message: str, screen, offsets, font_size=16, color="WHITE"):
    """ Draws a line of text at the specified offsets """
    text_image = pygame.font.SysFont(FONT_NAME, font_size).render(
        message, False, TUtils.get_color_tuple(COLORS.get(color)))
    screen.blit(text_image, offsets)
Пример #27
0
    def draw(self):
        # Background layer
        self.screen.fill(TUtils.get_color_tuple(COLORS.get("BACKGROUND_BLACK")))

        ################
        # Tetris Board #
        ################
        # Layered background layer
        for a in range(GRID_COL_COUNT):
            color = TUtils.get_color_tuple(COLORS.get("BACKGROUND_DARK" if a % 2 == 0 else "BACKGROUND_LIGHT"))
            pygame.draw.rect(self.screen, color,
                             (a * self.grid_size, 0, self.grid_size, SCREEN_HEIGHT))  # x, y, width, height

        # Tetris (tile) layer
        # Draw board first
        self.draw_tiles(self.board)
        # Draw hypothesized tile
        if DISPLAY_PREDICTION:
            self.draw_tiles(self.tile_shape, (
                self.tile_x, TUtils.get_effective_height(self.board, self.tile_shape, (self.tile_x, self.tile_y))),
                            True)
        # Draw current tile
        self.draw_tiles(self.tile_shape, (self.tile_x, self.tile_y))

        #################
        # Message Board #
        #################
        # Coordinates calculations
        margin = 20  # 20 pixels margin
        text_x_start = GRID_COL_COUNT * self.grid_size + margin
        text_y_start = margin

        # Title
        message = MESSAGES.get("TITLE")
        if not self.active:
            message = "Game Over"
        elif self.paused:
            message = "= PAUSED ="
        text_image = pygame.font.SysFont(FONT_NAME, 32).render(message, False, TUtils.get_color_tuple(COLORS.get("WHITE")))
        self.screen.blit(text_image, (text_x_start, text_y_start))
        text_y_start = 60

        # Controls
        for msg in MESSAGES.get("CONTROLS").split("\n"):
            text_image = pygame.font.SysFont(FONT_NAME, 16).render(msg, False, TUtils.get_color_tuple(COLORS.get("WHITE")))
            self.screen.blit(text_image, (text_x_start, text_y_start))
            text_y_start += 20
        text_y_start += 10

        # Score
        text_image = pygame.font.SysFont(FONT_NAME, 16).render(MESSAGES.get("SCORE").format(self.score, self.lines), False, TUtils.get_color_tuple(COLORS.get("WHITE")))
        self.screen.blit(text_image, (text_x_start, text_y_start))
        text_y_start += 20

        # High Score
        high_score = self.score if self.score > self.high_score else self.high_score
        high_score_lines = self.lines if self.lines > self.high_score_lines else self.high_score_lines
        text_image = pygame.font.SysFont(FONT_NAME, 16).render(MESSAGES.get("HIGH_SCORE").format(high_score, high_score_lines), False, TUtils.get_color_tuple(COLORS.get("WHITE")))
        self.screen.blit(text_image, (text_x_start, text_y_start))
        text_y_start += 20

        # Fitness score
        text_image = pygame.font.SysFont(FONT_NAME, 16).render(MESSAGES.get("FITNESS").format(self.fitness), False, TUtils.get_color_tuple(COLORS.get("WHITE")))
        self.screen.blit(text_image, (text_x_start, text_y_start))
        text_y_start += 20

        # Speed
        speed = SPEED_DEFAULT if not SPEED_SCALE_ENABLED else int(max(50, SPEED_DEFAULT - self.score * SPEED_SCALE))
        text_image = pygame.font.SysFont(FONT_NAME, 16).render(MESSAGES.get("SPEED").format(speed), False, TUtils.get_color_tuple(COLORS.get("WHITE")))
        self.screen.blit(text_image, (text_x_start, text_y_start))
        text_y_start += 20

        # Next tile
        text_image = pygame.font.SysFont(FONT_NAME, 16).render(MESSAGES.get("NEXT_TILE").format(self.get_next_tile()), False, TUtils.get_color_tuple(COLORS.get("WHITE")))
        self.screen.blit(text_image, (text_x_start, text_y_start))
        text_y_start += 20

        self.draw_next_tile((text_x_start, text_y_start))
        text_y_start += 60

        pygame.display.update()