python五子棋小游戏实现,其中的AI采取棋盘评分和博弈树算法。

项目背景与规划

Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。
Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比其他语言更有特色语法结构。
人工智能之父 John McCarthy说:人工智能就是制造智能的机器,更特指制作人工智能的程序。人工智能模仿人类的思考方式使计算机能智能的思考问题,人工智能通过研究人类大脑的思考、学习和工作方式,然后将研究结果作为开发智能软件和系统的基础。
项目规划
首先,要将一款游戏(五子棋)以代码和算法的形式呈现出来,需要对游戏规则与方式的准确把握

~游戏构成

~游戏规则

~基本棋型

构成
棋盘15*15

黑白棋子

规则
每人一次下一子,黑棋先下(为先手),白棋后下,黑白双方轮流交替下子,直到黑白中任意一方获胜。
获胜的判定:只要黑白方中任意一方的棋子有五子连在一起(即五子连珠),可以为横连、纵连、斜连,则该方获胜,游戏结束
基本棋型
_为空,○为敌方棋子,●为己方棋子

① 连五:五颗棋子连在一起,获得胜利。

●●●●●

② 活四:四颗棋子相连,同时两端均为空(即有两个位置可以形成连五)。

当活四出现的时候,对方如果单纯采取防守策略时,已经无法阻挡自己的胜利(除非对方采取进攻策略,一招制胜,我们的程序也要注意这一点)

●●●●

③ 死四:四颗棋子,但只有一个位置可以形成连五。

相比活四而言,死四的威胁要小的多,因为这个时候对方只要跟着防守即可。但是死四出现时,其优先级应当比下面提到的活三要高(因为活四虽能轻易破解,但是对于双方都意味着一步结束比赛,故必须注意)。

_●●●●○

●_●●●

●●_●●

④ 活三:可以形成活四的三,有如下常见的几种棋型:

活三棋型是进攻时最常见的棋型。因为活三之后,如果对方不予理会,则可直接一手变成活四。因此当敌方活三出现时,需要进行防守。

●●●

●_●●

⑤ 死三:能够形成死四的三。死三与活三相比,危险系数降低了不少,因为死三即便不去防守,下一手也只能形成死四,我们仍然可以防守的住。

_●●●○ _●_●●○
●●_●○ ● _●●
●_●_● ○_●●●_○

⑥ 活二:能够形成活三的二。活二看似人畜无害,因为它只下一手便能形成活三,等形成活三我们仍能防守。但其实活二其实很重要,因为在开局阶段,如果能够形成较多的活二棋型,那么当我们将活二变成活三时,就能将自己的活三绵延不绝,让对手防不胜防。

_ ●● _

_●_● _

⑦ 死二:能够形成眠三的二。

_ _ _●●○ _ _●_●○
●○ ● _ _●
代码实现
GobangGUI.py
(主代码,调用chessboard与ai中的方法)
1.定义线程类来执行AI算法
2.定义窗口并调用棋盘类绘制棋盘:
(1)图片加载 1棋盘背景加载2黑白子加载3落子可视化
(2)音效加载 1落子音效 2结束音效(win/lost)
(3)参数设置
3.UI优化实现交互简洁友好 :
(1)窗口名称图标设置
(2)鼠标化为黑子,并实现随鼠标移动
(3)电脑落子位置标记
4.实现玩家落子: 在棋盘上打印棋子,通过线程传递参数(UI绘制坐标/逻辑坐标)
5.实现电脑落子: 通过AI算法获得最佳落子位置,传出参数,实现落子
(UI绘制坐标/逻辑坐标)定义坐标转化算法
6.调用棋盘类判断输赢并弹出QMessageBox选择退出(离开)还是重置(再来一次)

