さらに続く。
ボードの実装(続き)
合法手の実行
// 続き public func play(row: Int, _ col: Int) -> Board { assert( self.isPlayable(row, col), "not playable. [row: \(row), col: \(col)]") let newBoard = Board(self) newBoard.putPiece(row, col) newBoard.changeTurn() newBoard.addMove() return newBoard } public func change(row: Int, _ col: Int, check: Bool = true) -> Board { if check { assert( self.isChangeable(row, col), "not changeable. [row: \(row), col: \(col)]") } let newBoard = Board(self) newBoard.putPiece(row, col) newBoard.changeToken() newBoard.changeTurn() newBoard.setPrevious(self) newBoard.addMove() return newBoard } public func pass() -> Board { assert( self.mustPass, "cannot pass.") let newBoard = Board(self) newBoard.changeTurn() newBoard.addMove() return newBoard } // 続く
Rubyで書いたときと同じく、コピーで新しいオブジェクトを生成し、そのオブジェクトの状態を変化させて返してる。
ここでやはり迷ったのが、Board#pass()
を計算型プロパティにするか、メソッドにするかということ。
どちらでもいいのだけど、振る舞いをしているという要素が強そうなので、ここではメソッドにしてみた。
終了判定
// 続き public var isGameEnd: Bool { if self.count(.EMPTY) == 0 { return true } if self.mustPass { let passed = self.pass() if passed.mustPass { return true } } return false } public func win(color: Color) -> Bool { assert( (color == .BLACK) || (color == .WHITE), "invalid color. [color: \(color)]") if !self.isGameEnd { return false } if color == .BLACK { return (self.count(.BLACK) > self.count(.WHITE)) || ((self.count(.BLACK) == self.count(.WHITE)) && (self.token == .BLACK)) } else { return (self.count(.BLACK) < self.count(.WHITE)) || ((self.count(.BLACK) == self.count(.WHITE)) && (self.token == .WHITE)) } } // 続く
このBoard#isGameEnd
も、やはり計算型プロパティにもメソッドにも出来るパターン。
さすがにうんざりしてくる・・・
これは振る舞いというよりかは状態を示すので、計算型プロパティにしてみた。
ここまでが外部に公開するインタフェースの実装(ただし、一部のプロパティを除く)で、あとはそれらを実行するためのprivateメソッドのみ。
千日手チェックのためのメソッド
まずは、千日手をチェックするためのメソッドから。
(といっても、これも計算型プロパティとしているけど)
// 続き private var hasSameSituationBefore: Bool { var ancestorOpt = self.previous while let ancestor = ancestorOpt { if self.isSameSituation(ancestor) { return true } else { ancestorOpt = ancestor.previous } } return false } // 続く
これも、計算型プロパティにもメソッドにも出来るヤツ。
ホント、いい加減にしてくれ・・・
ここでは、nilチェックを行うためにオプショナル束縛構文を使ってる。
ただ、ここでnilが入っているというのは異常な状態ではなく正常な状態の一つなので、ちょっとどうかなと思ってしまう。
というのも、オプショナル型から通常の型にアンラップする必要があるので、オプショナル型を入れる変数を別に用意してやる必要があるから。
ancestorOpt
というのがそれだけど、見るからにダサい名前。
ハンガリアン記法のような感じで、すごく嫌。
この辺りも(例外周りの扱いと併せて)微妙な言語設計と言わざるをえない感じ。
今日はここまで!