using Global; using Logica; using System.Drawing; namespace Logic { public class WallsCreationAlgoritm { public Maze maze; public bool isDone = false; public int wallpercentage = 25; public int destructionpercentage = 15; private static readonly Random random = new Random(); private List Rows = new List(); private List Columns = new List(); private List oldVisitedCells = new List(); private List newVisitedCells = new List(); private Cell stopid; public WallsCreationAlgoritm(Maze maze) { this.maze = maze; } public void Generate() { CreateWalls(false); // Create walls for columns CreateWalls(true); // Create walls for rows PlaceWalls(); GenerateBorder(); Cell startid = CalculateStartStop(1); stopid = CalculateStartStop(maze.length - 2); SolveMaze(startid); SetWalls(); } /// /// Source: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/conditional-operator /// /// private void CreateWalls(bool forRows) { int rowCount = maze.height; //rows int colCount = maze.length; //columns for (int i = 2; i < (forRows ? rowCount - 2 : colCount - 2); i++) { if (!HasAdjacentFilledRow(i, forRows) && random.Next(100) < wallpercentage) { if (forRows) { Rows.Add(i); } else { Columns.Add(i); } } } } /// /// Check if an adjacent row is not filled so theres always a gap /// /// /// /// private bool HasAdjacentFilledRow(int index, bool RowColumn) { if (RowColumn) { if (Rows.Contains(index - 1) || Rows.Contains(index + 1)) { return true; } else { return false; } } else { if (Columns.Contains(index - 1) || Columns.Contains(index + 1)) { return true; } else { return false; } } } /// /// Place the vertical and horizontal walls /// private void PlaceWalls() { foreach (int x in Columns) { int[] randomIndex = Randomize(maze.height); bool oneGapPlaced = false; while (oneGapPlaced == false) { for (int i = 0; i < maze.height; i++) { int y = randomIndex[i]; if (!maze.cels[x, y].isWall) { if (!oneGapPlaced) { if (random.Next(100) < 15 && CheckCentrumOfCross(x, y)) { oneGapPlaced = true; continue; // gap } } maze.cels[x, y].isWall = true; maze.cels[x, y].color = Color.Red; } } } MakeGap(x, false); } foreach (int y in Rows) { int[] randomIndex = Randomize(maze.length); bool gapPlaced = false; while (gapPlaced == false) { for (int i = 0; i < maze.length; i++) { int x = randomIndex[i]; if (!maze.cels[x, y].isWall) { if (!gapPlaced) { if (random.Next(100) < 15 && CheckCentrumOfCross(x, y)) { gapPlaced = true; continue; //gap } } maze.cels[x, y].isWall = true; maze.cels[x, y].color = Color.Red; } } } MakeGap(y, true); } } /// /// Make a gap /// /// /// private void MakeGap(int xy, bool forRows) { for (int i = 1; i < (forRows ? maze.height - 1 : maze.length - 1); i++) { if (random.Next(100) < destructionpercentage && (forRows ? CheckCentrumOfCross(i, xy) : CheckCentrumOfCross(xy, i))) { if (forRows) { maze.cels[i, xy].isWall = false; //maze.cels[i, xy].visited = false; maze.cels[i, xy].color = Color.Black; } else { maze.cels[xy, i].isWall = false; // maze.cels[xy, i].visited = false; maze.cels[xy, i].color = Color.Black; } } } } /// /// Check if an empty cell is not surrounded by walls. /// /// /// /// private bool CheckCentrumOfCross(int x, int y) { int i = 0; if (!(x - 1 < 0 || x + 1 >= maze.length || y - 1 < 0 || y + 1 >= maze.height)) { if (maze.cels[x + 1, y].color == Color.Red) { i++; } if (maze.cels[x - 1, y].color == Color.Red) { i++; } if (maze.cels[x, y + 1].color == Color.Red) { i++; } if (maze.cels[x, y - 1].color == Color.Red) { i++; } } if (i >= 3) { return false; } else { return true; } } /// /// Taken from https://stackoverflow.com/questions/56378647/fisher-yates-shuffle-in-c-sharp /// /// /// private int[] Randomize(int size) { int[] index = new int[size]; for (int i = 0; i < size; i++) { index[i] = i; } for (int i = size - 1; i > 0; i--) { int j = random.Next(0, i + 1); int temp = index[i]; index[i] = index[j]; index[j] = temp; } return index; } /// /// Find a usable cell in a row for the start or the stop of the maze. /// /// private Cell CalculateStartStop(int x) { Cell cel = new Cell(); if (x >= 0 && x < maze.length) { for (int i = 0; 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; } maze.cels[x, randomY].isWall = false; cel.setLocation(x, randomY); cel.id = maze.cels[x, randomY].id; break; } } } return cel; } /// /// Create a border /// private void GenerateBorder() { for (int i = 0; i < maze.length; i++) { maze.cels[i, 0].color = Color.Red; maze.cels[i, 0].isWall = true; maze.cels[i, maze.length - 1].color = Color.Red; maze.cels[i, maze.length - 1].isWall = true; } for (int j = 0; j < maze.height - 1; j++) { maze.cels[0, j].color = Color.Red; maze.cels[0, j].isWall = true; maze.cels[maze.height - 1, j].color = Color.Red; maze.cels[maze.height - 1, j].isWall = true; } } /// /// Solve the maze with an algoritm to see if its solvable /// /// private void SolveMaze(Cell start) { int i = 0; while (!isDone && i < 1) { bool solved = false; oldVisitedCells.Add(start); while (!solved && !isDone) { solved = true; foreach (Cell cell in oldVisitedCells.ToList()) { if (FillInAvailableNeighbours(cell.x, cell.y)) { solved = false; } if (isDone) { break; } } oldVisitedCells = newVisitedCells.ToList(); newVisitedCells.Clear(); } if (!isDone && i < 1) { MakeAdditionalGaps(); i++; } } } /// /// Make additional gaps /// private void MakeAdditionalGaps() { foreach (int x in Columns) { MakeGap(x, false); } foreach (int y in Rows) { MakeGap(y, true); } } /// /// See if the neighbouring cells are available /// /// /// /// private bool FillInAvailableNeighbours(int x, int y) { bool isOk = false; if (!IsVisited(x + 1, y)) { SetVisited(x + 1, y); isOk = true; } if (!IsVisited(x - 1, y)) { SetVisited(x - 1, y); isOk = true; } if (!IsVisited(x, y + 1)) { SetVisited(x, y + 1); isOk = true; } if (!IsVisited(x, y - 1)) { SetVisited(x, y - 1); isOk = true; } return (isOk); } /// /// Set the cell as visited to prevent index array errors /// /// /// private void SetVisited(int x, int y) { Cell cell = maze.cels[x, y]; cell.setLocation(x, y); if (cell.isWall == false && cell.visited == false) { //cell.color = Color.White; cell.visited = true; newVisitedCells.Add(cell); if (cell.id == stopid.id) { isDone = true; } } } /// /// Return if the cell has been visited /// /// /// /// private bool IsVisited(int x, int y) { if (x >= 0 && x < maze.length && y >= 0 && y < maze.height) { return maze.cels[x, y].visited; } else if (x == -1 || y == -1 || x == maze.length || y == maze.height) { return true; } else { return true; } } /// /// Not too proud of this fix /// 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; } } } } } }