一字棋游戏简介

“一字棋"游戏(又叫"三子棋"或"井字棋”),是一款十分经典的益智小游戏。“井字棋"的棋盘很简单,是一个 3×3 的格子,很像中国文字中的"井"字,所以得名"井字棋”。"井字棋"游戏的规则与"五子棋"十分类似,"五子棋"的规则是一方首先五子连成一线就胜利;"井字棋"是一方首先三子连成一线就胜利。
尽可能的朝着可以让计算机获胜的方向走步。需要采用极大极小搜索算法。
“一字棋”游戏(又叫“三子棋”或“井字棋”),是一款十分经典的益智小游戏。“井字棋”的棋盘很简单,是一个 3×3 的格子,很像中国文字中的“井”字,所以得名“井字棋”。“井字棋”游戏的规则与“五子棋”十分类似,“五子棋”的规则是一方首先五子连成一线就胜利;“井字棋”是一方首先三子连成一线就胜利。

启发式搜索简介

启发式搜索(Heuristically Search)又称为有信息搜索(Informed Search),它是利用问题拥有的启发信息来引导搜索,达到减少搜索范围、降低问题复杂度的目的,这种利用启发信息的搜索过程称为启发式搜索。
启发式策略可以通过指导搜索向最有希望的方向前进,降低了复杂性。通过删除某些状态及其延伸,启发式算法可以消除组合爆炸,并得到令人能接受的解(通常并不一定是最佳解)。
然而,启发式策略是极易出错的。在解决问题的过程中启发仅仅是下一步将要采取措施的一个猜想,常常根据经验和直觉来判断。由于启发式搜索只有有限的信息(比如当前状态的描述),要想预测进一步搜索过程中状态空间的具体行为则很难。一个启发式搜索可能得到一个次最佳解,也可能一无所获。这是启发式搜索固有的局限性。这种局限性不可能由所谓更好的启发式策略或更有效的搜索算法来消除。一般说来,启发信息越强,扩展的无用节点就越少。引入强的启发信息,有可能大大降低搜索工作量,但不能保证找到最小耗散值的解路径(最佳路径)。因此,在实际应用中,最好能引入降低搜索工作量的启发信息而不牺牲找到最佳路径的保证。

实现

(1) 一字棋棋盘的生成

import random
import copy
import numpy as np
import matplotlib.pyplot as plt
from numpy.linalg import cholesky

class maps:
    
    def __init__(self,inf):#初始化

        self.matrix = [[" "]*3,[" "]*3,[" "]*3]
        for i in range(0,3):
            for j in range(0,3):
                self.matrix[i][j] = inf[i][j]
        self.cnt = 0 
def __str__(self):
        return str( self.matrix[0] ) + "\n" +str( self.matrix[1] ) + "\n" +str( self.matrix[2] ) + "\n"
    
    def getvalue(self):
        
        for i in range(0,3):
           
            if self.matrix[0][i] == 'X' and self.matrix[1][i] == 'X' and self.matrix[2][i] == 'X' :
                return 100
            if self.matrix[0][i] == 'O' and self.matrix[1][i] == 'O' and self.matrix[2][i] == 'O' :
                return -100
            
            if self.matrix[i] == ['X', 'X', 'X']:
                return 100
            if self.matrix[i] == ['O', 'O', 'O']:
                return -100
            
        if self.matrix[0][0] == 'X' and self.matrix[1][1] == 'X' and self.matrix[2][2] == 'X' :
            return 100
        if self.matrix[0][0] == 'O' and self.matrix[1][1] == 'O' and self.matrix[2][2] == 'O' :
            return -100
        if self.matrix[0][2] == 'X' and self.matrix[1][1] == 'X' and self.matrix[2][0] == 'X' :
            return 100
        if self.matrix[0][2] == 'O' and self.matrix[1][1] == 'O' and self.matrix[2][0] == 'O' :
            return -100
        
        value = 0
        
        for i in range(0,3):
           
            if self.matrix[0][i] != 'O' and self.matrix[1][i] != 'O' and self.matrix[2][i] != 'O' :
                value += 1
            if self.matrix[0][i] != 'X' and self.matrix[1][i] != 'X' and self.matrix[2][i] != 'X' :
                value -= 1

          if self.matrix[0][i] != 'O' and self.matrix[1][i] != 'O' and self.matrix[2][i] != 'O' :
                value += 1
            if self.matrix[0][i] != 'X' and self.matrix[1][i] != 'X' and self.matrix[2][i] != 'X' :
                value -= 1
            
        if self.matrix[0][0] != 'O' and self.matrix[1][1] != 'O' and self.matrix[2][2] != 'O' :
            value += 1
        if self.matrix[0][0] != 'X' and self.matrix[1][1] != 'X' and self.matrix[2][2] != 'X' :
            value -= 1
        if self.matrix[0][2] != 'O' and self.matrix[1][1] != 'O' and self.matrix[2][0] != 'O' :
            value += 1
        if self.matrix[0][2] != 'X' and self.matrix[1][1] != 'X' and self.matrix[2][0] != 'X' :
            value -= 1
        
        return value

