Skip to content

Commit

Permalink
refactor: use shared HandStrength type
Browse files Browse the repository at this point in the history
BREAKING CHANGE
  • Loading branch information
mhuggins committed Nov 26, 2023
1 parent 1d2701e commit 3e63982
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 62 deletions.
16 changes: 3 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,6 @@ The following types are defined & utilized by this package.

- `EvaluatedHand`: An object representing the effective hand & strength, given a coordination of cards.
- `Odds`: An object representing how a hand will perform given a scenario. Includes the number of `wins`, `ties`, and `total` possible outcomes.
- `Strength`: An enumeration of hand strengths.
- `Strength.ROYAL_FLUSH`
- `Strength.STRAIGHT_FLUSH`
- `Strength.FOUR_OF_A_KIND`
- `Strength.FULL_HOUSE`
- `Strength.FLUSH`
- `Strength.STRAIGHT`
- `Strength.THREE_OF_A_KIND`
- `Strength.TWO_PAIR`
- `Strength.ONE_PAIR`
- `Strength.HIGH_CARD`

### Core Functions

Expand All @@ -29,7 +18,8 @@ The following types are defined & utilized by this package.
Given a list of hole cards and (optionally) community cards, determine the best possible poker hand and the strength of that hand.

```ts
import { evaluate, Strength } from '@poker-apprentice/hand-evaluator';
import { evaluate } from '@poker-apprentice/hand-evaluator';
import { HandStrength } from '@poker-apprentice/types';

const result = evaluate({
holeCards: ['As', 'Qd'],
Expand All @@ -39,7 +29,7 @@ const result = evaluate({
console.log(result);
// => { hand: ['As', 'Kc', 'Qd', 'Js', 'Td'], strength: 6 };

console.log(result.strength === Strength.STRAIGHT);
console.log(result.strength === HandStrength.Straight);
// => true
```

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"prepublishOnly": "yarn clean && yarn lint && yarn format && yarn test && yarn build"
},
"dependencies": {
"@poker-apprentice/types": "^1.1.2",
"@poker-apprentice/types": "^1.4.0",
"lodash": "^4.17.21"
},
"devDependencies": {
Expand Down
34 changes: 17 additions & 17 deletions src/__tests__/evaluate.test.ts
Original file line number Diff line number Diff line change
@@ -1,87 +1,87 @@
import { HandStrength } from '@poker-apprentice/types';
import { evaluate } from '../evaluate';
import { Strength } from '../types';

describe('evaluate', () => {
it('recognizes royal flushes', () => {
expect(evaluate({ holeCards: ['Qh', 'Qd', 'Td', 'Qs', 'Kd', 'Ad', 'Jd'] })).toEqual({
strength: Strength.ROYAL_FLUSH,
strength: HandStrength.RoyalFlush,
hand: ['Ad', 'Kd', 'Qd', 'Jd', 'Td'],
});
});

it('recognizes straight flushes', () => {
expect(evaluate({ holeCards: ['Qh', 'Qd', 'Td', 'Qs', 'Kd', '9d', 'Jd'] })).toEqual({
strength: Strength.STRAIGHT_FLUSH,
strength: HandStrength.StraightFlush,
hand: ['Kd', 'Qd', 'Jd', 'Td', '9d'],
});
});

it('recognizes straight flushes with ace treated as low', () => {
expect(evaluate({ holeCards: ['Qh', '5d', '2d', '3d', '8d', 'Ad', '4d'] })).toEqual({
strength: Strength.STRAIGHT_FLUSH,
strength: HandStrength.StraightFlush,
hand: ['5d', '4d', '3d', '2d', 'Ad'],
});
});

it('recognizes four of a kind', () => {
expect(evaluate({ holeCards: ['As', 'Qd', 'Js', 'Qs', 'Qc', 'Qh'] })).toEqual({
strength: Strength.FOUR_OF_A_KIND,
strength: HandStrength.FourOfAKind,
hand: ['Qd', 'Qs', 'Qc', 'Qh', 'As'],
});
});

it('recognizes full houses', () => {
expect(evaluate({ holeCards: ['As', 'Qd', 'Js', 'Qs', 'Jc', 'Qh'] })).toEqual({
strength: Strength.FULL_HOUSE,
strength: HandStrength.FullHouse,
hand: ['Qd', 'Qs', 'Qh', 'Js', 'Jc'],
});
});

it('recognizes stronger full houses', () => {
expect(evaluate({ holeCards: ['Js', 'Qd', 'Jc', 'Qs', 'Ac', 'Qh', 'Ah'] })).toEqual({
strength: Strength.FULL_HOUSE,
strength: HandStrength.FullHouse,
hand: ['Qd', 'Qs', 'Qh', 'Ac', 'Ah'],
});
});

it('recognizes flushes', () => {
expect(evaluate({ holeCards: ['Js', 'Qd', '8s', '4s', '6c', 'Qs', 'As'] })).toEqual({
strength: Strength.FLUSH,
strength: HandStrength.Flush,
hand: ['As', 'Qs', 'Js', '8s', '4s'],
});
});

it('recognizes straights', () => {
expect(evaluate({ holeCards: ['Qh', 'Qd', 'Td', 'Qs', 'Kh', '9d', 'Jc'] })).toEqual({
strength: Strength.STRAIGHT,
strength: HandStrength.Straight,
hand: ['Kh', 'Qh', 'Jc', 'Td', '9d'],
});
});

it('recognizes straights with ace treated as low', () => {
expect(evaluate({ holeCards: ['Qh', '5d', '2h', '3d', '8c', 'As', '4d'] })).toEqual({
strength: Strength.STRAIGHT,
strength: HandStrength.Straight,
hand: ['5d', '4d', '3d', '2h', 'As'],
});
});

it('recognizes three of a kind', () => {
expect(evaluate({ holeCards: ['As', 'Qd', 'Js', 'Qs', 'Qc', '2h'] })).toEqual({
strength: Strength.THREE_OF_A_KIND,
strength: HandStrength.ThreeOfAKind,
hand: ['Qd', 'Qs', 'Qc', 'As', 'Js'],
});
});

it('recognizes two pair', () => {
expect(evaluate({ holeCards: ['As', 'Qd', 'Js', 'Qs', '2h', 'Jh'] })).toEqual({
strength: Strength.TWO_PAIR,
strength: HandStrength.TwoPair,
hand: ['Qd', 'Qs', 'Js', 'Jh', 'As'],
});
});

it('recognizes one pair', () => {
expect(evaluate({ holeCards: ['As', 'Qd', 'Js', 'Qs', '2h', '3h'] })).toEqual({
strength: Strength.ONE_PAIR,
strength: HandStrength.OnePair,
hand: ['Qd', 'Qs', 'As', 'Js', '3h'],
});
});
Expand All @@ -95,7 +95,7 @@ describe('evaluate', () => {
maximumHoleCards: 2,
});
expect(omahaHand).toEqual({
strength: Strength.STRAIGHT,
strength: HandStrength.Straight,
hand: ['5d', '4s', '3d', '2s', 'As'],
});
});
Expand All @@ -107,7 +107,7 @@ describe('evaluate', () => {
maximumHoleCards: 2,
});
expect(pineappleHand).toEqual({
strength: Strength.HIGH_CARD,
strength: HandStrength.HighCard,
hand: ['Jc', 'Tc', '9h', '8c', '2d'],
});
});
Expand All @@ -121,7 +121,7 @@ describe('evaluate', () => {
maximumHoleCards: 2,
}),
).toEqual({
strength: Strength.HIGH_CARD,
strength: HandStrength.HighCard,
hand: ['Kh', 'Jc'],
});