from chessboard import ChessBoard
from ai import searcherWIDTH = 540      #窗口宽度
HEIGHT = 540     #窗口高度
MARGIN = 22      #窗口边缘宽度
GRID = (WIDTH - 2 * MARGIN) / (15 - 1)
PIECE = 34
EMPTY = 0
BLACK = 1
WHITE = 2
#从PyQt5中引入库
import sys
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QMessageBox
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap, QIcon, QPalette, QPainter
from PyQt5.QtMultimedia import QSound#定义线程类执行AI算法
class AI(QtCore.QThread):finishSignal = QtCore.pyqtSignal(int, int)# 构造函数里增加形参def __init__(self, board, parent=None):super(AI, self).__init__(parent)self.board = board# 重写 run() 函数def run(self):self.ai = searcher()self.ai.board = self.boardscore, x, y = self.ai.search(2, 2)self.finishSignal.emit(x, y)#重新定义Lable类
class LaBel(QLabel):def __init__(self, parent):super().__init__(parent)self.setMouseTracking(True)def enterEvent(self, e):e.ignore()class GoBang(QWidget):def __init__(self):super().__init__()self.initUI()def initUI(self):self.chessboard = ChessBoard()  # 棋盘类# 设置棋盘背景palette1 = QPalette()  palette1.setBrush(self.backgroundRole(), QtGui.QBrush(QtGui.QPixmap('img/chessboard.jpg')))self.setPalette(palette1)# self.setStyleSheet("board-image:url(img/chessboard.jpg)")self.setCursor(Qt.PointingHandCursor)        # 鼠标变成手指形状#音效加载self.sound_piece = QSound("sound/move.wav")  # 加载落子音效self.sound_win = QSound("sound/win.wav")     # 加载胜利音效self.sound_defeated = QSound("sound/defeated.wav")  # 加载失败音效self.resize(WIDTH, HEIGHT)  # 固定大小 540*540self.setMinimumSize(QtCore.QSize(WIDTH, HEIGHT))self.setMaximumSize(QtCore.QSize(WIDTH, HEIGHT))self.setWindowTitle("GoBang")  # 窗口名称self.setWindowIcon(QIcon('img/black.png'))  # 窗口图标#加载黑白棋子self.black = QPixmap('img/black.png')self.white = QPixmap('img/white.png')self.piece_now = BLACK  # 黑棋先行self.my_turn = True  # 玩家先行self.step = 0  # 步数self.x, self.y = 1000, 1000#将鼠标图标改为黑色棋子self.mouse_point = LaBel(self)  # 将鼠标图片改为棋子self.mouse_point.setScaledContents(True)self.mouse_point.setPixmap(self.black)  # 加载黑棋self.mouse_point.setGeometry(270, 270, PIECE, PIECE)self.pieces = [LaBel(self) for i in range(225)]  # 新建棋子标签,准备在棋盘上绘制棋子for piece in self.pieces:piece.setVisible(True)  # 图片可视piece.setScaledContents(True)  # 图片大小根据标签大小可变self.mouse_point.raise_()  # 鼠标始终在最上层self.ai_down = True  # AI已下棋,主要是为了加锁,当值是False的时候说明AI正在思考,这时候玩家鼠标点击失效,要忽略掉 mousePressEventself.setMouseTracking(True)self.show()#用箭头显示出电脑落子的位置def paintEvent(self, event):qp = QPainter()qp.begin(self)self.drawLines(qp)qp.end()# 黑色棋子随鼠标移动def mouseMoveEvent(self, e):  # self.lb1.setText(str(e.x()) + ' ' + str(e.y()))self.mouse_point.move(e.x() - 16, e.y() - 16)# 实现玩家落子def mousePressEvent(self, e):  # 玩家下棋if e.button() == Qt.LeftButton and self.ai_down == True:x, y = e.x(), e.y()  # 鼠标坐标i, j = self.coordinate_transform_pixel2map(x, y)  # 对应棋盘坐标if not i is None and not j is None:  # 棋子落在棋盘上,排除边缘if self.chessboard.get_xy_on_logic_state(i, j) == EMPTY:  # 棋子落在空白处self.draw(i, j)self.ai_down = Falseboard = self.chessboard.board()self.AI = AI(board)  # 新建线程对象,传入棋盘参数self.AI.finishSignal.connect(self.AI_draw)  # 结束线程,传出参数self.AI.start()  # rundef AI_draw(self, i, j):if self.step != 0:self.draw(i, j)  # AIself.x, self.y = self.coordinate_transform_map2pixel(i, j)self.ai_down = Trueself.update()def draw(self, i, j):x, y = self.coordinate_transform_map2pixel(i, j)if self.piece_now == BLACK:self.pieces[self.step].setPixmap(self.black)  # 放置黑色棋子self.piece_now = WHITEself.chessboard.draw_xy(i, j, BLACK)else:self.pieces[self.step].setPixmap(self.white)  # 放置白色棋子self.piece_now = BLACKself.chessboard.draw_xy(i, j, WHITE)self.pieces[self.step].setGeometry(x, y, PIECE, PIECE)  # 画出棋子self.sound_piece.play()  # 落子音效self.step += 1  # 步数+1winner = self.chessboard.anyone_win(i, j)  # 判断输赢if winner != EMPTY:self.mouse_point.clear()self.gameover(winner)def drawLines(self, qp):  # 指示AI当前下的棋子if self.step != 0:pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine)qp.setPen(pen)qp.drawLine(self.x - 5, self.y - 5, self.x + 3, self.y + 3)qp.drawLine(self.x + 3, self.y, self.x + 3, self.y + 3)qp.drawLine(self.x, self.y + 3, self.x + 3, self.y + 3)# 从 chessMap 里的逻辑坐标到 UI 上的绘制坐标的转换def coordinate_transform_map2pixel(self, i, j):return MARGIN + j * GRID - PIECE / 2, MARGIN + i * GRID - PIECE / 2# 从 UI 上的绘制坐标到 chessMap 里的逻辑坐标的转换def coordinate_transform_pixel2map(self, x, y):i, j = int(round((y - MARGIN) / GRID)), int(round((x - MARGIN) / GRID))# 有MAGIN, 排除边缘位置导致 i,j 越界if i < 0 or i >= 15 or j < 0 or j >= 15:return None, Noneelse:return i, j#定义游戏结束后弹出QMessageboxdef gameover(self, winner):#胜利if winner == BLACK:self.sound_win.play()reply = QMessageBox.question(self, 'You Win!', 'Continue?',QMessageBox.Yes | QMessageBox.No, QMessageBox.No)#失败else:self.sound_defeated.play()reply = QMessageBox.question(self, 'You Lost!', 'Continue?',QMessageBox.Yes | QMessageBox.No, QMessageBox.No)# 若选择Yes,重置棋盘if reply == QMessageBox.Yes:  self.piece_now = BLACKself.mouse_point.setPixmap(self.black)self.step = 0for piece in self.pieces:piece.clear()self.chessboard.reset()self.update()#若选择No时关闭窗口else:self.close()if __name__ == '__main__':app = QApplication(sys.argv)ex = GoBang()sys.exit(app.exec_())

