파이썬 실전 프로젝트

프로젝트 오일러 54번 - 포커게임

토픽 파이썬 실전 프로젝트 > 파이썬으로 수학문제 풀기

https://projecteuler.net/problem=54

포커게임 룰은 모르시는분은 별로 없을거라 생각되네요.

  • High Card: Highest value card.
  • One Pair: Two cards of the same value.
  • Two Pairs: Two different pairs.
  • Three of a Kind: Three cards of the same value.
  • Straight: All cards are consecutive values.
  • Flush: All cards of the same suit.
  • Full House: Three of a kind and a pair.
  • Four of a Kind: Four cards of the same value.
  • Straight Flush: All cards are consecutive values of same suit.
  • Royal Flush: Ten, Jack, Queen, King, Ace, in same suit.

위와 같은 룰을 가지고, 주어진 카드 패를 분석해서, 한 사람이 몇번을 이겼는지를 알아내는 문제입니다.

8C TS KC 9H 4S 7D 2S 5D 3S AC
5C AD 5D AC 9C 7C 5H 8D TD KS
3H 7H 6S KC JS QH TD JC 2D 8S
TH 8H 5C QS TC 9H 4D JC KS JS
7C 5H KC QH JD AS KH 4C AD 4S
5H KS 9C 7D 9H 8D 3S 5D 5C AH
6H 4H 5C 3H 2H 3S QH 5S 6S AS
TD 8C 4H 7C TC KC 4C 3H 7S KS
7C 9C 6D KD 3H 4C QS QC AC KH
...
..
.

 가로 한줄이 한 게임이고, 왼쪽 5장이 player 1, 오른쪽 5장이 player 2 입니다. 게임수는 총 1000 줄입니다.

 

접근방법

여러가지로 풀수 있겠지만, 가장 간단하게 풀수있는 방법은, 카드를 카운트 하는겁니다.

아래와 같이 10장의 카드가 주어졌다면, 두명의 플레이어로 5장씩 나눕니다.

8CTSKC9H4S 7D2S5D3SAC  

 1. 카운트

그리고 각 패의 카드숫자를 23456789TJQKACSHD 의 순서로 카운트 합니다.

플레이어1을 카운트 해보면, 4 가 1장, 8이 1장, 9가 1장, 10이 1장, K가 1장이고, 모양은 크로버가 2장, 스페이드가 2장, 하트가 1장입니다. 이걸 문자열로 만들면,

000100011100102210

 이런 모양이 됩니다.(맨 왼쪽의 0 한자리는 의미없는 숫자입니다.)

마찬가지로 플레이어 2도 카운트를 해줍니다.

011010100000011202

2. 조건문 걸기.

그리고 이 문자열에 조건문을 걸어서, 원페어인지 투페어인지, 스트레이트인지 등등을 판별해서 리스트로 만들어 줍니다.

[0, 0, 0, 0, 0, 0, 13, 0, 8] [0, 0, 0, 0, 0, 0, 0, 0, 12]

이렇게 만들어진 리스트의 각 자리는 오른쪽에서부터 그냥 높은수, 원페어, 투페어,,,, 순서입니다. 즉 왼쪽에 높은 등급의 패가 위치합니다.

 3. 비교

그리고 파이썬에서 위 두 리스트를 통째로 비교하면 큰 값을 알아낼수 있습니다.

if [0, 0, 0, 0, 0, 0, 13, 1, 8] > [0, 0, 0, 0, 0, 0, 0, 0, 12]:

위 조건문의 경우에는 True 가 됩니다. 왼쪽부터 차례대로 비교를 해나가다가, 7번째 인덱스의 숫자 13에서 오른쪽 리스트의 0과 비교하면 왼쪽이 크기 때문에, 왼쪽 리스트가 크게 되고, 값은 True 가 됩니다.

이걸 이제 코드로 만들기만 하면 됩니다.

 

카드 카운트

카운트 부분의 코드입니다.

def counter(g):
    c='0'
    for i in '23456789TJQKACSHD':
        c+=str(g.count(i))
    return c

5장의 카드패를 입력받아, '000100011100102210'같은 형태의 문자열을 돌려줍니다.

 줄여서 아래처럼 쓸수도 있습니다.

def counter(g):
    c='0'+''.join([str(cards.count(i)) for i in '23456789TJQKACSHD'])
    return c

 

조건문
if '1' in pair:                     # high card
if pair.count('2')==1:              # 원페어
if pair.count('2')==2:              # 투페어 
if '3' in pair:                     # 3 카드 
if '11111' in pair:                 # 스트레이트 
if '5' in suit:                     # 플러쉬 
if rank[7] != 0 and rank[5] != 0 :  # 풀하우스(3카드와 원페어)    
if '4' in pair:                     # 4 카드
if rank[4] != 0 and rank[3] != 0 :  # 스트레이트 플러쉬

 

 

전체 코드
with open('p054_poker.txt') as data:
    games = [''.join(i.split()) for i in data.readlines()]

def grader(count):
    pair = count[:14]
    suit = count[14:18]
    rank=[0]*9
    if '1' in pair:        rank[8] = pair.rindex('1')
    if pair.count('2')==1: rank[7] = pair.index('2')
    if pair.count('2')==2: rank[6] = pair.rindex('2')
    if '3' in pair:        rank[5] = pair.index('3')
    if '11111' in pair:    rank[4] = rank[8]
    if '5' in suit:        rank[3] = rank[8]   
    if rank[7] != 0 and rank[5] != 0 : rank[2] = rank[5]    
    if '4' in pair:                    rank[1] = pair.index('4')
    if rank[4] != 0 and rank[3] != 0 : rank[0] = rank[4]    
    return rank

def counter(cards):
    c='0'+''.join([str(cards.count(i)) for i in '23456789TJQKACSHD'])
    return c

p1count=0
for g in games:
    if grader(counter(g[:10])) > grader(counter(g[10:20])):
        p1count+=1
print(p1count)

** 문제를 푸는데는 충분한 코드이나, 실행 속도가 빠른 코드는 아닙니다.

그리고 A2345 스트레이트의 경우는 조건문을 만들지 않아서 확인불가능합니다. 문제에는 해당 케이스가 없었네요.

또, 동일패의 경우, 차순위의 카드 숫자를 비교하게 되는데, 현재는 차순위 1장의 카드만 가능하고, 5장 모두를 비교하게 할려면, 리스트의 크기를 좀더 늘려야 됩니다.

댓글

댓글 본문