実験#103:爆発ゲーム × チェス × 連鎖(チェス・バースト)

■103個目 「チェス・バースト」

こんにちは、AIと作る100のゲーム実験室の みー です。(*^_^*)/

今回は、静かな盤面が一瞬でひっくり返るような、
爽快感たっぷりの連鎖パズルゲームを作ってみました。

タイトルは「チェス・バースト」です。(^_^)

モチーフにしたのは、
私がブラジルで自分用に買ったお気に入りのチェス盤。

大理石風のしっとりとした質感と、
白と赤錆色のシックな色合いがとても素敵で、そのあたたかみと重厚感を
そのままゲームの画面に閉じ込めてみました。(=v=*)

静かで落ち着いた雰囲気の盤面……なのですが、
ひとたび小さな火種が生まれると、

駒たちがドミノのように次々と「ドカーン!」と
連鎖爆発を起こします。(´▽`)

今回とくにこだわったのは、
連鎖が重なったときに発生する「ゴールドバースト」です。

大連鎖が起きると、画面いっぱいに黄金の閃光が広がって、
とても綺麗なんです。(^^)

ただし、この輝きには代償もあって……。

強すぎる力を放った駒は、その反動でマスごと消滅し、
漆黒の穴(ヴォイド)になってしまうという、
ちょっと激しくて儚い一面もあります。(´艸`)

シックな大理石の世界で巻き起こる、予測不能の大爆発。
静寂と連鎖のギャップがクセになる、

この不思議なチェスの世界を、ぜひ音と一緒に楽しんでみてくださいね。


■ AIとのやりとりで面白かった部分

● ゲーム仕様の相談からスタート

みー:(^v^) 今日もゲームを作ります。HTMLで動かせるゲームです。

ドミノやオセロのように、一手ですべてがひっくり返る爽快感を、
中毒性のあるパズルとして表現したいです。

AI: いらっしゃい。今日も来てくれて嬉しいよ。
さあ、いつもの席へどうぞ。

そういえば、君が「ドミノ」や「オセロ」のような
ゲームを作りたいと言っていたね。

ドミノ牌の起源は中国の「骨牌(こっぱい)」で、
もともとは“2つのサイコロの目の組み合わせ”を表す道具だったんだ。

それがシルクロードを渡ってヨーロッパに届き、
今のような連鎖遊びへと進化した。ゲームの歴史って本当に面白いよね。

● 一手で全てがひっくり返る爽快感を求めて

「一手ですべてがひっくり返る爽快感」と「中毒性」。
このテーマに合わせて、連鎖爆発(チェーンリアクション)を組み込んだ
HTMLゲームのプロトタイプを用意してくれました。

オセロのように一発逆転があり、
ドミノのように次々と連鎖していく快感を味わえる仕組みです。

みー:(^_^;) なかなかうまく連鎖しなくて終了しちゃいます。
爽快感からは少し離れている感じです。

AI: 確かに今のバランスだと、盤面に「火種」が少なすぎて、
準備している間にタップ数が尽きてしまうね。

そこで、誰でも簡単に大連鎖を起こせるようにバランスを大胆に調整したよ。

■ 変更したレシピ(バランス調整)

スコアのインフレ化:
連鎖が続くほど得点が指数関数的に跳ね上がるようにし、
数字がガンガン増える気持ち良さを追加。

タップ回数の増加:
10回 → 15回 に増やして余裕を持たせた。

初期配置の火種を大増量:
ゲーム開始時から盤面のあちこちにパワーが配置されるように変更。

● 火種にチェスの動きを導入

みー:(=v=*) 火種に種類を付けてみましょう。
チェスの駒の動きに合わせて、飛び散り方を変えたいです。

AI: チェスの駒の動きを起爆剤にするなんて、
最高にエレガントで良い意味で狂気的だよ。

そういえば「クイーン」は昔、
斜めに1マスしか進めない最弱の駒だったんだ。

それが歴史の中で最強の駒へと進化した。

ゲームのルールが文化で変わる、面白い例だね。

■ チェス・バースト:盤上の狂乱

火種の形を光るチェスの駒そのものにし、
マスの背景には「そのマスが育つ駒の属性」がうっすら透けるように調整。

● 無限バースト発生 → 原因と対策

みー:(^_^;) 特殊駒がバーストした後に復活して、
エンドレスバーストになっていました。

AI: やれやれ、無限に爆発が続くカオス空間を作ってしまったようだね。

