Project Euler 54: Poker hands
在纸牌游戏中,一手包含五张牌并且每一手都有自己的排序,从低到高的顺序如下:
- 大牌:牌面数字最大
- 一对:两张牌有同样的数字
- 两对:两个不同的一对
- 三条:三张牌有同样的数字
- 顺子:所有五张牌的数字是连续的
- 同花:所有五张牌有同样的花色
- 船牌:三张同样数字的牌加一个一对
- 四条:四张牌有同样的数字
- 同花顺:牌面数字连续且有同样的花色
- 皇家同花顺:由\(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的更多相关文章
- Project Euler 54
#include<bits/stdc++.h> using namespace std; ]; ]; ; map<char,int> mp; //map<char,cha ...
- Python练习题 039:Project Euler 011:网格中4个数字的最大乘积
本题来自 Project Euler 第11题:https://projecteuler.net/problem=11 # Project Euler: Problem 10: Largest pro ...
- [project euler] program 4
上一次接触 project euler 还是2011年的事情,做了前三道题,后来被第四题卡住了,前面几题的代码也没有保留下来. 今天试着暴力破解了一下,代码如下: (我大概是第 172,719 个解出 ...
- Python练习题 029:Project Euler 001:3和5的倍数
开始做 Project Euler 的练习题.网站上总共有565题,真是个大题库啊! # Project Euler, Problem 1: Multiples of 3 and 5 # If we ...
- Project Euler 9
题意:三个正整数a + b + c = 1000,a*a + b*b = c*c.求a*b*c. 解法:可以暴力枚举,但是也有数学方法. 首先,a,b,c中肯定有至少一个为偶数,否则和不可能为以上两个 ...
- 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 ...
- project euler 169
project euler 169 题目链接:https://projecteuler.net/problem=169 参考题解:http://tieba.baidu.com/p/2738022069 ...
- 【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 × ...
- Project Euler 第一题效率分析
Project Euler: 欧拉计划是一系列挑战数学或者计算机编程问题,解决这些问题需要的不仅仅是数学功底. 启动这一项目的目的在于,为乐于探索的人提供一个钻研其他领域并且学习新知识的平台,将这一平 ...
随机推荐
- 从0开始学FreeRTOS-1
我们知道,(单核)单片机某一时刻只能干一件事,会造成单片机资源的浪费,而且还有可能响应不够及时,所以,在比较庞大的程序或者是要求实时性比较高的情况下,我们可以移植操作系统.因为这种情况下操作系统比裸机 ...
- .NET Core 读取配置文件方式总结
基于.NET Core的跨平台开发,配置文件与之前.NET Framework采用xml的config文件不同,目前主要是采用json文件键值对配置方式读取. 参考网上相关资料总结如下: 一.引入扩展 ...
- js 判断对象是否为空
利用JSON.stringify var objData = {};JSON.stringify(objData) ==="{}" // true 第二种用原声js 方法 Obje ...
- UVA - 11795 Mega Man's Mission
Mega Man is off to save the world again. His objective is to kill the Robots created by Dr. Wily who ...
- 从零学习基于Python的RobotFramework自动化
从零学习基于Python的RobotFramework自动化 一. Python基础 1) 版本差异 版本 编码 语法 其他 2.X ASCII try: raise Type ...
- asp.net core 腾讯验证码的接入
asp.net core 腾讯验证码的接入 Intro 之前使用的验证码服务是用的极验验证,而且是比较旧的,好久之前接入的,而且验证码服务依赖 Session,有点不太灵活,后来发现腾讯也有验证码服务 ...
- 程序员IT狗有什么副业可以做呢?
1. 开篇 副业有很多,全网有做什么公众号.闲鱼.手机卡,各种各样的都有,大部分是骗子,小部分是通过自己的努力,获得了成功. 从年初就开始实践如何做一个自由职业者,近大半年有一些感受正好一起分享交流一 ...
- python selenium下拉框定位
一.前言 总结一下python+selenium select下拉选择框定位处理的两种方式,以备后续使用时查询: 二.直接定位(XPath) 使用Firebug找到需要定位到的元素,直接右键复制XPa ...
- Echart:前端很好的数据图表展现工具+demo
官网: http://echarts.baidu.com/index.html 通过一个简单的小Demo介绍echart的使用:demo均亲测可以运行 demo1: 1.新建一个echarts.ht ...
- shell变量(二)
变量名的命名规范: 1.命名只能使用英文字母.数字和下划线,且不能以数字开头: 2.不能存在空格‘: 3.不能使用标点符号: 4.不能使用bash里的关键字(可使用help命令查看保留关键字) 变量的 ...