Advertisement
Guest User

poker-elm

a guest
Aug 7th, 2015
271
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 20.59 KB | None | 0 0
  1. class Card
  2.   SUITS = "cdhs"
  3.   FACES = "L23456789TJQKA"
  4.   SUIT_LOOKUP = {
  5.     'c' => 0,
  6.     'd' => 1,
  7.     'h' => 2,
  8.     's' => 3
  9.   }
  10.   FACE_VALUES = {
  11.     'L' =>  0,   # this is a low ace
  12.     '2' =>  1,
  13.     '3' =>  2,
  14.     '4' =>  3,
  15.     '5' =>  4,
  16.     '6' =>  5,
  17.     '7' =>  6,
  18.     '8' =>  7,
  19.     '9' =>  8,
  20.     'T' =>  9,
  21.     'J' => 10,
  22.     'Q' => 11,
  23.     'K' => 12,
  24.     'A' => 13
  25.   }
  26.  
  27.   def Card.face_value(face)
  28.     FACE_VALUES[face.upcase]
  29.   end
  30.  
  31.   private
  32.  
  33.   def build_from_value(given_value)
  34.     @suit  = given_value / 13
  35.     @face  = given_value % 13
  36.   end
  37.  
  38.   def build_from_face_suit(face, suit)
  39.     suit.downcase!
  40.     @face  = Card::face_value(face)
  41.     @suit  = SUIT_LOOKUP[suit]
  42.     raise ArgumentError, "Invalid card: \"#{face}#{suit}\"" unless @face and @suit
  43.   end
  44.  
  45.   def build_from_face_suit_values(face_int, suit_int)
  46.     @face = face_int
  47.     @suit = suit_int
  48.   end
  49.  
  50.   def build_from_string(card)
  51.     build_from_face_suit(card[0,1], card[1,1])
  52.   end
  53.  
  54.   # Constructs this card object from another card object
  55.   def build_from_card(card)
  56.     @suit = card.suit
  57.     @face = card.face
  58.   end
  59.  
  60.   public
  61.  
  62.   def initialize(*args)
  63.     if (args.size == 1)
  64.       arg = args.first
  65.       if (arg.respond_to?(:to_card))
  66.         build_from_card(arg)
  67.       elsif (arg.respond_to?(:to_str))
  68.         build_from_string(arg)
  69.       elsif (arg.respond_to?(:to_int))
  70.         build_from_value(arg)
  71.       end
  72.     elsif (args.size == 2)
  73.       arg1, arg2 = args
  74.       if (arg1.respond_to?(:to_str) &&
  75.           arg2.respond_to?(:to_str))
  76.         build_from_face_suit(arg1, arg2)
  77.       elsif (arg1.respond_to?(:to_int) &&
  78.              arg2.respond_to?(:to_int))
  79.         build_from_face_suit_values(arg1, arg2)
  80.       end
  81.     end
  82.   end
  83.  
  84.   attr_reader :suit, :face
  85.   include Comparable
  86.  
  87.   def value
  88.     (@suit * 13) + @face
  89.   end
  90.  
  91.   # Returns a string containing the representation of Card
  92.   #
  93.   # Card.new("7c").to_s                   # => "7c"
  94.   def to_s
  95.     FACES[@face].chr + SUITS[@suit].chr
  96.   end
  97.  
  98.   # If to_card is called on a `Card` it should return itself
  99.   def to_card
  100.     self
  101.   end
  102.  
  103.   # Compare the face value of this card with another card. Returns:
  104.   # -1 if self is less than card2
  105.   # 0 if self is the same face value of card2
  106.   # 1 if self is greater than card2
  107.   def <=> card2
  108.     @face <=> card2.face
  109.   end
  110.  
  111.   # Returns true if the cards are the same card. Meaning they
  112.   # have the same suit and the same face value.
  113.   def == card2
  114.     value == card2.value
  115.   end
  116.   alias :eql? :==
  117.  
  118.   # Compute a hash-code for this Card. Two Cards with the same
  119.   # content will have the same hash code (and will compare using eql?).
  120.   def hash
  121.     value.hash
  122.   end
  123.  
  124.   # A card's natural value is the closer to it's intuitive value in a deck
  125.   # in the range of 1 to 52. Aces are low with a value of 1. Uses the bridge
  126.   # order of suits: clubs, diamonds, hearts, and spades. The formula used is:
  127.   # If the suit is clubs, the natural value is the face value (remember
  128.   # Aces are low). If the suit is diamonds, it is the clubs value plus 13.
  129.   # If the suit is hearts, it is plus 26. If it is spades, it is plus 39.
  130.   #
  131.   #     Card.new("Ac").natural_value    # => 1
  132.   #     Card.new("Kc").natural_value    # => 12
  133.   #     Card.new("Ad").natural_value    # => 13
  134.   def natural_value
  135.     natural_face = @face == 13 ? 1 : @face+1  # flip Ace from 13 to 1 and
  136.                                               # increment everything else by 1
  137.     natural_face + @suit * 13
  138.   end
  139. end
  140.  
  141.  
  142.  
  143. class Deck
  144.   def initialize
  145.     @cards = []
  146.     Card::SUITS.each_byte do |suit|
  147.       # careful not to double include the aces...
  148.       Card::FACES[1..-1].each_byte do |face|
  149.         @cards.push(Card.new(face.chr, suit.chr))
  150.       end
  151.     end
  152.     shuffle
  153.   end
  154.  
  155.   def shuffle
  156.     @cards = @cards.sort_by { rand }
  157.   end
  158.  
  159.   # removes a single card from the top of the deck and returns it
  160.   # synonymous to poping off a stack
  161.   def deal
  162.     @cards.pop
  163.   end
  164. end
  165.  
  166.  
  167.  
  168. require_relative 'deck'
  169. require_relative 'poker_hand'
  170.  
  171. deck = Deck.new # get standard deck with 52 card
  172. poker_hand = PokerHand.new([deck.deal, deck.deal, deck.deal, deck.deal, deck.deal]) # get five cards from deck, deck.deal => pop from deck (one card)
  173.  
  174. puts poker_hand.rank # => "Strhait or Two pairs, Three of kind, or something like that"
  175.  
  176. puts poker_hand.just_cards # => array with only cards []
  177.  
  178. # You can compare hands pokeer_hand_one > poker_hand_two, etc...
  179.  
  180.  
  181. class PokerHand
  182.   include Comparable
  183.   include Enumerable
  184.   attr_reader :hand
  185.  
  186.   @@allow_duplicates = true    # true by default
  187.   def self.allow_duplicates; @@allow_duplicates; end
  188.   def self.allow_duplicates=(v); @@allow_duplicates = v; end
  189.  
  190.   # Returns a new PokerHand object. Accepts the cards represented
  191.   # in a string or an array
  192.   #
  193.   #     PokerHand.new("3d 5c 8h Ks")   # => #<PokerHand:0x5c673c ...
  194.   #     PokerHand.new(["3d", "5c", "8h", "Ks"])  # => #<PokerHand:0x5c2d6c ...
  195.   def initialize(cards = [])
  196.     @hand = case cards
  197.     when Array
  198.       cards.map do |card|
  199.         if card.is_a? Card
  200.           card
  201.         else
  202.           Card.new(card.to_s)
  203.         end
  204.       end
  205.     when String
  206.       cards.scan(/\S{2}/).map { |str| Card.new(str) }
  207.     else
  208.       cards
  209.     end
  210.  
  211.     check_for_duplicates unless allow_duplicates
  212.   end
  213.  
  214.   # Returns a new PokerHand object with the cards sorted by suit
  215.   # The suit order is spades, hearts, diamonds, clubs
  216.   #
  217.   #     PokerHand.new("3d 5c 8h Ks").by_suit.just_cards   # => "Ks 8h 3d 5c"
  218.   def by_suit
  219.     PokerHand.new(@hand.sort_by { |c| [c.suit, c.face] }.reverse)
  220.   end
  221.  
  222.   # Returns a new PokerHand object with the cards sorted by face value
  223.   # with the highest value first.
  224.   #
  225.   #     PokerHand.new("3d 5c 8h Ks").by_face.just_cards   # => "Ks 8h 5c 3d"
  226.   def by_face
  227.     PokerHand.new(@hand.sort_by { |c| [c.face, c.suit] }.reverse)
  228.   end
  229.  
  230.   # Returns string representation of the hand without the rank
  231.   #
  232.   #     PokerHand.new(["3c", "Kh"]).just_cards     # => "3c Kh"
  233.   def just_cards
  234.     @hand.join(" ")
  235.   end
  236.   alias :cards :just_cards
  237.  
  238.   # Returns an array of the card values in the hand.
  239.   # The values returned are 1 less than the value on the card.
  240.   # For example: 2's will be shown as 1.
  241.   #
  242.   #     PokerHand.new(["3c", "Kh"]).face_values     # => [2, 12]
  243.   def face_values
  244.     @hand.map { |c| c.face }
  245.   end
  246.  
  247.   # The =~ method does a regular expression match on the cards in this hand.
  248.   # This can be useful for many purposes. A common use is the check if a card
  249.   # exists in a hand.
  250.   #
  251.   #     PokerHand.new("3d 4d 5d") =~ /8h/           # => nil
  252.   #     PokerHand.new("3d 4d 5d") =~ /4d/           # => #<MatchData:0x615e18>
  253.   def =~ (re)
  254.     re.match(just_cards)
  255.   end
  256.  
  257.   def royal_flush?
  258.     if (md = (by_suit =~ /A(.) K\1 Q\1 J\1 T\1/))
  259.       [[10], arrange_hand(md)]
  260.     else
  261.       false
  262.     end
  263.   end
  264.  
  265.   def straight_flush?
  266.     if (md = (/.(.)(.)(?: 1.\2){4}/.match(delta_transform(true))))
  267.       high_card = Card::face_value(md[1])
  268.       arranged_hand = fix_low_ace_display(md[0] + ' ' +
  269.           md.pre_match + ' ' + md.post_match)
  270.       [[9, high_card], arranged_hand]
  271.     else
  272.       false
  273.     end
  274.   end
  275.  
  276.   def four_of_a_kind?
  277.     if (md = (by_face =~ /(.). \1. \1. \1./))
  278.       # get kicker
  279.       result = [8, Card::face_value(md[1])]
  280.       result << Card::face_value($1) if (md.pre_match + md.post_match).match(/(\S)/)
  281.       return [result, arrange_hand(md)]
  282.     end
  283.     false
  284.   end
  285.  
  286.   def full_house?
  287.     if (md = (by_face =~ /(.). \1. \1. (.*)(.). \3./))
  288.       arranged_hand = rearrange_full_house(by_face.cards)
  289.       [
  290.         [7, Card::face_value(md[1]), Card::face_value(md[3])],
  291.         arranged_hand
  292.       ]
  293.     elsif (md = (by_face =~ /((.). \2.) (.*)((.). \5. \5.)/))
  294.       arranged_hand = rearrange_full_house(by_face.cards)
  295.       [
  296.         [7, Card::face_value(md[5]), Card::face_value(md[2])],
  297.         arranged_hand
  298.       ]
  299.     else
  300.       false
  301.     end
  302.   end
  303.  
  304.   def flush?
  305.     if (md = (by_suit =~ /(.)(.) (.)\2 (.)\2 (.)\2 (.)\2/))
  306.       [
  307.         [
  308.           6,
  309.           Card::face_value(md[1]),
  310.           *(md[3..6].map { |f| Card::face_value(f) })
  311.         ],
  312.         arrange_hand(md)
  313.       ]
  314.     else
  315.       false
  316.     end
  317.   end
  318.  
  319.   def straight?
  320.     if hand.size >= 5
  321.       transform = delta_transform
  322.       # note we can have more than one delta 0 that we
  323.       # need to shuffle to the back of the hand
  324.       i = 0
  325.       until transform.match(/^\S{3}( [1-9x]\S\S)+( 0\S\S)*$/) or i >= hand.size  do
  326.         # only do this once per card in the hand to avoid entering an
  327.         # infinite loop if all of the cards in the hand are the same
  328.         transform.gsub!(/(\s0\S\S)(.*)/, "\\2\\1")    # moves the front card to the back of the string
  329.         i += 1
  330.       end
  331.       if (md = (/.(.). 1.. 1.. 1.. 1../.match(transform)))
  332.         high_card = Card::face_value(md[1])
  333.         arranged_hand = fix_low_ace_display(md[0] + ' ' + md.pre_match + ' ' + md.post_match)
  334.         return [[5, high_card], arranged_hand]
  335.       end
  336.     end
  337.     false
  338.   end
  339.  
  340.   def three_of_a_kind?
  341.     if (md = (by_face =~ /(.). \1. \1./))
  342.       # get kicker
  343.       arranged_hand = arrange_hand(md)
  344.       matches = arranged_hand.match(/(?:\S\S ){2}(\S\S)/)
  345.       if matches
  346.         result = [4, Card::face_value(md[1])]
  347.         matches = arranged_hand.match(/(?:\S\S ){3}(\S)/)
  348.         result << Card::face_value($1) if matches
  349.         matches = arranged_hand.match(/(?:\S\S ){3}(\S)\S (\S)/)
  350.         result << Card::face_value($2) if matches
  351.         return [result, arranged_hand]
  352.       end
  353.     end
  354.     false
  355.   end
  356.  
  357.   def two_pair?
  358.     # \1 is the face value of the first pair
  359.     # \2 is the card in between the first pair and the second pair
  360.     # \3 is the face value of the second pair
  361.     if (md = (by_face =~ /(.). \1.(.*?) (.). \3./))
  362.       # to get the kicker this does the following
  363.       # md[0] is the regex matched above which includes the first pair and
  364.       # the second pair but also some cards in the middle so we sub them out
  365.       # then we add on the cards that came before the first pair, the cards
  366.       # that were in-between, and the cards that came after.
  367.       arranged_hand = arrange_hand(md[0].sub(md[2], '') + ' ' +
  368.           md.pre_match + ' ' + md[2] + ' ' + md.post_match)
  369.       matches = arranged_hand.match(/(?:\S\S ){3}(\S\S)/)
  370.       if matches
  371.         result = []
  372.         result << 3
  373.         result << Card::face_value(md[1])    # face value of the first pair
  374.         result << Card::face_value(md[3])    # face value of the second pair
  375.         matches = arranged_hand.match(/(?:\S\S ){4}(\S)/)
  376.         result << Card::face_value($1) if matches    # face value of the kicker
  377.       return [result, arranged_hand]
  378.       end
  379.     end
  380.     false
  381.   end
  382.  
  383.   def pair?
  384.     if (md = (by_face =~ /(.). \1./))
  385.       arranged_hand_str = arrange_hand(md)
  386.       arranged_hand = PokerHand.new(arranged_hand_str)
  387.  
  388.       if arranged_hand.hand[0].face == arranged_hand.hand[1].face &&
  389.           arranged_hand.hand[0].suit != arranged_hand.hand[1].suit
  390.         result = [2, arranged_hand.hand[0].face]
  391.         result << arranged_hand.hand[2].face if arranged_hand.size > 2
  392.         result << arranged_hand.hand[3].face if arranged_hand.size > 3
  393.         result << arranged_hand.hand[4].face if arranged_hand.size > 4
  394.  
  395.         return [result, arranged_hand_str]
  396.       end
  397.     else
  398.       false
  399.     end
  400.   end
  401.  
  402.   def highest_card?
  403.     if size > 0
  404.       result = by_face
  405.       [[1, *result.face_values[0..result.face_values.length]], result.hand.join(' ')]
  406.     else
  407.       false
  408.     end
  409.   end
  410.  
  411.   def empty_hand?
  412.     if size == 0
  413.       [[0]]
  414.     end
  415.   end
  416.  
  417.   OPS = [
  418.     ['Royal Flush',     :royal_flush? ],
  419.     ['Straight Flush',  :straight_flush? ],
  420.     ['Four of a kind',  :four_of_a_kind? ],
  421.     ['Full house',      :full_house? ],
  422.     ['Flush',           :flush? ],
  423.     ['Straight',        :straight? ],
  424.     ['Three of a kind', :three_of_a_kind?],
  425.     ['Two pair',        :two_pair? ],
  426.     ['Pair',            :pair? ],
  427.     ['Highest Card',    :highest_card? ],
  428.     ['Empty Hand',      :empty_hand? ],
  429.   ]
  430.  
  431.   # Returns the verbose hand rating
  432.   #
  433.   #     PokerHand.new("4s 5h 6c 7d 8s").hand_rating     # => "Straight"
  434.   def hand_rating
  435.     OPS.map { |op|
  436.       (method(op[1]).call()) ? op[0] : false
  437.     }.find { |v| v }
  438.   end
  439.  
  440.   alias :rank :hand_rating
  441.  
  442.   def score
  443.     # OPS.map returns an array containing the result of calling each OPS method against
  444.     # the poker hand. The truthy cell closest to the front of the array represents
  445.     # the highest ranking.
  446.     OPS.map { |op|
  447.       method(op[1]).call()
  448.     }.find { |score| score }
  449.   end
  450.  
  451.   # Returns a string of the hand arranged based on its rank. Usually this will be the
  452.   # same as by_face but there are some cases where it makes a difference.
  453.   #
  454.   #     ph = PokerHand.new("As 3s 5s 2s 4s")
  455.   #     ph.sort_using_rank        # => "5s 4s 3s 2s As"
  456.   #     ph.by_face.just_cards       # => "As 5s 4s 3s 2s"
  457.   def sort_using_rank
  458.     score[1]
  459.   end
  460.  
  461.   # Returns string with a listing of the cards in the hand followed by the hand's rank.
  462.   #
  463.   #     h = PokerHand.new("8c 8s")
  464.   #     h.to_s                      # => "8c 8s (Pair)"
  465.   def to_s
  466.     just_cards + " (" + hand_rating + ")"
  467.   end
  468.  
  469.   # Returns an array of `Card` objects that make up the `PokerHand`.
  470.   def to_a
  471.     @hand
  472.   end
  473.   alias :to_ary :to_a
  474.  
  475.   def <=> other_hand
  476.     self.score[0].compact <=> other_hand.score[0].compact
  477.   end
  478.  
  479.   # Add a card to the hand
  480.   #
  481.   #     hand = PokerHand.new("5d")
  482.   #     hand << "6s"          # => Add a six of spades to the hand by passing a string
  483.   #     hand << ["7h", "8d"]  # => Add multiple cards to the hand using an array
  484.   def << new_cards
  485.     if new_cards.is_a?(Card) || new_cards.is_a?(String)
  486.       new_cards = [new_cards]
  487.     end
  488.  
  489.     new_cards.each do |nc|
  490.       unless allow_duplicates
  491.         raise "A card with the value #{nc} already exists in this hand. Set PokerHand.allow_duplicates to true if you want to be able to add a card more than once." if self =~ /#{nc}/
  492.       end
  493.  
  494.       @hand << Card.new(nc)
  495.     end
  496.   end
  497.  
  498.   # Remove a card from the hand.
  499.   #
  500.   #     hand = PokerHand.new("5d Jd")
  501.   #     hand.delete("Jd")           # => #<Card:0x5d0674 @value=23, @face=10, @suit=1>
  502.   #     hand.just_cards             # => "5d"
  503.   def delete card
  504.     @hand.delete(Card.new(card))
  505.   end
  506.  
  507.   # Same concept as Array#uniq
  508.   def uniq
  509.     PokerHand.new(@hand.uniq)
  510.   end
  511.  
  512.   # Resolving methods are just passed directly down to the @hand array
  513.   RESOLVING_METHODS = [:each, :size, :-]
  514.   RESOLVING_METHODS.each do |method|
  515.     class_eval %{
  516.       def #{method}(*args, &block)
  517.         @hand.#{method}(*args, &block)
  518.       end
  519.     }
  520.   end
  521.  
  522.   def allow_duplicates
  523.     @@allow_duplicates
  524.   end
  525.  
  526.   # Checks whether the hand matches usual expressions like AA, AK, AJ+, 66+, AQs, AQo...
  527.   #
  528.   # Valid expressions:
  529.   # * "AJ": Matches exact faces (in this case an Ace and a Jack), suited or not
  530.   # * "AJs": Same but suited only
  531.   # * "AJo": Same but offsuit only
  532.   # * "AJ+": Matches an Ace with any card >= Jack, suited or not
  533.   # * "AJs+": Same but suited only
  534.   # * "AJo+": Same but offsuit only
  535.   # * "JJ+": Matches any pair >= "JJ".
  536.   # * "8T+": Matches connectors (in this case with 1 gap : 8T, 9J, TQ, JK, QA)
  537.   # * "8Ts+": Same but suited only
  538.   # * "8To+": Same but offsuit only
  539.   #
  540.   # The order of the cards in the expression is important (8T+ is not the same as T8+), but the order of the cards in the hand is not ("AK" will match "Ad Kc" and "Kc Ad").
  541.   #
  542.   # The expression can be an array of expressions. In this case the method returns true if any expression matches.
  543.   #
  544.   # This method only works on hands with 2 cards.
  545.   #
  546.   #     PokerHand.new('Ah Ad').match? 'AA' # => true
  547.   #     PokerHand.new('Ah Kd').match? 'AQ+' # => true
  548.   #     PokerHand.new('Jc Qc').match? '89s+' # => true
  549.   #     PokerHand.new('Ah Jd').match? %w( 22+ A6s+ AJ+ ) # => true
  550.   #     PokerHand.new('Ah Td').match? %w( 22+ A6s+ AJ+ ) # => false
  551.   #
  552.   def match? expression
  553.     raise "Hands with #{@hand.size} cards is not supported" unless @hand.size == 2
  554.  
  555.     if expression.is_a? Array
  556.       return expression.any? { |e| match?(e) }
  557.     end
  558.  
  559.     faces = @hand.map { |card| card.face }.sort.reverse
  560.     suited = @hand.map { |card| card.suit }.uniq.size == 1
  561.     if expression =~ /^(.)(.)(s|o|)(\+|)$/
  562.       face1 = Card.face_value($1)
  563.       face2 = Card.face_value($2)
  564.       raise ArgumentError, "Invalid expression: #{expression.inspect}" unless face1 and face2
  565.       suit_match = $3
  566.       plus = ($4 != "")
  567.  
  568.       if plus
  569.         if face1 == face2
  570.           face_match = (faces.first == faces.last and faces.first >= face1)
  571.         elsif face1 > face2
  572.           face_match = (faces.first == face1 and faces.last >= face2)
  573.         else
  574.           face_match = ((faces.first - faces.last) == (face2 - face1) and faces.last >= face1)
  575.         end
  576.       else
  577.         expression_faces = [face1, face2].sort.reverse
  578.         face_match = (expression_faces == faces)
  579.       end
  580.       case suit_match
  581.       when ''
  582.         face_match
  583.       when 's'
  584.         face_match and suited
  585.       when 'o'
  586.         face_match and !suited
  587.       end
  588.     else
  589.       raise ArgumentError, "Invalid expression: #{expression.inspect}"
  590.     end
  591.   end
  592.  
  593.   def +(other)
  594.     cards = @hand.map { |card| Card.new(card) }
  595.     case other
  596.     when String
  597.       cards << Card.new(other)
  598.     when Card
  599.       cards << other
  600.     when PokerHand
  601.       cards += other.hand
  602.     else
  603.       raise ArgumentError, "Invalid argument: #{other.inspect}"
  604.     end
  605.     PokerHand.new(cards)
  606.   end
  607.  
  608.   private
  609.  
  610.   def check_for_duplicates
  611.     if @hand.size != @hand.uniq.size && !allow_duplicates
  612.       raise "Attempting to create a hand that contains duplicate cards. Set PokerHand.allow_duplicates to true if you do not want to ignore this error."
  613.     end
  614.   end
  615.  
  616.   # if md is a string, arrange_hand will remove extra white space
  617.   # if md is a MatchData, arrange_hand returns the matched segment
  618.   # followed by the pre_match and the post_match
  619.   def arrange_hand(md)
  620.       hand = if md.respond_to?(:to_str)
  621.         md
  622.       else
  623.         md[0] + ' ' + md.pre_match + md.post_match
  624.       end
  625.       hand.strip.squeeze(" ")   # remove extra whitespace
  626.   end
  627.  
  628.   # delta transform returns a string representation of the cards where the
  629.   # delta between card values is in the string. This is necessary so a regexp
  630.   # can then match a straight and/or straight flush
  631.   #
  632.   # Examples
  633.   #
  634.   #   PokerHand.new("As Qc Jh Ts 9d 8h")
  635.   #   # => '0As 2Qc 1Jh 1Ts 19d 18h'
  636.   #
  637.   #   PokerHand.new("Ah Qd Td 5d 4d")
  638.   #   # => '0Ah 2Qd 2Td 55d 14d'
  639.   #
  640.   def delta_transform(use_suit = false)
  641.     # In order to check for both ace high and ace low straights we create low
  642.     # ace duplicates of all of the high aces.
  643.     aces = @hand.select { |c| c.face == Card::face_value('A') }
  644.     aces.map! { |c| Card.new(0, c.suit) }  # hack to give the appearance of a low ace
  645.  
  646.     base = if (use_suit)
  647.       (@hand + aces).sort_by { |c| [c.suit, c.face] }.reverse
  648.     else
  649.       (@hand + aces).sort_by { |c| [c.face, c.suit] }.reverse
  650.     end
  651.  
  652.     # Insert delta in front of each card
  653.     result = base.inject(['',nil]) do |(delta_hand, prev_card), card|
  654.       if (prev_card)
  655.         delta = prev_card - card.face
  656.       else
  657.         delta = 0
  658.       end
  659.       # does not really matter for my needs
  660.       delta = 'x' if (delta > 9 || delta < 0)
  661.       delta_hand += delta.to_s + card.to_s + ' '
  662.       [delta_hand, card.face]
  663.     end
  664.  
  665.     # we just want the delta transform, not the last cards face too
  666.     result[0].chop
  667.   end
  668.  
  669.   def fix_low_ace_display(arranged_hand)
  670.     # remove card deltas (this routine is only used for straights)
  671.     arranged_hand.gsub!(/\S(\S\S)\s*/, "\\1 ")
  672.  
  673.     # Fix "low aces"
  674.     arranged_hand.gsub!(/L(\S)/, "A\\1")
  675.  
  676.     # Remove duplicate aces (this will not work if you have
  677.     # multiple decks or wild cards)
  678.     arranged_hand.gsub!(/((A\S).*)\2/, "\\1")
  679.  
  680.     # cleanup white space
  681.     arranged_hand.gsub!(/\s+/, ' ')
  682.     # careful to use gsub as gsub! can return nil here
  683.     arranged_hand.gsub(/\s+$/, '')
  684.   end
  685.  
  686.   def rearrange_full_house(cards)
  687.     card_array = cards.split.uniq
  688.     card_hash = Hash[card_array.collect{|c| [c[0], card_array.count{|n| n[0] == c[0]}]}]
  689.     arranged_hand = card_array.select{|c| c if c[0] == card_hash.key(3)}
  690.     arranged_hand += card_array.select{|c| c if c[0] == card_hash.key(2)}
  691.     (arranged_hand + (card_array - arranged_hand)).join(" ")
  692.   end
  693.  
  694. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement