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

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
......
.**.*.
....*.
.*....
.*.**.
......
......
.*..**
.*....
......
.**.*.
....*.

Related

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

Checking For Multiple Collisions While Iterating Through an ArrayList in Java

I am working on a matching game (in Java) where the player throws and object and the system will check if it matches any adjacent pieces on the board. I currently am able to get it to check one adjacent piece (using rectangle intersection), but if I don't return; after the if/else block (thereby preventing it from checking more than one adjacent piece), it will throw a java.util.ConcurrentModificationException exception. I haven't been able to figure out any way of doing this.
I am using iterators to loop through all the pieces and check if they intersect with the thrown piece. This check is done after the piece hits something (also checked by rectangle intersection).
Below is a code snippet of the problem area:
Iterator<Food> it3 = foods.iterator();
while(it3.hasNext()) {
Food f2 = it3.next();
if(t.checkAll().intersects(f2.getBounds())) { // check for matches
if(t.getNutritionType() == f2.getNutritionType() || t.getType() == 0) {
app.log("Throw intersects with like food.");
int numMatches = 2; // hard-coded until multiple matching works
player.addNutrient(f2.getNutritionType(), 1);
player.addCalories(10*numMatches);
// move food to cart and add to matched
f2.fallToCart(600, 540);
matched.add(f2);
// remove from food arraylist
it.remove();
it3.remove();
return;
} else {
app.log("Throw intersects with unlike food.");
t.stop();
Food n = new Food(t.getType());
n.setX(t.getX());
n.setY(t.getY());
foods.add(n);
it.remove();
return;
}
}
}
Note that it is the 'thrown' iterator and it2 is the first collision check with pieces to stop the throw. Throws are throws and foods are game pieces.
I'm sorry if what I wrote is confusing. I'd be more than happy to clarify anything. Basically my question comes down to how to iterator for matches multiple times (either separately or at once), as matches can be on any side of the throw and more than one object can match.
Without being able to understand your code (see my comments), I can tell you where your problem is. If you read what a ConcurrentModificationException actually is, you will get to the part where it explains
[...] if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.
You are iterating with it3 and then calling it3.remove().

Jigsaw Puzzle Solver Method

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.

Sudoku solver Recurrence stack with try and fail technique

