Help me with code ideas please?

Started by
10 comments, last by mepis 10 years, 3 months ago

Code is below, but first some background:

I'm hoping to make a game via the Construct 2 engine. It intrigues my so I want to play with it. I have an idea for a game that involves dungeon/maze generation. I want it to be random for replayability. It's the first time I've done something like this though (still very much a newb) so I went to prototype the idea of the generation in Java because I can form the logic easier. I figured I could translate it to Construct's events later on. I sat down and thought about doing it and my mind drew a blank. I figured the easiest way would be to use some type of recursive methods, and after looking into the idea of maze generation algorithms, it seems I was right. The problem is though that I can't do anything recursively. It has to be done through loops. I'm also building the maze in a 2D array so I can build the map from that. That means I need empty spaces between paths. The algorithms I was looking at didn't do this. I understand the idea of running through a loop with a structure but structure are a bit non-existent in Construct from what I can tell. I can build a second 2D parallel array to track where the maze has been but I was hoping not to so I could save memory and reduce garbage collection and stuff. I got completely stuck at this point so I just started throwing ideas at the IDE and saw what stuck.

Anyway, by a really happy mistake, I came up with the code below. Now, it's not really good to develop mazes. The output results in exactly what I ultimately aim to achieve though. I wanted random tunnels and random large rooms and caverns. I wanted the path to the exit different every time. I was planning on doing some other silly stuff once a maze was created, but this creates the output right away. I ran through the algorithm a lot of times and adjusted the chokepoint to where it gives me a seemingly good result with a viable path every time (though I still have to write something to double check this each time. The code is very much a prototype but I wanted to get opinions on it, what looks crappy, what I could improve on. I'm still very much a newb so all the criticism I can get is good. Keep in mind I want to translate this to Construct 2 at some point though it doesn't bother me if you give me tips as if I was continuing this project in Java.

I thank anyone for their feedback. I really do appreciate it.



import java.util.Random;

public class Main {

	//These variables define the size of the maze.
	//Change only these to change the size of the maze
	private static final int ROWSIZE = 40;
	private static final int COLUMNSIZE = 40;
	
	public static void main(String[] args) {
		
		//variable declarations
		int[][] maze;
		int randX = 0;
		int randY = 0;
		int choke = 0;
		int checkSurroundings = 0;
		Random rand = new Random();
		maze = new int[ROWSIZE][COLUMNSIZE];
		
		//Initialize maze array to all zeros (make every space a non-moveable area)
		for (int x = 0;x < ROWSIZE; x++){
			for (int y = 0; y < COLUMNSIZE; y++){
				maze[x][y] = 0;
			}
		}
		
		//Define outside walls
		//Construct 2 specific:
		//We will use the number two to define all outside walls of maze so the game engine 
		//knows where to put these special tiles
		for (int x = 0; x < ROWSIZE; x++){
			maze[x][0] = 2;
			maze[x][COLUMNSIZE - 1] = 2;
		}
		for (int x = 0; x < COLUMNSIZE; x++){
			maze[0][x] = 2;
			maze[ROWSIZE - 1][x] = 2;
		}
		
		//Maze Generation
		randX = rand.nextInt(ROWSIZE - 10) + 5;
		maze[randX][0] = 1; //Mark beginning
		maze[randX][1] = 1;
		randX = rand.nextInt(ROWSIZE - 10) + 5;
		maze[randX][COLUMNSIZE - 1] = 1; //Mark ending
		maze[randX][COLUMNSIZE - 2] = 1;
		do {
			randX = rand.nextInt(ROWSIZE - 2) + 1;
			randY = rand.nextInt(COLUMNSIZE - 2) + 1;
			checkSurroundings = maze[randX - 1][randY] + maze[randX + 1][randY] + maze[randX][randY - 1] + maze[randX][randY + 1];		
			if (checkSurroundings < 4){
				maze[randX][randY] = 1;
				choke = 0;
			} else {
				choke ++;
			}
			
			
		} while (choke < 4); 
		
		//Print results of the maze array
		System.out.printf("\n");
		for (int x = 0; x < ROWSIZE; x++){
			for (int y = 0; y < COLUMNSIZE; y++){
				if (maze[x][y] == 0 || maze[x][y] == 2)
					System.out.printf("%d ", maze[x][y]);
				if (maze[x][y] == 1)
					System.out.printf("# ");
			}
			System.out.print("\n");
		}
	}
}

