loading...

Daily Challenge #258 - Ranking Poker Hands

thepracticaldev profile image dev.to staff ・2 min read

A famous casino is suddenly faced with a sharp decline of their revenues. They decide to offer Texas hold'em also online. Can you help them by writing an algorithm that can rank poker hands?

Create a poker hand that has a method to compare itself to another poker hand:
PokerHand.prototype.compareWith = function(hand){...};

A poker hand has a constructor that accepts a string containing 5 cards:
var hand = new PokerHand("KS 2H 5C JD TD");

The characteristics of the string of cards are:

  • Each card consists of two characters, where
  • The first character is the value of the card: 2, 3, 4, 5, 6, 7, 8, 9, T(en), J(ack), Q(ueen), K(ing), A(ce)
  • The second character represents the suit: S(pades), H(earts), D(iamonds), C(lubs)
  • A space is used as card separator between cards

The result of your poker hand compare can be one of these 3 options:

var Result = 
{
    "win": 1,
    "loss": 2,
    "tie": 3
}

Notes

  • Apply the Texas Hold'em rules for ranking the cards.
  • Low aces are NOT valid in this challenge.
  • There is no ranking for the suits.

Examples
("2H 3H 4H 5H 6H", "AS AD AC AH JD") => WIN "Straight flush wins of 4 of a kind"
("2H 3H 4H 5H 6H", "KS AS TS QS JS") => LOSS "Highest straight flush wins"

Tests
("2H 3H 5H 6H 7H", "2S 3H 4H 5S 6C")
("2S 3H 4H 5S 6C", "3D 4C 5H 6H 2S")
("2S AH 4H 5S KC", "AH AC 5H 6H 7S")

Good luck!


This challenge comes from FrankK on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Discussion

pic
Editor guide
Collapse
sam_ferree profile image
Sam Ferree

Sad this is a bit too difficult for me to do with Befunge, even Funge++, but I did think it could work quite well if done functionally.

This C# solution seems to work, have not tested beyond the initial to test cases

Edit: Did not add HighCard rules....

using System;
using System.Collections.Generic;
using System.Linq;
using HandRule = System.Func<string[], bool>;
namespace ConsoleSandbox
{
  public class PokerRanker
  {
    public static List<char> Ranks = new List<char> { '2', '3', '4', '5', '6', '7', '8',
      '9', 'T', 'J', 'Q', 'K', 'A' };
    public static List<char> Suits = new List<char> { 'S', 'H', 'D', 'C' };

    public List<HandRule> Rules = new List<HandRule>();

    public PokerRanker()
    {
      var ranksRanked = new List<char>(Ranks);
      ranksRanked.Reverse();

      char[] invalidStraightStarts = { 'J', 'Q', 'K', 'A' };

      Rules.AddRange(ranksRanked.Except(invalidStraightStarts).Select(rank =>
        StraightFlush(rank)));

      Rules.AddRange(ranksRanked.SelectMany(fourRank =>
        ranksRanked.Except(new[] {fourRank}).Select(highCard =>
          FourOfAKind(fourRank, highCard))));

      Rules.Add(Flush());

      Rules.AddRange(ranksRanked.Except(invalidStraightStarts).Select(rank =>
        Straight(rank)));

      Rules.AddRange(ranksRanked.SelectMany(threeRank =>
        ranksRanked.Except(new[] { threeRank }).Select(highCard =>
        ThreeOfAKind(threeRank, highCard))));

      Rules.AddRange(ranksRanked.SelectMany(pairRank =>
        ranksRanked.Except(new[] { pairRank }).Select(highCard =>
        Pair(pairRank, highCard))));

      Rules.AddRange(ranksRanked.Select(rank =>
        HighCard(rank)));
    }

    public string Rank(string[] myHand, string[] theirHand)
    {
      var mine = Rules.FindIndex(rule => rule(myHand));
      var theirs = Rules.FindIndex(rule => rule(theirHand));
      return mine < theirs
        ? "Win!"
        : theirs < mine
          ? "Lose!"
          : "Draw!";
    }

