알고리즘

[기술 테스트 과제] poker hands_python

작은코딩 2022. 5. 9. 04:40

🔒 문제

https://projecteuler.net/problem=54

 

원문은 영어로 되어있지만 구글 번역기의 도움을 받았다.

 

[poker hands]

카드 게임 포커에서 핸드는 5장의 카드로 구성되며 다음과 같은 방식으로 가장 낮은 것부터 가장 높은 것까지 순위가 매겨집니다.

  • High Card : 가장 가치가 높은 카드.
  • One Pair : 같은 값의 두 장의 카드.
  • Two Pairs : 두 개의 다른 쌍.
  • Three of a Kind : 같은 값의 카드 3장.
  • Straight : 모든 카드는 연속된 값입니다.
  • Flush : 같은 슈트의 모든 카드.
  • Full House : 세 종류와 한 쌍.
  • Four of Kind : 같은 값의 카드 4장.
  • Straight Flush : 모든 카드는 동일한 슈트의 연속 값입니다.
  • Royal Flush : 텐, 잭, 퀸, 킹, 에이스, 같은 슈트.

카드의 가치는
2, 3, 4, 5, 6, 7, 8, 9, 10, 잭, 퀸, 킹, 에이스 순입니다.

두 명의 플레이어가 동일한 랭킹 핸드를 가지고 있으면 가장 높은 값으로 구성된 랭크가 승리합니다. 예를 들어, 8 쌍이 5 쌍을 이깁니다(아래 예 1 참조). 그러나 예를 들어 두 등급이 동률인 경우 두 플레이어 모두 한 쌍의 퀸을 가지고 있는 경우 각 핸드에서 가장 높은 카드가 비교됩니다(아래 예 4 참조). 가장 높은 카드가 동점인 경우 다음으로 높은 카드가 비교되는 식입니다.

 

두 명의 플레이어에게 다음 5가지 핸드를 처리하는 것을 고려하십시오.

Hand Player 1 Player 2 Winner
1 5H 5C 6S 7S KD
Pair of Fives
2C 3S 8S 8D TD
Pair of Eights
Player 2
2 5D 8C 9S JS AC
Highest card Ace
2C 5C 7D 8S QH
Highest card Queen
Player 1
3 2D 9C AS AH AC
Three Aces
3D 6D 7D TD QD
Flush with Diamonds
Player 2
4 4D 6S 9H QH QC
Pair of Queens
Highest card Nine
3D 6D 7H QD QS
Pair of Queens
Highest card Seven
Player 1
5 2H 2D 4C 4D 4S
Full House
With Three Fours
3C 3D 3S 9S 9D
Full House
with Three Threes
Player 1

poker.txt 파일 에는 2명의 플레이어에게 1,000개의 무작위 핸드가 포함되어 있습니다. 파일의 각 줄에는 10장의 카드가 있습니다(공백 하나로 구분). 처음 5장은 참가자 1의 카드이고 마지막 5장은 참가자 2의 카드입니다. 모든 패가 유효하고(유효하지 않은 문자나 반복되는 카드가 없음), 각 플레이어의 패가 특정한 순서가 없으며, 각 패에 확실한 승자가 있다고 가정할 수 있습니다.

플레이어 1은 몇 핸드를 이기나요?

poker.txt
0.03MB


🔓 풀이

🔑 문제 해결 / 코드

"""
개요 : 5장으로 하는 핸드포커게임 만들기

input | poker_file: file.txt (1000게임)
output | result: int (player1의 승리 횟수)

10장의 카드가 주어졌을 때 앞 5장은 player1, 뒤 5장은 player2의 카드가 되어 비교하여 승자를 정한다.

"""
"""
## 비고 ##
1. 10은 T이기도 함(주어진 텍스트 파일 마지막에 J대신 10이 들어있음)
2. 핸드, 패 비교 동점인경우 무승부, 승리 카운트 X
"""

# 핸드 랭크 딕셔너리
rank_dict = {"Royal flush": 10, "Straight flush": 9, "Four of a kind": 8, "Full house": 7, "Flush": 6,
             "Straight": 5, "Three of a kind": 4, "Two pair": 3, "Pair": 2, "High card": 1}


#################################
#       핸드 체크 함수 시작     #
#################################

# 원페어 체크
def check_pair(hand_rankings):
    pair_count = 0
    hand_rankings[0] = 0
    for i in hand_rankings:
       if i == 2:
           pair_count += 1

    if pair_count == 1:
        return True
    else:
        return False


