我在整理程序中的一个循环时遇到了麻烦,该循环不符合我的预期。该程序可让您播放“四连奏”。我包括了完整的(可运行的代码)和最后困扰我的摘录。
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的图片。
在这一点上,我想知道我的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)
答案 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