前言
- 使用技术:
- canvas
- 算法
- 三维数组
- 循环遍历(计算赢法,计算坐标)
棋盘阴影
display: block;margin: 50px auto;box-shadow: 5px 5px 5px #b9b9b9, -2px -2px 2px #efefef;
背景渐变
background-image: linear-gradient(to top, #fbc2eb 0%, #a6c1ee 100%);
绘制棋盘
// 获取棋盘元素容器let chess = document.querySelector('.chess');// 获取显示胜利的元素let title = document.querySelector('h3');// 获取开始游戏按钮let start = document.querySelector('.start');// 控制游戏状态let flag = false;// 将该元素变成一个可绘制的画板let context = chess.getContext('2d');// 设置画笔绘制颜色context.strokeStyle = '#b9b9b9';// 定义一个绘制棋盘的函数function drawChessBoard () {// 循环遍历生成棋盘for (let i = 0; i < 15; i++) {// 设置横线起始点坐标context.moveTo(15, 15+i*30);// 设置横线结束点坐标context.lineTo(435, 15+i*30);// 连接 2 点context.stroke();// 设置竖线起始点坐标context.moveTo(15+i*30, 15);// 设置竖线结束点坐标context.lineTo(15+i*30, 435);// 连接 2 点context.stroke();}}
赢法数组
- 定义一个赢法数组
- 将每个子的坐标放入数组中,判断五个子的坐标是否和赢法数组相匹配,匹配则赢,不匹配则继续游戏
```javascript
// 定义赢法数组
let wins = [];
// 循环生成赢法数组
for(let i = 0; i < 15; i++) {
wins[i] = []
for(let j = 0; j < 15; j++) {
} }wins[i][j] = []
// 设置赢法编号 let count = 0; // 统计横线赢 for(let i = 0; i < 15; i++) { for(let j = 0; j < 11; j++) { for(let k = 0; k < 5; k++) { wins[j+k][i][count]=true; } count++; } }
// 统计竖线赢 for(let i = 0; i <15; i++) { for(let j = 0; j < 11; j++) { for(let k = 0; k < 5; k++) { wins[i][j+k][count]=true; } count++; } }
// 统计正斜线赢 for(let i = 0; i < 11; i++) { for(let j = 0; j < 11; j++) { for(let k = 0; k < 5; k++) { wins[i+k][j+k][count]=true; } count++; } }
// 统计反斜线赢 for(let i = 0; i < 11; i++) { for(let j = 14; j > 3; j—) { for(let k = 0; k < 5; k++) { wins[i+k][j-k][count]=true; } count++; } }
// 判断棋盘上的每个坐标内是否已经下了棋子 // 定义一个二维数组,标记棋盘上的所有坐标 let chessboard = []; // 循环遍历棋盘所有坐标 for(let i = 0; i < 15; i++) { chessboard[i]=[]; for(let j = 0; j < 15; j++) { chessboard[i][j] = 0; } }
// 下棋 let me = true; // 标记玩家是否可以下棋 let over = false; // 标记游戏是否结束
chess.onclick = function(e) { // 如果游戏结束,不可以下棋 if(over) { return; } // 判断玩家是否可以下棋 if(!me) { return; } // 获取 x 轴坐标 let x = e.offsetX; // 获取 y 轴坐标 let y = y.offsetY;
let i = Math.floor(x/30); let j = Math.floor(y/30);
if(chessboard[i][j] == 0) { // 下一个子 chessboard[i][j] = 1; } }
<a name="mB0pk"></a># 落子方法(绘制棋子)```javascriptfunction oneStep(i, j, me) {// 绘制棋子ctx.beginPath();ctx.arc(15+i*30, 15+j*30, 13, 0, 2*Math.PI);ctx.closePath();// 判断玩家或AI下棋,更改棋子颜色let color;if(me){color = '#000'}else {color = '#fff'}// 更改画笔颜色并填充ctx.fillStyle = color;ctx.fill()}
下棋
- 判断玩家是否可以下棋
- 游戏是否结束
- 记录玩家赢法分值
记录AI赢法分值
// 定义所需变量let me = true; // 标记玩家是否可以下棋let over = false; // 标记游戏是否结束let myWin = []; // 记录玩家在赢法上的分值let computerWin = []; // 记录计算机在赢法上的分值
// 初始化玩家赢法分值与AI赢法分值for(let i = 0; i < count; i++) {myWin[i] = 0;computerWin[i] = 0;}
// 判断是否点击开始游戏start.onclick = () => {if(!flag){flag = !flag;start.innerHTML = '重新开始';}else {window.location.reload()}}
// 点击画板落子 chess.onclick = function(e) { // 如果游戏结束,不可以下棋 if(flag) { if(over) { return; } // 判断玩家是否可以下棋 if(!me) { return; } // 获取 x 轴坐标 let x = e.offsetX; // 获取 y 轴坐标 let y = e.offsetY; let i = Math.floor(x/30); let j = Math.floor(y/30); if(chessboard[i][j] == 0) { // 下一个子 oneStep(i, j, me); chessboard[i][j] = 1; for(let k = 0; k < count; k++) { if(wins[i][j][k]) { myWin[k]++; if(myWin[k] == 5) { title.innerHTML = 'player win!' over = true; } } } } if(!over) { me = !me; // 计算机落子 computerAI(); } // AI落子 function computerAI() { // 空白子在用户所占用赢法上的分值 let myScore = []; // 空白子在计算机所占用赢法上的分值 let computerScore = []; for(let i = 0; i < 15; i++) { myScore[i] = []; computerScore[i] = []; for(let j = 0; j < 15; j++) { myScore[i][j] = 0; computerScore[i][j] = 0; } } // 空白子的最大分值 let max = 0; // 最大分值空白子所在的坐标 let x = 0, y = 0; for(let i = 0; i < 15; i++) { for(let j = 0; j < 15; j++) { // 判断是否是空白子 if(chessboard[i][j] == 0) { for(let k = 0; k < count; k++) { if(wins[i][j][k]) { // 玩家分值 if(myWin[k] == 1) { myScore[i][j] += 200; }else if(myWin[k] == 2) { myScore[i][j] += 400; }else if(myWin[k] == 3) { myScore[i][j] += 2000; }else if(myWin[k] == 4) { myScore[i][j] += 10000; } // AI分值 if(computerWin[k] == 1) { computerScore[i][j] += 220; }else if(computerWin[k] == 2) { computerScore[i][j] += 420; }else if(computerWin[k] == 3) { computerScore[i][j] += 2200; }else if(computerWin[k] == 4) { computerScore[i][j] += 20000; } } } // 判断玩家最大分值的空白子 if(myScore[i][j] > max) { max = myScore[i][j]; x = i; y = j; }else if(myScore[i][j] == max) { if(computerScore[i][j] > max) { max = computerScore[i][j]; x = i; y = j; } } // 判断AI最大分值的空白子 if(computerScore[i][j] > max) { max = computerScore[i][j]; x = i; y = j; }else if(computerScore[i][j] == max){ if(myScore[i][j] > max) { max = myScore[i][j]; x = i; y = j; } } } } } oneStep(x, y, me); chessboard[x][y] = 1; // 判断计算机是否赢 for(let k = 0; k < count; k++) { if(wins[x][y][k]) { computerWin[k] += 1; if(computerWin[k] == 5) { title.innerHTML = 'AI win!' over = true; } } } if(!over) { me = !me; } } // 落子方法 } }落子方法
function oneStep(i, j, me) { // 绘制棋子 ctx.beginPath(); ctx.arc(15+i*30, 15+j*30, 13, 0, 2*Math.PI); ctx.closePath(); // 判断玩家或AI下棋,更改棋子颜色 let color; if(me){ color = '#000' }else { color = '#fff' } // 更改画笔颜色并填充 ctx.fillStyle = color; ctx.fill() }