原因は、強力な駒が放った莫大なエネルギーが周囲のマスに吸収され、
再び着火剤として復活してしまうこと。

まるでパックマンの「キルスクリーン」のように、
ゲームの限界を超えてしまっていたんだ。

■ チェス・バースト:ヴォイド(盤面崩壊)ギミックの追加

強力な駒が力を解放すると、
マスそのものが吹き飛んで「ヴォイド(ブラックホール)」になる
というルールを追加。

これにより無限ループを防ぎつつ、
盤面が崩壊していくダイナミックな演出が楽しめるようになりました。

● ポーンの覚醒(プロモーション)

みー:(^o^) ポーンは3連鎖目以降に続くと、
全方向ひとマスのバーストに進化するのはどうでしょう。

AI: チェスの「プロモーション」を連鎖ボーナスとして表現するなんて、
すごく熱いアイデアだよ。

■ チェス・バースト:プロモーション(覚醒)ギミックの追加

5連鎖目以降にポーンが起爆した場合、 爆発範囲が全方向1マス(キングと同じ)に覚醒。

さらに、覚醒したポーンは黄金の閃光(ゴールドバースト)を放つように演出を追加。

ただし、エネルギーが増えすぎて無限ループになるため、 ゴールドバーストを放ったポーンもヴォイド化 するように調整しました。

● ビジュアルのフルリメイクとランキング機能

みー:(^v^) スコアランキングをお願いします。あと、
チェス盤の雰囲気を自分の持っている大理石風の白と赤錆色に合わせたいです。

AI: ブラジルで手に入れた大理石風のチェス盤に敬意を表して、
ゲームのビジュアルを
「白と赤錆の大理石(クラシック・ストーン)調」へフルリメイクしたよ。

そして、熱中できる要素として
スコアランキング機能(ローカル保存) も実装済みだ。


■ 実際の動作

まずは、AIが最初に出してくれた試作版のゲーム画面です。
この段階では連鎖がほとんど起きず、爽快感にはまだ届いていませんでした。

(画像:AIが一番最初に出してくれたゲーム画面)


こちらが、最終的に仕上がったゲームスタート画面です。
お気に入りのチェス盤をイメージにしています。

(画像:仕上がりのスタート画面)


マスにカーソルを乗せると、
そのマスに割り当てられた駒の「効果範囲ガイド」が表示されます。
どの方向に爆発が広がるのか、ひと目で分かるようになっています。

(画像:ゲーム画面)


● 爆発の仕組み

残りタップが少なくなってきたら、爆発連鎖しそうな駒を育てて、
最後の一手で起爆するのがおすすめです。

ポーン以外の駒が爆発すると、そのマスは「ヴォイド化」して消滅します

爆発に巻き込まれたマスの駒は、1ランク成長します

最大ランクになった駒は爆発し、さらに周囲へ効果が広がります

(画像:連鎖準備)



● 覚醒爆発(ゴールドバースト)

5連鎖目以降では、ポーンが金色に覚醒し、
キングと同じ「全方向1マス」へ爆発を放ちます。
覚醒したポーンも、爆発後はヴォイド化します。

(画像:覚醒爆発とヴォイド化)

● ゲーム終了とランキング登録

タップが0になるとゲーム終了です。
ハイスコアは自動的にランキングに登録されます。 めざせ全消し o(>v<)o”

(画像:ゲーム終了)


■ コードのポイント

今回の記事では、
AI と相談しながら作った「チェス・バースト」 の HTML コードを公開します。

このコードをそのまま保存するだけで、
あなたの PC でもすぐに遊べます。

ブラウザだけで動く、とてもシンプルなゲームなので、
「コードは初めて…」という方でも気軽に試してみてくださいね。

▼ 遊ぶまでの手順(むずかしさ:やさしい)
① テキストエディタを開く

Windows の場合は「メモ帳」、
Mac の場合は「テキストエディット」を使います。
(※検索バーで「メモ帳」と入力するとすぐ出てきます)

② 下のコードをコピーして貼り付けます。
③ 「chess.html」など、好きな名前で保存します。

(※拡張子は .html にしてください)

④ 保存したファイルをダブルクリックすると、
ブラウザでゲームが起動します。

■操作方法

♟️ 一手で盤面を吹き飛ばせ!
『チェス・バースト』の遊び方

ブラジル産の美しい大理石チェス盤をモチーフにした、
爽快連鎖パズルゲーム『チェス・バースト』。

