import random import collections # Configuration: 11 types, 9 cards each. Total 99 cards. CARD_TYPES = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K'] CARDS_PER_TYPE = 9 BOARD_SIZE = 9 def create_deck(): """Create a full deck of 99 cards.""" deck = [] for t in CARD_TYPES: for _ in range(CARDS_PER_TYPE): deck.append(t) random.shuffle(deck) return deck def play_one_game(): """Simulate one full game session.""" # 1. Initialize full_deck = create_deck() board = full_deck[:BOARD_SIZE] deck = full_deck[BOARD_SIZE:] pairs_found = 0 # Loop until game over while True: # 2. Check for matches on board # Strategy: Always eliminate the first pair found. # (In real game, user choice might matter, but for max pairs, # elimination order rarely changes the total count if we have infinite reshuffles) counts = collections.Counter(board) match_type = None for t, count in counts.items(): if count >= 2: match_type = t break if match_type: # ELIMINATE pairs_found += 1 # Remove 2 instances of match_type removed_count = 0 new_board = [] for card in board: if card == match_type and removed_count < 2: removed_count += 1 continue # Skip (remove) new_board.append(card) board = new_board # Fill from deck while len(board) < BOARD_SIZE and len(deck) > 0: board.append(deck.pop(0)) else: # DEADLOCK (No matches on board) if len(deck) == 0: # Game Over break else: # RESHUFFLE # Collect all remaining cards (board + deck) remaining = board + deck random.shuffle(remaining) # Refill board and deck board = remaining[:BOARD_SIZE] deck = remaining[BOARD_SIZE:] # Check if new board has matches. # Theoretically possibility of repeated deadlocks, but loop continues. # To prevent infinite loop in case of bad logic (e.g. 1 card left), check solvability. # But here we have types. If we have pairs left in TOTAL, reshuffle will eventually bring them to board. # Optimization: If total remaining of every type is < 2, we can never match again. total_counts = collections.Counter(remaining) can_match = False for t, c in total_counts.items(): if c >= 2: can_match = True break if not can_match: # Impossible to match anything more break return pairs_found def run_simulation(times=10000): print(f"Simulating {times} games...") print(f"Config: {len(CARD_TYPES)} types x {CARDS_PER_TYPE} cards = {len(CARD_TYPES)*CARDS_PER_TYPE} total.") results = [] for i in range(times): score = play_one_game() results.append(score) if (i+1) % 1000 == 0: print(f"Progress: {i+1}/{times}") max_score = max(results) min_score = min(results) avg_score = sum(results) / len(results) print("\n=== Results ===") print(f"Max Pairs: {max_score}") print(f"Min Pairs: {min_score}") print(f"Avg Pairs: {avg_score:.2f}") # Theoretical Max # floor(9/2) * 11 = 4 * 11 = 44 print(f"Theoretical Max: {int(CARDS_PER_TYPE/2) * len(CARD_TYPES)}") # Distribution dist = collections.Counter(results) print("\nDistribution:") for score in sorted(dist.keys()): print(f"{score} pairs: {dist[score]} times") if __name__ == "__main__": run_simulation()