【问题标题】:How to hard-code legal moves for fast lookup?如何对合法动作进行硬编码以进行快速查找?
【发布时间】:2015-03-09 14:47:24
【问题描述】:

我已经创建了一个游戏板 (5x5),我现在想尽快决定什么时候移动是合法的。例如,(0,0)的一块想要去(1,1),这合法吗?首先,我试图通过计算找出这一点,但这似乎很麻烦。我想根据棋盘上的位置对可能的移动进行硬编码,然后遍历所有可能的移动,看看它们是否与棋子的目的地相匹配。我很难把它写在纸上。这就是我想要的:

//game piece is at 0,0 now, decide if 1,1 is legal
Point destination = new Point(1,1);     
destination.findIn(legalMoves[0][0]);

我面临的第一个问题是我不知道如何将可能的移动列表放入数组中,例如索引 [0][0]。这一定是相当明显的,但我坚持了一段时间。我想创建一个数组,其中有一个 Point 对象列表。所以在半代码中:legalMoves[0][0] = {Point(1,1),Point(0,1),Point(1,0)} 我不确定这是否有效,但它在逻辑上比 [[1,1],[0,1],[1,0]] 更有意义,但我不喜欢这个。

我遇到的第二个问题是,我不想在每次游戏开始时都使用实例变量 legalMoves 创建对象,而是让它从磁盘读取。我认为这样应该更快?可序列化类是要走的路吗?

我的第三个小问题是 25 个位置的合法移动不平衡。有些有 8 个可能的合法动作,有些有 3 个。也许这根本不是问题。

【问题讨论】:

  • 你如何计算合法移动?也许您正在尝试做的是不必要的过早优化。
  • 游戏板不平衡。它来自于 BagChal 游戏,在该游戏中,您始终可以水平和垂直移动,但并不总是沿对角线移动。 upload.wikimedia.org/wikipedia/commons/thumb/e/eb/…
  • 看起来如果x或y坐标的差异大于1,或者如果新点的坐标为负数则非法,否则合法。编辑:另外,如果坐标是奇数行和偶数列,那么你不能对角移动。
  • 对@NeplatnyUdaj,这有强烈的过早优化的味道。
  • 您是否正在寻找类似Map<Point, Set<Point>> mapFromPointToSetOfLegalDestinations 的内容?

标签: java arrays lookup


【解决方案1】:

您正在寻找一种结构,可以为您提供给定点的候选者,即Point -> List<Point>

通常,我会选择Map<Point, List<Point>>

您可以在程序启动时静态初始化此结构,也可以在需要时动态初始化。例如,这里我使用了 2 个辅助数组,其中包含一个点的可能翻译,这些将产生该点的 neighbours

// (-1  1) (0  1) (1  1)
// (-1  0) (----) (1  0)
// (-1 -1) (0 -1) (1 -1)
// from (1 0) anti-clockwise:
static int[] xOffset = {1,1,0,-1,-1,-1,0,1};
static int[] yOffset = {0,1,1,1,0,-1,-1,-1};

以下Map 包含Point 的实际邻居,其函数计算、存储和返回这些邻居。您可以选择一次性初始化所有邻居,但考虑到数量较少,我认为这不是性能问题。

static Map<Point, List<Point>> neighbours = new HashMap<>();

static List<Point> getNeighbours(Point a) {
    List<Point> nb = neighbours.get(a);
    if (nb == null) {
        nb = new ArrayList<>(xOffset.length); // size the list
        for (int i=0; i < xOffset.length; i++) {
            int x = a.getX() + xOffset[i];
            int y = a.getY() + yOffset[i];
            if (x>=0 && y>=0 && x < 5 && y < 5) {
                nb.add(new Point(x, y));
            }
        }
        neighbours.put(a, nb);
    }
    return nb;
}

现在检查一个合法的举动是在邻居中找到点:

static boolean isLegalMove(Point from, Point to) {
    boolean legal = false;
    for (Point p : getNeighbours(from)) {
        if (p.equals(to)) {
            legal = true;
            break;
        }
    }
    return legal;
}

注意:Point 类必须定义 equals()hashCode() 才能使地图按预期运行。

【讨论】:

    【解决方案2】:

    我面临的第一个问题是我不知道如何将可能的移动列表放入数组中,例如索引[0][0]

    由于棋盘是 2D 的,并且合法移动的数量通常可能不止一个,因此您最终会得到一个 3D 数据结构:

    Point legalMoves[][][] = new legalMoves[5][5][];
    legalMoves[0][0] = new Point[] {Point(1,1),Point(0,1),Point(1,0)};
    

    与其在每次游戏开始时使用实例变量legalMoves 创建对象,不如从磁盘读取它。我认为这样应该更快?可序列化类是要走的路吗?

    没有分析就无法回答这个问题。我无法想象为 5x5 板计算任何类型的合法移动会在计算上如此密集,以至于证明任何类型的额外 I/O 操作是合理的。

    对于 25 个位置,合法移动是不平衡的。有些有 8 个可能的合法动作,有些有 3 个。也许这根本不是问题。

    这可以用上面描述的 3D“锯齿状数组”很好地处理,所以这根本不是问题。

    【讨论】:

    • 3D 数组可以工作,或者您可以实现 Points 列表的 2D 数组
    • @mstbaum 对,列表也可以工作,只要在 foreach 循环中使用是合法的,
    猜你喜欢
    • 2014-03-29
    • 1970-01-01
    • 1970-01-01
    • 2011-05-26
    • 2021-12-22
    • 1970-01-01
    • 1970-01-01
    • 2021-03-28
    • 2012-03-12
    相关资源
    最近更新 更多