Ruby'de bir dizi için bir diziyi nasıl kontrol edersiniz?

Bir poker programı yazıyorum ve straights ile nasıl başa çıkacağımı anlayamıyorum.

Düz: 5 kartlık bir eldeki tüm kartlar ardışık değerlerdir. ex. 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"]

Bu kombinasyonlar için bir dizi olan bir eli nasıl kontrol edebilirim? Tercihen kart dizisinde art arda 5 olup olmadığını kontrol edebilirim.

4
Lütfen şimdiye kadar sahip olduğunuz kodu gönderin.
katma yazar Jordan Running, kaynak
Ayrıca sorunuzda 1..5 eksik de değilsiniz. Normal pokerde As, aynı zamanda değer 1 olarak kabul edilir.
katma yazar hirolau, kaynak

7 cevap

Eğer her bir kartı bir değerle eşleştirirsek (9 9, "T" 10, "J" 11, vs.), o zaman problemimizi çözmek için kullanabileceğimiz tüm gerçekler için doğru olan iki gerçek vardır:

  1. Tüm grupların tam olarak beş benzersiz kart değeri var
  2. Son ve ilk kartların değerleri arasındaki fark her zaman 4

Ve bu yüzden:

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

Bu yöntem (1), her bir kartı map ile sayısal değerine dönüştürür, sonra (2) sort onları ve sonra (3) uniq ile kopyaları atar . Çeşitli ellerle göstermek için:

    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

Alternatif ...

Aslen kötü olmayan ama kesinlikle daha kıvrık olan şu çözümü yayınladım:

El sıralanırsa, bu kolaydır. Enumerable # each_cons 'ı kullanabilirsiniz. Her olası doğru kontrol etmek.

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.

Bunun en verimli çözüm olmadığını unutmayın, ancak saniyede binlerce eli kontrol etmeniz gerekmedikçe, bu yeterli performanstan daha fazlasıdır.

Elleriniz henüz sıralanmadıysa, önce bunu yapmanız gerekir. Bunu yapmanın en basit yolu, kartları bir sayısal değere (yukarıdaki gibi) eşleyen ve ardından sort_by 'i kullanan bir Hash oluşturur:

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" ]

4
katma
Hash'u tercih ederim çünkü O (1) endeksi olan O (c) olduğu için. Bu durumda gerçekten c = 12 (küçücük!) 'Den beri önemli değil, bu yüzden kişisel tercihlere inanıyorum.
katma yazar Jordan Running, kaynak
Yedi-kartlı stud ile verimlilik konusunda endişe duyabilirdim, ama çeşitli beş-kartlı oyunlar için endişelenme.
katma yazar Cary Swoveland, kaynak
İyi cevap. hand.sort_by {| kart | kullanmayı düşünün CARDS.index (kart)} bir karma yerine.
katma yazar Cary Swoveland, kaynak

Edit 2: This is my absolutely final solution:

require 'set'
STRAIGHTS = ['A',*2..9,'T','J','Q','K','A'].each_cons(5).map(&:to_set)
  #=> [#, #,
  #   ...#, #]

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

STRAIGHTS.include?([6,3,4,5,2].to_set)
  # STRAIGHTS.include?(#)
  #=> 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.)

Aşağıdakileri öneririm:

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
4
katma
A2345 'de başarısız.
katma yazar mudasobwa, kaynak
@CarySwoveland Sadece CARDS 'un önüne gereksiz bir "A" eklenmesi sorunu giderir gibi görünüyor.
katma yazar mudasobwa, kaynak
hand.sort_by {| kart | CARDS.index (kart)} benim için biraz daha güzel görünüyor.
katma yazar Jordan Running, kaynak
@mudasobwa Bu bir bahane mazeret ama ben bir poker oyuncusu değilim ...
katma yazar Cary Swoveland, kaynak
Eğer @udasobwa seni Perşembe gecesi poker grubuna katılmaya davet ederse, saygılı bir şekilde reddedersiniz.
katma yazar Cary Swoveland, kaynak
Belki de, belki. Çözümünüzü okuyana kadar sort_by kullanmayı düşünmedim.
katma yazar Cary Swoveland, kaynak
@Gagan, bu, hirolau'nun, eller arasında, "A", 2,3,4,5 'i eklemenin önerildiği yoldur.
katma yazar Cary Swoveland, kaynak
@hirolau, haklısın. CARDS 'ı kaldırmak için bir düzenleme yaptım (bitirdiğimi söylediğim halde bile, ama asla bitirmedik, değil mi?). Teşekkürler.
katma yazar Cary Swoveland, kaynak
@CarySwoveland Ben seni çok beğendim. Şikayet edilecek tek şey, aslında bir kod değişkeni listesi değil, daha çok "düz yardımcı dizi" nin bir listesi olduğundan, CARDS değişkeninin isimlendirilmesidir.
katma yazar hirolau, kaynak
@CarySwoveland: 2 Düzenle içinde iki kez A kullanmanın ardındaki sebep nedir?
katma yazar Gagan Gami, kaynak
Bende aynı sorum var. 'K', 'A', 2,3,4 geçerli mi? ve benzer şekilde tüm diğerleri "J", "Q", "K", "A", 2
katma yazar shivam, kaynak

Katılmak istemedim, ancak etraftaki tüm bu aşırı karmaşık çözümlere bakarken sessiz kalamam.

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

Sıkı olmayan kodlanmış bir şekilde:

suit = '23456789TJQKA'

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

Bir Kartı (ve belki de Güverte ve El de) temsil etmek için sınıflar yazmanızı tavsiye ederim. Bunun gibi bir arayüz için hedefleyin:

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

İşlevsellik kapsüllü okunabilirlik verir ve genişletmeyi kolaylaştırır (örneğin, takımlarla)

İşte tek bir Card sınıfı olarak uygulanan daha basit bir versiyonu. Gerçi takım elbise ekledim.

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

Bu aşağıdaki gibi çalışır

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
1
katma

Geçerli ellerin listesini oluştur:

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"]]

Geçerli tüm ellerin listesi elinize geçtikten sonra, sağlanan elin herhangi biri? (geçerli olanlar) veya değil:

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

GÜNCELLEME

Durumda 2, 3, 4, 5, "A" , 2, 3, 4, "K", "A" , 2, 3, "Q", "K", "A" , 2, "J", "Q", "K", "A" da geçerli el olarak kabul edilir. aşağıdaki gibidir:

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"]]
1
katma
@CarySwoveland ROFL
katma yazar mudasobwa, kaynak
BTW, bu cevap hala yanlış. A2345 , mükemmel bir sokak elidir.
katma yazar mudasobwa, kaynak
katma yazar mudasobwa, kaynak
@mudasobwa bir yüksek silindirdir. Eğer öyle diyorsa, öyle. Sanırım hepimiz baştan başlamak zorundayız.
katma yazar Cary Swoveland, kaynak
Gözden geçirilmiş cevabını beğendim. Şimdiye kadarki en iyi cevap, imo. Bu satırları düşünüyordum, ancak çiftler bir sorun olsa da, elbette ki düzensizliklerin kopyaları yok. Aferin.
katma yazar Cary Swoveland, kaynak
Hmmm. el = [3,4,5, "A", "Q"]; hand.sort # => ArgumentError: Fixnum'un String ile karşılaştırması başarısız oldu . Tamsayıları dizelere değiştirirseniz hala sorun vardır: hand.map (&: to_s) .sort # => ["3", "4", "5", "A", "Q"] .
katma yazar Cary Swoveland, kaynak
true any? daha iyi okunabilirliğe sahip. Teşekkürler :)
katma yazar shivam, kaynak
@CarySwoveland sabit :)
katma yazar shivam, kaynak
@mudasobwa çok teşekkürler :)
katma yazar shivam, kaynak

Ben böyle yazabilirim:

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  

Alternatif olarak, tüm sıralamadan hoşlanmıyorsanız, son kısmı aşağıdaki şekilde değiştirebilirsiniz:

  hand_as_stings = hand.map(&:to_s)

  list_of_straights.any? do |straight| 
    (straight-hand_as_stings).empty?
  end
0
katma
Oups. Üzgünüm, benim hatam.
katma yazar mudasobwa, kaynak
Tabii ki!.....
katma yazar Cary Swoveland, kaynak
hand.uiq.size == 5 'i kontrol etmeniz gerekiyor mu?
katma yazar Cary Swoveland, kaynak
@mudasobwa Senin noktan nedir? Daha sonra kodda straight.sort yaparız ve aynı şekilde sıralar.
katma yazar hirolau, kaynak
@CarySwoveland Bu algoritmanın sonucunu değiştirmeyecektir, ama çoğu el düzensiz olmayacak, başlangıçta kolay kontrol ve hesaplama çok fazla hesaplama gerektirecektir. Bu söylendiği gibi, muhtemelen burada erken optimizasyondan suçluyum.
katma yazar hirolau, kaynak

Adım 0: Boş bir sınıfla başlayalım

class CardUtils
end

1. Adım: Hash'daki kartın değerlerini saklayın

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
}

Böylece, kart değerini aşağıdaki gibi basit bir şekilde referans alabilirsiniz.

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

@@card_values[8]
# => 8

2. Adım: Elinizi sıralayın

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

4. Adım: 'düz' kontrol edin

Basit yineleme ile yapılabilir.

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

Son sonuç

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
0
katma
Reddedilme benden gelmedi, sanırım bu cevabın nesi yanlış olduğunu biliyorum: sadece çok uzun gibi görünüyor. Yaklaşık bir kilobayt üretirken, görev birkaç kod satırı gerektirir.
katma yazar mudasobwa, kaynak
Dikkatli bir şekilde kontrol etmedim, ancak sonuçta muhtemelen QKA23 'i de dahil ediyorsunuz.
katma yazar mudasobwa, kaynak
Görünüşe göre sonuçtan TJQKA sokaklarını eledik.
katma yazar mudasobwa, kaynak
@mudasobwa Eleştirmen için teşekkürler. Daha okunaklı ve anlaşılabilir olduğunu söyleyebilirim.
katma yazar Gavin, kaynak
Downvote nerede geliyor? Yanlış bir şey mi yapıyorum?
katma yazar Gavin, kaynak
@mudasobwa İkinci koşul, etrafı sarmayı kontrol eder: val_x + 13 == val_y
katma yazar Gavin, kaynak
@mudasobwa Teşekkürler, ardışık kart kontrolünün kesin uygulamasını ekledim.
katma yazar Gavin, kaynak