# 투페어 체크
def check_two_pair(hand_rankings):
    pair_count = 0
    for i in hand_rankings:
        if i == 2:
            pair_count += 1

    if pair_count == 2:
        return True
    else:
        return False


# 트리플 체크
def check_three_of_a_kind(hand_rankings):
    for i in hand_rankings:
        if i == 3:
            return True
    return False


# 스트레이트 체크
def check_straight(hand_rankings):
    straight_count = 0

    # 1이 연속으로 5번 나오면 스트레이트 // straight_count = 5
    for i in hand_rankings:
        if straight_count == 5:
            return True
        if i == 1:
            straight_count += 1
        else:
            straight_count = 0

    if straight_count == 5:
        return True
    return False


# 플러시 체크
def check_flush(p_suit):
    if len(set(p_suit)) == 1:
        return True
    else:
        return False


# 풀하우스 체크
def check_full_house(hand_rankings):
    if check_three_of_a_kind(hand_rankings) and check_pair(hand_rankings):
        return True
    else:
        return False


# 포카드 체크
def check_four_of_a_kind(hand_rankings):
    for p in hand_rankings:
        if p == 4:
            return True
    return False


# 스트레이트 플러시 체크
def check_straight_flush(p_suit ,hand_rankings):
    if check_straight(hand_rankings) and check_flush(p_suit):
        return True
    else:
        return False


# 로얄 플러시 체크
def check_royal_flush(p_num, p_suit):
    if p_num == [10, 11, 12, 13, 14] and check_flush(p_suit):
        return True
    else:
        return False

################################
#       핸드 체크 함수 끝      #
################################


# 손패 순위 정하기
def rank(p_num, p_suit):
    hand_rankings = [0]*14
    rank = "High card"

    for num in p_num:
        hand_rankings[num-1] += 1
        if num == 14: # 카드가 Ace인 경우 0번째 인덱스에도 추가
            hand_rankings[0] += 1

    # Royal flush
    if check_royal_flush(p_num, p_suit):
        rank = "Royal flush"

    # Straight flush
    elif check_straight_flush(p_suit, hand_rankings):
        rank = "Straight flush"

    # Four of a kind
    elif check_four_of_a_kind(hand_rankings):
        rank = "Four of a kind"

    # Full house
    elif check_full_house(hand_rankings):
        rank = "Full house"

    # Flush
    elif check_flush(p_suit):
        rank = "Flush"

    # Straight
    elif check_straight(hand_rankings):
        rank = "Straight"

    # Three of a kind
    elif check_three_of_a_kind(hand_rankings):
        rank = "Three of a kind"

    # Two pair
    elif check_two_pair(hand_rankings):
        rank = "Two pair"

    # Pair
    elif check_pair(hand_rankings):
        rank = "Pair"

    return rank, rank_dict[rank], hand_rankings


# 플레이어 카드 숫자와 문양 구분하기
def player_num_suit_split(player):
    p_num_list = []
    p_suit_list = []
    card_dict = {"T": 10, "J": 11, "Q": 12, "K": 13, "A": 14}

    for p in player:
        # 카드 숫자와 문양 구분하기
        p_num= p[:-1]
        p_suit = p[-1]

        try: # 영문인 경우
            p_num_list.append(card_dict[p_num])
        except KeyError: # 숫자인 경우
            p_num_list.append(int(p_num))

        p_suit_list.append(p_suit)
    # 카드 숫자 오름차순 정렬
    p_num_list.sort()
    return p_num_list, p_suit_list

#############################
# 핸드가 같을경우 비교 함수 #
#############################

# hand_rankings revers index 비교
def compare_hand_rankings_revers_index(p1_hand_rankings, p2_hand_rankings):
    p1_hand_rankings.reverse()
    p2_hand_rankings.reverse()

    for p1, p2 in zip(p1_hand_rankings, p2_hand_rankings):
        if p1 > p2:
            return True
        elif p1 < p2:
            break

# hand rankings number index 비교
def compare_hand_rankings_number_index(p1_hand_rankings, p2_hand_rankings, number):
    # hand rankings 에서 ace 1번위치 지워주기
    p1_hand_rankings[0] = 0
    p2_hand_rankings[0] = 0
    # hand rankings 3 index 비교
    if p1_hand_rankings.index(number) > p2_hand_rankings.index(number):
        return True



