Swift でトランプを実装しようとして、勉強になってる話

まだ全然実装できていないけど、とても勉強になっているので途中経過をブログに書いてみる。

github.com

なぜやっているのか

トランプくらい簡単に実装できる・・・そう思っていた時期が(ry

環境

この環境で勉強を始めたのが間違いだったかもしれない。

やりたいこと

  • 汎用的にトランプのゲームを作れるようなライブラリ設計にする
  • ポーカー、七並べ、大富豪など、メジャーなゲームを実装する

設計など

  • Card が Rank ( 1 〜 13 ) と Suit (スペード、ハート、クラブ、ダイヤ) を持つ
  • Card の強弱をゲームによって変える
    • Generics で変更できるようにした
  • まだ Jocker は未実装

Swift で実装方法が分からなかった箇所など

stringLiteralConvertible で特定文字だけ許容したい

let card: Card<DefaultComparing> = "13S"

これで「スペード13」が生成したかった。結局、できなかったので、 init? を使った。

https://github.com/sinsoku/PlayingCard/blob/908faee6fe4fe1d66e028ee481b6ad07c06f4322/Sources/PlayingCard/Card.swift#L14-L33

init?(_ value: String) {
  var characters = value.characters
  let suit = characters.popLast()
  let rank = Int(String(characters))

  guard let rankInt = rank where 1...13 ~= rankInt else {
    return nil
  }

  guard let suitStr = suit where ["S", "H", "C", "D"].contains(suitStr) else {
    return nil
  }

  switch suitStr {
    case "S": self = Card<T>(rank: Rank(rawValue: rankInt)!, suit: .Spade)
    case "H": self = Card<T>(rank: Rank(rawValue: rankInt)!, suit: .Heart)
    case "C": self = Card<T>(rank: Rank(rawValue: rankInt)!, suit: .Club)
    case "D": self = Card<T>(rank: Rank(rawValue: rankInt)!, suit: .Diamond)
    default: return nil
  }
}

無理矢理感がある・・・。
もうちょい綺麗になりそうだけど、よく分からんかったので他の実装を進めている。

戻り値を Set, Array で切り替えたい

https://github.com/sinsoku/PlayingCard/blob/908faee6fe4fe1d66e028ee481b6ad07c06f4322/Sources/PlayingCard/Util.swift

class Util {
    static func factory<C: CardInitializeable>(_ values: String...) -> Set<C> {
      let cards = values.map { C($0)! }
      return Set(cards)
    }
}

戻り値も Generics でうまく返したかったけど、よく分からなかった。

let arrCards: Array<Card<DefaultComparing>> = Util.factory("1S", "2S", "3S", "4S", "5S")
let setCards: Set<Card<DefaultComparing>> = Util.factory("1S", "2S", "3S", "4S", "5S")

本当は ArrayLiteral で定義できると綺麗なんだけど、これ init? だから出来ないんですよね・・・。

let setCards: Set<Card<DefaultComparing>> = ["1S", "2S", "3S", "4S", "5S"]