Ejemplo n.º 1
0
 def any_ships_alive(self, locations_grid: GameGrid,
                     opponents_guesses_grid: GameGrid):
     for row_idx in range(self.num_rows):
         for col_idx in range(self.num_cols):
             if (locations_grid.read_grid(row_idx,
                                          col_idx) != SHIP_LOCATION_EMPTY
                     and opponents_guesses_grid.read_grid(
                         row_idx, col_idx) != LOCATION_GUESS_HIT):
                 return True
     return False
Ejemplo n.º 2
0
 def check_ship_alive(self, locations_grid: GameGrid,
                      opponents_guesses_grid: GameGrid, ship_value):
     assert ship_value != SHIP_LOCATION_EMPTY
     for row_idx in range(self.num_rows):
         for col_idx in range(self.num_cols):
             if (locations_grid.read_grid(row_idx, col_idx) == ship_value
                     and opponents_guesses_grid.read_grid(
                         row_idx, col_idx) != LOCATION_GUESS_HIT):
                 return True
     return False
Ejemplo n.º 3
0
 def clear_ship_placement(self, locations_grid: GameGrid, ship_value,
                          ship_dims: Tuple[int, int]):
     print(
         f"clearing ship placement: value = {ship_value}, dims = {ship_dims}"
     )
     for row_idx in range(self.num_rows):
         for col_idx in range(self.num_cols):
             if locations_grid.read_grid(row_idx, col_idx) == ship_value:
                 locations_grid.update_grid(row_idx,
                                            col_idx,
                                            new_value=SHIP_LOCATION_EMPTY)
Ejemplo n.º 4
0
    def _is_valid_ship_placement(self, ship_locations_grid: GameGrid,
                                 ship_value, ship_dims: Tuple[int, int]):
        # ship dimensions must not be 0
        assert ship_dims[0] > 0 and ship_dims[1] > 0

        # make sure the ship is located, and the dimensions match
        # 1. check the count of locations vs. dimensions
        # 2. check the bounds of dimensions (min and max, x and y)
        min_row_idx, max_row_idx = None, None
        min_col_idx, max_col_idx = None, None
        count_ship_value = 0
        for row_idx in range(self.num_rows):
            for col_idx in range(self.num_cols):
                if ship_locations_grid.read_grid(row_idx,
                                                 col_idx) == ship_value:
                    count_ship_value += 1
                    if min_row_idx is None or row_idx < min_row_idx:
                        min_row_idx = row_idx
                    if max_row_idx is None or row_idx > max_row_idx:
                        max_row_idx = row_idx
                    if min_col_idx is None or col_idx < min_col_idx:
                        min_col_idx = col_idx
                    if max_col_idx is None or col_idx > max_col_idx:
                        max_col_idx = col_idx

        # make sure the correct number of squares are labeled as the ship and the dimension boundaries match
        return ship_dims[0] * ship_dims[1] == count_ship_value and (
            (max_row_idx - min_row_idx + 1 == ship_dims[0]
             and max_col_idx - min_col_idx + 1 == ship_dims[1]) or
            (max_row_idx - min_row_idx + 1 == ship_dims[1]
             and max_col_idx - min_col_idx + 1 == ship_dims[0]))
Ejemplo n.º 5
0
    def get_move(self, current_grid : GameGrid, current_state):
        available_moves = [move for move in self._moves_list if current_grid.canMove(move, current_state)]

        # self._logger.debug('From state %s : available moves : %s', current_state, available_moves)
        if len(available_moves) == 1:
            # self._logger.debug("One move available : %s", available_moves[0])
            return available_moves[0]       # Don't waste time running AI
        if len(available_moves) == 0:
            # self._logger.debug("No move available.")
            return self._moves_list[0]      # whatever, it wont't move !

        if (self.epsilon > 0) and (random.uniform(0, 1) < self.epsilon):
            self._logger.debug("Randomly choose move")
            return random.choice(available_moves)

        current_q_val = self.q_values.iloc[current_state, :][available_moves]

        max_val = current_q_val.max()
        optimal_moves = current_q_val[current_q_val == max_val].index.tolist()
        self._logger.debug("Optimal moves : %s", optimal_moves)

        if len(optimal_moves) == 0:     # shouldn't happen
            raise Exception("No optimal move in Get move function, %s", max_val)
        elif len(optimal_moves) == 1:
            return optimal_moves[0]
        else:
            return random.choice(optimal_moves)
Ejemplo n.º 6
0
    def __init__(
        self,
        num_rows: int = 10,
        num_cols: int = 10,
        is_my_turn: bool = True,
        our_ship_locations: Optional[List[List]] = None,
        opponent_ship_locations: Optional[List[List]] = None,
        our_guesses: Optional[List[List]] = None,
        opponent_guesses: Optional[List[List]] = None,
        ships_dimensions: Optional[List[Tuple[int, int]]] = None,
    ):
        self.is_my_turn = is_my_turn
        self.is_game_over = False
        self.num_rows = num_rows
        self.num_cols = num_cols

        self.our_ship_locations = GameGrid(num_rows=num_rows,
                                           num_cols=num_cols,
                                           initial_value=SHIP_LOCATION_EMPTY)
        if our_ship_locations is not None:
            self.our_ship_locations.update_entire_grid(our_ship_locations)
        self.opponent_ship_locations = GameGrid(
            num_rows=num_rows,
            num_cols=num_cols,
            initial_value=SHIP_LOCATION_EMPTY)
        if opponent_ship_locations is not None:
            self.opponent_ship_locations.update_entire_grid(
                opponent_ship_locations)

        self.our_guesses = GameGrid(num_rows=num_rows,
                                    num_cols=num_cols,
                                    initial_value=LOCATION_NOT_GUESSED)
        if our_guesses is not None:
            self.our_guesses.update_entire_grid(our_guesses)
        self.opponent_guesses = GameGrid(num_rows=num_rows,
                                         num_cols=num_cols,
                                         initial_value=LOCATION_NOT_GUESSED)
        if opponent_guesses is not None:
            self.opponent_guesses.update_entire_grid(opponent_guesses)

        # track which ships are still alive for us and our opponent
        if ships_dimensions is None:
            ship_dimensions_to_use = STANDARD_SHIP_DIMENSIONS
        else:
            ship_dimensions_to_use = ships_dimensions

        # lists of tuples: ship value in the grid, and the dimensions of the ship
        self.our_ships = []
        self.opponent_ships = []
        next_ship_number = 1
        for ship_dims in ship_dimensions_to_use:
            self.our_ships.append((next_ship_number, ship_dims))
            self.opponent_ships.append((next_ship_number, ship_dims))
            next_ship_number += 1

        self.ships_placed = self.check_placements_ready()
def _check_rectangular_region_values(
    grid: GameGrid,
    top_row_idx: int,
    left_col_idx: int,
    height: int,
    width: int,
    region_value,
    other_value,
):
    for row_idx in range(grid.num_rows):
        for col_idx in range(grid.num_rows):
            if (
                top_row_idx <= row_idx < top_row_idx + height
                and left_col_idx <= col_idx < left_col_idx + width
            ):
                assert grid.read_grid(row_idx, col_idx) == region_value
            else:
                assert grid.read_grid(row_idx, col_idx) == other_value
