先日、計算問題を解きながら盤面を塗り合う「陣取りペンキ」というブラウザゲームを作りました。エンジニアとして胸を張れるような大作ではありません。AIと対話しながら、仕掛けを一つずつ思いついては形にしていった、という感じでしょうか。
かかった時間は、およそ1日。この開発スタイルには「バイブコーディング」という名前が付き始めているようです。
この記事では、インク量の決め方やCPUの強さの正体など、ゲームの中身をプログラムごと覗いていきます。難しい話は最小限に、コードの意味を日本語に置き換えながら進めるつもりです。

まずは遊んでみてください
説明の前に、まず触ってみてください。細かいルールを読むより、「速く解くと大きく塗れる」感覚は遊んだほうが早く伝わります。
ルールはシンプルです。
- 計算問題に4択で答える
- 正解までの速さで、次に使えるインクの量が決まる
- 盤面をタップして、そのインクで塗る
- 8×8マスを全部塗るか、CPUと面積を競う
難易度は5段階。計算が苦手な方はLv1〜2、大人はLv3の九九あたりから試すとちょうどいい塩梅だと思います。細かいルールは別記事に譲るとして、ここからはプログラムの中身の話に入ります。
576行、たった1枚のファイルで完結している
種明かしから始めると、このゲームはindex.htmlという1つのファイルだけでできています。画面のデザイン(CSS)も、動きを作るプログラム(JavaScript)も、全部同じファイルの中。行数を数えると576行でした。npmもフレームワークも画像素材も一切使っていません。
塗ったときの「ピコン」という効果音も、音声ファイルを鳴らしているわけではなく、その場で音を合成しています。
function tone(freq, dur, type = "square", vol = 0.12, delay = 0) {
try {
const ctx = audio(), t = ctx.currentTime + delay;
const o = ctx.createOscillator(), g = ctx.createGain();
o.type = type; o.frequency.value = freq;
g.gain.setValueAtTime(vol, t);
g.gain.exponentialRampToValueAtTime(0.001, t + dur);
o.connect(g).connect(ctx.destination);
o.start(t); o.stop(t + dur);
} catch (e) { /* 音は失敗しても無視 */ }
}
難しそうに見えて、やっていることは「指定した高さの音を、指定した長さだけ鳴らして、だんだん小さくフェードアウトする」だけです。呼び出す高さや長さを変えて、正解音・不正解音・塗った音を作り分けています。
ファイルが1枚だけという構成は、地味に効いています。軽いし、壊れにくいし、ブログ記事にそのままiframeで埋め込めたのもこの単一ファイル構成のおかげ。中小企業の業務用ツールも、実はここまでシンプルに作れる場面が案外多いものです。
「速く解くほど大きく塗れる」を作る数行
このゲームの一番の売りである「速さ→インクの量」は、種を明かすと拍子抜けするほど単純です。正解した瞬間の経過時間を、performance.now()という時刻取得の仕組みで測っているだけ。
if (val === p.problem.ans) {
const dt = (performance.now() - p.t0) / 1000;
let tier = dt <= 2 ? 2 : dt <= 4 ? 1 : 0;
p.combo++;
const bonus = Math.min(2, Math.floor(p.combo / 3));
tier = Math.min(3, tier + bonus + (game.lastSpurt ? 1 : 0));
p.pending = tier;
日本語に直すと、こうなります。
| 正解までの時間 | インクの大きさ |
|---|---|
| 2秒以内 | 大 |
| 4秒以内 | 中 |
| それ以降 | 小 |
さらに3連続で正解すると、コンボとしてインクが1段階アップ。条件分岐と足し算の組み合わせだけで「速さがそのまま攻撃力になる」感覚を作っています。
塗る形が四角ではなく「ひし形」なのも、地味な工夫のひとつです。中心のマスから縦と横にどれだけ離れているか(この距離の測り方を「マンハッタン距離」と呼びます)を数えて、範囲内かどうかを判定しています。
function diamondCells(cx, cy, r) {
const out = [];
for (let dy = -r; dy <= r; dy++) for (let dx = -r; dx <= r; dx++) {
if (Math.abs(dx) + Math.abs(dy) > r) continue;
const x = cx + dx, y = cy + dy;
if (x >= 0 && x < N && y >= 0 && y < N) out.push(y * N + x);
}
return out;
}
「縦の移動距離+横の移動距離」が半径以下のマスだけを塗る対象に加える。それだけでスプラトゥーンのような塗り広がる気持ちよさが出るのは、正直ちょっと感動しました。

4択のひっかけは、ちゃんと考えて作られている
誤答の選択肢は、ランダムな数字を並べているわけではありません。「間違えそうな数字」をわざと混ぜてあります。
基本形は正解の「±1」「±2」「±10」。うっかり指がずれた時のミスを再現するような数字です。九九の問題では、もう一段仕掛けがあります。
} else if (meta.mult) {
pool.push((meta.a + 1) * meta.b, (meta.a - 1) * meta.b,
meta.a * (meta.b + 1), meta.a * (meta.b - 1),
ans + 1, ans - 1, ans + 10, ans - 10);
例えば「7×8」なら、正解の56だけでなく、48(6×8)や63(7×9)といった「隣の段・隣の数の答え」も候補に混ざります。九九を覚えたての子どもがよくつまずくポイントを、そのまま選択肢にしている形ですね。大人が解いても、一瞬指が止まる場面があります。
CPUの強さは、サイコロの重みでできている
CPU対戦モードの「よわい」「ふつう」「つよい」。この差、複雑な判断ロジックで作られていると思うかもしれません。正体は、数値の重みの違いだけです。
const CPU_DEF = [
{ name:"よわい", base:6.0, jit:2.0, tiers:[.7,.25,.05,0], errRate:.20, samples:1 },
{ name:"ふつう", base:4.0, jit:1.5, tiers:[.25,.55,.20,0], errRate:.10, samples:8 },
{ name:"つよい", base:2.5, jit:1.0, tiers:[.05,.35,.50,.10], errRate:.04, samples:20 },
];
見た目は暗号のようですが、意味は拍子抜けするほどシンプル。
- base:平均何秒で答えるか(よわいは6秒、つよいは2.5秒)
- tiers:小・中・大・特大のインクをどの確率で選ぶか
- errRate:わざと間違えて硬直する確率(よわいは20%、つよいは4%)
- samples:塗る場所を選ぶ前に、何箇所を下見して比較するか
地味に効いているのが、最後のsamples。「つよい」は20箇所を見比べてから一番良さそうな場所を選ぶのに対し、「よわい」はたった1箇所しか見ません。賢く見える動きの正体は、実は「たくさん下見してから選んでいるだけ」というわけですね。
わざと一定確率でミスをする設計も仕込んであります。狙いは、理不尽に強すぎるCPUにしないための保険でしょう。実際に動かして計測したところ、「つよい」CPUは9秒間でおよそ35マスを塗っていました。大人がLv3(九九)くらいの速さで対抗すると、ちょうど競り合う強さです。

これ、AIとの1日のバイブコーディングで作りました
ここまで紹介した仕掛けは、コードを1行ずつ手で書いていったわけではありません。Claude Code(AIによるコーディング支援ツール)に「こういうゲームを作りたい」「速さでインクの量を変えたい」と話しかけながら、少しずつ形にしていく——いわゆる「バイブコーディング」という作り方(内部リンク: バイブコーディング入門)。
正直に言うと、一発で思い通りに動いたわけではありません。塗る形が不格好だったり、CPUが強すぎたりして、動かしては直すの繰り返し。それでも企画から動くものができるまで丸1日というのは、自分でも少し驚きました。
プログラマーではない人でも、小さく試すところから始められる。そこがこのやり方の面白さです。「うちの業務にも、こういう小さなツールが使えないか」——そう感じた方は、一度話を聞かせてください。
まとめ
- 「陣取りペンキ」は576行・1枚のファイルだけで完結している
- 「速く解くほど大きく塗れる」は、経過時間を測って条件分岐しているだけ
- 誤答の選択肢は「間違えそうな数字」をわざと混ぜて作られている
- CPUの強さは、平均解答時間・インクの確率・下見する候補地点の数といった数値の違いだけで表現されている
- これら全部を、AIとの1日の対話(バイブコーディング)で組み立てた
「うちの業務にも、こういう小さな自作ツールが使えるかもしれない」——そう感じた方は、個別相談も承っています。まずはお問い合わせから、お気軽にご連絡ください。



コメント