UNPKG

zholdem

Version:

A library for computing holdem hands

168 lines (152 loc) 6.68 kB
import {DevTool} from "./dev_tool"; import {Hand} from "./hand"; export class SimulationParameter { public numOfPlayers:number; public knownPlayerCardIndices:number[][]; public knownCommunityCardIndices:number[]; public simulationTimes:number; } export class SimulationResult { /** * splitInMRate = splitRates[M -1]; * totalEquity = splitIn1Rate / 1 + splitIn2Rate / 2 + ... splitIn9Rate / 9 */ public totalEquity:number; /** * ( * (splitIn2Rate / 1 - totalEquity)^2 * splitIn1Rate * N + * (splitIn2Rate / 2 - totalEquity)^2 * splitIn2Rate * N + * (splitIn2Rate / 3 - totalEquity)^2 * splitIn3Rate * N + * ... * (splitIn2Rate / 9 - totalEquity)^2 * splitIn9Rate * N + * * (totalEquity - 0)^2 * (1 - Sum(all split rate)) * N + * ) ^ 0.5 / (n-1)^0.5 */ public totalEquityStD:number; public winTimesByPlayers:number[]; /** for example, if in one time two players split the pot, the split times for each will be 0.5 */ public splitTimesByPlayers:number[]; public totalEquityByPlayers:number[]; public totalEquityStdByPlayers:number[]; public splitEquitiesByPlaysers:number[]; } export class Simulator { public static simulate(param:SimulationParameter):SimulationResult { let numOfPlayers = param.numOfPlayers; let splitEquitiesByPlaysers:number[] = DevTool.createZeroArray(numOfPlayers); let allPickedCardIndices = []; let winTimesByPlayers:number[] = DevTool.createZeroArray(numOfPlayers); let splitTimesByPlayers:number[] = DevTool.createZeroArray(numOfPlayers); let totalEquityStdByPlayers:number[] = new Array(numOfPlayers); let totalEquityByPlayers:number[] = new Array(numOfPlayers); param.knownPlayerCardIndices .forEach((cardIndicesList) => { allPickedCardIndices = allPickedCardIndices.concat(cardIndicesList); }); allPickedCardIndices = allPickedCardIndices.concat(param.knownCommunityCardIndices); // TODO(zzn): add exact computation (instead of random) // to see if it's picking a small number of cards. for (let s = 0 ; s < param.simulationTimes; s++) { let randomlyPickedCardIndices = Simulator.randomlyPickCards( numOfPlayers * 2 + 5 - allPickedCardIndices.length, allPickedCardIndices); // fill in all unfulfilled player's cards let cursorOfRandomPick = 0; let simulatedPlayerCardIndices:number[] = []; for (let p = 0; p < numOfPlayers; p++) { if (p < param.knownPlayerCardIndices.length) { if (param.knownPlayerCardIndices[p].length == 2) { simulatedPlayerCardIndices = simulatedPlayerCardIndices.concat(param.knownPlayerCardIndices[p]); } else { let fullFileNumOfCards = 2 - param.knownPlayerCardIndices[p].length; simulatedPlayerCardIndices = simulatedPlayerCardIndices.concat( randomlyPickedCardIndices .slice(cursorOfRandomPick, cursorOfRandomPick + fullFileNumOfCards)); cursorOfRandomPick += fullFileNumOfCards; } } else { simulatedPlayerCardIndices = simulatedPlayerCardIndices.concat( randomlyPickedCardIndices .slice(cursorOfRandomPick, cursorOfRandomPick + 2)); cursorOfRandomPick += 2; } } // last 5 cards as community cards; let communityCardIndices = param.knownCommunityCardIndices.concat(randomlyPickedCardIndices.slice(cursorOfRandomPick)); let currentWinners = []; let hands:Hand[] = []; for (let p = 0; p < numOfPlayers; p++) { hands.push(new Hand([ simulatedPlayerCardIndices[p * 2], simulatedPlayerCardIndices[p * 2 + 1] ].concat(communityCardIndices))); } let p1 = 0; let p2 = 1; do { let p1CompareWithP2:number = hands[p1].compareWith(hands[p2]); if (p1CompareWithP2 > 0) { currentWinners = [p1]; p2++; } else if (p1CompareWithP2 == 0) { if (currentWinners.indexOf(p1) < 0) currentWinners = currentWinners.concat([p1]); if (currentWinners.indexOf(p2) < 0) currentWinners = currentWinners.concat([p2]); p2++; } else { // p1CompareWithP2 < 0 currentWinners = [p2]; p1++; } } while(p1 < numOfPlayers && p2 < numOfPlayers); for (let p = 0; p < numOfPlayers; p++) { if (currentWinners.indexOf(p) >= 0) { // winners include me if (currentWinners.length > 1) { // me split with others splitTimesByPlayers[p] += 1; splitEquitiesByPlaysers[p] += 1/ currentWinners.length; } else winTimesByPlayers[p] += 1; // me win alone } } } let result = new SimulationResult(); result.winTimesByPlayers = winTimesByPlayers; result.splitTimesByPlayers = splitTimesByPlayers; let tmp = 0; for (let p = 0; p < numOfPlayers; p++) { let e/*equity*/ = (winTimesByPlayers[p] + splitEquitiesByPlaysers[p]) / param.simulationTimes; totalEquityByPlayers[p] = e; let part1 = Math.pow(1 - e, 2) * winTimesByPlayers[p]; let part2 = Math.pow((e - splitEquitiesByPlaysers[p]),2 ) * splitTimesByPlayers[p]; let part3 = Math.pow(e, 2) * (param.simulationTimes - winTimesByPlayers[p] - splitTimesByPlayers[p]); let denominator = param.simulationTimes * (param.simulationTimes - 1); totalEquityStdByPlayers[p] = Math.sqrt((part1 + part2 + part3) / denominator); } result.totalEquityByPlayers = totalEquityByPlayers; result.totalEquityStdByPlayers = totalEquityStdByPlayers; result.splitTimesByPlayers = splitTimesByPlayers; result.splitEquitiesByPlaysers = splitEquitiesByPlaysers; return result; } /** * Randomly picks a number of cards from a suit of poker, 52 cards, does not contain the jokers. * @param numberOfCardsToPick, for example, 5 means picking 5 cards from the suit of poker * @param alreadyPickedCards * @returns {null} * TODO(zzn): test randomness */ private static randomlyPickCards(numberOfCardsToPick:number, alreadyPickedCards:number[]):number[] { let localAlreadyPickedCards:number[] = alreadyPickedCards.concat([]); // clone a new instance let picked = []; for (let i = 0; i < numberOfCardsToPick; i++) { let candidateCardIndex = null; do { candidateCardIndex = Math.floor(Math.random() * 52); } while (localAlreadyPickedCards.indexOf(candidateCardIndex) >= 0); localAlreadyPickedCards.push(candidateCardIndex); picked.push(candidateCardIndex); } return picked; } }