Recursive bruteforce sudoku solver takes too long - java

I'm making a sudoku program, and I 'created' a recursive algorithm to solve a sudoku. The problem is, that sometimes it works perfectly and in an instant, sometimes it gets stuck and works 10s of seconds, and sometimes I just have to quit it.
Any ideas what might be causing this?
I left the code as it is, since I'm not sure how you answer the question(if you copy and try it out or just check the logic). If you want, I can just write out snippets.
Thanks!
import java.util.Random;
/**
* #Program Sudoku
*
* #date November 2013
* #hardware MacBook Pro 17", mid 2010, i7, 8GiB RAM
* #IDE eclipse SDK 4.3.1
* #purpose generates a valid 9x9 sudoku grid
*
*/
public class SudokuSolver //seems to werk !!
{
//create a validitychecker object(will be used as Sudoku.isValid();)
validityChecker Sudoku = new validityChecker();
//Create a 2D array where the full sudoku grid will be stored
private int[][] grid = new int[9][9];
//Creates a 2D array for the playable sudoku grid (with elements removed)
private int[][] playingGrid = new int[9][9];
private Random Ran = new Random();
/**
* #purpose use this construct if you wish to generate a new sudoku
* #param difficultyLevel removes amount of elements from sudoku using the equation elementToRemove=40+15*difficultyLevel
*/
SudokuSolver(int difficultyLevel)
{
//generate an empty grid
generateBaseGrid();
//populate it with a valid sudoku
solveSudoku(0,0);
//store this in a new from which elements shall be removed
for(int i = 0; i < grid.length; i++)
playingGrid[i] = grid[i].clone();
//calculate the amount of elements to be removed
int difficultyMultiplier = 15;
int baseDifficulty = 40;
int difficulty = baseDifficulty+difficultyLevel*difficultyMultiplier;
//and remove them
removeElements(difficulty);
}
/**
* #purpose use this constructor if you just want to use methods and solve
* #param the sudoku you wish to solve. values have to be within the range 1-9(inclusive), and -1 for unknown
* #note to get the solved sudoku use the fullGrid getter.
*/
SudokuSolver(int[][] pg)
{
//lets clone out the arrays - we don't want to just have references ...
for(int i = 0; i < pg.length; i++)
grid[i] = pg[i].clone();
for(int i = 0; i < grid.length; i++)
playingGrid[i] = grid[i].clone();
int coords[] = findOnes(grid);
solveSudoku(coords[0],coords[1]);
System.out.println(coords[0]+" "+coords[1]);
}
/**
* Use this if you only wish to use the internal methods
*/
SudokuSolver()
{}
//this method was implemented later, and I'm too lazy to change methods that use the procedure, but don't call the method. Maybe in next version
/**
* #purpose creates a copy of the passed array
* #param the array you wish to be copied
* #return returns a clone of the passed 2D array
*/
public int[][] cloneBoard(int[][] sudokuArray)
{
int[][] result = new int[9][9];
for(int i = 0; i < sudokuArray.length; i++)
result[i] = sudokuArray[i].clone();
return result;
}
/*
*#purpose fills the grid with -1s; This is for proper functionality during validation
*/
private void generateBaseGrid()
{
//iterates through all the values and stores -1s in it
for(int r=0;r<9;r++)
for(int c=0;c<9;c++)
grid[r][c] = -1;
//System.out.println("Base Grid Created");
}
/**
* #purpose checks if there are -1s in the grid, if so the grid is playable (its not a solution)
* #return true if its playable
*/
public boolean isGridPlayable()
{
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(grid[i][j]==-1)
return true;
return false;
}
/**
*
* #return the generated grid with all elements (for one without some elements use theGrid()) for generator
* #return the solved grid for solver
*/
public int[][] fullGrid()
{
return grid;
}
/**
* #purpose returns the playing grid
* #return the playable grid
*/
public int[][] theGrid()
{
return playingGrid;
}
/*
* #purpose removes "amnt" of elements from the playingGrid
* #return whether the current method was successful
* #param the amount of elements to be removed
*/
private boolean removeElements(int amnt)
{
if(amnt==0) //yay base case
return true;
for(int i=0; i<20;i++)
{
int r=Ran.nextInt(9);
int c=Ran.nextInt(9);
int element=playingGrid[r][c];
if(element!=-1)
{
playingGrid[r][c]=-1;
if(removeElements(amnt-1))
{return true;}
}else{playingGrid[r][c]=element;//removed as per suggestioni--;}
}
}
return false;
}
//--------------Debugging--------------------------------
public void printUserGrid(int[][] printie)
{
for(int i=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
int x = printie[i][j];
String bexp = Integer.toString(x);
if(x==-1)
bexp="[]";
else bexp+=" ";
System.out.print(bexp+" ");
if(j==2||j==5)
System.out.print(" ");
}
System.out.println();
if(i==2||i==5)
System.out.println();
}
}
// //----------Main only for debugging-----------------------
public static void main(String[] args)
{
SudokuSolver Generator = new SudokuSolver(2);
int[][] generatedGrid = Generator.theGrid();
int[][] fullGrid = Generator.fullGrid();
Generator.printUserGrid(fullGrid);
// Generator.printUserGrid(generatedGrid);
System.out.println("\n\n");
SudokuSolver Solver = new SudokuSolver(generatedGrid);
Solver.printUserGrid(fullGrid);
}
}
EDIT:
One key thing I forgot to mention, the solveSudoku method, it rearranges some of the values. That means if I'm starting with a **3 it doesn't have a problem returning 312 (this is just an example for illustration). So I'd assume there is some serious logic error somewhere in there.

