迷宫的基础算法在严蔚敏老师写的数据结构(C语言版)已经描述过了。主要是用栈的特性来保存在迷宫之中走过的路径,走到死胡同后再用栈弹出栈顶,再将指针指到栈顶。这种穷尽的算法有点笨(额 电脑就是这样),貌似也有一些更好的搜索路径,不过现在俺还没接触学习到0 0 ,比如A*算法。记得老师讲过,不过很快就还给老师了。哈哈~~废话少说,现在就是用面向对象的思想来完成走迷宫的这样一个程序。(第一次写,不好勿怪,纯属菜鸟交流~~)
首先可以认为迷宫分为许多个划分为许多个小格子的有序集合(乱扯的。。。),迷宫就是这些小格子组成的。可以认为它们有坐标,x,y;给一个标记Flag来指示该格子是否已经走过(默认为false);因为迷宫之间还应该有一些不能走的地方,称之为墙,那么在用一个IsWall来标识该格子是否是墙。哦,还应该给这个格子标记上行走的方向(这个方向我想应该做为一个枚举)。下面是代码:
- <p> //迷宫中的一个小格子
- public class MazeElem
- {
- //这是方向,初始的方向应该是None~木有方向~
- public Direction dir = Direction.None;
- //构造函数
- public MazeElem(int x,int y,bool isWall)
- {
- _x=x;
- _y=y;
- _isWall=isWall;
- }</p><p> private int _x;
- private int _y;</p><p> //flag的默认值应该是false,假定所有的格子都没有走过
- private bool _flag=false;
- private bool _isWall;
- //X坐标,作为属性了
- public int X
- {
- get { return _x; }
- set { _x = value; }
- }
- //Y坐标,也成属性了0 0
- public int Y
- {
- get { return _y; }
- set { _y = value; }
- }
- //是否墙
- public bool IsWall
- {
- get{return _isWall;}
- set{_isWall=value;}
- }
- //是否走过
- public bool Flag
- {
- get { return _flag; }
- set { _flag = value; }
- }
- }</p>
//迷宫中的一个小格子
public class MazeElem
{
//这是方向,初始的方向应该是None~木有方向~
public Direction dir = Direction.None;
//构造函数
public MazeElem(int x,int y,bool isWall)
{
_x=x;
_y=y;
_isWall=isWall;
}
private int _x;
private int _y;
//flag的默认值应该是false,假定所有的格子都没有走过
private bool _flag=false;
private bool _isWall;
//X坐标,作为属性了
public int X
{
get { return _x; }
set { _x = value; }
}
//Y坐标,也成属性了0 0
public int Y
{
get { return _y; }
set { _y = value; }
}
//是否墙
public bool IsWall
{
get{return _isWall;}
set{_isWall=value;}
}
//是否走过
public bool Flag
{
get { return _flag; }
set { _flag = value; }
}
}
好了,格子类写好了。应该挺容易理解的把。这些都是基础,俺只是想打一下自己的基础~我们再写一个方向的枚举。一个格子的方向很容易想到:东西南北对应(右左下上),除了这些,还应该有这两个:一个是初始的方向,(一开始方向的初始值我想设置为null的,结果报错了。。。。切记。。枚举是值类型。。)还有当这个格子各个方面都无路可走的时候(人若如此只能感叹悲催...),我以为要做一个标记,那就设置NoDir。
- //方向枚举
- public enum Direction
- {
- /// <summary>
- /// 初始值,木有方向
- /// </summary>
- None,
- /// <summary>
- /// 北/上
- /// </summary>
- North,
- /// <summary>
- /// 南/下
- /// </summary>
- South,
- /// <summary>
- /// 西/左
- /// </summary>
- West,
- /// <summary>
- /// 东/右
- /// </summary>
- East,
- /// <summary>
- /// 无放向可走
- /// </summary>
- NoDir
- }
//方向枚举
public enum Direction
{
/// <summary>
/// 初始值,木有方向
/// </summary>
None,
/// <summary>
/// 北/上
/// </summary>
North,
/// <summary>
/// 南/下
/// </summary>
South,
/// <summary>
/// 西/左
/// </summary>
West,
/// <summary>
/// 东/右
/// </summary>
East,
/// <summary>
/// 无放向可走
/// </summary>
NoDir
}
接下来开始写这个迷宫类。可以认为迷宫是一个n X n 个迷宫格子的集合,所以设置一个迷宫格子的的二维数组MazeElem[,];为了指示当前行走的迷宫格子,设置一个MazeElem类型cur变量;当然还要设置一个迷宫的出口end(这里简化一下吧,默认出口就是这个迷宫右下角的那个小格子,入口就是迷宫左上角的格子。。。cur初始值为入口处。。。)。还有栈是必须的,主要是用来保存走过的路径,.NET已经提供一个Stack类,不需要我们自己写(想想用List来写栈好像也不是很难,这是看到某位大神的思路的)。嗯,还要用一对xy变量来保存这个迷宫的大小,方便后面的比较。
- public class Maze
- {
- //很多的格子
- public MazeElem[,] maze;
- //当前格子
- MazeElem cur;
- //这是出口的那个格子
- MazeElem end;
- //用来保存路径的栈
- Stack<MazeElem> path = new Stack<MazeElem>();
- //这两个变量是用来保存迷宫的大小的
- int x, y;
- public Maze()
- {
- }
- //构造函数,这个是最简单木有任何墙出现的空白迷宫。。
- public Maze(int x,int y)
- {
- x = x;
- y = y;
- maze=new MazeElem[x,y];
- //初始化
- for(int i=0;i<x;i++)
- for(int j=0;j<y;j++)
- maze[i,j]=new MazeElem(i,j,false);
- }
- //用一个二维数组来初始化的构造函数- -
- public Maze(int[,] mazeArray)
- {
- //获取数组mazeArray0维中的个数
- x = (int)mazeArray.GetLongLength(0);
- //获取数组mazeArray1维中的个数
- y = (int)mazeArray.GetLongLength(1);
- //初始化maze
- maze = new MazeElem[x, y];
- for (int i = 0; i < x; i++)
- for(int j=0;j<y;j++)
- maze[i, j] = new MazeElem(i, j, mazeArray[i,j]>0?true:false);
- //cur默认为左上角的格子,出口默认为右下角的格子
- cur=maze[0, 0];
- end = maze[x - 1, y - 1];
- }
public class Maze
{
//很多的格子
public MazeElem[,] maze;
//当前格子
MazeElem cur;
//这是出口的那个格子
MazeElem end;
//用来保存路径的栈
Stack<MazeElem> path = new Stack<MazeElem>();
//这两个变量是用来保存迷宫的大小的
int x, y;
public Maze()
{
}
//构造函数,这个是最简单木有任何墙出现的空白迷宫。。
public Maze(int x,int y)
{
x = x;
y = y;
maze=new MazeElem[x,y];
//初始化
for(int i=0;i<x;i++)
for(int j=0;j<y;j++)
maze[i,j]=new MazeElem(i,j,false);
}
//用一个二维数组来初始化的构造函数- -
public Maze(int[,] mazeArray)
{
//获取数组mazeArray0维中的个数
x = (int)mazeArray.GetLongLength(0);
//获取数组mazeArray1维中的个数
y = (int)mazeArray.GetLongLength(1);
//初始化maze
maze = new MazeElem[x, y];
for (int i = 0; i < x; i++)
for(int j=0;j<y;j++)
maze[i, j] = new MazeElem(i, j, mazeArray[i,j]>0?true:false);
//cur默认为左上角的格子,出口默认为右下角的格子
cur=maze[0, 0];
end = maze[x - 1, y - 1];
}
有一些方法还没写出来。先理清一下走迷宫的算法:
判断当前格子是否已经走过,如果没走过(flag=false):设置为已经走过flag=true,该格子入栈,然后选定一个方向尝试走其他格子;如果走过了:检查该格子的方向,如果不是NoDir(NoDir说明四个方向都尝试过了),选定一个方向尝试走其他格子;如果是NoDir,那么将栈顶弹出,并将cur指向栈顶的格子。重复操作直道格当前格子指向迷宫的出口。选定一个方向尝试走其他格子的方法流程是这样的:先切换这个格子的方向,然后根据切换后的方向进行尝试,如果该方向的格子无法走过,那么继续选定一个方向尝试走其他格子,如果可以走过,那么将cur指向该格子。切换这个格子的方向,这个就比较简单~我们顺时针方向切换,开始的是East,向右走(dir为None,我就返回East;如果是East,那我返回South;如果是South,那返回West;如果是West,返回North;如果是North,那返回NoDir)。额,应该说得稍微清楚些把?具体看代码~一下都是Maze里面的方法,想复制的童鞋别复制错地方了0 0
- private void CurGoNextStep(MazeElem me)
- {
- switch (SwitchDirection(me))
- {
- case Direction.East:
- if (me.Y + 1 < y)
- {
- MazeElem next = maze[me.X, me.Y+1];
- //不能为墙,是墙你会走过去么?而且也不能是已经走过的,好马不吃回头草
- if (!next.IsWall && !next.Flag)
- {
- //next.Flag = true;
- cur = next;
- }
- //不行就换个方向把.
- else
- {
- CurGoNextStep(me);
- }
- }
- else
- {
- CurGoNextStep(me);
- }
- break;
- case Direction.South:
- if (me.X + 1 <x)
- {
- MazeElem next = maze[me.X+1, me.Y];
- if (!next.IsWall && !next.Flag)
- {
- //next.Flag = true;
- cur = next;
- }
- else
- {
- CurGoNextStep(me);
- }
- }
- else
- {
- CurGoNextStep(me);
- }
- break;
- case Direction.West:
- if (me.Y - 1>0)
- {
- MazeElem next = maze[me.X, me.Y - 1];
- if (!next.IsWall && !next.Flag)
- {
- //next.Flag = true;
- cur = next;
- }
- else
- {
- CurGoNextStep(me);
- }
- }
- else
- {
- CurGoNextStep(me);
- }
- break;
- case Direction.North:
- if (me.X - 1 >0)
- {
- MazeElem next = maze[me.X - 1, me.Y];
- if (!next.IsWall && !next.Flag)
- {
- //next.Flag = true;
- cur = next;
- }
- else
- {
- CurGoNextStep(me);
- }
- }
- else
- {
- CurGoNextStep(me);
- }
- break;
- case Direction.NoDir:
- //无路可走了已经
- break;
- }
- }
- //转换方向
- private Direction SwitchDirection(MazeElem me)
- {
- switch (me.dir)
- {
- case Direction .None:
- me.dir = Direction.East;
- return Direction.East;
- break;
- case Direction.East:
- me.dir = Direction.South;
- return Direction.South;
- break;
- case Direction.South:
- me.dir = Direction.West;
- return Direction.West;
- break;
- case Direction.West:
- me.dir = Direction.North;
- return Direction.North;
- break;
- case Direction.North:
- me.dir = Direction.NoDir;
- return Direction.NoDir;
- break;
- }
- return Direction.None;
- }
private void CurGoNextStep(MazeElem me)
{
switch (SwitchDirection(me))
{
case Direction.East:
if (me.Y + 1 < y)
{
MazeElem next = maze[me.X, me.Y+1];
//不能为墙,是墙你会走过去么?而且也不能是已经走过的,好马不吃回头草
if (!next.IsWall && !next.Flag)
{
//next.Flag = true;
cur = next;
}
//不行就换个方向把.
else
{
CurGoNextStep(me);
}
}
else
{
CurGoNextStep(me);
}
break;
case Direction.South:
if (me.X + 1 <x)
{
MazeElem next = maze[me.X+1, me.Y];
if (!next.IsWall && !next.Flag)
{
//next.Flag = true;
cur = next;
}
else
{
CurGoNextStep(me);
}
}
else
{
CurGoNextStep(me);
}
break;
case Direction.West:
if (me.Y - 1>0)
{
MazeElem next = maze[me.X, me.Y - 1];
if (!next.IsWall && !next.Flag)
{
//next.Flag = true;
cur = next;
}
else
{
CurGoNextStep(me);
}
}
else
{
CurGoNextStep(me);
}
break;
case Direction.North:
if (me.X - 1 >0)
{
MazeElem next = maze[me.X - 1, me.Y];
if (!next.IsWall && !next.Flag)
{
//next.Flag = true;
cur = next;
}
else
{
CurGoNextStep(me);
}
}
else
{
CurGoNextStep(me);
}
break;
case Direction.NoDir:
//无路可走了已经
break;
}
}
//转换方向
private Direction SwitchDirection(MazeElem me)
{
switch (me.dir)
{
case Direction .None:
me.dir = Direction.East;
return Direction.East;
break;
case Direction.East:
me.dir = Direction.South;
return Direction.South;
break;
case Direction.South:
me.dir = Direction.West;
return Direction.West;
break;
case Direction.West:
me.dir = Direction.North;
return Direction.North;
break;
case Direction.North:
me.dir = Direction.NoDir;
return Direction.NoDir;
break;
}
return Direction.None;
}
- //开始走迷宫
- public void Start()
- {
- while (cur != end)
- {
//开始走迷宫
public void Start()
{
while (cur != end)
{
- //如果没走过
- if (!cur.Flag)
- {
- //留下痕迹
- cur.Flag = true;
- //节点入栈
- path.Push(maze[cur.X,cur.Y]);
- //换方向走
//如果没走过
if (!cur.Flag)
{
//留下痕迹
cur.Flag = true;
//节点入栈
path.Push(maze[cur.X,cur.Y]);
//换方向走
- CurGoNextStep(cur);
- }
- else
- {
CurGoNextStep(cur);
}
else
{
- //如果不是无路可走的情况
- if (cur.dir != Direction.NoDir)
- CurGoNextStep(cur);
- else//无路可走了~~那就回头...
- {
- if (path.Count != 0)
- //栈顶弹出
- path.Pop();
- if (path.Count != 0)
- {
- //更新cur
- cur = path.Peek();
- }
- }
- }
- }
- }
//如果不是无路可走的情况
if (cur.dir != Direction.NoDir)
CurGoNextStep(cur);
else//无路可走了~~那就回头...
{
if (path.Count != 0)
//栈顶弹出
path.Pop();
if (path.Count != 0)
{
//更新cur
cur = path.Peek();
}
}
}
}
}
就这么多吧。可以测试一下:
- //用数组简单表示一个迷宫,大于0则表示该格子为墙
- int[,] array = new int[5, 5] { {0,0,1,0,0 }, {0,1,0,0,0}, {0,1,0,0,0},{ 0,1,0,0,0},{0,0,0,1,0}};
//用数组简单表示一个迷宫,大于0则表示该格子为墙
int[,] array = new int[5, 5] { {0,0,1,0,0 }, {0,1,0,0,0}, {0,1,0,0,0},{ 0,1,0,0,0},{0,0,0,1,0}};
大概是下面这样的图~~这编辑器有点蛋疼,直接用表格画了(黑色表示墙),左上角和右下角分别为入口和出口(黄色的....)
- //构造迷宫
- Maze m = new Maze(array);
- //开始走迷宫
- m.Start();
- //打印出迷宫中走的方向
- if (m.maze != null)
- {
- int tmp = 0;
- foreach (MazeElem me in m.maze)
- {
- tmp++;
- Console.Write(me.dir +" ");
- if (tmp % 5 == 0)
- Console.WriteLine();
- }
- }
//构造迷宫
Maze m = new Maze(array);
//开始走迷宫
m.Start();
//打印出迷宫中走的方向
if (m.maze != null)
{
int tmp = 0;
foreach (MazeElem me in m.maze)
{
tmp++;
Console.Write(me.dir +" ");
if (tmp % 5 == 0)
Console.WriteLine();
}
}
结果如下:
感觉面向对象是好理解些,不过我不是很能够体现出面向对象的好处