프로젝트 오일러 054
포커 게임에서 이긴 횟수 구하기
문제
점수 체계 정하기
포커의 승패는 다섯장의 카드로 만들 수 있는 계급의 높낮이로 승부를 가르며, 계급이 같은 경우, 더 높은 숫자를 가진 사람이 이기는 규칙이 있습니다. 문제에서는 각 계급이 어떻게 결정되는지를 정의해두었으니, 규칙과 높은 카드를 비교하여 높은 쪽이 승리하도록 한 판의 게임을 판정하는 로직을 구현하면 됩니다.
그런데 아무래도 판단해야 하는 규칙이 많고 복잡하다보니, 계급을 점수로 환산하려고 합니다. 그런데 Two Pair는 두 개의 One Pair이고, Full House는 One Pair + Three of a Kind 의 조합입니다. 따라서 T/P의 점수는 O/P의 점수의 두 배가, F/H의 점수는 O/P + T/K의 점수가 되도록 점수 체계를 구성한다면, 이 두 케이스의 계급을 판정하는 로직은 구현할 필요가 없습니다.
계급의 중간쯤에 있는 Flush를 100점으로 둔다면 다음과 같이 계급별 점수를 정할 수 있을 겁니다. 이 점수 체계는 상대적인 것이므로 보다 더 적절한 체계를 사용해도 좋습니다. 다른 계급 두 개의 조합이 동시에 성립하는 계급의 점수가 이를 구성하는 계급들의 점수의 합이 되고, 전체 계급의 순서가 지켜질 수 있도록만 설정하면 됩니다.
- One Pair = 25
- Two Pairs = 50 (25 × 2)
- Three of a Kind = 80
- Straight = 90
- Flush = 100
- Full House = 105 (25 + 80)
- Four of a Kind = 110
- Straight Flush = 190 (90 + 100)
- Royal Flush = 200
판정
점수 체계를 위와 같이 구성하면 계급 판정에서 불필요한 판정을 계산할 필요가 없습니다. 아래와 같은 순서대로 판정합니다.
- 플러시 조건을 만족한다면 점수에 100점을 더합니다.
- 로열 플러시 조건이라면 100점을 더하고 종료합니다.
- (플러시 여부와 상관없이) 스트레이트 조건을 만족한다면 점수에 90점을 더합니다.
- F/K 조건을 만족한다면 110점을 더합니다.
- T/K 조건을 만족한다면 80점을 더합니다.
- One Pair를 이루는 쌍이 몇 개인지 찾고, 각 쌍마다 25점을 더합니다.
하이 카드를 통해서 비교하기 위해서는 5장의 카드에서 숫자의 개수가 많고, 숫자가 더 큰 순서대로 정렬하여 5개를 나열합니다. 어차피 하이 카드는 같은 계급(점수)에서만 비교하므로, 이렇게 정렬하면, 계급과 그 계급의 숫자가 같은 (예를 들어 두 사람이 모두 5 One Pair 라면) 경우에 남은 카드로 승부를 결정하게 됩니다.
참고로 주의할 점은, A는 K보다 높은 카드이지 1이 아닙니다. 따라서 Q, K, A, 2, 3 은 스트레이트가 아니라는 점입니다.
from utils import streamReader
from euler import elapsed
def evaluate(cards: list[tuple[int, str]]) -> tuple[int, list[int]]:
score = 0
d = dict()
for n, _ in cards:
d[n] = d.get(n, 0) + 1
ns = sum(
[
[x] * y
for x, y in sorted(d.items(), key=lambda x: (x[1], x[0]), reverse=True)
],
[],
)
# flush
if all(card[1] == cards[0][1] for card in cards):
score += 80
if ns == list(range(14, 9, -1)):
return (200, ns)
# straight
if all(x - y == 1 for (x, y) in zip(ns, ns[1:])):
score += 75
# kinds
if any(v == 4 for v in d.values()):
score += 100
if any(v == 3 for v in d.values()):
score += 70
score += sum(25 for v in d.values() if v == 2)
return score, ns
def main():
ps = {c: i for (i, c) in enumerate(",.23456789TJQKA")}
res = 0
url = "https://euler.synap.co.kr/files/poker.txt"
for line in streamReader(url):
# if not line.strip():
# continue
cards = [(ps[x[0]], x[1]) for x in line.split()]
a, b = cards[:5], cards[5:]
x, y = map(evaluate, (a, b))
if x > y:
res += 1
print(res)
if __name__ == "__main__":
main()
이 풀이에서 사용된 streamReader는 다른 글에서 소개한 바 있습니다. urlopen 함수가 리턴하는 http.client.HTTPResponse
객체역시 같은 i/o 프로토콜을 지원하므로 동일하게 적용할 수 있습니다.