概要
rubyでドローポーカーを作ってみる~準備編~
↓
rubyでドローポーカーを作ってみる~test-unit準備編~
に続いて。
ソース: https://github.com/rytkmt/ruby_poker
今回からようやく実装に入っていこうと思います。
カードの実装
ポーカーで一番単一で動く末端はカードかなと思い、カードから作っていきます。
情報を整理していきましょう。
- 数字
- スート(記号)
をまず全カードが保持しているのでこれを実装します。
生成
- 全カードを生成するため、その際は数字とスートを必ず渡すため
initialize
の引数とする - 外部から数字とスートを参照できるが、変更はできないようにする
module RubyPoker
class Card
attr_reader :suit, :number
def initialize(suit:, number:)
@suit = suit
@number = number
end
end
end
そして新しいファイルのため読込
require "ruby_poker/card"
これで終わりかと思いきや、次にカードを使う際のことを考えてみます。
比較
カードは数字とスートを使い「役」を作り、役が判定となるため、カードは参照できれば十分?とも思ったのですが、
詳しく役の説明を確認すると、同一の役の場合に役のなかで使われたカードの強弱をもとに判定する といったカード同士の強弱があるとのこと。
例えば「ツーカード」同士の場合
- 数字が大きいほうが勝ち
- 数字が同じ場合、スートの強いほうが勝ち
準備編で確認したとおり下記の順番となる。
- 2人のプレイヤーが同じ役を作った場合は、役を構成しているカードの強い方が勝ちとなる
- カードの強い順位は、A・K・Q・J・10~2
- スートの強い順位は、スペード・ハート・ダイヤ・クローバー
配列の要素番号で判定できそうなので、定義する(定義は強い順に並んでいるのでindexを取る場合は逆順にする)
module RubyPoker
NUMBERS = ([1] + [*2..13].reverse).freeze
SUITS = %i[spade heart diamond club].freeze
end
またせっかくなので、card.rb
側で引数チェックにも使用する
def initialize(suit:, number:)
+ raise(ArgumentError) unless RubyPoker::SUITS.include?(suit)
+ raise(ArgumentError) unless RubyPoker::NUMBERS.include?(number)
また、要素の強弱を判定するユースケースですが、下記が考えられました。
- 自分の役に使われた手札の中で、一番強いカード がどれか(ストレート同士の比較の場合など)
- 敵の役との比較の際に、一番強いカード同士での比較
1に関しては、複数の中から一番強いものを取りたいためmax
2に関しては大小の比較>
など
そのためComparable
を使用し<=>
を実装すれば解決(max
は<=>
で判定されている Array#max )
数字は単なる数字で比較してしまうと1
が最弱になってしまうため、ちゃんとindex
をもとに比較する
module RubyPoker
class Card
+ include Comparable
attr_reader :suit, :number
def suit_level
RubyPoker::SUITS.reverse.index(@suit)
end
def number_level
RubyPoker::NUMBERS.reverse.index(@number)
end
def <=>(other)
number_comparision = number_level <=> other.number_level
number_comparision.zero? ? suit_level <=> other.suit_level : number_comparision
end
テストケース
簡単にテストケースも実装する ※test-unit初めてなので記述でいい方法あれば教えてください
require "test_helper"
module RubyPoker
class CardTest < Test::Unit::TestCase
sub_test_case "#initialize" do
test "correct arguments" do
assert_nothing_raised do
Card.new(suit: :heart, number: 3)
end
end
test "wrong suit" do
assert_raise_kind_of(ArgumentError) do
Card.new(suit: :test, number: 3)
end
end
test "wrong number" do
assert_raise_kind_of(ArgumentError) do
Card.new(suite: :heart, number: 14)
end
end
end
sub_test_case "#<=>" do
sub_test_case "compare number" do
test "simple numbers" do
a = Card.new(suit: :heart, number: 8)
b = Card.new(suit: :heart, number: 2)
assert(a > b)
end
test "compare ace" do
a = Card.new(suit: :heart, number: 13)
b = Card.new(suit: :heart, number: 1)
assert(a < b)
end
test "max number" do
cards = [*1..5]
.shuffle
.map { |i| Card.new(suit: :heart, number: i) }
assert_equal(1, cards.max.number)
end
end
sub_test_case "compare suit(same number)" do
test "spade and heart" do
a = Card.new(suit: :spade, number: 1)
b = Card.new(suit: :heart, number: 1)
assert(a > b)
end
test "heart and club" do
a = Card.new(suit: :club, number: 1)
b = Card.new(suit: :heart, number: 1)
assert(a < b)
end
test "spade and diamond" do
a = Card.new(suit: :spade, number: 1)
b = Card.new(suit: :diamond, number: 1)
assert(a > b)
end
end
end
end
end
これでひとまずカードに関しては完成 かな?