📌 Pinned
🔒 Locked
You may now post code
Advertisement
Original Post
With the project half way over, feel free to begin uploading source examples or links to zip files. For people still trying to work this out, feel free to look over other people's code, that's a natural part of learning correct design and programming. But be aware, not everyone does things "correctly" and there is, strictly speaking, no "right" or "wrong" way to implement something. You may find you were doing something completely different than someone else. Don't feel the need to scratch your work and start over simply because someone else took a different approach. Perhaps your approach is just as valid. Evaluate the differences critically and then decide if you made a mistake, or simply did something differently. Cheers!
Hey! I think I took a bit of a different route in the way the map is stored.
I have it like this:
11111
10101
11111
Where the 1's are walls and the 0's are free spaces. The outer walls enclosing the whole maze are also stored in this array.
I think doing it like this complicated things a bit when trying to assign two rooms to a wall and when calculating the wall position in the array to knock down a wall. But see for yourself, all comments and critique is welcome!
Main.cs - includes main loop, introduction, and input/output:
Maze.cs - stores all data about maze ( maze map, number of walls, rooms, room sets, wall sequence; does all logic, including making random entrance/exit points, randomizing the wall sequence, and finally knocking down the walls and printing them to the console.
Again, feel free to add critique / comments!
Edit: One question I came across while writing this: Is there any way to declare the class methods inside the actual class definition, but define the method outside? I'm thinking of C++ here, with declaring inside:
Class bla {
public:
void hello();
};
void bla::hello() {
}
It makes the classes so much tidier and easier to see the big picture imo.
Edit 2: On second though, maybe some more explanation of my Maze.cs is in order. I use two different widths and heights. The first, which is passed to the constructor of Maze is the number of cells horizontally and vertically. In the Maze class, width and height refer to the number of spaces in the map array - which is 2*c+1 ( c is the number of cells).
One cell:
111
101
111
Two cells:
11111
10101
11111
etc.
I have it like this:
11111
10101
11111
Where the 1's are walls and the 0's are free spaces. The outer walls enclosing the whole maze are also stored in this array.
I think doing it like this complicated things a bit when trying to assign two rooms to a wall and when calculating the wall position in the array to knock down a wall. But see for yourself, all comments and critique is welcome!
Main.cs - includes main loop, introduction, and input/output:
using System;namespace Workshop_Project_1{ class Program { static void Main(string[] args) { bool quit = false; ConsoleKeyInfo key; int width, height; Console.Clear(); Console.Title = "Perfect Maze Generator"; Console.WriteLine("\n\nWelcome to the perfect maze generator!\n"); while (!quit) { do { Console.WriteLine("\nWidth (2-50): "); int.TryParse(Console.ReadLine(), out width); if (width < 2 || width > 50) Console.WriteLine("Invalid input, try again\n"); } while (width < 2 || width > 50); do { Console.WriteLine("\nHeight (2-50): "); Int32.TryParse(Console.ReadLine(), out height); if (height < 2 || height > 50) Console.WriteLine("Invalid input, try again\n"); } while (height < 2 || height > 50); Maze m = new Maze(width, height); Console.SetWindowSize(width < 40 ? 80 : width * 2 + 1, height < 10 ? 21 : (height < 30 ? height*2+1 + 20 : 79)); m.knockdown(); m.print(); Console.WriteLine("\nContinue? (y/n)"); do { key = Console.ReadKey(true); } while (key.KeyChar != 'y' && key.KeyChar != 'n'); if (key.KeyChar != 'y') quit = true; } } }}Maze.cs - stores all data about maze ( maze map, number of walls, rooms, room sets, wall sequence; does all logic, including making random entrance/exit points, randomizing the wall sequence, and finally knocking down the walls and printing them to the console.
using System;namespace Workshop_Project_1{ class Maze { private int[,] arr; private int[] wallseq; private int height, width, rooms, walls; private bool[,] sets; private Random rand; public struct assignedrooms { public int room1; public int room2; } public assignedrooms[] ar; public Maze(int w, int h) { int tmp1, tmp2; height = h*2+1; width = w*2+1; rooms = w * h; walls = rooms * 2 - h - w; wallseq = new int[walls]; ar = new assignedrooms[walls]; sets = new bool[rooms, rooms]; arr = new int[width, height]; rand = new Random(); // init map for (int j = 0; j < height; j++) { if (j % 2 == 0) for (int i = 0; i < width; i++) { arr[i, j] = 1; } else for (int i = 0; i < width; i++) { if (i % 2 == 0) arr[i, j] = 1; } } //init start + end point arr[(rand.Next(rooms) % w) * 2 + 1, (rand.Next(rooms) / w) * 2 + 1] = 2; arr[(rand.Next(rooms) % w) * 2 + 1, (rand.Next(rooms) / w) * 2 + 1] = 2; // init room sets // [set, rooms in this set] for (int i = 0; i < rooms; i++) sets[i, i] = true; // init and randomize wall sequence for (int i = 0; i < walls; i++) wallseq = i; //debug //foreach (int i in wallseq) Console.Write(i + " "); //Console.Write("\n"); for (int i = 0; i < walls; i++) { tmp1 = rand.Next(walls); tmp2 = wallseq; wallseq = wallseq[tmp1]; wallseq[tmp1] = tmp2; } //debug //foreach (int i in wallseq) Console.Write(i + " "); //Console.Write("\n"); //init assigned rooms int tmp=0, row=0; for (int i = 0; i < walls; ) { tmp=0; while (tmp < w - 1) { ar.room1 = tmp+row*w; ar.room2 = tmp+row*w + 1; i++; tmp++; } tmp = 0; while (tmp < w && i < walls) { ar.room1 = tmp + row * w; ar.room2 = tmp + (row + 1) * w; i++; tmp++; } row++; } //for (int i=0; i < walls; i++) //{ // Console.WriteLine("Wall {0} : Rooms {1} and {2}", i, ar.room1, ar.room2); //} } public void print() { for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { if (arr[i, j] == 0) Console.Write(" "); else if (arr[i, j] == 2) Console.Write("x"); else if (j % 2 == 0) Console.Write("-"); else Console.Write('|'); } Console.Write("\n"); } //debug //for (int j = 0; j < height; j++) //{ // for (int i = 0; i < width; i++) // { // Console.Write(arr[i,j]); // } // Console.Write("\n"); //} } public void knockdown() { for (int i=0; i< walls; i++) { // if both rooms in same set -> continue int j, k; for (j = 0; j < walls; j++) if (sets[j, ar[wallseq].room1] == true) break; // debug Console.WriteLine("room 1 from wall number {0} in wallsequence is in set {1}", i, j); if (sets[j, ar[wallseq].room2] == true) continue; // else knock down wall, move all rooms from room 2's set to room 1's set // combine sets, empty set 2 for (k = 0; k < walls; k++) if (sets[k, ar[wallseq].room2] == true) break; for (int h = 0; h < rooms; h++) { sets[j, h] = sets[k, h] | sets[j, h]; sets[k, h] = false; } // modify map (arr) int x = 2; int y = 1; int row = 0; int counter=((width-1)/2)-1; while (true) { if (wallseq - counter >= 0) { counter += ((width-1)/2); if (wallseq - counter >= 0) { row++; counter += (((width-1)/2) - 1); } else { x = (wallseq - (counter - ((width-1)/2))) * 2 + 1; y = row * 2 + 2; break; } } else { x = (wallseq - (counter - (((width-1)/2) - 1)))*2 + 2; y = row * 2 + 1; break; } } arr[x, y] = 0; //debug print(); } } }}Again, feel free to add critique / comments!
Edit: One question I came across while writing this: Is there any way to declare the class methods inside the actual class definition, but define the method outside? I'm thinking of C++ here, with declaring inside:
Class bla {
public:
void hello();
};
void bla::hello() {
}
It makes the classes so much tidier and easier to see the big picture imo.
Edit 2: On second though, maybe some more explanation of my Maze.cs is in order. I use two different widths and heights. The first, which is passed to the constructor of Maze is the number of cells horizontally and vertically. In the Maze class, width and height refer to the number of spaces in the map array - which is 2*c+1 ( c is the number of cells).
One cell:
111
101
111
Two cells:
11111
10101
11111
etc.
Ok here's my version its not very pretty but it works. It needs alot of commenting and refactoring. Which I'll work on later.
The drawing code is particularly ugly I think. And I think roomset has functions that are never used.
The drawing code is particularly ugly I think. And I think roomset has functions that are never used.
using System;using System.Collections.Generic;using System.Text;namespace ConsoleApplication1{ class Program { static void Main(string[] args) { Console.Title = "Maze Program1"; Console.WriteLine("Welcome to maze generator"); string str; do { Console.WriteLine("Please Specify maze width: "); str = Console.ReadLine(); int width = 0; while (!Int32.TryParse(str, out width) || width < 2 || width > 50) { Console.WriteLine("Please specify a numeric value betweeen 2 and 50: "); str = Console.ReadLine(); }; Console.WriteLine("Please Specify maze height: "); str = Console.ReadLine(); int height; while (!Int32.TryParse(str, out height) || height < 2 || height > 50) { Console.WriteLine("Please specify a numeric value betweeen 2 and 50: "); str = Console.ReadLine(); }; Maze myMaze = new Maze(width, height); myMaze.GenerateMaze(); } while (PlayAgain()); Console.WriteLine("Press any key to continue"); Console.ReadKey(); } static bool PlayAgain() { Console.Write("Play Again? "); string str = Console.ReadLine(); if (str[0] == 'y' || str[0] == 'Y') { Console.Clear(); return true; } return false; } } public struct Wall { public bool up; public int room1, room2; } public class RoomSet { int[] rooms; public RoomSet(params int[] arr) { rooms = new int[arr.Length]; for(int i = 0; i < arr.Length; i++) rooms = arr; } public bool IsInSet(int room) { for (int i = 0; i < rooms.Length; i++) { if (room == rooms) return true; } return false; } public bool AddRoom(int room) { if (IsInSet(room)) return false; int[] newRooms = new int[rooms.Length + 1]; for (int i = 0; i < rooms.Length; i++) newRooms = rooms; newRooms[rooms.Length] = room; rooms = newRooms; return true; } public bool AddRoomSet(RoomSet room) { int newSize = rooms.Length + room.rooms.Length; int[] newRooms = new int[newSize]; int i = 0; for (; i < rooms.Length; i++) newRooms = rooms; for (; i < newSize; i++) newRooms = room.rooms[i - rooms.Length]; rooms = newRooms; return true; } public void Clear() { rooms = new int[0]; } } public class Maze { private int width, height; private int numRooms, numWalls; private Wall[] walls; private RoomSet[] roomSets; private int[] wallPerm; public Maze(int width, int height) { this.width = width; this.height = height; numRooms = width * height; numWalls = ((width - 1) * height) + ((height - 1) * width); walls = new Wall[numWalls]; roomSets = new RoomSet[numRooms]; } public void Init() { int i = 0; // Init Walls for (int x = 0; x < height; x++) { for (int y = 0; y < (width - 1); y++, i++) { walls.up = true; walls.room1 = (x * width) + y; walls.room2 = walls.room1 + 1; } } for (int x = 0; x < (height - 1); x++) { for (int y = 0; y < width; y++, i++) { walls.up = true; walls.room1 = (x * width) + y; walls.room2 = walls.room1 + width; } } //Init RoomSets for (i = 0; i < numRooms; i++) roomSets = new RoomSet(i); //Create random wall Permutations wallPerm = new int[numWalls]; for (i = 0; i < numWalls; i++) wallPerm = i; Random rand = new Random(); //Specify seed when debugging so we always get the same maze int temp, randVal; //Swap values in wallPerm randomly (i.e shuffle) for (i = 0; i < numWalls; i++) { randVal = rand.Next(i); temp = wallPerm[randVal]; wallPerm[randVal] = wallPerm; wallPerm = temp; } } int FindRoomSet(int room) { for (int i = 0; i < numRooms; i++) { if (roomSets.IsInSet(room)) return i; } return -1; } public void GenerateMaze() { Init(); for (int i = 0; i < numWalls; i++) { int wallIndx = wallPerm; int roomSet1 = FindRoomSet(walls[wallIndx].room1); int roomSet2 = FindRoomSet(walls[wallIndx].room2); if (roomSet1 != roomSet2) { //Knock down wall walls[wallIndx].up = false; roomSets[roomSet1].AddRoomSet(roomSets[roomSet2]); roomSets[roomSet2].Clear(); } } DrawMaze(); } void DrawMaze() { int mazeWidth = ((width * 2) + 1); int mazeHeight = ((height * 2) + 1); int mazeSize = mazeWidth * mazeHeight; char[] maze = new char[mazeSize]; //Init to blank for (int i = 0; i < mazeSize - mazeWidth; i++) { maze = ' '; } //Draw Border int lastline = mazeSize - mazeWidth; for (int i = 0; i < mazeWidth; i++) { maze = '#'; maze[lastline + i] = '#'; } for (int i = 1; i < mazeHeight - 1; i++) { maze[i * mazeWidth] = '#'; maze[(i * mazeWidth) + mazeWidth - 1] = '#'; } //Draw Vertical Walls int mazeStart = mazeWidth + 1; int a = mazeStart + 1; int wallCnt = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width - 1; x++, wallCnt++, a += 2) { if (walls[wallCnt].up) maze[a] = '#'; //else //Draw "doorways" for debugging // maze[a] = '.'; } a += mazeWidth + 3; } //Draw Horizontal Walls a = mazeStart + mazeWidth; for (int y = 0; y < height - 1; y++) { for (int x = 0; x < width; x++, wallCnt++, a += 2) { if (walls[wallCnt].up) maze[a] = '#'; //else // maze[a] = '.'; maze[a + 1] = '#'; } a += mazeWidth + 1; } // Display Maze a = 0; for (int y = 0; y < mazeHeight; y++) { for (int x = 0; x < mazeWidth; x++, a++) { Console.Write(maze[a]); } Console.WriteLine(); } } }}Quote:
One question I came across while writing this: Is there any way to declare the class methods inside the actual class definition, but define the method outside?
No, not really. Methods should be short. Long ones should be an indicator that you might want to abstract something there. Though if you still find it cluttered, Edit > Outlining > Collapse to Definitions will make classes look like C++ headers, which you can then expand to edit or view details of at your whimsy.
This looks pretty nasty, but oh well...
I like this line
int wallindex = (cols - 1) * rows + r * cols + c; //EWW
using System;namespace ConsoleApplication1{ struct Wall : IComparable<Wall> { public int randint; public int wall; //id public bool destroyed; public int CompareTo(Wall w) { return (this.randint.CompareTo(w.randint)); } } class Program { static Wall GetWallById(Wall[] walls, int id) { foreach (Wall w in walls) { if (w.wall == id) return (w); } throw new Exception("Fuckup"); } static void destroy(int[,] rooms, int first, int second, int rows, int cols) //puts rooms in same set { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { if(rooms[i,j] == first) rooms[i,j] = second; //All your base... } } } static int PromptIntInRange(string prompt, int lowbound, int highbound) { while (true) { Console.WriteLine(prompt + " ({0},{1})", lowbound, highbound); try { int input = Int32.Parse(Console.ReadLine()); if (input >= lowbound && input <= highbound) return (input); Console.WriteLine("ur int wuz not in teh specified raeng LOL!!!\n"); } catch (FormatException) { Console.WriteLine("LOLZ nublet U shud try inpt a valid integr!!1one\n"); } } } static void Main(string[] args) { string titlestring = "Welcome to Dale's maze generator!"; Console.WriteLine(titlestring + "\n"); Console.Title = titlestring; const int maxwidth = 50; const int maxheight = 50; const int minsize = 2; string inp; //input,used to prompt if the user wants to try again; do { inp = ""; int rows, cols; rows = PromptIntInRange("How many rows?", minsize, maxheight); cols = PromptIntInRange("How many columns?", minsize, maxwidth); if (rows + 1 > Console.WindowHeight) //If window isn't big enough, make it big enough or as big as possible if "big enough" can't fit { if (rows + 1 > Console.LargestWindowHeight) Console.WindowHeight = Console.LargestWindowHeight; //only need an error message for width else Console.WindowHeight = rows + 1; //if it isnt high enough, people can just scroll down :) } if (cols * 2 + 2 > Console.WindowWidth) { if (cols * 2 + 2 > Console.LargestWindowWidth) { Console.WriteLine("Your screen is not wide enough to display a maze of this size"); continue; } else Console.WindowWidth = cols*2+2; } int numwalls = cols * (rows - 1) + rows * (cols - 1); int[,] rooms = new int[rows, cols]; Wall[] walls = new Wall[numwalls]; Random rand = new Random(); // for (int i = 0; i < numwalls; i++) { walls.wall = i; walls.destroyed = false; walls.randint = rand.Next(500);// } for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { rooms[i, j] = cols * i + j; //give each a unique number } } //Randomly permutate walls here (last thing to do, lazy) Array.Sort(walls); for (int i = 0; i < numwalls; i++) //each (Wall w in walls) { /* Wall layout!!! * horizontal walls first, then vertical * so the first (cols-1)*rows are vertical | walls, the rest ( (rows-1)*cols ) are horizontal _ */ int wall = walls.wall; if (wall >= (cols - 1) * rows) // horizontal { int horizwall = wall - (cols - 1) * rows; int wallindex = horizwall % cols; int wallrow = horizwall / cols; if (rooms[wallrow, wallindex] != rooms[wallrow + 1, wallindex]) { //BREAKIT! destroy(rooms, rooms[wallrow, wallindex], rooms[wallrow + 1, wallindex], rows, cols); walls.destroyed = true; } } else //vertical { int wallindex = wall % (cols - 1); // column index int wallrow = wall / (cols - 1); // row index if (rooms[wallrow, wallindex] != rooms[wallrow, wallindex + 1]) { //BREAKIT! destroy(rooms, rooms[wallrow, wallindex], rooms[wallrow, wallindex + 1], rows, cols); walls.destroyed = true; } } } for (int i = 0; i < (cols * 2) + 1; i++) //Start drawing! { Console.Write("_"); } Console.WriteLine(); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { if (c == 0) //always create a vertical wall on the first set (left wall) { Console.Write("|"); } else //check | { int wallindex = r * (cols - 1) + c - 1; if (GetWallById(walls, wallindex).destroyed) Console.Write("_"); else Console.Write("|"); } // check _ here if (r == rows - 1) { Console.Write("_"); //always create bottom walls on bottom row } else { int wallindex = (cols - 1) * rows + r * cols + c; //EWW if (GetWallById(walls, wallindex).destroyed) Console.Write(" "); else Console.Write("_"); } } Console.WriteLine("|"); } while (inp == "") { Console.WriteLine("Do you want to generate another maze? (y/yes/n/no)"); inp = Console.ReadLine(); if (inp != "n" && inp != "y" && inp != "yes" && inp != "no") inp = ""; } } while (inp == "yes" || inp == "y"); } }}I like this line
int wallindex = (cols - 1) * rows + r * cols + c; //EWW
Quote:
Original post by Telastyn Quote:
One question I came across while writing this: Is there any way to declare the class methods inside the actual class definition, but define the method outside?
No, not really. Methods should be short. Long ones should be an indicator that you might want to abstract something there. Though if you still find it cluttered, Edit > Outlining > Collapse to Definitions will make classes look like C++ headers, which you can then expand to edit or view details of at your whimsy.
I like to put sections of each class into #region's in order to make it more readable and navigable. Usually, I have the regions broken down into variables, properties, events, constructor(s), methods, event handlers, and private helper methods.
Quote:
Original post by Mike.Popoloski Quote:
Original post by Telastyn Quote:
One question I came across while writing this: Is there any way to declare the class methods inside the actual class definition, but define the method outside?
No, not really. Methods should be short. Long ones should be an indicator that you might want to abstract something there. Though if you still find it cluttered, Edit > Outlining > Collapse to Definitions will make classes look like C++ headers, which you can then expand to edit or view details of at your whimsy.
I like to put sections of each class into #region's in order to make it more readable and navigable. Usually, I have the regions broken down into variables, properties, events, constructor(s), methods, event handlers, and private helper methods.
That's a good plan. You might also want to further group things by functionality - one region for maze-building functions, one region for maze-drawing functions, etcetera.
I haven't gotten anywhere near finished. But since I am now stuck I will post what I have to see if what I've got so far is at least a decent start.
I can not figure out how I'm going to keep the drawing of the border of the maze separate and yet somehow control the difference between whether I am working with a border wall or one of the inner walls. This is more complicated than I thought at first.
Well, this is the first time I ever finally realized how to create a class and an object from that class with what I think is success. So, I've at least come that far.
I'm kinda disenchanted at the moment. After looking at some of the posted code so far, I realize I'm a long ways away from anywhere. But here goes anyways, I'm appreciative that I have learned a lot of new things.
The class that contains Main:
The class that will be used to create maze objects:
Thanks in advance for your time in looking at it and for offering suggestions.
I can not figure out how I'm going to keep the drawing of the border of the maze separate and yet somehow control the difference between whether I am working with a border wall or one of the inner walls. This is more complicated than I thought at first.
Well, this is the first time I ever finally realized how to create a class and an object from that class with what I think is success. So, I've at least come that far.
I'm kinda disenchanted at the moment. After looking at some of the posted code so far, I realize I'm a long ways away from anywhere. But here goes anyways, I'm appreciative that I have learned a lot of new things.
The class that contains Main:
using System;using System.Collections.Generic;using System.Text;namespace MazeGen{ class MazeGenerator { static void Main(string[] args) { // Set the title of the Console Window Console.Title = "Random Maze Generator"; // Write a greeting to the user Console.WriteLine("Welcome to The C# Maze Generator Program"); // Enter a blank line Console.WriteLine(); // Ask for dimensions once then ask if repetition is desired do { // Code for getting response from user MazeQuery(); // Check for actual input by user } while (YesNo()); } static void MazeQuery() { // Prompt for maze height Console.Write("How many rows do you want the maze to have 2-50: "); // Get rid of whitespace on each end of input received by user string strHeight = Console.ReadLine().Trim(); // Create an integer variable to hold the converted string int mazeHeight; int mazeWidth; // Try the casting of the string in strHeight to int and return it to the int variable if (Int32.TryParse(strHeight, out mazeHeight)) { // Variable mazeHeight contains the integer that was typed // Check that the number chosen was between 2 and 50 inclusive if (mazeHeight >= 2 && mazeHeight <= 50) { // Code to continue when number is 2 - 50 // Enter a blank line Console.WriteLine(); // Prompt for maze width Console.Write("How many columns do you want the maze to have 2-50: "); // Get rid of whitespace on each end of input received by user string strWidth = Console.ReadLine().Trim(); if (Int32.TryParse(strWidth, out mazeWidth)) { MazeClass MazeObject = new MazeClass(mazeHeight, mazeWidth); } } else { // The number is not 2 - 50 Console.WriteLine("Please follow instructions."); } } else { Console.Write("Not a valid number"); } } static Boolean YesNo() { Console.Write("Would you like to create another maze?"); string answer_y_n = Console.ReadLine(); if (answer_y_n == "t" || answer_y_n == "true" || answer_y_n == "y" || answer_y_n == "yes") { Console.WriteLine(); return true; } else { return false; } } }}The class that will be used to create maze objects:
using System;using MazeGen;/// <summary>/// Summary description for Class1/// </summary>namespace MazeGen{ public class MazeClass { // Each object will have it's own height and width int mazeHeight; int mazeWidth; // Default constructor public MazeClass() { // // TODO: Add constructor logic here // } // Custom constructor public MazeClass(int h, int w) { // Assign parameters to object variables mazeHeight = h; mazeWidth = w; // Write out the mazeHeight for testing purposes Console.WriteLine("Rows high: " + mazeHeight); Console.WriteLine("Rows wide: " + mazeWidth); Console.WriteLine(); } }}Thanks in advance for your time in looking at it and for offering suggestions.
First, JWalsh thank you so very much for this workshop. I have really been enjoying this workshop (and this first project was a lot of fun - Not sure if my wifes "enthusiasm" matched mine when I got my perfect maze to work, but shes a good sport!). And my apologies now for not being an active contributor to the workshop forums. My programming prior to this has been very rudimentary so I'm nowhere near comfortable enough yet to be on the advising side. And so far others have beaten me to needed questions and the tutors here helping are in a word, awesome.
For those tutoring here, you guys Rock! Your time and knowledge is greatly appreciated and is not wasted with me.
Now on to the maze code...
I have two primary classes, Project1 and PerfectMaze.
Project1 class has the main loop and the i/o methods.
PerfectMaze class contains all the maze generation and drawing code.
There are also two structures contained within the PerfectMaze class, one for the maze walls, the other for the maze rooms.
I've done a pass over of my programs structure and I'm getting happier with it, but where I feel I need a lot of improvement is in my naming of methods and fields. I'm going to spend some needed time on the MSDN naming conventions guide.
For those tutoring here, you guys Rock! Your time and knowledge is greatly appreciated and is not wasted with me.
Now on to the maze code...
I have two primary classes, Project1 and PerfectMaze.
Project1 class has the main loop and the i/o methods.
PerfectMaze class contains all the maze generation and drawing code.
There are also two structures contained within the PerfectMaze class, one for the maze walls, the other for the maze rooms.
I've done a pass over of my programs structure and I'm getting happier with it, but where I feel I need a lot of improvement is in my naming of methods and fields. I'm going to spend some needed time on the MSDN naming conventions guide.
using System;using System.Collections.Generic;using System.Text;namespace Rule3o3{ class Project1 { static void Main() { string Welcome_Message; Welcome_Message = "Welcome to the C# Perfect Maze Generator"; bool run = true; PerfectMaze MyPerfectMaze = new PerfectMaze(); int number_rows; int number_columns; Console.Title = Welcome_Message; Console.WriteLine(Welcome_Message); Console.WriteLine(); while (run) { Console.WriteLine(); Console.WriteLine("What dimensions do you want the maze to be?"); Console.Write("Enter the number of rows (min 2, max 50): "); number_rows = GetMazeDimension(); Console.Write("Enter the number of columns (min 2, max 50): "); number_columns = GetMazeDimension(); ResizeConsole(number_columns, number_rows); MyPerfectMaze.GenerateMaze(number_rows, number_columns); MyPerfectMaze.DrawMaze(); Console.WriteLine(); Console.WriteLine("Behold! Your perfect {0} by {1} maze.", number_rows, number_columns); Console.WriteLine(); run = GetRepeatMazeFlag(); } } static int GetMazeDimension() { Boolean repeat = false; string in_dimension; int dimension = 0; do { in_dimension = Console.ReadLine(); try { dimension = Convert.ToInt32(in_dimension); if (dimension > 1 && dimension < 51) { repeat = false; } else { Console.Write("Invalid entry! Please enter a number between 2 and 50: "); repeat = true; } } catch { Console.Write("Invalid entry! Please enter a number between 2 and 50: "); repeat = true; } } while (repeat); return (dimension); } static bool GetRepeatMazeFlag() { bool repeat; bool run = false; string response; Console.Write("Would you like to generate another maze? "); do { Console.Write("Enter [y]es or [n]o: "); response = Console.ReadLine(); response = response.ToLower(); if (response == "n" || response == "no" || response == "y" || response == "yes") { if (response.StartsWith("n")) { run = false; } else if (response.StartsWith("y")) { run = true; } repeat = false; } else { Console.Write("Incorrect response! "); repeat = true; } } while (repeat); return (run); } static void ResizeConsole(int width, int height) { int display_width = Console.WindowWidth; int display_height = Console.WindowHeight; if (width > 39) { display_width = 2 * (width+1); if (display_width > Console.LargestWindowWidth) { display_width = Console.LargestWindowWidth; } } if (height > 20) { display_height = 2 * (height+1); if (display_height > Console.LargestWindowHeight) { display_height = Console.LargestWindowHeight; } } Console.SetWindowSize(display_width, display_height); } } class PerfectMaze { //characters needed to draw maze static string wall = "|"; static string floor = "_"; //maze dimensions int number_rows = 0; int number_columns = 0; //Maze variables MazeRoom[] MazeRooms; MazeWall[] MazeWalls; public void GenerateMaze(int number_rows, int number_columns) { this.number_rows = number_rows; this.number_columns = number_columns; int num_rooms; num_rooms = number_rows * number_columns; this.MazeRooms = InitializeMazeRooms(num_rooms); this.MazeWalls = InitializeMazeWalls(); BuildMaze(ref this.MazeWalls, ref this.MazeRooms); } public void DrawMaze() { //Drawing top row (ceiling) Console.Write(" "); for (int i = 0; i < (number_columns + number_columns - 1); i++) { Console.Write(floor); } Console.Write(" "); Console.Write("\n"); Console.Write(wall); //Drawing each room foreach (MazeRoom mazeroom in this.MazeRooms) { if (mazeroom.SouthWall == true) { Console.Write(floor); } else if (mazeroom.SouthWall == false) { Console.Write(" "); } if (mazeroom.EastWall == true) { Console.Write(wall); } else if (mazeroom.EastWall == false) { Console.Write(floor); } //Border room in this case is the last row. Newline and then draw first wall if (mazeroom.BorderRoom == true) { Console.Write("\n"); if (mazeroom.RoomNumber != this.MazeRooms.Length) { Console.Write(wall); } } } } private MazeRoom[] InitializeMazeRooms(int num_rooms) { MazeRoom[] MazeRooms = new MazeRoom[num_rooms]; int room_number = 0; for (int i = 0; i < num_rooms; i++) { room_number = i + 1; MazeRoom mazeroom = new MazeRoom(); mazeroom.RoomNumber = i + 1; mazeroom.RoomCell = i; mazeroom.NorthWall = true; mazeroom.SouthWall = true; mazeroom.WestWall = true; mazeroom.EastWall = true; //Sets border rooms (last column) int border_check; border_check = room_number % number_columns; if (border_check == 0) { mazeroom.BorderRoom = true; } else { mazeroom.BorderRoom = false; } MazeRooms = mazeroom; } return (MazeRooms); } private MazeWall[] InitializeMazeWalls() { int number_walls = 0; number_walls = number_rows * (number_columns - 1) + number_columns * (number_rows - 1); MazeWall[] MazeWalls = new MazeWall[number_walls]; int x = 1; int y = 1; int wallcount = 0; int index = 0; int[] Mazewalls_index = new int[number_walls]; int num_wall_inrow = 2 * number_columns - 1; Mazewalls_index = RandomIndexArray(Mazewalls_index.Length); //Even wall on a row == floor wall //Odd wall on a row == side wall //Rows for (int i = 0; i < number_rows; i++) { //Columns for (int j = 0; j < num_wall_inrow; j++) { if (wallcount < Mazewalls_index.Length) { index = Mazewalls_index[wallcount]; } MazeWall mazewall = new MazeWall(); mazewall.WallNumber = wallcount; //Even wall => adjacent rooms are above and below //additionally, don't initialze floors on last row if (j % 2 == 0 && i != number_rows - 1) { mazewall.FloorWall = true; mazewall.SideWall = false; mazewall.AdjacentRoom1 = x; mazewall.AdjacentRoom2 = x + number_columns; x++; MazeWalls[index] = mazewall; wallcount++; } //Odd wall => adjacent rooms are to the left and right else if (j % 2 != 0) { mazewall.SideWall = true; mazewall.FloorWall = false; mazewall.AdjacentRoom1 = y; mazewall.AdjacentRoom2 = y + 1; y++; MazeWalls[index] = mazewall; wallcount++; } } y++; //increment inside walls when at end of row } return (MazeWalls); } private void BuildMaze(ref MazeWall[] mazewalls, ref MazeRoom[] mazerooms) { int room1 = 0; int room2 = 0; int room1_cell = 0; int room2_cell = 0; MazeRoom mazeroom1 = new MazeRoom(); MazeRoom mazeroom2 = new MazeRoom(); foreach (MazeWall mazewall in mazewalls) { room1 = mazewall.AdjacentRoom1 - 1; room2 = mazewall.AdjacentRoom2 - 1; mazeroom1 = (MazeRoom)mazerooms[room1]; mazeroom2 = (MazeRoom)mazerooms[room2]; room1_cell = mazeroom1.RoomCell; room2_cell = mazeroom2.RoomCell; //Checking to see if mazerooms are already connected. //If not, continue and knock down appropriate walls. if (mazeroom1.RoomCell != mazeroom2.RoomCell) { mazeroom2.RoomCell = mazeroom1.RoomCell; //Moves all mazerooms in room2 cell to room1 cell for (int i = 0; i < mazerooms.Length; i++) { if (mazerooms.RoomCell == room2_cell) { mazerooms.RoomCell = room1_cell; } } //Wall knockdowns if (mazewall.SideWall == true) { mazeroom1.EastWall = false; mazeroom2.WestWall = false; } if (mazewall.FloorWall == true) { mazeroom1.SouthWall = false; mazeroom2.NorthWall = false; } mazerooms[room1] = mazeroom1; mazerooms[room2] = mazeroom2; } } } //This function return an array of unique integers randomly arranged. private int[] RandomIndexArray(int array_length) { int[] walls_index = new int[array_length]; int[] random_walls_index = new int[array_length]; Random random_number = new Random(); int random_index; int temp; int shift_index; //Initilize array of wall indexes. for (int i = 0; i < array_length; i++) { walls_index = i; } for (int j = walls_index.Length-1; j >= 0; j--) { random_index = random_number.Next(0, j); random_walls_index[j] = walls_index[random_index]; temp = walls_index[random_index]; shift_index = 0; for (shift_index = random_index; shift_index < walls_index.Length - 1; shift_index++) { walls_index[shift_index] = walls_index[shift_index + 1]; } walls_index[shift_index] = temp; } return(random_walls_index); } struct MazeRoom { public int RoomNumber; public int RoomCell; public bool BorderRoom; public bool NorthWall; public bool WestWall; public bool SouthWall; public bool EastWall; } struct MazeWall { public int WallNumber; public int AdjacentRoom1; public int AdjacentRoom2; public bool SideWall; public bool FloorWall; } }}
Ok I havn't finished all of project 1 just yet but what I do have is how I'm going to implement it on paper. I've seen that theres a few people having problems with displaying there mazes so I will post my maze display method.
[WARNING]
Please note that this is not the only way there is to display the maze and is most defiantly not going to be the best way of displaying the maze, but it does however work for how I have designed my code for this project. So I'm not posting this code to say this is how it should be done. I'm simply posting my display method to give some of you guys that are struggling some ideas.
[/WARNING]
So there you have it, you have probably noticed that I have a member field of type Int32 in my Room structure which holds the amount of walls that room currently has. Then simply using nested for loops I looped through each room and drawn the right number of walls.
[OFF TOPIC]
Hope this gives you guys a couple of ideas, also if theres anything you need help on don't be afriad to ask for help, thats what this workshop is for. I'm pretty sure the tutors would rather spend a day writing answers than have students struggle until they cant go no further and decide to give up.
[/OFF TOPIC]
EDIT: Added comments to my code
[Edited by - cNoob on August 6, 2007 1:06:07 AM]
[WARNING]
Please note that this is not the only way there is to display the maze and is most defiantly not going to be the best way of displaying the maze, but it does however work for how I have designed my code for this project. So I'm not posting this code to say this is how it should be done. I'm simply posting my display method to give some of you guys that are struggling some ideas.
[/WARNING]
public void Display(){ // This is the outer edge drawn at the top of the maze // minus one from maze (width * 2) so the edge doesnt stick when drawn string topEdge = new string('_', (mazeWidth * 2)-1); // Draw a space character before the edge for neat formating Console.Write(" " + topEdge); // Loop through every row in the maze for (int y = 0; y < mazeHeight; y++) { // Each time starting a new row draw a new line and the left edge Console.Write("\n|"); // loop through every column for each row for (int x = 0; x < mazeWidth; x++) { // Work out what room we are working with int curRoom = y * mazeWidth + x; // Find out how what state the room is in switch(rooms[curRoom].wallsStanding) { // This room has no walls case 0: { Console.Write(" "); }break; // This room has a wall to the right case 1: { Console.Write(" |"); }break; // This room has a floor case 2: { Console.Write("_ "); }break; // This room has both walls case 3: { Console.Write("_|"); }break; } } } // Draw 2 new lines for neat formating purposes Console.WriteLine("\n");}So there you have it, you have probably noticed that I have a member field of type Int32 in my Room structure which holds the amount of walls that room currently has. Then simply using nested for loops I looped through each room and drawn the right number of walls.
[OFF TOPIC]
Hope this gives you guys a couple of ideas, also if theres anything you need help on don't be afriad to ask for help, thats what this workshop is for. I'm pretty sure the tutors would rather spend a day writing answers than have students struggle until they cant go no further and decide to give up.
[/OFF TOPIC]
EDIT: Added comments to my code
[Edited by - cNoob on August 6, 2007 1:06:07 AM]
Well, here's my maze generator. Its not prefect code but it does generate perfect maze. [grin]
Any comments and critique is welocme.
Program class:
Maze class:
Any comments and critique is welocme.
Program class:
class Program { static void Main(string[] args) { int defaultHeight = Console.WindowHeight; int defaultWidth = Console.WindowWidth; Console.Title = "Maze Factory"; Console.WriteLine("Welcome to Maze Factory!"); Console.WriteLine(); bool exit = false; do { int rows, columns; rows = getNumber("rows"); columns = getNumber("columns"); Maze maze = new Maze(columns, rows); maze.Draw(); exit = exitProgram(); resetConsole(defaultWidth, defaultHeight); } while (!exit); } private static bool exitProgram() { do { Console.Write("Would you like to create another maze ([y]es,|[n]o): "); ConsoleKeyInfo key = Console.ReadKey(); if (key.KeyChar == 'y' || key.KeyChar == 'Y') return false; else if (key.KeyChar == 'n' || key.KeyChar == 'N') return true; Console.WriteLine(); } while (true); } private static int getNumber(string numOfWhat) { int number; bool OK = false; do { Console.WriteLine(); Console.Write("Enter number of {0} (2-50): ", numOfWhat); if (int.TryParse(Console.ReadLine().ToString(), out number)) { if (number < 2 || number > 50) Console.WriteLine("Sorry, that's an invalid number. Please try a number between 2 and 50."); else OK = true; } else Console.WriteLine("You have not entered a number! Please try a number between 2 and 50."); } while (!OK); return number; } private static void resetConsole(int width, int height) { Console.Clear(); Console.WindowWidth = width; Console.WindowHeight = height; } }Maze class:
class Maze { #region Fields int x, y; Room[][] RoomSets; Room[] Rooms; Wall[] Walls; Room[,] Table; #endregion #region Constructor public Maze(int x, int y) { this.x = x; this.y = y; int numOfRooms = x * y; Rooms = new Room[numOfRooms]; RoomSets = new Room[numOfRooms][]; Table = new Room[x, y]; //fill Rooms, RoomSets and Table with rooms for (int i = 0; i < (x * y); i++) { Rooms = new Room(i, i); RoomSets = new Room[1] { Rooms }; Table[i % x, i / x] = Rooms; } //fill Walls list int numOfWalls = x * (y - 1) + y * (x - 1); Walls = new Wall[numOfWalls]; int thisRoom = 0; int nextRoom = 1; for (int i = 0; i < numOfWalls; i++) { Walls = new Wall(Rooms[thisRoom], Rooms[nextRoom]); thisRoom++; nextRoom++; if (nextRoom % x == 0) { if (nextRoom - x == thisRoom) nextRoom -= (x - 1); else thisRoom -= (x - 1); } } //create perfect maze Random random = new Random(); while (Walls.Length > 0) { int randomWall = random.Next(Walls.Length - 1); if (Walls[randomWall].Room1.Set != Walls[randomWall].Room2.Set) { deleteRoomsWall(Walls[randomWall].Room1, Walls[randomWall].Room2); copyRoomsToOneSet(Walls[randomWall].Room1.Set, Walls[randomWall].Room2.Set); } this.Walls = deleteWall(randomWall); } } #endregion #region Public Methods public void Draw() { //check bounds //x int width = this.x * 2 + 3; if (Console.BufferWidth<width) Console.BufferWidth = width; if (Console.WindowWidth < width) { if (Console.LargestWindowWidth < width) Console.WindowWidth = Console.LargestWindowWidth; else Console.WindowWidth = width; } //y int height = this.y + 2; if (Console.BufferHeight < height) Console.BufferHeight = height; if (Console.WindowHeight < height) { if (Console.LargestWindowHeight < height) Console.WindowHeight = Console.LargestWindowHeight; else Console.WindowHeight = height; } //draw Console.Clear(); for (int x = 0; x < this.x; x++) { for (int y = 0; y < this.y; y++) { drawSquare(Table[x, y]); } } Console.CursorLeft = 0; Console.CursorTop = this.y+1; } #endregion #region Private Methods private void copyRoomsToOneSet(int set1, int set2) { int numOfCombinedRooms = this.RoomSets[set1].Length + this.RoomSets[set2].Length; Room[] rooms = new Room[numOfCombinedRooms]; int i = 0; foreach (Room room in this.RoomSets[set1]) { room.Set = set1; rooms = room; i++; } foreach (Room room in this.RoomSets[set2]) { room.Set = set1; rooms = room; i++; } this.RoomSets[set1] = rooms; this.RoomSets[set2] = new Room[0]; } private void deleteRoomsWall(Room room1, Room room2) { if (room1.Number + 1 == room2.Number) { room1.RightWall = false; room2.LeftWall = false; } else if (room1.Number == room2.Number + 1) { room2.RightWall = false; room1.LeftWall = false; } else if (room2.Number - x == room1.Number) { room1.BottomWall = false; room2.TopWall = false; } else if (room1.Number - x == room2.Number) { room2.BottomWall = false; room1.TopWall = false; } } private Wall[] deleteWall(int wallToDelete) { Wall[] Walls = new Wall[this.Walls.Length - 1]; for (int i = 0; i < this.Walls.Length; i++) { if (i != wallToDelete) { if (i < wallToDelete) Walls = this.Walls; else Walls[i - 1] = this.Walls; } } return Walls; } private void drawSquare(Room room) { int x, y; x = room.Number % this.x; y = room.Number / this.x; x *= 2; Console.CursorLeft = x + 1; Console.CursorTop = y; if (room.TopWall) Console.Write('_'); Console.CursorLeft = x; Console.CursorTop = y + 1; if (room.LeftWall) Console.Write('|'); else Console.CursorLeft++; if (room.BottomWall) Console.Write('_'); else Console.CursorLeft++; if (room.RightWall) Console.Write('|'); } #endregion #region Nested Types class Room { public int Number; public int Set; public bool LeftWall, RightWall, TopWall, BottomWall; public Room(int Number, int Set) { this.Number = Number; this.Set = Set; LeftWall = RightWall = TopWall = BottomWall = true; } } class Wall { public Room Room1, Room2; public Wall(Room Room1, Room Room2) { this.Room1 = Room1; this.Room2 = Room2; } } #endregion }
I was told that Lists are beyond the scope of the project, but I saw nothing prohibiting their use (and I already know all about arrays anyway), so take a look.
P.S. For those beginners who are taking a look, just think of a list as a dynamic array (not technically correct, but correct enough to look at my code). Nothing complicated, it just makes common/easy operations (add, remove, sort) even easier without the programmer having to worry so much about memory management.
Program.cs:
Maze.cs:
Have fun.
P.S. For those beginners who are taking a look, just think of a list as a dynamic array (not technically correct, but correct enough to look at my code). Nothing complicated, it just makes common/easy operations (add, remove, sort) even easier without the programmer having to worry so much about memory management.
Program.cs:
using System;using System.Collections.Generic;using System.Text;namespace CSharp_Workshop_Project_1___Maze_Generation{ class Program { static void Main(string[] args) { bool generate_maze = true; Console.Title = "C# Workshop Project 1 - Perfect Maze Generation"; Console.WriteLine("Welcome to the C# Workshop Perfect Maze Generator.\n\n"); int rows, columns; while (true) { // if we're no longer generating mazes if (!generate_maze) break; GetUserInput(out rows, out columns); // resize the console bigger than the correct size if (Console.WindowHeight < rows) Console.WindowHeight = rows + 10; if (Console.WindowWidth < (columns*2)+10) Console.WindowWidth = (columns * 2) + 10; //System.IO.TextWriter file = new System.IO.StreamWriter("maze.txt"); Maze maze = new Maze(rows, columns); maze.GenerateMaze(); // generate the maze maze.OutputMaze(Console.Out); // output the maze to the output stream //maze.OutputMaze(file); // output the maze to the output stream //file.Close(); string str = ""; while (str.Length == 0) { Console.Write("\n\nWould you like to generate another maze? ([y]es|[n]o): "); str = Console.ReadLine(); str = str.ToLower(); if (str == "yes" || str == "y") break; else if (str == "no" || str == "n") { generate_maze = false; break; } else { str = ""; Console.Write("\nInvalid input. Please answer either 'yes' or 'no'"); } }// while str is empty }// while } static void GetUserInput(out int r, out int c) { string str; int rows, columns; Console.Write("\nGenerating a maze:\n"); // read the number of rows Console.Write("\nEnter the number of rows for the maze (2-50): "); str = Console.ReadLine(); while (true) { if (!Int32.TryParse(str, out rows)) { Console.Write("\nInvalid input, please try again."); } else if (rows < 2 || rows > 50) { Console.Write("\nInvalid number of rows (must be between 2 and 50), please try again."); } else break; Console.Write("\nEnter the number of rows for the maze (2-50): "); str = Console.ReadLine(); }// while // read the number of columns Console.Write("\nEnter the number of columns for the maze (2-50): "); str = Console.ReadLine(); while (true) { if (!Int32.TryParse(str, out columns)) { Console.Write("\nInvalid input, please try again."); } else if (columns < 2 || columns > 50) { Console.Write("\nInvalid number of columns (must be between 2 and 50), please try again."); } else break; Console.Write("\nEnter the number of columns for the maze (2-50): "); str = Console.ReadLine(); }// while r = rows; c = columns; }// GetUserInput }}Maze.cs:
using System;using System.Collections.Generic;using System.Text;namespace CSharp_Workshop_Project_1___Maze_Generation{ class Maze { private int NumRooms, NumRows, NumColumns, NumWalls, NumVertWallsPerRow, NumHorWallsPerRow; private List<List<int>> RoomSets; // the sets of the rooms that have a path to each other private List<int> WallsUp; // a list of the walls up public Maze(int r, int c) { NumRows = r; NumColumns = c; NumRooms = NumRows * NumColumns; NumVertWallsPerRow = NumColumns - 1; NumHorWallsPerRow = NumColumns; RoomSets = new List<List<int>>(); WallsUp = new List<int>(); // init all rooms into a set each for (int i = 0; i < NumRooms; ++i) { List<int> temp = new List<int>(); temp.Add(i); RoomSets.Add(temp); } // calc the number of walls NumWalls = ((r * (c - 1)) + (c * (r - 1))); // init all walls as up for (int i = 0; i < NumWalls; ++i) WallsUp.Add(i); Random rand = new Random(); // randomize the WallsUp array for (int i = 0; i < WallsUp.Count; ++i) { int temp, temp_index = rand.Next() % (WallsUp.Count - i) + i; temp = WallsUp; WallsUp = WallsUp[temp_index]; WallsUp[temp_index] = temp; } } public void OutputMaze(System.IO.TextWriter stream) { stream.Write("\n\n"); stream.Write(' '); // print out the top border for (int i = 0; i < (NumColumns * 2)-1; ++i) stream.Write('_'); // Output the walls based on the walls that are still up - iterate through all the rooms // and if there should be a wall there, output it, else, output a space. for (int i = 0; i < NumRows; ++i) { stream.Write("\n|"); for (int j = 0; j < NumColumns; ++j) { // calc the room number we should be looking at int room_num = (i * NumColumns) + j; int wall1, // vertical wall for room wall2; // horizontal wall for room (at bottom of room) GetWallsFromRoom(room_num, out wall1, out wall2); // for the first character (horizontal wall) if (WallsUp.Contains(wall2)) stream.Write('_'); // else just a space else { // if we're on the last row of rooms, it will never have a horizontal wall, // but it needs a border. if (i == NumRows - 1) stream.Write('_'); // else this is just a room with no horizontal wall else stream.Write(' '); } // for the second character (vertical wall) if (WallsUp.Contains(wall1)) stream.Write('|'); // else just a space else { // if we're on the last room in the row, it will never have a wall, but it needs the border if (j == NumColumns - 1) stream.Write('|'); // if we're on the last row of rooms, and there is no vertical wall, we have to write it // as the border. else if (i == NumRows - 1) stream.Write('_'); // else this is just a room with no vertical wall else stream.Write(' '); } }// for j < NumColumns }// for i < NumRows stream.Write("\n\n"); } public void GenerateMaze() { // DEBUG //Console.WriteLine("Number of rows: " + NumRows.ToString()); //Console.WriteLine("Number of columns: " + NumColumns.ToString()); //Console.WriteLine("Number of walls: " + NumWalls.ToString()); // DEBUG Console.WriteLine("\nGenerating maze"); /////// Generate the maze /////// for (int i = 0; i < WallsUp.Count; ++i ) { int room1, room2; GetRoomsFromWall(WallsUp, out room1, out room2); List<int> room1_set = FindSetThatContains(room1); List<int> room2_set = FindSetThatContains(room2); // if the two rooms are not in the same set if (room1_set != room2_set) { // knock down the wall between them WallsUp.RemoveAt(i); --i; // so when we increment at the beginning of the loop, we end up at the same index // which is the next item since we just deleted one. // Put everything from room2_set into room1_set // This is because if two rooms are in a set, that means that there is a path between them // so, if we have three rooms in two sets: (A) and (B,C), if a path opens up between A and B, // then there is automatically a path from A to C. room1_set.AddRange(room2_set); room2_set.Clear(); } // else the two rooms are in the same set, so leave the wall up } } /** * Return the two walls that bound a particular room. If the room only has 1 wall, ie. it is on the * boundary of the maze, the other var will be set to '-1' * \param wall1 The vertical wall for the room * \param wall2 The horizontal wall for the room */ private void GetWallsFromRoom(int room, out int wall1, out int wall2) { if( room < 0 || room >= NumRooms ) throw new Exception("Invalid room index: " + room.ToString()); // integer division to find the row the room is on int row = room / NumColumns; // the index of the first room on the row int start_room = row * NumColumns; // the first index of the first vertical wall on the first row int start_vert = (NumVertWallsPerRow + NumHorWallsPerRow) * row; int start_hor = NumVertWallsPerRow + ((NumHorWallsPerRow + NumVertWallsPerRow) * row); // find the offset of the room index into the current row int room_offset = room - start_room; wall1 = start_vert + room_offset; wall2 = start_hor + room_offset; // The vertical wall for the last room is not considered to be a wall. It is the border of the maze. // It is ignored. if (wall1 >= NumVertWallsPerRow + start_vert) wall1 = -1; // The bottom border of the maze are not considered to be walls. They are ignored. if (wall2 >= NumWalls || wall2 >= NumHorWallsPerRow + start_hor) wall2 = -1; } /** * Find the two rooms that are connected by the given wall. */ private void GetRoomsFromWall(int wall, out int room1, out int room2) { if (wall >= NumWalls || wall < 0) throw new Exception("Invalid wall index: " + wall.ToString()); int start_vert = 0, // the index of the first vertical wall in the row start_hor = NumVertWallsPerRow; // the index of the first horizontal wall in the row for (int i = 0; i < NumRows; ++i) { // where 'i' is the row index int start_room = i * NumColumns; // if 'wall' is a verical wall in row 'i' if (wall >= start_vert && wall <= ((start_vert + NumVertWallsPerRow) - 1)) { int wall_offset = wall - start_vert; room1 = start_room + wall_offset; room2 = start_room + 1 + wall_offset; return; } // if 'wall' is a horizontal wall in row 'i' else if (wall >= start_hor && wall <= ((start_hor + NumHorWallsPerRow) - 1)) { int wall_offset = wall - start_hor; room1 = start_room + wall_offset; room2 = room1 + NumColumns; return; } // get ready for the next row start_vert += NumVertWallsPerRow + NumHorWallsPerRow; start_hor += NumHorWallsPerRow + NumVertWallsPerRow; }// for room1 = -1; room2 = -1; throw new Exception("Error: invalid wall index, or something. I reached the end of a function I shouldn't have."); } /** * Return the set that contains the given element */ public List<int> FindSetThatContains(int item) { foreach (List<int> temp in RoomSets) { if (temp.Contains(item)) { List<int> return_temp = temp; return return_temp; } } return null; } public List<int> FindBiggestRoomSet() { int index = 0, size = RoomSets[0].Count; for (int i = 1; i < RoomSets.Count; ++i) { if (RoomSets.Count > size) { index = i; size = RoomSets.Count; } } return RoomSets[index]; } }}Have fun.
Ok, first, I decided to use classes to represent my walls and rooms rather than structures. Partially out of preference, though I also added several methods to simplify the code in the main part of the program. I also, like Endar, used the List class, mostly because I already know it, as it handles most of the common operations for you.
Also, instead of using one primary set of rooms lists, I gave each room a self-contained list that would hold all the rooms its connected to. Each wall also knows which rooms it is connected to, and the rooms know which walls they are connected to.
Program.cs
Room.cs
Wall.cs
Also, instead of using one primary set of rooms lists, I gave each room a self-contained list that would hold all the rooms its connected to. Each wall also knows which rooms it is connected to, and the rooms know which walls they are connected to.
Program.cs
using System;using System.Collections.Generic;using System.Text;namespace ConsoleApplication1{ class Program { static void Main(string[] args) { //welcome and title Console.Title = "Maze Generator 32354 by Xeio"; Console.WriteLine("Welcome to a Random Maze Generator 32354, yet another way to make random mazes."); //loop used to coninue if user wishes to make multiple mazes do { //gets size of maze from user, passes them back to main method int height, width; querySize(out height, out width); //creates rooms and walls, passes back 2d array of rooms and list of all walls to main method Room[,] mazeRooms; List<Wall> wallList; createFullMaze(height, width, out mazeRooms, out wallList); //destroys random walls untill all rooms are connected breakWalls(wallList); //draws the maze using the room array drawMaze(mazeRooms); //checks if the user wishes to continue and make another maze } while (queryContinue()); //pauses program waiting for enter key to exit Console.WriteLine("Press any key to continue..."); Console.ReadLine(); } private static void querySize(out int height, out int width) { height = 0; width = 0; //querys user for number of rows (width) Console.WriteLine("Enter the number of rows for the maze between 2 and 50:"); do { //try block, used to catch non-numbers and int overflows when the computer parses the entry try { //parses the entered number width = int.Parse(Console.ReadLine()); //checks if the number is valid, if not, prints a message that you need to re-enter it if (width < 2 || width > 50) Console.WriteLine("Invalid entry, please retry:" + width); } catch (FormatException) { Console.WriteLine("Not a number, please enter a number between 1 and 50"); } catch (OverflowException) { Console.WriteLine("Invalid entry, please try again."); } //repeats the loop as long as the number is still invalid } while (width < 2 || width > 50); //querys user for number of colums (height) Console.WriteLine("Enter the number of columns for the maze between 2 and 50:"); do { //try block, used to catch non-numbers and int overflows when the computer parses the entry try { //parses the entered number height = int.Parse(Console.ReadLine()); //checks if the number is valid, if not, prints a message that you need to re-enter it if (height < 2 || height > 50) Console.WriteLine("Invalid entry, please retry:" + height); } catch (FormatException) { Console.WriteLine("Not a number, please enter a number between 1 and 50"); } catch (OverflowException) { Console.WriteLine("Invalid entry, please try again."); } //repeats the loop as long as the number is still invalid } while (height < 2 || height > 50); } private static void createFullMaze(int height, int width, out Room[,] mazeRooms, out List<Wall> wallList) { //creates list for walls, and the array for the maze rooms (using the passed in width and height) mazeRooms = new Room[height, width]; wallList = new List<Wall>(); //creates rooms and puts in multidimensional array (2d) int count = 0; for (int h = 0; h < height; h++) for (int w = 0; w < width; w++) mazeRooms[h, w] = new Room(count++); //creates walls then adds walls to rooms for (int h = 0; h < height; h++) for (int w = 0; w < width; w++) { //loops iterate through every room //horizontal walls if (!(w == width - 1)) { //as long as room is not on right end of maze, adds a wall (dis)connecting it to the right room Wall wall = new Wall(mazeRooms[h, w], mazeRooms[h, w + 1]); wallList.Add(wall); mazeRooms[h, w].right = wall; mazeRooms[h, w + 1].left = wall; } //verticle walls if (!(h == height - 1)) { //as long as room is not on bottom end of maze, adds a wall (dis)connecting it to the below room Wall wall = new Wall(mazeRooms[h, w], mazeRooms[h + 1, w]); wallList.Add(wall); mazeRooms[h, w].down = wall; mazeRooms[h + 1, w].up = wall; } } } private static void breakWalls(List<Wall> wallList) { //seeds a random for wall destruction Random rand = new Random(); while (wallList.Count > 0) { //picks a random wall that is not destroyed, and has not already been checked int index = rand.Next(wallList.Count - 1); if (!wallList[index].roomsAlreadyConnected()) { //if the rooms next to the wall are not already connected destroys the wall wallList[index].destroyWall(); } //removes wall so it is not checked again wallList.RemoveAt(index); } } private static void drawMaze(Room[,] rooms) { //draws the top of the maze Console.Write(" "); for (int width = 0; width < rooms.GetLength(1) - 1; width++) Console.Write("__"); Console.WriteLine("_"); //draws the main part of the maze for (int height = 0; height < rooms.GetLength(0); height++) { //left wall Console.Write("|"); for (int width = 0; width < rooms.GetLength(1); width++) { //if room has wall down, draws it //also draws wall down if edge of maze if (rooms[height, width].hasWallDown() || height == rooms.GetLength(0) - 1) Console.Write("_"); else Console.Write(" "); //if room has wall right, draws it //also draws wall right if edge of maze if (rooms[height, width].hasWallRight() || width == rooms.GetLength(1) - 1) Console.Write("|"); else Console.Write("_"); } Console.WriteLine(); } } private static bool queryContinue() { //checks if the user wants to continue Console.WriteLine("Maze Completed! Would you like to create another? ([Y]es, [N]o):"); while (true) { String temp = Console.ReadLine(); if (temp.ToLower().Equals("yes") || temp.ToLower().Equals("y")) return true; else if (temp.ToLower().Equals("no") || temp.ToLower().Equals("n")) return false; else Console.WriteLine("Invalid entry, please enter one of the choices ([Y]es, [N]o)"); } } }}Room.cs
using System;using System.Collections.Generic;using System.Text;namespace ConsoleApplication1{ class Room { public int roomnum; //walls of the room public Wall down, right, up, left; //list of connected rooms private List<Room> connected; public Room(int num) { roomnum = num; //makes new list of connected rooms, and adds this room (its connected to itself) connected = new List<Room>(); connected.Add(this); } public void removeWall(Wall wall) { //removes a specified wall (from any direction) if present in this room if(up == wall) up = null; if(right == wall) right = null; if(down == wall) down = null; if(left == wall) left = null; } public bool hasWallDown() { //checks if the down wall is a null reference (I.E. there is no wall) and returns false if it is return (down != null); } public bool hasWallRight() { //checks if the right wall is a null reference (I.E. there is no wall) and returns false if it is return (right != null); } public void mergeWith(Room room) { //adds list of connected rooms from passed room and this together connected.AddRange(room.connected); //loop ensures that every connected room's list is updated to reflect merge foreach (Room r in connected) r.connected = connected; } public bool isConnectedTo(Room room) { //checks if this room is connected to the passed room return connected.Contains(room); } }}Wall.cs
using System;using System.Collections.Generic;using System.Text;namespace ConsoleApplication1{ class Wall { public Room room1, room2; public Wall(Room room1, Room room2) { this.room1 = room1; this.room2 = room2; } public void destroyWall() { //destroys this wall, then updates each room's list of connected rooms to reflect the newly destroyed wall room1.removeWall(this); room2.removeWall(this); room1.mergeWith(room2); } public bool roomsAlreadyConnected() { //checks if rooms are already connected return room1.isConnectedTo(room2); } }}
[WARNING]
I am a cheater. I went through the project so I could better answer questions, so I focused on the bits that would help me most.
I used stuff that's not yet been covered in the workshop. I also circumvented the requirements, skipping the user input stuff. I also used an arguably different algorithm to generate the maze (slight modifications to a pathfinding algorithm I once implemented).
Oh, and I did some naughty habit things (fixed indexes in TileSet, bad variable naming in places, leaving things (maze wrapping, 3d bits, specifying the size via cli) undone, nesting method calls in places, lack of refactoring...).
All in all it works, and well; but it is very thrown together. Review at your own peril!
[/warning]
That out of the way, my maze gen was split into two projects. One for the infrastructure and the generations, the other for the 'front end' which would eventually take parameters and the such.
The part that took the longest was easily Map.ToAscii. Map.Generate took about 20 mins.
First, the front-end:
Yeah.
Saw a nifty maze and promptly stopped. I'm not exactly a front-end kinda guy...
I am a cheater. I went through the project so I could better answer questions, so I focused on the bits that would help me most.
I used stuff that's not yet been covered in the workshop. I also circumvented the requirements, skipping the user input stuff. I also used an arguably different algorithm to generate the maze (slight modifications to a pathfinding algorithm I once implemented).
Oh, and I did some naughty habit things (fixed indexes in TileSet, bad variable naming in places, leaving things (maze wrapping, 3d bits, specifying the size via cli) undone, nesting method calls in places, lack of refactoring...).
All in all it works, and well; but it is very thrown together. Review at your own peril!
[/warning]
That out of the way, my maze gen was split into two projects. One for the infrastructure and the generations, the other for the 'front end' which would eventually take parameters and the such.
The part that took the longest was easily Map.ToAscii. Map.Generate took about 20 mins.
First, the front-end:
using System;using System.Collections.Generic;using System.Text;namespace GDnet_csWorkshop_proj1 { class Program { static void Main(string[] args) { Console.SetWindowSize(80, 80); foreach (string line in GDnet_csWorkshop_mazelib.Map.Generate(20,20,1,false,false,false,42).ToAscii()) { Console.WriteLine(line); } } }}Yeah.
Saw a nifty maze and promptly stopped. I'm not exactly a front-end kinda guy...
using System;using System.Collections.Generic;using System.Text;namespace GDnet_csWorkshop_mazelib { // stolen from moe. public class CoordinateLookupTable { private Dictionary<int, Dictionary<int, Dictionary<int, Tile>>> Table = new Dictionary<int, Dictionary<int, Dictionary<int, Tile>>>(); public void Add(Tile tile) { if (!Table.ContainsKey(tile.X)) { Table.Add(tile.X, new Dictionary<int, Dictionary<int, Tile>>()); } if (!Table[tile.X].ContainsKey(tile.Y)) { Table[tile.X].Add(tile.Y, new Dictionary<int, Tile>()); } if (!Table[tile.X][tile.Y].ContainsKey(tile.Z)) { Table[tile.X][tile.Y].Add(tile.Z, tile); } else { Table[tile.X][tile.Y][tile.Z] = tile; } } public void Add(Tile tile, int[] from, int[] to) { Add(tile); } public Tile Fetch(int x, int y, int z) { if (!Table.ContainsKey(x)) { return (null); } if (!Table[x].ContainsKey(y)) { return (null); } if (!Table[x][y].ContainsKey(z)) { return (null); } return (Table[x][y][z]); } public void Clear() { Table = new Dictionary<int, Dictionary<int, Dictionary<int, Tile>>>(); } public void Remove(Tile tile) { if (Fetch(tile.X, tile.Y, tile.Z) == tile) { Table[tile.X][tile.Y].Remove(tile.Z); } } } public class Map { protected List<Tile> Tiles = new List<Tile>(); protected CoordinateLookupTable TileTable = new CoordinateLookupTable(); public readonly bool WrapX; public readonly bool WrapY; public readonly bool WrapZ; internal int minX = int.MaxValue; internal int maxX = int.MinValue; internal int minY = int.MaxValue; internal int maxY = int.MinValue; internal int minZ = int.MaxValue; internal int maxZ = int.MinValue; public enum Direction { North, East, South, West } public static Direction OppositeOf(Direction direction) { Direction toDir = Direction.North; switch (direction) { case Direction.East: toDir = Direction.West; break; case Direction.North: toDir = Direction.South; break; case Direction.South: toDir = Direction.North; break; case Direction.West: toDir = Direction.East; break; } return (toDir); } public void Add(Tile what) { Tiles.Add(what); TileTable.Add(what); if (what.X < minX) { minX = what.X; } if (what.X > maxX) { maxX = what.X; } if (what.Y < minY) { minY = what.Y; } if (what.Y > maxY) { maxY = what.Y; } if (what.Z < minZ) { minZ = what.Z; } if (what.Z > maxZ) { maxZ = what.Z; } } internal static void SetPath(Tile from, Direction direction, Tile to, Path value) { Direction fromDir = direction; from.Add(fromDir, value); if (value.State != Path.Type.Solid && to != null) { to.Add(OppositeOf(fromDir), value); } } protected Map(bool wrapX, bool wrapY, bool wrapZ) { } protected Map() : this(false, false, false) { } protected Tile[] TileSet(int x, int y, int z) { Tile[] rtn = new Tile[10]; rtn[1] = TileTable.Fetch(x - 1, y + 1, z); rtn[2] = TileTable.Fetch(x, y + 1, z); rtn[3] = TileTable.Fetch(x + 1, y + 1, z); rtn[4] = TileTable.Fetch(x - 1, y, z); rtn[5] = TileTable.Fetch(x, y, z); rtn[6] = TileTable.Fetch(x + 1, y, z); rtn[7] = TileTable.Fetch(x - 1, y - 1, z); rtn[8] = TileTable.Fetch(x, y - 1, z); rtn[9] = TileTable.Fetch(x + 1, y - 1, z); return (rtn); } protected Tile[] TileSet(Tile what) { return (TileSet(what.X, what.Y, what.Z)); } protected char AsciiFor(float x, float y, float z) { if (z % 1 != 0) { throw new NotImplementedException(); } if (x % 1 == 0) { if (y % 1 == 0) { return (Path.AsciiRepresentation[Path.Type.Clear]); } else { Tile high = TileTable.Fetch((int)Math.Floor(x), (int)Math.Floor(y), (int)Math.Floor(z)); Tile low = TileTable.Fetch((int)Math.Floor(x), (int)Math.Ceiling(y), (int)Math.Floor(z)); if (high == null && low == null) { return (' '); } if (high != null) { return (Path.AsciiRepresentation[high.PathTo(Direction.South).State]); } else { return (Path.AsciiRepresentation[low.PathTo(Direction.North).State]); } } } else { if (y % 1 == 0) { Tile right = TileTable.Fetch((int)Math.Ceiling(x), (int)Math.Floor(y), (int)Math.Floor(z)); Tile left = TileTable.Fetch((int)Math.Floor(x), (int)Math.Floor(y), (int)Math.Floor(z)); if (right == null && left == null) { return (' '); } if (left != null) { return (Path.AsciiRepresentation[left.PathTo(Direction.East).State]); } else { return (Path.AsciiRepresentation[right.PathTo(Direction.West).State]); } } else { Tile nw = TileTable.Fetch((int)Math.Floor(x), (int)Math.Floor(y), (int)Math.Floor(z)); Tile ne = TileTable.Fetch((int)Math.Ceiling(x), (int)Math.Floor(y), (int)Math.Floor(z)); Tile se = TileTable.Fetch((int)Math.Ceiling(x), (int)Math.Ceiling(y), (int)Math.Floor(z)); Tile sw = TileTable.Fetch((int)Math.Floor(x), (int)Math.Ceiling(y), (int)Math.Floor(z)); if (nw != null || ne != null || se != null || sw != null) { return (Path.AsciiRepresentation[Path.Type.Solid]); } else { return (' '); } } } } private enum GenState { Closed, Used, Open } public static Map Generate(int xSize, int ySize, int zSize, bool xWrap, bool yWrap, bool zWrap, int seed) { if (xSize < 1 || ySize < 1 || zSize < 1) { return (null); } Map rtn = new Map(xWrap, yWrap, zWrap); for (int x = 0; x < xSize; ++x) { for (int y = 0; y < ySize; ++y) { for (int z = 0; z < zSize; ++z) { Tile newbie = new Tile(x, y, z); if (y == 0) { newbie.Add(Direction.North, new Path(Path.Type.Solid)); } else { newbie.Add(Direction.North, new Path(Path.Type.Wall)); } if (y == ySize - 1) { newbie.Add(Direction.South, new Path(Path.Type.Solid)); } else { newbie.Add(Direction.South, new Path(Path.Type.Wall)); } if (x == 0) { newbie.Add(Direction.West, new Path(Path.Type.Solid)); } else { newbie.Add(Direction.West, new Path(Path.Type.Wall)); } if (x == xSize - 1) { newbie.Add(Direction.East, new Path(Path.Type.Solid)); } else { newbie.Add(Direction.East, new Path(Path.Type.Wall)); } rtn.Add(newbie); } } } Random rng = new Random(seed); Dictionary<Tile, GenState> GenStatus = new Dictionary<Tile, GenState>(); List<Tile> OpenPath = new List<Tile>(); foreach (Tile t in rtn.Tiles) { GenStatus.Add(t, GenState.Closed); } int startX; int startY; int startZ; startX = rng.Next(rtn.minX, rtn.maxX + 1); startY = rng.Next(rtn.minY, rtn.maxY + 1); startZ = rng.Next(rtn.minZ, rtn.maxZ + 1); Tile CurrentTile = rtn.TileTable.Fetch(startX, startY, startZ); OpenPath.Add(CurrentTile); GenStatus[CurrentTile] = GenState.Open; Tile[] SurroundingTiles; while (OpenPath.Count > 0) { CurrentTile = OpenPath[OpenPath.Count - 1]; SurroundingTiles = rtn.TileSet(CurrentTile); if (SurroundingTiles[2] != null && GenStatus[SurroundingTiles[2]] == GenState.Closed || SurroundingTiles[4] != null && GenStatus[SurroundingTiles[4]] == GenState.Closed || SurroundingTiles[6] != null && GenStatus[SurroundingTiles[6]] == GenState.Closed || SurroundingTiles[8] != null && GenStatus[SurroundingTiles[8]] == GenState.Closed) { int Target; do { Target = rng.Next(1, 5) * 2; } while (SurroundingTiles[Target] == null || GenStatus[SurroundingTiles[Target]] != GenState.Closed); Direction TargetDirection = Direction.South; switch(Target){ case 4: TargetDirection=Direction.West; break; case 6: TargetDirection=Direction.East; break; case 8: TargetDirection=Direction.North; break; } OpenPath.Add(SurroundingTiles[Target]); GenStatus[SurroundingTiles[Target]] = GenState.Open; SetPath(CurrentTile, TargetDirection, SurroundingTiles[Target], new Path(Path.Type.Clear)); } else { GenStatus[CurrentTile] = GenState.Used; OpenPath.Remove(CurrentTile); } } return (rtn); } public static Map TestCreate() { Map rtn = new Map(); Tile[] Tiles = new Tile[10]; int a = 1; for (int y = 2; y >= 0; --y) { for (int x = 0; x <= 2; ++x) { Tiles[a] = new Tile(x, y, 0); ++a; } } Map.SetPath(Tiles[5], Direction.East, Tiles[6], new Path(Path.Type.Clear)); Map.SetPath(Tiles[5], Direction.South, Tiles[2], new Path(Path.Type.Wall)); Map.SetPath(Tiles[5], Direction.West, Tiles[4], new Path(Path.Type.Wall)); Map.SetPath(Tiles[5], Direction.North, Tiles[8], new Path(Path.Type.Wall)); Map.SetPath(Tiles[6], Direction.North, Tiles[9], new Path(Path.Type.Clear)); Map.SetPath(Tiles[6], Direction.East, null, new Path(Path.Type.Solid)); Map.SetPath(Tiles[6],Direction.South,Tiles[3],new Path(Path.Type.Wall)); Map.SetPath(Tiles[9], Direction.West, Tiles[8], new Path(Path.Type.Clear)); Map.SetPath(Tiles[9], Direction.North, null, new Path(Path.Type.Solid)); Map.SetPath(Tiles[9], Direction.East, null, new Path(Path.Type.Solid)); Map.SetPath(Tiles[8], Direction.West, Tiles[7], new Path(Path.Type.Clear)); Map.SetPath(Tiles[8], Direction.North, null, new Path(Path.Type.Solid)); Map.SetPath(Tiles[7], Direction.South, Tiles[4], new Path(Path.Type.Clear)); Map.SetPath(Tiles[7], Direction.North, null, new Path(Path.Type.Solid)); Map.SetPath(Tiles[7], Direction.West, null, new Path(Path.Type.Solid)); Map.SetPath(Tiles[4], Direction.South, Tiles[1], new Path(Path.Type.Clear)); Map.SetPath(Tiles[4], Direction.West, null, new Path(Path.Type.Solid)); Map.SetPath(Tiles[1], Direction.East, Tiles[2], new Path(Path.Type.Clear)); Map.SetPath(Tiles[1], Direction.South, null, new Path(Path.Type.Solid)); Map.SetPath(Tiles[1], Direction.West, null, new Path(Path.Type.Solid)); Map.SetPath(Tiles[2], Direction.East, Tiles[3], new Path(Path.Type.Clear)); Map.SetPath(Tiles[2], Direction.South, null, new Path(Path.Type.Solid)); Map.SetPath(Tiles[3], Direction.South, null, new Path(Path.Type.Solid)); Map.SetPath(Tiles[3], Direction.East, null, new Path(Path.Type.Solid)); for (int x = 1; x < 10; ++x) { rtn.Add(Tiles[x]); } return (rtn); } public List<string> ToAscii() { List<string> rtn = new List<string>(); string currentLine; float curX; float curY; for (curY = (float)(minY - .5); curY <= maxY + .5; curY += (float).5) { currentLine = ""; for (curX = (float)(minX - .5); curX <= maxX + .5; curX += (float).5) { currentLine += AsciiFor(curX, curY, minZ); } rtn.Add(currentLine); } return (rtn); } } public class Tile { public int X; public int Y; public int Z; protected Dictionary<Map.Direction,Path> paths=new Dictionary<Map.Direction,Path>(); public Path PathTo(Map.Direction Direction) { if (paths.ContainsKey(Direction)) { return (paths[Direction]); } else { return (null); } } internal void Add(Map.Direction Direction, Path path) { paths[Direction] = path; } internal Tile(int inX, int inY, int inZ) { X = inX; Y = inY; Z=inZ; } } public class Path { public static Dictionary<Type, char> AsciiRepresentation = new Dictionary<Type, char>(); public enum Type { Unknown, Solid, Clear, Wall, OpenDoor, ClosedDoor } protected Type state; public Type State{ get{ return(state); } internal set{ state=value; } } internal Path(Type inState) { state = inState; } static Path(){ AsciiRepresentation[Type.Unknown] = '?'; AsciiRepresentation[Type.Solid] = '#'; AsciiRepresentation[Type.Clear] = '.'; AsciiRepresentation[Type.Wall] = '#'; } }}
Time to give you my own solution. I tried to stick to the requirements, and hopefully I succeeded (well, I throw an exception; that's not that good). The design of this solution is not that bad, although it could be better (if we were allowed to use more bits of the language). I believe that the whole project will serve as a base for the forthcoming projects, so I should be able to refactor it a bit.
Take care, this is a bit long...
I'm afraid I forgot to comment most things. Sorry for the inconvenience... [smile] I'm going to comment everything and put that into a ZIP later.
Take care, this is a bit long...
using System;using System.Collections.Generic;using System.Text;namespace MazeGen{ class Program { static int GetIntegerFromConsole(string message, int min, int max, string onError) { int value = 0; bool isValid = false; do { try { Console.Write(message + " "); string userInput = Console.ReadLine(); if (userInput.Length > 0) { value = Convert.ToInt32(userInput); if (value > min && value < max) { isValid = true; } else { throw new OverflowException(); } } } catch (FormatException) { Console.WriteLine(onError); } catch (OverflowException) { Console.WriteLine(onError); } } while (!isValid); return value; } static void UpdateConsole(int columns, int lines) { if (Console.WindowWidth < columns + 10) { Console.WindowWidth = columns + 10; } if (Console.WindowHeight < lines + 10) { Console.WindowHeight = lines + 10; } } static void GenerateAndPrintMaze(int columns, int lines) { Model.MazeGenerator generator = new Model.MazeGenerator(columns, lines); generator.MakeMaze(); generator.PrintMaze(new Model.MazePrinter()); } static void Main(string[] args) { Console.WriteLine("Thy be Cee Pound MazeGen!"); Console.WriteLine(""); int columns = GetIntegerFromConsole("Gimme thy number of columns!", 2, 51, "Nay. Thy number be greater than 2 or weaker than 50"); int lines = GetIntegerFromConsole("Gimme thy number of lines!", 2, 51, "Nay. Thy number be greater than 2 or weaker than 50"); Console.WriteLine(""); UpdateConsole(columns, lines); GenerateAndPrintMaze(columns, lines); Console.WriteLine(""); } }}namespace MazeGen.Model{ public class MazeGenerator { internal int columns; internal int lines; internal RoomSet roomSet = null; internal WallSet wallSet = null; public MazeGenerator(int cols, int lines) { this.columns = cols; this.lines = lines; wallSet = new WallSet(this.columns, this.lines); roomSet = new RoomSet(this.columns, this.lines); } public void MakeMaze() { wallSet.Begin(); do { int room1; int room2; bool lr; int wall = wallSet.CurrentWall; wallSet.GetAttachedRoom(wall, out room1, out room2, out lr); if (roomSet.Join(room1, room2)) { wallSet.RemoveWall(); } } while (roomSet.Count > 1 && wallSet.Advance()); wallSet.End(); } public void PrintMaze(MazePrinter printer) { int[] upWallSet = wallSet.UpWallSet; if (upWallSet != null) { printer.Print(upWallSet, this.columns, this.lines); } } } class WallSet { internal int columns; internal int lines; internal int[] walls = null; internal int[] removedWalls = null; internal int currentWallIndex = 0; internal int endWallIndex = 0; public WallSet(int columns, int lines) { this.columns = columns; this.lines = lines; int wallCount = 2 * columns * lines - columns - lines; this.walls = new int[wallCount]; this.removedWalls = new int[wallCount]; for (int i = 0; i < wallCount; ++i) { this.walls = i; this.removedWalls = -1; } RandomizeWallSet(); } private void RandomizeWallSet() { Random rndGenerator = new Random(); int iterCount = 2 * this.walls.Length; for (int i = 0; i < iterCount; ++i) { // swap two datas at random int index1 = rndGenerator.Next() % this.walls.Length; int index2 = rndGenerator.Next() % this.walls.Length; int temp = this.walls[index1]; this.walls[index1] = this.walls[index2]; this.walls[index2] = temp; } } public int CurrentWall { get { return this.walls[this.currentWallIndex]; } } public bool Advance() { if (this.currentWallIndex < this.walls.Length - 1) { this.currentWallIndex++; return true; } return false; } public void GetAttachedRoom(int wall, out int room1, out int room2, out bool lrDirection) { int lineToConsider = wall / (2 * this.columns - 1); int positionInLine = wall % (2 * this.columns - 1); if (positionInLine >= this.columns - 1) { room1 = lineToConsider * this.columns + positionInLine - (this.columns - 1); room2 = room1 + this.columns; lrDirection = false; } else { room1 = lineToConsider * this.columns + positionInLine; room2 = room1 + 1; lrDirection = true; } } public void Begin() { this.currentWallIndex = 0; this.endWallIndex = 0; } public void End() { } public void RemoveWall() { this.removedWalls[this.endWallIndex] = this.walls[this.currentWallIndex]; this.endWallIndex++; } public int[] UpWallSet { get { if (this.endWallIndex > 0) { int[] upWallSet = new int[this.walls.Length]; for (int i = 0; i < upWallSet.Length; ++i) { upWallSet = i; } for (int i = 0; i < this.endWallIndex; ++i) { upWallSet[this.removedWalls] = -1; } return upWallSet; } else { return null; } } } } class RoomSet { internal int columns; internal int lines; internal int roomSetCount; // this array contains the roomset to which the room belongs internal int[] rooms; public RoomSet(int columns, int lines) { this.columns = columns; this.lines = lines; this.roomSetCount = this.columns * this.lines; this.rooms = new int[this.roomSetCount]; for (int i = 0; i < this.rooms.Length; ++i) { // each room is in its own room set this.rooms = i; } } public bool Join(int room1, int room2) { if (this.rooms[room1] != this.rooms[room2]) { // put everything from this.rooms[room2] into this.rooms[room1] int fromSet = this.rooms[room2]; int toSet = this.rooms[room1]; for (int i = 0; i < this.rooms.Length; ++i) { if (this.rooms == fromSet) { this.rooms = toSet; } } --roomSetCount; return true; } return false; } public int Count { get { return roomSetCount; } } } public class MazePrinter { internal void WriteHeaderMazeLine(int lineSize) { Console.Write(" "); for (int i = 0; i < lineSize - 1; ++i) { Console.Write("__"); } Console.WriteLine("_"); } internal void WriteFooterMazeLine(int[] upWalls, int fromIndex, int lineSize) { Console.Write("|"); int index = fromIndex; int lineSizeVert = lineSize - 1; for (int c = 0; c < lineSize; ++c) { bool endLine = (c == lineSizeVert); string wallRep = "__"; if (endLine || upWalls[index] >= 0) { wallRep = "_|"; } if (endLine) { Console.WriteLine(wallRep); } else { Console.Write(wallRep); } ++index; } } internal void WriteMazeLine(int[] upWalls, int fromIndex, int lineSize) { Console.Write("|"); int index = fromIndex; int lineSizeVert = lineSize - 1; for (int c = 0; c < lineSize; ++c) { string wallRep = ""; bool endLine = (c == lineSizeVert); bool lrWall = upWalls[index] >= 0 || endLine; bool udWall = upWalls[index + lineSizeVert] >= 0; if (udWall) wallRep += "_"; else wallRep += " "; if (lrWall) wallRep += "|"; else wallRep += wallRep; if (endLine) { Console.WriteLine(wallRep); } else { Console.Write(wallRep); } ++index; } } public void Print(int[] upWalls, int columns, int lines) { int index = 0; WriteHeaderMazeLine(columns); for (int l = 0; l < lines; ++l) { if (l == lines - 1) { WriteFooterMazeLine(upWalls, index, columns); } else { WriteMazeLine(upWalls, index, columns); index += 2 * columns - 1; } } } }}I'm afraid I forgot to comment most things. Sorry for the inconvenience... [smile] I'm going to comment everything and put that into a ZIP later.
Here is my implementation. This is my first program written in C#, so I'm not familiar with the standard formatting, apologies if it insults someone's sensibilities.
One thing I'd like to point out is I used a formula to find what two rooms surround a wall. Took me a while to convert that to code from the idea I had in my head, so it is an achievement in my opinion. It only works with the "left-right top-bottom" numbering scheme, the one JWalsh described in the original post.
The MazeMaker class:
The client program:
Please point out anything you see wrong. Hope this helps someone.
One thing I'd like to point out is I used a formula to find what two rooms surround a wall. Took me a while to convert that to code from the idea I had in my head, so it is an achievement in my opinion. It only works with the "left-right top-bottom" numbering scheme, the one JWalsh described in the original post.
The MazeMaker class:
using System;public class MazeMaker{ #region local variables public int height, width, numOfWalls; private int[] walls; private bool[] wallsUp; private int[] roomSets; #endregion #region constructors //default constructor of a 5X5 maze public MazeMaker() { height = 5; width = 5; numOfWalls = (width * (height - 1)) + (height * (width - 1)); initArrays(); } //constuctor which sets a variable height with a constant width of 10 public MazeMaker(int y) { width = 10; height = y; numOfWalls = (width * (height - 1)) + (height * (width - 1)); initArrays(); } //constructor which sets both the width and the height public MazeMaker(int x, int y) { width = x; height = y; numOfWalls = (width * (height - 1)) + (height * (width - 1)); initArrays(); } #endregion #region mazemaking methods //initializes the arrays public void initArrays() { walls = new int[numOfWalls]; for (int i = 0; i < walls.Length; i++) walls = i; //randomization starts here Random randGenerator = new Random(); double rand; for (int i = 0; i < walls.Length; i++) { rand = randGenerator.NextDouble(); swap(i, (int)(rand * walls.Length)); } //initializes all walls as up wallsUp = new bool[numOfWalls]; for (int i = 0; i < wallsUp.Length; i++) wallsUp = true; //initializes all rooms in their own set roomSets = new int[height * width]; for (int i = 0; i < roomSets.Length; i++) roomSets = i; } //helper method for randomizing the walls array public void swap(int index1, int index2) { int tmp = walls[index1]; walls[index1] = walls[index2]; walls[index2] = tmp; } //given the wall number, returns the two rooms touching the wall public void findRoomsFromWall(int wallNum, out int r1, out int r2) { int magicNumber = 2 * width - 1; int reducedWallNum = wallNum % magicNumber; int rowNumber = wallNum / magicNumber; if (reducedWallNum < width - 1) { r1 = reducedWallNum + magicNumber * rowNumber - (width - 1) * rowNumber; r2 = r1 + 1; } else { r1 = wallNum - (width - 1) * (rowNumber + 1); r2 = r1 + width; } } //checks to see if two rooms occupy the same set public bool isRoomInSameSet(int r1, int r2) { return roomSets[r1] == roomSets[r2]; } //checks to see if the wall is up //if it is, checks if the two rooms are in the same set //if they are, removes the wall and combines the two sets public void knockdownWall(int wallNum) { int room1, room2; findRoomsFromWall(wallNum, out room1, out room2); if (!isRoomInSameSet(room1, room2)) { wallsUp[wallNum] = false; int r2OldSet = roomSets[room2]; for (int i = 0; i < roomSets.Length; i++) { if (roomSets == r2OldSet) roomSets = roomSets[room1]; } } } //a check method to see if there are multiple room sets //returns false when all rooms are in the same set public bool isMultipleSets() { bool answer = false; int baseSet = roomSets[0]; for(int i = 1; i<roomSets.Length; i++) { if (roomSets != baseSet) answer = true; } return answer; } //maze generation method public void generateMaze() { int num = 0; while(isMultipleSets()) { knockdownWall(walls[num]); num++; } } #endregion //method to print the maze //its convoluted and confusing, but it works well with the implementation I've used //kudos to you if you can decipher it public void printMaze() { int verticalWallIndex = 0; int horizontalWallIndex = width - 1; int rowIndex = 0; int endPoint; //draws the roof of the maze Console.WriteLine(" " + new string('_', width * 2 - 1) + " "); //this while loop draws each row one at a time by checking to see if the walls are up or not while (rowIndex < (height - 1)) { //draws the left side wall Console.Write("|"); endPoint = horizontalWallIndex + width; //checks if the first horizontal wall is up or not if (wallsUp[horizontalWallIndex] == true) Console.Write("_"); else Console.Write(" "); horizontalWallIndex++; //enters a loop to draw the rest of the row while (horizontalWallIndex < endPoint) { if (wallsUp[verticalWallIndex] == true) Console.Write("|"); else Console.Write("_"); verticalWallIndex++; if (wallsUp[horizontalWallIndex] == true) Console.Write("_"); else Console.Write(" "); horizontalWallIndex++; } //draws the right side wall Console.WriteLine("|"); horizontalWallIndex = horizontalWallIndex + width - 1; verticalWallIndex = verticalWallIndex + width; rowIndex++; } //since the bottom row is different from the others, it is outside the loop //draws the bottom left corner Console.Write("|_"); //draws the bottom row, but instead of checking if the horizontal rows are up, //it just prints an underscore while (verticalWallIndex < walls.Length) { if (wallsUp[verticalWallIndex] == true) Console.Write("|"); else Console.Write("_"); verticalWallIndex++; Console.Write("_"); } Console.Write("|"); }}The client program:
using System;class ClientProgram{ public static void Main() { Console.Title = "Maze Generator"; Console.WriteLine("Welcome to the Maze Generator."); Console.WriteLine(); bool loop = true, loop2; string answer; int width, height; Console.WriteLine(Console.WindowHeight); Console.WriteLine(Console.WindowWidth); //loop for user input while (loop) { do { Console.Write("How many columns? (2-50): "); Int32.TryParse(Console.ReadLine(), out width); if (width < 2 || width > 50) Console.Write("Invalid input, try again:"); } while (width < 2 || width > 50); do { Console.Write("How many rows? (2-50): "); Int32.TryParse(Console.ReadLine(), out height); if (height < 2 || height > 50) Console.Write("Invalid input, try again: "); } while (height < 2 || height > 50); Console.SetWindowSize(width < 40 ? 80 : width * 2 + 2, height > 30 ? height + 5 : 30); Console.WriteLine(); MazeMaker test = new MazeMaker(width, height); test.generateMaze(); test.printMaze(); Console.WriteLine(); loop2 = true; do { Console.Write("Create another? (Y)es or (N)o "); answer = Console.ReadLine(); if (answer[0] == 'n' || answer[0] == 'N') { loop = !loop; loop2 = !loop2; } else if (answer[0] == 'y' || answer[0] == 'Y') loop2 = !loop2; else Console.WriteLine("Invalid answer, try again."); } while (loop2); } }}Please point out anything you see wrong. Hope this helps someone.
First of all, thank you Jeromy for the challenge. It was a good way to get me back in to coding again after such a long long break.
Second thing I would like to mention, is that I did make use of List<> but I treated it like an array at times, so I think I should have either not used it or should have used it properly. (edit:) I read in a post above that we should not use parts of the language that have not been covered, I can go back and fit it to an array if needed.
Last of all I didn't follow the first chapters, I have been coding in C# for a while but I didn't start following this Workshop until I saw the post for the challenge in the 'most recent threads' list. To add to that, I have started to read the chapters from Week-1 just to get a better handle on where everyone else is currently, and to pickup anything that I didn't know from my previous learning of C#.
Here is my code, ~370 lines with 3 classes.
I'm not really happy with the way that everything looks, but I am happy that everything does actually work (as best as I can tell). My approach to the challenge was to first make all the room's (roomSets), then for each room find if there is room to the right or room to the bottom, and making a wall (in wallList) for each that exists. After the rooms and walls are setup it is the usual permutate the wall indices, cycle through them and combine any roomSets that are not already together.
I thought it was going to be difficult to display the maze, but I found it to be rather simple once I implimented the getWallsForRoom() function (which made it easy to find which walls were still up).
One example of something I don't like about my code (but couldnt come up with a different solution) are the few lines like this: 1 + (index / mazeWidth) which I use to find if it is in the last row of the maze. I think it looks cheap, and isn't exactly clear (so I comment what it does), but it works just fine so I am keeping them.
Thanks for reading!
EDIT: Fixed a small display issue in the source. Added comment about making my lists an array if needed. Removed some text from the end of my post.
[Edited by - mozie on August 7, 2007 6:39:49 PM]
Second thing I would like to mention, is that I did make use of List<> but I treated it like an array at times, so I think I should have either not used it or should have used it properly. (edit:) I read in a post above that we should not use parts of the language that have not been covered, I can go back and fit it to an array if needed.
Last of all I didn't follow the first chapters, I have been coding in C# for a while but I didn't start following this Workshop until I saw the post for the challenge in the 'most recent threads' list. To add to that, I have started to read the chapters from Week-1 just to get a better handle on where everyone else is currently, and to pickup anything that I didn't know from my previous learning of C#.
Here is my code, ~370 lines with 3 classes.
using System;using System.Collections.Generic;using System.Text;namespace mazeGame{ class MazeWall { public bool isDown; public int wallNumber; public int roomOne; public int roomTwo; public MazeWall() { isDown = false; wallNumber = 0; roomOne = 0; roomTwo = 0; } }; class Maze { int mazeWidth; int mazeHeight; int totalWalls; List<int> wallOrder; List<MazeWall> wallList; List<List<int>> roomSets; void generateWallOrder() { Random r = new Random(); int tempInt; while (wallOrder.Count != wallList.Count) { tempInt = r.Next(wallList.Count); if (!wallOrder.Contains(tempInt)) { wallOrder.Add(tempInt); } } } void combineRoomSets(int setOne, int setTwo) { if (setTwo < setOne) // move in to lower set numbers set. { int tempI = setOne; setOne = setTwo; setTwo = tempI; } for(int i=0;i<roomSets[setTwo].Count;++i) { roomSets[setOne].Add(roomSets[setTwo]); } roomSets[setTwo].Clear(); roomSets[setOne].Sort(); } void generateMazeFromData() { int i = 0; int x = 0; int r1set, r2set; do { x = wallOrder; r1set = getSetIndexFromRoom(wallList[x].roomOne); r2set = getSetIndexFromRoom(wallList[x].roomTwo); if (r1set != r2set) { wallList[x].isDown = true; combineRoomSets(r1set, r2set); } ++i; } while (i < wallOrder.Count); } int getSetIndexFromRoom(int room) { int retVal = -1; for (int i = 0; i < roomSets.Count; ++i) { if (roomSets.Contains(room)) { retVal = i; break; } } return retVal; } void generateWallList() { int i = 0; MazeWall aWall; // each room for (i = 0; i < (mazeWidth * mazeHeight); ++i) { // room to left? int tempInt = (i + 1); tempInt = tempInt % mazeWidth; if (tempInt > 0) // true if we are not in last column in row { // make wall aWall = new MazeWall(); aWall.wallNumber = totalWalls++; aWall.roomOne = i; aWall.roomTwo = i + 1; wallList.Add(aWall); } // room to bottom? tempInt = 1 + (i / mazeWidth); // in last row? if (tempInt < mazeHeight) { // make wall aWall = new MazeWall(); aWall.wallNumber = totalWalls++; aWall.roomOne = i; aWall.roomTwo = i + mazeWidth; wallList.Add(aWall); } } // loop - next room } void generateRoomSets() { roomSets.Clear(); int rooms = mazeWidth * mazeHeight; for (int i = 0; i < rooms; ++i) { List<int> newInt = new List<int>(); newInt.Add(i); roomSets.Add(newInt); } } void getWallsForRoom(int room, out MazeWall wallOne, out MazeWall wallTwo) { MazeWall firstWall = null; MazeWall secondWall = null; wallOne = firstWall; wallTwo = secondWall; foreach (MazeWall m in wallList) { if (m.roomOne == room) // roomOne is the origin room for walls { if (firstWall == null) firstWall = m; else secondWall = m; } } wallOne = firstWall; wallTwo = secondWall; } string getViewOfRoom(int room) { string retVal = " "; MazeWall wallOne, wallTwo; getWallsForRoom(room, out wallOne, out wallTwo); bool rightWall = false; bool bottomWall = false; int roomDistance; if(wallOne != null) // the last room has no walls if (wallOne.isDown == false) { roomDistance = wallOne.roomTwo - wallOne.roomOne; if (roomDistance == 1) rightWall = true; if (roomDistance == mazeWidth) bottomWall = true; } if(wallTwo != null) // not all rooms have two walls if (wallTwo.isDown == false) { roomDistance = wallTwo.roomTwo - wallTwo.roomOne; if (roomDistance == 1) rightWall = true; if (roomDistance == mazeWidth) bottomWall = true; } if ((room / mazeWidth) + 1 == mazeHeight) // always draw floor in last row bottomWall = true; if ((1 + room) % mazeWidth == 0) // in last column of row, draw right wall rightWall = true; if (rightWall && !bottomWall) retVal = " |"; if (!rightWall && bottomWall) retVal = "__"; if (rightWall && bottomWall) retVal = "_|"; return retVal; } public void outputMazeToConsole() { int consoleWidth = Console.WindowWidth; int consoleHeight = Console.WindowHeight; int targetWidth = (2 * mazeWidth) + 2; int targetHeight = (2 * mazeHeight) + 2; if (consoleWidth < targetWidth || consoleHeight < targetHeight) { Console.SetWindowSize(targetWidth,targetHeight); } int i; Console.Write(" "); for (i = 0; i < ((mazeWidth*2) - 1); ++i) Console.Write("_"); Console.WriteLine(""); i = 0; for(i=0;i<mazeHeight;++i) { Console.Write("|"); for (int j = 0; j < mazeWidth; ++j) { Console.Write(getViewOfRoom((i*mazeWidth) + j)); } Console.WriteLine(""); } Console.WriteLine(""); } public Maze(int width, int height) { mazeWidth = width; mazeHeight = height; roomSets = new List<List<int>>(); wallList = new List<MazeWall>(); wallOrder = new List<int>(); generateRoomSets(); generateWallList(); totalWalls = wallList.Count; generateWallOrder(); generateMazeFromData(); } }; class Program { static void displayIntro() { string buff = "mazeGame C# challenge, gamedev.net"; Console.WriteLine(buff); Console.Title = buff; } static void displayWRequest() { Console.WriteLine("How wide of a maze to make? (Please enter a number from 2-50)"); } static void displayHRequest() { Console.WriteLine("How tall of a maze to make? (Please enter a number from 2-50)"); } static int inputNumber() { int retVal = -1; string buff = Console.ReadLine(); if(int.TryParse(buff, out retVal)) return retVal; return -1; } static int getInputRange(int minRange, int maxRange) { int inputResult; int userInput = -1; do { inputResult = inputNumber(); if (inputResult == -1) { Console.WriteLine("Please try again. Enter a number from {0} through {1}.", minRange, maxRange); userInput = -1; } else if (inputResult >= minRange && inputResult <= maxRange) userInput = inputResult; else { Console.WriteLine("Please try again. Enter a number from {0} through {1}.", minRange, maxRange); inputResult = -1; userInput = -1; } } while (userInput == -1); return userInput; } static int getWidthInput() { // get width imput int userWidth = -1; displayWRequest(); userWidth = getInputRange(2, 50); return userWidth; } static int getHeightInput() { // get width imput int userHeight = -1; displayHRequest(); userHeight = getInputRange(2, 50); return userHeight; } static bool inputContinue() { bool doContinue = false; int userInput = -1; string buff; do { Console.WriteLine("Would you like to do another maze? ('Y'es or 'N'o)"); buff = Console.ReadLine(); buff = buff.ToUpper(); if (buff == "Y" || buff == "YES") { doContinue = true; userInput = 1; } else if (buff == "N" || buff == "NO") { doContinue = false; userInput = 0; } else { userInput = -1; continue; } } while (userInput == -1); return doContinue; } static void Main(string[] args) { displayIntro(); bool playing = true; int userWidth; int userHeight; while (playing) { userWidth = getWidthInput(); userHeight = getHeightInput(); Console.WriteLine("Generating maze, width {0} x height {1}.", userWidth, userHeight); Maze maze = new Maze(userWidth, userHeight); maze.outputMazeToConsole(); playing = inputContinue(); } Console.WriteLine("Thanks for playing!\r\n"); } }}I'm not really happy with the way that everything looks, but I am happy that everything does actually work (as best as I can tell). My approach to the challenge was to first make all the room's (roomSets), then for each room find if there is room to the right or room to the bottom, and making a wall (in wallList) for each that exists. After the rooms and walls are setup it is the usual permutate the wall indices, cycle through them and combine any roomSets that are not already together.
I thought it was going to be difficult to display the maze, but I found it to be rather simple once I implimented the getWallsForRoom() function (which made it easy to find which walls were still up).
One example of something I don't like about my code (but couldnt come up with a different solution) are the few lines like this: 1 + (index / mazeWidth) which I use to find if it is in the last row of the maze. I think it looks cheap, and isn't exactly clear (so I comment what it does), but it works just fine so I am keeping them.
Thanks for reading!
EDIT: Fixed a small display issue in the source. Added comment about making my lists an array if needed. Removed some text from the end of my post.
[Edited by - mozie on August 7, 2007 6:39:49 PM]
Hi all, i have been having problems getting inside this section, i see no direct link to project 1!! :( maybe i am missing other projects too...
i think i am done with my project, but my maze doenst look random at all! its just plane boring, what am i doing wrong? heres the code, there is no need to analize it too much maybe looking at a maze example can help someone tell me whats wrong.
i think i am done with my project, but my maze doenst look random at all! its just plane boring, what am i doing wrong? heres the code, there is no need to analize it too much maybe looking at a maze example can help someone tell me whats wrong.
[source lang=c#]using System;using System.Collections.Generic;using System.Text;namespace maze{ class Program { public class Settings { //gets and sets rows, columns and verifys user input private int Rows; private int Colmns; private int Temp; public void RowSettings() { Console.Write("Please enter the number of rows between 2-50: "); if (int.TryParse(Console.ReadLine(), out Temp) && Temp >= 2 && Temp <= 50) Rows = Temp; else RowSettings(); } public void Columns() { Console.Write("Please enter the number of columns between 2-37: "); if (int.TryParse(Console.ReadLine(), out Temp) && Temp >= 2 && Temp <= 37) Colmns = Temp; else Columns(); } public int GetRows { get { return Rows; } } public int GetColmns { get { return Colmns; } } } public class MazeGenerator { //generates the necesary arrays to work with private int [] RoomArray; private int [,] WallsUpArray; private int [] RoomSetsArray; private bool[] WallsBoolArray; private int Walls; private int Rooms; public void SetArrays(int a, int b) { Rooms = a * b; Walls = ((2 * a * b) - a - b); RoomArray = new int[Rooms]; for (int i = 0; i < Rooms; i++) RoomArray = i; RoomSetsArray = new int [Rooms]; for (int i = 0; i < Rooms; i++) RoomSetsArray = i; WallsBoolArray = new bool[Walls]; for (int i = 0; i < Walls; i++) WallsBoolArray = true; WallsUpArray = new int [Walls, 2]; int x = 0; int t = 0; int c = 0; int n = (a - 1); int y = (2 * b - 1); for (int i = 0; i < y; i++) { for (int e = 0; e < n; e++) { WallsUpArray[x, 0] = (x - (t * n)); WallsUpArray[x, 1] = (x - (c * n) + 1); //Console.Write("{0} ", WallsUpArray[x, 0]); //Console.Write("{0} ", WallsUpArray[x, 1]); x++; } t++; i++; if (i == y) break; for (int g = 0; g < a; g++) { WallsUpArray[x, 0] = (x - (t * n)); WallsUpArray[x, 1] = (x - (c * n) + 1); //Console.Write("{0} ", WallsUpArray[x, 0]); //Console.Write("{0} ", WallsUpArray[x, 1]); x++; } c++; } //Console.WriteLine(""); } //a random number generator for the knockdown algorithm private int RandomNumber(int min, int max) { Random random = new Random(); //Console.WriteLine("{0}", random.Next(min, max)); return random.Next(min, max); } //i am stuck here public void KnockDown() { int StartPoint = RandomNumber(0, (Walls - 1)); Console.Write("{0} out of ", StartPoint); //for (int i = 0; i < Walls; i++) //Console.Write(" {0}", i); //Console.WriteLine(""); for (int g = 0; g < (Walls - 1); g++) { int Room1 = WallsUpArray[StartPoint, 0]; int Room2 = WallsUpArray[StartPoint, 1]; if (!(RoomSetsArray[Room1] == RoomSetsArray[Room2])) { WallsBoolArray[StartPoint] = false; //Console.Write(" {0} ", StartPoint); int check = RoomSetsArray[Room1]; RoomSetsArray[Room1] = RoomSetsArray[Room2]; for (int d = 0; d < Rooms; d++) { if (RoomSetsArray[d] == check) RoomSetsArray[d] = RoomSetsArray[Room2]; } } if (StartPoint == (Walls -1)) StartPoint = 0; else StartPoint++; } } public void DrawMaze(int a, int b) { int n = (a - 1); int y = (2 * b - 1); int x = 0; Console.WriteLine(""); Console.Write(" ");//used for upper boundry for (int i = 0; i < a; i++) //used for upper boundry Console.Write(" _"); //used for upper boundry Console.WriteLine(""); //used for upper boundry for (int i = 0; i < (b - 1); i++) { Console.Write(" |");//border if (WallsBoolArray[(x + ((i + 1) * n))])//first wall Console.Write("_"); else Console.Write(" "); for (int e = 0; e < n; e++)//walls iteration { if (WallsBoolArray[(x + (i * n))]) Console.Write("|"); else Console.Write(" "); x++; if (WallsBoolArray[(x + ((i + 1)* n))]) Console.Write("_"); else Console.Write(" "); } Console.Write("|");//border Console.WriteLine(""); x++; } Console.Write(" |_");//border for (int i = 0; i < n; i++) { if (WallsBoolArray[(x + ((b - 1) * n))])//final floor Console.Write("|_"); else Console.Write(" _"); x++; } Console.Write("|");//border } } public static void Main(string[] args) { Console.WriteLine("Random Maze Generator by Yolda!"); Settings Setts = new Settings(); Setts.RowSettings(); Setts.Columns(); MazeGenerator MazeGen = new MazeGenerator(); MazeGen.SetArrays(Setts.GetColmns, Setts.GetRows); MazeGen.KnockDown(); MazeGen.DrawMaze(Setts.GetColmns, Setts.GetRows); Console.WriteLine(""); } }}
alvarofrank, It looks like you're just picking a single random number as the start point and going through the maze randomly from there, which means that there are only numberofwalls variations. You need to find a way to randomize the entire order in which you access walls.
thnks i did as soon is i found out that was my mistale, now my maze looks likes taking WAY too long to load if its a bog one. i used this code:
public void KnockDown() { bool[] StartpointList = new bool[Walls]; int StartPoint = RandomNumber(0, (Walls - 1)); int StartPoint2; StartpointList[StartPoint] = true; //Console.Write("{0} out of ", StartPoint); //for (int i = 0; i < Walls; i++) //Console.Write(" {0}", i); //Console.WriteLine(""); for (int g = 0; g < (Walls - 1); g++) { int Room1 = WallsUpArray[StartPoint, 0]; int Room2 = WallsUpArray[StartPoint, 1]; if (!(RoomSetsArray[Room1] == RoomSetsArray[Room2])) { WallsBoolArray[StartPoint] = false; //Console.Write(" {0} ", StartPoint); int check = RoomSetsArray[Room1]; RoomSetsArray[Room1] = RoomSetsArray[Room2]; for (int d = 0; d < Rooms; d++) { if (RoomSetsArray[d] == check) RoomSetsArray[d] = RoomSetsArray[Room2]; } } do { StartPoint2 = RandomNumber(0, (Walls - 1)); } while (StartPoint2 == StartPoint || StartpointList[StartPoint2]); StartPoint = StartPoint2; } } public void DrawMaze(int a, int b) { int n = (a - 1); int y = (2 * b - 1); int x = 0; Console.WriteLine(""); Console.Write(" ");//used for upper boundry for (int i = 0; i < a; i++) //used for upper boundry Console.Write(" _"); //used for upper boundry Console.WriteLine(""); //used for upper boundry for (int i = 0; i < (b - 1); i++) { Console.Write(" |");//border if (WallsBoolArray[(x + ((i + 1) * n))])//first wall Console.Write("_"); else Console.Write(" "); for (int e = 0; e < n; e++)//walls iteration { if (WallsBoolArray[(x + (i * n))]) Console.Write("|"); else Console.Write(" "); x++; if (WallsBoolArray[(x + ((i + 1)* n))]) Console.Write("_"); else Console.Write(" "); } Console.Write("|");//border Console.WriteLine(""); x++; } Console.Write(" |_");//border for (int i = 0; i < n; i++) { if (WallsBoolArray[(x + ((b - 1) * n))])//final floor Console.Write("|_"); else Console.Write(" _"); x++; } Console.Write("|");//border }Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement