マガジン

数を分ける遊び:合計が同じになる二つの山

遊びをつくる

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

分かれ道の前後に駒と橋を置き、サイコロの運とプレイヤーの選択を表したイラスト。
記事のテーマをもとに制作したAI生成イラスト。

「3、5、7、8、11、14、2、6」の8枚のカードを、合計が等しくなるように二つの山へ分けられるでしょうか。紙に書いて試すと、最初の数分は「14を片方に置いて、残りで14に近づける」といった当てずっぽうになりがちです。ところが、カードを手で動かせる画面にすると、同じ問題の手触りが変わります。 この記事で確かめたいのは、同じ「合計が等しい二つの山」の問題でも、数の並びや画面の見せ方で、遊びとしての難しさと面白さがどう変わるかです。数学では「分割問題」と呼ばれる題材です。この記事は結果を報告するのではなく、読者が自分で数人に遊んでもらい、答えを出すための実験の設計書です。

この記事の目次
  1. 問いの立て方:分けられることと、分けやすいことは別
  2. 準備するもの:カードを振り分ける最小の試作
  3. 手順:四つのセットと二つの表示
  4. 記録の取り方と結果の読み方
  5. 起こりやすい誤り
  6. 次の行動

問いの立て方:分けられることと、分けやすいことは別

分割問題は、数の集まりを二つに分けて合計を等しくできるか、という問題です。ここで押さえておきたい事実が二つあります。

  • 全部の合計が奇数なら、二つの山の合計は等しくなりません。整数を足した二つの合計が同じなら、全体は偶数になるからです。
  • 合計が偶数でも、分けられるとは限りません。たとえば大きな数が一枚だけ飛び抜けていて、その数が全体の半分を超えるなら、その山には勝てません。

つまり「解がある」だけでは、ゲームとして成立するかどうかは決まりません。この実験では、次の三つの問いを順に確かめます。

  1. 数の並びが違うと、遊ぶ人が完成までにかける時間と操作の回数はどう違うか。
  2. 「二つの山の差」を表示する場合としない場合で、遊び方はどう変わるか。
  3. 解のないセットに出会った人は、どの時点で、どんな理由で手を止めるか。

準備するもの:カードを振り分ける最小の試作

用意するのは、ブラウザで開ける一枚のHTMLと、記録用の紙(または表計算)、時計です。下のHTMLは、全部のカードがまず左の山に置かれ、押すたびに左右を行き来します。二つの山の差が0になると完成です。

