UNPKG

cactus-josh

Version:

An implementation of the CactusKev poker hand evaluator

73 lines (62 loc) 3.18 kB
const { flushes, fiveUniqueCards, hashAdjust, hashValues } = require( "./lookupTables" ); exports.suits = { 8: "Clubs", 4: "Diamonds", 2: "Hearts", 1: "Spades" }; exports.rankPrimes = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41 ]; exports.rank = card => ( card >>> 8 ) % 16; exports.suit = card => ( card >>> 12 ) % 16; exports.rankNames = [ "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King", "Ace" ]; exports.suitNames = [ null, "Spades", "Hearts", null, "Diamonds", null, null, null, "Clubs" ]; exports.cardName = card => `${ this.rankNames[ this.rank( card ) ] } of ${ this.suitNames[ this.suit( card ) ] }`; exports.fullDeck = shuffled => { const result = []; for ( let rank = 0; rank < 13; rank++ ) for ( let suit of [ 8, 4, 2, 1 ] ) result.push( ( this.rankPrimes[ rank ] ) | ( rank << 8 ) | ( suit << 12 ) | ( ( 1 << rank ) << 16 ) ); if ( !shuffled ) return result; for ( let i = 51; i > 0; i-- ) { const j = Math.floor( Math.random() * ( i + 1 ) ); [ result[ i ], result[ j ] ] = [ result[ j ], result[ i ] ]; } return result; } exports.flush = hand => hand.reduce( ( total, card ) => total & card, 0xF000 ); exports.flushBitPattern = flush => flush.reduce( ( total, card ) => total | card , 0 ) >>> 16; exports.flushRank = flush => flushes[ this.flushBitPattern( flush ) ]; exports.fiveUniqueCardsRank = hand => fiveUniqueCards[ this.flushBitPattern( hand ) ]; exports.primeMultiplicand = hand => hand.reduce( ( total, card ) => total * ( card & 0xFF ), 1 ); exports.findFast = u => { u += 0xe91aaa35; u ^= u >>> 16; u += u << 8; u ^= u >>> 4; let a = ( u + ( u << 2 ) ) >>> 19; return a ^ hashAdjust[ ( u >>> 8 ) & 0x1ff ]; }; exports.handRank = hand => { if ( this.flush( hand ) ) return this.flushRank( hand ); let fiveUniqueCardsRank = this.fiveUniqueCardsRank( hand ); if ( fiveUniqueCardsRank ) return fiveUniqueCardsRank; return hashValues[ this.findFast( this.primeMultiplicand( hand ) ) ]; }; exports.handValue = hand => { const rank = this.handRank( hand ); if ( rank > 6185 ) return "High card"; else if ( rank > 3325 ) return "One pair"; else if ( rank > 2467 ) return "Two pair"; else if ( rank > 1609 ) return "Three of a kind"; else if ( rank > 1599 ) return "Straight"; else if ( rank > 322 ) return "Flush"; else if ( rank > 166 ) return "Full house"; else if ( rank > 10 ) return "Four of a kind"; else return "Straight flush"; }; exports.possibleHands = ( deck, combinationLength ) => { let head, tail, result = []; if ( combinationLength > deck.length || combinationLength < 1 ) { return []; } if ( combinationLength === deck.length ) { return [ deck ]; } if ( combinationLength === 1 ) { return deck.map( element => [ element ] ); } for ( let i = 0; i < deck.length - combinationLength + 1; i++ ) { head = deck.slice( i, i + 1 ); tail = this.possibleHands( deck.slice( i + 1 ), combinationLength - 1 ); for ( let j = 0; j < tail.length; j++ ) { result.push( head.concat( tail[ j ] ) ); } } return result; }