Advertisement

You do not need to initialize everything with 0.

Java does that for you.

You only need to do that if each dimenson has a different size, but this is not the case.

int[][] maze = new int[ROW_SIZE][COLUMN_SIZE]; should initialize everything with 0.

The same for everything else.

I do not like ROWSIZE, I would prefer ROW_SIZE instead (the same for COLUMN_SIZE).

You could also use enum instead of 1,2,3 to indicate the "what" in your maze.

enum Type {

Wall, Water, Tunnel, Whatever...;

}

Type[][] maze = new Type[ROW_SIZE][COLUMN_SIZE];

I have no idea how to generate dungeons, but you could try defining several "tunnels" or "dungeon parts" and simply concatenate them randomly.

Thanks for the tips. Initializing everything to 0 was beaten into me in a high school programming class years ago. I've never shaken that habit. I like the criticism for everything else. That helps a lot.

I'm not sure if I can use enum. I'll have to go look at Construct again and see if there is a way to do something similar. Ultimately I plan to translate this to Construct for a game idea later on. That idea is being put on hold for the moment but I'll be getting back to it in the next few weeks.

First, you don't have anything wrong with your code. You are in great shape. Even the minor nitpicks I'll mention are just personal preference, and if you prefer otherwise than ignore the advice.

If you can't use enum, define constants and use them instead of literal numbers (like your ROW_SIZE and COLUMN_SIZE consts). It makes your code self-describing, which is a very good thing for readability and future debugging. I'd probably use constants for the 5 and 10 when getting a random X value too. I suggest that more for the self-documenting aspect than code clarity (it's already perfectly clear what you're doing). Named constants would just ensure ROW_SIZE has a reminder of constraints/assumptions on the value (can't be smaller than 10 in your code) in case you ever make it user-defined or whatever.

There's nothing wrong with initializing to zero. You'll want to initialize each maze element once you're using an enum or constant anyways since you wouldn't want your code to break just by changing the enum or constant from zero to one or something.

Most coding tips you'll figure out on your own. I could tell you to make a function for printing the maze since you'll want that functionality elsewhere, but you'd figure that out on your own pretty darn quick anyways. Your checkSurroundings calculation may break as your code evolves, but it'll be pretty obvious and you'll figure it out quickly. So I suppose my biggest advice is not to worry so much about what other coders think is "right" or "wrong" since you're doing great.

Thanks guys! The kind words are nice to hear.

I think I might make a quick mine sweeper type protoype game in java then for the experience. I can understand why you'd say I would benefit from it. I don't think I would need to add a lot of polish to the game. It sounds like the learning experience would be in the algorithms to make the game work. It sounds like it would be a good experience.

Thank you d4n1!!!

So (and I apologize for the details now) I have been thinking about making a mindsweeper clone since I wrote the last post. I was taking a poop today and was thinking about the algorithms involved. I quickly stepped through it and thought of how I would roughly program it. Than it hit me that I could make random patterns of rooms and implement a corridor system with a very simple separate algorithm. It would create a much better effect than the code I have listed above and would ensure a random, but exact path through every single time!

You're suggestion was awesome!

Too much information, we don't need to know about your push/plop algorithm.

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

Code is below, but first some background:

I'm hoping to make a game via the Construct 2 engine. It intrigues my so I want to play with it. I have an idea for a game that involves dungeon/maze generation. I want it to be random for replayability. It's the first time I've done something like this though (still very much a newb) so I went to prototype the idea of the generation in Java because I can form the logic easier. I figured I could translate it to Construct's events later on. I sat down and thought about doing it and my mind drew a blank. I figured the easiest way would be to use some type of recursive methods, and after looking into the idea of maze generation algorithms, it seems I was right. The problem is though that I can't do anything recursively. It has to be done through loops. I'm also building the maze in a 2D array so I can build the map from that. That means I need empty spaces between paths. The algorithms I was looking at didn't do this. I understand the idea of running through a loop with a structure but structure are a bit non-existent in Construct from what I can tell. I can build a second 2D parallel array to track where the maze has been but I was hoping not to so I could save memory and reduce garbage collection and stuff. I got completely stuck at this point so I just started throwing ideas at the IDE and saw what stuck.

Anyway, by a really happy mistake, I came up with the code below. Now, it's not really good to develop mazes. The output results in exactly what I ultimately aim to achieve though. I wanted random tunnels and random large rooms and caverns. I wanted the path to the exit different every time. I was planning on doing some other silly stuff once a maze was created, but this creates the output right away. I ran through the algorithm a lot of times and adjusted the chokepoint to where it gives me a seemingly good result with a viable path every time (though I still have to write something to double check this each time. The code is very much a prototype but I wanted to get opinions on it, what looks crappy, what I could improve on. I'm still very much a newb so all the criticism I can get is good. Keep in mind I want to translate this to Construct 2 at some point though it doesn't bother me if you give me tips as if I was continuing this project in Java.

I thank anyone for their feedback. I really do appreciate it.


import java.util.Random;

public class Main {

	//These variables define the size of the maze.
	//Change only these to change the size of the maze
	private static final int ROWSIZE = 40;
	private static final int COLUMNSIZE = 40;
	
	public static void main(String[] args) {
		
		//variable declarations
		int[][] maze;
		int randX = 0;
		int randY = 0;
		int choke = 0;
		int checkSurroundings = 0;
		Random rand = new Random();
		maze = new int[ROWSIZE][COLUMNSIZE];
		
		//Initialize maze array to all zeros (make every space a non-moveable area)
		for (int x = 0;x < ROWSIZE; x++){
			for (int y = 0; y < COLUMNSIZE; y++){
				maze[x][y] = 0;
			}
		}
		
		//Define outside walls
		//Construct 2 specific:
		//We will use the number two to define all outside walls of maze so the game engine 
		//knows where to put these special tiles
		for (int x = 0; x < ROWSIZE; x++){
			maze[x][0] = 2;
			maze[x][COLUMNSIZE - 1] = 2;
		}
		for (int x = 0; x < COLUMNSIZE; x++){
			maze[0][x] = 2;
			maze[ROWSIZE - 1][x] = 2;
		}
		
		//Maze Generation
		randX = rand.nextInt(ROWSIZE - 10) + 5;
		maze[randX][0] = 1; //Mark beginning
		maze[randX][1] = 1;
		randX = rand.nextInt(ROWSIZE - 10) + 5;
		maze[randX][COLUMNSIZE - 1] = 1; //Mark ending
		maze[randX][COLUMNSIZE - 2] = 1;
		do {
			randX = rand.nextInt(ROWSIZE - 2) + 1;
			randY = rand.nextInt(COLUMNSIZE - 2) + 1;
			checkSurroundings = maze[randX - 1][randY] + maze[randX + 1][randY] + maze[randX][randY - 1] + maze[randX][randY + 1];		
			if (checkSurroundings < 4){
				maze[randX][randY] = 1;
				choke = 0;
			} else {
				choke ++;
			}
			
			
		} while (choke < 4); 
		
		//Print results of the maze array
		System.out.printf("\n");
		for (int x = 0; x < ROWSIZE; x++){
			for (int y = 0; y < COLUMNSIZE; y++){
				if (maze[x][y] == 0 || maze[x][y] == 2)
					System.out.printf("%d ", maze[x][y]);
				if (maze[x][y] == 1)
					System.out.printf("# ");
			}
			System.out.print("\n");
		}
	}
}


Nice code Mepis. The first thing I see where you could improve on is putting this code in a class, not in a main method.

You do not need to initialize everything with 0.
Java does that for you.


the mole is correct. But this is also more the reason why it should not be in main though. Their is something called the default constructor, which the JVM (Java Virtual Machine) creates for you when instantiating classes. It is used to assign all primitives with a 0, 0.0 or null value. However, if you needed to override or overload the default constructor, you can. An example would be like this:


File 1.
public class Main{

    public static void main(String[] args){
        ClassExample ce1; = new ClassExample(); // Uses default constructor.
        int[][] maze = new int[40][40];
        ClassExample ce2; = new ClassExample(0, 0, 0, 0, maze); // Overloads default constructor.
    }
}

File 2
import java.util.Random;

public class ClassExample {
	
    // Your variables should look like this.
    private final int ROWSIZE = 40;
    private final int COLUMNSIZE = 40;
    private int[][] maze;
    private int randX;
    private int randY;
    private int choke;
    private int checkSurroundings;
    
    /*If you want to define a default value on creation of the class,
      you then want to override the default constructor like this.
    */
	public ClassExample() {

		randX = 0;
		randY = 0;
		choke = 0;
		checkSurroundings = 0;
		maze = new int[ROWSIZE][COLUMNSIZE]; // Will create instantiate on instantiate.
	}
	
	/*This will allow you to do the same thing as above but also allow
	  you to override the default values assigned in the default constructor
	  for reasons relating to making the code scalable. Remember, if you want 
	  to overload the default constructor, you need to override the default 
	  constructor first!
	*/
	public ClassExample(int x, int y, int choke, int cS, int[][] maze){
		this.randX = x;
		this.randY = y;
		this.choke = choke;
		this.checkSurroundings = cS;
		this.maze = maze;
	}

	// <-- Put your logic methods here.
	
	// Setters/Getters for all private class primatives.
	public int[][] getMaze() {
		return maze;
	}

	public void setMaze(int[][] maze) {
		this.maze = maze;
	}

	public int getRandX() {
		return randX;
	}

	public void setRandX(int randX) {
		this.randX = randX;
	}

	public int getRandY() {
		return randY;
	}

	public void setRandY(int randY) {
		this.randY = randY;
	}

	public int getChoke() {
		return choke;
	}

	public void setChoke(int choke) {
		this.choke = choke;
	}

	public int getCheckSurroundings() {
		return checkSurroundings;
	}

	public void setCheckSurroundings(int checkSurroundings) {
		this.checkSurroundings = checkSurroundings;
	}

	// Getters only for final primatives
	public int getROWSIZE() {
		return ROWSIZE;
	}

	public int getCOLUMNSIZE() {
		return COLUMNSIZE;
	}
}

Also, remember that static means global. You must assign a static value to any variable called outside of a method in the main class because of runtime limitations. This is useful for some reasons, but not in the way you are using it. If you create those variables in a sub class to main, they do not need to be static as seen in the example above.

Additionally, remember that you can use getters for final (constant) variables too, you just can't use setters because they are final.

Another good reason for using a subclass like this for the sake of being able to instantiate it multiple times. For example, if you had a game with two players, and it was a race to see who could get through the maze faster, but one of the players was very experienced with your game, you may need to create a handicap for the more experienced. In this instance, you may want to overload the default constructor to make the maze size larger, thus creating two maze objects; one for each player with the larger maze going to the experienced player. Additionally, if you have to reuse the values stored in the instantiated objects, the values could remain the same since you could overload the default constructor. Or, if you needed to reset their values, you could also do this by instantiating with the default constructor. And finally, because the primitives are not static, their's less chance of exploitation. The above example are key points of overloading, overriding, encapsulation and polymorphism.

One more note: Try to get in the habit of using try/catch/finally. Here's an example:


	public boolean Game(){
		try {
			// <-- Put your logic here.
                        
                        // Example if only
			if(true){
				System.out.println(maze[500][500]); // Will create a null exception!
			}
			return true;
		} catch (Exception e) { // Will attempt to catch exception and keep application running.
			e.printStackTrace(); // Will tell you what happened.
			return false;
		}
	}

The key reasons for using try/catch/finally is it keeps the application running even if their is an exception, and it also makes your life easier to track down the bug. This greatly improves on quality assurance. Hope this helps. Please feel free to add on to this in any way needed, or correct anywhere I am wrong. Thanks!

Thank you for the feedback subtle_wonders. I didn't go to far into constructing the code with best practices for Java as I know them because I was only testing an idea. The algorithm may eventually be ported to Construct 2 for a game.

With that said though, I'm still very much a novice. I have a question. With Java best practices, is it always better to shove things into a separate class or is it acceptable, if the program is small, to keep it all in a single class? What's the pros and cons?

I actually deviated at the moment as suggested to make a minesweeper game. The logic was a breeze to zip through and gave me some better ideas to implement somethings for a planned game down the road. It actually became a better exercise in the Java GUI classes. They still confuse me just because of how many there are. I got a really good intro to the grid bag layout.

I digress though... While making the new minesweeper program I kept everything in a single class. I figured the program is small so it would create more work than needed for a program that doesn't really require any scaling later on. I separated everything into functions in the main class instead. I then made a lot of variables and objects as whole class variables and objects and labeled them as static. I did this because there would only ever be one game running at once and I would be destroying and remaking everything on resetting the game. My presumptions also guessed that doing this would make the program leaner on memory use and garbage collection. I was reading a bit on how Java and Javascript do this (two different languages, I know) and it was a practice on controlling that.

What are the thoughts on those ideas?

This topic is closed to new replies.

Advertisement