diff --git a/package.json b/package.json index 9336f72..28186ed 100644 --- a/package.json +++ b/package.json @@ -28,5 +28,8 @@ }, "engines": [ "node >= 4.0.0" - ] + ], + "dependencies": { + "percom": "^1.1.3" + } } diff --git a/pokersolver.js b/pokersolver.js index 04c89da..700c8a4 100644 --- a/pokersolver.js +++ b/pokersolver.js @@ -4,6 +4,8 @@ * http://goldfirestudios.com */ +var percom = require('percom'); + (function() { 'use strict'; @@ -1824,10 +1826,52 @@ } } + class OmahaGame { + /** + * Build and return the best hand for Omaha Hold'em. + * @param {Array} boardCards Array of board cards (['Ad', '3c', 'Th', ...]). + * @param {Array} playerCards Array of player's cards (['Ad', '3c', 'Th', ...]). + * @return {Hand} Best hand. + */ + static solve(boardCards, playerCards) { + const game = new Game('standard'); + + var boardCombinations = percom.com(boardCards, 3); + var playerCombinations = percom.com(playerCards, 2); + var allCombinations = boardCombinations.reduce(function (res, b) { + return res.concat( + playerCombinations.map(function (p) { + return b.concat(...p); + }) + ); + }, []); + + var hands = game.handValues; + var result = null; + + for (var j = 0; j < allCombinations.length; j++) { + var combinationResult = null; + var cards = allCombinations[j]; + for (var i = 0; i < hands.length; i++) { + combinationResult = new hands[i](cards, game); + if (combinationResult.isPossible) { + break; + } + } + if (!result || result.compare(combinationResult) > 0) { + result = combinationResult; + } + } + + return result; + } + } + function exportToGlobal(global) { global.Card = Card; global.Hand = Hand; global.Game = Game; + global.OmahaGame = OmahaGame; global.RoyalFlush = RoyalFlush; global.NaturalRoyalFlush = NaturalRoyalFlush; global.WildRoyalFlush = WildRoyalFlush; diff --git a/test/omaha.js b/test/omaha.js new file mode 100644 index 0000000..e260cf8 --- /dev/null +++ b/test/omaha.js @@ -0,0 +1,190 @@ +var Flush = require('../pokersolver').Flush; +var StraightFlush = require('../pokersolver').StraightFlush; +var Straight = require('../pokersolver').Straight; +var FourOfAKind = require('../pokersolver').FourOfAKind; +var FullHouse = require('../pokersolver').FullHouse; +var ThreeOfAKind = require('../pokersolver').ThreeOfAKind; +var TwoPair = require('../pokersolver').TwoPair; +var OnePair = require('../pokersolver').OnePair; +var OmahaGame = require('../pokersolver').OmahaGame; + +describe('A basic hand', function () { + var boardCards = ['As', '4h', 'Kd', '3h', '5h']; + var playerCards = ['Ah', 'Ks', '4d', '2h']; + it('should return a hand with cards sorted descending', function () { + var hand = OmahaGame.solve(boardCards, playerCards); + hand.cardPool[0].toString().should.equal('Ah'); + return hand.cardPool[4].toString().should.equal('2h'); + }); + it('should return a correct description', function () { + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.descr.should.equal('Straight Flush, 5h High'); + }); + return it('should return throw an Error for a hand with duplicate cards', function () { + (function () { + var playerCards = ['As', 'Ah', 'Ks', '4d']; + OmahaGame.solve(boardCards, playerCards); + }.should.throw()); + }); +}); + +describe('A Straight Flush', function () { + it('should be detected as possible', function () { + var boardCards = ['As', '4h', 'Kd', '3h', '5h']; + var playerCards = ['Ah', 'Ks', '4d', '2h']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.be.an.instanceOf(StraightFlush); + }); + it('should be detected as not possible', function () { + var boardCards = ['Ah', '4h', 'Kd', '3h', '5h']; + var playerCards = ['As', 'Ks', '4d', '2h']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.not.be.an.instanceOf(StraightFlush); + }); + return it('should return 5 cards with low ace', function () { + var boardCards = ['Ah', '4h', 'Kd', '3h', '5h']; + var playerCards = ['As', 'Ks', '4d', '2h']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.cards.length.should.equal(5); + }); +}); + +describe('A Four of a Kind', function () { + it('should be detected as possible', function () { + var boardCards = ['Ah', '4h', 'Kd', 'Ad', '5h']; + var playerCards = ['As', 'Ac', '4d', '2h']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.be.an.instanceOf(FourOfAKind); + }); + return it('should be detected as not possible', function () { + var boardCards = ['Ah', '4h', 'Kd', '4d', '5h']; + var playerCards = ['As', 'Ac', 'Ad', '2h']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.not.be.an.instanceOf(FourOfAKind); + }); +}); + +describe('A Full House', function () { + it('should be detected as possible', function () { + var boardCards = ['Ah', '4h', '5d', 'Kd', '5h']; + var playerCards = ['As', '5c', '4d', '2h']; + var hand = OmahaGame.solve(boardCards, playerCards); + hand.should.be.an.instanceOf(FullHouse); + + boardCards = ['Ah', '4h', '5d', 'Ad', '7h']; + playerCards = ['As', '5c', '4d', '2h']; + hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.be.an.instanceOf(FullHouse); + }); + it('should be detected as not possible', function () { + var boardCards = ['2h', 'Ac', '4d', 'Kd', '5h']; + var playerCards = ['As', '5c', '5d', 'Ah']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.not.be.an.instanceOf(FullHouse); + }); + it('should pick the high kickers', function () { + var boardCards = ['Ah', '4h', '5d', 'Ad', '7h']; + var playerCards = ['As', '5c', '4d', '2h']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.cards.toString().indexOf('4h').should.equal(-1); + }); + return it('should be in order', function () { + var boardCards = ['Ah', '4h', '5d', 'Ad', '7h']; + var playerCards = ['As', '5c', '4d', '2h']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.cards.toString().should.equal('Ah,Ad,As,5d,5c'); + }); +}); + +describe('A Flush', function () { + it('should be detected as possible', function () { + var boardCards = ['2h', 'Ac', '4d', 'Kh', '5h']; + var playerCards = ['As', '4h', '5d', 'Ah']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.be.an.instanceOf(Flush); + }); + return it('should be detected as not possible', function () { + var boardCards = ['2h', 'Ac', '4d', 'Kh', '5d']; + var playerCards = ['As', '4h', '5h', 'Ah']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.not.be.an.instanceOf(Flush); + }); +}); + +describe('A Straight', function () { + it('should be detected as possible', function () { + var boardCards = ['2h', '3c', '4d', 'Kh', '5h']; + var playerCards = ['7s', '6c', '5d', 'Ah']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.be.an.instanceOf(Straight); + }); + it('should be detected as possible', function () { + var boardCards = ['9h', 'Jc', '4d', 'Th', '5h']; + var playerCards = ['As', '7c', '8d', 'Ah']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.be.an.instanceOf(Straight); + }); + it('should be detected as not possible', function () { + var boardCards = ['3h', '3c', '4d', 'Kh', '4h']; + var playerCards = ['7s', '6c', '5d', 'Ah']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.not.be.an.instanceOf(Straight); + }); + it('should detect a low ace', function () { + var boardCards = ['2h', '3c', '4d', 'Kh', '5h']; + var playerCards = ['7s', '7c', '5d', 'Ah']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.be.an.instanceOf(Straight); + }); + it('should detect a high ace', function () { + var boardCards = ['2h', 'Jc', '4d', 'Kh', 'Ah']; + var playerCards = ['7s', 'Tc', 'Qd', 'As']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.be.an.instanceOf(Straight); + }); +}); + +describe('Three of a Kind', function () { + it('should be detected as possible', function () { + var boardCards = ['2h', '3c', '4d', 'Kh', '7h']; + var playerCards = ['7s', '7c', '8d', 'Ah']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.be.an.instanceOf(ThreeOfAKind); + }); + it('should be detected as not possible', function () { + var boardCards = ['2h', '3c', '4d', 'Kh', '5h']; + var playerCards = ['7s', '7c', '7d', 'Ah']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.not.be.an.instanceOf(ThreeOfAKind); + }); +}); + +describe('Two Pair', function () { + it('should be detected as possible', function () { + var boardCards = ['2h', 'Jc', '4d', '8h', '8d']; + var playerCards = ['7s', '7c', '9d', 'Ah']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.be.an.instanceOf(TwoPair); + }); + it('should be detected as not possible', function () { + var boardCards = ['2h', '3c', '4d', 'Kh', '5h']; + var playerCards = ['7s', '7c', '5d', 'Jh']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.not.be.an.instanceOf(TwoPair); + }); +}); + +describe('One Pair', function () { + it('should be detected as possible', function () { + var boardCards = ['2h', '3c', '4d', 'Kh', '8h']; + var playerCards = ['Js', 'Qc', '8d', 'Ac']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.be.an.instanceOf(OnePair); + }); + it('should be detected as not possible', function () { + var boardCards = ['2h', '3c', '4d', 'Kh', '7h']; + var playerCards = ['Js', 'Qc', '8d', 'Ac']; + var hand = OmahaGame.solve(boardCards, playerCards); + return hand.should.not.be.an.instanceOf(OnePair); + }); +}); diff --git a/test/test.js b/test/test.js index e71163c..f1c7564 100644 --- a/test/test.js +++ b/test/test.js @@ -1,18 +1,19 @@ var should = require('should'); function importTest(name, path) { - describe(name, function() { + describe(name, function () { require(path); }); } -describe('main', function() { +describe('main', function () { importTest('Standard', './standard'); + importTest('Omaha', './omaha'); importTest('Jacks or Better', './jacksbetter'); importTest('Joker Poker', './joker'); importTest('Deuces Wild', './deuceswild'); importTest('Three Card Poker', './threecard'); importTest('Four Card Poker', './fourcard'); importTest('Four Card Aces Up Bonus', './fourcardbonus'); - return importTest('Pai Gow Poker', './paigowpokerfullhands'); + importTest('Pai Gow Poker', './paigowpokerfullhands'); });