我正在编写一款扑克游戏程序,但是我无法解决如何处理顺子。
顺子:五张牌的手牌中所有牌面值都是连续的。 例如: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"]
我该如何检查一个手牌(即数组)中是否包含以下组合?最好的方法是检查它是否在卡牌数组中连续有5张。
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
这种方法 (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
我最初发布了以下解决方案,虽然不错,但有点复杂:
如果手牌已排序,则很容易实现。您可以使用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)
函数返回连续的5项,因此在上面的示例中,hand
首先与[ 2, 3, 4, 5, 6 ]
进行比较,然后是[ 3, 4, 5, 6, 7 ]
,再是[ 4, 5, 6, 7, 8 ]
,这是一个匹配,因此any?
函数返回true
。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" ]
hand.sort_by { |card| CARDS.index(card) }
代替哈希表。 - Cary Swovelandindex
则是O(c)。在这种情况下,由于c=12(非常小!),所以我认为这取决于个人偏好。 - Jordan Running编辑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
编辑1: @mudasobwa指出'A',2,3,4,5
是有效的顺子,打破了常规。我相信我已经修正了我的回答。(我相信他不会告诉我'K','A',2,3,4
也是有效的。)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
hand.sort_by {|card| CARDS.index(card) }
更好一些。 - Jordan Runningsort_by
。 - Cary Swoveland'K','A',2,3,4
是否有效?以及所有其他类似的,直到"J","Q","K","A",2
。 - shivam"A",2,3,4,5
包含在顺子手牌中的方法。 - Cary Swovelandvalid_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"]]
一旦你获取了所有有效手牌的列表,你现在可以检查提供的手牌是否在它们(有效手牌)中的any?之一:
if valid_hands.any? { |h| (h - hand).empty? }
puts "Valid hand"
else
puts "Not Valid"
end
更新
在情况 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"]]
hand = [3,4,5,"A","Q"]; hand.sort #=> ArgumentError: comparison of Fixnum with String failed
. 如果你将整数改为字符串,仍然存在问题:hand.map(&:to_s).sort #=> ["3", "4", "5", "A", "Q"]
。 - Cary SwovelandA2345
是一个完全有效的街道牌组。 - Aleksei Matiushkin我本不想参与,但看到周围这些过于复杂的解决方案,我无法保持沉默。
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
suit = '23456789TJQKA'
suit =~ hand.sort_by{|hc| suit.index(hc)}.join ||
suit.rotate(-1) =~ hand.sort_by{|hc| suit.rotate(-1).index(hc)}.join
我建议编写类来表示一张卡牌(也许是牌堆和手牌)。目标是实现以下接口:
deck = Deck.new.shuffle!
hand = Hand.new(deck.draw 5)
hand.straight?
#=>false
puts hand
8♣ 8♦ T♠ 2♦ 7♦
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
这的工作原理如下
"。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
class CardUtils
end
哈希 可以快速引用卡的值。
@@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
}
@@card_values['A']
# => 1
@@card_values[8]
# => 8
参考卡牌点数,使用排序方法对手牌进行整理。
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"]
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
可以通过简单的迭代来完成。
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
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
TJQKA
иЎ—йҒ“гҖӮ - Aleksei MatiushkinQKA23
包含在结果中。 - Aleksei Matiushkinval_x + 13 == val_y
。 - Gavin这是我会写的方式:
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
或者,如果您不喜欢所有的排序,您可以将最后一部分更改为:
hand_as_stings = hand.map(&:to_s)
list_of_straights.any? do |straight|
(straight-hand_as_stings).empty?
end
straight.sort
并且它会以同样的方式进行排序。 - hirolauhand.uiq.size==5
吗? - Cary Swoveland
1..5
。在普通扑克牌中,Ace也被视为价值1。 - hirolau