@@ -136,11 +136,202 @@ const {Com} = LOTW.globals.ShellMod.comClasses;
136136const { log, jlog, cwarn, cerr} = LOTW . api . util ;
137137//»
138138
139+ const generatePokerHands = ( startingHand ) => { //«
140+
141+ // Constants for ranks, suits, and iteration limit«
142+ const result = { } ;
143+ const ranks = [ 'A' , 'K' , 'Q' , 'J' , 'T' , '9' , '8' , '7' , '6' , '5' , '4' , '3' , '2' ] ;
144+ const suits = [ 's' , 'h' , 'd' , 'c' ] ;
145+ const MAX_ITERATIONS = 5000000 ;
146+ let iterationCount = 0 ;
147+
148+ // Validate starting hand (e.g., "AA", "AKs", "AKo")
149+ if ( ! startingHand || startingHand . length < 2 || startingHand . length > 3 ||
150+ ! ranks . includes ( startingHand [ 0 ] ) || ! ranks . includes ( startingHand [ 1 ] ) ) {
151+ throw new Error ( 'Invalid starting hand format' ) ;
152+ }
153+ const isPair = startingHand [ 0 ] === startingHand [ 1 ] ;
154+ if ( isPair && startingHand . length !== 2 ) {
155+ throw new Error ( 'Pairs must have exactly 2 characters' ) ;
156+ }
157+ if ( ! isPair && startingHand . length !== 3 ) {
158+ throw new Error ( 'Non-pairs must have exactly 3 characters' ) ;
159+ }
160+ if ( ! isPair && ! [ 's' , 'o' ] . includes ( startingHand [ 2 ] ) ) {
161+ throw new Error ( 'Non-pairs must end with s or o' ) ;
162+ }
163+
164+ // Extract base hole cards and suitedness
165+ const isSuited = startingHand . includes ( 's' ) ;
166+ const baseHole = startingHand . slice ( 0 , 2 ) ;
167+ const sortedHole = ranks . indexOf ( baseHole [ 0 ] ) <= ranks . indexOf ( baseHole [ 1 ] ) ? baseHole : baseHole [ 1 ] + baseHole [ 0 ] ;
168+ /* » */
169+ // Helper: Count distinct ranks in the board«
170+ const getDistinctBoardRanks = ( board ) => {
171+ return new Set ( board . split ( '' ) ) . size ;
172+ } ;
173+ /* » */
174+ // Helper: Get max possible suit-connecting cards«
175+ // - For suited hands: 2 hole cards + distinct board ranks
176+ // - For unsuited: 1 hole card + distinct board ranks
177+ const getMaxSuits = ( board ) => {
178+ const distinctBoardRanks = getDistinctBoardRanks ( board ) ;
179+ return isSuited ? distinctBoardRanks + 2 : distinctBoardRanks + 1 ;
180+ } ;
181+ /* » */
182+ // Helper: Validate if a suit count is possible for the round«
183+ const isValidSuitCount = ( round , suitCount ) => {
184+ if ( round === 'flop' ) return suitCount === 3 ;
185+ if ( round === 'turn' ) return suitCount >= 4 ;
186+ if ( round === 'river' ) return suitCount >= 4 ; // 4 for draw, 5+ for flush
187+ return false ;
188+ } ;
189+ /* » */
190+ // Helper: Assign valid flush statuses based on constraints«
191+ const assignStatuses = ( round , board , maxSuits , prevStatus = '' ) => {
192+ const statuses = new Set ( ) ;
193+ const prevSuitCount = prevStatus . match ( / \d $ / ) ? parseInt ( prevStatus . match ( / \d $ / ) [ 0 ] ) : 0 ;
194+
195+ // Always include 'a' (board-driven flush draw) and 'b' (no flush draw)
196+ // - 'a' requires enough distinct ranks (4 on turn, 5 on river for draw)
197+ const totalDistinctRanks = new Set ( [ ...baseHole . split ( '' ) , ...board . split ( '' ) ] ) . size ;
198+ const minDistinctRanks = round === 'river' ? 5 : round === 'turn' ? 4 : 3 ;
199+ if ( totalDistinctRanks >= minDistinctRanks ) statuses . add ( 'a' ) ;
200+ statuses . add ( 'b' ) ;
201+
202+ // Handle unsuited hands (c: lower rank connects, d: higher rank or pair)
203+ if ( ! isSuited ) {
204+ if ( ! isPair && totalDistinctRanks >= minDistinctRanks ) {
205+ if ( round === 'flop' && ( ! prevStatus || prevStatus === 'c3' ) && isValidSuitCount ( round , 3 ) ) {
206+ if ( maxSuits >= 3 ) statuses . add ( 'c3' ) ;
207+ } else if ( ( round === 'turn' || round === 'river' ) && ( ! prevStatus || prevStatus === 'c3' ) && isValidSuitCount ( round , 4 ) ) {
208+ if ( maxSuits >= 4 ) statuses . add ( 'c4' ) ;
209+ if ( round === 'river' && maxSuits >= 5 ) statuses . add ( 'c5' ) ;
210+ }
211+ }
212+ if ( totalDistinctRanks >= minDistinctRanks ) {
213+ if ( round === 'flop' && ( ! prevStatus || prevStatus === 'd3' ) && isValidSuitCount ( round , 3 ) ) {
214+ if ( maxSuits >= 3 ) statuses . add ( 'd3' ) ;
215+ } else if ( ( round === 'turn' || round === 'river' ) && ( ! prevStatus || prevStatus === 'd3' ) && isValidSuitCount ( round , 4 ) ) {
216+ if ( maxSuits >= 4 ) statuses . add ( 'd4' ) ;
217+ if ( round === 'river' && maxSuits >= 5 ) statuses . add ( 'd5' ) ;
218+ }
219+ }
220+ return Array . from ( statuses ) ;
221+ }
222+
223+ // Handle suited hands (e: both hole cards connect)
224+ if ( totalDistinctRanks >= minDistinctRanks ) {
225+ if ( round === 'flop' && ( ! prevStatus || prevStatus === 'e3' ) && isValidSuitCount ( round , 3 ) ) {
226+ if ( maxSuits >= 3 ) statuses . add ( 'e3' ) ;
227+ } else if ( round === 'turn' && prevStatus === 'e3' && isValidSuitCount ( round , 4 ) ) {
228+ if ( maxSuits >= 4 ) statuses . add ( 'e4' ) ;
229+ if ( maxSuits >= 5 ) statuses . add ( 'e5' ) ;
230+ if ( maxSuits >= 6 ) statuses . add ( 'e6' ) ;
231+ } else if ( round === 'river' && prevStatus . startsWith ( 'e' ) ) {
232+ if ( prevSuitCount === 3 && maxSuits >= 4 ) statuses . add ( 'e4' ) ;
233+ if ( ( prevSuitCount === 3 || prevSuitCount === 4 ) && maxSuits >= 5 ) statuses . add ( 'e5' ) ;
234+ if ( ( prevSuitCount === 3 || prevSuitCount === 4 || prevSuitCount === 5 ) && maxSuits >= 6 ) statuses . add ( 'e6' ) ;
235+ if ( ( prevSuitCount === 3 || prevSuitCount === 4 || prevSuitCount === 5 || prevSuitCount === 6 ) && maxSuits >= 7 ) statuses . add ( 'e7' ) ;
236+ }
237+ }
238+ return Array . from ( statuses ) ;
239+ } ; /* » */
240+
241+ // Generate all possible flops«
242+ const generateFlops = ( ) => {
243+ if ( ++ iterationCount > MAX_ITERATIONS ) {
244+ throw new Error ( 'Infinite loop detected in generateFlops' ) ;
245+ }
246+
247+ const flops = { } ;
248+ const usedRanks = baseHole . split ( '' ) ;
249+ const availableRanks = ranks . filter ( r => usedRanks . filter ( u => u === r ) . length < 4 ) ;
250+
251+ for ( let i = 0 ; i < availableRanks . length ; i ++ ) {
252+ for ( let j = i ; j < availableRanks . length ; j ++ ) {
253+ for ( let k = j ; k < availableRanks . length ; k ++ ) {
254+ const flopRanks = [ availableRanks [ i ] , availableRanks [ j ] , availableRanks [ k ] ] ;
255+ const combinedRanks = [ ...usedRanks , ...flopRanks ] ;
256+ if ( combinedRanks . some ( r => combinedRanks . filter ( cr => cr === r ) . length > 4 ) ) continue ;
257+ const sortedFlop = flopRanks . sort ( ( a , b ) => ranks . indexOf ( a ) - ranks . indexOf ( b ) ) . join ( '' ) ;
258+ const maxSuits = getMaxSuits ( sortedFlop ) ;
259+ const flushStatuses = assignStatuses ( 'flop' , sortedFlop , maxSuits ) ;
260+ for ( let status of flushStatuses ) {
261+ const flopKey = baseHole + sortedFlop + status ;
262+ if ( flopKey . length === 6 || flopKey . length === 7 ) {
263+ flops [ flopKey ] = generateTurns ( flopKey ) ;
264+ }
265+ }
266+ }
267+ }
268+ }
269+ return flops ;
270+ } ; /* » */
271+ // Generate all possible turns for a given flop«
272+ const generateTurns = ( flopKey ) => {
273+ if ( ++ iterationCount > MAX_ITERATIONS ) {
274+ throw new Error ( 'Infinite loop detected in generateTurns' ) ;
275+ }
276+
277+ const turns = { } ;
278+ const usedRanks = flopKey . replace ( / [ s o a - e 0 - 7 ] / g, '' ) . split ( '' ) ;
279+ const availableRanks = ranks . filter ( r => usedRanks . filter ( u => u === r ) . length < 4 ) ;
280+ const prevStatus = flopKey . match ( / [ a - e ] [ 0 - 7 ] ? $ / ) [ 0 ] ;
281+
282+ for ( let i = 0 ; i < availableRanks . length ; i ++ ) {
283+ const turnCard = availableRanks [ i ] ;
284+ const board = flopKey . slice ( 2 , 5 ) + turnCard ;
285+ const combinedRanks = [ ...usedRanks , turnCard ] ;
286+ if ( combinedRanks . some ( r => combinedRanks . filter ( cr => cr === r ) . length > 4 ) ) continue ;
287+ const sortedBoard = board . split ( '' ) . sort ( ( a , b ) => ranks . indexOf ( a ) - ranks . indexOf ( b ) ) . join ( '' ) ;
288+ const maxSuits = getMaxSuits ( sortedBoard ) ;
289+ const flushStatuses = assignStatuses ( 'turn' , sortedBoard , maxSuits , prevStatus ) ;
290+ for ( let status of flushStatuses ) {
291+ const turnKey = baseHole + sortedBoard + status ;
292+ turns [ turnKey ] = generateRivers ( turnKey ) ;
293+ }
294+ }
295+ return turns ;
296+ } ; /* » */
297+ // Generate all possible rivers for a given turn«
298+ const generateRivers = ( turnKey ) => {
299+ if ( ++ iterationCount > MAX_ITERATIONS ) {
300+ throw new Error ( 'Infinite loop detected in generateRivers' ) ;
301+ }
302+
303+ const rivers = [ ] ;
304+ const usedRanks = turnKey . replace ( / [ s o a - e 0 - 7 ] / g, '' ) . split ( '' ) ;
305+ const availableRanks = ranks . filter ( r => usedRanks . filter ( u => u === r ) . length < 4 ) ;
306+ const prevStatus = turnKey . match ( / [ a - e ] [ 0 - 7 ] ? $ / ) [ 0 ] ;
307+
308+ for ( let i = 0 ; i < availableRanks . length ; i ++ ) {
309+ const riverCard = availableRanks [ i ] ;
310+ const board = turnKey . slice ( 2 , 6 ) + riverCard ;
311+ const combinedRanks = [ ...usedRanks , riverCard ] ;
312+ if ( combinedRanks . some ( r => combinedRanks . filter ( cr => cr === r ) . length > 4 ) ) continue ;
313+ const sortedBoard = board . split ( '' ) . sort ( ( a , b ) => ranks . indexOf ( a ) - ranks . indexOf ( b ) ) . join ( '' ) ;
314+ const maxSuits = getMaxSuits ( sortedBoard ) ;
315+ const flushStatuses = assignStatuses ( 'river' , sortedBoard , maxSuits , prevStatus ) ;
316+ for ( let status of flushStatuses ) {
317+ const riverKey = baseHole + sortedBoard + status ;
318+ rivers . push ( riverKey ) ;
319+ }
320+ }
321+ return rivers ;
322+ } ; /* » */
323+
324+ // Generate the tree for the sorted starting hand
325+ result [ sortedHole ] = generateFlops ( ) ;
326+ return result ;
327+ } ; //»
328+
329+ /*
139330const generatePokerHands = (startingHand) => {
140331 const result = {};
141332 const ranks = ['A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2'];
142333 const suits = ['s', 'h', 'd', 'c'];
143- const MAX_ITERATIONS = 1250000 ; // Infinite loop protection limit
334+ const MAX_ITERATIONS = 5000000 ; // Infinite loop protection limit
144335 let iterationCount = 0;
145336
146337 const isSuited = startingHand.includes('s');
@@ -161,7 +352,6 @@ const generatePokerHands = (startingHand) => {
161352 const boardHasQuads = boardRanks.some(r => boardRanks.filter(br => br === r).length >= 4);
162353 const allRanks = [...holeRanks, ...boardRanks];
163354 const distinctRanks = new Set(allRanks).size;
164- // const minDistinctRanks = isRiver ? 5 : 4; // 4 for flop/turn, 5 for river
165355 const minDistinctRanks = isRiver ? 5 : isTurn ? 4 : 3; // 4 for flop/turn, 5 for river
166356 const statuses = new Set();
167357
@@ -270,6 +460,7 @@ const generatePokerHands = (startingHand) => {
270460
271461 return result;
272462};
463+ */
273464/*«
274465const generatePokerHands = (startingHand) => {
275466 const result = {};
@@ -768,9 +959,11 @@ try{
768959let hand = args [ 0 ] ;
769960const pokerHands = generatePokerHands ( hand ) ;
770961log ( pokerHands ) ;
962+ /*
771963log(pokerHands.AKo.AKAAAb);
772964log(pokerHands.AKo.AKAAAc);
773965log(pokerHands.AKo.AKAAAd);
966+ */
774967//let
775968/*
776969a) The hole has no suited connections with a board that has a flush draw.
@@ -780,13 +973,13 @@ d) A suit-connected hole+board such that the highest ranked suit connects with t
780973e) A suit-connected hole+board, such that both hole cards connect with the board's suits.
781974*/
782975//log(pokerHands.AA.AAAAKd);
783- /*
976+ /// *
784977let hand_no_suit = hand . replace ( / [ s o ] $ / , "" ) ;
785- for (let c of ["a", "b", "c ", "d ", "e "]){
978+ for ( let c of [ "a" , "b" , "c3 " , "d3 " , "e3 " ] ) {
786979
787980log ( c , pokerHands [ hand ] [ `${ hand_no_suit } 432${ c } ` ] ) ;
788981}
789- */
982+ // */
790983//log(pokerHands[hand]["22222b"]);
791984//log("", pokerHands[hand][hand+"432b"]);
792985//log("", pokerHands[hand][hand+"432c"]);
0 commit comments