bindbox-game/scripts/matching_simulation.py
邹方成 e2782a69d3 feat: 添加对对碰游戏功能与Redis支持
refactor: 重构抽奖逻辑以支持可验证凭据
feat(redis): 集成Redis客户端并添加配置支持
fix: 修复订单取消时的优惠券和库存处理逻辑
docs: 添加对对碰游戏前端对接指南和示例JSON
test: 添加对对碰游戏模拟测试和验证逻辑
2025-12-21 17:31:32 +08:00

125 lines
4.0 KiB
Python

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()