Chessboard.py
1.定义棋盘类(定义方法获取落子点坐标,指定点坐标,制定坐标的状态(有无子)

2.绘制棋盘形状

3.定义判断输赢方法(每次落子检测米子方向是否达到五子)

4.定义“重置”清空棋盘

#定义棋子类型,输赢状况
EMPTY = 0
BLACK = 1
WHITE = 2#定义棋盘类,绘制棋盘的形状,切换先后手。判断输赢...class ChessBoard(object):def __init__(self):self.__board = [[EMPTY for n in range(15)] for m in range(15)]self.__dir = [[(-1, 0), (1, 0)], [(0, -1), (0, 1)], [(-1, 1), (1, -1)], [(-1, -1), (1, 1)]]#                (左      右)      (上       下)     (左下     右上)      (左上     右下)def board(self):  # 返回数组对象return self.__boarddef draw_xy(self, x, y, state):  # 获取落子点坐标的状态self.__board[x][y] = statedef get_xy_on_logic_state(self, x, y):  # 获取指定点坐标的状态return self.__board[x][y]def get_next_xy(self, point, direction):  # 获取指定点的指定方向的坐标x = point[0] + direction[0]y = point[1] + direction[1]if x < 0 or x >= 15 or y < 0 or y >= 15:return Falseelse:return x, ydef get_xy_on_direction_state(self, point, direction):  # 获取指定点的指定方向的状态if point is not False:xy = self.get_next_xy(point, direction)if xy is not False:x, y = xyreturn self.__board[x][y]return Falsedef anyone_win(self, x, y):state = self.get_xy_on_logic_state(x, y)for directions in self.__dir:  # 对米字的4个方向分别检测是否有5子相连的棋count = 1for direction in directions:  # 对落下的棋子的同一条线的两侧都要检测,结果累积point = (x, y)while True:if self.get_xy_on_direction_state(point, direction) == state:count += 1point = self.get_next_xy(point, direction)else:breakif count >= 5:return statereturn EMPTYdef reset(self):  # 重置self.__board = [[EMPTY for n in range(15)] for m in range(15)]

ai.py
1.着棋估值
通过着棋估值算法啊,基本的ai已经有了,但要想像现实中的棋手一样有“远见”(阴谋,套路) 需要下一步博弈树的优化
着棋估值,(棋盘打分算法,棋型打分算法)是整个程序中最关键的一步。因为估值方法,是教会电脑判断如何根据当前棋盘形式,找到最适合的着棋位置的关键。而一个好的估值方法,也能大大提高电脑AI的获胜概率。

要求给定棋盘上一个点,求出该点在当前棋局下的权值。若在该点落子后更容易接近胜利,则该点权值就高,越接近5子相连,权值越高。
首先考虑每个点有8个方向可以连子,每个方向上又有多种连子棋型,如活四、活三、死三等,而这些子又可能属于己方或者对方。活四与活三的权值自然不同。而同样是活三,己方的活三与对方的活三权值也不同,这样才能实现攻守的策略。假如现在棋局上同时有己方的活三和对方的活三,此时轮到我方落子,则正常情况下应当在己方活三上落子,使之成为活四,从而获胜。则计算机在判断棋局时,遇到己方活三,权值应当较高,遇到对方活三,权值应当较低。
对于一个棋局, 判断它对我来说是占优势还是劣势, 能不能用个比较确定的数值来评估呢?答案是可以的。 对于五子棋就是统计目前的棋型,并累加分数。 比如如果有4个子连起来了, 那就给个很高的评分,因为下一步就可以赢了, 如果是3个子连起来了,给个相对较低的评分,因为不一定就能赢,对方会堵你呢, 但是比只有2 个子连在一起的得分要高吧, 如是就有了棋型评分表
由于着眼处在于对棋型的判断,而不是方向,所以首先应该想个方法把方向问题先解决掉,这样在棋型判断时就能够对各个方向进行比较统一的处理,不至于棋型判断时对每个方向都写一段代码。
 继续分析,在判断棋型时,着眼点在于棋子的相对位置,而常见棋型都呈线形排列,所以这个相对位置也就是顺序。相对位置、顺序,很容易想到要用一维的坐标解决。若取某一斜列(行、列),假设当前点的坐标为0,取右下(下、右、右上)为正方向,则在该斜列(行、列)上各点都能得到相应的坐标。
给定一个点、一个方向、一个相对坐标值,就能得到一个二维坐标,对应棋盘上一个点,进而可以获得任意一点的落子情况。

棋型判断
  对于方向的处理完成后,就是棋型的判断。判断棋型时需要区分当前所判断的棋型是哪一方的,假设当前所判断的棋型所属方的代号为plyer,则它的值可以是1或2,而要确定这个plyer是自己还是对方,就需要和自己的代号比对一下,假设自己的代号是me。则这个判断棋型的函数应该满足以下要求:给出一个点p,自己的代号me,一个plyer,能得出当前点对应plyer的权值。
此外由于两个或多个方向上都有活二的棋型较为常见且胜率较高。所以又增加对此种棋型的判断。
即在每一个方向的棋型判断中扫描0110或1110并计数,若最终计数值大于等于2,则权值增加一个较大的数值,否则不增加。
至此只要循环8次,每次循环中扫描各个棋型,并更新权值即可。

2.博弈树搜索(走一步看三布)(避免最糟糕形式的发生以及构建无法破解的局面)

采用构建博弈树的方式,选择能够导致未来最佳情形的策略。所谓博弈树的构建,其实是以当前棋局为根节点,然后下一步,我们可能在当前的任意一个空位着棋,那么生成相应数目的叶节点(即每个叶节点,是我们在其父结点的基础上,着下一棋的结果)。

那么这样,我们重复多次之后,就有可能生成如下的博弈树:



(图片来自互联网)
这里,我们只需要简单的递归即可实现这个步骤。我们只需分析每个叶节点的权值(也就是未来几步的情形),从中选取最好的情形,并按照这个策略着棋即可。

极大极小值搜索

对于AI要走在那里最好,那就是计算它在走在某一个点后, 计算局面的得分,然后取得分最大的那个点,不就是最应该下的点吗? so easy! 这就是极大值搜索。
但不要忘了, 这是只考虑了一步, 搜索的深度只有1, 没听说老谋深算的家伙都是考虑3步的吗, 也就是要考虑下了这一步后,对手下一步会怎么下。对手不傻,肯定会在我得分最小的那个点上下, 这个得分是相对于我而言的,我的得分最小, 那就是对手的最优策略了, 这就是极小值搜索。

了解alpha/beta 剪枝减少计算量
α为已知的最大值, β为已知的最小值, 因为还没搜索不知道是多少,保险起见,初始化为-∞ 和+∞。

搜索到D的时候,局面得分是5,(顺便说一句,这样的搜索是深度优先搜索)那么也就是说要搜索最大值,那么只可能会在(5,+∞) 之间, 同理,要搜索最小值,也只会在(-∞,5)之间。
继续搜索, 搜索到G时,F暂时等于6 ,F是要找最大值, 那么F不可能再小于6了, 而B是要找最小值的,B的已知最小值是在(-∞,5)之间的, 你F还不可能比6小了, 我还要搜索你F后面的情况干嘛?不是浪费时间吗, 于是果断咔嚓掉F这个分支,不搜索了, 这就是剪枝。
同样对于另外一边的已知可能的极限范围β也是一样的情况,遇到就算是搜索也是浪费时间的情况,就剪枝不搜索了。
这样就减少了很多不必要是搜索步骤, 特别是一开始就找到最有可能的极大极小值, 更能迅速的剪枝。

#evaluation:棋盘评估类,给当前棋盘打分
class evaluation(object):def __init__(self):self.POS = []for i in range(15):row = [(7 - max(abs(i - 7), abs(j - 7))) for j in range(15)]self.POS.append(tuple(row))self.POS = tuple(self.POS)self.STWO = 1  # 冲二self.STHREE = 2  # 冲三self.SFOUR = 3  # 冲四self.TWO = 4  # 活二self.THREE = 5  # 活三self.FOUR = 6  # 活四self.FIVE = 7  # 活五self.DFOUR = 8  # 双四self.FOURT = 9  # 四三self.DTHREE = 10  # 双三self.NOTYPE = 11self.ANALYSED = 255  # 已经分析过self.TODO = 0  # 没有分析过self.result = [0 for i in range(30)]  # 保存当前直线分析值self.line = [0 for i in range(30)]  # 当前直线数据self.record = []  # 全盘分析结果 [row][col][方向]for i in range(15):self.record.append([])self.record[i] = []for j in range(15):self.record[i].append([0, 0, 0, 0])self.count = []  # 每种棋局的个数:count[黑棋/白棋][模式]for i in range(3):data = [0 for i in range(20)]self.count.append(data)self.reset()# 复位数据def reset(self):TODO = self.TODOcount = self.countfor i in range(15):line = self.record[i]for j in range(15):line[j][0] = TODOline[j][1] = TODOline[j][2] = TODOline[j][3] = TODOfor i in range(20):count[0][i] = 0count[1][i] = 0count[2][i] = 0return 0# 四个方向(水平,垂直,左斜,右斜)分析评估棋盘,再根据结果打分def evaluate(self, board, turn):score = self.__evaluate(board, turn)count = self.countif score < -9000:stone = turn == 1 and 2 or 1for i in range(20):if count[stone][i] > 0:score -= ielif score > 9000:stone = turn == 1 and 2 or 1for i in range(20):if count[turn][i] > 0:score += ireturn score# 四个方向(水平,垂直,左斜,右斜)分析评估棋盘,再根据结果打分def __evaluate(self, board, turn):record, count = self.record, self.countTODO, ANALYSED = self.TODO, self.ANALYSEDself.reset()# 四个方向分析for i in range(15):boardrow = board[i]recordrow = record[i]for j in range(15):if boardrow[j] != 0:if recordrow[j][0] == TODO:  # 水平没有分析过?self.__analysis_horizon(board, i, j)if recordrow[j][1] == TODO:  # 垂直没有分析过?self.__analysis_vertical(board, i, j)if recordrow[j][2] == TODO:  # 左斜没有分析过?self.__analysis_left(board, i, j)if recordrow[j][3] == TODO:  # 右斜没有分析过self.__analysis_right(board, i, j)FIVE, FOUR = self.FIVE, self.FOURTHREE, TWO = self.THREE, self.TWOSFOUR, STHREE, STWO = self.SFOUR, self.STHREE, self.STWOcheck = {}# 分别对白棋黑棋计算:FIVE, FOUR, THREE, TWO等出现的次数for c in (FIVE, FOUR, SFOUR, THREE, STHREE, TWO, STWO):check[c] = 1for i in range(15):for j in range(15):stone = board[i][j]if stone != 0:for k in range(4):ch = record[i][j][k]if ch in check:count[stone][ch] += 1# 如果有五连则马上返回分数BLACK, WHITE = 1, 2if turn == WHITE:  # 当前是白棋if count[BLACK][FIVE]:return -9999if count[WHITE][FIVE]:return 9999else:  # 当前是黑棋if count[WHITE][FIVE]:return -9999if count[BLACK][FIVE]:return 9999# 如果存在两个冲四,则相当于有一个活四if count[WHITE][SFOUR] >= 2:count[WHITE][FOUR] += 1if count[BLACK][SFOUR] >= 2:count[BLACK][FOUR] += 1# 具体打分wvalue, bvalue, win = 0, 0, 0if turn == WHITE:if count[WHITE][FOUR] > 0: return 9990if count[WHITE][SFOUR] > 0: return 9980if count[BLACK][FOUR] > 0: return -9970if count[BLACK][SFOUR] and count[BLACK][THREE]:return -9960if count[WHITE][THREE] and count[BLACK][SFOUR] == 0:return 9950if count[BLACK][THREE] > 1 and \count[WHITE][SFOUR] == 0 and \count[WHITE][THREE] == 0 and \count[WHITE][STHREE] == 0:return -9940if count[WHITE][THREE] > 1:wvalue += 2000elif count[WHITE][THREE]:wvalue += 200if count[BLACK][THREE] > 1:bvalue += 500elif count[BLACK][THREE]:bvalue += 100if count[WHITE][STHREE]:wvalue += count[WHITE][STHREE] * 10if count[BLACK][STHREE]:bvalue += count[BLACK][STHREE] * 10if count[WHITE][TWO]:wvalue += count[WHITE][TWO] * 4if count[BLACK][TWO]:bvalue += count[BLACK][TWO] * 4if count[WHITE][STWO]:wvalue += count[WHITE][STWO]if count[BLACK][STWO]:bvalue += count[BLACK][STWO]else:if count[BLACK][FOUR] > 0: return 9990if count[BLACK][SFOUR] > 0: return 9980if count[WHITE][FOUR] > 0: return -9970if count[WHITE][SFOUR] and count[WHITE][THREE]:return -9960if count[BLACK][THREE] and count[WHITE][SFOUR] == 0:return 9950if count[WHITE][THREE] > 1 and \count[BLACK][SFOUR] == 0 and \count[BLACK][THREE] == 0 and \count[BLACK][STHREE] == 0:return -9940if count[BLACK][THREE] > 1:bvalue += 2000elif count[BLACK][THREE]:bvalue += 200if count[WHITE][THREE] > 1:wvalue += 500elif count[WHITE][THREE]:wvalue += 100if count[BLACK][STHREE]:bvalue += count[BLACK][STHREE] * 10if count[WHITE][STHREE]:wvalue += count[WHITE][STHREE] * 10if count[BLACK][TWO]:bvalue += count[BLACK][TWO] * 4if count[WHITE][TWO]:wvalue += count[WHITE][TWO] * 4if count[BLACK][STWO]:bvalue += count[BLACK][STWO]if count[WHITE][STWO]:wvalue += count[WHITE][STWO]# 加上位置权值,棋盘最中心点权值是7,往外一格-1,最外圈是0wc, bc = 0, 0for i in range(15):for j in range(15):stone = board[i][j]if stone != 0:if stone == WHITE:wc += self.POS[i][j]else:bc += self.POS[i][j]wvalue += wcbvalue += bcif turn == WHITE:return wvalue - bvaluereturn bvalue - wvalue# 分析横向def __analysis_horizon(self, board, i, j):line, result, record = self.line, self.result, self.recordTODO = self.TODOfor x in range(15):line[x] = board[i][x]self.analysis_line(line, result, 15, j)for x in range(15):if result[x] != TODO:record[i][x][0] = result[x]return record[i][j][0]# 分析横向def __analysis_vertical(self, board, i, j):line, result, record = self.line, self.result, self.recordTODO = self.TODOfor x in range(15):line[x] = board[x][j]self.analysis_line(line, result, 15, i)for x in range(15):if result[x] != TODO:record[x][j][1] = result[x]return record[i][j][1]# 分析左斜def __analysis_left(self, board, i, j):line, result, record = self.line, self.result, self.recordTODO = self.TODOif i < j:x, y = j - i, 0else:x, y = 0, i - jk = 0while k < 15:if x + k > 14 or y + k > 14:breakline[k] = board[y + k][x + k]k += 1self.analysis_line(line, result, k, j - x)for s in range(k):if result[s] != TODO:record[y + s][x + s][2] = result[s]return record[i][j][2]# 分析右斜def __analysis_right(self, board, i, j):line, result = self.line, self.resultrecord = self.recordTODO = self.TODOif 14 - i < j:x, y, realnum = j - 14 + i, 14, 14 - ielse:x, y, realnum = 0, i + j, jk = 0while k < 15:if x + k > 14 or y - k < 0:breakline[k] = board[y - k][x + k]k += 1self.analysis_line(line, result, k, j - x)for s in range(k):if result[s] != TODO:record[y - s][x + s][3] = result[s]return record[i][j][3]# 分析一条线:五四三二等棋型def analysis_line(self, line, record, num, pos):TODO, ANALYSED = self.TODO, self.ANALYSEDTHREE, STHREE = self.THREE, self.STHREEFOUR, SFOUR = self.FOUR, self.SFOURwhile len(line) < 30: line.append(0xf)while len(record) < 30: record.append(TODO)for i in range(num, 30):line[i] = 0xffor i in range(num):record[i] = TODOif num < 5:for i in range(num):record[i] = ANALYSEDreturn 0stone = line[pos]inverse = (0, 2, 1)[stone]num -= 1xl = posxr = poswhile xl > 0:  # 探索左边界if line[xl - 1] != stone: breakxl -= 1while xr < num:  # 探索右边界if line[xr + 1] != stone: breakxr += 1left_range = xlright_range = xrwhile left_range > 0:  # 探索左边范围(非对方棋子的格子坐标)if line[left_range - 1] == inverse: breakleft_range -= 1while right_range < num:  # 探索右边范围if line[right_range + 1] == inverse: breakright_range += 1# 如果该直线范围小于 5,则直接返回if right_range - left_range < 4:for k in range(left_range, right_range + 1):record[k] = ANALYSEDreturn 0# 设置已经分析过for k in range(xl, xr + 1):record[k] = ANALYSEDsrange = xr - xl# 如果是 5连if srange >= 4:record[pos] = self.FIVEreturn self.FIVE# 如果是 4连if srange == 3:leftfour = False  # 是否左边是空格if xl > 0:if line[xl - 1] == 0:  # 活四leftfour = Trueif xr < num:if line[xr + 1] == 0:if leftfour:record[pos] = self.FOUR  # 活四else:record[pos] = self.SFOUR  # 冲四else:if leftfour:record[pos] = self.SFOUR  # 冲四else:if leftfour:record[pos] = self.SFOUR  # 冲四return record[pos]# 如果是 3连if srange == 2:  # 三连left3 = False  # 是否左边是空格if xl > 0:if line[xl - 1] == 0:  # 左边有气if xl > 1 and line[xl - 2] == stone:record[xl] = SFOURrecord[xl - 2] = ANALYSEDelse:left3 = Trueelif xr == num or line[xr + 1] != 0:return 0if xr < num:if line[xr + 1] == 0:  # 右边有气if xr < num - 1 and line[xr + 2] == stone:record[xr] = SFOUR  # XXX-X 相当于冲四record[xr + 2] = ANALYSEDelif left3:record[xr] = THREEelse:record[xr] = STHREEelif record[xl] == SFOUR:return record[xl]elif left3:record[pos] = STHREEelse:if record[xl] == SFOUR:return record[xl]if left3:record[pos] = STHREEreturn record[pos]# 如果是 2连if srange == 1:  # 两连left2 = Falseif xl > 2:if line[xl - 1] == 0:  # 左边有气if line[xl - 2] == stone:if line[xl - 3] == stone:record[xl - 3] = ANALYSEDrecord[xl - 2] = ANALYSEDrecord[xl] = SFOURelif line[xl - 3] == 0:record[xl - 2] = ANALYSEDrecord[xl] = STHREEelse:left2 = Trueif xr < num:if line[xr + 1] == 0:  # 左边有气if xr < num - 2 and line[xr + 2] == stone:if line[xr + 3] == stone:record[xr + 3] = ANALYSEDrecord[xr + 2] = ANALYSEDrecord[xr] = SFOURelif line[xr + 3] == 0:record[xr + 2] = ANALYSEDrecord[xr] = left2 and THREE or STHREEelse:if record[xl] == SFOUR:return record[xl]if record[xl] == STHREE:record[xl] = THREEreturn record[xl]if left2:record[pos] = self.TWOelse:record[pos] = self.STWOelse:if record[xl] == SFOUR:return record[xl]if left2:record[pos] = self.STWOreturn record[pos]return 0# ----------------------------------------------------------------------# DFS: 博弈树搜索
# ----------------------------------------------------------------------
class searcher(object):# 初始化def __init__(self):self.evaluator = evaluation()self.board = [[0 for n in range(15)] for i in range(15)]self.gameover = 0self.overvalue = 0self.maxdepth = 3# 产生当前棋局的走法def genmove(self, turn):moves = []board = self.boardPOSES = self.evaluator.POSfor i in range(15):for j in range(15):if board[i][j] == 0:score = POSES[i][j]moves.append((score, i, j))moves.sort()moves.reverse()return moves# 递归搜索:返回最佳分数def __search(self, turn, depth, alpha, beta):# 深度为零则评估棋盘并返回if depth <= 0:score = self.evaluator.evaluate(self.board, turn)return score# 如果游戏结束则立马返回score = self.evaluator.evaluate(self.board, turn)if abs(score) >= 9999 and depth < self.maxdepth:return score# 产生新的走法moves = self.genmove(turn)bestmove = None# 枚举当前所有走法for score, row, col in moves:# 标记当前走法到棋盘self.board[row][col] = turn# 计算下一回合该谁走nturn = turn == 1 and 2 or 1# 深度优先搜索,返回评分,走的行和走的列score = - self.__search(nturn, depth - 1, -beta, -alpha)# 棋盘上清除当前走法self.board[row][col] = 0# 计算最好分值的走法# alpha/beta 剪枝if score > alpha:alpha = scorebestmove = (row, col)if alpha >= beta:break# 如果是第一层则记录最好的走法if depth == self.maxdepth and bestmove:self.bestmove = bestmove# 返回当前最好的分数,和该分数的对应走法return alpha# 具体搜索:传入当前是该谁走(turn=1/2),以及搜索深度(depth)def search(self, turn, depth=3):self.maxdepth = depthself.bestmove = Nonescore = self.__search(turn, depth, -0x7fffffff, 0x7fffffff)if abs(score) > 8000:self.maxdepth = depthscore = self.__search(turn, 1, -0x7fffffff, 0x7fffffff)row, col = self.bestmovereturn score, row, col

完整项目代码

链接:https://pan.baidu.com/s/1OYAxOVs8wFX3lGpdVYAK7g
提取码:tzku

Python 2 python五子棋游戏实现相关推荐

  1. python写游戏棋牌游戏_使用python实现简单五子棋游戏

    使用python实现简单五子棋游戏 发布时间:2020-08-29 06:12:30 来源:脚本之家 阅读:73 作者:weixin_42874933 用python实现五子棋简单人机模式的练习过程, ...

  2. 用python实现简单五子棋游戏的练习过程

    用python实现五子棋简单人机模式的练习过程 第一次写博客,我尽力把它写好. 最近在初学python,今天就用自己的一些粗浅理解,来记录一下这几天的python简单人机五子棋游戏的练习,下面是实现过 ...

  3. Python+PyQt5实现五子棋游戏(人机博弈+深搜+α-β剪枝)

    Python+PyQt5实现五子棋游戏(人机博弈+深搜+α-β剪枝) 一.问题描述 1.五子棋 五子棋是全国智力运动会竞技项目之一,是一种两人对弈的纯策略型棋类游戏. 五子棋的棋具与围棋通用,是一种传 ...

  4. 基于Python实现的五子棋游戏设计

    一.设计目的: 1.1 课程设计教学目的 本课程设计是本专业的一门重要实践性教学环节.在学习了专业基础课和<Python程序设计>课程的基础上,本课程设计旨在加深对Python程序设计的认 ...

  5. 用Python编写简易五子棋游戏

    "无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家.教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家.点这里可以跳转到教程." 最近, ...

  6. 用Python代码实现五子棋游戏

    在做python实践的时候突然对python写游戏代码产生兴趣这,于是查了查资料,敲了敲代码,这样能够熟悉pygame模块,发现还挺好玩的 讲解一下编写单机五子棋程序的几个重要部分: ①创建初始化棋盘 ...

  7. 编写五子棋的完整python代码_python制作简单五子棋游戏

    本文实例为大家分享了python五子棋游戏的具体代码,供大家参考,具体内容如下 #五子棋 '" 矩阵做棋盘 16*16 "+" 打印棋盘 for for 游戏是否结束 开 ...

  8. python之穿越火线游戏代码_Python 大作业之五子棋游戏(附代码)

    Python 大作业--五子棋游戏 姓名:吴欣 学号: 姓名:张雨清 学号: 一 游戏介绍: 我们设计的是五子棋游戏,支持两人一个鼠标对下,黑方用左 键单击,白方用右键单击,谁先下均可,落子无悔,下过 ...

  9. python简单网格五子棋_python实现简单五子棋游戏

    本文实例为大家分享了python实现简单五子棋游戏的具体代码,供大家参考,具体内容如下 from graphics import * from math import * import numpy a ...

  10. python五子棋算法_python实现简单五子棋游戏

    本文实例为大家分享了python实现简单五子棋游戏的具体代码,供大家参考,具体内容如下 from graphics import * from math import * import numpy a ...

最新文章

  1. Oracle笔记 四、增删改、事务
  2. Isolation Forest
  3. SpringMVC传递JSON数据的方法
  4. 天气模式_江西现罕见持续阴雨寡照天气 市民开启“花式吐槽”模式
  5. python awk 读文件_测试python awk sed 读取文件指定位置时的性能
  6. SSL 2311-车厢调度[栈]
  7. html语言简单,简单的html语言计算器
  8. python长沙_长沙python
  9. C#获取屏幕大小的“简单整理”。。
  10. mysql如何保证高可用_mysql怎么保证高可用
  11. 局部变量 和 全局变量
  12. nubiax系统Android p正式版,nubia X更新 升级为基于Android P的JOS
  13. elasticsearch监控平台cerebro-0.8.3 相关操作
  14. 计算机更新和网络有关系吗,路由器跟网速有关系吗 电脑的网速慢怎么调
  15. 22条最常用Python代码,快收藏
  16. Linux下视频流媒体服务器搭建详解
  17. 阿里图标管理网站[iconfont.cn](iconfont.cn)的正确打开方式
  18. 关于Unity资源包导入项目后版本不匹配问题
  19. 运行docker镜像,出现Exited (137) 5 seconds ago问题
  20. telnet连接失败的常见错误

热门文章

  1. a豆独立的重要性 浅析华硕市场战略布局
  2. android解决底部导航栏遮挡问题,Android虚拟导航栏遮挡底部的输入框的解决方法...
  3. 使用HTML建立一个简单的网页
  4. JavaWeb笔记——JDBC
  5. 垃圾分类创业风口已来,就差一个程序员了,快上车!
  6. 依托于亚马逊云科技的开发者学习体验
  7. 【游戏开发】定时器循环播放动画
  8. 哪个大学的医学系和计算机系最好,上海交大湖畔学校,北京交通大学和上海交大,学校哪个好...
  9. 给视频配音的这两种方法你知道吗?
  10. 【办公自动化Excel】数据汇总和合并计算