refactor: 重构抽奖逻辑以支持可验证凭据 feat(redis): 集成Redis客户端并添加配置支持 fix: 修复订单取消时的优惠券和库存处理逻辑 docs: 添加对对碰游戏前端对接指南和示例JSON test: 添加对对碰游戏模拟测试和验证逻辑
125 lines
4.0 KiB
Python
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()
|