<style>
/* ゲームコンテナのスタイル */
#game-container {
height: 497px;
width: 310px;
}
/* ゲームボードのスタイル */
#game-board {
display: grid;
gap: 1px;
border: 1px solid #000;
background-color: #000;
}
/* 各ブロックのスタイル */
.block {
width: 30px;
height: 30px;
cursor: pointer;
}
/* スコア表示のスタイル */
#score {
margin-top: 10px;
font-size: 18px;
}
</style>
<div id="game-container">
<div id="game-board"></div>
<div id="score">Score: 0</div>
</div>
<script>
// グリッドの横のマス目の数
const GRID_WIDTH = 10;
// グリッドの縦のマス目の数
const GRID_HEIGHT = 16;
// 使用する色
const COLORS = ['#FF0000', '#00FF00', '#0000FF', '#FFFF00'];
// ゲームのグリッドを保存する配列
let grid = [];
// プレイヤーのスコア
let score = 0;
// グリッドを初期化する関数
function initGrid() {
// 2重のforループを使って、縦と横のマス目を作成
for (let y = 0; y < GRID_HEIGHT; y++) {
grid[y] = [];
// 各行(y)に対して新しい配列を作成
for (let x = 0; x < GRID_WIDTH; x++) {
// COLORSの中からランダムに色を選ぶ
grid[y][x] = Math.floor(Math.random() * COLORS.length);
}
}
}
// グリッドを画面に描画する関数
function renderGrid() {
// game-boardという id を持つ HTML 要素を取得
const board = document.getElementById('game-board');
// ボードの中身を空にする
board.innerHTML = '';
// グリッドの列数を設定(各列の幅を30pxに)
board.style.gridTemplateColumns = `repeat(${GRID_WIDTH}, 30px)`;
// 2重のforループを使って、各マス目にブロックを作成
for (let y = 0; y < GRID_HEIGHT; y++) {
for (let x = 0; x < GRID_WIDTH; x++) {
// 新しい div 要素(HTML の箱)を作成
const block = document.createElement('div');
block.className = 'block'; // CSS クラスを設定
// ブロックの背景色を設定
block.style.backgroundColor = COLORS[grid[y][x]];
// ブロックがクリックされたときの動作を設定
block.addEventListener('click', () => handleClick(x, y));
// 作成したブロックをゲームボードに追加
board.appendChild(block);
}
}
}
// ブロックがクリックされたときの処理
function handleClick(x, y) {
// クリックされたマス目が空かどうかをチェック
if (grid[y][x] === -1) {
// 何も処理しない
return;
}
// クリックされたブロックの色を取得
const color = grid[y][x];
// 同じ色のブロックの塊を見つける
const cluster = findCluster(x, y, color);
// 2つ以上の同じ色のブロックが隣接している場合
if (cluster.length >= 2) {
// ブロックの塊を削除
removeCluster(cluster);
// 上のブロックを下に落とす
dropBlocks();
// 空の列を左に詰める
shiftColumns();
// スコアを更新(削除したブロック数-2の2乗)
score += (cluster.length - 2) ** 2;
// 画面上のスコア表示を更新
updateScore();
// グリッドを再描画
renderGrid();
// ゲームオーバーかどうかチェック
if (isGameOver()) {
// ゲームオーバー時にメッセージを表示
alert(`Game Over! Final Score: ${score}`);
}
}
}
// 同じ色のブロックの塊(クラスター)を見つける関数
function findCluster(x, y, color) {
// クラスターを保存する配列
const cluster = [];
// 探索するブロックの位置を保存するスタック
const stack = [{x, y}];
// 既に調べたマス目を記録するSet
const visited = new Set();
// スタックが空になるまで繰り返す
while (stack.length > 0) {
// スタックから次の探索位置を取り出す
const {x, y} = stack.pop();
// 訪問済みチェック用のキー
const key = `${x},${y}`;
// グリッドの範囲外、異なる色、または既に調べたマス目の場合はスキップ
if (x < 0 || x >= GRID_WIDTH || y < 0 || y >= GRID_HEIGHT ||
grid[y][x] !== color || visited.has(key)) {
continue;
}
// 調べたマス目として記録
visited.add(key);
// クラスターに追加
cluster.push({x, y});
// 上下左右のマス目をスタックに追加(次の探索候補)
stack.push({x: x + 1, y}, {x: x - 1, y}, {x, y: y + 1}, {x, y: y - 1});
}
return cluster;
}
// クラスターを削除する関数
function removeCluster(cluster) {
cluster.forEach(({x, y}) => {
// -1は空のマス目を表す
grid[y][x] = -1;
});
}
// ブロックを下に落とす関数
function dropBlocks() {
for (let x = 0; x < GRID_WIDTH; x++) {
// 書き込み位置(下から)
let writeY = GRID_HEIGHT - 1;
for (let y = GRID_HEIGHT - 1; y >= 0; y--) {
// 空でないマス目を見つけたら
if (grid[y][x] !== -1) {
// 下に移動
grid[writeY][x] = grid[y][x];
// 次の書き込み位置を上に
writeY--;
}
}
// 残りの上部マス目を空(-1)にする
for (let y = writeY; y >= 0; y--) {
grid[y][x] = -1;
}
}
}
// 空の列を左に詰める関数
function shiftColumns() {
// 書き込み位置(左から)
let writeX = 0;
for (let x = 0; x < GRID_WIDTH; x++) {
// 列が空でない場合
if (grid[GRID_HEIGHT - 1][x] !== -1) {
if (writeX !== x) {
// 列を左に移動
for (let y = 0; y < GRID_HEIGHT; y++) {
grid[y][writeX] = grid[y][x];
grid[y][x] = -1;
}
}
// 次の書き込み位置を右に
writeX++;
}
}
}
// ゲームオーバーかどうかをチェックする関数
function isGameOver() {
for (let y = 0; y < GRID_HEIGHT; y++) {
for (let x = 0; x < GRID_WIDTH; x++) {
if (grid[y][x] !== -1 && hasAdjacentSameColor(x, y)) {
return false;
}
}
}
return true;
}
// 隣接する同色ブロックがあるかをチェックする関数
function hasAdjacentSameColor(x, y) {
const color = grid[y][x];
// 上下左右のマス目をチェック
const directions = [
{dx: -1, dy: 0}, // 左
{dx: 1, dy: 0},// 右
{dx: 0, dy: -1}, // 上
{dx: 0, dy: 1} // 下
];
for (let dir of directions) {
const nx = x + dir.dx;
const ny = y + dir.dy;
// グリッド内で、かつ同じ色のブロックが隣接しているか確認
if (nx >= 0 && nx < GRID_WIDTH && ny >= 0 && ny < GRID_HEIGHT && grid[ny][nx] === color) {
return true;
}
}
return false;
}
// スコアを更新する関数
function updateScore() {
// 'score' という id を持つ HTML 要素のテキストを更新
document.getElementById('score').textContent = `Score: ${score}`;
}
// ゲームを初期化する関数
function init() {
// グリッドを初期化
initGrid();
// グリッドを画面に描画
renderGrid();
// スコアを更新(初期値0を表示)
updateScore();
}
// ゲームを開始
init();
</script>
コメント