How to try something until available object is found - java

The title is probably a bit confusing, but I don't really know how to explain this. I have a list of objects, in this case, locations, and those locations can be occupied by a player. If the selected location is already occupied, how can I try to find a new location, and continue this until non-occupied location is found?
I already know that there are 20 locations, I could manually check each and every one of those locations and see if it's occupied, but is there a better way to do this?
Here is a snippet of my code.
List<Location> spawnList = arena.getManager().getRandomSpawns(); // Returns a list of possible locations
Location random = spawnList.get(new Random().nextInt(spawnList.size())); // Selects a random location from the list
if (random.isOccupied()) {
/* Location is occupied, find another one from the list, and continue doing this until non-occupied location is found */
}
Sorry if you didn't understand, I don't know a good way of explaining this.

List<Location> spawnList = arena.getManager().getRandomSpawns();
Location random;
Random r = new Random();
do {
random = spawnList.get(r.nextInt(spawnList.size()))
} while(random.isOccupied());
This will fail if all locations are occupied, you should check this before.

You can choose one of two ways:
Push - when a location becomes available , notify that it is now available. (By calling a method, for example).
Polling: Something like you are doing now. It is possible to hold a collection of available locations, when a location becomes available it is added to the collection. you can wait for the list to have values. I would suggest A blockig queue:

The trivial approach would be to randomize a location in a loop until one is found:
List<Location> spawnList = arena.getManager().getRandomSpawns(); // Returns a list of possible locations
Location random = spawnList.get(new Random().nextInt(spawnList.size())); // Selects a random location from the list
while (random.isOccupied()) {
random = spawnList.get(new Random().nextInt(spawnList.size()));
}
The problem here is that this may take a very long time if most of the locations are already occupied.
A "safer" approach, which promises the same order of performance regardless of the percentage of pre-occupied locations could be to shuffle the list of locations, and then simply iterate through it:
List<Location> spawnList = new LinkedList<Location>(arena.getManager().getRandomSpawns());
Location random = null;
for (Location loc : spawnList) {
if (!loc.isOccupied()) {
random = loc;
}
}

You can declare a flag to check if candidate Location is found, and using while - loop to generate random Location, like,
Location random = null;
boolean foundLocation = false;
while(!foundLocation)
{
random = spawnList.get(new Random().nextInt(spawnList.size()));
if(!random.isOccupied())
{
foundLocation = true;
}
}
Note: Here has a assumption that there is at least one Location in the Location List, which is not occupied. If all of the Locations are occupied. Then the above code can not be used. It will be in infinite loop. We'd better check if at least one Location is not occupied in the List first.

Instead of stochastically probing until you hit an empty spot, you should
first collect all available locations and then
pick a random free location.
List<Integer> freeLocations = new ArrayList<>();
for (int i = 0; i < spawnList.size(); i++)
if (!spawnList.get(i).isOccupied) freeLocations.add(i);
Location random =
spawnList.get(freeLocations.get(rnd.nextInt(freeLocations.size()));

Related

check unrepeated random number

I have a method that return a number:
public String getNum()
{
Random random = new Random();
return random.nextInt(1000) + "";
}
And I have this method that stores an object
public void store(User user)
{
String str = getNum();
user.setIdCode(str);
for (User users: userList)
{
if(users.getId() == user.getId())
{
user.setIdCode(getNum);
}
}
}
if Id is excited than re-set the id. This is for sure checks if the id exists the first time but how about the second time that id is set. there would be a possibility of repeating the same number. At the same time we cant go in infinite loop.
What you can try is add a few random numbers in a Set and then use them separately whenever you want.
What I mean by it is that if you're trying to get a random number every time, you can just store some random numbers at once and then retrieve them one by one when you need them.
In code, put this in some method called storeRandomNumbers() and call it in the beginning of your program. Also, declare the Set as a global variable.
You can also make a separate variable to keep track of how many random numbers have been used up and use it to retrieve the next random number from the Set.
The code would look something like this (makes changes according to your needs):
Random rand = new Random();
Set<Integer> uniqueRandoms = new HashSet<>();
while (uniqueRandoms.size()<10){
uniqueRandoms.add(rand.nextInt(11));
}
for (Integer i : uniqueRandoms){
// System.out.print(i+" ");
// retrieve them from here and use them whenever you want
}
Edit:
Yes as #Andreas gave the link to suggest, you don't compare Strings with ==, rather you use the equals() method.

Rush Hour solver running into infinite loop any non-trivial puzzle

So I'm writing a Rush Hour solver in Java, which is meant to be able to solve the configurations here. However, even the simplest puzzle from that page results in the solver running infinitely and eventually running out of memory. I'm using a breadth first search to work my way through all possible moves arising from each board state (using a HashSet to ensure I'm not repeating myself), and mapping each state to the move that got it there so I can backtrack through them later.
The thing is, I've tried it with more trivial puzzles that I've come up with myself, and it's able to solve them (albeit slowly).
Is there anything blatantly wrong with how I'm approaching this problem? I can put up some code from the relevant classes as well if I need to, but I've tested them pretty thoroughly and I'm pretty sure the problem lies somewhere in the code below. My gut says it's something to do with the HashSet and making sure I'm not repeating myself (since the Queue's size regularly reaches the hundred thousands).
Any suggestions?
// Start at the original configuration
queue.add(originalBoard);
// We add this to our map, but getting here did not require a move, so we use
// a dummy move as a placeholder move
previous.put(originalBoard, new Move(-1, -1, "up"));
// Breadth first search through all possible configurations
while(!queue.isEmpty()) {
// Dequeue next board and make sure it is unique
Board currentBoard = queue.poll();
if (currentBoard == null) continue;
if (seen.contains(currentBoard)) continue;
seen.add(currentBoard);
// Check if we've won
if (currentBoard.hasWon()) {
System.out.println("We won!");
currentBoard.renderBoard();
return solved(currentBoard);
}
// Get a list of all possible moves for the current board state
ArrayList<Move> possibleMoves = currentBoard.allPossibleMoves();
// Check if one of these moves is the winning move
for (Move move : possibleMoves) {
Board newBoard = move.execute(currentBoard);
// We don't need to enqueue boards we've already seen
if (seen.contains(newBoard)) continue;
queue.add(newBoard);
// Map this board to the move that got it there
previous.put(newBoard, move);
}
}
As requested, here are my initialisations of the HashSet (they're class level variables):
private static HashSet<Board> seen = new HashSet<>();
And my Board.equals() method:
#Override
public boolean equals (Object b) {
Board otherBoard = (Board) b;
boolean equal = false;
if (this.M == otherBoard.getM() && this.N == otherBoard.getN()) {
equal = true;
// Each board has an ArrayList of Car objects, and boards are only
// considered equal if they contain the exact same cars
for (Car car : this.cars) {
if (otherBoard.getCar(car.getPosition()) == null) {
equal = false;
}
}
}
System.out.println(equal);
return equal;
}
You must implement Board.hashCode() to override the default Object-based version, in such a way that, per its contract, any two equal Board objects have the same hash code. If you do not, then your seen set does not in fact accomplish anything at all for you.
On another issue, I suspect that the way you're checking the boards' cars is not fully correct. If it works the way I think it does, it would consider these two boards to be equal:
. = empty space
* = part of a car
......
.**.*.
....*.
.*....
.*.**.
......
......
.*..**
.*....
......
.**.*.
....*.

Spawn random objects without overlapping (Java)?

I'm developing a game in Java, and part of it requires that objects spawn at the top of the screen and proceed to fall down. I have three objects that can possibly spawn, and three possible x coordinates for them to spawn at, all stored in an array called xCoordinate[].
One of the objects is of a class called Enemy, which inherits a class I have called FallingThings. In the FallingThings class, I have methods to generate new objects, my enemy method is below:
public static void generateNewEnemy() {
xIndexEnemyOld = xIndexEnemy;
xIndexEnemy = new Random().nextInt(3);
if (delayTimer == 0) {
while (xIndexEnemy == xIndexEnemyOld) {
xIndexEnemy = new Random().nextInt(3);
}
}
if (xIndexEnemy != xIndexMoney && xIndexEnemy != xIndexFriend) {
Enemy enemy = new Enemy(xCoordinates[xIndexEnemy]);
enemies.add((Enemy) enemy);
} else {
generateNewEnemy();
}
}
xIndexEnemy represents the index of the xCoordinates array.
xIndexMoney and xIndexFriend are the indexes of the xCoordinates array for the two other objects (the comparisons with these values ensures that one object does not spawn directly on top of another).
The delayTimer variable represents the random delay between when new objects spawn, which was set earlier in my main class.
I store each instance of an Enemy object in an ArrayList.
Everything works except for the fact that sometimes, an object will spawn over itself (for example, the delay is 0, so two enemy objects spawn directly on top of each other, and proceed to fall down at the same speed at the same time).
I've been trying to crack this for the past two days, but I understand exactly why my code right now isn't working properly. I even tried implementing collision detection to check if another object already exists in the space, but that didn't work either.
I would be extremely grateful for any suggestions and ideas.
EDIT2
It seems that you still don't understand the problem with your function. It was addressed in the other answer but I'll try to make it more clear.
public static void generateNewEnemy() {
xIndexEnemyOld = xIndexEnemy;
This is just wrong. You can't set the Old index without having actually used a new index yet.
xIndexEnemy = new Random().nextInt(3);
if (delayTimer == 0) {
while (xIndexEnemy == xIndexEnemyOld) {
xIndexEnemy = new Random().nextInt(3);
}
}
This is actually ok. You're generating an index until you get one that is different. It may not be the most elegant of solutions but it does the job.
if (xIndexEnemy != xIndexMoney && xIndexEnemy != xIndexFriend) {
Enemy enemy = new Enemy(xCoordinates[xIndexEnemy]);
enemies.add((Enemy) enemy);
} else {
generateNewEnemy();
}
}
This is your problem (along with setting the Old index back there). Not only do you have to generate an index thats different from the Old index, it must also be different from IndexMoney and IndexFriend.
Now, what happens if, for example, IndexOld = 0, IndexMoney = 1 and IndexFriend = 2? You have to generate an index that's different from 0, so you get (again, for instance) 1. IndexMoney is 1 too, so the condition will fail and you do a recursive call. (Why do you even have a recursive call?)
OldIndex was 0, and now in the next call you're setting it to 1. So IndexOld = 1, IndexMoney = 1 and IndexFriend = 2. Do you see the problem now? The overlapped index is now wrong. And the new index can only be 0 no matter how many recursive calls it takes.
You're shooting yourself in the foot more than once. The recursive call does not result in an infinite loop (stack overflow actually) because you're changing the Old index. (Which, again is in the wrong place)
That if condition is making it so the newly generated index cannot overlap ANY of the previous indexes. From what you said before it's not what you want.
You can simplify your function like this,
public static void generateNewEnemy() {
xIndexEnemy = new Random().nextInt(3);
if (delayTimer == 0) {
while (xIndexEnemy == xIndexEnemyOld) {
xIndexEnemy = new Random().nextInt(3);
}
}
Enemy enemy = new Enemy(xCoordinates[xIndexEnemy]);
enemies.add((Enemy) enemy);
xIndexEnemyOld = xIndexEnemy;
// Now that you used the new index you can store it as the Old one
}
Will it work? It will certainly avoid overlapping when the delayTimer is 0 but I don't know the rest of your code (nor do I want to) and what do you do. It's you who should know.
About my suggestions, they were alternatives for how to generate the index you wanted. I was assuming you would know how to fit them in your code, but you're still free to try them after you've fixed the actual problem.
Original Answer
Here's one suggestion.
One thing you could do is to have these enemies "borrow" elements from the array. Say you have an array,
ArrayList< Float > coordinates = new ArrayList< Float >();
// Add the coordinates you want ...
You can select one of the indexes as you're doing, but use the maximum size of the array instead and then remove the element that you choose. By doing that you are removing one of the index options.
int nextIndex = new Random().nextInt( coordinates.size() );
float xCoordinate = coordinates.get( nextIndex );
coordinates.remove( nextIndex ); // Remove the coordinate
Later, when you're done with the value (say, when enough time has passed, or the enemy dies) you can put it back into the array.
coordinates.add( xCoordinate );
Now the value is available again and you don't have to bother with checking indexes.
Well, this is the general idea for my suggestion. You will have to adapt it to make it work the way you need, specifically when you place the value back into the array as I don't know where in your code you can do that.
EDIT:
Another alternative is, you keep the array that you previously had. No need to remove values from it or anything.
When you want to get a new coordinate create an extra array with only the values that are available, that is the values that won't overlap other objects.
...
if (delayTimer == 0) {
ArrayList< Integer > availableIndexes = new ArrayList< Integer >();
for ( int i = 0; i < 3; ++i ) {
if ( i != xIndexEnemyOld ) {
availableIndexes.add( i );
}
}
int selectedIndex = new Random().nextInt( availableIndexes.size() );
xIndexEnemy = availableIndexes.get( selectedIndex );
}
// Else no need to use the array
else {
xIndexEnemy = new Random().nextInt( 3 );
}
...
And now you're sure that the index you're getting should be different, so no need to check if it overlaps.
The downside is that you have to create this extra array, but it makes your conditions simpler.
(I'm keeping the "new Random()" from your code but other answers/comments refer that you should use a single instance, remember that)
As I see, if delay == 0 all is good, but if not, you have a chance to generate new enemy with the same index. Maybe you want to call return; if delayTimer != 0?
UPDATED
Look what you have in such case:
OldEnemyIndex = 1
NewEnemyIndex = random(3) -> 1
DelayTimer = 2
Then you do not pass to your if statement, then in the next if all is ok, if your enemy has no the same index with money or something else, so you create new enemy with the same index as previous

DouglasPeuckerSimplifier usage

I am attempting to reduce shape files generated from OSM path data. I am using the DouglasPeuckerSimplifier implementation from VTS.
I want to build up a geojson of the routemap for a specific GTFS (general transit feed spec). I cant just use the set straight from the map as it's too heavy, I end up with multi-megabyte size json files.
My code looks like this, I have incuded the loop to populate the input just to give you some confidence that I have a valid input array. What I am querying is really just the last 3 lines, and the general concept of taking a path from OSM and reducing the number of points in it, which I thought was exactly what Douglas-Peucker was all about.
ArrayList<Geometry> points = new ArrayList <Geometry>();
GeometryFactory gf= new GeometryFactory();
for (Object sh : shape_points){
double thisShapeLat=((Shapes)sh).getshapePtLat();
double thisShapeLon=((Shapes)sh).getshapePtLon();
// void identical consecutive points
if (lastShapeLat == thisShapeLat && lastShapeLon == thisShapeLon) continue;
lastShapeLat = thisShapeLat;
lastShapeLon = thisShapeLon;
Coordinate coord= new Coordinate(thisShapeLon,thisShapeLat);
// System.err.println("added coord="+coord);
points.add(gf.createPoint(coord));
}
Geometry[] points_ar = (Geometry [])points.toArray(new Geometry[points.size()]);
GeometryCollection geometries = new GeometryCollection(points_ar, gf);
DouglasPeuckerSimplifier simplifier = new DouglasPeuckerSimplifier(geometries);
simplifier.setDistanceTolerance(0.00001);
Geometry result=simplifier.getResultGeometry();
No matter what value I set for the tolerance, I get the same points in (points) as out (result). It's not doing anything at all.
I have also called simplify() as a static, with the same result, i.e nothing.
You need to use a LineString not GeometryCollection for tyhe parameters to simplify.
Coordinate list2[] = new Coordinate[coords.size()];
list2 = coords.toArray(list2);
CoordinateArraySequence cas=new CoordinateArraySequence(list2);
LineString ls = new LineString(cas,gf);
Geometry result=DouglasPeuckerSimplifier.simplify(ls,0.001);

Recursive method in Java seems to just "goto" the first line of the method instead of actually go into the next call

I am creating a factory that makes rooms, and it is passed an int of steps and a start room and it is supposed to do a step, build a room, and then call itself with one fewer step and the new room as the start room. The problem is that it never ends. In the debugger, I can see that it's calling itself, which creates another method call in memory that actually has one fewer step, but then the execution line goes to the top of the current method call! so it never actually completes the new call. As though it were putting the new call into heap instead of stack, and then never actually getting to it.
Code:
#Override
public Room place(Level level, int cycles, Room start_room,
Direction direction, int shop, int exit, LevelFactoryReport report) throws Exception
{
Room room = null;
if(cycles < 1)
{
return start_room;
}
else
{
report.addEvent("--Placer step--");
report.addEvent("Steps remaining: "+cycles);
room = this.Step(level, start_room, direction, shop, exit, report);
if(room == null)
{
cycles = 0;
report.addEvent("Step returned a null room (probably because it ran into an existing room). Ending cycle.");
}
}
return place(level, (cycles--), room, direction, (shop--), (exit--), report);
}
In the code above, it goes through the various implementation, then gets to the new call for place(), and then it just creates a new instance of place(), but doesn't step into it, and instead the execution line goes back to "Room room = start_room" of the original call. It does this infinitely, with the cycles always at its initial value of 4, and more and more instances of place() filling up the stack. I looked into the new instances, and all of them actually do have a "cycles" value of 3.
The strange thing is, each iteration that actually runs is being run on the next room, so when it goes back to the top, it is going back to the top passing the next room. But why is it creating a new instance of place() (with the new room AND the new cycles value of 3), and then re-running the old place() using the new room BUT NOT the new cycles value of 3?
You're using cycles--, shop-- to decrement the variables. However while x-- does decrement x, it does not return the decremented value. The return value of the expression x-- is the old value of x. Use x-1 instead of x--. (Or --x if you must, but there is no point in mutating the variable here).
try replacing this line:
return place(level, (cycles--), room, direction, (shop--), (exit--), report);
with this line:
return place(level, (--cycles), room, direction, (--shop), (--exit), report);
Maybe you can find some more help here

Categories

Resources