rubyでドローポーカーを作ってみる～実装編1（カード）～

## 概要

[rubyでドローポーカーを作ってみる～準備編～](https://qiita.com/r12tkmt/items/56a60f2d69226b2bdcf2)
↓
[rubyでドローポーカーを作ってみる～test-unit準備編～](https://qiita.com/r12tkmt/items/48940d0e09a77d210bf2)

に続いて。

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

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

## カードの実装

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

- 数字
- スート（記号）

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

### 生成

- 全カードを生成するため、その際は数字とスートを必ず渡すため`initialize`の引数とする
- 外部から数字とスートを参照できるが、変更はできないようにする

```ruby:ruby_poker/card.rb
module RubyPoker
  class Card
    attr_reader :suit, :number

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

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

```ruby:ruby_poker.rb
require "ruby_poker/card"
```

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

### 比較

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

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

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

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

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

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


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

```diff: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](https://docs.ruby-lang.org/ja/latest/method/Array/i/max.html) ）

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

```diff:ruby_poker/card.rb
module RubyPoker
  class Card
+   include Comparable
    attr_reader :suit, :number
```

```ruby: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初めてなので記述でいい方法あれば教えてください

```ruby
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（役）～](https://qiita.com/r12tkmt/items/cd22e04eb102eabf03cd)