Combinatorial game theory studies two-player games with perfect information (no hidden state, so chess yes, poker no), no chance (no dice or cards, so no Monopoly), alternating turns and finite termination. Under normal play the player who cannot move loses; under misère play that player wins. Games that can go on forever (draws) are in Loopy Games; games with scores in Game DP and Minimax.

ImpartialPartizan
legal movesthe same for both playersdepend on whose turn it is
examplesNim, subtraction games, Chomp, Kayleschess, checkers, Blue-Red Hackenbush, Domineering
theorySprague–Grundy: every position equals one Nim heapgame values, surreal numbers
solved byXOR of Grundy valuessums of values, minimax
TermMeaning
terminal positionno legal moves
P-positionthe Previous player wins: the player to move loses
N-positionthe Next player (to move) wins
nim-sumXOR of heap sizes
Grundy number (nim-value)the size of the equivalent single Nim heap: Sprague–Grundy
mexminimum excluded non-negative integer
disjoint sumindependent games side by side; a move is made in exactly one component

Win / lose classification

Under normal play, for any finite game: a position is losing iff every move leads to a winning position (in particular terminal positions lose), and winning iff some move leads to a losing position.

// moves(s) lists the positions reachable from s (the game must be finite and acyclic)
vector<int> memoWin;                                     // -1 unknown, 0 lose, 1 win
bool winning(int s, const vector<vector<int>>& moves) {
    if (memoWin[s] != -1) return memoWin[s];
    bool w = false;
    for (int t : moves[s]) if (!winning(t, moves)) { w = true; break; }
    memoWin[s] = w;
    return w;
}

Solving an unfamiliar game: (1) impartial? If not, minimax or game values. (2) Does it split into independent components? Then Grundy values and XOR. (3) Small state space? Memoized W/L search. (4) Otherwise compute small cases and look for a pattern: brute-force the first 30–50 values, print them, search OEIS; periodic sequences, and -style patterns are common, and the guess must be verified on a longer range. (5) Can positions repeat? Plain memoization loops: use retrograde analysis and allow draws (Loopy Games).

Nim

Several heaps; a move removes any positive number of stones from one heap; the player who cannot move loses.

Bouton’s theorem. The first player wins iff the nim-sum . A winning move: with , pick a heap with (any heap having the highest set bit of ) and reduce it to .

Misère Nim (taking the last stone loses): play as in normal Nim unless every heap has size at most 1; then the first player wins iff the number of heaps of size 1 is even.

bool nimFirstWins(const vector<ll>& a) { ll x = 0; for (ll h : a) x ^= h; return x != 0; }
 
pair<int, ll> nimWinningMove(const vector<ll>& a) {      // (heap, new size), or (-1, -1) if losing
    ll S = 0; for (ll h : a) S ^= h;
    if (!S) return {-1, -1};
    for (int i = 0; i < sz(a); i++) if ((a[i] ^ S) < a[i]) return {i, a[i] ^ S};
    return {-1, -1};
}
 
bool misereNimFirstWins(const vector<ll>& a) {
    ll S = 0; int big = 0, ones = 0;
    for (ll h : a) { S ^= h; big += h > 1; ones += h == 1; }
    if (big == 0) return ones % 2 == 0;                  // only heaps of size 0 and 1
    return S != 0;
}

Nim variants

VariantRuleLosing positions (or Grundy value)
Nimremove any number from one heapXOR
misère Nimthe last stone losesas above
bounded Nimremove from one heap; XOR of these
Moore’s Nimremove from up to heaps at onceevery binary digit column sums to
staircase Nimmove coins from step to step ; step 0 is deadXOR of the odd-indexed steps
Wythoff’s gametwo heaps; remove from one, or the same amount from both: see Wythoff’s Game
subtraction gamesremove Grundy values, eventually periodic for finite : Sprague–Grundy
Lasker’s Nimremove any number, or split a heap in two, , ,
Kayles, Dawson’s chess, Grundy’s gameremove pins / split a rowsplitting games, periods 12 and 34: Sprague–Grundy
turning games (Turning Turtles, Mock Turtles)flip coinsGrundy theory, and odious numbers
Green Hackenbushcut edges of a rooted graphcolon and fusion principles: Hackenbush
Cram (impartial) / Domineering (partizan)place dominoesGrundy values / game values; no simple closed form on general boards, both are computed, not derived
Col, Snort (partizan map-colouring games)colour regions of a map, adjacent regions must differ (Col) / may not both be the player’s colour (Snort)game values; Snort reduces to a sum of games on the complement graph’s components
Chomp, Hexstrategy stealing: Chomp and Hex
Dots and Boxes (via strings-and-coins)draw one edge of a grid; completing a box’s 4th edge scores and grants another moveeach undrawn edge of a box is a “string” to a “coin” (a shared edge is one string to two coins); long chains and loops of coins behave like Nim heaps with an extra “loony” twist (double-crossing a long chain or loop controls who opens the next one)

Many “push tokens toward the exit” games reduce to staircase Nim (for example coins on a line moving left, scored by the gaps between them), and “each move takes ” is the most common contest variation.

bool mooreLoses(const vector<ll>& a, int k) {            // Nim_k: remove from up to k heaps
    for (int b = 0; b < 62; b++) {
        int s = 0;
        for (ll h : a) s += h >> b & 1;
        if (s % (k + 1)) return false;
    }
    return true;
}
 
bool staircaseFirstWins(const vector<ll>& c) {           // c[i] coins on step i, moves go i -> i-1
    ll x = 0;
    for (int i = 1; i < sz(c); i += 2) x ^= c[i];
    return x != 0;
}
 
bool boundedNimFirstWins(const vector<ll>& a, ll m) {     // each move removes 1..m from one heap
    ll x = 0; for (ll h : a) x ^= h % (m + 1);
    return x != 0;
}