websocket实现五子棋联机对战
GoBang.html // 对弈的页面
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style> *{ padding: 0;margin: 0; } html,body{width:100%;height: 100%;} div{ text-align: center; } div button{ line-height: 50px; font-size: 36px; } .canvas{ margin: 0 auto; } </style> </head> <body> <div class="canvas"> <canvas id="gobang"></canvas> </div> <div> <button id="regret">悔棋</button> <button id="chessAgain">再来一局</button> <button id="goHome">返回大厅</button> <button id="getConnection">获取connection</button> </div> </body> <script type="text/javascript"> var user,chessBoard,next; var gobang; if(sessionStorage.getItem("user")){ user = JSON.parse(sessionStorage.getItem("user")); var qs = qs2obj(location.href); if(qs.player1==user.name){ // 玩家1 console.log("玩家1"); user.type = "player1"; }else if(qs.player2==user.name){// 玩家2 console.log("玩家2"); user.type = "player2"; }else{ console.log("观众"); user.type = "viewer"; } user.roomId = qs.roomId; }else{ location.href = "index.html"; } var ws = new WebSocket("ws://10.100.106.114:9000"); ws.onopen = function(e){ sendUserMsg(); } ws.onerror = function(e){ console.log("链接服务器失败"); } ws.onclose = function(e){ console.log("链接断开"); } ws.onmessage = function(e){ console.log(e.data); var data = JSON.parse(e.data); switch(data.type){ case "baseMsg": break; case "chessBoard": chessBoard = data.data; next = data.next; gobang.pieces = chessBoard; gobang.active = next; gobang.renderUi(); gobang.highLight(data.lastStep.x,data.lastStep.y,gobang.active); gobang.hisStatus.push(gobang.deepClone(gobang.pieces));//历史记录(悔棋复盘功能使用) gobang.getVictor(); break; case "users": break; default:; } } function sendUserMsg(){ var msg = {}; msg.type = "user"; msg.data = user; ws.send(JSON.stringify(msg)); } function sendChessBoard(next,chessBoard,x,y){ var msg = {}; msg.type = "chess"; msg.next = next;// 下一个走棋的玩家 msg.data = chessBoard; msg.roomId = user.roomId; msg.lastStep = {x:x,y:y}; ws.send(JSON.stringify(msg)); } function sendVictor(){ var msg = {}; msg.type = "victor"; msg.data = victor; ws.send(JSON.stringify(msg)); } function chessAgain(eventOrigin){ var msg = {}; msg.type = "again"; msg.data = {eventOrigin:eventOrigin}; ws.send(JSON.stringify(msg)); } function getConnection(){ var msg = {}; msg.type = "connection"; msg.data = {}; ws.send(JSON.stringify(msg)); } /** * @author web得胜 * @param {String} url url地址栏 * @return {Object} */ function qs2obj(url) { var qs = url.split("?")[1]; var arr = []; var res = {}; if(!qs) { // return res; } else { arr = qs.split("&"); for(var i = 0, len = arr.length; i < len; i++) { var key = arr[i].split("=")[0]; var val = arr[i].split("=")[1]; res[key] = decodeURIComponent(val); } } return res; } </script> <script type="text/javascript"> // 五子棋 class Gobang { constructor(canvasId, rowCount = 16) { this.canvasId = canvasId; this.rowCount = rowCount; this.resetData(); } // 渲染页面 renderUi() { //清除之前的画布 this.ctx.clearRect(0, 0, this.width, this.height); // 重绘画布 this.drawChessBoard(); this.drawPieces(); } // 重置数据,再来一局 resetData() { var body = document.documentElement || document.body; var minWidth = Math.min(body.clientWidth,body.clientHeight); // 属性 this.pieces = []; // 棋子数组 二位数组[[],[]] 0——空 1——白 2——黑 this.colCount = this.rowCount; // 列数 this.cellWidth = minWidth / (this.rowCount); //每个格子的宽 this.width = this.rowCount * this.cellWidth; // 棋盘的宽 this.height = this.width; // 棋盘的高 this.R = this.cellWidth * 0.4; // 棋子半径 this.hisStatus = []; // 历史记录 history status this.active = 2; // 当前走棋方 this.canvas = document.getElementById(this.canvasId); // canvas DOM this.ctx = this.canvas.getContext("2d"); // canvas环境 this.victor = 0; // 胜利方 this.canContinue = true; // 是否可以继续下棋(产生赢家以后不可以) this.myPositions = []; // 我方的推荐位置数组 格式:{x:5,y:6,weight:8} this.enemyPositions = []; // 敌方的推荐位置数组 this.init(); } // 初始化数据 init() { this.initCanvas(); this.initPiece(); this.renderUi(); } // 暂时给棋盘中间加一个黑棋 first(){ var center = Math.floor((this.rowCount+1)/2); this.pieces[center][center] = this.active; this.hisStatus[1] = this.deepClone(this.pieces); this.exchange(); } // 设置棋盘的宽高 initCanvas() { this.canvas.width = this.width; this.canvas.height = this.height; } // 初始化棋子 initPiece() { var initPieces = []; for(let i = 0; i <= this.rowCount; i++) { // 行 initPieces.push([]); for(let j = 0; j <= this.colCount; j++) { // 列 if(i==0 || j==0 || i==this.rowCount || j==this.rowCount){ initPieces[i].push(-1); // 不可以走的位置-1 空0 白1 黑2 }else{ initPieces[i].push(0); // 空0 白1 黑2 } } } this.pieces = this.deepClone(initPieces); this.hisStatus[0] = this.deepClone(initPieces); //this.first(); } // 获取某个棋子的颜色 0——空 1——白 2——黑 getColor (num){ var res = ""; switch(num){ case 0: res = ""; break; case 2: res = "black"; break; case 1: res = "white"; break; case -1: res = "yellow"; break; default: res="red"; // 错误了 } return res; } // 画棋盘 drawChessBoard() { // 背景 this.ctx.beginPath(); this.ctx.rect(0, 0, this.width, this.height); this.ctx.closePath(); this.ctx.fillStyle = "#0099CC"; this.ctx.fill(); // 画横线 this.ctx.beginPath(); for(let i = 0; i < this.rowCount; i++) { this.ctx.moveTo(0, this.cellWidth * i); this.ctx.lineTo(this.width, this.cellWidth * i); } this.ctx.strokeStyle = "#000000"; this.ctx.stroke(); // 画纵线 this.ctx.beginPath(); for(let i = 0; i < this.colCount; i++) { this.ctx.moveTo( this.cellWidth * i, 0); this.ctx.lineTo( this.cellWidth * i, this.height); } this.ctx.strokeStyle = "#000000"; this.ctx.stroke(); } // 画一个棋子 drawDot(x, y, r, color) { this.ctx.beginPath(); this.ctx.arc(x, y, r, 0, 2 * Math.PI); this.ctx.closePath(); this.ctx.fillStyle = color; this.ctx.fill(); } // 画所有的棋子 drawPieces() { //console.log(this.pieces) for(var i = 0; i < this.pieces.length; i++) { // 边界线不让走棋 for(let j=0; j < this.pieces[i].length;j++){ // if(this.pieces[i][j] == 1 || this.pieces[i][j] == 2){ if(this.pieces[i][j] !== 0){ var x = i * this.cellWidth; var y = j * this.cellWidth; // var y = j * this.cellWidth - this.cellWidth/2; this.drawDot(x,y,this.R,this.getColor(this.pieces[i][j])); } } } } // 高亮最近走的棋子 highLight(x,y,oneSide){ var anotherSide = oneSide == 1 ? 2 :1; this.ctx.beginPath(); this.ctx.moveTo(x * this.cellWidth - 10, y * this.cellWidth); this.ctx.lineTo(x * this.cellWidth + 10, y * this.cellWidth); this.ctx.moveTo(x * this.cellWidth, y * this.cellWidth - 10); this.ctx.lineTo(x * this.cellWidth, y * this.cellWidth + 10); this.ctx.closePath(); this.ctx.strokeStyle = this.getColor(oneSide); this.ctx.stroke(); } // drawOnePiece(i,j){ var x = i * this.cellWidth; var y = j * this.cellWidth; this.drawDot(x,y,this.R,this.getColor(this.active)); } // 判断是否可以走这一步 canGo(x, y) { if(this.canContinue === false){ alert("游戏已结束"); return false; } if(x==0 || y==0 || x==this.rowCount || y==this.rowCount){ alert("边界上不可以走棋哦"); return false; } if(this.pieces[x][y]==0){ return true; }else { return false; } } // 切换角色(换着走棋) exchange(){ this.active = this.active == 1 ? 2 : 1; } // 走一步棋 goStep(x,y) { // 判断这一步是否可以走 if(this.canGo(x,y)){ console.log(this.active, user.type.slice(-1)) if(this.active == user.type.slice(-1)){ this.pieces[x][y] = this.active;//添加棋子 this.exchange(); sendChessBoard(this.active,this.pieces,x,y); // this.renderUi(); // this.hisStatus.push(this.deepClone(this.pieces));//历史记录(悔棋复盘功能使用) // this.getVictor();//这里有个坑,棋子还没有画上去,已经把输赢判断了???? // this.exchange(); }else{ alert("轮到对方走棋了"); } /*if(this.active == 1 && !this.victor){ // 白棋,机器人 this.helpRecommend(this.active); console.log(this.myPositions,this.enemyPositions) var p = this.bestPosition(); console.log(p); this.pieces[p.x][p.y] = this.active; this.drawOnePiece(p.x,p.y); this.hisStatus.push(this.deepClone(this.pieces));//历史记录(悔棋复盘功能使用) this.getVictor(); this.exchange(); // this.renderUi(); }*/ }else { // alert("这个位置已经被占领了,换个位置走吧"); } } // 悔棋 regret() { if(this.hisStatus.length<=1){ console.log("当前不可以悔棋了"); return; } this.hisStatus.pop(); this.hisStatus.pop(); this.pieces = this.deepClone(this.hisStatus[this.hisStatus.length-1]); // this.exchange(); this.renderUi(); } // helpRecommend(oneSide){ var enemySide = oneSide == 1 ? 2 : 1; this.myPositions = []; this.enemyPositions = []; for(let i=1;i<this.rowCount;i++){ for(let j=1;j<this.rowCount;j++){ // var arr = ["r","rd","d","ld","l","lt","t","rt"]; var arr = ["r","rd","d","ld"]; // 权重相关变量 forward backward center double null var n2f1 = 0.2, // 两头空时 前面第一个空位置的权重 n2f2 = 0.1, // 两头空时 前面第二个空位置的权重 n2b1 = n2f1, n2b2 = n2f2, n1f1 = -0.1, // 一头空另一头是敌方或边界时 前面第一个空位置的权重 n1f2 = -0.2, n1b1 = n1f1, n1b2 = n1f2, dn2c = 0.2, // 有两个片段时 两端都是空的时 中间位置的权重 dn2b1 = 0.1,// 有两个片段时 两端都是空的时 后方第一个位置的权重 dn2f1 = dn2b1, dn1c = -0.1, dn1b1 = -0.1, dn1f1 = dn1b1; if(this.pieces[i][j]==oneSide){ // 我方 for(var d =0;d<arr.length;d++){ var count = 0; count = this.directionCount(arr[d],oneSide,i,j); var nd = this.nd(arr[d]); var h = nd.h; var v = nd.v; // 某个方向的末端的推荐权重 (权重暂时认为后方第一个位置和第二位位置一样) 两头空的+0.2 一端空的+0 两端都死的考虑能否凑5个子 if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === 0){ // 前1空 if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === 0){ // 末1空 if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === 0){ //末2空 this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+n2b1)); this.sumWeight(this.myPositions,i+(count+1)*h,j+(count+1)*v,10**(count+n2b2)); }else if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === oneSide){ // 末2己 let count2 = this.directionCount(arr[d],oneSide,i+(count+1)*h,j+(count+1)*v); if(this.pieces[i+(count+1+count2)*h] && this.pieces[i+(count+1+count2)*h][j+(count+1+count2)*v] === 0){ this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+count2+dn2c)); this.sumWeight(this.myPositions,i+(count+1+count2)*h,j+(count+1+count2)*v,10**(count+count2+dn2b1)); }else { this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+count2+dn1c)); } }else { //末2敌或边界 this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+n1b1)); } }else { // 末1敌或边界 末1不可能是己方的 // 末端没有推荐的位置 } }else if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === oneSide){ // 前1己 这里已经计算过了,跳过逻辑 continue; }else { // 前1 敌方或边界 if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === 0){ // 末1空 if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === 0){ //末2空 this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+n1b1)); this.sumWeight(this.myPositions,i+(count+1)*h,j+(count+1)*v,10**(count+n1b2)); }else if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === oneSide){ // 末2己 // this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+0.1)); let count2 = this.directionCount(arr[d],oneSide,i+(count+1)*h,j+(count+1)*v); if(this.pieces[i+(count+1+count2)*h] && this.pieces[i+(count+1+count2)*h][j+(count+1+count2)*v] === 0){ this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+count2+dn1c)); this.sumWeight(this.myPositions,i+(count+1+count2)*h,j+(count+1+count2)*v,10**(count+count2+dn1b1)); }else {// 两端是死的 中间要么是5要么就没意义 if(count+1+count2 == 5){ this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+count2+1)); }else{ this.sumWeight(this.myPositions,i+count*h,j+count*v,0); //console.log("中间凑不够5个,中间权重是0"); } } }else { //末2敌或边界 if(count==4){ // 只有四颗子的时候这个位置才有意义 this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+1)); } } }else { // 末1敌或边界 末1不可能是己方的 // 走不了了 } } // 某个方向的前端的推荐权重 if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === 0){ // 后1空 if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === 0){ // 前1空 if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === 0){ //前2空 this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+n2f1)); this.sumWeight(this.myPositions,i-2*h,j-2*v,10**(count+n2f2)); }else if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === oneSide){ // 前2己 // this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+0.3)); let count2 = this.directionCount(this.reverseDirection(arr[d]),oneSide,i-2*h,j-2*v); if(this.pieces[i-(1+count2)*h] && this.pieces[i-(1+count2)*h][j-(1+count2)*v] === 0){ this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+count2+dn2c)); this.sumWeight(this.myPositions,i-(1+count2)*h,j-(1+count2)*v,10**(count+count2+dn2f1)); }else { this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+count2+dn1c)); this.sumWeight(this.myPositions,i-(1+count2)*h,j-(1+count2)*v,10**(count+count2+dn1f1)); } }else { //前2敌或边界 this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+n1f1)); } }else { // 前1敌或边界 前1不可能是己方的 // 前端没有推荐的位置 } }else if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === oneSide){ // 后1己 这里已经计算过了,跳过逻辑 continue; }else { // 后1 敌方或边界 if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === 0){ // 前1空 if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === 0){ //前2空 this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+n1f1)); this.sumWeight(this.myPositions,i-2*h,j-2*v,10**(count+n1f2)); }else if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === oneSide){ // 前2己 // this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+0.1)); let count2 = this.directionCount(this.reverseDirection(arr[d]),oneSide,i-2*h,j-2*v); if(this.pieces[i-(1+count2)*h] && this.pieces[i-(1+count2)*h][j-(1+count2)*v] === 0){ this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+count2+dn1c)); this.sumWeight(this.myPositions,i-(1+count2)*h,j-(1+count2)*v,10**(count+count2+dn1f1)); }else { if(count+1+count2 == 5){ this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+count2+1)); }else{ this.sumWeight(this.myPositions,i-1*h,j-1*v,0); //console.log("中间凑不够5个,中间权重是0"); } } }else { //前2敌或边界 if(count==4){ // 只有四颗子的时候这个位置才有意义 this.sumWeight(this.myPositions,i-2*h,j-2*v,10**(count+1)); } } }else { // 前1敌或边界 前1不可能是己方的 // 前后都是敌,推荐个锤子 } } } }else if(this.pieces[i][j]==enemySide){ // 敌方 for(var d =0;d<arr.length;d++){ var count = 0; count = this.directionCount(arr[d],enemySide,i,j); var nd = this.nd(arr[d]); var h = nd.h; var v = nd.v; // 某个方向的末端的推荐权重 if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === 0){ // 前1空 if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === 0){ // 末1空 if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === 0){ //末2空 this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+n2b1)); this.sumWeight(this.enemyPositions,i+(count+1)*h,j+(count+1)*v,10**(count+n2b2)); }else if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === enemySide){ // 末2己 let count2 = this.directionCount(arr[d],enemySide,i+(count+1)*h,j+(count+1)*v); if(this.pieces[i+(count+1+count2)*h] && this.pieces[i+(count+1+count2)*h][j+(count+1+count2)*v] === 0){ this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+count2+dn2c)); this.sumWeight(this.enemyPositions,i+(count+1+count2)*h,j+(count+1+count2)*v,10**(count+count2+dn2b1)); }else { this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+count2+dn1c)); } // this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+0.3)); }else { //末2敌或边界 this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+n1b1)); } }else { // 末1敌或边界 末1不可能是己方的 // 末端没有推荐的位置 } }else if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === enemySide){ // 前1己 这里已经计算过了,跳过逻辑 continue; }else { // 前1 敌方或边界 if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === 0){ // 末1空 if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === 0){ //末2空 this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+n1b1)); this.sumWeight(this.enemyPositions,i+(count+1)*h,j+(count+1)*v,10**(count+n1b2)); }else if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === enemySide){ // 末2己 //this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+0.1)); let count2 = this.directionCount(arr[d],enemySide,i+(count+1)*h,j+(count+1)*v); if(this.pieces[i+(count+1+count2)*h] && this.pieces[i+(count+1+count2)*h][j+(count+1+count2)*v] === 0){ this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+count2+dn1c)); this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+count2+dn1f1)); }else {// 两端是死的,看中间够5个不 if(count+1+count2 == 5){ this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+count2+1)); }else{ this.sumWeight(this.enemyPositions,i+count*h,j+count*v,0); //console.log("中间凑不够5个,中间权重是0"); } } }else { //末2敌或边界 if(count==4){ // 只有四颗子的时候这个位置才有意义 this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+1)); } } }else { // 末1敌或边界 末1不可能是己方的 // 走不了了 } } // 某个方向的前端的推荐权重 if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === 0){ // 后1空 if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === 0){ // 前1空 if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === 0){ //前2空 this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+n2f1)); this.sumWeight(this.enemyPositions,i-2*h,j-2*v,10**(count+n2f2)); }else if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === enemySide){ // 前2己 // this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+0.3)); let count2 = this.directionCount(this.reverseDirection(arr[d]),enemySide,i-2*h,j-2*v); if(this.pieces[i-(1+count2)*h] && this.pieces[i-(1+count2)*h][j-(1+count2)*v] === 0){ this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+count2+dn2c)); this.sumWeight(this.enemyPositions,i-(1+count2)*h,j-(1+count2)*v,10**(count+count2+dn2f1)); }else { this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+count2+dn1c)); } }else { //前2敌或边界 this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+dn1c)); } }else { // 前1敌或边界 前1不可能是己方的 // 前端没有推荐的位置 } }else if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === enemySide){ // 后1己 这里已经计算过了,跳过逻辑 continue; }else { // 后1 敌方或边界 if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === 0){ // 前1空 if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === 0){ //前2空 this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+n1f1)); this.sumWeight(this.enemyPositions,i-2*h,j-2*v,10**(count+n1f2)); }else if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === enemySide){ // 前2己 // this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+0.1)); let count2 = this.directionCount(this.reverseDirection(arr[d]),enemySide,i-2*h,j-2*v); if(this.pieces[i-(1+count2)*h] && this.pieces[i-(1+count2)*h][j-(1+count2)*v] === 0){ this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+count2+dn1c)); this.sumWeight(this.enemyPositions,i-(1+count2)*h,j-(1+count2)*v,10**(count+count2+dn1f1)); }else { // 前后是死的 if(count+1+count2 == 5){ this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+count2+1)); }else{ this.sumWeight(this.enemyPositions,i-1*h,j-1*v,0); //console.log("中间凑不够5个,中间权重是0"); } } }else { //前2敌或边界 if(count==4){ // 只有四颗子的时候这个位置才有意义 this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+1)); } } }else { // 前1敌或边界 前1不可能是己方的 // 前后都是敌,推荐个锤子 } } } } } } } reverseDirection(direction){ var rd = ""; switch(direction){ case "r": rd = "l";break; case "rd": rd = "lt";break; case "d": rd = "t";break; case "ld": rd = "rt";break; case "l": rd = "r";break; case "lt": rd = "rd";break; case "t": rd = "d";break; case "rt": rd = "ld";break; default: console.error("输入方向不对,无法反转"); } return rd; } // 方向数字化numberDirection r(1,0) rd(1,1) ld(-1,1) nd(direction){ var res = {h:0,v:0}; // h horizontal v vertical switch(direction){ case "r": res.h = 1; res.v = 0; break; case "rd": res.h = 1; res.v = 1; break; case "d": res.h = 0; res.v = 1; break; case "ld": res.h = -1; res.v = 1; break; case "l": res.h = -1; res.v = 0; break; case "lt": res.h = -1; res.v = -1; break; case "t": res.h = 0; res.v = -1; break; case "rt": res.h = 1; res.v = -1; break; default: console.error("方向输入有误"); } return res; } // 合并同一个位置的权重 sumWeight(arr,x,y,weight){ var index = -1; for(let i=0,len=arr.length;i<len;i++){ if(arr[i].x==x && arr[i].y==y){ index = i; break; } } if(index!=-1){ // 如果已存在则权重相加 arr[index].weight += weight; }else{ // 如果不存在则添加一条 arr.push({x,y,weight}); } } // 从推荐位置中找出最佳位置 权重最大的位置 bestPosition (){ var myMax=0,myP={},myArr=[]; for(let i=0,len=this.myPositions.length;i<len;i++){ if(this.myPositions[i].weight>myMax){ myMax = this.myPositions[i].weight; myP.x = this.myPositions[i].x; myP.y = this.myPositions[i].y; } } var enemyMax = 0, enemyP = {}, enemyArr = []; for(let i=0,len=this.enemyPositions.length;i<len;i++){ if(this.enemyPositions[i].weight>enemyMax){ enemyMax = this.enemyPositions[i].weight; enemyP.x = this.enemyPositions[i].x; enemyP.y = this.enemyPositions[i].y; } } // console.log(this.myPositions,this.ememyPositions); // console.log("敌方权重最大:"+enemyMax,"我方权重最大:"+myMax); for(let i=0,len=this.myPositions.length;i<len;i++){ if(this.myPositions[i]==myMax){ myArr.push(this.deepClone(this.myPositions[i])); } } for(let i=0,len=this.enemyPositions.length;i<len;i++){ if(this.enemyPositions[i]==enemyMax){ enemyArr.push(this.deepClone(this.enemyPositions[i])); } } if(enemyMax>myMax){ // 敌方权重最大的地方(有相同位置时,谋求自己的大权重位置) var myMaxW = 0; // 我方在敌方最有位置处的最佳权重 var recommedP = enemyP; for(let i=0, len=enemyArr.length;i<len;i++){ for(let j=0,len1=this.myPositions.length;j<len1;j++){ if(this.myPositions[j].x==enemyArr[i].x && this.myPositions[j].y==enemyArr[i].y){ if(this.myPositions[j].weight>myMaxW){ myMaxW = this.myPositions[j].weight; recommedP.x = this.myPositions[j].x; recommedP.y = this.myPositions[j].y; } } } } return recommedP; }else { // 我方权重最大的地方(有相同位置时,占据敌方的相对大权重位置) var enemyMaxW = 0; // 我方在敌方最有位置处的最佳权重 var recommedP = myP; for(let i=0, len=myArr.length;i<len;i++){ for(let j=0,len1=this.enemyPositions.length;j<len1;j++){ if(this.enemyPositions[j].x==myArr[i].x && this.enemyPositions[j].y==myArr[i].y){ if(this.enemyPositions[j].weight>enemyMaxW){ enemyMaxW = this.enemyPositions[j].weight; recommedP.x = this.enemyPositions[j].x; recommedP.y = this.enemyPositions[j].y; } } } } return recommedP; } } // 获取赢家 getWinner(){ switch(this.victor){ case 0: console.log("还没产生赢家");break; case 1: this.canContinue = false; setTimeout(()=>{alert("白棋赢");},30); break; case 2: this.canContinue = false; setTimeout(()=>{alert("黑棋赢");},30); break; default:; } } // 判断输赢 getVictor() { //1.找到一个当前棋子,2.判断它的右、右下、下、左下四个方向是否连成5个棋子 var arr = ["r","rd","d","ld"]; for(var i=1;i<this.pieces.length;i++){ for(var j=1;j<this.pieces[i].length;j++){ if(this.pieces[i][j] == 1){ for(let k = 0;k<arr.length;k++){ if(this.directionCount(arr[k],1,i,j) == 5){// 右r 下d 左l 上t this.victor = 1; this.getWinner(); return; } } }else{ for(let k = 0;k<arr.length;k++){ if(this.directionCount(arr[k],2,i,j) == 5){// 右r 下d 左l 上t this.victor = 2; this.getWinner(); return; } } } } } } // 此函数替代了原来的 directionCount directionCount(direction,oneSide,i,j){ var count = 0; var nd = this.nd(direction); if(this.pieces[i][j] == oneSide){ count = 1; for(let k=1;k<5;k++){ if(this.pieces[i+k*nd.h] && this.pieces[i+k*nd.h][j+k*nd.v] === oneSide){ count++; continue; }else { break; } } } return count; } // 深拷贝 deepClone(values) { var copy; // Handle the 3 simple types, and null or undefined if(null == values || "object" != typeof values) return values; // Handle Date if(values instanceof Date) { copy = new Date(); copy.setTime(values.getTime()); return copy; } // Handle Array if(values instanceof Array) { copy = []; for(var i = 0, len = values.length; i < len; i++) { copy[i] = this.deepClone(values[i]); } return copy; } // Handle Object if(values instanceof Object) { copy = {}; for(var attr in values) { if(values.hasOwnProperty(attr)) copy[attr] = this.deepClone(values[attr]); } return copy; } throw new Error("Unable to copy values! Its type isn\'t supported."); } } </script> <script type="text/javascript"> gobang = new Gobang("gobang"); var canvas = document.getElementById("gobang"); console.log(canvas.getBoundingClientRect()); var offset; canvas.addEventListener("click",function(e){ offset = canvas.getBoundingClientRect(); var x = Math.round((e.clientX - offset.left) / gobang.cellWidth); var y = Math.round((e.clientY - offset.top) / gobang.cellWidth); // console.log(x,y,"点击位置"); // 走棋 gobang.goStep(x,y); },false); document.getElementById("regret").addEventListener("click",()=>{ gobang.regret(); },false); document.getElementById("goHome").addEventListener("click",()=>{ // 关闭当前窗口 window.opener=null; window.open(\'\',\'_self\'); window.close(); }); document.getElementById("chessAgain").addEventListener("click",()=>{ if(gobang.victor){ gobang.resetData(); }else{ alert("当前对弈结束后才可以重新开始~"); } }); document.getElementById("getConnection").addEventListener("click",()=>{ getConnection(); }); </script> </html>
goBangServer.js // 对弈的服务
var ws = require("nodejs-websocket"); var users = []; var onlineCount = 0; var chessBoard = []; var next = 2;// 下一步走棋的人 var lastStep = {}; // 上一步 var IP = "10.100.106.114"; var port = 9000; var server = ws.createServer(function(connection){ var user; console.log("有新用户接入"); onlineCount = server.connections.length; connection.on("text",function(str){ console.log("接收到消息",str); var data = JSON.parse(str); if(data.type == "chess"){ // 走一步棋子 chessBoard = data.data; next = data.next; lastStep = data.lastStep; broadCastChessBoard(data.roomId); }else if(data.type == "regret"){ // 悔棋 }else if(data.type == "giveUp"){ // 认输 }else if(data.type == "again"){ // 再来一局 if(data.data.eventOrigin == "player1"){ var msg = {}; msg.type = "again"; server.connections.forEach(function(conn,index) { connection.sendText(JSON.stringify(msg)); }) }else if(data.data.eventOrigin == "player2"){ var msg = {}; msg.type = "again"; server.connections.forEach(function(conn,index) { connection.sendText(JSON.stringify(msg)); }) }else{ // continue; } }else if(data.type == "user"){ // 新用户接入 user = data.data; user.sessionId = connection.headers[\'sec-websocket-key\']; if(users.containUserName(user.name)){ var index = users.userIndex(user.name); users[index] = user; }else{ users.push(user); } //broadCastChessBoard(); }else{ // 未知类型 server.connections.forEach(function(conn,index) { // console.log(connection.headers[\'sec-websocket-key\']); // console.log(server.connections[index].headers[\'sec-websocket-key\']) // connection.sendText(JSON.stringify(server.connections[index])); }) } }); connection.on("error",function(err){ console.log(\'遇到错误,详情:\'); console.log(err); }); connection.on("close",function(code,reason){ console.log("code closed", code); console.log("reason closed", reason); users.removeUser(user); }); }).listen(port); Array.prototype.removeUser = function(user){ for(let i=0;i<this.length;i++){ //console.log(JSON.stringify(this[i]), JSON.stringify(user)); if(JSON.stringify(this[i]) == JSON.stringify(user)){ this.splice(i,1); } } } Array.prototype.containUserName = function(str) { for (var i = 0; i < this.length; i++) { if (this[i].name == str) { return true; } } return false; } Array.prototype.userIndex = function(str) { var index = -1; for (var i = 0; i < this.length; i++) { if (this[i].name == str) { index = i; } } return index; } function broadCastChessBoard(roomId){ console.log("在线人数:"+server.connections.length); var msg = {}; msg.type = "chessBoard"; msg.data = chessBoard; msg.next = next; msg.lastStep = lastStep; console.log(users) users.forEach((user,index)=>{ if(user.roomId == roomId){ //sessionId server.connections.forEach(function(connection,index) { if(server.connections[index].headers[\'sec-websocket-key\'] == user.sessionId){ connection.sendText(JSON.stringify(msg)); } }) } }) } //console.log(server); console.log("服务已搭建好,静候佳音;链接地址:ws://"+IP+":"+port);
index.html // 选房间的页面
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> *{ padding: 0; margin: 0; } .room{ width: 240px; float: left; height: 100px; line-height: 100px; border: 1px solid #e73480; margin: 10px 10px; display: flex; } .left,.right{ flex: 1; width:80px; text-align: center; height: 100%; } .center{ flex: 1; color: #006699; width: 80px; height: 100%; text-align: center; } </style> </head> <body> <div class="wrapper"> </div> <!--<div class="room" data-room="1"> <span class="left">玩家1</span> <span class="center">桌子</span> <span class="right">玩家2</span> </div>--> </body> <script type="text/javascript"> var user = {name:"",roomId: -1,type: "hangOut",chessing:false}; // 玩家当前角色——玩家(player)/观众(viewer)/游客(hangOut) if(sessionStorage.getItem("user")){ user = JSON.parse(sessionStorage.getItem("user")); } while(!user.name){ user.name = window.prompt("请给你起一个响亮的名字","玩家姓名"); sessionStorage.setItem("user",JSON.stringify(user)); } var rooms = []; // 每个房间有房间号id,玩家1{name:赵德升},玩家2,观众[{name:solid}],棋盘chessBoard var roomCount = 0; var users = []; var left, right, center; var wrapper = getClass("wrapper")[0]; function initUi(){ var htmlStr = ""; for(let i=0;i<roomCount;i++){ htmlStr += \'<div class="room" data-room=\'+i+\'>\'+ \'<span class="left">\'+"玩家1"+\'</span>\'+ \'<span class="center">桌子\'+i+\'</span>\'+ \'<span class="right">玩家2</span>\'+ \'</div>\'; } wrapper.innerHTML = htmlStr; for(let i=0;i<roomCount;i++){ rooms[i] = {}; rooms[i].id = i; rooms[i].player1 = {}; rooms[i].player2 = {}; rooms[i].viewers = []; rooms[i].chessBoard = []; } left = getClass("left"); for(let i=0;i<left.length;i++){ left[i].onclick = function(){ if(!rooms[i].player1.name){ clearUsers(); user.roomId = i; user.type = "player1"; rooms[i].player1 = user; this.innerText = user.name; sendUserMsg(); goToChess(i); }else{ alert("此位置已经有人了"); } } } right = getClass("right"); for(let i=0;i<right.length;i++){ right[i].onclick = function(){ if(!rooms[i].player2.name){ clearUsers(); user.roomId = i; user.type = "player2"; rooms[i].player2 = user; this.innerText = user.name; sendUserMsg(); goToChess(i); }else{ alert("此位置已经有人了"); } } } center = getClass("center"); for(let i=0;i<center.length;i++){ center[i].onclick = function(){ if(rooms[i].player1.name && rooms[i].player2.name){ clearUsers(); user.roomId = i; user.type = "viewer"; rooms[i].player1 = user; sendUserMsg(); goToChess(i); }else{ alert("这里人手不够,不可以观战哦~"); } } } } function getClass(className){ return document.getElementsByClassName(className); } function goToChess(roomId){ if(rooms[roomId].player1.name && rooms[roomId].player2.name){ if(user.roomId == roomId && !user.chessing){ user.chessing = true; window.open("GoBang.html?roomId="+roomId+"&player1="+rooms[roomId].player1.name+"&player2="+rooms[roomId].player2.name); } } } function clearUsers(){ for(let i = 0;i<left.length;i++){ left[i].innerText = "玩家1"; } for(let i = 0;i<right.length;i++){ right[i].innerText = "玩家2"; } for(let i=0;i<roomCount;i++){ rooms[i] = {}; rooms[i].id = i; rooms[i].player1 = {}; rooms[i].player2 = {}; rooms[i].viewers = []; rooms[i].chessBoard = []; } } function showUsers(){ clearUsers(); for(let i=0;i<users.length;i++){ console.log(users) if(users[i].roomId>=0){ if(users[i].type == "player1"){//玩家1 rooms[users[i].roomId].player1 = users[i]; left[users[i].roomId].innerText = users[i].name; }else if(users[i].type == "player2"){//玩家2 rooms[users[i].roomId].player2 = users[i]; right[users[i].roomId].innerText = users[i].name; }else{//观众 } goToChess(users[i].roomId); } } } /*left.map((val,index,arr)=>{ val.onclick = function(){ var val = window.prompt("请输入你的姓名"); val.innerText = val; } });*/ var ws = new WebSocket(\'ws://10.100.106.114:8080\'); ws.onopen = function(e){ sendUserMsg(); } ws.onerror = function(e){ console.log("链接服务器失败"); user.chessing = false; } ws.onclose = function(e){ console.log("链接断开"); user.chessing = false; } ws.onmessage = function(e){ console.log(e.data); var data = JSON.parse(e.data); switch(data.type){ case "baseMsg": roomCount = data.data.roomCount; initUi(); break; case "usersMsg": users = data.data; showUsers(); break; case "users": break; default:; } } function sendUserMsg(){ var msg = {}; msg.type = "user"; msg.data = user; ws.send(JSON.stringify(msg)); } function recieveMsg(d){ console.log(d); } </script> </html>
server.js // 创建房间的服务
var ws = require("nodejs-websocket"); //console.log(ws); var users = []; var onlineCount = 0; var roomCount = 30; var ip = "10.100.106.114"; var port = 9009; var server = ws.createServer(function(connection){ var user; console.log("有新用户接入"); onlineCount++; connection.on("text",function(str){ console.log("接收到消息",str); //connection.sendText("把你的消息再还给你——"+str); var data = JSON.parse(str); if(data.type == "user"){ user = data.data; // server.connections.user = user; if(users.containUserName(user.name)){ var index = users.userIndex(user.name); users[index] = user; }else{ users.push(user); } //broadCastUsers(connection,JSON.stringify(users)); broadCastUsers(); } }); connection.on("error",function(err){ console.log(\'handle err\'); console.log(err); }); connection.on("close",function(code,reason){ console.log("code closed", code); console.log("reason closed", reason); users.removeUser(user); }); var msg = {}; msg.type = "baseMsg"; msg.data = {"roomCount":roomCount}; connection.send(JSON.stringify(msg)); //connection.send("服务器已和你建立链接"); }).listen(port); function broadCast(connection,roomId,str){ for(let i=0;i<connection.lenth;i++){ if(connection[i].user.roomId == roomId){ connection[i].senText("广播消息——"+str); } } } Array.prototype.removeUser = function(user){ for(let i=0;i<this.length;i++){ //console.log(JSON.stringify(this[i]), JSON.stringify(user)); if(JSON.stringify(this[i]) == JSON.stringify(user)){ this.splice(i,1); } } } Array.prototype.containUserName = function(str) { for (var i = 0; i < this.length; i++) { if (this[i].name == str) { return true; } } return false; } Array.prototype.userIndex = function(str) { var index = -1; for (var i = 0; i < this.length; i++) { if (this[i].name == str) { index = i; } } return index; } function broadCastUsers(){ console.log(server.connections.length); var msg = {}; msg.type = "usersMsg"; msg.data = users; server.connections.forEach(function(connection,index) { //console.log(index); connection.sendText(JSON.stringify(msg)); }) } //console.log(server); console.log("服务已搭建好,静候佳音;链接地址:ws://"+ip+":"+port);
安装依赖nodejs-websocket,启动那两个服务js,把代码放在服务器上或者用HBuilder打开就可以用了
版权声明:本文为zhaodesheng原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。