Removing Actors does not delet all Actors - java

I am currently trying to save special Actors so i can put them on a map again if the old map get loaded. Therefor i want to put them into a HashMap<String, ArrayList<Monster>> monsterAtMap and remove them from there Stages. So i am trying this:
private void saveMonsters() {
if (this.screen.figureStage.getActors().size == 0)
return;
ArrayList<Monster> monsters = new ArrayList<Monster>();
for (Actor a : this.screen.figureStage.getActors()) {
a.remove();
}
Gdx.app.log("Figurstage size", ""+ this.screen.figureStage.getActors().size);
this.monsterAtMap.put(this.currentMap.name, monsters);
}
As start. But i noticed that it does not delete all. It does just delete 10 thats all. I do log the size of it befor and after the deleting. It's current 21 (20Monsters and 1 Character) after delete the size is 11.I also added this this.screen.figureStage.getRoot().removeActor(a); but this does not change anything.
Any Idea to that?
[EDIT] I wrote a workaround so my idea is working but the general idea that should work isnt possible because the .remove() does not always delete the Actor in anyway?! The workaround does look like this:
private void saveMonsters() {
this.chara = this.screen.character;
if (this.screen.figureStage.getActors().size == 0)
return;
ArrayList<Monster> monsters = new ArrayList<Monster>();
for (Actor a : this.screen.figureStage.getActors()) {
if (a.getClass() == Monster.class)
monsters.add((Monster) a);
}
this.screen.figureStage.clear();
this.screen.figureStage.addActor(chara);
this.monsterAtMap.put(this.currentMap.name, monsters);
}
The .clear()does work correct.

Deleting objects from a container while iterating over that container is always fraught with issues and complications, and I think you're running into some of these issues with the Stage's list of actors. The Stage code tries to use SnapshotArray to hide some of these issues, but its not clear to me that it will work with the code you've written.
One way to avoid this would be to loop through getActors() once and copy the actors into the monsters array, then loop through the monsters array and remove the actors from the Stage (or invoke figureStage.getRoot().clearChildren()). This should prevent you from iterating over a list that you're modifying.
Alternatively, look at how Group.clearChildren() is implemented (it uses an explicit integer index in the array of children, and not an iterator over the Array, and avoid some of the issues).

Related

Including new elements in for:each

Before I start my question, i'd like to mention that i DID read up some other topics and i tried around a bit but im just really confused atm so i figured i'd just ask.
So what i wanna do is use for each through a Set and within that for each, add elements to that set and also iterate through those.
The solution I found elsewhere was the following:
for(Object obj : new HashSet<Object>(oldSet))
I tried that, however I keep missing some of the last elements i'd like to match so im not really sure if this is the right approach in the first place?
To be specific, this is basically what my code looks like:
for(Position pos : new HashSet<Position>(oldSet){
for(Delta delta : deltas){
if(board.getTokenAt(pos.plus(delta).equals(initial){
hitList.add(pos.plus(delta);
oldSet.add(pos.plus(delta);
}
}
oldSet.remove(pos);
}
Again, I'd just like to know if my approach is wrong or there must be an error elsewhere in my code so i know what to look at.
Thanks beforehand!
You can't really add to a data structure while iterating over it, that is almost guaranteed to have unexpected results.
However, there is a simple enough solution to your issue. Just process each item recursively when you find that it needs to be added, and add it to a separate List. At the end of iteration, add everything in the List to the main Set. This avoids the issue of adding during iteration while still allowing you to to process the newly added items.
It would look something like this:
List<Position> toAdd = new LinkedList<>();
for(Position pos : oldSet){
for(Delta delta : deltas){
addIfGoodAndRecurse(pos, delta, toAdd);
}
}
And then you can use this helper method to add the item if it meets your conditions and also recursively process added items. Note you will need to change the method signature to pass in your board, initial, and hitList if they are local variables. I didn't know their types or whether they were global variables or fields, so I couldn't really add them in the example.
private void addIfGoodAndRecurse(Position pos, Delta delta, List<Position> toAdd) {
Position toCheck = pos.plus(delta);
if(board.getTokenAt(toCheck.equals(initial))) {
hitList.add(toCheck);
toAdd.add(toCheck);
for (Delta recursionDelta : deltas) {
addIfGoodAndRecurse(toCheck, recursionDelta, toAdd);
}
}
}
I don't have your code, so I can't test this. The idea should work fine, but you may need to make slight modifications.
You can iterate through new elements added to a list that you're iterating if you add them to the end of the list and iterate through it using an index and the get() method, not through an Iterator. You can also use the Set as you are doing now, but only to make sure you only add unique items to your collection.
List<Position> list = new ArrayList<>(oldSet);
for (int i = 0; i < list.length; ++i) { // NB list.length could be different each time
Position pos = list.get(i);
for(Delta delta : deltas){
if(board.getTokenAt(pos.plus(delta).equals(initial){
hitList.add(pos.plus(delta));
if (oldSet.add(pos.plus(delta))) // Check if it already exists in the list
list.add(pos.plus(delta));
}
}
oldSet.remove(pos);
}

I can't get to modify my static variable in java

You give a grid (4x4 here). you need to find out the total no of unique paths from (0,0) to (4,4). main() call a function pathify for this. It finds the possible "next steps" and calls it again. When (4,4) is reached noOfPaths++; is supposed to execute. This doesn't happen and I can't find the problem.
import java.util.ArrayList;
public class NoOfPaths {
static int xRows = 4;
static int yColumns = 4;
static int noOfPaths = 0;
/*A robot is located in the upper-left corner of a 4×4 grid.
* The robot can move either up, down, left, or right,
* but cannot go to the same location twice.
* The robot is trying to reach the lower-right corner of the grid.
* Your task is to find out the number of unique ways to reach the destination.
**/
static ArrayList validNeighbours (int x,int y, ArrayList visited) {
ArrayList valid = new ArrayList();
if((x+1 <= xRows) && !visited.contains(((x+1)*10)+y) ) {
valid.add(((x+1)*10)+y);
}
if((x-1 >= 0) && !visited.contains(((x-1)*10)+y) ) {
valid.add(((x-1)*10)+y);
}
if((y+1 <= yColumns) && !visited.contains(x*10+y+1) ) {
valid.add(x*10+y+1);
}
if((y-1 >= 0) && !visited.contains(x*10+y-1) ) {
valid.add(x*10+y-1);
}
return valid;
}
static void pathify(int x,int y, ArrayList alreadyVisited) {
if(x == xRows && y == yColumns) {
noOfPaths++;
} else {
alreadyVisited.add(x*10+y);
ArrayList callAgain = new ArrayList();
callAgain = validNeighbours(x,y,alreadyVisited);
for (int t=0,temp; t<callAgain.size(); t++) {
temp=(int) callAgain.get(t);
pathify(temp/10, temp%10, alreadyVisited);
}
}
}
public static void main(String[] args) {
ArrayList alreadyVisited = new ArrayList();
pathify(0, 0, alreadyVisited);
System.out.println(noOfPaths);
}
}
The error is in how you're handling alreadyVisited. The first time pathify is called, this list will contain only the initial square (0,0), which is fine. Here's the important part of your code:
for (int t=0,temp; t<callAgain.size(); t++) {
temp=(int) callAgain.get(t);
pathify(temp/10, temp%10, alreadyVisited);
}
You've found the neighbors of the initial cell. Your code will pick the first neighbor; then it will find paths starting with that neighbor, and the recursive calls to pathify will add cells to alreadyVisited.
Now, after all the recursive calls come back, you're ready to find cells starting with the second neighbor of the initial cell. But you have a problem: alreadyVisited still has all the cells it's collected from the paths it found starting with the second neighbor. So you won't find all possible paths starting with the second neighbor; you won't find any path that includes any cell in any path you've previously found. This isn't what you want, since you only want to avoid visiting the same cell in each path--you don't want to avoid visiting the same cell in all your previous paths. (I simplified this a little bit. In reality, the problem will start occurring deeper down the recursive stack, and you won't even find all the paths beginning with the first neighbor.)
When implementing a recursive algorithm, I've found that it's generally a bad idea to keep an intermediate data structure that is shared by recursive invocations that will be modified by those invocations. In this case, that's the list alreadyVisited. The problem is that when an invocation deeper down the stack modifies the structure, this affects invocations further up, because they will see the modifications after the deeper invocations return, which is basically data they need changing underneath them. (I'm not talking about a collection that is used to hold a list of results, if the list is basically write-only.) The way to avoid it here is that instead of adding to alreadyVisited, you could create a clone of this list and then add to it. That way, a deeper invocation can be sure that it's not impacting the shallower invocations by changing their data. That is, instead of
alreadyVisited.add(x*10+y);
write
alreadyVisited = [make a copy of alreadyVisited];
alreadyVisited.add(x*10+y);
The add will modify a new list, not the list that other invocations are using. (Personally, I'd declare a new variable such as newAlreadyVisited, since I don't really like modifying parameters, for readability reasons.)
This may seem inefficient. It will definitely use more memory (although the memory should be garbage-collectible pretty quickly). But trying to share a data structure between recursive invocations is very, very difficult to do correctly. It can be done if you're very careful about cleaning up the changes and restoring the structure to what it was when the method began. That might be necessary if the structure is something like a large tree, making it unfeasible to copy for every invocation. But it can take a lot of skill to make things work.
EDIT: I tested it and it appears to work: 12 if xRows=yColumns=2, 8512 if both are 4 (is that correct?). Another approach: instead of copying the list, I tried
alreadyVisited.remove((Object)(x*10+y));
at the end of the method ((Object) is needed so that Java doesn't think you're removing at an index) and that gave me the same results. If you do that, you'll make sure that alreadyVisited is the same when pathify returns as it was when it started. But I want to emphasize that I don't recommend this "cleanup" approach unless you really know what you're doing.

Only one instance of object from ListIterator moves

I am working on a game project. So far so good, but i just stuck on ome basic thing and i cant find a solution and make it work properly. I decided to come here and ask you ppl of suggestions.
PROBLEM:
First, I had 2 levels with one bot in each of them. Later i noticed, that only the bot in the first level updates its behavior. If i only load the next level, the bots update method is not called and i dotn know why.
I printed the ListIterator in to the console and the bot is there. But when i iterate trough the list in the main update method it seem it doesnt call the objects update method in it. I can still move with player and take diamonds from the map which both of them are also the part of the list so those methods are called. I can provide you with some smaller code blocks if you need to get some info on some specific things if i have them or not, just leave it in the comment. Here are some major blocks which in my opinion arent working proper.
If only the bot in the first level moves, i made another 4 bots to it. Only the first instance of Bot moves. All other items of the same class do nothing.
0.Declaration:
private ArrayList<AbstractObject> supp = new ArrayList<AbstractObject>();
private ListIterator<AbstractObject> objects=supp.listIterator();
1.Main update method:
public void update() {
resetList(); //sets the cursor at begining
if(menu.getChoice()==-1){
menu.update();
}
else if(menu.getChoice()==2)
System.exit(0);
else if(menu.getChoice()==0){
if(currentLevel>lvlm.getLevel() || currentLevel<lvlm.getLevel()){
clearList(); //remove all items in the list
init(lvlm.getLevelPath());
currentLevel=lvlm.getLevel();
}
while(objects.hasNext()){
objects.next().update(); //calls all updates from each object in the list
}
}
}
Update method in the Bot Class:
public void update() {
movingCount++;
switch(getFacing()){
case 2:
moved=true;
setFacing(2);
setVectorX(-0.5);
break;
case 3:
moved=true;
setFacing(3);
setVectorX(0.5);
break;
case 0:
case 1:
setVectorX(0);
moved=false;
}
if(movingCount>=200){
setFacing(randInt(0,3));
movingCount=0;
}
moveOnX(); //updates pos
moveOnY(); //updates pos
getAnimationL().runAnimation();
getAnimationR().runAnimation();
}
EDIT_1:
Ok so the bots only in the first line in the file move.
LEVEL 1
P-player
B-bots
D-diamonds
1-walls
1111111 B B
1P D 1
11111 1
1F 1 B
1111111
B B B
Any ideas why only first line bots move? Other line objects works properly.
Iterators only work once. You seem to be initialising the iterator once and then expecting it to work each time update is called.
Three options:
(Bad) reinitialise the iterator in update:
objects = supp.iterator();
(Better) don't use an iterator; use a for loop:
for (AbstractObject obj: supp) {
obj.update();
}
(Best) use a stream:
supp.stream().forEach(AbstractObject::update);
If the update could potentially remove an item then your best option is to remove them after you've processed the list. For example, create a boolean method in AbstractObject called shouldBeRemoved. Then you can use the following:
supp.removeIf(AbstractObject::shouldBeRemoved);
After you have used one of the three methods above to process the list.

Understanding ArrayList in conjunction with Objects

I am experimenting with creating my own particle effect (simple) when the mouse is clicked on the screen. I dont think the language is relevant (but I'm still learning)
Is my logic as follows correct:
Each time I click, the particles get added to an ArrayList to be iterated through to increment size, color and opacity
Now this would still work if I had multiple clicks on the screen because each set of particles would simple be added to the ArrayList...
however, this does not seem efficient because when the first particle blast has ended, it is no longer needed in the ArrayList and should not take up memory
Could someone help me with the logic?
And would animating a PNG series be more efficient than dynamically creating your own particle effect?
Thanks
PS - I'm not creating a game/application...just experimenting with concepts I'm learning
You should delete expired particle effects. It would probably be more suitable to use a LinkedList for this, as you can iterate over it and remove any expired elements in constant time. ArrayList and LinkedList both implement the List interface, so if you use a List in your code it can accept either. Be careful to avoid indexed access on linked lists as it is slow; whenever possible use an iterator or for-each loop.
Iterator<ParticleBlast> itr = particles.iterator();
while(itr.hasNext())
{
ParticleBlast next = itr.next();
if(next.hasExpired()) itr.remove();
}
How about making the particles expire?
class Particle
{
static final long EXPIRE_TIME = 2000; // 2 seconds
final long expireTime;
public Particle ()
{
expireTime = System.currentTimeMillis() + EXPIRE_TIME;
}
}
SortedSet<Particle> particles = new TreeSet<>(new Comparator<Particle>{
public compare (Particle a, Particle b)
{
if(a.expireTime < b.expireTime)
return -1;
if(a.expireTime > b.expireTime)
return 1;
return a.hashCode() - b.hashCode();
}
});
Then you can add the particles to the 'particles' set using the add method. On an interval, like each time the view is updated, remove all particles from the front of the set which have expired (their expireTime field is less than System.currentTimeMillis)

Code explanation

Given these set of instructons am I right in saying that :
1) Here I declare two list objects called : audiobytes and lines
List<Byte> audioBytes;
List<Line2D.Double> lines;
2) Here I empty the lists created in step 1 , and update the component --> repaint()
public void resetWaveform() {
audioBytes = new ArrayList<Byte>();
lines = new ArrayList<Line2D.Double>();
repaint();
}
3) Here unfortunately I don't understand:
public void createWaveForm() {
// what does it mean ????
if (audioBytes.size() == 0) {
return;
}
}
What puzzles me is that the method is called createWaveForm but actually does nothing..is
that correct?
1) Correct
2) Almost. I would create lists in #1 and then call clear() on each. Simply because dealing with nulls is a nightmare
3) It does nothing only when list of audio bytes has no data. I guess there is more code after the if statement
You did not empty the lists in step 2, you threw them away and created 2 new lists. This is not the same if something is still holding a reference to the old lists. You should use the clear() method instead.
As far as your code in part 3, yes it really does nothing (except needlessly check the size), assuming you haven't cut anything out. If there was something else there, then all that segment of code is saying is "if there is no data, return (don't execute anything more)".

Categories