【问题标题】:Better move generator than brute force in NeutreekoNeutreeko 中比蛮力更好的移动生成器
【发布时间】:2014-02-16 15:59:20
【问题描述】:

我正在实施一个名为 Neutreeko 的游戏(5x5 棋盘,两名玩家各 3 个棋子),对于我正在实施的蒙特卡洛树搜索,我需要一种快速的方法来为拥有的棋子生成所有可能的移动由玩家。我将棋盘状态存储在一维数组中,其中每个单元格等于 'player'、'cpu' 或 0。

关于规则,如果棋子沿着水平线、垂直线或对角线移动到最远的点而不撞到墙或其他棋子,则移动是有效的(这意味着,如果你假设你站在棋子的中心一个空棋盘,你只能在最角落和与这些边相邻的每边的中间单元格中,总共 8 步)。

有什么比在 8 个方向中的每一个方向上蛮力行进直到撞到墙或其他棋子更好的方法呢?这也需要许多条件来确保 - 在检查对角线时 - 我们在使用升序索引时不会意外地转到另一条线等等。当然可以,但我敢打赌,有一些博弈论实践可以更优雅、更有效地处理它。

【问题讨论】:

  • 这里至少有一个想法,chessprogramming.wikispaces.com/…,在名为“通过计算的滑动攻击”的部分中。
  • @groovy - 一定会去看看的,谢谢。
  • ...如果您进一步阅读下面的内容,可以预先计算所有选项 - (2^5=32) 任何一条射线的可能性。

标签: algorithm


【解决方案1】:

希望这不是完全不清楚......

每个棋子都会通过阻塞线在允许的移动中引起某些排除。有 24 条线(10 + 14 条对角线),据我统计,有 320 条可能的滑动动作。最好在每个方向上使用 5 条“环绕”对角线,总共 20 条线,每条线最多 20 次移动。每一块都位于 3(角)或 4 条线上,当一块移动时,您会更新它移动的线,以及受影响的其他 4-6 条线。

特定方格的排除,即棋子在该方格上的移动是恒定的。您需要做的就是在每一行中添加或减去一个常量二进制字符串。要生成一个新位置,请检查玩家的三块方格中每个移动的一些位标志,以查看该移动的排除数是否为零。

每次移动的最大排除数各不相同(例如,如果整行已满,则有 5 个原因您不能从 A 滑到 E)。一条普通线的总表示是 23 位。

