Sprite will not move within my .txt based map - java

I am trying to get my Zombie to move around (using keypressed) in the map I made (using scanner and file reader), however it just procreates and then sits there. I still have more code to do as the program isnt finished but I cant do anything else until the Zombie moves. Thanks in advance for any help!
PS. EZ is a multimedia library designed to make it easier for novice programmers to quickly build Java applications that incorporate graphics and sound. It is utilized at UH Hawaii at Manoa.
zombie sprite sheet
barbwire
brains
40 26
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
M M
M B M
M B B M
M M
M M M
M M M M
M M WWW M
M WWWW B M
M M M
M M M
M B M
M B M
M WWWWWW M
M M M
M B M M
M M M
M M B M
M M M
M WWWWWWW M
M M M
M M B M
M B B M
M M
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
Zombie
public class Zombie {
EZImage zombieSheet;
int x = 0; // Position of Sprite
int y = 0;
int zombieWidth; // Width of each sprite
int zombieHeight; // Height of each sprite
int direction = 0; // Direction character is walking in
int walkSequence = 0; // Walk sequence counter
int cycleSteps; // Number of steps before cycling to next animation step
int counter = 0; // Cycle counter
Zombie(String imgFile, int startX, int startY, int width, int height, int steps) {
x = startX; // position of the sprite character on the screen
y = startY;
zombieWidth = width; // Width of the sprite character
zombieHeight = height; // Height of the sprite character
cycleSteps = steps; // How many pixel movement steps to move before changing the sprite graphic
zombieSheet = EZ.addImage(imgFile, x, y);
setImagePosition();
}
private void setImagePosition() {
// Move the entire sprite sheet
zombieSheet.translateTo(x, y);
// Show only a portion of the sprite sheet.
// Portion is determined by setFocus which takes 4 parameters:
// The 1st two numbers is the top left hand corner of the focus region.
// The 2nd two numbers is the bottom right hand corner of the focus region.
zombieSheet.setFocus(walkSequence * zombieWidth, direction, walkSequence * zombieWidth + zombieWidth, direction + zombieHeight);
}
public void moveDown(int stepSize) {
y = y + stepSize;
direction = 0;
if ((counter % cycleSteps) == 0) {
walkSequence++;
if (walkSequence > 3)
walkSequence = 0;
}
counter++;
setImagePosition();
}
public void moveLeft(int stepSize) {
x = x - stepSize;
direction = zombieHeight;
if ((counter % cycleSteps) == 0) {
walkSequence--;
if (walkSequence < 0)
walkSequence = 3;
}
counter++;
setImagePosition();
}
public void moveRight(int stepSize) {
x = x + stepSize;
direction = zombieHeight * 2;
if ((counter % cycleSteps) == 0) {
walkSequence++;
if (walkSequence > 3)
walkSequence = 0;
}
counter++;
setImagePosition();
}
public void moveUp(int stepSize) {
y = y - stepSize;
direction = zombieHeight * 3;
if ((counter % cycleSteps) == 0) {
walkSequence--;
if (walkSequence < 0)
walkSequence = 3;
}
setImagePosition();
counter++;
}
// Keyboard controls for moving the character.
public void go() {
if (EZInteraction.isKeyDown('w')) {
moveUp(2);
} else if (EZInteraction.isKeyDown('a')) {
moveLeft(2);
} else if (EZInteraction.isKeyDown('s')) {
moveDown(2);
} else if (EZInteraction.isKeyDown('d')) {
moveRight(2);
}
}
}
ZombieMain
import java.awt.Color;
import java.io.FileReader;
import java.util.Scanner;
public class ZombieMain {
static EZImage[] walls = new EZImage[500];
static EZImage[] sideWalls = new EZImage[500];
static EZImage[] brains = new EZImage[50];
static int wallsCount = 0;
static int sideWallsCount = 0;
static int brainsCount = 0;
public static void main(String[] args) throws java.io.IOException {
//initialize scanner
Scanner fScanner = new Scanner(new FileReader("boundaries.txt"));
int w = fScanner.nextInt();
int h = fScanner.nextInt();
String inputText = fScanner.nextLine();
//create backdrop
EZ.initialize(w * 33, h * 32);
EZ.setBackgroundColor(new Color(0, 0, 0));
Zombie me = new Zombie("zombieSheet.png", 650, 450, 48, 58, 10);
//set reading parameters and establish results of case readings
for (int row = 0; row < 41; row++) {
inputText = fScanner.nextLine();
for (int column = 0; column < inputText.length(); column++) {
char ch = inputText.charAt(column);
switch (ch) {
case 'W':
walls[wallsCount] = EZ.addImage("barbwire.jpg", column * 32, row * 32);
wallsCount++;
break;
case 'M':
sideWalls[wallsCount] = EZ.addImage("barb.jpg", column * 32, row * 32);
wallsCount++;
break;
case 'B':
brains[brainsCount] = EZ.addImage("brains.png", column * 32, row * 32);
brainsCount++;
break;
default:
// Do nothing
break;
}
//printed count of walls, side walls, and brains
System.out.println("W = " + wallsCount);
System.out.println("M = " + sideWallsCount);
System.out.println("B = " + brainsCount);
}
}
fScanner.close();
while (true) {
me.go();
EZ.refreshScreen();
}
}
}

Okay, got it.
You use a for loop for iterating the text file with the map. Your for loop assumes that your map file has exactly 41 lines. It doesn't, therefore the scanner throws an exception when it reaches outside the loop.
Generally, assuming a fixed size is a bad solution when reading files. What you should do is use some sort of method (that is usually provided with such readers) that checks whether you have not reached the end, and use a while loop. Since you need a row number anyways, you need to keep a separate counter for it and increment it in every while loop pass.
In the case of scanner, the method you're looking for is fScanner.hasNext(), so your code would look like this:
int row = 0;
//set reading parameters and establish results of case readings
while (fScanner.hasNext()) {
...
row++;
}

Related

Want to generate random set of coordinates (Java/Swing)

I have a program in Java like this one (https://www3.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html). It is the object oriented one after Example 2 but with some slightly changes.
I want to generate random coordinates for the balls to spawn. But they are not allowed to intersect each other at spawning moment. The generated coordinates are the top left corner of an rectangle around the circle.
So the coordinates need a minimum distance of 2 * ballRadius.
I only got ether coordinates that have the distance of 2 * ballRadius but then there are only unique coordinates for x and y. So i only got one ball per available y coordinate.
Example There could be a Ball at the red circle but the one left to it "blocks" the y coordinate.
Every other coordinates i get are intersecting each other.
Thats my code so far.
int uniqueXY[][] = new int[ballCount][2];
for (int i = 0; i < ballCount; i++) {
int tempx = 0;
int tempy = 0;
Boolean foundX = true;
Boolean foundY = true;
while(foundX && foundY) {
tempx = (int) (Math.random() * field.maxX); // generate random number in range of filed
tempy = (int) (Math.random() * field.maxY);
for (int j = 0; j < ballCount; j++) { // Here it should check if the number is within the given rules
if ((uniqueXY[j][0] - (2 * ballRadius) > tempx) || (uniqueXY[j][0] + (2 * ballRadius) < tempx)) {
foundX = false;
} else {
foundX = true;
if ((uniqueXY[j][1] - (2 * ballRadius) > tempy) || (uniqueXY[j][1] + (2 * ballRadius) < tempy)) {
foundY = false;
foundX = false;
break;
} else {
foundY = true;
break;
}
}
}
}
uniqueXY[i][0] = tempx;
uniqueXY[i][1] = tempy;
So I have come up with some new Code with the similar problem.
It calculates the distance between every set coordinate and the temporay ones. For the most part it works fine, it just behaves akward if I strees the code and force large numbers of balls.Example with 400 balls Only the balls at the border get forced together.
int uniqueXY[][] = new int[ballCount][2];
for (int k = 0; k < ballCount; k++) {
Boolean found = true;
while(found) {
int tempX = (int) (Math.random() * field.maxX); //create rnd x/y in range of field
int tempY = (int) (Math.random() * field.maxY);
if (k == 0) { // first case gets set, because nothing to compare
uniqueXY[k][0] = tempX;
uniqueXY[k][1] = tempY;
found = false;
break;
}
for (int j = 0; j < k; j++) { //calculates distance between every set coordinate and the temp
int erg1 = (int) (Math.pow(uniqueXY[j][0] - tempX, 2));
int erg2 = (int) (Math.pow(uniqueXY[j][1] - tempY, 2));
int distance = (int) Math.sqrt(erg1 + erg2);
if (distance < 60) { // if distance between coordinates < 60 temp gets discarded
found = true;
break;
}
if (j == k - 1) { // if every set case is checked and distance was always fine, temp gets set
uniqueXY[k][0] = tempX;
uniqueXY[k][1] = tempY;
found = false;
}
}
}
}

Calculating 'color distance' between 2 points in a 3-dimensional space

I have a homework task where I have to write a class responsible for contour detection. It is essentially an image processing operation, using the definition of euclidean distance between 2 points in the 3-dimensional space. Formula given to us to use is:
Math.sqrt(Math.pow(pix1.red - pix2.red,2) + Math.pow(pix1.green- pix2.green,2) + Math.pow(pix1.blue- pix2.blue,2));
We need to consider each entry of the two dimensional array storing the colors of the pixels of an image, and if some pixel, pix, the color distance between p and any of its neighbors is more than 70, change the color of the pixel to black, else change it to white.
We are given a seperate class as well responsible for choosing an image, and selecting an output, for which method operationContouring is applied to. Java syntax and convention is very new to me having started with python. Conceptually, I'm struggling to understand what the difference between pix1 and pix2 is, and how to define them. This is my code so far.
Given:
import java.awt.Color;
/* Interface for ensuring all image operations invoked in same manner */
public interface operationImage {
public Color[][] operationDo(Color[][] imageArray);
}
My code:
import java.awt.Color;
public class operationContouring implements operationImage {
public Color[][] operationDo(Color[][] imageArray) {
int numberOfRows = imageArray.length;
int numberOfColumns = imageArray[0].length;
Color[][] results = new Color[numberOfRows][numberOfColumns];
for (int i = 0; i < numberOfRows; i++)
for (int j = 0; j < numberOfColumns; j++) {
int red = imageArray[i][j].getRed();
int green = imageArray[i][j].getGreen();
int blue = imageArray[i][j].getBlue();
double DistanceColor = Math.sqrt(Math.pow(pix1.red - pix2.red,2) + Math.pow(pix1.green- pix2.green,2) + Math.pow(pix1.blue- pix2.blue,2));
int LIMIT = 70;
if (DistanceColor> LIMIT ) {
results[i][j] = new Color((red=0), (green=0), (blue=0));
}
else {
results[i][j] = new Color((red=255), (green=255), (blue=255));
}
}
return results;
}
}
This is a solution I wrote that uses BufferedImages. I tested it and it should work. Try changing it such that it uses your data format (Color[][]) and it should work for you too. Note that "pix1" is nothing more than a description of the color of some pixel, and "pix2" is the description of the color of the pixel you are comparing it to (determining whether the color distance > 70).
public static boolean tooDifferent(Color c1, Color c2) {
return Math.sqrt(Math.pow(c1.getRed() - c2.getRed(),2) + Math.pow(c1.getGreen()- c2.getGreen(),2) + Math.pow(c1.getBlue()- c2.getBlue(),2)) > 70;
}
public static Color getColor(int x, int y, BufferedImage img) {
return new Color(img.getRGB(x, y));
}
public static BufferedImage operationDo(BufferedImage img) {
int numberOfRows = img.getHeight();
int numberOfColumns = img.getWidth();
BufferedImage results = new BufferedImage(numberOfColumns, numberOfRows, BufferedImage.TYPE_INT_ARGB);
for (int y = 0; y < numberOfRows; y++) {
for (int x = 0; x < numberOfColumns; x++) {
Color color = new Color(img.getRGB(x, y));
boolean aboveExists = y > 0;
boolean belowExists = y < numberOfRows - 1;
boolean leftExists = x > 0;
boolean rightExists = x < numberOfColumns - 1;
if ((aboveExists && tooDifferent(color, getColor(x, y - 1, img))) ||
(belowExists && tooDifferent(color, getColor(x, y + 1, img))) ||
(leftExists && tooDifferent(color, getColor(x - 1, y, img))) ||
(rightExists && tooDifferent(color, getColor(x + 1, y, img)))) {
results.setRGB(x, y, Color.black.getRGB());
} else {
results.setRGB(x, y, Color.white.getRGB());
}
}
}
return results;
}

Java - Detecting collisions via Array-stored collision

I am currently working on collision for my 2D game. I did some research and deducted that I should use the method of storing alpha value pixels into a "mask" of the image of the entity, and the same for the other. Then, I take both entitys' x & y co-ords, as well as height and width, and make a Rectangle object and use the method Rectangle.intersects(Rectangle r) to check if they do infact collide inorder to make it more efficent instead of going through 2 for loops.
IF they intersect, I then make a new Array with the dimensions :
int maxLengthY = Math.max(thisEntity.getMask().length, e.getMask().length);
int maxLengthX = Math.max(thisEntity.getMask()[0].length, thisEntity.getMask()[0].length);
int minX = Math.min(thisEntity.getX(), e.getX());
int minY = Math.min(thisEntity.getY(), e.getY());
int[][] map = new int[maxLengthX + minX][maxLengthY + minY];
and then add the other two masks onto this one with their corresponding y & x "boundaries", like so:
for(int curX = 0; curX < maxLengthX + minX; curX++) { //only loop through the co-ords that area affected
for(int curY = 0; curY < maxLengthY + minY; curY++) {
int this_x = thisEntity.getX();
int this_width = thisEntity.getImage().getWidth();
int this_y = thisEntity.getY();
int this_height = thisEntity.getImage().getHeight();
int[][] this_mask = thisEntity.getMask();
if(curX < (this_x + this_width) && curX < this_x) {//check that the co-ords used are relevant for thisEntity's mask
if(curY < (this_y + this_height) && curY < this_y) {
map[curX][curY] = this_mask[Math.abs(curX - this_x)][Math.abs(curY - this_y)]; // store data from mask to map
}
}
int other_x = e.getX();
int other_width = e.getImage().getWidth();
int other_y = e.getY();
int other_height = e.getImage().getHeight();
int[][] other_mask = e.getMask();
if(curX < (other_x + other_width) && curX > other_x) { //check that the co-ords used are relevant for e's mask
if(curY < (other_y + other_height) && curY > other_y) {
if(map[curX][curY] == 1) { //check if this segment is already written by thisEntity
map[curX][curY] = 2; //if yes, set to 2 instead of e's value to show collision
} else {
map[curX][curY] = other_mask[curX][curY]; // the minus to nullify minX and minY "requirements"
}
}
}
}
}
resulting in the Array "map" looking like SO:
(excuse my 1337 paint skills)
This is the code in all it's beauty:
public Entity[] collisions(Entity thisEntity) {
ArrayList<Entity> list = new ArrayList<Entity>();
try {
for (Entity e : getLevel().getEntities()) {
System.out.println("rect contains = "+thisEntity.getRect().contains(e.getRect()));
if (!thisEntity.equals(e)) {
Rectangle r = e.getRect();
r = thisEntity.getRect();
if (thisEntity.getRect().intersects(e.getRect())) {
//get variables to create a space designated for the intersection areas involved
int maxLengthY = Math.max(thisEntity.getMask().length, e.getMask().length);
int maxLengthX = Math.max(thisEntity.getMask()[0].length, thisEntity.getMask()[0].length);
int minX = Math.min(thisEntity.getX(), e.getX());
int minY = Math.min(thisEntity.getY(), e.getY());
int[][] map = new int[maxLengthX + minX][maxLengthY + minY]; //create a matrix which merges both Entity's mask's to compare
for(int curX = 0; curX < maxLengthX + minX; curX++) { //only loop through the co-ords that area affected
for(int curY = 0; curY < maxLengthY + minY; curY++) {
int this_x = thisEntity.getX();
int this_width = thisEntity.getImage().getWidth();
int this_y = thisEntity.getY();
int this_height = thisEntity.getImage().getHeight();
int[][] this_mask = thisEntity.getMask();
if(curX < (this_x + this_width) && curX > this_x) {//check that the co-ords used are relevant for thisEntity's mask
if(curY < (this_y + this_height) && curY > this_y) {
map[curX][curY] = this_mask[Math.abs(curX - this_x)][Math.abs(curY - this_y)]; // store data from mask to map
}
}
int other_x = e.getX();
int other_width = e.getImage().getWidth();
int other_y = e.getY();
int other_height = e.getImage().getHeight();
int[][] other_mask = e.getMask();
if(curX < (other_x + other_width) && curX > other_x) { //check that the co-ords used are relevant for e's mask
if(curY < (other_y + other_height) && curY > other_y) {
if(map[curX][curY] == 1) { //check if this segment is already written by thisEntity
map[curX][curY] = 2; //if yes, set to 2 instead of e's value to show collision
} else {
map[curX][curY] = other_mask[curX][curY]; // the minus to nullify minX and minY "requirements"
}
}
}
}
}
}
}
}
} catch (Exception excp) {
excp.printStackTrace();
}
return list.toArray(new Entity[1]);
}
Also, here is the method getMask() :
public int[][] getMask() {
return mask;
}
...
private void createMask(BufferedImage image) {
final int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
final int width = image.getWidth();
final int height = image.getHeight();
final boolean hasAlphaChannel = image.getAlphaRaster() != null;
int[][] result = new int[height][width];
if (hasAlphaChannel) {
for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += 4) {
int alpha = pixels[pixel];
if(alpha != 0) {
result[row][col] = 1;
} else {
result[row][col] = 0;
}
if (col == width) {
col = 0;
row++;
}
}
}
mask = result;
}
However... this code does not work as intended, and in some cases at all as when adding the individual masks to the map I get IndexOutOfBounds even though it should work so it's probably just me overlooking something...
So, to conclude, I need help with my code:
What is wrong with it?
How can I fix it?
Is there a more efficent way of doing this type of collision?
Do you recommend other types of collision? If so, what are they?
When you create Entities, do you create their masks from images of the exact same size? Because otherwise entity's mask and image map would be using different coordinate systems (entity.mask[0][0] might be is at its corner, when map[0][0] is at the corner of the "world"), and you're comparing same indices at the line:
map[curX][curY] = other_mask[curX][curY];
(above in the code you're actually getting those to the same coordinate system with Math.abs(a-b))
As for more efficient ways to detect collisions, you can look into binary space partitioning and more on collision detection in general on Wikipedia.

How to measure time for maze generation?

How can I insert a timer to my code? My goal is to know how long it would take for my maze to generate because I am comparing it to the previous program for my thesis.
Thanks for the big help. :)
Here is the code that I used:
public class Maze extends JPanel {
private Room[][] rooms;// m x n matrix of rooms
private ArrayList<Wall> walls; // List of walls
private Random rand;// for random wall
private int height;// height of matrix
private int width;// width of matrix
private int num;// incrementor
private JoinRoom ds;// union paths
// paint methods //
private int x_cord; // x-axis rep
private int y_cord;// y-axis rep
private int roomSize;
private int randomWall;
public Maze(int height, int width) {
this.height = height;
this.width = width;
rooms = new Room[height][width];
walls = new ArrayList<Wall>((height - 1) * (width - 1));
generateRandomMaze();
setPreferredSize(new Dimension(800, 700));
}
private void generateRandomMaze() {
generateInitialRooms();// see next method
ds = new JoinRoom(width * height);
rand = new Random(); // here is the random room generator
num = width * height;
while (num > 1) {
// when we pick a random wall we want to avoid the borders getting eliminated
randomWall = rand.nextInt(walls.size());
Wall temp = walls.get(randomWall);
// we will pick two rooms randomly
int roomA = temp.currentRoom.y + temp.currentRoom.x * width;
int roomB = temp.nextRoom.y + temp.nextRoom.x * width;
// check roomA and roomB to see if they are already members
if (ds.find(roomA) != ds.find(roomB)) {
walls.remove(randomWall);
ds.unionRooms(ds.find(roomA), ds.find(roomB));
temp.isGone = true;
temp.currentRoom.adj.add(temp.nextRoom);
temp.nextRoom.adj.add(temp.currentRoom);
num--;
}// end of if
}// end of while
}
// name the room to display
private int roomNumber = 0;
/**
* Sets the grid of rooms to be initially boxes
* This is self explanitory, we are only creating an reverse L for all
* The rooms and there is an L for the border
*/
private void generateInitialRooms() {
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
// create north walls
rooms[i][j] = new Room(i, j);
if (i == 0) {
rooms[i][j].north = new Wall(rooms[i][j]);
} else {
rooms[i][j].north = new Wall(rooms[i - 1][j], rooms[i][j]);
walls.add(rooms[i][j].north);
}
if (i == height - 1) {
rooms[i][j].south = new Wall(rooms[i][j]);
}
if (j == 0) {
rooms[i][j].west = new Wall(rooms[i][j]);
} else {
rooms[i][j].west = new Wall(rooms[i][j - 1], rooms[i][j]);
walls.add(rooms[i][j].west);
}
if (j == width - 1) {
rooms[i][j].east = new Wall(rooms[i][j]);
}
rooms[i][j].roomName = roomNumber++;// we will name the rooms
}
}
// initalize entrance and exit
rooms[0][0].west.isGone = true;// you can replace .west.isGone with .north.isGone
// this is just saying the roomName for top left is 0
rooms[0][0].roomName = 0;
// we will remove the south wall of the last room
rooms[height - 1][width - 1].south.isGone = true;
// this is just saying the roomName for bottom right is the last element in the mxn room matrix
rooms[height - 1][width - 1].roomName = (height * width);
}
public void paintComponent(Graphics g) {
x_cord = 40;
y_cord = 40;
// could have taken height as well as width
// just need something to base the roomsize
roomSize = (width - x_cord) / width + 7;
// temp variables used for painting
int x = x_cord;
int y = y_cord;
for (int i = 0; i <= height - 1; i++) {
for (int j = 0; j <= width - 1; j++) {
if (!(rooms[i][j].north.isGone)) {
g.drawLine(x, y, x + roomSize, y);
}//end of north if
// west wall not there draw the line
if (rooms[i][j].west.isGone == false) {
g.drawLine(x, y, x, y + roomSize);
}// end of west if
if ((i == height - 1) && rooms[i][j].south.isGone == false) {
g.drawLine(x, y + roomSize, x + roomSize,
y + roomSize);
}// end of south if
if ((j == width - 1) && rooms[i][j].east.isGone == false) {
g.drawLine(x + roomSize, y, x + roomSize,
y + roomSize);
}// end of east if
x += roomSize;// change the horizontal
}// end of inner for loop
x = x_cord;
y += roomSize;
}// end of outer for loop
}
public static void main(String[] args) {
// we will use the scanner for userInput
Scanner userInput = new Scanner(System.in);
int m, n;// these are variables for the size of maze (m x n)
System.out.print("Enter the size of your maze: ");
// store the input
m = userInput.nextInt();
n = userInput.nextInt();
// use JFrame to put the created panel on
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 800);
frame.getContentPane().add(new Maze(m, n));
frame.pack();
frame.setVisible(true);
}// end of main
}// END OF CLASS
I once wrote a Timer class to do this, you can find it here: https://github.com/twothe/newdawn/blob/master/two/newdawn/util/TimeCounter.java
It is very simple and sufficient for most tasks.
The general difficulty with Java is that code is not executed at a constant time. Somewhere in between the Garbage Collector might interrupt your timing, or the JIT considers a piece of code to slow and suddenly optimizes it. All these things will mess up any measurements done with System.nanoTime(), so don't take the numbers as facts, but more as a tendency.
If you want to have exact numbers, you need to use more sophisticated tools and especially run the code in question a thousand or billion times to rule out background noise, but even then those numbers are only valid for your local machine, and could be entirely different on a different hardware.
It depends how you want the information. The easiest way is to use System.currentTimeInMillis() before you start generation, then again afterwards, and compare the results. That'll give you the number of ms taken to generate.
e.g.
walls = new ArrayList<Wall>((height - 1) * (width - 1));
long startTime = System.currentTimeMillis();
generateRandomMaze();
long endTime = System.currentTimeMillis();
System.out.println("Time Taken: " + (endTime-startTime) + "ms");
setPreferredSize(new Dimension(800, 700));

Simple Java 2d array maze sample

I am working or understanding how to create a simple java 2d maze that should look like this:
int [][] maze =
{ {1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,1,0,1,0,1,0,0,0,0,0,1},
{1,0,1,0,0,0,1,0,1,1,1,0,1},
{1,0,0,0,1,1,1,0,0,0,0,0,1},
{1,0,1,0,0,0,0,0,1,1,1,0,1},
{1,0,1,0,1,1,1,0,1,0,0,0,1},
{1,0,1,0,1,0,0,0,1,1,1,0,1},
{1,0,1,0,1,1,1,0,1,0,1,0,1},
{1,0,0,0,0,0,0,0,0,0,1,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1}
};
Ones this has been created the idea is to set a starting point and goal point and by using recursive depth first find the path. but must say i am having difficulties to create the maze.
Do you have any suggestion on how to do it?
Or perhaps a link to a tutorial?
The main focus for me right now is just to create the maze.
Maze implementation has a lot of variations.
All depends on which of there aspects you want to use?
Here is some start point Maze generation algorithm.
I tried to solve this problem in the past. Instead of many words how I tried this, I guess to show code snippet.
maze generator code:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;
public class MyMaze {
private int dimensionX, dimensionY; // dimension of maze
private int gridDimensionX, gridDimensionY; // dimension of output grid
private char[][] grid; // output grid
private Cell[][] cells; // 2d array of Cells
private Random random = new Random(); // The random object
// initialize with x and y the same
public MyMaze(int aDimension) {
// Initialize
this(aDimension, aDimension);
}
// constructor
public MyMaze(int xDimension, int yDimension) {
dimensionX = xDimension;
dimensionY = yDimension;
gridDimensionX = xDimension * 4 + 1;
gridDimensionY = yDimension * 2 + 1;
grid = new char[gridDimensionX][gridDimensionY];
init();
generateMaze();
}
private void init() {
// create cells
cells = new Cell[dimensionX][dimensionY];
for (int x = 0; x < dimensionX; x++) {
for (int y = 0; y < dimensionY; y++) {
cells[x][y] = new Cell(x, y, false); // create cell (see Cell constructor)
}
}
}
// inner class to represent a cell
private class Cell {
int x, y; // coordinates
// cells this cell is connected to
ArrayList<Cell> neighbors = new ArrayList<>();
// solver: if already used
boolean visited = false;
// solver: the Cell before this one in the path
Cell parent = null;
// solver: if used in last attempt to solve path
boolean inPath = false;
// solver: distance travelled this far
double travelled;
// solver: projected distance to end
double projectedDist;
// impassable cell
boolean wall = true;
// if true, has yet to be used in generation
boolean open = true;
// construct Cell at x, y
Cell(int x, int y) {
this(x, y, true);
}
// construct Cell at x, y and with whether it isWall
Cell(int x, int y, boolean isWall) {
this.x = x;
this.y = y;
this.wall = isWall;
}
// add a neighbor to this cell, and this cell as a neighbor to the other
void addNeighbor(Cell other) {
if (!this.neighbors.contains(other)) { // avoid duplicates
this.neighbors.add(other);
}
if (!other.neighbors.contains(this)) { // avoid duplicates
other.neighbors.add(this);
}
}
// used in updateGrid()
boolean isCellBelowNeighbor() {
return this.neighbors.contains(new Cell(this.x, this.y + 1));
}
// used in updateGrid()
boolean isCellRightNeighbor() {
return this.neighbors.contains(new Cell(this.x + 1, this.y));
}
// useful Cell representation
#Override
public String toString() {
return String.format("Cell(%s, %s)", x, y);
}
// useful Cell equivalence
#Override
public boolean equals(Object other) {
if (!(other instanceof Cell)) return false;
Cell otherCell = (Cell) other;
return (this.x == otherCell.x && this.y == otherCell.y);
}
// should be overridden with equals
#Override
public int hashCode() {
// random hash code method designed to be usually unique
return this.x + this.y * 256;
}
}
// generate from upper left (In computing the y increases down often)
private void generateMaze() {
generateMaze(0, 0);
}
// generate the maze from coordinates x, y
private void generateMaze(int x, int y) {
generateMaze(getCell(x, y)); // generate from Cell
}
private void generateMaze(Cell startAt) {
// don't generate from cell not there
if (startAt == null) return;
startAt.open = false; // indicate cell closed for generation
ArrayList<Cell> cells = new ArrayList<>();
cells.add(startAt);
while (!cells.isEmpty()) {
Cell cell;
// this is to reduce but not completely eliminate the number
// of long twisting halls with short easy to detect branches
// which results in easy mazes
if (random.nextInt(10)==0)
cell = cells.remove(random.nextInt(cells.size()));
else cell = cells.remove(cells.size() - 1);
// for collection
ArrayList<Cell> neighbors = new ArrayList<>();
// cells that could potentially be neighbors
Cell[] potentialNeighbors = new Cell[]{
getCell(cell.x + 1, cell.y),
getCell(cell.x, cell.y + 1),
getCell(cell.x - 1, cell.y),
getCell(cell.x, cell.y - 1)
};
for (Cell other : potentialNeighbors) {
// skip if outside, is a wall or is not opened
if (other==null || other.wall || !other.open) continue;
neighbors.add(other);
}
if (neighbors.isEmpty()) continue;
// get random cell
Cell selected = neighbors.get(random.nextInt(neighbors.size()));
// add as neighbor
selected.open = false; // indicate cell closed for generation
cell.addNeighbor(selected);
cells.add(cell);
cells.add(selected);
}
}
// used to get a Cell at x, y; returns null out of bounds
public Cell getCell(int x, int y) {
try {
return cells[x][y];
} catch (ArrayIndexOutOfBoundsException e) { // catch out of bounds
return null;
}
}
public void solve() {
// default solve top left to bottom right
this.solve(0, 0, dimensionX - 1, dimensionY -1);
}
// solve the maze starting from the start state (A-star algorithm)
public void solve(int startX, int startY, int endX, int endY) {
// re initialize cells for path finding
for (Cell[] cellrow : this.cells) {
for (Cell cell : cellrow) {
cell.parent = null;
cell.visited = false;
cell.inPath = false;
cell.travelled = 0;
cell.projectedDist = -1;
}
}
// cells still being considered
ArrayList<Cell> openCells = new ArrayList<>();
// cell being considered
Cell endCell = getCell(endX, endY);
if (endCell == null) return; // quit if end out of bounds
{ // anonymous block to delete start, because not used later
Cell start = getCell(startX, startY);
if (start == null) return; // quit if start out of bounds
start.projectedDist = getProjectedDistance(start, 0, endCell);
start.visited = true;
openCells.add(start);
}
boolean solving = true;
while (solving) {
if (openCells.isEmpty()) return; // quit, no path
// sort openCells according to least projected distance
Collections.sort(openCells, new Comparator<Cell>(){
#Override
public int compare(Cell cell1, Cell cell2) {
double diff = cell1.projectedDist - cell2.projectedDist;
if (diff > 0) return 1;
else if (diff < 0) return -1;
else return 0;
}
});
Cell current = openCells.remove(0); // pop cell least projectedDist
if (current == endCell) break; // at end
for (Cell neighbor : current.neighbors) {
double projDist = getProjectedDistance(neighbor,
current.travelled + 1, endCell);
if (!neighbor.visited || // not visited yet
projDist < neighbor.projectedDist) { // better path
neighbor.parent = current;
neighbor.visited = true;
neighbor.projectedDist = projDist;
neighbor.travelled = current.travelled + 1;
if (!openCells.contains(neighbor))
openCells.add(neighbor);
}
}
}
// create path from end to beginning
Cell backtracking = endCell;
backtracking.inPath = true;
while (backtracking.parent != null) {
backtracking = backtracking.parent;
backtracking.inPath = true;
}
}
// get the projected distance
// (A star algorithm consistent)
public double getProjectedDistance(Cell current, double travelled, Cell end) {
return travelled + Math.abs(current.x - end.x) +
Math.abs(current.y - current.x);
}
// draw the maze
public void updateGrid() {
char backChar = ' ', wallChar = 'X', cellChar = ' ', pathChar = '*';
// fill background
for (int x = 0; x < gridDimensionX; x ++) {
for (int y = 0; y < gridDimensionY; y ++) {
grid[x][y] = backChar;
}
}
// build walls
for (int x = 0; x < gridDimensionX; x ++) {
for (int y = 0; y < gridDimensionY; y ++) {
if (x % 4 == 0 || y % 2 == 0)
grid[x][y] = wallChar;
}
}
// make meaningful representation
for (int x = 0; x < dimensionX; x++) {
for (int y = 0; y < dimensionY; y++) {
Cell current = getCell(x, y);
int gridX = x * 4 + 2, gridY = y * 2 + 1;
if (current.inPath) {
grid[gridX][gridY] = pathChar;
if (current.isCellBelowNeighbor())
if (getCell(x, y + 1).inPath) {
grid[gridX][gridY + 1] = pathChar;
grid[gridX + 1][gridY + 1] = backChar;
grid[gridX - 1][gridY + 1] = backChar;
} else {
grid[gridX][gridY + 1] = cellChar;
grid[gridX + 1][gridY + 1] = backChar;
grid[gridX - 1][gridY + 1] = backChar;
}
if (current.isCellRightNeighbor())
if (getCell(x + 1, y).inPath) {
grid[gridX + 2][gridY] = pathChar;
grid[gridX + 1][gridY] = pathChar;
grid[gridX + 3][gridY] = pathChar;
} else {
grid[gridX + 2][gridY] = cellChar;
grid[gridX + 1][gridY] = cellChar;
grid[gridX + 3][gridY] = cellChar;
}
} else {
grid[gridX][gridY] = cellChar;
if (current.isCellBelowNeighbor()) {
grid[gridX][gridY + 1] = cellChar;
grid[gridX + 1][gridY + 1] = backChar;
grid[gridX - 1][gridY + 1] = backChar;
}
if (current.isCellRightNeighbor()) {
grid[gridX + 2][gridY] = cellChar;
grid[gridX + 1][gridY] = cellChar;
grid[gridX + 3][gridY] = cellChar;
}
}
}
}
}
// simply prints the map
public void draw() {
System.out.print(this);
}
// forms a meaningful representation
#Override
public String toString() {
updateGrid();
String output = "";
for (int y = 0; y < gridDimensionY; y++) {
for (int x = 0; x < gridDimensionX; x++) {
output += grid[x][y];
}
output += "\n";
}
return output;
}
// run it
public static void main(String[] args) {
MyMaze maze = new MyMaze(20);
maze.solve();
maze.draw();
}
}
It isn't the best solution, my task at this time was implement this algorithm by myself. It has clear comments.
Output:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
X * X ********* X ***** X X X
X * X * XXXXX * X * X * X X X X
X ***** X ***** X * X * X X X X
XXXXXXXXX * XXXXX * X * X X X X
X X ***** X * X * X X X
X X XXXXX * X * X * XXXXXXXXX X
X X X ***** X * X
X XXXXXXXXXXXXXXXXX * XXXXXXXXXXXXX
X ***************** X ***** X X
X * XXXXXXXXXXXXX * XXXXX * X X X
X ***** X X ********* X X X
XXXXX * X XXXXXXXXXXXXXXXXXXXXX X
X ***** X ***** X ***** X
X * XXXXXXXXXXXXX * X * XXXXX * X * X
X ************* X * X * X ***** X * X
XXXXXXXXXXXXX * X * X * X * XXXXX * X
X ***** X ***** X * X
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
I hope it will be useful as an illustration of some solution.
I know this is probably completely outdated, but...
First, you should realize that the underlying structure of such a maze is an undirected graph on a 2-dimensional grid. Now to create a so called "perfect maze", you just have to create any spanning tree of a full grid graph. And to do that there are plenty of algorithms, from random graph traversals (BFS, DFS) over algorithms derived from the known minimum-spanning tree algorithms (Kruskal, Prim, Boruvka, Reverse-Delete) to algorithms creating "uniformly random" spanning trees (Wilson, Aldous-Broder) to other algorithms that don't fit into these categories like "recursive division", "Eller's" etc.
I implemented lots of these algorithms based on a grid graph structure and you can find my implementation here:
https://github.com/armin-reichert/mazes
If I understand your question correctly, what I would do is:
1. create a board of a specific size (change all the coordinates to your desired number - in your example '1').
I wouldn't use a recursive function, because you will probably end up drawing the whole board (think about what will make the recursion stop).
you can create a function that receives a starting coordination, an ending coordination,
and the array (the board).
pseudo code of the function:
set a variable for the next direction of painting (set it to the starting coordination).
paint the next coordination 0.
while the next coordination != to the ending coordination:
paint the next coordination 0.
use Random to set the coordination to one of the 4 directions.
you should add limits (if the next coordination is a painted one/the border of the maze etc... chose a different coordination).
good luck!

Categories