【備忘録】プログラミング未経験者がrubyでブラックジャックゲームを作ってみたお話

## はじめに
この記事は、プログラミング未経験者である私が、ブラックジャックゲームの作成を通じて学んだことや感じたことを、思いつくまま書き連ねたものになります。
このような記事を書くのも初めてなので、まとまりのない雑記になってしまいそうですが、今後も記事を書きつつ精度を上げていきたいと思いますので、何卒ご容赦ください:bow_tone1:

## 使用教材
・プロを目指す人のためのRuby入門[改訂2版] 
　言語仕様からテスト駆動開発・デバッグ技法まで (Software Design plus) 
　伊藤 淳一 (著)

## ブラックジャックゲームについて
今回はプレイヤーとディーラーの1対1の対戦というシンプルなケースで作成します。
なお、今後はCPUありのプレイヤーが複数人いるパターンも作成する予定です。
ルール上の注意点としてはトランプのA（エース）を引いた場合の点数は１点または１１点となり、持ち札の合計点が21点に近くなるよう（かつ21点は超えないよう）自動的に決定するという点です。
（ブラックジャックの詳しいルールについては各サイトをご確認ください）

それでは作成開始です！

## STEP1：全体像を把握し作成するクラスを決める
まずはゲームの全体像を把握し、必要なクラスを決めていきました。
今回はオブジェクト指向でプログラムを書いていくため、まずは実際にブラックジャックで遊んでいるところを想像します（カジノ行ったことないですけど:sweat_smile:）
オブジェクト指向は、現実世界そのままをプログラムの世界に落とし込み、開発効率をあげる手法（というか考え方）なので、まずは想像することが大事だと思っています。

・まずは当然プレイヤーとディーラがいる（→playerクラスとdealerクラスが必要）
・カードの山（デッキ）がある（→cardクラスが必要）

この3つのクラスでディーラーとプレイヤーがカードのやり取りをしている姿が目に浮かびます。
あと、得点をつける人・・・というのはいませんが、得点集計は別のクラスに任せた方が良さそうです。

・得点集計はこれまで用意したクラスとは別のクラスを作成する（→scoreクラスが必要）

また、プレイヤーとディーラーの挙動はほとんど同じものになるので、基本的な機能を実装したクラスを別に作り、それをplayaerクラスとdealerクラスに継承させるのが良さそうです。
そこで、baseplayerクラスを作成し、基本的な機能を書いていきます

・プレイヤーとディーラーに同一の機能を継承させる（→baseplayerクラスを作成）

```ruby:baseplayer.rb
class BasePlayer
end
```
```ruby:player.rb
class Player < BasePlayer
end
```
```ruby:dealer.rb
class Dealer < BasePlayer
end
```

最後に、ゲームの全体を統括するクラスとして、Blackjackクラスも作成しました。

## STEP２：コメントを書きつつ流れを確認
クラスを作ったので、さて作成開始・・・といきたいところですが、このままだとどこから着手すればよいかわかりません。まずはBlackjackクラスで、処理の流れをコメントで書いていくことにしました。

```ruby:Blackjack.rb
# ブラックジャックのゲームを開始します
# 開始するには game_start メソッドを実行してください
class Blackjack
    def game_start
        #ブラックジャックを開始します。
        #あなたの引いたカードはハートの7です。
        #あなたの引いたカードはクラブの8です。
        #ディーラーの引いたカードはダイヤのQです。
        #...（以下続く）   
    end
end
```
## STEP3：各クラスにメソッドを追加していく
さて、ブラックジャッククラスで書いたコメントからも分かる通り、ディーラーやプレイヤーがカードを引くためのメソッドが必要です。また引いたカードを手札として保持する配列も必要そうです。
そこでBasePlayerクラスに以下のように記載しました。

```ruby:baseplayer.rb
class BasePlayer
  # 手札の配列を作成
  def initialize
    @hand = []
  end

  # カードを山札から引いて手札に格納する
  def get_card(card)
  end
end
```
その前にカードクラスで山札を作る必要がありました！
山札がなければカード引くことができません。

```ruby:card.rb
# 52枚のトランプを管理するクラス
class Card
  attr_reader :deck

  def initialize
    # 52枚のカードを用意する
    numbers = ['A', 2, 3, 4, 5, 6, 7, 8, 9, 'J', 'Q', 'K']
    marks = %w[ハート スペード ダイヤ クラブ]
    @deck = []
    marks.each do |m|
      numbers.each do |n|
        @deck << [m, n]
      end
    end
  end
end
```
numbersにトランプの数字を、marksにトランプのマークを格納し、ブロックを入れ子にして、52枚のトランプを再現しました。initializeメソッドに入れいてるため、newでインスタンス化された時点で山札が作成されます。

cardクラスができたので、baseplayerクラスに山札からカードを引く機能を実装します。

```ruby:baseplayer.rb
class BasePlayer
  # 手札の配列を作成
  def initialize
    @hand = []
  end

  # カードを山札から引いて手札に格納する
  def get_card(card)
    hand << card.deck.shuffle!.first
    card.deck.shift
    hand
  end
end
```
get_cardメソッドは、インスタンス変数handに、cardクラスの配列deck（山札）からひいたカードを格納するメソッドです。
shuffle！で配列をランダムに入れ替えした後、firstで配列先頭のカードを取得します。
また、shiftで配列先頭のカードを削除するので、取得したカードは山札に残りません。

このBasePlayerクラスをplayerクラスとdealerクラスが継承しているため、どちらのクラスも同じ機能（メソッド）が利用できるようになります。

## 現在の得点を見たり最終得点を比較するためScoreクラスを実装する
Scoreクラスの中身は以下の通りです

```ruby:baseplayer.rb
# プレイヤー、ディーラークラスの手札の得点を集計する
class Score
  # 各プレイヤーの現在の得点を集計する
  def get_score(player)
    sum = 0
    have_a = nil # 手札にエースがあるか判定するための変数

    # 手札の合計得点を求める
    player.hand.each do |p|
      if p[1] == 'A'
        sum += 1
        have_a = true # 手札にエースがあることを判定するためtrueにする
      elsif p[1] == 'J' || p[1] == 'Q' || p[1] == 'K'
        sum += 10
      else
        sum += p[1]
      end
    end

    # エース有かつ手札の合計得点が11点以下なら10点追加(エースを11点としてカウント)
    sum += 10 if have_a && sum <= 11

    sum
  end
end
```
get_scoreメソッドはその名の通りプレイヤーまたはディーラの合計点を求めるメソッドです。
Playerクラスのインスタンス変数hand（手札）をeachで回し、変数sumに順に足していき、合計点を求めています。

その際、A,J,Q,K はそのままだと文字列で計算ができないので、if文でそれぞれ対応した点数を足しています。
ただ、A（エース）は1点または11点のため、まずは1点で計算し、最後合計点sumが11以下であれば10点を足すという順序で計算しています。
## 終わりに
技術ブログというよりは私が実装した順序を振り返るだけになってしまいましたが、こんな感じの流れで完成させることができました。
これからも適宜コードを見直し、加筆修正していくつもりですので、何卒よろしくお願いします。