How to resolve the algorithm Poker hand analyser step by step in the Ruby programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Poker hand analyser step by step in the Ruby programming language

Table of Contents

Problem Statement

Create a program to parse a single five card poker hand and rank it according to this list of poker hands.

A poker hand is specified as a space separated list of five playing cards. Each input card has two characters indicating face and suit.

Faces are:    a, 2, 3, 4, 5, 6, 7, 8, 9, 10, j, q, k Suits are:    h (hearts),   d (diamonds),   c (clubs),   and   s (spades),   or alternatively,   the unicode card-suit characters:    ♥ ♦ ♣ ♠

Duplicate cards are illegal. The program should analyze a single hand and produce one of the following outputs:

The programs output for the above examples should be displayed here on this page.

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Poker hand analyser step by step in the Ruby programming language

Card Class

The Card class represents a playing card. It includes the Comparable module, allowing cards to be compared for sorting purposes.

  • The attr_accessor :ordinal line creates a getter and setter for the ordinal instance variable, which represents the card's position in the rank ordering of cards, usually from 2 to ace.

  • The attr_reader :suit, :face line creates getters for the suit and face instance variables, which represent the card's suit and face value, respectively.

  • The SUITS and FACES constants define valid suits and faces for cards.

  • The initialize method initializes a card object with a string representing the card in the format 'face-suit', such as '2-♥'.

  • The parse method is a private method that validates the input string and extracts the face and suit of the card.

  • The <=> method implements the comparison operator for cards, which is used for sorting. It compares the ordinal values of the cards.

  • The to_s method returns a string representation of the card in the format 'face-suit'.

Hand Class

The Hand class represents a hand of cards. It also includes the Comparable module, allowing hands to be compared for sorting and ranking.

  • The attr_reader :cards, :rank line creates getters for the cards and rank instance variables. cards is an array of Card objects, and rank is a symbol representing the hand's ranking.

  • The RANKS constant defines the possible rankings for hands, from 'high-card' to 'five-of-a-kind'.

  • The WHEEL_FACES constant defines the faces that can form a wheel straight (A-2-3-4-5).

  • The initialize method initializes a hand object with a string representing the cards in the hand, separated by spaces. It parses the string, creates Card objects, and groups them by face value.

  • The categorize method determines the rank of the hand based on its composition, such as 'one-pair', 'three-of-a-kind', or 'straight'.

  • The <=> method implements the comparison operator for hands, which is used for sorting. It compares the compare_value of the hands, which is a tuple of the rank and a tiebreaker value.

  • The to_s method returns a string representation of the hand, with the cards separated by spaces.

Private Methods

  • The one_suit? method checks if all cards in the hand belong to the same suit.

  • The consecutive? method checks if the cards in the hand form a consecutive sequence.

  • The sort method sorts the cards in the hand based on their ordinal values. It also handles the special case of a wheel straight (A-2-3-4-5), by setting the ordinal value of the ace to -1.

  • The compare_value method returns a tuple of the rank and a tiebreaker value, which is used for comparing hands.

Demo

The demo code includes a sample input string test_hands with various hands. Each hand is initialized and ranked. Then the sorted and ranked hands are printed in descending order (highest rank to lowest rank).

Handling Jokers

The provided code does not explicitly support jokers. However, since hands are comparable and sortable, the demo code attempts to handle jokers by creating a product of all possible cards from a standard deck and replacing jokers in the input hand with each of these cards. It then calculates the best hand for each replacement and prints the rank of the best hand for each line in the input string.

Source code in the ruby programming language

class Card
  include Comparable
  attr_accessor :ordinal
  attr_reader :suit, :face 
  
  SUITS = %i(♥ ♦ ♣ ♠)
  FACES = %i(2 3 4 5 6 7 8 9 10 j q k a)
  
  def initialize(str)
    @face, @suit = parse(str)
    @ordinal = FACES.index(@face)
  end
  
  def <=> (other) #used for sorting
    self.ordinal <=> other.ordinal
  end
  
  def to_s
    "#@face#@suit"
  end
  
  private
  def parse(str)
    face, suit = str.chop.to_sym, str[-1].to_sym
    raise ArgumentError, "invalid card: #{str}" unless FACES.include?(face) && SUITS.include?(suit)
    [face, suit]
  end
end

