@@ -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