在纸牌游戏中,一手包含五张牌并且每一手都有自己的排序,从低到高的顺序如下:

  • 大牌:牌面数字最大
  • 一对:两张牌有同样的数字
  • 两对:两个不同的一对
  • 三条:三张牌有同样的数字
  • 顺子:所有五张牌的数字是连续的
  • 同花:所有五张牌有同样的花色
  • 船牌:三张同样数字的牌加一个一对
  • 四条:四张牌有同样的数字
  • 同花顺:牌面数字连续且有同样的花色
  • 皇家同花顺:由\(10,J,Q,K,A\)五张牌构成的同花顺

十三张牌根据牌面数字从小到大排序为:\(2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, A\)

如果两个玩家所持的一手牌排序相同,那么牌面数字较大的获胜。比如,一对八要大于一对五(见下面例一)。但是如果两手牌排序打平,比如两个玩家都有一对\(Q\),那么比较剩下的牌中的最大者(见下面例四)。如果这张牌仍然打平,则比较其它剩下的牌中的最大者,依次类推。

考虑两个玩家所持的下面五手牌,观察他们的胜负情况:

文本文件poker.txt中包含一千条随机生成的两个玩家的牌面数据,每一行包含十张牌(用一个空格分开),前五张牌是第一个玩家的,后五张牌是第二个玩家的。你可以认为所有牌都是有效的,每个玩家所持的牌没有特定的顺序,并且在每一手牌中总有一个确定的获胜者。求在这一千手牌中玩家一获胜了多少次?

分析:这是我们到目前为止看到的文字最多的题目,而且我为了解决这个题目所写的代码也是到目前行数最多的。和以前的题目不同,这道题目的难点并不在于其中的数学原理,而是要对这个游戏的规则本身有清楚的了解,并且能够把这些规划用代码表示出来。题目本身介绍规则的介绍不是很清楚,我建议大家参考这篇维基百科,里面对这个游戏的规则有更透彻清晰的介绍。总结起来游戏的规则有这么几条:第一,每一手牌有九个可能的排序(皇家同花顺就是同花顺,所以不用单独考虑),排序高的牌大于排序低的牌;第二,如果两手牌的排序相同,则需要根据这一手牌的不同类型来进行排序,总结起来也有两种情况:(1)如果类型中不涉及对子以及各种条牌,也就是同花顺、同花、顺子以及大牌这四种类型时,只需要对牌面数字形成的列表从大到小排序,然后比较两个列表即可。因为比较列表时,它会从第一个数字陆续向后比较,因此和这四种类型的牌的比较方式相同;(2)剩下五种涉及到对子和条牌的类型,也即四条、三条、船牌、二对和一对,则需要首先按条或者对分组,然后对条或者对中的牌面数字分别比较大小,这里可以用到python中Counter这个数据结构。

由于解题过程涉及大量的逻辑判断,为了让代码结构更加清晰,我使用了面向对象的方式来组织代码。我编写了两个类:第一类是纸牌类,它有两个属性,分别是花色与牌面数字,为了让之后的比较更加方便,我把\(T,J,Q,K,A\)五个牌面分别映射至对应的数字。第二个类表示五张牌形成的一手,这个类有八个属性,分别表示五张牌的牌面数字、五张牌的花色、五张牌牌面数字的分组统计、第一个分组的牌面数字、第二个分组的牌面数字、五张牌花色的类型数量、五张牌面数字从大到小排序的列表以及五张牌从大到小排序后,前后两个数字之差构成的集合。此外,这个类有两个方法:第一个方法用来判断这一手牌的类型,返回每一手牌的类型名称以及类型排序;第二个方法用来比较这一手牌和另一手牌的大小,使用的是我们前面总结的比较各手之间大小的规则。

最后,我们从文本文件中导入数据,把每一行数据拆分为第一个玩家和第二个玩家,并记录第一个玩家获胜的次数,最后返回这个次数即为题目所求。代码如下:

# time cost = 42.1 ms ± 157 µs

from collections import Counter

class Card:
def __init__(self,vs):
d = {'T':10,'J':11,'Q':12,'K':13,'A':14}
self.s = vs[1]
if vs[0] not in set('TJQKA'):
self.v = int(vs[0])
else:
self.v = d[vs[0]] class Hand:
def __init__(self,cards):
self.values = [x.v for x in cards]
self.suits = [x.s for x in cards]
self.value_counter = Counter(self.values).most_common()
self.fc = self.value_counter[0][1]
self.sc = self.value_counter[1][1]
self.suit_kind = len(set(self.suits))
self.ranks = sorted([x.v for x in cards],reverse=True)
self.diff = set([self.ranks[:-1][i]-self.ranks[1:][i] for i in range(4)]) def categories(self):
if self.suit_kind == 1 and self.diff == {1}:
return ('Straight Flush',9)
elif self.suit_kind == 1:
return ('Flush',6)
elif self.diff == {1}:
return ('Straight',5)
elif self.fc == 4:
return ('Four of a Kind',8)
elif self.fc == 3 and self.sc == 2:
return ('Full House',7)
elif self.fc == 3 and self.sc == 1:
return ('Three of a Kind',4)
elif self.fc == 2 and self.sc == 2:
return ('Two Pairs',3)
elif len(self.value_counter) == 4 and self.fc == 2:
return ('One Pair',2)
else:
return ('High Card',1) def is_winner(self,hand):
if self.categories()[1] > hand.categories()[1]:
return True
elif self.categories()[1] < hand.categories()[1]:
return False
elif self.categories()[1] in [8,7,4,3,2]:
return self.value_counter > hand.value_counter
else:
return self.ranks > hand.ranks def main():
count = 0
with open('data/ep54.txt') as f:
hands = [line.split() for line in f]
for hand in hands:
p1_cards = [Card(x) for x in hand[:5]]
p2_cards = [Card(x) for x in hand[5:]]
p1_hand,p2_hand = Hand(p1_cards),Hand(p2_cards)
if p1_hand.is_winner(p2_hand):
count += 1
return count

