以下,参考<算法精粹:经典计算机科学问题的Python实现> 写的一个简单Tictoe 井字棋搜索Ai
- board.py
from typing import NewType,List
# 一步棋,可以用一个整数代表这一步棋(放在哪个方格)
Move = NewType('Move',int)
# 表示棋盘上的一种棋子 opposite是回合指示器
class Piece:
def __init__(self):
pass
#棋盘的状态
class Board():
# 从当前位置走到新的位置
def move(self,location:Move):
pass
# 评估函数 当前谁占优?
def evaluate(self,player:Piece) -> float:
pass
# 该谁走
def turn(self) -> Piece:
pass
# 当前有哪些符合规则的走法
def legal_moves(self) -> List[Move]:
pass
# is anybody win?
def is_win(self) -> bool:
pass
# otherwise, is there be draw(平局)?
def is_draw(self) -> bool:
return (not self.is_win()) and (len(self.legal_moves()) == 0)
- minimax.py
from board import Move
# 基础minimax算法
def minimax(board,ismaxlay,orig,depth):
if board.is_win() or board.is_draw() or depth == 0:
return board.evaluate(orig)
if ismaxlay:
best_eval = float('-inf')
for move in board.legal_moves():
res = minimax(board.move(move),False,orig,depth-1)
best_eval = max(res,best_eval)
return best_eval
else:
worst_eval = float('inf')
for move in board.legal_moves():
res = minimax(board.move(move),True,orig,depth-1)
worst_eval = min(res,worst_eval)
return worst_eval
# minimax+alphabeta剪枝
def alphabeta(board,ismaxlay,orig,depth,alpha=float('-inf'),beta=float('inf')):
if board.is_win() or board.is_draw() or depth == 0:
return board.evaluate(orig)
if ismaxlay:
for move in board.legal_moves():
res = alphabeta(board.move(move),False,orig,depth-1,alpha,beta)
alpha = max(res,alpha)
if beta <= alpha:
break
return alpha
else:
for move in board.legal_moves():
res = alphabeta(board.move(move),True,orig,depth-1,alpha,beta)
beta = min(res,beta)
if beta <= alpha:
break
return beta
def find_best_move(board,orig,depth = 9):
best_eval = float('-inf')
best_move = Move(-1)
for move in board.legal_moves():
res = alphabeta(board.move(move),False,orig,depth)
#print(f'move{move} res{res}')
if res > best_eval:
best_eval = res
best_move = move
return best_move
- tictactoe.py
from board import Piece,Board,Move
import minimax
# TicTactToe Piece ('X','O','blank')
class TTTPiece(Piece):
def __init__(self,name):
if name == 'X':
self.name = 'X'
if name == 'O':
self.name = 'O'
if name == ' ':
self.name = ' '
def __str__(self):
return self.name
# TicTactToe Board 棋盘当前状态 维护两个: 该谁走,棋盘目前位置
class TTTBoard(Board):
# 初始化一个空棋盘 position就是当前棋盘的一维数组表示
def __init__(self,position = [TTTPiece(' ')] * 9, turn = 'X'):
self.position = position
self._turn = turn
# 现在该谁走
def turn(self):
return self._turn
# 之后该谁走
def getNextTurn(self):
if self.turn() == 'X':
return 'O'
elif self.turn() == 'O':
return 'X'
else:
print(f'#{self.turn()}#Wrong turn!')
return
# 移动一步 注意没有对本棋盘更改,而是生成一个新的棋盘
def move(self,location):
temp_position = self.position.copy()
temp_position[location] = TTTPiece(self._turn)
return TTTBoard(temp_position,self.getNextTurn())
# 计算合法下一步 空的都能走
def legal_moves(self):
return [Move(l)for l in range(len(self.position)) if self.position[l].name == ' ']
# 判断是否赢了游戏 直白地扫描行列 对角线
'''
0 1 2
3 4 5
6 7 8
'''
def is_win(self):
if(self.position[0].name != ' ' and self.position[0].name == self.position[1].name and self.position[0].name == self.position[2].name ):
return True
if(self.position[3].name != ' ' and self.position[3].name == self.position[4].name and self.position[3].name == self.position[5].name ):
return True
if(self.position[6].name != ' ' and self.position[6].name == self.position[7].name and self.position[6].name == self.position[8].name ):
return True
if(self.position[0].name != ' ' and self.position[0].name == self.position[3].name and self.position[0].name == self.position[6].name ):
return True
if(self.position[1].name != ' ' and self.position[1].name == self.position[4].name and self.position[1].name == self.position[7].name ):
return True
if(self.position[2].name != ' ' and self.position[2].name == self.position[5].name and self.position[2].name == self.position[8].name ):
return True
if(self.position[0].name != ' ' and self.position[0].name == self.position[4].name and self.position[0].name == self.position[8].name ):
return True
if(self.position[2].name != ' ' and self.position[2].name == self.position[4].name and self.position[2].name == self.position[6].name ):
return True
return False
# 评估函数 写的相当简陋 因为井字棋搜索空间比较小
def evaluate(self, player):
if self.is_win() and self.turn() == player:
return -1
elif self.is_win() and self.turn() != player:
return 1
else:
return 0
# 美化打印棋盘
def __repr__(self):
return f'''
{self.position[0]} | {self.position[1]} | {self.position[2]}
---------
{self.position[3]} | {self.position[4]} | {self.position[5]}
---------
{self.position[6]} | {self.position[7]} | {self.position[8]}'''
########## 若干测试 #########
'''
# 人机对弈
TheBoard = TTTBoard()
print(TheBoard)
while True:
myMove = int(input('input your position:'))
TheBoard = TheBoard.move(myMove)
if TheBoard.is_draw():
print(TheBoard)
print('Draw!')
break
if TheBoard.is_win():
print(TheBoard)
if TheBoard.turn() == 'X':
print('Ai win!')
else:
print('You win!')
break
bstMove = minimax.find_best_move(TheBoard,'O')
TheBoard = TheBoard.move(bstMove)
if TheBoard.is_draw():
print(TheBoard)
print('Draw!')
break
if TheBoard.is_win():
print(TheBoard)
if TheBoard.turn() == 'X':
print('Ai win!')
else:
print('You win!')
break
print(TheBoard)
'''
# 测试一种情况 看ai决策是否正确
testBoard = TTTBoard(position=[
TTTPiece(' '),TTTPiece('O'),TTTPiece(' '),
TTTPiece('X'),TTTPiece('X'),TTTPiece(' '),
TTTPiece(' '),TTTPiece(' '),TTTPiece(' ')
])
print(testBoard)
print(minimax.find_best_move(testBoard,'O'))
# 以下 自我对弈
'''
testBoard = TTTBoard()
res = 0
orig = 'O'
while res != -1:
if orig == 'O':
orig = 'X'
else:
orig = 'O'
if testBoard.is_draw() or testBoard.is_win():
break
res = minimax.find_best_move(testBoard,orig)
testBoard = testBoard.move(res)
print(testBoard)
'''