怎么用python实现五子棋 : 第六节,升级版人机对战

上一节,我们实现了简单版的人机对战,只不过电脑的水平太弱鸡了,下面接下来,我们需要做的就是升级电脑的落子水平。

首先,要先了解一下五子棋的棋型知识。

棋型知识库主要包括各种既定的棋盘形式,有如下几种:

 活二:能够形成活三的二,如下图,是三种基本的活二棋型。图中白点为活三点。

眠二:能够形成眠三的二。图中四个为最基本的眠二棋型。图中白点为眠三点。

活三:可以形成活四的三,如下图,代表两种最基本的活三棋型。图中白点为活四点。活三棋型是进攻中最常见的一种,因为活三之后,如果对方不以理会,将可以下一手将活三变成活四,而活四是无法防守的。所以,面对活三的时候,需要非常谨慎对待。在没有更好的进攻手段的情况下,必须对其进行防守,以防止其形成可怕的活四棋型。

眠三只能够形成冲四的三,如下各图,分别代表最基础的六种眠三形状。图中白点代表冲四点。眠三的棋型与活三的棋型相比,危险系数下降不少,因为眠三棋型即使不去防守,下一手它也只能形成冲四,而对于单纯的冲四棋型,是可以很简单的防守住的。

活四:有两个连五点(即有两个点可以形成五),图中白点即为连五点。当活四出现的时候,整个局势已经无法阻止连五了,活四的归属方一定能取得胜利。

冲四:有一个连五点,如下面三图,均为冲四棋型。图中白点为连五点。 相对比活四来说,冲四的威胁性就小了很多,因为这个时候,只要跟着防守在那个唯一的连五点上,冲四就没法形成连五。

对于上述的棋型,我们主要考虑的是活三、眠三、活四、冲四这几种主要的进攻棋型的防守与构成,整体棋型遵从以下原则:优先考虑数目,同等数目的情况下考虑是活是眠。评分表算法的设计整体偏向于防守。

其次,要考虑人机博弈过程。

当下棋型的评估分析,算法严格遵从以下流程:

当人类方落下一子,算法启动,扫描全局,得到人类棋子的集合和电脑棋子的集合。全局扫描之后,对当前局势进行排序、计算。对每个集合的每个空白点位置打分,打分依据是根据这个点周围四个方向上的同色连续棋子的数量。按照这些最后得到的评分,得出最大值。得到人类方和电脑方的两个最大值之后,进行比较,如果人类方局势较好(分数较高),则算法将下一次落子位置设置为人类方得分最高的点,尽力降低人类方的下一步得分;如果电脑方的分数较高,那么则直接在使得分数最高的点落子即可。

 

完整的代码如下:

# -*- coding: utf-8 -*-
import random
from random import *

'''
Created on 2020年1月2日

@author: Fan Xiaoxin
'''

# 五子棋类的定义