# 게임 함수
def play_game(player1, player2):
    p1_num, p1_suit = player_num_suit_split(player1)
    p2_num, p2_suit = player_num_suit_split(player2)

    p1_hand, p1_rank, p1_hand_rankings = rank(p1_num, p1_suit)
    p2_hand, p2_rank, p2_hand_rankings = rank(p2_num, p2_suit)

    # print(f"p1: {p1_hand} | p2: {p2_hand}") TODO
    if p1_rank > p2_rank:
        return True

    # 핸드가 같을 경우
    elif p1_rank == p2_rank:
        if p1_hand and p2_hand == "High card":
            # hand_rankings revers index 비교
            if compare_hand_rankings_revers_index(p1_hand_rankings, p2_hand_rankings):
                return True

        elif p1_hand and p2_hand == "Pair":
            if p1_hand_rankings.index(2) > p2_hand_rankings.index(2):
                return True
            elif p1_hand_rankings.index(2) == p2_hand_rankings.index(2):
                # hand_rankings revers index 비교
                if compare_hand_rankings_revers_index(p1_hand_rankings, p2_hand_rankings):
                    return True

        elif p1_hand and p2_hand == "Two pair":
            p1_index = [i for i, y in enumerate(p1_hand_rankings) if y == 2]
            p2_index = [i for i, y in enumerate(p2_hand_rankings) if y == 2]
            # print(p1_index, p2_index) TODO
            if p1_index[1] > p2_index[1]:
                return True
            elif p1_index[1] == p2_index[1]:
                if p1_index[0] > p2_index[0]:
                    return True
                elif p1_index[0] == p2_index[0]:
                    # hand rankings 에서 ace 1번위치 지워주기
                    p1_hand_rankings[0] = 0
                    p2_hand_rankings[0] = 0
                    if p1_hand_rankings.index(1) > p2_hand_rankings.index(1):
                        return True

        elif p1_hand and p2_hand == "Three of a kind":
            # hand rankings number index 비교
            if compare_hand_rankings_number_index(p1_hand_rankings, p2_hand_rankings, 3):
                return True

        elif p1_hand and p2_hand == "Straight":
            # hand_rankings revers index 비교
            if compare_hand_rankings_revers_index(p1_hand_rankings, p2_hand_rankings):
                return True

        elif p1_hand and p2_hand == "Flush":
            # hand_rankings revers index 비교
            if compare_hand_rankings_revers_index(p1_hand_rankings, p2_hand_rankings):
                return True

        elif p1_hand and p2_hand == "Full house":
            # hand rankings number index 비교
            if compare_hand_rankings_number_index(p1_hand_rankings, p2_hand_rankings, 3):
                return True

        elif p1_hand and p2_hand == "Four of a kind":
            # hand rankings number index 비교
            if compare_hand_rankings_number_index(p1_hand_rankings, p2_hand_rankings, 4):
                return True

        elif p1_hand and p2_hand == "Straight flush":
            # hand_rankings revers index 비교
            if compare_hand_rankings_revers_index(p1_hand_rankings, p2_hand_rankings):
                return True

        # 둘다 로얄 플러쉬 나오는 경우는 무승부
        elif p1_hand and p2_hand == "Royal flush":
            pass
    return False


# 게임 시작하기
def start_game(poker_txt_file):
    # Poker.txt 파일 열기
    poker_file = open(poker_txt_file, "r")
    # 모든 line 가져오기
    lines = poker_file.readlines()
    # type(line[0]) = str
    p1_number_of_win = 0

    for line in lines:
        # 줄바꿈 제거 (\n)
        line = line.strip()
        # player 카드분배
        cards = line.split(" ")
        player1 = cards[:5]
        player2 = cards[5:]
        # print(f"p1_hand: {player1} | p2_hand: {player2}") TODO
        if play_game(player1, player2):
            p1_number_of_win += 1

    # Poker.txt 파일 닫기
    poker_file.close()
    return p1_number_of_win

print(start_game("Poker.txt"))

 

디렉토리 구조


💉 피드백

분명 이렇게 길게 쓸 코드가 아닐 텐데 쓰다 보니 어느새 300줄이 훌쩍 넘어버렸다. (함수가 몇 개야,,,)

 

간단한 포커게임이지만 구현하는데 고민에 또 고민을,, 좋은 코더의 길은 멀고도 험하구나 ㅠ

 

속도, 성능에 신경 쓰기보단 최대한 주어진 조건을 충족하는 함수를 만들고자 노력했는데 정확도가 얼마나 될지는 모르겠다.

 

프로그래머스처럼 제출해서 채점이 되면 좋을 텐데 마땅한 답안이 없어서 찜찜하지만, 그래도 완성은 해서 기분은 좋다 😁