Ejemplo n.º 8
0
    def attempt_strike(
        self,
        struck_locations_grid: GameGrid,
        strikers_guesses_grid: GameGrid,
        square_row_idx: int,
        square_col_idx: int,
    ) -> bool:
        """ Returns True if the guess hit a ship """
        # Did the guess hit a ship? (check struck_locations_grid)
        if (struck_locations_grid.read_grid(
                square_row_idx, square_col_idx) == SHIP_LOCATION_EMPTY):
            # The guess missed! (update strikers_guesses_grid with a miss)
            strikers_guesses_grid.update_grid(square_row_idx, square_col_idx,
                                              LOCATION_GUESS_MISS)
            return False

        # The guess hit! (update strikers_guesses_grid with a hit)
        strikers_guesses_grid.update_grid(square_row_idx, square_col_idx,
                                          LOCATION_GUESS_HIT)
        # Did the hit sink a ship? (check struck_locations_grid again)
        ship_that_was_hit = struck_locations_grid.read_grid(
            square_row_idx, square_col_idx)
        if not self.check_ship_alive(struck_locations_grid,
                                     strikers_guesses_grid, ship_that_was_hit):
            # The guess sunk an ship!
            # TODO update the struck player's alive ships

            # Did the guess end the game?
            if not self.any_ships_alive(struck_locations_grid,
                                        strikers_guesses_grid):
                print("game is now over!")
                self.is_game_over = True
        return True
Ejemplo n.º 9
0
    def __init__(self, manager, white_player='human', black_player='human'):

        # game state members
        self.game_manager = manager
        self.game_running = False
        self.needs_redraw = True
        self.screen = None
        self.clock = pygame.time.Clock()

        # game components
        self.game_logic = GameLogic(self)
        self.game_grid = GameGrid(self)
        self.game_board = None

        self.white_player = None
        self.black_player = None
        self.initialize_players(white_player, black_player)

        self.turn_manager = TurnManager(self)
        self.highlighter = Highlighter(self)
        self.logger = Logger(self)

        self.buttons = {}
Ejemplo n.º 10
0
    def rotate_ship_placement(self, locations_grid: GameGrid,
                              ship_value) -> bool:
        """
        Rotate the ship around its top left corner.
        Returns False if the rotation isn't possible (and doesn't change the game state).
        """
        min_row_idx, max_row_idx = None, None
        min_col_idx, max_col_idx = None, None
        count_ship_value = 0
        for row_idx in range(self.num_rows):
            for col_idx in range(self.num_cols):
                if locations_grid.read_grid(row_idx, col_idx) == ship_value:
                    count_ship_value += 1
                    if min_row_idx is None or row_idx < min_row_idx:
                        min_row_idx = row_idx
                    if max_row_idx is None or row_idx > max_row_idx:
                        max_row_idx = row_idx
                    if min_col_idx is None or col_idx < min_col_idx:
                        min_col_idx = col_idx
                    if max_col_idx is None or col_idx > max_col_idx:
                        max_col_idx = col_idx

        ship_width, ship_height = (
            max_col_idx - min_col_idx + 1,
            max_row_idx - min_row_idx + 1,
        )
        self.clear_ship_placement(locations_grid,
                                  ship_value,
                                  ship_dims=(ship_width, ship_height))

        rotate_success = self.place_ship(
            min_row_idx,
            min_col_idx,
            ship_width=ship_height,
            ship_height=ship_width,
            ship_value=ship_value,
            is_our_ship=True,
        )
        if not rotate_success:
            self.place_ship(
                min_row_idx,
                min_col_idx,
                ship_width=ship_width,
                ship_height=ship_height,
                ship_value=ship_value,
                is_our_ship=True,
            )
            return False
        else:
            return True
Ejemplo n.º 11
0
def start():

    global grid
    # create the game grid
    grid = GameGrid(grid_h, grid_w)
    # create the first tetromino to enter the game grid
    # by using the create_tetromino function defined below
    current_tetromino = create_tetromino(grid_h, grid_w)
    # print("next tetromino:")
    next_tetromino = create_tetromino(grid_h, grid_w)

    grid.current_tetromino = current_tetromino
    grid.next_tetromino = next_tetromino
    stddraw.clearKeysTyped()
    pause = False
    # main game loop (keyboard interaction for moving the tetromino)
    while True:

        if not pause:
            mx, my = stddraw.getPosition()
            tileX = grid.current_tetromino.bottom_left_corner.x
            ax = int(mx / 42.35) - 1
            # print(ax, tileX)

            if ax > tileX:
                for i in range(ax - tileX):
                    grid.current_tetromino.move("right", grid)
            elif ax < tileX:
                for i in range(tileX - ax):
                    grid.current_tetromino.move("left", grid)

        # check user interactions via the keyboard
        if stddraw.hasNextKeyTyped():
            key_typed = stddraw.nextKeyTyped()

            # Pause
            if key_typed == 'p':
                print("Pause")
                if pause:
                    pause = False
                else:
                    pause = True

            elif not pause:

                # if the left arrow key has been pressed
                if key_typed == "left":
                    # move the tetromino left by one
                    # print("Left Typed")
                    current_tetromino.move(key_typed, grid)
                # if the right arrow key has been pressed
                elif key_typed == "right":
                    # print("Right Typed")
                    # move the tetromino right by one
                    current_tetromino.move(key_typed, grid)
                # if the down arrow key has been pressed
                elif key_typed == "down":

                    # move the tetromino down by one
                    # (causes the tetromino to fall down faster)
                    current_tetromino.move(key_typed, grid)
                # piece drop
                elif key_typed == 'space':
                    for i in range(grid_h):
                        current_tetromino.move('down', grid)
                # Speed Increase
                elif key_typed == 'w':
                    if grid.delta_time > 50:
                        grid.delta_time -= 40
                # Speed Decrease
                elif key_typed == 's':
                    if grid.delta_time < 500:
                        grid.delta_time += 40

                elif key_typed == 'e':
                    current_tetromino.rotate(grid)

                elif key_typed == 'q':
                    current_tetromino.rotate_ccw(grid)

            if key_typed == 'r':
                print("restart")
                start()

            # clear the queue that stores all the keys pressed/typed
            stddraw.clearKeysTyped()

        # move (drop) the tetromino down by 1 at each iteration
        if not pause:
            success = current_tetromino.move("down", grid)

        # place the tetromino on the game grid when it cannot go down anymore
        if not success and not pause:
            # get the tile matrix of the tetromino
            tiles_to_place = current_tetromino.tile_matrix
            # update the game grid by adding the tiles of the tetromino
            game_over = grid.update_grid(tiles_to_place)
            # end the main game loop if the game is over
            if game_over:
                if display_game_over(grid_h, grid_w + 5):
                    pause = True
                    start()

            # create the next tetromino to enter the game grid
            # by using the create_tetromino function defined below
            current_tetromino = next_tetromino
            grid.current_tetromino = current_tetromino
            print("next tetromino:")
            next_tetromino = create_tetromino(grid_h, grid_w)
            grid.next_tetromino = next_tetromino
            next_tetromino.draw_dummy()

        # display the game grid and as well the current tetromino
        grid.display(pause)

    print("Game over")
