[お題にTRY] Canvas三目並べ #3 : コマ配置

2020年6月25日

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

朝はパンだけど、本当は卵ご飯が大好きな、ユゲタです。 canvasについて、ネットで調べていると、unityの記事ばかりに引っかかります。 どんなスマホでも動くという、アプリ開発のunityは、canvasでのGUI処理をするライブラリなんですかね? こんどちゃんと使ってみないと、細かなことはわかりませんが、検索結果から、世の中でのニーズが伺えます。

本日のIT謎掛け

「unity」と、かけまして・・・ 「今どきの低燃費車」と、ときます。 そのココロは・・・ ハイブリッドが売りです。

コンピュータ処理

前回クリック対応して○印を表示することができたので、今回はまず、コンピュータ側の×マークを表示して、ユーザーがクリックした次の手をコンピュータが打つという感じで交互に表示をしてみたいと思います。 この際に、まだ勝ち負けの思考アルゴリズムは入れずに表示とフローだけに絞るので、まだ完成ではありません。 ※今回もindex.htmlは前回から変更はありません。 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>TicTacToe</title> <style> #mycanvas{ border:1px solid #ccc; } </style> <script src="tictactoe.js"></script> </head> <body> <canvas id="mycanvas" width="400" height="200">not canvas</canvas> </body> </html>   (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(cell); // 記号を表示する this.drawBuffer(); // 続けてコンピュータの手 this.turnComputer(); // 打つ手が無くなったら終了 if(this.buffer.length >= 9){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++){ // 奇数番 if(i % 2 === 0){ this.drawMark_0(this.buffer[i]); } // 偶数 else{ this.drawMark_1(this.buffer[i]); } } }; // 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(cellNum); // 記号を表示する this.drawBuffer(); }; MAIN.prototype.getEmptyCells = function(){ var arr = []; for(var i=1; i<=9; i++){ if(this.buffer.indexOf(i) !== -1){continue;} arr.push(i); } return arr; } MAIN.prototype.getComPlace = function(emptyCells){ var num = Math.floor(Math.random() * emptyCells.length); return emptyCells[num]; } MAIN.prototype.finish = function(){ console.log("Finish !"); }; new LIB().construct(); })();  

解説

ユーザー(先手)、コンピュータ側(後手)、ということで、ユーザーがクリックしたら、すぐにコンピュータが入力する仕様にしてみました。 打った手は、this.buffer(配列)に、打った順に登録していっているので、奇数を「○」、偶数を「×」として表示するようにします。 表示する記号の「○」はcanvasのcircleで書きますが、「×」は線を二本書くので、全く形状と座標が違うため、別関数にして処理するようにしました。 ここでの座標もベタ処理をしているので、今後のリファクタリングで、効率改善したいと思います。 クリックして、コンピュータ表示もできて、あとは、アルゴリズムだけかな・・・と思ったんですが、 コンピュータがあまりに早い対応をするので、ゲームをしている感覚にならないため、コンピュータの考える時間を演出する必要があると感じましたね。 やっぱりゲームをするっていうのは、こういう感情コントロールが重要だとよく分かりました!

このブログを検索

ごあいさつ

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