一字棋游戏终止判断条件

if __name__ == '__main__':
    start = maps([[" "]*3,[" "]*3,[" "]*3])
    
    print(start
    time = 0 
    
    while True :
        if start.getvalue() == 100 or start.getvalue() == -100:
            break 
        
        print("轮到你下棋")
        
        x , y = input("输入下棋的点:").split()
        x = int(x)-1
        y = int(y)-1
        
        start.matrix[x][y]='O'
        start.cnt += 1 
        print("你下棋后")
        print(start)
        
        time += 1
        if time == 9:#下满了棋盘
            break
        
        maxvalue = -200
        
        for i in range(0,3):#遍历、寻找一个合适的点
            for j in range(0,3):
                if start.matrix[i][j] == ' ' :
                    start.cnt += 1 
                    start.matrix[i][j] = 'X'
                    tmp = dfs(start,maxvalue,0)
                    start.matrix[i][j] = ' '
                    start.cnt -= 1 
                    if tmp > maxvalue:
                        maxvalue = tmp
                        x,y=i,j
        start.matrix[x][y]='X'
        start.cnt += 1 
        print("电脑下棋后")
        print(start)
        time += 1
        #break
    if start.getvalue() == -100 :
        print("胜利")
    elif start.getvalue() == 100 :
        print("失败")
 else:
        print("平局")

一字棋游戏博弈树搜索

def dfs(nowmaps , pre ,step):#博弈树核心算法
    
    #print(nowmaps,nowmaps.getvalue())
    
    if nowmaps.cnt == 9 or nowmaps.getvalue() == 100 or nowmaps.getvalue() == -100:
        return nowmaps.getvalue();
    
    if step % 2 ==0:
        value = 200
        
        for i in range(0,3):
            for j in range(0,3):
                if nowmaps.matrix[i][j] == ' ' :
                    nowmaps.matrix[i][j] = 'O'
                    nowmaps.cnt += 1 
                    tmp = dfs(nowmaps,value,step+1)
                    nowmaps.cnt -= 1 
                    nowmaps.matrix[i][j] = ' '
                    if tmp < value:
                        value = tmp
                    if value <= pre :
                        return value
    
    else:
        value = -200
        
        for i in range(0,3):
            for j in range(0,3):
                if nowmaps.matrix[i][j] == ' ' :
                    nowmaps.matrix[i][j] = 'X'
                    nowmaps.cnt += 1 
                    tmp = dfs(nowmaps,value,step+1)
                    nowmaps.cnt -= 1 
                    nowmaps.matrix[i][j] = ' '
                    if tmp > value:
                        value = tmp
                    if value >= pre :
                        return value
    return value

结果与对比分析

常用的启发式搜索有极大极小搜索法和α—β剪枝技术。极大极小搜索过程将搜索树的产生和位置评估完全分开,只有生成规定深度的博弈搜索树后,才利用估价函数对位置进行评估,算法的效率比较低。在本博弈算法中,采用极大极小搜索方法搜索计算机的下一步走步方法,每一次走步的时候以计算机当前所面对的棋局状态作为根顶点,生成一棵有限深度的博弈子树,然后从该博弈子树的叶结点向上回溯,确定在根顶点处的当前最好的策略,找到一条当前最好行动的边。在生成博弈子树的过程中,计算机己方对应的走步状态的节点称为MAX节点,对应的对手走步的节点成为MIN节点。该算法从结果入手,分别选择最有利于自身的策略。通过设置最大值最小值来实现有利于自身的策略。通过轮流取大取小来模拟两人博弈。
在极大极小搜索过程中,首先生成博弈子树,然后计算各个顶点的估值,这一过程中,生成子树和计算顶点的估值是彼此分离的,在本程序中,构造博弈子树采用递归的方法构造,在递归退出的过程中,由子节点的估值计算返回节点的估值。但是极大极小搜索仍然会搜索很多不必要的分支,为了减少搜索的次数,提高算法效率,这种技术称为α—β剪枝过程,它的搜索效率比极大极小搜索法有了很大的提高。
在没有剪枝的计算机搜索走步的过程中,根据搜索的深度,需要搜索的走步的数量非常多。根据实验结果,走步计算根据实验次数的增加而减少到一个稳定的值。
带有α-β剪枝的搜索结果中,可以看到搜索的次数很显著的减少了。
通过以上实验结果,可以看出,极大极小搜索可以找到当前状态下的最优解之一,如果最优解同时有多个,则简单的选择第一个最优解。然而在极大极小的搜索过程中,可能会搜索很多无效的分支,这样就可以通过α-β剪枝方法剪去不必要的搜索分支,加快搜索速度。

出现的问题及解决方法

Q1:算法执行的过程,步骤以及所要达到的效果不理解

A1:查阅资料画流程图

Q2:对于什么情况下需要剪枝不清楚

A2解决:熟悉剪枝算法

Q3:对于代码中出现的极大极小等边界情况不理解

A3解决:举例推导极限情况

结果分析与体会

搜索算法分为盲目搜索和启发式搜索,其中盲目搜索也成为无信息搜索,这种搜索方式十分费时费力,而启发式搜索则是一种有信息的搜索,现在已经在多种领域内被应用。为了提高搜索效率,引入启发信息来进行搜索,在启发式搜索过程中,要对 open 表进行排序,这就需要有一种方法来计算待扩展节点有希望通向目标节点的程度,我们总希望能找到最有希望通向目标节点的待扩展节点优先扩展。其一般形式是: f(n)=g(n)+h(n)其中 f(n) 是从初始节点 Xs 经由节点 Xn 移动到目标节点 Xt 的移动损耗,g(n) 是从初始节点 Xs 移动到节点 Xn 的实际代价,h(n) 是从节点 Xn 到目标节点 Xt 的估计移动代价。在评估函数的基础上现代学者提出 A 算法和 A* 算法,其中 A 算法是运用评估函数公式在图的搜索过程中对 open表的节点进行排序,而 A* 算法在 A 算法的基础上引入节点j 的启发函数 f*(j),则启发函数重新定义为 f*(j)=g*(j)+h*(j),两个算法使得图的搜索方向更加智能地趋向于终点。

启发式搜索解决剪枝问题的基本思想是根据倒推值的计算方法,或中取大,与中取小,在扩展和计算过程中,能剪掉不必要的分枝,提高效率。启发式搜索对于剪枝问题这两种应用方式,一种是极大极小剪枝,另一种是 α-β 剪枝。在现代社会中人们往往为了省时省力并且最快达到自己的目的而寻找一些“捷径”,比如在人们在从一个地方到达另一个地方时会打开地图软件搜索相关路线,那么路线的规划就是一种图的搜索。由于从地点 A 到地点 Z 的路径有很多种,且用户的要求可能各不相同,有些用户会选择自己较为熟悉的路线,有些用户会选择行驶路程最短的路线,那么地图软件如何在短时间内根据用户的起始地点和目的地点进行路线规划和推荐是目前研究的热点,这个过程可认为是一种剪枝。在此本文使用启发式搜索方法解决此类剪枝问题,首先根据目的地 A 到目的地 Z 所有的路线分列出来,其中经过的建筑物设为 X={B,C,D…Y}。然后根据建筑物所在街道的区域划分成图搜索中的最大层和最小层,模拟出每条路径的长度,由此向下开展搜索,以此帮助地图软件规划路线。

启发式搜索算法是一种很实用、很有效的算法,其中的剪枝方法结合人工智能技术在各个领域有了很广泛的应用。同时启发式剪枝通常应用于裁剪搜索树中没有意义的不需要搜索的数值,以提高运算速度,如在游戏的求解搜索,其中有智能五子棋和一字棋等,该算法在模糊推理中也会起到优化的作用。

目前,人工智能面临的问题越来越复杂,其中以非结构化问题居多, 以往盲目搜索需要搜索所有节点消耗了大量时间, 这种弊病将会严重限制搜索能力, 究其原因在于顾及了所有可能性, 即一个一个盲目搜索。针对该问题,人们需要运用有知识的生成器避免走一些明显不可能搜索到正确答案的路径, 即启发式搜索。启发式搜索已成为实际中求解智能规划问题的重要工具, 特别是不确定性规划问题。近年来,放松规划在图规划问题中的探究、基于单值变量的启发式研究等均推动着对启发式搜索的探索。用启发式搜索思维构建问题解成为一种常用的思考方式, 比如最大权独立集问题、 普遍共享骑行问题等。启发式搜索结合模糊逻辑、频谱频率分配等领域技术更是加快了众多领域搜索发展的进程。归根究底,人们还是希望搜索路径沿着自己认为有希望的方向前进,这样就可以大大减少搜索时间。本文首先追溯研究起点, 再从源头到人机大战应用对启发式搜索及其启发能力等进行探讨。

一字棋游戏是一个流传已久的传统游戏。游戏由两个人轮流来下,分别用“X”和“O”来代替自身的棋子。棋盘分9个格,双方可以在轮到自己下的时候,可以用棋子占领其中一个空的格子。如果双方中有一方的棋子可以连成一条直线,则这一方判胜,对方判负。当所有的格子都被占领,但双方都无法使棋子连成一条直线的话,则判和棋。这是一个智能型的一字棋游戏,机器可以模拟人与用户对弈。当轮到机器来下的时候,机器会根据当前棋局的形势,利用极大极小算法算出一个评价值,判断如何下才对自身最有利,同时也是对方来说对不利的,然后下在评价值最高的地方。另外利用α-β剪枝,使机器在搜索评价值的时候不用扩展不必要的结点,从而提高机器计算的效率。

在用户界面方法,用一个3×3的井字格来显示用户与机器下的结果。当要求用户输入数据的时候会有提示信息。用户在下的过程中可以中途按下“0”退出。当用户与计算机分出了胜负后,机器会显示出比赛的结果,并按任意键退出。如果用户在下棋的过程中,输入的是非法字符,机器不会做出反应。

人和机器在围棋中的较量比拼已是试验机器的试金石, 其中核心部分便是蒙特卡洛树搜索。该搜索建立于二人零和博弈基础上, 其搜索基础是最大最小搜索。最大最小搜索的中心思想是轮到我方搜索扩展时选择最有利于我方的扩展; 轮到对方搜索扩展时选择最不利于我方的扩展。最大最小搜索有两大明显缺点: ①搜索树宽度太广,导致该树非常 “胖”;②搜索树深度太深, 导致该树非常长。

人机对弈的难点在于当人走一步棋之后, 计算机如何走下一步, 即计算机如何找出最合适的位置去走棋。 这就需要一定的算法, 或者叫做计算机的 AI。 对 于 井 字 棋 、 五 子 棋 等 两 方较量的游戏来说, Minimax 算法 (极小极大算法) 是最基本也是最常用的。 算法的原理不在这里解释了, 直接看该算法在井字棋中的应用。井字棋中, 假设使用 “X” 的是人, 使 用 “O” 的 是 计 算机。 “X” 方先走,设定 X 方的最大利益为正无穷 (程序使用常量+INFINITY 表示),O方的 最大利益为负无穷(程序中使用-INFINITY 表示), 即 X 方和 O 方走的每步棋都要力图使自己的利益最大化,而使对方的利益最小化。这样称 X方为MAX(因为它总是追求更大的值 ),O方为MIN(它总是追求更小的值),各自都为争取自己的最大获益而努力.

简单说, X 方或者 MAX 方的走棋是由人来控制的。对于O方或者MIN方,它走棋时要考虑哪 个位置对X方最有利 ,然后把该位置占据,即 O 的最佳走棋就是 X 的最佳走棋。 所以 O 在走棋之前, 先站在 X 的角度寻找最佳走棋位置。 minimax 方法就是站在 X 角度来考虑极小极大算法, 找到 X的最佳走棋位置, 然后由 O 方来占据该位置。

在极小极大搜索方法中,由于要先生成指定深度以内的所有节点,其节点数将随着搜索深度的增加承指数增长。这极大地限制了极小极大搜索方法的使用。能否在搜索深度不变的情况下,利用已有的搜索信息减少生成的节点数呢?实际上,MINIMAX过程是把搜索树的生成和格局估值这两个过程分开来进行,即先生成全部搜索树,然后再进行端节点静态估值和倒推值计算,这显然会导致低效率。其中一个MIN节点要全部生成A、B、C、D四个节点,然后还要逐个计算其静态估值,最后在求倒推值阶段,才赋值。

搜索深度并非越深越好,局限于估值函数是根据能构成三子一线的数目决定的,所以搜索到最后一层,如果有人胜出则出现,如果没人胜出,则三子一线数目为0,因此毫无意义。这也是大多数情况下出现平局的原因。

α-β剪枝算法是计算机博弈类游戏算法的基础,是一种优化的博弈树搜索算法,使用α-β剪枝算法搜索博弈树时,可以剪去一些不必要的分枝,从而提高搜索效率;而α-β剪枝算法的剪枝效率又取决于所构建的博弈树的分枝的排列顺序,理想排列顺序下的剪枝效率和最差情况下相比差别极大。后人也提出的使用机器学习的算法来改进博弈树分枝的排列顺序,可以极大的提高α-β剪枝算法的剪枝效率。使用机器学习的算法来改进博弈树分枝的排列顺序,可以极大的提高α-β剪枝算法的剪枝效率。在文中,作者介绍了α-β剪枝算法的剪枝原理及已经存在的各种优化算法,给出了构建用于辅助α-β剪枝算法剪枝的学习系统的通用方法及注意事项。为了验证本文所提出的改进方法,作者首先设计并实现了基于α-β剪枝算法的井字棋游戏,然后通过人机博弈来获取用于训练学习系统的训练样例;其次,作者设计并实现了一个BP神经网络,该网络由14个隐藏节点,9个输出节点构成,能够对博弈树的分枝按其成为最佳路径的概率排序,经过前面提取的训练样例的训练后,BP神经网络对测试样本中的50%可以做出正确预测,即训练后的神经网络能够以当前棋局状态为输入,以50%的正确率预测下一步的最佳走法。