マガジン

30分で作るすごろく:サイコロとマスのイベント

遊びをつくる

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

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

サイコロを振って進むだけのゲームは、運だけで決まります。それなのにすごろくが「遊び」として成り立つのは、出目のあとに何かが起きるからです。2マス進める、3マス戻される、一回休みになる。盤の上の小さな出来事が、同じ出目を良い知らせにも悪い知らせにも変えます。この記事では、乱数、移動、マスの効果という3つの部品を、20マスの盤で動かします。 手を動かす前に場面を考えてみます。すごろくを作ろうとして、まず見た目を整え、キャラクターを描き、アニメーションを付けたくなったとします。それらを足す前に、「出目→移動→マスの効果」の流れが数十行で動くかどうかを確かめておくと、あとの工夫が空回りしにくくなります。 用意するのはテキストエディタとブラウザだけです。外部ライブラリもサーバーも使わず、HTMLファイル1つで完結させます。時間配分の目安は、盤とサイコロに10分、マスの効果に10分、調整に10分です。

この記事の目次
  1. 最小の盤の仕様を先に決める
  2. 手順1:ファイルを作り、盤とサイコロを動かす
  3. コードの要点を3つに分けて読む
  4. 手順2:マスの効果を足して、盤を調整する
  5. 手順3:長さと偏りを、自分の手で数えて確かめる
  6. 起こりやすい失敗
  7. 次の行動

最小の盤の仕様を先に決める

作る前に、次の4つを決めておきます。ここが曖昧だと、あとでコードを何度も書き直すことになります。

  • 盤は0番から19番までの20マスとし、0番がスタート、19番がゴールです。
  • サイコロは1から6までの目が等確率で出ます。
  • ゴールにぴったり止まらなくても、超えたらゴールとします。
  • マスの効果は「止まったマスの分だけ」発動し、効果で移動した先のマスの効果は発動させません。

最後の取り決めは特に重要です。たとえば「2マス進む」マスの先が「3マス戻る」マスだと、効果が連鎖して堂々巡りが起きかねません。最初は連鎖なしにしておけば、動きを目で追いやすくなります。連鎖はあとから足せます。

効果の種類は、次の3つだけにします。

  • 進む(+2)、動き:位置が2増える、遊びへの影響:小さな幸運になります
  • 戻る(-3)、動き:位置が3減る、遊びへの影響:小さな不運になります
  • 一回休み、動き:次の手番を飛ばす、遊びへの影響:位置は変えず、順番だけ遅らせます

手順1:ファイルを作り、盤とサイコロを動かす

任意のフォルダに index.html という名前でファイルを作り、下のコードを貼り付けます。ファイル名を index.html にしておくと、あとで作品として投稿するときにそのまま入口として使えます。

