前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >一道算法题:德州扑克多家ALLIN如何分筹码?算法+代码

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

原创
作者头像
HullQin
修改于 2023-08-18 09:03:53
修改于 2023-08-18 09:03:53
6K00
代码可运行
举报
文章被收录于专栏:教你做小游戏教你做小游戏
运行总次数:0
代码可运行

难点

虽然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
代码运行次数:0
运行
AI代码解释
复制
{
  'chip': 1000,  # 手上有1000筹码(对局中下注时,这里的值会实时减少,对局结束后再把赢的部分加回来)
}

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

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

其中action包括:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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:
            bonus = bonus_together // len(max_score_seats)  # 除数不会是0,若为0,不会进入这里逻辑
            deliver_bonus += bonus
            room['players'][max_score_seat['id']]['chip'] += bonus
        # 为了避免小数,以上计算都是向下取整,下面做个补偿。座位靠后玩家有略微优势,瓜分筹码会多分一点点。
        # 为什么是座位靠后的玩家有优势?因为庄家是从座位靠前的玩家开始轮流的,座位靠后的玩家当庄家的次数相对少一点点。
        room['players'][max_score_seat['id']]['chip'] += bonus_together - deliver_bonus

这里有个逻辑:deliver_bonus记录本轮分配了多少筹码,作用是:1、避免小数点,2、避免算法漏洞导致筹码总量异常。

