マガジン

30分で作る数字並べ:1から順に押す反応ゲーム

遊びをつくる

執筆にClaudeを使用しています。

増えすぎた部品を脇へよけ、一周できる小さなゲームのコースを完成させる手元。
記事のテーマをもとに制作したAI生成の写真風イメージ。

画面にばらばらに並んだ1から25までの数字を、小さい順に押していくだけのゲームです。ルールは数秒で説明できますが、遊ぶと「次の数字がどこにあるか」を探す時間と、見つけて押す時間が混ざって、意外と手が止まります。この記事は、その小さな遊びを1枚のHTMLファイルで作る手順です。 題名の「30分」は、作業を4つに分けたときの目安の配分です。ファイルを用意して盤面を出す、押す順番の判定を入れる、計測を付ける、調整して確認する、という順に進めます。進み具合には個人差があるので、30分で終わらなくても問題ありません。 このゲームの面白さの核は「計測」にあります。数字を並べるだけなら作業ですが、秒数とミス数が出ると「もう一回」が生まれます。そこで、計測の表示と、ミスの扱い方(罰則の付け方)を中心に組み立てます。外部ライブラリは使わず、ブラウザだけで動く最小構成にします。

この記事の目次
  1. 作るものと、先に決めておく3つのこと
  2. 手順1:ファイルを作る(0〜5分)
  3. 手順2:盤面と判定の仕組みを読む(5〜15分)
  4. 手順3:計測を整える(15〜22分)
  5. 手順4:自分の手で調整して確かめる(22〜30分)
  6. つまずきやすい点
  7. 次の行動

作るものと、先に決めておく3つのこと

最初に、遊びの仕様を3つだけ決めます。後から変えられますが、決めておくと手が止まりません。

  • 盤面:5×5のマスに1〜25をランダムに配置します。
  • 判定:いま押すべき数字(次の数字)だけが正解です。違う数字を押すとミスになります。
  • 記録:クリアまでの秒数と、ミス数を表示します。ミス1回につき2秒を加えた値を最終スコアにします。

ミスに秒数を足す方式にするのは、「焦って連打する」遊び方と「慎重に探す」遊び方の釣り合いを取るためです。罰則がないと、ミスしても損がないので、当てずっぽうに押し続けるのが最善になってしまいます。何秒足すのがちょうどよいかは後半で自分の手で確かめます。

手順1:ファイルを作る(0〜5分)

  1. テキストエディタで新しいファイルを作り、名前を index.html にします。アソバースへ投稿するときも、入口のファイル名はこの名前にします。
  2. 下のコードをそのまま貼り付けて保存します。後の手順では、この中のどこが何を担当しているかを順に見ていきます。
  3. ファイルをブラウザで開き、「開始」を押して、数字の盤面が出れば最初の確認は完了です。
html
<!doctype html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>数字並べ</title>
<style>
body{font-family:sans-serif;text-align:center;margin:0;padding:12px}
#grid{display:grid;grid-template-columns:repeat(5,1fr);gap:6px;max-width:400px;margin:12px auto}
button{font-size:1.4rem;padding:14px 0;border:2px solid #345;border-radius:8px;background:#fff;touch-action:manipulation}
button.done{visibility:hidden}
button.miss{background:#fcc}
</style>
<h1>数字並べ</h1>
<p id="info">開始を押すと1から順に押します</p>
<button id="start">開始</button>
<div id="grid"></div>
<script>
const N=25;
let next=1,t0=0,miss=0,best=null,timer=null;
const grid=document.getElementById('grid');
const info=document.getElementById('info');
function shuffle(a){for(let i=a.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]];}return a;}
function start(){
  next=1;miss=0;grid.innerHTML='';
  const nums=shuffle(Array.from({length:N},(_,i)=>i+1));
  nums.forEach(n=>{const b=document.createElement('button');b.textContent=n;b.onclick=()=>press(b,n);grid.appendChild(b);});
  t0=performance.now();
  clearInterval(timer);timer=setInterval(show,100);show();
}
function show(){
  const s=((performance.now()-t0)/1000).toFixed(1);
  info.textContent='次は '+next+' / '+s+'秒 / ミス '+miss;
}
function press(b,n){
  if(n!==next){miss++;b.classList.add('miss');setTimeout(()=>b.classList.remove('miss'),200);show();return;}
  b.classList.add('done');next++;
  if(next>N)finish();
}
function finish(){
  clearInterval(timer);
  const t=(performance.now()-t0)/1000;
  const score=t+miss*2;
  if(best===null||score<best)best=score;
  info.textContent='完了 '+t.toFixed(2)+'秒 + ミス'+miss+'×2秒 = '+score.toFixed(2)+'秒(ベスト '+best.toFixed(2)+')';
}
document.getElementById('start').onclick=start;
</script>

手順2:盤面と判定の仕組みを読む(5〜15分)

貼り付けたコードを、3か所に分けて読み取ります。自分の好みに変えたいときの手がかりになります。

