Combinatorial Game Theory | Set 4 (Sprague - Grundy Theorem) - GeeksforGeeks

04 Sep, 2021
Prerequisites : Grundy Numbers/Numbers and MexWe have already seen in Set 2 (https://www.geeksforgeeks.org/combinatorial-game-theory-set-2-game-nim/), that we can find who wins in a game of Nim without actually playing the game.Suppose we change the classic Nim game a bit. This time each player can only remove 1, 2 or 3 stones only (and not any number of stones as in the classic game of Nim). Can we predict who will win?Yes, we can predict the winner using Sprague-Grundy Theorem.
What is Sprague-Grundy Theorem? Suppose there is a composite game (more than one sub-game) made up of N sub-games and two players, A and B. Then Sprague-Grundy Theorem says that if both A and B play optimally (i.e., they don’t make any mistakes), then the player starting first is guaranteed to win if the XOR of the grundy numbers of position in each sub-games at the beginning of the game is non-zero. Otherwise, if the XOR evaluates to zero, then player A will lose definitely, no matter what.
How to apply Sprague Grundy Theorem ? We can apply Sprague-Grundy Theorem in any impartial game and solve it. The basic steps are listed as follows: 
Break the composite game into sub-games.Then for each sub-game, calculate the Grundy Number at that position.Then calculate the XOR of all the calculated Grundy Numbers.If the XOR value is non-zero, then the player who is going to make the turn (First Player) will win else he is destined to lose, no matter what.
Break the composite game into sub-games.
Then for each sub-game, calculate the Grundy Number at that position.
Then calculate the XOR of all the calculated Grundy Numbers.
If the XOR value is non-zero, then the player who is going to make the turn (First Player) will win else he is destined to lose, no matter what.
Example Game : The game starts with 3 piles having 3, 4 and 5 stones, and the player to move may take any positive number of stones upto 3 only from any of the piles [Provided that the pile has that much amount of stones]. The last player to move wins. Which player wins the game assuming that both players play optimally?
How to tell who will win by applying Sprague-Grundy Theorem? As, we can see that this game is itself composed of several sub-games. First Step : The sub-games can be considered as each piles. Second Step : We see from the below table that 
Grundy(3) = 3
Grundy(4) = 0 
Grundy(5) = 1 
We have already seen how to calculate the Grundy Numbers of this game in the previous article.Third Step : The XOR of 3, 0, 1 = 2Fourth Step : Since XOR is a non-zero number, so we can say that the first player will win.
Below is the program that implements above 4 steps. 
C++
Java
Python3
C#
Javascript
/*  Game Description- "A game is played between two players and there are N piles of stones such that each pile has certain number of stones. On his/her turn, a player selects a pile and can take any non-zero number of stones upto 3 (i.e- 1,2,3) The player who cannot move is considered to lose the game (i.e., one who take the last stone is the winner). Can you find which player wins the game if both players play optimally (they don't make any mistake)? "  A Dynamic Programming approach to calculate Grundy Number and Mex and find the Winner using Sprague - Grundy Theorem. */ #include<bits/stdc++.h>using namespace std; /* piles[] -> Array having the initial count of stones/coins            in each piles before the game has started.   n       -> Number of piles    Grundy[] -> Array having the Grundy Number corresponding to             the initial position of each piles in the game    The piles[] and Grundy[] are having 0-based indexing*/ #define PLAYER1 1#define PLAYER2 2 // A Function to calculate Mex of all the values in that setint calculateMex(unordered_set<int> Set){    int Mex = 0;     while (Set.find(Mex) != Set.end())        Mex++;     return (Mex);} // A function to Compute Grundy Number of 'n'int calculateGrundy(int n, int Grundy[]){    Grundy[0] = 0;    Grundy[1] = 1;    Grundy[2] = 2;    Grundy[3] = 3;     if (Grundy[n] != -1)        return (Grundy[n]);     unordered_set<int> Set; // A Hash Table     for (int i=1; i<=3; i++)            Set.insert (calculateGrundy (n-i, Grundy));     // Store the result    Grundy[n] = calculateMex (Set);     return (Grundy[n]);} // A function to declare the winner of the gamevoid declareWinner(int whoseTurn, int piles[],                    int Grundy[], int n){    int xorValue = Grundy[piles[0]];     for (int i=1; i<=n-1; i++)        xorValue = xorValue ^ Grundy[piles[i]];     if (xorValue != 0)    {        if (whoseTurn == PLAYER1)            printf("Player 1 will win\n");        else            printf("Player 2 will win\n");    }    else    {        if (whoseTurn == PLAYER1)            printf("Player 2 will win\n");        else            printf("Player 1 will win\n");    }     return;}  // Driver program to test above functionsint main(){    // Test Case 1    int piles[] = {3, 4, 5};    int n = sizeof(piles)/sizeof(piles[0]);     // Find the maximum element    int maximum = *max_element(piles, piles + n);     // An array to cache the sub-problems so that    // re-computation of same sub-problems is avoided    int Grundy[maximum + 1];    memset(Grundy, -1, sizeof (Grundy));     // Calculate Grundy Value of piles[i] and store it    for (int i=0; i<=n-1; i++)        calculateGrundy(piles[i], Grundy);     declareWinner(PLAYER1, piles, Grundy, n);     /* Test Case 2    int piles[] = {3, 8, 2};    int n = sizeof(piles)/sizeof(piles[0]);      int maximum = *max_element (piles, piles + n);     // An array to cache the sub-problems so that    // re-computation of same sub-problems is avoided    int Grundy [maximum + 1];    memset(Grundy, -1, sizeof (Grundy));     // Calculate Grundy Value of piles[i] and store it    for (int i=0; i<=n-1; i++)        calculateGrundy(piles[i], Grundy);     declareWinner(PLAYER2, piles, Grundy, n);   */     return (0);}
import java.util.*; /* Game Description-"A game is played between two players and there are N pilesof stones such that each pile has certain number of stones.On his/her turn, a player selects a pile and can take anynon-zero number of stones upto 3 (i.e- 1,2,3)The player who cannot move is considered to lose the game(i.e., one who take the last stone is the winner).Can you find which player wins the game if both players playoptimally (they don't make any mistake)? " A Dynamic Programming approach to calculate Grundy Numberand Mex and find the Winner using Sprague - Grundy Theorem. */ class GFG {      /* piles[] -> Array having the initial count of stones/coins            in each piles before the game has started.n     -> Number of piles Grundy[] -> Array having the Grundy Number corresponding to            the initial position of each piles in the game The piles[] and Grundy[] are having 0-based indexing*/ static int PLAYER1 = 1;static int PLAYER2 = 2; // A Function to calculate Mex of all the values in that setstatic int calculateMex(HashSet<Integer> Set){    int Mex = 0;     while (Set.contains(Mex))        Mex++;     return (Mex);} // A function to Compute Grundy Number of 'n'static int calculateGrundy(int n, int Grundy[]){    Grundy[0] = 0;    Grundy[1] = 1;    Grundy[2] = 2;    Grundy[3] = 3;     if (Grundy[n] != -1)        return (Grundy[n]);     // A Hash Table    HashSet<Integer> Set = new HashSet<Integer>();     for (int i = 1; i <= 3; i++)            Set.add(calculateGrundy (n - i, Grundy));     // Store the result    Grundy[n] = calculateMex (Set);     return (Grundy[n]);} // A function to declare the winner of the gamestatic void declareWinner(int whoseTurn, int piles[],                    int Grundy[], int n){    int xorValue = Grundy[piles[0]];     for (int i = 1; i <= n - 1; i++)        xorValue = xorValue ^ Grundy[piles[i]];     if (xorValue != 0)    {        if (whoseTurn == PLAYER1)            System.out.printf("Player 1 will win\n");        else            System.out.printf("Player 2 will win\n");    }    else    {        if (whoseTurn == PLAYER1)            System.out.printf("Player 2 will win\n");        else            System.out.printf("Player 1 will win\n");    }     return;}  // Driver codepublic static void main(String[] args){         // Test Case 1    int piles[] = {3, 4, 5};    int n = piles.length;     // Find the maximum element    int maximum = Arrays.stream(piles).max().getAsInt();     // An array to cache the sub-problems so that    // re-computation of same sub-problems is avoided    int Grundy[] = new int[maximum + 1];    Arrays.fill(Grundy, -1);     // Calculate Grundy Value of piles[i] and store it    for (int i = 0; i <= n - 1; i++)        calculateGrundy(piles[i], Grundy);     declareWinner(PLAYER1, piles, Grundy, n);     /* Test Case 2    int piles[] = {3, 8, 2};    int n = sizeof(piles)/sizeof(piles[0]);      int maximum = *max_element (piles, piles + n);     // An array to cache the sub-problems so that    // re-computation of same sub-problems is avoided    int Grundy [maximum + 1];    memset(Grundy, -1, sizeof (Grundy));     // Calculate Grundy Value of piles[i] and store it    for (int i=0; i<=n-1; i++)        calculateGrundy(piles[i], Grundy);     declareWinner(PLAYER2, piles, Grundy, n); */     }} // This code is contributed by PrinciRaj1992
'''  Game Description- "A game is played between two players and there are N piles of stones such that each pile has certain number of stones. On his/her turn, a player selects a pile and can take any non-zero number of stones upto 3 (i.e- 1,2,3) The player who cannot move is considered to lose the game (i.e., one who take the last stone is the winner). Can you find which player wins the game if both players play optimally (they don't make any mistake)? "    A Dynamic Programming approach to calculate Grundy Number and Mex and find the Winner using Sprague - Grundy Theorem.       piles[] -> Array having the initial count of stones/coins            in each piles before the game has started.   n       -> Number of piles      Grundy[] -> Array having the Grundy Number corresponding to             the initial position of each piles in the game      The piles[] and Grundy[] are having 0-based indexing''' PLAYER1 = 1PLAYER2 = 2   # A Function to calculate Mex of all# the values in that setdef calculateMex(Set):      Mex = 0;       while (Mex in Set):        Mex += 1       return (Mex) # A function to Compute Grundy Number of 'n'def calculateGrundy(n, Grundy):     Grundy[0] = 0    Grundy[1] = 1    Grundy[2] = 2    Grundy[3] = 3       if (Grundy[n] != -1):        return (Grundy[n])         # A Hash Table    Set = set()       for i in range(1, 4):        Set.add(calculateGrundy(n - i,                                Grundy))         # Store the result    Grundy[n] = calculateMex(Set)       return (Grundy[n])  # A function to declare the winner of the gamedef declareWinner(whoseTurn, piles, Grundy, n):     xorValue = Grundy[piles[0]];       for i in range(1, n):        xorValue = (xorValue ^                    Grundy[piles[i]])       if (xorValue != 0):             if (whoseTurn == PLAYER1):            print("Player 1 will win\n");        else:            print("Player 2 will win\n");    else:             if (whoseTurn == PLAYER1):            print("Player 2 will win\n");        else:            print("Player 1 will win\n");     # Driver codeif __name__=="__main__":         # Test Case 1    piles = [ 3, 4, 5 ]    n = len(piles)       # Find the maximum element    maximum = max(piles)       # An array to cache the sub-problems so that    # re-computation of same sub-problems is avoided    Grundy = [-1 for i in range(maximum + 1)];       # Calculate Grundy Value of piles[i] and store it    for i in range(n):        calculateGrundy(piles[i], Grundy);       declareWinner(PLAYER1, piles, Grundy, n);       ''' Test Case 2    int piles[] = {3, 8, 2};    int n = sizeof(piles)/sizeof(piles[0]);          int maximum = *max_element (piles, piles + n);       // An array to cache the sub-problems so that    // re-computation of same sub-problems is avoided    int Grundy [maximum + 1];    memset(Grundy, -1, sizeof (Grundy));       // Calculate Grundy Value of piles[i] and store it    for (int i=0; i<=n-1; i++)        calculateGrundy(piles[i], Grundy);       declareWinner(PLAYER2, piles, Grundy, n);   ''' # This code is contributed by rutvik_56
using System;using System.Linq;using System.Collections.Generic; /* Game Description-"A game is played between two players and there are N pilesof stones such that each pile has certain number of stones.On his/her turn, a player selects a pile and can take anynon-zero number of stones upto 3 (i.e- 1,2,3)The player who cannot move is considered to lose the game(i.e., one who take the last stone is the winner).Can you find which player wins the game if both players playoptimally (they don't make any mistake)? " A Dynamic Programming approach to calculate Grundy Numberand Mex and find the Winner using Sprague - Grundy Theorem. */ class GFG{      /* piles[] -> Array having the initial count of stones/coins            in each piles before the game has started.n -> Number of piles Grundy[] -> Array having the Grundy Number corresponding to            the initial position of each piles in the game The piles[] and Grundy[] are having 0-based indexing*/ static int PLAYER1 = 1;//static int PLAYER2 = 2; // A Function to calculate Mex of all the values in that setstatic int calculateMex(HashSet<int> Set){    int Mex = 0;     while (Set.Contains(Mex))        Mex++;     return (Mex);} // A function to Compute Grundy Number of 'n'static int calculateGrundy(int n, int []Grundy){    Grundy[0] = 0;    Grundy[1] = 1;    Grundy[2] = 2;    Grundy[3] = 3;     if (Grundy[n] != -1)        return (Grundy[n]);     // A Hash Table    HashSet<int> Set = new HashSet<int>();     for (int i = 1; i <= 3; i++)            Set.Add(calculateGrundy (n - i, Grundy));     // Store the result    Grundy[n] = calculateMex (Set);     return (Grundy[n]);} // A function to declare the winner of the gamestatic void declareWinner(int whoseTurn, int []piles,                    int []Grundy, int n){    int xorValue = Grundy[piles[0]];     for (int i = 1; i <= n - 1; i++)        xorValue = xorValue ^ Grundy[piles[i]];     if (xorValue != 0)    {        if (whoseTurn == PLAYER1)            Console.Write("Player 1 will win\n");        else            Console.Write("Player 2 will win\n");    }    else    {        if (whoseTurn == PLAYER1)            Console.Write("Player 2 will win\n");        else            Console.Write("Player 1 will win\n");    }     return;}  // Driver codestatic void Main(){         // Test Case 1    int []piles = {3, 4, 5};    int n = piles.Length;     // Find the maximum element    int maximum = piles.Max();     // An array to cache the sub-problems so that    // re-computation of same sub-problems is avoided    int []Grundy = new int[maximum + 1];    Array.Fill(Grundy, -1);     // Calculate Grundy Value of piles[i] and store it    for (int i = 0; i <= n - 1; i++)        calculateGrundy(piles[i], Grundy);     declareWinner(PLAYER1, piles, Grundy, n);         /* Test Case 2    int piles[] = {3, 8, 2};    int n = sizeof(piles)/sizeof(piles[0]);      int maximum = *max_element (piles, piles + n);     // An array to cache the sub-problems so that    // re-computation of same sub-problems is avoided    int Grundy [maximum + 1];    memset(Grundy, -1, sizeof (Grundy));     // Calculate Grundy Value of piles[i] and store it    for (int i=0; i<=n-1; i++)        calculateGrundy(piles[i], Grundy);     declareWinner(PLAYER2, piles, Grundy, n); */     }} // This code is contributed by mits
<script> /* Game Description-"A game is played between two players and there are N pilesof stones such that each pile has certain number of stones.On his/her turn, a player selects a pile and can take anynon-zero number of stones upto 3 (i.e- 1,2,3)The player who cannot move is considered to lose the game(i.e., one who take the last stone is the winner).Can you find which player wins the game if both players playoptimally (they don't make any mistake)? "  A Dynamic Programming approach to calculate Grundy Numberand Mex and find the Winner using Sprague - Grundy Theorem. */  /* piles[] -> Array having the initial count of stones/coins            in each piles before the game has started.n     -> Number of piles  Grundy[] -> Array having the Grundy Number corresponding to            the initial position of each piles in the game  The piles[] and Grundy[] are having 0-based indexing*/let PLAYER1 = 1;let PLAYER2 = 2; // A Function to calculate Mex of all the values in that setfunction calculateMex(Set){    let Mex = 0;      while (Set.has(Mex))        Mex++;      return (Mex);} // A function to Compute Grundy Number of 'n'function calculateGrundy(n,Grundy){    Grundy[0] = 0;    Grundy[1] = 1;    Grundy[2] = 2;    Grundy[3] = 3;      if (Grundy[n] != -1)        return (Grundy[n]);      // A Hash Table    let Set = new Set();      for (let i = 1; i <= 3; i++)            Set.add(calculateGrundy (n - i, Grundy));      // Store the result    Grundy[n] = calculateMex (Set);      return (Grundy[n]);} // A function to declare the winner of the gamefunction declareWinner(whoseTurn,piles,Grundy,n){    let xorValue = Grundy[piles[0]];      for (let i = 1; i <= n - 1; i++)        xorValue = xorValue ^ Grundy[piles[i]];      if (xorValue != 0)    {        if (whoseTurn == PLAYER1)            document.write("Player 1 will win<br>");        else            document.write("Player 2 will win<br>");    }    else    {        if (whoseTurn == PLAYER1)            document.write("Player 2 will win<br>");        else            document.write("Player 1 will win<br>");    }      return;} // Driver code // Test Case 1    let piles = [3, 4, 5];    let n = piles.length;               // Find the maximum element    let maximum = Math.max(...piles)      // An array to cache the sub-problems so that    // re-computation of same sub-problems is avoided    let Grundy = new Array(maximum + 1);    for(let i=0;i<maximum+1;i++)        Grundy[i]=0;      // Calculate Grundy Value of piles[i] and store it    for (let i = 0; i <= n - 1; i++)        calculateGrundy(piles[i], Grundy);      declareWinner(PLAYER1, piles, Grundy, n);      /* Test Case 2    int piles[] = {3, 8, 2};    int n = sizeof(piles)/sizeof(piles[0]);        int maximum = *max_element (piles, piles + n);      // An array to cache the sub-problems so that    // re-computation of same sub-problems is avoided    int Grundy [maximum + 1];    memset(Grundy, -1, sizeof (Grundy));      // Calculate Grundy Value of piles[i] and store it    for (int i=0; i<=n-1; i++)        calculateGrundy(piles[i], Grundy);      declareWinner(PLAYER2, piles, Grundy, n); */ // This code is contributed by avanitrachhadiya2155</script>
Output : 
Player 1 will win
References : https://en.wikipedia.org/wiki/Sprague%E2%80%93Grundy_theorem
Exercise to the Readers: Consider the below game. “A game is played by two players with N integers A1, A2, .., AN. On his/her turn, a player selects an integer, divides it by 2, 3, or 6, and then takes the floor. If the integer becomes 0, it is removed. The last player to move wins. Which player wins the game if both players play optimally?”Hint : See the example 3 of previous article.
This article is contributed by Rachit Belwariar. If you like GeeksforGeeks and would like to contribute, you can also write an article and mail your article to review-team@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above 
vinayb21
princiraj1992
Mithun Kumar
rutvik_56
avanitrachhadiya2155
adnanirshad158
Combinatorial
Game Theory
Mathematical
Mathematical
Game Theory
Combinatorial
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Comments
Old Comments
Combinational Sum
Count ways to reach the nth stair using step 1, 2 or 3
Count of subsets with sum equal to X
Print all possible strings of length k that can be formed from a set of n characters
Find the Number of Permutations that satisfy the given condition in an array
Implementation of Tic-Tac-Toe game
Minimax Algorithm in Game Theory | Set 2 (Introduction to Evaluation Function)
Expectimax Algorithm in Game Theory
Game Theory (Normal-form game) | Set 3 (Game with Mixed Strategy)
Choice of Area