What you are attempting to solve is an Artificial Intelligence problem. Going by brute-force, or better called plain backtracking would actually mean you possibly have an exponential time complexity.
An exponential solution is expected to take long. In some cases where the order of guesswork matches the actual solution, your solver will return with a result faster.
So what you can do:
Read upon the AI technique called Constraint Satisfaction, and try to implement that.
Read upon more specific AI sudoku solving techniques, maybe some research paper if there is one and try and implement that.

This is my version of a SudokuSolver:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Sudoku
{
//This member has been intentionally left public, as the solved sudoku is
//obtained from here and user will find it much easier to handle this.
//The Sudoku input has to be given through this instance variable only.
//Moreover, many a times, Sudoku inputs as an edit-able 2D array is more
//comfortable than passing a whole 9x9 2D array as a constructor parameter.
public String SUDOKU[][]=new String[9][9];
//This variable records the nature of the Sudoku whether solvable or not
public boolean VALID=true;
public Sudoku()//this constructor can be used to create SUDOKU objects
{
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
SUDOKU[i][j]="";
}
private Sudoku(String SDK[][])//This is constructor kept private for program use
{
SUDOKU=SDK;
}
//This function checks if a certain digit is possible in a particular cell
private boolean isPossible(int n,int i,int j)
{
for(int a=i/3*3;a<i/3*3+3;a++)
for(int b=j/3*3;b<j/3*3+3;b++)
if(SUDOKU[a][b].length()==1 && n==Integer.parseInt(SUDOKU[a][b]))
return false;
for(int k=0;k<9;k++)
if(SUDOKU[i][k].length()==1 && n==Integer.parseInt(SUDOKU[i][k]) || SUDOKU[k][j].length()==1 && n==Integer.parseInt(SUDOKU[k][j]))
return false;
return true;
}
//The following function is compulsory as it is the only function to place appropriate
//possible digits in the cells of the Sudoku. The easiest Sudokus will get solved here.
private void fillPossibles()
{
boolean newdigit=false;
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(SUDOKU[i][j].length()!=1)
{
SUDOKU[i][j]="";
int count=0;
for(int k=1;k<10;k++)
if(isPossible(k,i,j))
{
SUDOKU[i][j]+=k;
count++;
}
if(count==1)
newdigit=true;
}
if(newdigit)
fillPossibles();
}
//This following function is optional, if the Sudoku is known to be genuine. As,
//in that case it only increases the solving speed!! But if the nature is not known,
//this function becomes necessary because the nature of the Sudoku is checked here.
//It returns true if any cell is filled with a digit and false for all other cases
private boolean deepFillPossibles(int ai,int aj,int bi,int bj,boolean first)
{
if(SUDOKU!=null)
for(char c='1';c<='9';c++)
{
int count=0,digit=0,possibility=0,i=0,j=0;
boolean valid=true;
for(int a=ai;a<bi;a++)
for(int b=aj;b<bj;b++)
{
if(SUDOKU[a][b].length()==0)
valid=false;
for(int k=0;k<SUDOKU[a][b].length();k++)
if(SUDOKU[a][b].charAt(k)==c)
{
if(SUDOKU[a][b].length()>1)
{
i=a; j=b; count++;
}
if(SUDOKU[a][b].length()==1)
digit++;
possibility++;
}
}
//the following code is executed only if solution() is called first time
if(first && (digit>1 || valid && possibility==0))
{
SUDOKU=null; return false;
}
if(count==1)
{
SUDOKU[i][j]=String.valueOf(c);
fillPossibles(); return true;
}
}
return false;
}
//This function is also optional if Sudoku is genuine. It only combines the solving
//power of fillPossibles() and deepFillPossibles() to reduce memory consumption
//in the next stages. In many cases the Sudoku gets solved at this stage itself.
private void solution(boolean first)
{
fillPossibles();
for(int i=0;i<9;i++)
if(deepFillPossibles(i,0,i+1,9,first) || deepFillPossibles(0,i,9,i+1,first) ||
deepFillPossibles(i/3*3,i%3*3,i/3*3+3,i%3*3+3,first))
i=-1;
}
//This function is the most challenging. No Sudoku can ever escape solution after
//passing this stage. It uses ECHO TREE logic implementing brute force to check all
//kinds of combinations until solution is obtained. It returns a null for no solution.
private void treeSolution(Tracker track)
{
if(SUDOKU==null)
{
track.TRACK_SUDOKU=null;
return;
}
solution(false);
//For a genuine sudoku the statement could have been replaced by "fillPossibles();"
//(Only it would make solving slower and increase memory consumption!)
//But it is risky if there is a doubt regarding the genuineness of the Sudoku
int a=-1,b=-1;
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(SUDOKU[i][j].length()>1 && a==-1)
{
a=i; b=j;
}
else if(SUDOKU[i][j].length()==0)
return;
if(a==-1)//checks if the Sudoku is solved or not setting necessary flags
{
track.TRACK_SOLUTION++;
for(int i=0;i<9;i++)
for(int j=0;j<9;track.TRACK_SUDOKU[i][j]=SUDOKU[i][j],j++);
return;
}
String temp[][]=new String[9][9];
for(int k=0;k<SUDOKU[a][b].length();k++)
{
for(int i=0;i<9;i++)
for(int j=0;j<9;temp[i][j]=SUDOKU[i][j],j++);
temp[a][b]=String.valueOf(SUDOKU[a][b].charAt(k));
new Sudoku(temp).treeSolution(track);
//The following condition stops the braching TREE if the Sudoku is solved by
//echoing back to the root, depending on the flags set on being solved
if(track.TRACK_SOLUTION==2 || track.TRACK_SUDOKU==null)
return;
}
return;
}
//This is the last function which has public access and can be called by the user.
//It sets the Sudoku as null if non-genuine and VALIDity as false if no unique solution
public void solve()
{
try
{
for(int i=0;i<9;SUDOKU[i][8]=SUDOKU[i][8],SUDOKU[8][i]=SUDOKU[8][i],i++);
}
catch(Exception e)
{
SUDOKU=null; VALID=false; return;
}
Tracker track=new Tracker();
solution(true);
treeSolution(track);
SUDOKU=track.TRACK_SOLUTION==0?null:track.TRACK_SUDOKU;
if(track.TRACK_SOLUTION!=1)
VALID=false;
}
}
//the following class is purposely designed to easily track the changes during solution,
//including the nature of the Sudoku and the solution of the Sudoku(if possible)
class Tracker
{
protected int TRACK_SOLUTION=0;
protected String TRACK_SUDOKU[][]=new String[9][9];
}
public class SudokuSolver extends JApplet implements KeyListener, MouseListener
{
private String SUDOKU[][]=new String[9][9];
private Sudoku SDK=new Sudoku();
private Image BOX[][]=new Image[9][9],SELECT_IMG;//BOX is an array of square images
private int SELECT_ROW=4,SELECT_COLUMN=4;//stores the position of the selection box
//this function is used to initialize the coloured square images and fonts
public void init()
{
resize(190,190);
setFont(new Font("Dialog",Font.BOLD,12));
addMouseListener(this);
addKeyListener(this);
Graphics g;
for(int i=0;i<9;i++)
for(int j=0;j<9;SUDOKU[i][j]="",j++)
{
BOX[i][j]=createImage(22,22);
g=BOX[i][j].getGraphics();
if((i/3+j/3)%2==0)
g.setColor(Color.yellow);
else
g.setColor(Color.green);
g.fillRect(0,0,21,21);
g.setColor(Color.black);
g.drawRect(0,0,21,21);
}
//the following statements colour the selection box red
SELECT_IMG=createImage(22,22);
g=SELECT_IMG.getGraphics();
g.setColor(Color.red);
g.fillRect(0,0,21,21);
g.setColor(Color.black);
g.drawRect(0,0,21,21);
}
public void mouseExited(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseReleased(MouseEvent e){}
public void mouseClicked(MouseEvent e){}
//the following function handles the mouse click operations
public void mousePressed(MouseEvent e)
{
if(SDK.SUDOKU==null)
{
SDK.SUDOKU=new String[9][9];
for(int i=0;i<9;i++)
for(int j=0;j<9;SUDOKU[i][j]="",SDK.SUDOKU[i][j]="",j++);
SDK.VALID=true;
}
if(e.getY()<190 && e.getX()<190 && e.getY()%21!=0 && e.getX()%21!=0)
{
SELECT_ROW=e.getY()/21;
SELECT_COLUMN=e.getX()/21;
}
repaint();
}
public void keyReleased(KeyEvent e){}
//this function manages the operations associated with the various keys
public void keyPressed(KeyEvent e)
{
int code=e.getKeyCode();
if(code==KeyEvent.VK_DELETE || code==KeyEvent.VK_BACK_SPACE || code==KeyEvent.VK_SPACE)
{
SUDOKU[SELECT_ROW][SELECT_COLUMN]=""; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="";
}
switch(code)
{
case KeyEvent.VK_Y : if(SDK.SUDOKU!=null) SDK.VALID=true; break;
case KeyEvent.VK_UP : SELECT_ROW=(SELECT_ROW+8)%9; break;
case KeyEvent.VK_DOWN : SELECT_ROW=(SELECT_ROW+1)%9; break;
case KeyEvent.VK_LEFT : SELECT_COLUMN=(SELECT_COLUMN+8)%9; break;
case KeyEvent.VK_RIGHT : SELECT_COLUMN=(SELECT_COLUMN+1)%9; break;
case KeyEvent.VK_ENTER : SDK.solve(); break;
case KeyEvent.VK_N : if(SDK.SUDOKU!=null){ SDK.VALID=true; code=KeyEvent.VK_ESCAPE;}
case KeyEvent.VK_ESCAPE : if(SDK.SUDOKU==null)
{
SDK.SUDOKU=new String[9][9];
for(int i=0;i<9;i++)
for(int j=0;j<9;SUDOKU[i][j]="",SDK.SUDOKU[i][j]="",j++);
SDK.VALID=true;
}
else
for(int i=0;i<9;i++)
for(int j=0;j<9;SDK.SUDOKU[i][j]=SUDOKU[i][j],j++);
}
repaint();
}
//this function is for entering the numbers in the sudoku grid
public void keyTyped(KeyEvent e)
{
char code=e.getKeyChar();
if(Character.isDigit(code))
switch(code)
{
case '1': SUDOKU[SELECT_ROW][SELECT_COLUMN]="1"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="1"; break;
case '2': SUDOKU[SELECT_ROW][SELECT_COLUMN]="2"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="2"; break;
case '3': SUDOKU[SELECT_ROW][SELECT_COLUMN]="3"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="3"; break;
case '4': SUDOKU[SELECT_ROW][SELECT_COLUMN]="4"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="4"; break;
case '5': SUDOKU[SELECT_ROW][SELECT_COLUMN]="5"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="5"; break;
case '6': SUDOKU[SELECT_ROW][SELECT_COLUMN]="6"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="6"; break;
case '7': SUDOKU[SELECT_ROW][SELECT_COLUMN]="7"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="7"; break;
case '8': SUDOKU[SELECT_ROW][SELECT_COLUMN]="8"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="8"; break;
case '9': SUDOKU[SELECT_ROW][SELECT_COLUMN]="9"; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="9"; break;
default : SUDOKU[SELECT_ROW][SELECT_COLUMN]=""; SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN]="";
}
}
//the paint() function is designed to print the the sudoku grid and other messages
public void paint(Graphics g)
{
if(!SDK.VALID)
{
g.setColor(Color.white);
g.fillRect(1,1,188,188);
g.setColor(Color.black);
g.drawRect(0,0,189,189);
if(SDK.SUDOKU==null)
{
g.drawString("INVALID SUDOKU!!",45,80);
g.drawString("[PRESS ESCAPE TO RE-ENTER]",10,105);
}
else
{
g.drawString("INCOMPLETE SUDOKU!!",30,60);
g.drawString("Would you like to see a",30,90);
g.drawString("possible solution?",45,105);
g.drawString("Y / N",80,120);
}
}
else
{
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
{
g.drawImage(BOX[i][j],j*21,i*21,null);
g.drawString(SDK.SUDOKU[i][j],8+j*21,15+i*21);
}
g.drawImage(SELECT_IMG,SELECT_COLUMN*21,SELECT_ROW*21,null);
g.drawString(SDK.SUDOKU[SELECT_ROW][SELECT_COLUMN],8+SELECT_COLUMN*21,15+SELECT_ROW*21);
}
}
}
I have tried to use minimum brute force wherever possible