Ejemplo n.º 12
0
 def clear_all_ship_placements(self, locations_grid: GameGrid):
     for row_idx in range(self.num_rows):
         for col_idx in range(self.num_cols):
             locations_grid.update_grid(row_idx,
                                        col_idx,
                                        new_value=SHIP_LOCATION_EMPTY)
Ejemplo n.º 13
0
class GameScene(Scene):
    def __init__(self, rect, levels):
        super().__init__(rect)
        self.levels = levels
        self.curr_level_index = 0
        self.num_levels = len(levels)

    def build(self, level=None):
        level = self.levels[self.curr_level_index] if level is None else level
        self.level_name = level['level_name']

        self.grid = GameGrid(glb.GAMEGRIDRECT, glb.CELL_SIZE)

        # place head of the snake in the center and align to the grid
        snake_x, snake_y = self.grid.cell2xy(*self.grid.xy2cell(
            glb.GAMEGRIDRECT.centerx, glb.GAMEGRIDRECT.centery))

        # create snake parts
        head = SnakePart(snake_x, snake_y, head_image, glb.DIRECTION_UP,
                         glb.DIRECTION_UP, dir2img_head)
        neck = SnakePart(snake_x, snake_y + glb.SNAKE_PART_HEIGHT,
                         body_straight_image, glb.DIRECTION_UP,
                         glb.DIRECTION_UP, dir2img_body)
        body = SnakePart(snake_x, snake_y + glb.SNAKE_PART_HEIGHT * 2,
                         body_straight_image, glb.DIRECTION_UP,
                         glb.DIRECTION_UP, dir2img_body)
        tail = SnakePart(snake_x, snake_y + glb.SNAKE_PART_HEIGHT * 3,
                         tail_image, glb.DIRECTION_UP, glb.DIRECTION_UP,
                         dir2img_tail)
        parts = [head, neck, body, tail]

        self.snake = Snake(parts,
                           speed=level['game_speed'],
                           wrap_around=level.get('wrap_around',
                                                 glb.SNAKE_CAN_WRAP_AROUND),
                           bounding_rect=glb.GAMEGRIDRECT)
        self.group_snake = Group([self.snake])

        for part in self.snake.parts:
            self.grid.occupy_cell(*self.grid.xy2cell(*part.getpos()))

        # create some food
        self.food = Group()
        food_table = level.get('food_table', default_food_table)
        self.food_factory = FoodFactory(food_table)

        for _ in range(level['num_starting_food']):
            fc = self.grid.get_random_free_cell()
            if fc:
                food_x, food_y = self.grid.cell2xy(*fc)
                self.grid.occupy_cell(*fc)
                self.food.append(self.food_factory.make_random(food_x, food_y))

        # create some walls
        self.walls = Group()
        for _ in range(level['num_starting_walls']):
            fc = self.grid.get_random_free_cell()
            if fc:
                wall_x, wall_y = self.grid.cell2xy(*fc)
                self.grid.occupy_cell(*fc)
                self.walls.append(Wall(wall_x, wall_y, wall_image))

        self.score = 0
        self.score_needed = level['score_needed']

        # tile background with sand texture
        for cix in range(self.grid.n_cells_x):
            for ciy in range(self.grid.n_cells_y):
                self.background.blit(ground_image, self.grid.cell2xy(cix, ciy))
        # make bounding fence with electricity
        for cix in range(self.grid.n_cells_x):
            self.background.blit(fence_horiz_image,
                                 self.grid.cell2xy(cix, -1, False))
            self.background.blit(
                fence_horiz_image_flip_v,
                self.grid.cell2xy(cix, self.grid.n_cells_y, False))
        for ciy in range(self.grid.n_cells_y):
            self.background.blit(fence_vert_image,
                                 self.grid.cell2xy(-1, ciy, False))
            self.background.blit(
                fence_vert_image_flip_h,
                self.grid.cell2xy(self.grid.n_cells_x, ciy, False))
        self.background.blit(fence_corner_image,
                             self.grid.cell2xy(-1, -1, False))
        self.background.blit(fence_corner_image_flip_h,
                             self.grid.cell2xy(self.grid.n_cells_x, -1, False))
        self.background.blit(fence_corner_image_flip_v,
                             self.grid.cell2xy(-1, self.grid.n_cells_y, False))
        self.background.blit(
            fence_corner_image_flip_hv,
            self.grid.cell2xy(self.grid.n_cells_x, self.grid.n_cells_y, False))

        # reset timer
        self.time_elapsed = 0
        self.max_time = level['max_time']
        if self.timer is not None and self.timer.is_running:
            self.timer.stop()
        self.timer = RepeatedTimer(1, self.increase_time, None)

        # reset texts
        self.text_score = ScreenTextBitmapFont('', 10, 20, bitmap_font)
        self.text_health = ScreenTextBitmapFont('', 630, 20, bitmap_font)
        self.text_time = ScreenTextBitmapFont('', glb.WIDTH // 2, 20,
                                              bitmap_font)
        self.text_level_name = ScreenTextBitmapFont(f'{self.level_name}',
                                                    glb.WIDTH // 4 + 32, 20,
                                                    bitmap_font)

        self.texts = Group([
            self.text_score, self.text_health, self.text_time,
            self.text_level_name
        ])

        # reset user events
        self.EVENT_PLAYER_LOOSE_LEVEL = pygame.USEREVENT + 1
        self.EVENT_PLAYER_WIN_LEVEL = pygame.USEREVENT + 2

        self.up = 0
        self.right = 0

        self.blinking_rect_fx = BlinkingRectFX()

        self.particles = []

        self.built = True

    def increase_time(self, *args, **kwargs):
        # TODO: VERY UGLY CODE!!
        self.time_elapsed += 1
        if self.time_is_out(10):
            snd_clock_tick.play()

    def update(self):
        snake = self.snake
        grid = self.grid
        food = self.food

        for part in snake.parts:
            grid.release_cell(*grid.xy2cell(*part.getpos()))

        snake.move(self.up, self.right, self.walls)

        for part in snake.parts:
            grid.occupy_cell(*grid.xy2cell(*part.getpos()))

        if snake.intersect_itself or not snake.within_world:
            snake.kill()

        if snake.wrap_around and self.time_elapsed - self.wrap_around_time > 15:
            self.snake.wrap_around = False

        # grow snake if it eats food
        for f in food:
            food_rect = grid.get_cell_rect(*grid.xy2cell(*f.rect.center))
            if snake.head.rect.colliderect(food_rect):
                snake.add_part(grid)

                if f.food_type == 'portal':
                    snake.wrap_around = True
                    self.wrap_around_time = self.time_elapsed

                if f.food_type == 'potion':
                    snd_eat_potion.play()
                elif f.food_type == 'mushroom':
                    snd_eat_bad_food.play()
                else:
                    snd_eat_good_food.play()

                if f.health > 0:
                    self.score += f.score

                snake.health += f.health

                food_cix, food_ciy = grid.xy2cell(f.rect.x, f.rect.y)
                grid.release_cell(food_cix, food_ciy)
                food.remove(f)

                fc = grid.get_random_free_cell()
                if fc is not None:
                    food_x, food_y = grid.cell2xy(*fc)
                    grid.occupy_cell(*fc)
                    new_food = self.food_factory.make_random(food_x, food_y)
                    food.append(new_food)
                    # FX: create new pretty color bubble that will expand around created food
                    for _ in range(1):
                        x = new_food.rect.centerx
                        y = new_food.rect.centery
                        color = food_type_to_bubble_color.get(
                            new_food.food_type, glb.PINK)
                        gravity = 0
                        vx = 0
                        vy = 0
                        radius = 1
                        border_size = 2
                        lifetime = 25
                        rect = new_food.rect.inflate(10, 10)
                        particle = Particle(
                            x,
                            y,
                            vx,
                            vy,
                            color,
                            radius,
                            lifetime,
                            gravity,
                            border_size,
                            size_func=lambda size: size + 3.5,
                            border_size_func=spawn_bubble_border_size_func,
                            #rect=rect
                        )
                        self.particles.append(particle)

        if not self.snake.alive or self.time_is_out():
            pygame.event.post(pygame.event.Event(
                self.EVENT_PLAYER_LOOSE_LEVEL))
            return

        if self.score >= self.score_needed:
            pygame.event.post(pygame.event.Event(self.EVENT_PLAYER_WIN_LEVEL))

        snake.update()

        for p in self.particles:
            p.update()
        self.particles = [p for p in self.particles if p.alive]

        # update texts
        self.text_score.set_text(f'SCORE: {self.score}/{self.score_needed}')
        self.text_health.set_text(f'HEALTH: {snake.health}')
        if self.max_time:
            self.text_time.set_text(
                f'TIME: {self.time_elapsed}/{self.max_time}')
        else:
            self.text_time.set_text(f'TIME: {self.time_elapsed}')

    def clear_screen(self, screen):
        dirtyrects.clear()
        self.group_snake.clear(screen, self.background)
        self.walls.clear(screen, self.background)
        self.food.clear(screen, self.background)
        self.texts.clear(screen, self.background)

    def draw(self, screen):
        self.group_snake.draw(screen)
        self.walls.draw(screen)
        self.food.draw(screen)
        self.texts.draw(screen)
        if self.snake.wrap_around:
            self.blinking_rect_fx.draw(screen)
        pygame.display.update(dirtyrects)
        for particle in self.particles:
            particle.draw(screen)

    def time_is_out(self, delta=0):
        if self.max_time <= 0:
            return False
        return self.time_elapsed >= self.max_time - delta

    def handle_transitions(self, event):
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            self.scene_manager.pop_scene()
            return

        if event.type == self.EVENT_PLAYER_LOOSE_LEVEL:
            self.build()  # here we should rebuild current level
            self.scene_manager.push_scene(you_loose_splash_screen)
            return

        if event.type == self.EVENT_PLAYER_WIN_LEVEL:
            if self.curr_level_index < self.num_levels - 1:
                self.curr_level_index += 1
                self.build()  # here we should build next level
                self.scene_manager.push_scene(you_win_splash_screen)
            else:
                self.scene_manager.push_scene(final_splash_screen)

    def handle_events(self, event):
        keystate = pygame.key.get_pressed()
        # handle input
        self.up = keystate[pygame.K_UP] - keystate[pygame.K_DOWN]
        self.right = keystate[pygame.K_RIGHT] - keystate[pygame.K_LEFT]

    def enter(self):
        if self.timer is not None and not self.timer.is_running:
            self.timer.start()

    def leave(self):
        if self.timer is not None and self.timer.is_running:
            self.timer.stop()

    def destroy(self):
        self.curr_level_index = 0
        self.built = False
Ejemplo n.º 14
0
def start():
    # set the dimensions of the game grid
    grid_h, grid_w = 17, 12
    # set the size of the drawing canvas
    canvas_h, canvas_w = 40 * grid_h, 40 * grid_w + 100
    stddraw.setCanvasSize(canvas_w, canvas_h)
    # set the scale of the coordinate system
    stddraw.setXscale(-0.5, grid_w + 3)
    stddraw.setYscale(-0.5, grid_h - 0.5)

    # create the game grid
    grid = GameGrid(grid_h, grid_w)
    # create the first tetromino to enter the game grid
    # by using the create_tetromino function defined below
    current_tetromino = create_tetromino(grid_h, grid_w)
    grid.current_tetromino = current_tetromino

    # display a simple menu before opening the game
    display_game_menu(grid_h, grid_w)

    # initial score
    score = 0
    speed = 250  #initial speed

    # main game loop (keyboard interaction for moving the tetromino)
    while True:
        # check user interactions via the keyboard
        if stddraw.hasNextKeyTyped():
            key_typed = stddraw.nextKeyTyped()
            # if the left arrow key has been pressed
            if key_typed == "left":
                # move the tetromino left by one
                current_tetromino.move(key_typed, grid)
            # if the right arrow key has been pressed
            elif key_typed == "right":
                # move the tetromino right by one
                current_tetromino.move(key_typed, grid)
            # if the down arrow key has been pressed
            elif key_typed == "down":
                # move the tetromino down by one
                # (causes the tetromino to fall down faster)
                current_tetromino.move(key_typed, grid)
            elif key_typed == "up":
                # rotate the tetromino 90 degree clock-wise
                current_tetromino.rotate(grid)
            elif key_typed == "space":
                # drop the tetromino
                for i in range(grid_h):
                    current_tetromino.move("down", grid)

            # clear the queue that stores all the keys pressed/typed
            stddraw.clearKeysTyped()

        # move (drop) the tetromino down by 1 at each iteration
        success = current_tetromino.move("down", grid)
        grid.connected_4()

        # place the tetromino on the game grid when it cannot go down anymore
        if not success:
            # get the tile matrix of the tetromino
            tiles_to_place = current_tetromino.tile_matrix
            # update the game grid by adding the tiles of the tetromino
            game_over = grid.update_grid(tiles_to_place)

            indv_score = 0  # starting value for a full row's score
            ind_score = 0  # starting value for a merged tiles score

            # check is_row_full for all rows
            for i in range(grid_h):
                grid.check_2048(grid.tile_matrix)
                # score from merged tiles
                ind_score = grid.update_score(grid.tile_num2)
                if grid.is_row_full(i, grid.tile_matrix):
                    # score from deleted full rows
                    indv_score = grid.update_score(grid.tile_num)
            grid.tile_num2 = np.zeros(100)  # for merged score
            score_val = ind_score + indv_score
            score += int(score_val)

            print(score)
            # end the main game loop if the game is over
            if game_over:
                break
            # increasing difficulty by increasing speed as the game process
            if score > 450:
                speed = 10
            elif score > 250:
                speed = 50
            elif score > 150:
                speed = 100
            if score > 10000:
                break
            # create the next tetromino to enter the game grid
            # by using the create_tetromino function defined below
            current_tetromino = create_tetromino(grid_h, grid_w)
            grid.current_tetromino = current_tetromino

        # display the game grid and as well the current tetromino
        grid.display(score, speed)

    # finish the game and display game over
    finish_game(grid_h, grid_w)
    print("Game over")
