简体   繁体   English

递归蛮力迷宫求解器Java

[英]Recursive brute force maze solver Java

In an attempt to write a brute force maze solving C program, I've written this java program first to test an idea. 为了编写一个解决C程序的蛮力迷宫,我首先编写了这个Java程序来测试一个想法。 I'm very new to C and intend to convert it after getting this right in java. 我对C还是很陌生,打算在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. 结果,我试图远离arraylist,fancy库等,以便更轻松地转换为C。该程序需要生成最短步骤的单个宽度路径来解决迷宫。 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 + "-------");
}

}

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 (ie most of them) 老实说,我认为从Java开始并转向C是一个坏主意,因为这两种语言确实没有什么相似之处,而且您不会帮上忙,因为如果您依赖于Java的任何功能,在移植它时都会遇到严重的问题有C没有(即大多数)

That said, I'll sketch out some algorithmic C stuff. 也就是说,我将草拟一些算法C的东西。

Support Structures 支持结构

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. 您可以将每个节点存储为一个掩码,详细说明可以从该节点移动到的节点。

Maze Format 迷宫格式

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

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. 例如,您的格式允许更精细的移动,而我的格式允许单向路径。

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, ie there is a continuous wall surrounding the whole maze) 我创建的迷宫有3个水平墙和5个垂直墙(并且也是封闭的,即有连续的墙围住整个迷宫)

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. 由于无论如何都要遍历每个对象,因此访问时间是无关紧要的,因此仅使用数组而不是某种更快的关联容器就足够了。

Data Format to Offset Mappings 数据格式到偏移映射

// 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. 我的示例使用递归。

Search Algorithm Pseudocode 搜索算法伪代码

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: 要调用此函数,您应该像这样设置所有内容:

Calling The Solver 调用解算器

// 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. 最好将目标节点传递给搜索功能,而不要使用全局变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM