相关文章推荐
活泼的芒果  ·  传说中的大叔文_子泽华【完结】在线阅读_52书库·  7 月前    · 
苦恼的伤痕  ·  Hai Tang Guo Yue ...·  8 月前    · 
玉树临风的硬币  ·  Officers: Yingli ...·  8 月前    · 
聪明的瀑布  ·  对等与免费:骨干网网间结算重磅新政出台或将引 ...·  1 年前    · 
悲伤的熊猫  ·  中华名医号|危险的肿块不会“喊疼”,专家:出 ...·  1 年前    · 
小百科  ›  一道算法题:德州扑克多家ALLIN如何分筹码?算法+代码开发者社区
chip
不敢表白的牛肉面
1 年前
HullQin

一道算法题:德州扑克多家ALLIN如何分筹码?算法+代码

原创
前往小程序,Get 更优 阅读体验!
立即前往
腾讯云
开发者社区
文档 建议反馈 控制台
首页
学习
活动
专区
工具
TVP
最新优惠活动
文章/答案/技术大牛
发布
首页
学习
活动
专区
工具
TVP 最新优惠活动
返回腾讯云官网
HullQin
首页
学习
活动
专区
工具
TVP 最新优惠活动
返回腾讯云官网
社区首页 > 专栏 > 一道算法题:德州扑克多家ALLIN如何分筹码?算法+代码

一道算法题:德州扑克多家ALLIN如何分筹码?算法+代码

原创
作者头像
HullQin
修改 于 2023-08-18 17:03:53
2.7K 0
修改 于 2023-08-18 17:03:53
举报
文章被收录于专栏: 教你做小游戏 教你做小游戏

难点

虽然95%以上的场景,都是一家胜出,但是你要做系统,就要覆盖100%的场景。

给你举个特殊场景的例子:

  • 玩家1: ALLIN 100 牌最大
  • 玩家2: ALLIN 200 牌也最大
  • 玩家3: ALLIN 200 牌第二大
  • 玩家4: ALLIN 400 牌也第二大
  • 玩家5: ALLIN 1000 牌第三大
  • 玩家6: ALLIN 2000 牌第四大
  • 玩家7: ALLIN 400 牌最小

整个底池:4300,这时候怎么分筹码?

你先思考一下。即使你不知道,也可以想想,如果让你来设计分法,怎么分最公平?

我可以明确告诉你:不是玩家1和2平分4300,也不是玩家1和玩家2按1:2比例分这4300。

分法

先按照所有人拥有的筹码排序:玩家1、2、3、7、4、5、6。

然后,以每人下注100作为主池,做第一次分配。

第一次分配完毕后,每人还有剩余未分配的筹码,再选最小的下注,做第二次分配,这叫边池。

以此类推,直到分配结束。如下表:

至此,分配结束。你看懂分配算法了嘛?

数据结构

我们必须要记清楚,每个玩家本局总共投入了多少筹码,才方便后续计算。

以下变量类型都是python。

第一,定义 room['players'] 是本房间(群聊)的玩家筹码总量信息,是个dict,key是用户ID,value是个dict,形如:

代码语言: javascript
复制
{
  'chip': 1000,  # 手上有1000筹码(对局中下注时,这里的值会实时减少,对局结束后再把赢的部分加回来)
}

第二,定义 playing_seats 是本局的玩家信息,是个list,每一项是个dict,形如:

代码语言: javascript
复制
{
  'i': 1,  # 座位号,从0开始
  'id': 'uuid',  # 用户ID,跟room['players']的key对应
  'all_chip': 100,  # 本局投入的所有筹码
  'action': SeatAction.Fold,  # 最终的行动
}

其中 action 包括:

代码语言: javascript
复制
import enum
class SeatAction(enum.IntEnum):
    WAIT = 0
    CHECK = 1
    CALL = 2
    RAISE = 3
    ALLIN = 4
    FOLD = 5
    WIN = 6  # 胜利,不摊牌(适用其他人都Fold的情况)
    WIN_SHOW = 7  # 胜利,需要摊牌
    LOSE_SHOW = 8  # 失败,需要摊牌

开发

排序逻辑

注: cards 就是所有牌,是一个长度52的list,每一项的值都是0-51,开局前会做一次shuffle(随机打乱顺序)。底牌、玩家的牌的信息,就藏在这个list中。

