Repository: chenosaurus/poker-evaluator Branch: master Commit: 73217b5857e3 Files: 8 Total size: 9.7 KB Directory structure: gitextract_jq9kxenc/ ├── .gitignore ├── .travis.yml ├── lib/ │ ├── 3CardConverter.js │ └── PokerEvaluator.js ├── package.json ├── readme.md └── test/ ├── poker-evaluator.spec.js └── speedtest.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ node_modules npm-debug.log ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - 0.8 env: NODE_ENV=travis before_install: - npm config set loglevel warn ================================================ FILE: lib/3CardConverter.js ================================================ //converts 3 card to lowest equivalent 5 card hand module.exports = { CARDVALS: ['2', '3', '4', '5', '6', '7', '8', '9', 't', 'j', 'q', 'k', 'a'], CARDS: { "2c": 1, "2d": 2, "2h": 3, "2s": 4, "3c": 5, "3d": 6, "3h": 7, "3s": 8, "4c": 9, "4d": 10, "4h": 11, "4s": 12, "5c": 13, "5d": 14, "5h": 15, "5s": 16, "6c": 17, "6d": 18, "6h": 19, "6s": 20, "7c": 21, "7d": 22, "7h": 23, "7s": 24, "8c": 25, "8d": 26, "8h": 27, "8s": 28, "9c": 29, "9d": 30, "9h": 31, "9s": 32, "tc": 33, "td": 34, "th": 35, "ts": 36, "jc": 37, "jd": 38, "jh": 39, "js": 40, "qc": 41, "qd": 42, "qh": 43, "qs": 44, "kc": 45, "kd": 46, "kh": 47, "ks": 48, "ac": 49, "ad": 50, "ah": 51, "as": 52 }, makesStraight: function(cardsUsed, rank) { //add ace to bottom as well var newCards = [cardsUsed[12]].concat(cardsUsed); //add in new card (pushed up one by ace) newCards[rank+1] = 2; //determine if there are 5 cards in a row return 5 === newCards.reduce(function(prev, next) { if (prev === 5) { return 5; } else { return next ? prev + 1 : 0; } }); }, fillHand: function(cards) { var cardsUsed = [0,0,0,0,0,0,0,0,0,0,0,0,0]; //convert each card to vals 0-12, strip suit cards.forEach(function(card) { var i = Math.floor((this.CARDS[card.toLowerCase()]-1)/4); cardsUsed[i] = 1; }, this); var toFill = 2; //need to fill 2 cards //fill in cards to complete 5 card hand for (var i = 0; i < 13; i++) { if (toFill == 0) break; //done filling //prevent adding a card to finish a straight if (cardsUsed[i] == 0 && !this.makesStraight(cardsUsed,i)) { cardsUsed[i] = 2; toFill--; } } //fill dummy cards for lowest possible hand var suit = ['s', 'd']; for (var i = 0; i <= 13; i++) { if (cardsUsed[i] == 2) { var card = this.CARDVALS[i] + suit[0]; suit.splice(0, 1); cards.push(card); } } return cards; } }; ================================================ FILE: lib/PokerEvaluator.js ================================================ var fs = require("fs"); var path = require("path"); var ThreeCardConverter = require("./3CardConverter"); module.exports = { HANDTYPES: [ "invalid hand", "high card", "one pair", "two pairs", "three of a kind", "straight", "flush", "full house", "four of a kind", "straight flush" ], CARDS: { "2c": 1, "2d": 2, "2h": 3, "2s": 4, "3c": 5, "3d": 6, "3h": 7, "3s": 8, "4c": 9, "4d": 10, "4h": 11, "4s": 12, "5c": 13, "5d": 14, "5h": 15, "5s": 16, "6c": 17, "6d": 18, "6h": 19, "6s": 20, "7c": 21, "7d": 22, "7h": 23, "7s": 24, "8c": 25, "8d": 26, "8h": 27, "8s": 28, "9c": 29, "9d": 30, "9h": 31, "9s": 32, "tc": 33, "td": 34, "th": 35, "ts": 36, "jc": 37, "jd": 38, "jh": 39, "js": 40, "qc": 41, "qd": 42, "qh": 43, "qs": 44, "kc": 45, "kd": 46, "kh": 47, "ks": 48, "ac": 49, "ad": 50, "ah": 51, "as": 52 }, evalHand: function(cards) { if (!this.ranks) { throw new Error("HandRanks.dat not loaded"); } if (cards.length != 7 && cards.length != 6 && cards.length != 5 && cards.length != 3) { throw new Error("Hand must be 3, 5, 6, or 7 cards, but got " + cards.length); } //if 3 card hand, fill in to make 5 card if (cards.length == 3) { cards = ThreeCardConverter.fillHand(cards); } //if passing in string formatted hand, convert first if (typeof cards[0] == "string") { cards = cards.map(function(card) { return this.CARDS[card.toLowerCase()]; }.bind(this)); } return this.eval(cards); }, eval: function(cards) { var p = 53; for (var i = 0; i < cards.length; i++) { p = this.evalCard(p + cards[i]); } if (cards.length == 5 || cards.length == 6) { p = this.evalCard(p) } return { handType: p >> 12, handRank: p & 0x00000fff, value: p, handName: this.HANDTYPES[p >> 12] } }, evalCard: function(card) { return this.ranks.readUInt32LE(card * 4); } } var ranksFile = path.join(__dirname, '../data/HandRanks.dat'); module.exports.ranks = fs.readFileSync(ranksFile); ================================================ FILE: package.json ================================================ { "name": "poker-evaluator", "version": "0.3.2", "description": "A library to evaluate 3, 5 or 7 card poker hands", "homepage": "http://github.com/chenosaurus/poker-evaluator", "keywords": ["poker", "games"], "author": "David Chen ", "contributors": [ { "name": "Andreas Brekken", "email": "a@abrkn.com" } ], "repository": { "type": "git", "url": "https://github.com/chenosaurus/poker-evaluator.git" }, "dependencies": {}, "scripts": { "test": "jest && node test/speedtest.js" }, "devDependencies": { "jest": "^20.0.4" }, "main": "./lib/PokerEvaluator.js", "engines": { "node": ">= 0.6.0" } } ================================================ FILE: readme.md ================================================ # Poker Hand Evaluator Poker hand evaluator using the Two Plus Two alogorithm and lookup table. The lookup table HandRanks.dat is included in the module. It is capable of evaluating 7, 6, 5, and 3 card hands. The highest hand possible in a 3 card hand is 3 of a kind, straights & flushes do not apply to 3 cards. Hands can be evaluated by comparing the handType then the handRank to determine the better hand. This can evaluate about 22MM hands per second on a quad-core 2.7GHz Macbook Pro. Run the speedtest.js file under /test to try it. ## to install: npm install poker-evaluator ## Usage: ```js var PokerEvaluator = require("poker-evaluator"); PokerEvaluator.evalHand(["As", "Ks", "Qs", "Js", "Ts", "3c", "5h"]); //{ handType: 9, // handRank: 10, // value: 36874, // handName: 'straight flush' } PokerEvaluator.evalHand(["As", "Ac", "Ad", "5d", "5s"]); //{ handType: 7, // handRank: 148, // value: 28820, // handName: 'full house' } PokerEvaluator.evalHand(["As", "Ac", "Qs"]); //{ handType: 2, // handRank: 2761, // value: 10953, // handName: 'one pair' } ``` ================================================ FILE: test/poker-evaluator.spec.js ================================================ const PokerEvaluator = require('../lib/PokerEvaluator'); describe('evalHand', function() { describe('should throw', function() { it('if 4 cards', function() { expect(function() { PokerEvaluator.evalHand(['As', 'Ac', 'Ad', '5s']); }).toThrow(); }); it('if 8 cards', function() { expect(function() { PokerEvaluator.evalHand([ 'As', 'Ac', 'Ad', '5s', 'Ad', 'Ah', '5c', '5s' ]); }).toThrow(); }); }); it('5 cards, full house', function() { expect(PokerEvaluator.evalHand(['As', 'Ac', 'Ad', '5d', '5s'])).toEqual({ handType: 7, handRank: 148, value: 28820, handName: 'full house' }); }); it('3 cards, one pair', function() { expect(PokerEvaluator.evalHand(['As', 'Ac', 'Qs'])).toEqual({ handType: 2, handRank: 2761, value: 10953, handName: 'one pair' }); }); it('7 cards, straight flush', function() { expect( PokerEvaluator.evalHand(['As', 'Ks', 'Qs', 'Js', 'Ts', '3c', '5h']) ).toEqual({ handType: 9, handRank: 10, value: 36874, handName: 'straight flush' }); }); }); ================================================ FILE: test/speedtest.js ================================================ var evaluator = require(".."); var path = require("path"); var assert = require("assert"); function enumerateAllHands() { var u0, u1, u2, u3, u4, u5; var c0, c1, c2, c3, c4, c5, c6; var handTypeSum = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; var count = 0; console.log("Enumerating and evaluating all 133,784,560 possible 7-card poker hands...\n"); var startTime = Date.now(); for (c0 = 1; c0 < 47; c0++) { u0 = evaluator.evalCard(53+c0); for (c1 = c0+1; c1 < 48; c1++) { u1 = evaluator.evalCard(u0+c1); for (c2 = c1+1; c2 < 49; c2++) { u2 = evaluator.evalCard(u1+c2); for (c3 = c2+1; c3 < 50; c3++) { u3 = evaluator.evalCard(u2+c3); for (c4 = c3+1; c4 < 51; c4++) { u4 = evaluator.evalCard(u3+c4); for (c5 = c4+1; c5 < 52; c5++) { u5 = evaluator.evalCard(u4+c5); for (c6 = c5+1; c6 < 53; c6++) { handTypeSum[evaluator.evalCard(u5+c6) >> 12]++; count++; } } } } } } } var endTime = Date.now(); console.log("BAD: %d\n", handTypeSum[0]); console.log("High Card: %d\n", handTypeSum[1]); console.log("One Pair: %d\n", handTypeSum[2]); console.log("Two Pair: %d\n", handTypeSum[3]); console.log("Trips: %d\n", handTypeSum[4]); console.log("Straight: %d\n", handTypeSum[5]); console.log("Flush: %d\n", handTypeSum[6]); console.log("Full House: %d\n", handTypeSum[7]); console.log("Quads: %d\n", handTypeSum[8]); console.log("Straight Flush: %d\n", handTypeSum[9]); // Perform sanity checks.. make sure numbers are where they should be var testCount = 0; for (var index = 0; index < 10; index++) testCount += handTypeSum[index]; assert(testCount === count); assert(count === 133784560); assert(handTypeSum[0] === 0); console.log("\nEnumerated " + count + " hands in " + (endTime - startTime) + " milliseconds!\n"); } assert.equal(evaluator.CARDS['as'], 52); assert.equal(evaluator.HANDTYPES.length, 10); assert.deepEqual(evaluator.eval([1, 2, 3, 4, 5]), { handType: 8, handName: 'four of a kind', value: 32769, handRank: 1 }); enumerateAllHands();