当前位置:Gxlcms > html代码 > HTML5Canvas实现五子棋游戏的代码分享(图文)

HTML5Canvas实现五子棋游戏的代码分享(图文)

时间:2021-07-01 10:21:17 帮助过:18人阅读

背景介绍

  因为之前用c#的winform中的gdi+,java图形包做过五子棋,所以做这个逻辑思路也就驾轻就熟,然而最近想温故html5的canvas绘图功能(公司一般不用这些),所以做了个五子棋,当然没参考之前的客户端代码,只用使用之前计算输赢判断算法和电脑AI(网络借取)的算法,当然现在html5做的五子棋百度一下非常多,但是自己实现一边总归是好事情,好了废话不多说了进入正题。^_^

界面功能介绍与后续可增加功能

目前界面功能:

  主界面包含

    1:人人、人机对战选项 2:棋子外观选择 3:棋盘背景选择 4:棋盘线条颜色选择

  游戏界面包含

    1:玩家名称 2:玩家棋子 3:当前由谁下棋背景定位 4:玩家比分 5:功能菜单区域(重新开始和无限悔棋) 6:棋盘区域 7.胜利后连环棋子连接 8.最后下棋位置闪烁显示 9.光标定位

  游戏结束界面

    1:胜利背景图 2:胜利玩家姓名 3:继续下一把按钮

可增加功能

  1.返回主界面 2.保存棋局和相关数据 3.读取棋局和相关数据 4.交换角色 5.网络对战(2台机器)6.双方思考总时间记录

界面截图赏析(没花时间美化,比较丑)

相关设置选择

整体设计和主代码介绍

整体设计

  下棋流程:玩家or电脑AI下棋 ---> 绘制棋子 ---> 设定棋子二维坐标值 ----> logic(逻辑判断) ----> (玩家)一方五子连环 ---> 获胜界面
      ↑                                          |
      |                                          ↓
      <--------------------------------------------------------------------------------------------没有五子

  悔棋流程(人人对战):一方玩家悔棋 ----> 弹出下棋记录堆栈并设定为它是最后一枚棋 ---> 清除最后一枚棋子图像 ---> 清除棋子二维坐标值---> 重新定位显示最后下棋位置并闪烁

  悔棋流程(人机对战):玩方悔棋 ----> 弹出下棋记录堆栈2次,设定上一次电脑为最后一枚棋 ---> 清除弹出的2次记录图像 ---> 清除棋子2个棋子二维坐标值---> 重新定位显示最后下棋位置并闪烁

主代码介绍

  主代码分为二块: 1.界面逻辑块 2.游戏主体块 (界面与游戏代码分离,逻辑清晰,分工明确)

