あともうちょい。
ボードの実装(続き)
走査メソッド
まずは走査メソッドから。
// 続き private func traverseFrom(row: Int, _ col: Int, to direction: (Int, Int), _ block: (Int, Int, Int, Color) -> Bool) { var traverseRow = row + direction.0 var traverseCol = col + direction.1 var traverseColor = self.board[traverseRow][traverseCol] var stepCount = 1 while (traverseColor != .WALL) && (traverseColor != .EMPTY) { let success = block(stepCount, traverseRow, traverseCol, traverseColor) if !success { break } traverseRow += direction.0 traverseCol += direction.1 traverseColor = self.board[traverseRow][traverseCol] stepCount += 1 } } // 続く
引数の最後にblock: (Int, Int, Int, Color) -> Bool
というクロージャをとるようにしている。
このおかげで、Rubyのときと同じように使える。
ただし、Rubyの場合、ブロックの中で
- returnすると、ブロックが書かれているメソッドから抜ける
- breakすると、ブロックの呼び出しを行っているメソッドから抜ける
- nextすると、ブロックの呼び出しから抜ける
と、絶妙なコントロールが効いたりするんだけど、Swiftだとそれは出来ないので、処理を継続するかどうかをブロックの戻り値として受け取り、フローをコントロールするようにしている。
なお、direction.0
やdirection.1
というのは、それぞれタプルの1つ目の要素、タプルの2つ目の要素ということ。
合法手を実行するためのメソッド
// 続き private func putPiece(row: Int, _ col: Int) { self.board[row][col] = self.turn for direction in Board.Direction { if self.hasColorFrom(row, col, to: direction) { self.traverseFrom(row, col, to: direction) { stepCount, traverseRow, traverseCol, traverseColor in if traverseColor == self.turn { return false } else { self.board[traverseRow][traverseCol].changeWithColor(self.turn) return true } } } } } private func changeTurn() { self.turn = self.opponent } private func changeToken() { self.token.changeWithColor(self.opponent) } private func setPrevious(other: Board) { self.previous = other } private func addMove() { self.move += 1 } // 続く
ここで先ほどの走査メソッドを使っている。
クロージャをメソッドの最後の引数としてとる場合、そのクロージャの定義を引数リストの外側に書けるという機能(接尾クロージャ)があるので、この辺りはRubyとかなり近い書き方が出来る感じ。
一つ注目したいのが、Board.Color#changeWithColor(_: Color)
の呼び出し。
これは列挙型Board.Color
を定義したときに一緒に定義したメソッドで、列挙型のオブジェクト自体を変化させるように定義してある。(定義は変種オセロをSwiftに移植してみた。(その1) - いものやま。を参照)
これにより、引数で黒を指定すれば、灰色は黒に、白は灰色に変化するし、逆に白を指定すれば、灰色は白に、黒は灰色に変化する。
トークンの色を変えるときにもこれは使えて、灰色の石を自分の色に変えた場合、トークンは相手の色側に寄るので、引数として相手の色を指定している。
合法手のチェックをするためのメソッド
// 続き private func hasColorFrom(row: Int, _ col: Int) -> Bool { for direction in Board.Direction { if self.hasColorFrom(row, col, to: direction) { return true } } return false } private func hasColorFrom(row: Int, _ col: Int, to direction: (Int, Int)) -> Bool { var found = false self.traverseFrom(row, col, to: direction) { stepCount, traverseRow, traverseCol, traverseColor in if traverseColor == self.turn { if stepCount == 1 { found = false } else { found = true } return false } else { return true } } return found } }
ここでも先ほどの走査メソッドを使っている。
Rubyのときとちょっと違うのは、メソッドの名前。
Rubyの場合、外部引数名というものはないので、Board#has_color_from_to
というちょっと無理やりな名前のつけ方になったけど、Swiftの場合、Objective-Cと同様に外部引数名をつけることが出来るので、Board#hasColorFrom(_: Int, _: Int, to: (Int, Int)
という、より自然な命名になってる。
これ自体はいい感じ。
ただ、ちょっと文句をつけさせてもらうなら、この外部引数名の仕様をどうして関数/イニシャライザ/メソッドでそれぞれ違うようにしたのか・・・
分かりにくくてしょうがない。
ここはObjective-Cの都合に引っ張られすぎで、言語を新しく設計したのなら、その言語に都合のいい設計にして欲しかった。
何はともあれ、これでボードの実装は完了。
今日はここまで!