当前位置:   article > 正文

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

c++五子棋算法

更多精彩,请点击上方蓝字关注我们!

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

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

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

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

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

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

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

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

    下面贴上我的代码! 

  1. void GameScene::Robot(int *x, int *y, int *Sum)
  2. {
  3. ExWhile1 = true;
  4. if (*Sum == 1)
  5. {
  6. while (ExWhile1)
  7. {
  8. ChessOne(*x, *y);
  9. if (ch[*x][*y] == 2){ ExWhile1 = false; }
  10. }
  11. ch[*x][*y] = tp; //记录这个点
  12. printpart(*x, *y, tp); //打印出电脑AI第一次落子
  13. isTouch = true;
  14. tp++;
  15. tp = tp % 2;
  16. }
  17. else //从第2步开始,使用评分系统
  18. {
  19. Findscore(*x, *y);
  20. }
  21. }
  22. void GameScene::Findscore(int &x, int &y) //查找评分最高的坐标
  23. {
  24. srand((unsigned)time(NULL));
  25. int i, j, x1, x2, y1, y2, lx;
  26. int Max = 0;
  27. ChessScore(); //调用评分函数
  28. for (i = 0; i<15; i++)
  29. {
  30. for (j = 0; j<15; j++)
  31. {
  32. if (Score[i][j]>Max)
  33. {
  34. Max = Score[i][j]; //获取所有点中,评分最高的
  35. x1 = i;
  36. y1 = j;
  37. }
  38. }
  39. }
  40. x2 = x1; y2 = y1;
  41. for (i = 0; i<15; i++) //可能的话,有评分相同的多个点
  42. {
  43. for (j = 0; j<15; j++)
  44. {
  45. if (Score[i][j] == Max&&i != x2&&j != y2) //在这么多个相同分数的点中,随机找一个
  46. {
  47. lx = rand() % 10;
  48. if (lx<5)
  49. {
  50. x2 = i, y2 = j;
  51. break;
  52. }
  53. }
  54. }
  55. }
  56. if (x2 != x1 || y2 != y1) //棋盘上有2个最高分
  57. {
  58. lx = rand() % 10; //随机一个
  59. if (lx>6)
  60. {
  61. x = x1, y = y1;
  62. }
  63. else
  64. {
  65. x = x2, y = y2;
  66. }
  67. }
  68. else //棋盘上只有一个最高分
  69. {
  70. x = x1, y = y1;
  71. }
  72. Max = 0; //清空最大值
  73. ch[x][y] = tp; //记录这个点
  74. printpart(x, y, tp); //打印出电脑AI落子
  75. if (winerValue==2)
  76. {
  77. isTouch = true;
  78. }
  79. tp++;
  80. tp = tp % 2;
  81. }
  82. inline void GameScene::ChessOne(int &x, int &y) //玩家走第1步时的落子
  83. {
  84. int i, j;
  85. srand((unsigned)time(NULL)); //随机数随着时间的改变而改变
  86. for (i = 0; i<15; i++)
  87. {
  88. for (j = 0; j<15; j++)
  89. {
  90. if (ch[i][j] == 0) //如果找到了玩家的棋子,在它的8个方的任意一点落子
  91. {
  92. int lx = rand() % 7;
  93. if (lx == 0)
  94. {
  95. x = i + 1; y = j + 1;
  96. if (ch[x][y] == 2){ break; }
  97. }
  98. else if (lx == 1)
  99. {
  100. x = i + 1; y = j - 1;
  101. if (ch[x][y] == 2){ break; }
  102. }
  103. else if (lx == 2)
  104. {
  105. x = i - 1; y = j - 1;
  106. if (ch[x][y] == 2){ break; }
  107. }
  108. else if (lx == 3)
  109. {
  110. x = i - 1; y = j + 1;
  111. if (ch[x][y] == 2){ break; }
  112. }
  113. else if (lx == 4)
  114. {
  115. x = i - 1; y = j; //上
  116. if (ch[x][y] == 2){ break; }
  117. }
  118. else if (lx == 5)
  119. {
  120. x = i; y = j - 1; //左
  121. if (ch[x][y] == 2){ break; }
  122. }
  123. else if (lx == 6)
  124. {
  125. x = i; y = j + 1; //右
  126. if (ch[x][y] == 2){ break; }
  127. }
  128. else
  129. {
  130. x = i + 1; y = j; //下
  131. if (ch[x][y] == 2){ break; }
  132. }
  133. }
  134. }
  135. }
  136. }
  137. void GameScene::ChessScore()
  138. {
  139. int x, y, i, j, k; //循环变量
  140. int number1 = 0, number2 = 0; //number用来统计玩家或电脑棋子连成个数
  141. int empty = 0; //empty用来统计空点个数
  142. memset(Score, 0, sizeof(Score)); //把评分数组先清零
  143. for (x = 0; x<15; x++)
  144. {
  145. for (y = 0; y<15; y++)
  146. {
  147. if (ch[x][y] == 2) //如果这个点为空
  148. {
  149. for (i = -1; i <= 1; i++)
  150. {
  151. for (j = -1; j <= 1; j++) //判断8个方向
  152. {
  153. if (i != 0 || j != 0) //若是都为0的话,那不就是原坐标嘛
  154. {
  155. //对玩家落点评分
  156. for (k = 1; i <= 4; k++) //循环4次
  157. { //这点没越界 且这点存在黑子(玩家)
  158. if (x + k*i >= 0 && x + k*i <= 14 &&
  159. y + k*j >= 0 && y + k*j <= 14 &&
  160. ch[x + k*i][y + k*j] == 0)
  161. {
  162. number1++;
  163. }
  164. else if (ch[x + k*i][y + k*j] == 2) //这点是个空点,+1后退出
  165. {
  166. empty++;
  167. break;
  168. }
  169. else //否则是墙或者对方的棋子了
  170. {
  171. break;
  172. }
  173. }
  174. for (k = -1; k >= -4; k--) //向它的相反方向判断
  175. { //这点没越界 且这点存在黑子(玩家)
  176. if (x + k*i >= 0 && x + k*i <= 14 &&
  177. y + k*j >= 0 && y + k*j <= 14 &&
  178. ch[x + k*i][y + k*j] == 0)
  179. {
  180. number1++;
  181. }
  182. else if (ch[x + k*i][y + k*j] == 2) //这点是个空点,+1后退出
  183. {
  184. empty++;
  185. break;
  186. }
  187. else
  188. {
  189. break;
  190. }
  191. }
  192. if (number2 == 1) //2个棋子
  193. {
  194. Score[x][y] += 1;
  195. }
  196. else if (number1 == 2) //3个棋子
  197. {
  198. if (empty == 1)
  199. {
  200. Score[x][y] += 5; //有一个空点+5分 死3
  201. }
  202. else if (empty == 2)
  203. {
  204. Score[x][y] += 10; //有两个空点+10分 活3
  205. }
  206. }
  207. else if (number1 == 3) //4个棋子
  208. {
  209. if (empty == 1)
  210. {
  211. Score[x][y] += 20; //有一个空点+20分 死4
  212. }
  213. else if (empty == 2)
  214. {
  215. Score[x][y] += 100; //有2个空点+100分 活4
  216. }
  217. }
  218. else if (number1 >= 4)
  219. {
  220. Score[x][y] += 1000; //对方有5个棋子,分数要高点,先堵
  221. }
  222. empty = 0; //统计空点个数的变量清零
  223. //对电脑落点评分
  224. for (k = 1; i <= 4; k++) //循环4次
  225. { //这点没越界 且这点存在白子(电脑)
  226. if (x + k*i >= 0 && x + k*i <= 14 &&
  227. y + k*j >= 0 && y + k*j <= 14 &&
  228. ch[x + k*i][y + k*j] == 1)
  229. {
  230. number2++;
  231. }
  232. else if (ch[x + k*i][y + k*j] == 2)
  233. {
  234. empty++; break; //空点
  235. }
  236. else
  237. {
  238. break;
  239. }
  240. }
  241. for (k = -1; k >= -4; k--) //向它的相反方向判断
  242. {
  243. if (x + k*i >= 0 && x + k*i <= 14 &&
  244. y + k*j >= 0 && y + k*j <= 14 &&
  245. ch[x + k*i][y + k*j] == 1)
  246. {
  247. number2++;
  248. }
  249. else if (ch[x + k*i][y + k*j] == 2)
  250. {
  251. empty++; break;
  252. }
  253. else
  254. {
  255. break; //注释与上面玩家版相同
  256. }
  257. }
  258. if (number2 == 0)
  259. {
  260. Score[x][y] += 1; //1个棋子
  261. }
  262. else if (number2 == 1)
  263. {
  264. Score[x][y] += 2; //2个棋子
  265. }
  266. else if (number2 == 2) //3个棋子
  267. {
  268. if (empty == 1)
  269. {
  270. Score[x][y] += 8; //死3
  271. }
  272. else if (empty == 2)
  273. {
  274. Score[x][y] += 30; //活3
  275. }
  276. }
  277. else if (number2 == 3) //4个棋子
  278. {
  279. if (empty == 1)
  280. {
  281. Score[x][y] += 50; //死4
  282. }
  283. else if (empty == 2)
  284. {
  285. Score[x][y] += 200; //活4
  286. }
  287. }
  288. else if (number2 >= 4)
  289. {
  290. Score[x][y] += 10000; //自己落在这点能形成5个,也就能胜利了,分数最高
  291. }
  292. number1 = 0; //清零,以便下次重新统计
  293. number2 = 0;
  294. empty = 0;
  295. }
  296. }
  297. }
  298. }
  299. }
  300. }
  301. }

它,

不仅仅是一个码

扫码关注

C++资源免费送

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/336385
推荐阅读
相关标签
  

闽ICP备14008679号