GKMinmaxStrategy не возвращает никаких ходов

У меня есть следующий код в моем main.swift:

let strategist = GKMinmaxStrategist()
strategist.gameModel = position
strategist.maxLookAheadDepth = 1
strategist.randomSource = nil

let move = strategist.bestMoveForActivePlayer()

... где position является экземпляром моего подкласса GKGameModel position. После запуска этого кода move - nil. bestMoveForPlayer(position.activePlayer!) также приводит к nil (но position.activePlayer! приводит к объекту Player).

Однако

let moves = position.gameModelUpdatesForPlayer(position.activePlayer!)!

приводит к непустому массиву возможных ходов. Из документации Apple (около bestMoveForPlayer(_:)):

Возвращает ноль, если игрок недействителен, игрок не является частью игровой модели, или у игрока нет доступных доступных ходов.

Насколько я знаю, все это не так, но функция все еще возвращает nil. Что может здесь происходить?

Если это может помочь, вот моя реализация протокола GKGameModel:

var players: [GKGameModelPlayer]? = [Player.whitePlayer, Player.blackPlayer]
var activePlayer: GKGameModelPlayer? {
    return playerToMove
}

func setGameModel(gameModel: GKGameModel) {
    let position = gameModel as! Position
    pieces = position.pieces
    ply = position.ply
    reloadLegalMoves()
}

func gameModelUpdatesForPlayer(thePlayer: GKGameModelPlayer) -> [GKGameModelUpdate]? {
    let player = thePlayer as! Player
    let moves = legalMoves(ofPlayer: player)
    return moves.count > 0 ? moves : nil
}

func applyGameModelUpdate(gameModelUpdate: GKGameModelUpdate) {
    let move = gameModelUpdate as! Move
    playMove(move)
}

func unapplyGameModelUpdate(gameModelUpdate: GKGameModelUpdate) {
    let move = gameModelUpdate as! Move
    undoMove(move)
}

func scoreForPlayer(thePlayer: GKGameModelPlayer) -> Int {
    let player = thePlayer as! Player
    var score = 0
    for (_, piece) in pieces {
        score += piece.player == player ? 1 : -1
    }
    return score
}

func isLossForPlayer(thePlayer: GKGameModelPlayer) -> Bool {
    let player = thePlayer as! Player
    return legalMoves(ofPlayer: player).count == 0
}

func isWinForPlayer(thePlayer: GKGameModelPlayer) -> Bool {
    let player = thePlayer as! Player
    return isLossForPlayer(player.opponent)
}

func copyWithZone(zone: NSZone) -> AnyObject {
    let copy = Position(withPieces: pieces.map({ $0.1 }), playerToMove: playerToMove)
    copy.setGameModel(self)
    return copy
}

Если есть какой-либо другой код, который я должен показать, дайте мне знать.

Ответ 1

Вам нужно изменить activePlayer после применения или отмены перемещения. В вашем случае это будет playerToMove.

Игрок, которому необходимо выполнить обновление игровой модели. GKMinmaxStrategy предполагает, что следующий вызов метода applyGameModelUpdate: будет выполнять движение от имени этого игрока.

и, конечно же:

Функция applyGameModelUpdate Применяет GKGameModelUpdate к игровой модели, что потенциально может привести к появлению нового activePlayer. GKMinmaxStrategy вызовет этот метод на копии основной игровой модели, чтобы рассказать о возможных будущих движениях и их эффектах. Предполагается, что вызов этого метода выполняет движение от имени игрока, идентифицированного свойством activePlayer.

func applyGameModelUpdate(gameModelUpdate: GKGameModelUpdate) {
    let move = gameModelUpdate as! Move
    playMove(move)

    //Here change the current Player
    let player = playerToMove as! Player
    playerToMove = player.opponent
}

То же самое касается реализации unapplyGameModelUpdate.

Также обратите особое внимание на реализацию setGameModel, поскольку она должна копировать данные Все в вашей модели. Это включает activePlayer

Устанавливает данные другой игровой модели. Все данные должны быть скопированы и не должны содержать никаких указателей на скопированное состояние игры. Это используется GKMinmaxStrategy для обработки перестановок игры без необходимости применять потенциально разрушительные обновления к основной игровой модели.

Ответ 2

У меня была та же проблема. Оказывается, .activePlayer должен возвращать один из экземпляров, возвращаемых .players. Этого недостаточно, чтобы вернуть новый экземпляр с совпадением .playerId.