Skip to content

Commit 96d5c3d

Browse files
committed
Fix dealer weak-hand logic and schmaltz discard count to match original BASIC
1 parent a28e5a0 commit 96d5c3d

2 files changed

Lines changed: 59 additions & 54 deletions

File tree

71_Poker/python/players/dealer.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import random
44
from enum import Enum, auto
55

6-
from cards import Deck
6+
from cards import CardRank, Deck
77
from pokerhand import Hand, HandEvaluation, HandRank
88

99
from .player import Player
@@ -60,13 +60,13 @@ def decide_postdraw_bet(self) -> None:
6060

6161
def _is_hand_weak(self, result: HandEvaluation, is_redeal: bool) -> bool:
6262
"""Check whether the hand qualifies as 'weak'."""
63-
# Original logic: IF Z>5 THEN Weak.
64-
# HandRank 1-4 are Schmaltz, Partial Straight, Pair, Two-Pair.
65-
if result.hand_rank < HandRank.THREE_OF_A_KIND:
66-
# Partial Straight is only weak on re-evaluation
67-
if result.hand_rank == HandRank.PARTIAL_STRAIGHT:
68-
return is_redeal
69-
return True
63+
rank = result.hand_rank
64+
if rank < HandRank.PAIR:
65+
# Schmaltz is always weak; partial straight only on re-deal
66+
return rank != HandRank.PARTIAL_STRAIGHT or is_redeal
67+
if rank <= HandRank.TWO_PAIR:
68+
# Pair/two-pair is weak only when high card is Eight or lower
69+
return result.high_card.rank <= CardRank.EIGHT
7070
return False
7171

7272
def get_opening_action(self) -> Player.Action:

71_Poker/python/pokerhand/evaluation.py

Lines changed: 51 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@ class HandEvaluation:
2525
hand_rank: HandRank
2626
high_card: Card
2727
discard_indices: list[int]
28+
_ctx: "_EvaluationContext | None"
2829

2930
def __init__(self, hand: "Hand"):
3031
"""Perform evaluation of the provided hand."""
3132
self.discard_indices = []
33+
self._ctx = None
3234
self._evaluate(hand)
3335

3436
def __str__(self) -> str:
@@ -68,6 +70,9 @@ def __eq__(self, other: object) -> bool:
6870
other.high_card.rank.value,
6971
)
7072

73+
def __hash__(self) -> int:
74+
return hash((self.hand_rank, self.high_card.rank.value))
75+
7176
class _EvaluationContext: # pylint: disable=too-few-public-methods
7277
"""Intermediate calculations for hand evaluation."""
7378

@@ -92,9 +97,9 @@ def __init__(self, hand: "Hand"):
9297

9398
def _evaluate(self, hand: "Hand") -> None:
9499
"""Internal algorithm to determine hand strength."""
95-
ctx = self._EvaluationContext(hand)
100+
self._ctx = self._EvaluationContext(hand)
96101

97-
checks: list[Callable[[HandEvaluation._EvaluationContext], bool]] = [
102+
checks: list[Callable[[], bool]] = [
98103
self._check_four_of_a_kind,
99104
self._check_full_house,
100105
self._check_flush,
@@ -107,104 +112,104 @@ def _evaluate(self, hand: "Hand") -> None:
107112
]
108113

109114
for check in checks:
110-
if check(ctx):
115+
if check():
111116
break
112117

113-
def _check_four_of_a_kind(self, ctx: _EvaluationContext) -> bool:
114-
if ctx.freq_pattern != (4, 1):
118+
def _check_four_of_a_kind(self) -> bool:
119+
if self._ctx.freq_pattern != (4, 1):
115120
return False
116121
self.hand_rank = HandRank.FOUR_OF_A_KIND
117122
self.high_card = next(
118123
c
119-
for _, c in reversed(ctx.rank_indexed)
120-
if c.rank == ctx.ranks_by_freq[0]
124+
for _, c in reversed(self._ctx.rank_indexed)
125+
if c.rank == self._ctx.ranks_by_freq[0]
121126
)
122127
return True
123128

124-
def _check_full_house(self, ctx: _EvaluationContext) -> bool:
125-
if ctx.freq_pattern != (3, 2):
129+
def _check_full_house(self) -> bool:
130+
if self._ctx.freq_pattern != (3, 2):
126131
return False
127132
self.hand_rank = HandRank.FULL_HOUSE
128133
self.high_card = next(
129134
c
130-
for _, c in reversed(ctx.rank_indexed)
131-
if c.rank == ctx.ranks_by_freq[0]
135+
for _, c in reversed(self._ctx.rank_indexed)
136+
if c.rank == self._ctx.ranks_by_freq[0]
132137
)
133138
return True
134139

135-
def _check_flush(self, ctx: _EvaluationContext) -> bool:
136-
if len({c.suit for c in ctx.cards}) != 1:
140+
def _check_flush(self) -> bool:
141+
if len({c.suit for c in self._ctx.cards}) != 1:
137142
return False
138143
self.hand_rank = HandRank.FLUSH
139-
self.high_card = max(ctx.cards, key=lambda c: c.rank)
144+
self.high_card = self._ctx.rank_indexed[4][1]
140145
return True
141146

142-
def _check_straight(self, ctx: _EvaluationContext) -> bool:
147+
def _check_straight(self) -> bool:
143148
if (
144-
len(set(ctx.sorted_ranks)) == 5
145-
and ctx.sorted_ranks[4].value - ctx.sorted_ranks[0].value == 4
149+
len(set(self._ctx.sorted_ranks)) == 5
150+
and self._ctx.sorted_ranks[4].value - self._ctx.sorted_ranks[0].value == 4
146151
):
147152
self.hand_rank = HandRank.STRAIGHT
148-
self.high_card = ctx.rank_indexed[4][1]
153+
self.high_card = self._ctx.rank_indexed[4][1]
149154
return True
150155
return False
151156

152-
def _check_three_of_a_kind(self, ctx: _EvaluationContext) -> bool:
153-
if ctx.freq_pattern != (3, 1, 1):
157+
def _check_three_of_a_kind(self) -> bool:
158+
if self._ctx.freq_pattern != (3, 1, 1):
154159
return False
155160
self.hand_rank = HandRank.THREE_OF_A_KIND
156161
self.high_card = next(
157162
c
158-
for _, c in reversed(ctx.rank_indexed)
159-
if c.rank == ctx.ranks_by_freq[0]
163+
for _, c in reversed(self._ctx.rank_indexed)
164+
if c.rank == self._ctx.ranks_by_freq[0]
160165
)
161-
keep_rank = ctx.ranks_by_freq[0]
162-
self.discard_indices = [i for i, c in enumerate(ctx.cards) if c.rank != keep_rank]
166+
keep_rank = self._ctx.ranks_by_freq[0]
167+
self.discard_indices = [i for i, c in enumerate(self._ctx.cards) if c.rank != keep_rank]
163168
return True
164169

165-
def _check_two_pair(self, ctx: _EvaluationContext) -> bool:
166-
if ctx.freq_pattern != (2, 2, 1):
170+
def _check_two_pair(self) -> bool:
171+
if self._ctx.freq_pattern != (2, 2, 1):
167172
return False
168173
self.hand_rank = HandRank.TWO_PAIR
169174
self.high_card = next(
170175
c
171-
for _, c in reversed(ctx.rank_indexed)
172-
if c.rank == ctx.ranks_by_freq[0]
176+
for _, c in reversed(self._ctx.rank_indexed)
177+
if c.rank == self._ctx.ranks_by_freq[0]
173178
)
174-
keep = {ctx.ranks_by_freq[0], ctx.ranks_by_freq[1]}
179+
keep = {self._ctx.ranks_by_freq[0], self._ctx.ranks_by_freq[1]}
175180
self.discard_indices = [
176-
i for i, c in enumerate(ctx.cards) if c.rank not in keep
181+
i for i, c in enumerate(self._ctx.cards) if c.rank not in keep
177182
]
178183
return True
179184

180-
def _check_pair(self, ctx: _EvaluationContext) -> bool:
181-
if ctx.freq_pattern != (2, 1, 1, 1):
185+
def _check_pair(self) -> bool:
186+
if self._ctx.freq_pattern != (2, 1, 1, 1):
182187
return False
183188
self.hand_rank = HandRank.PAIR
184189
self.high_card = next(
185190
c
186-
for _, c in reversed(ctx.rank_indexed)
187-
if c.rank == ctx.ranks_by_freq[0]
191+
for _, c in reversed(self._ctx.rank_indexed)
192+
if c.rank == self._ctx.ranks_by_freq[0]
188193
)
189-
keep_rank = ctx.ranks_by_freq[0]
190-
self.discard_indices = [i for i, c in enumerate(ctx.cards) if c.rank != keep_rank]
194+
keep_rank = self._ctx.ranks_by_freq[0]
195+
self.discard_indices = [i for i, c in enumerate(self._ctx.cards) if c.rank != keep_rank]
191196
return True
192197

193-
def _check_partial_straight(self, ctx: _EvaluationContext) -> bool:
194-
if ctx.sorted_ranks[3].value - ctx.sorted_ranks[0].value == 3:
198+
def _check_partial_straight(self) -> bool:
199+
if self._ctx.sorted_ranks[3].value - self._ctx.sorted_ranks[0].value == 3:
195200
self.hand_rank = HandRank.PARTIAL_STRAIGHT
196-
self.high_card = ctx.rank_indexed[3][1]
197-
self.discard_indices = [ctx.rank_indexed[4][0]]
201+
self.high_card = self._ctx.rank_indexed[3][1]
202+
self.discard_indices = [self._ctx.rank_indexed[4][0]]
198203
return True
199-
if ctx.sorted_ranks[4].value - ctx.sorted_ranks[1].value == 3:
204+
if self._ctx.sorted_ranks[4].value - self._ctx.sorted_ranks[1].value == 3:
200205
self.hand_rank = HandRank.PARTIAL_STRAIGHT
201-
self.high_card = ctx.rank_indexed[4][1]
202-
self.discard_indices = [ctx.rank_indexed[0][0]]
206+
self.high_card = self._ctx.rank_indexed[4][1]
207+
self.discard_indices = [self._ctx.rank_indexed[0][0]]
203208
return True
204209
return False
205210

206-
def _check_schmaltz(self, ctx: _EvaluationContext) -> bool:
211+
def _check_schmaltz(self) -> bool:
207212
self.hand_rank = HandRank.SCHMALTZ
208-
self.high_card = ctx.rank_indexed[4][1]
209-
self.discard_indices = [idx for idx, _ in ctx.rank_indexed[0:4]]
213+
self.high_card = self._ctx.rank_indexed[4][1]
214+
self.discard_indices = [idx for idx, _ in self._ctx.rank_indexed[0:3]]
210215
return True

0 commit comments

Comments
 (0)