我正在为我制作的原创4人桌面游戏创建AI。
有关棋盘游戏的详情:
4名玩家轮流在四个基本方向中的一个方向同时移动彩色棋子。碎片可以从板上移开。每个玩家在开始时都有5条生命。对于从棋盘上移走的每件棋子,玩家将失去1点生命。新作品将在整个游戏中确定性地产生。
我查找了如何进行minimax算法并找到了this。我仔细阅读并认为我理解了所有内容,因此我尝试将第1.5节中的Java代码翻译为Swift。
这是我的思考过程:
dropLast
。 / LI>
Direction
枚举,我将返回(Int, Direction)
元组,而不是像Java代码那样的int数组。game
是一个只返回gameStates.last!
game.currentPlayer
moveUp/Down/Left/Right
方法之一时,game
都会更改,因此我不需要编写额外的代码来决定谁是下一位球员。(bestScore, bestDirection)
,但我意识到有时bestDirection
未被分配。因此,我将bestDirection
作为可选项。如果没有在return语句中分配,我只会返回一个任意方向。这是我的尝试:
private func minimax(depth: Int, color: Color) -> (score: Int, direction: Direction) {
var bestScore = color == myColor ? Int.min : Int.max
var currentScore: Int
var bestDirection: Direction?
if game.players.filter({$0.lives > 0}).count < 2 || depth == 0 {
// This is a call to my heuristic evaluation function
bestScore = evaluateHeuristics()
} else {
// if the player has no pieces on the board, just move up since moving in any direction won't change anything
for move in (game.board.indicesOf(color: color).count == 0 ? [Direction.up] : [Direction.up, .down, .left, .right]) {
let gameCopy = game.createCopy()
switch move {
case .up: gameCopy.moveUp()
case .down: gameCopy.moveDown()
case .left: gameCopy.moveLeft()
case .right: gameCopy.moveRight()
}
gameStates.append(gameCopy)
// myColor is like mySeed in the original Java code
if color == myColor {
currentScore = minimax(depth: depth - 1, color: game.currentPlayer.color).score
if currentScore > bestScore {
bestScore = currentScore
bestDirection = move
}
} else {
currentScore = minimax(depth: depth - 1, color: game.currentPlayer.color).score
if currentScore < bestScore {
bestScore = currentScore
bestDirection = move
}
}
_ = gameStates.dropLast()
}
}
return (bestScore, bestDirection ?? .left)
}
当我用4 depth
测试这个AI时,它似乎要么做愚蠢的动作,比如将棋子从棋盘上移开,要么只朝一个方向移动棋子。
我还注意到,当递归调用返回时,gameStates
的长度约为90。通常它应该是1对吗?因为递归调用返回时AI尝试的所有移动都应该被撤消,gameStates
只包含初始状态。
我做错了什么?
答案 0 :(得分:1)
dropLast()返回一个数组切片,其中包含除数组最后一个元素之外的所有元素。它不会修改原始数组。使用removeLast()
修改
您真正想要的是堆栈数据结构。这是一个。
public struct Stack<Element>
{
fileprivate var elements: [Element] = []
public init() {}
/// Push an element onto the top of the stack
///
/// - parameter newElement: The element to push
public mutating func push(_ newElement: Element)
{
elements.append(newElement)
}
/// Pops the top element off the stack
///
/// - returns: The top element or nil if the stack is empty.
public mutating func pop() -> Element?
{
let ret = elements.last
if ret != nil
{
elements.removeLast()
}
return ret
}
/// The top element of the stack. Will be nil if the stack is empty
public var top: Element?
{
return elements.last
}
/// Number of items in the stack
public var count: Int
{
return elements.count
}
/// True if the stack is empty
public var isEmpty: Bool
{
return elements.isEmpty
}
}