数取りゲームは、シンプルなルールながら奥深い戦略性を持つ「数を取り合うゲーム」です。この記事ではきっと忘れてしまったルールの解説と一般化、プログラミング実装例までの解説をしていきます。
この記事ではAIを活用しておりますが全体の編集等は人間が行っておりますので基本的にミスはないと思いますがあった場合はコメントで教えていただけると幸いです。
数取りゲームとは…
数取りゲームとは2人以上(明確には2人らしい)のプレイヤーが交互に1からkまでの数を取り合い、最後に目標の数Nを取った側または言った側の勝利となります。
単純に思うかもしれませんが意外と難しかったりします。
基本ルール
1.初期設定
目標数 N(例:21)と、一手で取れる最大数 k(例:3)を決める。
2.手番の進行
プレイヤー A→プレイヤー B の順に交互に手番を行う。複数人の場合は連続にならないようにする。
3.数の取り方
手番では「1~k」のいずれかの数を選び、その数を現在の合計に加算する(あるいは取り除く)。
4.勝利条件
最終的に合計がちょうどNになった(あるいは N を宣言した)プレイヤーが勝利。(ルールによって「言った側が負け」など変則もあります。)
簡単に言えばこんな感じです。
プレイヤーは交互に「1~3」までの数字を言って、その数字を合計していきます。
たとえば、最初に「2」と言ったら合計は2、次に相手が「3」と言ったら合計は5、というふうに増えていきます。
「21」を言った人が勝ち!(ルールによっては負けの場合もありますが、ここでは言ったら勝ちとします)
勝つためのポイントは、相手に「秘密の数字」を言わせないようにすることです。その「秘密の数字」とは…
「1+(ゲームで一度に言える最大数字)」のくり返し
ここでは「1+3」で「4」のくり返し、つまり「4、8、12、16、20」が大事な数字です。
なぜ「4のくり返し」が強いの?(戦略を簡単に解説!)
- あなたが「1」を言うと合計は1(スタート!)
- 相手が何を言っても、たとえば相手が「1」を言えば合計は2、「3」を言えば合計は4になります。
- 次はあなたの番なので、
- 合計が2なら「2」を言って4にする
- 合計が3なら「1」を言って4にする
- 合計が4ならもう4だから次の「8」を目指す…というように、いつも「4」「8」「12」「16」「20」に合わせられます。
こうすると、最後の「20」の次は相手が何を言えても、あなたが必ず「21」を言うことができて勝ちます!
戦略解説(難しいやつ)
ここの解説はまじで何言ってるの?となる場合もありますのでご注意ください。
PポジションとNポジション
- Nポジション(Next-player win)
次の手番のプレイヤーが必勝戦略を持つ状態。 - Pポジション(Previous-player win)
前手番のプレイヤーが必勝戦略を持つ状態。
解析は、合計 x における状態を P/N に分類することで進めます。
- 合計が N ならゲーム終了(前手番勝利)。
- 合計 x の手番で、いずれかの一手(1~k)を取って Pポジションへ遷移できれば、x は Nポジション。
- すべての一手が Nポジションへしか行けなければ、x は Pポジション。
具体例(N=21, k=3):
- 21 → ゲーム終了(Pポジション)
- 20,19,18 → いずれも21(P)へ行けるので Nポジション
- 17 → 18,19,20 はすべて N なので 17 は Pポジション
- 以下、17,13,9,5,1 が Pポジションの列となるため、最初に合計を「1」にできれば必勝、という方針が得られます。
必勝法の発見
上記の解析から、合計を (k+1) の倍数+1 に保つように操作すれば、相手が何を取っても次に必ずまたその形に戻せます。
例:k=3 の場合、4の倍数+1(1,5,9,13,17,21)が Pポジションとなり、自分が最初に「1」を言えれば必勝です。
実際のプレイ例
あなた:「1」 → 合計1
相手:「2」 → 合計3
あなた:「1」 → 合計4
相手:「3」 → 合計7
あなた:「1」 → 合計8
相手:「2」 → 合計10
あなた:「2」 → 合計12
相手:「1」 → 合計13
あなた:「3」 → 合計16
相手:「3」 → 合計19
あなた:「1」 → 合計20
相手:「1」「2」「3」のどれを言んでも、次にあなたが「21」を言って勝ち!
プログラミング実装例
. 目的と全体像
- 合計を
0
からスタートし、プレイヤー同士が交互に「1~k」のいずれかを取っていくゲームをシミュレート。 - 各状態(現在の合計)が「次に打てる手番の勝敗」を示すか(Nポジション)、「前手番の勝敗」を示すか(Pポジション)を判定し、最適手を自動で推薦する。
コードイメージ(Python)
def compute_positions(N: int, k: int) -> list[bool]:
"""
合計xがNポジション(True)かPポジション(False)かを計算する。
- Nポジション: 次の手番で必勝
- Pポジション: 前の手番で必勝
"""
# pos[x] = True (Nポジション), False (Pポジション)
pos = [False] * (N + 1)
# 合計がNになったらゲーム終了。前手番の勝ち=Pポジション
pos[N] = False
# x=N-1 から 0 へ遡って判定
for x in range(N - 1, -1, -1):
# 1手で到達できる先に Pポジション があれば、自分はNポジション
pos[x] = any(
(x + move <= N) and (not pos[x + move])
for move in range(1, k + 1)
)
return pos
def choose_move(current: int, N: int, k: int, pos: list[bool]) -> int:
"""
現在の合計 current から取るべき数を返す。
最初にPポジションに移れる手があればそれを返し、
なければ1を返す(とりあえずのフォールバック)。
"""
for move in range(1, k + 1):
if current + move <= N and not pos[current + move]:
return move
return 1 # 安全策
def simulate_game(N: int = 21, k: int = 3):
"""
コンピュータ同士で自動対戦。交互に choose_move を呼んで
勝者を表示する。デバッグや動作確認用。
"""
pos = compute_positions(N, k)
current = 0
turn = 0 # 0: プレイヤーA, 1: プレイヤーB
history = []
while current < N:
move = choose_move(current, N, k, pos)
current += move
history.append((turn, move, current))
if current == N:
winner = "A" if turn == 0 else "B"
break
turn ^= 1 # 手番交代
# 結果表示
for t, mv, total in history:
print(f"Player {'A' if t==0 else 'B'}: +{mv} → 合計 {total}")
print(f"勝者: Player {winner}")
まとめ
数取りゲームは、シンプルかつ視覚的に楽しめる一方で、深い数学的原理を含む奥の深いゲームです。P/Nポジションを理解し、最初に適切な数を取ることで必勝法が導かれます。プログラミングや教育、さらには研究へと応用範囲は広く、ぜひ一度自分で手を動かして解析や実装にチャレンジしてみてください。
コメント