Skip to content

Commit a5a1a9b

Browse files
committed
Wheee! What a lineup!!!
1 parent 8abeb69 commit a5a1a9b

1 file changed

Lines changed: 198 additions & 5 deletions

File tree

coms/games/poker.js

Lines changed: 198 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,202 @@ const {Com} = LOTW.globals.ShellMod.comClasses;
136136
const{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(/[soa-e0-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(/[soa-e0-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+
/*
139330
const 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
/*«
274465
const generatePokerHands = (startingHand) => {
275466
const result = {};
@@ -768,9 +959,11 @@ try{
768959
let hand = args[0];
769960
const pokerHands = generatePokerHands(hand);
770961
log(pokerHands);
962+
/*
771963
log(pokerHands.AKo.AKAAAb);
772964
log(pokerHands.AKo.AKAAAc);
773965
log(pokerHands.AKo.AKAAAd);
966+
*/
774967
//let
775968
/*
776969
a) 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
780973
e) A suit-connected hole+board, such that both hole cards connect with the board's suits.
781974
*/
782975
//log(pokerHands.AA.AAAAKd);
783-
/*
976+
///*
784977
let hand_no_suit = hand.replace(/[so]$/, "");
785-
for (let c of ["a", "b", "c", "d", "e"]){
978+
for (let c of ["a", "b", "c3", "d3", "e3"]){
786979

787980
log(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

Comments
 (0)