Room placement in a maze using Prim's algorithm - java

I am attempting to place rooms on an ASCII screen, and then use Prim's algorithm to "fill" the space between rooms with maze, but without actually breaking into the rooms. I have been tinkering for a few hours, and I can't figure out a way to stop my algorithm from breaking into my rooms.
Can anyone help me? I'm pretty lost. I'm practicing map generation techniques, and this is my 5th one I'm on. No, I don't want to do it another way, I simply want to do it this way - but right.
Below is both a photo of my current output with rooms, my current output without rooms, and a link to the relevant source code (AKA the Prim's algorithm section). Thanks again if you can actually help me!
Note: All the opposite method does is figure out which cell the "parent" cell is, and determine direction based off of that. So if parent cell's x value is 7, and the child's x value is 6, then it know that's the new child.
start = new Point(x,y, null);
map[start.x][start.y] = Tile.STAIRS_DOWN;
for(int nx = -1; nx <= 1; nx++){
for(int ny = -1; ny <= 1; ny++){
if((nx == 0 && ny == 0) || (nx != 0 && ny != 0)){
continue;
}
try{
if(map[start.x + nx][start.y + ny] == Tile.FLOOR){
continue;
}
frontier.add(new Point(start.x+nx, start.y + ny, start));
}
catch(Exception e){
continue;
}
}
}
Point last = null;
while(!frontier.isEmpty()){
Point cu = frontier.remove(RandomGen.rand(0, frontier.size() - 1));
Point op = cu.opposite();
try{
if((map[cu.x][cu.y] == Tile.WALL) && (map[op.x][op.y] == Tile.WALL)){
for (int bx = -1; bx <= 1; bx++)
for (int by = -1; by <= 1; by++) {
boolean failed = false;
if (bx == 0 && by == 0 || bx != 0 && by != 0)
continue;
try {
if(map[op.x + bx][op.y + by] == Tile.FLOOR){
break;
}
last = op;
if(!failed){
map[cu.x][cu.y] = Tile.FLOOR;
map[op.x][op.y] = Tile.FLOOR;
frontier.add(new Point(op.x + bx, op.y + by, op));
}
}
catch(Exception e){
continue;
}
}
}
}
catch(Exception e){}
}
Without Rooms
With Rooms

Solved: I needed to check my forward facing corners for open spaces. So if I'm going "east" then I need to check Northeast and Southeast tiles as well, or else I will potentially burrow into the rooms.

Related

Draw stops working after if

I'm trying to create a maze with Union Find, but am unable to remove walls.
This is what I have got so far.
private void createMaze (int cells, Graphics g) {
s = new int[cells*cells]; //No unions yet setting all to -1
for(int i = 0; i < cells*cells; ++i){
s[i] = -1;
}
g.setColor(Color.yellow);
random = new Random();
while(breaker){
g.setColor(Color.yellow);
int innerWall = random.nextInt(4)+0;
int randomCellX = random.nextInt(cells-1)+0;
int randomCellY = random.nextInt(cells)+0;
if(randomCellX==cells&&innerWall==2||
randomCellX==0&&innerWall==0||
randomCellY==cells-1&&innerWall==3||
randomCellY==0&&innerWall==1){
continue;
}
else{
int location = randomCellX+(randomCellY*cells);
int neighbour = 0;
if(innerWall==0){
neighbour =location-1;
}
else if(innerWall==1){
neighbour =location-cells;
}
else if(innerWall==2){
neighbour =location+1;
}
else if(innerWall==3){
neighbour =location+cells;
}
int locationRoot =find(location);
int neighbourRoot =find(neighbour);
if(locationRoot==neighbourRoot){
breaker = checkIfDone(s);
}
union(location,neighbour);
drawWall(randomCellX,randomCellY,innerWall,g);
}
}
}
If I remove the
if(randomCellX==cells&&innerWall==2||
randomCellX==0&&innerWall==0||
randomCellY==cells-1&&innerWall==3||
randomCellY==0&&innerWall==1){
continue;
}
It removes the lines fine,but when it is added the walls are not removed. The method is called but doesn't do anything.
It seems some logical mistake have been done obviously. But can not spot specifically as you haven't given the explanation of the logic you have done. Only can help showing the path where the problem might be happening.
Since you can not reach the else block anyway, it is obvious that one or more of these conditions in if are always true.
(randomCellX == cells && innerWall == 2)
(randomCellX == 0 && innerWall == 0)
(randomCellY == cells - 1 && innerWall == 3)
(randomCellY == 0 && innerWall == 1)
That's why you are getting true for the if condition and continuing the loop without doing anything. Ensure the conditions are okay.
If these conditions are right, the next suspect might be these lines:
int innerWall = random.nextInt(4)+0;
int randomCellX = random.nextInt(cells-1)+0;
int randomCellY = random.nextInt(cells)+0;
Check whether these random values range are exactly what you want or not. For example: you are taking random values for randomCellX from 0 to cells-2, but for randomCellY the range is 0 to cells-1.

How to get variables from for loop

I'm trying to make a for loop, which makes me 1000 objects and places those to a randomly generated spot (x, y). So here is the code. I have been struggling with this many hours and I have also been searching from the net but haven't found out any way to do that. After that loop I try to add those objects into some kind of radar.
Here's the code (so the problem is that I can't figure out how to take variables from the loop and make it appear outside the loop):
case "look": {
System.out.print("You are at: " +px +", " +py);
System.out.println("");
StringBuilder objects = new StringBuilder(); //That's something i found out form the net..
while (objnum>=0){ objnum--; //Creates randomly 1000objects around the map
int objid = (int)(Math.random() * 11 + 1); //int objnum is 1000, told above
int objx = (int)(Math.random() * 10000 + 1);
int objy = (int)(Math.random() * 10000 + 1);}
board.spawnObject(new BoardObject(objectid, objx, //That's something i found out form the net..
objy, objnum));
for(int x=px-2 ; x< px+3 ; x++ ){ //px=player position
for(int y=py-2 ; y< py+3 ; y++ ){ //this is how radar is created
if(objid==1 && x==objx && y==objy){board[x][y]=1;}
else if(objid==2 && x==objx && y==objy){board[x][y]=2;}
else if(objid==3 && x==objx && y==objy){board[x][y]=3;} //That's where i need info from the loop..
else if(objid==4 && x==objx && y==objy){board[x][y]=4;}
else if(objid==5 && x==objx && y==objy){board[x][y]=5;}
else if(objid==6 && x==objx && y==objy){board[x][y]=-1;}
else if(objid==7 && x==objx && y==objy){board[x][y]=-2;}
else if(objid==8 && x==objx && y==objy){board[x][y]=-3;}
else if(objid==9 && x==objx && y==objy){board[x][y]=-4;}
else if(objid==10 && x==objx && y==objy){board[x][y]=-5;}
if(x==px && y==py){
board[x][y]=6;}//<- this shows players position on radar
if(board[x][y]==-1){
System.out.print("[sto]");
}else if(board[x][y]==0){
System.out.print("[___]");//<- This works well..
}else if(board[x][y]==-2){
System.out.print("[box]");
}
else if(board[x][y]==-3){
System.out.print("[ppl]");
}
else if(board[x][y]==-4){
System.out.print("[pit]");
}
else if(board[x][y]==-5){
System.out.print("[brk]");
} //That's how radar shows dots/objects
else if(board[x][y]==1){
System.out.print("[kid]");
}
else if(board[x][y]==2){
System.out.print("[tre]");
}
else if(board[x][y]==3){
System.out.print("[pet]");
}
else if(board[x][y]==4){
System.out.print("[bus]");
}
else if(board[x][y]==5){
System.out.print("[???]");
}
else if(board[x][y]==6){
System.out.print("[You]");} //<- This works well..
}
System.out.println();
}; }break;
Simply use Collection interface class like Vector for storing objects and then, access them from outside the loop.Follow this link: http://docs.oracle.com/javase/6/docs/api/java/util/Collection.html
You have to create a variable outside the scope and if a condition is true, than change the value of the variable outside the loop, so:
class example {
int number = 0;
.....
for (int i = 0; i < 10; i++) {
if (i == 5) {
number = 5;
}
}
If the condition is true, then number becomes 5, to get this data, you can create getters and setters.
But if you want to store all those 1000 objects in 1 object, i would recommend to use an array(list), so:
class Example {
ArrayList<Integer> object = new ArrayList<Integer>();
case.....
object.add(objx)
object.add(objy)
}

java: tetris random block generated upon row clear

I'm trying to make the game Tetris in java.
I've gotten it to the point where:
a new block is generated when it hits the floor or its y+1 is not null (meaning there's another block under it)
public void collisionCheck(int x, int y) {
if (activetile.getY() == this.height-2 || getTileAt(x, y+1) != null) {
activetile = new Tile(this, 0, 0);
}
}
A row clears when the bottom row is full of non-null values, or the Tetris pieces (for y = 4 (the floor), loop through x till x = 4 and check if all non-null)
public void checkBottomFull(int x, int y) {
while (getTileAt(x,y) != null) {
say("(" + x + ", " + y +")");
if (x == 3) {
say("row is full");
//replace full row with tiles from above
for (int i = 0; i < 4; i++) {
for (int j = 5; j > 0; j--) {
grid[j][i] = getTileAt(i,j-1);
grid[j-1][i] = null;
}
}
break;
}
x++;
}
}
Right now, I'm using keys to move the block:
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_DOWN) {
activetile.setLocation(activetile.getX(), activetile.getY()+1);
System.out.println("coordinates: " + activetile.getX() + ", " + activetile.getY());
collisionCheck(activetile.getX(),activetile.getY());
checkBottomFull(0,4);
repaint();
}
}
There's two issues I'm having:
1) In the picture you'll notice I've dropped the block all the way to the floor... and the row cleared. After it's cleared, it will generate a block to the top left (x=0, y=1) which I have no control over.
2) On the floor there seems to be a red line... which I'm assuming is a row of blocks hidden by the JFrame... I'm not sure why that's there.
FYI: If you're wondering why grid[j][i] has the rows and columns flipped (aka, why it's not grid[i][j]) is because I instantiated it as grid = new Tile[height][width];
Any thoughts?
Thanks!
It is hard to say what is wrong without actually debugging your app.
But maybe try this one:
public void checkBottomFull(int x, int y) {
while (getTileAt(x,y) != null) {
say("(" + x + ", " + y +")");
if (x == 3) {
say("row is full");
//replace full row with tiles from above
for (int i = 0; i < 4; i++) {
for (int j = 4; j >= 0; j--) {
grid[j][i] = getTileAt(i,j-1);
grid[j-1][i] = null;
}
}
break;
}
x++;
}
}
You have 5 rows (indexed from 0 to 4) and 4 columns (indexed from 0 to 3).
What values of height and width do you pass to:
grid = new Tile[height][width];
Because from what I see you should do something like that:
grid = new Tile[5][4];
Bah,
Turns out in the key event, I needed to check if the bottom was full before checking if there is a collision.
I guess what was happening is, when I was checking collisionCheck(activetile.getX(),activetile.getY()); before checkBottomFull(0,4);, when the bottom was full, it would clear the row and set the current row equal to the row above it: grid[j][i] = getTileAt(i,j-1);, the problem was that collisionCheck was generating a new piece and the that newly generated piece was getting cleared and replaced by checkBottomFull.
Putting the collision check after the checkBottomFull ensures that the newly generated piece won't be replaced if bottom is full.

Can anyone lend a fresh pair of eyes for debugging?

The purpose of this assignment was to create a Field and Robot class and objects of those classes.
The single field object is limited to a square of points from (0, 0) to (50, 50), and contains 3 pieces of gold and 3 bombs.
Three robot objects search the field (one after another) for gold from left to right (0, 0) to (0, 50) and descend through the field (1, 0) to 1, 50) and so on.
The robots are destroyed by bombs that are places by input from the user. Once the gold is collected it cannot be picked up by another robot, and one a bomb explodes it does not do so again.
This is my attempt at solving the problem so far, I am continuing to work on it, but would appreciate a second pair of eyes on it to catch something im missing. The program compiles, but the bombs and gold arent being "found" correctly and the output states that the following robots die on the same bomb as the one before it. Also, there are several pieces of code removed by comments, I did this to test different parts of the program. I think this section is where I'm having trouble. The methods field.alreadyFound() and field.alreadyBombed() return a boolean with the value true. My if statements should be saying if the gold/bomb has already been found, ignore it.
while(x <= 50 && y <= 50 && alive2 == true) {
foundGold1 = robot2.look(field.locateGold1());
foundGold2 = robot2.look(field.locateGold2());
foundGold3 = robot2.look(field.locateGold3());
foundBomb1 = robot2.look(field.locateBomb1());
foundBomb2 = robot2.look(field.locateBomb2());
foundBomb3 = robot2.look(field.locateBomb3());
/*gotBomb1 = field.alreadyBombed1();
gotBomb2 = field.alreadyBombed2();
gotBomb3 = field.alreadyBombed3();
gotGold1 = field.alreadyFound1();
gotGold2 = field.alreadyFound2();
gotGold3 = field.alreadyFound3();*/
if (foundGold1 == true){
if (field.alreadyFound1() == true){}
else {robot2.addGold();
field.foundGold1();}
}
if (foundGold2 == true) {
if (field.alreadyFound2() == true){}
else {robot2.addGold();
field.foundGold2();}
}
if (foundGold3 == true) {
if (field.alreadyFound3() == true){}
else {robot2.addGold();
field.foundGold3();}
}
if (foundBomb1 == true) {
if (field.alreadyBombed1() == true){}
else alive2 = false;
}
if (foundBomb2 == true) {
if (field.alreadyBombed2() == true){}
else alive2 = false;
}
if (foundBomb3 == true) {
if (field.alreadyBombed3() == true){}
else alive2 = false;
}
y = y + 1;
robot2.setLocation(x, y);
//System.out.println(y);
if (y == 50)
{x = x + 1;
y = 0;}
}
I don't see where you set that a bomb has exploded, so from what I see here, you're missing that part.
Below is the code reformatted and slightly restructured: I found the code fairly difficult to work with as-is. It may be easier to work with this shorter, more canonical, and IMO more communicative version.
while (x <= 50 && y <= 50 && alive2 == true) {
foundGold1 = robot2.look(field.locateGold1());
foundGold2 = robot2.look(field.locateGold2());
foundGold3 = robot2.look(field.locateGold3());
foundBomb1 = robot2.look(field.locateBomb1());
foundBomb2 = robot2.look(field.locateBomb2());
foundBomb3 = robot2.look(field.locateBomb3());
/*gotBomb1 = field.alreadyBombed1();
gotBomb2 = field.alreadyBombed2();
gotBomb3 = field.alreadyBombed3();
gotGold1 = field.alreadyFound1();
gotGold2 = field.alreadyFound2();
gotGold3 = field.alreadyFound3();*/
if (foundGold1 && !field.alreadyFound1()) {
robot2.addGold();
field.foundGold1();
}
if (foundGold2 && !field.alreadyFound2()) {
robot2.addGold();
field.foundGold2();
}
if (foundGold3 && !field.alreadyFound3()) {
robot2.addGold();
field.foundGold3();
}
if (foundBomb1 && !field.alreadyBombed1()) {
alive2 = false;
}
if (foundBomb2 && !field.alreadyBombed2()) {
alive2 = false;
}
if (foundBomb3 && !field.alreadyBombed3()) {
alive2 = false;
}
y = y + 1;
robot2.setLocation(x, y);
if (y == 50) {
x = x + 1;
y = 0;
}
}
You may also be hampered by the amount of code it takes to do this work: I assume you have essentially identical code for robot1 and robot2, the only difference being which robot you're currently processing.
Rather than repeat the code, consider passing a currentRobot in to a single method. There are a variety of ways to go about dealing with issues like this, but that fits in well with what you've already done. You're likely want to add an isAlive method/property to the robot class.