class Gomoku(object):
    level = 15
    grade = 10
    MAX = 1000111

    def __init__(self, size=15):
        """初始化"""
        # 棋盘横纵向变量
        self.size = size
        # 定义一个存储棋子的位置的矩阵
        self.go_map = [[0] * self.size for _ in range(self.size)]
        # 存储棋盘界面
        self.str = ''
        # 步数
        self.cur_step = 0

    def Scan(self, go_map, player_mode):
        shape = [[[0 for _ in range(5)] for _ in range(Gomoku.level)] for _ in range(Gomoku.level)]
        # 扫描每一个点,然后在空白的点每一个方向上做出价值评估!!
        for i in range(Gomoku.level):
            for j in range(Gomoku.level):
                # 如果此处为空 那么就可以开始扫描周边
                if go_map[i][j] == 0:
                    m = i
                    n = j
                    # 如果上方跟当前传入的颜色参数一致,那么加分到0位!
                    if player_mode:
                        while n - 1 >= 0 and go_map[m][n - 1] == 1:
                            n -= 1
                            shape[i][j][0] += Gomoku.grade
                        if n - 1 >= 0 and go_map[m][n - 1] == 0:
                            shape[i][j][0] += 1
                        if n - 1 >= 0 and go_map[m][n - 1] == 2:
                            shape[i][j][0] -= 2
                    else:
                        while n - 1 >= 0 and go_map[m][n - 1] == 2:
                            n -= 1
                            shape[i][j][0] += Gomoku.grade
                        if n - 1 >= 0 and go_map[m][n - 1] == 0:
                            shape[i][j][0] += 1
                        if n - 1 >= 0 and go_map[m][n - 1] == 1:
                            shape[i][j][0] -= 2

                    m = i
                    n = j
                    # 如果下方跟当前传入的颜色参数一致,那么加分到0位!
                    if player_mode:
                        while n + 1 < Gomoku.level and go_map[m][n + 1] == 1:
                            n += 1
                            shape[i][j][0] += Gomoku.grade
                        if n + 1 < Gomoku.level and go_map[m][n + 1] == 0:
                            shape[i][j][0] += 1
                        if n + 1 < Gomoku.level and go_map[m][n + 1] == 2:
                            shape[i][j][0] -= 2
                    else:
                        while n + 1 < Gomoku.level and go_map[m][n + 1] == 2:
                            n += 1
                            shape[i][j][0] += Gomoku.grade
                        if n + 1 < Gomoku.level and go_map[m][n + 1] == 0:
                            shape[i][j][0] += 1
                        if n + 1 < Gomoku.level and go_map[m][n + 1] == 1:
                            shape[i][j][0] -= 2
                    m = i
                    n = j
                    # 如果左边跟当前传入的颜色参数一致,那么加分到1位!
                    if player_mode:
                        while m - 1 >= 0 and go_map[m - 1][n] == 1:
                            m -= 1
                            shape[i][j][1] += Gomoku.grade
                        if m - 1 >= 0 and go_map[m - 1][n] == 0:
                            shape[i][j][1] += 1
                        if m - 1 >= 0 and go_map[m - 1][n] == 2:
                            shape[i][j][1] -= 2
                    else:
                        while m - 1 >= 0 and go_map[m - 1][n] == 2:
                            m -= 1
                            shape[i][j][1] += Gomoku.grade
                        if m - 1 >= 0 and go_map[m - 1][n] == 0:
                            shape[i][j][1] += 1
                        if m - 1 >= 0 and go_map[m - 1][n] == 1:
                            shape[i][j][1] -= 2
                    m = i
                    n = j
                    # 如果右边跟当前传入的颜色参数一致,那么加分到1位!
                    if player_mode:
                        while m + 1 < Gomoku.level and go_map[m + 1][n] == 1:
                            m += 1
                            shape[i][j][1] += Gomoku.grade
                        if m + 1 < Gomoku.level and go_map[m + 1][n] == 0:
                            shape[i][j][1] += 1
                        if m + 1 < Gomoku.level and go_map[m + 1][n] == 2:
                            shape[i][j][1] -= 2
                    else:
                        while m + 1 < Gomoku.level and go_map[m + 1][n] == 2:
                            m += 1
                            shape[i][j][1] += Gomoku.grade
                        if m + 1 < Gomoku.level and go_map[m + 1][n] == 0:
                            shape[i][j][1] += 1
                        if m + 1 < Gomoku.level and go_map[m + 1][n] == 1:
                            shape[i][j][1] -= 2
                    m = i
                    n = j
                    # 如果左下方跟当前传入的颜色参数一致,那么加分到2位!
                    if player_mode:
                        while m - 1 >= 0 and n + 1 < Gomoku.level and go_map[m - 1][n + 1] == 1:
                            m -= 1
                            n += 1
                            shape[i][j][2] += Gomoku.grade
                        if m - 1 >= 0 and n + 1 < Gomoku.level and go_map[m - 1][n + 1] == 0:
                            shape[i][j][2] += 1
                        if m - 1 >= 0 and n + 1 < Gomoku.level and go_map[m - 1][n + 1] == 2:
                            shape[i][j][2] -= 2
                    else:
                        while m - 1 >= 0 and n + 1 < Gomoku.level and go_map[m - 1][n + 1] == 2:
                            m -= 1
                            n += 1
                            shape[i][j][2] += Gomoku.grade
                        if m - 1 >= 0 and n + 1 < Gomoku.level and go_map[m - 1][n + 1] == 0:
                            shape[i][j][2] += 1
                        if m - 1 >= 0 and n + 1 < Gomoku.level and go_map[m - 1][n + 1] == 1:
                            shape[i][j][2] -= 2
                    m = i
                    n = j
                    # 如果右上方跟当前传入的颜色参数一致,那么加分到2位!
                    if player_mode:
                        while m + 1 < Gomoku.level and n - 1 >= 0 and go_map[m + 1][n - 1] == 1:
                            m += 1
                            n -= 1
                            shape[i][j][2] += Gomoku.grade
                        if m + 1 < Gomoku.level and n - 1 >= 0 and go_map[m + 1][n - 1] == 0:
                            shape[i][j][2] += 1
                        if m + 1 < Gomoku.level and n - 1 >= 0 and go_map[m + 1][n - 1] == 2:
                            shape[i][j][2] -= 2
                    else:
                        while m + 1 < Gomoku.level and n - 1 >= 0 and go_map[m + 1][n - 1] == 2:
                            m += 1
                            n -= 1
                            shape[i][j][2] += Gomoku.grade
                        if m + 1 < Gomoku.level and n - 1 >= 0 and go_map[m + 1][n - 1] == 0:
                            shape[i][j][2] += 1
                        if m + 1 < Gomoku.level and n - 1 >= 0 and go_map[m + 1][n - 1] == 1:
                            shape[i][j][2] -= 2
                    m = i
                    n = j
                    # 如果左上方跟当前传入的颜色参数一致,那么加分到3位!
                    if player_mode:
                        while m - 1 >= 0 and n - 1 >= 0 and go_map[m - 1][n - 1] == 1:
                            m -= 1
                            n -= 1
                            shape[i][j][3] += Gomoku.grade
                        if m - 1 >= 0 and n - 1 >= 0 and go_map[m - 1][n - 1] == 0:
                            shape[i][j][3] += 1
                        if m - 1 >= 0 and n - 1 >= 0 and go_map[m - 1][n - 1] == 2:
                            shape[i][j][3] -= 2
                    else:
                        while m - 1 >= 0 and n - 1 >= 0 and go_map[m - 1][n - 1] == 2:
                            m -= 1
                            n -= 1
                            shape[i][j][3] += Gomoku.grade
                        if m - 1 >= 0 and n - 1 >= 0 and go_map[m - 1][n - 1] == 0:
                            shape[i][j][3] += 1
                        if m - 1 >= 0 and n - 1 >= 0 and go_map[m - 1][n - 1] == 1:
                            shape[i][j][3] -= 2
                    m = i
                    n = j
                    # 如果右下方跟当前传入的颜色参数一致,那么加分到3位!
                    if player_mode:
                        while m + 1 < Gomoku.level and n + 1 < Gomoku.level and go_map[m + 1][n + 1] == 1:
                            m += 1
                            n += 1
                            shape[i][j][3] += Gomoku.grade
                        if m + 1 < Gomoku.level and n + 1 < Gomoku.level and go_map[m + 1][n + 1] == 0:
                            shape[i][j][3] += 1
                        if m + 1 < Gomoku.level and n + 1 < Gomoku.level and go_map[m + 1][n + 1] == 2:
                            shape[i][j][3] -= 2
                    else:
                        while m + 1 < Gomoku.level and n + 1 < Gomoku.level and go_map[m + 1][n + 1] == 2:
                            m += 1
                            n += 1
                            shape[i][j][3] += Gomoku.grade
                        if m + 1 < Gomoku.level and n + 1 < Gomoku.level and go_map[m + 1][n + 1] == 0:
                            shape[i][j][3] += 1
                        if m + 1 < Gomoku.level and n + 1 < Gomoku.level and go_map[m + 1][n + 1] == 1:
                            shape[i][j][3] -= 2
        return shape

    def Sort(self, shape):
        for i in shape:
            for j in i:
                for x in range(5):
                    for w in range(3, x - 1, -1):
                        if j[w - 1] < j[w]:
                            temp = j[w]
                            j[w - 1] = j[w]
                            j[w] = temp
        print("This Time Sort Done !")
        return shape

    def Evaluate(self, shape):
        for i in range(Gomoku.level):
            for j in range(Gomoku.level):

                if shape[i][j][0] == 4:
                    return i, j, Gomoku.MAX
                shape[i][j][4] = shape[i][j][0] * 1000 + shape[i][j][1] * 100 + shape[i][j][2] * 10 + shape[i][j][3]
        max_x = 0
        max_y = 0
        max = 0
        for i in range(Gomoku.level):
            for j in range(Gomoku.level):
                if max < shape[i][j][4]:
                    max = shape[i][j][4]
                    max_x = i
                    max_y = j
        print("the max is " + str(max) + " at ( " + str(max_x) + " , " + str(max_y) + " )")
        return max_x, max_y, max

    def Autoplay(self, go_map):
        a1 = [1, -1, 1, -1, 1, -1, 0, 0]
        b1 = [1, -1, -1, 1, 0, 0, 1, -1]
        m = Gomoku.level / 2
        n = Gomoku.level / 2
        rand = randint(0, 7)
        while m + a1[rand] >= 0 and m + a1[rand] < Gomoku.level and n + b1[rand] >= 0 and \
                n + b1[rand] < Gomoku.level and go_map[m + a1[rand]][n + b1[rand]] != 0:
            rand = randint(0, 7)
        return m + a1[rand], n + b1[rand]
    
    def player_drop(self):
        """
        玩家落子
        :param pos_x: 从图形界面输入时,输入的x坐标为多少
        :param pos_y: 从图形界面输入时,输入的y坐标为多少
        """
        while True:
            try:
                # 接受玩家的输入
                pos_x = int(input('x: '))  
                pos_y = int(input('y: '))
                
                if 0 <= pos_x <= self.size - 1 and 0 <= pos_y <= self.size - 1:
                    if self.go_map[pos_x][pos_y] == 0:
                        self.go_map[pos_x][pos_y] = 1
                        self.cur_step += 1
                        return
                    else:
                        print "该位置已经被占据,请选择其他位置"
                        continue
                    
            except ValueError:  # 玩家输入不正确的情况(例如输入了‘A’)
                continue
    
    def computer_drop(self):
        """
        电脑落子
        : param pos_x: 从图形界面输入时,输入的x坐标为多少
        : param pos_y: 从图形界面输入时,输入的y坐标为多少
        """
        while True:
            print "cur_step = {}" .format(self.cur_step)
            try:
                if self.cur_step < 2:
                    pos_x, pos_y = self.Autoplay(self.go_map)
                else:
                    # 接受电脑的输入
                    player_x, player_y, player_max = self.Evaluate(self.Sort(self.Scan(self.go_map, player_mode=True)))
                    computer_x, computer_y, computer_max = self.Evaluate(self.Sort(self.Scan(self.go_map, player_mode=False)))
                    if player_max > computer_max and player_max < Gomoku.MAX:
                        pos_x, pos_y = player_x, player_y
                    else:
                        pos_x, pos_y = computer_x, computer_y

                if 0 <= pos_x <= self.size - 1 and 0 <= pos_y <= self.size - 1:
                    if self.go_map[pos_x][pos_y] == 0:
                        self.go_map[pos_x][pos_y] = 2
                        self.cur_step += 1
                        return
                    else:
                        print "该位置已经被占据,请选择其他位置"
                        continue
            # 玩家输入不正确的情况(例如输入了‘A’)
            except ValueError:
                continue
    
    def go_result(self):
        """判断游戏的结局。0为游戏进行中,1为玩家获胜,2为电脑获胜,3为平局"""
        # 1. 判断是否横向连续五子
        for x in range(self.size - 4):
            for y in range(self.size):
                if self.go_map[x][y] == 1 and self.go_map[x + 1][y] == 1 and self.go_map[x + 2][y] == 1 and self.go_map[x + 3][y] == 1 and self.go_map[x + 4][y] == 1:
                    return 1
                if self.go_map[x][y] == 2 and self.go_map[x + 1][y] == 2 and self.go_map[x + 2][y] == 2 and self.go_map[x + 3][y] == 2 and self.go_map[x + 4][y] == 2:
                    return 2
                
        # 2. 判断是否纵向连续五子
        for x in range(self.size):
            for y in range(self.size - 4):
                if self.go_map[x][y] == 1 and self.go_map[x][y + 1] == 1 and self.go_map[x][y + 2] == 1 and self.go_map[x][y + 3] == 1 and self.go_map[x][y + 4] == 1:
                    return 1
                if self.go_map[x][y] == 2 and self.go_map[x][y + 1] == 2 and self.go_map[x][y + 2] == 2 and self.go_map[x][y + 3] == 2 and self.go_map[x][y + 4] == 2:
                    return 2
        # 3. 判断是否有左上-右下的连续五子
        for x in range(self.size - 4):
            for y in range(self.size -4):
                if self.go_map[x][y] == 1 and self.go_map[x + 1][y + 1] == 1 and self.go_map[x + 2][y + 2] == 1 and self.go_map[x + 3][y + 3] == 1 and self.go_map[x + 4][y + 4] == 1:
                    return 1
                if self.go_map[x][y] == 2 and self.go_map[x + 1][y + 1] == 2 and self.go_map[x + 2][y + 2] == 2 and self.go_map[x + 3][y + 3] == 2 and self.go_map[x + 4][y + 4] == 2:
                    return 2
        
        # 4. 判断是否有右上-左下的连续五子
        for x in range(self.size - 4):
            for y in range(self.size -4):
                if self.go_map[x + 4][y] == 1 and self.go_map[x + 3][y + 1] == 1 and self.go_map[x + 2][y + 2] == 1 and self.go_map[x + 1][y + 3] == 1 and self.go_map[x][y + 4] == 1:
                    return 1
                if self.go_map[x + 4][y] == 2 and self.go_map[x + 3][y + 1] == 2 and self.go_map[x + 2][y + 2] == 2 and self.go_map[x + 1][y + 3] == 2 and self.go_map[x][y + 4] == 2:
                    return 2
        
        # 5. 判断是否为平局
        for x in range(self.size):
            for y in range(self.size):
                # 棋盘中还有剩余的格子,不能判断为平局
                if self.go_map[x][y] == 0:
                    return 0
        return 3
    
    def gomoku_board(self, res):
        """画出棋盘"""
        self.str = ''
        for y in xrange(self.size):
            for x in xrange(self.size-1):
                # 该位置没有棋子
                if self.go_map[x][y]==0:
                    self.str += ' '
                # 该位置已被我方占据
                elif self.go_map[x][y]==1:
                    self.str += 'O'
                # 该位置已被对方占据
                elif self.go_map[x][y]==2:
                    self.str += 'X'
                    
                self.str += '-'
                
            self.str += '\n'
            if y != (self.size-1):
                for _ in xrange(self.size):
                    self.str +=  '| '
                self.str +=  '\n'
        print self.str       
        
        if res == 0:
            print u'游戏正在进行中!'
        elif res == 1:
            print u'玩家获胜!'
        elif res == 2:
            print u'电脑获胜!'
        elif res == 3:
            print u'平局!'
            
        return self.str
    
# 主函数


if __name__ == '__main__':
    gomoku = Gomoku()
    while True:
        gomoku.player_drop()
        res = gomoku.go_result()
        gomoku.gomoku_board(res)
        
        gomoku.computer_drop()
        res = gomoku.go_result()
        gomoku.gomoku_board(res)

 

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值