程序设计思路
大致思路:
井字棋最后的结果无非就是玩家赢、电脑赢和平局三种结果,而最后的结果正对应这整棵棋盘生成树的叶子节点,那么可以把玩家赢、电脑赢和平局三种结果赋值,然后根据当前深度是Max层还是Min层来,往上推出该子节点的父子节点的值,一直往上走到根节点,这也正是极大极小搜索的一个过程,而alpha-beta算法只要在极大极小上加以修改即可。
主要步骤和代码
1、 棋盘:用一个长度为9的数组存储每一位的字符,其中空位为‘-’,玩家棋子默认为‘O’,电脑玩家默认为‘#’,该数组为全局变量;定义print_board()函数来输出当前棋盘状况;定义reset_board()函数来重置棋盘;
2、 先手问题:定义一个变量first,根据输入来定义先手,输入1为玩家先手,输入-1为电脑先手;
first=0#先手变量,玩家先手为1,电脑先手为-1
board_list=['-' for i in range(9)]#棋盘位置以数组存储
#重置棋盘
def reset_board():
global board_list#将其定义成全局变量
board_list=['-' for i in range(9)]
#输出棋盘
def print_board():
print("棋盘:")
for i in range(1,10):
if i%3==0:
print(board_list[i-1])
else:
print(board_list[i-1],end="")
return board_list
3、 胜者判断:定义check_win( )函数来判断当前是否有练成一线的情况,胜利的情况有8种,水平线3种,竖直线3种,斜线2种,根据位置的下标加以判断;定义winner( )函数判断赢家,如果是玩家先手,则玩家赢赋值为1,电脑赢为-1,平局为0;如果是电脑赢,则电脑赢赋值为1,玩家赢为-1,平局为0,这样可以方便Max和Min的区别;
#判断胜负
def check_win(tag):
return ((board_list[0] == tag and board_list[1] == tag and board_list[2] == tag) or # 顶水平连线
(board_list[3] == tag and board_list[4] == tag and board_list[5] == tag) or # 中间水平连线
(board_list[6] == tag and board_list[7] == tag and board_list[8] == tag) or # 底部水平连线
(board_list[0] == tag and board_list[3] == tag and board_list[6] == tag) or # 左侧垂直连线
(board_list[1] == tag and board_list[4] == tag and board_list[7] == tag) or # 中间垂直连线
(board_list[2] == tag and board_list[5] == tag and board_list[8] == tag) or # 右侧垂直连线
(board_list[0] == tag and board_list[4] == tag and board_list[8] == tag) or # 左斜线
(board_list[2] == tag and board_list[4] == tag and board_list[6] == tag)) # 右斜线
#判断赢家
def winner():
'''
当玩家先手时,max为玩家,所以返回1表示玩家赢,返回-1表示电脑赢,返回0表示平局或者还没结束;
当电脑先手时,max为电脑,所以返回1表示电脑赢,返回-1表示玩家赢,返回0表示平局或者还没结束。
'''
if check_win('O')==True and first==1 or check_win('X') and first==-1:
return 1
elif check_win('X')==True and first==1 or check_win('O') and first==-1:
return -1
else:
return 0
4、 玩家下棋:定义player( )函数来实现玩家下棋,通过输入位置0-8来下自己想要下的位置,同时也需要判断下得位置是否能下;
#玩家下棋
def player():
while(1):
print("输入想要走的位置,0-8:")
number=int(input())
if board_list[number]=='-':
board_list[number]='O'
break
else:
print("该位置有棋子!请输入数字0-8!")
continue
5、 极大极小算法:定义max_min_search( )函数实现极大极小算法。把玩家赢、电脑赢和平局三种结局的赋值当作当前叶子节点的评估值,然后根据当前深度是Max层还是Min层来,往上推出该子节点的父子节点的值,一直往上走,这一过程可以通过递归函数生成。比如玩家先手下了一个棋子,那么轮到电脑时,电脑调用极大极小算法,把剩下每个空位的值给算出来,然后比较选出这些位置中算出值最小的一个位置,而这计算空位的值时,电脑会自行模拟玩家选择当前最优的一步,这样自己交替下棋,完成一个棋盘的整个下棋动作。
#极大极小值算法
def max_min_search(player,nextplayer):
'''
通过递归,从叶子节点,推出父子节点(即往上推出)的值,该树的取值只有三种要么1,要么-1,要么0。
player:当前玩家;
nextplayer:下一个玩家;
'''
global first
max_val=-2
min_val=2
win=winner()
#如果是叶子节点,即最后棋局结果,就返回胜利者的值
if win!=0:
return win
#如果下满没有赢家,为平局
elif '-' not in board_list:
return 0
#如果不是叶子节点,则通过叶子节点来推出当下节点的值。遍历当前所有能下的位置,找到这层节点中最大或者最小的值。
for left_index in range(len(board_list)):
if board_list[left_index]=='-':
board_list[left_index]=player
val=max_min_search(nextplayer,player)
board_list[left_index]='-'#还原
if player=='O' and first==1 or player=='X' and first==-1:
if max_val<val:
max_val=val
elif player=='X' and first==1 or player=='O' and first==-1:
if min_val>val:
min_val=val
#需要对先手情况进行判断,先手返回的是max,后手返回的是min,然后将最大或者最小的值传给父子节点
if player=='O' and first==1 or player=='X' and first==-1:
reval=max_val
elif player=='X' and first==1 or player=='O' and first==-1:
reval=min_val
return reval
6、 Alpha-beta算法:定义alpha_beta_search( )函数实现Alpha-beta算法。在极大极小算法的基础上,加入alpha和beta来进行剪枝,因为这里三种结果的赋值为-1、0、1,这里设置alpha初始值为-2,beta值为2;如果当前子节点的父子节点为Min层,那么根据第一个子节点得出beta值下界,如果后面子节点比beta大则进行剪枝不再往下深究,如果后面子节点有比beta小的则更新beta的值,最后把全部子节点筛选完,父子节点就为beta值;如果当前子节点的父子节点为Max层,那么根据第一个子节点得出alpha值上界,如果后面子节点有比alpha小则进行剪枝,不再往下进行深究,alpha大的值则更新alpha,最后把全部子节点筛选完,父子节点就是alpha值。
#alpha-beta算法
def alpha_beta_search(player,nextplayer,alpha,beta):
global first
win=winner()
if win!=0:
return win
elif '-' not in board_list:
return 0
for left_index in range(len(board_list)):
if board_list[left_index]=='-':
board_list[left_index]=player
val=alpha_beta_search(nextplayer,player,alpha,beta)
board_list[left_index]='-'
if player=='O' and first==1 or player=='X' and first==-1:
if alpha<val:
alpha=val
if alpha>=beta:
return beta #直接返回当前的最大值beta,进行剪枝
elif player=='X' and first==1 or player=='O' and first==-1:
if beta>val:
beta=val
if beta<=alpha:
return alpha #直接返回当前的最小值beta,进行剪枝
#需要对先手情况进行判断,先手返回的是max,后手返回的是min,然后将最大或者最小的值传给父子节点
if player=='O' and first==1 or player=='X' and first==-1:
reval=alpha
elif player=='X' and first==1 or player=='O' and first==-1:
reval=beta
return reval
7、 电脑下棋:定义一个computer( )函数来实现电脑下棋,通过调用max_min_search( )或者alpha_beta_search( ),来推算出当前最优的下棋位置。这个推算过程中,电脑也来模拟人下棋,也是调用极大极小算法或者alpha-beta算法,算出当前对玩家最优位置。假设玩家先手走了第一部棋子,电脑把剩下的空位都走一遍第二步棋,电脑自行模拟玩家走第三步棋子,也是调用相应算法得出当前玩家最优的位置,这样轮流走到最后结果,然后电脑选出最后局面中对自己最优的结果,回溯得到第二步棋子应该下什么(假设玩家先手下了第一步棋)。
#电脑决定下棋位置
def computer():
'''
best_val:用来记录先手为玩家时,电脑取min时,能取的最小值
best_val2:用来记录先手为电脑时,电脑取max时,能取的最大值
my_moves:用来记录电脑能走的位置
'''
best_val=2
best_val2=-2
my_moves=[]
for index in range(len(board_list)):
if board_list[index]=='-':
board_list[index]='X'
val=max_min_search('O','X')
#val=alpha_beta_search('O','X',-2,2)
board_list[index]='-'
if first==1:
if val < best_val:
best_val=val
my_moves=[index]
elif val==best_val:
my_moves.append(index)
elif first==-1:
if val > best_val2:
best_val2=val
my_moves=[index]
elif val==best_val2:
my_moves.append(index)
print(my_moves)
return random.choice(my_moves)
8、主函数:
def main():
while(1):
global first
while(1):
first=int(input('选择是先手对象,如果选择玩家则输入1,选择电脑输入-1,退出输入0:'))
if first==-1 or first==1 or first==0:
next_move=first
break
else:
print('输入错误!重新输入!')
continue
if next_move==0:
break
while '-' in board_list and winner()==0:
print_board()
if next_move==1 and '-' in board_list:
player()
next_move=-1
if next_move==-1 and '-' in board_list:
computer_move=computer()
board_list[computer_move]='X'
next_move=1
print('电脑走了'+str(computer_move))
print_board()
if first==1:
print(["电脑赢了", "平局", "你赢了"][winner()+1])
else:
print(["你赢了", "平局", "电脑赢了"][winner()+1])
reset_board()
main()
对于两个算法流程图
根据个人理解手写的,写得不好多见谅!如果实在理解不了可以在csdn再搜索,挺多的!


运行结果
结果挺多的,在这里我就贴一张极大极小算法玩家先手的情况:
电脑胜利:

平局:


本文介绍了使用Python实现井字棋AI的极大极小搜索和Alpha-beta剪枝算法。详细阐述了算法思路,包括棋盘状态、先手判断、胜者检测、玩家和电脑下棋的逻辑,并给出了两种算法的流程图和部分运行结果展示。
1万+

被折叠的 条评论
为什么被折叠?



