Skip to content

Commit

Permalink
feat(Poker): Improve poker hand result details with kicker cards
Browse files Browse the repository at this point in the history
Related to #7
  • Loading branch information
mitch-b committed Mar 22, 2018
1 parent e077945 commit fa3211b
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 21 deletions.
39 changes: 38 additions & 1 deletion src/models/poker/pokerHandResult.model.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { PokerHandType } from './pokerHandType.model'
import { PlayingCard } from '../card/playingCard.model'
import { IRankSet } from '../card/rankSet.interface'
import { AceHighRankSet } from '../card/aceHighRankSet.model'

export class PokerHandResult {
/**
Expand All @@ -22,12 +24,47 @@ export class PokerHandResult {
* Cards in result.
*/
public cardsUsed: PlayingCard[] = []
/**
* RankSet used to help determine highest ranked kickers
* not used in hand score
*/
public rankSet: IRankSet
/**
* Cards that were used when scoring the hand
* but had no impact on the score itself. Used to
* help determine a winner in event of a tied score value.
*/
public get kickers (): PlayingCard[] {
const cardsNotUsedInResult =
this.cards.filter((c) => this.cardsUsed.map(u => u.getIndex()).indexOf(c.getIndex()) === -1)
.sort((a, b) => this.rankSet.getRankValue(b) - this.rankSet.getRankValue(a))
return [...cardsNotUsedInResult]
}

// public get scoringHandCardNames (): string[] {
// const pullSingleCardForDescription =
// [PokerHandType.FourOfAKind, PokerHandType.ThreeOfAKind, PokerHandType.OnePair]
// if (pullSingleCardForDescription.indexOf(this.handType) >= 0) {
// return [CardName[this.cardsUsed[0].cardName]]
// } else if (this.handType === PokerHandType.TwoPair) {
// return [CardName[this.cardsUsed[0].cardName], CardName[this.cardsUsed[2].cardName]]
// } else if (this.handType === PokerHandType.FullHouse) {
// return [CardName[this.cardsUsed[0].cardName], CardName[this.cardsUsed[3].cardName]]
// } else {
// return this.cardsUsed.map(c => CardName[c.cardName].toString())
// }
// }

constructor (
cards: PlayingCard[] = [],
value: number = 0) {
value: number = 0,
cardsUsed: PlayingCard[] = [],
rankSet = new AceHighRankSet()
) {
this.cards = cards
this.value = value
this.cardsUsed = cardsUsed
this.rankSet = rankSet
}

setHandType (type: PokerHandType): this {
Expand Down
32 changes: 31 additions & 1 deletion src/models/poker/pokerHandResult.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { test } from 'ava'
import { PokerHandResult, PokerHandType } from 'typedeck'
import { Suit, PlayingCard, CardName, PokerHandResult, PokerHandType, AceLowRankSet, AceHighRankSet } from 'typedeck'

test('test tostring of hand types', async t => {
const handResult = new PokerHandResult()
Expand All @@ -22,3 +22,33 @@ test('test tostring of hand types', async t => {
handResult.setHandType(PokerHandType.RoyalFlush)
t.deepEqual(handResult.toString(), 'Royal Flush')
})

test('kickers should be cards which do not exist in cardsUsed', t => {
const cardsInPlay = [new PlayingCard(CardName.Seven, Suit.Spades), new PlayingCard(CardName.Six, Suit.Diamonds),
new PlayingCard(CardName.Nine, Suit.Spades), new PlayingCard(CardName.Nine, Suit.Diamonds),
new PlayingCard(CardName.Nine, Suit.Hearts)]
const cardsUsed = [new PlayingCard(CardName.Nine, Suit.Spades), new PlayingCard(CardName.Nine, Suit.Diamonds),
new PlayingCard(CardName.Nine, Suit.Hearts)]
const handResult = new PokerHandResult(cardsInPlay, 0, cardsUsed)
t.true(handResult.kickers.length === 2)
t.true(handResult.kickers[0].cardName === CardName.Seven)
t.true(handResult.kickers[1].cardName === CardName.Six)
})

test('kickers should be empty if all cards used', t => {
const cardsInPlay = [new PlayingCard(CardName.Seven, Suit.Spades), new PlayingCard(CardName.Seven, Suit.Diamonds),
new PlayingCard(CardName.Nine, Suit.Spades), new PlayingCard(CardName.Nine, Suit.Diamonds),
new PlayingCard(CardName.Nine, Suit.Hearts)]
const cardsUsed = [new PlayingCard(CardName.Seven, Suit.Spades), new PlayingCard(CardName.Seven, Suit.Diamonds),
new PlayingCard(CardName.Nine, Suit.Spades), new PlayingCard(CardName.Nine, Suit.Diamonds),
new PlayingCard(CardName.Nine, Suit.Hearts)]
const handResult = new PokerHandResult(cardsInPlay, 0, cardsUsed)
t.true(handResult.kickers.length === 0)
})

test('can be created with different RankSets', t => {
const handResult1 = new PokerHandResult([], 0, [], new AceHighRankSet())
t.true(handResult1.rankSet instanceof AceHighRankSet)
const handResult2 = new PokerHandResult([], 0, [], new AceLowRankSet())
t.true(handResult2.rankSet instanceof AceLowRankSet)
})
36 changes: 22 additions & 14 deletions src/services/pokerScore.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,29 +153,37 @@ export class PokerScoreService implements IPokerScoreService {
}

private calculate (cards: PlayingCard[]): PokerHandResult {
let result: PokerHandResult
const ranked: PlayingCard[][] = this.ranked(cards)
const isFlush = this.isFlush(cards)
const isStraight = this.isStraight(ranked)
if (isStraight && isFlush && ranked[0][0].cardName === CardName.Ace) {
return new PokerHandResult(cards, this.value(ranked, 9)).setHandType(PokerHandType.RoyalFlush)
const highestPlayedCards = ranked[0]
const rankSet = this.gameType.rankSet
if (isStraight && isFlush && highestPlayedCards[0].cardName === CardName.Ace) {
const royalFlushCards = [ranked[0][0], ranked[1][0], ranked[2][0], ranked[3][0], ranked[4][0]]
result = new PokerHandResult(cards, this.value(ranked, 9), royalFlushCards, rankSet).setHandType(PokerHandType.RoyalFlush)
} else if (isStraight && isFlush) {
return new PokerHandResult(cards, this.value(ranked, 8)).setHandType(PokerHandType.StraightFlush)
} else if (ranked[0].length === 4) {
return new PokerHandResult(cards, this.value(ranked, 7)).setHandType(PokerHandType.FourOfAKind)
const straightFlushCards = [ranked[0][0], ranked[1][0], ranked[2][0], ranked[3][0], ranked[4][0]]
result = new PokerHandResult(cards, this.value(ranked, 8), straightFlushCards, rankSet).setHandType(PokerHandType.StraightFlush)
} else if (highestPlayedCards.length === 4) {
result = new PokerHandResult(cards, this.value(ranked, 7), highestPlayedCards, rankSet).setHandType(PokerHandType.FourOfAKind)
} else if (ranked[0].length === 3 && ranked[1].length === 2) {
return new PokerHandResult(cards, this.value(ranked, 6)).setHandType(PokerHandType.FullHouse)
result = new PokerHandResult(cards, this.value(ranked, 6), ranked[0].concat(ranked[1]), rankSet).setHandType(PokerHandType.FullHouse)
} else if (isFlush) {
return new PokerHandResult(cards, this.value(ranked, 5)).setHandType(PokerHandType.Flush)
const flushCards = [ranked[0][0], ranked[1][0], ranked[2][0], ranked[3][0], ranked[4][0]]
result = new PokerHandResult(cards, this.value(ranked, 5), flushCards, rankSet).setHandType(PokerHandType.Flush)
} else if (isStraight) {
return new PokerHandResult(cards, this.value(ranked, 4)).setHandType(PokerHandType.Straight)
} else if (ranked[0].length === 3) {
return new PokerHandResult(cards, this.value(ranked, 3)).setHandType(PokerHandType.ThreeOfAKind)
const straightCards = [ranked[0][0], ranked[1][0], ranked[2][0], ranked[3][0], ranked[4][0]]
result = new PokerHandResult(cards, this.value(ranked, 4), straightCards, rankSet).setHandType(PokerHandType.Straight)
} else if (highestPlayedCards.length === 3) {
result = new PokerHandResult(cards, this.value(ranked, 3), highestPlayedCards, rankSet).setHandType(PokerHandType.ThreeOfAKind)
} else if (ranked[0].length === 2 && ranked[1].length === 2) {
return new PokerHandResult(cards, this.value(ranked, 2)).setHandType(PokerHandType.TwoPair)
} else if (ranked[0].length === 2) {
return new PokerHandResult(cards, this.value(ranked, 1)).setHandType(PokerHandType.OnePair)
result = new PokerHandResult(cards, this.value(ranked, 2), ranked[0].concat(ranked[1]), rankSet).setHandType(PokerHandType.TwoPair)
} else if (highestPlayedCards.length === 2) {
result = new PokerHandResult(cards, this.value(ranked, 1), highestPlayedCards, rankSet).setHandType(PokerHandType.OnePair)
} else {
return new PokerHandResult(cards, this.value(ranked, 0)).setHandType(PokerHandType.HighCard)
result = new PokerHandResult(cards, this.value(ranked, 0), highestPlayedCards, rankSet).setHandType(PokerHandType.HighCard)
}
return result
}
}
19 changes: 14 additions & 5 deletions src/services/pokerScore.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ test('evaluates a OnePair', async t => {
for (const hand of getHands(testForHandType)) {
let result = service.scoreCards(hand)
t.true(result.handType === testForHandType)
t.true(result.cardsUsed.length === 2)
}
})

Expand All @@ -224,6 +225,7 @@ test('evaluates a TwoPair', async t => {
for (const hand of getHands(testForHandType)) {
let result = service.scoreCards(hand)
t.true(result.handType === testForHandType)
t.true(result.cardsUsed.length === 4)
}
})

Expand All @@ -233,6 +235,7 @@ test('evaluates a ThreeOfAKind', async t => {
for (const hand of getHands(testForHandType)) {
let result = service.scoreCards(hand)
t.true(result.handType === testForHandType)
t.true(result.cardsUsed.length === 3)
}
})

Expand All @@ -242,51 +245,57 @@ test('evaluates a Straight', async t => {
for (const hand of getHands(testForHandType)) {
let result = service.scoreCards(hand)
t.true(result.handType === testForHandType)
t.true(result.cardsUsed.length === 5)
}
})

test('evaluates a Straight', async t => {
test('evaluates a Flush', async t => {
const service = new PokerScoreService()
const testForHandType = PokerHandType.Flush
for (const hand of getHands(testForHandType)) {
let result = service.scoreCards(hand)
t.true(result.handType === testForHandType)
t.true(result.cardsUsed.length === 5)
}
})

test('evaluates a Straight', async t => {
test('evaluates a FullHouse', async t => {
const service = new PokerScoreService()
const testForHandType = PokerHandType.FullHouse
for (const hand of getHands(testForHandType)) {
let result = service.scoreCards(hand)
t.true(result.handType === testForHandType)
t.true(result.cardsUsed.length === 5)
}
})

test('evaluates a Straight', async t => {
test('evaluates a FourOfAKind', async t => {
const service = new PokerScoreService()
const testForHandType = PokerHandType.FourOfAKind
for (const hand of getHands(testForHandType)) {
let result = service.scoreCards(hand)
t.true(result.handType === testForHandType)
t.true(result.cardsUsed.length === 4)
}
})

test('evaluates a Straight', async t => {
test('evaluates a StraightFlush', async t => {
const service = new PokerScoreService()
const testForHandType = PokerHandType.StraightFlush
for (const hand of getHands(testForHandType)) {
let result = service.scoreCards(hand)
t.true(result.handType === testForHandType)
t.true(result.cardsUsed.length === 5)
}
})

test('evaluates a Straight', async t => {
test('evaluates a RoyalFlush', async t => {
const service = new PokerScoreService()
const testForHandType = PokerHandType.RoyalFlush
for (const hand of getHands(testForHandType)) {
let result = service.scoreCards(hand)
t.true(result.handType === testForHandType)
t.true(result.cardsUsed.length === 5)
}
})

Expand Down

0 comments on commit fa3211b

Please sign in to comment.