Books · Solving Puzzles through Mathematical Programming
Chapter 23
Nim
Nim is the oldest game with a known perfect strategy. Two players face several piles of objects; they move in turn, and a move is to take any number of objects, at least one, from a single pile. Take the last object and you win. Charles Bouton settled the game completely in : write each pile size in binary, add the columns without carrying, and the player to move loses exactly when every column comes out even. That column sum without carry is the bitwise exclusive-or of the pile sizes, now called the nim-sum, and the whole of strategy is to hand your opponent a position whose nim-sum is zero. The rule is so clean that it reads less like a heuristic than like a law, and it is the kind of law an integer program can be made to obey. Chlond and Akyol gave the formulation in the INFORMS Transactions on Education puzzle column; the modelling lesson is how to make a solver respect a constraint about binary digits.
The position
Four piles hold one, two, four, and eight objects, as in Figure 23.1. It is your move. The nim-sum is the exclusive-or of the four sizes; in binary each pile lights a single distinct bit, so the four together light every bit up to the fourth, and the nim-sum is fifteen, not zero. The position is therefore won for the player to move, and the task is to find the move that wins: reduce one pile so that the nim-sum of the four becomes zero.
The model
Variables
Let the current pile sizes be . For each pile let be its size after the move, and let flag the single pile that changes. To reason about the nim-sum we expose the binary digits: with bits enough to hold the largest pile, let be bit of , and let be an integer used to force a column to be even.
Constraints
A move touches exactly one pile, and that pile strictly shrinks while the others stay put: The digits must be the genuine binary expansion of the new size, and the winning condition is that every binary column sums to an even number, which is exactly nim-sum zero: There is no objective. A feasible point is a winning move; if the program is infeasible, no winning move exists and the position is already lost for the player to move, which Bouton’s theorem says happens precisely when the current nim-sum is zero.
The solver
The model goes straight to CP-SAT. The even-column constraint is the whole trick: it linearises the exclusive-or that defines the nim-sum, so the solver never has to reason about bit arithmetic directly, only about parity.
from ortools.sat.python import cp_model
def winning_move(piles):
n = len(piles)
B = max(piles).bit_length()
m = cp_model.CpModel()
y = [m.NewIntVar(0, piles[i], f"y{i}") for i in range(n)]
d = [m.NewBoolVar(f"d{i}") for i in range(n)]
m.Add(sum(d) == 1)
for i in range(n):
m.Add(y[i] == piles[i]).OnlyEnforceIf(d[i].Not())
m.Add(y[i] <= piles[i] - 1).OnlyEnforceIf(d[i])
z = {(i, b): m.NewBoolVar(f"z{i}_{b}")
for i in range(n) for b in range(B)}
for i in range(n):
m.Add(y[i] == sum((1 << b) * z[i, b] for b in range(B)))
for b in range(B):
w = m.NewIntVar(0, n // 2, f"w{b}")
m.Add(sum(z[i, b] for i in range(n)) == 2 * w)
s = cp_model.CpSolver()
if s.Solve(m) not in (cp_model.OPTIMAL, cp_model.FEASIBLE):
return None
return tuple(s.Value(y[i]) for i in range(n))
On the piles the solver returns : the only winning move is to take a single object from the pile of eight, leaving seven, shown as the copper ring in Figure 23.1. It is unique. The nim-sum’s top bit, the fourth, is lit by the pile of eight alone, so that pile is the only one whose size the move can lower without first having to raise another. After the move the piles are , every binary column holds an even count, and the position is handed back with a nim-sum of zero.
The misère turn
Bouton solved a second version in the same paper. In misère Nim the player who takes the last object loses, and the optimal play is identical to the ordinary game right up to the endgame: play to leave a nim-sum of zero until your move would leave only piles of a single object, and at that moment leave an odd number of them instead. The integer program adapts by switching the endgame condition once the heaps thin out, but the engine, the even-column constraint that pins the nim-sum, is the same. That a game this old yields entirely to a few parity constraints is the point worth carrying away: the binary representation is not a computational convenience laid over the game, it is the structure of the game, and an integer program that respects the digits inherits the whole theory for free.
Sources. The integer-programming treatment follows Martin J. Chlond and Oguz Akyol, A Nimatron, INFORMS Transactions on Education volume , number (), pages –. The complete theory of the game is Charles L. Bouton, Nim, a Game with a Complete Mathematical Theory, Annals of Mathematics volume (–), pages –, which introduced the nim-sum and the binary winning rule. The chapter’s title recalls the Nimatron, the relay machine built by Edward Condon for Westinghouse that played Nim against visitors to the New York World’s Fair (U.S. patent , “Machine to Play Game of Nim,” granted September ). The position solved here is a re-authored instance with a unique solution.