Генератор идеального лабиринта по алгориму Hunt-and-Kill
Зависание программы и переполнение стека при рисовании в PictureBox.
Пробую себя в реализации алгоритма идеального лабиринта hunt-and-kill. Алгоритм выбирает случайную ячейку и идет в случайном направлении и стирает границы между этими ячейками. Если он заходит в тупик, то, начиная с первого ряда, ведется поиск пустой ячейки. Алгоритм продолжается до тех пор, пока не будет заполнена вся таблица.
Возможно это не самый правильный способ его создания, который я написал.
Я решил реализовать его через рекурсию, и дело в том, что на определенном этапе программа зависает, а иногда и выдает ошибку переполнения стека.
Вопрос в том, можно ли как-то оптимизировать код, или обойти переполнение стека?
public Form1()
{
InitializeComponent();
}
Graphics g;
Bitmap buf;
SolidBrush brushCell = new SolidBrush(Color.LightGreen);
SolidBrush fillCell = new SolidBrush(Color.White);
SolidBrush brushBack = new SolidBrush(Color.LightGray);
Pen penBorder = new Pen(Color.Black);
string[,] labirint;
Random rnd = new Random();
int sizeBox = 25;
int countX = 15;
int countY = 15;
int x0, y0;
int randomNum;
private void Form1_Load(object sender, EventArgs e)
{
buf = new Bitmap(pictureBox1.Width, pictureBox1.Height);
g = Graphics.FromImage(buf);
int w = countX * sizeBox;
int h = countY * sizeBox;
x0 = pictureBox1.Width / 2 - w / 2;
y0 = pictureBox1.Height / 2 - h / 2;
g.FillRectangle(brushBack, x0, y0, w, h);
g.DrawRectangle(penBorder, x0, y0, w, h);
for (int i = x0; i < x0 + w; i += sizeBox)
{
g.DrawLine(penBorder, i, y0, i, y0 + h);
}
for (int j = y0; j < y0 + h; j += sizeBox)
{
g.DrawLine(penBorder, x0, j, x0 + w, j);
}
pictureBox1.BackgroundImage = buf;
labirint = new string[countX, countY];
for (int i = 0; i < countX; i++)
{
for (int j = 0; j < countY; j++)
{
labirint[i, j] = "00000";
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
g.Dispose();
}
private void button1_Click(object sender, EventArgs e)
{
int firstCell_X = rnd.Next(countX);
int firstCell_Y = rnd.Next(countY);
g.FillRectangle(brushCell, x0 + firstCell_X * sizeBox + 1, y0 + firstCell_Y * sizeBox + 1, sizeBox - 1, sizeBox - 1);
nextCell(firstCell_X, firstCell_Y);
}
void nextCell(int lastCell_X, int lastCell_Y)
{
pictureBox1.Refresh();
randomNum = randN(lastCell_X, lastCell_Y);
try
{
if (labirint[lastCell_X, lastCell_Y][randomNum] == '0')
{
if (randomNum == 0 && labirint[lastCell_X, lastCell_Y - 1][4] == '0')
{
up(lastCell_X, lastCell_Y, randomNum);
}
else if (randomNum == 1 && labirint[lastCell_X + 1, lastCell_Y][4] == '0')
{
right(lastCell_X, lastCell_Y, randomNum);
}
else if (randomNum == 2 && labirint[lastCell_X, lastCell_Y + 1][4] == '0')
{
down(lastCell_X, lastCell_Y, randomNum);
}
else if (randomNum == 3 && labirint[lastCell_X - 1, lastCell_Y][4] == '0')
{
left(lastCell_X, lastCell_Y, randomNum);
}
else if (lastCell_Y != 0 && labirint[lastCell_X, lastCell_Y - 1][4] == '0')
{
up(lastCell_X, lastCell_Y, 0);
}
else if (lastCell_X != countX - 1 && labirint[lastCell_X + 1, lastCell_Y][4] == '0')
{
right(lastCell_X, lastCell_Y, 1);
}
else if (lastCell_Y != countY - 1 && labirint[lastCell_X, lastCell_Y + 1][4] == '0')
{
down(lastCell_X, lastCell_Y, 2);
}
else if (lastCell_X != 0 && labirint[lastCell_X - 1, lastCell_Y][4] == '0')
{
left(lastCell_X, lastCell_Y, 3);
}
else
{
g.FillRectangle(fillCell, x0 + lastCell_X * sizeBox + 1, y0 + lastCell_Y * sizeBox + 1, sizeBox - 1, sizeBox - 1);
hunt();
}
}
else if (lastCell_Y != 0 && labirint[lastCell_X, lastCell_Y - 1][4] == '0')
{
up(lastCell_X, lastCell_Y, 0);
}
else if (lastCell_X != countX - 1 && labirint[lastCell_X + 1, lastCell_Y][4] == '0')
{
right(lastCell_X, lastCell_Y, 1);
}
else if (lastCell_Y != countY - 1 && labirint[lastCell_X, lastCell_Y + 1][4] == '0')
{
down(lastCell_X, lastCell_Y, 2);
}
else if (lastCell_X != 0 && labirint[lastCell_X - 1, lastCell_Y][4] == '0')
{
left(lastCell_X, lastCell_Y, 3);
}
else
{
g.FillRectangle(fillCell, x0 + lastCell_X * sizeBox + 1, y0 + lastCell_Y * sizeBox + 1, sizeBox - 1, sizeBox - 1);
hunt();
}
}
catch { }
}
int randN(int lastCell_X, int lastCell_Y)
{
if (lastCell_X == 0 && lastCell_Y == 0)
{
return rnd.Next(1, 3);
}
else if (lastCell_X == 0 && lastCell_Y > 0 && lastCell_Y < countY)
{
return rnd.Next(0, 3);
}
else if (lastCell_X == 0 && lastCell_Y == countY - 1)
{
return rnd.Next(0, 2);
}
else if (lastCell_Y == countY - 1 && lastCell_X > 0 && lastCell_X < countX)
{
if (rnd.Next(2) == 0)
{
return rnd.Next(0, 2);
}
else
{
return 3;
}
}
else if (lastCell_Y == countY - 1 && lastCell_X == countX - 1)
{
if (rnd.Next(2) == 0)
{
return 0;
}
else
{
return 3;
}
}
else if (lastCell_X == countX - 1 && lastCell_Y > 0 && lastCell_Y < countY)
{
if (rnd.Next(2) == 0)
{
return rnd.Next(2, 4);
}
else
{
return 0;
}
}
else if (lastCell_X == countX - 1 && lastCell_Y == 0)
{
return rnd.Next(2, 4);
}
else if (lastCell_Y == 0 && lastCell_X > 0 && lastCell_X < countX)
{
return rnd.Next(1, 4);
}
else
{
return rnd.Next(0, 4);
}
}
void up(int lastCell_X, int lastCell_Y, int randomNum)
{
labirint[lastCell_X, lastCell_Y] = labirint[lastCell_X, lastCell_Y].Remove(randomNum, 1).Insert(randomNum, "1");
labirint[lastCell_X, lastCell_Y] = labirint[lastCell_X, lastCell_Y].Remove(4, 1).Insert(4, "1");
labirint[lastCell_X, lastCell_Y - 1] = labirint[lastCell_X, lastCell_Y - 1].Remove(randomNum + 2, 1).Insert(randomNum + 2, "1");
labirint[lastCell_X, lastCell_Y - 1] = labirint[lastCell_X, lastCell_Y - 1].Remove(4, 1).Insert(4, "1");
g.FillRectangle(fillCell, x0 + lastCell_X * sizeBox + 1, y0 + lastCell_Y * sizeBox + 1 - sizeBox, sizeBox - 1, sizeBox * 2 - 1);
g.FillRectangle(brushCell, x0 + lastCell_X * sizeBox + 1, y0 + lastCell_Y * sizeBox + 1 - sizeBox, sizeBox - 1, sizeBox - 1);
nextCell(lastCell_X, lastCell_Y - 1);
}
void right(int lastCell_X, int lastCell_Y, int randomNum)
{
labirint[lastCell_X, lastCell_Y] = labirint[lastCell_X, lastCell_Y].Remove(randomNum, 1).Insert(randomNum, "1");
labirint[lastCell_X, lastCell_Y] = labirint[lastCell_X, lastCell_Y].Remove(4, 1).Insert(4, "1");
labirint[lastCell_X + 1, lastCell_Y] = labirint[lastCell_X + 1, lastCell_Y].Remove(randomNum + 2, 1).Insert(randomNum + 2, "1");
labirint[lastCell_X + 1, lastCell_Y] = labirint[lastCell_X + 1, lastCell_Y].Remove(4, 1).Insert(4, "1");
g.FillRectangle(fillCell, x0 + lastCell_X * sizeBox + 1, y0 + lastCell_Y * sizeBox + 1, sizeBox * 2 - 1, sizeBox - 1);
g.FillRectangle(brushCell, x0 + lastCell_X * sizeBox + 1 + sizeBox, y0 + lastCell_Y * sizeBox + 1, sizeBox - 1, sizeBox - 1);
nextCell(lastCell_X + 1, lastCell_Y);
}
void down(int lastCell_X, int lastCell_Y, int randomNum)
{
labirint[lastCell_X, lastCell_Y] = labirint[lastCell_X, lastCell_Y].Remove(randomNum, 1).Insert(randomNum, "1");
labirint[lastCell_X, lastCell_Y] = labirint[lastCell_X, lastCell_Y].Remove(4, 1).Insert(4, "1");
labirint[lastCell_X, lastCell_Y + 1] = labirint[lastCell_X, lastCell_Y + 1].Remove(randomNum - 2, 1).Insert(randomNum - 2, "1");
labirint[lastCell_X, lastCell_Y + 1] = labirint[lastCell_X, lastCell_Y + 1].Remove(4, 1).Insert(4, "1");
g.FillRectangle(fillCell, x0 + lastCell_X * sizeBox + 1, y0 + lastCell_Y * sizeBox + 1, sizeBox - 1, sizeBox * 2 - 1);
g.FillRectangle(brushCell, x0 + lastCell_X * sizeBox + 1, y0 + lastCell_Y * sizeBox + 1 + sizeBox, sizeBox - 1, sizeBox - 1);
nextCell(lastCell_X, lastCell_Y + 1);
}
void left(int lastCell_X, int lastCell_Y, int randomNum)
{
labirint[lastCell_X, lastCell_Y] = labirint[lastCell_X, lastCell_Y].Remove(randomNum, 1).Insert(randomNum, "1");
labirint[lastCell_X, lastCell_Y] = labirint[lastCell_X, lastCell_Y].Remove(4, 1).Insert(4, "1");
labirint[lastCell_X - 1, lastCell_Y] = labirint[lastCell_X - 1, lastCell_Y].Remove(randomNum - 2, 1).Insert(randomNum - 2, "1");
labirint[lastCell_X - 1, lastCell_Y] = labirint[lastCell_X - 1, lastCell_Y].Remove(4, 1).Insert(4, "1");
g.FillRectangle(fillCell, x0 + lastCell_X * sizeBox + 1 - sizeBox, y0 + lastCell_Y * sizeBox + 1, sizeBox * 2 - 1, sizeBox - 1);
g.FillRectangle(brushCell, x0 + lastCell_X * sizeBox + 1 - sizeBox, y0 + lastCell_Y * sizeBox + 1, sizeBox - 1, sizeBox - 1);
nextCell(lastCell_X - 1, lastCell_Y);
}
void hunt()
{
for (int j = 0; j < countY; j++)
{
for (int i = 0; i < countX; i++)
{
if (labirint[i, j][4] == '0')
{
nextCell(i, j);
return;
}
}
}
}