Expand All @@ -132,7 +132,7 @@ describe('evaluate', () => {
maximumHoleCards: 2,
}),
).toEqual({
strength: Strength.ONE_PAIR,
strength: HandStrength.OnePair,
hand: ['As', 'Ad'],
});
});
Expand Down
2 changes: 1 addition & 1 deletion src/compare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ export const compare = (a: EvaluatedHand, b: EvaluatedHand) => {
if (a.strength === b.strength) {
return handComparator(a.hand, b.hand);
}
return a.strength < b.strength ? -1 : 1;
return a.strength < b.strength ? 1 : -1;
};
22 changes: 11 additions & 11 deletions src/evaluate.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Card, Rank, Suit, getRank, getSuit } from '@poker-apprentice/types';
import { Card, HandStrength, Rank, Suit, getRank, getSuit } from '@poker-apprentice/types';
import { compare } from './compare';
import { rankOrder } from './constants';
import { EvaluatedHand, Strength } from './types';
import { EvaluatedHand } from './types';
import { cardComparator } from './utils/cardComparator';
import { getCombinations } from './utils/getCombinations';
import { handComparator } from './utils/handComparator';
Expand Down Expand Up @@ -172,7 +172,7 @@ const evaluateHand = (unsortedCards: Card[]): EvaluatedHand => {
const straightFlushes = straights.filter((straight) => uniq(straight.map(getSuit)).length === 1);
if (straightFlushes.length > 0) {
const strength =
getRank(straightFlushes[0][0]) === 'A' ? Strength.ROYAL_FLUSH : Strength.STRAIGHT_FLUSH;
getRank(straightFlushes[0][0]) === 'A' ? HandStrength.RoyalFlush : HandStrength.StraightFlush;
return { strength, hand: straightFlushes[0] };
}

Expand All @@ -183,50 +183,50 @@ const evaluateHand = (unsortedCards: Card[]): EvaluatedHand => {
if (allQuads.length > 0) {
const quads = allQuads[0];
const kickers = getKickers(quads, cards);
return { strength: Strength.FOUR_OF_A_KIND, hand: [...quads, ...kickers] };
return { strength: HandStrength.FourOfAKind, hand: [...quads, ...kickers] };
}

// full house
const allTrips = getCardsOfLength(duplicates, 3);
const allPairs = getCardsOfLength(duplicates, 2);
if (allTrips.length > 0 && allPairs.length > 0) {
return { strength: Strength.FULL_HOUSE, hand: [...allTrips[0], ...allPairs[0]] };
return { strength: HandStrength.FullHouse, hand: [...allTrips[0], ...allPairs[0]] };
}

// flush
const flushes = getFlushes(cards);
if (flushes.length > 0) {
return { strength: Strength.FLUSH, hand: flushes[0] };
return { strength: HandStrength.Flush, hand: flushes[0] };
}

// straight
if (straights.length > 0) {
return { strength: Strength.STRAIGHT, hand: straights[0] };
return { strength: HandStrength.Straight, hand: straights[0] };
}

// three of a kind
if (allTrips.length > 0) {
const trips = allTrips[0];
const kickers = getKickers(trips, cards);
return { strength: Strength.THREE_OF_A_KIND, hand: [...trips, ...kickers] };
return { strength: HandStrength.ThreeOfAKind, hand: [...trips, ...kickers] };
}

// two pair
if (allPairs.length >= 2) {
const twoPair = [...allPairs[0], ...allPairs[1]];
const kickers = getKickers(twoPair, cards);
return { strength: Strength.TWO_PAIR, hand: [...twoPair, ...kickers] };
return { strength: HandStrength.TwoPair, hand: [...twoPair, ...kickers] };
}

// one pair
if (allPairs.length > 0) {
const pair = allPairs[0];
const kickers = getKickers(pair, cards);
return { strength: Strength.ONE_PAIR, hand: [...pair, ...kickers] };
return { strength: HandStrength.OnePair, hand: [...pair, ...kickers] };
}

// high card
return { strength: Strength.HIGH_CARD, hand: getKickers([], cards) };
return { strength: HandStrength.HighCard, hand: getKickers([], cards) };
};

export const evaluate = ({
Expand Down
17 changes: 2 additions & 15 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,7 @@
import { Hand } from '@poker-apprentice/types';

export enum Strength {
ROYAL_FLUSH = 1,
STRAIGHT_FLUSH = 2,
FOUR_OF_A_KIND = 3,
FULL_HOUSE = 4,
FLUSH = 5,
STRAIGHT = 6,
THREE_OF_A_KIND = 7,
TWO_PAIR = 8,
ONE_PAIR = 9,
HIGH_CARD = 10,
}
import { Hand, HandStrength } from '@poker-apprentice/types';

export interface EvaluatedHand {
strength: Strength;
strength: HandStrength;
hand: Hand;
}

Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1508,10 +1508,10 @@
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==

"@poker-apprentice/types@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@poker-apprentice/types/-/types-1.1.2.tgz#7f42f82db845c4dcb837644ce8faef0575bd9460"
integrity sha512-e1S56lyHYAjPGXOqMMaEr0CUaHIMqOssI77L1SJXRkEJ2J7EJX6QTfDcfLe0lJ8MIrYS9zWUqarSAMeuROyj6A==
"@poker-apprentice/types@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@poker-apprentice/types/-/types-1.4.0.tgz#367f679a3cb5b44c3a7c68b2b4b9fb14a85e842f"
integrity sha512-Tndj0HPJbu/Jg8gVOJkR0dymW1FkWPiXx9xGtmZgS1YtfTGpiS6zfixWlSQW/ZfAwJ0UF/MB2B/rVj5OGoHU5A==

"@rollup/plugin-babel@^6.0.3":
version "6.0.4"
Expand Down

0 comments on commit 3e63982

Please sign in to comment.