용어와 Hand rank에 관한 내용은 이전포스팅을 참고하십시오.
승률
확률적 맥락에 의해 ♠A♣A와
승자판별
각 player의 hand와 board(5장의 community card)가 공개되면, 각각의 hand로부터 rank를 계산해서 비교합니다. 이 때, split(공동 1등)을 염두해 두어야 합니다. (원본 code에 TC가 포함되어 있습니다.)

def get_winner(players, board):
ranks = []
for player in players:
ranks.append(get_rank(player + board))
best_rank = ranks[0] # 0번(p1) 임시 우승
winner = [0] # split(동시 우승) 가능성 있음
for i in range(1, len(ranks)): # 1번(p2)부터 챌린지
current_rank = ranks[i]
if current_rank[0] < best_rank[0]: # 랭킹 우위
best_rank = current_rank # 우승자 변경
winner = [i] # 우승자 목록 단독 기록
elif current_rank[0] == best_rank[0]: # 동일 랭킹, 하이카드 비교
for cur, best in zip(current_rank[1], best_rank[1]):
if values.index(cur) < values.index(best): # 새 우승자 탄생
best_rank = current_rank
winner = [i]
break
elif values.index(cur) > values.index(best):
break
else:
winner.append(i) # 공동 우승자 탄생
return winner
승리 횟수 판별
pre-flop에서 board는 공개되지 않았으므로, 모든 경우의 수 \( _{52 - 2 * players}C_5 \)를 고려해야 합니다. heads-up의 경우 \( _{48}C_5 = 1,712,304 \)가지 경우의 수가 존재합니다.
def cnt_winner(players, board=["", "", "", "", ""]):
"""players, 카드 숫자로 넘어온다.(예)[[0,1],[2,3]]"""
outs = [i for i in range(52)]
winner_count = [0 for _ in range(len(players))]
# players의 hand를 문자열로 바꾼다.
for player in players:
for i in range(2):
outs.remove(player[i])
player[i] = int2card(player[i])
tot_match = 0
for flop1 in range(0, len(outs) - 4):
board[0] = int2card(outs[flop1])
for flop2 in range(flop1 + 1, len(outs) - 3):
board[1] = int2card(outs[flop2])
for flop3 in range(flop2 + 1, len(outs) - 2):
board[2] = int2card(outs[flop3])
for turn in range(flop3 + 1, len(outs) - 1):
board[3] = int2card(outs[turn])
for river in range(turn + 1, len(outs)):
board[4] = int2card(outs[river])
winners = get_winner(players, board)
for winner in winners:
winner_count[winner] += 1
# players의 hand를 숫자로 바꾼다.
for player in players:
for i in range(2):
player[i] = card2int(player[i])
# print(test_code) # test Code: 테스트 결과 출력력
return winner_count
코드 검증
간단한 계산으로 예측가능한 값과 실제로 출력된 갯수를 비교하여 검증하였습니다.
CASE #1.
Hand) [As, Ac] [Ah, Ad] 조건) p2가 p1을 flush vs flush로 이길 경우의 수 출력결과) 1,540 예측) CC 5장이 모두 heart 또는 diamond가 나와야 함. 계산) 1. 모두 diamond가 나오면, p1도 flush가 성립하므로 12(Ad빠짐)에서 5장이 나올 경우의 수는 792가지 p2가 R. flush가 나오는 경우를 제외[8개] : [Kd, Qd, Jd, Td] + 9d, 8d, 7d, 6d, 5d, 4d, 3d, 2d p2가 (back) S. flush [8개] : [5d, 4d, 3d, 2d] + 6d, 7d, 8d, 9d, Td, Jd, Qd, Kd (A Top 아닌) S.flush [6개] : (Qd, Jd, Td, 9d, 8d, 7d) Kd->R., 6d-> back 도합 (792-22)*2개의 case발생 = 1540개
CASE #2.
Hand) [As, Ts] [9s, 2s] 조건) p2가 p1을 S. flush로이길 경우의 수 출력 결과) 86개 예측) '♠3456' + wild = 44C1, '♠5678' + wild = 44C1, 88개 예외) S. flush Top8, Top7 무승부, 2개 예측 결과) 86개 p1 hand('As') ['Ac', '6s', '5s', '4s', '3s'] ['Ah', '6s', '5s', '4s', '3s'] ['Ad', '6s', '5s', '4s', '3s'] ['Ks', '6s', '5s', '4s', '3s'] ['Kc', '6s', '5s', '4s', '3s'] ['Kh', '6s', '5s', '4s', '3s'] ['Kd', '6s', '5s', '4s', '3s'] ['Qs', '6s', '5s', '4s', '3s'] ['Qc', '6s', '5s', '4s', '3s'] ['Qh', '6s', '5s', '4s', '3s'] ['Qd', '6s', '5s', '4s', '3s'] ['Js', '6s', '5s', '4s', '3s'] ['Jc', '6s', '5s', '4s', '3s'] ['Jh', '6s', '5s', '4s', '3s'] ['Jd', '6s', '5s', '4s', '3s'] p1 hand('Ts') ['Tc', '6s', '5s', '4s', '3s'] ['Th', '6s', '5s', '4s', '3s'] ['Td', '6s', '5s', '4s', '3s'] p2 hand('9s') ['9c', '6s', '5s', '4s', '3s'] ['9h', '6s', '5s', '4s', '3s'] ['9d', '6s', '5s', '4s', '3s'] ['8s', '6s', '5s', '4s', '3s'] ['8c', '6s', '5s', '4s', '3s'] ['8h', '6s', '5s', '4s', '3s'] ['8d', '6s', '5s', '4s', '3s'] (S. flush Top7 무승부) ['7c', '6s', '5s', '4s', '3s'] ['7h', '6s', '5s', '4s', '3s'] ['7d', '6s', '5s', '4s', '3s'] 65432 (p2: S. flush) ['6s', '6c', '5s', '4s', '3s'] ['6s', '6h', '5s', '4s', '3s'] ['6s', '6d', '5s', '4s', '3s'] ['6s', '5s', '5c', '4s', '3s'] ['6s', '5s', '5h', '4s', '3s'] ['6s', '5s', '5d', '4s', '3s'] ['6s', '5s', '4s', '4c', '3s'] ['6s', '5s', '4s', '4h', '3s'] ['6s', '5s', '4s', '4d', '3s'] ['6s', '5s', '4s', '3s', '3c'] ['6s', '5s', '4s', '3s', '3h'] ['6s', '5s', '4s', '3s', '3d'] p2 hand('2s') ['6s', '5s', '4s', '3s', '2c'] ['6s', '5s', '4s', '3s', '2h'] ['6s', '5s', '4s', '3s', '2d'] p1 hans('As') ['Ac', '8s', '7s', '6s', '5s'] ['Ah', '8s', '7s', '6s', '5s'] ['Ad', '8s', '7s', '6s', '5s'] ['Ks', '8s', '7s', '6s', '5s'] ['Kc', '8s', '7s', '6s', '5s'] ['Kh', '8s', '7s', '6s', '5s'] ['Kd', '8s', '7s', '6s', '5s'] ['Qs', '8s', '7s', '6s', '5s'] ['Qc', '8s', '7s', '6s', '5s'] ['Qh', '8s', '7s', '6s', '5s'] ['Qd', '8s', '7s', '6s', '5s'] ['Js', '8s', '7s', '6s', '5s'] ['Jc', '8s', '7s', '6s', '5s'] ['Jh', '8s', '7s', '6s', '5s'] ['Jd', '8s', '7s', '6s', '5s'] p1 hand('Ts') ['Tc', '8s', '7s', '6s', '5s'] ['Th', '8s', '7s', '6s', '5s'] ['Td', '8s', '7s', '6s', '5s'] p2 hand('9s') ['9c', '8s', '7s', '6s', '5s'] ['9h', '8s', '7s', '6s', '5s'] ['9d', '8s', '7s', '6s', '5s'] 98765 (p2: S. flush) ['8s', '8c', '7s', '6s', '5s'] ['8s', '8h', '7s', '6s', '5s'] ['8s', '8d', '7s', '6s', '5s'] ['8s', '7s', '7c', '6s', '5s'] ['8s', '7s', '7h', '6s', '5s'] ['8s', '7s', '7d', '6s', '5s'] ['8s', '7s', '6s', '6c', '5s'] ['8s', '7s', '6s', '6h', '5s'] ['8s', '7s', '6s', '6d', '5s'] ['8s', '7s', '6s', '5s', '5c'] ['8s', '7s', '6s', '5s', '5h'] ['8s', '7s', '6s', '5s', '5d'] (S. flush Top8 무승부) ['8s', '7s', '6s', '5s', '4c'] ['8s', '7s', '6s', '5s', '4h'] ['8s', '7s', '6s', '5s', '4d'] ['8s', '7s', '6s', '5s', '3s'] ['8s', '7s', '6s', '5s', '3c'] ['8s', '7s', '6s', '5s', '3h'] ['8s', '7s', '6s', '5s', '3d'] p2 hand('2s') ['8s', '7s', '6s', '5s', '2c'] ['8s', '7s', '6s', '5s', '2h'] ['8s', '7s', '6s', '5s', '2d']
CASE #3
HAND) [As Ts] [2s 3s] 조건) p2가 p1을 S. flush로 이길 경우의 수 출력결과) 989 예측) 45C2 = 990 예외) ['4s', '5s', '6s', '7s', '8s']의 경우 8 Top S. flush로 동점 예측 결과) 989
파일로 저장
- 총 169개의 hand가 존재(총: 1690시간, 70일)
- 하나의 Hand는 1개의 파일을 생성
- 1개의 파일은 총 1,225개의 승부를 기록(약 600분: 10시간)
- 1개의 승부는 각각 1,712,304의 경우의 수를 기록 (2~30초)
- 각 경우의 수에서 2명의 플레이어의 hand rank를 비교
- 각 hand는 10개의 rank로 구성
def print_file(my_hand):
# 1. 저장할 파일 불러오기
filename = f"{my_hand[0]}{my_hand[1]}.txt"
with open(filename, "r") as f:
lines = f.readlines()
# 2. 저장된 내용 불러오기(중단부분이 있다면), 시작할 지점 세팅
players = [[card2int(my_hand[0]), card2int(my_hand[1])], [0, 0]]
if lines:
last_record = lines[-1]
print("last record: ", last_record)
last_record = ast.literal_eval(f"[{last_record}]")
players[1] = [card2int(last_record[0][0]), card2int(last_record[0][1])]
print(players)
else:
players[1] = [0, 0]
start_processing = False # 중간 시작을 위한 flag
# 3. draw된 카드 풀 삭제
outs = [x for x in range(52)]
outs.remove(players[0][0])
outs.remove(players[0][1])
# 4. player2의 hand 생성
for i in range(50):
if (not start_processing) and outs[i] < players[1][0]:
continue
patterns = [] # hand패턴을 기억해서, 동일한 계산을 생략
for j in range(i + 1, 50):
if not start_processing:
if outs[j] <= players[1][1]:
if j == 49: # player2의 마지막 hand까지('2d') 계산이 완료된 경우
start_processing = True
print(">> Start process: ", players)
continue
start_processing = True
print(">> Start process: ", players)
# 5. 패턴 검사
players[1] = [outs[i], outs[j]]
cur_pattern = get_pattern(players)
message_out = f"\n['{int2card(players[1][0])}', '{int2card(players[1][1])}'], "
for k, pattern in enumerate(patterns):
if cur_pattern == pattern: # 5-1: 이전 계산값을 파일에서 찾는다.
search_text = f"{int2card(outs[i])}', '{int2card(outs[j] -len(patterns) + k)}"
with open(filename, "r") as f:
for line in f:
if search_text in line:
winner_count = ast.literal_eval(f"[{line}]")[1]
message_out += f"{winner_count}, '*'" # 이 계산은 복사되었음을 표시한다.
break
break
else:
# 6. 1,712,304가지의 승부를 계산한다.
winner_count = cnt_winner(players)
message_out += f"{winner_count}"
# 7. 파일에 기록한다.
with open(filename, "a") as f:
f.write(message_out)
# 8. 패턴 정보를 업데이트 한다.
if outs[j] % 4 == 3:
patterns = []
else:
patterns.append(cur_pattern)
저장된 파일(예시)
AsAc.txt
아래 코드는 ♠A♣A hand가 다른 hand와 붙었을 때, 승리 횟수입니다.
예를들어
승률은 약 88.4867%이며, 동점을 제외하면 87.2316% 확률로 승리합니다.
['Ah', 'Ad'], [1675094, 1675094]
['Ah', 'Ks'], [1612022, 123343]
['Ah', 'Kc'], [1612022, 123343]
['Ah', 'Kh'], [1515162, 218634] # 예시
['Ah', 'Kd'], [1595877, 137865]
['Ah', 'Qs'], [1604389, 130734]
['Ah', 'Qc'], [1604389, 130734]
['Ah', 'Qh'], [1508243, 225365]
['Ah', 'Qd'], [1588400, 145101]
['Ah', 'Js'], [1596756, 138125]
['Ah', 'Jc'], [1596756, 138125]
['Ah', 'Jh'], [1501324, 232096]
['Ah', 'Jd'], [1580923, 152337]
['Ah', 'Ts'], [1589123, 145516]