Related

How to debug my poker game recursion logic?

public static ArrayList<Hand> getPossibleHands(Hand h) {
ArrayList<Hand> allPossible = new ArrayList<Hand>();
addNext(allPossible, h);
return allPossible;
}
public static void addNext(ArrayList<Hand> poss, Hand h) {
if (h.cards.size() == 5)
poss.add(h);
else
for (int i = 0; i < 52; i++) {
Card c = Card.makeCard(i);
if (!h.contains(c))
h.add(c);
addNext(poss,h);
}
}
The code above is supposed to essentially take an incomplete poker board (anywhere from 0-4 cards) and return all possible complete boards (5 cards). The logic that I feel like it should follow is as follows: recurse through every combination of adding valid (not on the board already) cards until the size of the board is equal to 5 in which case it will add the board to the list and skip over the rest of the function.
However upon using a print statement at the beginning of the function I see that hand sizes of greater than 5 or being created. Since the first part of the function should catch all hands at 5 and terminate it there, I don't see how the code executes at the rest of the function.
Your class should receive stack overflow with an empty hand.
You send new Card(0) to the hand. This is added.
Then you call add next again - and the 'for' starts from 0 again. Checks adds 1. Then starts from 0 - it is there, does not add anything and starts over. Where it starts from 0. Does not do anything. Starts from 0. Ad infinum -> StackOverFlow.
You also need to reset to previous state of the hand every time you finish with 5 cards and backtrack.
If you want a recursive solution you can try :
private static ArrayList<Hand> getPossibleHands(Hand h) {
ArrayList<Integer> except;
if (h.cards == null) except = new ArrayList<>();
else
except = h.cards.stream().map(c -> (c.getCard())).collect(Collectors.toCollection(ArrayList::new));
ArrayList<Hand> allPossible = new ArrayList<>();
addNext(allPossible, h, except);
return allPossible;
}
private static void addNext(ArrayList<Hand> poss, Hand h, ArrayList<Integer> except) {
//assuming hands 0-4 - we don't need to check on entry, only when we add
Hand localHand = h.copy();
for (int i = 0; i < 52; i++) {
if (except.contains(i)) continue;
Card c = Card.makeCard(i);
if (!localHand.contains(c)) {
addNext(poss, localHand.copy(), copyExcept(except, i));
localHand.add(c);
if (localHand.cards.size() == 5) {
poss.add(localHand);
break;
}
}
}
}
private static ArrayList<Integer> copyExcept(ArrayList<Integer> except, int i) {
ArrayList<Integer> clonedExcept = new ArrayList<>(except);
clonedExcept.add(i);
return clonedExcept;
}
import java.util.ArrayList;
public class Hand {
ArrayList<Card> cards = new ArrayList<>();
public boolean contains(Card c) {
for (Card card : cards) {
if (card.getCard() == c.getCard())
return true;
}
return false;
}
public void add(Card c) {
cards.add(c);
}
Hand copy() {
Hand temp = new Hand();
for (Card c : cards) {
temp.add(new Card(c.getCard()));
}
return temp;
}
}
class Card {
private int card;
public Card(int card) {
this.card = card;
}
public static Card makeCard(int i) {
return new Card(i);
}
public int getCard() {
return card;
}
}
Initially, h is (presumably) empty. So addNext will loop through all possible cards, and since none of them are in the hand, add each card to the hand, regardless of how many cards are currently in the hand.
It looks to me like your for loop is eventually adding whole deck to hand.
In your loop you run 52 iterations. On each iteration you (conditionally) add a card to the hand and then you call your function recursively. But after that recursive call has returned, you go to the next iteration and again add a card to the hand.
So that '5 card restriction' does not restrict anything here.

