Help us understand the problem. What is going on with this article?

rubyでドローポーカーを作ってみる~実装編1(カード)~

概要

rubyでドローポーカーを作ってみる~準備編~

rubyでドローポーカーを作ってみる~test-unit準備編~

に続いて。

ソース: https://github.com/rytkmt/ruby_poker

今回からようやく実装に入っていこうと思います。

カードの実装

ポーカーで一番単一で動く末端はカードかなと思い、カードから作っていきます。
情報を整理していきましょう。

  • 数字
  • スート(記号)

をまず全カードが保持しているのでこれを実装します。

生成

  • 全カードを生成するため、その際は数字とスートを必ず渡すためinitializeの引数とする
  • 外部から数字とスートを参照できるが、変更はできないようにする
ruby_poker/card.rb
module RubyPoker
  class Card
    attr_reader :suit, :number

    def initialize(suit:, number:)
      @suit = suit
      @number = number
    end
  end
end

そして新しいファイルのため読込

ruby_poker.rb
require "ruby_poker/card"

これで終わりかと思いきや、次にカードを使う際のことを考えてみます。

比較

カードは数字とスートを使い「役」を作り、役が判定となるため、カードは参照できれば十分?とも思ったのですが、
詳しく役の説明を確認すると、同一の役の場合に役のなかで使われたカードの強弱をもとに判定する といったカード同士の強弱があるとのこと。

例えば「ツーカード」同士の場合
- 数字が大きいほうが勝ち
- 数字が同じ場合、スートの強いほうが勝ち

準備編で確認したとおり下記の順番となる。

  • 2人のプレイヤーが同じ役を作った場合は、役を構成しているカードの強い方が勝ちとなる
    • カードの強い順位は、A・K・Q・J・10~2
    • スートの強い順位は、スペード・ハート・ダイヤ・クローバー

配列の要素番号で判定できそうなので、定義する(定義は強い順に並んでいるのでindexを取る場合は逆順にする)

ruby_poker.rb
module RubyPoker
  NUMBERS = ([1] + [*2..13].reverse).freeze
  SUITS = %i[spade heart diamond club].freeze
end

またせっかくなので、card.rb側で引数チェックにも使用する

ruby_poker/card.rb
    def initialize(suit:, number:)
+      raise(ArgumentError) unless RubyPoker::SUITS.include?(suit)
+      raise(ArgumentError) unless RubyPoker::NUMBERS.include?(number)

また、要素の強弱を判定するユースケースですが、下記が考えられました。

  1. 自分の役に使われた手札の中で、一番強いカード がどれか(ストレート同士の比較の場合など)
  2. 敵の役との比較の際に、一番強いカード同士での比較

1に関しては、複数の中から一番強いものを取りたいためmax
2に関しては大小の比較>など

そのためComparableを使用し<=>を実装すれば解決(max<=>で判定されている Array#max

数字は単なる数字で比較してしまうと1が最弱になってしまうため、ちゃんとindexをもとに比較する

ruby_poker/card.rb
module RubyPoker
  class Card
+   include Comparable
    attr_reader :suit, :number
ruby_poker/card.rb
    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

これでひとまずカードに関しては完成 かな?

続き


rubyでドローポーカーを作ってみる~実装編2(役)~

r12tkmt
vimとruby関連が多いかと思います :)
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away