1.生成迷宫
采用不相交集生成迷宫,主要思路:
<1>生成n*n个迷宫房间;
<2>不断拆房间的墙,直到入口和出口连通(不相交集)。
注意:为了产生迷宫效果,拆掉的不应该有规律,所以应该是随机产生一个房间号,再随机拆掉一堵墙。
//迷宫房间格数
//
int m = 50, n = 50;
//迷宫房间大小
//
int size = 10;
//迷宫起点距离窗口左上角距离d*size
//
int d = 10;
//窗口尺寸
//
int width = 700;
int height = 700;
Bitmap image;
public void Paint_Maze()
{
image = new Bitmap(width, height);
Graphics g = Graphics.FromImage(image);
//渲染背景
//
g.FillRectangle(new SolidBrush(Color.White), 0, 0, width, height);
//绘制迷宫房间
//
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
g.DrawRectangle(new Pen(new SolidBrush(Color.Black),1), new Rectangle((i + d) * size, (j + d) * size, size, size));
}
}
//绘制入口、出口,其中加一是为了拆掉墙时,在与其相交的墙上不会留下断开的现象
//
g.DrawLine(new Pen(new SolidBrush(Color.White), 1), new Point(d * size, d * size), new Point((d + 1) * size-1, d * size)); //入口横墙
g.DrawLine(new Pen(new SolidBrush(Color.White), 1), new Point(d * size, d * size), new Point(d * size, (d + 1) * size-1)); //入口竖墙
g.DrawLine(new Pen(new SolidBrush(Color.White), 1), new Point(((int)((m * n - 1) % n) + d) * size+1, ((int)((m * n - 1) / n) + 1 + d) * size), new Point(((int)((m * n - 1) % n) + 1 + d) * size, ((int)((m * n - 1) / n) + 1 + d) * size)); //出口横墙
g.DrawLine(new Pen(new SolidBrush(Color.White), 1), new Point(((int)((m * n - 1) % n) + 1 + d) * size, ((int)((m * n - 1) / n) + d) * size+1), new Point(((int)((m * n - 1) % n) + 1 + d) * size, ((int)((m * n - 1) / n) + 1 + d) * size)); //出口竖墙
//不相交集
//
ADT adt = new ADT();
adt.InitSet(m * n);
Random random = new Random();
while (adt.Find(0) != adt.Find(m * n - 1))
{
//随机产生一个房间号
//
int room_a = random.Next(m * n);
List<int> list=new List<int>();
//检查房间a相邻四间房子a-n(上)、a+n(下)、a-1(左)、a+1(右)是否存在
//
if (room_a - n >= 0)
{
list.Add(room_a - n);
}
if (room_a + n < m * n)
{
list.Add(room_a + n);
}
if (room_a - 1 >= ((int)(room_a / n)) * n)
{
list.Add(room_a - 1);
}
if (room_a + 1 < ((int)(room_a / n) + 1) * n)
{
list.Add(room_a + 1);
}
//随机产生一个相邻的房间
//
int index = random.Next(list.Count);
int room_b = list[index];
//检查房间是否连通
//
if (adt.Find(room_a) == adt.Find(room_b))
continue;
else
{
int root_a = adt.Find(room_a);
int root_b = adt.Find(room_b);
//记录哪个房间序号小,靠近入口
//
int room_min = Math.Min(room_a, room_b);
//定义即将拆掉的墙的坐标
//
int x1, x2, y1, y2;
//相差1,拆竖墙;否则拆横墙
//
if (Math.Abs(room_a - room_b) == 1)
{
//拆竖墙
if (room_a == room_min) //room_a在room_b前面
{
x1 = ((int)(room_a % n) + 1+d) * size;
y1 = ((int)(room_a / n) + d) * size + 1;
x2 = x1;
y2 = ((int)(room_a / n) + 1 + d) * size - 1;
}
else
{
x1 = ((int)(room_b % n) + 1+d) * size;
y1 = ((int)(room_b / n) + d) * size + 1;
x2 = x1;
y2 = ((int)(room_b / n) + 1 + d) * size - 1;
}
}
else //拆横墙
{
if (room_a == room_min) //room_a在room_b上面
{
x1 = ((int)(room_a % n) + d) * size + 1;
y1 = ((int)(room_a / n)+1+d) * size;
x2 = ((int)(room_a % n) + 1 + d) * size - 1;
y2 = y1;
}
else
{
x1 = ((int)(room_b % n)+d) * size+1;
y1 = ((int)(room_b / n) + 1+d) * size;
x2 = ((int)(room_b % n) +1+ d) * size - 1;
y2 = y1;
}
}
//将room_a和room_b连通
//
adt.Union(root_a, root_b);
g.DrawLine(new Pen(new SolidBrush(Color.White), 1), new Point(x1, y1), new Point(x2, y2));
}
}
//显示图像
//
pictureBox1.Image = image;
image.Save("maze.bmp");
}
生成的迷宫(50*50):
2.解迷宫
生成完迷宫后,自然会想怎么去解迷宫,在学完广度优先搜索算法,这个问题便迎刃而解。
思路:找出每个房间与入口(或出口)的路径,具体算法参照广度优先搜索算法,在此不详述。
<1>检测迷宫的连通性
private Graphic_List DetectMaze(Bitmap bitmap)
{
int house_num=m*n;
Graphic_List g_list = new Graphic_List(house_num, true);
Vertex cur_vertex;
Color c = new Color();
bool IsConnect = true;
//横向连通检测
//
for (int j = 0; j < n; j++)
{
cur_vertex = new Vertex((j*n).ToString());
g_list.AddVertex(cur_vertex); //将左边第一个房间添加至邻接表中
for (int i = 1; i < m; i++)
{
IsConnect = true;
cur_vertex = new Vertex((j*n+i).ToString());
g_list.AddVertex(cur_vertex);
for (int k = 0; k < size; k++)
{
c = bitmap.GetPixel(k + (int)((d + i - 0.5) * size), (int)((d + j + 0.5) * size));
if (c.G == 0) //根据G分量判断是否连通
{
IsConnect = false;
break;
}
}
if (IsConnect) //有问题,不能只添加单向边
{
g_list.AddDirectedEdge(new Vertex((j * n + i - 1).ToString()), new Vertex((j * n + i).ToString()));
g_list.AddDirectedEdge(new Vertex((j * n + i).ToString()), new Vertex((j * n + i - 1).ToString()));
}
}
}
//纵向连通检测
//
for (int i = 0; i < m; i++) //横向计数器
{
for (int j = 1; j < n; j++) //纵向计数器
{
IsConnect = true;
cur_vertex = new Vertex((j * m + i).ToString());
for (int k = 0; k < size; k++)
{
c = bitmap.GetPixel((int)((d + i + 0.5) * size), (int)((d + j - 0.5) * size) + k);
if (c.G == 0) //判断是否连通
{
IsConnect = false;
break;
}
}
if (IsConnect)
{
g_list.AddDirectedEdge(new Vertex(((j - 1) * m + i).ToString()), new Vertex((j * m + i).ToString()));
g_list.AddDirectedEdge(new Vertex((j * m + i).ToString()), new Vertex(((j - 1) * m + i).ToString()));
}
}
}
return g_list;
}
<2>用于初始化广度优先搜索算法中table的函数
private void InitTable(Graphic_List g_list, Vertex start_vertex, Table[] table)
{
for (int i = 0; i < g_list.Items.Count; i++)
{
table[i] = new Table();
table[i].Name = g_list.Items[i].Name;
table[i].Dist = double.PositiveInfinity;
table[i].Known = false;
table[i].Path = null;
}
if (table[g_list.GetIndex(start_vertex)].Name == start_vertex.Name)
{
table[g_list.GetIndex(start_vertex)].Dist = 0;
}
else
{
throw new Exception("表初始化与图不一致!");
}
}
<3>广度优先搜索算法
/// <summary>
/// 广度优先搜索算法
/// </summary>
/// <param name="g_list">待搜索的图</param>
/// <param name="start_vertex">搜索起点</param>
/// <param name="table">搜索结果表</param>
private void BFS(Graphic_List g_list, Vertex start_vertex, Table[] table)
{
int NumVertex = g_list.Items.Count;
Queue<Vertex> queue;
Vertex current_vertex, adjacent_vertex;
queue = Queue<Vertex>.CreateQueue(NumVertex);
Queue<Vertex>.MakeEmpty(queue);
Queue<Vertex>.Enqueue(g_list.Items[g_list.GetIndex(start_vertex)], queue);
while (!Queue<Vertex>.IsEmpty(queue))
{
current_vertex = Queue<Vertex>.Dequeue(queue);
table[g_list.GetIndex(current_vertex)].Known = true;
adjacent_vertex = current_vertex;
while (adjacent_vertex.NextVertex != null)
{
adjacent_vertex = adjacent_vertex.NextVertex;
if (table[g_list.GetIndex(adjacent_vertex)].Dist == double.PositiveInfinity)
{
table[g_list.GetIndex(adjacent_vertex)].Dist = table[g_list.GetIndex(current_vertex)].Dist + 1;
table[g_list.GetIndex(adjacent_vertex)].Path = current_vertex;
Queue<Vertex>.Enqueue(g_list.Items[g_list.GetIndex(adjacent_vertex)], queue);
}
}
}
}
<4>绘制迷宫路径
private void DrawPath(Table[] table, Point in_point, Point out_point)
{
Bitmap bitmap = (Bitmap)pictureBox1.Image;
Graphics g = Graphics.FromImage(bitmap);
int cur_point_index = (int)((out_point.X - d * size) / size) + m * (int)((out_point.Y - d * size) / size); //出口在table中的索引
Point cur_point, forward_point;
while (table[cur_point_index].Path != null)
{
string foward_point_name = table[cur_point_index].Path.Name; //当前点的路径,前一点
int forward_point_index = Int32.Parse(foward_point_name); //转换成数字
cur_point = new Point((int)((cur_point_index % m + d + 0.5) * size), (int)((cur_point_index / m + d + 0.5) * size));
forward_point = new Point((int)((forward_point_index % m + d + 0.5) * size), (int)((forward_point_index / m + d + 0.5) * size));
g.DrawLine(new Pen(new SolidBrush(Color.Green), 2), cur_point, forward_point);
cur_point_index = forward_point_index;
pictureBox1.Image = bitmap;
}
}
<5>其他类或函数
Table:
class Table
{
public string Name;
public bool Known;
public double Dist;
public Vertex Path;
public Table()
{
Name = null;
Known = false;
Dist = double.PositiveInfinity;
Path = null;
}
public static int GetIndex(Table[] table,string name)
{
int Index = -1;
for (int i = 0; i < table.Length; i++)
{
if (table[i].Name == name)
{
Index = i;
break;
}
}
return Index;
}
}
队列:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Maze
{
class Queue <T>
{
private int Capacity; //队列容量
private int Size; //当前队列中元素个数
private int Front; //队头
private int Rear; //队尾
private T[] Data; //队列数组
//创建队列
//
public static Queue<T> CreateQueue(int numElement)
{
Queue<T> queue=new Queue<T>();
queue.Capacity=numElement;
queue.Size=0;
queue.Front=0;
queue.Rear=0;
queue.Data=new T[numElement];
return queue;
}
//检查队列是否为空
//
public static bool IsEmpty(Queue<T> queue)
{
return queue.Size == 0;
}
//检查队列是否是满的
//
public static bool IsFull(Queue<T> queue)
{
return queue.Size == queue.Capacity;
}
//清空队列
//
public static void MakeEmpty(Queue<T> queue)
{
queue.Size = 0;
queue.Front = 0;
queue.Rear = 0;
}
//入队
//
public static void Enqueue(T X, Queue<T> queue)
{
if (IsFull(queue))
{
throw new Exception("Full queue");
}
else
{
queue.Size++;
queue.Data[queue.Rear] = X;
queue.Rear++;
queue.Rear %= queue.Capacity;
}
}
//出队
//
public static T Dequeue(Queue<T> queue)
{
if (IsEmpty(queue))
{
throw new Exception("Empty queue");
}
else
{
T t;
queue.Size--;
t=queue.Data[queue.Front];
queue.Front++;
queue.Front%=queue.Capacity;
return t;
}
}
//查询当前队头元素
//
public static T GetFront(Queue<T> queue)
{
if (!IsEmpty(queue))
{
return queue.Data[queue.Front];
}
else
{
throw new Exception("Empty queue");
}
}
//查询当前队尾元素
//
public static T GetRear(Queue<T> queue)
{
if (!IsEmpty(queue))
{
return queue.Data[queue.Rear];
}
else
{
throw new Exception("Empty queue");
}
}
}
}
图的表示:http://blog.csdn.net/wj080211140/article/details/21442255
解迷宫结果:
C#中指针用起来不是特别方便,在存储相邻顶和路径是用的嵌套,导致消耗内存过大,这个地方应该在整理修改一下。
对于迷宫问题,想进一步做成一个迷宫游戏或者解非本程序生成的迷宫。