広告
前回の画像データを分割+シャッフルして表示する処理に機能を追加してパズルゲームにしてみた。
目次
完成品
8パズル
画像データのURL:
シャッフル処理の変更
前回作ったシャッフルの処理をそのまま使うとパズルがゴール不可能なケースが発生する問題がある。この問題を回避するにはパズルのゴール可否を判定して再シャッフルする処理を追加するか、ゴール可能なシャッフル処理をするか2択になるが、前者は面倒そうだったので後者に変更することにした。
変更前
1 2 3 4 5 6 7 8 9 10 11 12 |
function shuffleArray(array) { var n = array.length, t, i; while (n) { i = Math.floor(Math.random() * n--); t = array[n]; array[n] = array[i]; array[i] = t; } return array; } |
変更後
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
function shuffleBlocks(array) { var blk_id = -1; for (var i = 0; i < BOARD.X_BLOCKS * BOARD.Y_BLOCKS; i++) { if ((array[i][0] == (BOARD.X_BLOCKS-1)) && (array[i][1] == (BOARD.Y_BLOCKS-1))) { // 空きブロックのindex blk_id = i; break; } } var n = 0; while (n < BOARD.SHUFFLE_CNT) { var direct = Math.floor(n * Math.random() % BOARD.DIRECT_MAX); // 空きブロックを可能な限り繰り返し移動する if (0 <= blk_id + diff[direct] && blk_id + diff[direct] < BOARD.X_BLOCKS * BOARD.Y_BLOCKS) { var tmp_blocks = array[blk_id]; array[blk_id] = array[blk_id + diff[direct]]; array[blk_id + diff[direct]] = tmp_blocks; blk_id = blk_id + diff[direct]; } n++; } return array; } |
パズルのゴールから空ブロックを適当に移動させることでシャッフルしている。
ソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
<html> <head> <meta charset="UTF-8"> <title>canvasで図形を描く</title> <script type="text/javascript"> var blocks = []; var canvas; var img; var status = 0; var BOARD = { 'X_RANGE' : 300, 'Y_RANGE' : 300, 'X_BLOCKS' : 3, 'Y_BLOCKS' : 3, 'DIRECT_MAX' : 4, 'SHUFFLE_CNT' : 100, }; var diff = [-BOARD.X_BLOCKS, -1, 1, BOARD.X_BLOCKS]; function shuffleBlocks(array) { var blk_id = -1; for (var i = 0; i < BOARD.X_BLOCKS * BOARD.Y_BLOCKS; i++) { if ((array[i][0] == (BOARD.X_BLOCKS-1)) && (array[i][1] == (BOARD.Y_BLOCKS-1))) { // 空きブロックのindex blk_id = i; break; } } var n = 0; while (n < BOARD.SHUFFLE_CNT) { var direct = Math.floor(n * Math.random() % BOARD.DIRECT_MAX); // 空きブロックを移動可能な限り適当に移動する if (0 <= blk_id + diff[direct] && blk_id + diff[direct] < BOARD.X_BLOCKS * BOARD.Y_BLOCKS) { var tmp_blocks = array[blk_id]; array[blk_id] = array[blk_id + diff[direct]]; array[blk_id + diff[direct]] = tmp_blocks; blk_id = blk_id + diff[direct]; } n++; } return array; } function moveBlock(e) { var rect = e.target.getBoundingClientRect(); var width = BOARD.X_RANGE / BOARD.X_BLOCKS; var height = BOARD.Y_RANGE / BOARD.Y_BLOCKS; // クリックしたブロック(座標)を算出 var mouseX = Math.floor((e.clientX - Math.floor(rect.left)) / width); var mouseY = Math.floor((e.clientY - Math.floor(rect.top)) / height); // ブロック(blocks)のindexに変換 var blk_id = mouseX * BOARD.X_BLOCKS + mouseY; // 上下左右の4方向 for (i = 0; i < BOARD.DIRECT_MAX; i++) { if (0 <= blk_id + diff[i] && blk_id + diff[i] < BOARD.X_BLOCKS * BOARD.Y_BLOCKS) { // 隣が空ブロックなら位置を交換 if ((blocks[blk_id + diff[i]][0] == (BOARD.X_BLOCKS-1)) && (blocks[blk_id + diff[i]][1] == (BOARD.Y_BLOCKS-1))) { var tmp_blocks = blocks[blk_id]; blocks[blk_id] = blocks[blk_id + diff[i]]; blocks[blk_id + diff[i]] = tmp_blocks; break; } } } } function drawBlock(ctx) { var width = BOARD.X_RANGE / BOARD.X_BLOCKS; var height = BOARD.Y_RANGE / BOARD.Y_BLOCKS; var width0 = img.width / BOARD.X_BLOCKS; var height0 = img.height / BOARD.Y_BLOCKS; var blk_cnt = BOARD.X_BLOCKS * BOARD.Y_BLOCKS; // 一度キャンバスを白に戻す ctx.fillStyle = "rgb(255,255,255)"; ctx.fillRect(0, 0, BOARD.X_RANGE, BOARD.Y_RANGE); // ブロックを描画 for(var i = 0; i < BOARD.X_BLOCKS; i++){ for(var j = 0; j < BOARD.Y_BLOCKS; j++){ var x = blocks[(BOARD.X_BLOCKS * BOARD.Y_BLOCKS) - blk_cnt][0]; var y = blocks[(BOARD.X_BLOCKS * BOARD.Y_BLOCKS) - blk_cnt][1]; // 最右下のブロックは空ブロック(黒塗り) if ((x == BOARD.X_BLOCKS - 1) && (y == BOARD.Y_BLOCKS - 1)) { ctx.fillStyle = "rgb(0,0,0)"; ctx.fillRect(width * i, height * j, width, height); } else { ctx.drawImage(img, width0 * x, height0 * y,width0, height0, width * i, height * j, width, height); } blk_cnt--; } } } function shuffle() { // 描画コンテキストの取得 var canvas = document.getElementById('c'); if (canvas.getContext) { var ctx = canvas.getContext('2d'); ctx.fillStyle = "rgb(255,255,255)"; ctx.fillRect(0, 0, BOARD.X_RANGE, BOARD.Y_RANGE); // 画像データがロード済みならシャッフルして描画 if (status != 0) { var width = BOARD.X_RANGE / BOARD.X_BLOCKS; var height = BOARD.Y_RANGE / BOARD.Y_BLOCKS; var width0 = img.width / BOARD.X_BLOCKS; var height0 = img.height / BOARD.Y_BLOCKS; var blk_cnt = 0; // 初期配置 for(var i = 0; i < BOARD.X_BLOCKS; i++){ for(var j = 0; j < BOARD.Y_BLOCKS; j++){ blocks[blk_cnt++] = [i,j]; } } // ブロックをシャッフル //blocks = shuffleArray(blocks); blocks = shuffleBlocks(blocks); // ブロックの配置 for(var i = 0; i < BOARD.X_BLOCKS; i++){ for(var j = 0; j < BOARD.Y_BLOCKS; j++){ var x = blocks[(BOARD.X_BLOCKS * BOARD.Y_BLOCKS) - blk_cnt][0]; var y = blocks[(BOARD.X_BLOCKS * BOARD.Y_BLOCKS) - blk_cnt][1]; // 最右下のブロックは空ブロック(黒塗り) if ((x == BOARD.X_BLOCKS - 1) && (y == BOARD.Y_BLOCKS - 1)) { ctx.fillStyle = "rgb(0,0,0)"; ctx.fillRect(width * i, height * j, width, height); } else { ctx.drawImage(img, width0 * x, height0 * y,width0, height0, width * i, height * j, width, height); } blk_cnt--; } } } else { ctx.font = "20px Arial"; ctx.strokeStyle = "red"; ctx.strokeText("画像データが未ロードです", 0, 150); } // ブロック移動処理 canvas.onclick = function(event) { moveBlock(event); drawBlock(ctx); } } } function loadImage() { status = 0; canvas = document.getElementById('c'); if (canvas.getContext) { var ctx = canvas.getContext('2d'); ctx.fillStyle = "rgb(255,255,255)"; ctx.fillRect(0, 0, 300, 300); img = new Image(); img.src = document.getElementById('url').value; img.onload = function() { ctx.drawImage(img, 0,0, this.width, this.width, 0, 0, BOARD.X_RANGE, BOARD.Y_RANGE); status = 1; } } } </script> </head> <body> <p>8パズル</p> <canvas id="c" style="background-color:white;" width=300 height=300></canvas> <p>画像データのURL: <input id="url" type="text" value=""> </p> <input type="button" value="Load" onclick="loadImage()"> <input type="button" value="Shuffle" onclick="shuffle()"> </body> </html> |
広告
広告