    public HandRule HighCard(char rank) => hand =>
      hand.Any(card => card[0] == rank);

    public HandRule Pair(char pairRank, char highCard) => hand =>
      hand.Count(card => card[0] == pairRank) == 2
      && HighCard(highCard)(hand);

    public HandRule ThreeOfAKind(char threeRank, char highCard) => hand =>
      hand.Count(card => card[0] == threeRank) == 3
      && HighCard(highCard)(hand);

    public HandRule Straight(char startingRank) => hand =>
    {
      var start = Ranks.IndexOf(startingRank);
      var ranks = Ranks.GetRange(start, 5);
      return ranks.All(rank => hand.Any(card => card[0] == rank));
    };

    public HandRule Flush() => hand =>
      hand.All(card => card[1] == hand[0][1]);

    public HandRule FullHouse(char threeRank, char pairRank) => hand =>
      ThreeOfAKind(threeRank, threeRank)(hand) &&
      Pair(pairRank, pairRank)(hand);

    public HandRule FourOfAKind(char fourRank, char highCard) => hand =>
      hand.Count(card => card[0] == fourRank) == 4
      && hand.Any(card => card[0] == highCard);

    public HandRule StraightFlush(char startingRank) => hand =>
      Straight(startingRank)(hand)
      && Flush()(hand);
  }
}
Collapse
sam_ferree profile image
Sam Ferree

I think I also missed flush with high card... hrmm

Collapse
mellen profile image
Matt Ellen

Created a python version

link to gist if embed isn't working

use it like:

python rank.py AS AC 2H 2C KD JC JD QH JH AS
Collapse
miketalbot profile image
Mike Talbot

function PokerHand(hand) {
    this.hand = hand
}
PokerHand.prototype.compareWith = function(hand) {
    return compareHands(this.hand, hand.hand || hand)
}

const order = "23456789TJQKA"
function getHandDetails(hand) {
    const cards = hand.split(" ")
    const faces = cards.map(a => String.fromCharCode([77 - order.indexOf(a[0])])).sort()
    const suits = cards.map(a => a[1]).sort()
    const counts = faces.reduce(count, {})
    const duplicates = Object.values(counts).reduce(count, {})
    const flush = suits[0] === suits[4]
    const first = faces[0].charCodeAt(0)
    //Also handle low straight
    const lowStraight = faces.join("") === "AJKLM"
    const straight = lowStraight || faces.every((f, index) => f.charCodeAt(0) - first === index)
    let rank =
        (flush && straight && 1) ||
        (duplicates[4] && 2) ||
        (duplicates[3] && duplicates[2] && 3) ||
        (flush && 4) ||
        (straight && 5) ||
        (duplicates[3] && 6) ||
        (duplicates[2] > 1 && 7) ||
        (duplicates[2] && 8) ||
        9

    return { rank, value: faces.sort(byCountFirst).join("") }

    function byCountFirst(a, b) {
        //Counts are in reverse order - bigger is better
        const countDiff = counts[b] - counts[a]
        if (countDiff) return countDiff // If counts don't match return
        if (lowStraight) {
            a = a === "A" ? "N" : a
            b = b === "A" ? "N" : b
        }
        return b > a ? -1 : b === a ? 0 : 1
    }
    function count(c, a) {
        c[a] = (c[a] || 0) + 1
        return c
    }
}

function compareHands(h1, h2) {
    let d1 = getHandDetails(h1)
    let d2 = getHandDetails(h2)
    if (d1.rank === d2.rank) {
        if (d1.value < d2.value) {
            return "WIN"
        } else if (d1.value > d2.value) {
            return "LOSE"
        } else {
            return "DRAW"
        }
    }
    return d1.rank < d2.rank ? "WIN" : "LOSE"
}

Collapse
Collapse
bravemaster619 profile image
bravemaster619

I once implemented with Node.js. It was a little bit complicated as I remember. I think there is no quick and easy solution.