You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
321 lines
11 KiB
C#
321 lines
11 KiB
C#
using Global;
|
|
using Logica;
|
|
using System.Drawing;
|
|
|
|
namespace Logic {
|
|
|
|
public class DepthFirstAlgoritm {
|
|
public Maze maze;
|
|
public bool isDone = false;
|
|
private static readonly Random random = new Random();
|
|
private int x;
|
|
private int y;
|
|
private int attempts = 0;
|
|
private bool goodStep;
|
|
private int startcellId;
|
|
public DepthFirstAlgoritm(Maze maze) {
|
|
this.maze = maze;
|
|
x = 0;
|
|
y = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate the maze.
|
|
/// </summary>
|
|
public void Generate() {
|
|
maze.length--;
|
|
maze.height--;
|
|
CalculateIdStartCell();
|
|
while (isDone == false) {
|
|
SetStep();
|
|
}
|
|
CalculateStartStop(1);
|
|
CalculateStartStop(maze.length - 1);
|
|
maze.height++;
|
|
maze.length++;
|
|
GenerateBorder();
|
|
SetWalls();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set a step in a random direction. If there are too many unsuccesfull attempts it will force a step
|
|
/// </summary>
|
|
private void SetStep() {
|
|
goodStep = false;
|
|
attempts = 0;
|
|
while (goodStep == false && isDone == false) {
|
|
goodStep = false;
|
|
int tempX = x;
|
|
int tempY = y;
|
|
if (attempts > 5) {
|
|
ForceStep();
|
|
break;
|
|
}
|
|
else {
|
|
Direction direction = GenerateRandomMovement();
|
|
switch (direction) {
|
|
case Direction.up: tempY = y - 1; break;
|
|
case Direction.down: tempY = y + 1; break;
|
|
case Direction.right: tempX = x + 1; break;
|
|
case Direction.left: tempX = x - 1; break;
|
|
}
|
|
if (tempY >= 1 && tempX >= 1 && tempX < maze.length && tempY < maze.height) {
|
|
if (CheckStepInvalid(tempX, tempY) || CheckNeighbours(x, y, tempX, tempY)) {
|
|
attempts = attempts + 1;
|
|
}
|
|
else {
|
|
x = tempX;
|
|
y = tempY;
|
|
maze.cels[x, y].SetVisited(true);
|
|
maze.cels[x, y].color = Color.Red;
|
|
goodStep = true;
|
|
}
|
|
}
|
|
else {
|
|
attempts = attempts + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// will force check every direction for a good step.
|
|
/// </summary>
|
|
private void ForceStep() {
|
|
if (CheckStepInvalid((x + 1), y) == false && CheckNeighbours(x, y, x + 1, y) == false) {
|
|
goodStep = true;
|
|
x++;
|
|
}
|
|
else if (CheckStepInvalid((x - 1), y) == false && CheckNeighbours(x, y, x - 1, y) == false) {
|
|
goodStep = true;
|
|
x--;
|
|
}
|
|
else if (CheckStepInvalid(x, (y + 1)) == false && CheckNeighbours(x, y, x, y + 1) == false) {
|
|
goodStep = true;
|
|
y++;
|
|
}
|
|
else if (CheckStepInvalid(x, (y - 1)) == false && CheckNeighbours(x, y, x, y - 1) == false) {
|
|
goodStep = true;
|
|
y--;
|
|
}
|
|
if (goodStep) {
|
|
maze.cels[x, y].SetVisited(true);
|
|
maze.cels[x, y].color = Color.Red;
|
|
attempts = 0;
|
|
}
|
|
if (goodStep == false) {
|
|
BacktrackStep();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Backstep in a random direction untill a good step has been found
|
|
/// if the step is back to the startcell then the maze is done.
|
|
/// </summary>
|
|
private void BacktrackStep() {
|
|
int tempY = y;
|
|
int tempX = x;
|
|
maze.cels[x, y].isBacktracked = true;
|
|
Direction[] directions = Enum.GetValues(typeof(Direction))
|
|
.Cast<Direction>()
|
|
.OrderBy(_ => Guid.NewGuid())
|
|
.ToArray();
|
|
//Random order
|
|
foreach (Direction direction in directions) {
|
|
switch (direction) {
|
|
case Direction.up: tempY = y - 1; break;
|
|
case Direction.down: tempY = y + 1; break;
|
|
case Direction.right: tempX = x + 1; break;
|
|
case Direction.left: tempX = x - 1; break;
|
|
}
|
|
if (IsVisited(tempX, tempY) == true) {
|
|
y = tempY;
|
|
x = tempX;
|
|
maze.cels[x, y].color = Color.Black;
|
|
break;
|
|
}
|
|
}
|
|
if (maze.cels[x, y].id == startcellId) {
|
|
isDone = true;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Check if a cell has a neighbouring cell that has been visited
|
|
/// </summary>
|
|
/// <param name="x1"></param>
|
|
/// <param name="y1"></param>
|
|
/// <param name="x2"></param>
|
|
/// <param name="y2"></param>
|
|
/// <returns></returns>
|
|
private bool CheckNeighbours(int x1, int y1, int x2, int y2) {
|
|
bool notOk = false;
|
|
|
|
// Check adjacent neighbors
|
|
if (IsVisited(x2 + 1, y2) && x2 + 1 != x1) {
|
|
notOk = true;
|
|
}
|
|
else if (IsVisited(x2 - 1, y2) && x2 - 1 != x1) {
|
|
notOk = true;
|
|
}
|
|
else if (IsVisited(x2, y2 + 1) && y2 + 1 != y1) {
|
|
notOk = true;
|
|
}
|
|
else if (IsVisited(x2, y2 - 1) && y2 - 1 != y1) {
|
|
notOk = true;
|
|
}
|
|
|
|
// Check diagonal neighbors
|
|
if (IsVisited(x2 + 1, y2 + 1) && x2 + 1 != x1 && y2 + 1 != y1) {
|
|
notOk = true;
|
|
}
|
|
else if (IsVisited(x2 - 1, y2 - 1) && x2 - 1 != x1 && y2 - 1 != y1) {
|
|
notOk = true;
|
|
}
|
|
else if (IsVisited(x2 + 1, y2 - 1) && x2 + 1 != x1 && y2 - 1 != y1) {
|
|
notOk = true;
|
|
}
|
|
else if (IsVisited(x2 - 1, y2 + 1) && x2 - 1 != x1 && y2 + 1 != y1) {
|
|
notOk = true;
|
|
}
|
|
|
|
return notOk;
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Check if a cell has been visited to prevent array out of bounds errors
|
|
/// </summary>
|
|
/// <param name="x"></param>
|
|
/// <param name="y"></param>
|
|
/// <returns></returns>
|
|
private bool IsVisited(int x, int y) {
|
|
if (x >= 1 && x < maze.length && y >= 1 && y < maze.height) {
|
|
return maze.cels[x, y].visited;
|
|
}
|
|
else if (x == 0 || y == 0 || x == maze.length || y == maze.height) {
|
|
return false;
|
|
//wall, so can ignore
|
|
}
|
|
else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate a random direction
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <exception cref="InvalidOperationException"></exception>
|
|
private Direction GenerateRandomMovement() {
|
|
int randomNumber = random.Next(1, 5);
|
|
switch (randomNumber) {
|
|
case 1:
|
|
return Direction.up;
|
|
case 2:
|
|
return Direction.down;
|
|
case 3:
|
|
return Direction.left;
|
|
case 4:
|
|
return Direction.right;
|
|
default:
|
|
throw new InvalidOperationException("Invalid random number generated.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pick random cell at the left side to generate the maze from
|
|
/// </summary>
|
|
private void CalculateIdStartCell() {
|
|
y = random.Next(1, maze.height - 1);
|
|
x = random.Next(1, maze.length - 1);
|
|
startcellId = maze.cels[x, y].id;
|
|
maze.cels[x, y].SetVisited(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find a usable cell in a row for the start or the stop of the maze.
|
|
/// </summary>
|
|
/// <param name="x"></param>
|
|
private void CalculateStartStop(int x) {
|
|
if (x >= 1 && x < maze.length) {
|
|
for (int i = 1; i < maze.length; i++) {
|
|
Random random = new Random();
|
|
int randomY = random.Next(0, maze.length);
|
|
if (maze.cels[x, randomY].color == Color.Black) {
|
|
if (x == 1) {
|
|
maze.ball = new Ball();
|
|
maze.ball.SetLocation(x, randomY);
|
|
}
|
|
else {
|
|
maze.cels[x, randomY].color = Color.Blue;
|
|
Cell endCell = new Cell();
|
|
endCell.setLocation(x, randomY);
|
|
maze.endCell = endCell;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public void GenerateBorder() {
|
|
int width = maze.length;
|
|
int height = maze.height;
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
for (int y = 0; y < height; y++) {
|
|
if (x == 0 || x == width - 1 || y == 0 || y == height - 1) {
|
|
maze.cels[x, y].isWall = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Check if a step is invalid
|
|
/// </summary>
|
|
/// <param name="x"></param>
|
|
/// <param name="y"></param>
|
|
/// <returns></returns>
|
|
private bool CheckStepInvalid(int x, int y) {
|
|
bool isInvalid = true;
|
|
if (x >= 1 && x < maze.length && y >= 1 && y < maze.height) {
|
|
if (x < maze.length && x >= 1 && isInvalid != false) {
|
|
isInvalid = false;
|
|
}
|
|
if (y < maze.height && y >= 1 && isInvalid != false) {
|
|
isInvalid = false;
|
|
}
|
|
if (IsVisited(x, y) == true) {
|
|
isInvalid = true;
|
|
}
|
|
|
|
}
|
|
return isInvalid;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Not too proud of this fix
|
|
/// </summary>
|
|
private void SetWalls() {
|
|
for (int x = 0; x < maze.length; x++) {
|
|
for (int y = 0; y < maze.height; y++) {
|
|
if (maze.cels[x, y].color == Color.Red) {
|
|
maze.cels[x, y].isWall = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
} |