Ruby
BitStarDay 15

[初級者向け]Rubyで作るトランプデッキ

はじめに

こんにちは 
BitStarの新米エンジニア Narachiiです。

DECKの作り方

デッキ作成とシャッフル、そしてカードのドロー方法を紹介したいと思います。

1. 52枚(ジョーカーを除く)1組のデッキ作成
2. デッキのシャッフル
3. ドロー

それでは、早速取り掛かっていきましょう!

デッキ作成

トランプはスペード・クローバー・ハート・ダイアの4つのスートとA~Kまでの13枚のカードの
組み合わせで成り立ちます。
まずはこの組み合わせを作成しましょう。

ここでは初めに個々のトランプを作成するCardクラス
52枚の組み合わせを作成するDeckクラスを定義します。
Cardクラスは引数に絵柄(suit)と数字(number)を取ります。

まずはCardクラス内に
絵柄(suit)と数字(number)のインスタンス変数を作成し
♠︎1をshowメソッドで表示させてみましょう。

bj.rb
class Card
  def initialize(suit, number)
    @suit = suit
    @number = number
  end

  def show
    puts "#{@number} of #{@suit}"
  end
end

class Deck
end

card = Card("Spades", 1)
card.show

実行結果

$ ruby bj.rb
→ Spades of 1

続いてDeckクラスにbuildメソッドを定義して
デッキを作成します。

bj.rb
 class Card
   def initialize(suit, number)
     @suit = suit
     @number = number
    end

   def show
      puts "#{@number} of #{@suit}"
   end
 end

 class Deck
   #追記
   def initialize
     @cards = []
     build
   end

   def build
     for s in ["♠","♥", "♦", "♣"] do
       for v in 1..13 do
         @cards << Card.new(s,v)
       end
     end
   end

   def show
     @cards.each do |card|
       card.show #Cardクラスのshowを呼び出し
    end
  end
end

絵柄と数字でforをネストさせ
それぞれを引数にCardクラスのインスタンスを作成することで
4×13枚のカードを作成し、@cards配列に
格納しています。

最後にDeckクラスのインスタンスを生成して
showメソッドを呼び出すコードを追記します。

bj.rb
class Card
  ~ 省略 ~
end

class Deck
  ~ 省略 ~
end
# 追記
deck = Deck.new
deck.show

ここまで作成したコードを
ターミナルで確認してみましょう。

実行結果は以下のようになります。

terminal
$ ruby bj.rb
♠:1
♠:2
♠:3
♠:4
~省略~
♣:10
♣:11
♣:12
♣:13

デッキのシャッフル

続いてデッキをシャッフルするメソッドを作成していきましょう。
ここでは以下のshuffleメソッド作成します。

bj.rb
def shuffle
  cards_length = (@cards.count) -1
  cards_length.step(1,-1) do |i|
  r = rand(i)
  @cards[i] , @cards[r] = @cards[r], @cards[i]
end

まず始めの行で、@cards配列に格納されている値の個数をカウントします。(52個)
その後、stepメソッドで
52~1までの値 i とrandメソッドで生成されたランダムの値 rで
@cardsの配列を取り出し、値を入れ替える処理を繰り返し行うことで
シャッフルしています。

irb
irb(main):001:0> card = [1, 2 ,3]
=> [1, 2, 3]
irb(main):002:0> card[0], card[2] = card[2], card[0]
=> [3, 1]
irb(main):003:0> card
=> [3, 2, 1]

この際、(@cards.count) - 1 としているのは
配列は0番から始まるためです。

irb
irb(main):001:0> array = [1, 2, 3]
=> [1, 2, 3]
irb(main):001:1> array.length
=> 3
array[3]
=> nil

それではshuffleメソッドをDeckクラスに定義して
動作を確認してみましょう。

