I am developing GUI for algorithm which finds path on grid GUI. Starting point is always 0,0 and is where starfish is located! For calculating destination I am using Random class which generates random x, y depending on grid bounds(of course). I opened debugger debugger results and see that my algorithm became infinite loop and is not able to find path, because at some point starfish starts moving left-right again and again, simply waste of moves. How can i solve this bug, I have hard time on solving it, can anyone help?
private Queue<Point> findPath(Rectangle[][] matrix, Point destPoint) {
Point move = null;
var dir = new ArrayList<Point>();
dir.add(new Point(1, 0)); // right
dir.add(new Point(0, 1)); // down
dir.add(new Point(-1, 0)); // left
dir.add(new Point(0, -1)); // up
Point start = new Point(0, 0);
var tmpPath = new ArrayDeque<Point>();
var path = new ArrayDeque<Point>();
tmpPath.add(new Point(0, 0));
while (!tmpPath.isEmpty()) {
for (int dc = 0; dc < dir.size(); dc++) {
move = new Point(start.x() + dir.get(dc).x(), start.y() + dir.get(dc).y());
if (!move.isValid(matrix[0].length, matrix.length)) {
continue;
}
if (matrix[move.y()][move.x()].getFill() != Color.MAGENTA) {
start = move;
tmpPath.add(move);
path.add(tmpPath.poll());
System.out.println(path.peek());
if (path.getLast().equals(destPoint)) {
path.poll();
return path;
}
break;
}
}
}
return null;
}
Explanation: my method adds all walked path in a queue which if path is found(or when starfish will arrive at destination) will be returned! Color.MAGENTA is considered as wall, if i click on rectangle on my GUI, rectangles color will be assigned to MAGENTA.
I am stuck at solving the bug! Project Repository: https://github.com/gchapidze/maze-solver
This is a bit of a traditional pathfinding approach. You need to keep track of all of your possible paths.
You start at point A, aka path 0. You start by "opening" the path. You go through all of the valid moves for each move you add a new path.
Now you grab the next viable path, then you "open" that path by removing it from the list, and adding a new path for each possible move.
The type of pathfinding algorithm you use determines how you find the next viable path.
In your case, your paths are just a Deque of Points, so you can keep track of your paths with a.
I would select the next path using the length. That way you'll perform a depth first search, which is basically what you're doing.
Point start = new Point(0, 0);
//var tmpPath = new ArrayDeque<Point>(); obsolete
var path = new ArrayDeque<Point>();
//tmpPath.add(new Point(0, 0)); obsolete
Deque<Deque<Point>> availablePaths = new ArrayDeque<>();
//initialize space
path.add(start);
availablePaths.add(path);
while (!availablePaths.isEmpty()) {
//removes the last element, which will be a depth first search.
path = availablePaths.removeLast();
//check for success.
if (path.getLast().equals(destPoint)) {
//path.poll();
return path;
}
//open the current path.
start = path.getLast();
for (int dc = 0; dc < dir.size(); dc++) {
move = new Point(start.x() + dir.get(dc).x(), start.y() + dir.get(dc).y());
if (!move.isValid(matrix[0].length, matrix.length)) {
continue;
}
if(path.contains(move){
continue; //this prevents the path from walking on itself.
}
if (matrix[move.y()][move.x()].getFill() != Color.MAGENTA) {
//start = move; obsolete.
// tmpPath.add(move); obsolete
var newPath = new ArrayDeque(path);
newPath.add(move);
//moved the check for complete to the start of the loop!
availablePaths.add(newPath);
//break; don't break when you find a possible path.
//get all possible paths, and explore in the next loop.
}
}
}
I've updated this to have an example using your code. I've commented out the code of yours I would remove or change and added the other code.
The main difference is there are a lot of paths that it keeps track of. This prevents the paths from overlapping. Also if a path becomes a dead end, it will not ruin the algorithm. If there is a path, this should find it.
Related
Situation: The tank has a Shot() method and a code that checks if the bullet hits the obstacle after being fired. The bullet flies up the X-coordinate. The do...while loop checks how far the bullet can travel without obstacles. After that, the animation of the bullet flight itself takes place through TranslateTransition. And the last loop goes through all the game obstacles and checks for contact through intersects and, if successful, removes the obstacle.
do {
y = (int) (bulletObj.getImageBullet().getTranslateX() + register) / PlayField.BRICK_SIZE;
x = (int) bulletObj.getImageBullet().getTranslateY() / PlayField.BRICK_SIZE;
line = LevelData.LevelOne[x][y];
register += 1;
} while (line.equals("0"));
System.out.println(System.currentTimeMillis()); // 1643642047472 ms.
bulletTranslate = new TranslateTransition();
bulletTranslate.setFromX(bulletObj.getImageBullet().getTranslateX());
bulletTranslate.setToX(bulletObj.getImageBullet().getTranslateX() + register - 18);
bulletTranslate.setNode(bulletObj.getImageBullet());
bulletTranslate.setDuration(Duration.millis(register)); // Let register = 300 мs.
bulletTranslate.play();
System.out.println(System.currentTimeMillis()); // 1643642047474 ms.
bulletObj.getImageBullet().setTranslateX(bulletObj.getImageBullet().getTranslateX() + register - 18);
for (GameObject platform: PlayField.platforms) {
if (platform.getImage().getBoundsInParent().intersects(bulletObj.getImageBullet().getBoundsInParent()))
{
System.out.println(System.currentTimeMillis()); // 1643642047474 ms.
tankRoot.getChildren().remove(platform.getImage());
PlayField.platforms.remove(platform);
LevelData.LevelOne[x][y] = "0";
break;
}
}
Everything works as expected, but there is only one problem.
The problem is that the obstacle objects are removed faster than the animation bullets pass.
And it is necessary that after contact they are deleted simultaneously.
How to solve the problem?
Before the shot
After the shot, the object disappeared during the flight of the bullet
P.S Sorry, I used google translate.
As #jewelsea suggested, you need to supply a minimal reproducible example to get some proper help.
Having said that, based on the code you provided, below is my initial analysis. Note that this is a rough analysis by reading the code.. so everything is just an assumption ;)
The first part of the code (do-while) is to determine the distance the bullet needs to travel (determining the value of register)
The second part is to initiate animation of bullet to translate from its current position (using the register value).
The third part is to set the final translate value to the bullet and the final part is to check if the bounds are intersected and delete the node.
I think the thrid part (below line of code) is not needed and could be the cause for your issue. You are updating the value to the end value and immediately checking if it is intersected.. which will be true and will delete instantly.
bulletObj.getImageBullet().setTranslateX(bulletObj.getImageBullet().getTranslateX() + register - 18);
Try moving your 'for' loop code to onFinished of translation. Something like..
bulletTranslate = new TranslateTransition();
bulletTranslate.setFromX(bulletObj.getImageBullet().getTranslateX());
bulletTranslate.setToX(bulletObj.getImageBullet().getTranslateX() + register - 18);
bulletTranslate.setNode(bulletObj.getImageBullet());
bulletTranslate.setDuration(Duration.millis(register)); // Let register = 300 мs.
bulletTranslate.setOnFinished(e->{
for (GameObject platform: PlayField.platforms) {
if (platform.getImage().getBoundsInParent().intersects(bulletObj.getImageBullet().getBoundsInParent()))
{
tankRoot.getChildren().remove(platform.getImage());
PlayField.platforms.remove(platform);
LevelData.LevelOne[x][y] = "0";
break;
}
}
});
bulletTranslate.play();
I have a game template which has two classes as its levels.
The game has a player, enemies, treasure chests, and walls.
When I run the program, it loads Level 1, successfully. I have designed the code so if a certain condition is met, Level 2 must load. Now, Level 2 loads. But certain elements from Level 1 remain on screen, ignoring certain elements from Level 2.
To be exact:
From Level 2, it loads the player's initial coordinates, the treasure chests, and the final destination, ignoring the enemies and the walls.
It brings with itself from Level 1 to Level 2: the walls, the treasure chests, and the enemies. The latter two are in a frozen state. They don't function.
//Instance variables:
private int lvlCount = 1;
// Game() method
public Game(Stage stage) {
this.score = new SimpleIntegerProperty(0);
this.mobs = new ArrayList<>();
this.hitBoxes = new ArrayList<>();
this.treasure = new ArrayList<>();
this.newLevel = new ArrayList<>();
this.enemyCount = 0;
bg();
loadLevel(1);
living();
stuff();
mob2();
finalText();
stage.setOnShown(e -> this.run());
}
//The switch for loading the Levels:
void loadLevel(int in) {
switch (in) {
case 1:
this.level = new Level1();
break;
case 2:
this.level = new Level2(); // placeholder for second level
break;
}
hitBoxes.clear(); // clear the list of hitboxes
mobs.clear(); // remove any existing old mobs
this.getChildren().addAll(hitBoxes); // add all the hitboxes for the walls to the scene
background.setImage(level.getImage()); // get the background image
hitBoxes.addAll(level.getWalls()); // get all the wall hitboxes
enemyCount = level.getEnemyCount(); // get the enemy count from the level
chestCount = level.treasureCount(); // get the treasure count from the level
this.initTreasure(chestCount); // add the treasure chests for the level
this.initMobs(enemyCount); // initialize our mobs
lvlCount = level.lvlCount();
this.initLevel(lvlCount);
}
//The collision check:
private void collisionCheck() {if (r.getFill().equals(Color.WHITE)){lvlCount--;}
//Main Game loop method:
private void play() {
AnimationTimer gameLoop = new AnimationTimer() {
public void handle(long arg0) {
for (int i = 0; i < mobs.size(); i++) {
MOB m = mobs.get(i);
if (m instanceof Enemy) {
((Enemy) m).moveCheck(arg0);
}
}
collisionCheck();
if (lvlCount == 0){
loadLevel(2);} //The condition that loads Level 2
gameLoop.start();
}
Please help me to figure out how I can load Level 2, successfully.
Please note that it does not produce any errors in the console. So, I am having a very hard time figuring out where the problem lies.
I only pasted the code that I thought is necessary to achieve my goal. Here you can see the full code, for veiwing purposes only, no download necessary:
http://paste.mooc.fi/955c6d30
Thank you very much in advance.
I have to develop a Dijkstra Alogirthm in Java and I have a question to Dijkstra in a circle.
So for a Tree or a normal graph without a loop it works.
So I have a Status White means not found, gray = found but not dealt with and black means done.
So when I have a loop I tryed a if (next.status == Node.Black) but then he didn't found all nodes.
So the question is, how can I add a loop detection and found all nodes?
Thanks for help and tips
best regards
witar7
PS: the if (next.equals(startNode) was only an idea to stop the loop.
public void populateDijkstraFrom(Node startNode) {
this.resetState();
PriorityQueue<Node> distanceQueue = new PriorityQueue<Node>();
for (int x = 0; x < nodes.size(); x++) { // for each node
nodes.get(x).distance = Double.POSITIVE_INFINITY; // set distance to inf
nodes.get(x).predecessor = null; // delete existing predecessors
}
startNode.distance = 0.0; // set distance from startNode to zero
startNode.status = Node.GRAY; // mark startNode as active
Node current = startNode;
distanceQueue.add(current); // add startNode to prio queue
while (!distanceQueue.isEmpty()) { // while contains elements
current = distanceQueue.poll(); // set current to head of queue
current.status = Node.BLACK; // mark head as settled
for (Node next : current.getAdjacentNodes() ) { // get all adjacent nodes
if (next.equals(startNode)) {
break;
}
next.status = Node.GRAY;
// stopExecutionUntilSignal();// mark status as found
distanceQueue.add(next);
if (distanceQueue.contains(next)) {
if (next.distance > current.distance + current.getWeight(next)) { // if the found distance is smaller then the existing one
next.predecessor = current; // change distance in node
next.distance = current.distance + current.getWeight(next); // set predecessor
}
}
}
}
this.clearMarks();
}
PS: the if (next.equals(startNode) was only an idea to stop the loop.
There's no need to do this, your while condition will terminate anyway when it can't find anymore unvisited adjacent nodes. You just have to check whether current visited node status is BLACK and if yes, don't add it to the queue (it's already been visited before).
P.S.: I don' think you need GRAY status, just BLACK or WHITE. Deal with the node right away, no need to delay.
Am i implementing this correctly? I'm having issues with the direction my character is in, yet the state manager is working correctly. I'm unsure whether this is the problem. I have my player facing right automatically when constructed yet he faces left.
I have two arrays for both right and left animation inside my assets manager, The original images are facing right, i declare that array and then flip the same image on the left array. Does the left array override the right?
int idleRIndex = 1;
TextureRegion[] idleRight = new TextureRegion[3];
for (int i = 0; i < idleRight.length; i++)
{
idleRight[i] = rubenSprite.findRegion("iframe" + idleRIndex);
idleRIndex++;
}
rubenIdleRight = new Animation(0.2f, idleRight);
int idleLIndex = 1;
TextureRegion[] idleLeft = new TextureRegion[3];
for (int i = 0; i < idleLeft.length; i++)
{
idleLeft[i] = rubenSprite.findRegion("iframe" + idleLIndex);
idleLeft[i].flip(true, false);
idleLIndex++;
}
rubenIdleLeft = new Animation(0.2f, idleLeft);
It seems so after a test. The findRegion("iframe" + idleLIndex) and for the right are returning the same object reference. So I think the flip for the left will also affect the right. Maybe you can create a new TextureRegion object from the atlas. It shouldn't be much overhead. Try:
idleLeft[i] = new TextureRegion(rubenSprite.findRegion("iframe" + idleLIndex));
Then the flip shouldn't affect the right anymore.
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... :)