So I'm writing a program to make a robot explore a maze to find a specified cavern. There are four types of Cells: Cavern, Start, Wall, and Passage. Robots can only move to a passage or a cavern. I implemented my method so that a robot can't move to a visited cell or go out of bounds. But once it moves to a cell where there isn't a valid adjacent cell, the program stops. So how do I make my robot backtrace to where there is a valid cell? I'm using a recursion for this. Below is my code. Any help will be great. Thank you!
public void explore (Cell cavern, Maze maze) throws InterruptedException {
// for debugging
System.out.println(row + " " + col);
System.out.println(cavern.getRow() + " " + cavern.getCol());
System.out.println(visited.toString());
TimeUnit.MILLISECONDS.sleep(10); // delay program
//base case
if (row == cavern.getRow() && col == cavern.getCol()) {
foundCavern = true;
return;
}
else {
// move right
if ((col+1) < maze.getNumCols() && !visited.contains(maze.getCell(row, col+1)) && (maze.getCell(row, col+1).isPassage() || maze.getCell(row, col+1).isCavern())) {
visited.add(maze.getCell(row, col+1));
setRobotLocation(row,col+1);
explore(cavern, maze);
}
// move down
else if ((row+1) < maze.getNumRows() && !visited.contains(maze.getCell(row+1, col)) && (maze.getCell(row+1, col).isPassage() || maze.getCell(row+1, col).isCavern())) {
visited.add(maze.getCell(row+1, col));
setRobotLocation(row+1,col);
explore(cavern, maze);
}
// move left
else if ((col-1) >= 0 && !visited.contains(maze.getCell(row, col-1)) && (maze.getCell(row, col-1).isPassage() || maze.getCell(row, col-1).isCavern())) {
visited.add(maze.getCell(row, col-1));
setRobotLocation(row,col-1);
explore(cavern, maze);
}
// move up
else if ((row-1) >= 0 && !visited.contains(maze.getCell(row-1, col)) && (maze.getCell(row-1, col).isPassage() || maze.getCell(row-1, col).isCavern())) {
visited.add(maze.getCell(row-1, col));
setRobotLocation(row-1,col);
explore(cavern, maze);
}
else {
foundCavern = false;
return;
}
}
}
I think you are in the right direction.
What I think you should do is keep checking for all direction unless you found the cavern.
Right now you are checking for only one direction in each iteration because of the else clause. So when the call to explore returns you can't keep checking a different direction and essentially you don't backtrack.
If you make your explore function return a Boolean field indicating whether you reached the cavern changing your code like this may work:
// move right
if ... // your condition to check if moving right is possible and you didn't visit
// code to move right
found = explore()
//move down if didn't find cavern
if (!found) // and moving down is possible and didn't visit
// code to move down
found = explore()
// keep checking other directions
Related
So I've searched on overflow for about 30min now and I can't find anything that I think would've helped me. So here I am.
I'm doing a maze algorithm basically and working on this recursive method but idk if what I'm doing works. Sounds stupid ik...it won't display the maze with the arrows(directions) on my displayer for one, two apparently I need to "pass a coordinate next to it (say, the above cell, for example)" and I need to code it this way somehow. Here is my method.
private boolean findPath(char[][] maze, int r, int c) {
// Base Case #1: If Current Cell == 'H'
if(maze[r][c] == 'H') {
return true; // Stop Recursion, Conclusion: Path Found from 'G' to 'H'
}
// Base Case #2: If Current Cell == '#' (Wall)
else if(maze[r][c] == '#') {
return false; // Stop Recursion, Conclusion: Path NOT Found
}
// Base Case #3: If Current Cell == '.' (Blocked Path/Dead End)
else if(maze[r][c] == '.') {
return false; // Stop Recursion, Conclusion: Path NOT Found
}
// Recursive Cases:
if(maze[r][c] == ' ') { // Check If Current Cell == ' '
maze[r][c] = '^'; // If Yes, Place Symbol
findPath(maze, r-1, c); // Recursively Go Up
// code here ???? add missing code??? HELP MEEEE
if(maze[r][c] == 'H') { // Check If We Have Reached The End
return true; // If Yes, Return True
}
}
// Viewing Purposes
for (int x = 0; x < maze.length; x++) { // Go Through Rows
display.newLine(); // Viewing Purposes
for (int y = 0; y < maze[x].length; y++) { // Go Through Columns
display.writeC(maze[x][y]); // Viewing Purposes
}
}
return false; // If End of Method Reached. Therefore, Hansel Hasn't Been Found Yet
}
A very important thing to do when writing a recursive method is if the method returns a value, make use of the return value returned by the recursive call. If you are not making use of it, like you are doing here:
if(maze[r][c] == ' ') { // Check If Current Cell == ' '
maze[r][c] = '^'; // If Yes, Place Symbol
// you are discarding the return value here!
findPath(maze, r-1, c); // Recursively Go Up
Then you are probably going something wrong.
The return value of findPath tells you whether a path can be found from a certain position. You can make use of this information. If a path can be found by going up, then you know you've marked the right path, and can return true. If a path can't be found by going up, then you know that going up isn't the right direction, so you check another direction. If after checking all directions, and you still haven't found a path, you know that you are on the wrong path, so you set maze[r][c] back to ' ' and return false.
private static boolean findPath(char[][] maze, int r, int c) {
// Base Case #1: If Current Cell == 'H'
if(maze[r][c] == 'H') {
return true; // Stop Recursion, Conclusion: Path Found from 'G' to 'H'
}
// (Your other base cases are covered by the last "return false" statement)
// Recursive Cases:
if(maze[r][c] == ' ') { // Check If Current Cell == ' '
maze[r][c] = '^';
if (findPath(maze, r-1, c)) {
return true;
}
maze[r][c] = 'v';
if (findPath(maze, r+1, c)) {
return true;
}
maze[r][c] = '>';
if (findPath(maze, r, c+1)) {
return true;
}
maze[r][c] = '<';
if (findPath(maze, r, c-1)) {
return true;
}
maze[r][c] = ' ';
}
return false; // If End of Method Reached. Therefore, Hansel Hasn't Been Found Yet
}
Example usage:
char[][] maze = {
"#####H#####".toCharArray(),
"#G# # # # #".toCharArray(),
"# # # # # #".toCharArray(),
"# # # # # #".toCharArray(),
"# #".toCharArray(),
"###########".toCharArray(),
};
boolean hasPath = findPath(maze, 1, 1);
System.out.println(hasPath);
for (char[] row : maze) {
System.out.println(row);
}
Output:
true
#####H#####
#v# #^# # #
#v# #^# # #
#v# #^# # #
#>>>>^ #
###########
Note that my code assumes that the maze has walls or H all around. If your maze doesn't have that, you need to check that c and r are within the bounds of the 2D array first. If they are out of bounds, you should return false.
I've been attempting to follow some pseudocode, namely
https://www.geeksforgeeks.org/a-search-algorithm/ &
http://mat.uab.cat/~alseda/MasterOpt/AStar-Algorithm.pdf ,
to create an A star pathfinding algorithm for a four-directional tile/cell-based map with obstacles. I understand the concept, and I could definitely explain how it should work in words/images, but putting it into code is proving challenging. For a few days now whenever I've run my program it crashes and I have to manually stop the application. I believe this is due to an infinite while-loop. This confuses me because the program should exit the while-loop once its found the end destination, but obviously that isn't working. This is the code which I think should make it exit the while-loop once the destination is found:
if (n.getX() == end.getX() && n.getY() == end.getY()) {
currentNode = n;
break;
}
I hope this isn't too much code to put in this post, but this is the meat of my algorithm with comments on what I think each piece is doing:
public void attempt2() {
double leastF = Integer.MAX_VALUE;
// Initializes the starting Node and, in the beginning, currentNode is the same
// as the starting node
Node start = new Node(r.getCell());
Node currentNode = start;
start.setParent(start);
closed.add(start);
open.add(start);
start.setEnd(destinationCP);
start.calculateH();
start.isCalculatedH();
while (open.size() > 0) {
// Finds the node with the least F score on Open
for (Node n : open) {
// Calculates the H-score if it hasn't been already
if (n.haveNotCalculatedH()) {
n.setEnd(destinationCP);
n.calculateH();
n.isCalculatedH();
}
// Calculates the g-score, with 1 being the value/distance of a cell
n.setAdditiveDistanceG(n.getAdditiveDistanceG() + 1);
// Calculates the F-score
n.calculateF();
// Actually finds the least F score in the open list and sets currentNode to the
// node with the least F
if (n.getTotalCostF() < leastF) {
leastF = n.getTotalCostF();
currentNode = n;
}
}
//
// Creates easy-access variables for the x and y values of the node on open with
// the least F score
int thisX = currentNode.getX();
int thisY = currentNode.getY();
// if this cell (cell in open w least F) is the end destination cell, stop the calculations
if (thisX == end.getX() && thisY == end.getY()) {
break;
}
//
// Generate 1-4 successors if Robot can pass into the cell
if (World.getCell(thisX + 1, thisY).canEnter(r)) {
successors.add(new Node(World.getCell(thisX + 1, thisY)));
}
if (World.getCell(thisX, thisY + 1).canEnter(r)) {
successors.add(new Node(World.getCell(thisX, thisY + 1)));
}
if (World.getCell(thisX - 1, thisY).canEnter(r)) {
successors.add(new Node(World.getCell(thisX - 1, thisY)));
}
if (World.getCell(thisX, thisY - 1).canEnter(r)) {
successors.add(new Node(World.getCell(thisX, thisY - 1)));
}
//
/*
* Loops through each of the 1-4 neighbors to currentNode (I need to add in to
* erase & add to open/closed every one in here so its empty before new ones are
* generated
*/
for (Node n : successors) {
double successorCurrentCost = 0;
// if this successor is already in the closed list, skip doing all the code for
// this node and add this successor's parent (currentNode) to the closed list
if (isInClosed(n)) {
continue;
}
// if this is the goal/end node, exit the 'successors' for-loop. the step that
// follows this (exiting the loop) is that this particular node/successor is
// added to the closed list
if (n.getX() == end.getX() && n.getY() == end.getY()) {
currentNode = n;
break;
}
//
// Calculates the F cost for each successor to currentNode
if (n.haveNotCalculatedH()) {
n.setEnd(destinationCP);
n.calculateH();
n.isCalculatedH();
}
n.setAdditiveDistanceG(n.getAdditiveDistanceG() + currentNode.getAdditiveDistanceG());
n.calculateF();
successorCurrentCost = n.getTotalCostF();
//
if (!isInOpen(n) && n.getAdditiveDistanceG() > successorCurrentCost
|| n.getAdditiveDistanceG() > successorCurrentCost && !isInClosed(n)) {
open.add(n);
if (n.haveNotCalculatedH()) {
n.setEnd(destinationCP);
n.calculateH();
n.isCalculatedH();
}
} else if (isInClosed(n) && n.getAdditiveDistanceG() <= successorCurrentCost) {
successorCurrentCost = n.getAdditiveDistanceG();
n.setParent(currentNode);
} else {
successorCurrentCost = n.getAdditiveDistanceG();
n.setParent(currentNode);
}
if (isInClosed(n)) {
closed.remove(n);
open.add(n);
}
}
closed.add(currentNode);
if (thisX == end.getX() && thisY == end.getY()) {
break;
}
}
if (currentNode.getMyCell() != this.destinationCP) {
System.out.println("ERROR: open list is empty");
return;
} else {
createPath();
}
}
I am aware that there are a few things that should be changed in this code, however I am most concerned about the while loop never being terminated. Any other comments about my code are appreciated but definitely not necessary.
My program has been experiencing an odd bug i just cannot seem to fix. In summary, whenever I switch two if statements, only the one coming afte tthe first one will work.
Example: (The ball will successfully collide with the top, but won't with the bottom.)
public void update()
{
if(enemies != null)
{
for(Enemy e : enemies)
if(c.getBall().getHitbox().intersects(e.getHitbox()))
{
if(e.collidedBottom())
c.getBall().setSpeedY((c.getBall().ballSpeed));
if(e.collidedTop())
c.getBall().setSpeedY(-(c.getBall().ballSpeed));
deadEnemies.add(e);
}
enemies.removeAll(deadEnemies);
}
Now if i switched the positions of the two if statements like so;
if(e.collidedTop())
c.getBall().setSpeedY(-(c.getBall().ballSpeed));
if(e.collidedBottom())
c.getBall().setSpeedY((c.getBall().ballSpeed));
The ball will successfully react the way it should when colliding with the bottom, but not with the top. Here is the two collision methods:
public boolean collidedBottom()
{
if(c.getBall().getBallY() <= enemy.y + enemy.height &&
c.getBall().getBallX() + c.getBall().getHitbox().width >= enemy.x
&& c.getBall().getBallX() <= enemy.x + enemy.width)
return true;
return false;
}
public boolean collidedTop()
{
if(c.getBall().getBallY() + c.getBall().getHitbox().height >= enemy.y &&
c.getBall().getBallX() + c.getBall().getHitbox().width >= enemy.x
&& c.getBall().getBallX() <= enemy.x + enemy.width)
return true;
return false;
}
Enemy refers to the current focused block in the loop.
I currently developing a Chess Game in java using the eclipse IDE,
I Have all the movement of the pieces down apart from the Pawn which is the hardest because the Pawn has to be able to make to different moves
The Pawn should be able to move twice at the start and then only one after that
Currently I have set the pawn to only move twice but I am stuck on getting the rest of the logic to work
I have been working with the idea of an if/else statement
I could use some help writing it
Here is the Code so far for the Pawn and I have included comment's for your use
Update to Problem it is only with the black pawn is not moving right I was able to set the white one the right way but not the black I dont know why it doesnt work
//Pawn Movement
private boolean isValidPawnMove(int sourceRow, int sourceColumn, int targetRow, int targetColumn) {
boolean isValid = false;
if( isTargetLocationFree() ){
if( sourceColumn == targetColumn){
if( sourcePiece.getColor() == Piece.COLOR_WHITE ){
// White Pawn
if( sourceRow+1 == targetRow || sourceRow == 1 && targetRow == 3){//Pawns can move to at the start then only 1 after that
isValid = true;
}else{
isValid = false;
}
}
else{
// Black Pawn
if( sourceRow-1 == targetRow || sourceRow == -1 && targetRow == -3){
isValid = true;
}else{
isValid = false;
}
}
}else{
//If you try to move left or right into a different Column
isValid = false;
}
//Take square occupied by an opponent’s piece, which is diagonally in front
}else if( isTargetLocationCaptureable() ){
if( sourceColumn+1 == targetColumn || sourceColumn-1 == targetColumn){
//One column to the right or left
if( sourcePiece.getColor() == Piece.COLOR_WHITE ){
//White Piece
if( sourceRow+1 == targetRow ){
//Move one up
isValid = true;
}else{
//Not moving one up
isValid = false;
}
}else{
//Black Piece
if( sourceRow-1 == targetRow ){
//Move one down
isValid = true;
}else{
//Not moving one down
isValid = false;
}
}
}else{
//Not one column to the left or right
isValid = false;
}
}
return isValid;
}
Thanks for any help you can provide
I think the easiest solution is to explicitly check the source and target rows as white pawns can only move two forward from the second rank, so your logic becomes (for white):
if( sourceRow+1 == targetRow || sourceRow == 2 && targetRow == 4) {
Obviously you will also need to check that (sourceColumn, 3) is also empty.
In your class for the pawns, you can have an instance boolean, say,
boolean hasMoved
and initially make it false. If this boolean is false, then the pawn can move one OR two units. Whenever you move a pawn, you check this boolean, and if it's false, set it to true after moving it. That should work out.
In chess a pawn can choose if it wants to go one or two squares forward if it has never moved and therefore still stands on the second row. You have to ask, if it's on the second row and the two squares in front of it are empty and not only the "target location".
if( sourcePiece.getColor() == Piece.COLOR_WHITE ){
// White Pawn
if( sourceRow+1 == targetRow ){
isValid = true;
} else if (sourceRow+2 == targetRow && sourceRow == ROW_2) {
if ((isFreeSquare(sourceColumn+1) && isFreeSquare(sourceColumn+2)) {
isValid = true;
} else {
isValid = false;
}
} else {
isValid = false;
}
}
else{
// Black Pawn
...
}
You can leave the isFreeSquare(sourceColumn+2) code because you asked it with isTargetLocationFree() already. For black you have to ask if the pawn is still on ROW_7.
i am developing a chessgame and have been able to made my Pawn piece move both one and two squares forward but i am having difficulty preventing it from moving two squares forward after its first move. Please i will be grateful if someone can help me with a logic or idea on how to implement it.
Here is my code:
private boolean isValidPawnMove(int sourceRow, int sourceColumn, int targetRow, int
targetColumn) {
boolean isValid = false;
if(isTargetLocationFree()){
if(sourceColumn == targetColumn){
//same column
if(sourcePiece.getColor() == Piece.YELLOW_COLOR){
//yellow
if(sourceRow + 1 == targetRow ){
//move one up
isValid = true;
}else if(sourceRow + 2 == targetRow ){
isValid = true;
}else{
//not moving one up
isValid = false;
}
}else{
//brown
if(sourceRow - 1 == targetRow){
//move one down
isValid = true;
}else if(sourceRow - 2 == targetRow){
isValid = true;
}else{
//not moving one down
isValid = false;
}
}
}else{
//not the same column
isValid = false;
}
}else if(isTargetLocationCaptureable()){
if(sourceColumn + 1 == targetColumn || sourceColumn - 1 == targetColumn ){
//one column to the right or left
if(sourcePiece.getColor() == Piece.YELLOW_COLOR){
//yellow
if(sourceRow + 1 == targetRow){
//move one up
isValid = true;
}else{
//not moving one up
isValid = false;
}
}else{
//brown
if(sourceRow - 1 == targetRow ){
//move one down
isValid = true;
}else{
//not moving one down
isValid = false;
}
}
}else{
//One column to the left or right
isValid = false;
}
}
return isValid;
}
private boolean isTargetLocationCaptureable(){
if(targetPiece == null){
return false;
}else if( targetPiece.getColor() != sourcePiece.getColor()){
return true;
}else{
return false;
}
}
private boolean isTargetLocationFree(){
return targetPiece == null;
}
I won't write the code for you, but your condition should be based on the sourceRow.
If your source row is the original row the pawn started on (second or seventh row, depending on which player), only then can the pawn move two rows.
For yellow pieces, just check that sourceRow is 1 (or 2, depending on how you are numbering rows):
...
}else if(sourceRow == 1 && targetRow == 3){
// double-row first move
isValid = true;
}else{
...
Do a similar thing for the brown pieces.
Assuming you have a class for your Pawn, then you should create a field in the class called firstmove or numMoves. This would be set to true or 0 respectively, and after the first move set to false or numMoves++. Then, in your validity checker, you can say
if p.firstMove
//accept 1 or 2
else
//accept only 1
I'd do it one of two ways:
Check if the pawn is in the row it starts in. If it is, allow 2-space moves, otherwise, don't.
Give each pawn a boolean that is false if it hasn't been moved, and true if it has, than check that. (Wouldn't recommend this as it doesn't seem you're making Pawn an object so it'd be very difficult to implement)