Weird behavior in Java While Loop [duplicate]

This question already has an answer here:
Loop doesn't see value changed by other thread without a print statement
(1 answer)
Closed 7 years ago.
I am writing a basic Tic-Tac-Toe Single player game using basic swing graphics. I completed the game, but there is a weird problem I am facing. At one place, I used a while loop with a SOP statement. If I omit this statement, program works differently and nothing happens (like some kind of infinite loop), and if I keep it, it works just fine. I don't know what's happening in the code. Please help.
Below is the source code which causing problem. Sorry for my amateur coding style.
import java.util.Random;
public class SinglePlayer implements Runnable{
public final int MINIMUM = -1000000;
private GameBoard game;
public SinglePlayer(){
game = new GameBoard("Single Player");
}
public static void main(String[] args){
SinglePlayer gameSingle = new SinglePlayer();
gameSingle.run();
}
public void run(){
boolean machinePlayed = true, userPlayed = false;
// Outer loop is to maintain re-match option of program
while(this.game.quitTwoPlayer == false){
// Inner loop is a single game b/w user and machine
while(this.game.GameQuitStatus() == false){
/* I kept two conditions to switch b/w machine and user mode
* of game and they just keep changing to simulate the game
* b/w machine and user.
*/
if(machinePlayed == false && userPlayed){
try {
MachineMove("O");
} catch (CloneNotSupportedException e) {
e.printStackTrace();
break;
}
this.game.ChangePlayerLabels();
machinePlayed = true;
userPlayed = false;
}
else if(machinePlayed && userPlayed == false){
int earlierCount = this.game.CountSteps();
/* THIS IS THE WHILE LOOP I AM TALKING ABOUT.
* If I omit the print statement inside the body of loop,
* program behaves differently, but when I keep it,
* it working just fine.
* */
while(earlierCount == this.game.CountSteps()){
System.out.println("Player User thinking");
}
this.game.ChangePlayerLabels();
machinePlayed = false;
userPlayed = true;
}
this.game.DeclareResult();
}
this.game.dispose();
}
}
public void MachineMove(String player) throws CloneNotSupportedException{
/* If board is empty, play at center of the board */
if(this.game.CountSteps() == 0){
this.game.MakeMove(1, 1);
}
/* If center is blank, play it there. Otherwise, pick a corner randomly */
else if(this.game.CountSteps() == 1){
if(this.game.IsEmpty(1, 1))
this.game.MakeMove(1, 1);
else{
Random randomNum = new Random();
int num = randomNum.nextInt(4);
if(num == 0)
this.game.MakeMove(0, 0);
else if(num == 1)
this.game.MakeMove(2, 0);
else if(num == 2)
this.game.MakeMove(0, 2);
else if(num == 3)
this.game.MakeMove(2, 2);
}
}
else{
/* If the next move is such that it should be taken, otherwise opponent will win */
String opponent = "";
if(this.game.GetCurrentPlayer().equals("O"))
opponent = "X";
else
opponent = "O";
for(int i = 0; i<3; i++){
for(int j = 0; j<3; j++){
if(this.game.IsEmpty(i,j)){
GameBoard tempGame = new GameBoard(this.game, "Single Player");
tempGame.MakePossibleMove(i, j, opponent);
if(tempGame.GameWinner().equals(opponent + " wins")){
this.game.MakeMove(i,j);
return;
}
}
}
}
/* If the next move is not such that if missed, game is lost, then play most optimal move towards winning */
Move tempMove = new Move(MINIMUM, 0, 0);
Move bestMove = new Move(MINIMUM, 0, 0);
for(int i = 0; i<3; i++){
for(int j = 0; j<3; j++){
if(this.game.IsEmpty(i,j)){
GameBoard tempGame = new GameBoard(this.game, "Single Player");
tempMove = MakeMoves(tempGame, i, j);
if(tempMove.score > bestMove.score){
bestMove.row = tempMove.row;
bestMove.col = tempMove.col;
bestMove.score = tempMove.score;
}
}
}
}
this.game.MakeMove(bestMove.row, bestMove.col);
}
}
public Move MakeMoves(GameBoard tempGame, int row, int col){
String player = tempGame.GetCurrentPlayer();
tempGame.MakeMove(row, col);
if(tempGame.GameWinner().equals("Match Draw")){
return new Move(0, row, col);
}
else if(tempGame.GameWinner().equals("X wins")){
if(player.equals("X")){
return new Move(1, row, col);
}
else{
return new Move(-1, row, col);
}
}
else if(tempGame.GameWinner().equals("O wins")){
if(player.equals("O")){
return new Move(1, row, col);
}
else{
return new Move(-1, row, col);
}
}
else{
Move bestMove = new Move(MINIMUM, 0, 0);
Move tempBestMove = new Move(0, 0, 0);
for(int i = 0; i<3; i++){
for(int j = 0; j<3; j++){
if(tempGame.IsEmpty(i,j)){
GameBoard newGame = new GameBoard(tempGame, "Single Player");
tempBestMove = MakeMoves(newGame, i, j);
if(tempBestMove.score > bestMove.score)
bestMove = tempBestMove;
}
}
}
return bestMove;
}
}
}
class Move{
public int score;
public int row;
public int col;
public Move(int score, int row, int col){
this.score = score;
this.row = row;
this.col = col;
}
}
Your loop is likely typing up your processor, and the SOP slows the loop enough to allow other processes to occur. But regardless and most importantly, you don't want to have this loop present in the first place. You state that you have a,
Tic-Tac-Toe Single player game using basic swing graphics
Remember that Swing is an event driven GUI library, so rather than loop as you would in a linear console program, let events occur, but respond to them based on the state of the program.
In other words, give your class several fields including a boolean variable that tells whose turn it is, such as boolean playersTurn, a boolean variable gameOver, ..., and change the state of these variables as the game is played, and base the games behavior depending on these states. For instance the game would ignore the player's input if it was not his turn.