通过防御式编程,降低了错账风险。事实上最初版本确实有过bug,无法正确处理平分筹码的情况。幸亏有deliver_bonus,导致筹码总量没有变多变少,系统上记录的筹码量依然有参考价值,只要通过人工简单记着我欠你xxx,结合系统上的数据,就能很快的得到正确的数据。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
筹码分布原理探索
最近在学习一点股票的知识,可是发现自己在像个小白,找不到门路,可是如果不学习就会变韭菜。人们常说股市就如同赌博,这没有问题。但谁能说去买菜或者买东西不是博弈?只是量级的问题。所以很多问题并没有什么标准和规则,全凭自己如何看待。可是物质的运动规律是可以掌握的,问题是你是否掌握了足够的信息量。以及如何看待其中的关系。写这种文章的意义是什么,对于我来说就是整理思维,我特别喜欢辩证的思考问题,在不断的反驳自己能够让自己深度思考某些问题。然后对于这个世界有比较深入的解。
写一点笔记
2020/08/25
1.6K0
筹码分布原理探索
用python随机生成座位表
学习中总会遇到大大小小的考试,考试场地和考试座位的确立是考试准备工作的重要一环,那么能否用python随机生成座位表呢。
算法与编程之美
2023/11/13
3200
用python随机生成座位表
Kotlin版本的最大余额法实现(网上有Java版,解决百分比相加结果不等于100%)
object PercentKit { /** * 修复百分比结果相加不等于100的问题 * @param arr 原始值的数组 * @param sum 原始值之和 * @param idx 当前位置索引 * @param scale 精度 * * @return 返回结果乘以100后的值,比如 0.333 返回 33.3 */ fun getPercentValue(arr: List<Double>, su
飞奔去旅行
2021/02/01
1.3K0
Leetcode题解——849/950
我们将有人的座位的下标记录到一个list中,剩下的事情就是要找一个点,距离list中某个元素的最大距离
出其东门
2019/08/06
3510
Golang Leetcode 849. Maximize Distance to Closest Person.go
版权声明:原创勿转 https://blog.csdn.net/anakinsun/article/details/89378645
anakinsun
2019/05/05
3910
Codeforces 725B Food on the Plane
B. Food on the Plane time limit per test:2 seconds memory limit per test:256 megabytes input:standard input output:standard output A new airplane SuperPuperJet has an infinite number of rows, numbered with positive integers starting with 1 from cockpit to
Angel_Kitty
2018/04/08
8720
Codeforces 725B Food on the Plane
设计模式系列| 原型模式
Java 的 Cloneable (克隆) 接口就是立即可用的原型模式。任何类都可通过实现该接口来实现可被克隆的性质。
狼王编程
2021/06/01
3510
设计模式系列| 原型模式
LeetCode 2037. 使每位学生都有座位的最少移动次数
一个房间里有 n 个座位和 n 名学生,房间用一个数轴表示。 给你一个长度为 n 的数组 seats ,其中 seats[i] 是第 i 个座位的位置。 同时给你一个长度为 n 的数组 students ,其中 students[j] 是第 j 位学生的位置。
Michael阿明
2022/01/07
4520
设计模式-建造者模式(一)
建造者模式(Builder Pattern)是一种创建型设计模式,它可以将对象的构建过程分离出来,使得同样的构建过程可以创建不同的表示。在该模式中,一个类负责对象的创建,并将对象的创建过程分解为多个步骤,每个步骤都由一个独立的建造者类来实现。最终通过指导这些步骤来组装出一个完整的对象。
堕落飞鸟
2023/05/03
3111
设计模式系列| 建造者模式
建造者模式是一种创建型设计模式, 使你能够分步骤创建复杂对象。该模式允许你使用相同的创建代码生成不同类型和形式的对象。
狼王编程
2021/06/01
3800
设计模式系列| 建造者模式
LeetCode 1217. 玩筹码(脑筋急转弯)
1. 题目 数轴上放置了一些筹码,每个筹码的位置存在数组 chips 当中。 你可以对 任何筹码 执行下面两种操作之一(不限操作次数,0 次也可以): 将第 i 个筹码向左或者右移动 2 个单位,代价为 0。 将第 i 个筹码向左或者右移动 1 个单位,代价为 1。 最开始的时候,同一位置上也可能放着两个或者更多的筹码。 返回将所有筹码移动到同一位置(任意位置)上所需要的最小代价。 示例 1: 输入:chips = [1,2,3] 输出:1 解释:第二个筹码移动到位置三的代价是 1, 第一个筹码移动到位置三
Michael阿明
2020/07/13
4010
LeetCode 1845. 座位预约管理系统(set)
我的CSDN博客地址 https://michael.blog.csdn.net/
Michael阿明
2021/09/06
5590
Go语言中的建造者模式
在Go语言中,建造者模式可以通过接口和结构体来实现。接口定义了建造者的抽象,它有一些方法,用于设置不同的属性或组件。结构体实现了具体的建造者,它包含了一个产品类型的字段,用于存储构建过程中的结果。结构体也有一个方法,用于返回最终的产品对象。
运维开发王义杰
2023/08/10
1740
Go语言中的建造者模式
【数据结构和算法】到达首都的最少油耗
给你一棵 n 个节点的树(一个无向、连通、无环图),每个节点表示一个城市,编号从 0 到 n - 1 ,且恰好有 n - 1 条路。0 是首都。给你一个二维整数数组 roads ,其中 roads[i] = [ai, bi] ,表示城市 ai 和 bi 之间有一条 双向路 。
绿毛龟
2024/01/19
1910
【数据结构和算法】到达首都的最少油耗
1349. Maximum Students Taking Exam(DP,状态压缩)
Share Given a m * n matrix seats that represent seats distributions in a classroom. If a seat is broken, it is denoted by '#' character otherwise it is denoted by a '.' character.
ShenduCC
2020/02/17
5640
LeetCode精讲——1217.玩筹码(难度:简单)
有n 个筹码。第 i 个筹码的位置是position[i]。假设有3个筹码,1个放在1的位置,其余都放在2的位置,那么数组中的表示就是:position=[1,2,2]。
爪哇缪斯
2023/05/10
2160
LeetCode精讲——1217.玩筹码(难度:简单)
学界|德州扑克算法幕后研发者CMU博士Noam Brown专访:AI如何打败顶级人类牌手?
在上个月举行的单挑无限注德州扑克( heads-up no-limit hold’em)人机对战中,由卡耐基梅隆大学研发的AI程序Libratus以每100手14倍大盲(译者注:缩写为14bb/100,意为玩100手牌,平均能赢对手14倍大盲注)完胜世界级人类玩家团队,震惊所有在场人员。 比赛共打12万手,最后Libratus赢得了1,776,250个筹码,近90个买入。虽然人类玩家输惨了,但幸运的是他们并不需要真的自掏腰包把输掉的钱给赢家Libratus(虽然他们来参加这场对战也需缴纳费用)。 Lib
AI科技评论
2018/03/09
1.8K0
学界|德州扑克算法幕后研发者CMU博士Noam Brown专访:AI如何打败顶级人类牌手?
德州扑克被AI攻破?刚进行到围棋九路盘而已
德州扑克被AI攻克?才刚开始 作者:余小鲁 作者系理论物理博士,人工智能专家。新浪扑克学院特邀讲师 Sunday, 31 December 2017 余小鲁博士(右)在新浪总部大厦接受专访 不只是一个纸牌游戏——德州扑克AI的意义 和谷歌研究围棋AI一样,卡内基梅隆大学研究德州扑克AI,也是“志不在此”。因为德州扑克中存在很多和社会生活类似的普遍难题,此研究才有根本重要的意义。德州扑克AI的意义 德州扑克AI的里程碑——Libratus(冷扑) Libratus是“balanced”的拉丁文,意为均
企鹅号小编
2018/01/10
9040
德州扑克被AI攻破?刚进行到围棋九路盘而已
Maximize Distance to Closest Person
1. Description 2. Solution Version 1 class Solution { public: int maxDistToClosest(vector<int>&
Tyan
2019/05/25
4230
All In! 我学会了用强化学习打德州扑克
选自willtipton 机器之心编译 参与:Jane W、蒋思源 最近,强化学习(RL)的成功(如 AlphaGo)取得了大众的高度关注,但其基本思路相当简单。下面我们在一对一无限注德州扑克游戏上进
机器之心
2018/05/08
1.4K0
All In! 我学会了用强化学习打德州扑克
相关推荐
筹码分布原理探索
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验