<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style> body{ font-size: 36px; } *{ padding: 0; margin: 0; } .score { text-align: center; line-height: 50px; height: 50px; } .buttons { text-align: center; } button{ font-size: 36px; line-height: 50px; margin-right: 30px; padding: 0 30px; } div{ text-align: center; } </style> </head> <body> <div> <canvas id="blackWhite"></canvas> </div> <p class="score"> <!--<span>比分</span> --> <span id="score">黑:白 = 2:2</span> </p> <p class="buttons"> <button id="regret">悔棋</button> <button id="giveUp">認輸</button> <button id="negotiate">求和</button> </p> </body> <!--<script src="js/index.js" type="text/javascript" charset="utf-8"></script>--> <script type="text/javascript"> // 黑白棋 又叫反棋 class Reversi { constructor (canvasId){ this.canvasId = canvasId; this.resetData(); } // 重置數據,再來一局 resetData (){ var body = document.documentElement || document.body; var minWidth = Math.min(body.clientWidth, body.clientHeight); // 屬性 this.pieces = []; // 棋子數組 二位數組[[],[]] this.recommend = []; this.rowCount = 8; // 行數 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.HR = this.cellWidth / 4; // 提示格子的半徑 hintRadius this.HColor = "#e73480"; // 提示棋子的顏色 this.hisStatus = []; // 歷史記錄 history status this.noChessCount = 0; // 沒棋走次數 this.active = "black"; // 當前走棋方 this.canvas = document.getElementById(this.canvasId); // canvas DOM this.ctx = this.canvas.getContext("2d"); // canvas環境 this.whiteCount = 2; // 白棋數量 this.blackCount = 2; // 黑棋數量 this.victor = ""; // 勝利方 this.init(); } // 初始化數據 init (){ this.initCanvas(); this.initPiece(); this.recommed(); this.renderUi(); } // 設置棋盤的寬高 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++){ // 列 initPieces[i].push({value:0,color:""}); } } // 初始的時候中間有四個棋子 var center = Math.floor((this.rowCount-1)/2); // initPieces[center][center].value = 1; initPieces[center][center].color = "black"; initPieces[center][center+1].value = 1; initPieces[center][center+1].color = "white"; initPieces[center+1][center].value = 1; initPieces[center+1][center].color = "white"; initPieces[center+1][center+1].value = 1; initPieces[center+1][center+1].color = "black"; // console.log(initPieces); this.pieces = this.deepClone(initPieces); this.hisStatus[0] = this.deepClone(initPieces); } renderUi(){ //清除以前的畫布 this.ctx.clearRect(0,0,this.width,this.height); // 重繪畫布 this.drawMap(); this.drawPieces(); this.recommed(); this.drawHint(); } //畫一個棋子或一個提示圓點 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(); } // 畫棋盤 drawMap(){ // 背景 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.cellWidth*this.rowCount,this.cellWidth*i); } 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.cellWidth*this.colCount); } this.ctx.stroke(); } // 畫全部的棋子 drawPieces(){ for(let i=0;i<this.pieces.length;i++){ for(let j=0;j<this.pieces[i].length;j++){ if(this.pieces[i][j].value){ var x = i * this.cellWidth + this.cellWidth/2; var y = j * this.cellWidth + this.cellWidth/2; this.drawDot(x,y,this.R,this.pieces[i][j].color); } } } } // 畫全部的提示 drawHint(){ for(let i=0;i<this.recommend.length;i++){ var x = this.recommend[i].x * this.cellWidth + this.cellWidth/2; var y = this.recommend[i].y * this.cellWidth + this.cellWidth/2; this.drawDot(x,y,this.HR, this.HColor); } } // 是否能夠走這一步 canGo (xx,yy){// 條件 1.雙方無期可走 2.棋盤下滿了 3.沒有本色的棋子了 var flag = false; for(var i=0;i<this.recommend.length;i++){ if(this.recommend[i].x == xx && this.recommend[i].y == yy){ flag = true; break; } } return flag; } // 吃子 fire (x,y){ for(var i=0;i<this.recommend.length;i++){ if(this.recommend[i].x == x && this.recommend[i].y == y){ // 走推薦位置(吃掉能夠吃的各個方向) // console.log("x:"+this.recommend[i].x+";y:"+this.recommend[i].y,"匹配位置"); var xx =x, yy = y; switch(this.recommend[i].direction){ //上加下減y 左加右減x case "上": // j在下(大) yy在上(小) xx不變 while(++yy < this.recommend[i].j){ this.pieces[xx][yy].color = this.active; } break; case "右":// x在左(小) xx在右(大) yy不變 while(--xx > this.recommend[i].i){ this.pieces[xx][yy].color = this.active; } break; case "下":// j在上(小) yy在下(大) xx不變 while(--yy > this.recommend[i].j){ this.pieces[xx][yy].color = this.active; } break; case "左":// i在左(小) yy在右(大) i不變 while(++xx < this.recommend[i].i){ this.pieces[xx][yy].color = this.active; } break; case "右上": while(--xx > this.recommend[i].i){ this.pieces[xx][++yy].color = this.active; } break; case "右下": while(--xx > this.recommend[i].i){ this.pieces[xx][--yy].color = this.active; } break; case "左下": while(++xx < this.recommend[i].i){ this.pieces[xx][--yy].color = this.active; } break; case "左上": while(++xx < this.recommend[i].i){ this.pieces[xx][++yy].color = this.active; } break; default: console.log("jjle,吃不了子"); } } } } // 顯示能夠走的位置 recommed (){ this.recommend = []; for(var i=0;i<this.rowCount;i++){ for(var j=0;j<this.colCount;j++){ if(this.pieces[i][j].color == this.active){ // 輪到當前方走棋 // console.log("當前棋子顏色:"+active+";x:"+i+";y:"+j); // 判斷8個方向上是否能夠走 // 右 1右測第一個是敵方棋子,2而後排查後面的每個但不越界,3遇到己方或者空結束(己方的不能夠走,空能夠走,敵方的繼續排查) if(this.pieces[i+1] && this.pieces[i+1][j].color && this.pieces[i+1][j].color != this.active){ // 有對方的棋才能夠走 for(var a=2;i+a<this.rowCount;a++){ if(this.pieces[i+a][j].color == ""){ // 空 this.recommend.push({x:i+a,y:j,i:i,j:j,direction:"右"}); // console.log(i+a,j,"右"); break; }else if(this.pieces[i+a][j].color == this.active){ // 己方 break; }else{ // 敵方 continue; } } } // 左 if(this.pieces[i-1] && this.pieces[i-1][j].color && this.pieces[i-1][j].color != this.active){ // 有對方的棋才能夠走 for(var a=2;i-a>=0;a++){ if(this.pieces[i-a][j] && this.pieces[i-a][j].color){ // 有棋子 判斷是不是敵方棋子 if(this.pieces[i-a][j].color != this.active){ continue; }else{ break; } }else{ //沒有棋子 能夠走 this.recommend.push({x:i-a,y:j,i:i,j:j,direction:"左"}); // console.log(i-a,j,"左") break; } } } // 上 if(this.pieces[i][j-1] && this.pieces[i][j-1].color && this.pieces[i][j-1].color != this.active){ // 有對方的棋才能夠走 for(var a=2;j-a>=0;a++){ if(this.pieces[i][j-a] && this.pieces[i][j-a].color){ // 有棋子 判斷是不是敵方棋子 if(this.pieces[i][j-a].color != this.active){ continue; }else{ break; } }else{ //沒有棋子 能夠走 this.recommend.push({x:i,y:j-a,i:i,j:j,direction:"上"}); // console.log(i,j-a,"上") break; } } } // 下 if(this.pieces[i][j+1] && this.pieces[i][j+1].color && this.pieces[i][j+1].color != this.active){ // 有對方的棋才能夠走 for(var a=2;j+a<this.rowCount;a++){ if(this.pieces[i][j+a].color){ // 有棋子 判斷是不是敵方棋子 if(this.pieces[i][j+a].color != this.active){ continue; }else{ break; } }else{ //沒有棋子 能夠走 this.recommend.push({x:i,y:j+a,i:i,j:j,direction:"下"}); // console.log(i,j+a,"下") break; } } } // 右下 if(this.pieces[i+1] && this.pieces[i+1][j+1] && this.pieces[i+1][j+1].color && this.pieces[i+1][j+1].color != this.active){ // 有對方的棋才能夠走 for(var a=2;a+j<this.rowCount && a+i<this.rowCount;a++){ if(this.pieces[i+a][j+a].color){ // 有棋子 判斷是不是敵方棋子 if(this.pieces[i+a][j+a].color != this.active){ continue; }else{ break; } }else{ //沒有棋子 能夠走 this.recommend.push({x:i+a,y:j+a,i:i,j:j,direction:"右下"}); // console.log(i+a,j+a,"右下") break; } } } // 右上 if(this.pieces[i+1] && this.pieces[i+1][j-1] && this.pieces[i+1][j-1].color && this.pieces[i+1][j-1].color != this.active){ // 有對方的棋才能夠走 for(var a=2;i+a<this.rowCount && j-a>=0;a++){ if(this.pieces[i+a][j-a].color){ // 有棋子 判斷是不是敵方棋子 if(this.pieces[i+a][j-a].color != this.active){ continue; }else{ break; } }else{ //沒有棋子 能夠走 this.recommend.push({x:i+a,y:j-a,i:i,j:j,direction:"右上"}); // console.log(i+a,j-a,"右上") break; } } } // 左上 if(this.pieces[i-1] && this.pieces[i-1][j-1] && this.pieces[i-1][j-1].color && this.pieces[i-1][j-1].color != this.active){ // 有對方的棋才能夠走 for(var a=2;i-a>=0 && j-a>=0;a++){ if(this.pieces[i-a][j-a].color){ // 有棋子 判斷是不是敵方棋子 if(this.pieces[i-a][j-a].color != this.active){ continue; }else{ break; } }else{ //沒有棋子 能夠走 this.recommend.push({x:i-a,y:j-a,i:i,j:j,direction:"左上"}); // console.log(i-a,j-a,"左上") break; } } } // 左下 if(this.pieces[i-1] && this.pieces[i-1][j+1] && this.pieces[i-1][j+1].color && this.pieces[i-1][j+1].color != this.active){ // 有對方的棋才能夠走 for(var a=2;i-a>=0 && j+a<this.rowCount;a++){ if(this.pieces[i-a][j+a].color){ // 有棋子 判斷是不是敵方棋子 if(this.pieces[i-a][j+a].color != this.active){ continue; }else{ break; } }else{ //沒有棋子 能夠走 this.recommend.push({x:i-a,y:j+a,i:i,j:j,direction:"左下"}); // console.log(i-a,j+a,"左下") break; } } } }else{ // 沒有棋子或不是當前方 continue; } } } console.log(this.recommend,"推薦位置數組"); if(this.recommend.length==0){ var piecesCount = this.getNum(this.active)+this.getNum(this.active == "black" ? "white" : "black"); if(piecesCount<this.rowCount*this.colCount){ this.noChess++; if(this.noChess<=64-piecesCount){ // 頂多連走 64-棋子數 this.active = this.active == "black" ? "white" : "black"; // console.log(this.active+"沒有棋走,另外一方繼續"); alert(this.active+"沒有棋走,另外一方繼續"); this.renderUi(); }else{ this.noChess=0; this.over(); } }else{ // 下滿了 this.over(); } } } // 遊戲結束 判斷輸贏、再來一局 over (){ switch (this.winner()){ case "white": // console.log("恭喜恭喜","白棋贏"); alert("白棋贏"); break; case "black": // console.log("恭喜恭喜","黑棋贏"); alert("黑棋贏"); break; default: // console.log("恭喜恭喜","和棋"); alert("和棋"); } } // 走一步 1.判斷這一步是否能夠走 2.改變路途的棋子顏色 goStep (x,y){ // this.recommed(); if(this.canGo(x,y)){ this.pieces[x][y].value = 1; this.pieces[x][y].color = this.active; this.fire(x,y); // 這裏有個大坑 // var curPieces = Object.assign([],this.pieces); // this.hisStatus.push(Object.assign([],curPieces)); this.hisStatus.push(this.deepClone(this.pieces)); // console.log(this.hisStatus,"棋局") // 輪流走棋 this.active = this.active == "black" ? "white" : "black"; this.renderUi(); this.whiteCount = this.getNum ("white"); this.blackCount = this.getNum ("black"); }else{ // console.log("別搗亂,好好走"); alert("別搗亂,好好走"); } } // 深拷貝 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."); } // 判斷誰贏winner winner (){ if(this.getNum("white") > this.getNum("black")){ this.victor = "white"; return "white"; } else if(this.getNum("white") < this.getNum("black")){ this.victor = "black"; return "black"; } else { this.victor = "和棋"; return "和棋"; } } // 白棋或黑棋的數量 getNum (color){ var count = 0; for(var i=0,len=this.pieces.length;i<len;i++){ for(var j=0,len1=this.pieces[i].length;j<len1;j++){ if(color == this.pieces[i][j].color){ count++; } } } return count; } // return 贏家 getVictor (){ switch(this.victor){ case "white": alert("白棋勝"); break; case "black": alert("黑棋勝"); break; case "和棋": alert("和棋"); break; default: alert("出bug了"); } } // 認輸 giveUp (){ this.victor = this.active == "black" ? "white" : "black"; this.getVictor(); this.resetData(); } // 求和 negotiate (){ this.victor = "和棋"; this.getVictor(); this.resetData(); } // 悔棋 regret (){ if(this.hisStatus.length>1){ this.hisStatus.pop(); // console.log(this.hisStatus,"刪除一步的棋局"); this.pieces = Object.assign([],this.hisStatus[this.hisStatus.length-1]); //[...this.hisStatus[this.hisStatus.length-1]]; this.active = this.active == "black" ? "white" : "black"; // console.log(this.pieces,"當前棋局"); this.renderUi(); this.whiteCount = this.getNum ("white"); this.blackCount = this.getNum ("black"); }else { alert("沒有棋能夠悔了"); } } } </script> <script type="text/javascript"> var score = document.getElementById("score"); var regret = document.getElementById("regret"); var giveUp = document.getElementById("giveUp"); var negotiate = document.getElementById("negotiate"); var offset; var reversi = new Reversi("blackWhite"); reversi.canvas.addEventListener("click",function(e){ offset = reversi.canvas.getBoundingClientRect(); var x = Math.floor((e.clientX - offset.left) / reversi.cellWidth); var y = Math.floor((e.clientY - offset.top) / reversi.cellWidth); console.log(x,y,"點擊位置"); // 走棋 reversi.goStep(x,y); getScore(); },false); function getScore(){ score.innerHTML = "黑:白 = "+reversi.blackCount + ":"+reversi.whiteCount; } giveUp.onclick = function(){ if(confirm("你肯定認輸嗎?")){ reversi.giveUp(); getScore(); } } negotiate.onclick = function (){ if(confirm("你肯定求和嗎?")){ if(confirm("你贊成和棋嗎?")){ reversi.negotiate(); getScore(); }else{ alert("對方拒絕和棋!"); } } } regret.onclick = function(){ if(confirm("對方請求悔棋,是否贊成?")){ reversi.regret(); getScore(); }else{ alert("對方拒絕悔棋"); } } </script> </html>