質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.48%

  • Ruby

    9620questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

  • 配列

    627questions

    配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Rubyでポーカーの役を判定する

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,158

yuz55

score 3

前提・実現したいこと

Rubyで、ポーカーの役を判定するアプリケーションを作っています。
五枚のカードの情報(例:S8 S7 H6 H5 S4)を入力すると、それが何の役になっているのか判定できるようにしたいのですが、パターンマッチの部分で行き詰っています。

該当のソースコード

#役の配列をつくる。インデックスの大小で役の強弱を表す。
YAKU = ["High_Cards", "One_Pair", "Two_Pair", "Three_of_a_Kind", "Straight", "Flush", "Fullhouse", "Four_of_a_Kind", "Straight_Flush", "Royal_Straight_Flush"]


#役の判断。大きい順に行う。


def eval_hand

  puts "文字列を入力してください(例:S8 S7 H6 H5 S4)"
  #半角空白で文字列を分割し、配列を返す。
  hand = gets.split
  #それそれのカードから、スートの部分だけ、数字の部分だけを取り出した配列を別に作る。
  suits = Array.new
  numbers = Array.new

  hand.each do |c|
    suits.push c[0]
    numbers.push c[1]
  end

  def same_number_counts
    same_numbers = numbers.group_by{|r|r}.map(&:size).sort.reverse
  end

  def straight?
    steps = numbers.sort.map{|r|r-numbers[0]}
    steps == [0,1,2,3,4] || steps == [0,9,10,11,12]
  end

  def flush?
    suits.uniq.size == 1
  end

  case same_number_counts
    when [2,1,1,1]
      yaku = YAKU[1]
    when [2,2,1]
      yaku = YAKU[2]
    when [3,1,1]
      yaku = YAKU[3]
    when [3,2]
      yaku = YAKU[6]
    when [4,1]
      yaku = YAKU[7]
    else
      case [straight?, flush?]
      when [true, false]
        yaku = YAKU[4]
      when [false, true]
        yaku = YAKU[5]
      when [true, true]
      if numbers == [1,10,11,12,13]
          yaku = YAKU[8]
       else
          yaku = YAKU[9]
      end
      else
        yaku = YAKU[0]
    end
  end
end

def end_program
  exit
end

def exception
  puts "入力された値は無効です"
end

#メニューの表示
while true do
  puts "[0]役を判定する"
  puts "[1]アプリを終了する"
  input = gets.to_i

  if input == 0 then
    eval_hand
    elsif input == 1 then
    end_program
    else
    exception
  end
end


上記のファイルpoker.rbをターミナル上で実行したところ、以下のようなエラーが発生してしまいました。

:in same_number_counts': undefined local variable or method numbers' for main:Object (NameError)
from poker.rb:38:in eval_hand'         from poker.rb:82:in <main>'

numbersの定義の部分に問題があるようなのですが…どうすれば解決できるでしょうか。ご助言いただけると嬉しいです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

+4

それぞれのカードから「スートの部分だけ」「ナンバーの部分だけ」を取り出した配列を別に作って置けばよいと思います。

  hand = gets.split
  suits = new Array
  numbers = new Array

  hand.each do |c|
    suits.push c[0]
    numbers.push c[1]
  end

こうしておいて、さらにsuits と numbers をソートしておけば、

  • ロイヤルストレート:numbers が A,10,J,Q,K で構成されている
  • ストレート:numbers が先頭から重複も抜けもなく5つの値が連続していること
  • フラッシュ:suits が全て同じスートのみで構成されている
  • スリーカード: numbers の中の値毎の個数を数えたとき、個数が3個である値が存在する
  • ツーペア: numbers の中の値毎の個数を数えたとき、個数が2以上である値が二つ存在する

といったように個別にチェックできるでしょう。
※ストレートとフラッシュがそれぞれ成立していれば、それはストレートフラッシュです。また、スリーカードとツーペアが同時に成立するなら、それはフルハウスになります

ジョーカー(ワイルドカード)を許容するかどうかでまた変わってきますが、基本的にはこの考え方で行けるはずです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/04/14 13:14

    ありがとうございます。いただいた回答を参考に、上のようなコードを書いたのですが、実行するとエラーが発生してしまいました。numbersの定義に問題があるようなのですが…。
    どうか、今一度お力を貸していただけるとありがたいです!

    キャンセル

  • 2017/04/14 14:05 編集

    tacsheaven様の式はRubyっぽく書くと

    arr=[['S',8] ['S',7] ['H',6],['H',5],['S',4]]
    tarr=arr.transpose
    suits=tarr[0]
    numbers=tarr[1]
    と書けると思います。

    キャンセル

+2

tacsheaven様の回答に補足という感じですが。
rubyはポーカー判定に向いているかもと思いrubyっぽい記述で概略を描いてみました。
※強さ判定が必要になったら
強さを数値で置き換えて判定すればいいとして(1が強くスペードが強い)
[役,数字,マーク]にして<=>で辞書式順序で比較してください
まず簡単のため
[J,Q,K,A]=[11,12,13,14]
[S,H,D,C]=[4,3,2,1]
とおきます。数字の大きさが強さ順になります。
tacsheaven様の式はRubyっぽく書くと

arr=[[4,8] [4,7] [3,6],[3,5],[4,4]]
tarr=arr.transpose
 suits=tarr[0]
 numbers=tarr[1]


と書けます。

で、まずnumbersの重複を調べます。

重複なし→ストレートかフラッシュかブタ
重複あり→ワンペアかツーペアかスリーカードかフルハウスかフォーカード

if numbers.uniq.size==5#重複なし
'フラッシュ' if suits.uniq.size==1#マークが1種類
'ストレート' if (numbers.map{|n|n+1} & numbers).size=4#自分自身に1を足した数列との共通部分の要素が4つになる
'ロイヤル' if numbers.sort==[10,11,12,13,14]
else#重複あり
 case when.group_by(&:itself).map{|k,v|v.size}.sort#重複ありの場合group_byを使って、それぞれの要素の数を数える。
when [1,1,1,2]
'ワンペア'
when [1,2,2]
'ツーペア'
when [1,1,3]
'スリーカード'
when [2,3]
'フルハウス'
when [1,4]
'フォーカード'
end
end


動くかは試していませんが雰囲気はわかるかと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/04/15 08:06

    ありがとうごじます。具体的にコードまで教えていただきありがたいです。参考にさせて頂きました!

    キャンセル

checkベストアンサー

+1

エラーしている単純な理由は same_number_counts メソッドの中で参照している numbers が、この場所では有効(参照可能)でないためです。

関数定義のすぐ外側で有効だから中でも有効かというとそうではありません。ようするにある変数がある場所で有効かどうかという「スコープ」の問題です。

(ちなみにJavaScriptでは同様なケースで有効なので、そこらへんと混同しているのかもですね。またRubyの場合でも普通の関数定義ではなく「クロージャ」という別のものを使う場合は参照できたりしますがここでは割愛します)

そのコードのようにしたいのであれば、一例としては suits と numbers を @suits と @numbers に置き換えます。「@」から始まる変数名はインスタンス変数となり、今回のようなメソッドをまたがる場合でも参照可能なスコープを持っているというわけです。

補足

なお今回のソースはclass定義をしてないので「インスタンス?」となるかもしれませんが、今回のエラーメッセージにも含まれているようにclassの外で定義したインスタンス変数やメソッド等は「main」という名のデフォルトインスタンスに含まれるようになっているので、 @suits 等はその main のインスタンス変数となります。

さらにおまけ

メソッド定義(def)の中でメソッド定義を行ってますが、Rubyの場合、普通はこのようなことはしませんので、外に出した方が自然なコードとして良いです。中でメソッド定義を行っても外で定義してるのとほとんど変わらないので、今回のような勘違いが起きるだけです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/04/15 08:09

    詳しく説明していただき大変助かります。自分が混乱していたこと、理解が足りなかったことがしっくり頭に入ってきました。ありがとうございます!

    キャンセル

+1

エラーにならないように書き換えてみました。
(判定結果がただしいかは検証していません。)

# 役の配列をつくる。インデックスの大小で役の強弱を表す。
YAKU = %w[
  High_Cards One_Pair Two_Pair Three_of_a_Kind
  Straight Flush Fullhouse Four_of_a_Kind
  Straight_Flush Royal_Straight_Flush
].freeze

# 役の判断。大きい順に行う。
def eval_hand
  puts '文字列を入力してください(例:S8 S7 H6 H5 S4)'
  # 半角空白で文字列を分割し、配列を返す。
  hand = gets.split
  # それそれのカードから、スートの部分だけ、数字の部分だけを取り出した配列を別に作る。
  suits = []
  numbers = []

  hand.each do |c|
    suits << c[0]
    numbers << c[1..-1].to_i
  end

  def same_number_counts(numbers)
    numbers.group_by { |r| r }.map(&:size).sort.reverse
  end

  def straight?(numbers)
    sorted = numbers.sort
    steps = sorted.map { |r| r - sorted[0] }
    steps == [0, 1, 2, 3, 4] || steps == [0, 9, 10, 11, 12]
  end

  def flush?(suits)
    suits.uniq.size == 1
  end

  case same_number_counts(numbers)
  when [2, 1, 1, 1]
    YAKU[1]
  when [2, 2, 1]
    YAKU[2]
  when [3, 1, 1]
    YAKU[3]
  when [3, 2]
    YAKU[6]
  when [4, 1]
    YAKU[7]
  else
    case [straight?(numbers), flush?(suits)]
    when [true, false]
      YAKU[4]
    when [false, true]
      YAKU[5]
    when [true, true]
      if numbers == [1, 10, 11, 12, 13]
        YAKU[8]
      else
        YAKU[9]
      end
    else
      YAKU[0]
    end
  end
end

def end_program
  exit
end

def exception
  puts '入力された値は無効です'
end

# メニューの表示
loop do
  puts '[0]役を判定する'
  puts '[1]アプリを終了する'
  input = gets.to_i

  case input
  when 0
    puts eval_hand
  when 1
    end_program
  else
    exception
  end
end

実行例:

$ ruby poker.rb 
[0]役を判定する
[1]アプリを終了する
0
文字列を入力してください(例:S8 S7 H6 H5 S4)
S8 S7 H6 H5 S4
Straight
[0]役を判定する
[1]アプリを終了する
1

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/04/15 16:51

    ご回答を参考に、類似のコードを自分で書いてみたところ、無事に完成させることができました。詳しく教えていただきありがとうございます!

    キャンセル

同じタグがついた質問を見る

  • Ruby

    9620questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

  • 配列

    627questions

    配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。