How to resolve the algorithm Poker hand analyser step by step in the Ruby programming language
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 theordinal
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 thesuit
andface
instance variables, which represent the card's suit and face value, respectively. -
The
SUITS
andFACES
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 theordinal
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 thecards
andrank
instance variables.cards
is an array ofCard
objects, andrank
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, createsCard
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 thecompare_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 theirordinal
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