尼姆游戏问题

时间:2010-11-11 15:29:13

标签: java variable-assignment

好的,我必须制作一个nim游戏并尝试找到总是通过以下nim游戏获胜的策略:

21场比赛,每场比赛1和2每场比赛需要1,2,3,4或5场比赛,其中一场比赛不能获得前一场比赛所需的相同数量的比赛。如果/当他们参加最后一场比赛时,电子游戏将获胜。

我必须为此编程,但我甚至不明白是要开始。如何通过这种类型的nim游戏找到获胜策略?

编辑:

所以我认为当你仍然处于中间的7场比赛时,你总能获胜。另一个可以拿2-5,最多可以加7个。当另一个取1时,你取3(另一个不能取3)然后必须选择1或2,在这种情况下你将获得一个并获胜。

然而,从21岁到7岁对我来说是一个难题,我无法弄清楚你如何能够永远成为7岁的人。

编辑2: 好吧,如果没有规则,你不能像以前的玩家一样,我认为这很简单。

你要使k = 5 + 1 = 6.然后你应该进行第一次移动,使得匹配离开然后%6 = 0.所以在这种情况下先取3然后再填充另一个的移动玩家到6.但是在这种情况下,这将无法工作,因为其他玩家可以采取3后,你不能采取3来填补6.所以有我的问题。有什么想法吗?

EDIT3:

好的,所以你说我可以逼7场比赛。但是假设我对14-7比赛步骤采取相同的想法。 (然后是轮到他人了)

然后有两种情况: 1:他拿下2-5,我把它填到七,让7在那里,我赢了。 2:他拿1分,所以剩下13分。当我在(7-0)步骤中取3时,它变为10.然后他取5,我不能再拿5来完成,我会松开。

这就是问题所在,方案2在(7-0)步骤中没有问题。我该如何解决这个问题?

是的,解决方案:

顺便说一句,na speler 1意味着:在玩家1轮之后等(我是荷兰人)。

alt text

好的,所以我尝试了一些东西,我想我有解决方案。你必须先将1场比赛作为第一名球员。然后其他人可以参加2-5场比赛。你匹配(双关语)他的金额最多为7,所以你总共会有(21-1-7 =)13场比赛。然后又是玩家2的回合,并且有两种情况:玩家2需要1,2,4或者5场比赛,在这种情况下,您将获得与左侧将有7个匹配的匹配。 (正如之前所说的那样,当你进行比赛时,剩下7分,你总会获胜)。第二种情况是,玩家2需要3场比赛,在这种情况下,当轮到你时,中间有10场比赛。你不能拿3来制作7,因为你不能拿2倍相同的金额。所以你拿5分,剩下5分。玩家2然后不能拿5赢,并且必须选择1-4,之后你可以拿下其余的并获胜。

这是我猜的解决方案。我不知何故来到它,因为我注意到了这一点:

使用模数等的正常Nim游戏:

P2  1  2  3  4  5  
P1  5  4  3  2  1  
------------------
    6  6  6  6  6 

但你不能在这里做3,3所以这就是谎言:

p2 1  2  3  4  5 
p1    5  4  3  2  1
---------------------
      7  7  7  7 

所以你每次都能做7次,1次是特例。我不知道为什么,但我直觉地把1作为起点,因为你觉得你需要主动控制对方的动作。 (一个人不能做两次1,所以另一个人必须拿2-5才能让你掌控)

无论如何,感谢所有的帮助。也为整个程序编写。我无法使用它,因为它不会编译为缺乏良好的Java技能:)我也想自己解决它。

无论如何,我看到这是一个维基,祝未来的人们好好解决这个问题!

3 个答案:

答案 0 :(得分:8)

在这样的游戏中,你需要保持处于获胜位置的不变性(如果你已经在一个位置)。所以你需要知道如何从一个胜利的位置开始,然后回到它无论你的对手做什么动作

以下是三个提示:

  1. 你的移动组和对手的移动组是相同的:参加1,2,3,4或5场比赛。
  2. 这个游戏,当你开始它,是一个添加游戏。是的,你正在减去匹配,但在你制定战略时,仍然需要考虑增加。
  3. 从这开始:对于任何对手移动X(其中X是1,2,3,4或5),你可以采取什么措施来“取消”移出?
  4. modular arithmetic的概念中解释了我想要的概念,如果有帮助的话。


    编辑:但是,不采取相同数量的匹配的限制使事情变得有趣。我们将不得不在以后作为一个角落案例解决这个问题,但让我们首先看看你是如何看到我到目前为止所说的。请随时评论。


    编辑2:你的第二次编辑是正确的(如果不存在重复移动的规则,我的意思是)。但是你的第一次编辑是在正确的轨道上:你可以让事情在7年代起作用。

    不断质疑和回答自己:

    问:如何通过让AI进行最后一场比赛来可靠地强制获胜? 答:强制AI留下7场比赛,然后用你的策略迫使AI拿下第7个。这是因为您可以强制减去7个匹配项 问:那么如何通过确保AI进行最后一场比赛(但是七场)来强制赢得AI?

    不要过度思考它。拿走你已经知道的东西 - 你已经可以做到的AI - 并尽可能多地应用这一步。


    编辑3:这只是我想到的一个小问题,可以帮助您解决在第三次编辑中提到的问题。

    如果,对于移动组中的所有X(1,2,3,4,5),当AI轮到时仍然有2X个匹配,那么你可以通过X匹配来强制获胜。 (您在第三次编辑中详细说明了除了其他玩家之外的其他方式)

    不幸的是,这不是你可以强迫的,因为我说的是在AI轮到之前有2X匹配,而其他战略获胜条件取决于之后的位置 AI转向,因此AI的举动可以迫使它。

    同样,你要避免让AI的移动结果为任何一个X的2X匹配。