Javafx - Syncronization delay - card flips

I'm actually working on a memory game and I'm stuck at the point where I should write the gameplay-part of the game.
So:
I have an array of N card objects. Each object has an attribute called cardNum - an identifier. I think I should write an actionListener on that array, so when I flip a card, it puts the flipped card's cardNum in an array of two elements and if the two elements of the array are equal, a pair is found.
The problem is that I just don't know how to get the last flipped card's cardNum.
Any help would be appreciated.
Here's the way I tried:
private void easyGame(Card[] cards) {
int flippedCards = 0;
int card1;
while(flippedCards != 24) {
for(int i=0; i<cards.length; i++) {
if(cards[i].getIsFlipped())
flippedCards ++;
}
if(flippedCards % 2 == 0 && flippedCards > 0)
for(int i=0; i<cards.length; i++) {
card1 = getCardIndByCardNum(cards[i].getCardNum(), cards, i);
if(!cards[card1].getIsFlipped()) {
for(int j=0; j<cards.length; j++) {
if(cards[i].getIsFlipped())
cards[i].flip();
}
flippedCards = 0;
break;
}
}
}
}
The problem is that if I call this method, the game won't be drawn. May I use use threads somehow?
EDIT
Here is how I get the indexes of the clicked cards, and I call it in the UI:
private void setCardHandlers() {
for(final Card card : cards) {
card.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
clickedCardInd = getChildren().indexOf(card)-1;
}
});
}
}
Than here is how I am using it:
setOnMouseReleased(new EventHandler<MouseEvent> () {
#Override
public void handle(MouseEvent t) {
int cardIndex = clickedCardInd; // get index of what user clicked
clickedCardInd = -1;
if (cardIndex != -1 && moveRequestedFlag) { // our controller waits for the move
// get the position and report
moveRequestedFlag = false; // we handled the move
//System.out.println(cardIndex);
nextMove.setMove(cardIndex); // this will unblock controller's thread
}
}
});
It has a delay on fliping cards, also in the easyGame the requestMove method sets both indexes to the same.
I would recommend splitting you responsibilities a bit into Model/View/Controller modules, which, in simplest case would look like :
Model - your game current state and data, i.e. cards array Cards mCards = new Cards[24];
View - your UI, that can reflect current state of mCards(model) on screen in Main thread
Controller - your main game logic. This is most complex part, responsible for
requesting/handling user move,
updating mCards(model) based on user move,
Requesting UI to re-draw.
Contoroller's code (easyGame method) should run on separate thread to not block the UI.
Below I sketched a skeleton code that should fit your requirements :
class Game {
/*
* controller - main logic
*/
void startEasyGame() {
// initialize cards array, shuffle if needed
// we start with zero cards flipped
int flippedCards = 0;
// main loop
while (flippedCards != mCards.length) {
// 1. show updated UI
mBoard.showUpdatedCards();
// 2. request player move
// and block current thread to wait till move is done
// the result of the move - index of the card
int index1 = requestMove();
// temporarily flip first card face-up
mCards[index1].flip();
// show it on screen
mBoard.showUpdatedCards();
// same for second card
int index2 = requestMove();
mCards[index2].flip();
mBoard.showUpdatedCards();
// 3. check the result
if (mCards[index1].getCardNum() == mCards[index2].getCardNum()) {
// hooray, correct guess, update count
// possibly show some encouraging feedback to user
flippedCards += 2;
} else {
// incorrect, flip cards back face down
mCards[index1].flip();
mCards[index2].flip();
}
} // end of while loop
// game ended -> show score and time
mBoard.showResult();
}
}
EDIT
Extra details on how to await for result from UI thread :
int requestMove() {
// 1. show user prompt to make a move
// ...
// 2. construct latch to wait for move done on UI thread
mBoard.moveRequestedFlag = true;
NextMove nextMove = new NextMove();
mBoard.nextMove = nextMove;
// 3. await for move and get the result
return nextMove.getMove();
}
then, somewhere in UI code :
// handling card onClick somewhere on UI thread
if (mBoard.moveRequestedFlag) { // our controller waits for the move
// get the position and report
int cardIndex = ... // get index of what user clicked
mBoard.moveReqestedFlag = false; // we handled the move
mBoard.nextMove.setMove(cardIndex); // this will unblock controller's thread
}
and NextMove utility class to sync threads :
public class NextMove {
private volatile int mCardIndex;
private final CountDownLatch mMoveReady = new CountDownLatch(1);
public int getMove() throws InterruptedException {
mMoveReady.await();
return mCardIndex;
}
public synchronized void setMove(int selectedCardIndex) {
if (mMoveReady.getCount() > 0) {
mCardIndex = selectedCardIndex;
mMoveReady.countDown();
}
}
}