Ejemplo n.º 15
0
    def build(self, level=None):
        level = self.levels[self.curr_level_index] if level is None else level
        self.level_name = level['level_name']

        self.grid = GameGrid(glb.GAMEGRIDRECT, glb.CELL_SIZE)

        # place head of the snake in the center and align to the grid
        snake_x, snake_y = self.grid.cell2xy(*self.grid.xy2cell(
            glb.GAMEGRIDRECT.centerx, glb.GAMEGRIDRECT.centery))

        # create snake parts
        head = SnakePart(snake_x, snake_y, head_image, glb.DIRECTION_UP,
                         glb.DIRECTION_UP, dir2img_head)
        neck = SnakePart(snake_x, snake_y + glb.SNAKE_PART_HEIGHT,
                         body_straight_image, glb.DIRECTION_UP,
                         glb.DIRECTION_UP, dir2img_body)
        body = SnakePart(snake_x, snake_y + glb.SNAKE_PART_HEIGHT * 2,
                         body_straight_image, glb.DIRECTION_UP,
                         glb.DIRECTION_UP, dir2img_body)
        tail = SnakePart(snake_x, snake_y + glb.SNAKE_PART_HEIGHT * 3,
                         tail_image, glb.DIRECTION_UP, glb.DIRECTION_UP,
                         dir2img_tail)
        parts = [head, neck, body, tail]

        self.snake = Snake(parts,
                           speed=level['game_speed'],
                           wrap_around=level.get('wrap_around',
                                                 glb.SNAKE_CAN_WRAP_AROUND),
                           bounding_rect=glb.GAMEGRIDRECT)
        self.group_snake = Group([self.snake])

        for part in self.snake.parts:
            self.grid.occupy_cell(*self.grid.xy2cell(*part.getpos()))

        # create some food
        self.food = Group()
        food_table = level.get('food_table', default_food_table)
        self.food_factory = FoodFactory(food_table)

        for _ in range(level['num_starting_food']):
            fc = self.grid.get_random_free_cell()
            if fc:
                food_x, food_y = self.grid.cell2xy(*fc)
                self.grid.occupy_cell(*fc)
                self.food.append(self.food_factory.make_random(food_x, food_y))

        # create some walls
        self.walls = Group()
        for _ in range(level['num_starting_walls']):
            fc = self.grid.get_random_free_cell()
            if fc:
                wall_x, wall_y = self.grid.cell2xy(*fc)
                self.grid.occupy_cell(*fc)
                self.walls.append(Wall(wall_x, wall_y, wall_image))

        self.score = 0
        self.score_needed = level['score_needed']

        # tile background with sand texture
        for cix in range(self.grid.n_cells_x):
            for ciy in range(self.grid.n_cells_y):
                self.background.blit(ground_image, self.grid.cell2xy(cix, ciy))
        # make bounding fence with electricity
        for cix in range(self.grid.n_cells_x):
            self.background.blit(fence_horiz_image,
                                 self.grid.cell2xy(cix, -1, False))
            self.background.blit(
                fence_horiz_image_flip_v,
                self.grid.cell2xy(cix, self.grid.n_cells_y, False))
        for ciy in range(self.grid.n_cells_y):
            self.background.blit(fence_vert_image,
                                 self.grid.cell2xy(-1, ciy, False))
            self.background.blit(
                fence_vert_image_flip_h,
                self.grid.cell2xy(self.grid.n_cells_x, ciy, False))
        self.background.blit(fence_corner_image,
                             self.grid.cell2xy(-1, -1, False))
        self.background.blit(fence_corner_image_flip_h,
                             self.grid.cell2xy(self.grid.n_cells_x, -1, False))
        self.background.blit(fence_corner_image_flip_v,
                             self.grid.cell2xy(-1, self.grid.n_cells_y, False))
        self.background.blit(
            fence_corner_image_flip_hv,
            self.grid.cell2xy(self.grid.n_cells_x, self.grid.n_cells_y, False))

        # reset timer
        self.time_elapsed = 0
        self.max_time = level['max_time']
        if self.timer is not None and self.timer.is_running:
            self.timer.stop()
        self.timer = RepeatedTimer(1, self.increase_time, None)

        # reset texts
        self.text_score = ScreenTextBitmapFont('', 10, 20, bitmap_font)
        self.text_health = ScreenTextBitmapFont('', 630, 20, bitmap_font)
        self.text_time = ScreenTextBitmapFont('', glb.WIDTH // 2, 20,
                                              bitmap_font)
        self.text_level_name = ScreenTextBitmapFont(f'{self.level_name}',
                                                    glb.WIDTH // 4 + 32, 20,
                                                    bitmap_font)

        self.texts = Group([
            self.text_score, self.text_health, self.text_time,
            self.text_level_name
        ])

        # reset user events
        self.EVENT_PLAYER_LOOSE_LEVEL = pygame.USEREVENT + 1
        self.EVENT_PLAYER_WIN_LEVEL = pygame.USEREVENT + 2

        self.up = 0
        self.right = 0

        self.blinking_rect_fx = BlinkingRectFX()

        self.particles = []

        self.built = True
