はじめに
参考記事様
プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべし
オブジェクト指向を意識しつつ、テストをしっかり書く事を目的としてPython3で作成しました。
なお、オブジェクト指向での設計もテスト記述も初心者のため、温かい目でお願いします。
使用ツール
- python 3.5.5
- pytest 3.5.1
コード
想定よりもコードが長くなってしまったのでgistに置きました。
https://gist.github.com/yokoc1322/ebff7d2e677481481384c07ca75ca6ac
テスト内容
カード
- 不正なスートが渡された時に例外を出す
- 不正なランクが渡された時に例外を出す
- ランクに応じたポイントを持つ
デック
- 52枚のカードを持つ
- 有り得るカードを持つ(スペード、ハート、クラブ、ダイヤをそれぞれ1~13まで)
- カードの重複がない
- カードが引ける
- 0枚目を引こうとした時はNoneを返す
- 未実装 インスタンス作成後の並びはランダムである
手札
- カードが引ける
- 手札が0枚の時のポイントは0である
- 正しく計算できる
- 22を越えた時は-1を出す
- Aは1と11のどちらかの値を取り、手札の点数によって最適な値に成る
- カードの一覧としてスートとランクを取れる
人
- デック切れでカードが引けないとき、終了フラグ(isFinished)が立つ
- 未実装 カードの表示ができる
プレイヤー(人を継承)
- 手札が22を超える場合、終了フラグが立つ
- 未実装 引き続けるかの確認においてy/nで答えることができる
コンピュータ(人を継承)
- 手札が17点以上のとき、終了フラグが立つ
- 手札が16点以下のとき、終了フラグは立たない
- 未実装 ゲーム中の手札表示は、一枚目を除いて*で表示される
ゲーム
勝敗判定のみ
- 一人目のプレイヤーの点数が最も高いとき、勝者は一人目のプレイヤーである
- 二人目のプレイヤーの点数が最も高いとき、勝者は二人目のプレイヤーである
- 一人目と二人目の点数が最も高いとき、勝者は両方のプレイヤーである
- どちらも22を越えた手札のとき、引き分けとなる
反省点
実コードについて
ブラックジャックのルールを間違えた
ディーラー対プレイヤーではなく、プレイヤー同士のカードの点数勝負として実装を行ってしまいました。
各カードが数値を複数持てるという無駄な拡張
「Aは1と11のどちらか都合の良い数値を取れる」ルールを実装しました。
その際に何を思ったのか、すべてのカードが複数の値を取れるようにして、計算の際に21以下で最も高い数値を計算するメソッドを作成しました。
そのメソッドを再帰で呼び出すなど、非常にメンテナンスし辛くしてしまったので、まさに無駄な拡張でした。
Hand(手札)クラスは必要だったのか?
内容としてはPersonクラスに統合できるとも考えました。
しかしその場合はPersonクラスが大きくなりすぎると思ったため、どちらが良かったのか未だわからずじまいです。
テストコードについて
テストのためだけにHandsの内部状態を変更するメソッドを作成した
HandsクラスはCardの配列をプライベートで持っています。
しかしテストの際にそのCardsを任意に変更したいと考えたため、そのテストのためだけにメソッドを追加しました。
非常に悪手だとは思っているのですが、他に方法がわからなかったという現状です。
内部状態を保持するクラスのテストの場合、どのようにテストするのがベストなのでしょうか。
ランダムが関わる部分のテストが書けなかった
「デッキがシャッフルされている」部分のテストが書けませんでした。
シャッフル前の状態を覚えておいて、シャッフル後と比較するのがよかったのでしょうか。
標準入出力が関わる部分に対してテストが書けなかった
カードを引き続けるかどうか?の標準入力による判断や、手札の表示などについてテストが書けていません。 大変そうだったので
標準入出力とロジック部分を分離しておけばよかったなと反省しています。
所感
今まで作成してきた物ではどのようにテストを書けばよいのかわからず、全くテストを書いてきませんでしたが、このような題材でテストを書けたのは非常に良い経験になったと思います。
ただ、テストについて調べれば調べるほど「こうしておけばよかった」が沢山出てくるので、圧倒的に経験値が足りないなと実感しました。