I was trying to make a minesweeper game in Java, but I keep running into this error. This function sets the current square to clicked and any adjacent squares to be clicked and continues recursively. It should stop when it runs out of squares, but even when I set the field size to 2x2 with 0 mines, it overflows.
public void setClicked(boolean clicked){
this.clicked = clicked;
if(adjacentMines == 0)
for(mine m : adjacent){
if(!m.isClicked() && !m.isMine()){
setClicked(true); //Should be m.setClicked(true);
}
}
}
Problem solved, I was missing the "m." in my method call. Thanks to everyone for your help.
You need to call setClicked on the adjacent mine, not on original mine, otherwise, you'll get setClicked(true) called over and over again for the source mine
public void setClicked(boolean clicked){
this.clicked = clicked;
if(adjacentMines == 0)
for(mine m : adjacent){
if(!m.isClicked() && !m.isMine()){
m.setClicked(true); // setClicked should be called on the adjacent mine, not on itself!
}
}
}
You are calling setClicked on the same mine instead of the adjacent ones.
Change to:
public void setClicked(boolean clicked){
this.clicked = clicked;
if(adjacentMines == 0)
for(mine m : adjacent){
if(!m.isClicked() && !m.isMine()){
//missing the "m."
m.setClicked(true);
}
}
}
Well, I can only guess that setClicked() method is a member of mine, but shouldn't you be calling m.setClicked(true) inside your condition instead of just setClicked(true)?
Difficult to tell from your code snippet, but I would say you do not iterate over the cells. That means you are recursively checking the same cell over and over (to infinity and beyond woosh). The surrounding code would be very useful to see, though.
Related
Say I'm making a Minesweeper game that either has 99, 1516, or 30*16 grid.
Here's the code:
public void dfs(CustomButtonV2 c){
if(c.nbrBombsAdjacent != 0 || c.isReveal)
return;
if(c.nbrBombsAdjacent == 0 && !c.isReveal){
c.isReveal = true;
if(!c.isBomb)
c.setIcon(new javax.swing.ImageIcon(getClass().getResource("/minesweeper/resources/revealed.png")));
for(CustomButtonV2 n: c.neighbors){
if(!n.isBomb){
reveal(n);
dfs(n);
}
}
}
}
Each Button (CustomButtonV2) has either 3,5, or 8 neighbors which are saved in an arraylist inside the CustomButtonV2 class.
The function reveal(n) has a time complexity of O(1)
I tried using master theorem, but it didn't end up working!
When I used the tree method to find it. I found that big O is = O(8^k) is that possible?
I am trying to create "AI" for Nine Men's Morris but I got hardstuck on minMax algorithm. Summing up, I was trying to find the issue for over 10h but didn't manage to. (debugging this recursion is nasty or I am doing it badly or both)
Since I started doubting everything I wrote I decided to post my issue so someone can find anything wrong in my version of minMax. I realise it is really hard task without the whole application so any suggestions where I should triple check my code are also very welcome.
Here is link to the video, explaining minMax, on which I based my implementation: https://www.youtube.com/watch?v=l-hh51ncgDI (First video that pops up on yt after searching for minmax - just in case you want to watch the video and don't want to click the link)
My minMax without alpha-beta pruning:
//turn - tells which player is going to move
//gameStage - what action can be done in this move, where possible actions are: put pawn, move pawn, take opponent's pawn
//depth - tells how far down the game tree should minMax go
//spots - game board
private int minMax(int depth, Turn turn, GameStage gameStage, Spot[] spots){
if(depth==0){
return evaluateBoard(spots);
}
//in my scenario I am playing as WHITE and "AI" is playing as BLACK
//since heuristic (evaluateBoard) returns number equal to black pawns - white pawns
//I have decided that in my minMax algorithm every white turn will try to minimize and black turn will try to maximize
//I dont know if this is correct approach but It seems logical to me so let me know if this is wrong
boolean isMaximizing = turn.equals(Turn.BLACK);
//get all possible (legal) actions based on circumstances
ArrayList<Action> children = gameManager.getAllPossibleActions(spots,turn,gameStage);
//this object will hold information about game circumstances after applying child move
//and this information will be passed in recursive call
ActionResult result;
//placeholder for value returned by minMax()
int eval;
//scenario for maximizing player
if(isMaximizing){
int maxEval = NEGATIVE_INF;
for (Action child : children){
//aplying possible action (child) and passing its result to recursive call
result = gameManager.applyMove(child,turn,spots);
//evaluate child move
eval = minMax(depth-1,result.getTurn(),result.getGameStage(),result.getSpots());
//resets board (which is array of Spots) so that board is not changed after minMax algorithm
//because I am working on the original board to avoid time consuming copies
gameManager.unapplyMove(child,turn,spots,result);
if(maxEval<eval){
maxEval = eval;
//assign child with the biggest value to global static reference
Instances.theBestAction = child;
}
}
return maxEval;
}
//scenario for minimizing player - the same logic as for maximizing player but for minimizing
else{
int minEval = POSITIVE_INF;
for (Action child : children){
result = engine.getGameManager().applyMove(child,turn,spots);
eval = minMax(depth-1,result.getTurn(),result.getGameStage(),result.getSpots());
engine.getGameManager().unapplyMove(child,turn,spots,result);
if(minEval>eval){
minEval=eval;
Instances.theBestAction = child;
}
}
return minEval;
}
}
Simple heuristic for evaluation:
//calculates the difference between black pawns on board
//and white pawns on board
public int evaluateBoard(Spot[] spots) {
int value = 0;
for (Spot spot : spots) {
if (spot.getTurn().equals(Turn.BLACK)) {
value++;
}else if(spot.getTurn().equals(Turn.WHITE)){
value--;
}
}
return value;
}
My issue:
//the same parameters as in minMax() function
public void checkMove(GameStage gameStage, Turn turn, Spot[] spots) {
//one of these must be returned by minMax() function
//because these are the only legal actions that can be done in this turn
ArrayList<Action> possibleActions = gameManager.getAllPossibleActions(spots,turn,gameStage);
//I ignore int returned by minMax() because,
//after execution of this function, action choosed by minMax() is assigned
//to global static reference
minMax(1,turn,gameStage,spots);
//getting action choosed by minMax() from global
//static reference
Action aiAction = Instances.theBestAction;
//flag to check if aiAction is in possibleActions
boolean wasFound = false;
//find the same action returned by minMax() in possibleActions
//change the flag upon finding one
for(Action possibleAction : possibleActions){
if(possibleAction.getStartSpotId() == aiAction.getStartSpotId() &&
possibleAction.getEndSpotId() == aiAction.getEndSpotId() &&
possibleAction.getActionType().equals(aiAction.getActionType())){
wasFound = true;
break;
}
}
//when depth is equal to 1 it always is true
//because there is no other choice, but
//when depth>1 it really soon is false
//so direct child of root is not chosen
System.out.println("wasFound?: "+wasFound);
}
Is the idea behind my implementation of minMax algorithm correct?
I think the error might exist in that you are updating Instances.theBestAction even while evaluating child moves.
For example, lets say 'Move 4' is the true best move that will eventually be returned, but while evaluating 'Move 5', theBestAction is set to the best child action of 'Move 5'. From this point on, you won't update the original theBestAction back to 'Move 4'.
Perhaps just a simple condition that only sets theBestAction when depth == originalDepth?
Rather than using a global, you could also consider returning a struct/object that contains both the best score AND the move that earned the score.
New programmer here, writing a Tictactoe game using Java on Eclipse.
I have problems with my win conditions I think. It comes up with the error:
Exception in thread "main" java.lang.NullPointerException
at Game.NoughtsCrosses.(NoughtsCrosses.java:106)
at Game.Main.main(Main.java:5)
Here is my win conditions bit. It's not well made imo, but I'm having problems when compiling. Can anyone spot why? Ty!!
I have squares set up in a 3x3 grid, 0 -> 8. Each button has its own text which is set to X or O when clicked by each player.
winconditions code:
if (square[0].getText().equals(square[1].getText()) && square[1].getText().equals(square[2].getText()) != square[0].getText().isEmpty()) {
win = true;
}
Full Pastebin of code
Thanks again :) Any questions, I can elaborate :D
It looks like one of the squares text is null. One thing that is important to remember is that an empty string is not the same thing as null. In java, if you haven't specifically assigned a value to a String then it will be null. To fix this, you will want to explicitly set each squares text to "" (an empty string) when you set up your game board.
Well I took the code that you provided and after significant finagling was able to make a fully functioning Tic-Tac-Toe game. You were mostly on the right track with what you were doing you just needed to first begin with a design.
In my NoughtsCrosses class I have the following:
class Action implements ActionListener
This has a JButton attribute that I pass in through a constructor
In the actionPerformed
set the text
disable the button
increment the counter
check if someone wins
If there is a winner or draw game ends set the "Play again?" text
else call the changeTurn function
class Reset implements ActionListenter
This has a JButton attribute that I pass in through a constructor
In the actionPerformed
I call the resetGame function
function changeTurn
function resetGame
function checkForWinners
as a hint, this is my implementation of the Action class and an example of the constructor I mentioned
class Action implements ActionListener{
private JButton button;
public Action(JButton button){
this.button = button;
}
public void actionPerformed(ActionEvent e) {
button.setText(letter);
button.setEnabled(false);
counter++;
boolean gameOver = checkForWinners();
if(!gameOver)
changeTurn();
else{
newgame.setText("Play again?");
newgame.addActionListener(resetButton);
}
}
}
a call like new Action(square[i]) is what you need to make this work.
Note: the resetButton is of the Reset class I mention above much like the Action class it has the same construct that I passed newgame into.
It seems like your win-condition check is not within your actionPerformed code, but on the class level, hence it is possibly called before the window is populated with your buttons.
Try placing the check inside the actionPerformed like this: http://pastebin.com/xRViSUzy
What is the scope (most simply, which curly braces) is the problematic line inside of?
It was a bit tricky to tell based on your indentation, but it appears to me that your "if" was not inside a method (e.g. the constructor). I would guess that you intended this line and the ones around them to be executed after the lines in the body of your constructor where the squares are initialized. Instead, these lines are being run beforehand and therefore the call to "new" haven't been run yet.
I think that if you do some restructuring to move these conditions into your constructor or into another method that you call after construction then things will look a lot better.
Hope that helps.
If you're going to implement this type of solution, then simplify the job for yourself. Based on the tiny snippet of code I see above, it looks like you're really over-complicating the job you have to do.
char cell0 = //get that char, be it X or O
char cell1 = //
...
char cell8 = //
Now you can compare cells one by one to determine a victory. Your board game is set up as follows:
0 1 2
3 4 5
6 7 8
So you can just go in order:
Horizontal Solutions:
(cell0 == cell1 && cell0 == cell2)
(cell3 == cell4 && cell3 == cell5)
(cell6 == cell7 && cell6 == cell8)
Vertical Solutions
(cell0 == cell3 && cell0 == cell6)
//And so on
Cross Solutions:
(cell0 == cell4 && cell0 == cell8)
(cell2 == cell4 && cell2 == cell6)
This will check for your victory conditon.
The problem is that you have a surplus of braces in your code so that the statements in the question actually appear in an instance initializer block of the class NoughtsCrosses but none of the JButton components have been initialized yet as instance initializers are invoked before constructors which is where the JButton instantiated exist (but never called). When you attempt to invoke getText on the first element of the array square, a
NullPointerException is thrown.
To fix remove the additional braces to that the code is enclosed in the preceding ActionListener
class Action implements ActionListener {
public void actionPerformed(ActionEvent e) {
// existing code here
/// } remove
//} remove
// { remove
// win conditions. if true, set win==true; else set win
// here is where the compilation error is, next line
if (square[0].getText() == square[1].getText() ...) {
win = true;
} //etc
} <-- add this
Ok, so I have a 3 x 3 jig saw puzzle game that I am writing and I am stuck on the solution method.
public Piece[][] solve(int r, int c) {
if (isSolved())
return board;
board[r][c] = null;
for (Piece p : pieces) {
if (tryInsert(p, r, c)) {
pieces.remove(p);
break;
}
}
if (getPieceAt(r, c) != null)
return solve(nextLoc(r, c).x, nextLoc(r, c).y);
else {
pieces.add(getPieceAt(prevLoc(r, c).x, prevLoc(r, c).y));
return solve(prevLoc(r, c).x, prevLoc(r, c).y);
}
}
I know I haven't provided much info on the puzzle, but my algorithm should work regardless of the specifics. I've tested all helper methods, pieces is a List of all the unused Pieces, tryInsert attempts to insert the piece in all possible orientations, and if the piece can be inserted, it will be. Unfortunately, when I test it, I get StackOverflow Error.
Your DFS-style solution algorithm never re-adds Piece objects to the pieces variable. This is not sound, and can easily lead to infinite recursion.
Suppose, for example, that you have a simple 2-piece puzzle, a 2x1 grid, where the only valid arrangement of pieces is [2, 1]. This is what your algorithm does:
1) Put piece 1 in slot 1
2) It fits! Remove this piece, pieces now = {2}. Solve on nextLoc()
3) Now try to fit piece 2 in slot 2... doesn't work
4) Solve on prevLoc()
5) Put piece 2 in slot 1
6) It fits! Remove this piece, pieces is now empty. Solve on nextLoc()
7) No pieces to try, so we fail. Solve on prevLoc()
8) No pieces to try, so we fail. Solve on prevLoc()
9) No pieces to try, so we fail. Solve on prevLoc()
Repeat ad infinitum...
As commenters have mentioned, though, this may only be part of the issue. A lot of critical code is missing from your post, and their may be errors there as well.
I think you need to structure your recursion differently. I'm also not sure adding and removing pieces from different places of the list is safe; much as I'd rather avoid allocation in the recursion it might be safest to create a list copy, or scan the board
so far for instances of the same piece to avoid re-use.
public Piece[][] solve(int r, int c, List<Piece> piecesLeft) {
// Note that this check is equivalent to
// 'have r and c gone past the last square on the board?'
// or 'are there no pieces left?'
if (isSolved())
return board;
// Try each remaining piece in this square
for (Piece p : piecesLeft) {
// in each rotation
for(int orientation = 0; orientation < 4; ++orientation) {
if (tryInsert(p, r, c, orientation)) {
// It fits: recurse to try the next square
// Create the new list of pieces left
List<Piece> piecesLeft2 = new ArrayList<Piece>(piecesLeft);
piecesLeft2.remove(p);
// (can stop here and return success if piecesLeft2 is empty)
// Find the next point
Point next = nextLoc(r, c);
// (could also stop here if this is past end of board)
// Recurse to try next square
Piece[][] solution = solve(next.x, next.y, piecesLeft2);
if (solution != null) {
// This sequence worked - success!
return solution;
}
}
}
}
// no solution with this piece
return null;
}
StackOverflowError with recursive functions means that you're either lacking a valid recursion stop condition or you're trying to solve too big problem and should try an iterated algorithm instead. Puzzle containing 9 pieces isn't too big problem so the first thing must be the case.
The condition for ending recursion is board completion. You're only trying to insert a piece in the for loop, so the problem is probably either that the tryInsert() method doesn't insert the piece or it doesn't get invoked. As you're sure that this method works fine, I'd suggest removing break; from
if (p.equals(prev[r][c]))
{
System.out.println("Hello");
break;
}
because it's the only thing that may prevent the piece from being inserted. I'm still unsure if I understand the prev role though.
OK so I seem to be getting an Array Index out of Bounds error in a part of my code. Specifically in lines 85-102...
My code: http://www.sosos.pastebin.com/f0JQBWui
I just want it to check for blocked tiles AHEAD of time that way my sprite doesn't move in the direction it can't. This exception only happens when I am on the RIGHT or BOTTOM corners of my map.
My GUESS of why this error happens if because when I am on the corner.. it checks for the tiles to the RIGHT and BOTTOM of it which are not there...
1) The way you implemented blocked(tx,ty), it only accepts legal board coordinates (0<=tx<=12 and 0<=ty<=8). Otherwise it checks an illegal array position, producing an ArrayIndexOutOfBoundsException. Are you sure this is your intention? I think it makes sense to consider off board tiles as blocked.
2) In lines 85-102 there seems to be many errors. I think you meant something like:
if (spawnX == 0 || blocked(spawnX - 1, spawnY)) {
left = false;
System.out.println("You can't go left!");
}
if (spawnX == 12 || blocked(spawnX + 1, spawnY)) {
right = false;
System.out.println("You can't go right!");
}
if (spawnY ==0 || blocked(spawnX, spawnY - 1)) {
up = false;
System.out.println("You can't go up!");
}
if (spawnY == 8 || blocked(spawnX, spawnY + 1)) {
down = false;
System.out.println("You can't go down!");
}
Anyway, if you fix (1) as I suggested, the extra bound condition per direction is unecessary.
3) isInBound(r,c) is implemented incorrectly. It always returns false, due to the conditions on c.
4) There are many other problems with the code, but I will not enter into details. As a principle, try to make your design simple and make sure the code does not repeat itself.
You're going to have to do some bounds-checking in your blocked() function. Make sure that the coordinates they're giving you actually exist and return some "blocked" value if they don't.
The description of getting the error at the bottom or right would seem to suggest that you need to test if the value exceeds the array bounds. Have a look at Array.length