三目並べはなぜ必ず引き分けになるのか:全部の手を数える
遊びを見つける執筆にClaudeを使用しています。

三目並べを何度か遊ぶと、勝負がつかなくなる時期が来ます。お互いに相手の「あと1つ」を止めるようになり、盤が埋まって引き分けになる場面を考えてみます。それは偶然ではなく、このゲームの構造から決まっていることです。ただし「必ず引き分け」には条件があります。どちらも最善の手を打ち続けた場合に限り、片方がひとつでも間違えれば勝敗はつきます。 この記事で確かめるのは、「最善同士なら引き分け」を自分の手で確認する方法です。盤面に番号を振り、手の総数を数え、対称な局面をまとめ、先手の最初の一手から枝を追います。最後に短いプログラムで全部の手を機械に数えさせます。結果の数字はこの記事には書きません。数えた結果を自分の目で見ることが、いちばんの納得につながるからです。
盤面に番号を振り、勝ちの線を数える
まず九つのマスに番号を振ります。左上から右へ1・2・3、次の段が4・5・6、最下段が7・8・9です。中央は5、四隅は1・3・7・9、辺の中央は2・4・6・8になります。先手を×、後手を○と呼びます。
勝ちの線は8本です。横が1-2-3、4-5-6、7-8-9、縦が1-4-7、2-5-8、3-6-9、斜めが1-5-9、3-5-7です。この8本を紙に書き出しておくと、以降の確認がすべて楽になります。
マスごとに、そのマスを通る線の本数を数えてみてください。中央は4本、隅は3本、辺は2本になるはずです。中央が最も強いと言われるのは、通る線が多く、相手の線も多く止められるからです。
全部の手を数える:上限と、縮め方
「全部の手」は、先手が9通り、後手が8通り、先手が7通りと続けて、掛け算で上限が出ます。9×8×7×6×5×4×3×2×1で362,880通りです。ただし実際の数はこれより少なくなります。最短で5手目に先手が3つそろえば、その後の枝は存在しないからです。決着が早くつく枝の分だけ、数は減ります。
実際の数を手で数えるのは現実的ではありません。そこで局面を同じ形にまとめます。盤を90度回しても、左右や上下に裏返しても、ゲームの性質は変わりません。この性質を使うと、先手の最初の手は次の3種類にまとまります。
- 中央、マス:5、個数:1
- 角、マス:1・3・7・9、個数:4
- 辺、マス:2・4・6・8、個数:4
九つの選択肢が、3つの代表を調べれば足りる形になります。後手の二手目も同じ方法でまとまります。
中央から始まる枝を追う
初手が中央(5)のときを追います。後手の応答は、角(1・3・7・9)と辺(2・4・6・8)の2種類です。角の4つは互いに同じ形で、辺の4つも同じです。代表をひとつずつ調べれば足ります。
後手が辺に置くと、先手が勝つ
後手が2に置いたとします。先手は7に置きます。5と7で3-5-7の線に王手がかかるので、後手は3を打つしかありません。ここが手順の要で、後手に選択肢がありません。3を打たなければ、先手が3に置いて勝ちます。
1:× 2:○ 3:○
4:. 5:× 6:.
7:× 8:. 9:.後手の2と3が1-2-3を狙うので、先手は1を打ちます。この1が、1-4-7(4が空き)と1-5-9(9が空き)の二つの王手を同時に作ります。後手が防げるのは片方だけなので、先手が勝ちます。つまり後手は、中央に対して辺に置くと負けます。
後手が角に置くと、引き分けの道がある
次に後手が1に置いた場合です。先手が3に打つと、後手は7が強制されます。後手の7が1-4-7を狙うので、先手は4が強制されます。ここから先も、どちらも相手の「あと1つ」を止める手だけが続きます。
1:○ 2:× 3:×
4:× 5:× 6:○
7:○ 8:○ 9:×手順は、×5、○1、×3、○7、×4、○6、×2、○8、×9です。勝ちの線8本を順に確かめると、どの線にも×と○が混ざり、そろっていません。引き分けです。
ここで追ったのは、先手が3を選んだ場合の1本の枝だけです。先手が3以外を選んだ枝も、同じ方法で広げてみてください。後手が毎回、相手の王手を止めるだけで引き分けに持ち込めるかどうかを確認します。ほかの初手(角、辺)でも、同様の作業が必要になります。
全部の手をコードで数える
手作業の確認を、機械にまかせます。次のコードを count.html として保存し、ブラウザで開きます。外部ライブラリは使いません。
<pre id="o"></pre>
<script>
const L=[[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]];
const win=b=>{for(const[a,c,d]of L)if(b[a]&&b[a]===b[c]&&b[a]===b[d])return b[a];return 0};
const c={X:0,O:0,draw:0};
function walk(b,t){
const w=win(b);
if(w){c[w]++;return}
if(!b.includes(0)){c.draw++;return}
for(let i=0;i<9;i++)if(!b[i]){b[i]=t;walk(b,t==='X'?'O':'X');b[i]=0}
}
function value(b,t){
const w=win(b);
if(w)return w==='X'?1:-1;
if(!b.includes(0))return 0;
let best=t==='X'?-2:2;
for(let i=0;i<9;i++)if(!b[i]){
b[i]=t;const v=value(b,t==='X'?'O':'X');b[i]=0;
best=t==='X'?Math.max(best,v):Math.min(best,v);
}
return best;
}
walk(Array(9).fill(0),'X');
const out=['先手勝ち '+c.X+' / 後手勝ち '+c.O+' / 引き分け '+c.draw];
for(let i=0;i<9;i++){
const b=Array(9).fill(0);b[i]='X';
out.push('初手'+(i+1)+' → '+value(b,'O'));
}
for(let j=1;j<9;j++){
const b=Array(9).fill(0);b[0]='X';b[j]='O';
out.push('初手1・後手'+(j+1)+' → '+value(b,'X'));
}
document.getElementById('o').textContent=out.join('\n');
</script>1行目の出力は、勝負がつくまでのすべての手順を、先手勝ち・後手勝ち・引き分けに分けた件数です。ここで数えるのは手順の本数で、局面の数ではありません。
その後の行の数字は、お互いが最善を尽くしたときの結果です。1なら先手勝ち、0なら引き分け、-1なら後手勝ちを表します。
結果の読み方と、起こりやすい誤り
出力は次のように読みます。
- 「初手」の9行がすべて0なら、先手がどこから始めても、最善同士では引き分けです。これが「必ず引き分け」の中身です。
- 「初手1・後手〇」の8行は、先手が角から始めたときの後手の応答を比べています。0になる応答が少ないほど、後手の選択肢が狭いことを意味します。0にならない応答では、後手が負ける手順が見つかります。
- 前の章で手作業で追った「中央に辺で応じると負ける」を確かめるには、b[0]='X' を b[4]='X'、b[j]='O' の j を辺のマスに書き換えて実行します。
数え間違いが起こりやすい点も挙げておきます。
- 手順と局面を混ぜる。 同じ盤の形に、異なる順序で到達できます。コードの1行目は手順を数えているので、局面の数とは一致しません。
- 決着後も数え続ける。 3つそろった後の手を足すと、数が増えてしまいます。コードの walk は、そろった時点で打ち切っています。
- 回転した形を別物として数える。 手作業では、まとめた代表だけを調べ、数え直さないようにします。
- 引き分けの意味を取り違える。 盤が埋まれば引き分けですが、これは「最後まで打ち続けた」ことを意味しません。そろっていないことを確かめる必要があります。
最後に、「ゲームとして成立しない」の意味を整理します。最善同士なら結果が最初から決まっている、という意味では、三目並べは「解けているゲーム」です。大人が遊んで退屈に感じるのはこのためです。ただし人間同士の対戦は、いつも最善とは限りません。子どもが勝ちの形を見つけて喜ぶ場面は、最善でない手があるからこそ成り立っています。
次の行動
まず紙に9マスを書き、中央に×を置いて、後手の角と辺の応答を自分の手で追ってみてください。次にコードを実行し、手作業の結論と出力が一致するかを見比べます。もしゲームを作る側に回るなら、結果が最初から決まっていないルールの工夫を考える出発点になります。HTMLファイルひとつで作れるので、できた作品はアソバースにアップロードして公開することもできます。