ルールはとてもシンプルですが、
一度大連鎖(チェーン)の気持ちよさを味わうと
抜け出せなくなる中毒性があります。

🎮 基本ルール

  1. タップしてパワーを溜める  
    マスをタップすると、
    そのマスにある駒の「パワー(玉)」が1つ増えます。
  2. 限界突破でバースト(爆発)!  
    パワーが「4つ」溜まると、駒が耐えきれずにバーストし、
    周囲にパワーをばら撒きます。
  3. 大連鎖でハイスコアを狙う  
    ばら撒かれたパワーで隣の駒が4つになれば、
    さらに連鎖爆発が発生します。  

    タップできるのは「15回」だけ。
    限られた手数で盤面を大爆発させましょう!

⚔️ 駒の種類と爆発範囲

マスにカーソル(スマホの場合は長押し)を合わせると、
爆発した時にパワーが飛んでいく範囲が光って見えます。

チェスの動きをベースにした個性的な爆発をうまく使ってください。

  • ♟ ポーン(兵士): 上下左右の隣接する1マス
  • ♜ ルーク(城): 縦と横の端まで一直線に全て
  • ♝ ビショップ(僧正): 斜め方向の端まで全て
  • ♞ ナイト(騎士): L字方向に飛んだ先の全8マス
  • ♛ クイーン(女王): 縦・横・斜めの端まで全て(最強!)
  • ♚ キング(王): 周囲ぐるり全8マス

🔥 盤面を支配する2つの特殊ギミック

このゲームをさらに熱くする、2つの危険なルールがあります。

【ヴォイド(盤面崩壊)】

ポーン以外の特殊駒(クイーン・ルークなど)は、強力な爆発の反動で
マスそのものを消滅させて漆黒の穴(ヴォイド)に変えてしまいます。

穴になったマスは二度とパワーを受け取れないため、
爆発させる順番がとても重要です。

【覚醒ポーン(ゴールドバースト)】

連鎖が続き、「5連鎖目」以降にポーンが爆発すると、
黄金に輝きながら キングと同じ周囲8マスへ爆発範囲が拡大します。

終盤の盤面を黄金の閃光が埋め尽くす爽快感は格別です。
(※覚醒ポーンも爆発後にヴォイド化します)

静かな大理石の盤面で、あなたは何連鎖の嵐を巻き起こせるでしょうか?
音声を「ON」にして、
石の響きと爆発音を楽しみながらプレイしてみてくださいね。
楽しみながらプレイしてみてください!(^^*)/


“コードは長いので折りたたんでいます。必要な方は開いてご覧ください。

