递归暴力迷宫求解器Java [英] Recursive brute force maze solver Java

查看:142
本文介绍了递归暴力迷宫求解器Java的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了尝试编写一个强力迷宫来解决C程序,我先编写了这个java程序来测试一个想法。我是C的新手,打算在java中获得这个权利之后转换它。因此,我正试图远离arraylists,花式库等,以便更容易转换为C.程序需要生成一个最短步骤的单宽度路径来解决迷宫。我认为我的问题可能在于片段化每个递归传递的路径存储数组。谢谢你看这个。 -Joe

In an attempt to write a brute force maze solving C program, I've written this java program first to test an idea. I'm very new to C and intend to convert it after getting this right in java. As a result, I'm trying stick away from arraylists, fancy libraries, and such to make it easier to convert to C. The program needs to generate a single width path of shortest steps to solve a maze. I think my problem may be in fragmenting a path-storing array passed through each recursion. Thanks for looking at this. -Joe

maze:

1 3 3 3 3 
3 3 3 3 3 
3 0 0 0 3 
3 0 3 3 3 
0 3 3 3 2 


Same maze solved by this program:
4 4 4 4 4 
4 4 4 4 4 
4 0 0 0 4 
3 0 3 3 4 
0 3 3 3 2 

数字表示法在代码中解释

number notation are explained in code

    public class javamaze {

static storage[] best_path;
static int best_count;
static storage[] path;

//the maze - 1 = start; 2 = finish; 3 = open path
static int maze[][] = {{1, 3, 3, 3, 3}, 
    {3, 3, 3, 3, 3},
    {0, 0, 0, 0, 3},
    {0, 0, 3, 3, 3},
    {3, 3, 3, 3, 2}};

public static void main(String[] args) {

    int count1;
    int count2;

    //declares variables used in the solve method
    best_count = 0;
    storage[] path = new storage[10000];
    best_path = new storage[10000];
    int path_count = 0;


    System.out.println("Here is the maze:");
    for(count1 = 0; count1 < 5; count1++) {
        for(count2 = 0; count2 < 5; count2++) {
            System.out.print(maze[count1][count2] + " ");   
        }                       
        System.out.println("");         
    }                       

    //solves the maze
    solve(findStart()/5, findStart()%5, path, path_count);  

    //assigns an int 4 path to the maze to visually represent the shortest path
    for(int count = 0; count <= best_path.length - 1; count++)
        if (best_path[count] != null)
            maze[best_path[count].getx()][best_path[count].gety()] = 4;

    System.out.print("Here is the solved maze\n");

    //prints the solved maze
    for(count1 = 0; count1 < 5; count1++) {
        for(count2 = 0; count2 < 5; count2++){
            System.out.print(maze[count1][count2] + " ");
        }
        System.out.print("\n");
    }
}

//finds maze start marked by int 1 - this works perfectly and isn't related to the problem
public static int findStart() {
    int count1, count2;
    for(count1 = 0; count1 < 5; count1++) {
        for(count2 = 0; count2 < 5; count2++) {
            if (maze[count1][count2] == 1)
                return (count1 * 5 + count2);
        }
    }
    return -1;
}

//saves path coordinate values into a new array
public static void save_storage(storage[] old_storage) {
    int count;
    for(count = 0; count < old_storage.length; count++) {
        best_path[count] = old_storage[count];
    }
}

//solves the maze
public static Boolean solve(int x, int y, storage[] path, int path_count) {

    //checks to see if grid squares are valid (3 = open path; 0 = wall
    if (x < 0 || x > 4) { //array grid is a 5 by 5
        //System.out.println("found row end returning false");
        return false;
    }
    if (y < 0 || y > 4) {
        //System.out.println("Found col end returning false");
        return false;
    }

    //when finding finish - records the number of moves in static int best_count
    if (maze[x][y] == 2) {
        if (best_count == 0 || best_count > path_count) {
            System.out.println("Found end with this many moves: " + path_count);
            best_count = path_count;
            save_storage(path); //copies path counting array into a new static array
        }
    }
    //returns false if it hits a wall
    if (maze[x][y] == 0)
        return false;

    //checks with previously crossed paths to prevent an unnecessary repeat in steps
    for(storage i: path) 
        if (i != null)
            if (i.getx() == x && i.gety() == y) 
                return false;

    //saves current recursive x, y (row, col) coordinates into a storage object which is then added to an array.
    //this array is supposed to fragment per each recursion which doesn't seem to - this may be the issue
    storage storespoints = new storage(x, y);
    path[path_count] = storespoints;

    //recurses up, down, right, left
    if (solve((x-1), y, path, path_count++) == true || solve((x+1), y, path, path_count++) == true ||
            solve(x, (y+1), path, path_count++) == true || solve(x, (y-1), path, path_count++) == true) {
        return true;
    }

    return false;
}
} 

//stores (x, y) aka row, col coordinate points
class storage {

private int x;
private int y;

public storage(int x, int y) {
    this.x = x;
    this.y = y;
}
public int getx() {
    return x;
}
public int gety() {
    return y;
}
public String toString() {
    return ("storage coordinate: " + x + ", " + y + "-------");
}

}


推荐答案

这本来不是一个答案,但它有点演变为一个。老实说,我认为从Java开始并转向C是一个坏主意,因为这两种语言实际上并不相同,而且你不会给自己任何好处,因为如果你依赖任何特性,你会遇到严重的问题移植它那个C没有(即大多数)

This wasn't originally intended to be an answer but it sort of evolved into one. Honestly, I think starting in Java and moving to C is a bad idea because the two languages are really nothing alike, and you won't be doing yourself any favors because you will run into serious issues porting it if you rely on any features java has that C doesn't (i.e. most of them)

那就是说,我将勾勒出一些算法C的东西。

That said, I'll sketch out some algorithmic C stuff.

支持结构

typedef
struct Node
{
    int x, y;
    // x and y are array indices
}
Node;

typedef
struct Path
{
    int maxlen, head;
    Node * path;
    // maxlen is size of path, head is the index of the current node
    // path is the pointer to the node array
}
Path;

int    node_compare(Node * n1, Node * n2); // returns true if nodes are equal, else false

void   path_setup(Path * p, Node * n); // allocates Path.path and sets first node
void   path_embiggen(Path * p);        // use realloc to make path bigger in case it fills up
int    path_toosmall(Path * p);        // returns true if the path needs to be reallocated to add more nodes
Node * path_head(Path * p);            // returns the head node of the path
void   path_push(Path * p, Node * n);  // pushes a new head node onto the path
void   path_pop(Path * p);             // pops a node from path

您可以将迷宫格式更改为邻接列表。您可以将每个节点存储为一个掩码,详细说明您可以从节点前往哪些节点。

You might to change your maze format into an adjacency list sort of thing. You could store each node as a mask detailing which nodes you can travel to from the node.

迷宫格式

const int // these constants indicate which directions of travel are possible from a node
N = (1 << 0),       // travel NORTH from node is possible
S = (1 << 1),       // travel SOUTH from node is possible
E = (1 << 2),       // travel EAST  from node is possible
W = (1 << 3),       // travel WEST  from node is possible
NUM_DIRECTIONS = 4; // number of directions (might not be 4.  no reason it has to be)

const int
START  = (1 << 4),  // starting  node
FINISH = (1 << 5);  // finishing node

const int
MAZE_X = 4,         // maze dimensions
MAZE_Y = 4;

int maze[MAZE_X][MAZE_Y] = 
{
    {E,        S|E|W,    S|E|W,    S|W       },
    {S|FINISH, N|S,      N|START,  N|S       },
    {N|S,      N|E,      S|E|W,    N|S|W     },
    {N|E,      E|W,      N|W,      N         }
};

Node start  = {1, 2}; // position of start node
Node finish = {1, 0}; // position of end node

我的迷宫与你的迷宫不同:这两种格式并不完全映射彼此1:1。例如,您的格式允许更精细的移动,但我的允许单向路径。

My maze is different from yours: the two formats don't quite map to each other 1:1. For example, your format allows finer movement, but mine allows one-way paths.

请注意,您的格式明确定位墙。使用我的格式,墙在概念上位于无法路径的任何地方。我创建的迷宫有3个水平墙和5个垂直墙(也是封闭的,即整个迷宫周围有一个连续的墙)

Note that your format explicitly positions walls. With my format, walls are conceptually located anywhere where a path is not possible. The maze I created has 3 horizontal walls and 5 vertical ones (and is also enclosed, i.e. there is a continuous wall surrounding the whole maze)

对于你的暴力遍历,我会使用深度优先搜索。您可以通过多种方式将标志映射到方向,例如以下内容。因为你无论如何都要遍历每个访问时间,所以访问时间是无关紧要的,因此数组而不是某种更快的关联容器就足够了。

For your brute force traversal, I would use a depth first search. You can map flags to directions in a number of ways, like maybe the following. Since you are looping over each one anyway, access times are irrelevant so an array and not some sort of faster associative container will be sufficient.

数据格式抵消映射

// map directions to array offsets
// format is [flag], [x offset], [y offset]
int mappings[][] =
{
    {N, -1,  0},
    {S,  1,  0},
    {E,  0,  1},
    {W,  0, -1}
}

最后,你的搜索。您可以迭代或递归地实现它。我的示例使用递归。

Finally, your search. You could implement it iteratively or recursively. My example uses recursion.

搜索算法伪代码

int search_for_path(int ** maze, char ** visited, Path * path)
{
    Node * head = path_head(path);
    Node temp;
    int i;

    if (node_compare(head, &finish)) return 1; // found finish
    if (visited[head->x][head->y])   return 0; // don't traverse again, that's pointless

    visited[head->x][head->y] = 1;
    if (path_toosmall(path)) path_embiggen(path);

    for (i = 0; i < NUM_DIRECTIONS; ++i)
    {
        if (maze[head->x][head->y] & mappings[i][0]) // path in this direction
        {
            temp = {head->x + mappings[i][1], head->y + mappings[i][2]};
            path_push(path, &temp);
            if (search_for_path(maze, visited, path)) return 1; // something found end
            path_pop(path);
        }
    }
    return 0; // unable to find path from any unvisited neighbor
}

要调用此函数,应该像这样设置所有内容:

To call this function, you should set everything up like this:

致电解算器

// we already have the maze
// int maze[MAZE_X][MAZE_Y] = {...};

// make a visited list, set to all 0 (unvisited)
int visited[MAZE_X][MAZE_Y] = 
{
    {0,0,0,0},
    {0,0,0,0},
    {0,0,0,0},
    {0,0,0,0}
};

// setup the path
Path p;
path_setup(&p, &start);

if (search_for_path(maze, visited, &path))
{
    // succeeded, path contains the list of nodes containing coordinates from start to end
}
else
{
    // maze was impossible
}

值得注意的是,因为我在编辑框中写了这一切,所以我没有测试过它。它可能不会在第一次尝试时工作,可能需要一点点摆弄。例如,除非全局声明开始和结束,否则会出现一些问题。最好将目标节点传递给搜索函数,而不是使用全局变量。

It's worth noting that because I wrote this all in the edit box, I haven't tested any of it. It probably won't work on the first try and might take a little fiddling. For example, unless start and finish are declared globally, there will be a few issues. It would be better to pass the target node to the search function instead of using a global variable.

这篇关于递归暴力迷宫求解器Java的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