challenge ポーカーの役判定

引数に手札を与えると、ポーカーの役を表示するプログラムを作ってください。

条件:

  • スートはS,D,H,C、ランクはA,2~9,T,J,Q,Kのそれぞれ一文字で表します。
  • 手札は S2D5H3CQS9 のように10文字で指定されます。特にソートはされていません。
  • 手札にジョーカーは含まれません。
  • ストレートで取りうるランクの種類はA2345, 23456 ... 9TJQK, TJQKAの10種類で、JQKA2のようにK-A-2をまたぐものはストレートではありません。

実行例:

% ./poker SQSJSASKST
Royal flush

% ./poker D9D7D6D5D8
Straight flush

% ./poker C2D2S2H3H2
Four of a kind

% ./poker C2D3S2H3H2
Full house

% ./poker S9S4S8STSJ
Flush

% ./poker C4H7D5S6H3
Straight

% ./poker S6H6C5DQC6
Three of a kind

% ./poker S6HQC5DQC6
Two pair

% ./poker S6H4C5DQC6
One pair

% ./poker SJSQSKSAC2
No pair

お題にしようと思っていたのに間違えてしまいました。今から変更可能でしょうか?

(説明)
当初間違ってトピックに投稿していたので、このようなコメントを付けていたのですが、
このコメントに気づいた管理人さんにお題に移していただきました。
(最初の2つだけ投稿日時が早いのはそのためです)

Posted feedbacks - Ruby

rankとsuitを分離して扱いました.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def poker(hand)
  def one_pair(rank, count = 0)
    rank.values.map{|x| count += 1 if x.size == 2}
    count == 1
  end
  def two_pair(rank, count = 0)
    rank.values.each{|x| count += 1 if x.size == 2}
    count == 2
  end
  def three_cards(rank)
    rank.values.map{|x| x.size == 3 ? true : nil}.compact.shift
  end
  def straight(rank)
    rank.size == 5 ? rank.keys.sort.first + 4 == rank.keys.sort.last : false
  end
  def flush(suit); suit.size == 1 ;end
  def fourcards(rank)
    rank.values.map{|x| x.size == 4 ? true : nil}.compact.shift
  end
  def convert(char)
    case char
    when 'T': 10
    when 'J': 11
    when 'Q': 12
    when 'K': 13
    when 'A': 14
    else
      char.to_i
    end
  end
  def div_suit_and_rank(hand)
    suit_hash, rank_hash = Hash.new, Hash.new
    cards = hand.split('')
    Array.new(hand.size.div(2)){|i| 2*i}.each{|i|
      suit = cards[i]
      rank = convert(cards[i+1])
      eval("suit_hash[suit] #{(suit_hash[suit] ? '<< rank' : '= [rank]')}")
      eval("rank_hash[rank] #{(rank_hash[rank] ? '<< suit' : '= [suit]')}")
    }
    [suit_hash, rank_hash]
  end
  suit, rank = *div_suit_and_rank(hand)
  if straight(rank)
    "#{rank.keys.sort.first == 10 ? 'Loyal ' : ''}Straight #{(flush(suit) ? 'Flush' : '')}"
  elsif fourcards(rank)
    "Four of a Kind"
  elsif three_cards(rank) and one_pair(rank)
    "Full House"
  elsif flush(suit)
    "Flush"
  elsif three_cards(rank)
    "Three of a Kind"
  elsif two_pair(rank)
    "Two Pair"
  elsif one_pair(rank)
    "One Pair"
  else
    "No Pair"
  end
end
hands = ['SQSJSASKST', 'D9D7D6D5D8', 'C2D2S2H3H2', 'C2D3S2H3H2', 'S9S4S8STSJ', 'C4H7D5S6H3', 'S6H6C5DQC6', 'S6HQC5DQC6', 'S6H4C5DQC6', 'SJSQSKSAC2']
hands.each{|x| puts poker(x)}

確かに, Aを14としたのが敗因でした. straightメソッドに場合分けを追加する必要がありますね.


短さ優先。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
ranks = []
1.step(10, 2) do |i| ranks << ARGV[0][i, 1] end
rank_hash = Hash.new(0)
ranks.each do |rank| rank_hash[rank] += 1 end
flush = ARGV[0].count(ARGV[0][0,1].to_s) == 5
straight = ("23456789AJKQT".include? ranks.sort!.to_s or "2345A".include? ranks.to_s)
case
when (ranks.to_s == "AJKQT" and flush) then puts "Royal Flush"
when (straight and flush) then puts "Straight Flush"
when rank_hash.values.max == 4 then puts "Four of a Kind"
when (rank_hash.values.max == 3 and rank_hash.values.include? 2) then puts "Full House"
when flush then puts "Flush"
when straight then puts "Straight"
when rank_hash.values.max == 3 then puts "Three of a Kind"
when rank_hash.values.to_s.count("2") == 2 then puts "Two Pair"
when rank_hash.values.max == 2 then puts "One Pair"
else puts "No Pair" 
end

素直に書いてみた。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class Poker
    def initialize str
        suits=[]
        nums=[]
        str.scan(/[SDHCTJQKA0-9]{2}/).each{|card|
            tmp=card.scan(/./)
            num=case tmp[1]
                when "T": 10
                when "J": 11
                when "Q": 12
                when "K": 13
                when "A": 1
                else tmp[1].to_i
            end
            suits << tmp[0]
            nums << num
        }
        @cards = {
            :suit=>suits,
            :num=>nums,
            :layer => nums.inject({}){|result,n| # [1,2,2,2,3,3,4] => [1,3,2,1]
                result[n]=0 if !result[n]
                result[n]+=1
                result
            }.values
        }
    end

    def one_pair 
        @cards[:layer].max == 2 && @cards[:layer].length == 4
    end

    def two_pair 
        @cards[:layer].max == 2 && @cards[:layer].length == 3 
    end

    def three_card 
        @cards[:layer].max == 3 && @cards[:layer].length == 3 # not full house
    end

    def four_card 
        @cards[:layer].max == 4 && @cards[:layer].length == 2 
    end

    def full_house 
        @cards[:layer].max == 3 && @cards[:layer].length == 2
    end

    def five_card
        @cards[:layer].max == 5 && @cards[:layer].length == 1
    end

    def straight 
        ((1..13).to_a.join(" ")).include?(@cards[:num].sort.join(" "))
    end

    def flush 
        @cards[:suit].uniq.length == 1
    end

    def royal_straight_flush 
        flush() && @cards[:num].sort == [1,10,11,12,13]
    end

    def cards_rank 
        # p @cards
        case
            when (royal_straight_flush()) : "Royal Straight Flush"
            when (straight() && flush()): "Straight Flush"
            when (four_card()): "Four of a kind"
            when (full_house()): "Full house"
            when (flush()) : "Flush"
            when (straight()) : "Straight"
            when (three_card()): "Three of a kind"
            when (two_pair()) : "Two pair"
            when (one_pair()) : "One pair"
            else "No pair"
        end
    end
end

arr="SQSJSASKST D9D7D6D5D8 C2D2S2H3H2 C2D3S2H3H2 S9S4S8STSJ C4H7D5S6H3 S6H6C5DQC6 S6HQC5DQC6 S6H4C5DQC6 SJSQSKSAC2".split(/\s/)


arr.each{|cards|
    puts Poker.new(cards).cards_rank
}


# str=ARGV.shift
# puts poker(str)

合っているかいるかどうかわからないので全組合せを喰わせてみました。
かなり汚いできではありますが、うまくいっているようです。
皆様のコードを参考にさせていただきました。


結果は
 Royal flush           4   1/649740 
 Straight flush       36 1/72193.33 
 Four of a kind      624     1/4165 
 Full house         3744   1/694.17 
 Flush              5108    1/508.8 
 Straight          10200    1/254.8 
 Three of a kind   54912    1/47.33 
 Two pair         123552    1/21.04 
 One pair        1098240     1/2.37 
 No pair         1302540        1/2 
 total           2598960            
でした。

全組合せは以下で作りました。


suits = ["S","D","H","C"]
rank = (["A"] + ("2".."9").to_a + ["T","J","Q","K"])

card = []

for i in suits.product(rank)
  card << i.join
end

for i in card.combination(5).to_a
  puts i.shuffle.join #一応、シャッフル。
end


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def porker(hand)
  narabi =(["0","A"] + ("2".."9").to_a + ["T","J","Q","K"])
  
  def num(a)
    (["0","A"] + ("2".."9").to_a + ["T","J","Q","K"]).index(a)
  end
  
  tmp = hand.scan(/[SDHCTJQKA2-9]{2}/).map(&:chars).map(&:to_a)
  
  card = Hash.new{|h,k|h[k]=[]}
  for i in tmp
    i[0]
    card[i[0]] << i[1]
  end
  
  suuji = Hash.new(0)
  for i in card.values.flatten
    suuji[i] += 1
  end
  
  case
  when  card.keys.size == 1 && card.values.flatten.sort_by{|a|num(a)}.join(" ") == "A T J Q K"
    "Royal flush"
  when card.keys.size == 1 && %r(#{suuji.keys.sort_by{|a|num(a)}.join(" ")}) =~ narabi.join(" ")
    "Straight flush"
  when suuji.select{|k,v|v == 4}.size == 1
    "Four of a kind"
  when suuji.keys.size == 2
    "Full house"
  when card.keys.size == 1
    "Flush"
  when suuji.keys.size == 5 && (%r(#{suuji.keys.sort_by{|a|num(a)}.join(" ")}) =~ narabi.join(" ") ||
    suuji.keys.sort_by{|a|num(a)}.join(" ") == "A T J Q K")
    "Straight"
  when suuji.select{|k,v|v == 3}.size == 1
    "Three of a kind"
  when suuji.select{|k,v|v == 2}.size == 2
    "Two pair"
  when suuji.select{|k,v|v == 2}.size == 1
    "One pair"
  else
    "No pair"
  end
end

#ruby porker.rb file|SQSJSASKST|なしの場合は__END__以下
if ARGV.size == 1
  if File.exist?(ARGV[0])
    f = open(ARGV[0])
    while f.gets
      hand = $_.chomp
      puts [hand,porker(hand)].join(" ")
    end
  else
    puts [ARGV[0],porker(ARGV[0])].join(" ")
  end
else
  while DATA.gets
      hand = $_.chomp
      puts [hand,porker(hand)].join(" ")
  end
end
__END__
SQSJSASKST
D9D7D6D5D8
C2D2S2H3H2
C2D3S2H3H2
S9S4S8STSJ
C4H7D5S6H3
S6H6C5DQC6
S6HQC5DQC6
S6H4C5DQC6
SJSQSKSAC2

Index

Feed

Other

Link

Pathtraq

loading...