Ejemplo n.º 16
0
class BattleshipGameState:
    def __init__(
        self,
        num_rows: int = 10,
        num_cols: int = 10,
        is_my_turn: bool = True,
        our_ship_locations: Optional[List[List]] = None,
        opponent_ship_locations: Optional[List[List]] = None,
        our_guesses: Optional[List[List]] = None,
        opponent_guesses: Optional[List[List]] = None,
        ships_dimensions: Optional[List[Tuple[int, int]]] = None,
    ):
        self.is_my_turn = is_my_turn
        self.is_game_over = False
        self.num_rows = num_rows
        self.num_cols = num_cols

        self.our_ship_locations = GameGrid(num_rows=num_rows,
                                           num_cols=num_cols,
                                           initial_value=SHIP_LOCATION_EMPTY)
        if our_ship_locations is not None:
            self.our_ship_locations.update_entire_grid(our_ship_locations)
        self.opponent_ship_locations = GameGrid(
            num_rows=num_rows,
            num_cols=num_cols,
            initial_value=SHIP_LOCATION_EMPTY)
        if opponent_ship_locations is not None:
            self.opponent_ship_locations.update_entire_grid(
                opponent_ship_locations)

        self.our_guesses = GameGrid(num_rows=num_rows,
                                    num_cols=num_cols,
                                    initial_value=LOCATION_NOT_GUESSED)
        if our_guesses is not None:
            self.our_guesses.update_entire_grid(our_guesses)
        self.opponent_guesses = GameGrid(num_rows=num_rows,
                                         num_cols=num_cols,
                                         initial_value=LOCATION_NOT_GUESSED)
        if opponent_guesses is not None:
            self.opponent_guesses.update_entire_grid(opponent_guesses)

        # track which ships are still alive for us and our opponent
        if ships_dimensions is None:
            ship_dimensions_to_use = STANDARD_SHIP_DIMENSIONS
        else:
            ship_dimensions_to_use = ships_dimensions

        # lists of tuples: ship value in the grid, and the dimensions of the ship
        self.our_ships = []
        self.opponent_ships = []
        next_ship_number = 1
        for ship_dims in ship_dimensions_to_use:
            self.our_ships.append((next_ship_number, ship_dims))
            self.opponent_ships.append((next_ship_number, ship_dims))
            next_ship_number += 1

        self.ships_placed = self.check_placements_ready()

    def place_ship(
        self,
        top_row_idx: int,
        left_col_idx: int,
        ship_width: int,
        ship_height: int,
        ship_value,
        is_our_ship: bool,
    ) -> bool:
        # ship dimensions must not be 0
        assert ship_width > 0 and ship_height > 0

        # check that (ship value, ship dims) match the expected ship values
        if is_our_ship and ((ship_value,
                             (ship_height, ship_width)) not in self.our_ships
                            and
                            (ship_value,
                             (ship_width, ship_height)) not in self.our_ships):
            return False
        elif not is_our_ship and (
            (ship_value,
             (ship_height, ship_width)) not in self.opponent_ships and
            (ship_value,
             (ship_width, ship_height)) not in self.opponent_ships):
            return False

        # if the ship wasn't already placed, clear its old position first before placing again
        if is_our_ship and self._is_valid_ship_placement(
                self.our_ship_locations, ship_value,
            (ship_height, ship_width)):
            self.clear_ship_placement(self.our_ship_locations, ship_value,
                                      (ship_height, ship_width))
        elif not is_our_ship and self._is_valid_ship_placement(
                self.opponent_ship_locations, ship_value,
            (ship_height, ship_width)):
            self.clear_ship_placement(self.opponent_ship_locations, ship_value,
                                      (ship_height, ship_width))

        # check if the ship placement overlaps with a buffer! (can do neighboring check for each)
        bottom_row_idx = top_row_idx + ship_height - 1
        right_col_idx = left_col_idx + ship_width - 1

        # check if ship placement has an invalid coordinates
        for row_idx in range(top_row_idx, bottom_row_idx + 1):
            for col_idx in range(left_col_idx, right_col_idx + 1):
                if is_our_ship and not self.our_ship_locations.are_indexes_valid(
                        row_idx, col_idx):
                    return False
                elif (not is_our_ship
                      and not self.opponent_ship_locations.are_indexes_valid(
                          row_idx, col_idx)):
                    return False

        top_buffer_row_idx = max(0, top_row_idx - 1)
        bottom_buffer_row_idx = min(self.num_rows - 1, bottom_row_idx + 1)
        left_buffer_row_idx = max(0, left_col_idx - 1)
        right_buffer_row_idx = min(self.num_cols - 1, right_col_idx + 1)
        for row_idx in range(top_buffer_row_idx, bottom_buffer_row_idx + 1):
            for col_idx in range(left_buffer_row_idx,
                                 right_buffer_row_idx + 1):
                if is_our_ship:
                    ship_loc_value = self.our_ship_locations.read_grid(
                        row_idx, col_idx)
                else:
                    ship_loc_value = self.opponent_ship_locations.read_grid(
                        row_idx, col_idx)

                if ship_loc_value != SHIP_LOCATION_EMPTY:
                    # can't place ship because another ship is overlapping with the buffer
                    return False

        # now that the ship placement is verified, we can safely update the locations grid
        for row_idx in range(top_row_idx, bottom_row_idx + 1):
            for col_idx in range(left_col_idx, right_col_idx + 1):
                if is_our_ship:
                    self.our_ship_locations.update_grid(row_idx,
                                                        col_idx,
                                                        new_value=ship_value)
                else:
                    self.opponent_ship_locations.update_grid(
                        row_idx, col_idx, new_value=ship_value)

        self.ships_placed = self.check_placements_ready()
        return True

    def check_placements_ready(self) -> bool:
        # not only check if placements are valid, but check that both players have placed all available ships
        for ship_value, ship_dims in self.our_ships:
            if not self._is_valid_ship_placement(self.our_ship_locations,
                                                 ship_value, ship_dims):
                return False
        for ship_value, ship_dims in self.opponent_ships:
            if not self._is_valid_ship_placement(self.opponent_ship_locations,
                                                 ship_value, ship_dims):
                return False
        return True

    def _is_valid_ship_placement(self, ship_locations_grid: GameGrid,
                                 ship_value, ship_dims: Tuple[int, int]):
        # ship dimensions must not be 0
        assert ship_dims[0] > 0 and ship_dims[1] > 0

        # make sure the ship is located, and the dimensions match
        # 1. check the count of locations vs. dimensions
        # 2. check the bounds of dimensions (min and max, x and y)
        min_row_idx, max_row_idx = None, None
        min_col_idx, max_col_idx = None, None
        count_ship_value = 0
        for row_idx in range(self.num_rows):
            for col_idx in range(self.num_cols):
                if ship_locations_grid.read_grid(row_idx,
                                                 col_idx) == ship_value:
                    count_ship_value += 1
                    if min_row_idx is None or row_idx < min_row_idx:
                        min_row_idx = row_idx
                    if max_row_idx is None or row_idx > max_row_idx:
                        max_row_idx = row_idx
                    if min_col_idx is None or col_idx < min_col_idx:
                        min_col_idx = col_idx
                    if max_col_idx is None or col_idx > max_col_idx:
                        max_col_idx = col_idx

        # make sure the correct number of squares are labeled as the ship and the dimension boundaries match
        return ship_dims[0] * ship_dims[1] == count_ship_value and (
            (max_row_idx - min_row_idx + 1 == ship_dims[0]
             and max_col_idx - min_col_idx + 1 == ship_dims[1]) or
            (max_row_idx - min_row_idx + 1 == ship_dims[1]
             and max_col_idx - min_col_idx + 1 == ship_dims[0]))

    def clear_ship_placement(self, locations_grid: GameGrid, ship_value,
                             ship_dims: Tuple[int, int]):
        print(
            f"clearing ship placement: value = {ship_value}, dims = {ship_dims}"
        )
        for row_idx in range(self.num_rows):
            for col_idx in range(self.num_cols):
                if locations_grid.read_grid(row_idx, col_idx) == ship_value:
                    locations_grid.update_grid(row_idx,
                                               col_idx,
                                               new_value=SHIP_LOCATION_EMPTY)

    def clear_all_ship_placements(self, locations_grid: GameGrid):
        for row_idx in range(self.num_rows):
            for col_idx in range(self.num_cols):
                locations_grid.update_grid(row_idx,
                                           col_idx,
                                           new_value=SHIP_LOCATION_EMPTY)

    def randomize_ship_placements(
        self,
        ship_dims: List[Tuple[int, int]],
        our_ships: bool,
    ):
        available_squares = []
        for r in range(self.num_rows):
            row = []
            for c in range(self.num_cols):
                row.append(True)
            available_squares.append(row)
        random_ship_placements = random_ships_placement(
            ship_dims,
            available_squares=available_squares,
            num_rows=self.num_rows,
            num_cols=self.num_cols,
            rotate_allowed=True,
        )
        for idx in range(len(random_ship_placements)):
            ship_placement = random_ship_placements[idx]
            top_row_idx, left_col_idx, ship_height, ship_width = ship_placement
            self.place_ship(
                top_row_idx=top_row_idx,
                left_col_idx=left_col_idx,
                ship_width=ship_width,
                ship_height=ship_height,
                ship_value=idx + 1,
                is_our_ship=our_ships,
            )
            print(
                f"placed ship {idx + 1}, dims {ship_height}, {ship_width} at {top_row_idx}, {left_col_idx}"
            )

    def rotate_ship_placement(self, locations_grid: GameGrid,
                              ship_value) -> bool:
        """
        Rotate the ship around its top left corner.
        Returns False if the rotation isn't possible (and doesn't change the game state).
        """
        min_row_idx, max_row_idx = None, None
        min_col_idx, max_col_idx = None, None
        count_ship_value = 0
        for row_idx in range(self.num_rows):
            for col_idx in range(self.num_cols):
                if locations_grid.read_grid(row_idx, col_idx) == ship_value:
                    count_ship_value += 1
                    if min_row_idx is None or row_idx < min_row_idx:
                        min_row_idx = row_idx
                    if max_row_idx is None or row_idx > max_row_idx:
                        max_row_idx = row_idx
                    if min_col_idx is None or col_idx < min_col_idx:
                        min_col_idx = col_idx
                    if max_col_idx is None or col_idx > max_col_idx:
                        max_col_idx = col_idx

        ship_width, ship_height = (
            max_col_idx - min_col_idx + 1,
            max_row_idx - min_row_idx + 1,
        )
        self.clear_ship_placement(locations_grid,
                                  ship_value,
                                  ship_dims=(ship_width, ship_height))

        rotate_success = self.place_ship(
            min_row_idx,
            min_col_idx,
            ship_width=ship_height,
            ship_height=ship_width,
            ship_value=ship_value,
            is_our_ship=True,
        )
        if not rotate_success:
            self.place_ship(
                min_row_idx,
                min_col_idx,
                ship_width=ship_width,
                ship_height=ship_height,
                ship_value=ship_value,
                is_our_ship=True,
            )
            return False
        else:
            return True

    def check_ship_alive(self, locations_grid: GameGrid,
                         opponents_guesses_grid: GameGrid, ship_value):
        assert ship_value != SHIP_LOCATION_EMPTY
        for row_idx in range(self.num_rows):
            for col_idx in range(self.num_cols):
                if (locations_grid.read_grid(row_idx, col_idx) == ship_value
                        and opponents_guesses_grid.read_grid(
                            row_idx, col_idx) != LOCATION_GUESS_HIT):
                    return True
        return False

    def any_ships_alive(self, locations_grid: GameGrid,
                        opponents_guesses_grid: GameGrid):
        for row_idx in range(self.num_rows):
            for col_idx in range(self.num_cols):
                if (locations_grid.read_grid(row_idx,
                                             col_idx) != SHIP_LOCATION_EMPTY
                        and opponents_guesses_grid.read_grid(
                            row_idx, col_idx) != LOCATION_GUESS_HIT):
                    return True
        return False

    def get_player_home_grid(self) -> List[List]:
        grid_symbols = []
        for row_idx in range(self.num_rows):
            grid_row = []
            for col_idx in range(self.num_cols):
                ship_loc_value = self.our_ship_locations.read_grid(
                    row_idx, col_idx)
                opponent_guess_value = self.opponent_guesses.read_grid(
                    row_idx, col_idx)
                grid_symbol = " "
                if ship_loc_value != SHIP_LOCATION_EMPTY:
                    # check if opponent has struck our ship here:
                    if opponent_guess_value == LOCATION_GUESS_HIT:
                        # check if the ship is sunk
                        if self.check_ship_alive(
                                self.our_ship_locations,
                                self.opponent_guesses,
                                ship_loc_value,
                        ):
                            grid_symbol = "X"
                        else:
                            grid_symbol = "S"
                    else:
                        grid_symbol = str(ship_loc_value)
                else:
                    # check if opponent has missed here
                    if opponent_guess_value == LOCATION_GUESS_MISS:
                        grid_symbol = "."
                grid_row.append(grid_symbol)
            grid_symbols.append(grid_row)
        return grid_symbols

    def get_player_tracking_grid(self) -> List[List]:
        grid_symbols = []
        for row_idx in range(self.num_rows):
            grid_row = []
            for col_idx in range(self.num_cols):
                guess_value = self.our_guesses.read_grid(row_idx, col_idx)
                grid_symbol = " "
                if guess_value == LOCATION_GUESS_HIT:
                    # check if we struck a ship here:
                    ship_loc_value = self.opponent_ship_locations.read_grid(
                        row_idx, col_idx)
                    assert ship_loc_value != SHIP_LOCATION_EMPTY
                    # check if the ship is sunk
                    if self.check_ship_alive(
                            self.opponent_ship_locations,
                            self.our_guesses,
                            ship_loc_value,
                    ):
                        grid_symbol = "X"
                    else:
                        grid_symbol = "S"
                elif guess_value == LOCATION_GUESS_MISS:
                    grid_symbol = "."
                grid_row.append(grid_symbol)
            grid_symbols.append(grid_row)
        return grid_symbols

    def is_game_over(self):
        return self.any_ships_alive(
            self.our_ship_locations,
            self.opponent_guesses) or self.any_ships_alive(
                self.opponent_ship_locations, self.our_guesses)

    def attempt_strike(
        self,
        struck_locations_grid: GameGrid,
        strikers_guesses_grid: GameGrid,
        square_row_idx: int,
        square_col_idx: int,
    ) -> bool:
        """ Returns True if the guess hit a ship """
        # Did the guess hit a ship? (check struck_locations_grid)
        if (struck_locations_grid.read_grid(
                square_row_idx, square_col_idx) == SHIP_LOCATION_EMPTY):
            # The guess missed! (update strikers_guesses_grid with a miss)
            strikers_guesses_grid.update_grid(square_row_idx, square_col_idx,
                                              LOCATION_GUESS_MISS)
            return False

        # The guess hit! (update strikers_guesses_grid with a hit)
        strikers_guesses_grid.update_grid(square_row_idx, square_col_idx,
                                          LOCATION_GUESS_HIT)
        # Did the hit sink a ship? (check struck_locations_grid again)
        ship_that_was_hit = struck_locations_grid.read_grid(
            square_row_idx, square_col_idx)
        if not self.check_ship_alive(struck_locations_grid,
                                     strikers_guesses_grid, ship_that_was_hit):
            # The guess sunk an ship!
            # TODO update the struck player's alive ships

            # Did the guess end the game?
            if not self.any_ships_alive(struck_locations_grid,
                                        strikers_guesses_grid):
                print("game is now over!")
                self.is_game_over = True
        return True

    def call_square(self, square_row_idx: int, square_col_idx: int) -> bool:
        """ Returns True if the guess hit a ship """
        if self.is_my_turn:
            did_hit = self.attempt_strike(
                self.opponent_ship_locations,
                self.our_guesses,
                square_row_idx,
                square_col_idx,
            )
        else:
            did_hit = self.attempt_strike(
                self.our_ship_locations,
                self.opponent_guesses,
                square_row_idx,
                square_col_idx,
            )

        # if the guess missed, then the turn passes to the other player
        if not did_hit and not self.is_game_over:
            self.is_my_turn = not self.is_my_turn
        return did_hit
