0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Swiftでブラックジャックを実装してみる

Last updated at Posted at 2023-06-30

こんないい記事をみたので、Swiftでブラックジャックを実装してみよう。
実装するルールは上記サイトを少し改変する。

  • 初期カードは52枚。1ゲーム中にカードの重複は起きない。
  • ディーラーと任意の数のプレイヤーの対戦。ディーラーは自動、プレイヤーは実行者。
  • 実行開始時、プレイヤーとディーラーはそれぞれ、カードを2枚引く。引いたカードは画面に表示する。ただし、ディーラーの2枚目のカードは分からないようにする
  • その後、先にプレイヤーがカードを引く。プレイヤーが21を超えていたらバーストしてプレイヤーの負け、その時点でゲーム終了
  • プレイヤーは、カードを引くたびに、次のカードを引くか選択できる
  • プレイヤーが引き終えたら、その後ディーラーは、自分の手札が17以上になるまで引き続ける
  • プレイヤーとディーラーが引き終えたら勝負。より21に近い方の勝ち
  • JとQとKは10として扱う
  • Aはとりあえず「1」としてだけ扱う。「11」にはしない
  • ダブルダウンなし、スプリットなし、サレンダーなし、その他特殊そうなルールなし

成果物

依存関係

View → Presenter → Game → Blackjack →Card
image.png

それぞれの責務

概念モジュール(SwiftyBlackjack)

Card
  • 表面を向いているかどうか
  • ランク
  • スート
Blackjack
  • 山札
  • ディーラー
  • プレイヤー → 勝負の結果(Blackjackは複数人で遊べるが、その勝負はディーラー対各プレイヤーで、プレイヤー同士は干渉しない)
  • 山札からドローし手札に加えるといったBlackjackの本質的な操作

実装モジュール

Game

進行状況として

  • 開始前
  • プレイ中 → ゲームの進行状況
  • 勝敗 → 勝負の結果
  • 実際に進行する
GamePresenter
  • Gameの保持しViewと仲介

まずは本質から作成する。

アプリに依存しない最も本質の論理を切り離すためにSwiftyBlackjackという別モジュールとして作成。今回の場合、抽象度を高めるためにSwiftyBlackjackにおいてSwift標準ライブラリ以外のライブラリを使用していない。つまりimport...をしていない

今回の設計の大部分は下記を参考にしている。
詳しくはhttps://www.youtube.com/watch?app=desktop&v=uTaFfUHUmvY

まず、ゲームルール以前にトランプカードを作成しよう。

Card.swift
public struct Card{
    public let face:Face
    public var isFaceUp:Bool
    ...
}

extension Card{
    public mutating func putFaceUp(){ self = faceUp }
    public var faceUp: Card{ .init(face: face, isFaceUp: true) }
    ...
}
...
extension Card{
    public struct Face{
        public let rank:Rank
        public let suit:Suit
        ...
    }

    public enum Rank:String,CaseIterable{
        case ace = "A"
        case two = "2"
        case three = "3"
        ...
    }

    public enum Suit:String, CaseIterable{
        case heart = "❤︎"
        case clover = "♣︎"
        case diamond = "◆"
        case spade = "♠︎"
    }
}

次はblackjackの本質的なオブジェクトやゲーム操作を定義

オブジェクト

  • talon(山札)
  • Dealer
  • Player
  • point(点数)
  • GameResult

操作

  • dealCard
  • dealCardToDealer
  • flipDealersSecondCard

など

Blackjack.swift

public struct Blackjack {
    public private(set) var talon:[Card] //山札
    public private(set) var dealer: Dealer
    public private(set) var players: [Player]
    ...
}

//MARK: input interface

extension Blackjack{
    mutating public func dealCard(toPlayerIndexOf index: Int){...}
    mutating public func dealCardToDealer(isFaceUp: Bool = true){...}
    mutating public func flipDealersSecondCard(){...}
    mutating private func drawFromTalon(isFaceUp: Bool = true)->Card{...}
}

//MARK: Definition of Types. Dealer/Player/GameResult

extension Blackjack{
    public struct Dealer{...}

    public struct Player{...}

    public enum GameResult: String{
        case win = "win"
        case lose = "lose"
        case draw = "draw"
    }
}

//MARK: point

extension Blackjack{
    ///点数はカードの本質ではなくゲームルールなのでCardではなくBlackjackで定義
    static func point(of card:Card)->Int{
        switch card.face.rank{
        case .ace:
            return 1
        ...
        case .nine:
            return 9
        case .ten:
            return 10
        ...
        case .king:
            return 10
        }
    }

}

次にアプリに依存する実装

Game.swift
import SwiftyBlackjack

struct Game{
    private var blackJack:Blackjack

    //MARK: output interface
    public var dealer: Blackjack.Dealer{ blackJack.dealer }
    public var players: [Blackjack.Player]{ blackJack.players }
    public private(set) var gameState: GameState
    public private(set) var message:Message?

    init(numberOfPlayers:Int = 1) {
        self.blackJack = Blackjack(numberOfPlayers: numberOfPlayers)
        gameState = .beforeStart
    }
}
...

Viewには可能な限り論理分岐を持たせたくないので、PresenterがUIに関することはなんでも計算。

GamePresenter.swift
import SwiftyBlackjack

struct GamePresenter{
    public private(set) var game: Game = Game()
}

// MARK: Output interface

extension GamePresenter{
    public var playerCount:Int{ game.players.count }//コンピューテッドプロパティとしてself.gameから求める
    public var playerTurnIndex:Int?{...}
    public var alert:Message?{...}
    public var addPlayerIsEnabled:Bool{...}
    ...

}

// MARK: Input interface

extension GamePresenter{
    public mutating func addPlayer(){...}

    public mutating func removePlayer(){...}

    public mutating func startToPlay(){ game.startToPlay() }
    public mutating func hit(){ game.hit() }
    public mutating func stand(){ game.stand() }
    public mutating func reset(){ self = .init() }
    public mutating func onAlertDismissed(){...}
}


今後改善したい点

ゲームなのでアクションに適切なアニメーションをつけたい

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?