在线TCG模拟玩家被动洗牌作弊的算法设计咨询
TCG 可控随机洗牌算法实现方案
以下是三种可落地的实现方案,可满足你要的随机+防同类卡牌聚集的需求,同时可适配万智牌等TCG的地牌分布约束:
方案1:带约束校验的Fisher-Yates洗牌
这是实现成本最低的方案,适合卡组规模在40-100张的常规TCG场景:
- 前置配置:先给所有卡牌打上类型标签,提前定义约束规则,比如万智牌场景可设置
任意连续10张内地牌数量区间为[3,7]、相同卡牌连续出现最多2张等规则。 - 实现逻辑:
- 先运行一次标准Fisher-Yates洗牌得到初始随机序列
- 用滑动窗口遍历整个序列,检查所有预设约束是否全部满足
- 符合约束直接输出序列,不符合则重新执行洗牌+校验流程,直到拿到符合要求的结果
- 优化点:可设置最大重试次数(比如20次),如果多次重试仍不满足,可临时放宽10%的约束阈值,避免程序卡死。
方案2:加权互斥逐位采样算法
完全匹配你提到的权重互斥思路,可灵活调整不同卡牌的约束强度:
- 前置配置:给每类/每张卡牌设置自定义互斥权重规则,比如地牌的互斥权重为3,意思是每出一张地牌,后续位置抽到地牌的概率对应降低;相同卡牌的互斥权重可设为5,大幅压低连续出相同卡的概率。
- 实现步骤:
- 将所有卡牌放入待选池,统计各类卡牌的剩余数量
- 每次抽取卡牌前,先给待选池中每张卡计算实时权重得分:基础权重(默认100)减去最近N张牌中出现过同类型/同卡牌的扣分项,比如最近10张里有6张地牌,当前待选的地牌就扣90分,得分低于0的直接移出本次可选列表
- 按照每张卡的最终权重做加权随机抽取,抽到的卡放入结果序列,从待选池移除
- 重复2-3步直到待选池为空
- 优势:可精准控制连续N张内的同类型卡牌数量,比如万智牌地牌约束可直接配置为:最近10张内每多1张地牌,待选地牌权重减15,少于3张地牌时额外加50分权重,即可把地牌分布控制在你要的3-7张区间内。
核心逻辑示例代码:
# 万智牌地牌约束的权重计算示例 def calculate_card_weight(current_card, recent_cards): base_weight = 100 # 统计最近10张内地牌数量 recent_land_num = sum(1 for c in recent_cards[-10:] if c["type"] == "land") if current_card["type"] == "land": # 连续10张已经有7张以上地牌时直接禁止抽取 if recent_land_num >= 7: return 0 # 连续10张地牌少于3张时提高抽取概率 if recent_land_num < 3: base_weight += 50 # 每多一张地牌扣15分权重 base_weight -= recent_land_num * 15 # 禁止连续出现3张相同卡牌 if len(recent_cards) >= 2 and recent_cards[-1]["id"] == current_card["id"] and recent_cards[-2]["id"] == current_card["id"]: return 0 return max(base_weight, 0)
方案3:模拟真实人手洗牌的物理模拟算法
输出的牌序最贴合真实玩家实际洗牌的分布,体感更自然:
- 实现步骤:
- 把卡组随机分成2-3堆,每堆大小随机,最大不超过总卡组的60%
- 每堆内部先做一次轻度随机打乱
- 交替从各堆顶部抽取1-2张牌放入结果序列,直到所有堆被抽完
- 重复上述过程3-4次,模拟真实玩家洗3-4轮牌的效果
- 最后过一遍约束校验,不符合要求则多洗一轮即可
- 优势:天然降低了同类卡牌聚集的概率,很少出现完全随机洗牌里那种10张全是地牌或者全没有地牌的极端情况。
上线前建议先跑上万次模拟测试,统计牌序分布,调整权重/约束参数,确保极端情况出现概率低于千分之一,符合玩家对局预期。
内容的提问来源于stack exchange,提问作者Martin Brandel
相关产品推荐
相关产品推荐

