info

ライブドアインターンに受かったポーカーのRubyスクリプトを貼ってみる

自分の動作環境
$ruby -v
ruby 1.9.1p243 (2009-07-16 revision 24175) [i686-linux]

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

class Game
  def initialize
    @deck = Deck::new #山札
    @playerA = Player::new("A") #プレイヤー
    @playerB = Player::new("B") 
    @turn_times = 0 #ゲーム回数カウント
  end

#1ターン経過させる
  def do_one_turn
    now_turn
    player_draw_card
    compare_yaku
    print_line
  end

#ターンのカウントと表示
  def now_turn
    @turn_times += 1
    puts("Game " + @turn_times.to_s)
  end
  
#プレイヤーにカードをひかせ、役を表示する
  def player_draw_card
    [@playerA,@playerB].each do |player|
      player.draw_a_hand(@deck.draw_five)
      player.print_hand{|name, hand, yaku| "Player " + name + ":\n" + "    " + hand + "  " +  yaku}
    end
  end

#どちらが勝ったか表示する、PlayerクラスはComparableなので比較できる
  def compare_yaku
    if @playerA > @playerB then
      @playerA.shorino_otakebi{|i| "<<<< " + i + " <<<<"}
    elsif @playerA < @playerB then 
      @playerB.shorino_otakebi{|i| ">>>> " + i + " >>>>"}
    else
      puts("---- DRAW ---")
    end
  end

  def print_line
    puts("------------------------------------------------")
  end

#ループの最初でこれを見てゲームに入れるか確認する
  def continue?
    @deck.drawable?([@playerA,@PlayerB].size)
  end
end

#山札クラス
class Deck
#カードをつくり、山札につめ、シャッフルする (:Sスペード、:Dダイヤ、:Hハート、:Cクラブ)
  def initialize
    @cards = [:S, :D, :H, :C].inject([]) do |x, y|
      x.concat((1..13).map {|a| Card::new(y, a)})
    end
    shuffle
  end

#でたらめな値をソートのキーにすることによってシャッフルしている
  def shuffle
    @cards = @cards.sort_by {rand}
  end

#人数ぶんカードがデッキに残っているか
  def drawable?(x)
    @cards.size >= (5 * x)
  end
  
#プレイヤーに5枚カードをあげる
  def draw_five
    hand = Array::new(5)
    hand = hand.map do
      @cards.pop
    end
    hand
  end
end

#カードクラス一枚のカードを表わしている、to_sでスートと番号を文字にできる
class Card
  attr_reader :suit, :num
  def initialize(suit, num)
    @suit = suit
    @num = num
  end

  def to_s
    @num.to_s + " (" + @suit + ")"
  end
end

#プレイヤークラス、ゲームからカードをもらって役の判断を行なう、Coparableでプレイヤー同士の役の大小を判断する
class Player
  attr_reader :yaku
  include Comparable

  #役テーブル、インデックスの大小で役の大きさを表わす
  YAKU = [:NOTHING, :One_Pair, :Two_Pair, :Tree_Card, :Straight, :Flush, :Fullhouse, :Four_Card, :Straight_Flush, :Royal_Straight_Flush]

  def initialize(name)
    @name = name
    @hand = Array::new(5)
    @yaku = YAKU[0]
  end

  def <=>(other)
    myindex = YAKU.index{|item| @yaku == item}
    otherindex = YAKU.index{|item| other.yaku == item}
    myindex <=> otherindex
  end

#手札を表示、ブロックで出力の形式をGameクラスに書けるように
  def print_hand(&block)
    stringhand = @hand.inject("") do |string, card|
      string += card.to_s + " "
    end
    puts(block.call(@name, stringhand, @yaku.to_s.gsub(/_/, " ")))
  end

#どこかしらの親(ここではGame)からカードを配列でもらう
  def draw_a_hand(cards)
    @hand = cards
    eval_hand
  end

#勝者表示でプレイヤーネームを表示、ゲッターを使わないためと、大なり小なり記号の出力をGameクラスにさせるためブロックを使う
  def shorino_otakebi(&block)
    puts(block.call("PLAYER " +  @name + " WINS!"))
  end

  private
#各役の判断、大きい順からする
  def eval_hand
    if royal_straight_flush? then
      @yaku = YAKU[9]
    elsif straight_flush? then
      @yaku = YAKU[8]
    elsif four_card? then
      @yaku = YAKU[7]
    elsif fullhouse? then
      @yaku = YAKU[6]
    elsif flush? then
      @yaku = YAKU[5] 
    elsif straight? then
      @yaku =  YAKU[4]
    elsif three_card? then
      @yaku = YAKU[3]
    elsif two_pair? then
      @yaku = YAKU[2]
    elsif one_pair? then
      @yaku = YAKU[1]
    else
      @yaku = YAKU[0]
    end
  end

#ストレートの判断、手札をソートして並んでいるか判断する、エースからはじまってる場合は2があるか確認して、無かったら10..11..12に飛ぶ、枚数ぶん続いていなかったらbreakしてinjectからnilが返るのでこの関数の戻り値はその場合falseになる
  def straight?
    nil != @hand.sort do |a, b|
      a.num <=> b.num
    end.inject do |x, y|
      if x.num + 1 == y.num then
        y
      elsif x.num == 1 && y.num == 10
        y 
      else
        break 
      end
    end
  end

#そのカードがそろっている枚数を配列で返す各numの値が[2,4,2,5,4]だとすると[2,2,2,1,2]が返る
  def find_same_card
    @hand.map do |x|
      @hand.inject(0) do |a, b|
        if x.num == b.num then
          a + 1
        else
          a
        end
      end
    end
  end

#フォーカードが揃っているなら配列から4の値が4つ見つかる
  def four_card?
    find_same_card.find_all{|x| x == 4}.size == 4
  end

#スリーカードが揃っているなら配列から3の値が3つ見つかる
  def three_card?
    find_same_card.find_all{|x| x == 3}.size == 3
  end

#ワンペアが揃っているなら配列から2の値が2つ見つかる
  def one_pair?
    find_same_card.find_all{|x| x == 2}.size == 2
  end

#ツーペアが揃っているなら配列から2の値が4つ見つかる
  def two_pair?
    find_same_card.find_all{|x| x == 2}.size == 4
  end

#フルハウスが揃っているならばこの調べかたなら偶然にワンペアかつスリーカードの条件と同じ
  def fullhouse?
    one_pair? && three_card?
  end

  def flush?
    @hand.all?{|x| x.suit == @hand[0].suit}
  end

  def straight_flush?
    straight? && flush?
  end

#ロイヤルストレートフラッシュ、エースの値を取りのぞいた一番小さい値が10か
  def royal_straight_flush?
    straight_flush?  && @hand.map{|x| x.num}.find_all{|x| x != 1}.min == 10
  end
end


my_game = Game::new

while my_game.continue?
    my_game.do_one_turn
end