基于C++实现五子棋AI算法思想

 更新时间:2020年4月25日 17:28  点击:1957

         今天我想要分享一下我做五子棋AI的思路。因为在做这个之前,我没有接触过任何像这种类似的东西。通过这一次,我也算是有所了解,我的思路也是来自很多网络上的博客,看了很多,最终总结出了自己的这样一个。

         那我的五子棋是15*15的大小(一般也就是这样的一个大小)。我的AI算法要求每一次落子之后都要去计算每一个空暇的位置的“分值”,简单的说,我们需要一个存放棋子的数组,表示是否存放了棋子,还要一个计算每一个空格的数组来记录“分数”,这个分数是后期AI用来运算的基础,也是你AI难度控制的点。

         我现有的思路就是分两部分。首先是如果是玩家先落子,那么要求电脑AI随即在你落子的地方的任意一个方向,随机落子,这是第一步。接下来以后就正式进入到算法中去。

首先初始化你的分数数组,让他们全部为零。然后在每一次落子之后进行全盘的遍历,如果发现该处为空白,于是检查其四周八个方向(当然如果是边缘位置就相对修改,判断是否出了边界)。若在空白处,且发现在某一对角线方向发现有一个其他颜色的棋子,那么相对的给这个空白区域的分数数组加上一定的分值,然后继续往这个方向检测是否还有连续的同一颜色的棋子,若没有则检查其他方向或者检测下一个空白位置。若是还在同一方向上面找到了相同颜色的棋子,那么第二个棋子的出现,你可以给改空白处加上双倍的分值,表明这个空白位置更加重要。一次类推,继续检测。(PS:因为最终AI棋子落在什么地方,依靠的是最后遍历整个分数数组,然后根据分数的高低来进行判断落子落在哪里的,在下面讲)。

         经过上一遍的遍历,每一次落子都会使得分数数组得到一些变化,每一次都会导致AI判断的变化。在这个基础上,每一次落子还要进行一次对自己本身棋子颜色的一个遍历,判断自己的情况,同时加分加在分数数组之中,这样一来,电脑就会根据自己的棋子的情况以及玩家的落子情况进行判断,哪一个地方更加适合落子。

         因为我是第一次做AI,网络上搜到的一些思想一般也是这种类似的遍历思想。理解了以后写代码就比较方便。最后可能会有一些点的分数是相同的,所以还有设置一下随机落子。把分数相同的地点随机落子。

         个人感觉AI的强弱是根据你每一次给他增加分数的多少来确定的。这个我的AI有时候也会抽风,不过一般情况比较正常,可能运气也占了一部分,当初设计加分的时候其实没想那么多,现在却发现好像还不错。

         大家要多去实践练习,多改改分数可能就会出来不错的AI了,o(^▽^)o。

下面贴上我的代码! 

void GameScene::Robot(int *x, int *y, int *Sum) 
{ 
  ExWhile1 = true; 
  if (*Sum == 1) 
  { 
    while (ExWhile1) 
    { 
      ChessOne(*x, *y); 
      if (ch[*x][*y] == 2){ ExWhile1 = false; } 
    } 
    ch[*x][*y] = tp;   //记录这个点 
    printpart(*x, *y, tp);     //打印出电脑AI第一次落子 
    isTouch = true; 
    tp++; 
    tp = tp % 2; 
  } 
  else      //从第2步开始,使用评分系统 
  { 
    Findscore(*x, *y); 
  } 
} 
 
 
void GameScene::Findscore(int &x, int &y)   //查找评分最高的坐标 
{ 
  srand((unsigned)time(NULL)); 
  int i, j, x1, x2, y1, y2, lx; 
  int Max = 0; 
  ChessScore();      //调用评分函数 
  for (i = 0; i<15; i++) 
  { 
    for (j = 0; j<15; j++) 
    { 
      if (Score[i][j]>Max) 
      { 
        Max = Score[i][j];  //获取所有点中,评分最高的 
        x1 = i; 
        y1 = j; 
      } 
    } 
  } 
  x2 = x1; y2 = y1; 
  for (i = 0; i<15; i++)    //可能的话,有评分相同的多个点 
  { 
    for (j = 0; j<15; j++) 
    { 
      if (Score[i][j] == Max&&i != x2&&j != y2)   //在这么多个相同分数的点中,随机找一个 
      { 
        lx = rand() % 10; 
        if (lx<5) 
        { 
          x2 = i, y2 = j; 
          break; 
        } 
      } 
    } 
  } 
  if (x2 != x1 || y2 != y1)   //棋盘上有2个最高分 
  { 
    lx = rand() % 10;    //随机一个 
    if (lx>6) 
    { 
      x = x1, y = y1; 
    } 
    else 
    { 
      x = x2, y = y2; 
    } 
  } 
  else    //棋盘上只有一个最高分 
  { 
    x = x1, y = y1; 
  } 
  Max = 0;    //清空最大值 
  ch[x][y] = tp;       //记录这个点 
  printpart(x, y, tp);    //打印出电脑AI落子 
  if (winerValue==2) 
  { 
    isTouch = true; 
  } 
   
  tp++; 
  tp = tp % 2; 
} 
 
 
inline void GameScene::ChessOne(int &x, int &y)   //玩家走第1步时的落子 
{ 
  int i, j; 
  srand((unsigned)time(NULL));    //随机数随着时间的改变而改变 
  for (i = 0; i<15; i++) 
  { 
    for (j = 0; j<15; j++) 
    { 
      if (ch[i][j] == 0)  //如果找到了玩家的棋子,在它的8个方的任意一点落子 
      { 
        int lx = rand() % 7; 
        if (lx == 0) 
        { 
          x = i + 1; y = j + 1; 
          if (ch[x][y] == 2){ break; } 
        } 
        else if (lx == 1) 
        { 
          x = i + 1; y = j - 1; 
          if (ch[x][y] == 2){ break; } 
        } 
        else if (lx == 2) 
        { 
          x = i - 1; y = j - 1; 
          if (ch[x][y] == 2){ break; } 
        } 
        else if (lx == 3) 
        { 
          x = i - 1; y = j + 1; 
          if (ch[x][y] == 2){ break; } 
        } 
        else if (lx == 4) 
        { 
          x = i - 1; y = j;    //上 
          if (ch[x][y] == 2){ break; } 
        } 
        else if (lx == 5) 
        { 
          x = i; y = j - 1;   //左 
          if (ch[x][y] == 2){ break; } 
        } 
        else if (lx == 6) 
        { 
          x = i; y = j + 1;   //右 
          if (ch[x][y] == 2){ break; } 
        } 
        else 
        { 
          x = i + 1; y = j;    //下 
          if (ch[x][y] == 2){ break; } 
        } 
      } 
    } 
  } 
} 
 
void GameScene::ChessScore() 
{ 
  int x, y, i, j, k;      //循环变量 
  int number1 = 0, number2 = 0;   //number用来统计玩家或电脑棋子连成个数 
  int empty = 0;    //empty用来统计空点个数 
  memset(Score, 0, sizeof(Score));                    //把评分数组先清零 
  for (x = 0; x<15; x++) 
  { 
    for (y = 0; y<15; y++) 
    { 
      if (ch[x][y] == 2)    //如果这个点为空  
      { 
        for (i = -1; i <= 1; i++) 
        { 
          for (j = -1; j <= 1; j++)   //判断8个方向  
          { 
            if (i != 0 || j != 0)   //若是都为0的话,那不就是原坐标嘛 
            { 
              //对玩家落点评分 
              for (k = 1; i <= 4; k++)   //循环4次 
              {                        //这点没越界  且这点存在黑子(玩家) 
                if (x + k*i >= 0 && x + k*i <= 14 &&  
                  y + k*j >= 0 && y + k*j <= 14 &&  
                  ch[x + k*i][y + k*j] == 0) 
                {  
                  number1++; 
                } 
                else if (ch[x + k*i][y + k*j] == 2)     //这点是个空点,+1后退出 
                {  
                  empty++;  
                  break;  
                }    
                else                    //否则是墙或者对方的棋子了  
                {  
                  break;  
                }     
              } 
              for (k = -1; k >= -4; k--)            //向它的相反方向判断  
              {                        //这点没越界  且这点存在黑子(玩家) 
                if (x + k*i >= 0 && x + k*i <= 14 && 
                  y + k*j >= 0 && y + k*j <= 14 &&  
                  ch[x + k*i][y + k*j] == 0) 
                {  
                  number1++; 
                } 
                else if (ch[x + k*i][y + k*j] == 2)     //这点是个空点,+1后退出 
                {  
                  empty++;  
                  break;  
                }   
                else 
                {  
                  break; 
                } 
              } 
              if (number2 == 1)   //2个棋子  
              {  
                Score[x][y] += 1;  
              }   
              else if (number1 == 2)   //3个棋子  
              { 
                if (empty == 1)     
                { 
                  Score[x][y] += 5;   //有一个空点+5分 死3  
                }      
                else if (empty == 2)  
                {  
                  Score[x][y] += 10;  //有两个空点+10分 活3 
                }  
              } 
              else if (number1 == 3)   //4个棋子  
              { 
                if (empty == 1)    
                {  
                  Score[x][y] += 20;  //有一个空点+20分 死4  
                }   
                else if (empty == 2) 
                {  
                  Score[x][y] += 100;  //有2个空点+100分 活4 
                }   
              } 
              else if (number1 >= 4)   
              {  
                Score[x][y] += 1000;  //对方有5个棋子,分数要高点,先堵 
              }   
 
              empty = 0;   //统计空点个数的变量清零  
 
              //对电脑落点评分 
              for (k = 1; i <= 4; k++)   //循环4次 
              {       //这点没越界  且这点存在白子(电脑) 
                if (x + k*i >= 0 && x + k*i <= 14 &&  
                  y + k*j >= 0 && y + k*j <= 14 &&  
                  ch[x + k*i][y + k*j] == 1) 
                {  
                  number2++; 
                } 
                else if (ch[x + k*i][y + k*j] == 2) 
                {  
                  empty++; break;   //空点 
                }  
                else  
                { 
                  break; 
                } 
              } 
              for (k = -1; k >= -4; k--)   //向它的相反方向判断  
              { 
                if (x + k*i >= 0 && x + k*i <= 14 && 
                  y + k*j >= 0 && y + k*j <= 14 &&  
                  ch[x + k*i][y + k*j] == 1) 
                {  
                  number2++; 
                } 
                else if (ch[x + k*i][y + k*j] == 2) 
                {  
                  empty++; break; 
                } 
                else 
                {  
                  break;   //注释与上面玩家版相同 
                }       
              } 
              if (number2 == 0)    
              {  
                Score[x][y] += 1;    //1个棋子 
              }  
              else if (number2 == 1)   
              { 
                Score[x][y] += 2;    //2个棋子  
              }    
              else if (number2 == 2)   //3个棋子 
              { 
                if (empty == 1)    
                {  
                  Score[x][y] += 8;  //死3 
                }   
                else if (empty == 2)  
                {  
                  Score[x][y] += 30;  //活3  
                }  
              } 
              else if (number2 == 3)   //4个棋子 
              { 
                if (empty == 1)    
                {  
                  Score[x][y] += 50;   //死4 
                }   
                else if (empty == 2)  
                {  
                  Score[x][y] += 200;   //活4 
                }   
              } 
              else if (number2 >= 4)  
              {  
                Score[x][y] += 10000;   //自己落在这点能形成5个,也就能胜利了,分数最高 
              }  
 
              number1 = 0;     //清零,以便下次重新统计 
              number2 = 0;                   
              empty = 0; 
            } 
          } 
        } 
      } 
    } 
  } 
} 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持猪先飞。