Java: Trying To Get User Input As Int In Simon Game

I'm trying to figure out why 1) my Simon game hangs up after saying "Enter A Number"- it doesn't look like it even gets past validation. I'm trying to get user input, and check to see if that was the right number to press at that time. 2) Also, it used to generate a random number but when the user pressed it, it came back as false for some reason. Some other random number would pass though. 3) Also, is the code below color coded for you? Thanks guys.
import acm.program.*;
import acm.graphics.*;
import java.awt.Color;
import java.awt.Font;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.JOptionPane;
import java.util.Scanner;
import java.io.DataInputStream;
public class Simon extends Program implements ActionListener
{
Scanner usersInputScanner = new Scanner(System.in);
private int array[];
private int currentSeqLength;
private int usersInput;
private String usersInputString;
public Simon()
{
//Initialize Class Values
array = new int[20];
currentSeqLength = 1;
usersInput = 0;
generateSequence();
while(currentSeqLength < array.length)
{
playSequence();
//Wait For User's Input, Assign To Variable
System.out.println("Enter A Number");
usersInput = usersInputScanner.nextInt();
if (pushButton(usersInput) == true)
{
System.out.println("You Entered: " + usersInput);
currentSeqLength++;
}
else
{
gameOverMessage();
break;
//Reset Variables:
}
}
}
//----------------------- Methods Here On Down -----------------------------
public void generateSequence()
{
//Fill Array With Random Numbers
for (int i = 0; i < array.length; i++ )
{
array[i] = (int)(Math.random()*4);
}
}
public void setLength(int length)
{
//Set Current Length To Size Of Given Argument
currentSeqLength = length;
}
int getLength()
{
return currentSeqLength;
}
int[] playSequence()
{
//Print Out The Current Sequence
//New Local Array To Return
int newArray[]= new int[currentSeqLength];
//Repeat As Many Times As Value Of currentSeqLength
for(int i = 0; i < currentSeqLength ; i++)
{
System.out.println(array[i]);
//Return an array of int's to the player.
newArray[i] = array[i];
}
return newArray;
}
boolean pushButton(int usersInput)
{
//Given A Button Press (0-3), Return Whether That Was The
//Correct Button To Play At The Moment
if (usersInput == array[currentSeqLength])
{
return true;
}
else
{
return false;
}
}
boolean isTurnOver()
{
//If Current Sequence Length Matches Or Exceeds Value Of
//Array Element In Location Of Current Sequence Length
if (currentSeqLength >= array[currentSeqLength])
{
return true;
}
else
{
return false;
}
}
//Not Needed?
boolean isGameOver()
{
if (pushButton(usersInput) == false)
{
return true;
}
else
{
return false;
}
}
String gameOverMessage()
{
return "Game Over";
}
/*public void actionPerformed(ActionEvent event)
{
int input;
}
*/
}
1) my Simon game hangs up after saying "Enter A Number"- it doesn't
look like it even gets past validation.
It's working fine, but you need to System.out.println the string that is returned from gameOverMessage(). Right now, it runs perfectly but there's no output to the console, so it looks unresponsive (and it's not hanging, it just reaches the end of execution, and stops).
else {
gameOverMessage();
break;
}
should be
else {
System.out.println(gameOverMessage());
}
2) Also, it used to generate a random number but when the user pressed
it, it came back as false for some reason.
I'm not getting this behavior in your sample code, it looks like it works as expected.
3) Also, is the code below color coded for you?
Yep, the preview box on SO sometimes takes a second to do the syntax highlighting. It works fine though.
Incidentally, 3 questions in one is a bit of a slog for the answerer. In the future perhaps try to limit yourself to one :)

