为什么我的极小化极大算法没有撤销每一步棋?

我正在为自己设计的一个四人棋盘游戏创建AI。

关于棋盘游戏的详细信息:

四名玩家轮流移动他们的彩色棋子,同时向四个基本方向之一移动。棋子可以移出棋盘。游戏开始时,每个玩家有5条命。每移出一枚棋子,玩家就会失去1条命。新棋子会在游戏过程中按一定规则生成。

我查看了如何实现极小化极大算法,并找到了这个。我阅读了相关内容,认为自己已经理解了一切,于是尝试将第1.5节中的Java代码翻译成Swift。

这是我的思考过程:

  • 由于我的游戏有4个玩家,我会将其他所有玩家视为最小化玩家。
  • 在Java代码中,有一行代码用于撤销移动。由于我的游戏状态在每一步棋中可能发生巨大变化,我会将所有游戏状态存储在一个数组中,当需要撤销时,我可以直接在数组上调用dropLast
  • 由于我的游戏中的移动由Direction枚举表示,我将返回一个(Int, Direction)元组,而不是像Java代码中的整数数组。
  • game是一个计算属性,只返回gameStates.last!
  • 每次我调用game上的moveUp/Down/Left/Right方法时,game.currentPlayer都会改变,所以我不需要编写额外的代码来决定下一个玩家是谁。
  • 在最后一行,我需要返回(bestScore, bestDirection),但我意识到有时bestDirection没有被赋值。因此,我将bestDirection设为可选类型。如果在返回语句时它未被赋值,我将返回一个任意方向。

这是我的尝试:

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 {        // 这是一个对我的启发式评估函数的调用        bestScore = evaluateHeuristics()    } else {        // 如果玩家在棋盘上没有棋子,只向上移动,因为向任何方向移动都不会改变任何事情        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 类似于原始Java代码中的 mySeed            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)}

当我用depth为4测试这个AI时,它似乎要么做一些愚蠢的移动,比如将棋子移出棋盘,要么只向一个方向移动棋子。

我还注意到,当递归调用返回时,gameStates的长度大约为90。正常情况下它应该是1,对吗?因为AI尝试的所有移动在递归调用返回之前都应该被撤销,gameStates只会包含初始状态。

我做错了什么?


回答:

dropLast()返回一个包含数组中除最后一个元素之外的所有元素的数组切片。它不会修改原始数组。请使用removeLast()

编辑

你真正想要的是一个堆栈数据结构。这里有一个。

public struct Stack<Element>{    fileprivate var elements: [Element] = []    public init() {}    ///    将一个元素推到堆栈顶部    ///    /// - parameter newElement: 要推送的元素    public mutating func push(_ newElement: Element)    {        elements.append(newElement)    }    ///    从堆栈顶部弹出一个元素    ///    ///    - returns: 顶部元素或如果堆栈为空则返回nil。    public mutating func pop() -> Element?    {        let ret = elements.last        if ret != nil        {            elements.removeLast()        }        return ret    }    ///  堆栈的顶部元素。如果堆栈为空则为nil    public var top: Element?    {        return elements.last    }    /// 堆栈中的项目数量    public var count: Int    {        return elements.count    }    /// 如果堆栈为空则为真    public var isEmpty: Bool    {        return elements.isEmpty    }}

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注