DEV Community

dev.to staff
dev.to staff

Posted on

Daily Challenge #258 - Ranking Poker Hands

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!

Top comments (6)

Collapse
 
sam_ferree profile image
Sam Ferree • Edited

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-Tsivintzeli • Edited

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 ⭐ • Edited

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
 
miketalbot profile image
Mike Talbot ⭐
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.