class Hand
  include Comparable
  attr_reader :cards, :rank
  
  RANKS       = %i(high-card one-pair two-pair three-of-a-kind straight flush
                   full-house four-of-a-kind straight-flush five-of-a-kind)
  WHEEL_FACES = %i(2 3 4 5 a)
  
  def initialize(str_of_cards)
    @cards = str_of_cards.downcase.tr(',',' ').split.map{|str| Card.new(str)}
    grouped = @cards.group_by(&:face).values
    @face_pattern = grouped.map(&:size).sort
    @rank = categorize
    @rank_num = RANKS.index(@rank)
    @tiebreaker = grouped.map{|ar| [ar.size, ar.first.ordinal]}.sort.reverse
  end
  
  def <=> (other)    # used for sorting and comparing
    self.compare_value <=> other.compare_value
  end
  
  def to_s
    @cards.map(&:to_s).join(" ")
  end
  
  protected          # accessible for Hands
  def compare_value
    [@rank_num, @tiebreaker]
  end
  
  private
  def one_suit?
    @cards.map(&:suit).uniq.size == 1
  end
  
  def consecutive?
    sort.each_cons(2).all? {|c1,c2| c2.ordinal - c1.ordinal == 1 }
  end
  
  def sort
    if @cards.sort.map(&:face) == WHEEL_FACES
      @cards.detect {|c| c.face == :a}.ordinal = -1
    end 
    @cards.sort
  end
  
  def categorize
    if consecutive?
      one_suit? ? :'straight-flush' : :straight
    elsif one_suit?
      :flush
    else
      case @face_pattern
        when [1,1,1,1,1] then :'high-card'
        when [1,1,1,2]   then :'one-pair'
        when [1,2,2]     then :'two-pair'
        when [1,1,3]     then :'three-of-a-kind'
        when [2,3]       then :'full-house'
        when [1,4]       then :'four-of-a-kind'
        when [5]         then :'five-of-a-kind'
      end
    end
  end
end

# Demo
test_hands = <<EOS
2♥ 2♦ 2♣ k♣ q♦
2♥ 5♥ 7♦ 8♣ 9♠
a♥ 2♦ 3♣ 4♣ 5♦
2♥ 3♥ 2♦ 3♣ 3♦
2♥ 7♥ 2♦ 3♣ 3♦
2♥ 6♥ 2♦ 3♣ 3♦
10♥ j♥ q♥ k♥ a♥
4♥ 4♠ k♠ 2♦ 10♠
4♥ 4♠ k♠ 3♦ 10♠
q♣ 10♣ 7♣ 6♣ 4♣
q♣ 10♣ 7♣ 6♣ 3♣
9♥ 10♥ q♥ k♥ j♣
2♥ 3♥ 4♥ 5♥ a♥
2♥ 2♥ 2♦ 3♣ 3♦
EOS

hands = test_hands.each_line.map{|line| Hand.new(line) }
puts "High to low"
hands.sort.reverse.each{|hand| puts "#{hand}\t #{hand.rank}" }
puts

str = <<EOS
joker  2♦  2♠  k♠  q♦
joker  5♥  7♦  8♠  9♦
joker  2♦  3♠  4♠  5♠
joker  3♥  2♦  3♠  3♦
joker  7♥  2♦  3♠  3♦
joker  7♥  7♦  7♠  7♣
joker  j♥  q♥  k♥  A♥
joker  4♣  k♣  5♦ 10♠
joker  k♣  7♣  6♣  4♣
joker  2♦  joker  4♠  5♠
joker  Q♦  joker  A♠ 10♠
joker  Q♦  joker  A♦ 10♦
joker  2♦  2♠  joker  q♦
EOS

# Neither the Card nor the Hand class supports jokers
# but since hands are comparable, they are also sortable.
# Try every card from a deck for a joker and pick the largest hand:

DECK = Card::FACES.product(Card::SUITS).map(&:join)
str.each_line do |line|
  cards_in_arrays = line.split.map{|c| c == "joker" ? DECK.dup : [c]} #joker is array of all cards
  all_tries  = cards_in_arrays.shift.product(*cards_in_arrays).map{|ar| Hand.new(ar.join" ")} #calculate the Whatshisname product
  best = all_tries.max
  puts "#{line.strip}: #{best.rank}"
end


  

You may also check:How to resolve the algorithm Arithmetic-geometric mean step by step in the LiveCode programming language
You may also check:How to resolve the algorithm Hello world/Web server step by step in the Dylan.NET programming language
You may also check:How to resolve the algorithm Fusc sequence step by step in the Ring programming language
You may also check:How to resolve the algorithm Averages/Arithmetic mean step by step in the IDL programming language
You may also check:How to resolve the algorithm Associative array/Creation step by step in the Racket programming language