我正在为 Hexapawn 开发一个 python minimax 实现,但遇到了一个问题:它打印出 ai 的正确移动,但它不会执行该移动。
这是我的代码:
import copy
class Hexapawn:
def __init__(self):
self.board = [[2,2,2],
[0,0,0],
[1,1,1]]
self.player = 1
def display_board(self):
for row in self.board:
print(row)
def ai_turn(self):
move = self.minimax(3, float('-inf'), float('inf'), True)[1]
if move:
self.make_move(move, self.board)
print("AI moves:", move)
else:
print("AI has no valid moves.")
def player_turn(self):
player_move = self.get_player_move()
self.make_move(player_move, self.board)
if self.is_game_over():
self.display_board()
print("Player wins!")
return
self.display_board()
def play_game(self):
while True:
self.player_turn()
self.player = 1 if self.player == 2 else 2
if self.is_game_over():
break
self.ai_turn()
self.player = 1 if self.player == 2 else 2
if self.is_game_over():
self.display_board()
print("AI wins!")
break
def evaluate_board(self):
player1_pawns = sum(row.count(1) for row in self.board)
player2_pawns = sum(row.count(2) for row in self.board)
return player1_pawns- player2_pawns
def get_player_move(self):
while True:
try:
orow, ocol = map(int, input("Enter row and column of the pawn you want to move (e.g., 0 1): ").split())
nrow, ncol = map(int, input("Enter row and column of the destination (e.g., 1 1): ").split())
move = ((orow, ocol), (nrow, ncol))
if move in self.get_possible_moves():
return move
else:
print("Invalid move. Try again.")
except ValueError:
print("Invalid input. Please enter row and column numbers separated by a space.")
def make_move(self, move, board):
orow,ocol = move[0]
nrow,ncol = move[1]
board[nrow][ncol] = board[orow][ocol]
board[orow][ocol] = 0
def undo_move(self, move, board):
orow, ocol = move[0]
nrow, ncol = move[1]
board[orow][ocol] = board[nrow][ncol]
board[nrow][ncol] = 0
def is_game_over(self):
if not self.get_possible_moves():
return True
for col in range(len(self.board[0])):
if self.board[0][col] == 1 or self.board[2][col] == 2:
return True
return False
def get_possible_moves(self):
possible = []
opponent=2 if self.player == 1 else 1
for row in range(len(self.board)):
for col in range(len(self.board[row])):
if self.board[row][col] == self.player:
if self.player == 1:
#diagonal
#r
if row -1 >= 0 and col +1 <= 2 and self.board[row-1][col+1] == opponent:
possible.append(((row,col),(row-1,col+1)))
#l
if row-1 >= 0 and col-1 >= 0 and self.board[row-1][col-1] == opponent:
possible.append(((row,col),(row-1,col-1)))
#vert
if row -1 >= 0 and self.board[row-1][col] == 0:
possible.append(((row,col),(row-1,col)))
elif self.player == 2:
#diag
#r
if row+1 <= 2 and col +1 <= 2 and self.board[row+1][col+1] == opponent:
possible.append(((row,col), (row+1, col+1)))
#l
if row+1 <= 2 and col -1 >= 0 and self.board[row+1][col-1] == opponent:
possible.append(((row,col),(row+1, col-1)))
#vert
if row+1 <= 2 and self.board[row+1][col] == 0:
possible.append(((row,col), (row+1, col)))
return possible
def minimax(self, depth, alpha, beta, maximizing_player):
if depth == 0 or self.is_game_over():
return self.evaluate_board(), None
board = copy.deepcopy(self.board)
if maximizing_player:
max_eval = float('-inf')
best_move = None
for move in self.get_possible_moves():
self.make_move(move, board)
evaluate = self.minimax(depth-1,alpha, beta, False)[0]
self.undo_move(move, board)
if evaluate > max_eval:
max_eval = evaluate
best_move = move
alpha = max(alpha, evaluate)
if beta <= alpha:
break
return max_eval, best_move
else:
min_eval= float('inf')
for move in self.get_possible_moves():
self.make_move(move, board)
evaluate = self.minimax(depth-1, alpha, beta, True)[0]
self.undo_move(move, board)
if evaluate<min_eval:
min_eval = evaluate
beta = min(beta, evaluate)
if beta <= alpha:
break
return min_eval, None
if __name__ == "__main__":
game = Hexapawn()
game.display_board()
game.play_game()
我试图找出为什么人工智能的举动没有反映在棋盘上,但没有成功。我错过了什么?
它打印出人工智能的正确动作,但它不会执行该动作
实际上,AI 动作已经做出了。但要真正看到结果,您应该在移动完成后立即打印棋盘:
def ai_turn(self):
move = self.minimax(3, float('-inf'), float('inf'), True)[1]
if move:
self.make_move(move, self.board)
print("AI moves:", move)
self.display_board() # <----------------- add this
else:
print("AI has no valid moves.")
上面回答了您的直接问题,但您的实现有几个问题:
在极小极大搜索期间,玩家的回合不会改变,即
self.player
不会改变,这意味着 get_possible_moves
在整个极小极大搜索过程中只会返回同一玩家的移动。为了解决这个问题,我建议在函数 self.player
和 make_move
中切换 undo_move
,并且仅在那里。一旦你做到了这一点,你就不需要 maximizing_player
参数,因为它是由 self.player
暗示的。
minimax
将 self.board
的副本放入局部变量 board
中,并在该副本上执行可能的动作,但递归调用不会对该副本有任何引用,并将从初始 self.board
,忽略刚刚下的棋步。我建议不要复制棋盘,直接在self.board
上玩。由于您总是对每个动作执行撤消操作,因此您最终会得到原始的棋盘,没有任何问题。这也意味着采用 board
参数(如 make_move
)的函数可以删除该参数并转而使用 self.board
。
当游戏结束时,
evaluate
函数用于求极小极大分数。但这个函数有两个问题:
该函数最大化玩家 1 的得分,但 minimax 函数是在 AI 玩家(玩家 2)作为最大化玩家的情况下调用的,因此这会影响良好的评估。
该功能不利于赢得未结束的游戏。例如,这里左边是一场获胜的比赛,右边是一场未获胜的比赛,两者的最后一步都是玩家 2 下的:
. . . . 2 .
2 1 2 1 1 .
. . . . . 2
修正第一个问题后,对于玩家 2 来说,评估函数会倾向于左侧的棋盘,因为它的棋盘比对手的棋子多,但右侧的棋盘显然更好,因为它是一个棋盘。赢。要么首先检测胜利并为此类棋盘提供优先分数,要么为棋盘上更先进的棋子提供更多分数。