br.jb
 class Card
   def initialize(suit, number)
     @suit = suit
     @number = number
    end

   def show
      puts "#{@number} of #{@suit}"
   end
 end

 class Deck
   def initialize
     @cards = []
     build
   end

   def build
     for s in ["♠","♥", "♦", "♣"] do
       for v in 1..13 do
         @cards << Card.new(s,v)
       end
     end
   end

   def show
     @cards.each do |card|
       card.show 
    end
  end
  # 追記
  def shuffle
    cards_length = (@cards.count) -1
    cards_length.step(1,-1) do |i|
    r = rand(i)
    @cards[i] , @cards[r] = @cards[r], @cards[i]
  end

end

deck = Deck.new
deck.shuffle # メソッドを呼び出し
deck.show

実行してみると
シャッフルされているのが分かります。

terminal
$ ruby bj.rb
♣:4
♥:1
♠:4
♣:3
♣:2
♦:2
~省略~

カードのドロー

カードのドローを実行するためにPlayerクラスを定義していきましょう。
カードを引くには以下の2つの動作が必要です。

1.@cardsの末尾から要素を一つ抜き出す。 
2.抜き出した要素を別の配列に格納

まずは、1を行うためのメソッドdrawCardをDeckクラスに定義しましょう
popメソッドを使い、配列の末尾の要素を削除し、その要素を返します。

bj.rb
  def drawCard
    @cards.pop
  end

続いて2のためにPlayerクラスを作成し
先程抜き出した要素を@handsとして
Playerクラスのインスタンス変数に格納するように
していきます。

bj.rb
class Player
  def initialize
    @hands = []
  end

次にdrawメソッドをPlayerクラス内に定義します。

  def draw(deck)
    @hands.push(deck.drawCard)
  end

pushメソッドは引数の要素を配列の末尾に格納します。
引数でDeckクラスのdrawCardメソッドを呼び出すことで
@cardsから取り出した要素を@handsに格納することが出来ます。

これでデッキからカードを引くことは出来ましたが
引いたカードを確認出来ないので
showHandsメソッドを作成しましょう。

bj.rb
  def showHands
    puts "あなたの手札"
    @hands.each do |hand|
      hand.show
    end
  end

showHandsメソッドでは
@handsに格納されている要素毎にshowメソッドを呼び出し
カードを表示させています。

これまでの実装でPlayerクラスは以下のようになります。

bj.rb
 class Player
  def initialize
    @hands = []
  end

  def draw(deck)
    @hands.push(deck.drawCard)
  end

  def showHands
    puts "あなたの手札"
    @hands.each do |hand|
      hand.show
    end
  end
 end

それでは最後にPlayerクラスを呼び出して
動作を確認してみましょう。

bj.rb
 class Card
   def initialize(suit, number)
     @suit = suit
     @number = number
    end

   def show
      puts "#{@number} of #{@suit}"
   end
 end

 class Deck
   def initialize
     @cards = []
     build
   end

   def build
     for s in ["♠","♥", "♦", "♣"] do
       for v in 1..13 do
         @cards << Card.new(s,v)
       end
     end
   end

   def show
     @cards.each do |card|
       card.show
     end
    end
  # 追記
   def shuffle
     cards_length = (@cards.count) -1
     cards_length.step(1,-1) do |i|
      r = rand(i)
      @cards[i] , @cards[r] = @cards[r], @cards[i]
     end
   end

  def drawCard
    @cards.pop
  end
 end


 class Player
  def initialize
    @hands = []
  end

  def draw(deck)
    @hands.push(deck.drawCard)
  end

  def showHands
    puts "あなたの手札"
    @hands.each do |hand|
      hand.show
    end
  end
 end


deck = Deck.new
deck.shuffle
deck.show
###追記
card = Player.new
card.draw(deck)
card.showHands
puts "引いた後のデッキ"
deck.show ### 引いた後のデッキを確認

以下のような実行結果になっていれば完成です!

terminal
$ ruby bj.rb
3 of ♦
1 of ♠
10 of ♣
~省略~
10 of ♠
9 of ♠
12 of ♠
あなたの手札
12 of ♠
引いた後のデッキ
3 of ♦
1 of ♠
10 of ♣
~省略~
10 of ♠
9 of ♠

引いた後のデッキには
12 of ♠が ないことが確認できますね!

終わりに

これにてデッキの実装は完成です。
お疲れ様でした!