html
<!doctype html>
<meta charset="utf-8">
<title>二つの山</title>
<style>
body{font-family:sans-serif;max-width:480px;margin:1em auto}
.row{display:flex;gap:8px;flex-wrap:wrap;min-height:48px;border:1px solid #888;padding:6px;margin:6px 0}
button{font-size:18px;padding:8px 12px}
</style>
<p>カードを押すと左右の山を行き来します。</p>
<div>左の合計:<b id="sl">0</b></div><div class="row" id="L"></div>
<div>右の合計:<b id="sr">0</b></div><div class="row" id="R"></div>
<p id="msg"></p>
<script>
const cards=[3,5,7,8,11,14,2,6]; // セットを書き換える
const SHOW_DIFF=true;            // 差の表示を切り替える
const side=cards.map(()=>0);
let moves=0,t0=0,done=false;
function draw(){
 L.innerHTML="";R.innerHTML="";
 const s=[0,0];
 cards.forEach((v,i)=>{
  s[side[i]]+=v;
  const b=document.createElement("button");
  b.textContent=v;
  b.onclick=()=>{if(done)return;if(!t0)t0=Date.now();side[i]=1-side[i];moves++;draw();};
  (side[i]?R:L).appendChild(b);
 });
 sl.textContent=s[0];sr.textContent=s[1];
 const d=Math.abs(s[0]-s[1]);
 msg.textContent=SHOW_DIFF?"差: "+d:"";
 if(d===0){done=true;msg.textContent="そろいました 操作"+moves+"回 "+Math.round((Date.now()-t0)/1000)+"秒";}
}
draw();
</script>

次に、セットの解の数を調べる関数です。ブラウザの開発者ツールのコンソールに貼って使います。カードは20枚程度までなら、全部の振り分けを試しても現実的な時間で終わります。

js
function countSplits(a){
 const total=a.reduce((x,y)=>x+y,0);
 if(total%2) return 0;
 let n=0;
 for(let m=0;m<(1<<a.length);m++){
  let s=0;
  a.forEach((v,i)=>{ if(m>>i&1) s+=v; });
  if(s*2===total) n++;
 }
 return n/2; // 左右を入れ替えた同じ分け方を一つと数える
}

使うセットは四つです。合計は検算済みです。

  • A、カード:3, 5, 7, 8, 11, 14, 2, 6、合計:56、狙い:大きさがばらつく標準のセット
  • B、カード:7が8枚、合計:56、狙い:数がそろい、4枚ずつで分けられる
  • C、カード:3, 5, 7, 8, 11, 14, 2, 5、合計:55、狙い:合計が奇数で、解がない
  • D、カード:2, 4, 6, 8, 10, 12, 14, 100、合計:156、狙い:合計は偶数だが、解がない

Dは、100を含む山が100以上になる一方、100を含まない山は最大でも56にしかなりません。半分の78には届かないので、分けられません。実験の前に、countSplits で四つのセットの解の数を必ず確認してください。Cは0、Dも0になるはずです。AとBは1以上になります。

手順:四つのセットと二つの表示

遊んでもらう人を数人集めます。人数に決まりはありませんが、一人だけでは個人差と数の並びの差を区別できません。

  1. 1人につき、A、B、C、Dの順序をずらして割り当てます。たとえば1人目はA→B→C→D、2人目はB→C→D→A、のように回します。順序で慣れが生じるのを、ならすためです。
  2. 差の表示ありと、なし(SHOW_DIFF=false)は、同じ人が同じセットを二度遊ぶのを避けるため、人ごとに分けます。偶数番目の人は表示あり、奇数番目の人は表示なし、といった割り振りにします。
  3. 1セットにつき上限を3分とします。完成しなくても3分で止めてもらいます。
  4. 開始前に、解がないセットが混ざる可能性を伝えるかどうかを決め、全員に同じ説明をします。伝えるか伝えないかで、手の止め方が変わるためです。
  5. 全部終わってから、CとDに解がなかったことを伝えます。長く悩んだ人への配慮にもなります。

記録の取り方と結果の読み方

1回の挑戦ごとに、次の項目を1行にまとめます。

人 / セット / 差の表示 / 完成の秒数 / 操作回数 / 終了の理由 / 気づき

「終了の理由」は、完成、上限、自分で諦めた、のどれかを書きます。「気づき」には、遊んだ人が口にした言葉をそのまま書き留めます。「奇数だから無理」といった発言があれば、その時点の秒数も添えます。

結果は次のように読みます。

  • AとBの比較。 同じ合計でも、Bのほうが速く終わる人が多いなら、難しさは合計の大きさより、数のばらつきから来ていると考えられます。AとBの差が小さいなら、カードの枚数のほうが効いている可能性があります。枚数を変えたセットを追加して確かめます。
  • 差の表示あり・なし。 表示ありで操作回数が多く時間が短ければ、触って確かめる遊びになっています。表示なしで操作回数が少なく時間が長ければ、頭の中で計算する遊びになっています。どちらが目指す体験かは、作る人が決めます。
  • Cでの行動。 合計が奇数だと気づいた人が出たなら、「偶数かどうか」というルールの発見が遊びの一部になっています。誰も気づかないまま上限まで続けたなら、合計の表示や、気づきを促す演出が必要です。
  • Dでの行動。 奇数・偶数に気づいた人でも、Dでは詰まるはずです。ここで「100が大きすぎる」と言う人が出るかを見ます。出なければ、解がないことを遊び手に伝える手がかり(最大のカードが全体の半分を超える、といった情報)をゲームが用意する余地があります。
  • 操作回数の目安。 全カードが左に置かれた状態から始まるため、一つの解を作るのに最低限必要な操作は、右へ移すカードの枚数です。実際の操作回数がそれを大きく超えるほど、試行錯誤が多かったと読めます。

起こりやすい誤り

  • 同じ人に同じセットを二度遊んでもらう。 一度解いた配置は覚えてしまうので、二回目の数字は使えません。
  • 計時の起点がずれる。 試作では最初のカードを押した時点から計ります。カードを眺めて考えた時間は含まれません。眺めていた時間が長そうな人には、気づきの欄にその様子を書いておきます。
  • 解の数の確認を省く。 手で作ったセットは、解の有無を思い違いやすいものです。countSplits の確認を必ず挟んでください。
  • 記録を画面に頼る。 投稿したアップロード作品の中の記録は、画面を開いている間の一時メモリーとして扱われます。実験の記録は、紙や表計算に手で書き留めます。
  • 解のない問題で長く悩ませる。 上限を守り、終わったらすぐに種明かしをします。

次の行動

まず、上のHTMLを保存してブラウザで開き、Aのセットを自分で解いてみてください。次に、同じカードで差の表示を消し、どちらが遊びとして面白いかを数人に尋ねて記録します。そこから、解を先に作って数を割り振る「逆向きの問題づくり」や、countSplits で解の数を調べて難易度を並べる方法に進めます。一枚のHTMLにまとまっているので、アソバースへの投稿にも向いた形です。