From b17963c30f8dedf6c402b52a98ffd4a4ddbfa390 Mon Sep 17 00:00:00 2001 From: drowning-in-codes <1322767102@qq.com> Date: Thu, 2 Dec 2021 12:33:26 +0800 Subject: [PATCH 1/2] first push --- README.en.md | 36 ---- README.md | 40 +--- game/ai.js | 452 +++++++++++++++++++++++++++++++++++++++++++ game/canvas.js | 275 ++++++++++++++++++++++++++ game/chess.js | 14 ++ game/game.css | 112 +++++++++++ game/game.html | 117 +++++++++++ game/game_control.js | 275 ++++++++++++++++++++++++++ game/player.js | 21 ++ 9 files changed, 1267 insertions(+), 75 deletions(-) delete mode 100644 README.en.md create mode 100644 game/ai.js create mode 100644 game/canvas.js create mode 100644 game/chess.js create mode 100644 game/game.css create mode 100644 game/game.html create mode 100644 game/game_control.js create mode 100644 game/player.js diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 492f3c6..0000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# Front-practice3 - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 37c7324..ff5f2d8 100644 --- a/README.md +++ b/README.md @@ -1,39 +1 @@ -# Front-practice3 - -#### 介绍 -{**以下是 Gitee 平台说明,您可以替换此简介** -Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} - -#### 软件架构 -软件架构说明 - - -#### 安装教程 - -1. xxxx -2. xxxx -3. xxxx - -#### 使用说明 - -1. xxxx -2. xxxx -3. xxxx - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request - - -#### 特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +最近没多少时间,直接照着敲了一遍源码... diff --git a/game/ai.js b/game/ai.js new file mode 100644 index 0000000..af2f899 --- /dev/null +++ b/game/ai.js @@ -0,0 +1,452 @@ +function ComputerAI() { + + //打分表 + var MARK = []; + MARK[0] = 100000000000;//成5 + MARK[1] = 1000000000;//活4 + MARK[2] = 10000000;//冲4 + MARK[3] = 1000000;//活3 + MARK[4] = 800000;//跳活3 + MARK[5] = 10000;//眠3 + MARK[6] = 1000;//活2 + MARK[7] = 100;//眠2 + MARK[8] = 1;//单子 + + + //是否使用AI + this.isPlayer = true; + //是否执黑 + this.isBlack = false; + //棋步记录器 + this.steps; + //对手棋步记录器 + this.hSteps; + //搜索深度 + this.depth = 1; + //初始化AI状态 + this.Init = function () { + this.isPlayer = true; + this.isBlack = false; + this.steps = []; + } + //计算机走棋 + this.putChess = function (canvas, plate) { + var point = this.tryToGo(plate); + var chess = new Chess(); + chess.Location.X = point.X; + chess.Location.Y = point.Y; + chess.isBlack = this.isBlack; + plate.putChess(canvas, chess); + var win = plate.hasWin(); + return win; + } + + + // + this.tryToGo = function (plate) { + + /* + * 思路分析 + + * 1.获取我下一步最佳落子点的集合 + * 2.获取对手下一步最佳落子点的集合 + * 3.比较此时双方局势分数 + * 1---- 如果我的分数大于等于对手分数 + * 1.我的分数大于等于800000 (有活三) + * 则可以选择先进攻 + * 2.我的分数小于800000 + * 比较我的下一最佳落子点,与对手下一落子点 落子后的局势分数 , + * 若对手分数高,则选择堵截,如果我高则选择进攻 + * 2---- 如果我的分数小于对手分数 + * 1.对手分数大于等于 800000 + * 则必须采取防守 + * 2.对手分数小于 800000 + * 比较我的下一最佳落子点,与对手下一落子点 落子后的局势分数 , + * 若我的分数较高,则选择进攻,否则选择防守 + * 4.返回最佳落子点中的一个随机点 + * + * + * */ + + /* + * 多步搜索 思路分析 + * + * 1.找出当前最佳点集合 + * 2.遍历当前最佳点集合,并落子入棋盘副本中,找出对手最佳集合,遍历对手最佳点集合,让对手落子入棋盘副本中, + * 调用递归。 + * 3.当搜索深度到底,返回当前局面分数 + * + * + * */ + var myType = this.isBlack ? 1 : -1; + var hisType = -myType; + + var myMark = this.getPlateInfo(plate, myType); + var hisMark = this.getPlateInfo(plate, hisType); + + var index = -1; + + var nextPoints = this.getNextChessPoint(plate, myType); + var myPoints = nextPoints[0]; + var hisPoints = nextPoints[1]; + + //创建推演棋盘 + //var plateT = this.getVirtualPlate(plate); + + + /*使用精确判断*/ + // if (myMark >= hisMark) { + // + // if (myMark < MARK[4] && hisPoints.length > 0) { + // plateT.Chesses[myPoints[0].X][myPoints[0].Y] = myType; + // var myMarkT = this.getPlateInfo(plateT, myType); + // plateT.Chesses[myPoints[0].X][myPoints[0].Y] = 0; + // plateT.Chesses[hisPoints[0].X][hisPoints[0].Y] = hisType; + // var hisMarkT = this.getPlateInfo(plateT, hisType); + // plateT.Chesses[hisPoints[0].X][hisPoints[0].Y] = 0; + // if (myMarkT >= hisMarkT) { + // //进攻 + // index = getRandom(0, myPoints.length); + // return myPoints[index]; + // + // } else { + // //防守 + // index = getRandom(0, hisPoints.length); + // return hisPoints[index]; + // } + // } else { + // /*进攻*/ + // index = getRandom(0, myPoints.length); + // return myPoints[index]; + // } + // } else { + // if (hisMark < MARK[4] && hisPoints.length > 0) { + // plateT.Chesses[myPoints[0].X][myPoints[0].Y] = myType; + // var myMarkT = this.getPlateInfo(plateT, myType); + // plateT.Chesses[myPoints[0].X][myPoints[0].Y] = 0; + // plateT.Chesses[hisPoints[0].X][hisPoints[0].Y] = hisType; + // var hisMarkT = this.getPlateInfo(plateT, hisType); + // plateT.Chesses[hisPoints[0].X][hisPoints[0].Y] = 0; + // if (myMarkT >= hisMarkT) { + // //进攻 + // index = getRandom(0, myPoints.length); + // return myPoints[index]; + // + // } else { + // //防守 + // index = getRandom(0, hisPoints.length); + // return hisPoints[index]; + // } + // } else { + // /*防守*/ + // index = getRandom(0, hisPoints.length); + // return hisPoints[index]; + // } + // } + + /*不适用精确判断*/ + if (myMark >= hisMark) { + index = getRandom(0, myPoints.length); + return myPoints[index]; + } else { + index = getRandom(0, hisPoints.length); + return hisPoints[index]; + } + + } + + this.DFSgo = function (plate, myType, hisType, depth) { + if (!depth) { + return this.getPlateInfo(plate, myType); + } else { + + + } + } + //获取一个用于AI推演的棋盘数组 + this.getVirtualPlate = function (plate) { + //复制plate对象到tryToPut + var tryToPut = new Plate(); + for (var i = 0; i < plate.Chesses.length; i++) { + tryToPut.Chesses[i] = plate.Chesses[i].slice(); + } + tryToPut.blackPlay = plate.blackPlay; + tryToPut.blackPlay = plate.blackPlay; + return tryToPut; + } + + //获取下一组最佳落子点 : + /* + * 返回值: 【最佳落子点,对手最佳落子点】 + * */ + + this.getNextChessPoint = function (plate, myType) { + var hisType = -myType; + var points = []; + var hPoints = []; + + //游戏进行中,使用AI算法下棋 + if (plate.isStart) { + //AI执白棋且AI第一次落子 + if (plate.chessCount == 1) { + var rt = this.putFirstWhiteChess(plate); + var point = new Location(); + point.X = rt.X; + point.Y = rt.Y; + points.push(point); + this.hSteps = this.getAllStepsInfo(plate, hisType, myType); + hPoints = this.getNextPoints(this.hSteps); + } + //棋盘中至少已经有两颗棋 + else { + //获取所有可行步的信息,并储存到this.steps中 + this.steps = this.getAllStepsInfo(plate, myType, hisType); + this.hSteps = this.getAllStepsInfo(plate, hisType, myType); + //获取最佳落子位置集合 + points = this.getNextPoints(this.steps); + //获取对手最佳落子位置集合 + hPoints = this.getNextPoints(this.hSteps); + + } + } + //AI 先走,直接在棋盘中间下棋 + else { + var point = new Location(); + point.X = plate.BLOCKS / 2; + point.Y = plate.BLOCKS / 2; + points.push(point); + } + return [points, hPoints]; + } + //获取一步棋的所有可行点的分数信息 + this.getAllStepsInfo = function (plate, myType, hisType) { + + //将step初始化为二维数组 + var steps = []; + for (var i = 0; i <= plate.BLOCKS; i++) { + steps[i] = []; + } + + //重新复制一张用于推演的棋盘 + var tryToPut = this.getVirtualPlate(plate); + + //遍历所有步骤 + for (var i = 0; i <= plate.BLOCKS; i++) { + for (var j = 0; j <= plate.BLOCKS; j++) { + steps[i][j] = this.getStepInfo(tryToPut, myType, hisType, i, j); + } + } + return steps; + } + //获取单步分值信息 + this.getStepInfo = function (tryToPut, myType, hisType, stepX, stepY) { + var step = new Object(); + if (tryToPut.Chesses[stepX][stepY] != 0) { + //此处有棋子,此处分数设为 + step.hisMark = -1000000000; + step.myMark = 0; + } else { + //试探性放下一颗棋子 + tryToPut.Chesses[stepX][stepY] = myType; + //将棋子放在此处的分数 + step.myMark = this.getPlateInfo(tryToPut, myType); + step.hisMark = this.getPlateInfo(tryToPut, hisType); + tryToPut.Chesses[stepX][stepY] = 0; + } + return step; + } + //AI下第一颗白棋 + this.putFirstWhiteChess = function (plate) { + var point = new Location(); + var theChess = plate.lastTemp[0]; + var X = theChess.Location.X; + var Y = theChess.Location.Y; + + //八个止点 + var points = [ + [X - 1, Y - 1], + [X, Y - 1], + [X + 1, Y - 1], + [X - 1, Y], + [X + 1, Y], + [X - 1, Y + 1], + [X, Y + 1], + [X + 1, Y + 1] + ]; + //获取随机一个索引 + var index = getRandom(0, 8); + point.X = points[index][0]; + point.Y = points[index][1]; + return point; + } + // //随机获取下一个最佳下棋点 + // this.getNextPointByRandom = function (nextStep) { + // var result = this.getNextPoints(nextStep); + // var index = getRandom(0, result.length); + // return result[index]; + // } + //获取下一组最佳落子位置 + this.getNextPoints = function (nextStep) { + var maxScore = -1000000000000; + + var temp = []; + + var row = nextStep.length; + var col = nextStep[0].length; + + //初步筛选出使我方分数最高的点 + for (var i = 0; i < row; i++) { + for (var j = 0; j < col; j++) { + //创建点 + var point = new Location(); + point.X = i; + point.Y = j; + + if (maxScore < nextStep[i][j].myMark) { + maxScore = nextStep[i][j].myMark; + temp = []; + temp.push(point); + } else if (maxScore == nextStep[i][j].myMark) { + temp.push(point); + } + } + } + var minScore = 1000000000000; + //筛选最终结果 + var result = []; + for (var i = 0; i < temp.length; i++) { + var X = temp[i].X; + var Y = temp[i].Y; + + if (minScore > nextStep[X][Y].hisMark) { + //清空result + result = []; + result.push(temp[i]); + minScore = nextStep[X][Y].hisMark; + } else if (minScore == nextStep[X][Y].hisMark) { + result.push(temp[i]); + } + } + + return result; + } + //获取棋盘信息,返回类型为 myType 的棋型分数 + this.getPlateInfo = function (plate, myType) { + var myMark = 0; + //判断黑棋(假设myType = 1, 黑棋) + //1.横向查找 + for (var i = 0; i <= plate.BLOCKS; i++) { + var crt_line = []; + for (var j = 0; j <= plate.BLOCKS; j++) { + crt_line.push(plate.Chesses[j][i]); + } + var lineStr = plate.chessToStr(crt_line); + myMark += this.getOneLineMark(lineStr, myType); + } + //2.纵向查找 + for (var i = 0; i <= plate.BLOCKS; i++) { + var crt_line = []; + crt_line = plate.Chesses[i]; + var lineStr = plate.chessToStr(crt_line); + myMark += this.getOneLineMark(lineStr, myType); + } + + //3.正斜向查找上半 + for (var i = 0; i <= plate.BLOCKS; i++) { + var crt_line = []; + for (var j = 0; i + j <= plate.BLOCKS; j++) { + crt_line.push(plate.Chesses[i + j][j]); + } + var lineStr = plate.chessToStr(crt_line); + myMark += this.getOneLineMark(lineStr, myType); + } + //4.正斜向查找下半 + for (var i = 1; i <= plate.BLOCKS; i++) { + var crt_line = []; + for (var j = 0; j + i <= plate.BLOCKS; j++) { + crt_line.push(plate.Chesses[j][i + j]); + } + var lineStr = plate.chessToStr(crt_line); + myMark += this.getOneLineMark(lineStr, myType); + } + //5.反斜向查找上半 + for (var i = 0; i <= plate.BLOCKS; i++) { + var crt_line = []; + for (var j = 0; i - j >= 0; j++) { + crt_line.push(plate.Chesses[j][i - j]); + } + var lineStr = plate.chessToStr(crt_line); + myMark += this.getOneLineMark(lineStr, myType); + } + //6.反斜向查找下半 + for (var i = 1; i <= plate.BLOCKS; i++) { + var crt_line = []; + for (var j = 0; j <= plate.BLOCKS && i + j <= plate.BLOCKS; j++) { + crt_line.push(plate.Chesses[plate.BLOCKS - j][i + j]); + } + var lineStr = plate.chessToStr(crt_line); + myMark += this.getOneLineMark(lineStr, myType); + } + return myMark; + } + //给一组棋打分 + this.getOneLineMark = function (lineStr, type) { + var myMark = 0; + + //如果是白棋则将字符串中a 换成 b, b 换成 a + if (type == -1) { + lineStr = this.exchangeAB(lineStr); + } + + + var reg = []; + //1.成5 + reg[0] = [/aaaaa/g]; + //2.活4 + reg[1] = [/_aaaa_/g]; + //3.冲4 + reg[2] = [/^aaaa_/g, /aa_aa/g, /a_aaa/g, /aaa_a/g, /_aaaa$/g, /baaaa_/g, /_aaaab/g]; + //4.活3 + reg[3] = [/_aaa__/g, /__aaa_/g]; + //5.跳活3 + reg[4] = [/_aa_a_/g, /_a_aa_/g]; + //6.眠3 + reg[5] = [/aaa__/g, /__aaa/g, /aa_a_/g, /a_aa_/g, /aa__a/g, /a_a_a/g, /_aaa_/g, /_a_aa/g, /_aa_a/g, /a__aa/g]; + //7.活2 + reg[6] = [/__aa__/g, /_a_a_/g, /_a__a_/g]; + //8.眠2 + reg[7] = [/___aab/g, /___aa$/g, /^aa___/g, /baa___/g, /ba_a__/g, /__a_ab/g, /^a_a__/g, /__a_a$/g, + /ba__a_/g, /^a__a_/g, /_a__ab/g, /_a__a$/g, /a___a/g]; + //9.单子 + reg[8] = [/a/g]; + for (var i = 0; i < reg.length; i++) { + for (var j = 0; j < reg[i].length; j++) { + //符合条件的棋型个数 + var count = 0; + var result = lineStr.match(reg[i][j]); + count = result == null ? 0 : result.length; + // if (i == 3) count /= 2;//消除活三重复登记 + myMark += MARK[i] * count; + } + } + return myMark; + } + //交换字符串中的黑棋和白棋 + this.exchangeAB = function (lineStr) { + lineStr = lineStr.replace(/a/g, 'c'); + lineStr = lineStr.replace(/b/g, 'a'); + lineStr = lineStr.replace(/c/g, 'b'); + return lineStr; + } +} + +//产生begin到end(不包含end)之间的随机数 +function getRandom(begin, end) { + var rd = Math.random() * (end - begin); + rd = Math.floor(rd); + rd += begin; + return rd; +} + + + diff --git a/game/canvas.js b/game/canvas.js new file mode 100644 index 0000000..bce8a8a --- /dev/null +++ b/game/canvas.js @@ -0,0 +1,275 @@ +/* +* 棋盘类 +* 实现走棋,悔棋,撤销悔棋,判断输赢,绘制棋盘,绘制棋子 +* */ +function Canvas() { + //开始游戏 + this.Init = function () { + //清空棋子 + this.chessCount = 0; + this.isStart = false; + this.Chesses.length = 0; + this.lastTemp.length = 0; + this.nextTemp.length = 0; + this.blackPlay = true; + for (let i = 0; i <= this.BLOCKS; i++) { + this.Chesses[i] = []; + for (let j = 0; j <= this.BLOCKS; j++) { + this.Chesses[i][j] = 0; + } + } + + } + //画网格线 + this.drawLine = function (canvas) { + var context = canvas.getContext("2d"); + context.strokeStyle = "#000000"; + for (var i = 0; i <= this.BLOCKS; i++) { + context.moveTo(this.LEFTX + i * this.PERSIZE, this.LEFTY);//画竖线 + context.lineTo(this.LEFTX + i * this.PERSIZE, this.LEFTY + this.BLOCKS * this.PERSIZE); + context.stroke(); + context.moveTo(this.LEFTX, this.LEFTY + i * this.PERSIZE);//画横线 + context.lineTo(this.LEFTX + this.BLOCKS * this.PERSIZE, this.LEFTY + i * this.PERSIZE); + context.stroke(); + + //画横标 + context.font = "20px bold 黑体"; + context.fillStyle = "#000000"; + context.fillText((this.BLOCKS - i + 1).toString(), this.LEFTX - 40, this.LEFTY + i * this.PERSIZE); + } + + var str = "ABCDEFGHIJKLMNO"; + for (var i = 0; i <= this.BLOCKS; i++) { + //画纵标 + context.font = "20px bold 黑体"; + context.fillStyle = "#000000"; + context.fillText((str[i]), this.LEFTX + this.PERSIZE * i - 10, this.LEFTY + this.PERSIZE * this.BLOCKS + 40); + } + //绘制五子棋棋盘的五个星 + var star = [ + [this.LEFTX + this.PERSIZE * 3, this.LEFTY + this.PERSIZE * 3], + [this.LEFTX + this.PERSIZE * 11, this.LEFTY + this.PERSIZE * 3], + [this.LEFTX + this.PERSIZE * 7, this.LEFTY + this.PERSIZE * 7], + [this.LEFTX + this.PERSIZE * 3, this.LEFTY + this.PERSIZE * 11], + [this.LEFTX + this.PERSIZE * 11, this.LEFTY + this.PERSIZE * 11] + ]; + var context = canvas.getContext("2d"); + for (var i = 0; i < 5; i++) { + context.beginPath(); + context.arc(star[i][0], star[i][1], 6, 0, 2 * Math.PI); + context.closePath(); + context.fillStyle = "#000000"; + context.fill(); + } + } + //下棋 + this.putChess = function (canvas, chess) { + this.isStart = true; + //棋子位置没有越界 + if (chess.Location.X <= this.BLOCKS && chess.Location.Y <= this.BLOCKS && chess.Location.X >= 0 && chess.Location.Y >= 0) { + //向棋盘中加入这颗棋子 + this.Chesses[chess.Location.X][chess.Location.Y] = chess.isBlack ? 1 : -1; + //给恢复上一步的记忆数组中加入这颗棋子 + this.lastTemp.push(chess); + + this.justChesss = this.lastTemp[this.lastTemp.length - 2]; + } else return; + + var context = canvas.getContext("2d"); + //设置圆的位置 + var gradient = context.createRadialGradient(this.LEFTX + chess.Location.X * this.PERSIZE + 2, this.LEFTY + chess.Location.Y * this.PERSIZE - 2, + chess.Radius - 2, this.LEFTX + chess.Location.X * this.PERSIZE + 2, this.LEFTY + chess.Location.Y * this.PERSIZE - 2, 0); + if (chess.isBlack == true) { + gradient.addColorStop(0, '#0a0a0a'); + gradient.addColorStop(1, '#636766'); + } else { + gradient.addColorStop(0, '#d1d1d1'); + gradient.addColorStop(1, '#f9f9f9'); + } + this.chessCount++; + this.showChess(canvas, chess, gradient); + this.blackPlay = !this.blackPlay; + } + //在canvas中绘制刚走的棋子 + this.showChess = function (canvas, chess, gradient) { + var context = canvas.getContext("2d"); + context.beginPath(); + context.arc(this.LEFTX + chess.Location.X * this.PERSIZE, this.LEFTY + chess.Location.Y * this.PERSIZE, chess.Radius, 0, 2 * Math.PI); + context.closePath(); + context.fillStyle = gradient; + context.fill(); + + if(this.canShowOrder){ + //绘制数字 + context.font = "12px bold 黑体"; + context.fillStyle = chess.isBlack ? "#ffffff" : "#000000"; + context.fillText(this.chessCount.toString(), this.LEFTX + chess.Location.X * this.PERSIZE - 2, this.LEFTY + chess.Location.Y * this.PERSIZE); + } + } + + //在canvas中绘制上一步棋子 + this.showJustChess = function (canvas, chess, gradient) { + var context = canvas.getContext("2d"); + context.beginPath(); + context.arc(this.LEFTX + chess.Location.X * this.PERSIZE, this.LEFTY + chess.Location.Y * this.PERSIZE, chess.Radius, 0, 2 * Math.PI); + context.closePath(); + context.fillStyle = gradient; + context.fill(); + } + //拿掉一颗棋子 + this.clearChess = function (canvas, chess) { + var context = canvas.getContext("2d"); + context.clearRect(chess.Location.X * this.PERSIZE + this.LEFTX - this.PERSIZE / 2, + chess.Location.Y * this.PERSIZE + this.LEFTY - this.PERSIZE / 2, + this.PERSIZE, this.PERSIZE); + + //棋子数减少 + this.chessCount--; + //重画周围的格子 + context.strokeStyle = "#000000"; + context.beginPath(); + context.moveTo(chess.Location.X * this.PERSIZE + this.LEFTX, + chess.Location.Y * this.PERSIZE + this.LEFTY - this.PERSIZE / 2); + context.lineTo(chess.Location.X * this.PERSIZE + this.LEFTX, + chess.Location.Y * this.PERSIZE + this.LEFTY + this.PERSIZE / 2); + context.stroke(); + context.moveTo(chess.Location.X * this.PERSIZE + this.LEFTX - this.PERSIZE / 2, + chess.Location.Y * this.PERSIZE + this.LEFTY); + context.lineTo(chess.Location.X * this.PERSIZE + this.LEFTX + this.PERSIZE / 2, + chess.Location.Y * this.PERSIZE + this.LEFTY); + context.stroke(); + } + + + //正则表达式查找是否有五子连珠 + this.hasWin = function () { + if (this.chessCount == (this.BLOCKS + 1) * (this.BLOCKS + 1)) return -100; + //1.横向查找 + for (var i = 0; i <= this.BLOCKS; i++) { + var crt_line = []; + for (var j = 0; j <= this.BLOCKS; j++) { + crt_line.push(this.Chesses[j][i]); + } + var chessStr = this.chessToStr(crt_line); + //查找结果 + var result = this.checkFiveChess(chessStr); + if (result != 0) return result; + } + //2.纵向查找 + for (var i = 0; i <= this.BLOCKS; i++) { + var crt_line = []; + crt_line = this.Chesses[i]; + var chessStr = this.chessToStr(crt_line); + //查找结果 + var result = this.checkFiveChess(chessStr); + if (result != 0) return result; + } + + //3.正斜向查找上半 + for (var i = 0; i <= this.BLOCKS; i++) { + var crt_line = []; + for (var j = 0; i + j <= this.BLOCKS; j++) { + crt_line.push(this.Chesses[i + j][j]); + } + var chessStr = this.chessToStr(crt_line); + //查找结果 + var result = this.checkFiveChess(chessStr); + if (result != 0) return result; + } + //4.正斜向查找下半 + for (var i = 0; i <= this.BLOCKS; i++) { + var crt_line = []; + for (var j = 0; j + i <= this.BLOCKS; j++) { + crt_line.push(this.Chesses[j][i + j]); + } + var chessStr = this.chessToStr(crt_line); + //查找结果 + var result = this.checkFiveChess(chessStr); + if (result != 0) return result; + } + //5.反斜向查找上半 + for (var i = 0; i <= this.BLOCKS; i++) { + var crt_line = []; + for (var j = 0; i - j >= 0; j++) { + crt_line.push(this.Chesses[j][i - j]); + } + var chessStr = this.chessToStr(crt_line); + //查找结果 + var result = this.checkFiveChess(chessStr); + if (result != 0) return result; + } + //6.反斜向查找下半 + for (var i = 1; i <= this.BLOCKS; i++) { + var crt_line = []; + for (var j = 0; j <= this.BLOCKS && i + j <= this.BLOCKS; j++) { + crt_line.push(this.Chesses[this.BLOCKS - j][i + j]); + } + var chessStr = this.chessToStr(crt_line); + //查找结果 + var result = this.checkFiveChess(chessStr); + if (result != 0) return result; + } + + //均没有找到 + return 0; + } + //棋子集合转化为字符串 + this.chessToStr = function (chesses) { + var result = ""; + + for (var i = 0; i < chesses.length; i++) { + switch (chesses[i]) { + case 1: { + result += "a"; + break; + } + case -1: { + result += "b"; + break; + } + case 0: { + result += "_"; + break; + } + } + } + return result; + } + //检查字符串中是否有 五子连珠 + this.checkFiveChess = function (chessStr) { + var whiteWin = /bbbbb/g; + var blackWin = /aaaaa/g; + if (whiteWin.test(chessStr)) { + return -1; + } else if (blackWin.test(chessStr)) { + return 1; + } else return 0; + } + //背景颜色 + this.backgroundColor = "#eec254"; + //上一步的存档 + this.lastTemp = []; + //恢复到上一步后下一步的存档 + this.nextTemp = []; + //是否轮黑棋走棋 + this.blackPlay = true; + //棋子集合 + this.Chesses = []; + //单向格数 + this.BLOCKS = 14; + //格子宽度 + this.PERSIZE = 42; + //左上角横坐标 + this.LEFTX = 40; + //左上角纵坐标 + this.LEFTY = 30; + //游戏是否开始 + this.isStart = false; + //上一颗棋子 + this.justChesss = new Chess(); + //棋子个数 + this.chessCount = 0; + //是否显示棋子数字 + this.canShowOrder = true; +} + diff --git a/game/chess.js b/game/chess.js new file mode 100644 index 0000000..5b8462c --- /dev/null +++ b/game/chess.js @@ -0,0 +1,14 @@ +/*棋子类*/ +function Chess() { + //棋子半径大小 + this.Radius = 17; + //棋子颜色 + this.isBlack = true; + //棋子位置 + this.Location = new Location(); +} +/*坐标*/ +function Location(x, y) { + this.X = x; + this.Y = y; +} \ No newline at end of file diff --git a/game/game.css b/game/game.css new file mode 100644 index 0000000..e5b2d5a --- /dev/null +++ b/game/game.css @@ -0,0 +1,112 @@ +html { + background-color: #fffeec; +} + +.btn { + width: 100px; + height: 50px; + margin-bottom: 50px; + border: none; + border-radius: 25px; + text-align: center; + cursor: pointer; + background: rgb(2,0,36); + background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(17,196,184,1) 66%, rgba(0,212,255,1) 100%); + color: #ffffff; + font-size: large; + font-family:'Courier New', Courier, monospace; + font-weight: bolder; + /* outline: none; */ +} +.btn:hover { + cursor: pointer; + border: none; +} + +.btn:active { + background: linear-gradient(to bottom, #ff0080, #d900fb); +} + +.c-btn { + margin: 10px 20px; + width: 100px; + height: 30px; +} + +.marks { + width: 160px; + height: 50px; + text-align: center; +} +.message-box { + display: none; + position: absolute; + border-style: solid; + border-width: 5px; + border-color: #000000; + border-radius: 10px; + height: 200px; + width: 250px; + text-align: center; + padding: 20px; + top: 20%; + left: 30%; + background-color: lemonchiffon; +} +.header { + margin: 0px auto; + width: 1000px; + height: 40px; + line-height: 40px; + vertical-align: middle; + background: rgb(2,0,36); + background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(27,171,59,1) 66%, rgba(0,212,255,1) 100%); + font-weight: bolder; + font-style: italic; + text-align: center; + text-decoration: blink; + color: white; + font-size: 32px; + font-family:'Courier New', Courier, monospace; +} +.mainbar { + margin: 0 auto; + padding: 20px 20px; + width: 1000px; + height: 760px; +} +#canvas { + float: left; + margin: 0 20px; + display: block; + box-shadow: -2px -2px 2px #ff0016, 5px 5px 5px #00a9ff; + background-color: #eec254; +} + +#canvas:hover { + cursor: pointer; +} +.sidebar { + float: left; + width: 200px; + padding: 0px 20px; + height: 660px; + margin: 0px 20px; + text-align: center; + background-color: rgba(187, 173, 160, 0.45); +} + +#game-over-box { + margin: 20px; +} + +#game-result { + margin: 20px; + width: 200px; +} + +.game-info { + margin: 20px; + text-align: center; + padding: 5px; +} \ No newline at end of file diff --git a/game/game.html b/game/game.html new file mode 100644 index 0000000..24ed74e --- /dev/null +++ b/game/game.html @@ -0,0 +1,117 @@ + + + + + + + + +
+ +五子棋游戏 + made by self +
+ + + + + + + + + + diff --git a/game/game_control.js b/game/game_control.js new file mode 100644 index 0000000..763c652 --- /dev/null +++ b/game/game_control.js @@ -0,0 +1,275 @@ + +window.onload = function (ev) { + //DOM 组件 + //画布 + var canvas = document.getElementById("canvas"); + //div 盒子 + var div_choose_box = document.getElementById("choose-box"); + var div_game_over = document.getElementById("game-over-box"); + var div_black_marks = document.getElementById("black-marks"); + var div_white_marks = document.getElementById("white-marks"); + var div_ai_strength = document.getElementById("AI-strength-box"); + //按钮 + var btn_new = document.getElementById("btn-new"); + var btn_last = document.getElementById("btn-last"); + var btn_next = document.getElementById("btn-next"); + var btn_choose_black = document.getElementById("choose-black"); + var btn_choose_white = document.getElementById("choose-white"); + var btn_choose_none = document.getElementById("choose-none"); + var btn_choose_all = document.getElementById("choose-all"); + var btn_replay = document.getElementById("replay"); + var btn_ok = document.getElementById("ok"); + var btn_change_style = document.getElementById("btn-change-style"); + var btn_ai_sts = document.getElementsByClassName("ai-st"); + + + //游戏状态标识 + var blackWin = 1; + var whiteWin = -1; + var gameContinue = 0; + var noWin = -100; + var playerGo = -2; + + //游戏开始组件 + var AI = new ComputerAI(); + + var player = new Player(); + var plate = new Canvas(); + //初始化棋盘 + plate.Init(); + plate.drawLine(canvas); + + //按钮显示状态 + btnStatusControl(plate); + + //新游戏 + btn_new.onclick = function () { + //强制制止AI走棋 + AI.isPlayer = false; + //清空画布 + canvas.height = canvas.height; + + //分数显示器初始化 + div_white_marks.innerHTML = ""; + div_black_marks.innerHTML = ""; + + div_choose_box.style.display = "block"; + + //创建新游戏 + plate.Init(); + //重画棋盘 + plate.drawLine(canvas); + + //点击棋盘 + canvas.onclick = function (event) { + //如果不轮玩家走 + if (plate.blackPlay != player.isBlack) return; + //点击在棋盘上的坐标 + var location = new Location(); + //将鼠标点击坐标转化为棋盘坐标 + location.X = Math.floor((event.offsetX - plate.LEFTX + plate.PERSIZE / 2) / plate.PERSIZE); + location.Y = Math.floor((event.offsetY - plate.LEFTY + plate.PERSIZE / 2) / plate.PERSIZE); + //放置棋子后的结果 + var result = player.putChess(canvas, plate, location); + if (!AI.isPlayer) { + player.isBlack = !player.isBlack; + } + + //设置定时器,防止游戏结果在棋子显示之前显示 + setTimeout(function () { + //显示游戏结果 + getGameResult(result); + }, 10); + + btnStatusControl(plate); + } + + btnStatusControl(plate); + } + //上一步 + btn_last.onclick = function () { + plate.blackPlay = !plate.blackPlay; + div_game_over.style.display = "none"; + //移除这颗已经从棋盘上收回的棋子 + var lastChess = plate.lastTemp.pop(); + //放入下一步的记忆数组中 + plate.nextTemp.push(lastChess); + //从棋盘上拿除棋子 + plate.Chesses[lastChess.Location.X][lastChess.Location.Y] = 0; + //从画布上清除棋子 + plate.clearChess(canvas, lastChess); + + //获取黑白棋的分数 + var black_marks = AI.getPlateInfo(plate, 1); + var white_marks = AI.getPlateInfo(plate, -1); + + //显示分数 + div_black_marks.innerHTML = "黑棋分数