maze C++

This week, you will write a program that generates random mazes and finds a path in there from a beginning to an end position, and then prints the maze on the screen. To give you an idea, this is how the output of your program is supposed to look like:

./mazegen 5 6
±–±–±–±–±–±–+
| . . . | |
±–±–+ ±–±–+ +
| | . . . | |

±–±–±–+ + +
| | . | |

±–±–+ + + +
| | | | . | |

±–+ + +
| | . . |
±–±–±–±–±–±–+
Your program should perform the following major steps:

Read from the program’s command line parameters either 2 or 3 integer values. The first one is the number of rows of the maze, the second one is the number of columns. The third parameter is optional. (But you must implement handling it.) If present, the third parameter must be used as seed value for the random generator. (So you can decide to always generate the same maze for testing purposes.) When called with two parameters only, the program must generate a different maze, each time it is run. Hint: you can convert a string from argv[] to an int using std::stoi.
Generate a random maze of the given size, using the algorithm called Recursive backtracker (see below). The core of this assignment is using classes in a meaningful way. Before you start implementing, first decide what are the “things” in your program, and create classes for them. (Example things are the maze, coordinates, cells in the maze, or even the command line parameters.) Your program must implement at least two classes. But more are likely helpful for achieving good structure.
Find a path inside your random maze, from the top-left corner – we refer to as position (0,0) --, to the bottom right corner. For details, see below.
Print your maze with the path to cout. (Details, see below)

Maze generation

The maze consists of walls and cells. The maze must be closed, so there must be no openings in the outer walls. Initially, all cells are separated from each other by walls. Inside the maze, some walls must be removed to connect the cells to each other. All cells must be connected to each other; from any cell it must be possible to reach any other cell by following paths that have been created by removing some of the internal walls. Cycles inside the maze are forbidden. So, the number of removed walls must be equal to the number of cells, minus 1. Fortunately, there is an algorithm, called Recursive backtracker, that does this for us. You can watch this video with a very nice explanation. You only need the first 8 minutes where he explains the algorithm. Please do not get carried away with the code he describes. While I love his explanation of the algorithm, his code is using his own graphics library and too many C++ features that are as debatable as advanced. (And if you take “too much inspiration” from his code, you will get in trouble anyways…) Not needed, but a nice read is the description of maze generation on wikipedia.

Path finding

Once you have generated your maze (and stored inside a beautiful, class-based data structure), it is time to find a path through your maze. By convention, the path must always start in the top left corner, and must end in the bottom right corner. The finally identified path must not include any detours. For path finding, not having cycles in the maze makes our lives much easier!

Path finding can be done by a (recursive) technique called backtracking. But all you need is implement the following pseudo code, using your own class data structures:

Function findPath, given a maze M, and two coordinates, from and to, returns true if a path could be found, false otherwise:

M.at(from).visited <- true
if from equals to, return true
neighbours <- list of all direct neighbours of from that can be reached (that are not blocked by walls)
for all n in neighbours:
if M.at(n).visited == false
if findPath(n,to) == true, return true
M.at(from).visited <- false
return false

Maze printing

Maze printing must be done very precisely, according to this description. Otherwise, the automated tests will fail. This means, no other characters, no extra characters, no extra spaces or newlines. Let’s consider the following 2-by-2 example maze:

±–±–+
| . . |
±–+ +
| . |
±–±–+
Each cell is 5 characters wide and 3 characters high. Walls and corners are shared among neighbouring cells. Each corner is denoted as +. A horizontal wall is denoted as 3 minus signs: — A vertical wall is denoted by |

The contents of a cell are either three spaces: ’ ’ ,

or two spaces with a . in the middle: ’ . ’ ,

the latter if the cell is part of the path from the top left corner to the bottom right corner.

Topics to exercise

classes and their interfaces
recursion, command line parameters

To use and not to use

Your program should:
use well-designed classes (at least two)

use exceptions for indicating error conditions

implement all functionality in the member functions of the classes; no other functions outside the classes

use main() as a minimalistic driver for the classes(plus error handling)

implement the recursive backtracker algorithm as outlined above (mandatory) implementing the recursive pseudocode shown above for finding the path is highly recommended

Using the following C++ features is not allowed

