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: 欧拉计划是一系列挑战数学或者计算机编程问题,解决这些问题需要的不仅仅是数学功底. 启动这一项目的目的在于,为乐于探索的人提供一个钻研其他领域并且学习新知识的平台,将这一平 ...
随机推荐
- HTTP/1.1与HTTP/2有什么区别?
介绍 超文本传输协议(HTTP)是一种应用协议,自1989年发明以来,它一直是事实上在万维网上进行通信的标准.从1997年发布HTTP / 1.1到最近,对它的修改很少.协议.但是在2015年,重 ...
- git分支的创建、删除、切换、合并
需求背景 开发新功能和修改bug一般放在新建分支,如果觉得可行,可以合并到master分支上. 方式 1.查看分支 git branch (查看本地分支及当前所属分支) git branch -a ( ...
- session,cookie,sessionStorage,localStorage的相关设置以及获取删除
一.cookie 什么是 Cookie? "cookie 是存储于访问者的计算机中的变量.每当同一台计算机通过浏览器请求某个页面时,就会发送这个 cookie.你可以使用 JavaScrip ...
- 【源码解析】自动配置的这些细节不知道,别说你会 springboot
spring-boot 相对于 spring,很重要的一个特点就是自动配置,使约定大于配置思想成功落地.xxx-spring-boot-starter 一系列引导器能够开箱即用,或者只需要很少的配置( ...
- maven更新慢,改用国内镜像地址
方法很简单: 在 maven根目录 > conf > settings.xml 中 <mirrors>里添加以下子节点: <mirror> <id>al ...
- TensorFlow2.0(7):激活函数
.caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px so ...
- 经典的Redis的主从复制搭建
##### 配置服务器 1).打开redis.conf文件修改 bind 指定的ip地址: ; /*设置显示的列网格线,且重复3次1 ...
- [BZOJ5280] [Usaco2018 Open]Milking Order
Description Farmer John的N头奶牛(1≤N≤105),仍然编号为1…N,正好闲得发慌.因此,她们发展了一个与Farmer John每 天早上为她们挤牛奶的时候的排队顺序相关的复杂 ...
- 使用git如何规范地向主线提交代码
使用git向主干分支合并代码通常采用两种方式:第一种是merge,第二种是利用BeyondCompare等工具进行比对,将差异合并到主干: 通过merge合并代码出现冲突时,并不清楚谁的修改和谁的修改 ...
