I develope a software that should solve some types of Peg Solitaire and print the steps to solve it like that:
[3,3,"^"],[4,5,"<"]
I'm using a Backtracking algorithm and most of times(not always) it's work on the "original" board, the france one and another one that is 5x6, but when i try to solve a new one that look like that:
XXXXXXXXX
XXXXXXXXX
XX XX
XX XX
XXXXXXXXX
XXXXXXXXX
the program can't find the answer, I try some basic board and this work but after 3 or 4 steps the program can't find the path.
I Try to debbug the code but can't find why he can't find the path
my PegSolver:
package com.company;
public class PegSolver {
public int peg=0;
String answer[];
public boolean superLevel=false;
public void start(){
boolean makefrance=false;
boolean lastLevel = false;
//france
String end,start;
//the starting board
start="OOOOOOOOO\", \"OOOOOOOOO\", \"OO OO\", \"OO O.\", \"OO OO\", \"OOOOOOOOO\", \"OOOOOOOOO\"";
//the wanted board
end = " OOOOOOOOO\", \"OOOOOOOO.\", \"OO OO\", \"OO O.\", \"OO OO\", \"OOOOOOOO.\", \"OOOOOOOO.\"] ";
//clean the Strings, let only 'O' or '.'
start = removeUnwanted(start);
end = removeUnwanted(end);
//adapt the board to normal, france, or other by the number of char in the string after remove the unwanted chars
if(start.length()==33)
{
makefrance=false;
lastLevel=false;
}
else if(start.length()==37)
{
makefrance = true;
lastLevel = false;
}
else if(start.length()==30){
makefrance=false;
lastLevel=true;
}
else if(start.length()==48){
superLevel=true;
}
int startDots=0;
int endDots=0;
Board board;
Board destinationBoard;
if(lastLevel)
{
board = new Board(start,true,true,superLevel);//The start of my board
destinationBoard = new Board(end,true,true,superLevel);//the wanted destination board, '.' for no peg and O for peg.
}
else{
board = new Board(start,makefrance,lastLevel,superLevel);//The start of my board
destinationBoard = new Board(end,makefrance,lastLevel,superLevel);//the wanted destination board, '.' for no peg and O for peg.
}
//count the pegs from the begining and ending board
for(int i=0;i<start.length()-1;i++) {
if (start.charAt(i) == '.') {
startDots++;
}
}
for(int i=0;i<end.length()-1;i++) {
if (end.charAt(i) == '.') {
endDots++;
}
}
//calculate the number of turns to the destination, every turn only one peg is disapear so finally there is:
//pegs at the end - pegs at the begining = number of turns
peg=endDots-startDots;
//create a string for save the steps.
answer = new String[peg];
//if we have a result, return the answer and print a text
if(Solve(board,destinationBoard,0))
{
System.out.println("The best day of my life");
for(int i=0;i<answer.length;i++)
{
System.out.println(answer[i]);
}
}
//if don't find path, print WTF
else{
System.out.println("WTFFFFF");
}
}
//recurse method that solve the peg solitaire
public boolean Solve(Board board, Board destination, int turn)
{
int rows=0;
//if we are on the end of the game
if(turn==peg)
{
//check if the current board is like the destination board.
if(board.isFinished(destination))
{
System.out.println("found the path");
return true;//this board is the same as the desitnation
}
else
{
return false;//this board is not the same as the desitnation
}
}
else {
if (superLevel)//if its a bigger board, need to check the board on more rows and lines
{
rows=9;
}
else{
rows=7;
}
for(int i=0;i<rows;i++){
for(int j=0;j<rows;j++)
{
if(board.isLegal(j,i))
{
if(board.canTurn(j,i,"right"))//test if it's possible to turn right
{
if(Solve(board.turn(board,j,i,"right"),destination,turn+1))//create a new board after changment of direction and recurse
{
answer[turn]= "["+j+","+i+",\">\"],";//add this step to the answer
return true;
}
}
if(board.canTurn(j,i,"up"))//test if it's possible to turn up
{
if(Solve(board.turn(board,j,i,"up"),destination,turn+1))//create a new board after changment of direction and recurse
{
answer[turn]= "["+j+","+i+",\"^\"],";//add this step to the answer
return true;
}
}
if(board.canTurn(j,i,"down"))//test if it's possible to turn down
{
if(Solve(board.turn(board,j,i,"down"),destination,turn+1))//create a new board after changment of direction and recurse
{
answer[turn]= "["+j+","+i+",\"v\"],";//add this step to the answer
return true;
}
}
if(board.canTurn(j,i,"left"))//test if it's possible to turn left
{
if(Solve(board.turn(board,j,i,"left"),destination,turn+1))//create a new board after changment of direction and recurse
{
answer[turn]= "["+j+","+i+",\"<\"],";//add this step to the answer
return true;
}
}
}
}
}
return false;//if don't find any path return.
}
}
public String removeUnwanted(String boardText)
{
String returnBoard="";
for(int i=0; i<boardText.length();i++)
{
if(boardText.charAt(i) == '.' || boardText.charAt(i)== 'O')//remove all the char that are not '.' or 'O'
{
returnBoard += boardText.charAt(i);
}
}
System.out.println(returnBoard);
return returnBoard;
}
}
and my board class:
package com.company;
import java.security.PublicKey;
public class Board {
//the array that storage the board data
private int[][] board = new int[9][9];;
public boolean superLvl=false;
int pegs = 0;
public Board(String place, boolean makefrance,boolean lastLevel,boolean superlevel) {
int boardx,boardy;
//add 2 to make the board compatible to the string
if(superlevel) {
superLvl=true;
boardx = 9;
boardy = 9;
int[][] temp = new int[][]{{0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0},{0,0,2,2,2,2,2,0,0},{0,0,2,2,2,2,2,0,0},{0,0,2,2,2,2,2,0,0},{0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0},{2,2,2,2,2,2,2,2,2},{2,2,2,2,2,2,2,2,2}};
for(int i=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
board[j][i]=temp[i][j];
}
}
}
else {
if (!lastLevel) {
boardx = 7;
boardy = 7;
board[0][0] = 2;
board[1][0] = 2;
board[5][0] = 2;
board[6][0] = 2;
board[0][1] = 2;
board[1][1] = 2;
board[5][1] = 2;
board[6][1] = 2;
board[0][5] = 2;
board[1][5] = 2;
board[5][5] = 2;
board[6][5] = 2;
board[0][6] = 2;
board[1][6] = 2;
board[5][6] = 2;
board[6][6] = 2;
if (makefrance) {
board[1][1] = 1;
board[1][5] = 1;
board[5][1] = 1;
board[5][5] = 1;
}
} else {
boardx = 6;
boardy = 6;
board[0][6] = 2;
board[1][6] = 2;
board[2][6] = 2;
board[3][6] = 2;
board[4][6] = 2;
board[5][6] = 2;
board[6][6] = 2;
board[0][5] = 2;
board[1][5] = 2;
board[2][5] = 2;
board[3][5] = 2;
board[4][5] = 2;
board[5][5] = 2;
board[6][5] = 2;
board[6][0] = 2;
board[6][1] = 2;
board[6][2] = 2;
board[6][3] = 2;
board[6][4] = 2;
}
}
int loca = 0;//location on the string of place
//add 0 or 1 to the table
for (int i = 0; i < boardy; i++) {
for (int j = 0; j < boardx; j++) {
if (board[j][i] != 2) {
if (place.charAt(loca) == 'O') {
loca++;
board[j][i] = 1;
pegs++;
} else if (place.charAt(loca) == '.') {
loca++;
board[j][i] = 0;
}
}
}
}
System.out.println();
this.printBoard();
}
public Board(Board newone)
{
int placement=7;
if(superLvl)
{
placement=9;
}
for(int i=0;i<placement;i++)
{
for(int j=0;j<placement;j++)
{
board[i][j]= newone.getValue(i,j);
}
}
}
//test if this board is like the other board at the end of the game
public boolean isFinished(Board destination) {
int placement=7;
if(superLvl)
{
placement=9;
}
for (int i = 0; i < placement; i++) {
for (int j = 0; j < placement; j++) {
if (this.getValue(j,i)!=destination.getValue(j,i))
{
return false;
}
}
}
return true;
}
//get the value of this coordinate.
public int getValue(int x, int y)
{
return board[x][y];
}
//test if this cooardiante is a place on the board(not 2)
public boolean isLegal(int x,int y)
{
if(board[x][y]!=2)
{
return true;
}
else{
return false;
}
}
//test if can turn- if this place is 1 and the next is 1 and the second next is 0
//also protect from stack overflow
public boolean canTurn(int x,int y,String direction)
{
if(direction.equals("right"))
{
if(x<5){
if(this.getValue(x,y)==1 && this.getValue(x+1,y)==1 && this.getValue(x+2,y)==0)
{
return true;
}
else {
return false;
}
}
else
{
return false;
}
}
else if(direction.equals("left"))
{
if(x>1){
if(this.getValue(x,y)==1 && this.getValue(x-1,y)==1 && this.getValue(x-2,y)==0)
{
return true;
}
else {
return false;
}
}
else
{
return false;
}
}
else if(direction.equals("up"))
{
if(y>1){
if(this.getValue(x,y)==1 && this.getValue(x,y-1)==1 && this.getValue(x,y-2)==0)
{
return true;
}
else {
return false;
}
}
else
{
return false;
}
}
else if(direction.equals("down"))
{
if(y<5){
if(this.getValue(x,y)==1 && this.getValue(x,y+1)==1 && this.getValue(x,y+2)==0)
{
return true;
}
else {
return false;
}
}
else
{
return false;
}
}
else{
System.out.println("method canTurn on board get a wrong direction string");
return false;
}
}
//make the move and return a "new" board for saving the originial one.
public Board turn(Board oldBoard,int x, int y ,String direction)
{
Board board = new Board(oldBoard);
if(direction.equals("right"))
{
board.setValue(x,y,0);
board.setValue(x+1,y,0);
board.setValue(x+2,y,1);
return board;
}
else if(direction.equals("left"))
{
board.setValue(x,y,0);
board.setValue(x-1,y,0);
board.setValue(x-2,y,1);
return board;
}
else if(direction.equals("up"))
{
board.setValue(x,y,0);
board.setValue(x,y-1,0);
board.setValue(x,y-2,1);
return board;
}
else if(direction.equals("down"))
{
board.setValue(x,y,0);
board.setValue(x,y+1,0);
board.setValue(x,y+2,1);
return board;
}
else{
System.out.println("method Turn on board get a wrong direction string");
return null;
}
}
//change the value of the wanted board.
public void setValue(int x,int y,int value)
{
board[x][y]=value;
}
//print the board, made for tests.
public void printBoard()
{
int placement=7;
if(superLvl)
{
placement=9;
}
for(int i=0;i<placement;i++)
{
for(int j=0;j<placement;j++)
{
System.err.print(board[j][i]);
}
System.err.println();
}
System.err.println("------------------");
}
}
I am making a little game for my university coursework and got stuck at the menu creation. Been trying to solve this for a while. Here goes the code:
Before trying to fiddle around the loop and noLoop() functions and before I moved the arrow placement from void setup to void keypressed my game has worked. Now the program freezes before running draw.
What I want is to only display the menu background before the key, in this case s for start, has been pressed. If anyone could come up with some directions it would be of a great help.
Thanks in advance.
int totalArrows = 6;
arrowclass[] theArrows = new arrowclass[totalArrows];
PImage[] imgList = new PImage[4];
PImage bg;
PImage life;
PImage menu;
int score;
int loose = 3;
void setup() {
size(400, 600);
bg = loadImage("bck.jpg");
life = loadImage("life.png");
menu = loadImage("menu.jpg");
background(menu);
// loading the arrow imgs
for (int i= 0; i<4; i++) {
println(i+".png");
imgList[i] = loadImage(i+".png");
}
noLoop();
}
void draw() {
textSize(32);
text(score,30,100);
// active area
fill(255,31,31);
rect(0,500,width,100);
//lifetrack
if (loose==3){
image(life,10,10);
image(life,45,10);
image(life,80,10);
}
if (loose==2){
image(life,10,10);
image(life,45,10);
}
if (loose==1){
image(life,10,10);
}
// calling class action, movement
for (int i = 0; i<totalArrows; i++) {
theArrows[i].run();
if (theArrows[i].y>475 && theArrows[i].y<600) {
theArrows[i].inactive = true;
}
if(theArrows[i].did == false && theArrows[i].y>598) {
theArrows[i].lost = true;
}
if(theArrows[i].lost && theArrows[i].did == false && theArrows[i].wrongclick == false){
loose--;
theArrows[i].lost = false;
}
}
if (loose == 0){
textSize(32);
text("gameover",width/2,height/2);
for(int i=0; i<totalArrows;i++){
}
}
}
void keyPressed() {
if (key == 'p'){
// placing tha arrows. (i*105 = positioning) THIS PART IS AN ISSUE FOR SURE. SEE ARROW CLASS
for (int i= 0; i<totalArrows; i++) {
theArrows[i] = new arrowclass(-i*105);
}
loop();
}
}
void keyReleased() {
for (int i= 0; i<totalArrows; i++) {
if (theArrows[i].inactive && theArrows[i].did == false) {
if (keyCode == UP && theArrows[i].id == 3){
score++;
theArrows[i].did = true;
}
if (keyCode != UP && theArrows[i].id == 3){
loose--;
theArrows[i].wrongclick = true;
}
if (keyCode == DOWN && theArrows[i].id == 0){
score++;
theArrows[i].did = true;
}
if (keyCode != DOWN && theArrows[i].id == 0){
loose--;
theArrows[i].wrongclick = true;
}
if (keyCode == RIGHT && theArrows[i].id == 2){
score++;
theArrows[i].did = true;
}
if (keyCode != RIGHT && theArrows[i].id == 2){
loose--;
theArrows[i].wrongclick = true;
}
if (keyCode == LEFT && theArrows[i].id == 1){
score++;
theArrows[i].did = true;
}
if (keyCode != LEFT && theArrows[i].id == 1){
loose--;
theArrows[i].wrongclick = true;
}
}
}
}
And the arrow class:
class arrowclass{
int y;
int id;
boolean did;
boolean inactive;
boolean lost;
boolean wrongclick;
// proprieties of the class
arrowclass(int initY){
y = initY;
id = int(random(4));
did = false;
inactive = false;
lost = false;
wrongclick = false;
}
//actions
void run(){
image(imgList[id],width/2,y);
// score goes up, speed goes up
y += 2+score;
// reset arrow to default
if (y>600) {
y = -50;
id = int(random(4));
inactive = false;
did = false;
lost = false;
wrongclick = false;
}
}
}
I'm trying to check whether or not a move is legal in the game Othello, using eclipse and gridworld. The first thing I do to the location is check if it is valid, but in order to check the validity location, it needs to not be null. The problem is, one of the requirements of it being a legal move is that it is null/empty/unoccupied. How do I avoid this? I have pointed out where the error is supposedly at. (Sorry if this confused anyone.)
public boolean isLegal(Location loc1)
{
boolean isLegal = false;
String currentColor = currentPlayer.getColor();
int row = loc1.getRow();
int col = loc1.getCol();
if(board.isValid(loc1))
{
if(board.get(loc1) == null)
{
for(Location tempLoc : board.getValidAdjacentLocations(loc1))
{
**if(!board.get(tempLoc).equals(currentColor))**
{
if((row != tempLoc.getRow()) && (col == tempLoc.getCol()))
{
//count up column
if(tempLoc.getRow() < row)
{
for(int i = row; i > 1;)
{
Location tempLoc2 = new Location(i-2, col);
if(!board.get(tempLoc2).equals(currentColor))
{
i--;
}
else
{
i=-1;
isLegal = true;
}
}
}
//count down column
else
{
for(int i = row; i < 6;)
{
Location tempLoc2 = new Location(i+2, col);
if(!board.get(tempLoc2).equals(currentColor))
{
i++;
}
else
{
i=9;
isLegal = true;
}
}
}
}
else if(col != tempLoc.getCol() && row == tempLoc.getRow())
{
//count right row
if(col > tempLoc.getCol())
{
for(int i = col; i > 1;)
{
Location tempLoc2 = new Location(row, i-2);
if(!board.get(tempLoc2).equals(currentColor))
{
i--;
}
else
{
i=-1;
isLegal = true;
}
}
}
//count left row
else
{
for(int i = col; i < 6;)
{
Location tempLoc2 = new Location(row, i+2);
if(!board.get(tempLoc2).equals(currentColor))
{
i++;
}
else
{
i=9;
isLegal = true;
}
}
}
}
else
{ //count up/right diag
if(row-1 == tempLoc.getRow() && col+1 == tempLoc.getCol())
{
int j = col;
for(int i = row; i > 1;)
{
Location tempLoc2 = new Location(i-1, j+1);
if(!board.get(tempLoc2).equals(currentColor))
{
i--;
j++;
}
else
{
i=-1;
isLegal = true;
}
}
}
//count down/left diag
else if(row+1 == tempLoc.getRow() && col-1 == tempLoc.getCol())
{
int i = row;
for(int j = col; j > 1;)
{
Location tempLoc2 = new Location(i+1, j-1);
if(!board.get(tempLoc2).equals(currentColor))
{
i++;
j--;
}
else
{
i=9;
isLegal = true;
}
}
}
//count up/left diag
else if(row-1 == tempLoc.getRow() && col-1 == tempLoc.getCol())
{
int j = col;
for(int i = row; i > 1;)
{
Location tempLoc2 = new Location(i-1, j-1);
if(!board.get(tempLoc2).equals(currentColor))
{
i--;
j--;
}
else
{
i=-1;
isLegal = true;
}
}
}
//count down/right diag
else
{
int j = col;
for(int i = row; i > 6;)
{
Location tempLoc2 = new Location(i+1, j+1);
if(!board.get(tempLoc2).equals(currentColor))
{
i++;
j++;
}
else
{
i=-1;
isLegal = true;
}
}
}
}
}
}
}
}
return isLegal;
}
One solution is to change your design so that no location is ever null.
You seem to have equated null with "unoccupied" or "empty". Instead create all positions first (there aren't many of them on an Othello board) and initialize them all with boolean occupied = false or an equivalent member variable. Then you'd have:
if ( !board.get(loc1).isOccupied() ) { /*stuff*/ }
instead of a null check.
This is better object oriented design because an empty location is still a location, and should be manipulable.
You shouldn't use null as a part of your logic.
null it's not a state, it is a symbol that there is no state.
You should leave null out of your logic, then if some reference is null, you know that for sure something really nasty happen, not related to your model. Inside Location you can create for example a method isEmpty() or similar, so you can easily avoid comparing to null.
Use an enum with the values BLACK, WHITE and VACANT, instead of a String, to store what colour token is at each location. Return the same enum from getColor() in the Player class.