[Ruby] Making Draw Poker with ruby-Mounting Edition 2 (role)-

4 minute read

Overview

Try making draw poker with ruby ~Preparation~Try making draw poker with ruby ~test-unit preparation~Try to make a draw poker with ruby ~ Implementation 1 (card) ~

Followed by.

Source: https://github.com/rytkmt/ruby_poker

This time we will implement the role.

Implementing a role

Let’s organize the requirements again.

  • The role is judged based on 5 cards
  • Players compare wins and losses based on their roles -If you create a “role” class to compare roles and use Comparable, you can compare
  • Holds a card to be compared even in the same role (1 card)
  • The role is “hand” in English.

That’s why I implemented it.

Create file

First create a file normally

ruby_poker/hand.rb


module RubyPoker
  class Hand
    def intialize(cards:)
    end
  end
end

ruby_poker.rb


require "ruby_poker/hand"

Role judgment

If you are generating a “role” class, you should make a judgment at that timing. ..

Therefore, use the judgment method at the time of generation and set the type and comparison card for the same role.

Generate

ruby_poker/hand.rb


    attr_reader :hand_type, :comparison_card

    def intialize(cards:)
      raise(ArgumentError) if cards.size != 5
      @hand_type, @comparison_card = judge(cards: cards)
    end

  private

    def judge(cards:)
    end

Don’t make it an instance variable because you don’t have to use cards to judge the role.

How to implement the role determination…

There are many roles, so prepare a method one by one, and … there is a feeling that you have not been able to separate it. .. So, let’s start with a judgment module.

Then, the judgments should be made in strong order, and the one that matches first should be set. First of all, the definition is done like the card.

Type Definition

For the English name, refer to this… lol https://en.wikipedia.org/wiki/List_of_poker_hands

ruby_poker.rb


  HAND_TYPES = %i[
    royal_straight_flush
    straight_flush
    four_of_a_kind
    full_house
    flush
    straight
    three_of_a_kind
    two_pair
    one_pair
    high_card
  ].reverse.freeze

I want to get the module class dynamically, so I want to convert it from snake case to camel case and get it with const_get. .. So let’s use the active_support gem for that word conversion (It’s convenient to use the rails and it’s convenient to use it because it’s easy to reach the itchy areas around here.)

Gemfile


gem "activesupport"
$ bundle install

This completes the installation

ruby_poker.rb


require "active_support/all"

Do not forget to read

Transfer judgment to module

ruby_poker/hand.rb


Dir[__dir__ + "/hand/hand_type_judgers/*.rb"].each {|p| require p}

module RubyPoker
  class Hand

ruby_poker/hand.rb


  private

    def judge(cards:)
      matched_cards = nil
      matched_type = RubyPoker::HAND_TYPES.find do |type|
        judger = RubyPoker::Hand::HandTypeJudgers.const_get(type.to_s.classify) # <= for active_support
        matched_cards = judger.judge(cards: cards)
      end

      [matched_type, matched_cards.max]
    end

I decided to implement it as an inner module of the hand because I thought that it was within the responsibility of the hand to separate the internal processing of the hand. If the Judger’s .judge matches, make sure to return the card that is the target of the same role judgment, and if it does not match, return nil

Role determination module implementation

Royal Straight Flush

ruby_poker/lib/ruby_poker/hand/hand_type_judgers/royal_straight_flush.rb


module RubyPoker
  class Hand
    module HandTypeJudgers
      module RoyalStraightFlush
        def self.judge(cards:)
          return nil unless cards.map(&:suit).uniq.size == 1
          return nil unless cards.sort.map(&:number) == [10, 11, 12, 13, 1]
          cards
        end
      end
    end
  end
end
Straight flash

ruby_poker/lib/ruby_poker/hand/hand_type_judgers/straight_flush.rb


module RubyPoker
  class Hand
    module HandTypeJudgers
      module StraightFlush
        def self.judge(cards:)
          return nil unless cards.map(&:suit).uniq.size == 1
          min_number_level = cards.min.number_level
          expected = [*min_number_level..min_number_level + 4]
          return nil unless cards.sort.map(&:number_level) == expected
          cards
        end
      end
    end
  end
