【问题标题】:Ice Sliding Puzzle Path Finding冰滑拼图寻路
【发布时间】:2015-03-20 15:37:14
【问题描述】:

我为这个有点模糊的标题道歉,我不确定你会怎么称呼这个谜题。

我正在制作一种寻路方法来找到移动最少的路线,而不是行进的距离。

游戏规则很简单,你必须从橙色方格穿过到绿色方格,但你只能沿直线移动,并且在碰到边界之前不能停止沿该方向移动(无论是墙竞技场或障碍物),就像他们在冰上滑行一样。

示例地图,除非我弄错了,否则是所需的路径(8 步)

Arena.java:https://gist.github.com/CalebWhiting/3a6680d40610829b1b6d

ArenaTest.java:https://gist.github.com/CalebWhiting/9a4767508831ea5dc0da

我假设这最好使用 Dijkstras 或 A* 路径查找算法来处理,但是我不仅对这些算法不是很有经验,而且也不知道如何定义路径规则.

提前感谢您的帮助。

【问题讨论】:

  • 根据您发布的规则,您示例中的第一轮看起来是非法的...
  • 不确定 A* 的启发式函数应该是什么样子,因此 BFS 可能也能正常工作(如果您计算移动次数,而不是所走路径的长度)。对于图表,您将有一个有向图,例如从 (5,4) 到 (2,4)、(2,4) 到 (2,3)、(5,4) 和 (2,6) 等边。
  • 橙色块是开始,而不是绿色块
  • 哈哈橙色开始了..明白了
  • 您能否提供更多代码,尤其是初始化代码,这样我们就不必手动找出flags[][] 了?另外,我认为您还需要 NORTH_BLOCKEDEAST_BLOCKED 标志。

标签: java path-finding


【解决方案1】:

如果有人仍然感兴趣,这是我的解决方案 (Java)。正如@tobias_k 在上面的comment 中所建议的那样,确实BFS 是要走的路:

import java.util.LinkedList;

public class PokemonIceCave {
    public static void main(String[] args) {
        int[][] iceCave1 = {
                {0, 0, 0, 1, 0},
                {0, 0, 0, 0, 1},
                {0, 1, 1, 0, 0},
                {0, 1, 0, 0, 1},
                {0, 0, 0, 1, 0}
        };
        System.out.println(solve(iceCave1, 0, 0, 2, 4));
        System.out.println();

        int[][] iceCave2 = {
                {0, 0, 0, 1, 0},
                {0, 0, 0, 0, 1},
                {0, 1, 1, 0, 0},
                {0, 1, 0, 0, 1},
                {0, 0, 0, 1, 0},
                {0, 0, 0, 0, 0}
        };
        System.out.println(solve(iceCave2, 0, 0, 2, 5));
    }

    public static int solve(int[][] iceCave, int startX, int startY, int endX, int endY) {
        Point startPoint = new Point(startX, startY);

        LinkedList<Point> queue = new LinkedList<>();
        Point[][] iceCaveColors = new Point[iceCave.length][iceCave[0].length];

        queue.addLast(new Point(0, 0));
        iceCaveColors[startY][startX] = startPoint;

        while (queue.size() != 0) {
            Point currPos = queue.pollFirst();
            System.out.println(currPos);
            // traverse adjacent nodes while sliding on the ice
            for (Direction dir : Direction.values()) {
                Point nextPos = move(iceCave, iceCaveColors, currPos, dir);
                System.out.println("\t" + nextPos);
                if (nextPos != null) {
                    queue.addLast(nextPos);
                    iceCaveColors[nextPos.getY()][nextPos.getX()] = new Point(currPos.getX(), currPos.getY());
                    if (nextPos.getY() == endY && nextPos.getX() == endX) {
                        // we found the end point
                        Point tmp = currPos;  // if we start from nextPos we will count one too many edges
                        int count = 0;
                        while (tmp != startPoint) {
                            count++;
                            tmp = iceCaveColors[tmp.getY()][tmp.getX()];
                        }
                        return count;
                    }
                }
            }
            System.out.println();
        }
        return -1;
    }

    public static Point move(int[][] iceCave, Point[][] iceCaveColors, Point currPos, Direction dir) {
        int x = currPos.getX();
        int y = currPos.getY();

        int diffX = (dir == Direction.LEFT ? -1 : (dir == Direction.RIGHT ? 1 : 0));
        int diffY = (dir == Direction.UP ? -1 : (dir == Direction.DOWN ? 1 : 0));

        int i = 1;
        while (x + i * diffX >= 0
                && x + i * diffX < iceCave[0].length
                && y + i * diffY >= 0
                && y + i * diffY < iceCave.length
                && iceCave[y + i * diffY][x + i * diffX] != 1) {
            i++;
        }

        i--;  // reverse the last step

        if (iceCaveColors[y + i * diffY][x + i * diffX] != null) {
            // we've already seen this point
            return null;
        }

        return new Point(x + i * diffX, y + i * diffY);
    }

    public static class Point {
        int x;
        int y;

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }

        @Override
        public String toString() {
            return "Point{" +
                    "x=" + x +
                    ", y=" + y +
                    '}';
        }
    }

    public enum Direction {
        LEFT,
        RIGHT,
        UP,
        DOWN
    }
}

【讨论】:

    【解决方案2】:

    我认为最好的解决方案可能是 BFS,您可以在其中使用具有以下参数的“状态”对象表示棋盘的状态:到目前为止的移动次数和坐标。它还应该有一个方法来找到下一个可达到的状态(这应该很容易编码,只需 N、S、E、W 并返回第一个阻塞点的数组)。

    Create initial state (0 moves with initial coordinates)
    Put in a priority queue (sorting by number moves)
    while(priority queue has more states):
        Remove node
        if it is a goal state:
            return the state
        Find all neighbors of current state
        Add them to priority queue (remembering to increment number of moves by 1)
    

    这使用隐式图形表示。由于优先队列,最优性得到保证;当找到目标状态时,将以最少的步数达到目标状态。如果整个优先级队列用尽并且没有返回任何状态,则不存在解决方案。由于优先级队列,此解决方案需要 O(V^2logV) 时间,但我认为这是最简单的编码。一个直接的 O(V) BFS 解决方案是可能的,但您必须跟踪您已经访问或尚未访问的状态以及到达它们的最少移动次数,这将占用 O(V) 内存。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-04
      • 1970-01-01
      相关资源
      最近更新 更多