I am working on a two-player strategy board game with the option to play against CPU and I am using the GKMinmaxStrategist to give some brains to the CPU player.Just for background, there are two "factions" in the game (King, Enemy) and the one playing King always starts.
Now, I have managed to implement the strategist and it works if I play King against the CPU and make the first move. However, when I play as Enemy and the CPU has to make the first move (playing as King), it doesn't work strangely enough. It appears that, after invoking the strategist's bestMove method, it doesn't call any of the underlying simulation methods, like isWin, apply, score, etc.
I have tried changing the rules so that the Enemy always starts, but again it didn't work, whereas it did spit out moves for the Enemy when I played as King and made the first move (as mentioned above).
Also, instead of using the strategist, I have simply selected moves at random (of all possible moves), just to see whether the turn-based logic works, and it did. For both factions it moves when and how I would expect it.
So this first move, no matter for which faction seems to be the issue and I can't work out why that would be the case. Has anyone experienced a similar issue or does anyone have any bright ideas as to why this might be the case?
Any help would be much appreciated.
This is the extension of the game model to include the strategist as well as the GameVC, which manages the gameplay.
extension Board: GKGameModel {
var activePlayer: GKGameModelPlayer? {
return currentPlayer
}
var players: [GKGameModelPlayer]? {
return Player.allPlayers
}
func copy(with zone: NSZone? = nil) -> Any {
let copy = Board()
copy.setGameModel(self)
return copy
}
func setGameModel(_ gameModel: GKGameModel) {
if let board = gameModel as? Board {
tokens = board.tokens
}
}
func isWin(for player: GKGameModelPlayer) -> Bool {
guard let player = player as? Player else {
return false
}
if let winner = isWinner() {
return player.player == winner
} else {
return false
}
}
func gameModelUpdates(for player: GKGameModelPlayer) -> [GKGameModelUpdate]? {
guard let player = player as? Player else {
return nil
}
if isWin(for: player) {
return nil
}
return allPossibleMoves(faction: player.player)
}
func apply(_ gameModelUpdate: GKGameModelUpdate) {
guard let move = gameModelUpdate as? Move else {
return
}
performMove(move: move)
currentPlayer = currentPlayer.opponent
}
func score(for player: GKGameModelPlayer) -> Int {
guard let player = player as? Player else {
return Score.none.rawValue
}
if isWin(for: player) {
return Score.win.rawValue
} else {
return Score.none.rawValue
}
}
}```
```class GameViewController: UIViewController, BoardDelegate {
var scene: GameScene!
var board: Board!
var strategist: GKMinmaxStrategist!
override func loadView() {
self.view = SKView(frame: UIScreen.main.bounds)
}
override func viewDidLoad() {
super.viewDidLoad()
let skView = view as! SKView
skView.isMultipleTouchEnabled = false
skView.isUserInteractionEnabled = false
scene = GameScene(size: skView.bounds.size)
scene.scaleMode = .aspectFill
board = Board()
board.delegate = self
scene.board = board
scene.handleUserMove = handleUserMove
scene.fillBoardWithSprites()
strategist = GKMinmaxStrategist()
strategist.maxLookAheadDepth = 1
strategist.randomSource = GKARC4RandomSource()
strategist.gameModel = board
skView.presentScene(scene)
initiateMove()
}
//Check if user`s turn
func checkUserTurn() -> Bool {
return board.userPlayer.player == board.currentPlayer.player
}
//Change turn of players between user and CPU
func changeTurn() {
board.currentPlayer = board.currentPlayer.opponent
}
//Find best move for CPU using GKMinmaxStrategist
func chooseCpuMove() -> Move? {
if let move = strategist.bestMove(for: board.currentPlayer) as? Move {
print("found move")
return move
}
return nil
}
//Perform CPU move
func handleCpuMove(move: Move) {
scene.animateMove(move: move) {}
board.performMove(move: move)
board.moved()
}
//Check whose turn it is and either turn on user interaction or start CPU move
func initiateMove() {
print("initiating")
self.view.isUserInteractionEnabled = checkUserTurn()
if checkUserTurn() {
//Wait for user to move
} else {
print("checking cpuMove")
if let cpuMove = chooseCpuMove() {
handleCpuMove(move: cpuMove)
}
}
}
func handleUserMove(move: Move, bool: Bool) {
if board.isMoveAllowed(move: move) {
scene.animateMove(move: move) {}
board.performMove(move: move)
board.moved()
}
}
func currentPlayerDidMove(board: Board) {
changeTurn()
if board.isWinner() != nil {
board.endGame()
} else {
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.initiateMove()
}
}
}
func gameDidEnd(board: Board) {
self.view.isUserInteractionEnabled = false
UserDefaults.standard.set(board.currentPlayer.player.name, forKey: "winner")
AppDelegate.shared.rootViewController.gameOverScreen()
}
}```