プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべし」という記事に感化されて、
Kotlinでブラックジャックを作成してみました。

私のスキル

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

ソースコード

https://github.com/matyahiko2831/BlackJackKotlin
参考サイトの開発開始以降はなるべく見ずに作成しました。
10時間くらいで作れたかなと思います。。。!

開発環境

  • Kotlin 1.2.40
  • Gradle 4.7
  • IntelliJ IDEA

実行結果

image.png
↑2018/05/08追記
PLAYERとあなた、DEALERとディーラー 文言統一されてないですねorz

作ってみた感想

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

ソース抜粋

メインクラス

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クラスに持たせるべきだったかな。

カードクラス

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もいらないんですね。

デッキクラス

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

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.