先计算5张底牌(PS:我是不是很严谨,按照了专业比赛的发牌顺序hhh😂):

代码语言: javascript
复制
playerCount = len(playing_seats)
five_cards = [cards[playerCount * 2 + 1], cards[playerCount * 2 + 2], cards[playerCount * 2 + 3], cards[playerCount * 2 + 5], cards[playerCount * 2 + 7]]

计算一下每个未Fold牌玩家的牌的得分,这里 get_max_score() 来自 《 这是一道算法题:如何比较徳州扑克牌大小?(附带python实现) 》 。算出得分后,按付出的筹码从小到大排序,放到 final_seats 里,为什么叫final呢?表示他们会最终参与到分筹码流程里(都是亮牌的人)。

此外,Fold牌的玩家,得分为(-1, -1),他们永远不会有收益(因为总有未Fold牌的人会收获他们所有筹码),所以他们不在 final_seats 里。

代码语言: javascript
复制
final_seats = []
for i, s in enumerate(playing_seats):
    if s['action'] < SeatAction.FOLD:
        s['score'] = get_max_score(five_cards + [cards[i], cards[i + playerCount]])
        final_seats.append(s)
    else:
        s['score'] = (-1, -1)
final_seats = sorted(final_seats, key=lambda x: x['all_chip'])

核心逻辑

整体是个大循环,遍历 final_seats ,每一次遍历,表示第N次分配。

每次分配中,其他玩家每个人都要付出至多 final_seat['all_chip'] 。

每次分配,计算 bonus_together ,表示这轮分配的总筹码,加上赢家投入的筹码、所有输家要付出的筹码。之后本轮赢家会平分 bonus_together 。

代码语言: javascript
复制
done_seats = set()
for final_seat in final_seats:
    if final_seat['i'] in done_seats:
        continue
    # 用current_chip存储本轮的最高分配额度,因为final_seat['all_chip']会变,不能一直引用final_seat['all_chip']
    current_chip = final_seat['all_chip']
    # 本轮分配的bonus_together
    bonus_together = 0
    # 以下逻辑,计算本轮分配最大牌力的玩家(们)
    max_score = (-1, -1)
    max_score_seats = []
    for other_seat in playing_seats:
        if other_seat['i'] in done_seats: 
           # 表明该玩家筹码分配完毕,就跳过该玩家
           continue
        if other_seat['score'] == max_score:
            max_score_seats.append(other_seat)
        elif other_seat['score'] > max_score:
            max_score = other_seat['score']
            max_score_seats = [other_seat]
        if other_seat['all_chip'] > current_chip:
            # 这个玩家投入的筹码超过“至多付出筹码”,只会付出“至多付出筹码”,后续还会参与下轮分配
            bonus_together += current_chip
            other_seat['all_chip'] -= current_chip
        else:
            # 这个玩家投入的筹码不够“至多付出筹码”,就全要给赢家,自己分配完毕
            done_seats.add(other_seat['i'])
            bonus_together += other_seat['all_chip']
            other_seat['all_chip'] = 0
    # 以上逻辑,计算本轮分配最大牌力的玩家(们)
    if bonus_together > 0:
        # deliver_bonus用于记录本轮分配了多少筹码,避免小数点,避免算法漏洞导致筹码总量异常
        deliver_bonus = 0
		max_score_seat = playing_seats[-1]
        for max_score_seat in max_score_seats:
 
推荐文章
活泼的芒果  ·  传说中的大叔文_子泽华【完结】在线阅读_52书库
7 月前
苦恼的伤痕  ·  Hai Tang Guo Yue “Autumn Mood&quot; 53&quot; Guzheng 海棠國樂135cm ...
8 月前
玉树临风的硬币  ·  Officers: Yingli Green Energy Holdings Company Limited 英利綠色 ...
8 月前
聪明的瀑布  ·  对等与免费:骨干网网间结算重磅新政出台或将引发产业格局巨变_ ...
1 年前
悲伤的熊猫  ·  中华名医号|危险的肿块不会“喊疼”,专家:出现在这4个部位别大意_ ...
1 年前
Link管理   ·   Sov5搜索   ·   小百科
小百科 - 百科知识指南