如何在Ruby中检查数组中的范围?

时间:2021-01-11 07:28:50

I'm writing a poker program, and I can't figure out how to handle straights.

我正在写一个扑克计划,我无法弄清楚如何处理直道。

Straight: All cards in a hand of 5 cards are consecutive values. ex. 2..6, 3..7, 4..8, 5..9, 6..T, 7..J, 8..Q, 9..K, T..A

直接:5张牌中的所有牌都是连续值。恩。 2..6,3..7,4..8,5..9,6..T,7..J,8..Q,9..K,T..A

cards = [2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K", "A"]

How can I check a hand, which is an array, for these combinations? Preferably I can check it to see if it's 5 in a row in the cards array.

如何检查这些组合的手,这是一个数组?我最好能检查它是否在卡阵列中连续5行。

7 个解决方案

#1


4  

Edit 2: This is my absolutely final solution:

编辑2:这是我绝对的最终解决方案:

require 'set'
STRAIGHTS = ['A',*2..9,'T','J','Q','K','A'].each_cons(5).map(&:to_set)
  #=> [#<Set: {"A", 2, 3, 4, 5}>, #<Set: {2, 3, 4, 5, 6}>,
  #   ...#<Set: {9, "T", "J", "Q", "K"}>, #<Set: {"T", "J", "Q", "K", "A"}>]

def straight?(hand)
  STRAIGHTS.include?(hand.to_set)
end

STRAIGHTS.include?([6,3,4,5,2].to_set)
  # STRAIGHTS.include?(#<Set: {6, 3, 4, 5, 2}>)
  #=> true 

straight?([6,5,4,3,2])            #=> true 
straight?(["T","J","Q","K","A"])  #=> true 
straight?(["A","K","Q","J","T"])  #=> true
straight?([2,3,4,5,"A"])          #=> true 

straight?([6,7,8,9,"J"])          #=> false 
straight?(["J",7,8,9,"T"])        #=> false 

Edit 1: @mudasobwa upset the apple cart by pointing out that 'A',2,3,4,5 is a valid straight. I believe I've fixed my answer. (I trust he's not going to tell me that 'K','A',2,3,4 is also valid.)

编辑1:@mudasobwa通过指出'A',2,3,4,5是有效的直线来扰乱苹果车。我相信我已经解决了问题。 (我相信他不会告诉我'K','A',2,3,4也是有效的。)

I would suggest the following:

我建议如下:

CARDS     = [2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K", "A"]
STRAIGHTS = CARDS.each_cons(5).to_a
  #=>[[2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8],
  #   [5, 6, 7, 8, 9], [6, 7, 8, 9, "T"], [7, 8, 9, "T", "J"],
  #   [8, 9, "T", "J", "Q"], [9, "T", "J", "Q", "K"],
  #   ["T", "J", "Q", "K", "A"]] 

def straight?(hand)
  (hand.map {|c| CARDS.index(c)}.sort == [0,1,2,3,12]) ||
  STRAIGHTS.include?(hand.sort {|a,b| CARDS.index(a) <=> CARDS.index(b)})
end

#2


4  

If we map each card to a value (9 is 9, "T" is 10, "J" is 11, etc.), then there are two facts that are true of all straights that we can use to solve our problem:

如果我们将每张牌映射到一个值(9是9,“T”是10,“J”是11等),那么我们可以用来解决问题的所有直道都有两个事实:

  1. All straights have exactly five unique card values
  2. 所有直道都有五个独特的卡值
  3. The difference between the last and first cards' values is always 4
  4. 最后一张牌和第一张牌的价值之间的差异总是4

And so:

所以:

CARD_VALUES = {
    2 =>  2,    3 =>  3,    4 =>  4,
    5 =>  5,    6 =>  6,    7 =>  7,
    8 =>  8,    9 =>  9,  "T" => 10,
  "J" => 11,  "Q" => 12,  "K" => 13,
  "A" => 14
}

def is_straight?(hand)
  hand_sorted = hand.map {|card| CARD_VALUES[card] }
    .sort.uniq

  hand_sorted.size == 5 &&
    (hand_sorted.last - hand_sorted.first) == 4
end

This method (1) converts each card to its numeric value with map, then (2) sorts them, and then (3) throws out duplicates with uniq. To illustrate with various hands:

这个方法(1)用map将每张卡转换为数值,然后(2)对它们进行排序,然后(3)用uniq抛出重复。用各种手来说明:

    hand |  4   A   T   A   2 |  2   2   3   3   4 |  5   6   4   8   7 |  3  6  2  8  7
---------+--------------------+--------------------+--------------------+----------------
 1. map  |  4  14  10  14   2 |  2   2   3   3   4 |  5   6   4   8   7 |  3  6  2  8  7
 2. sort |  2   4  10  14  14 |  2   2   3   3   4 |  4   5   6   7   8 |  2  3  6  7  8
 3. uniq |  2   4  10  14     |  2   3   4         |  4   5   6   7   8 |  2  3  6  7  8

Alternatively...

I originally posted the following solution, which isn't bad, but is definitely more convoluted:

我最初发布了以下解决方案,这不错,但肯定更复杂:

If the hand is sorted, this is easy. You can use Enumerable#each_cons to check each possible straight.

如果手被分类,这很容易。您可以使用Enumerable#each_cons来检查每个可能的直线。

CARDS = [ 2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K", "A" ]
hand = [ 4, 5, 6, 7, 8 ]

def is_straight?(hand)
  CARDS.each_cons(5).any? do |straight|
    hand == straight
  end
end

if is_straight?(hand)
  puts "Straight!"
else
  puts "Not straight!"
end
# => Straight!

each_cons(5) returns each consecutive set of 5 items, so in the above example hand is first compared to [ 2, 3, 4, 5, 6 ], then [ 3, 4, 5, 6, 7 ], and then [ 4, 5, 6, 7, 8 ], which is a match, so any? returns true.

each_cons(5)返回每组连续的5个项目,因此在上面的示例中,首先将手与[2,3,4,5,6],然后[3,4,5,6,7]进行比较,然后[ 4,5,6,7,8],这是匹配,所以任何?返回true。

Note that this is not the most efficient solution, but unless you need to check many thousands of hands per second, this is more than adequately performant.

请注意,这不是最有效的解决方案,但除非您需要每秒检查数千手,否则这不仅具有足够的性能。

If your hands aren't sorted yet, you'll need to do that first. The simplest way to do that is create a Hash that maps cards to a numeric value (as above) and then use sort_by:

如果你的手还没有排序,你需要先做。最简单的方法是创建一个Hash,将卡映射到数值(如上所示),然后使用sort_by:

def sort_hand(hand)
  hand.sort_by {|card| CARD_VALUES[card] }
end

hand = [ 4, "A", 2, "A", "T" ]
sort_hand(hand)
# => [ 2, 4, "T", "A", "A" ]

#3


1  

I did not want to participate, but I can’t keep silence looking at all these oversophisticated solutions around.

我不想参加,但我不能保持沉默,看看所有这些过于复杂的解决方案。

hand = [2, 5, 7, 'A', 'J'].map(&:to_s)

'23456789TJQKA' =~ hand.sort_by{|hc| '23456789TJQKA'.index(hc)}.join ||
   'A23456789TJQK' =~ hand.sort_by{|hc| 'A23456789TJQK'.index(hc)}.join

In a not lame hardcoded manner:

以一种不蹩脚的硬编码方式:

suit = '23456789TJQKA'

suit =~ hand.sort_by{|hc| suit.index(hc)}.join ||
   suit.rotate(-1) =~ hand.sort_by{|hc| suit.rotate(-1).index(hc)}.join

#4


1  

Generate list of valid hands:

生成有效手牌列表:

valid_hands = cards[0..8].each_with_index.map{|b,i| cards[i..i+4]}
#=> [[2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8], [5, 6, 7, 8, 9], [6, 7, 8, 9, "T"], [7, 8, 9, "T", "J"], [8, 9, "T", "J", "Q"], [9, "T", "J", "Q", "K"], ["T", "J", "Q", "K", "A"]]

Once you have the list of all valid hands, you can now check if provided hand is among any? of them (valid ones) or not:

获得所有有效牌的清单后,您现在可以检查提供的牌是否属于任何牌?他们(有效的)或不是:

if valid_hands.any? { |h| (h - hand).empty? } 
   puts "Valid hand"
else
   puts "Not Valid"
end

UPDATE

UPDATE

In-case 2, 3, 4, 5, "A", 2, 3, 4, "K", "A", 2, 3, "Q", "K", "A", 2, "J", "Q", "K", "A" are also considered as valid hands, calculate them as follows:

在案例2,3,4,5,“A”,2,3,4,“K”,“A”,2,3,“Q”,“K”,“A”,2,“J” ,“Q”,“K”,“A”也被认为是有效的手,计算如下:

valid_hands = cards.each_with_index.map { |b,i| i < 9 ? cards[i..i+4] : cards[0..i-9] + cards[i..-1] }
# => [[2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8], [5, 6, 7, 8, 9], [6, 7, 8, 9, "T"], [7, 8, 9, "T", "J"], [8, 9, "T", "J", "Q"], [9, "T", "J", "Q", "K"], ["T", "J", "Q", "K", "A"], [2, "J", "Q", "K", "A"], [2, 3, "Q", "K", "A"], [2, 3, 4, "K", "A"], [2, 3, 4, 5, "A"]]

#5


1  

I recommend writing classes to represent a Card (and maybe Deck and Hand too). Aim for an interface like this:

我建议写一个代表卡片的课程(也可能是Deck和Hand)。瞄准这样的界面:

deck = Deck.new.shuffle!
hand = Hand.new(deck.draw 5)
hand.straight?
#=>false
puts hand
8♣ 8♦ T♠ 2♦ 7♦

The encapsulation of functionality gives you readability and makes it easy to extend (i.e. with suits)

功能的封装为您提供了可读性并使其易于扩展(即适用于套装)

Here's a more simplistic version, implemented as a single Card class. I did add suits though.

这是一个更简单的版本,作为单个Card类实现。我确实加了西装。

class Card
  include Enumerable #enables sorting
  attr_accessor :value, :suit

  @values = [2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K", "A"]
  @suits  = ["♣","♦","♥","♠"]

  def self.all
    @values.product(@suits).map{|c| Card.new c}
  end

  def self.straight?(cards)
    ["A", *@values].each_cons(5).include?(cards.map(&:value))
  end

  def self.flush?(cards)
    cards.map(&:suit).uniq.size == 1
  end

  def initialize(v)
    @value, @suit = *v
  end

  def <=>(other) #for sorting
    @values.index(value) <=> @values.index(other.value)
  end

  def to_s
    "#{value}#{suit}"
  end
end

This works as follows

其工作原理如下

deck = Card.all
puts deck
#=> 2♣ 2♦ 2♥ 2♠ 3♣ 3♦ 3♥ 3♠ 4♣ 4♦ 4♥ 4♠ 5♣ 5♦ 5♥ 5♠ 6♣ 6♦ 6♥ 6♠ 7♣ 7♦ 7♥ 7♠ 8♣ 8♦ 8♥ 8♠ 9♣ 9♦ 9♥ 9♠ T♣ T♦ T♥ T♠ J♣ J♦ J♥ J♠ Q♣ Q♦ Q♥ Q♠ K♣ K♦ K♥ K♠ A♣ A♦ A♥ A♠
hand = deck.sample 5
puts hand
#=> Q♥ 6♦ 2♣ T♠ Q♦
Card.straight?(hand)
#=>false

#6


0  

Step 0: Let's start with an empty class

class CardUtils
end

Step 1: Store values of card in Hash

Hash allows fast referencing of values of a card.

哈希允许快速参考卡的值。

@@card_values = {
    'A' => 1,   2  => 2,   3  => 3, 4 => 4,  5 => 5,
     6  => 6,   7  => 7,   8  => 8, 9 => 9, 'T' => 10,
    'J' => 11, 'Q' => 12, 'K' => 13
}

Thus, you can reference the card value simply as below.

因此,您可以简单地参考卡值,如下所示。

@@card_values['A']
# => 1

@@card_values[8]
# => 8

Step 2: Sort the hand

Apply sort! method to the hand with reference to the card values.

申请排序!参考卡值的手的方法。

def self.sort(hand)
    hand.sort {|x,y| @@card_values[x] <=> @@card_values[y]}
end
#  => ["A", 2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K"] 

Step 3: Function that tells whether two cards are consecutive

def self.is_consecutive(x, y)
    val_x = @@card_values[x]
    val_y = @@card_values[y]

    val_x == val_y - 1 || val_x + 13 == val_y
end
# is_consecutive('A', 2)
#  => true
# is_consecutive('K', 'A')
#  => true
# is_consecutive('A', 3)
#  => false

Step 4: Check for 'straight'

It could be done with simple iteration.

它可以通过简单的迭代完成。

def self.has_straight(hand)
    hand = sort(hand)

    max_consecutive_count = 0
    consecutive_count = 0

    hand.each_with_index do |curr, i|
        prev = hand[i - 1]

        if is_consecutive(prev, curr) then
            consecutive_count += 1
        else
            consecutive_count = 0
        end

        if consecutive_count > max_consecutive_count then
            max_consecutive_count = consecutive_count
        end
    end

    max_consecutive_count >= 5
end
# hand = [2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K", "A"]
# CardUtils.has_straight(hand)
#  => true

Final Result

class CardUtils
    @@card_values = {
        'A' => 1,   2  => 2,   3  => 3, 4 => 4,  5 => 5,
         6  => 6,   7  => 7,   8  => 8, 9 => 9, 'T' => 10,
        'J' => 11, 'Q' => 12, 'K' => 13
    }

    def self.is_consecutive(x, y)
        val_x = @@card_values[x]
        val_y = @@card_values[y]

        val_x == val_y - 1 || val_x + 13 == val_y
    end

    def self.sort(hand)
        hand.sort {|x,y| @@card_values[x] <=> @@card_values[y]}
    end

    def self.has_straight(hand)
        hand = sort(hand)

        max_consecutive_count = 0
        consecutive_count = 0

        hand.each_with_index do |curr, i|
            prev = hand[i - 1]

            if is_consecutive(prev, curr) then
                consecutive_count += 1
            else
                consecutive_count = 0
            end

            if consecutive_count > max_consecutive_count then
                max_consecutive_count = consecutive_count
            end
        end

        max_consecutive_count >= 5
    end
end

#7


0  

This is how I would write it:

这是我写它的方式:

hand  = [3,4,5,2,'A']


def is_straight(hand)

  # No need to check further if we do not have 5 unique cards.
  return false unless hand.uniq.size == 5

  # Note the A at beginning AND end to count A as 1 or 14.
  list_of_straights = 'A23456789TJQKA'.chars.each_cons(5)

  sorted_hand = hand.map(&:to_s).sort

  list_of_straights.any? do |straight| 
    straight.sort==sorted_hand
  end

end

puts is_straight(hand) #=> true  

Alternatively if you do not like all the sorting you could exchange the last part to:

或者,如果您不喜欢所有排序,您可以将最后一部分交换为:

  hand_as_stings = hand.map(&:to_s)

  list_of_straights.any? do |straight| 
    (straight-hand_as_stings).empty?
  end

#1


4  

Edit 2: This is my absolutely final solution:

编辑2:这是我绝对的最终解决方案:

require 'set'
STRAIGHTS = ['A',*2..9,'T','J','Q','K','A'].each_cons(5).map(&:to_set)
  #=> [#<Set: {"A", 2, 3, 4, 5}>, #<Set: {2, 3, 4, 5, 6}>,
  #   ...#<Set: {9, "T", "J", "Q", "K"}>, #<Set: {"T", "J", "Q", "K", "A"}>]

def straight?(hand)
  STRAIGHTS.include?(hand.to_set)
end

STRAIGHTS.include?([6,3,4,5,2].to_set)
  # STRAIGHTS.include?(#<Set: {6, 3, 4, 5, 2}>)
  #=> true 

straight?([6,5,4,3,2])            #=> true 
straight?(["T","J","Q","K","A"])  #=> true 
straight?(["A","K","Q","J","T"])  #=> true
straight?([2,3,4,5,"A"])          #=> true 

straight?([6,7,8,9,"J"])          #=> false 
straight?(["J",7,8,9,"T"])        #=> false 

Edit 1: @mudasobwa upset the apple cart by pointing out that 'A',2,3,4,5 is a valid straight. I believe I've fixed my answer. (I trust he's not going to tell me that 'K','A',2,3,4 is also valid.)

编辑1:@mudasobwa通过指出'A',2,3,4,5是有效的直线来扰乱苹果车。我相信我已经解决了问题。 (我相信他不会告诉我'K','A',2,3,4也是有效的。)

I would suggest the following:

我建议如下:

CARDS     = [2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K", "A"]
STRAIGHTS = CARDS.each_cons(5).to_a
  #=>[[2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8],
  #   [5, 6, 7, 8, 9], [6, 7, 8, 9, "T"], [7, 8, 9, "T", "J"],
  #   [8, 9, "T", "J", "Q"], [9, "T", "J", "Q", "K"],
  #   ["T", "J", "Q", "K", "A"]] 

def straight?(hand)
  (hand.map {|c| CARDS.index(c)}.sort == [0,1,2,3,12]) ||
  STRAIGHTS.include?(hand.sort {|a,b| CARDS.index(a) <=> CARDS.index(b)})
end

#2


4  

If we map each card to a value (9 is 9, "T" is 10, "J" is 11, etc.), then there are two facts that are true of all straights that we can use to solve our problem:

如果我们将每张牌映射到一个值(9是9,“T”是10,“J”是11等),那么我们可以用来解决问题的所有直道都有两个事实:

  1. All straights have exactly five unique card values
  2. 所有直道都有五个独特的卡值
  3. The difference between the last and first cards' values is always 4
  4. 最后一张牌和第一张牌的价值之间的差异总是4

And so:

所以:

CARD_VALUES = {
    2 =>  2,    3 =>  3,    4 =>  4,
    5 =>  5,    6 =>  6,    7 =>  7,
    8 =>  8,    9 =>  9,  "T" => 10,
  "J" => 11,  "Q" => 12,  "K" => 13,
  "A" => 14
}

def is_straight?(hand)
  hand_sorted = hand.map {|card| CARD_VALUES[card] }
    .sort.uniq

  hand_sorted.size == 5 &&
    (hand_sorted.last - hand_sorted.first) == 4
end

This method (1) converts each card to its numeric value with map, then (2) sorts them, and then (3) throws out duplicates with uniq. To illustrate with various hands:

这个方法(1)用map将每张卡转换为数值,然后(2)对它们进行排序,然后(3)用uniq抛出重复。用各种手来说明:

    hand |  4   A   T   A   2 |  2   2   3   3   4 |  5   6   4   8   7 |  3  6  2  8  7
---------+--------------------+--------------------+--------------------+----------------
 1. map  |  4  14  10  14   2 |  2   2   3   3   4 |  5   6   4   8   7 |  3  6  2  8  7
 2. sort |  2   4  10  14  14 |  2   2   3   3   4 |  4   5   6   7   8 |  2  3  6  7  8
 3. uniq |  2   4  10  14     |  2   3   4         |  4   5   6   7   8 |  2  3  6  7  8

Alternatively...

I originally posted the following solution, which isn't bad, but is definitely more convoluted:

我最初发布了以下解决方案,这不错,但肯定更复杂:

If the hand is sorted, this is easy. You can use Enumerable#each_cons to check each possible straight.

如果手被分类,这很容易。您可以使用Enumerable#each_cons来检查每个可能的直线。

CARDS = [ 2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K", "A" ]
hand = [ 4, 5, 6, 7, 8 ]

def is_straight?(hand)
  CARDS.each_cons(5).any? do |straight|
    hand == straight
  end
end

if is_straight?(hand)
  puts "Straight!"
else
  puts "Not straight!"
end
# => Straight!

each_cons(5) returns each consecutive set of 5 items, so in the above example hand is first compared to [ 2, 3, 4, 5, 6 ], then [ 3, 4, 5, 6, 7 ], and then [ 4, 5, 6, 7, 8 ], which is a match, so any? returns true.

each_cons(5)返回每组连续的5个项目,因此在上面的示例中,首先将手与[2,3,4,5,6],然后[3,4,5,6,7]进行比较,然后[ 4,5,6,7,8],这是匹配,所以任何?返回true。

Note that this is not the most efficient solution, but unless you need to check many thousands of hands per second, this is more than adequately performant.

请注意,这不是最有效的解决方案,但除非您需要每秒检查数千手,否则这不仅具有足够的性能。

If your hands aren't sorted yet, you'll need to do that first. The simplest way to do that is create a Hash that maps cards to a numeric value (as above) and then use sort_by:

如果你的手还没有排序,你需要先做。最简单的方法是创建一个Hash,将卡映射到数值(如上所示),然后使用sort_by:

def sort_hand(hand)
  hand.sort_by {|card| CARD_VALUES[card] }
end

hand = [ 4, "A", 2, "A", "T" ]
sort_hand(hand)
# => [ 2, 4, "T", "A", "A" ]

#3


1  

I did not want to participate, but I can’t keep silence looking at all these oversophisticated solutions around.

我不想参加,但我不能保持沉默,看看所有这些过于复杂的解决方案。

hand = [2, 5, 7, 'A', 'J'].map(&:to_s)

'23456789TJQKA' =~ hand.sort_by{|hc| '23456789TJQKA'.index(hc)}.join ||
   'A23456789TJQK' =~ hand.sort_by{|hc| 'A23456789TJQK'.index(hc)}.join

In a not lame hardcoded manner:

以一种不蹩脚的硬编码方式:

suit = '23456789TJQKA'

suit =~ hand.sort_by{|hc| suit.index(hc)}.join ||
   suit.rotate(-1) =~ hand.sort_by{|hc| suit.rotate(-1).index(hc)}.join

#4


1  

Generate list of valid hands:

生成有效手牌列表:

valid_hands = cards[0..8].each_with_index.map{|b,i| cards[i..i+4]}
#=> [[2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8], [5, 6, 7, 8, 9], [6, 7, 8, 9, "T"], [7, 8, 9, "T", "J"], [8, 9, "T", "J", "Q"], [9, "T", "J", "Q", "K"], ["T", "J", "Q", "K", "A"]]

Once you have the list of all valid hands, you can now check if provided hand is among any? of them (valid ones) or not:

获得所有有效牌的清单后,您现在可以检查提供的牌是否属于任何牌?他们(有效的)或不是:

if valid_hands.any? { |h| (h - hand).empty? } 
   puts "Valid hand"
else
   puts "Not Valid"
end

UPDATE

UPDATE

In-case 2, 3, 4, 5, "A", 2, 3, 4, "K", "A", 2, 3, "Q", "K", "A", 2, "J", "Q", "K", "A" are also considered as valid hands, calculate them as follows:

在案例2,3,4,5,“A”,2,3,4,“K”,“A”,2,3,“Q”,“K”,“A”,2,“J” ,“Q”,“K”,“A”也被认为是有效的手,计算如下:

valid_hands = cards.each_with_index.map { |b,i| i < 9 ? cards[i..i+4] : cards[0..i-9] + cards[i..-1] }
# => [[2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8], [5, 6, 7, 8, 9], [6, 7, 8, 9, "T"], [7, 8, 9, "T", "J"], [8, 9, "T", "J", "Q"], [9, "T", "J", "Q", "K"], ["T", "J", "Q", "K", "A"], [2, "J", "Q", "K", "A"], [2, 3, "Q", "K", "A"], [2, 3, 4, "K", "A"], [2, 3, 4, 5, "A"]]

#5


1  

I recommend writing classes to represent a Card (and maybe Deck and Hand too). Aim for an interface like this:

我建议写一个代表卡片的课程(也可能是Deck和Hand)。瞄准这样的界面:

deck = Deck.new.shuffle!
hand = Hand.new(deck.draw 5)
hand.straight?
#=>false
puts hand
8♣ 8♦ T♠ 2♦ 7♦

The encapsulation of functionality gives you readability and makes it easy to extend (i.e. with suits)

功能的封装为您提供了可读性并使其易于扩展(即适用于套装)

Here's a more simplistic version, implemented as a single Card class. I did add suits though.

这是一个更简单的版本,作为单个Card类实现。我确实加了西装。

class Card
  include Enumerable #enables sorting
  attr_accessor :value, :suit

  @values = [2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K", "A"]
  @suits  = ["♣","♦","♥","♠"]

  def self.all
    @values.product(@suits).map{|c| Card.new c}
  end

  def self.straight?(cards)
    ["A", *@values].each_cons(5).include?(cards.map(&:value))
  end

  def self.flush?(cards)
    cards.map(&:suit).uniq.size == 1
  end

  def initialize(v)
    @value, @suit = *v
  end

  def <=>(other) #for sorting
    @values.index(value) <=> @values.index(other.value)
  end

  def to_s
    "#{value}#{suit}"
  end
end

This works as follows

其工作原理如下

deck = Card.all
puts deck
#=> 2♣ 2♦ 2♥ 2♠ 3♣ 3♦ 3♥ 3♠ 4♣ 4♦ 4♥ 4♠ 5♣ 5♦ 5♥ 5♠ 6♣ 6♦ 6♥ 6♠ 7♣ 7♦ 7♥ 7♠ 8♣ 8♦ 8♥ 8♠ 9♣ 9♦ 9♥ 9♠ T♣ T♦ T♥ T♠ J♣ J♦ J♥ J♠ Q♣ Q♦ Q♥ Q♠ K♣ K♦ K♥ K♠ A♣ A♦ A♥ A♠
hand = deck.sample 5
puts hand
#=> Q♥ 6♦ 2♣ T♠ Q♦
Card.straight?(hand)
#=>false

#6


0  

Step 0: Let's start with an empty class

class CardUtils
end

Step 1: Store values of card in Hash

Hash allows fast referencing of values of a card.

哈希允许快速参考卡的值。

@@card_values = {
    'A' => 1,   2  => 2,   3  => 3, 4 => 4,  5 => 5,
     6  => 6,   7  => 7,   8  => 8, 9 => 9, 'T' => 10,
    'J' => 11, 'Q' => 12, 'K' => 13
}

Thus, you can reference the card value simply as below.

因此,您可以简单地参考卡值,如下所示。

@@card_values['A']
# => 1

@@card_values[8]
# => 8

Step 2: Sort the hand

Apply sort! method to the hand with reference to the card values.

申请排序!参考卡值的手的方法。

def self.sort(hand)
    hand.sort {|x,y| @@card_values[x] <=> @@card_values[y]}
end
#  => ["A", 2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K"] 

Step 3: Function that tells whether two cards are consecutive

def self.is_consecutive(x, y)
    val_x = @@card_values[x]
    val_y = @@card_values[y]

    val_x == val_y - 1 || val_x + 13 == val_y
end
# is_consecutive('A', 2)
#  => true
# is_consecutive('K', 'A')
#  => true
# is_consecutive('A', 3)
#  => false

Step 4: Check for 'straight'

It could be done with simple iteration.

它可以通过简单的迭代完成。

def self.has_straight(hand)
    hand = sort(hand)

    max_consecutive_count = 0
    consecutive_count = 0

    hand.each_with_index do |curr, i|
        prev = hand[i - 1]

        if is_consecutive(prev, curr) then
            consecutive_count += 1
        else
            consecutive_count = 0
        end

        if consecutive_count > max_consecutive_count then
            max_consecutive_count = consecutive_count
        end
    end

    max_consecutive_count >= 5
end
# hand = [2, 3, 4, 5, 6, 7, 8, 9, "T", "J", "Q", "K", "A"]
# CardUtils.has_straight(hand)
#  => true

Final Result

class CardUtils
    @@card_values = {
        'A' => 1,   2  => 2,   3  => 3, 4 => 4,  5 => 5,
         6  => 6,   7  => 7,   8  => 8, 9 => 9, 'T' => 10,
        'J' => 11, 'Q' => 12, 'K' => 13
    }

    def self.is_consecutive(x, y)
        val_x = @@card_values[x]
        val_y = @@card_values[y]

        val_x == val_y - 1 || val_x + 13 == val_y
    end

    def self.sort(hand)
        hand.sort {|x,y| @@card_values[x] <=> @@card_values[y]}
    end

    def self.has_straight(hand)
        hand = sort(hand)

        max_consecutive_count = 0
        consecutive_count = 0

        hand.each_with_index do |curr, i|
            prev = hand[i - 1]

            if is_consecutive(prev, curr) then
                consecutive_count += 1
            else
                consecutive_count = 0
            end

            if consecutive_count > max_consecutive_count then
                max_consecutive_count = consecutive_count
            end
        end

        max_consecutive_count >= 5
    end
end

#7


0  

This is how I would write it:

这是我写它的方式:

hand  = [3,4,5,2,'A']


def is_straight(hand)

  # No need to check further if we do not have 5 unique cards.
  return false unless hand.uniq.size == 5

  # Note the A at beginning AND end to count A as 1 or 14.
  list_of_straights = 'A23456789TJQKA'.chars.each_cons(5)

  sorted_hand = hand.map(&:to_s).sort

  list_of_straights.any? do |straight| 
    straight.sort==sorted_hand
  end

end

puts is_straight(hand) #=> true  

Alternatively if you do not like all the sorting you could exchange the last part to:

或者,如果您不喜欢所有排序,您可以将最后一部分交换为:

  hand_as_stings = hand.map(&:to_s)

  list_of_straights.any? do |straight| 
    (straight-hand_as_stings).empty?
  end