前回はターンコントローラを実装して、AI相手にもプレイが出来るようにした。
次は各ボタンの機能を対応していく。
まずはPassボタンの対応から。
人間プレイヤーの修正
Passボタンを使うのは人なので、人間プレヤーの修正をしていく。
具体的には、
- HumanクラスをButtonNodeObserverプロトコルに準拠させる。
- Humanクラスの入力元として、ボードの他にPassボタンを追加する。
といった感じ。
それと、追加されたコードにあわせて、若干リファクタリングをしたりとか。
修正後のコードは以下。
//============================== // YWF //------------------------------ // Human.swift //============================== import Foundation public class Human: Player, BoardNodeObserver, ButtonNodeObserver { private var boardNode: BoardNode private var passButton: ButtonNode private var selectedAction: Board.Action! private let queue: NSOperationQueue private var semaphore: NSOperation! public init(boardNode: BoardNode, passButton: ButtonNode) { self.boardNode = boardNode self.passButton = passButton self.selectedAction = nil self.queue = NSOperationQueue() self.semaphore = nil } public func select(board: Board) -> Board.Action? { if board.mustPass { self.passButton.addObserver(self) self.passButton.enable() } else { self.boardNode.addObserver(self) } self.waitForInput() if board.mustPass { self.passButton.disable() self.passButton.removeObserver(self) } else { self.boardNode.removeObserver(self) } return self.selectedAction } public func boardNodeActionIsFinished(node: BoardNode) { // do nothing } public func boardNodeSquareIsSelected(node: BoardNode, _ square: SquareNode) { let row = square.row let col = square.col if node.board.isPlayable(row, col) { self.selectedAction = Board.Action.Play(row, col) } else if node.board.isChangeable(row, col) { self.selectedAction = Board.Action.Change(row, col) } self.notifyInput() } public func buttonIsSelected(button: ButtonNode) { switch button.type { case .Pass: self.selectedAction = Board.Action.Pass self.notifyInput() default: break } } private func waitForInput() { self.semaphore = NSBlockOperation { // do nothing } self.semaphore.waitUntilFinished() self.semaphore = nil } private func notifyInput() { self.queue.cancelAllOperations() self.queue.addOperation(self.semaphore) } }
入力待ちするための仕組みなどは、変種オセロのUIを作ってみた。(その15) - いものやま。を参照。
今回の主な修正は、Human#buttonIsSelected(_: ButtonNode)。
ここで、Passボタンが押されたという通知が来た場合、選択されたアクションとして.Passを指定し、入力があったことを入力待ちしているスレッドに通知している。
ターンコントローラの修正
人間プレイヤーの修正に伴って、ターンコントローラも修正を行う。
//============================== // YWF //------------------------------ // TurnController.swift //============================== import Foundation public class TurnController: BoardNodeObserver { // 省略 private func requestActionFor(node: BoardNode) { node.lightUpEnableSquares() let board = node.board let action: Board.Action switch board.turn { case .Bad: action = self.badPlayer.select(board)! case .Good: action = self.goodPlayer.select(board)! default: action = .Pass } node.lighDown() switch action { case let .Play(row, col): node.lightUpsquare(row, col) node.play(row, col) case let .Change(row, col): node.lightUpsquare(row, col) node.change(row, col) case .Pass: node.pass() } } }
違いは、パスをしなければならないときに、以前はプレイヤーに手を選択させることなく(=Player#select(_: Board)を呼び出すことなく)無条件でアクションを.Passとしていたけど、それを止め、パスしなければならないときにもプレイヤーに手の選択を委譲しているところ。
これにより、Passボタンを押してパスを行うということが可能になっている。
なお、これに伴って、AIのコードもちょっと修正が必要。
具体的には、パスしな手がないときにはアクションとして.Passを返すようにする必要がある。
ただ、修正は簡単なので、ここでは省略。
動作確認
いつものように、コントローラを書き換えて動作確認を行う。
といっても、変わるのはHumanクラスのオブジェクトの生成の引数を変えるところだけ。
なので、省略。
今日はここまで!