def _check_all_grid_squares_equal(grid: GameGrid, expected_value):
    for row_idx in range(grid.num_rows):
        for col_idx in range(grid.num_rows):
            assert grid.read_grid(row_idx, col_idx) == expected_value
Ejemplo n.º 18
0
def start():
    # set the dimensions of the game grid
    grid_h, grid_w = 20, 12
    # set the size of the drawing canvas
    canvas_h, canvas_w = 40 * grid_h, 40 * grid_w
    stddraw.setCanvasSize(canvas_w, canvas_h)
    # set the scale of the coordinate system
    stddraw.setXscale(-0.5, grid_w - 0.5)
    stddraw.setYscale(-0.5, grid_h - 0.5)

    # create the game grid
    grid = GameGrid(grid_h, grid_w)
    # create the first tetromino to enter the game grid
    # by using the create_tetromino function defined below
    current_tetromino = create_tetromino(grid_h, grid_w)
    next_tetromino = create_tetromino(grid_h, grid_w)
    grid.current_tetromino = current_tetromino

    # display a simple menu before opening the game
    display_game_menu(grid_h, grid_w)
    # main game loop (keyboard interaction for moving the tetromino)
    while True:
        # check user interactions via the keyboard
        if stddraw.hasNextKeyTyped():
            key_typed = stddraw.nextKeyTyped()
            # if the left arrow key has been pressed
            if key_typed == "left":
                # move the tetromino left by one
                current_tetromino.move(key_typed, grid)
            # if the right arrow key has been pressed
            elif key_typed == "right":
                # move the tetromino right by one
                current_tetromino.move(key_typed, grid)
            # if the down arrow key has been pressed
            elif key_typed == "down":
                # move the tetromino down by one
                # (causes the tetromino to fall down faster)
                current_tetromino.move(key_typed, grid)
            # clear the queue that stores all the keys pressed/typed
            elif key_typed == "up":
                current_tetromino.rotateTetromino()
            elif key_typed == "space":
                temp = current_tetromino.move("down", grid)
                while (temp):
                    temp = current_tetromino.move("down", grid)
            stddraw.clearKeysTyped()

        # move (drop) the tetromino down by 1 at each iteration
        success = current_tetromino.move("down", grid)

        # place the tetromino on the game grid when it cannot go down anymore
        if not success:
            # get the tile matrix of the tetromino
            tiles_to_place = current_tetromino.tile_matrix
            # update the game grid by adding the tiles of the tetromino
            game_over = grid.update_grid(tiles_to_place)
            rowSet = rowsToCheck(tiles_to_place)
            grid.rowCheck(rowSet)
            columnSet = columnsToCheck(tiles_to_place)
            grid.sumCheck(columnSet, current_tetromino)
            # end the main game loop if the game is over
            if game_over:
                break
            # create the next tetromino to enter the game grid
            # by using the create_tetromino function defined below
            current_tetromino = next_tetromino
            grid.current_tetromino = current_tetromino
            next_tetromino = create_tetromino(grid_h, grid_w)
            print("Score = " + str(grid.score))
            print("Next tetromino type is: " + next_tetromino.type)

        # display the game grid and as well the current tetromino
        grid.display()

    print("Game over")
