ボードの表示まで出来るようになった。
次は、人同士が対戦できるようにする。
Playerプロトコル
Rubyの場合、ダック・タイピングが出来るので、プレイヤーに共通のインタフェースをコード上に定義する必要はないのだけど、Swiftはそうではないので、プレイヤーに共通のインタフェースをちゃんと定義していく。
ということで、Playerプロトコルを以下のように定義。
/* Player.swift */ public protocol Player { func select(board: Board) -> Board.Action? }
戻り値の型がオプショナル型となっているのは、途中でやめたい場合のアクションの指定が面倒だったから。
どういうことかというと、もしちゃんとやるなら、列挙型をBoard.Action
とは別に定義して、そこに.Exit
という列挙子を追加し、さらにBoard.Action
との変換を用意したりすることになるのだけど、それはとても面倒。
なので、nilが返ってきた場合にはゲームを終了するということにした。
ゲーム進行を管理するクラスの実装
プロトコルを定義したので、先にゲーム進行を管理するクラスの実装から。
/* Game.swift */ public class Game { public private(set) var blackPlayer: Player public private(set) var whitePlayer: Player public init(blackPlayer: Player, whitePlayer: Player) { self.blackPlayer = blackPlayer self.whitePlayer = whitePlayer } public func start() { var board = Board() var done = false while true { BoardViewer.view(board) if board.isGameEnd { done = true break } if board.mustPass { println("pass!") board = board.pass() } else { let player: Player if board.turn == .BLACK { player = self.blackPlayer } else { player = self.whitePlayer } let actionOpt = player.select(board) if let action = actionOpt { switch action { case let .Play(row, col): board = board.play(row, col) case let .Change(row, col): board = board.change(row, col) default: break } } else { break } } } println("----------") println("black: \(board.count(.BLACK)), white: \(board.count(.WHITE))") if done { if board.win(.BLACK) { println("\(BoardViewer.mark(.BLACK)) win.") } else if board.win(.WHITE) { println("\(BoardViewer.mark(.WHITE)) win.") } else { println("draw.") } } else { println("exit.") } } }
Javaのinterfaceと同じく、Swiftのprotocolも型として使用することが出来るので、プロパティのblackPlayer
とwhitePlayer
はPlayer
という型になっている。
そして、Player
というプロトコルに準拠したクラスはPlayer#select(_: Board)
というメソッドを持っていることが約束されているので、それを呼び出して、ゲームを進めていく。
なお、ここで変種オセロをSwiftに移植してみた。(その2) - いものやま。で説明した、パラメータを持った列挙型のパターンマッチを行っている。
今日はここまで!