为什么此循环不按语句顺序重复?

时间:2018-11-26 19:47:39

标签: python loops pygame

我在整理程序中的一个循环时遇到了麻烦,该循环不符合我的预期。该程序可让您播放“四连奏”。我包括了完整的(可运行的代码)和最后困扰我的摘录。

import numpy as np
import random
import pygame
import time

BOARD_SIZE = 6
BOARD_BOX_NUM = BOARD_SIZE ** 2
GIVEN_IDS = 0

# ------------------------------- setting up pygame  --------------------------------------------
pygame.init()

display_height = 600
display_width = 600

game_display = pygame.display.set_mode((display_width, display_height))

clock = pygame.time.Clock()

# ------------------------------- colours ----------------------------------------------------

white = (255, 255, 255)
black = (0, 0, 0)

blue = (0,0,255)
light_blue = (30, 144, 255)

red = (200, 0, 0)
light_red = (255, 0, 0)

yellow = (200, 200, 0)
light_yellow = (255, 255, 0)

green = (34, 177, 76)
light_green = (0, 255, 0)

# ------------------------------- methods for the game algorythm ------------------------------


def rolling_window(a, size):
    # This method is required for the algorythm that broadcasts the board for a win
    shape = a.shape[:-1] + (a.shape[-1] - size + 1, size)
    strides = a.strides + (a. strides[-1],)
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)


class Chip:
    def __init__(self, colour):
        if colour == "Blue":
            self.colour = colour
        if colour == "Red":
            self.colour = colour
        global GIVEN_IDS
        self.iD = GIVEN_IDS
        GIVEN_IDS += 1

    def get_iD(self):
        return self.iD


