人机对战
在之前的博文基于tkinter的五子棋游戏中使用tkinter做了一个简单的五子棋游戏,只能实现人人对战,后来想着加上人机对战的功能。
不过,最初想想还是挺麻烦的,计算机怎么评估当前的棋局,找到最佳或者较佳的落子点呢,脑子真是越来越不灵光了。站在巨人的肩膀上,科学技术才在这几百年发展的如此迅速,先看看别人怎么做的吧,果然别人实现起来也就那么几行代码,真是惭愧啊,也感谢这个开源的时代,让我们可以站得更高,看得更远。
计算机算法
计算机根据黑白双方现有落子情况进行棋局评估,给出各空白落子点处的分数。五子棋的几种基本棋形包括连五,活四,冲四,活三,眠三,活二,眠二等,不同的棋形打不同的分数。这个分数不但要考虑到了己方的棋子,还要考虑对方的局势,即要分别给双方打分,并求和,这样就兼具了攻击和防守的能力。不过,这里算法上与原算法相比略去一部分,这大概也导致了这个计算机有点傻😂
对于空位的遍历也是很费资源的,所以这里参考了引用博文中做法,仅遍历了已有棋子周边的空位。
源码
保留了原程序的架构,有些地方又偷懒,所以代码有点乱
from tkinter import *
import random
offset = [(1, 0), (0, 1), (1, 1), (1, -1)]
class GoBang():
def __int__(self):
self.window = Tk()
self.window.title("五子棋")
self.window.iconbitmap('./gobang.ico')
self.window.geometry("500x560")
self.window.canvas = Canvas(self.window, width=480, height=480, bg='peachpuff')
self.window.canvas.pack()
for num in range(1, 16):
if num == 1 or num == 15:
self.window.canvas.create_line(num * 30, 30,
num * 30, 450,
width=2)
else:
self.window.canvas.create_line(num * 30, 30,
num * 30, 450,
width=1)
for num in range(1, 16):
if num == 1 or num == 15:
self.window.canvas.create_line(30, num * 30,
450, num * 30,
width=2)
else:
self.window.canvas.create_line(30, num * 30,
450, num * 30,
width=1)
self.window.canvas.create_oval(8 * 30 - 2, 8 * 30 - 2, 8 * 30 + 2,
8 * 30 + 2, fill='black')
self.window.canvas.create_oval(5 * 30 - 2, 5 * 30 - 2, 5 * 30 + 2,
5 * 30 + 2, fill='black')
self.window.canvas.create_oval(5 * 30 - 2, 11 * 30 - 2, 5 * 30 + 2,
11 * 30 + 2, fill='black')
self.window.canvas.create_oval(11 * 30 - 2, 5 * 30 - 2, 11 * 30 + 2,
5 * 30 + 2, fill='black')
self.window.canvas.create_oval(11 * 30 - 2, 11 * 30 - 2, 11 * 30 + 2,
11 * 30 + 2, fill='black')
self.startBtn = Button(self.window, text='Start', bg="DeepSkyBlue", width=8, height=1, command=self.chessGo)
self.quitBtn = Button(self.window, text='Quit', bg="OrangeRed", width=8, height=1, command=self.window.quit)
self.withDrawBtn = Button(self.window, text='Withdraw', bg="cyan", width=8, height=1, command=self.withDraw)
self.startBtn.place(relx=0.2, rely=0.89)
self.quitBtn.place(relx=0.6, rely=0.89)
self.withDrawBtn.place(relx=0.4, rely=0.89)
self.game_print = StringVar()
self.labelInfo = Label(self.window, width=12, textvariable=self.game_print, font=("Arial", 10, "bold"),
justify="center")
self.labelInfo.place(relx=0.36, rely=0.95)
self.window.canvas.bind("<Button-1>", self.place)
self.game_print.set("请开始新局")
self.chess = [([0] * 15) for i in range(15)]
# 悔棋用的顺序列表
self.order = []
# 棋子颜色
self.color_count = 0
self.color = 'black'
self.flag_win = 0
self.flag_go = 0
self.window.mainloop()
def chessGo(self):
if self.startBtn['text'] == "Start":
#
self.flag_go = 1
self.startBtn['text'] = "Going"
self.game_print.set('请black落子')
self.labelInfo['fg'] = 'black'
elif self.startBtn['text'] == "New":
self.startBtn['text'] = "Start"
self.newGame()
def newGame(self):
self.window.canvas.delete("chessman")
self.chess = [([0] * 15) for i in range(15)]
self.order = []
self.color_count = 0
self.color = 'black'
self.flag_win = 0
self.flag_go = 0
self.game_print.set('请开始新局')
self.labelInfo['fg'] = 'black'
def withDraw(self):
if len(self.order) == 0 or self.flag_win == 1:
self.game_print.set("Can't Withdraw")
self.labelInfo['fg'] = 'sandybrown'
return
else:
self.window.canvas.delete("chessman")
# add for machine
xylist = self.order.pop()
self.chess[xylist[0]][xylist[1]] = 0
#####
xylist = self.order.pop()
self.chess[xylist[0]][xylist[1]] = 0
self.color_count = 0
for xylist in self.order:
y = xylist[0]
x = xylist[1]
if self.color_count == 0:
self.chess[y][x] = 1
self.color = 'black'
elif self.color_count == 1:
self.chess[y][x] = 2
self.color = 'white'
self.color_count = 1 - self.color_count
self.window.canvas.create_oval((x + 1) * 30 - 12, (y + 1) * 30 - 12, (x + 1) * 30 + 12,
(y + 1) * 30 + 12, fill=self.color,
tags="chessman")
if self.color_count:
self.color = 'white'
else:
self.color = 'black'
self.game_print.set("请" + self.color + "落子")
self.labelInfo['fg'] = 'black'
def place(self, event):
if (self.flag_win == 0 and self.flag_go == 1):
print("enter place")
x, y = event.x, event.y
x = round(x / 30 - 1)
y = round(y / 30 - 1)
if x < 0 or x > 14 or y < 0 or y > 14:
# tkinter.messagebox.INFO('提示', '请在棋盘上落子!')
self.game_print.set('越界落子!')
self.labelInfo['fg'] = "darkorange"
return
# add for machine 221109
self.color_count = 0
if self.chess[y][x] == 0:
print("enter paint oval")
if self.color_count == 0:
self.chess[y][x] = 1
self.color = 'black'
elif self.color_count == 1:
self.chess[y][x] = 2
self.color = 'white'
self.color_count = 1 - self.color_count
self.window.canvas.create_oval((x + 1) * 30 - 12, (y + 1) * 30 - 12, (x + 1) * 30 + 12,
(y + 1) * 30 + 12, fill=self.color,
tags="chessman")
self.order.append([y, x])
else:
# tkinter.messagebox.INFO('提示', '此处已有棋子!')
self.game_print.set('请别处落子!')
self.labelInfo['fg'] = "darkorange"
return
if self.win(x, y):
self.flag_win = 1
self.startBtn['text'] = "New"
if self.color_count == 1:
self.color = 'black'
else:
self.color = 'white'
# self.messagebox.INFO('提示', '白棋胜!')
self.game_print.set(self.color + "胜!")
self.labelInfo['fg'] = "red"
else:
if self.color_count:
self.color = 'white'
else:
self.color = 'black'
self.game_print.set("请" + self.color + "落子")
self.labelInfo['fg'] = "black"
point = self.machine_drop()
y = point[0]
x = point[1]
self.chess[y][x] = 2
self.color = 'white'
self.color_count = 0
self.window.canvas.create_oval((x + 1) * 30 - 12, (y + 1) * 30 - 12, (x + 1) * 30 + 12,
(y + 1) * 30 + 12, fill=self.color,
tags="chessman")
self.order.append([y, x])
if self.win(x, y):
self.flag_win = 1
self.startBtn['text'] = "New"
if self.color_count == 1:
self.color = 'black'
else:
self.color = 'white'
# self.messagebox.INFO('提示', '白棋胜!')
self.game_print.set(self.color + "胜!")
self.labelInfo['fg'] = "red"
else:
if self.color_count:
self.color = 'white'
else:
self.color = 'black'
self.game_print.set("请" + self.color + "落子")
self.labelInfo['fg'] = "black"
def machine_drop(self):
_score = 0
point = [-1,-1]
for i in range(0,15):
for j in range(0,15):
if self.can_drop(i,j):
score = self.get_point_score(i,j)
print(i,j,score)
if score > _score:
point[0] = i
point[1] = j
_score = score
elif score == _score:
r = random.randint(0, 100)
if r % 2 == 0:
point[0] = i
point[1] = j
print(point)
return point
def can_drop(self, y, x):
if self.chess[y][x] != 0:
return False
else:
for i in (-1, 0, 1):
for j in (-1, 0, 1):
if y + i >= 0 and y + i <= 14 and x + j >= 0 and x + j <= 14:
if self.chess[y + i][x + j] != 0:
return True
return False
def get_point_score(self, y, x):
score = 0
for os in offset:
score += self.get_direction_score(y, x, os[0], os[1])
return score
def get_direction_score(self, y, x, y_offset, x_offset):
count = 0 # 落子处我方连续子数
_count = 0 # 落子处对方连续子数
space = None # 我方连续子中有无空格
_space = None # 对方连续子中有无空格
both = 0 # 我方连续子两端有无阻挡
_both = 0 # 对方连续子两端有无阻挡
tmp_x = x
tmp_y = y
# 如果是 2 表示是边上是我方子,1 表示敌方子
if 0 <= y+y_offset <=14 and 0 <= x + x_offset <=14:
flag = self.chess[y + y_offset][x + x_offset]
if flag != 0:
for step in range(1, 6):
x += x_offset
y += y_offset
if 0 <= x < 15 and 0 <= y < 15:
if flag == 2:
if self.chess[y][x] == 2:
count += 1
if space is False:
space = True
elif self.chess[y][x] == 1:
_both += 1
break
else:
if space is None:
space = False
else:
break # 遇到第二个空格退出
elif flag == 1:
if self.chess[y][x] == 2:
_both += 1
break
elif self.chess[y][x] == 1:
_count += 1
if _space is False:
_space = True
else:
if _space is None:
_space = False
else:
break
else:
# 遇到边也就是阻挡
if flag == 2:
both += 1
elif flag == 1:
_both += 1
if space is False:
space = None
if _space is False:
_space = None
x = tmp_x
y = tmp_y
if 0 <= y - y_offset <= 14 and 0 <= x - x_offset <= 14:
_flag = self.chess[y - y_offset][x - x_offset]
if _flag != 0:
for step in range(1, 6):
x -= x_offset
y -= y_offset
if 0 <= x < 15 and 0 <= y < 15:
if _flag == 2:
if self.chess[y][x] == 2:
count += 1
if space is False:
space = True
elif self.chess[y][x] == 1:
_both += 1
break
else:
if space is None:
space = False
else:
break # 遇到第二个空格退出
elif _flag == 1:
if self.chess[y][x] == 2:
_both += 1
break
elif self.chess[y][x] == 1:
_count += 1
if _space is False:
_space = True
else:
if _space is None:
_space = False
else:
break
else:
# 遇到边也就是阻挡
if _flag == 1:
both += 1
elif _flag == 2:
_both += 1
score = 0
if count == 4:
score = 10000
elif _count == 4:
score = 9000
elif count == 3:
if both == 0:
score = 1000
elif both == 1:
score = 100
else:
score = 0
elif _count == 3:
if _both == 0:
score = 900
elif _both == 1:
score = 90
else:
score = 0
elif count == 2:
if both == 0:
score = 100
elif both == 1:
score = 10
else:
score = 0
elif _count == 2:
if _both == 0:
score = 90
elif _both == 1:
score = 9
else:
score = 0
elif count == 1:
score = 10
elif _count == 1:
score = 9
else:
score = 0
if space or _space:
score /= 2
return score
def win(self, x, y):
count = 1
# -计算
for i in range(-1, -5, -1):
if x + i < 0:
break
else:
if self.chess[y][x + i] == self.chess[y][x]:
count += 1
else:
break
for i in range(1, 5, 1):
if x + i > 14:
break
else:
if self.chess[y][x + i] == self.chess[y][x]:
count += 1
else:
break
if count >= 5:
return True
# |计算
count = 1
for i in range(-1, -5, -1):
if y + i < 0:
break
else:
if self.chess[y + i][x] == self.chess[y][x]:
count += 1
else:
break
for i in range(1, 5, 1):
if y + i > 14:
break
else:
if self.chess[y + i][x] == self.chess[y][x]:
count += 1
else:
break
if count >= 5:
return True
# /计算
count = 1
for i in range(-1, -5, -1):
if y + i < 0 or x + i < 0:
break
else:
if self.chess[y + i][x + i] == self.chess[y][x]:
count += 1
else:
break
for i in range(1, 5, 1):
if y + i > 14 or x + i > 14:
break
else:
if self.chess[y + i][x + i] == self.chess[y][x]:
count += 1
else:
break
if count >= 5:
return True
# \计算
count = 1
for i in range(-1, -5, -1):
if y + i < 0 or x - i > 14:
break
else:
if self.chess[y + i][x - i] == self.chess[y][x]:
count += 1
else:
break
for i in range(1, 5, 1):
if y + i > 14 or x - i < 0:
break
else:
if self.chess[y + i][x - i] == self.chess[y][x]:
count += 1
else:
break
if count >= 5:
return True
if __name__ == "__main__":
myBang = GoBang()
myBang.__int__()
运行结果
人机对战取代了人人对战,不是可选模式,人执黑先手,计算机执白后手,可以悔棋。
文章来源:https://www.toymoban.com/news/detail-443554.html
引用
看了不少博文,有些算法应该出自一处,谢谢!
1.Python五子棋人机对战
2.python五子棋之人机对战
3.基于java的人机五子棋文章来源地址https://www.toymoban.com/news/detail-443554.html
到了这里,关于基于Python的五子棋人机对战的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!