Skip to content

Commit 45c5ffd

Browse files
authored
Add Sprague Grundy Theorem (TheAlgorithms#550)
1 parent c1e4a74 commit 45c5ffd

File tree

1 file changed

+70
-0
lines changed

1 file changed

+70
-0
lines changed

src/math/sprague_grundy_theorem.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* Sprague Grundy Theorem for combinatorial games like Nim
3+
*
4+
* The Sprague Grundy Theorem is a fundamental concept in combinatorial game theory, commonly used to analyze
5+
* games like Nim. It calculates the Grundy number (also known as the nimber) for a position in a game.
6+
* The Grundy number represents the game's position, and it helps determine the winning strategy.
7+
*
8+
* The Grundy number of a terminal state is 0; otherwise, it is recursively defined as the minimum
9+
* excludant (mex) of the Grundy values of possible next states.
10+
*
11+
* For more details on Sprague Grundy Theorem, you can visit:(https://en.wikipedia.org/wiki/Sprague%E2%80%93Grundy_theorem)
12+
*
13+
* Author : [Gyandeep](https://github.com/Gyan172004)
14+
*/
15+
16+
pub fn calculate_grundy_number(
17+
position: i64,
18+
grundy_numbers: &mut [i64],
19+
possible_moves: &[i64],
20+
) -> i64 {
21+
// Check if we've already calculated the Grundy number for this position.
22+
if grundy_numbers[position as usize] != -1 {
23+
return grundy_numbers[position as usize];
24+
}
25+
26+
// Base case: terminal state
27+
if position == 0 {
28+
grundy_numbers[0] = 0;
29+
return 0;
30+
}
31+
32+
// Calculate Grundy values for possible next states.
33+
let mut next_state_grundy_values: Vec<i64> = vec![];
34+
for move_size in possible_moves.iter() {
35+
if position - move_size >= 0 {
36+
next_state_grundy_values.push(calculate_grundy_number(
37+
position - move_size,
38+
grundy_numbers,
39+
possible_moves,
40+
));
41+
}
42+
}
43+
44+
// Sort the Grundy values and find the minimum excludant.
45+
next_state_grundy_values.sort_unstable();
46+
let mut mex: i64 = 0;
47+
for grundy_value in next_state_grundy_values.iter() {
48+
if *grundy_value != mex {
49+
break;
50+
}
51+
mex += 1;
52+
}
53+
54+
// Store the calculated Grundy number and return it.
55+
grundy_numbers[position as usize] = mex;
56+
return mex;
57+
}
58+
59+
#[cfg(test)]
60+
mod tests {
61+
use super::*;
62+
63+
#[test]
64+
fn calculate_grundy_number_test() {
65+
let mut grundy_numbers: Vec<i64> = vec![-1; 7];
66+
let possible_moves: Vec<i64> = vec![1, 4];
67+
calculate_grundy_number(6, &mut grundy_numbers, &possible_moves);
68+
assert_eq!(grundy_numbers, [0, 1, 0, 1, 2, 0, 1]);
69+
}
70+
}

0 commit comments

Comments
 (0)