答案 1 :(得分:2)

如果您需要缩短运行时间,请使用Minimax algorithm,可能还需要使用alpha-beta修剪。

基本上,您可以详尽地搜索可能移动的树,然后再向上工作以确定最佳结果。

编辑:这里有一些代码向您展示制作完美代理是多么容易。编码花了大约5分钟。

public class MinimaxNim {

    public static int getBestMove(int matchesLeft, int lastVal) {
        int max = Integer.MIN_VALUE;
        int bestMove = matchesLeft > 0 ? 1 : 0;
        for ( int move = 1; move <= 5 && move <= matchesLeft; move++ ) {
            if ( move == lastVal )
                continue;
            int val = minValue(matchesLeft - move, move);
            if ( val > max ) {
                bestMove = move;
                max = val;
            }
        }
        return bestMove;
    }

    private static int maxValue(int matchesLeft, int lastVal) {
        if ( matchesLeft == 0 ) 
            return -1; //min has won

        int max = Integer.MIN_VALUE;
        for ( int toTake = 1; toTake <= 5 && toTake <= matchesLeft; toTake++) {
            if ( toTake == lastVal ) 
                continue;
            int val = minValue(matchesLeft - toTake, toTake);
            if ( val > max ) {
                max = val;
            }
        }
        return max;
    }

    private static int minValue(int matchesLeft, int lastVal) {
        if ( matchesLeft == 0 ) 
            return 1; //max has won

        int min = Integer.MAX_VALUE;
        for ( int toTake = 1; toTake <= 5 && toTake <= matchesLeft; toTake++) {
            if ( toTake == lastVal ) 
                continue;
            int val = maxValue(matchesLeft - toTake, toTake);
            if ( val < min ) {
                min = val;
            }
        }
        return min;
    }
}

你可以用这个来测试:

public static void main(String[] args) {
    int count = 21;
    int move = -1;
    for ( ;; ) {
        move = getBestMove(count, move);
        System.out.println("Player 1 takes " + move);
        count -= move;
        if ( count == 0 ) {
            System.out.println("Player 1 has won");
            break;
        }
        move = getBestMove(count, move);
        System.out.println("Player 2 takes " + move);
        count -= move;
        if ( count == 0 ) {
            System.out.println("Player 2 has won");
            break;
        }
    }
}

但我建议用自己或随机代理替换玩家1或玩家2,以便检查完美玩家所做的动作。

同样,这并没有向您展示最佳策略,但它会展现出对任何对手的最佳游戏(虽然未经测试)。

修改2

如果您感到好奇,从初始状态开始,只有26705个终端状态(玩家赢了)需要检查。随着你做出更多动作,这变得越来越少。这对于极小极大运动来说是完美的,就是总是取得进步......一旦你还剩下15场比赛,你就不能回到17,例如在象棋这样的游戏中你可以在搜索树中获得循环,因为玩家可以在棋盘上跳舞,回到以前的状态等等。

答案 2 :(得分:1)

正如需要考虑的更多数据一样,我针对游戏中可能存在的每种可能情况运行我的代理(剩余1-21次,最后5次移动)。其中一些状态是不可能的(例如20,最后一步是2)。我从表中删除了大部分内容,但更多可能仍然存在。

如果该组合有一个值,则表示在该点播放该移动肯定会导致获胜(假设持续完美的比赛)。

标有x的那个意味着处于该状态肯定会导致失败而不管移动(假设对手的完美发挥)。

   0 1 2 3 4 5
21 1           
20   x         
19     3       
18   5 5 5     
17   4   4 5   
16   3 3 x 3 3 
15   2 1 1 1 1 
14   x 1 1 1 1 
13   x x x x x 
12   5 5 5 5 x 
11   4 4 4 x 4 
10   3 3 5 3 3 
 9   2 3 2 2 2 
 8   4 1 1 1 1 
 7   x x x x x 
 6   3 3 x 3 3 
 5   5 5 5 5 x 
 4   4 4 4 x 4 
 3   3 3 x 3 3 
 2   2 1 1 1 1 
 1   x 1 1 1 1 

因此,如果您坚持进行分析,您可以在此表中查找(或者,如果有多个最佳动作)最佳移动是在该特定状态。

有一点需要注意的是,到目前为止你的分析完全符合你的要求:如果这是你的举动,而且剩下7支,你就搞砸了!但注13也是如此。