ABCDE AB、BC、CD、DE、AC、BD、CE 各只需要 2 位(

【讨论】:

    【解决方案2】:

    更新:

    受clwhisk的建议启发,我有了以下想法:

    将所有棋盘位置散列为 30 位 - 前 15 位作为计算机的位置,接下来的 15 位作为玩家的位置(每个棋子 5 位)。对于每 25 个选择 3 (=2300) 个计算机位置,生成 22 个选择 3 (=1540) 个匹配玩家位置,并将哈希设置为指向下一个棋盘位置(前 15 位是计算机的棋子)这可能是由于其中一台计算机棋子的移动造成的。

    这种方式不需要生成任何动作,也不需要测试获胜位置。散列大小似乎是 2300 * 1540 = 3,542,000,但会同时包括所有三个棋子的预先计算的下一个棋盘状态,包括散列的获胜位置。此外,棋盘可以表示为一个数字,而不是有两个位棋盘代表每个玩家的位置(这在其他一些棋盘游戏中很常见)。

    嗯,这个想法在我的 Firefox 浏览器上崩溃了,即使是网络工作者也是如此。所以我的替代想法是使用 2300 项哈希作为二维数组 (2300x2300; player_1_position x player_2_position) 的键,其值是可以跟随 player_1 可用动作的棋盘状态。此外,可以尝试对一半的位置进行散列 - 如果它不在散列中,则它在镜像位置。

    结束更新。

    这是一个尝试对所有棋盘位置、棋盘和移动进行哈希处理的 JavaScript 示例——在我看来,棋盘有 25 个选择 5 = 53130 种可能的状态(不包括我们想要移动的棋子) );每个棋盘状态为我们的棋子提供了 20 个可能的位置。

    我不确定在这个相当大的哈希中查找的速度与动态生成可用移动的速度相比如何。正如预期的那样,预先计算哈希值需要几秒钟才能加载到浏览器上。

    要查找可用的移动,请输入:hash[board<<5|position]

    例如:

    console.log(hash[31<<5|6])
    [1048576,512,8388608]
    
    console.log(hash[31<<5|7])
    [2097152,512,32,16777216,1024]
    

    JavaScript 代码(函数 combmain 改编自 C 中的 Rosetta Code):

    var hash = {}
    
    function comb(m, n, c)
    {
        var i;
        for (i = 0; i < n; i++) c[i] = n - i;
    
        while (1) {
            var s = 0
            for (i = n; i--;)
                s|=1<<c[i]-1;
    
            //hash boards, positions, and moves
            for (var j=1; j<=25; j++){
                var pos = 1 << (j - 1)
                if (pos & s)
                    continue
                hash[(s<<5)|j] = moves(pos,s)
            }    
    
            /* this check is not strictly necessary, but if m is not close to n,
               it makes the whole thing quite a bit faster */
            if (c[i]++ < m) continue;
    
            for (i = 0; c[i] >= m - i;) if (++i >= n) return;
            for (c[i]++; i; i--) c[i-1] = c[i] + 1;
        }
    }
    
    function moves(position,board){
        var leftBorder = 17318416,
            rightBorder = 1082401,
            moves = [],
            positionTemp = position
    
        //up
        while (positionTemp < 1<<20 && !((positionTemp<<5) & board))
            positionTemp <<= 5
    
        if (positionTemp != position)
            moves.push(positionTemp)
    
        positionTemp = position
    
        //down
        while (positionTemp > 1<<4 && !((positionTemp>>5) & board))
            positionTemp >>= 5
    
        if (positionTemp != position)
            moves.push(positionTemp)
    
        positionTemp = position
    
        //left
        while (!((positionTemp<<1) & board)){
            if (positionTemp & leftBorder || positionTemp == 1<<24)
                break
            positionTemp <<= 1
        }
    
        if (positionTemp != position)
            moves.push(positionTemp)
    
        positionTemp = position
    
        //right
        while (!((positionTemp>>1) & board)){
            if (positionTemp & rightBorder || positionTemp == 1)
                break
            positionTemp >>= 1
        }
    
        if (positionTemp != position)
            moves.push(positionTemp)
    
        positionTemp = position
    
        //NW
        while (!((positionTemp<<6) & board)){
            if (positionTemp & leftBorder || positionTemp >= 1<<20)
                break
            positionTemp <<= 6
        }
    
        if (positionTemp != position)
            moves.push(positionTemp)
    
        positionTemp = position
    
        //NE
        while (!((positionTemp<<4) & board)){
            if (positionTemp & rightBorder  || positionTemp >= 1<<20)
                break
            positionTemp <<= 4
        }
    
        if (positionTemp != position)
            moves.push(positionTemp)
    
        positionTemp = position
    
        //SW
        while (!((positionTemp>>4) & board)){
            if (positionTemp & leftBorder || positionTemp <= 1<<4)
                break
            positionTemp >>= 4
        }
    
        if (positionTemp != position)
            moves.push(positionTemp)
    
        positionTemp = position
    
        //SE
        while (!((positionTemp>>6) & board)){
            if (positionTemp & rightBorder || positionTemp <= 1<<4)
                break
            positionTemp >>= 6
        }
    
        if (positionTemp != position)
            moves.push(positionTemp)
    
        return moves
    }
    
    function main()
    {
        var buf = new Array(100);
        comb(25, 5, buf);
        console.log("done")
    }
    
    
    main()
    

    【讨论】:

    • 为什么不简单地将所有 25 个选择 6 = 177100 个棋盘位置映射到(指向列表的指针)最多约 60 个(通常更少)可能的下一个位置?如果您知道游戏仅限于 5x5,那么简单就是要走的路。识别位置最简单的格式是 25*24*23*22*21*(6 选择 3) = 1.27 亿,这将只占用兆字节,而不是千兆字节的内存。
    • @clwhisk 感谢您的好主意——您如何建议计算机识别下一个可能的位置中的哪一个是计算机的棋子移动的结果?
    • 如果您要预先生成一个包含所有动作的表格,这并不重要,但与更简单的代码相比,我下面的回答可能会减少计算次数。只需要一个 32 位整数或指针来标识一个位置。
    • @clwhisk 感谢您的评论。我仍然不明白,如果计算机可以引用可用的next 位置,它如何区分那些将跟随计算机的棋子移动的位置? (关于您自己的答案,我必须承认我无法理解 - 您能否在您的答案中添加一些说明性示例?)
    • int[][][] nextpos;//173million x 2 x n array [position number][player][&lt;list of n position numbers that player can move to from this position&gt;]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-13
    • 2020-03-26
    • 2018-04-06
    • 1970-01-01
    相关资源
    最近更新 更多