I am building a Sudoku solver that use the Try and Fail technique to solve any problem. My algorithm is:
1)Update (method that remove any possible value that already given as a final value to element in the same Row, column or squar)
2)Get the minimum element that has minimum number of possible values
3)start solve assuming the first possible value is the final value
4)save the current sate into a stack
5)Try to solve
5-a)If solved, return
5-b)if not solved and with invalid Sudoku, then Pop previous state
6)Repeat step 3) for all possible vaues (9)
7)Repeat step 2) until the puzzel is solved
This is my code
Stack<Element[][]> myStack= new Stack<>();
private Element[][] mySudoku;
public void solve(){
update();//remove all final values from all possible values for each element
if(isSudokuSolved(mySudoku)){
return;
}
//find a cell that is not confirmed and has the minimal candidates
int celli=-1,cellj=-1, p=10;
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
if(mySudoku[i][j].getValue()==0){
if(mySudoku[i][j].getPossibleValues().size()<p){
celli=i;
cellj=j;
p=mySudoku[i][j].getPossibleValues().size();
}
}
}
}
try {
for (int c = 0; c < mySudoku[celli][cellj].getPossibleValues().size() - 1; c++) {
//save state
Element[][] copy=deepCopy(mySudoku);//copy the current state
myStack.push(copy);
//apply candidate to cell
mySudoku[celli][cellj].setValue(mySudoku[celli][cellj].getPossibleValues().get(c));
update();//check is solved
if(checkValidInputSudoku(mySudoku)){
solve();
}else{
try {
mySudoku = myStack.pop();
} catch (EmptyStackException est) {
//do nothing
}
}
}
} catch (Exception e) {
}
//if we have reached here then we are at the last possible value for the candidates so confirm candidate in cell
if(celli!=-1 && cellj!=-1 && p!=10) {//Some problems happen here "out of Boundry -1 Error"
mySudoku[celli][cellj].setValue(mySudoku[celli][cellj].getPossibleValues().get(mySudoku[celli][cellj].getPossibleValues().size()-1));
}
}//end of solve method
I have spent more than 6 hours trying to find out the problem. I have checked for the Update() method, deepCopy() method and checkValidInputSudoku() method. They all works fine. Thank you in Advance
I can see one problem in your code. You have a loop that is sawing off the branch it sits on:
for(int c = 0; c < mySudoku[celli][cellj].getPossibleValues().size() - 1; c++) {
...
mySudoku[celli][cellj].setValue(mySudoku[celli]cellj].getPossibleValues().get(c));
...
}
Apart from that, you are missing one of the values, it should be for(c=0; c!=size; ++c), i.e. not size - 1. Also, calling getPossibleValues() just once would make this code much more readable. Lastly, catching and ignoring a stack underflow is just stupid, because it hides errors in your algorithm, as far as I can tell. If you don't know how to handle an error, don't just silence it. Since java requires you to catch it, put it in the outermost place possible or at least abort or do something, but don't ignore it!
One more thing: You are recursing and passing the context data via mySodoku and myStack. This is completely missing the point of recursion (or at least the way it's typically used), because the function call stack is the only stack you need. Using these to pass parameters only makes things more complicated than necessary. Instead, the function should return a partial sodoku puzzle and return either the fully solved puzzle or null. Using is easier to distinguish than the exception you're using now, and it's a regular and expected thing, not really exceptional. Then, when trying different choices, you set the cell to the values in turn and recurse, until the call doesn't return null. If none of the choices returns a solution, you clear the cell and return null yourself.
solve(sodoku):
if sodoku is solved:
return true
if sodoku is invalid:
return false
c = some empty cell
for v in 1...9:
// set to a value and recurse
c = v
if solve(sodoku):
// found a solution
return true
// no solution found, clear cell and return failure
c = null
return false
BTW: This strategy is called "backtracking". Using a cell with the least amount of possible values is called "pruning", which allows you to cut off whole branches from the search tree. Actually determining the possible values also helps avoiding a few futile attempts.

Java maze solving and reinforcement learning

I'm writing code to automate simulate the actions of both Theseus and the Minoutaur as shown in this logic game; http://www.logicmazes.com/theseus.html
For each maze I provide it with the positions of the maze, and which positions are available eg from position 0 the next states are 1,2 or stay on 0. I run a QLearning instantiation which calculates the best path for theseus to escape the maze assuming no minotaur. then the minotaur is introduced. Theseus makes his first move towards the exit and is inevitably caught, resulting in reweighting of the best path. using maze 3 in the game as a test, this approach led to theseus moving up and down on the middle line indefinatly as this was the only moves that didnt get it killed.
As per a suggestion recieved here within the last few days i adjusted my code to consider state to be both the position of thesesus and the minotaur at a given time. when theseus would move the state would be added to a list of "visited states".By comparing the state resulting from the suggested move to the list of visited states, I am able to ensure that theseus would not make a move that would result in a previous state.
The problem is i need to be able to revisit in some cases. Eg using maze 3 as example and minotaur moving 2x for every theseus move.
Theseus move 4 -> 5, state added(t5, m1). mino move 1->5. Theseus caught, reset. 4-> 5 is a bad move so theseus moves 4->3, mino catches on his turn. now both(t5, m1) and (t3 m1) are on the visited list
what happens is all possible states from the initial state get added to the dont visit list, meaning that my code loops indefinitly and cannot provide a solution.
public void move()
{
int randomness =10;
State tempState = new State();
boolean rejectMove = true;
int keepCurrent = currentPosition;
int keepMinotaur = minotaurPosition;
previousPosition = currentPosition;
do
{
minotaurPosition = keepMinotaur;
currentPosition = keepCurrent;
rejectMove = false;
if (states.size() > 10)
{
states.clear();
}
if(this.policy(currentPosition) == this.minotaurPosition )
{
randomness = 100;
}
if(Math.random()*100 <= randomness)
{
System.out.println("Random move");
int[] actionsFromState = actions[currentPosition];
int max = actionsFromState.length;
Random r = new Random();
int s = r.nextInt(max);
previousPosition = currentPosition;
currentPosition = actions[currentPosition][s];
}
else
{
previousPosition = currentPosition;
currentPosition = policy(currentPosition);
}
tempState.setAttributes(minotaurPosition, currentPosition);
randomness = 10;
for(int i=0; i<states.size(); i++)
{
if(states.get(i).getMinotaurPosition() == tempState.getMinotaurPosition() && states.get(i).theseusPosition == tempState.getTheseusPosition())
{
rejectMove = true;
changeReward(100);
}
}
}
while(rejectMove == true);
states.add(tempState);
}
above is the move method of theseus; showing it occasionally suggesting a random move
The problem here is a discrepancy between the "never visit a state you've previously been in" approach and your "reinforcement learning" approach. When I recommended the "never visit a state you've previously been in" approach, I was making the assumption that you were using backtracking: once Theseus got caught, you would unwind the stack to the last place where he made an unforced choice, and then try a different option. (That is, I assumed you were using a simple depth-first-search of the state-space.) In that sort of approach, there's never any reason to visit a state you've previously visited.
For your "reinforcement learning" approach, where you're completely resetting the maze every time Theseus gets caught, you'll need to change that. I suppose you can change the "never visit a state you've previously been in" rule to a two-pronged rule:
never visit a state you've been in during this run of the maze. (This is to prevent infinite loops.)
disprefer visiting a state you've been in during a run of the maze where Theseus got caught. (This is the "learning" part: if a choice has previously worked out poorly, it should be made less often.)
For what is worth, the simplest way to solve this problem optimally is to use ALPHA-BETA, which is a search algorithm for deterministic two-player games (like tic-tac-toe, checkers, chess). Here's a summary of how to implement it for your case:
Create a class that represents the current state of the game, which
should include: Thesesus's position, the Minoutaur's position and
whose turn is it. Say you call this class GameState
Create a heuristic function that takes an instance of GameState as paraemter, and returns a double that's calculated as follows:
Let Dt be the Manhattan distance (number of squares) that Theseus is from the exit.
Let Dm be the Manhattan distance (number of squares) that the Minotaur is from Theseus.
Let T be 1 if it's Theseus turn and -1 if it's the Minotaur's.
If Dm is not zero and Dt is not zero, return Dm + (Dt/2) * T
If Dm is zero, return -Infinity * T
If Dt is zero, return Infinity * T
The heuristic function above returns the value that Wikipedia refers to as "the heuristic value of node" for a given GameState (node) in the pseudocode of the algorithm.
You now have all the elements to code it in Java.

Categories

Resources