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打开就可以用了

websocket实现五子棋联机对战的更多相关文章

  1. js实现五子棋人机对战源码

    indexhtml <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  2. websocket简单实现五子棋即时对战功能

    几年前做的一个小demo,代码比较老,先上下html显示效果图 因为代码中注释比较详细,所以就直接上代码了 html代码,也就是上图展示的效果页面 <!DOCTYPE html> < ...

  3. 完全自制的五子棋人机对战游戏(VC++实现)

    五子棋工作文档 1说明: 这个程序在创建初期的时候是有一个写的比较乱的文档的,但是很可惜回学校的时候没有带回来……所以现在赶紧整理一下,不然再过一段时间就忘干净了. 最初这个程序是受老同学所托做的,一 ...

  4. Pyhton实践项目之(一)五子棋人机对战

    1 """五子棋之人机对战""" 2 3 import random 4 import sys 5 6 import pygame 7 im ...

  5. java课程设计(个人)--五子棋

    1.团队课程设计博客链接 http://www.cnblogs.com/mz201521044152/p/7065575.html 2.个人负责模块说明 棋盘类,绘制棋盘,绘制棋子,按钮设置,鼠标监听 ...

  6. 小程序版好友对战实战-wss部署与小程序用户登录时序

    上一篇文章是对需求的分析,本次将逐渐进入代码阶段.本次主要的内容包括服务端wss的部署以及小程序端用户授权的时序及逻辑. wss的配置与部署 微信小程序出于安全考虑,要求所有涉及到网络的操作,必须使用 ...

  7. Android蓝牙联机Demo解析

    写在前面: 手游的双人对战实现方式有很多,比如: 联网对战(需要一个服务器负责转发客户端请求,各种大型手游的做法) 分屏对战(手机上下分屏,典型的例子就是切水果的双人对战) 蓝牙联机对战(通过蓝牙联机 ...

  8. 借鉴炉石传说的战棋游戏《DarkWar》

    <炉石传说>是现在很火的休闲对战游戏,本人也非常喜欢玩,玩的时候经常想能不能把炉石的这些元素融入到战棋类游戏中,于是思索良久,又恰逢游戏蛮牛开展第三届蛮牛杯游戏开发大赛,于是用Unity3 ...

  9. 实践周java基础软件开发app之五子棋

    五子棋人机对战实践项目 总的任务和目标 完成一个人机对战的五子棋项目,基本效果如下: 第一部分 Java绘图原理 1.   基本概念 像素,坐标 第二部分 绘制棋盘 1.   基本思路 在一个JPan ...

随机推荐

  1. applyMiddleware源码中的闭包

    闭包都是个老掉牙的话题了,这次又提起,是因为重看Redux源码时发现了applyMiddleware里的用法很巧妙.我们先看一个简单的例子. var a = (num) => num + 1 v ...

  2. Redhat/Fedora 或类似系统, 配置网络的工具介绍

    在Redhat早期的版本中, 有linuxconf .redhat-config-network .netconfig 等工具: 在Redhat/Fedora 最新的版本有 system-config ...

  3. cf round480D Perfect Groups

    题意:给一个序列,对于每一个连续的区间,区间内的数至少分成几个组,使得每个组内的数任意2个相乘是一个完全平方数(包括0). 输出每个组数的个数. $n \leq 5000 , |a_i| \leq 1 ...

  4. Vue--使用watch、computed、filter方法来监控

    watch与computed.filter: watch:监控已有属性,一旦属性发生了改变就去自动调用对应的方法 computed:监控已有的属性,一旦属性的依赖发生了改变,就去自动调用对应的方法 f ...

  5. web前端学习(三)css学习笔记部分(4)-- CSS选择器详解

    4.  元素选择器详解 4.1  元素选择器 4.2  选择器分组 用英文逗号","相连,使用相同的样式表 使用通配符对所有元素进行通用设定. 4.3  类选择器详解 4.3.1. ...

  6. loadrunner录制脚本时登录密码转md5

    在录制用户注册登录脚本时,常常会遇到web程序对用户密码进行加密处理.在很多时候采用的加密方式为MD5. 这时有两种处理方式: 一.所有用户采用同一密码 例如:每个用户名的密码都为e10adc3949 ...

  7. 双系统删除ubuntu

    我的电脑安装了双系统,Windows和Linux,不过由于Linux在最近一段时间内不会使用,所以我打算删除Linux.    删除Linux最需要注意的地方,就是MBR(Master Boot Re ...

  8. SQL Server2008 卸载

    先把SQL Server卸载,再把安装时产生的“Microsoft SQL Server”文件夹删掉,在运行注册表,把HKEY_CURRENT_USER\Software\Microsoft\Micr ...

  9. 遇到的bug

    1  div出现莫名其妙的空白bug 之前写了一个后台管理系统,项目不小加上是改版,很多的js都是用的之前的,  bug多到自己都不想看, 其中有个是用iframe 框架加载表格页面,但是右边跟下边出 ...

  10. golang中特殊的标识符

    你会发现在 Go 代码中的几乎所有东西都有一个名称或标识符.另外,Go 语言也是区分大小写的,这与 C 家族中的其它语言相同.有效的标识符必须以字符(可以使用任何 UTF-8 编码的字符或 _)开头, ...