胜负的关键在于随机生成的初始化球堆状态,对于一个完全随机生成的球堆,很显然 A 的赢面远大于 B,换句话说,在随机初始化的条件下,先手具有绝对的先发优势。
1 2 3 4 5 6 7
============================================================ STATISTICS ============================================================ Total cases: 1000 A wins: 821 (82.1%) B wins: 179 (17.9%) ============================================================
""" Game solver for the ball taking game using dynamic programming.
This module implements a solver for a two-player game where players take balls from piles. The player who takes the last ball wins. """
from functools import lru_cache from typing importTuple, List, Dict
classGameSolver: """Solver for the ball taking game using dynamic programming."""
def__init__(self, max_balls_per_take: int): """ Initialize the game solver.
Args: max_balls_per_take: Maximum number of balls allowed to take per turn (k). """ self.max_balls = max_balls_per_take self.modulo = max_balls_per_take + 1
defcalculate_xor_sum(self, piles: Tuple[int, ...]) -> int: """ Calculate the XOR sum of pile sizes modulo (k+1).
According to the mathematical theory, the winner is determined by whether the XOR sum is zero or not.
Args: piles: Tuple representing the current pile sizes.
Returns: XOR sum of (pile_i % (k+1)) for all piles. """ xor_sum = 0 for pile in piles: xor_sum ^= pile % self.modulo return xor_sum
defget_next_states(self, piles: Tuple[int, ...]) -> List[Tuple[int, ...]]: """ Generate all valid next states from current pile configuration.
Args: piles: Current pile sizes as a tuple.
Returns: List of all possible next states (pile configurations). """ next_states = []
for i, pile_size inenumerate(piles): # Try taking 1 to min(k, pile_size) balls from pile i max_take = min(self.max_balls, pile_size)
Args: piles: Current pile sizes as a tuple. is_a_turn: True if it's A's turn, False if it's B's turn.
Returns: True if A can force a win from this state, False otherwise. """ # Base case: no piles left, game over ifnot piles: # If it's A's turn and no balls, A loses (B took the last ball) # If it's B's turn and no balls, A wins (A took the last ball) returnnot is_a_turn
next_states = self.get_next_states(piles)
if is_a_turn: # A's turn: A wins if there exists ANY move that leads to A's victory for next_state in next_states: ifself._dp_solve(next_state, False): returnTrue returnFalse else: # B's turn: A wins only if ALL moves lead to A's victory for next_state in next_states: ifnotself._dp_solve(next_state, True): returnFalse returnTrue
defsolve_with_dp(self, piles: Tuple[int, ...]) -> str: """ Solve the game using dynamic programming.
Args: piles: Initial pile sizes.
Returns: 'A' if A (first player) has a winning strategy, 'B' otherwise. """ # Normalize piles: remove zeros and sort for consistency normalized_piles = tuple(sorted([(p % (self.max_balls + 1)) for p in piles if p > 0]))
result = self._dp_solve(normalized_piles, True) return"A"if result else"B"
defsolve_with_theory(self, piles: Tuple[int, ...]) -> str: """ Solve the game using mathematical theory (XOR sum).
According to the theorem: - If XOR sum != 0, first player (A) wins - If XOR sum == 0, second player (B) wins
Args: piles: Initial pile sizes.
Returns: 'A' if A (first player) has a winning strategy, 'B' otherwise. """ xor_sum = self.calculate_xor_sum(piles) return"A"if xor_sum != 0else"B"
defpredict_winner(self, piles: Tuple[int, ...], method: str) -> Dict[str, any]: """ Predict the winner for given initial pile configuration.
Args: piles: Initial pile sizes. method: 'theory' for mathematical solution, 'dp' for dynamic programming.
Returns: Dictionary containing: - winner: 'A' or 'B' - xor_sum: XOR sum of the position - method: Method used for prediction """ normalized_piles = tuple(sorted([p for p in piles if p > 0])) xor_sum = self.calculate_xor_sum(normalized_piles)
if method == "theory": winner = self.solve_with_theory(normalized_piles) if method == "dp": winner = self.solve_with_dp(normalized_piles)