When there is two for() loop, the second one doesn't work

Here are my for() loops :
public void showMovementCase(){
int movePlusAttack = moveAllowed+attackDistance;
int twiceMoveAllowed = (moveAllowed)*2;
for(int i = 0; i <= movePlusAttack*2; i++){
for(int j = 0; j <= movePlusAttack*2;j++){
boolean a = movePlusAttack <= j+i && movePlusAttack >= j-i && i <= movePlusAttack;
boolean b = movePlusAttack <= j+i && movePlusAttack >= i-j && i > movePlusAttack && j <= movePlusAttack;
boolean c = movePlusAttack*3 >= j+i && movePlusAttack >= j-i && i > movePlusAttack && j >= movePlusAttack;
if(a || b || c){
try{
actionSquare[i][j] = new JLabel();
actionSquare[i][j].setIcon(redsquare);
actionSquare[i][j].setBounds(sprite.getX()+(i-movePlusAttack)*16,sprite.getY()+(j-movePlusAttack)*16, 16, 16);
panel.add(actionSquare[i][j], new Integer(1));
}
catch(ArrayIndexOutOfBoundsException e){System.out.println("red :" + e);}
}
}
}
for(int x = 0; x <= twiceMoveAllowed; x++){
for(int y = 0; y <= twiceMoveAllowed;y++){
boolean a = moveAllowed <= y+x && moveAllowed >= y-x && x <= moveAllowed;
boolean b = moveAllowed <= y+x && moveAllowed >= x-y && x > moveAllowed && y <= moveAllowed;
boolean c = moveAllowed*3 >= y+x && moveAllowed >= y-x && x > moveAllowed && y >= moveAllowed;
if(a || b || c){
try{
actionSquare[x][y].setIcon(bluesquare);
System.out.println("Coucou !");
actionSquare[x][y].addMouseListener(mouse);
panel.repaint();
panel.revalidate();
}
catch(ArrayIndexOutOfBoundsException e){System.out.println("blue :" + e); }
}
}
}
}
if this.attackDistance is different of 0, then the second loop doesn't work (it seems to stop at the .setIcon() command).
Do you know a way to fix this ?
Thanks for reading.
Edit:
with :
try{
actionSquare[x][y].setIcon(bluesquare);
System.out.println("Coucou !");
[...]
}
On the second loop, nothing is printed.
but with :
try{
System.out.println("Coucou !");
actionSquare[x][y].setIcon(bluesquare);
[...]
}
"Coucou !" is printed once.
That's why I said that "it seems to stop at the .setIcon() command" I should have said that sooner, sorry.
Here are a few tips:
don't catch exceptions and do nothing with them. That's what you are doing here in both loops, and so it's normal you don't see the error message.
anytime you see long statements like you have, it should be a hint that you could refactor it. For example, create a separate method that validates whether or not you're going to do something in your loop, and then inside the main method you'd call it like if(shouldPerformAction())
consider using less than 8 spaces for indentation. This just eats up your screen real estate.
consider making computations before the loops instead of inside the loop conditions, if the computation is supposed to be fixed (for example this.moveAllowed*2)
imho, no point in prefixing all your methods/fields with this, it just clutters everything. Just call the methods directly.
This is a very, very bad idea:
catch(ArrayIndexOutOfBoundsException e){}
You effectively tell the JVM to ignore any problems with your arrays that it detects. And worse than that: you don't even print anything when that happens.
Put at least a e.printStackTrace() in there to see if a problem occurs and where.
And as a further step: fix your array access to not exceed any limits. Catching an ArrayIndexOutOfBoundsException is a terribly bad idea. Avoid having it thrown at all!
Hmmm... where to begin...
I would first suggest putting something (System.err.println(...)?) inside of your catch blocks. Or just commenting them out entirely so you'd see the full stacktrace. What if you're hitting an exception and just not seeing it?
catch(ArrayIndexOutOfBoundsException e){}
This is a bad practice for two reasons:
You should never catch RuntimeException. It is just a very helpful indicator for errors in code logic (i.e. developer errors) which ought be solved by writing good code.
You should never ignore e unless you know perfectly what you're doing. Add at least an e.printStackTrace() so that you at least know that something failed.
I cleaned up your code for you. Generally, when you have two sections of code that are supposed to be doing the exact same thing, but are not, then rolling them into one method can eliminate that possibility.public void showMovementCase(){
// probably want to remove anything left over from the last invocation
panel.removeAll();
for (JLabel[] array : actionSquare) Arrays.fill(array, null);
colorSquares(moveAllowed + attackDistance, redsquare, null);
colorSquares(moveAllowed * 2, bluesquare, mouse);
for (int x = 0; x < actionSquare.length; x++)
for (int y = 0; y < actionSquare[x].length; y++)
if (actionSquare[x][y] != null) panel.add(actionSquare[x][y], 1);
}
private void colorSquares(int move, Icon color, MouseListener mouse) {
int xMax = Math.min(2 * move, actionSquare.length);
int yMax = Math.min(2 * move, actionSquare[0].length);
for (int x = 0; x < xMax; x++) {
for (int y = 0; y < yMax; y++) {
if (isLegal(x, y, move)) {
if (actionSquare[x][y] == null)
actionSquare[x][y] = new JLabel();
actionSquare[x][y].setIcon(color);
actionSquare[x][y].setBounds(
sprite.getX() + (x - move) * 16,
sprite.getY() + (y - move) * 16, 16, 16 );
if (mouse != null) actionSquare[x][y].addMouseListener(mouse);
}
}
}
}
private static boolean isLegal(int x, int y, int move) {
// informative comment explaining why this mess makes sense
if (move <= y+x && move >= y-x && x <= move) return true;
// informative comment explaining why this mess makes sense
if (move <= y+x && move >= x-y && x > move && y <= move) return true;
// informative comment explaining why this mess makes sense
if (move * 3 >= y+x && move >= y-x && x > move && y >= move) return true;
return false;
}

Categories

Resources