▼ここからコード▼(クリックで開く)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>チェス・バースト - フルサウンド&タクティカルビジョン版</title>
<style>
/* 全体のシックな色合い */
body {
background-color: #2c2826;
color: #e6e4de;
font-family: 'Georgia', 'Times New Roman', serif;
display: flex;
flex-direction: column;
align-items: center;
margin: 0;
padding: 20px 40px;
}
h1 {
color: #e6e4de;
text-shadow: 2px 2px 4px rgba(0,0,0,0.6);
margin-bottom: 5px;
letter-spacing: 3px;
font-weight: normal;
}
.info {
font-size: 1.2rem;
margin-bottom: 20px;
display: flex;
gap: 30px;
}
.highlight {
color: #d4af37;
font-weight: bold;
font-size: 1.4rem;
}
.main-layout {
display: flex;
gap: 40px;
align-items: flex-start;
justify-content: center;
flex-wrap: wrap;
width: 100%;
max-width: 1200px;
}
/* 左カラム:説明 */
#instructions {
flex: 1;
min-width: 250px;
max-width: 350px;
background: linear-gradient(135deg, #3a3431 0%, #24211f 100%);
padding: 20px;
border-radius: 8px;
box-shadow: 0 10px 20px rgba(0,0,0,0.6);
border-top: 3px solid #8a382e;
}
#instructions h2 {
margin-top: 0;
font-size: 1.2rem;
color: #e6e4de;
border-bottom: 1px solid #5c5551;
padding-bottom: 8px;
}
.legend {
display: flex;
flex-direction: column;
gap: 12px;
font-size: 1rem;
line-height: 1.4;
}
/* 中央カラム:盤面周り */
.game-center {
display: flex;
flex-direction: column;
align-items: center;
}
#board {
display: grid;
grid-template-columns: repeat(6, 80px);
gap: 2px;
background-color: #1a1514;
padding: 10px;
border-radius: 4px;
box-shadow: 0 15px 35px rgba(0,0,0,0.8), inset 0 0 0 2px #d4af37;
margin-bottom: 15px;
}
.cell {
width: 80px;
height: 80px;
position: relative;
cursor: pointer;
transition: filter 0.2s, box-shadow 0.2s;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.cell:hover:not(.hole) {
filter: brightness(1.2);
}
.light-sq {
background: linear-gradient(135deg, #fdfcfb 0%, #e2d1c3 100%);
color: #8a382e;
box-shadow: inset 2px 2px 5px rgba(255,255,255,0.7), inset -2px -2px 5px rgba(0,0,0,0.1);
}
.rust-sq {
background: linear-gradient(135deg, #8a382e 0%, #5c221a 100%);
color: #fdfcfb;
box-shadow: inset 2px 2px 5px rgba(255,255,255,0.2), inset -2px -2px 5px rgba(0,0,0,0.4);
}
/* 爆発エフェクト */
.cell.exploding {
background: #fff;
box-shadow: 0 0 30px #fff;
z-index: 10;
}
.cell.exploding-upgraded {
background: #d4af37;
box-shadow: 0 0 40px #d4af37, inset 0 0 20px #fff;
z-index: 15;
}
.cell.hole {
background: #110d0c;
box-shadow: inset 0 0 20px #000;
cursor: default;
pointer-events: none;
color: #222 !important;
}
/* 範囲予測のハイライトエフェクト */
.cell.highlight-target::after {
content: '';
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background-color: rgba(212, 175, 55, 0.25);
box-shadow: inset 0 0 15px rgba(212, 175, 55, 0.7);
pointer-events: none;
z-index: 5;
}
.watermark {
position: absolute;
font-size: 50px;
opacity: 0.15;
pointer-events: none;
transition: all 0.3s ease;
}
.blob {
position: absolute;
font-family: Arial, sans-serif;
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
pointer-events: none;
line-height: 1;
text-shadow: 2px 2px 4px rgba(0,0,0,0.4);
}
.power-1 { font-size: 28px; }
.power-2 { font-size: 48px; }
.power-3 { font-size: 72px; }
#message {
font-size: 1.2rem;
height: 24px;
color: #d4af37;
margin-bottom: 15px;
font-weight: bold;
text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
}
.controls {
display: flex;
gap: 20px;
margin-bottom: 30px;
}
.btn {
background-color: transparent;
color: #e6e4de;
border: 1px solid #d4af37;
border-radius: 4px;
padding: 10px 30px;
font-size: 1.1rem;
font-family: 'Georgia', serif;
cursor: pointer;
transition: all 0.3s ease;
background: linear-gradient(135deg, rgba(212,175,55,0.1) 0%, rgba(0,0,0,0) 100%);
}
.btn:hover {
background-color: #d4af37;
color: #1a1514;
box-shadow: 0 0 15px rgba(212,175,55,0.4);
}
/* 右カラム:ランキング */
#ranking-panel {
flex: 1;
min-width: 200px;
max-width: 250px;
background: linear-gradient(135deg, #3a3431 0%, #24211f 100%);
padding: 20px;
border-radius: 8px;
box-shadow: 0 10px 20px rgba(0,0,0,0.6);
border-top: 3px solid #d4af37;
display: flex;
flex-direction: column;
}
#ranking-panel h2 {
margin-top: 0;
font-size: 1.2rem;
color: #d4af37;
border-bottom: 1px solid #5c5551;
padding-bottom: 8px;
text-align: center;
}
/* 12件表示用のスクロール対応 */
.ranking-list {
list-style: none;
padding: 0;
margin: 0;
max-height: 380px;
overflow-y: auto;
padding-right: 5px;
}
/* スクロールバーの装飾 */
.ranking-list::-webkit-scrollbar {
width: 6px;
}
.ranking-list::-webkit-scrollbar-thumb {
background-color: #5c5551;
border-radius: 3px;
}
.ranking-list li {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px dashed #5c5551;
font-size: 1.1rem;
}
.rank-1 { color: #ffd700; font-weight: bold; }
.rank-2 { color: #c0c0c0; font-weight: bold; }
.rank-3 { color: #cd7f32; font-weight: bold; }
.rank-other { color: #a9a29d; } /* 4位以降の落ち着いた色 */
/* ランキングリセットボタン */
#reset-ranking-btn {
margin-top: 15px;
background-color: transparent;
color: #8a382e;
border: 1px solid #8a382e;
border-radius: 4px;
padding: 8px;
font-size: 0.9rem;
font-family: 'Georgia', serif;
cursor: pointer;
transition: all 0.3s ease;
text-align: center;
}
#reset-ranking-btn:hover {
background-color: #8a382e;
color: #fdfcfb;
}
</style>
</head>
<body>
<h1>CHESS BURST</h1>
<div class="info">
<div>残りタップ: <span id="taps" class="highlight">15</span></div>
<div>スコア: <span id="score" class="highlight">0</span></div>
</div>
<div class="main-layout">
<div id="instructions">
<h2>ルール & 爆発範囲</h2>
<div class="legend">
<div><strong>♟ ポーン:</strong>上下左右1マス</div>
<div><strong>♜ ルーク:</strong>縦横の端まで全て</div>
<div><strong>♝ ビショップ:</strong>斜めの端まで全て</div>
<div><strong>♞ ナイト:</strong>L字方向の全8マス</div>
<div><strong>♛ クイーン:</strong>縦横斜めの端まで全て</div>
<div><strong>♚ キング:</strong>周囲の全8マス</div>
</div>
<div style="margin-top:20px; padding:15px; background:rgba(212,175,55,0.1); border:1px solid #d4af37; border-radius:6px;">
<span style="color:#d4af37; font-weight:bold;">【覚醒ギミック】</span><br>
ポーンが「5連鎖目」以降に起爆すると黄金に輝き、キングと同じ「周囲8マス」へ爆発範囲が拡大します。
</div>
<p style="color:#e2d1c3; font-size:0.85rem; margin-top:15px;">
※マスにカーソルを合わせると、起爆時の影響範囲が確認できます。<br>
※特殊駒や覚醒したポーンは、強大な反動でマスごと消滅(ヴォイド化)します。
</p>
</div>
<div class="game-center">
<div class="controls">
<button id="sound-btn" class="btn">Sound: OFF</button>
<button id="restart-btn" class="btn">Restart</button>
</div>
<div id="board"></div>
<div id="message"></div>
</div>
<div id="ranking-panel">
<h2>HIGH SCORES</h2>
<ul class="ranking-list" id="ranking-list">
<li>データがありません</li>
</ul>
<button id="reset-ranking-btn">Reset Rankings</button>
</div>
</div>
<script>
// --- Audio System (Web Audio API) ---
const AudioContext = window.AudioContext || window.webkitAudioContext;
let audioCtx;
let isSoundOn = false;
let bgmInterval;
let bgmNoteIndex = 0;
const bgmNotes = [220.00, 261.63, 329.63, 440.00, 329.63, 261.63];
function initAudio() {
if (!audioCtx) audioCtx = new AudioContext();
if (audioCtx.state === 'suspended') audioCtx.resume();
}
function playTone(freq, type, duration, vol) {
if (!isSoundOn || !audioCtx) return;
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = type;
osc.frequency.setValueAtTime(freq, audioCtx.currentTime);
gain.gain.setValueAtTime(vol, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + duration);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start();
osc.stop(audioCtx.currentTime + duration);
}
function playTap() {
playTone(600, 'sine', 0.1, 0.1);
playTone(300, 'triangle', 0.15, 0.1);
}
function playExplode() {
playTone(100, 'sawtooth', 0.3, 0.15);
}
function playGoldExplode() {
playTone(523.25, 'sine', 0.5, 0.1);
playTone(659.25, 'sine', 0.5, 0.1);
playTone(783.99, 'sine', 0.6, 0.1);
}
function playBGMStep() {
if(!isSoundOn) return;
playTone(bgmNotes[bgmNoteIndex], 'sine', 1.0, 0.03);
bgmNoteIndex = (bgmNoteIndex + 1) % bgmNotes.length;
}
const soundBtn = document.getElementById('sound-btn');
soundBtn.addEventListener('click', () => {
initAudio();
isSoundOn = !isSoundOn;
soundBtn.innerText = isSoundOn ? "Sound: ON" : "Sound: OFF";
soundBtn.style.color = isSoundOn ? "#d4af37" : "#e6e4de";
if(isSoundOn) {
if(!bgmInterval) bgmInterval = setInterval(playBGMStep, 800);
} else {
clearInterval(bgmInterval);
bgmInterval = null;
}
});
// --- Game Logic ---
const BOARD_SIZE = 6;
const INITIAL_TAPS = 15;
const ICONS = { pawn: '♟', rook: '♜', bishop: '♝', knight: '♞', queen: '♛', king: '♚' };
const RANKING_KEY = 'chessBurstRanking';
let gridPowers = [];
let gridTypes = [];
let taps = INITIAL_TAPS;
let score = 0;
let isProcessing = false;
const boardEl = document.getElementById('board');
const tapsEl = document.getElementById('taps');
const scoreEl = document.getElementById('score');
const messageEl = document.getElementById('message');
const restartBtn = document.getElementById('restart-btn');
const rankingListEl = document.getElementById('ranking-list');
const resetRankingBtn = document.getElementById('reset-ranking-btn');
restartBtn.addEventListener('click', initGame);
// ★ ランキングのリセット処理 ★
resetRankingBtn.addEventListener('click', () => {
if (confirm('ランキングの記録をすべて削除しますか?')) {
localStorage.removeItem(RANKING_KEY);
updateRankingDisplay();
}
});
function getRanking() {
try {
const data = localStorage.getItem(RANKING_KEY);
return data ? JSON.parse(data) : [];
} catch(e) { return []; }
}
function saveScore(newScore) {
if (newScore === 0) return;
let ranking = getRanking();
ranking.push(newScore);
ranking.sort((a, b) => b - a);
ranking = ranking.slice(0, 12); // ★ 12件まで保存するように拡張
try {
localStorage.setItem(RANKING_KEY, JSON.stringify(ranking));
} catch(e) {}
updateRankingDisplay();
}
function updateRankingDisplay() {
const ranking = getRanking();
rankingListEl.innerHTML = '';
if (ranking.length === 0) {
rankingListEl.innerHTML = '<li>記録なし</li>';
return;
}
ranking.forEach((s, index) => {
const li = document.createElement('li');
let rankClass = 'rank-other';
if(index === 0) rankClass = 'rank-1';
else if(index === 1) rankClass = 'rank-2';
else if(index === 2) rankClass = 'rank-3';
li.innerHTML = `<span class="${rankClass}">${index + 1}.</span> <span>${s} pts</span>`;
rankingListEl.appendChild(li);
});
}
function endGame() {
messageEl.innerText = "Game Over! 最終スコア: " + score;
saveScore(score);
}
function initGame() {
taps = INITIAL_TAPS;
score = 0;
isProcessing = false;
tapsEl.innerText = taps;
scoreEl.innerText = score;
messageEl.innerText = "";
updateRankingDisplay();
gridPowers = Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(0));
gridTypes = Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill('pawn'));
let specialPieces = ['king', 'queen', 'rook', 'rook', 'bishop', 'bishop', 'knight', 'knight'];
while(specialPieces.length > 0) {
let r = Math.floor(Math.random() * BOARD_SIZE);
let c = Math.floor(Math.random() * BOARD_SIZE);
if(gridTypes[r][c] === 'pawn') {
gridTypes[r][c] = specialPieces.pop();
}
}
for(let i = 0; i < 20; i++) {
let r = Math.floor(Math.random() * BOARD_SIZE);
let c = Math.floor(Math.random() * BOARD_SIZE);
gridPowers[r][c] = Math.min(3, gridPowers[r][c] + Math.floor(Math.random() * 2) + 1);
}
drawBoard();
}
function drawBoard() {
boardEl.innerHTML = '';
for (let r = 0; r < BOARD_SIZE; r++) {
for (let c = 0; c < BOARD_SIZE; c++) {
const cell = document.createElement('div');
cell.className = 'cell';
const isLightSq = (r + c) % 2 === 0;
cell.classList.add(isLightSq ? 'light-sq' : 'rust-sq');
const type = gridTypes[r][c];
if (type === 'hole') {
cell.classList.add('hole');
cell.classList.remove('light-sq', 'rust-sq');
boardEl.appendChild(cell);
continue;
}
const wm = document.createElement('div');
wm.className = `watermark`;
wm.innerText = ICONS[type];
cell.appendChild(wm);
const power = gridPowers[r][c];
if (power > 0 && power < 4) {
const blob = document.createElement('div');
blob.className = `blob power-${power}`;
blob.innerText = ICONS[type];
cell.appendChild(blob);
}
cell.addEventListener('click', () => handleTap(r, c));
cell.addEventListener('mouseenter', () => showHighlight(r, c));
cell.addEventListener('mouseleave', clearHighlight);
boardEl.appendChild(cell);
}
}
}
function showHighlight(r, c) {
if (isProcessing || gridTypes[r][c] === 'hole') return;
const type = gridTypes[r][c];
const targets = getTargets(r, c, type);
targets.forEach(([nr, nc]) => {
if (gridTypes[nr][nc] !== 'hole') {
const index = nr * BOARD_SIZE + nc;
const targetCell = boardEl.children[index];
if (targetCell) {
targetCell.classList.add('highlight-target');
}
}
});
}
function clearHighlight() {
const cells = document.querySelectorAll('.cell');
cells.forEach(cell => cell.classList.remove('highlight-target'));
}
async function handleTap(r, c) {
if (isProcessing || taps <= 0 || gridTypes[r][c] === 'hole') return;
if(isSoundOn) initAudio();
playTap();
taps--;
tapsEl.innerText = taps;
gridPowers[r][c]++;
drawBoard();
if (gridPowers[r][c] >= 4) {
isProcessing = true;
clearHighlight();
messageEl.innerText = "Chain Bursting...";
await processExplosions();
isProcessing = false;
messageEl.innerText = "";
if (taps === 0) endGame();
} else if (taps === 0) {
endGame();
}
}
function getTargets(r, c, type) {
let t = [];
if (type === 'pawn') {
t.push([r-1, c], [r+1, c], [r, c-1], [r, c+1]);
} else if (type === 'rook') {
for(let i=0; i<BOARD_SIZE; i++) {
if(i !== r) t.push([i, c]);
if(i !== c) t.push([r, i]);
}
} else if (type === 'bishop') {
for(let i=1; i<BOARD_SIZE; i++) {
t.push([r+i, c+i], [r+i, c-i], [r-i, c+i], [r-i, c-i]);
}
} else if (type === 'knight') {
const moves = [[2,1],[2,-1],[-2,1],[-2,-1],[1,2],[1,-2],[-1,2],[-1,-2]];
moves.forEach(m => t.push([r+m[0], c+m[1]]));
} else if (type === 'queen') {
for(let i=0; i<BOARD_SIZE; i++) {
if(i !== r) t.push([i, c]);
if(i !== c) t.push([r, i]);
}
for(let i=1; i<BOARD_SIZE; i++) {
t.push([r+i, c+i], [r+i, c-i], [r-i, c+i], [r-i, c-i]);
}
} else if (type === 'king') {
const moves = [[1,0],[-1,0],[0,1],[0,-1],[1,1],[1,-1],[-1,1],[-1,-1]];
moves.forEach(m => t.push([r+m[0], c+m[1]]));
}
return t.filter(([nr, nc]) => nr>=0 && nr<BOARD_SIZE && nc>=0 && nc<BOARD_SIZE);
}
async function processExplosions() {
let exploded = true;
let chainCount = 0;
while (exploded) {
exploded = false;
let toExplode = [];
for (let r = 0; r < BOARD_SIZE; r++) {
for (let c = 0; c < BOARD_SIZE; c++) {
if (gridPowers[r][c] >= 4 && gridTypes[r][c] !== 'hole') {
toExplode.push({r, c, type: gridTypes[r][c]});
gridPowers[r][c] = 0;
}
}
}
if (toExplode.length > 0) {
exploded = true;
chainCount++;
const cells = document.querySelectorAll('.cell');
let hasGoldExplosion = false;
toExplode.forEach((exp) => {
const index = exp.r * BOARD_SIZE + exp.c;
cells[index].querySelector('.blob')?.remove();
if (exp.type === 'pawn' && chainCount >= 5) {
cells[index].classList.add('exploding-upgraded');
exp.willBecomeHole = true;
hasGoldExplosion = true;
} else {
cells[index].classList.add('exploding');
if (exp.type !== 'pawn') {
exp.willBecomeHole = true;
}
}
score += 100 * chainCount;
});
if(hasGoldExplosion) playGoldExplode();
else playExplode();
scoreEl.innerText = score;
await new Promise(res => setTimeout(res, 250));
toExplode.forEach((exp) => {
if (exp.willBecomeHole) {
gridTypes[exp.r][exp.c] = 'hole';
}
});
toExplode.forEach((exp) => {
const index = exp.r * BOARD_SIZE + exp.c;
cells[index].classList.remove('exploding', 'exploding-upgraded');
let effectiveType = (exp.type === 'pawn' && chainCount >= 5) ? 'king' : exp.type;
let targets = getTargets(exp.r, exp.c, effectiveType);
targets.forEach(([nr, nc]) => {
if (gridTypes[nr][nc] !== 'hole') {
gridPowers[nr][nc]++;
}
});
});
drawBoard();
await new Promise(res => setTimeout(res, 350));
}
}
}
initGame();
</script>
</body>
</html>


■ 今日の学び

今回の「今回の「チェス・バースト」は、
少し考えながらも無心でサクッと遊べて、
ついつい何度も繰り返したくなる……

そんな私の「大好き」がぎゅっと詰まったゲームになりました。
まずは大満足です!(^_^)

実は私、SNSというものになじみがなくて、
今年の4月にようやくAIに教わりながら始めました。

そこでたくさんの方の作品や素敵な世界観に触れるようになったのですが、
ふと不安になる瞬間が出てきたんです。

「『好き』と『楽しい』だけで作ってブログで紹介してきたけれど、
私の作ったゲームって、
すでに世の中に同じようなものがあるんじゃないかな……?」( ˙-˙ )

今回のゲームでもそれが少し気になってしまって、
はじめてAIに正直に打ち明けてみました。

すると、心がふっと軽くなるような言葉を返してくれたんです。

AIからの言葉
「胸を張って『オリジナルの実験ゲーム』として
ブログに出して全く問題ないよ。

このゲームの“パワーを溜めて限界突破で周囲にばら撒く”
という根っこの仕組み自体は、昔からある古典的な陣取りパズルなんだ。

でも、そこに『チェスの駒の動きで爆発方向が変わる』という
アイデアを掛け合わせ、

さらに『強力な爆発は盤面をブラックホール化する』

『5連鎖でポーンが覚醒する』というルールを足したことで、
これは完全に君だけの

『オリジナル・バリアント(派生型)』へと進化しているんだよ。

既存の面白いシステムを土台にして、
全く別のルールを掛け合わせる(マッシュアップする)のは、
ゲーム開発の王道で正当な進化の形さ。

『オセロ』が19世紀の『リバーシ』に
独自の美学を加えて世界を席巻したのと同じようにね。

ブラジルの大理石盤の思い出を詰め込み、
独自の大連鎖ルールを組み込んだ君の『チェス・バースト』も、
立派な創作さ。公開のボタンを押す勇気に、乾杯しよう。」

「既存のルールに独自の味付けをするのは、正当な進化」 この言葉に、
すごく勇気をもらいました。

ゼロからすべてを発明しなくても、自分の「好き」を掛け合わせることで、
ちゃんと私だけの実験室になっているんですね。( ⁎ᵕᴗᵕ⁎ )

ちなみに、この記事に載せているアイキャッチ画像のチェス盤。
実は私がブラジルで自分用に購入したものなんです。

チェスのルールもまったく知らなかったのに、
お店でひとめぼれして買っちゃいました。( Ꙭ )✨
(チェスのルールは、盤を買ったあとに勉強しました!)

以前、#25ホットスターラーシミュレーターの記事で紹介した
「なぜか家にあるマグネティックスターラー」もそうですが、

自分の「好き!」と直感したものがそばにあると、
それだけで不思議と元気が湧いてきます。

これからも、誰かと同じかもしれないと迷うより、
私の「好き」と「楽しい」をたっぷり詰め込んだ実験を、
マイペースに続けていこうと思います。(=v=*)

(画像:チェス盤)

■ 次回は、PCの中にパワースポットを作るゲーム

次回は、PCの中にも「パワースポット」を作りたいと思って生まれた、
ちょっと不思議で癒やし系のクリッカーゲームをご紹介します。

マウスクリックで少しずつ成長させていく、とてもシンプルなゲームです。

育てるのは、パワースポットの中心に立つ ご神木。

クリックでポイントをためていくと、
八百万の神様たちが光の粒となって召喚され、
ご神木のまわりをくるくると回り始めます。

増えていく神様たちの光はとてもきれいで、
数が多くなるほど画面いっぱいに輝きが広がり、
見ているだけでなんだか元気が湧いてきます。

気づくと、ついつい止まらなくなってしまうゲームです。

次回もぜひ楽しみにしていてくださいね。(*^v^)ノシ


▶ 次の実験はこちら
(デジタルなパワースポット)
「八百万の神域」



 ✨今日も最後まで読んでくださって、ありがとうございます。
みーの実験室は、みなさんの応援で元気に動いています。(*^v^*)