JavaScriptさめがめのソースコード

<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>

コメント