#!/usr/bin/env python3
"""Comprehensive Nim Game Examples - Mathematical Strategy and Game Theory Demonstration.
This module provides 8 comprehensive examples demonstrating the mathematical properties
and strategic aspects of the Nim game, from basic gameplay to advanced analysis,
perfect play algorithms, and educational game theory concepts.
The examples cover:
1. Basic Standard Nim gameplay with optimal strategy
2. Misère Nim with endgame analysis
3. Mathematical analysis of positions using nim-sum
4. Game theory demonstration with P/N positions
5. Multiple pile variants and strategic considerations
6. Performance analysis and algorithm benchmarking
7. Educational game theory tutorial
8. Advanced tournament and ML integration
Each example includes detailed explanations of the mathematical concepts,
strategic reasoning, and implementation details for educational purposes.
"""
import argparse
import asyncio
import functools
import logging
import operator
import random
import sys
import time
from haive.games.nim.models import NimMove
from haive.games.nim.state_manager import NimStateManager
# Configure logging
logging.basicConfig(level=logging.INFO, format="%(levelname)s:%(name)s:%(message)s")
logger = logging.getLogger(__name__)
[docs]
def print_subsection(title: str) -> None:
"""Print a formatted subsection header."""
print(f"\n--- {title} ---")
[docs]
def calculate_nim_sum(piles: list[int]) -> int:
"""Calculate the nim-sum (XOR) of pile sizes."""
return functools.reduce(operator.xor, piles, 0)
[docs]
def binary_representation(piles: list[int]) -> None:
"""Display binary representation of piles for educational purposes."""
print("\nBinary Analysis:")
for i, pile in enumerate(piles):
print(f" Pile {i}: {pile:2d} = {bin(pile)[2:]:>8s}")
nim_sum = calculate_nim_sum(piles)
print(f" Nim-sum: {nim_sum:2d} = {bin(nim_sum)[2:]:>8s}")
print(
f" Position: {
'Winning (N-position)' if nim_sum != 0 else 'Losing (P-position)'
}"
)
[docs]
async def example_1_basic_standard_nim():
"""Example 1: Basic Standard Nim with Optimal Play Strategy.
Demonstrates the fundamental concepts of Nim including:
- Standard game rules
- Nim-sum calculation
- Optimal move selection
- Strategic reasoning
"""
print_section_header(
"EXAMPLE 1: BASIC STANDARD NIM", "Optimal Play Strategy and Nim-Sum Analysis"
)
# Initialize game with classic 3-5-7 configuration
initial_piles = [3, 5, 7]
print(f"Initial Position: {initial_piles}")
print("Rules: Standard Nim - last player to move wins")
# Show mathematical analysis
binary_representation(initial_piles)
# Create state manager for analysis
state = NimStateManager.initialize(pile_sizes=initial_piles)
# Analyze position using basic logic
nim_sum = calculate_nim_sum(state.piles)
print("\nPosition Analysis:")
if nim_sum == 0:
print(" Evaluation: Losing position (P-position)")
print(" Strategy: Defensive - any move gives opponent advantage")
else:
print(" Evaluation: Winning position (N-position)")
print(" Strategy: Offensive - force nim-sum to 0")
# Find optimal move
for pile_idx, pile_size in enumerate(state.piles):
target_size = pile_size ^ nim_sum
if target_size < pile_size:
stones_to_take = pile_size - target_size
print(f" Recommended Move: Take {stones_to_take} from pile {pile_idx}")
# Demonstrate optimal play sequence
print_subsection("Optimal Play Demonstration")
move_count = 0
current_state = state
current_player = "player1"
while sum(current_state.piles) > 0:
move_count += 1
current_piles = current_state.piles.copy()
print(f"\nMove {move_count}: Current position {current_piles}")
# Find and apply optimal move
nim_sum = calculate_nim_sum(current_piles)
optimal_move = None
if nim_sum != 0:
# Find optimal move
for pile_idx, pile_size in enumerate(current_piles):
target_size = pile_size ^ nim_sum
if target_size < pile_size:
stones_to_take = pile_size - target_size
optimal_move = NimMove(
pile_index=pile_idx,
stones_taken=stones_to_take,
player=current_player,
)
break
else:
# Losing position - take any legal move
legal_moves = NimStateManager.get_legal_moves(current_state)
if legal_moves:
optimal_move = legal_moves[0]
optimal_move.player = current_player
if optimal_move:
print(
f" Optimal move: Take {optimal_move.stones_taken} from pile {
optimal_move.pile_index
}"
)
# Show nim-sum before and after
nim_sum_before = calculate_nim_sum(current_piles)
current_state = NimStateManager.apply_move(current_state, optimal_move)
nim_sum_after = calculate_nim_sum(current_state.piles)
print(f" Nim-sum: {nim_sum_before} → {nim_sum_after}")
print(f" New position: {current_state.piles}")
# Switch player
current_player = "player2" if current_player == "player1" else "player1"
# Add small delay for readability
await asyncio.sleep(0.5)
winner = NimStateManager.get_winner(current_state)
print(f"\nGame Over! Winner: {winner}")
print(f"Total moves: {move_count}")
[docs]
async def example_2_misere_nim():
"""Example 2: Misère Nim with Endgame Analysis.
Demonstrates:
- Misère rules (last player loses)
- Strategy differences from standard Nim
- Endgame analysis with single-stone piles
- Transition from normal to misère strategy
"""
print_section_header(
"EXAMPLE 2: MISÈRE NIM", "Endgame Analysis and Strategy Reversal"
)
# Start with position that will reach misère endgame
initial_piles = [2, 2, 2]
print(f"Initial Position: {initial_piles}")
print("Rules: Misère Nim - last player to move loses")
# Create misère configuration
state = NimStateManager.initialize(pile_sizes=initial_piles)
state.misere_mode = True
# Analyze misère position
print_subsection("Misère Strategy Analysis")
# In misère, strategy depends on pile sizes
all_single = all(pile <= 1 for pile in initial_piles)
print(f"All piles ≤ 1: {all_single}")
if all_single:
non_empty_piles = len([p for p in initial_piles if p > 0])
print(f"Non-empty piles: {non_empty_piles}")
if non_empty_piles % 2 == 1:
print("Strategy: Leave opponent with even number of piles")
else:
print("Strategy: Leave opponent with odd number of piles")
else:
print("Strategy: Use standard Nim strategy until all piles ≤ 1")
binary_representation(initial_piles)
# Play through misère game
print_subsection("Misère Game Sequence")
move_count = 0
current_state = state
current_player = "player1"
while sum(current_state.piles) > 0:
move_count += 1
current_piles = current_state.piles.copy()
print(f"\nMove {move_count}: Position {current_piles}")
# Check if we're in misère endgame
in_endgame = all(pile <= 1 for pile in current_piles)
print(f" In misère endgame: {in_endgame}")
if in_endgame:
non_empty = len([p for p in current_piles if p > 0])
print(f" Non-empty piles: {non_empty}")
print(f" Current player should {'win' if non_empty % 2 == 0 else 'lose'}")
# Make move (simplified for demonstration)
legal_moves = NimStateManager.get_legal_moves(current_state)
move = legal_moves[0] # Take first legal move for simplicity
move.player = current_player
print(f" Move: Take {move.stones_taken} from pile {move.pile_index}")
current_state = NimStateManager.apply_move(current_state, move)
# Switch player
current_player = "player2" if current_player == "player1" else "player1"
await asyncio.sleep(0.5)
winner = NimStateManager.get_winner(current_state)
print(f"\nMisère Game Over! Winner: {winner}")
[docs]
async def example_3_mathematical_analysis():
"""Example 3: Mathematical Analysis of Nim Positions.
Demonstrates:
- Nim-sum calculation methods
- Position classification (P/N positions)
- Optimal move calculation
- Mathematical proofs and theorems
"""
print_section_header(
"EXAMPLE 3: MATHEMATICAL ANALYSIS", "Nim-Sum Calculation and Position Theory"
)
# Test various positions
test_positions = [
[3, 5, 7], # Classic position (nim-sum = 1)
[4, 5, 6], # Different winning position
[3, 5, 6], # Losing position (nim-sum = 0)
[1, 2, 3], # Simple position
[8, 8, 8], # Symmetric position
[15, 7, 12], # Larger numbers
[1, 1, 1, 1], # Multiple small piles
]
print_subsection("Position Analysis Matrix")
for i, piles in enumerate(test_positions, 1):
print(f"\nPosition {i}: {piles}")
# Calculate nim-sum
nim_sum = calculate_nim_sum(piles)
print(f" Nim-sum: {nim_sum}")
# Classify position
if nim_sum == 0:
classification = "P-position (Previous player wins - Losing)"
else:
classification = "N-position (Next player wins - Winning)"
print(f" Classification: {classification}")
# Show binary calculation
print(" Binary calculation:")
for j, pile in enumerate(piles):
print(f" Pile {j}: {pile:2d} = {bin(pile)[2:]:>6s}")
print(f" XOR: {nim_sum:2d} = {bin(nim_sum)[2:]:>6s}")
# Find optimal move if winning position
if nim_sum != 0:
print(" Optimal moves:")
for pile_idx, pile_size in enumerate(piles):
target_size = pile_size ^ nim_sum
if target_size < pile_size:
stones_to_take = pile_size - target_size
print(
f" Take {stones_to_take} from pile {pile_idx} (reduce {pile_size} → {target_size})"
)
# Demonstrate Sprague-Grundy theorem
print_subsection("Sprague-Grundy Theorem Demonstration")
position = [6, 10, 14]
print(f"Position: {position}")
nim_sum = calculate_nim_sum(position)
print(f"Nim-sum: {nim_sum}")
print(f"\nTheorem: Position is {'winning' if nim_sum != 0 else 'losing'}")
print(f"Proof: By Sprague-Grundy theorem, nim-sum = {nim_sum}")
if nim_sum != 0:
print("Since nim-sum ≠ 0, this is an N-position (winning for next player)")
else:
print("Since nim-sum = 0, this is a P-position (losing for next player)")
[docs]
async def example_4_game_theory_positions():
"""Example 4: Game Theory Demonstration with P/N Positions.
Demonstrates:
- P-positions (Previous player wins)
- N-positions (Next player wins)
- Move sequences and transitions
- Winning and losing strategies
"""
print_section_header(
"EXAMPLE 4: GAME THEORY POSITIONS",
"P-positions, N-positions, and Strategic Transitions",
)
# Create examples of different position types
positions = {
"Cold Position (P-position)": [3, 5, 6], # nim-sum = 0
"Hot Position (N-position)": [3, 5, 7], # nim-sum = 1
"Symmetric Position": [8, 8, 8], # nim-sum = 0
"Complex Position": [12, 15, 9], # nim-sum = 6
}
for position_name, piles in positions.items():
print_subsection(f"{position_name}")
nim_sum = calculate_nim_sum(piles)
print(f"Position: {piles}")
print(f"Nim-sum: {nim_sum}")
# Analyze according to game theory
if nim_sum == 0:
print("Game Theory Analysis:")
print(" - This is a P-position (Previous player wins)")
print(" - Any move gives opponent a winning position")
print(" - Current player is in a losing position")
print(" - Strategy: Make best defensive move")
else:
print("Game Theory Analysis:")
print(" - This is an N-position (Next player wins)")
print(" - There exists a move to force opponent into P-position")
print(" - Current player is in a winning position")
print(" - Strategy: Force nim-sum to 0")
# Show winning moves
print(" Winning moves:")
for pile_idx, pile_size in enumerate(piles):
target_size = pile_size ^ nim_sum
if target_size < pile_size:
stones_to_take = pile_size - target_size
new_piles = piles.copy()
new_piles[pile_idx] = target_size
new_nim_sum = calculate_nim_sum(new_piles)
print(
f" Take {stones_to_take} from pile {pile_idx}: {piles} → {new_piles} (nim-sum: {nim_sum} → {new_nim_sum})"
)
# Show what happens after various moves
print(" Move consequences:")
for pile_idx, pile_size in enumerate(piles):
if pile_size > 0:
# Show taking 1 stone
new_piles = piles.copy()
new_piles[pile_idx] -= 1
new_nim_sum = calculate_nim_sum(new_piles)
outcome = "winning" if new_nim_sum != 0 else "losing"
print(
f" Take 1 from pile {pile_idx}: nim-sum {nim_sum} → {new_nim_sum} ({outcome} for opponent)"
)
print()
[docs]
async def example_5_multiple_pile_variants():
"""Example 5: Multiple Pile Variants and Strategic Considerations.
Demonstrates:
- Games with different numbers of piles
- Large pile configurations
- Strategic complexity analysis
- Computational efficiency
"""
print_section_header(
"EXAMPLE 5: MULTIPLE PILE VARIANTS",
"Strategic Complexity and Computational Analysis",
)
# Test different pile configurations
configurations = [
("Two Piles", [7, 11]),
("Three Piles (Classic)", [3, 5, 7]),
("Four Piles", [2, 4, 6, 8]),
("Five Piles", [1, 3, 5, 7, 9]),
("Large Numbers", [23, 31, 47]),
("Many Small Piles", [1, 1, 1, 1, 1, 1]),
("Mixed Sizes", [1, 15, 3, 27, 5]),
]
for config_name, piles in configurations:
print_subsection(f"{config_name}")
print(f"Configuration: {piles}")
print(f"Number of piles: {len(piles)}")
print(f"Total stones: {sum(piles)}")
print(f"Largest pile: {max(piles)}")
print(f"Average pile size: {sum(piles) / len(piles):.1f}")
# Calculate nim-sum and analyze
nim_sum = calculate_nim_sum(piles)
print(f"Nim-sum: {nim_sum}")
# Complexity analysis
total_moves = sum(piles)
branching_factor = sum(min(pile, 10) for pile in piles) # Approximate
print("Game complexity:")
print(f" Maximum game length: {total_moves}")
print(f" Approximate branching factor: {branching_factor}")
# Strategic analysis
if nim_sum == 0:
print("Strategic analysis: Defensive position")
print(" - All moves lead to winning positions for opponent")
print(" - Strategy: Minimize opponent's advantage")
else:
print("Strategic analysis: Offensive position")
winning_moves = []
for pile_idx, pile_size in enumerate(piles):
target_size = pile_size ^ nim_sum
if target_size < pile_size:
stones_to_take = pile_size - target_size
winning_moves.append((pile_idx, stones_to_take))
print(f" - {len(winning_moves)} winning move(s) available")
if winning_moves:
pile_idx, stones = winning_moves[0]
print(f" - Best move: Take {stones} from pile {pile_idx}")
print()
[docs]
async def example_7_educational_tutorial():
"""Example 7: Educational Game Theory Tutorial.
Demonstrates:
- Step-by-step learning progression
- Mathematical concepts explanation
- Interactive analysis
- Teaching optimal play
"""
print_section_header(
"EXAMPLE 7: EDUCATIONAL GAME THEORY TUTORIAL",
"Interactive Learning and Mathematical Concepts",
)
print("Welcome to the Nim Game Theory Tutorial!")
print(
"This tutorial will teach you the mathematical foundations of optimal Nim play."
)
# Lesson 1: Basic Concepts
print_subsection("Lesson 1: Basic Concepts")
print("1.1 What is Nim?")
print(" - Nim is a mathematical strategy game")
print(" - Two players take turns removing objects from piles")
print(" - The player who takes the last object wins (standard rules)")
print(" - It's a game of perfect information (no hidden information)")
print("\n1.2 Why is Nim important?")
print(" - It's the foundation of combinatorial game theory")
print(" - It demonstrates perfect strategy algorithms")
print(" - It teaches binary arithmetic applications")
print(" - It's a solved game with mathematical proofs")
# Lesson 2: The Nim-Sum
print_subsection("Lesson 2: The Nim-Sum (XOR Operation)")
print("2.1 What is the nim-sum?")
print(" - The nim-sum is the XOR (exclusive OR) of all pile sizes")
print(" - XOR is a binary operation: 0 ⊕ 0 = 0, 0 ⊕ 1 = 1, 1 ⊕ 0 = 1, 1 ⊕ 1 = 0")
example_piles = [3, 5, 7]
print(f"\n2.2 Example calculation for {example_piles}:")
print(f" 3 in binary: {bin(3)[2:]:>4s}")
print(f" 5 in binary: {bin(5)[2:]:>4s}")
print(f" 7 in binary: {bin(7)[2:]:>4s}")
print(f" XOR result: {bin(3 ^ 5 ^ 7)[2:]:>4s} = {3 ^ 5 ^ 7}")
print("\n2.3 Practice:")
practice_positions = [[1, 2], [4, 6], [2, 4, 6]]
for piles in practice_positions:
nim_sum = calculate_nim_sum(piles)
print(f" {piles} → nim-sum = {nim_sum}")
# Lesson 3: Winning and Losing Positions
print_subsection("Lesson 3: Winning and Losing Positions")
print("3.1 Position Types:")
print(" - P-position (Previous player wins): nim-sum = 0")
print(" - N-position (Next player wins): nim-sum ≠ 0")
print("\n3.2 The Fundamental Theorem:")
print(" - From any N-position, there exists a move to a P-position")
print(" - From any P-position, every move leads to an N-position")
print(" - This gives us perfect strategy!")
# Lesson 4: Optimal Strategy
print_subsection("Lesson 4: Optimal Strategy")
print("4.1 The Winning Strategy:")
print(" - If nim-sum = 0: You're in a losing position (make best defensive move)")
print(" - If nim-sum ≠ 0: You're in a winning position (force nim-sum to 0)")
print("\n4.2 How to find the optimal move:")
winning_example = [4, 6, 8]
nim_sum = calculate_nim_sum(winning_example)
print(f" Example: {winning_example} (nim-sum = {nim_sum})")
for pile_idx, pile_size in enumerate(winning_example):
target_size = pile_size ^ nim_sum
if target_size < pile_size:
stones_to_take = pile_size - target_size
print(f" Move: Take {stones_to_take} from pile {pile_idx}")
print(f" Calculation: {pile_size} ⊕ {nim_sum} = {target_size}")
print(
f" New position: {winning_example[:pile_idx] + [target_size] + winning_example[pile_idx + 1 :]}"
)
break
# Lesson 5: Practice Game
print_subsection("Lesson 5: Practice Game Analysis")
print("Let's analyze a complete game:")
game_position = [3, 4, 5]
move_num = 1
state = NimStateManager.initialize(pile_sizes=game_position)
current_player = "Player1"
while sum(state.piles) > 0 and move_num <= 5: # Limit for example
current_piles = state.piles.copy()
nim_sum = calculate_nim_sum(current_piles)
print(f"\nMove {move_num}: Position {current_piles}")
print(f" Nim-sum: {nim_sum}")
print(
f" Position type: {
'N-position (winning)' if nim_sum != 0 else 'P-position (losing)'
}"
)
if nim_sum != 0:
# Find optimal move
for pile_idx, pile_size in enumerate(current_piles):
target_size = pile_size ^ nim_sum
if target_size < pile_size:
stones_to_take = pile_size - target_size
print(f" Optimal move: Take {stones_to_take} from pile {pile_idx}")
move = NimMove(
pile_index=pile_idx,
stones_taken=stones_to_take,
player=current_player,
)
state = NimStateManager.apply_move(state, move)
break
else:
# Make any legal move (losing position)
legal_moves = NimStateManager.get_legal_moves(state)
if legal_moves:
move = legal_moves[0]
move.player = current_player
print(
f" Forced move: Take {move.stones_taken} from pile {
move.pile_index
}"
)
state = NimStateManager.apply_move(state, move)
current_player = "Player2" if current_player == "Player1" else "Player1"
move_num += 1
await asyncio.sleep(0.8)
print(f"\nFinal position: {state.piles}")
if sum(state.piles) == 0:
winner = NimStateManager.get_winner(state)
print(f"Game over! Winner: {winner}")
[docs]
async def example_8_advanced_integration():
"""Example 8: Advanced Tournament and ML Integration.
Demonstrates:
- Tournament systems
- Statistical analysis
- Machine learning integration concepts
- Advanced configuration options
"""
print_section_header(
"EXAMPLE 8: ADVANCED INTEGRATION", "Tournament Systems and Statistical Analysis"
)
print_subsection("Tournament System Simulation")
# Create different configurations for tournament
tournament_configs = [
("Quick Game", [2, 3, 4]),
("Standard Game", [3, 5, 7]),
("Complex Game", [4, 6, 8, 10]),
("Asymmetric Game", [1, 5, 9]),
("Large Piles", [10, 15, 20]),
]
tournament_results = []
for config_name, piles in tournament_configs:
print(f"\nTournament Round: {config_name}")
print(f"Configuration: {piles}")
# Simulate multiple games
wins = {"Player1": 0, "Player2": 0}
total_moves = []
game_times = []
for _game_num in range(10): # 10 games per configuration
state = NimStateManager.initialize(pile_sizes=piles.copy())
start_time = time.time()
moves = 0
current_player = "Player1"
while sum(state.piles) > 0:
moves += 1
# Simulate optimal play
nim_sum = calculate_nim_sum(state.piles)
optimal_move = None
if nim_sum != 0:
# Find optimal move
for pile_idx, pile_size in enumerate(state.piles):
target_size = pile_size ^ nim_sum
if target_size < pile_size:
stones_to_take = pile_size - target_size
optimal_move = NimMove(
pile_index=pile_idx,
stones_taken=stones_to_take,
player=current_player,
)
break
else:
# Make any legal move
legal_moves = NimStateManager.get_legal_moves(state)
if legal_moves:
optimal_move = legal_moves[0]
optimal_move.player = current_player
if optimal_move:
state = NimStateManager.apply_move(state, optimal_move)
current_player = "Player2" if current_player == "Player1" else "Player1"
end_time = time.time()
winner = NimStateManager.get_winner(state)
wins[winner] += 1
total_moves.append(moves)
game_times.append(end_time - start_time)
# Calculate statistics
avg_moves = sum(total_moves) / len(total_moves)
avg_time = sum(game_times) / len(game_times)
result = {
"config": config_name,
"piles": piles,
"player1_wins": wins["Player1"],
"player2_wins": wins["Player2"],
"avg_moves": avg_moves,
"avg_time": avg_time,
}
tournament_results.append(result)
print(" Results after 10 games:")
print(f" Player1 wins: {wins['Player1']}")
print(f" Player2 wins: {wins['Player2']}")
print(f" Average moves: {avg_moves:.1f}")
print(f" Average time: {avg_time:.3f} seconds")
# Tournament summary
print_subsection("Tournament Summary")
print("Configuration Performance Analysis:")
for result in tournament_results:
win_rate = result["player1_wins"] / 10
print(f" {result['config']}:")
print(f" Win rate: {win_rate:.1%}")
print(f" Avg moves: {result['avg_moves']:.1f}")
print(f" Avg time: {result['avg_time']:.3f}s")
# Statistical analysis
print_subsection("Statistical Analysis")
all_moves = [r["avg_moves"] for r in tournament_results]
all_times = [r["avg_time"] for r in tournament_results]
print("Overall Statistics:")
print(f" Average moves per game: {sum(all_moves) / len(all_moves):.1f}")
print(f" Move range: {min(all_moves):.1f} - {max(all_moves):.1f}")
print(f" Average time per game: {sum(all_times) / len(all_times):.3f}s")
print(f" Time range: {min(all_times):.3f}s - {max(all_times):.3f}s")
# Machine Learning Integration Demo
print_subsection("Machine Learning Integration Concepts")
print("Potential ML Applications in Nim:")
print(" 1. Position Evaluation:")
print(" - Input: Pile sizes")
print(" - Output: Win probability")
print(" - Features: Nim-sum, pile distribution, total stones")
print(" 2. Move Prediction:")
print(" - Input: Current position + player style")
print(" - Output: Likely next move")
print(" - Training: Historical game data")
print(" 3. Strategy Classification:")
print(" - Input: Move sequence")
print(" - Output: Player skill level")
print(" - Application: Adaptive difficulty")
print(" 4. Game Outcome Prediction:")
print(" - Input: Initial position")
print(" - Output: Expected game length")
print(" - Use: Tournament scheduling")
# Feature extraction example
print("\nExample Feature Extraction:")
example_position = [7, 11, 13]
features = {
"total_stones": sum(example_position),
"num_piles": len(example_position),
"max_pile": max(example_position),
"min_pile": min(example_position),
"nim_sum": calculate_nim_sum(example_position),
"pile_variance": sum(
(p - sum(example_position) / len(example_position)) ** 2
for p in example_position
),
"single_stone_piles": sum(1 for p in example_position if p == 1),
"large_piles": sum(1 for p in example_position if p > 10),
}
print(f"Position {example_position} features:")
for feature, value in features.items():
print(f" {feature}: {value:.2f}")
[docs]
async def run_all_examples():
"""Run all examples sequentially."""
print("🎮 NIM GAME COMPREHENSIVE EXAMPLES")
print("Mathematical Strategy and Game Theory Demonstration")
print("=" * 80)
examples = [
("Basic Standard Nim", example_1_basic_standard_nim),
("Misère Nim", example_2_misere_nim),
("Mathematical Analysis", example_3_mathematical_analysis),
("Game Theory Positions", example_4_game_theory_positions),
("Multiple Pile Variants", example_5_multiple_pile_variants),
("Performance Analysis", example_6_performance_analysis),
("Educational Tutorial", example_7_educational_tutorial),
("Advanced Integration", example_8_advanced_integration),
]
for i, (name, example_func) in enumerate(examples, 1):
print(f"\n🔄 Running Example {i}: {name}")
try:
await example_func()
print(f"✅ Example {i} completed successfully")
except Exception as e:
print(f"❌ Example {i} failed: {e}")
logger.error(f"Example {i} error: {e}")
# Pause between examples
if i < len(examples):
print("\n⏸️ Pausing before next example...")
await asyncio.sleep(2)
print("\n🎉 All examples completed!")
[docs]
def parse_args():
"""Parse command line arguments."""
parser = argparse.ArgumentParser(description="Run Nim game examples")
parser.add_argument(
"--example", type=int, choices=range(1, 9), help="Run specific example (1-8)"
)
parser.add_argument(
"--all", action="store_true", help="Run all examples sequentially"
)
parser.add_argument(
"--delay",
type=float,
default=0.5,
help="Delay between operations (default: 0.5s)",
)
return parser.parse_args()
[docs]
async def main():
"""Main entry point."""
args = parse_args()
# Map example numbers to functions
example_functions = {
1: example_1_basic_standard_nim,
2: example_2_misere_nim,
3: example_3_mathematical_analysis,
4: example_4_game_theory_positions,
5: example_5_multiple_pile_variants,
6: example_6_performance_analysis,
7: example_7_educational_tutorial,
8: example_8_advanced_integration,
}
try:
if args.all:
await run_all_examples()
elif args.example:
example_func = example_functions[args.example]
await example_func()
else:
# Interactive mode
print("🎮 NIM GAME EXAMPLES")
print("=" * 40)
print("Available examples:")
print(" 1. Basic Standard Nim")
print(" 2. Misère Nim")
print(" 3. Mathematical Analysis")
print(" 4. Game Theory Positions")
print(" 5. Multiple Pile Variants")
print(" 6. Performance Analysis")
print(" 7. Educational Tutorial")
print(" 8. Advanced Integration")
print(" 9. Run all examples")
choice = input("\nEnter example number (1-9): ").strip()
if choice == "9":
await run_all_examples()
elif choice in ["1", "2", "3", "4", "5", "6", "7", "8"]:
await example_functions[int(choice)]()
else:
print("Invalid choice. Please enter 1-9.")
return 1
except KeyboardInterrupt:
print("\nExamples interrupted by user.")
return 0
except Exception as e:
logger.error(f"Error in main: {e}")
return 1
return 0
if __name__ == "__main__":
# Run just a quick basic example for testing
print("Running basic Nim example...")
# Quick demo - just show that the game works
initial_piles = [3, 5, 7]
print(f"Initial Position: {initial_piles}")
# Create state manager for analysis
state = NimStateManager.initialize(pile_sizes=initial_piles)
print(f"Game state created: {state.piles}")
# Test legal moves generation (this was the failing part)
legal_moves = NimStateManager.get_legal_moves(state)
print(f"Generated {len(legal_moves)} legal moves successfully")
# Show a few moves
for i, move in enumerate(legal_moves[:3]):
print(
f" Move {i + 1}: {move.player} takes {move.stones_taken} from pile {
move.pile_index
}"
)
# Test applying a move
first_move = legal_moves[0]
new_state = NimStateManager.apply_move(state, first_move)
print(f"After move: {new_state.piles}")
print("✅ Nim example completed successfully")
sys.exit(0)