Grundy的游戏扩展到两个以上

时间:2012-03-21 11:02:16

标签: algorithm theory combinatorics game-theory

如何在Grundy的游戏中将堆分成两堆?

如何将堆分成任意数量的堆(其中没有两个是相同的)?

2 个答案:

答案 0 :(得分:1)

在“Winning Ways for your Mathematical Plays”系列丛书中详细分析了此类游戏。您正在寻找的大部分内容可能都是第1卷。

您还可以查看以下链接:Nimbers (Wikipedia)Sprague-Grundy theorem (Wikipedia)或搜索“组合博弈论”。

我对此的了解非常生疏,所以我担心我无法帮助你解决这个具体问题。如果你已经知道我所关联的一切,那就是我的借口。

编辑:一般来说,解决这些类型游戏的方法是“建立”堆栈大小。所以从一堆1开始,决定谁以最佳游戏获胜。然后对于2的堆栈执行相同的操作,可以将其拆分为1& 1.转到3,可分为1& 2.同样的4(这里变得更加棘手):3& 1或2& 2,使用Spague-Grundy定理&对于nimbers的代数规则,你可以计算谁将获胜。继续前进,直到达到需要知道答案的堆栈大小。

编辑2:我在评论中谈论的网站似乎已经失效。以下是备份的链接:Wayback Machine - Introduction to Combinatorial Games

答案 1 :(得分:0)

Grundy的游戏以及类似的许多游戏都可以通过以下算法解决:

//returns a Move object representing the current player's optimal move, or null if the player has no chance of winning
function bestMove(GameState g){
    for each (move in g.possibleMoves()){
        nextState = g.applyMove(move)
        if (bestMove(nextState) == null){
            //the next player's best move is null, so if we take this move, 
            //he has no chance of winning. This is good for us!
            return move;
        }
    }

    //none of our possible moves led to a winning strategy.
    //We have no chance of winning. This is bad for us :-(
    return null;
}

GameState和Move的实现取决于游戏。对于Grundy的游戏,两者都很简单。

GameState存储一个整数列表,表示游戏中每个堆的大小。

Move存储initialHeapSize整数和resultingHeapSizes整数列表。

GameState::possibleMoves遍历其堆大小列表,并确定每个列的合法划分。

GameState::applyMove(Move)返回GameState的副本,除了给它的移动应用于棋盘。


GameState::possibleMoves可以像“经典”Grundy游戏一样实现:

function possibleMoves(GameState g){
    moves = []
    for each (heapSize in g.heapSizes){
        for each (resultingHeaps in possibleDivisions(heapSize)){
            Move m = new Move(heapSize, resultingHeaps)
            moves.append(m)
        }
    }
    return moves
}

function possibleDivisions(int heapSize){
    divisions = []
    for(int leftPileSize = 1; leftPileSize < heapSize; leftPileSize++){
        int rightPileSize = heapSize - leftPileSize
        if (leftPileSize != rightPileSize){
            divisions.append([leftPileSize, rightPileSize])
        }
    }
    return divisions
}

修改此项以使用“划分为任意数量的不等桩”规则只需更改possibleDivisions的实施。


我还没有完全计算出来,但是未经优化的bestMove有一个非常疯狂的最坏情况运行时。一旦你开始给它一个大约12块石头的起始状态,你将会等待很长时间。因此,您应该实施memoization以提高效果。

为了获得最佳效果,请将每个GameState的堆大小列表排序,并丢弃任何大小为2或1的堆。