How can I convert existing procedural code to use classes?

I'm trying to learn Java and basically my approach has been to take the procedural style I learned with python, and apply it to Java. So I never use classes and just put everything in a single class with many methods(which I just use as python functions). I think I've hit a problem, and need to bite the bullet and use classes, but I'm having trouble wrapping my head around how to do it.
To simplify my problem(ignore the poor design- it's just to illustrate the point), I have a program that takes a list and within a for loop does some math on each item(in this case adds 1 to the value of the list). I only want it to do work on 2 items on the list and then stop(in this example it's the first 2 items but in my real program it could be anywhere in the list). Here's the working code that is similar to how I'm already doing it:
No Classes:
public class LearningClasses {
public static void main(String[] args) {
int[] list = new int[]{1,2,3,4,5,6,7,8,9,10};
int[] data_list = new int[list.length];
for (int current_location = 0; current_location<list.length;current_location++) {
for (int i =0; i<100; i++){
if (check_size(data_list) == false ) {
break;
}
data_list[current_location] = (list[current_location]+1);
}
}
//its done now lets print the results
for (Integer item : data_list) {
System.out.println(item);
}
}
private static boolean check_size(int[] data_list) {
// TODO Auto-generated method stub
int count = 0;
for (int item : data_list) {
if (item != 0) {
count++;
if (count>=2) {
break;
}
}
}
if (count>=2) {
return false;
} else {
return true;
}
}
}
The problem with this code is although it works it's inefficient because it calculates the count on every iteration of the second for loop. In my program I cannot put anything above the first for loop but I can put anything below it, so I thought instead of doing the count every time maybe I could use a class to somehow maintain state and just increment the number as oppose to recalculating every time.
With classes:
public class LearningClassesCounter {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] list = new int[]{1,2,3,4,5,6,7,8,9,10};
int[] data_list = new int[list.length];
for (int current_location = 0; current_location<list.length;current_location++) {
//can only put commands in here. Nothing above.
Counter checker = new Counter(data_list);
System.out.println(checker.check_data());
for (int i =0; i<100; i++){
data_list[current_location] = (list[current_location]+1);
}
}
//its done now lets print the results
for (Integer item : data_list) {
System.out.println(item);
}
}
}
class Counter {
private int count; // current value
private boolean continue_or_not;
private int[] data_list;
// create a new counter with the given parameters
public Counter(int[] data_list) {
data_list = this.data_list;
count = 0;
continue_or_not = true;
}
public boolean check_data() {
// TODO Auto-generated method stub
int count = 0;
for (int item : data_list) {
if (item != 0) {
count++;
if (count>=3) {
break;
}
}
}
if (count>=3) {
return false;
} else {
return true;
}
}
// increment the counter by 1
public void increment() {
count++;
}
// return the current count
public int value() {
return count;
}
}
This doesn't work because it thinks the data_list is a null pointer(I know I'm declaring it null, but if I make it private int[] data_list = data_list it doesn't compile either). My ultimate goal is to have some kind of controls, in this case its limiting it to 2 items but I want to also add other limits like total value of al items cannot exceed X or cannot be lower than X and want to save CPU power by not having to do full calculations every time. So I think I need to be able to increment the values and then need to check that those increments haven't exceeded thresholds.
Can anyone help me understand what I'm doing wrong? Am I only wrong with syntax; or am I designing this wrong?
//can only put commands in here. Nothing above.
Counter checker = new Counter(data_list);
System.out.println(checker.check_data());
When you are calling checker.check_data(), its trying to parse through the data_list, but its empty. So, it throws a NullPointerException. The data_list is empty because inside your constructor, you may need to initialize like this this.data_list = data_list instead of data_list = this.data_list (here this.data_list has no reference so NULL)
If you avoid that call, the output will be 2,3,4,5,6,7,8,9,10,11.

Categories

Resources