arrays (except argv[l)
structs
pointers
iterators
auto


#include <iostream>
#include <vector>
#include <random>
#include <ctime>
#include <string>
#define UP 0
#define DOWN 1
#define LEFT 2
#define RIGHT 3

class Direction {
public:
    int x;
    int y;
    int dir;
    Direction(int dir) : dir(dir) {
        if (dir == 0) { // UP
            x = 0;
            y = -1;
        }
        else if (dir == 1) { // DOWN
            x = 0;
            y = 1;
        }
        else if (dir == 2) { // LEFT
            x = -1;
            y = 0;
        }
        else if (dir == 3) { // RIGHT
            x = 1;
            y = 0;
        }
    }
};


class MazeCell {
public:
    bool visited;
    bool path;
    Direction direction; // Direction for the cell
    MazeCell() : visited(false), path(false), direction(0) {} // Initialize with direction 0 (UP)
};

class Maze {
public:
    Maze(int rows, int cols, unsigned int seed = 0)
        : rows(rows), cols(cols), maze(rows, std::vector<MazeCell>(cols)) {
        if (seed == 0) {
            std::random_device rd;
            rng.seed(rd());
        }
        else {
            rng.seed(seed);
        }
    }

    void generateMaze() {
        generateMazeRecursive(0, 0);
    }

    void generateMazeRecursive(int x, int y) {
        maze[x][y].visited = true;

        std::vector<int> directions = { 0, 1, 2, 3 };
        std::shuffle(directions.begin(), directions.end(), rng);

        for (int dir : directions) {
            Direction direction(dir);
            int newX = x + direction.x;
            int newY = y + direction.y;

            if (isValid(newX, newY) && !maze[newX][newY].visited) {
                maze[x][y].direction = direction;
                generateMazeRecursive(newX, newY);
            }
        }
    }

    bool findPath() {
        return findPathRecursive(0, 0, rows - 1, cols - 1);
    }

    bool findPathRecursive(int x, int y, int targetX, int targetY) {
        maze[x][y].visited = true;

        if (x == targetX && y == targetY) {
            return true;
        }

        int dx[] = { 1, -1, 0, 0 };
        int dy[] = { 0, 0, 1, -1 };

        for (int dir = 0; dir < 4; ++dir) {
            int newX = x + dx[dir];
            int newY = y + dy[dir];

            if (isValid(newX, newY) && !maze[newX][newY].visited && maze[newX][newY].path) {
                if (findPathRecursive(newX, newY, targetX, targetY)) {
                    return true;
                }
            }
        }

        return false;
    }

    void printMaze() {
        // Define wall characters and path characters
        char horizontalWall = '-';
        char verticalWall = '|';
        char corner = '+';
        char space = ' ';
        char pathCharacter = '.';

        for (int i = 0; i < rows; ++i) {
            // Print the top row of each cell
            for (int j = 0; j < cols; ++j) {
                std::cout << corner;
                if (maze[i][j].direction.dir == UP || i == 0) {
                    for (int k = 0; k < 3; ++k) {
                        std::cout << horizontalWall;
                    }
                }
                else {
                    for (int k = 0; k < 3; ++k) {
                        std::cout << space;
                    }
                }
            }
            std::cout << corner << "\n";

            // Print the middle row of each cell
            for (int j = 0; j < cols; ++j) {
                if (maze[i][j].direction.dir == LEFT || j == 0) {
                    std::cout << verticalWall << space;
                }
                else {
                    std::cout << space << space;
                }

                if (maze[i][j].path) {
                    std::cout << pathCharacter << space;
                }
                else {
                    std::cout << space << space;
                }
            }
            std::cout << verticalWall << "\n";
        }

        // Print the bottom row of the last row of cells
        for (int j = 0; j < cols; ++j) {
            std::cout << corner;
            if (maze[rows - 1][j].direction.dir == DOWN) {
                for (int k = 0; k < 3; ++k) {
                    std::cout << horizontalWall;
                }
            }
            else {
                for (int k = 0; k < 3; ++k) {
                    std::cout << space;
                }
            }
        }
        std::cout << corner << "\n";
    }



private:
    int rows, cols;
    std::vector<std::vector<MazeCell>> maze;
    std::mt19937 rng;

    bool isValid(int x, int y) const {
        return x >= 0 && x < rows && y >= 0 && y < cols;
    }
};

class CommandLineParser {
public:
    CommandLineParser(int argc, char* argv[]) {
        if (argc != 3 && argc != 4) {
            throw std::invalid_argument("Usage: ./mazegen <rows> <cols> [seed]");
        }

        rows = std::stoi(argv[1]);
        cols = std::stoi(argv[2]);
        seed = (argc == 4) ? std::stoi(argv[3]) : 0;
    }

    int getRows() const { return rows; }
    int getCols() const { return cols; }
    unsigned int getSeed() const { return seed; }

private:
    int rows, cols;
    unsigned int seed;
};

int main(int argc, char* argv[]) {
    try {
        CommandLineParser parser(argc, argv);
        int rows = parser.getRows();
        int cols = parser.getCols();
        unsigned int seed = parser.getSeed();

        Maze maze(rows, cols, seed);
        maze.generateMaze();
        maze.findPath();
        maze.printMaze();
    }
    catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flood fill 是一种图像处理算法,用于填充连通区域。在计算机图形学中应用广泛。以下是C++实现: ```c++ #include <iostream> #include <cstring> using namespace std; const int MAXN = 1005; char maze[MAXN][MAXN]; bool vis[MAXN][MAXN]; int n, m; int dir[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; //四个方向 void dfs(int x, int y){ vis[x][y] = true; for(int i = 0; i < 4; i++){ int nx = x + dir[i][0]; int ny = y + dir[i][1]; if(nx >= 0 && nx < n && ny >= 0 && ny < m && !vis[nx][ny] && maze[nx][ny] == '.'){ dfs(nx, ny); } } } int main(){ cin >> n >> m; for(int i = 0; i < n; i++){ for(int j = 0; j < m; j++){ cin >> maze[i][j]; } } memset(vis, false, sizeof(vis)); int ans = 0; for(int i = 0; i < n; i++){ for(int j = 0; j < m; j++){ if(!vis[i][j] && maze[i][j] == '.'){ ans++; dfs(i, j); } } } cout << ans << endl; return 0; } ``` 这个算法基于深度优先搜索(DFS)实现。在这个算法中,我们从起始点开始遍历,然后不断地向四个方向(上、下、左、右)扩展。 如果扩展的下一个点未被访问过,并且是可行的(在这里,我们把可以走的点标记为 '.'),我们就沿着这个点继续遍历下去。如果在扩展时发现一个点已经被访问过,我们就返回上一个点,尝试扩展其他方向。 我们使用一个二维数组 `vis` 来记录每个点是否被访问过。我们遍历整个矩阵,如果发现一个点未被访问过,并且是可行的,我们就从这个点开始进行 DFS 遍历,并且每次遍历一个连通区域时计数器加一,最后输出计数器的值就是连通区域的个数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值