end
Four Card

ruby_poker/lib/ruby_poker/hand/hand_type_judgers/four_of_a_kind.rb


module RubyPoker
  class Hand
    module HandTypeJudgers
      module FourOfAKind
        def self.judge(cards:)
          cards.group_by(&:number).detect {|k, v| v.size == 4 }&.second
        end
      end
    end
  end
end
Full house

ruby_poker/lib/ruby_poker/hand/hand_type_judgers/full_house.rb


module RubyPoker
  class Hand
    module HandTypeJudgers
      module FullHouse
        def self.judge(cards:)
          grouped_number_cards = cards.group_by(&:number)
          three_card_number, three_cards = grouped_number_cards.detect {|k, v| v.size == 3}
          return nil unless three_card_number
          grouped_number_cards.delete(three_card_number)
          return nil unless grouped_number_cards.detect {|k, v| v.size == 2}
          three_cards
        end
      end
    end
  end
end
Flash

ruby_poker/lib/ruby_poker/hand/hand_type_judgers/flush.rb


module RubyPoker
  class Hand
    module HandTypeJudgers
      module Flush
        def self.judge(cards:)
          return nil unless cards.map(&:suit).uniq.size == 1
          cards
        end
      end
    end
  end
end
straight

ruby_poker/lib/ruby_poker/hand/hand_type_judgers/straight.rb


module RubyPoker
  class Hand
    module HandTypeJudgers
      module Straight
        def self.judge(cards:)
          min_number_level = cards.min.number_levelexpected = [*min_number_level..min_number_level + 4]
          return nil unless cards.sort.map(&:number_level) == expected
          cards
        end
      end
    end
  end
end
スリーカード

ruby_poker/lib/ruby_poker/hand/hand_type_judgers/three_of_a_kind.rb


module RubyPoker
  class Hand
    module HandTypeJudgers
      module ThreeOfAKind
        def self.judge(cards:)
          cards.group_by(&:number).detect { |k, v| v.size == 3 }&.second
        end
      end
    end
  end
end
ツーペア

ruby_poker/lib/ruby_poker/hand/hand_type_judgers/two_pair.rb


module RubyPoker
  class Hand
    module HandTypeJudgers
      module TwoPair
        def self.judge(cards:)
          pairs = cards.group_by(&:number_level).select { |k, v| v.size == 2 }
          return nil unless pairs.size == 2
          pairs.max.second
        end
      end
    end
  end
end
ワンペア

ruby_poker/lib/ruby_poker/hand/hand_type_judgers/one_pair.rb


module RubyPoker
  class Hand
    module HandTypeJudgers
      module OnePair
        def self.judge(cards:)
          cards.group_by(&:number_level).detect { |k, v| v.size == 2 }&.second
        end
      end
    end
  end
end
ハイカード(豚)

ruby_poker/lib/ruby_poker/hand/hand_type_judgers/high_card.rb


module RubyPoker
  class Hand
    module HandTypeJudgers
      module HighCard
        def self.judge(cards:)
          [cards.max]
        end
      end
    end
  end
end

テストの実装も含めて少し大変でしたが実装完了。これで役判定と、同役時の判定用カードの返却までしっかりと行うことができるようになりました。

Comparable用の判定追加

ruby_poker/hand.rb


+    include Comparable

ruby_poker/hand.rb


    def hand_level
      RubyPoker::HAND_TYPES.reverse.index(@hand_type)
    end

    def <=>(other)
      hand_comparison = hand_level <=> other.hand_level
      hand_comparison.zero? ? @comparison_card <=> other.comparison_card : hand_comparison
    end

これで役の勝敗を>などで比較することができるようになりました

終わりに

今回の内容にて、カード、役 などゲームに大切な処理は一通り終わったかと思います。 次回は、これらを使うプレイヤーやゲーム進行回りを作っていきたいと思います。

続き

rubyでドローポーカーを作ってみる~実装編3(プレイヤー)~

Tags:

Updated: