Stackoverflow in recursive method to create hiearchy - java

Suppose i have an image like this (the numbers are for explaining, so focus on the white):
I'm working on a method to create a hiearchy of the contours instead of a flat list. In the case of the image above:
-n1 holds 1 contour: n2
-n2 hold 3 contours: n3, n4, n5
-n5 holds 1 contour: n6
-n6 holds 1 contour: n7
Make sure you understand the above before continue reading.
I have the method addContainingBlob, this method is in the blob class. I already checked in another method if the blobToAdd is within the bounds of the blob where i add it. I get a StackOverflowError at this line: b.addContainingBlob(blobToAdd); // <<<<<<.
I'm breaking my head around this for the last hour. Can someone see why it goes wrong?
protected void addContainingBlob(Blob blobToAdd) {
if (containingBlobs.size() == 0) {
containingBlobs.add(blobToAdd);
return;
}
Rectangle r1 = new Rectangle();
r1.setBounds(blobToAdd.minX(), blobToAdd.minY(), blobToAdd.width(), blobToAdd.height());
Rectangle r2 = new Rectangle();
// first check if we already have a containing blob that
// can hold the one we like to add
for (Blob b : containingBlobs) {
r2.setBounds(b.minX(), b.minY(), b.width(), b.height());
if (r2.contains(r1)) {
b.addContainingBlob(blobToAdd); // <<<<<<
return;
}
}
// it can also be that one OR MORE of the containing blobs can fit in the blob we like to add
for (int i = containingBlobs.size()-1; i >= 0; i--) {
Blob b = containingBlobs.get(i);
r2.setBounds(b.minX(), b.minY(), b.width(), b.height());
if (r1.contains(r2)) {
containingBlobs.remove(i);
blobToAdd.addContainingBlob(b);
}
}
containingBlobs.add(blobToAdd);
}

If the code allows a blob to contains itself, the call to blobToAdd.addContainingBlob(b) will lead to infinite recursion because of
if (r2.contains(r1)) {
b.addContainingBlob(blobToAdd); // <<<<<<
return;
}

Related

Java - Loop gets executed, but header doesnt fit condition

This function should check if the explosion hit a box and should be canceled on the first box it hits.
For example bsp.getBomb().getStrength() is currently 2, when a box is hit i=3, but the loop is executed one more time even if the condition isn't met, why is that?
public void detectBomb(BombSpritePair bsp) {
for(int i = 0; i <= bsp.getBomb().getStrength(); i++) {
if(bd.detect(bsp.getBomb().getX(), bsp.getBomb().getY()+i)) {
Sprite sprite = new Sprite(new Texture("gras.png"));
sprite.setPosition(bsp.getBomb().getX()*16, (bsp.getBomb().getY()+i)*16);
// i = bsp.getBomb().getStrength()+1;
sprites.add(sprite);
System.out.println("RIP"+i);
System.out.println(bsp.getBomb().getStrength());
break;
}
}
}
Try adding break to the loop:
public void detectBomb(BombSpritePair bsp) {
for(int i = 0; i <= bsp.getBomb().getStrength(); i++) {
if(bd.detect(bsp.getBomb().getX(), bsp.getBomb().getY() + i)) {
Sprite sprite = new Sprite(new Texture("gras.png"));
sprite.setPosition(bsp.getBomb().getX()*16, (bsp.getBomb().getY()+i)*16);
sprites.add(sprite);
System.out.println("RIP"+i);
System.out.println(bsp.getBomb().getStrength());
break;
}
}
}
You may be seeing duplicate print statements if you are calling the detectBomb method multiple times. Putting a breakpoint or print statement at the beginning of this method will help determine what the problem is.
Another thing to keep in mind is that you are creating a new Texture for the Sprite every time the method get executed - it would be wise to only instantiate the Texture once and share it with all subsequent Sprite instances that require it.

Storing Blocks for Replace Later

Here's my snippet of code. I have Lists for the blocks and player. All I need is for after the five seconds in that runnable is up, it'll replace the blocks that were previously replaced, assigning a player to get the blocks.
#EventHandler
public void onSnowballHit(ProjectileHitEvent e) {
// If it's a snowball...
if (e.getEntity() instanceof Snowball) {
Snowball snowball = (Snowball) e.getEntity();
final Player p = (Player) snowball.getShooter();
// ...if a player threw it...
if (snowball.getShooter() instanceof Player) {
// Make a Player from the Entity
BlockIterator iterator = new BlockIterator(e.getEntity().getWorld(),
e.getEntity().getLocation().toVector(), e.getEntity().getVelocity().normalize(),
0.0D, 4);
// Make a block
Block hitBlock = null;
// Loop through possible blocks
while (iterator.hasNext()) {
// Set the hitBlock to the current block we're checking
hitBlock = iterator.next();
// If it's not air, STOP!
if (!hitBlock.getType().equals(Material.AIR)) {
break;
}
}
int min = 1;
int max = 15;
Random r = new Random();
byte clayBlocks = (byte) (r.nextInt(max - min + 1) + min);
paintBlockList.add(hitBlock);
painters.add(p);
// Set it to stained clay
hitBlock.setType(Material.STAINED_CLAY);
// red = 14, blue = 11 (data values)
hitBlock.setData(clayBlocks);
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
public void run(){
if(painters.contains(p)){
painters.remove(p);
}
}
}, 100);
}
}
}
You could do it the same way you remove the Player from the painters list. Once you've found the first solid block (hitBlock) create a new final reference so that you can access it in the Runnable, for example like this:
final Block solidBlock = hitBlock;
To return the block to the state it was before you changed its type and data, you could keep track of those attributes:
final byte previousData = solidBlock.getData();
final Material previousType = solidBlock.getType();
Then in your run() method you can simply change the block back if it is still at that point different like so:
if (solidBlock.getType() != previousType) {
solidBlock.setType(previousType);
}
if (solidBlock.getData() != previousType) {
solidBlock.setData(previousData);
}
I'm sure there's a cleaner, nicer way to do it but this might be good enough for your purposes (I did find a glitch where if you throw two snowballs at the same block, it won't revert to the true original block but to a stained clay block because of the way the future Runnable tasks are written here, to fix this you'd need to write this completely differently and keep track of more things).

how do i stop this "IndexOutOfBoundsException" issue?

So I the app lets a user place down blocks on a grid, if the user lines up 3 or more blocks with the same suit, or color, then something happens. When player places a block I call this method:
blocks_.add(new Block(new Vector2(rect_mouse.x, rect_mouse.y), blocks_.get(0).blockID, blockCount));
When you place 3 or more together I call these methods:
blocks_.removeValue(blocks_.get(left_bravo_indexNum), true);
blocks_.removeValue(blocks_.get(center_charlie_indexNum), true);
blocks_.removeValue(blocks_.get(right_alpha_indexNum), true);
stack:
Exception in thread "LWJGL Application" java.lang.IndexOutOfBoundsException: 13
at com.badlogic.gdx.utils.Array.get(Array.java:125)
at com.jrp.mygearapp.GameScreen.touchUp(GameScreen.java:1443)
at com.badlogic.gdx.backends.lwjgl.LwjglInput.processEvents(LwjglInput.java:297)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:186)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:110)
This was intended to remove the blocks, but it resulted in this IndexOutOfBoundsException. Is there a way to prevent this error?
This could be occurring because the array auto sorts the number of elements and lowers the number to the correct number of elements in the array, and I still have elements that are labeled higher then the size of the array. I am still a novice, so my analysis could be incorrect. Please alert me if this is the case and help me find a fix.
Thanks.
edirted* TouchUp() function-------
#Override
public boolean touchUp(int x, int y, int pointer, int button) {
if (button == 0) {
display_blockCheck = false;
////set blockCount to the size of blockArray so blocks can properly be indexed
blockCount = blocks_.size;
if (!overlap) {
Gdx.app.log("Block Added", "x: " + x + " y: " + y);
updateQueueBlocks();
//add block
Vector2 rect_vector = new Vector2(rect_mouse.x, rect_mouse.y);
Block block = new Block(rect_vector,blocks_.get(0).blockID, blocks_.size);
blocks_.add(block);
if (center_charlie_suit == "Square") {
center_charlie_bool = true;
if (right_bravo_suit == "Square") {
right_bravo_bool = true;
if (right_alpha_suit == "Square") {
Gdx.app.log("3-pair", "Square:345:lr");
right_alpha_bool = true;
//call 3-pair event
blocks_.removeValue(blocks_.get(center_charlie_indexNum), true);
blocks_.removeValue(blocks_.get(right_alpha_indexNum), true);
blocks_.removeValue(blocks_.get(right_bravo_indexNum), true);
}
}
}
the rest is just really long and just checks for other blocks next to each other..
You're right, as you remove the blocks, the indexes change.
You don't show what type of Collection blocks_ is (Vector2?, did you write it?), however, rather than tracking the indices of the elements, simply track the elements themselves and call remove() to find and remove that element.

java: breadth first traversal with backtracking for maze

I am trying to implement a breadth first traversal for a maze. This is the code I have so far using a linked list but I am not sure if it is searching breadth first. Is this the proper way to do it? any suggestions, comments?
public boolean traverseBreadth(){
//traverse the floor from entrance to exit
//stepping only on red tiles
steps = new LinkedList<Tile>();
possibleSteps = new LinkedList<Tile>();
//reset markings
reset();
//push the entrance onto the stack
entrance.setVisited();
steps.add(entrance);
System.out.println("add " + entrance);
nextMoves(entrance);
//keep going as long as we have a possibility to move
//and we haven't reached the end yet
while (!possibleSteps.isEmpty()&& (!possibleSteps.getLast().equals(exit)))
{
Tile x = possibleSteps.removeLast();
x.setMarked(); //walked on that square
steps.add(x); //walk to that square
System.out.println("Walked to the square " + x);
//now figure out where you can walk from this square
nextMoves(x);
try {
Thread.currentThread().sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
if (possibleSteps.getLast().equals(exit)){
steps.push(possibleSteps.removeLast());
System.out.println("made it from entrance to exit");
System.out.println(steps.toString());
return true;
}
else
{ JOptionPane.showMessageDialog(null,"sorry can't reach the exit");
return false;
}
}
This isn't a breadth first search - this is depth first.
There are 2 places that are obviously depth first
you remove from the end of the frontier (possibleTiles), not the beginning.
you do not have a traversal hierarchy (parent to child) to rebuild the traversal path, only a single "path" with tiles. Therefore, it is depth first.
Things to change:
instead of LinkedList, change that to Dictionary. The key of the dictionary will be the tile, and the value will be the tile's parent. Use this to reconstruct your final path.
your "moveNext" function is probably right. Make sure it is pushing to the end of the List.
do not pop (getLast()) from PossibleSteps. PossibleSteps should be a First-In-First-Out queue. Retrieve the first Tile of PossibleSteps (this will explore the tiles closest to the start first).
Things to improve:
instead of using Tile to track exploration, use a Set (call it ExploredTile) where you put tiles that you have traversed)
use a Queue instead of List.
Some C#/Pseudo Code (cuz I'm not at an IDE)
Dictionary<Tile,Tile> path = ...;
Queue<Tile> frontier = ...;
Set<Tile> explored = ...;
path[start] = null;
while(frontier.Count > 0){
var tile = frontier.Dequeue();
if(explored.Contains(tile)){
continue;
}
explored.add(tile);
if (Tile == Exit){
rebuildPath(Exit);
}else{
for (Tile t in Tile.neighbours){
if (!explored.Contains(t)){
frontier.Enqueue(t);
path[t] = tile; // Set the path hierarchy
}
}
}
}
RebuildPath
LinkedList<Tile> finalPath = ...;
Tile parent = path[exitTile];
finalPath.push(exitTile);
while(parent != null){
finalPath.push(parent);
parent = path[parent];
}
finalPath.reverse();
Hopefully I got it right... :)

wrong setting an arraylist

I want to ask a question about arraylist. In my program I defined an arraylist and an user defined object. The problem is when I want to add an object to this arraylist, it adds object, but next time when I give the different values to user object, it sets values of the old object that I added before to the new one. I mean, for example I have 13 in my old object and the new one is 14, it makes the old one 14. I couldn't find a solution for this. I'M working on a drawing program. I'm posting some parts of the code.
public class Tester extends JPanel implements MouseMotionListener, MouseListener {
ArrayList<Lines> array = new ArrayList<Lines>();
Lines l1;
...
public Tester(){
l1 = new Lines();
l1.point1 = new Point();
l1.point2 = new Point();
l1.denklem = new int[3];
And thi is how I add object into arraylist
else if(lineci == true){
if(mouseclicks == 0){
l1.point1.x = e.getX();
l1.point1.y = e.getY();
statusBar.setText( String.format( "Clicked at [%d, %d]",
e.getX(), e.getY() ) );
mouseclicks++;
starter = false;
}
else if(mouseclicks == 1){
l1.point2.x = e.getX();
l1.point2.y = e.getY();
statusBar.setText( String.format( "Clicked at [%d, %d]",
e.getX(), e.getY() ) );
mouseclicks = 0;
int a = l1.point2.y - l1.point1.y;
int b = l1.point1.x - l1.point2.x;
int c = (l1.point2.x * l1.point1.y) - (l1.point1.x * l1.point2.y);
l1.denklem[0] = a;
l1.denklem[1] = b;
l1.denklem[2] = c;
array.add(l1);
// array3.add(l1);
repaint();
}
}
When I click mouse, if lineci is true, it draws a line according to points. I get always last lines when I print elements of arraylist. If I draw 10 lines, 10 elements of the arraylist are the same. It never keeps old values inside arraylist. By the way boolean starter is not important. I just forget to remove it.
You are simply adding the same object repeatedly to the list. Each addition to the list is merely adding a reference to the same object: l1. Hence, when you update the state of l1, the old "object" also appears to have changed.
To rectify this you need to create a new instance of Lines for each addition you wish to perform.
You're referencing the original l1 object, so it will modify the original l1 object--whether or not it's in the list.
If you want to create a new object, create a new object.
i think your l1 reference is still pointing the previous object added to the arrayList , you should override the reference as soon as you add the object to the arraylist .
by either
l1 = new Lines();
or
l1 = null;
Use l1.clone() before changing the value inside if you want to keep all values same but one and the class is clonable or create a new object which is better for your case.

Categories

Resources