思想
将迷宫抽象成具有2种或多种值的row 行 column 列的网格,状态’1’ 代表墙体,状态’0’代表可通行。增加一个状态‘*’代表可能的最终路径。
由于网上没有合适的图片,自己用excel画了简易的图便于理解。
其中蓝色的坐标表示起点,绿色的坐标表示终点。
难点是对每一个坐标,判断周边(上下左右)是否有可行的cell?以及如何判断crack是否成功,如果没有成功怎么办?
二、java代码实现
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.Stack;
/**
* @apiNote
* 迷宫问题
*
* 规则:
* '1'代表墙体,'0'代表可通行
* 不能"穿墙"
*
* @author leejack
*/
@SuppressWarnings("all")
public class Maze {
/**
* program starter
*
* @param args
*/
public static void main(String[] args){
//1 initialize the maze
char[][] chars = initialize();
//2 print the maze
printMaze(chars);
//3 regular expression verifacation
Scanner sc = new Scanner(System.in);
String regex = "\\d*";
String rowStr = null;
do {
System.out.println("请再次输入迷宫的行数");
rowStr = sc.next();
}while(!rowStr.matches(regex));
int row = Integer.parseInt(rowStr);
String columnStr = null;
do {
System.out.println("请再次输入迷宫的列数");
columnStr = sc.next();
}while(!columnStr.matches(regex));
int column = Integer.parseInt(columnStr);
String beginXStr = null;
String beginYStr = null;
do {
System.out.println("请输入起点坐标,前一个是x轴坐标,后一个是y轴坐标,按回车确认");
beginXStr = sc.next();
beginYStr = sc.next();
}while(!beginXStr.matches(regex) && !beginYStr.matches(regex));
int beginX = Integer.parseInt(beginXStr);
int beginY = Integer.parseInt(beginYStr);
String endXStr = null;
String endYStr = null;
do {
System.out.println("请输入终点坐标,前一个是x轴坐标,后一个是y轴坐标,按回车确认");
endXStr = sc.next();
endYStr = sc.next();
}while(!endXStr.matches(regex) && !endYStr.matches(regex));
int endX = Integer.parseInt(endXStr);
int endY = Integer.parseInt(endYStr);
//4 initialize object
System.out.println("生成迷宫中");
Cell[][] maze = MazeCracker.createMaze(chars, row, column);
MazeCracker cracker = new MazeCracker(maze, beginX, beginY, endX, endY, row, column);
//5 crack the maze
System.out.println("破解迷宫中");
cracker.crackMaze();
//6 print the result
cracker.printResult(cracker.result);
}
/**
* initialize the character array and print the initial prompt message
*
* @return character array
*/
public static char[][] initialize(){
System.out.println("欢迎使用迷宫破解系统");
System.out.println("请输入迷宫的行数");
Scanner scanner = new Scanner(System.in);
int row = Integer.parseInt(scanner.next());
System.out.println("请输入迷宫的列数");
int column = Integer.parseInt(scanner.next());
System.out.println("迷宫的行数为:" + row + "迷宫的列数为:" + column);
System.out.println("接下来请输入迷宫中的每一个单元格");
System.out.println("'0'代表可通行,‘1'代表是墙体,不可通行\r");
String elements;
char[][] chars = new char[row][column];
for (int x = 0; x < row;){
System.out.println("请输入第" + (x + 1) + "行,共" + column + "个元素,中间不要有空格");
elements = scanner.next();
//check the validity of the input
if (elements.length() != column){
System.out.println("您输入的元素不足或者超出了字符数组的容量,请重新输入");
continue;
}
char[] rows = elements.toCharArray();
for (int y = 0; y < rows.length; y++){
chars[x][y] = rows[y];
}
x++;
}
System.out.println("输入完毕,打印迷宫中");
return chars;
}
/**
* print the input result
*
* @param chars
*/
public static void printMaze(char[][] chars) {
for (char[] rows : chars) {
for (char ch : rows) {
System.out.print(ch + " ");
}
System.out.println();
}
System.out.println("打印迷宫完毕");
}
}
/**
* 单元格
*/
class Cell{
int x;//x coordinate
int y;//y coordinate
char mark;
boolean visited;
public Cell(int x, int y, char mark, boolean visited) {
this.x = x;
this.y = y;
this.mark = mark;
this.visited = visited;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Cell){
Cell cell = (Cell) obj;
return this.x == cell.x && this.y == cell.y;
}else{
return super.equals(obj);
}
}
}
class MazeCracker{
Cell[][] cells;
int beginX;//起点x坐标
int beginY;//起点y坐标
int endX;//终点x坐标
int endY;//终点y坐标
int row;
int column;
boolean cracked = false;
Cell[][] result = {};
public MazeCracker(Cell[][] cells, int beginX, int beginY, int endX, int endY, int row, int column) {
this.cells = cells;
this.beginX = beginX;
this.beginY = beginY;
this.endX = endX;
this.endY = endY;
this.row = row;
this.column = column;
}
/**
* make char[][] change into Cell[][]
*
* @param chars 字符数组
* @param row 行
* @param column 列
* @return 单元格的二维数组
*/
public static Cell[][] createMaze(char[][] chars, int row, int column) {
if (checkCharArray(chars, row)) {
int x = 0;
int y = 0;
Cell[][] cells = new Cell[row][column];
for (char[] rows : chars) {
if (rows.length != row) {
throw new IllegalStateException("Incorrect length in the binary array!");
}
for (char ch : rows) {
Cell cell;
//1 表示墙体
if (ch == '1') {
cell = new Cell(x, y, '1', false);
} else {
cell = new Cell(x, y, '0', false);
}
cells[x][y] = cell;
y++;
if (y == column) {
y = 0;
}
}
x++;
if (x == row) {
break;
}
}
return cells;
}else{
throw new IllegalStateException("Illegal character array");
}
}
/**
* check whether the character array is legal simply
*
* @param chars the input character array
* @param row row of input
* @return legal
*/
public static boolean checkCharArray(char[][] chars, int row) {
return chars.length == row;
}
/**
* crack the maze
*
*/
public void crackMaze() {
//initialized the member variables
cracked = false;
//check the start point position and the end point position legalities
if (isPositionLegal(beginX, beginY) && isPositionLegal(endX, endY)) {
int count = 0;
//set the start point position and the end point position
Cell begin = new Cell(beginX, beginY, '0', true);
Cell end = new Cell(endX, endY, '0', true);
Cell[][] cells = this.cells;
//check all possible path
List<Cell> paths;
paths = getAllPossiblePaths(begin);
if (paths.size() == 0){
System.out.println("起点坐标为死路,这个迷宫没有解法");
}else{
Cell newCell;
Stack<Cell> stack = new Stack<>();
stack.push(begin);
loop1: while(!stack.isEmpty()){
loop2: while(true){
//avoid endless loops
if (paths.size() == 0){
newCell = stack.pop();
paths = getAllPossiblePaths(newCell);
}
for (Cell path : paths) {
path.visited = true;
path.mark = '*';
newCell = path;
count++;
System.out.println("第" + count + "次尝试,坐标为:[" + newCell.x + ", " + newCell.y + "]");
stack.push(newCell);
if (newCell.equals(end)) {
cracked = true;
break loop1;
} else {
paths = getAllPossiblePaths(path);
if (paths.size() == 0){
//if there is impossible path,pop the element from the stack and try again
//如果没有可能的路径了,则从栈中弹出栈顶元素重新尝试
newCell.mark = '0';
newCell = stack.pop();
paths = getAllPossiblePaths(newCell);
}else{
break loop2;
}
}
}
}
}
}
//check whether the crack is successful
if (cracked){
//successful
System.out.println("破解迷宫成功");
result = cells;
}else{
//fail
System.out.println("这个迷宫没有解法");
}
}
}
/**
* get all possible paths
*
* @param cell 单元格
* @return 可能的路径
*/
public List<Cell> getAllPossiblePaths(Cell cell) {
int x = cell.x;
int y = cell.y;
ArrayList<Cell> list = new ArrayList<>();
//up
//determine whether cell of upward direction is legal
if (x != row){
Cell u_cell = cells[x + 1][y];
if (!u_cell.visited && u_cell.mark == '0'){
list.add(u_cell);
}
}
//down
//determine whether cell of downward direction is legal
if (x != 0){
Cell d_cell = cells[x - 1][y];
if (!d_cell.visited && d_cell.mark == '0'){
list.add(d_cell);
}
}
//left
//determine whether cell of leftward direction is legal
if (y != 0){
Cell l_cell = cells[x][y - 1];
if (!l_cell.visited && l_cell.mark == '0'){
list.add(l_cell);
}
}
//right
//determine whether cell of rightward direction is legal
if (y != column){
Cell r_cell = cells[x][y + 1];
if (!r_cell.visited && r_cell.mark == '0'){
list.add(r_cell);
}
}
return list;
}
/**
* determine whether the coordinate is legal
*
* @param x x coordinate
* @param y y coordinate
* @return
*/
public boolean isPositionLegal(int x, int y){
return x >= 0 && x <= row && y >= 0 && y <= column;
}
/**
* print the final result if cracked successful, otherwise it prompts that the cracking failed
*
* @param cells cells
*/
public void printResult(Cell[][] cells) {
if (cracked && cells.length != 0){
for (Cell[] rows : cells) {
for (Cell cell : rows) {
char mark = cell.mark;
if (mark == '1'){
mark = 'X';
}
if (mark == '0'){
mark = 'O';
}
System.out.print(mark + " ");
}
System.out.println();
}
}else{
System.out.println("破解失败");
}
}
}
三、控制台反馈
欢迎使用迷宫破解系统
请输入迷宫的行数
10
请输入迷宫的列数
10
迷宫的行数为:10迷宫的列数为:10
接下来请输入迷宫中的每一个单元格
'0'代表可通行,‘1'代表是墙体,不可通行
请输入第1行,共10个元素,中间不要有空格
1111111111
请输入第2行,共10个元素,中间不要有空格
1001110011
请输入第3行,共10个元素,中间不要有空格
1001100101
请输入第4行,共10个元素,中间不要有空格
1000000101
请输入第5行,共10个元素,中间不要有空格
1000011001
请输入第6行,共10个元素,中间不要有空格
1001110001
请输入第7行,共10个元素,中间不要有空格
1000010101
请输入第8行,共10个元素,中间不要有空格
1011000101
请输入第9行,共10个元素,中间不要有空格
1100001001
请输入第10行,共10个元素,中间不要有空格
1111111111
输入完毕,打印迷宫中
1 1 1 1 1 1 1 1 1 1
1 0 0 1 1 1 0 0 1 1
1 0 0 1 1 0 0 1 0 1
1 0 0 0 0 0 0 1 0 1
1 0 0 0 0 1 1 0 0 1
1 0 0 1 1 1 0 0 0 1
1 0 0 0 0 1 0 1 0 1
1 0 1 1 0 0 0 1 0 1
1 1 0 0 0 0 1 0 0 1
1 1 1 1 1 1 1 1 1 1
打印迷宫完毕
请再次输入迷宫的行数
10
请再次输入迷宫的列数
10
请输入起点坐标,前一个是x轴坐标,后一个是y轴坐标,按回车确认
8
8
请输入终点坐标,前一个是x轴坐标,后一个是y轴坐标,按回车确认
1
7
生成迷宫中
破解迷宫中
第1次尝试,坐标为:[7, 8]
第2次尝试,坐标为:[8, 8]
第3次尝试,坐标为:[8, 7]
第4次尝试,坐标为:[6, 8]
第5次尝试,坐标为:[5, 8]
第6次尝试,坐标为:[4, 8]
第7次尝试,坐标为:[3, 8]
第8次尝试,坐标为:[2, 8]
第9次尝试,坐标为:[4, 7]
第10次尝试,坐标为:[5, 7]
第11次尝试,坐标为:[5, 6]
第12次尝试,坐标为:[6, 6]
第13次尝试,坐标为:[7, 6]
第14次尝试,坐标为:[7, 5]
第15次尝试,坐标为:[8, 5]
第16次尝试,坐标为:[8, 4]
第17次尝试,坐标为:[7, 4]
第18次尝试,坐标为:[6, 4]
第19次尝试,坐标为:[6, 3]
第20次尝试,坐标为:[6, 2]
第21次尝试,坐标为:[5, 2]
第22次尝试,坐标为:[4, 2]
第23次尝试,坐标为:[3, 2]
第24次尝试,坐标为:[2, 2]
第25次尝试,坐标为:[1, 2]
第26次尝试,坐标为:[1, 1]
第27次尝试,坐标为:[2, 1]
第28次尝试,坐标为:[3, 1]
第29次尝试,坐标为:[4, 1]
第30次尝试,坐标为:[5, 1]
第31次尝试,坐标为:[6, 1]
第32次尝试,坐标为:[7, 1]
第33次尝试,坐标为:[3, 3]
第34次尝试,坐标为:[4, 3]
第35次尝试,坐标为:[4, 4]
第36次尝试,坐标为:[3, 4]
第37次尝试,坐标为:[3, 5]
第38次尝试,坐标为:[2, 5]
第39次尝试,坐标为:[2, 6]
第40次尝试,坐标为:[3, 6]
第41次尝试,坐标为:[1, 6]
第42次尝试,坐标为:[1, 7]
破解迷宫成功
X X X X X X X X X X
X * * X X X * * X X
X * * X X * * X O X
X * * * * * O X * X
X * * * * X X * * X
X * * X X X * * * X
X * * * * X * X * X
X O X X * * * X * X
X X O O * * X O * X
X X X X X X X X X X