Ejemplo n.º 19
0
class Game(object):
    def __init__(self, manager, white_player='human', black_player='human'):

        # game state members
        self.game_manager = manager
        self.game_running = False
        self.needs_redraw = True
        self.screen = None
        self.clock = pygame.time.Clock()

        # game components
        self.game_logic = GameLogic(self)
        self.game_grid = GameGrid(self)
        self.game_board = None

        self.white_player = None
        self.black_player = None
        self.initialize_players(white_player, black_player)

        self.turn_manager = TurnManager(self)
        self.highlighter = Highlighter(self)
        self.logger = Logger(self)

        self.buttons = {}

    def init(self):

        self.screen = pygame.display.get_surface()
        self.game_running = True

        self.screen.fill(BLACK)

        # initialize game components
        self.game_board = GameBoard(self, self.game_grid)
        self.highlighter.init()
        self.initialize_buttons()

    def initialize_buttons(self):

        restart_button = Button(restart_coord, 'Restart', self.restart,
                                restart_anchor, False)
        end_button = Button(end_coord, 'End Game', self.exit_game, end_anchor)
        self.buttons['restart'] = restart_button
        self.buttons['end'] = end_button

    def initialize_players(self, white, black):

        players = []
        sides = ['white', 'black']
        constructors = []
        for p_type in (white, black):
            if p_type == 'human':
                constructors.append(Player)
            else:
                constructors.append(AIPlayer)

        for i in (0, 1):
            players.append(constructors[i](self, sides[i]))

        self.white_player = players[0]
        self.black_player = players[1]

    # main game loop
    def main(self):

        while self.game_running:

            self.handle_input()
            self.run_logic()
            if self.needs_redraw:
                self.draw_all()
                self.update_display()
                self.reset_redraw()
            self.tick()

    def handle_input(self):

        for event in pygame.event.get():

            if event.type == QUIT:
                self.exit_program()

            elif event.type == MOUSEBUTTONDOWN:

                if self.active_player.is_human() and self.mouse_over_grid():
                    self.active_player.try_to_place_piece(
                        self.mouse_grid_position())
                else:
                    for button in self.buttons.itervalues():
                        if button.active and button.mouse_is_over():
                            button.on_click()

    @property
    def active_player(self):
        return self.turn_manager.active_player

    def mouse_over_grid(self):
        mx, my = pygame.mouse.get_pos()
        return my > BOARD_Y_OFFSET

    def mouse_grid_position(self):
        mouse_pos = pygame.mouse.get_pos()
        return self.game_grid.translate_mouse_to_grid_coord(mouse_pos)

    def exit_game(self):
        self.game_running = False

    def exit_program(self):
        self.game_running = False
        self.game_manager.exit_game()
        print 'here'

    def run_logic(self):
        self.turn_manager.run()
        if not self.active_player.is_human(
        ) and self.turn_manager.game_running:
            self.active_player.run_logic()

    def draw_all(self):
        self.game_board.draw(self.screen)
        self.highlighter.draw(self.screen)
        self.logger.draw_scores(self.screen)
        self.logger.draw_log(self.screen)

        for button in self.buttons.itervalues():
            if button.active:
                button.draw(self.screen)

    def update_display(self):
        pygame.display.update()

    def request_redraw(self):
        self.needs_redraw = True

    def reset_redraw(self):
        self.needs_redraw = False

    def tick(self):
        self.clock.tick(FPS)

    def black_score(self):
        return self.game_grid.get_score(BLACK_PIECE)

    def white_score(self):
        return self.game_grid.get_score(WHITE_PIECE)

    def activate_button(self, key):
        self.buttons[key].set_active()

    def hide_button(self, key):
        self.buttons[key].hide()

    def restart(self):
        self.game_grid.reset_state()
        self.turn_manager.reset_state()
        self.request_redraw()
        self.hide_button('restart')