class Game:
    def __init__(self, player_colour="Blue", comp_colour="Red"):
        self.board = np.empty(BOARD_BOX_NUM, dtype=Chip)
        self.board = self.board.reshape(BOARD_SIZE, BOARD_SIZE)
        self.player_colour = player_colour
        self.comp_colour = comp_colour

    def get_comp_colour(self):
        return self.comp_colour

    def get_player_colour(self):
        return self.player_colour

    def give_str_board(self):
        """Returns a copy of the board array and replaces the Chip objects with the str with the colour of the chips."""
        this_board = np.copy(self.board)
        for x, y in np.ndindex(this_board.shape):
            if this_board[x, y] is not None:
                if this_board[x, y].colour == "Blue":
                    this_board[x, y] = "Blue"
                else:
                    this_board[x, y] = "Red"
        return this_board

    def print_board_in_console(self):
        """This function holds the board which is a 8x8 matrix."""
        this_board = self.give_str_board()
        print(this_board)
        print("-"*40)

    def insert_chip(self, chip, col):
        """Method for making a new entry to the board. For the player and enemy.
            The column has to be parametrised in the pythonic way. i.e. cols from 0-7."""
        # slices the entries of the column into a new array
        col_entries = self.board[:, col:col+1]
        # checks for all unoccupied pos in this column(entries are None)
        # double array of indexes with the form (array([row_i, ...]), array([column_i, ...]))
        # checking the condition with "is" is here not possible, because "is" operator cannot be overloaded
        none_indexes = np.where(col_entries == None)
        # check whether the column cannot contain an extra chip and function has to be aborted
        if len(none_indexes[0]) == 0:
            print("This column is full. Chose again.")
            return False
        # the pos where the chip will fall is the one with the highest index
        self.board[len(none_indexes[0]) - 1, col] = chip
        return True

    def get_chip(self, x, y):
        """This function can return the information about a chip in a pos of the board."""
        chip = self.board[x, y]
        return chip

    def is_won(self):
        """This function can be used to check the board on whether the game has been decided.
            The function differentiates between player and enemy and returns..."""
        winning_chip = None
        # get a copy of the board which only contains str and None
        this_board = self.give_str_board()
        flipped_board = np.fliplr(this_board)

        # in order to check the entire board for 4 Chips in a formation individual rows and cols are examined
        # use for loops to isolate rows and cols
        for this_ax in range(0, 2):
            for index in range(0, BOARD_SIZE):
                # the stack will contain the row[index] when this_ax = 0 and the col[index] when this_ax = 1
                stack = this_board.take(index, axis=this_ax)

                # this will be the patterns, that are searched for
                winning_formations = [['Blue', 'Blue', 'Blue', 'Blue'], ['Red', 'Red', 'Red', 'Red']]
                for i in winning_formations:
                    bool_array = rolling_window(stack, 4) == i

                    if [True, True, True, True] in bool_array.tolist():
                        # the stack_index is the index of the first chip in the 4xChip formation in the row/col
                        stack_index_tuple, = np.where(np.all(bool_array == [True, True, True, True], axis=1))
                        stack_index = stack_index_tuple[0]
                        loop_index = index
                        # this_ax = 0 means loop_index is row and stack_index is col
                        if this_ax == 0:
                            winning_chip = self.get_chip(loop_index, stack_index)
                            break
                        else:
                            winning_chip = self.get_chip(stack_index, loop_index)
                            break

            # This next part of the algorythm checks whether diagonal patterns of the array
            # contain a winning formation

            # if this bit is problematic: change the 0 in range()!!!

            for index in range(0, BOARD_SIZE - 2):
                for board in [this_board, flipped_board]:
                    diag_elements = board.diagonal(index)
                    for i in winning_formations:
                        bool_array = rolling_window(diag_elements, 4) == i
                        if [True, True, True, True] in bool_array.tolist():
                            # the stack_index is the index of the first chip in the 4xChip formation in the row/col
                            diag_el_index_tuple, = np.where(
                                np.all(bool_array == [True, True, True, True], axis=1))
                            diag_index = diag_el_index_tuple[0]
                            loop_index = index
                            # this_ax = 0 means loop_index is row and stack_index is col
                            if board == this_board:
                                winning_chip = self.get_chip(loop_index, diag_index)
                                break
                            else:
                                winning_chip = self.get_chip(diag_index, loop_index)
                                break

        if winning_chip is not None:
            return winning_chip.colour

        return None

    def get_comp_move(self):
        """This method generates the computer's move (the column) based on INTELLIGENCE!!!!
        Returns the column of the move"""
        c_colour = self.get_comp_colour()
        p_colour = self.get_player_colour()

        # check, if the comp can win in the next move
        for i in range(0, BOARD_SIZE):
            board = self.give_str_board()
            chip = Chip(c_colour)
            self.insert_chip(chip, i)
            if self.is_won() == c_colour:
                return i

        # check, if the player can win in the next move and block that position
        for i in range(0, BOARD_SIZE):
            board = self.give_str_board()
            chip = Chip(p_colour)
            self.insert_chip(chip, i)
            if self.is_won() == p_colour:
                return i

        # accumulate preferable positions for the next move
        good_spots = []
        board = self.give_str_board()
        for axis in range(0, 2):
            for index in range(0, BOARD_SIZE):
                # the stack will contain the row[index] when this_ax = 0 and the col[index] when this_ax = 1
                stack = board.take(index, axis=axis)

                # this will be the patterns, that are searched for
                for i in [c_colour, c_colour]:
                    bool_array = rolling_window(stack, 2) == i

                    if [True, True] in bool_array.tolist():
                        # the stack_index is the index of the first chip in the 4xChip formation in the row/col
                        stack_index_tuple, = np.where(np.all(bool_array == [True, True], axis=1))
                        stack_index = stack_index_tuple[0]
                        # this_ax = 0 means loop_index is row and stack_index is col
                        if axis == 0:
                            # "- 1" because this if-statement is called when broadcasting a row. i.e. column before
                            good_spots.append(stack_index - 1)
                        else:
                            good_spots.append(index)
            # The pick is the x-coo of the first of a series of two chips (column) or the x before (row).
            print(good_spots)
            pick = random.randint(0, len(good_spots))
            return pick

        # make a move, "better than nothing"
        flag = True
        while flag is True:
            rnd = random.randint(0, BOARD_SIZE)
            if self.board[rnd, 0] is None:
                return rnd

    # ------------------------------- this part will take care of the visualisation in pygame ------------------------

    # buttons = []
    #
    # def button(self, text, x, y, radius, inactive_colour, active_colour, action=None, size=" "):
    #     cur = pygame.mouse.get_pos()
    #     if x + radius > cur[0] > x - radius and y + radius > cur[1] > y - radius:
    #         pygame.draw.circle(game_display, active_colour, (x, y), radius)
    #
    #         ix = None
    #         iy = None
    #         for event in pygame.event.get():
    #             if event.type == pygame.MOUSEBUTTONDOWN:
    #                 ix, iy = event.pos
    #
    #         if ix is not None and iy is not None:
    #             if x + radius > ix > x - radius and y + radius > iy > y - radius and action is not None:
    #                 if action == "quit":
    #                     pygame.quit()
    #                     quit()
    #
    #                 if action == "move":
    #                     col = int(x / 80 - 90)
    #                     self.insert_chip(Chip(self.get_player_colour()), col)
    #     else:
    #         pygame.draw.circle(game_display, inactive_colour, (x, y), radius)

    def draw_board(self):
        board = self.give_str_board()
        pygame.draw.rect(game_display, green, (60, 60, 80*BOARD_SIZE, 80*BOARD_SIZE))
        for y in range(0, BOARD_SIZE):
            for x in range(0, BOARD_SIZE):
                dx = 90 + x * 80
                dy = 90 + y * 80
                if board[x, y] is None:
                    pygame.draw.circle(game_display, white, (dy, dx), 30)
                elif board[x, y] == "Blue":
                    pygame.draw.circle(game_display, blue, (dy, dx), 30)
                elif board[x, y] == "Red":
                    pygame.draw.circle(game_display, red, (dy, dx), 30)

        # draw the selector square
        pygame.draw.rect(game_display, yellow, (self.selector_pos * 80 + 80, 60, 20, 20))

    selector_pos = 0

    def move_selector(self, dir):
        selector_pos = self.selector_pos
        if dir == 'left' and selector_pos >= 0:
            new_x = selector_pos * 80 - 80
            pygame.draw.rect(game_display, yellow, (new_x, 60, 20, 20))
            self.selector_pos -= 1
        if dir == 'right' and selector_pos <= BOARD_SIZE - 1:
            new_x = selector_pos * 80 + 80
            pygame.draw.rect(game_display, yellow, (new_x, 60, 20, 20))
            self.selector_pos += 1

    def intro(self):
        return

    def game_loop(self, comp_goes_first=False):

        game_over = False
        while game_over is not True:

            # here comes the computer's move
            if comp_goes_first is True:
                col = self.get_comp_move()
                chip = Chip(self.get_comp_colour())
                self.insert_chip(chip, col)

            # This will be the player move
            move_over = False
            while move_over is not True:
                for event in pygame.event.get():
                    print(event)
                    if event.type == pygame.QUIT:
                        pygame.quit()
                        quit()
                    if event.type == pygame.KEYDOWN:
                        if event.key == pygame.K_RIGHT:
                            self.move_selector('right')
                            pygame.draw.rect(game_display, yellow, (50, 50, 50, 50))
                        if event.key == pygame.K_LEFT:
                            self.move_selector('left')
                        if event.key == pygame.K_RETURN:
                            # the selector position indicates the column which the player has chosen.
                            col = self.selector_pos
                            chip = Chip(self.get_player_colour())
                            move_over = self.insert_chip(chip, col)

                game_display.fill(white)
                self.draw_board()
                pygame.display.update()

            comp_goes_first = True
            clock.tick(15)