Project Euler 54: Poker hands的更多相关文章

  1. Project Euler 54

    #include<bits/stdc++.h> using namespace std; ]; ]; ; map<char,int> mp; //map<char,cha ...

  2. Python练习题 039:Project Euler 011:网格中4个数字的最大乘积

    本题来自 Project Euler 第11题:https://projecteuler.net/problem=11 # Project Euler: Problem 10: Largest pro ...

  3. [project euler] program 4

    上一次接触 project euler 还是2011年的事情,做了前三道题,后来被第四题卡住了,前面几题的代码也没有保留下来. 今天试着暴力破解了一下,代码如下: (我大概是第 172,719 个解出 ...

  4. Python练习题 029:Project Euler 001:3和5的倍数

    开始做 Project Euler 的练习题.网站上总共有565题,真是个大题库啊! # Project Euler, Problem 1: Multiples of 3 and 5 # If we ...

  5. Project Euler 9

    题意:三个正整数a + b + c = 1000,a*a + b*b = c*c.求a*b*c. 解法:可以暴力枚举,但是也有数学方法. 首先,a,b,c中肯定有至少一个为偶数,否则和不可能为以上两个 ...

  6. Project Euler 44: Find the smallest pair of pentagonal numbers whose sum and difference is pentagonal.

    In Problem 42 we dealt with triangular problems, in Problem 44 of Project Euler we deal with pentago ...

  7. project euler 169

    project euler 169 题目链接:https://projecteuler.net/problem=169 参考题解:http://tieba.baidu.com/p/2738022069 ...

  8. 【Project Euler 8】Largest product in a series

    题目要求是: The four adjacent digits in the 1000-digit number that have the greatest product are 9 × 9 × ...

  9. Project Euler 第一题效率分析

    Project Euler: 欧拉计划是一系列挑战数学或者计算机编程问题,解决这些问题需要的不仅仅是数学功底. 启动这一项目的目的在于,为乐于探索的人提供一个钻研其他领域并且学习新知识的平台,将这一平 ...

随机推荐

  1. C#的FTP服务器源代码

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  2. 关于javascript中的prototype

    作为一个致力于前端开发的人员,能够熟练掌握javascript的原理和机制是每个小白的必经之路,这也是最痛苦的.有人说前端功力好不好最主要的就是看对js的掌握能力,有人说十年也啃不完一门javascr ...

  3. Ubuntu16.04常用C++库安装及环境配置

    1. 常用非线性求解库Ceres #================================================================================== ...

  4. python编程基础之二十七

    列表生成式:[exp for iter_var in iterable] 同样也会有字典生成式,集合生成式,没有元组生成式,元组生成式的语法被占用了 字典生成式,集合生成式,就是外面那个括号换成{}  ...

  5. python的闭包操作

    调用外函数的时候,只是存了闭包函数给flast列表,这个闭包函数并不是一个结果,个人理解而是存的是当前的环境,比如第一次循环当前的环境就是i=0,第二次循环,当前的环境就是i=1,等. 然后,真正的计 ...

  6. sql server中Set与select的区别

    Set与select的区别 Set select 同时多个变量赋值 不支持 支持 表达式返回多个值时 出错 将返回的最后一个值赋给变量 表达式未返回值 变量被null赋值 变量保持原始值

  7. RF用例执行方法

    用例如下图: 1.执行整个项目下的所有用例 dos命令下输入robot D:\work_doc\RF (RF为下图中脚本项目Test目录的上级目录) 2.执行某个suite中的所有用例 dos命令下输 ...

  8. 代码审计-凡诺CMS 2.1文件包含漏洞

    0x01代码审计 后台账号密码: admin admin 安装好了是这样的 漏洞文件:/channel.php if (ism()) { include($dir.$t_mpath.$c_mcmode ...

  9. PHP7源码之array_unique函数分析

    以下源码基于 PHP 7.3.8 array array_unique ( array $array [, int $sort_flags = SORT_STRING ] ) (PHP 4 >= ...

  10. [USACO15DEC]高低卡(白金)High Card Low Card (Platinum)

    题目描述 Bessie the cow is a hu e fan of card games, which is quite surprising, given her lack of opposa ...