I am writing a simple minesweeper, but I can't find a way to reveal the adjacent tiles properly. If a tile is blank, it is revealed and then the algorithm reveals all adjacent blank tiles. However I would like to reveal a layer of non blank tiles as well, just like the real minesweeper.
Here is my code:
void revealAdjCells(Tile [][] t,int x, int y) {
if (!checkBounds(x,y)) {
return; // check for bounds
}
if ((t[x][y].getNeighbours() == 0) && (!t[x][y].getVisibleState())) { // NO neighbours and not revealed
t[x][y].setVisibleState(true); // reveal tile
revealAdjCells(t,x+1,y); // recursion, reveal adjacent tiles
revealAdjCells(t,x-1,y);
revealAdjCells(t,x,y-1);
revealAdjCells(t,x,y+1);
}
else
{
return;
}
}
getNeighbours() returns the amount of bombs that surround a nearby tile (horizontal,vertical,diagonal) and getVisibleState() returns a boolean that indicates whether a tile has been revealed or not.
Things that I have tried:
1) Removing getVisibleState() from if condition (terrible idea, leads obviously to stack overflow).
2) Checking bounds (x-1,x+1,y+1,y-1) and then revealing the tiles accordingly (doesn't work, getVisibleState() won't let the statement execute, because the tile that is examined by the recursion is already revealed).
So... yeah... I am stuck and I can't find a solution. Any algorithmic help is appreciated.
Your code is close, but you're not revealing the tile if t[x][y].getNeighbours() != 0 and you should be doing this. Perhaps something like:
void revealAdjCells(Tile [][] t,int x, int y) {
// if out of bounds **or** if already revealed, return
if (!checkBounds(x,y) || t[x][y].getVisibleState()) {
return;
}
t[x][y].setVisibleState(true); // reveal tile **here **
// do recursion only if no neighbors
if (t[x][y].getNeighbours() == 0) {
// t[x][y].setVisibleState(true); // not **here**
revealAdjCells(t,x+1,y);
revealAdjCells(t,x-1,y);
revealAdjCells(t,x,y-1);
revealAdjCells(t,x,y+1);
} else {
return;
}
}
Related
I am working on a minesweeper assignment right now. And I used recursion to implement the function that removes an empty area. However my program always runs into eroor. The error message states:
a Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
And this is my code, I know it looks messy, apologize in advance. I would appreciate any help!
public void removeEmptyRegion(int x, int y){ //note: uses recursive decomposition
if (x < 0 || x > width-1 || y < 0 || y > length-1) {
return; // check for bounds
}else if(tiles[y][x].getClicked()){ //first time activated this method
//checks the tiles surround it
removeEmptyRegion(x,y+1);//up
removeEmptyRegion(x,y-1);//down
removeEmptyRegion(x+1,y);//left
removeEmptyRegion(x-1,y);//right
removeEmptyRegion(x-1, y+1); //up-left
removeEmptyRegion(x+1, y+1); //up-right
removeEmptyRegion(x-1,y-1); //down-left
removeEmptyRegion(x+1,y-1); //down-right
}
else if(!(tiles[y][x].getValue() == -1) && tiles[y][x].getClicked() == false ) {
//check: -1 indicates it is a bomb
if(tiles[y][x].getValue() == 0) {
tiles[y][x].clickTile();
//chain reaction
removeEmptyRegion(x,y+1);//up
removeEmptyRegion(x,y-1);//down
removeEmptyRegion(x+1,y);//left
removeEmptyRegion(x-1,y);//right
removeEmptyRegion(x-1, y+1); //up-left
removeEmptyRegion(x+1, y+1); //up-right
removeEmptyRegion(x-1,y-1); //down-left
removeEmptyRegion(x+1,y-1); //down-right
return;
}else { //stops if the tile is a numbered tile
tiles[y][x].clickTile();
return;
}
} else {
return;
}
}
You check each possible direction from each tile recursively.
Say your code is at 0,0 and now it checks the tile above. Now it's at 0,1. Then, from 0,1 your code checks a few directions, including down. Now it's back at 0,0. This repeats infinitely, causing a stack overflow.
I suggest using something called memoization.
Create a boolean[][] with the same dimensions as your minesweeper grid. When you check a square, mark boolean[y][x]=true.
At the top of your method where you check if you are out of bounds, use if (boolean[y][x]) to check if you've already checked there.
Here is a link to a video I recorded of my issue: https://sendvid.com/rjpi6vnw
You'll notice that initially, I start at tile (0, 0) then after moving my character up and down multiple times, the screen will only go up to (1,0). So I lost a whole row of playable map. I only lose part of the map when my screen adjusts itself. You'll understand what I mean in a moment. I have a class called Player, and in it I have methods called moveRight(), moveLeft(), moveUp(), and moveDown(). I'm excluding all useless classes and methods in order to not waste your time. Here are my moveDown() and moveUp() methods:
public void moveUp(){
locY1 -= defaultMoveAmount;
if(viewShouldMoveVertically(locX1, locY1) == true){ //locX1 and locY1 refers to the player's bounds location as set by setBounds()
Display.uni.moveMapDown(defaultMoveAmount); //Display.uni just means in the Display class
}
}
public void moveDown(){
locY1 += defaultMoveAmount;
if(viewShouldMoveVertically(locX1, locY1) == true){
Display.uni.moveMapUp(defaultMoveAmount); //defaultMoveAmount is the # of pixels the player moves each time the program updates
}
}
So I have KeyListeners that decide when these methods are called. The viewShouldMoveVertically() method is as follows:
public boolean viewShouldMoveVertically(int X1, int Y1){
if(Y1 < screenCenterY){ //screenCenterY is the number of vertical pixels on my screen/2
return false;
}
return true;
}
The moveMapUp() or moveMapDown() method is then called in the Display class:
int backgroundX1 = 0;
int backgroundY1 = 0;
public void moveMapUp(int moveAmt){
backgroundY1 -= moveAmt;
background.setBounds(backgroundX1, backgroundY1, backgroundX2 , backgroundY2);
}
public void moveMapDown(int moveAmt){
backgroundY1 += moveAmt;
background.setBounds(backgroundX1, backgroundY1, backgroundX2 , backgroundY2);
}
So if you can't view the video at the link I posted, I'll describe the issue. When my character moves close to the edge of the map, I obviously wouldn't want the camera to show areas off of the map. So the camera stops, but my character may continue walking up to the border of the map. If my character is within 540 pixels of the top of the map, the camera won't move(I'm running on a 1920x1080 display). This is intended. When I move the character more than 540 pixels from the top of the map, the camera will now move with the player since he's in the center of the screen. But the issue is that IF and ONLY IF the camera ends up moving away from the top of the map, then I now lose exactly "defaultMoveAmount" pixels from the viewable area when I return to the top again. I can't seem to figure out how to fix this issue. Now, a little more you may end up wanting to know: I have the same issue moving horizontally as I have moving vertically. It is set up in the same way, so there was no point in making you guys read extra code. When viewing the video at the link, I have to click on the play button at the bottom left, or else it tries to make me add an extension to Chrome or something. The solution to my program's issue may end up being quite simple, but I just can't seem to figure it out. I ended up getting sick of it and decided getting a little help would be better than giving up for now. I am a beginner to programming, as I've only had 1 year of programming experience from an AP Compute Science class. I'm sure you may see a few things that seem dumb, and I welcome any suggestions or comments you may have, but please be aware that I am fairly new to this stuff. Primarily motion. And finally, I did not post a compile-able section of code due to things such as the graphics that are required. While I'm on here, if you have any suggestions or good references for figuring out whether a character is within an area in a large tile-like map, such as a door that can be opened, it would be appreciated.
I have solved the issue. Here is my updated viewShouldMoveVertically() method:
public boolean viewShouldMoveVertically(int X1, int Y1){
if( Y1 <= screenCenterY && previousY1 > screenCenterY ){ //if just touched highest camera point from below
return true;
}else if( Y1 >= mapPixelTotalY-screenCenterY && previousY1 < mapPixelTotalY-screenCenterY ){ //if just touched lowest camera point from above
return true;
} else if( Y1 <= screenCenterY){ //if just touched highest camera point from above or is just above
return false;
} else if( Y1 >= mapPixelTotalY-screenCenterY ){ //If touched lowest camera point from below or is just below
return false;
}
return true;
}
It turns out that when the player touches the "highest camera point from below" or the "highest camera point from above", different results are required. If touching the highest camera view from below, the camera still needs to move upwards just one more time. But when touching that same point from above, the camera shouldn't move, since the player was coming from an area that the camera wouldn't be vertically moving at.You can see my solution in the code. Also, I added a "previousY1" that records what the last Y1 value was, so that I can tell whether the player came from above or below.
I'm experimenting with a simple Break Out clone to get an understanding of Game States, Game Loops etc.
One of the things I'm having difficulty with is the collision detection.
I have a GameItem class that is extended by Ball, Brick and Paddle. In this class I have a getBounds() method to return the bounding Rectangle of the object, a collision(GameItem item) method to determine whether 2 GameItem objects have collided as well as methods for hitTop(), hitBottom(), hitLeft() and hitRight() to determine which side the object collided with the other.
These methods are as follows:
public Rectangle getBounds()
{
return new Rectangle(x, y, width, height);
}
public boolean collision(GameItem item)
{
return getBounds().intersects(item.getBounds());
}
public boolean hitTop(GameItem item)
{
return getY()+getHeight()>=item.getY();
}
public boolean hitLeft(GameItem item)
{
return getX()+getWidth()>=item.getX();
}
public boolean hitBottom(GameItem item)
{
return getY()<=item.getY()+item.getHeight();
}
public boolean hitRight(GameItem item)
{
return getX()<=item.getX()+item.getWidth();
}
The ball class has a method called checkBallCollissions(GameItem item) to determine when the ball has hit another object. This is as follows:
public boolean checkBallCollisions(GameItem item)
{
if(this.collision(item))
{
if(this.hitTop(item))
{
this.setVY(-1*this.getVY());
return true;
}
else if(this.hitBottom(item))
{
this.setVY(-1*this.getVY());
return true;
}
else if(this.hitLeft(item))
{
this.setVX(-1*this.getVX());
return true;
}
else if(this.hitRight(item))
{
this.setVX(-1*this.getVX());
return true;
}
return false;
}
return false;
}
Which would be called as follows:
ball.checkBallCollisions(paddle);
As you can see, if it hits one of the 4 edges, the x/y direction is reversed accordingly.
However, this doesn't work. Analysing the code it seems there's an obvious issue that I can't fathom a solution to.
The 3 images below show a collision of the ball with an object from above, below and the right.
With the code as it is, it first detects a collision based on the bounding Rectangles and the intersects() method. It then determines which side it hit. If it hits from above it works. However, when it hits form the bottom or right then hitTop() will return true since getY()+getHeight()>=item.getY() is true for both these cases (i.e the bottom edge of the ball has a greater y value than the top edge of the object it's colliding with).
What conditions am I missing to resolve this and have perfect collision detection?
for circles you use the circle's radius rather than bounds, as bounds is a rectangle.
i believe there are methods for doing that in java but i do not know them yet, so a little research should do it.
also, for advanced collision people tend to use vectors, again i never fiddled with them before so you will have to research that as well.
here is something to get you going:
Java area and intersection
The issue here is that as you mentioned
"hitTop() will return true since getY()+getHeight()>=item.getY() is true for both these cases (i.e the bottom edge of the ball has a greater y value than the top edge of the object it's colliding with)."
In order to fix this you also need a check to ensure that the y position of the circle is less than the y position of the gameobject + height of gameobject (possibly subtracting some offset from it as well).
Which should look something like this (not worrying about offset):
public boolean hitTop(GameItem item)
{
return getY() + getHeight() >= item.getY() &&
getY() < item.getY() + item.getHeight();
}
You will need to do this for all of the collisions.
In the game i'm building, I have made a basic collision detection system.
My current method is explained below:
I workout where the player will be in the next step of the game:
double checkforx = x+vx;
double checkfory = y+vy;
I then check for a collision with blocks (1) in mapArray.
public static Boolean checkForMapCollisions(double character_x,double character_y){
//First find our position in the map so we can check for things...
int map_x = (int) Math.round((character_x-10)/20);
int map_y = (int) Math.round((character_y-10)/20);
//Now find out where our bottom corner is on the map
int map_ex = (int) Math.round((character_x+10)/20);
int map_ey = (int) Math.round((character_y+10)/20);
//Now check if there's anything in the way of our character being there...
try{
for(int y = map_y; y <= map_ey; y++){
for(int x = map_x; x <= map_ex; x++){
if (levelArray[y][x] == 1){
return true;
}
}
}
}catch (Exception e){
System.out.println("Player outside the map");
}
return false;
}
If true is returned {nothing}
If false is returned {Player physics}
I need the player to be able to land on a block and then be able to walk around but I cannot find and adequate tutorial for this.
Can someone give me an idea on how to run my collision detection and/or movement?
There are 2 parts to this question. Collision detection, meaning determining whether a volume is touching or intersecting another volume. The second is collision response. Collision response is the physics portion.
I'll cover collision detection here as that's primarily what you asked about.
Ddefine a class for the map like so:
int emptyTile = 0;
//this assumes level is not a ragged array.
public boolean inBounds(int x, int y){
return x>-1 && y>-1 && x<levelArray[0].length && y<levelArray.length;
}
public boolean checkForCollisions(Rectangle rectangle){
boolean wasCollision = false;
for(int x=0;x<rectangle.width && !wasCollision;x++){
int x2 = x+rectangle.x;
for(int y=0;y<rectangle.height && !wasCollision;y++){
int y2 = y+rectangle.y;
if(inBounds(x2,y2) && levelArray[y2][x2] != emptyTile){
//collision, notify listeners.
wasCollision=true;
}
}
}
}
Do not make your methods static. You probably want more than one instance of a level right? Static is for when you need to share state which remains constant across multiple instances of a class. Level data will surely not remain constant for every level.
Instead of passing in a coordinate, try passing in an entire rectangle. This rectangle will be the bounding box of your character (the bounding box is also sometimes referred to as AABB, which means Axis-aligned bounding box, just FYI in case you're reading tutorials online for this sort of thing.) Let your Sprite class decide what its bounding rectangle is, that's not the map class's responsibility. All the map should be used for is maybe rendering, and whether a rectangle is overlapping tiles which are not empty.
I am sorry for a very shitty explanation but here is my github code and it will help better.
https://github.com/Quillion/Engine
Just to explain what I do. I have character object (https://github.com/Quillion/Engine/blob/master/QMControls.java) and it has vectors and a boolean called standing. Every time boolean standing is false. Then we pass it to the engine to check for collision, if collision happens then standing is true and y vector is 0. As to x vector whenever you press any arrow keys you make the xvector of the object to whatever value you want. And in the update loop you displace the given box by the amount of speed.
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I am trying recreate the board game "go" in Java. I am currently working on the capturing system. Basically once a stone has been surrounded by the enemy stone on all four sides (diagonal don't count) you remove that stone. Like in the screenshot below.
Or if multiple of the same stones are connected you have to surround all the open sides. Like in the screenshot below.
In both cases the black stones should be removed at this point. This link explains more on the rules of capturing stones. societies.cam. ac. uk /cugos/go/rules_03.html
I was told it would be best to use recursion to do this. After doing some research on recursion I managed to write some code. But it's not working. It only seems to detect the enemy stone on the second move of the game. I call my method every time a stone is placed in my mouseReleased.
public static boolean checkCapture(int x, int y)
{
{
if ((board[x][y + 1] != move) && (board[x][y + 1] != 0)) //bellow
{
System.out.println("enemy is bellow");
if (checkCapture(x, y + 1))
board[x][y] = 0;
} else if (board[x][y + 1] == 0)
{
return false;
}
if ((board[x][y - 1] != move) && (board[x][y - 1] != 0)) //above
{
System.out.println("enemy is above");
if (checkCapture(x, y - 1))
board[x][y] = 0;
} else if (board[x][y - 1] == 0)
{
return false;
}
if ((board[x + 1][y] != move) && (board[x + 1][y] != 0)) // right
{
System.out.println("enemy is right");
if (checkCapture(x + 1, y))
board[x][y] = 0;
} else if (board[x + 1][y] == 0)
{
return false;
}
if ((board[x - 1][y] != move) && (board[x - 1][y] != 0)) //left
{
System.out.println("enemy is left");
if (checkCapture(x - 1, y))
board[x][y] = 0;
} else if (board[x - 1][y] == 0)
{
return false;
}
}
return true;
}
My int x is my column and my int y is my row, move is my variable that holds whose turn it is( 1 = black , 2 = white) board is my 2d array that holds the position of all the stones on the board.
I think that recursion complicates this solution more than necessary. If I were to implement something like this, I would take the following steps:
Find connected groups of stones. You can limit this to just dragons if you can detect if a group is alive because it has two eyes.
For each group of connected stones, count the liberties vertically and horizontally. (Liberties are unoccupied locations adjacent to a connected group of stones.) If the number of liberties is 0, then the group is captured.
If you are checking for a capture after a move has been made, then you really only need to check the connected groups which are adjacent to the most recent move, not all connected groups.
First, start out by being explicit about what your function does.
/**
* Checks to see if the grid point passed in is captured.
* #param...(you should say what your params are here
**/
public static boolean checkCapture(int x, int y) {
//some code
}
This is important: what if this function checks to see if the gird point is capturing some other, arbitrary point? Further, we immediately see a problem... captured by who? Whenever solving a recursive problem you need to understand the base case: here it is that there is no vertical or horizontal adjacent area that isn't occupied by an enemy.
Therefore, we must check to see in regards to a particular color:
/**
* Checks to see if the grid point passed in is captured.
* #param...(you should say what your params are here
**/
public static boolean checkCapture(int x, int y) {
if (!isOccupied(x,y)) {//writing this function should be trivial
return false;//can't be captured; no one is there!
}
Color color = getColor(x,y);//similarly, get the color of whoever is there.
Status status = checkFlanked(x, y, color);
if (status = Status.FLANKED) {
return true;
}
}
private static Status checkFlanked(int x, int y, Color color) {
//check to see that this location is valid for the board
//check to see if this square is occupied at all
//if it is not, return LIBERTY (an empty space means no capture, right?)
//if it is, is it occupied by the opposite color? --> Return a FLANKED result!
//if it is, is it occupied by the same color? --> recurse!
}
Now we've broken down our problem a bit! And it's easy to see how the base case is resolved: if the square is unoccupied, it can't be flanking... so it returns a LIBERTY result. If it's occupied by the opposite color, then this square is flanking whomever you were originally checking. The only difficult part is then checking to see whether, in the case of this being occupied by the original color, any other locations have liberty or not.
//get all valid adjacent locations
//call checkFlanked on those locations.
//If any return LIBERTY, return LIBERTY. Otherwise return FLANKED.
(Note: I'm assuming LIBERTY and FLANKED have been defined as an enum for clarity's sake.)
I hope this helps you break down your problem in a more sensible way. Remember: when you're using recursion, you care about two cases: the base case, and the '+1 iteration' case. Note that even with the above you have to solve some problems:
You need to intelligently not recurse back to squares you've already visited. (Investigate tail recursion, but you can also just pass in additional state indicating squares that are checked already.)
You need to make sure you don't fall off the board and return an appropriate result if you do. Basically, you need to solve the 'what is a valid location?' problem.
Some other interesting questions to ask are:
Do you search by-breadth or by-depth?
Is this appropriate as a static method, or should it be captured in a class?
I have some code that lets you play and capture stones. See this answer: https://gamedev.stackexchange.com/questions/23291/go-game-placing-stones-on-grid-intersections/23406#23406
The trick is to keep track of contiguous blocks of stones and then check after every move to see if that move captures a block.
There is also ko to worry about.
The problem with recursion in this case is it is very easy to fall into an infinite loop. Checking two stones, checking the right stone will check the left stone will check the right again etc. You need to keep track of the stones you have already checked. You are going to need to pass some state of the stones that you have already checked.