game = Game()
game.game_loop()

现在特别让我感到疑惑的部分。

随着以下循环的运行,我可以使用箭头键按预期的方式使玩家在游戏中移动并返回,但是随后程序变得疯狂了。它将为计算机,有时甚至是播放器执行多次移动。这是循环运行一次(玩家的移动然后是计算机的移动,现在又是玩家再次移动)后GUI的图片。

GUI

在这一点上,我想知道我的while循环构造是否从根本上出错了,我无法弄清楚。有什么问题吗?

game_over = False
    while game_over is not True:

        # here comes the computer's move
        if comp_goes_first is True:
            col = self.get_comp_move()
            chip = Chip(self.get_comp_colour())
            self.insert_chip(chip, col)

        # This will be the player move
        move_over = False
        while move_over is not True:
            for event in pygame.event.get():
                print(event)
                if event.type == pygame.QUIT:
                    pygame.quit()
                    quit()
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_RIGHT:
                        self.move_selector('right')
                    if event.key == pygame.K_LEFT:
                        self.move_selector('left')
                    if event.key == pygame.K_RETURN:
                        # the selector position indicates the column which the player has chosen.
                        col = self.selector_pos
                        chip = Chip(self.get_player_colour())
                        move_over = self.insert_chip(chip, col)

            game_display.fill(white)
            self.draw_board()
            pygame.display.update()

        comp_goes_first = True
        clock.tick(15)

1 个答案:

答案 0 :(得分:1)

您的问题在我看来属于您的AI逻辑。您正在为AI做实验的电路板副本,但是您没有使用它,而是在实际的电路板上插入了芯片。

# check, if the comp can win in the next move
        for i in range(0, BOARD_SIZE):

            # Never used.
            board = self.give_str_board()
            chip = Chip(c_colour)

            # inserting onto the real board.
            self.insert_chip(chip, i)
            print("insert check", board, self)
            if self.is_won() == c_colour:
                return i