bindbox-game/scripts/matching_game_demo.py
邹方成 45815bfb7d chore: 清理无用文件与优化代码结构
refactor(utils): 修复密码哈希比较逻辑错误
feat(user): 新增按状态筛选优惠券接口
docs: 添加虚拟发货与任务中心相关文档
fix(wechat): 修正Code2Session上下文传递问题
test: 补充订单折扣与积分转换测试用例
build: 更新配置文件与构建脚本
style: 清理多余的空行与注释
2025-12-18 17:35:55 +08:00

170 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import hmac
import hashlib
import time
from typing import List, Dict, Any, Tuple
from collections import Counter
class SecureBlindBoxRNG:
"""使用 HMAC-SHA256 的加密安全随机数生成器"""
def __init__(self):
self.server_seed = os.urandom(32)
self.server_seed_hash = hashlib.sha256(self.server_seed).hexdigest()
self.nonce = 0
def _generate_random_bytes(self, context: str) -> bytes:
self.nonce += 1
message = f"{context}|nonce:{self.nonce}".encode()
return hmac.new(self.server_seed, message, hashlib.sha256).digest()
def secure_randint(self, max_value: int, context: str = "rand") -> int:
if max_value <= 0:
return 0
rand_bytes = self._generate_random_bytes(context)
return int.from_bytes(rand_bytes[:8], 'big') % max_value
def secure_shuffle(self, items: List) -> List:
items = items[:]
n = len(items)
for i in range(n - 1, 0, -1):
j = self.secure_randint(i + 1, f"shuffle:{i}")
items[i], items[j] = items[j], items[i]
return items
class MatchingGame:
"""对对碰游戏 - 可视化版本"""
def __init__(self):
self.rng = SecureBlindBoxRNG()
# 11种卡牌每种9张
self.card_types = ['🍎', '🍊', '🍋', '🍇', '🍓', '🍒', '🥝', '🍑', '🍌', '🍉', '🫐']
self.card_list = [card for card in self.card_types for _ in range(9)]
# 安全洗牌
self.card_list = self.rng.secure_shuffle(self.card_list)
self.hand = self.card_list[:9]
self.deck = self.card_list[9:]
self.total_pairs = 0
self.round = 0
def display_hand(self):
"""显示当前手牌"""
print(f"\n{'='*50}")
print(f"{self.round} 轮 | 手牌: {len(self.hand)}张 | 牌堆: {len(self.deck)}")
print(f"{'='*50}")
print(f" 手牌: {' '.join(self.hand)}")
# 统计每种牌的数量
counter = Counter(self.hand)
pairs_info = []
for card, count in counter.items():
if count >= 2:
pairs_info.append(f"{card}×{count}")
if pairs_info:
print(f" 可配对: {', '.join(pairs_info)}")
else:
print(f" 可配对: 无")
def find_and_remove_pairs(self) -> Tuple[int, List[str]]:
"""找出并移除配对的牌"""
counter = Counter(self.hand)
pairs_found = 0
pairs_detail = []
remaining = []
for card, count in counter.items():
pairs = count // 2
if pairs > 0:
pairs_found += pairs
pairs_detail.append(f"{card}×{pairs*2}")
# 剩余的单张
if count % 2 == 1:
remaining.append(card)
return pairs_found, remaining, pairs_detail
def play_round(self, is_first: bool = False):
"""执行一轮游戏"""
self.round += 1
# 第一轮:如果有🍎,额外抽牌
if is_first:
apple_count = self.hand.count('🍎')
if apple_count > 0:
print(f"\n 🎁 发现 {apple_count} 张🍎,额外抽取 {apple_count} 张牌!")
extra = self.deck[:apple_count]
self.hand.extend(extra)
self.deck = self.deck[apple_count:]
print(f" 抽到: {' '.join(extra)}")
self.display_hand()
# 配对
pairs, remaining, pairs_detail = self.find_and_remove_pairs()
if pairs > 0:
print(f"\n ✨ 配对成功: {', '.join(pairs_detail)}")
print(f" 本轮配对: {pairs}")
self.total_pairs += pairs
# 抽取新牌
draw_count = min(pairs, len(self.deck))
if draw_count > 0:
new_cards = self.deck[:draw_count]
self.deck = self.deck[draw_count:]
remaining.extend(new_cards)
print(f" 从牌堆抽取: {' '.join(new_cards)}")
self.hand = remaining
return True
else:
print(f"\n ❌ 无法配对,游戏结束")
self.hand = remaining
return False
def play(self):
"""完整游戏流程"""
print("\n" + "🎮" * 20)
print(" 欢迎来到 对对碰 游戏!")
print("🎮" * 20)
print(f"\n游戏规则:")
print(f" 1. 初始手牌 9 张")
print(f" 2. 相同的牌可以配对消除")
print(f" 3. 每配对一次,从牌堆抽取相应数量的新牌")
print(f" 4. 直到无法配对为止")
print(f"\n种子哈希: {self.rng.server_seed_hash[:16]}...")
# 第一轮
can_continue = self.play_round(is_first=True)
# 后续轮次
while can_continue:
input("\n 按回车继续...")
can_continue = self.play_round(is_first=False)
# 游戏结束
print(f"\n{'='*50}")
print(f" 🏆 游戏结束!")
print(f" 总配对数: {self.total_pairs}")
print(f" 剩余手牌: {len(self.hand)}{' '.join(self.hand)}")
print(f" 剩余牌堆: {len(self.deck)}")
print(f"{'='*50}")
return {
"total_pairs": self.total_pairs,
"remaining_hand": self.hand,
"remaining_deck_count": len(self.deck),
"seed_hash": self.rng.server_seed_hash
}
if __name__ == "__main__":
game = MatchingGame()
result = game.play()