NoneType对于极大极小井字游戏是不可下标的



我为电脑玩家设计了一个使用极大极小算法的井字游戏。Tkinter部分正常工作,真人玩家正常工作。然而,当计算机应该播放时,它给出了错误:&;NoneType is not subscripable .&;我不知道为什么。我是不是漏掉了一个变量的输入?提前谢谢你。

下面是我的代码:
from tkinter import *
import customtkinter
import random
import minimax
customtkinter.set_appearance_mode("Dark")
#creating CTk window for app
root = customtkinter.CTk()
#setting window width and height
root.geometry('500x300')

#Creating label
label = customtkinter.CTkLabel(master=root,
text="Tic Tac Toe",
width=120,
height=50,
font=("normal", 20),
corner_radius=8)
label.place(relx=0.25, rely=0.8, anchor=CENTER)
#Handling clicks
DEPTH=8
def clickbutton(r, c):
buttons[r][c]["text"]="X"
board[r][c]="X"
buttons[r][c]['state']=DISABLED
label = customtkinter.CTkLabel(master=root,
text=checkwin(board),
width=120,
height=25,
corner_radius=8)
label.place(relx=0.25, rely=0.9, anchor=CENTER)

computerplay()
DEPTH=DEPTH-1

#Button matrix
buttons = [
[0,0,0],
[0,0,0],
[0,0,0]]

#Matrix identifying whether buttons are active or inactive
board=[[0,0,0],[0,0,0],[0,0,0]]

for i in range(3):
for j in range(3):                                 
buttons[i][j] = Button(height = 3, width = 6, font = ("Normal", 20),
command = lambda r = i, c = j : clickbutton(r,c))
buttons[i][j].grid(row = i, column = j)

def computerplay():
bestmove=minimax.minimax(board, DEPTH, 1)
buttons[bestmove[0]][bestmove[1]]['text']="O"
buttons[bestmove[0]][bestmove[1]]['state']=DISABLED
board[bestmove[0]][bestmove[1]]="O"
def checkwin(b):
score=minimax.evaluate(b)
if score==10:
return 'Computer won!'
elif score==-10:
return 'You won!'
else:
return 'Player vs. Computer'

root.mainloop()

My minimax code:

import math
def change_board(board):
#changes board into -1, 0, and 1s instead of X, O, and 0 (zero).
new_board = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
for i in range(3):
for j in range(3):
if board[i][j]=='X':
new_board[i][j]==-1
elif board[i][j]=='O':
new_board[i][j]==1
return new_board
def empty_cells(board):
cells=[]
for i in range(3):
for j in range(3):
if board[i][j]==0:
cells.append([i, j])

return False
def game_over(board):
#check for wins using evaluate
if evaluate(board)==10 or evaluate(board)==-10:
return True

#check for full board
if (not empty_cells(board)):
return True
return False

def evaluate(board):
#check score
if board[0][0]==board[1][1] and board[1][1]==board[2][2]:
if board[0][0]==-1:
return -10
elif board[0][0]==1:
return 10

if board[0][2]==board[1][1] and board[1][1]==board[2][0]:
if board[0][2]==-1:
return -10
elif board[0][2]==1:
return 10

for row in range(3):
if board[row][0]==board[row][1] and board[row][1]==board[row][2]:
if board[row][0]==-1:
return -10
elif board[row][0]==1:
return 10
for col in range(3):
if board[0][col]==board[1][col] and board[1][col]==board[2][col]:
if board[0][col]==-1:
return -10
elif board[0][col]==1:
return 10


def minimax(board, depth, player):
if player==1:
#1 is computer. -1 is human player.
best=[-1, -1, -math.inf]
else:
best=[-1, -1, math.inf]
if depth==0 or game_over(board):
score=evaluate(board)
return score
#checking scores of valid moves 
for cell in empty_cells(board):
x, y = cell[0], cell[1]
board[x][y] = player
score = minimax(board, depth - 1, -player)
board[x][y] = 0
score[0], score[1] = x, y
if player == 1:
if score[2] > best[2]:
best = score
else:
if score[2] < best[2]:
best = score
return best

您的极大极小代码存在以下问题:

  • change_board中,您没有将任何内容分配给new_board单元格,因为您执行比较,而不是分配。将new_board[i][j]==-1改为new_board[i][j]=-1,else子句亦如是。

  • empty_cells中,您没有对cells所做的工作做任何事情,而是返回False。当然,调用者需要cells列表,所以您应该返回它。

  • evaluate中,当没有检测到获胜行时,您还应该返回一个数值:调用者需要一个数字来将其与迄今为止的最佳分数进行比较,因此让它返回None并不好。在函数底部添加一个return 0

  • minimax中,您应该始终返回相同的结构,即具有三个成员的列表。这不是在基本情况下做的,你只是返回一个数字。为了解决这个问题,基本情况应该是return [-1, -1, score]。与此相关的是,当分数是一个数字时(如这里),或者当分数是列表时,您应该考虑使用不同的名称,如您在代码的其他地方所做的那样。

有了这些修复,它将工作。

不过,还是有一些你可以改进的地方,包括:

  • 如果您基于布尔条件return Truereturn False,则可以仅返回该条件的计算结果。例如,game_over可以执行return abs(evaluate(board))==10 or not empty_cells(board)

  • 您可以链接比较操作符,例如board[0][0]==board[1][1]==board[2][2]

  • 避免game_over中的代码重复:将获胜行所在的信息放在数据结构中,然后循环它,只对三个单元格的比较进行一次编码。

  • 当不需要改变元组时,使用元组代替列表,例如x/y坐标。

  • 对于math.inf,您使用浮点数,否则分数将是整数。最好的做法是避免这种类型混合。在这种情况下,您可以使用值11代替。

如下图所示:

def change_board(board):
return [
[(cell == "O") - (cell == "X") for cell in row]
for row in board
]
def empty_cells(board):
return [(i, j) for i in range(3) for j in range(3) if board[i][j] == 0]
def game_over(board):
return abs(evaluate(board)) == 10 or not empty_cells(board)

lines = (
(0, 0, 1, 1, 2, 2),
(0, 2, 1, 1, 2, 0),
(0, 0, 0, 1, 0, 2),
(1, 0, 1, 1, 1, 2),
(2, 0, 2, 1, 2, 2),
(0, 0, 1, 0, 2, 0),
(0, 1, 1, 1, 2, 1),
(0, 2, 1, 2, 2, 2),
)
def evaluate(board):
def iswin(line):
arow, acol, brow, bcol, crow, ccol = line
if board[arow][acol] == board[brow][bcol] == board[crow][ccol]:
return board[arow][acol]*10
return next((win for win in map(iswin, lines) if win), 0)

def minimax(board, depth, player):
best = (-1, -1, -11*player)
if depth <= 0 or game_over(board):
return (-1, -1, evaluate(board))  # must return same structure
for x, y in empty_cells(board):
board[x][y] = player
score = minimax(board, depth - 1, -player)[2]  # only get the score
board[x][y] = 0
if (player == 1) == (score > best[2]):
best = (x, y, score)  # only inject x, y when move is best
return best

当你使用棋盘的位表示而不是2D矩阵时,更多的优化是可能的。

最新更新