0054 德州扑克
* * * *
拉格朗日计划
* * * *
德州扑克

在德州扑克中,每位玩家共有五张牌,其牌型由高到低分别为:
  • 皇家同花顺:同一花色的10、J、Q、K、A。

  • 同花顺:既是顺子也是同花。

  • 四条:包含四张同样的牌面。

  • 葫芦:三条带一个对子。

  • 同花:五张牌同一花色。

  • 顺子:五张连续的牌面。

  • 三条:三张同样牌面带两个单张。

  • 两对:包含两个不同的对子。

  • 对子:两张牌面一样的牌加三个单张。

  • 单牌:其它情况。

牌面由小到大的顺序是:2、3、4、5、6、7、8、9、10、J、Q、K、A。

胜负的规则是从高到低依次比较牌型,从皇家同花顺开始,若甲能组成皇家同花顺,乙不能,则甲胜。若双方都不能组成此牌型,则考察下一牌型。若双方都能组成某一牌型,则先比较组成该牌型的最大牌面,最大牌面大者胜,否则比较组成该牌型的次大牌面。。。若所有牌面都相同,则再考察下一牌型。

下面是一些能帮助理解的例子:
  • 甲:♡5 ♣5 ♠6 ♠7 ♢K
    乙:♣2 ♠3 ♠8 ♢8 ♢10
    乙胜 (对子5 小于 对子8)

  • 甲:♢5 ♣8 ♠9 ♠J ♣A
    乙:♣2 ♣5 ♢7 ♠8 ♡Q
    甲胜 (单牌A 大于 单牌Q)

  • 甲:♢2 ♣9 ♠A ♡A ♣A
    乙:♢3 ♢6 ♢7 ♢10 ♢Q
    乙胜 (三条A 小于 方片同花)

  • 甲:♢4 ♠6 ♡9 ♡Q ♣Q
    乙:♢3 ♢6 ♡7 ♢Q ♠Q
    甲胜 (对子Q、最大单牌9 大于 对子Q、最大单牌7)

  • 甲:♡2 ♢2 ♣4 ♢4 ♠4
    乙:♣3 ♢3 ♠3 ♠9 ♢9
    甲胜 (葫芦三条4 大于 葫芦三条3)

文件poker.txt中含两名玩家一千局扑克游戏的情况。其中每行有10张牌,相邻两张牌之间用一个空格隔开:前五张牌属于甲,后五张牌属于乙。

文件中花色♠♡♣♢分别用S、H、C、D表示,10用T表示,且牌面在前,花色在后。

所有的手牌都是有效组合(没有无效字符或重复牌),且每一局都有明确的赢家。

问其中有多少局甲获胜?

本题难度:



解答

按规则依次检查牌型和点数即可,除了过程略显繁琐以外也没什么可多说的。结果是$376$。

deck=[]
with open("054_poker.txt") as f:
    for line in f:
        deck.append(line.split())

cardDict=dict(zip("23456789TJQKA",range(2,15)))

def handleCards(s):
    return sorted([[cardDict[s[i][0]],s[i][1]] for i in range(5)])

def getMask(Cards):
    return Cards[0][0]+Cards[1][0]*16+Cards[2][0]*256+Cards[3][0]*4096+Cards[4][0]*65536

def isFlush(Cards):
    return all(Cards[i][1]==Cards[0][1] for i in range(1,5))

def isStraight(Cards):
    return all(Cards[i][0]+1==Cards[i+1][0] for i in range(4))

def isBomb(Cards):
    return Cards[0][0]==Cards[3][0] or Cards[1][0]==Cards[4][0]

def getFullhouse(Cards):
    if Cards[0][0]==Cards[2][0] and Cards[3][0]==Cards[4][0]:
        return Cards[0][0]*16+Cards[3][0]
    elif Cards[2][0]==Cards[4][0] and Cards[0][0]==Cards[1][0]:
        return Cards[4][0]*16+Cards[0][0]
    else:
        return 0

def getTriple(Cards):
    if Cards[0][0]==Cards[2][0]:
        return (Cards[0][0],Cards[3][0]+Cards[4][0]*16)
    elif Cards[1][0]==Cards[3][0]:
        return (Cards[1][0],Cards[0][0]+Cards[4][0]*16)
    elif Cards[2][0]==Cards[4][0]:
        return (Cards[2][0],Cards[0][0]+Cards[1][0]*16)
    else:
        return []

def getPairs(Cards):
    a=0
    b=0
    cDict={}
    for c in Cards:
        if c[0] in cDict:
            cDict[c[0]]=cDict[c[0]]+1
            if a==0:
                a=c[0]
            else:
                b=c[0]
        else:
            cDict[c[0]]=1
    if len(cDict)==5:
        return (0,0,getMask(Cards))
    elif len(cDict)==4:
        return (0,a,getMask(sorted([[0,c[1]] if c[0]==a else c for c in Cards])))
    else:
        return (b,a,[c[0] for c in Cards if c[0]!=a and c[0]!=b][0])

def cardVal(Cards):
    res=[0,0,0,0,0,0,0,0,0]
    if isFlush(Cards) and isStraight(Cards):
        res[0]=Cards[4][0]
        return res
    elif isBomb(Cards):
        res[1]=Cards[1][0]
        res[-1]=Cards[0][0] if Cards[1][0]==Cards[4][0] else Cards[4][0]
        return res
    elif getFullhouse(Cards):
        res[2]=getFullhouse(Cards)
    elif isFlush(Cards):
        res[3]=getMask(Cards)
    elif isStraight(Cards):
        res[4]=Cards[4][0]
    elif getTriple(Cards):
        res[5]=getTriple(Cards)[0]
        res[-1]=getTriple(Cards)[1]
    else:
        res[6],res[7],res[8]=getPairs(Cards)
    return res

print sum(1 for hands in deck if cardVal(handleCards(hands[:5]))>cardVal(handleCards(hands[5:])))