통계, IT, AI

[Project Euler] 54. Poker hands 본문

IT/PROJECT_EULER

[Project Euler] 54. Poker hands

Harold_Finch 2017. 7. 26. 20:39

1. 개요

    문제는 이곳에서 확인할 수 있다. 포커 게임에서 패의 순서는 다음과 같다.


  • High Card: 모든 값이 다르고 적어도 하나의 모양이 다른 경우
  • One Pair: 두 장이 같은 값을 가지는 경우
  • Two Pair: 두 개의 One Pair가 나오는 경우
  • Three of a Kind: 3장이 같은 값을 가지는 경우
  • Staight: 모든 카드의 값이 연속하는 경우
  • Flush: 모든 카드가 같은 모양인 경우
  • Full House: Three of a Kind와 One Pair가 나온 경우
  • Four of a Kind: 4장이 같은 값을 갖는 경우
  • Straight Flush: 모든 카드의 값이 연속하며 같은 모양인 경우
  • Royal Flush: Straight Flush이면서 가장 작은 값이 10인 경우

    카드의 값은 다음의 순서를 따른다.

$$2, \ 3, \ 4, \ 5, \ 6, \ 7, \ 8, \ 9, \ 10, \ Jack, \ Queen, \ King, \ Ace$$

    패가 같은 경우 값이 높은 패가 이긴다. 예를 들어, 두 명의 플레이어가 모두 One Pair of Queen이 나왔다면 Queen을 제외하고 가장 높은 값을 가진 플레이어가 승자이다. 마찬가지로 같은 Full House인 경우, Three of a Kind의 값이 높은 플레이어가 이긴다. 주어진 텍스트 파일에서 플레이어 1이 이긴 횟수를 구하는 것이 문제의 목표이다.

2. 구현

    패가 높은 경우에도 값을 고려 해야 하기 때문에 패와 값을 분리하면서도 크기 비교가 가능한 measure가 필요하다. 이를 위하여 이진법을 도입한다. 패는 정수 부분, 숫자는 소수 부분에 넣어 각 플레이어의 패를 인코딩한다.

# -*- coding: utf-8 -*-
# Project Euler 54

def card_to_num(card_list):
    digit_to_num = {num: idx+2 for idx, num in enumerate('23456789TJQKA')}
    result = 0

    # value
    v = sorted([digit_to_num[card[0]] for card in card_list])  # total values
    max_v = 2 ** max(v) /1e5  # overall max value
    v_count = {value: v.count(value) for value in set(v)}  # value and its count
    count_shape = sorted(v_count.values())  # distribution of counts ex) 1, 1, 2, 2
    count_v = {count: max(k for k, v in v_count.items() if v == count) for count in set(count_shape)}  # count and its maximum value
    count_v = {k: 2**count_v[k]/(1e5**(i+1)) for i, k in enumerate(sorted(count_v.keys(), reverse = True))}
    is_consecutive = all(r-f == 1 for f, r in zip(v[0:4], v[1:5]))

    # suit
    is_all_same_suit = len(set([card[1] for card in card_list])) == 1

    # 512, Royal Flush: Ten, Jack, Queen, King, Ace, in same suit.
    if is_consecutive and is_all_same_suit and max_v == 2 ** 14 / 1e5:
        result = 512 + max_v
    
    # 256, Straight Flush: All cards are consecutive values of same suit.
    elif is_consecutive and is_all_same_suit:
        result = 256 + max_v

    # 128, Four of a Kind: Four cards of the same value.
    elif count_shape == [1, 4]:
        result = 128 + count_v[4]

    # 64, Full House: Three of a kind and a pair.
    elif count_shape == [2, 3]:
        result = 64 + count_v[3]

    # 32, Flush: All cards of the same suit.
    elif is_all_same_suit:
        result = 32 + max_v

    # 16, Straight: All cards are consecutive values.
    elif is_consecutive:
        result = 16 + max_v

    # 8, Three of a Kind: Three cards of the same value.
    elif count_shape == [1, 1, 3]:
        result = 8 + count_v[3] + count_v[1]

    # 4, Two Pairs: Two different pairs.
    elif count_shape == [1, 2, 2]:
        result = 4 + count_v[2] + count_v[1]

    # 2, One Pair: Two cards of the same value.
    elif count_shape == [1, 1, 1, 2,]:
        result = 2 + count_v[2] + count_v[1]

    # 1, High Card: Highest value card.
    elif not is_consecutive and count_shape == [1]*5:
        result = 1 + max_v
	
    return result
	
with open('p054_poker.txt', 'rt', encoding='utf-8') as f:
    dat = [row.replace('\n', '').split(' ') for row in f.readlines()]

result = 0

for row in dat:
    p_1, p_2 = card_to_num(row[0:5]), card_to_num(row[5:10])
    result += 1 if p_1 > p_2 else 0

print(result)

답은 376이다.

Comments