.
此篇為前一篇走迷宮的進階應用
前一篇需要人工手動制造迷宮,如果迷宮很大就會不容易進行製作
製作迷宮的方式可以用很多演算法 例如 深度 / 廣度優先演算法
十字分割演算法、 還有本篇所使用的 普林(Prim)演算法
使用不同的演算法,產生的迷宮複雜度均會有所不同
普林演算法
圖論中的一種演算法,可在加權連通圖里搜索最小生成樹。意即由此演算法搜索到的邊子集所構成的樹中,不但包括了連通圖裡的所有頂點,且其所有邊的權值之和亦為最小。該演算法於1930年由捷克數學家沃伊捷赫·亞爾尼克發現;並在1957年由美國計算機科學家羅伯特·普林獨立發現;1959年,艾茲格·迪科斯徹再次發現了該演算法。因此,在某些場合,普林演算法又被稱為DJP演算法、亞爾尼克演算法或普林-亞爾尼克演算法。
產生迷宮的原理很簡單,一開始先預設一個全部都是牆壁的迷宮
然後開始隨機選擇一做牆進行挖掘,挖掘完成後在找隨機一面相鄰的牆壁進行挖掘
重複上述步驟直到全部打通
如下面順序進行挖掘
依此類推
最後附上實作程式碼
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Windows.Forms;
- using System.Collections;
- using System.Threading;
-
- namespace Maze
- {
- public partial class Form1 : Form
- {
- int[,] _maze;
- int[] _maze_F;
- int _size = 23;
- int _maze_index = 0;
- int _step_count = 0;
- bool _is_exit = false;
- bool _first = false;
- bool _is_dead = false;
-
- private delegate void UpdateUI(parameter.status current, Control ctl);
- private delegate void UpdateText(parameter.status current, String str, Control ctl);
- parameter _para = new parameter();
-
- private Label[] _label_wall;
- private Label[] _label_path;
- private Label[] _label_step;
- private Label[] _label_obj = new Label[1];
- private Label[] _label_Exit = new Label[1];
- private Panel _panel;
- private int _label_wall_x = 30, _label_wall_y = 30, _label_path_x = 30,
- _label_path_y = 30,
- _label_obj_x = 90, _label_obj_y = 60;
- int _label_step_index = 0;
- Stack _st = new Stack();
- private Thread _th;
- //private delegate void UpdateUI(parameter.status current, Control ctl);
- int pre_x;
- int pre_y;
- bool first = true;
- int[] _record;
- int index = 1;
- int r_count = 0, l_count = 0, u_count = 0, d_count = 0, _pre_index = 0;
- bool _check = true;
-
- public Form1()
- {
- InitializeComponent();
- }
-
- private void Form1_Load(object sender, EventArgs e)
- {
-
- this.comboBox1.Items.Clear();
-
- for (int i = 3; i <= _para.max_size; i+=2)
- {
- this.comboBox1.Items.Add(new dropdownList(i.ToString(), i));
- }
-
- this.comboBox1.SelectedIndex = 0;
- }
-
- private void generate_maze()
- {
-
- for (int i = 0; i < _size; i++)
- {
- for (int j = 0; j < _size; j++)
- {
- _maze_F[_maze_index] = _maze[i, j];
- if ((_maze_index + 1) % (_size) == 0)
- {
- _maze_F[_maze_index] = 1;
- }
- _maze_index++;
- }
-
- }
-
- //設定最後一排牆壁
- for (int ch = 1; ch < _size * _size; ch++)
- {
- if (ch / (_size - 1) == _size)
- {
- for (int i = (_size * (_size - 1)); i < (_size * (_size - 1)) + (_size - 1); i++)
- {
- if (i == (_size * (_size - 1)) + (_size - 2))
- {
- _maze_F[i] = 0; // 出口
- }
- else
- {
- _maze_F[i] = 1; //牆
- }
- }
- }
- }
-
- _label_obj[0] = new Label();
- _label_obj[0].AutoSize = false;
- _label_obj[0].Width = 80;
- _label_obj[0].Height = 30;
- _label_obj[0].Text = "";
- _label_obj[0].Font = new Font("Verdana", 12, FontStyle.Bold);
- _label_obj[0].ForeColor = Color.Orange;
- _label_obj[0].BackColor = Color.Lime;
- _label_obj[0].Location = new Point(_label_obj_x, _label_obj_y);
- this.Controls.Add(_label_obj[0]);
- _label_obj[0].BringToFront();
- for (int i = 0; i < _size * _size; i++)
- {
- if (i % _size == 0)
- {
- _label_wall_x = 30;
- _label_wall_y += 30;
-
- _label_path_x = 30;
- _label_path_y += 30;
- }
-
- //產生牆壁
- if (_maze_F[i] == 1)
- {
- _label_wall[i] = new Label();
-
- _label_wall[i].AutoSize = false;
- _label_wall[i].Width = 80;
- _label_wall[i].Height = 30;
- _label_wall[i].Text = "-";
- _label_wall[i].Font = new Font("Verdana", 12, FontStyle.Bold);
- _label_wall[i].ForeColor = Color.Orange;
- _label_wall[i].BackColor = Color.Black;
- _label_wall[i].Location = new Point(_label_wall_x, _label_wall_y);
- this.Controls.Add(_label_wall[i]);
- //_label_wall[i].BringToFront();
- _label_wall_x += 60;
- _label_path_x += 60;
- //_panel.Controls.Add(_label_wall[i]);
- }
-
- //產生通道
- else if (_maze_F[i] == 0)
- {
- _label_path[i] = new Label();
-
- _label_path[i].AutoSize = false;
- _label_path[i].Width = 80;
- _label_path[i].Height = 30;
- _label_path[i].Text = "";
- _label_path[i].Font = new Font("Verdana", 12, FontStyle.Bold);
- _label_path[i].ForeColor = Color.Orange;
- _label_path[i].BackColor = Color.Orange;
- _label_path[i].Location = new Point(_label_path_x, _label_path_y);
- this.Controls.Add(_label_path[i]);
- _label_wall_x += 60;
- _label_path_x += 60;
- }
- }
-
- _th = new Thread(walking);
- _th.Start();
- }
-
- private void walking()
- {
- //顯示初始座標
- updatePath(parameter.status.Running, _label_path[0]);
- updatePath(parameter.status.Running, _label_obj[0]);
- //定義初始移動位置與方向
- _pre_index = index;
- r_count = _pre_index += 1;
- _pre_index = index;
- d_count = _pre_index += _size;
- _pre_index = index;
- l_count = _pre_index -= 1;
- _pre_index = index;
- u_count = _pre_index -= _size;
-
- while ((r_count < _maze.Length) && (d_count < _maze.Length) && (l_count <
- _maze.Length) && (u_count < _maze.Length) && !_is_dead)
- {
- if (_maze_F[r_count] == 0)
- {
- _check = true; //有路
-
- if (_check)
- {
- _st.Push(r_count);//在 stack 加進一格的座標
- }
-
- _label_obj_x += 60; //向右走
- _pre_index = r_count;
- _maze_F[r_count] = 1; // 走過
- updatePath(parameter.status.Running, _label_path[0]);
- updatePath(parameter.status.Running, _label_obj[0]);
- }
-
- else if (_maze_F[d_count] == 0)
- {
- _check = true; //有路
-
- if (_check)
- {
- _st.Push(d_count);//在 stack 加進一格的座標
- }
-
- _label_obj_y += 30; //向下走
- _pre_index = d_count;
- _maze_F[d_count] = 1; // 走過
- updatePath(parameter.status.Running, _label_path[0]);
- updatePath(parameter.status.Running, _label_obj[0]);
- }
-
- else if (_maze_F[l_count] == 0)
- {
- _check = true; //有路
- if (_check)
- {
- _st.Push(l_count);//在 stack 加進一格的座標
- }
- _label_obj_x -= 60; //向左走
- _pre_index = l_count;
- _maze_F[l_count] = 1; // 走過
- updatePath(parameter.status.Running, _label_path[0]);
- updatePath(parameter.status.Running, _label_obj[0]);
- }
- else if (_maze_F[u_count] == 0)
- {
- _check = true; //有路
-
- if (_check)
- {
- _st.Push(u_count);//在 stack 加進一格的座標
- }
- _label_obj_y -= 30; //向上走
- _pre_index = u_count;
- _maze_F[u_count] = 1; // 走過
- updatePath(parameter.status.Running, _label_path[0]);
- updatePath(parameter.status.Running, _label_obj[0]);
- }
- else
- {
- _check = false;
- index = (int)_st.Peek();//三面都圍牆時 將當前位置設定為牆
- _maze_F[index] = 1; // 走過
-
- _st.Pop();//在 stack 移除後退的格子的座標,退到前一步
- try
- {
- index = (int)_st.Peek();
- }
- catch
- {
- updatePath(parameter.status.Dead, _label_obj[0]);
- updateText(parameter.status.Dead,"No Exit", textBox1);
- _st.Clear();
- _th.Abort();
- //MessageBox.Show("not found exit");
- break;
- }
- _maze_F[index] = 0;
-
- if (_maze_F[index] == 0) //更新移動路徑
- {
- updatePath(parameter.status.Running, _label_path[0]);
- updatePath(parameter.status.Running, _label_obj[0]);
- }
- }
-
- //更新往四個方向座標
- r_count = _pre_index + 1;
- d_count = _pre_index + _size;
- l_count = _pre_index - 1;
- u_count = _pre_index - _size;
-
- if (u_count == (_size * (_size - 1)) + (_size-2))
- {
- _is_exit = true;
- break;
- }
- _step_count++;
- updateText(parameter.status.Running,"running", textBox1);
- updateText(parameter.status.Running,_step_count.ToString(), textBox2);
- Thread.Sleep(50);
- }
-
- if (_is_exit)
- {
- updatePath(parameter.status.Exit, _label_obj[0]);
- updateText(parameter.status.Exit,"Find Exit", textBox1);
- //MessageBox.Show("Find Exit");
- updateText(parameter.status.button_on, "start", button1);
- _first = true;
- _st.Clear();
- _th.Abort();
-
- }
- }
-
- private void updatePath(parameter.status current, Control ctl)
- {
- if (this.InvokeRequired)
- {
- UpdateUI uu = new UpdateUI(updatePath);
- this.Invoke(uu, current, ctl);
- }
- else
- {
- if (current == parameter.status.Exit)
- {
- _label_Exit[0] = new Label();
- _label_Exit[0].AutoSize = false;
- _label_Exit[0].Width = 80;
- _label_Exit[0].Height = 30;
- _label_Exit[0].Text = "";
- _label_Exit[0].Font = new Font("Verdana", 12, FontStyle.Bold);
- _label_Exit[0].ForeColor = Color.Orange;
- _label_Exit[0].BackColor = Color.Red;
- _label_Exit[0].Location = new Point(_label_obj_x, _label_obj_y);
- this.Controls.Add(_label_Exit[0]);
- _label_Exit[0].BringToFront();
- }
- else if (current == parameter.status.Dead)
- {
- _label_Exit[0] = new Label();
- _label_Exit[0].AutoSize = false;
- _label_Exit[0].Width = 80;
- _label_Exit[0].Height = 30;
- _label_Exit[0].Text = "";
- _label_Exit[0].Font = new Font("Verdana", 12, FontStyle.Bold);
- _label_Exit[0].ForeColor = Color.Orange;
- _label_Exit[0].BackColor = Color.Yellow;
- _label_Exit[0].Location = new Point(_label_obj_x, _label_obj_y);
- this.Controls.Add(_label_Exit[0]);
- _label_Exit[0].BringToFront();
- button1.Enabled = true;
- }
- else
- {
- if (!first)
- {
- _label_step[_label_step_index] = new Label();
- _label_step[_label_step_index].AutoSize = false;
- _label_step[_label_step_index].Width = 80;
- _label_step[_label_step_index].Height = 30;
- _label_step[_label_step_index].Text = "";
- _label_step[_label_step_index].Font = new Font("Verdana", 12,
- FontStyle.Bold);
- _label_step[_label_step_index].ForeColor = Color.Orange;
- _label_step[_label_step_index].BackColor = Color.Blue;
- _label_step[_label_step_index].Location = new Point(pre_x, pre_y);
- this.Controls.Add(_label_step[_label_step_index]);
- _label_step[_label_step_index].BringToFront();
- _label_step_index++;
- }
- }
- _label_obj[0].Location = new Point(_label_obj_x, _label_obj_y);
- this.Controls.Add(_label_obj[0]);
- _label_obj[0].BringToFront();
- pre_x = _label_obj_x;
- pre_y = _label_obj_y;
- first = false;
- }
-
- }
- }
-
- private void updateText(parameter.status current, String str, Control ctl)
- {
- if (this.InvokeRequired)
- {
- UpdateText uu = new UpdateText(updateText);
- this.Invoke(uu, current, str, ctl);
- }
- else
- {
- ctl.Text = str;
-
- if (current == parameter.status.Exit)
- {
- ctl.BackColor = Color.Lime;
- }
- else if (current == parameter.status.Running)
- {
- ctl.BackColor = Color.Orange;
- }
- else if (current == parameter.status.Dead)
- {
- ctl.BackColor = Color.Yellow;
- }
- else if (current == parameter.status.button_on)
- {
- ctl.Enabled = true;
- }
-
- }
- }
-
- private int[,] Prim(int startX, int startY, int widthLimit, int heightLimit, bool
- haveBorder)
- {
- //block:不可通行 unBlock:可通行
- const int block = 1, unBlock = 0;
- var r = new Random();
- //迷宮尺寸合法化
- if (widthLimit < 1)
- widthLimit = 1;
- if (heightLimit < 1)
- heightLimit = 1;
- //迷宮起點合法化
- if (startX < 0 || startX >= widthLimit)
- startX = r.Next(0, widthLimit);
- if (startY < 0 || startY >= heightLimit)
- startY = r.Next(0, heightLimit);
- //減去邊框所佔的格子
- if (!haveBorder)
- {
- widthLimit--;
- heightLimit--;
- }
- //迷宮尺寸換算成帶牆尺寸
- widthLimit *= 1;
- heightLimit *= 1;
- //迷宮起點換算成帶牆起點
- startX *= 2;
- startY *= 2;
- if (haveBorder)
- {
- startX++;
- startY++;
- }
- //產生空白迷宮
- var mazeMap = new int[widthLimit + 1, heightLimit + 1];
- for (int x = 0; x <= widthLimit; x++)
- {
- //mazeMap.Add(new BitArray(heightLimit + 1));
- for (int y = 0; y <= heightLimit; y++)
- {
- mazeMap[x, y] = block;
- }
- }
-
- //鄰牆列表
- var blockPos = new List<int>();
- //將起點作為目標格
- int targetX = startX, targetY = startY;
- //將起點標記為通路
- mazeMap[targetX, targetY] = unBlock;
-
- //記錄鄰牆
- if (targetY > 1)
- {
- blockPos.AddRange(new int[] { targetX, targetY - 1, 0 });
- }
- if (targetX < widthLimit)
- {
- blockPos.AddRange(new int[] { targetX + 1, targetY, 1 });
- }
- if (targetY < heightLimit)
- {
- blockPos.AddRange(new int[] { targetX, targetY + 1, 2 });
- }
- if (targetX > 1)
- {
- blockPos.AddRange(new int[] { targetX - 1, targetY, 3 });
- }
- while (blockPos.Count > 0)
- {
- //隨機選一堵牆
- var blockIndex = r.Next(0, blockPos.Count / 3) * 3;
- //找到牆對面的牆
- if (blockPos[blockIndex + 2] == 0)
- {
- targetX = blockPos[blockIndex];
- targetY = blockPos[blockIndex + 1] - 1;
- }
- else if (blockPos[blockIndex + 2] == 1)
- {
- targetX = blockPos[blockIndex] + 1;
- targetY = blockPos[blockIndex + 1];
- }
- else if (blockPos[blockIndex + 2] == 2)
- {
- targetX = blockPos[blockIndex];
- targetY = blockPos[blockIndex + 1] + 1;
- }
- else if (blockPos[blockIndex + 2] == 3)
- {
- targetX = blockPos[blockIndex] - 1;
- targetY = blockPos[blockIndex + 1];
- }
- //如果目標格未連通
- if (mazeMap[targetX, targetY] == block)
- {
- //聯通目標格
- mazeMap[blockPos[blockIndex], blockPos[blockIndex + 1]] = unBlock;
- mazeMap[targetX, targetY] = unBlock;
- //添加目標格相鄰格
- if (targetY > 1 && mazeMap[targetX, targetY - 1] == block &&
- mazeMap[targetX, targetY - 2] == block)
- {
- blockPos.AddRange(new int[] { targetX, targetY - 1, 0 });
- }
- if (targetX < widthLimit && mazeMap[targetX + 1, targetY] == block &&
- mazeMap[targetX + 2, targetY] == block)
- {
- blockPos.AddRange(new int[] { targetX + 1, targetY, 1 });
- }
- if (targetY < heightLimit && mazeMap[targetX, targetY + 1] == block &&
- mazeMap[targetX, targetY + 2] == block)
- {
- blockPos.AddRange(new int[] { targetX, targetY + 1, 2 });
- }
- if (targetX > 1 && mazeMap[targetX - 1, targetY] == block &&
- mazeMap[targetX - 1, targetY] == block)
- {
- blockPos.AddRange(new int[] { targetX - 1, targetY, 3 });
- }
- }
- blockPos.RemoveRange(blockIndex, 3);
- }
- return mazeMap;
- }
-
- private void button1_Click(object sender, EventArgs e)
- {
- if (_first)
- {
- gc_collect();
- }
-
- dropdownList ddL = (dropdownList)this.comboBox1.SelectedItem;
- button1.Enabled = false;
- _size = Int32.Parse(ddL.value.ToString());
- //_size = Int32.Parse(textBox1.Text);
- //---------init component
- init_component();
- generate_maze();
- }
-
- private void init_component()
- {
- _maze = new int[_size, _size];
- _maze = Prim(1, 1, _size, _size, true);
- _maze_F = new int[_size * _size * 2];
- _label_wall = new Label[_size * _size * 2];
- _label_path = new Label[_size * _size * 2];
- _label_step = new Label[_size * _size * 4];
- //_st = new Stack();
- _record = new int[_size * _size * 2];
- _maze_index = 0;
- _step_count = 0;
- _is_dead = false;
- _is_exit = false;
- first = true;
-
- index = 1;
- r_count = 0;
- l_count = 0;
- u_count = 0;
- d_count = 0;
- _pre_index = 0;
- _label_wall_x = 30;
- _label_wall_y = 30;
- _label_path_x = 30;
- _label_path_y = 30;
- _label_obj_x = 90;
- _label_obj_y = 60;
- _label_step_index = 0;
- }
-
- private void gc_collect()
- {
- _maze = null;
- _maze_F = null;
- for(int i = 0 ; i < _label_wall.Length ; i++)
- {
- Controls.Remove(_label_wall[i]);
- }
- for (int i = 0; i < _label_path.Length; i++)
- {
- Controls.Remove(_label_path[i]);
- }
-
- for (int i = 0; i < _label_step.Length; i++)
- {
- Controls.Remove(_label_step[i]);
- }
- Controls.Remove(_label_obj[0]);
- Controls.Remove(_label_Exit[0]);
-
- _label_wall = null;
- _label_path = null;
- _label_step = null;
- GC.Collect();
- }
- }
- }
執行影片