rubyでドローポーカーを作ってみる～実装編5（ゲーム）【完結】～

## 概要

[rubyでドローポーカーを作ってみる～準備編～](https://qiita.com/r12tkmt/items/56a60f2d69226b2bdcf2)
↓
[rubyでドローポーカーを作ってみる～test-unit準備編～](https://qiita.com/r12tkmt/items/48940d0e09a77d210bf2)
↓
[rubyでドローポーカーを作ってみる～実装編1（カード）～](https://qiita.com/r12tkmt/items/175aa66fd91995f13524)
↓
[rubyでドローポーカーを作ってみる～実装編2（役）～](https://qiita.com/r12tkmt/items/cd22e04eb102eabf03cd)
↓
[rubyでドローポーカーを作ってみる～実装編3（プレイヤー）～](https://qiita.com/r12tkmt/items/ae44086d9bf7d01e7572)
↓
[rubyでドローポーカーを作ってみる～実装編4（山札）～](https://qiita.com/r12tkmt/items/649dbd9a6e4d097ceb4c)
に続いて。

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

## ゲームの実装

やってきました。
整理することが多そうなので若干びびってますが、一度すべて洗い出してみたら全体像がつかめて案外・・ってなるかもしれないですね。

では、整理していきましょう。

- 山札を作成
- 指定人数分、プレイヤーを作成
  - 山札から5枚ずつカードを配る
- 各プレイヤーを順番に変更するカードの確認
  - 手札を画面に表示する
      - 全カード
      - 現在の手札での役
  - 変更する場合、枚数分山札からカードを渡し、不要カードを回収し山札の不要カードとして追加する
  - 変更後の手札を画面に表示する
      - 全カード
      - 現在の手札での役
- 全プレイヤーの役を判定する
  - 勝者を表示

### ゲームの開始

まずゲームは開始するとともに山札を初期化します

```ruby:ruby_poker/game.rb
module RubyPoker
  class Game
    def initialize
      @deck = Deck.new
    end
  end
end
```

### ゲームの実行

`start`って付けようとしていましたが、gameを行うのは`play`のほうが適切だと思ったので`play`でいきます
このとき、`new.play`って毎回するのもあほらしいので、`.play`メソッドを用意してしまいます

```ruby:ruby_poker/game.rb
    def self.play
      new.send(:play)
    end

  private

    def play
      puts "＜ゲーム開始＞"

      puts "＜ゲーム終了＞"
    end
```

### プレイヤーの名称を保持

毎回プレイヤー番号を渡し続けるのもしんどいので、プレイヤーの名称を保持しておくように機能強化します。

```diff:ruby_poker/player.rb
 module RubyPoker
   class Player
     include Comparable
-    attr_reader :hand
+    attr_reader :name, :hand
 
-    def initialize(cards:)
+    def initialize(name:, cards:)
```

### プレイヤー人数の決定

プレイヤーを初期化するにあたり、人数を入力してもらい、カードを5枚ずつ配ります
人数の入力は間違うこともあるため、間違っている場合は再度入力を促すようにします

```ruby:ruby_poker/game.rb
    def init_players(count:)
      @players = count.times.map { |i| Player.new(name: "プレイヤー#{i + 1}", cards: @deck.draw(count: 5)) }
    end

    def input_player_count
      puts "プレイヤーの参加人数を入力してください(1～7)"
      player_count = gets.to_i
      return player_count if (1..7).include?(player_count)
      puts "入力が間違っています"
      input_player_count
    end
```

### プレイヤーの手札表示

プレイヤーの手札を表示するために、カード、役などそれぞれ表示用の文字列を生成します。
このとき、コンソール出力は`inspect`メソッドを使用するのが一般的なのでそちらで実装します。

プレイヤーは各カードと役を表示します。
なので、カード、役にもそれぞれ`inspect`を定義し、それを合わせて出力するのをプレイヤーの出力で行うようにします。

また、カードの表示がわりと見にくいので、色を付けて表示するようにしてみます。

#### 色表示用のgemインストール

```diff:Gemrfile
+gem "rainbow"
```

```shell
$ bundle install
```

```diff:ruby_poker.rb
+require "rainbow"
```

これで`puts Rainbow("hoge").red`とかすることでカラー表示できるようになります

#### カードのinspect

- スートの表示用に記号へ変換
- 数字の記号変換
- スートに応じてカラーを切替
- 
をそれぞれ行います

```ruby:ruby_poker/card.rb
    def inspect
      suit_str =
        case @suit
        when :spade; "♠"
        when :heart; "♥"
        when :diamond; "♦"
        when :club; "♣"
        end

      number_str =
        case @number
        when 1; "A"
        when 13; "K"
        when 12; "Q"
        when 11; "J"
        else; @number
        end

      color =
        case @suit
        when :spade; :blue
        when :heart; :red
        when :diamond; :yellow
        when :club; :green
        end

      Rainbow("[#{suit_str} #{number_str}]").send(color)
    end
```

#### 役のinspect

```ruby:ruby_poker/hand.rb
    def inspect
      hand_type_name =
        case @hand_type
        when :royal_straight_flush; "ロイヤルストレートフラッシュ"
        when :straight_flush; "ストレートフラッシュ"
        when :four_of_a_kind; "フォーカード"
        when :full_house; "フルハウス"
        when :flush; "フラッシュ"
        when :straight; "ストレート"
        when :three_of_a_kind; "スリーカード"
        when :two_pair; "ツーペア"
        when :one_pair; "ワンペア"
        when :high_card; "🐷"
        end

      "役: #{hand_type_name}"
    end
```

#### プレイヤーのinspect

最後にプレイヤーです

カード交換の際に番号入力をしてもらうため、各カードに番号をつけて表示します。

```ruby:ruby_poker/player.rb
    def inspect
      cards_str = @cards.map.with_index(1) do |card, i|
        "#{i}. #{card.inspect}"
      end.join("   ")

      cards_str + "\n" + @hand.inspect + "\n"
    end
```

これで、`p player`をすることで こんな感じになります（色はここでは映らないですが・・）
↓

```
1. [♥ 5]   2. [♦ 8]   3. [♣ 4]   4. [♠ 10]   5. [♠ 4]
役: ワンペア
```

### プレイヤーのターン処理

1. 各プレイヤーの現在の状況を表示
2. カード交換を確認
  - 入力が間違っていたら再度確認する
3. 交換後の手札を表示

```ruby:ruby_poker/game.rb
    def turn(player:)
      puts "\n#{player.name} のターンです"
      p player

      puts "カードの交換ができます"
      indexes = input_change_indexes
      return if indexes.empty?

      new_cards = @deck.draw(count: indexes.size)
      trushed = player.change(indexes: indexes, new_cards: new_cards)
      @deck.trush(cards: trushed)
      p player
    end

    def input_change_indexes
      puts "捨てるカードの番号を半角スペース区切りで入力してください（なければそのままEnter）"

      indexes = gets.chomp.split.map(&:to_i)
      return indexes.map { |i| i - 1 }.sort if indexes.all? { |i| (1..5).include?(i) }
      input_change_indexes
    end
```

### プレイヤーの勝敗判定

```ruby:ruby_poker/game.rb
    def judge
      puts "----- 結果 -----"
      @players.each do |player|
        puts "#{player.name} の手札"
        p player
        puts ""
      end

      winner = @players.max
      puts Rainbow("\n#{winner.name} の勝ち 👏👏").underline
    end
```

### 実行メソッドにまとめる

```ruby:ruby_poker/game.rb
    def play
      puts "＜ゲーム開始＞"

      init_players(count: input_player_count)
      @players.each { |player| turn(player: player) }

      judge

      puts "＜ゲーム終了＞"
    end
```

### ゲームの実行を簡単にする

`RubyPoker::Game.play` となりますが、 `RubyPoker.paly`がいい。。

```ruby:ruby_poker.rb
  def self.play
    RubyPoker::Game.play
  end
```

## 完成

`bundle exec console` を実行することでゲームを楽しむことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219776/d0d6b47b-0940-6e8a-9f05-88f32a9cc92b.png)

## 感想

Qiitaで見た1つの記事をきっかけに「やってみよう」と思い今回の記事を書きながら実装しました。

実際に実装していく流れなどをみなさんがどういう風に行っているのか？というところは、いざ仕事で開発をしていると各個人で頭の中で考えて全て整ったプログラムを確認することばかりのため、
あまり実装のプロセスを覗く機会がない印象があります。
今回の記事が少しでも参考になればうれしいです。

普段仕事ではrailsのプロジェクトなので、railsやgemを触ることはありますが、
単一のrubyプロジェクトを作ることは無かったためGemfile、Rakefileなど改めてこういう感じで進めるんだ～と自分のなかでもふわっとしていたところが理解できました。
普段使わないgemを触るのも楽しかったです。

この記事をもとに同じように刺激をうけ、なにか作ってみよう　と思う方がいればうれしいです。
また、流れを公開したことにより、もしここをこういう風にするとより良くなる　などのアドバイスがあればぜひ教えてください。お待ちしております。

実際のソースはこちらにあります。 https://github.com/rytkmt/ruby_poker

#### 実装に関しての感想

今回、ポーカーに限った実装でしたが、
カードを共通として、プレイヤーと山札の処理をゲームの種別に応じてモジュールを切り替えることによっていろんなゲームを載せる　なども出来そうかなーと思いました。

また、役判定を完全に切り分けることで、ローカルルールで役を追加する（奇数・偶数でのストレートとかあっても面白そう）などもできるかなと思います

今回jokerは考慮せず作ったため、それを追加するのは少し大変だなーとは思いますが、やってみるのも楽しいかもしれないですね。


以上、かなり記事数も多くなりましたが、長々と見ていただいた方、ありがとうございました。