Kotlinでブラックジャック作ってみた

「[プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべし](https://qiita.com/hirossyi73/items/cf8648c31898216312e5)」という記事に感化されて、
Kotlinでブラックジャックを作成してみました。

# 私のスキル
- エンジニア8年目
- Java,PHP:業務で書いている
- Kotlin:実務経験なし,ハンズオンに参加して得た知識のみ

# ソースコード
https://github.com/matyahiko2831/BlackJackKotlin
参考サイトの[開発開始](https://qiita.com/hirossyi73/items/cf8648c31898216312e5#開発開始)以降はなるべく見ずに作成しました。
10時間くらいで作れたかなと思います。。。！

# 開発環境
- Kotlin 1.2.40
- Gradle 4.7
- IntelliJ IDEA

# 実行結果
<img width="607" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/172019/518a65b7-a5bb-caa3-7973-2d5a36918407.png">
↑2018/05/08追記
PLAYERとあなた、DEALERとディーラー　文言統一されてないですねorz

# 作ってみた感想
- 成果物が明確なので「調べながら開発する」という実務に近い営みが出来る。そのため、漫然とチュートリアルやったり参考書読むよりは身になると思いました！
- ブラックジャックという題材はルールがわかりやすく難易度的にちょうどよいと思いました。
- Kotlinがほぼ未経験ということを置いといても、クラス設計が下手くそ。。。
- (本題から外れますが)無からKotlinが動作する環境を整えるということを経験できてよかったです。Gradleの設定やgitignoreがイケてないかもしれませんorz


# ソース抜粋
## メインクラス
```kotlin:Main.kt

package blackjack

import model.*
import exception.*

fun main(args: Array<String>) {

    println("☆★☆☆★☆ブラックジャックへようこそ！☆★☆☆★☆")
    println("ゲームを開始します")

    // デッキ初期化
    var deck = Deck()

    // プレイヤー・ディーラー初期化
    var player = User()
    var dealer = User()

    // プレイヤー 最初のドロー
    player.hand.add(deck.draw(Role.PLAYER))
    player.hand.add(deck.draw(Role.PLAYER))

    // ディーラー 最初のドロー
    dealer.hand.add(deck.draw(Role.DEALER))
    // 2枚目は何を引いたか表示させない
    dealer.hand.add(deck.draw(Role.DEALER,true))

    println("あなたの現在の得点は${player.getScore()}です。")

    var input: String?

    // ユーザーのターン
    while (true) {
        println("カードを引きますか？引く場合はYを、引かない場合はNを入力して下さい")
        input = readLine()

        if("Y" == input || "y" == input) {
            player.hand.add(deck.draw(Role.PLAYER))

            println("あなたの現在の得点は${player.getScore()}です。")

            if(player.isBurst()) {
                println("バーストしました。")
                println("あなたの負けです。")
                println("ブラックジャック終了！また遊んでね★")
                System.exit(0)
            }

        } else if("N" == input || "n" == input) {
            break
        } else {
            println("YかNを入力して下さい。")
        }
    }

    // ディーラーのターン
    println("ディーラーの2枚目のカードは${dealer.hand[1]}でした。")
    println("ディーラーの現在の得点は${dealer.getScore()}です。")

    // 得点が17点以上になるまで繰り返す
    while(dealer.getScore() < 17) {
        dealer.hand.add(deck.draw(Role.DEALER))
    }

    println("あなたの得点は${player.getScore()}です。")
    println("ディーラーの得点は${dealer.getScore()}です。")

    if(dealer.isBurst()
            || player.getScore() > dealer.getScore()) {
        println("あなたの勝ちです。")
    } else if (player.getScore() == dealer.getScore()){
        println("引き分けです。")
    } else {
        println("あなたの負けです。")
    }

    println("ブラックジャック終了！また遊んでね★")
}
```

### 所感
- 「カードを引いてユーザの手札に加える」というユースケースをもっとうまく表現出来たと思う。（Userクラスにdrawメソッドを持たせる？）
- RoleはUserクラスに持たせるべきだったかな。


## カードクラス
```kotlin:Card.kt

package model

/**
 *
 * カードクラス
 * @param suit マーク
 * @param index 表示
 * @param rank ランク
 * @param used 使用済み
 *
 * */
class Card(val suit: Suit, val index: String, val rank: Int, var used: Boolean = false){

    public override fun toString(): String {
        return "${suit}の${index}"
    }
}

```

### 所感
- PHPではおなじみの引数の初期値が設定できるのが良い。
- フィールドもgetterもsetterもいらないんですね。

## デッキクラス
```kotlin:Deck.kt
package model

import java.util.Random
import exception.*

/**
 * デッキクラス
 * */
class Deck {

    /** デッキ残数 */
    private var remaining: Int = 52

    public val cards = arrayOf<Card>(
            Card(Suit.SPADE, "1", 1),
            Card(Suit.SPADE, "2", 2),
            Card(Suit.SPADE, "3", 3),
            Card(Suit.SPADE, "4", 4),
            Card(Suit.SPADE, "5", 5),
            Card(Suit.SPADE, "6", 6),
            Card(Suit.SPADE, "7", 7),
            Card(Suit.SPADE, "8", 8),
            Card(Suit.SPADE, "9", 9),
            Card(Suit.SPADE, "10", 10),
            Card(Suit.SPADE, "J", 10),
            Card(Suit.SPADE, "Q", 10),
            Card(Suit.SPADE, "K", 10),
            Card(Suit.HART, "1", 1),
            Card(Suit.HART, "2", 2),
            Card(Suit.HART, "3", 3),
            Card(Suit.HART, "4", 4),
            Card(Suit.HART, "5", 5),
            Card(Suit.HART, "6", 6),
            Card(Suit.HART, "7", 7),
            Card(Suit.HART, "8", 8),
            Card(Suit.HART, "9", 9),
            Card(Suit.HART, "10", 10),
            Card(Suit.HART, "J", 10),
            Card(Suit.HART, "Q", 10),
            Card(Suit.HART, "K", 10),
            Card(Suit.DIAMOND, "1", 1),
            Card(Suit.DIAMOND, "2", 2),
            Card(Suit.DIAMOND, "3", 3),
            Card(Suit.DIAMOND, "4", 4),
            Card(Suit.DIAMOND, "5", 5),
            Card(Suit.DIAMOND, "6", 6),
            Card(Suit.DIAMOND, "7", 7),
            Card(Suit.DIAMOND, "8", 8),
            Card(Suit.DIAMOND, "9", 9),
            Card(Suit.DIAMOND, "10", 10),
            Card(Suit.DIAMOND, "J", 10),
            Card(Suit.DIAMOND, "Q", 10),
            Card(Suit.DIAMOND, "K", 10),
            Card(Suit.CLUB, "1", 1),
            Card(Suit.CLUB, "2", 2),
            Card(Suit.CLUB, "3", 3),
            Card(Suit.CLUB, "4", 4),
            Card(Suit.CLUB, "5", 5),
            Card(Suit.CLUB, "6", 6),
            Card(Suit.CLUB, "7", 7),
            Card(Suit.CLUB, "8", 8),
            Card(Suit.CLUB, "9", 9),
            Card(Suit.CLUB, "10", 10),
            Card(Suit.CLUB, "J", 10),
            Card(Suit.CLUB, "Q", 10),
            Card(Suit.CLUB, "K", 10)
    )

    /**
     * カードドロー
     * デッキが0枚になったらException
     * @return Card
     * @throws DeckOutException
     * */
    public fun draw(role: Role, hide: Boolean = false): Card {

        while (true) {
            val card = cards[Random().nextInt(cards.size)]

            if(!card.used) {
                card.used = true;
                remaining--

                if(hide) {
                    println("${role}の引いたカードは分かりません。")
                } else {
                    println("${role}の引いたカードは${card}です。")
                }
                return card
            }

            if (remaining == 0) {
                // いらないけど例外の勉強のために
                throw DeckOutException("デッキ切れ")
            }
        }
    }

}
```

### 所感
- カードリストは絶対もっと良い実装があったと思う。


ご意見お待ちしておりますm(_ _)m