[お題にTRY] Canvas三目並べ #4 : 勝ち負け判定

2020年6月26日

Javascript テクノロジー プログラミング 特集

eyecatch プログラミングは立ってする事が通常の、ユゲタです。 通常は椅子に座って行うプログラミングですが、人が椅子に座るという事は、逆に体に負荷を与えているという一面もあるので、なるべく立って行えるように作業テーブルの上に、パソコンスタンドをおいて作業してます。 人は、座っているよりも立っている方が、体に負担がかからない自然な姿勢なので、腰痛や膝痛などの軽減にもつながるそうですよ。

本日のIT謎掛け

「パソコン作業」と、かけまして・・・ 「大人の男性の正装」と、ときます。 そのココロは・・・ タイピン(typingとネクタイピン)が欠かせません。

アルゴリズム検討

見た目はほぼ完成した「三目並べ」ですが、ちゃんとしたゲームになっていません。 まずは、三目並べでそれぞれが、コマ(マーク)をおいた時に、その都度コマが3つ並んでいるかどうかの判定を行って、3つ並んでいた方の勝ち(○か×)を判定しなければいけません。 三目並べは最初の2手で、勝ち負けがほぼ決まってしまうらしいのですが、難易度の設定はまあまあ難しくなりそうなので、今回は省きます。 参考 : https://ja.wikipedia.org/wiki/%E4%B8%89%E7%9B%AE%E4%B8%A6%E3%81%B9

ソースコード

(function(){ var LIB = function(){}; LIB.prototype.event = function(target, mode, func , flg){ flg = (flg) ? flg : false; if (target.addEventListener){target.addEventListener(mode, func, flg)} else{target.attachEvent('on' + mode, function(){func.call(target , window.event)})} }; LIB.prototype.construct = function(){ switch(document.readyState){ case "complete" : new MAIN();break; case "interactive" : this.event(window , "DOMContentLoaded" , (function(){new MAIN()}).bind(this));break; default : this.event(window , "load" , (function(){new MAIN()}).bind(this));break; } }; var MAIN = function(){ // 棋譜格納用のバッファ this.buffer = []; this.drawBase(); this.setEvent(); }; MAIN.prototype.drawBase = function(){ var ctx = document.getElementById("mycanvas").getContext("2d"); // 背景 ctx.fillStyle = "#14bdac"; ctx.fillRect(0,0,400,200); // 枠線 ctx.strokeStyle = "#0da192"; ctx.lineWidth = 6; ctx.beginPath(); ctx.moveTo(170,20); ctx.lineTo(170,180); ctx.stroke(); ctx.fill(); ctx.beginPath(); ctx.moveTo(230,20); ctx.lineTo(230,180); ctx.stroke(); ctx.fill(); ctx.beginPath(); ctx.moveTo(120,70); ctx.lineTo(280,70); ctx.stroke(); ctx.fill(); ctx.beginPath(); ctx.moveTo(120,130); ctx.lineTo(280,130); ctx.stroke(); ctx.fill(); }; MAIN.prototype.setEvent = function(){ var ctx = document.getElementById("mycanvas"); new LIB().event(ctx , "click" , (function(e){this.clickCtx(e)}).bind(this)); }; MAIN.prototype.clickCtx = function(e){ var canvas = e.currentTarget; // canvas内のクリックされた座標 var posX = e.clientX - canvas.getBoundingClientRect().left; var posY = e.clientY - canvas.getBoundingClientRect().top; // 座標から枠判定 [1-9] var cell = this.checkCell(posX , posY); if(!cell){return;} // バッファに登録(登録済みのセルの場合は、処理しない) if(this.buffer.indexOf(cell) !== -1){return;} this.buffer.push({ mark : 0, num : cell }); // 記号を表示する this.drawBuffer(); // 判定 if(this.judgement()){return;} // 続けてコンピュータの手 this.turnComputer(); // 判定 if(this.judgement()){return;} // 打つ手が無くなったら終了 if(this.buffer.length >= 9){ this.draw(); this.finish(); } }; MAIN.prototype.checkCell = function(posX , posY){ if(118 <= posX && posX <= 118 + 47 && 18 <= posY && posY <= 18 + 47){ return 1; } if(177 <= posX && posX <= 177 + 47 && 18 <= posY && posY <= 18 + 47){ return 2; } if(236 <= posX && posX <= 236 + 47 && 18 <= posY && posY <= 18 + 47){ return 3; } if(118 <= posX && posX <= 118 + 47 && 76 <= posY && posY <= 76 + 47){ return 4; } if(177 <= posX && posX <= 177 + 47 && 76 <= posY && posY <= 76 + 47){ return 5; } if(236 <= posX && posX <= 236 + 47 && 76 <= posY && posY <= 76 + 47){ return 6; } if(118 <= posX && posX <= 118 + 47 && 134 <= posY && posY <= 134 + 47){ return 7; } if(177 <= posX && posX <= 177 + 47 && 134 <= posY && posY <= 134 + 47){ return 8; } if(236 <= posX && posX <= 236 + 47 && 134 <= posY && posY <= 134 + 47){ return 9; } return null; }; MAIN.prototype.drawBuffer = function(){ var canvas = document.getElementById("mycanvas"); var ctx = canvas.getContext("2d"); // 描画をクリアする ctx.clearRect(0, 0, canvas.width, canvas.height); // 基盤を表示 this.drawBase(); // バッファ全ての座標を指定 for(var i=0; i<this.buffer.length; i++){ var num = this.buffer[i].num; // 奇数番 if(i % 2 === 0){ this.drawMark_0(num); } // 偶数 else{ this.drawMark_1(num); } } }; // player-mark @ [0:○ , 1:×] MAIN.prototype.drawMark_0 = function(cell){ var ctx = document.getElementById("mycanvas").getContext("2d"); var x = y = null , r=16; switch(cell){ case 1: x = 118 + 24; y = 18 + 24; break; case 2: x = 177 + 24; y = 18 + 24; break; case 3: x = 236 + 24; y = 18 + 24; break; case 4: x = 118 + 24; y = 76 + 24; break; case 5: x = 177 + 24; y = 76 + 24; break; case 6: x = 236 + 24; y = 76 + 24; break; case 7: x = 118 + 24; y = 134 + 24; break; case 8: x = 177 + 24; y = 134 + 24; break; case 9: x = 236 + 24; y = 134 + 24; break; } if(x === null && y === null){return;} ctx.strokeStyle = "white"; ctx.beginPath(); ctx.arc(x,y,r, 0/180*Math.PI , 360/180*Math.PI); ctx.stroke(); }; // com-mark @ [0:○ , 1:×] MAIN.prototype.drawMark_1 = function(cell){ var ctx = document.getElementById("mycanvas").getContext("2d"); var x1 = y1 = x2 = y2 = null , size1 = 8 , size2 = 38; switch(cell){ case 1: x1 = 118 + size1; y1 = 18 + size1; x2 = 118 + size2; y2 = 18 + size2; break; case 2: x1 = 177 + size1; y1 = 18 + size1; x2 = 177 + size2; y2 = 18 + size2; break; case 3: x1 = 236 + size1; y1 = 18 + size1; x2 = 236 + size2; y2 = 18 + size2; break; case 4: x1 = 118 + size1; y1 = 76 + size1; x2 = 118 + size2; y2 = 76 + size2; break; case 5: x1 = 177 + size1; y1 = 76 + size1; x2 = 177 + size2; y2 = 76 + size2; break; case 6: x1 = 236 + size1; y1 = 76 + size1; x2 = 236 + size2; y2 = 76 + size2; break; case 7: x1 = 118 + size1; y1 = 134 + size1; x2 = 118 + size2; y2 = 134 + size2; break; case 8: x1 = 177 + size1; y1 = 134 + size1; x2 = 177 + size2; y2 = 134 + size2; break; case 9: x1 = 236 + size1; y1 = 134 + size1; x2 = 236 + size2; y2 = 134 + size2; break; } if(x1 === null && y1 === null && x2 === null && y2 === null){return;} ctx.strokeStyle = "white"; ctx.beginPath(); ctx.moveTo(x1,y1); ctx.lineTo(x2,y2); ctx.stroke(); ctx.fill(); ctx.strokeStyle = "white"; ctx.beginPath(); ctx.moveTo(x1,y2); ctx.lineTo(x2,y1); ctx.stroke(); ctx.fill(); }; MAIN.prototype.turnComputer = function(){ // 打つ手が無くなったら終了 if(this.buffer.length >= 9){return;} // 現在の盤から、空きセルを検索 var emptyCells = this.getEmptyCells(); // 場所を決める var cellNum = this.getComPlace(emptyCells); this.buffer.push({ mark : 1, num : cellNum }); // 記号を表示する this.drawBuffer(); }; MAIN.prototype.getEmptyCells = function(){ var arr = []; for(var i=1; i<=9; i++){ if(this.containNum(i)){continue;} arr.push(i); } return arr; } MAIN.prototype.containNum = function(num){ for(var i in this.buffer){ if(this.buffer[i].num === num){ return this.buffer[i]; } } }; MAIN.prototype.getComPlace = function(emptyCells){ var num = Math.floor(Math.random() * emptyCells.length); return emptyCells[num]; } MAIN.prototype.finish = function(){ console.log("Finish !"); }; // mark @ 対象のコマ(マーク)[0:○ , 1:×] : int // return @ 3つ並んだら並んでいるマーク(0:○ , 1:×):int を返す , それ以外はnull MAIN.prototype.judgement = function(){ if(this.buffer.length < 3){return 0;} // プレイヤーの勝ち if(this.judgement_player(0)){ return this.win(0); } // コンピュータの勝ち else if(this.judgement_player(1)){ return this.win(1); } return 0; } MAIN.prototype.judgement_player = function(mark){ // 横一列判定 for(var i in win_pattern){ // [0-2] var cnt = 0; for(var j in win_pattern[i]){ var res = this.containNum(win_pattern[i][j]) // bufferに含まれていなければ、NG if(!res){break;} // 違うプレイヤーであれば、NG if(res.mark !== mark){break;} cnt += 1; } if(cnt === 3){ return true; } } } // 勝ち配列 var win_pattern = [ [1,2,3], [4,5,6], [7,8,9], [1,4,6], [2,5,8], [3,6,9], [1,5,9], [3,5,7] ]; MAIN.prototype.win = function(mark){ if(mark !== 0 && mark !== 1){return null;} var user = mark === 0 ? "あなた" : "コンピュータ"; var ctx = document.getElementById("mycanvas").getContext("2d"); // window表示 ctx.fillStyle = "red"; ctx.fillRect(100,50,200,100); // 文字 ctx.fillStyle = "white"; ctx.font = "bold 16px sans-serif"; ctx.textAlign = "center"; ctx.fillText(user + "の勝ち", 200, 100 , 200); return 1; }; MAIN.prototype.draw = function(){ var ctx = document.getElementById("mycanvas").getContext("2d"); // window表示 ctx.fillStyle = "blue"; ctx.fillRect(100,50,200,100); // 文字 ctx.fillStyle = "white"; ctx.font = "bold 16px sans-serif"; ctx.textAlign = "center"; ctx.fillText("引き分け", 200, 100 , 200); return 1; } new LIB().construct(); })();

解説

勝ちパターンの判定は、3目並べの場合は、縦横ななめの合計8パターン存在します。 これを配列で持っておいて、プレイヤーサイドと、コンピュータサイドの登録された結果データで全てに配置されている場合は、3つ並んだという判定をするようにしました。 ビット計算方式で、もう少し簡潔なコードも書くことは可能ですが、初期版はシンプルアルゴリズムで行うことにしますね。 ちなみに、前回までのプログラムでは、this.bufferに、打ち込んだコマの番号だけを順番に登録していたんですが、どちらのプレイヤーが打ったコマなのかを判断しなければいけないことに気が付き、その点を改修しています。 今のバージョンだと、コンピュータがランダムだけなので、アホすぎるので、次回は、もう少し打つ手を考えるようにしてみたいと思います。

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