模拟事件通知:游戏主体逻辑块,每次结果都会通知到界面层来进行交互(类似于c#或者java的委托或事件)

界面逻辑代码

  1. <script type="text/javascript">
  2. var gb = null;
  3. var infoboj = document.getElementsByClassName("info")[0];
  4. var pl1obj = document.getElementById("pl1");
  5. var pl2obj = document.getElementById("pl2");
  6. var plname1obj = document.getElementById("plname1");
  7. var plname2obj = document.getElementById("plname2");
  8. var chesstypeobj = document.getElementsByName("chesstype");
  9. var chesscolorobj = document.getElementsByName("chesscolor");
  10. var chessbgObj = document.getElementsByName("chessbg");
  11. var winerpnl = document.getElementById("winer");
  12. document.getElementById("startgame").addEventListener("click", function() {
  13. function initParams() {
  14. var chessTypeValue = 1;
  15. if (chesstypeobj.length > 0) {
  16. for (var i = 0; i < chesstypeobj.length; i++) {
  17. if (chesstypeobj[i].checked) {
  18. chessTypeValue = chesstypeobj[i].value;
  19. break;
  20. }
  21. }
  22. }
  23. var linevalue = "";
  24. if (chesscolorobj.length > 0) {
  25. for (var i = 0; i < chesscolorobj.length; i++) {
  26. if (chesscolorobj[i].checked) {
  27. linevalue = chesscolorobj[i].value;
  28. break;
  29. }
  30. }
  31. }
  32. var bcorimgvalue = "";
  33. if (chessbgObj.length > 0) {
  34. for (var i = 0; i < chessbgObj.length; i++) {
  35. if (chessbgObj[i].checked) {
  36. bcorimgvalue = chessbgObj[i].value;
  37. break;
  38. }
  39. }
  40. }
  41. return {
  42. lineColor: linevalue,
  43. chessType: chessTypeValue, //1 色彩棋子 2 仿真棋子
  44. playAName: plname1Input.value,
  45. playBName: plname2Input.value,
  46. backColorORImg: bcorimgvalue,
  47. playAImg: "http://sandbox.runjs.cn/uploads/rs/62/nbqodq5i/playA.png",
  48. playBImg: "http://sandbox.runjs.cn/uploads/rs/62/nbqodq5i/playB.png",
  49. playerBIsComputer:openComputer.checked
  50. };
  51. }
  52. document.getElementById("cc").style.display = "block";
  53. gb = new gobang(initParams());
  54. /**
  55. * 设置一些界面信息
  56. * @param {Object} opt
  57. */
  58. gb.info = function(opt) {
  59. infoboj.style.visibility = "visible";
  60. document.getElementsByClassName("startpnl")[0].style.visibility = "hidden";
  61. plname1obj.innerHTML = opt.playAName;
  62. plname2obj.innerHTML = opt.playBName;
  63. if (opt.chessType == 1) {
  64. var span1 = document.createElement("span");
  65. pl1obj.insertBefore(span1, plname1obj);
  66. var span2 = document.createElement("span");
  67. pl2obj.insertBefore(span2, plname2obj);
  68. } else {
  69. var img1 = document.createElement("img");
  70. img1.src = opt.playAImg;
  71. pl1obj.insertBefore(img1, plname1obj);
  72. var img2 = document.createElement("img");
  73. img2.src = opt.playBImg;
  74. pl2obj.insertBefore(img2, plname2obj);
  75. }
  76. }
  77. /**
  78. * 每次下棋后触发事件
  79. * @param {Object} c2d
  80. */
  81. gb.operate = function(opt, c2d) {
  82. if (!c2d.winer || c2d.winer <= 0) {
  83. pl1obj.removeAttribute("class", "curr");
  84. pl2obj.removeAttribute("class", "curr");
  85. if (c2d.player == 1) {
  86. pl2obj.setAttribute("class", "curr");
  87. } else {
  88. pl1obj.setAttribute("class", "curr");
  89. }
  90. document.getElementById("backChessman").innerHTML="悔棋("+c2d.canBackTimes+")";
  91. } else {
  92. var winname = c2d.winer == 1 ? opt.playAName : opt.playBName;
  93. var str = "恭喜,【" + winname + "】赢了!"
  94. alert(str);
  95. winerpnl.style.display = "block";
  96. document.getElementById("winerName").innerHTML = "恭喜,【" + winname + "】赢了!";
  97. document.getElementById("pl" + c2d.winer).style.backgroundColor = "pink";
  98. document.getElementById("scoreA").innerHTML = c2d.playScoreA;
  99. document.getElementById("scoreB").innerHTML = c2d.playScoreB;
  100. }
  101. }
  102. gb.start();
  103. });
  104. document.getElementById("openComputer").addEventListener("change", function() {
  105. if (this.checked) {
  106. plname2Input.value = "电脑";
  107. plname2Input.disabled = "disabled";
  108. } else {
  109. plname2Input.value = "玩家二";
  110. plname2Input.disabled = "";
  111. }
  112. });
  113. //document.getElementById("openComputer").checked="checked";
  114. //重新开始
  115. function restartgui() {
  116. if (gb) {
  117. winerpnl.style.display = "none";
  118. pl1obj.removeAttribute("class", "curr");
  119. pl2obj.removeAttribute("class", "curr");
  120. document.getElementById("pl1").style.backgroundColor = "";
  121. document.getElementById("pl2").style.backgroundColor = "";
  122. gb.restart();
  123. }
  124. };
  125. </script>

游戏主体代码块(只包含函数声明代码)


  1. // ==========
  2. // =name:gobang 游戏
  3. // =anthor:jasnature
  4. // =last modify date:2016-04-13
  5. // ==========
  6. (function(win) {
  7. var gb = function(option) {
  8. var self = this,
  9. canObj = document.getElementById("cc"),
  10. can = canObj.getContext("2d");
  11. self.contextObj = canObj;
  12. self.context = can; if (!self.context) {
  13. alert("浏览器不支持html5"); return;
  14. };
  15. self.Opt = {
  16. lineColor: "green",
  17. chessType: 1, //1 色彩棋子 2 仿真棋子
  18. playAName: "play1",
  19. playBName: "play2",
  20. playAColor: "red",
  21. playBColor: "blue",
  22. playAImg: "img/playA.png",
  23. playBImg: "img/playB.png",
  24. backColorORImg: "default",
  25. playerBIsComputer: false
  26. };
  27. self.operate; //合并属性
  28. for (var a in option) { //console.log(opt[a]);
  29. self.Opt[a] = option[a];
  30. }; //私有变量
  31. var my = {};
  32. my.enableCalcWeightNum = false; //显示AI分数
  33. my.gameover = false; //棋盘相关
  34. my.baseWidth = 30;
  35. my.lastFocusPoint = {}; //鼠标最后移动到的坐标点,计算后的
  36. my.cw = self.contextObj.offsetWidth; //棋盘宽
  37. my.ch = self.contextObj.offsetHeight; //高
  38. my.xlen = Math.ceil(my.cw / my.baseWidth); //行数
  39. my.ylen = Math.ceil(my.ch / my.baseWidth); //列
  40. my.chessRadius = 14; //棋子半径
  41. my.playerBIsComputer = false; //棋手B是否是电脑
  42. my.ComputerThinking = false; //电脑是否在下棋
  43. my.goBackC2dIsComputer = false; //最后下棋是否为电脑
  44. my.switcher = 1; //由谁下棋了 1-a 2-b or computer
  45. my.winer = -1; //赢家,值参考my.switcher
  46. my.playScoreA = 0;
  47. my.playScoreB = 0; //x,y 正方形数量(20*20)
  48. my.rectNum = my.xlen; //存储已下的点
  49. my.rectMap = [];
  50. my.NO_CHESS = -1; //没有棋子标识
  51. my.goBackC2d = {}; //最后下的数组转换坐标
  52. my.downChessmanStackC2d = []; // 记录已下棋子的顺序和位置,堆栈
  53. my.focusFlashInterval = null; //焦点闪烁线程
  54. my.focusChangeColors = ["red", "fuchsia", "#ADFF2F", "yellow", "purple", "blue"];
  55. my.eventBinded = false;
  56. my.currChessBackImg = null;
  57. my.currChessAImg = null;
  58. my.currChessBImg = null;
  59. my.currDrawChessImg = null;
  60. my.ChessDownNum = 0; //2个玩家 下棋总数
  61. /**
  62. * 开始游戏
  63. */
  64. self.start = function() {
  65. };
  66. /**
  67. * 重新开始游戏
  68. */
  69. self.restart = function() {
  70. };
  71. /**
  72. * 悔棋一步 ,清棋子,并返回上一次参数
  73. */
  74. self.back = function() {
  75. }
  76. /**
  77. * 初始化一些数据
  78. */
  79. function init() {
  80. }
  81. //
  82. self.paint = function() {
  83. //
  84. //
  85. //window.requestAnimationFrame(drawChessboard);
  86. //
  87. };
  88. /**
  89. * 游戏逻辑
  90. */
  91. function logic(loc, iscomputer) {
  92. };
  93. /**
  94. * 判断是否有玩家胜出
  95. * @param {Object} c2d
  96. */
  97. function isWin(c2d) {
  98. return false;
  99. } /**
  100. * 连接赢家棋子线
  101. * @param {Object} points
  102. */
  103. function joinWinLine(points) {
  104. } /**
  105. * 画棋盘 */
  106. function drawChessboard() {
  107. }; /**
  108. * 画棋子
  109. * @param {Object} loc 鼠标点击位置
  110. */
  111. function drawChessman(c2d) {
  112. } function drawRect(lastRecord, defColor) {
  113. }
  114. /**
  115. * 闪烁最后下棋点
  116. */
  117. function flashFocusChessman() {
  118. }
  119. /**
  120. * 清棋子
  121. * @param {Object} c2d
  122. */
  123. function clearChessman() {
  124. }
  125. /**
  126. * @param {Object} loc
  127. * @return {Object} I 二维数组横点(),J二维数组纵点,IX 横点起始坐标,JY纵点起始坐标,player 最后下棋玩, winer 赢家
  128. */
  129. function calc2dPoint(loc) { var txp = Math.floor(loc.x / my.baseWidth),
  130. typ = Math.floor(loc.y / my.baseWidth)
  131. dxp = txp * my.baseWidth, dyp = typ * my.baseWidth;
  132. loc.I = txp;
  133. loc.J = typ;
  134. loc.IX = dxp;
  135. loc.JY = dyp; return loc;
  136. }
  137. my.isChangeDraw = true; /**
  138. * 位置移动光标
  139. * @param {Object} loc */
  140. function moveFocus(loc) {
  141. }
  142. /**
  143. * 绑定事件
  144. */
  145. function bindEvent() {
  146. if (!my.eventBinded) {
  147. self.contextObj.addEventListener("touchstart", function(event) {
  148. //console.log(event);
  149. var touchObj = event.touches[0];
  150. eventHandle({
  151. s: "touch",
  152. x: touchObj.clientX - this.offsetLeft,
  153. y: touchObj.clientY - this.offsetTop
  154. })
  155. });
  156. self.contextObj.addEventListener("click", function(event) {
  157. //console.log("click event");
  158. eventHandle({
  159. s: "click",
  160. x: event.offsetX,
  161. y: event.offsetY
  162. })
  163. });
  164. self.contextObj.addEventListener("mousemove", function(event) {
  165. //console.log("mousemove event");
  166. moveFocus({
  167. x: event.offsetX,
  168. y: event.offsetY
  169. });
  170. });
  171. my.eventBinded = true;
  172. } function eventHandle(ps) {
  173. if (!my.gameover && !my.ComputerThinking) {
  174. logic(ps);
  175. if (my.playerBIsComputer && my.switcher == 2) {
  176. my.ComputerThinking = true;
  177. var pp = AI.analysis(my.goBackC2d.I, my.goBackC2d.J);
  178. logic({
  179. I: pp.x,
  180. J: pp.y
  181. }, true);
  182. my.ComputerThinking = false;
  183. }
  184. }
  185. event.preventDefault();
  186. event.stopPropagation();
  187. return false;
  188. }
  189. }
  190. };
  191. win.gobang = gb;
  192. })(window);

主要算法介绍

玩家OR电脑胜出算法

  1. /**
  2. * 判断是否有玩家胜出
  3. * @param {Object} c2d
  4. */
  5. function isWin(c2d) {
  6. //四个放心计数 竖 横 左斜 右斜
  7. var hcount = 0,
  8. vcount = 0,
  9. lbhcount = 0,
  10. rbhcount = 0,
  11. temp = 0;
  12. var countArray = [];
  13. //左-1
  14. for (var i = c2d.I; i >= 0; i--) {
  15. temp = my.rectMap[i][c2d.J];
  16. if (temp < 0 || temp !== c2d.player) {
  17. break;
  18. }
  19. hcount++;
  20. countArray.push({
  21. I: i,
  22. J: c2d.J
  23. });
  24. }
  25. //右-1
  26. for (var i = c2d.I + 1; i < my.rectMap.length; i++) {
  27. temp = my.rectMap[i][c2d.J];
  28. if (temp < 0 || temp !== c2d.player) {
  29. break;
  30. }
  31. hcount++;
  32. countArray.push({
  33. I: i,
  34. J: c2d.J
  35. });
  36. }
  37. if (countArray.length < 5) {
  38. countArray = [];
  39. //上-2
  40. for (var j = c2d.J; j >= 0; j--) {
  41. temp = my.rectMap[c2d.I][j];
  42. if (temp < 0 || temp !== c2d.player) {
  43. break;
  44. }
  45. vcount++;
  46. countArray.push({
  47. I: c2d.I,
  48. J: j
  49. });
  50. }
  51. //下-2
  52. for (var j = c2d.J + 1; j < my.rectMap[c2d.I].length; j++) {
  53. temp = my.rectMap[c2d.I][j];
  54. if (temp < 0 || temp !== c2d.player) {
  55. break;
  56. }
  57. vcount++;
  58. countArray.push({
  59. I: c2d.I,
  60. J: j
  61. });
  62. }
  63. }
  64. if (countArray.length < 5) {
  65. countArray = [];
  66. //左上
  67. for (var i = c2d.I, j = c2d.J; i >= 0, j >= 0; i--, j--) {
  68. if (i < 0 || j < 0) break;
  69. temp = my.rectMap[i][j];
  70. if (temp < 0 || temp !== c2d.player) {
  71. break;
  72. }
  73. lbhcount++;
  74. countArray.push({
  75. I: i,
  76. J: j
  77. });
  78. }
  79. //右下
  80. if (c2d.I < my.rectMap.length - 1 && c2d.I < my.rectMap[0].length - 1) {
  81. for (var i = c2d.I + 1, j = c2d.J + 1; i < my.rectMap.length, j < my.rectMap[0].length; i++, j++) {
  82. if (i >= my.rectMap.length || j >= my.rectMap.length) break;
  83. temp = my.rectMap[i][j];
  84. if (temp < 0 || temp !== c2d.player) {
  85. break;
  86. }
  87. lbhcount++;
  88. countArray.push({
  89. I: i,
  90. J: j
  91. });
  92. }
  93. }
  94. }
  95. if (countArray.length < 5) {
  96. countArray = [];
  97. //右上
  98. for (var i = c2d.I, j = c2d.J; i < my.rectMap.length, j >= 0; i++, j--) {
  99. if (i >= my.rectMap.length || j < 0) break;
  100. temp = my.rectMap[i][j];
  101. if (temp < 0 || temp !== c2d.player) {
  102. break;
  103. }
  104. rbhcount++;
  105. countArray.push({
  106. I: i,
  107. J: j
  108. });
  109. }
  110. //左下
  111. if (c2d.I >= 1 && c2d.J < my.rectMap[0].length - 1) {
  112. for (var i = c2d.I - 1, j = c2d.J + 1; i > 0, j < my.rectMap[0].length; i--, j++) {
  113. if (j >= my.rectMap.length || i < 0) break;
  114. temp = my.rectMap[i][j];
  115. if (temp < 0 || temp !== c2d.player) {
  116. break;
  117. }
  118. rbhcount++;
  119. countArray.push({
  120. I: i,
  121. J: j
  122. });
  123. }
  124. }
  125. }
  126. if (hcount >= 5 || vcount >= 5 || lbhcount >= 5 || rbhcount >= 5) {
  127. my.winer = c2d.player;
  128. my.gameover = true;
  129. joinWinLine(countArray);
  130. return true;
  131. }
  132. return false;
  133. }

算法简介:主要思路是搜索最后落下棋子的位置(二维坐标)计算字形线坐标,看是否有连续5个或以上棋子出现。

连接赢家棋子线


  1. /**
  2. * 连接赢家棋子线
  3. * @param {Object} points
  4. */
  5. function joinWinLine(points) {
  6. points.sort(function(left, right) {
  7. return (left.I + left.J) > (right.I + right.J);
  8. }); var startP = points.shift();
  9. var endP = points.pop();
  10. var poffset = my.baseWidth / 2;
  11. can.strokeStyle = "#FF0000";
  12. can.lineWidth = 2;
  13. can.beginPath();
  14. var spx = startP.I * my.baseWidth + poffset,
  15. spy = startP.J * my.baseWidth + poffset;
  16. can.arc(spx, spy, my.baseWidth / 4, 0, 2 * Math.PI, false);
  17. can.moveTo(spx, spy);
  18. var epx = endP.I * my.baseWidth + poffset,
  19. epy = endP.J * my.baseWidth + poffset;
  20. can.lineTo(epx, epy);
  21. can.moveTo(epx + my.baseWidth / 4, epy);
  22. can.arc(epx, epy, my.baseWidth / 4, 0, 2 * Math.PI, false);
  23. can.closePath();
  24. can.stroke();
  25. }

算法简介:根据赢家返回的连子位置集合,做坐标大小位置排序,直接使用lineto 连接 第一个棋子和最后一个

坐标换算

  1. /**
  2. * 坐标换算
  3. * @param {Object} loc
  4. * @return {Object} I 二维数组横点(),J二维数组纵点,IX 横点起始坐标,JY纵点起始坐标,player 最后下棋玩, winer 赢家
  5. */
  6. function calc2dPoint(loc) {
  7. var txp = Math.floor(loc.x / my.baseWidth),
  8. typ = Math.floor(loc.y / my.baseWidth)
  9. dxp = txp * my.baseWidth, dyp = typ * my.baseWidth;
  10. loc.I = txp;
  11. loc.J = typ;
  12. loc.IX = dxp;
  13. loc.JY = dyp;
  14. return loc;
  15. }

算法简介:这个比较简单,根据每个格子的宽度计算出实际坐标

电脑AI主要代码(修改来源于网络)

  1. /**
  2. * AI棋型分析
  3. */
  4. AI.analysis = function(x, y) {
  5. //如果为第一步则,在玩家棋周围一格随机下棋,保证每一局棋第一步都不一样
  6. if (my.ChessDownNum == 1) {
  7. return this.getFirstPoint(x, y);
  8. }
  9. var maxX = 0,
  10. maxY = 0,
  11. maxWeight = 0,
  12. i, j, tem;
  13. for (i = BOARD_SIZE - 1; i >= 0; i--) {
  14. for (j = BOARD_SIZE - 1; j >= 0; j--) {
  15. if (my.rectMap[i][j] !== -1) {
  16. continue;
  17. }
  18. tem = this.computerWeight(i, j, 2);
  19. if (tem > maxWeight) {
  20. maxWeight = tem;
  21. maxX = i;
  22. maxY = j;
  23. }
  24. if (my.enableCalcWeightNum) {
  25. can.clearRect(i * 30 + 2, j * 30 + 2, 24, 24);
  26. can.fillText(maxWeight, i * 30 + 5, j * 30 + 15, 30);
  27. }
  28. }
  29. }
  30. return new Point(maxX, maxY);
  31. };
  32. //下子到i,j X方向 结果: 多少连子 两边是否截断
  33. AI.putDirectX = function(i, j, chessColor) {
  34. var m, n,
  35. nums = 1,
  36. side1 = false, //两边是否被截断
  37. side2 = false;
  38. for (m = j - 1; m >= 0; m--) {
  39. if (my.rectMap[i][m] === chessColor) {
  40. nums++;
  41. } else {
  42. if (my.rectMap[i][m] === my.NO_CHESS) {
  43. side1 = true; //如果为空子,则没有截断
  44. }
  45. break;
  46. }
  47. }
  48. for (m = j + 1; m < BOARD_SIZE; m++) {
  49. if (my.rectMap[i][m] === chessColor) {
  50. nums++;
  51. } else {
  52. if (my.rectMap[i][m] === my.NO_CHESS) {
  53. side2 = true;
  54. }
  55. break;
  56. }
  57. }
  58. return {
  59. "nums": nums,
  60. "side1": side1,
  61. "side2": side2
  62. };
  63. };
  64. //下子到i,j Y方向 结果
  65. AI.putDirectY = function(i, j, chessColor) {
  66. var m, n,
  67. nums = 1,
  68. side1 = false,
  69. side2 = false;
  70. for (m = i - 1; m >= 0; m--) {
  71. if (my.rectMap[m][j] === chessColor) {
  72. nums++;
  73. } else {
  74. if (my.rectMap[m][j] === my.NO_CHESS) {
  75. side1 = true;
  76. }
  77. break;
  78. }
  79. }
  80. for (m = i + 1; m < BOARD_SIZE; m++) {
  81. if (my.rectMap[m][j] === chessColor) {
  82. nums++;
  83. } else {
  84. if (my.rectMap[m][j] === my.NO_CHESS) {
  85. side2 = true;
  86. }
  87. break;
  88. }
  89. }
  90. return {
  91. "nums": nums,
  92. "side1": side1,
  93. "side2": side2
  94. };
  95. };
  96. //下子到i,j XY方向 结果
  97. AI.putDirectXY = function(i, j, chessColor) {
  98. var m, n,
  99. nums = 1,
  100. side1 = false,
  101. side2 = false;
  102. for (m = i - 1, n = j - 1; m >= 0 && n >= 0; m--, n--) {
  103. if (my.rectMap[m][n] === chessColor) {
  104. nums++;
  105. } else {
  106. if (my.rectMap[m][n] === my.NO_CHESS) {
  107. side1 = true;
  108. }
  109. break;
  110. }
  111. }
  112. for (m = i + 1, n = j + 1; m < BOARD_SIZE && n < BOARD_SIZE; m++, n++) {
  113. if (my.rectMap[m][n] === chessColor) {
  114. nums++;
  115. } else {
  116. if (my.rectMap[m][n] === my.NO_CHESS) {
  117. side2 = true;
  118. }
  119. break;
  120. }
  121. }
  122. return {
  123. "nums": nums,
  124. "side1": side1,
  125. "side2": side2
  126. };
  127. };
  128. AI.putDirectYX = function(i, j, chessColor) {
  129. var m, n,
  130. nums = 1,
  131. side1 = false,
  132. side2 = false;
  133. for (m = i - 1, n = j + 1; m >= 0 && n < BOARD_SIZE; m--, n++) {
  134. if (my.rectMap[m][n] === chessColor) {
  135. nums++;
  136. } else {
  137. if (my.rectMap[m][n] === my.NO_CHESS) {
  138. side1 = true;
  139. }
  140. break;
  141. }
  142. }
  143. for (m = i + 1, n = j - 1; m < BOARD_SIZE && n >= 0; m++, n--) {
  144. if (my.rectMap[m][n] === chessColor) {
  145. nums++;
  146. } else {
  147. if (my.rectMap[m][n] === my.NO_CHESS) {
  148. side2 = true;
  149. }
  150. break;
  151. }
  152. }
  153. return {
  154. "nums": nums,
  155. "side1": side1,
  156. "side2": side2
  157. };
  158. };
  159. /**
  160. * 计算AI下棋权重
  161. * chessColor 玩家1为玩家2为AI
  162. */
  163. AI.computerWeight = function(i, j, chessColor) {
  164. //基于棋盘位置权重(越靠近棋盘中心权重越大)
  165. var weight = 19 - (Math.abs(i - 19 / 2) + Math.abs(j - 19 / 2)),
  166. pointInfo = {}; //某点下子后连子信息
  167. //x方向
  168. pointInfo = this.putDirectX(i, j, chessColor);
  169. weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true); //AI下子权重
  170. pointInfo = this.putDirectX(i, j, chessColor - 1);
  171. weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false); //player下子权重
  172. //y方向
  173. pointInfo = this.putDirectY(i, j, chessColor);
  174. weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true); //AI下子权重
  175. pointInfo = this.putDirectY(i, j, chessColor - 1);
  176. weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false); //player下子权重
  177. //左斜方向
  178. pointInfo = this.putDirectXY(i, j, chessColor);
  179. weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true); //AI下子权重
  180. pointInfo = this.putDirectXY(i, j, chessColor - 1);
  181. weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false); //player下子权重
  182. //右斜方向
  183. pointInfo = this.putDirectYX(i, j, chessColor);
  184. weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, true); //AI下子权重
  185. pointInfo = this.putDirectYX(i, j, chessColor - 1);
  186. weight += this.weightStatus(pointInfo.nums, pointInfo.side1, pointInfo.side2, false); //player下子权重
  187. return weight;
  188. };
  189. //权重方案 活:两边为空可下子,死:一边为空
  190. //其实还有很多种方案,这种是最简单的
  191. AI.weightStatus = function(nums, side1, side2, isAI) {
  192. var weight = 0;
  193. switch (nums) {
  194. case 1:
  195. if (side1 && side2) {
  196. weight = isAI ? 15 : 10; //一
  197. }
  198. break;
  199. case 2:
  200. if (side1 && side2) {
  201. weight = isAI ? 100 : 50; //活二
  202. } else if (side1 || side2) {
  203. weight = isAI ? 10 : 5; //死二
  204. }
  205. break;
  206. case 3:
  207. if (side1 && side2) {
  208. weight = isAI ? 500 : 200; //活三
  209. } else if (side1 || side2) {
  210. weight = isAI ? 30 : 20; //死三
  211. }
  212. break;
  213. case 4:
  214. if (side1 && side2) {
  215. weight = isAI ? 5000 : 2000; //活四
  216. } else if (side1 || side2) {
  217. weight = isAI ? 400 : 100; //死四
  218. }
  219. break;
  220. case 5:
  221. weight = isAI ? 100000 : 10000; //五
  222. break;
  223. default:
  224. weight = isAI ? 500000 : 250000;
  225. break;
  226. }
  227. return weight;
  228. };

AI分析:这个只是最简单的算法,其实很简单,计算每个没有下棋坐标的分数,也是按照 字形 计算,计算格子8个方向出现的 一个棋子 二个棋子 三个棋子 四个棋子,其中还分为是否被截断,其实就是边缘是否被堵死。

其实这个AI算法后续还有很多可以优化,比如 断跳 二活 其实就是2个交叉的 活二 , 因为是断掉的所以没有纳入算法权重计算,如果加入这个算法,估计很难下赢电脑了。

如符号图:

*        *
*      *
空位
下这里

因为不是连续的,所有没有纳入。

以上就是HTML5 Canvas实现五子棋游戏的代码分享(图文)的详细内容,更多请关注Gxl网其它相关文章!

人气教程排行