[!--infotagslink--]

相关文章

  • C++ STL标准库std::vector的使用详解

    vector是表示可以改变大小的数组的序列容器,本文主要介绍了C++STL标准库std::vector的使用详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2022-03-06
  • C++中取余运算的实现

    这篇文章主要介绍了C++中取余运算的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
  • 详解C++ string常用截取字符串方法

    这篇文章主要介绍了C++ string常用截取字符串方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • C++中四种加密算法之AES源代码

    本篇文章主要介绍了C++中四种加密算法之AES源代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...2020-04-25
  • C++ 整数拆分方法详解

    整数拆分,指把一个整数分解成若干个整数的和。本文重点给大家介绍C++ 整数拆分方法详解,非常不错,感兴趣的朋友一起学习吧...2020-04-25
  • C++中 Sort函数详细解析

    这篇文章主要介绍了C++中Sort函数详细解析,sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变...2022-08-18
  • C++万能库头文件在vs中的安装步骤(图文)

    这篇文章主要介绍了C++万能库头文件在vs中的安装步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
  • AI虽非消除虚假新闻的完美解决方案 但能减轻这个问题

    【腾讯科技编者按】美国风投公司 Verizon Ventures 的董事大卫·法莫拉里(David Famolari)今日在科技博客 Venturebeat 上撰文称,虚假新闻问题因为跟总统大选牵连在一起而受到越来越多的人关注,现在很多公司开始尝试利用 AI(人工智能)技术来解决这个问题,这虽然并非最完美的解决方案,但是或许能够在一定程度上减轻这个问题。原文如下:...2017-07-06
  • 详解C++ bitset用法

    这篇文章主要介绍了C++ bitset用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • 浅谈C++中的string 类型占几个字节

    本篇文章小编并不是为大家讲解string类型的用法,而是讲解我个人比较好奇的问题,就是string 类型占几个字节...2020-04-25
  • C++ Eigen库计算矩阵特征值及特征向量

    这篇文章主要为大家详细介绍了C++ Eigen库计算矩阵特征值及特征向量,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-04-25
  • VSCode C++多文件编译的简单使用方法

    这篇文章主要介绍了VSCode C++多文件编译的简单使用方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-29
  • C++ pair的用法实例详解

    这篇文章主要介绍了C++ pair的用法实例详解的相关资料,需要的朋友可以参考下...2020-04-25
  • C++中的循环引用

    虽然C++11引入了智能指针的,但是开发人员在与内存的斗争问题上并没有解放,如果我门实用不当仍然有内存泄漏问题,其中智能指针的循环引用缺陷是最大的问题。下面通过实例代码给大家介绍c++中的循环引用,一起看看吧...2020-04-25
  • C++随机点名生成器实例代码(老师们的福音!)

    这篇文章主要给大家介绍了关于C++随机点名生成器的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • C++如何删除map容器中指定值的元素详解

    map容器是C++ STL中的重要一员,删除map容器中value为指定元素的问题是我们经常与遇到的一个问题,下面这篇文章主要给大家介绍了关于利用C++如何删除map容器中指定值的元素的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。...2020-04-25
  • C++ 约瑟夫环问题案例详解

    这篇文章主要介绍了C++ 约瑟夫环问题案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下...2021-08-15
  • C++中cin的用法详细

    这篇文章主要介绍了C++中cin的用法详细,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • 基于C++中常见编译错误的总结详解

    本篇文章是对C++中的常见编译错误进行了详细的分析介绍,需要的朋友参考下...2020-04-25