Every position of a finite impartial game under normal play is equivalent to a single Nim heap. Its size is the Grundy number (nim-value)

where mex is the minimum excluded non-negative integer; terminal positions get . Then iff is losing, and the value of a disjoint sum (a move changes exactly one component) is the XOR:

A game with independent components has positions but needs only Grundy computations. To find a winning move, pick a component and a move in it that turns the total XOR into 0 (as in Nim). Cost: for all values, to combine.

Nimber is the formal name (from Conway’s combinatorial game theory) for a Grundy value viewed as an element of an algebraic structure rather than just an integer: nimbers under “nimber addition” (defined as XOR, matching the disjoint-sum rule above) and a separate “nimber multiplication” (a more exotic operation, computed recursively via ) form a field — this lets a game be analysed as an algebraic value, which is how Hackenbush/Nim-like sums get combined in deeper combinatorial-game-theory arguments. Nimber multiplication itself is essentially never needed directly in CP (every problem in this repo’s scope only uses nimber addition, i.e. plain XOR); it is cited here, not derived, purely so the term “nimber” is not an unexplained gap.

// Grundy values of an acyclic game graph given in topological order of "moves go to smaller ids" (or any DAG order).
vector<int> grundyAll(const vector<vector<int>>& moves, const vector<int>& order) {   // order: sinks first
    int n = sz(moves); vector<int> g(n, 0), seen(n + 2, -1);
    for (int s : order) {
        for (int t : moves[s]) if (g[t] <= n) seen[g[t]] = s;                    // mark with the state id: no clearing
        int m = 0; while (seen[m] == s) m++;
        g[s] = m;
    }
    return g;
}
 
// Row games ("octal-like"): a row of n pins, a move removes r in R adjacent pins anywhere, possibly splitting the row.
// Kayles: R = {1, 2}; Dawson's Kayles: R = {2} (Dawson's chess with n pawns = Dawson's Kayles with n + 1 pins).
vector<int> rowGameGrundy(int N, const vector<int>& R) {
    vector<int> g(N + 1, 0), seen(2 * N + 4, -1);
    for (int n = 1; n <= N; n++) {
        for (int r : R) for (int a = 0; a + r <= n; a++) seen[g[a] ^ g[n - r - a]] = n;
        int m = 0; while (seen[m] == n) m++;
        g[n] = m;
    }
    return g;
}
 
// Smallest period p (with start n0) such that g[n + p] = g[n] holds on [n0, 2 n0 + p + t), which by the
// Guy-Smith periodicity theorem proves periodicity forever (t = largest number of pins removed). {-1,-1} if none.
pair<int, int> certifiedPeriod(const vector<int>& g, int t, int maxP) {
    int N = sz(g) - 1;
    for (int p = 1; p <= maxP; p++) {
        int n0 = N - p;
        while (n0 > 0 && g[n0 - 1 + p] == g[n0 - 1]) n0--;
        if (2 * n0 + p + t <= N - p) return {n0, p};
    }
    return {-1, -1};
}
 
vector<int> subtractionGrundy(int N, const vector<int>& S) {    // remove s in S stones from one heap
    vector<int> g(N + 1, 0), seen(sz(S) + 2, -1);
    for (int n = 1; n <= N; n++) {
        for (int s : S) if (s <= n && g[n - s] <= sz(S)) seen[g[n - s]] = n;
        int m = 0; while (seen[m] == n) m++;
        g[n] = m;
    }
    return g;
}
 
int mockTurtles(ll i) { return int(2 * i + (__builtin_popcountll(i) % 2 == 0)); }   // i-th odious number

Known values

GameGrundy value
Nim heap of size
subtraction game (so : )
period 7:
period 5:
any finite eventually periodic (theorem below)
= all squares, all primesirregular, no closed form known
Lasker’s Nim (remove, or split a heap in two), , ,
Kayles (remove 1 or 2 adjacent pins)period 12 from on:
Dawson’s Kayles (remove exactly 2 adjacent pins)period 34 from on
Grundy’s game (split a heap into two unequal heaps)computed to huge , no period found
Green Hackenbush on a tree: Hackenbush
Turning Turtles (turn one coin HT, optionally one coin to its left)single head at position (from 1): , i.e. plain Nim
Mock Turtles (turn up to 3 coins, the rightmost HT)single head at position (from 0): the -th odious number or
coin-turning games in generala position is the XOR of its single-head values

Practical recipe: write a brute-force grundy for small states, print the first 30–50 values, guess the pattern (constant, , periodic, , , a binary pattern), verify against the brute force on a larger range, and if nothing appears, precompute or search OEIS. For splitting moves the reachable set contains XORs of the parts, g[i] ^ g[n - r - i]: this XOR inside the mex is where the theory does its real work (Kayles, Dawson’s chess, “cut the row” games).

What Grundy numbers cannot do

SituationWhy it failsUse instead
partizan gamesSprague–Grundy needs identical move setsminimax, game values: Hackenbush and Surreal Numbers
misère playthe sum rule breaksmisère Nim, genus theory, or find the pattern
draws / infinite playnot a finite gamegeneralized Sprague–Grundy, retrograde analysis: Loopy Games
chancenot deterministicexpectiminimax
moves touching several componentsthe sum rule needs independencemodel as one game
scores (“who wins and by how much”)Grundy is about win/lose onlyadvantage DP: Game DP

Applications: Nim itself (heap = value), Wythoff’s game (with an extra move type), turning and coin games, Green Hackenbush, and almost every multi-pile contest game problem.