websocket實現五子棋聯機對戰

GoBang.html // 對弈的頁面javascript

<!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 // 對弈的服務css

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 // 選房間的頁面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打開就能夠用了java

相關文章
相關標籤/搜索