html
<!doctype html>
<meta charset="utf-8">
<title>ミニすごろく</title>
<style>
body{font-family:sans-serif;max-width:480px;margin:1em auto}
#board{display:grid;grid-template-columns:repeat(5,1fr);gap:4px}
.c{border:1px solid #888;padding:6px;min-height:48px;font-size:12px}
.on{background:#fd6}
</style>
<h1>ミニすごろく</h1>
<div id="board"></div>
<p id="info">ボタンでサイコロを振ります。</p>
<button id="roll">振る</button>
<script>
const CELLS=[
 {t:"スタート"},{t:""},{t:"+2",d:2},{t:""},{t:"休み",skip:1},
 {t:""},{t:"-3",d:-3},{t:""},{t:"+2",d:2},{t:""},
 {t:""},{t:"休み",skip:1},{t:""},{t:"-3",d:-3},{t:""},
 {t:""},{t:"+2",d:2},{t:""},{t:""},{t:"ゴール"}
];
const GOAL=CELLS.length-1;
let pos=0,turns=0,rest=0,over=false;
function draw(){
 board.innerHTML=CELLS.map((c,i)=>
  `<div class="c${i==pos?" on":""}">${i}<br>${c.t}</div>`).join("");
}
roll.onclick=()=>{
 if(over)return;
 turns++;
 if(rest){rest=0;info.textContent=turns+"回目:お休みです";return;}
 const n=1+Math.floor(Math.random()*6);
 pos=Math.min(pos+n,GOAL);
 let msg=turns+"回目:出目"+n+"で"+pos+"マス";
 const c=CELLS[pos];
 if(c.d){pos=Math.max(0,Math.min(pos+c.d,GOAL));msg+="、"+c.t+"で"+pos+"マスへ";}
 if(c.skip){rest=1;msg+="、次は休み";}
 if(pos==GOAL){over=true;msg+="。ゴール!";}
 info.textContent=msg;draw();
};
draw();
</script>

ファイルをブラウザで開き、「振る」を押してみてください。黄色いマスが駒の位置です。数回押して、ゴールまでたどり着ければ最初の目標は達成です。

コードの要点を3つに分けて読む

動かしたあと、どこが何を担当しているかを確かめます。

乱数:`1+Math.floor(Math.random()*6)`

Math.random() は0以上1未満の小数を返します。6倍して切り捨てると0〜5の整数になり、1を足して1〜6にします。「6倍して1を足す」だけでは小数が残るので、切り捨ての Math.floor が必要です。この式を変えると、サイコロそのものを変えられます。たとえば *3 にすれば1〜3の目になります。

移動:`Math.min` と `Math.max` による範囲の固定

Math.min(pos+n, GOAL) は、ゴールを超えても盤の外に出ないようにします。戻る効果のほうは Math.max(0, …) で、スタートより手前に戻らないようにします。盤の端の処理を書き忘れると、駒が盤から消えたり、画面に何も表示されなくなったりします。

マスの効果:データとして持つ

効果は if の羅列にせず、CELLS の配列に d(移動量)や skip(休み)として持たせています。効果を足したいときは、配列の一要素を書き換えるだけで済みます。盤を作り替える作業が、コードの修正ではなくデータの編集になります。

手順2:マスの効果を足して、盤を調整する

ここからが遊びの設計です。次の順で試してみてください。

  1. CELLS の中の {t:""} を1つ選び、{t:"+4",d:4} に書き換えます。保存して再読み込みし、そのマスに止まったときの動きを見ます。
  2. 「ゴール直前に戻るマスを置く」と「スタート直後に進むマスを置く」を、それぞれ試します。同じ効果でも、置く位置で印象が変わるかどうかを見ます。
  3. 休みのマスを増やしたり減らしたりして、手番を飛ばされる頻度がどれくらいなら不満が出ないかを探ります。
  4. サイコロを *6 から *4 に変え、盤の長さとのバランスがどう動くかを見ます。

調整のつまみは、おおよそ次の4つに絞れます。

  • 良い効果と悪い効果の数の比
  • 効果マスを置く位置(序盤・中盤・終盤)
  • 盤の長さ(マスの総数)
  • サイコロの目の幅

一度に変えるのは1つだけにします。2つ以上を同時に変えると、何が効いたのか分からなくなります。

手順3:長さと偏りを、自分の手で数えて確かめる

「この盤は長すぎないか」「戻るマスが多すぎないか」は、数回遊んだ感覚だけでは判断しにくい問題です。そこで、同じルールを大量に回して数える方法を使います。

ブラウザの開発者ツールのコンソール(多くのブラウザで F12 キーから開けます)に、次のコードを貼り付けて実行します。手順1のページを開いた状態で行ってください。

js
function sim(n){const r=[];for(let i=0;i<n;i++){let p=0,t=0,s=0;
 while(p<GOAL){t++;if(s){s=0;continue}
  p=Math.min(p+1+Math.floor(Math.random()*6),GOAL);
  const c=CELLS[p];
  if(c.d)p=Math.max(0,Math.min(p+c.d,GOAL));
  if(c.skip)s=1}
 r.push(t)}return r}
const a=sim(1000);
console.log(Math.min(...a),Math.max(...a),a.reduce((x,y)=>x+y)/a.length);

sim(1000) は、ゴールまでにかかった手番数を1000回分集めます。出力される3つの数は、最短、最長、平均です。

記録の取り方

  • 単位はゴールまでの手番数(休みの手番も数えます)です。
  • 1回の実行で1000回分を集め、同じ盤で3回ほど実行します。実行ごとの数字のぶれを見るためです。
  • 盤を1か所変えるたびに、同じ手順で記録し直します。

結果の読み方

  • 3回の平均が大きくばらつくなら、1000回でも足りません。回数を増やします。
  • 平均がとても短ければ、盤が短すぎるか、進む効果が強すぎる可能性があります。効果マスを減らすか、盤を長くしてみます。
  • 最長が平均の何倍にもなるなら、戻るマスや休みのマスが重なる区間があるかもしれません。その区間の配置を見直します。
  • 目安の数字は、何手番ぐらいで終わってほしいかを先に決めてから比べます。決めずに眺めても、良し悪しは判断できません。

起こりやすい失敗

最後に、つまずきやすい点を挙げます。

  • 効果の連鎖を意図せず作る。 「+2」の移動先がたまたま「-3」のマスだと、連鎖なしの取り決めと実際の動きがずれます。盤を編集したら、効果マスの2マス先と3マス先が効果マスになっていないかを確認します。
  • シミュレーションのルールが本体とずれる。 sim は本体のルールの複製なので、本体を変えたら sim も直す必要があります。直し忘れると、数字だけが別のゲームの結果になります。
  • ボタンを押しても何も起きない。 コンソールにエラーが出ていないか確認します。多くは CELLS の書き換え時に、カンマや括弧が抜けたことが原因です。
  • 休みの表示が分かりにくい。 休みの手番では駒が動かないので、遊ぶ人には「壊れた」ように見えかねません。メッセージ欄に「お休みです」と出る現在の作りを、消さないようにします。
  • 運だけで決まることに悩みすぎる。 このゲームでは、プレイヤーは出目に何も介入できません。ここから先に面白さを足すなら、「振る前に2つのコースから選ぶ」「出目を2つ出して1つ選ぶ」など、判断を入れる設計が考えられます。それは次の試作の題材です。

次の行動

まず、手順1のコードを保存して、盤を1か所だけ書き換えてみてください。その盤でシミュレーションを3回実行して平均を比べると、自分の盤の癖が数字で見えてきます。納得できる形になったら、index.html を単体のHTMLとして、アソバースへ投稿することもできます。投稿は、入力、表示の確認、公開の3段階で進み、公開にはメールアドレスの確認が必要です。投稿前に、現在の案内でアップロードの条件を確認しておくと安心です。