並べ替え(shuffle関数)

1〜25の配列を、後ろから順に「ランダムに選んだ位置の要素と入れ替える」ことで混ぜています。フィッシャー–イェーツ法と呼ばれる定番のやり方です。sort にランダムな比較関数を渡す書き方は手軽ですが、偏りが出やすいことが知られているため、この方法を使っています。

判定(press関数)

変数 next が「いま押すべき数字」です。押したボタンの数字が next と同じなら、そのボタンに done クラスを付けて見えなくし、next を1つ進めます。違えばミス数を増やし、0.2秒だけ赤くします。

ボタンを消すとき、display:none ではなく visibility:hidden にしています。前者だと残りのボタンが詰めて並び直され、数字の位置が毎回ずれて探しにくくなるからです。場所を保ったまま消えることで、「残りの数字を目で追う」遊びになります。

終了条件

next が25を超えた時点で finish を呼びます。「25個押した」ではなく「次の数字が範囲を超えた」で判定しているので、N を変えれば盤面の大きさを変えても動きます。ただし、盤面の列数は CSS の repeat(5,1fr) にも書かれているので、こちらも合わせて直します。

手順3:計測を整える(15〜22分)

このゲームの面白さを決めるのは、計測の見せ方です。コードでは次の3つを行っています。

  1. performance.now() で開始時刻を記録し、100ミリ秒ごとに経過秒数を画面に出します。Date.now() より細かく、システム時計の変更の影響を受けにくい時計です。
  2. ミスは回数として数え、最終スコアに「ミス数×2秒」を加えます。
  3. ベストスコアを変数 best に持ち、更新されたときだけ値を差し替えます。

ここで注意したいのは、best が「ページを開いている間だけ」の記録だという点です。再読み込みすると消えます。ブラウザの保存機能を使えば残せますが、アソバースにアップロードした作品内の localStorage や sessionStorage は、一時メモリーとして扱われる互換処理です。続けて遊んだときの記録は画面を開いている間だけ、と割り切るのが安全です。ログイン中のクラウドセーブをこのゲームで使えるかどうかは、現在の案内で確認してください。

手順4:自分の手で調整して確かめる(22〜30分)

ここからは、読者自身が計測して決める部分です。ミスの罰則や盤面の大きさは、設定ひとつで遊び心地が大きく変わります。次のように比べてみます。

比べる条件

  • ミスの罰則:0秒、2秒、5秒の3通り(コードの miss*2 の数字を書き換えます)。
  • 盤面の大きさ:4×4、5×5、6×6の3通り。

一度に両方を変えると原因が分からなくなるので、まず罰則だけを変え、次に盤面だけを変えます。

記録の取り方

  1. 1回のプレイを1行として、紙かメモに「条件、クリア秒数、ミス数」を書きます。
  2. 同じ条件を5回ずつ遊びます。最初の1〜2回は盤面に慣れる途中なので、集計では外すと決めておきます。
  3. 条件ごとに、秒数の最小値と最大値、ミス数の平均を並べます。

結果の読み方

  • 罰則0秒でミスが多く、秒数が短いなら、当てずっぽうが有利になっています。罰則を重くする目安です。
  • 罰則5秒でほぼ全員がミスを避けて遅くなるなら、重すぎて冒険しにくい状態です。
  • 同じ条件でも秒数の差が大きいときは、盤面の運(数字の並び)の影響が大きいサインです。回数を増やすか、同じ配置を再挑戦できる仕組みを加えることを考えます。
  • 慣れた後も秒数が縮み続けるなら、「もう一回」が続く遊びになっています。

自分ひとりの記録では偏りが出るので、家族や友人に遊んでもらえるなら、その人の最初の数回の動きも見ておきます。どこで手が止まるかが、盤面の設計への最大のヒントになります。

つまずきやすい点

  • 数字を押しても反応しない:<script> が閉じられていない、またはコードの貼り付け漏れが多いです。ブラウザの開発者ツールのコンソールにエラーが出ていないかを見ます。
  • スマホで拡大されてしまう:ボタンを素早く連続で押すと、ダブルタップで拡大されることがあります。コードの touch-action:manipulation がそれを抑えます。
  • 押し心地を速くしたい:onclick を onpointerdown に変えると、指を離す前に反応するので少し速くなります。そのぶん、誤タップも増えます。罰則と合わせて、手順4と同じ方法で確かめます。
  • 画面が小さくて押しにくい:max-width:400px と padding:14px 0 を調整します。盤面を大きくするほど、探す時間より押す時間が長くなります。

次の行動

まずコードを保存して、25個すべてを押し切るところまで遊んでみます。そのうえで、ミスの罰則を0秒・2秒・5秒に変えて、各5回ずつ記録を取ってみます。納得のいく遊び心地になったら、アソバースの投稿画面に index.html をアップロードします。入力、表示の確認、公開の3段階があり、公開にはメールアドレスの確認が必要です。ファイルは20MiB以内で、入口は直下の index.html にします。