【问题标题】:Using minimax search for card games with imperfect information使用极小极大搜索不完全信息的纸牌游戏
【发布时间】:2012-09-30 23:46:00
【问题描述】:

我想使用极小极大搜索(带有 alpha-beta 修剪),或者更确切地说是负极大搜索,让计算机程序玩纸牌游戏。

纸牌游戏实际上由 4 名玩家组成。因此,为了能够使用极小极大等,我将游戏简化为“我”对抗“他人”。在每一次“移动”之后,你都可以从游戏本身客观地读出当前状态的评价。当所有 4 名玩家都放置了卡片时,最高的玩家将赢得所有玩家 - 卡片的价值也很重要。

由于您不知道其他 3 名玩家之间的卡牌分布究竟如何,我认为您必须使用不属于您的卡牌模拟所有可能的分布(“世界”)。你有 12 张牌,其他 3 名玩家总共有 36 张牌。

所以我的方法是这个算法,其中player 是一个介于 1 和 3 之间的数字,表示程序可能需要为其寻找动作的三个计算机玩家。而-player代表对手,也就是其他三名选手一起。

private Card computerPickCard(GameState state, ArrayList<Card> cards) {
    int bestScore = Integer.MIN_VALUE;
    Card bestMove = null;
    int nCards = cards.size();
    for (int i = 0; i < nCards; i++) {
        if (state.moveIsLegal(cards.get(i))) { // if you are allowed to place this card
            int score;
            GameState futureState = state.testMove(cards.get(i)); // a move is the placing of a card (which returns a new game state)
            score = negamaxSearch(-state.getPlayersTurn(), futureState, 1, Integer.MIN_VALUE, Integer.MAX_VALUE);
            if (score > bestScore) {
                bestScore = score;
                bestMove = cards.get(i);
            }
        }
    }
    // now bestMove is the card to place
}

private int negamaxSearch(int player, GameState state, int depthLeft, int alpha, int beta) {
    ArrayList<Card> cards;
    if (player >= 1 && player <= 3) {
        cards = state.getCards(player);
    }
    else {
        if (player == -1) {
            cards = state.getCards(0);
            cards.addAll(state.getCards(2));
            cards.addAll(state.getCards(3));
        }
        else if (player == -2) {
            cards = state.getCards(0);
            cards.addAll(state.getCards(1));
            cards.addAll(state.getCards(3));
        }
        else {
            cards = state.getCards(0);
            cards.addAll(state.getCards(1));
            cards.addAll(state.getCards(2));
        }
    }
    if (depthLeft <= 0 || state.isEnd()) { // end of recursion as the game is finished or max depth is reached
        if (player >= 1 && player <= 3) {
            return state.getCurrentPoints(player); // player's points as a positive value (for self)
        }
        else {
            return -state.getCurrentPoints(-player); // player's points as a negative value (for others)
        }
    }
    else {
        int score;
        int nCards = cards.size();
        if (player > 0) { // make one move (it's player's turn)
            for (int i = 0; i < nCards; i++) {
                GameState futureState = state.testMove(cards.get(i));
                if (futureState != null) { // wenn Zug gültig ist
                    score = negamaxSuche(-player, futureState, depthLeft-1, -beta, -alpha);
                    if (score >= beta) {
                        return score;
                    }
                    if (score > alpha) {
                        alpha = score; // alpha acts like max
                    }
                }
            }
            return alpha;
        }
        else { // make three moves (it's the others' turn)
            for (int i = 0; i < nCards; i++) {
                GameState futureState = state.testMove(cards.get(i));
                if (futureState != null) { // if move is valid
                    for (int k = 0; k < nCards; k++) {
                        if (k != i) {
                            GameState futureStateLevel2 = futureState.testMove(cards.get(k));
                            if (futureStateLevel2 != null) { // if move is valid
                                for (int m = 0; m < nCards; m++) {
                                    if (m != i && m != k) {
                                        GameState futureStateLevel3 = futureStateLevel2.testMove(cards.get(m));
                                        if (futureStateLevel3 != null) { // if move is valid
                                            score = negamaxSuche(-player, futureStateLevel3, depthLeft-1, -beta, -alpha);
                                            if (score >= beta) {
                                                return score;
                                            }
                                            if (score > alpha) {
                                                alpha = score; // alpha acts like max
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return alpha;
        }
    }
}

这似乎工作正常,但是对于深度 1 (depthLeft=1),程序已经需要平均计算 50,000 次移动(放置的卡片)。当然,这太过分了!

所以我的问题是:

  1. 实施是否正确?你能模拟这样的游戏吗?尤其是关于不完善的信息?
  2. 如何提高算法的速度和工作量?
  3. 例如,我能否将可能的移动集减少到 50% 的随机集以提高速度,同时保持良好的结果?
  4. 我发现UCT algorithm 是一个很好的解决方案(也许)。你知道这个算法吗?你能帮我实现它吗?

【问题讨论】:

    标签: artificial-intelligence minimax


    【解决方案1】:

    我想澄清接受的答案并没有真正涉及的细节。

    在许多纸牌游戏中,您可以对对手可能拥有的未知牌进行抽样,而不是生成所有牌。在进行此采样时,您可以考虑短花色等信息以及到目前为止持有某些牌的概率,以加权每手可能的可能性(每手都是我们将独立解决的可能世界)。然后,您使用完美的信息搜索解决每一手牌。在所有这些世界中最好的举动通常是整体上最好的举动 - 有一些警告。

    在像扑克这样的游戏中,这不会很好地发挥作用——游戏全是关于隐藏信息的。你必须精确地平衡你的行动,以隐藏你的手的信息。

    但是,在像技巧类纸牌游戏这样的游戏中,这非常有效——尤其是因为新信息一直在被披露。真正优秀的玩家很清楚每个人都持有什么。因此,相当强大的 Skat 和 Bridge 项目都是基于这些想法。

    如果你能彻底解决底层世界,那是最好的,但如果你不能,你可以使用 minimax 或 UCT 在每个世界中选择最好的移动。还有一些混合算法(ISMCTS)试图将这个过程混合在一起。请注意此处的声明。简单的采样方法更容易编码——您应该在更复杂的方法之前尝试更简单的方法。

    以下是一些研究论文,它们将提供一些关于不完美信息的抽样方法何时运作良好的更多信息:

    Understanding the Success of Perfect Information Monte Carlo Sampling in Game Tree Search(本文分析抽样方法何时可能奏效。)

    Improving State Evaluation, Inference, and Search in Trick-Based Card Games(本文介绍Skat中采样的使用)

    Imperfect information in a computationally challenging game(本文介绍Bridge中的采样)

    Information Set Monte Carlo Tree Search(本文将采样和UCT/Monte Carlo Tree Search合并,以避免出现第一个参考文献中的问题。)

    在接受的答案中,基于规则的方法的问题在于,它们无法利用超出创建初始规则所需的计算资源。此外,基于规则的方法将受到您可以编写的规则的能力的限制。基于搜索的方法可以利用组合搜索的力量产生比程序作者更强大的游戏。

    【讨论】:

      【解决方案2】:

      您实施的极小极大搜索对于存在如此多不确定性的游戏来说是错误的方法。由于您不知道其他玩家之间的卡片分布情况,因此您的搜索将花费大量时间来探索根据卡片的实际分布情况不可能发生的游戏。

      我认为更好的方法是在您对其他玩家的牌局知之甚少或根本没有信息的情况下,从良好的游戏规则开始。比如:

      1. 如果您在一轮中先出牌,请打出最低的牌,因为您赢得这一轮的机会很小。
      2. 如果你在一轮中打到最后,打出你最低的牌,将赢得这一轮。如果你不能赢得这一轮,那就打出你最低的牌。

      让你的程序一开始不关心搜索,只按照这些规则玩并让它假设所有其他玩家也会使用这些启发式方法。当程序观察第一张和最后一张牌时每轮游戏的玩家都可以建立一个关于每个玩家可能持有的牌的信息表。例如。一个 9 会赢得这一轮,但玩家 3 没有玩它,所以他不能有任何 9 或更高的牌。由于收集了有关每个玩家手牌的信息,因此搜索空间最终将被限制在对可能的游戏进行极小极大搜索可以产生关于下一张要玩的牌的有用信息的地步。

      【讨论】:

      • 嗯,关于游戏快结束时的极小化。那时你知道你需要 x 把戏才能获胜。任何你不能(不应该)赢的世界,你都可以无视。因为如果那个世界是对的,那么无论如何你已经输了。如果您将您的概率建立在导致获胜的世界上(主要是使用一厢情愿),那么您可能会进一步减少搜索
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-09
      • 1970-01-01
      • 1970-01-01
      • 2016-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多