Algorithm for sliding puzzle does not find the optimal solution - java

Hey I'm trying to write a solver for a n x m sliding puzzle. My algorithm finds a solution but it's not the optimal one. I took a look at BFS Algorithm since it seemed pretty fitting for this kind of problem but I have no idea on how to adapt my code accordingly. I hope someone can find the problem in my code or give my some tips on how to improve it. The performance is very poor also.
private static final int maxMoves = 30;
private List<Direction> solutions = new ArrayList<Direction>();
public void solveHelper(List<Direction> moves, SlidingPuzzle g){
if(moves.size() > maxMoves){
return;
}
if(g.isGameOver()){
solutions.addAll(moves);
return;
}
for(Direction dir : Direction.values()){
Direction invDir = null;
switch(dir){
case UP:
invDir = Direction.DOWN;
break;
case RIGHT:
invDir = Direction.LEFT;
break;
case LEFT:
invDir = Direction.RIGHT;
break;
case DOWN:
invDir = Direction.UP;
break;
}
if(moves.size() == 0 || invDir != moves.get(moves.size() - 1)){
if(g.moveGap(dir)){
solveHelper(createNewListAndCopyAndAdd(moves, dir), g);
g.moveGap(invDir);
}
}
}
}
private List<Direction> createNewListAndCopyAndAdd(List<Direction> list, Direction dir){
List<Direction> newList = new ArrayList<Direction>(list);
newList.add(dir);
return newList;
}

Related

Java Snake Bug: It happens when change direction quickly

In my Snake code, if the snake is moving LEFT currently, and I then press either UP or DOWN, and then very quickly press RIGHT, the game is over. I knew the reason why it happened, but I don't know how to fix it. Maybe I should announce a new variable for last direction and then compare it with the new direction to decide if the snake should turn to the new direction? I don't know how to fix it. Can somebody help me to solve the problem ? :-)
Here is the relevant code.
//Judge the keyboard input
public void changeDirection(Direction newDirection) {
if (snakeDirection.compatibleWith(newDirection)) {
snakeDirection = newDirection;
}
Direction.java
public enum Direction {
UP(0),
RIGHT(1),
DOWN(2),
LEFT(3);
private final int directionCode;
Direction(int directionCode) {
this.directionCode = directionCode;
}
private int directionCode() {
return directionCode;
}
//
public boolean compatibleWith(Direction direction) {
return (this.directionCode() + direction.directionCode()) % 2 == 1;
}
}
GameController.java
public class GameController implements Runnable, KeyListener {
private final Grid grid;
private final GameView gameView;
boolean running;
public GameController(Grid grid, GameView gameView) {
this.grid = grid;
this.gameView = gameView;
this.running = true;
}
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
switch (keyCode) {
case KeyEvent.VK_UP:
grid.changeDirection(Direction.UP);
break;
case KeyEvent.VK_DOWN:
grid.changeDirection(Direction.DOWN);
break;
case KeyEvent.VK_LEFT:
grid.changeDirection(Direction.LEFT);
break;
case KeyEvent.VK_RIGHT:
grid.changeDirection(Direction.RIGHT);
break;
case KeyEvent.VK_ENTER:
if (running == false) {
grid.init();
running = true;
new Thread(this).start();
}
break;
case KeyEvent.VK_SPACE:
if (running) {
running = false;
} else {
running = true;
new Thread(this).start();
}
break;
default:
break;
}
}
// the Thread
public void run() {
while (running) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
break;
}
// your code here
if (grid.nextRound()) {
gameView.draw();
} else {
gameView.showGameOverMessage();
break;
}
}
running = false;
}
You really need a new variable. Your snakeDirection is the direction you want to use in the next time step and comparing it with newDirection only helps when it has been used. You need a lastUsedDirection set after a step was done.
Some comments
You can replace directionCode by ordinal().
You should start only a single thread and let it skip the action when running=false. Use the constructor allowing to start it with deamon=true, so that you can terminate the game without it terminating first. Don't let it terminate. You current code risks having multiple threads running when you press SPACE multiple times fast enough.
Be careful with you variable used in multiple threads. As long as one thread only reads the variable and the other one only writes it, it's enough to use volatile. You probably don't even do this and then it can happen that one thread never sees the writes made by another one (this is no theory; this really happens, maybe not in your case).
Just check the x and y positon of the head and the "neck".
What I mean is:
neck
|
| xxxx
| x <--- The snake
HNxx
|
|
head
If the position is the same in one axis, you should not turn.
Here's an actual example (it's C++, but Java has similar syntax, so this shouldn't be a problem).
void Snake::setDirection(char dir) {
switch (direction) {
case Snake::NORTH:
if (dir == Snake::SOUTH) return;
if (body.size() > 1 && body[0]->y == body[1]->y) return;
break;
case Snake::SOUTH:
if (dir == Snake::NORTH) return;
if (body.size() > 1 && body[0]->y == body[1]->y) return;
break;
case Snake::EAST:
if (dir == Snake::WEST) return;
if (body.size() > 1 && body[0]->x == body[1]->x) return;
break;
case Snake::WEST:
if (dir == Snake::EAST) return;
if (body.size() > 1 && body[0]->x == body[1]->x) return;
break;
}
direction = dir;
}
body is a std::vector that holds all the snake segments and direction is a Snake class variable.

Solving 2D Maze in Java

I've been trying to make a program that will solve a 2D integer maze. I keep getting a stackOverFlowError. I incorporated cases to set preferences in movement i.e North, South, East, West. I cannot find the issue in my recursive method.
import java.util.*;
import java.awt.*;
public class Runner
{
private static int[][] maze = MazeReader.getMaze("C:\\Users\\owner\\Desktop\\myMaze.txt");
private static int[][] solution = new int[maze.length][maze[0].length];
private static Point[] prefs = new Point[4];
private static int goalX, goalY, startX, startY;
/*
* if (x,y outside maze) return false
if (x,y is goal) return true
if (x,y not open) return false
mark x,y as part of solution path
if (FIND-PATH(North of x,y) == true) return true
if (FIND-PATH(East of x,y) == true) return true
if (FIND-PATH(South of x,y) == true) return true
if (FIND-PATH(West of x,y) == true) return true
unmark x,y as part of solution path
return false
*/
private static boolean FIND_PATH(int x, int y)
{
if(x<0||x>=maze.length||y<0||y>=maze[0].length){return false;}
if(x==goalX&&y==goalY){return true;}
if(maze[x][y]==1){return false;}
solution[x][y] = 2;
if(FIND_PATH(x+(int)prefs[0].getX(),y+(int)prefs[0].getY())){return true;}
else if(FIND_PATH(x+(int)prefs[1].getX(),y+(int)prefs[1].getY())){return true;}
else if(FIND_PATH(x+(int)prefs[2].getX(),y+(int)prefs[2].getY())){return true;}
else if(FIND_PATH(x+(int)prefs[3].getX(),y+(int)prefs[3].getY())){return true;}
else {solution[x][y] = 0;}
return false;
}
/*
* Locate the start position (call it startx, starty).
Call FIND-PATH(startx, starty).
*/
private static void solve(int sx, int sy, int gx, int gy, char p1, char p2, char p3, char p4)
{
establishPrefs(p1,p2,p3,p4);
startX = sx;
startY = sy;
goalX = gx;
goalY = gy;
if(FIND_PATH(startX,startY))
{
solution[startX][startY] = 3;
solution[goalX][goalY] = 4;
}
else{System.out.println("No Solution Found");}
FIND_PATH(startX,startY);
}
private static void establishPrefs(char p1, char p2, char p3, char p4)
{
switch(p1)
{
case 'N': prefs[0] = new Point(0,1);break;
case 'S': prefs[0] = new Point(0,-1);break;
case 'E': prefs[0] = new Point(1,0);break;
case 'W': prefs[0] = new Point(-1,0);break;
}
switch(p2)
{
case 'N': prefs[1] = new Point(0,1);break;
case 'S': prefs[1] = new Point(0,-1);break;
case 'E': prefs[1] = new Point(1,0);break;
case 'W': prefs[1] = new Point(-1,0);break;
}
switch(p3)
{
case 'N': prefs[2] = new Point(0,1);break;
case 'S': prefs[2] = new Point(0,-1);break;
case 'E': prefs[2] = new Point(1,0);break;
case 'W': prefs[2] = new Point(-1,0);break;
}
switch(p4)
{
case 'N': prefs[3] = new Point(0,1);break;
case 'S': prefs[3] = new Point(0,-1);break;
case 'E': prefs[3] = new Point(1,0);break;
case 'W': prefs[3] = new Point(-1,0);break;
}
}
private static Point[] getPrefs(){return prefs;}
public static void main(String[] args)
{
MazeReader.display(maze);
solve(0,0,0,2,'S','E','N','W');
MazeReader.display(solution);
}
}
Although you are marking solution[x][y] as part of your solution path, you are not checking if you are returning to a previous point on your solution. In essence, you end up going in circles.
You must not follow paths that lead you into a position that is currently part of your tentative solution.
if(maze[x][y]==1){return false;}
if(solution[x][y] == 2) {return false;} // <-- Add
solution[x][y] = 2;

Array of Characters not updated properly java

I am trying to make pong in a terminal on windows in java.
I have an array of characters (called board) and the paddle positions are updated by the following code
public void movePaddles()
{
int inp = 0;
try
{
inp = System.in.read();
}
catch(Exception e)
{
return;
}
int olr = rtop;
int oll = ltop;
switch(inp)
{
case 'w':
ltop -=1;
break;
case 's':
ltop += 1;
break;
case 'i':
rtop -= 1;
break;
case 'k':
rtop += 1;
break;
}
updatePaddle('L',oll);
updatePaddle('R',olr);
}
public void updatePaddle(char side,int oldtop)
{
int edge = 0;
int top = 0;
// Leave 1 char of space
if (side == 'L')
{
edge = 1;
top = ltop;
}
else if (side == 'R')
{
edge = y-2;
top = rtop;
}
for (int i = oldtop;i < paddleSize+1;i++)
{
board[i][edge] = ' ';
}
for (int i = top;i < paddleSize+2;i++)
{
board[i][edge] = paddleChar;
}
}
What ends up happening is that the paddles disappear when moving down, making the game unplayable. What am I doing wrong?
(full code on github at https://github.com/Tookmund/Text-Pong)
I did try debugging my program before I posted my question but not properly it appears.
Basically I needed to add the value of the current location (top/oldtop) so that the loop would not exit prematurely because i was initially bigger that the paddleSize.

When using KeyListener - 2 input happens

I am programing 2D rpg game and I have weird problem. When I use KeyPressed method it happens to call my move method twice which exits to rewrite player position from [10,10] to [10,12] insted of [10,11] and move my player 2 fields. I was looking around the internet and found nothing useful.
For displaying I use Canvas and Runnable with JFrame. A also use Thread
When I used debugger to search what's wrong I found that Keylistener use method
KeyPressed
public void keyPressed(KeyEvent e)
{
((KeyListener)a).keyPressed(e);
((KeyListener)b).keyPressed(e);
}
I believe that KeyListener a and b do the problem but I dont know hot to fix it.
My move method which change Point (x,y)
public void move() {
Point startPlayerPosition = player.getPosition();
Point newPoint = startPlayerPosition;
switch (direction) {
case Direction.NORTH:
newPoint = new Point(startPlayerPosition.x, startPlayerPosition.y - 1);
break;
case Direction.SOUTH:
newPoint = new Point(startPlayerPosition.x, startPlayerPosition.y + 1);
break;
case Direction.WEST:
newPoint = new Point(startPlayerPosition.x - 1, startPlayerPosition.y);
break;
case Direction.EAST:
newPoint = new Point(startPlayerPosition.x + 1, startPlayerPosition.y);
break;
}
playerPos = newPoint;
player.setPosition(newPoint);}
This is my KeyPressed method. I wait for pressing W,A,S,D to to change direction and then I call move() according to direction.
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_W:
playerWalk = true;
direction = Direction.NORTH;
move();
break;
case KeyEvent.VK_S:
playerWalk = true;
direction = Direction.SOUTH;
move();
break;
case KeyEvent.VK_D:
playerWalk = true;
direction = Direction.EAST;
move();
break;
case KeyEvent.VK_A:
playerWalk = true;
direction = Direction.WEST;
move();
break;
}
}
Thanks for Help

Android MultiTouch only working for one finger

I am developing a two player Pong game and wish to control the bats, each with one finger. However, my problem is that it only responds to one finger being on the screen at a time. The entire game is ran in one view. First, I check if a pointer is "active" before checking if it occured on the top or bottom of the screen before moving the corresponding bat. It works normally, except for that only one touch registers. My code for the method is below:
// variables
int _touchY = (int) event.getY();
boolean isTopSide = true;
// test if motionEvent was on the top or bottom of the screen
if (SCREEN_HEIGHT / 2 - _touchY < 0) {
isTopSide = false;
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
_activePointer = event.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE:
if(_activePointer != INVALID_POINTER_ID) {
if (isTopSide) { // top bat
_topBat.moveBat(event);
} else { // bottom bat
_botBat.moveBat(event);
}
}
if(_newPointer != INVALID_POINTER_ID){
if (isTopSide) { // top bat
_topBat.moveBat(event);
} else { // bottom bat
_botBat.moveBat(event);
}
}
break;
case MotionEvent.ACTION_UP:
if(_activePointer != INVALID_POINTER_ID){
_activePointer = INVALID_POINTER_ID;
}
break;
case MotionEvent.ACTION_CANCEL:
_activePointer = INVALID_POINTER_ID;
break;
case MotionEvent.ACTION_POINTER_UP:
int pointerIndex = event.getActionIndex();
int pointerId = event.getPointerId(pointerIndex);
if(pointerId == _activePointer){
_activePointer = INVALID_POINTER_ID;
}
case MotionEvent.ACTION_POINTER_DOWN:
int newPointerIndex = event.getActionIndex();
_newPointer = event.getPointerId(newPointerIndex);
break;
}
Relevant global code
// touched
private static final int INVALID_POINTER_ID = -1;
private int _activePointer = INVALID_POINTER_ID;
private int _newPointer = INVALID_POINTER_ID;
The main problem why you're only seeing values for a single pointer is because event.getY() without any parameter gives you the position for the first pointer index, even if the pointer that actually moved was the second pointer.
The MotionEvent class contains the coordinates for all the active pointers.
If you want to get the second pointer's position, you'll want to call event.getY(1).
You'll probably want to loop through all the active points, getting the total count via event.getPointerCount(), and then send the events accordingly.

Categories

Resources