java boxes mask not working - java

The idea here is to create a grid of boxes. underneath the black grid is another grid of multi-colored boxes. when you click a box it's mask disappears showing the colored box beneath. You then click a second box if the colors match hurray, if not then the game continues. Here is the code.
import java.applet.Applet;
import java.awt.Button;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class GuessingGame extends Applet{
/**
*
*/
private static final long serialVersionUID = 1L;
private final int START_X = 20;
private final int START_Y = 40;
private final int ROWS = 4;
private final int COLS = 4;
private final int BOX_WIDTH = 20;
private final int BOX_HEIGHT = 20;
//this is used to keep track of boxes that have been matched.
private boolean matchedBoxes[][];
//this is used to keep track of two boxes that have been clicked.
private MaskableBox chosenBoxes[];
private MaskableBox boxes[][];
private Color boxColors[][];
private Button resetButton;
public void init() {
boxes = new MaskableBox[ROWS][COLS];
boxColors = new Color[ROWS][COLS];
resetButton = new Button("Reset Colors");
resetButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
randomizeColors();
buildBoxes();
repaint();
}
});
add(resetButton);
//separate building colors so we can add a button later
//to re-randomize them.
randomizeColors();
buildBoxes();
}
public void paint(Graphics g) {
for (int row =0; row < boxes.length; row ++) {
for (int col = 0; col < boxes[row].length; col++) {
if(boxes[row][col].isClicked()) {
//boxes[row][col].setMaskColor(Color.black);
//boxes[row][col].setMask(!boxes[row][col].isMask());
//boxes[row][col].setClicked(false);
//}
if (!matchedBoxes[row][col]) {
gameLogic(boxes[row][col]);
//boxes[row][col].draw(g);
}
}
}
}
//loop through the boxes and draw them.
for (int row = 0; row < boxes.length; row++) {
for (int col = 0; col < boxes[row].length; col++) {
boxes[row][col].draw(g);
}
}
}
public void gameLogic(MaskableBox box) {
if ((chosenBoxes[0] != null)&&(chosenBoxes[1] != null)) {
if(chosenBoxes[0].getBackColor() == chosenBoxes[1].getBackColor()) {
for (int i=0; 0 < chosenBoxes.length; i++ ) {
for(int row = 0; row < boxes.length; row++) {
for(int col = 0; col < boxes[row].length; col++) {
if( boxes[row][col] == chosenBoxes[i] ) {
System.out.println("boxes [row][col] == chosenBoxes[] at index: " + i );
matchedBoxes[row][col] = true;
break;
}
}
}
}
}else {
chosenBoxes[0].setMask(true);
chosenBoxes[1].setMask(true);
}
chosenBoxes = new MaskableBox[2];
}else {
if (chosenBoxes[0] == null) {
chosenBoxes[0] = box;
chosenBoxes[0].setMask(false);
return;
}else{
if (chosenBoxes[1] == null) {
chosenBoxes[1] = box;
chosenBoxes[1].setMask(false);
}
}
}
}
private void removeMouseListeners() {
for(int row = 0; row < boxes.length; row ++) {
for(int col = 0; col < boxes[row].length; col++) {
removeMouseListener(boxes[row][col]);
}
}
}
private void buildBoxes() {
// need to clear any chosen boxes when building new array.
chosenBoxes = new MaskableBox[2];
// create a new matchedBoxes array
matchedBoxes = new boolean [ROWS][COLS];
removeMouseListeners();
for(int row = 0; row < boxes.length; row++) {
for(int col = 0; col < boxes[row].length; col++) {
boxes[row][col] =
new MaskableBox(START_X + col * BOX_WIDTH,
START_Y + row * BOX_HEIGHT,
BOX_WIDTH,
BOX_HEIGHT,
Color.gray,
boxColors[row][col],
true,
true,
this);
addMouseListener(boxes[row][col]);
}
}
}
private void randomizeColors() {
int[] chosenColors = {0,0,0,0,0,0,0,0};
Color[] availableColors = {Color.red, Color.blue, Color.green,
Color.yellow, Color.cyan, Color.magenta, Color.pink, Color.orange };
for(int row = 0; row < boxes.length; row++) {
for (int col = 0; col < boxes[row].length; col++) {
for (;;) {
int rnd = (int) (Math.random() * 8);
if (chosenColors[rnd]< 2) {
chosenColors[rnd]++;
boxColors[row][col] = availableColors[rnd];
break;
}
}
}
}
}
}
here is the second batch of code containing maskablebox
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
public class MaskableBox extends ClickableBox {
private boolean mask;
private Color maskColor;
Container parent;
public MaskableBox(int x, int y, int width, int height, Color borderColor,
Color backColor, boolean drawBorder, boolean mask, Container parent ) {
super(x, y, width, height, borderColor, backColor, drawBorder, parent);
this.parent = parent;
this.mask = mask;
}
public void draw(Graphics g) {
if(mask=false) {
super.draw(g);
// setOldColor(g.getColor());
// g.setColor(maskColor);
// g.fillRect(getX(),getY(),getWidth(), getHeight());
// if(isDrawBorder()) {
// g.setColor(getBorderColor());
// g.drawRect(getX(),getY(),getWidth(),getHeight());
// }
// g.setColor(getOldColor());
}else {
if(mask=true) {
//super.draw(g);
setOldColor(g.getColor());
g.setColor(maskColor);
g.fillRect(getX(),getY(),getWidth(), getHeight());
if(isDrawBorder()) {
g.setColor(getBorderColor());
g.drawRect(getX(),getY(),getWidth(),getHeight());
}
g.setColor(getOldColor());
}
}
}
public boolean isMask() {
return mask;
}
public void setMask(boolean mask) {
this.mask = mask;
}
public Color getMaskColor() {
return maskColor;
}
public void setMaskColor(Color maskColor) {
this.maskColor = maskColor;
}
}
I now get these error messages.
Exception in thread "AWT-EventQueue-1" java.lang.NullPointerException
at GuessingGame.gameLogic(GuessingGame.java:74)
at GuessingGame.paint(GuessingGame.java:55)
at java.awt.Container.update(Container.java:1801)
at sun.awt.RepaintArea.updateComponent(RepaintArea.java:239)
at sun.awt.RepaintArea.paint(RepaintArea.java:216)
at sun.awt.windows.WComponentPeer.handleEvent(WComponentPeer.java:306)
at java.awt.Component.dispatchEventImpl(Component.java:4706)
at java.awt.Container.dispatchEventImpl(Container.java:2099)
at java.awt.Component.dispatchEvent(Component.java:4460)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

Look, the error message you see there, is called: "the stack trace"
It contains very helpful information on what the error was:
Exception in thread "AWT-EventQueue-1" java.lang.ArrayIndexOutOfBoundsException: 2
at GuessingGame.gameLogic(GuessingGame.java:77) // This is where the error happened
at GuessingGame.paint(GuessingGame.java:55) // This is your code
at java.awt.Container.update(Container.java:1801) // not your code
at sun.awt.RepaintArea.updateComponent(RepaintArea.java:239) // not your code
If you see it says: GuessingGame.java:77 that means the error occurred in the file GuessingGame.java in the line 77. The other files are not in your source code ( Container.java and Repaint.java ) so, they are not where the problem happened.
In GuessingGame.java at line 77, you attempted to access an index beyond the array bounds ( hence ArrayIndexOutOfBoundException ) furthermore, the number 2 it the index you tried to use.
With that information you can take a look at the source code and see that line 77 ( at least from the posted code ) is:
if( boxes[row][col] == chosenBoxes[i] ) {
So, boxes is not the problem ( its size is 4 it was declared asboxes = new MaskableBox[ROWS][COLS]; and both ROWS and COLS are declared as 4 ). So the responsible must be: chosenBoxes
It was initialized as:
chosenBoxes = new MaskableBox[2];
Size 2, that means the only valid indexes are 0 and 1. When you attempt to use index=2 the exception raised.
Now having this information in mind you may search for code that might cause it.
As other answers points the cause is
for (int i=0; 0 <= chosenBoxes.length; ++i ) {
Just 3 lines above!. It reads, "while zero is lower or equals than 2..." which will always return true, because 0 will always be < than 2.
So, you just have to fix that part and re-test.
I hope this explanation help you to solve this kind of problems in the future. It is very important to learn to read the stacktrace.
I can even imagine an acronym in the future: RTFST ;)

In your gameLogic method you have chosenBoxes which has two elements, but you loop to i <= chosenBoxes.length (which would loop i = 0, 1, 2). That's why you get the exception. Change it to i < chosenBoxes.length.
Edit
In fact, you have:
for (int i=0; 0 <= chosenBoxes.length; ++i ) {
change that to:
for (int i=0; i < chosenBoxes.length; i++ ) {
I'm not sure why you were prefix incrementing i in that loop, either.

Here:
for (int i=0; 0 <= chosenBoxes.length; ++i ) {
Your condition is always true ;)

Related

Issues exporting Processing sketch to PDF: sketch gets cropped

I am trying to export my sketch to pdf. The issue that I have is that for some reason only a portion of my sketch is exported to pdf, as if the original sketch was cropped! When I run my sketch, 64 lines show up (as intended) and indeed when I save it to png all the 64 lines are there and sketch looks just the same as when I run it.
When I export the sketch to pdf though, only 16 line show up, as if the pdf was cropping my sketch and only the cropped portion was being exported.
This is the png showing what the sketch is supposed to look like:
This is what the pdf has exported:
And this is my code:
import processing.pdf.*;
import java.util.Random;
int cols, rows;
int videoScale = 100;
boolean recordPDF = false;
void setup() {
size(800,800);
pixelDensity(2);
frameRate(0.5);
cols = width/videoScale;
rows = height/videoScale;
}
void draw() {
if (recordPDF) {
beginRecord(PDF, "pdfs/" + str(random(1000)) + ".pdf");
}
background(255);
strokeWeight(1.5);
drawLines();
if (recordPDF) {
endRecord();
recordPDF = false;
println("Printed pdf.");
}
}
void keyPressed() {
if (key == 'p') {
recordPDF = true;
}
if (key == 's') {
saveFrame("img.png");
}
}
void drawLines() {
// Begin loop for columns
for (int i = 0; i < cols; i++) {
// Begin loop for rows
for (int j = 0; j < rows; j++) {
int x = i*videoScale;
int y = j*videoScale;
line(x,y,x+30,y+30);
}
}
}
I have looked into the relevant documentation on PDF exports but could not find a solution to this. Any help would be greatly appreciated!
Remove pixelDensity(2) from the setup() to fix. PixelDensity of 2 was designed to allow retina monitors to use all of the pixels. If you must use it, then you would need to write a separate drawLines() for the pdf file (example follows). Note that for the pdf drawLines() the videoScale is cut in half and the second set of x,y coordinates for each line is +15 instead of +30. You will also have to change the path for each file saved to be correct for your system. I used a different method for creating a pdf from pGraphics, which should be irrelevant.
/*
If pixelDensity(2) is used, need to modify second drawLines() for pdf.
Change file paths to suit your system.
*/
import processing.pdf.*;
import java.util.Random;
int cols, rows;
int videoScale = 100;
void drawLines() {
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
int x = i*videoScale;
int y = j*videoScale;
strokeWeight(1.5);
line(x, y, x+30, y+30);
}
}
}
void setup() {
size(800, 800);
background(255);
pixelDensity(2);
// frameRate(0.5);
cols = width/videoScale;
rows = height/videoScale;
drawLines();
}
void draw() {
}
void keyPressed() {
if (key == 'p') {
PGraphics pdf = createGraphics(width, height, PDF, "/Users/me/Desktop/output.pdf"); // change this
videoScale = 50; // cut in half
pdf.beginDraw();
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
int x = i*videoScale;
int y = j*videoScale;
pdf.line(x, y, x+15, y+15); // +15 instead of +30 for second x,y coordinates
}
}
pdf.dispose();
pdf.endDraw();
println("Printed pdf.");
}
if (key == 's') {
saveFrame("/Users/me/Desktop/img.png"); // change this
println("Image saved.");
}
}

Array index out of bounds while traversing edge members of array

I am creating a random maze generator in Java using the recursive backtracker algorithm. I need to set the edges of my maze cell data array to have been already visited by the algorithm so that it won't step out of bounds. The problem is probably staring me right in the face, I just cannot see where my error is.
Here is the whole error message:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 30
at mazeMaker.Maze.initBoundries(Maze.java:55)
at mazeMaker.Maze.<init>(Maze.java:46)
at mazeMaker.Main.main(Main.java:8)
I have tried tracing the error as best I could and experimented with changing my variables. The program relies on multiple class files, so I think it would be best just to show the whole thing.
Main.java
package mazeMaker;
public class Main
{
public static void main(String[] args)
{
Maze mainMaze = new Maze(20, 30);
}
}
Maze.java
package mazeMaker;
import java.util.Random;
import java.util.Stack;
public class Maze
{
public int xSize = 0;
public int ySize = 0;
public int totalDimensions = 0;
Random randomGenerator = new Random();
public Cell[][] cellData;
public Stack<Cell> cellStack = new Stack<Cell>();
Cell tempCell; // Temporary variable used for maze generation
public Maze(int xSize, int ySize)
{
cellData = new Cell[xSize][ySize];
this.xSize = xSize;
this.ySize = ySize;
this.totalDimensions = this.xSize * this.ySize;
// Initialize array objects
for (int i = 0; i < this.xSize; i++)
{
for (int j = 0; j < this.ySize; j++)
{
cellData[i][j] = new Cell();
}
}
// Assign x and y positions
for (int i = 0; i < this.xSize; i++)
{
for (int j = 0; j < this.ySize; j++)
{
cellData[i][j].xPos = i;
cellData[i][j].yPos = j;
}
}
initBoundries();
}
private void initBoundries()
{
// Initialize the border cells as visited so we don't go out of bounds
for (int col = 0; col < this.xSize; col++)
{
cellData[0][col].hasBeenVisited = true;
cellData[this.ySize][col].hasBeenVisited = true;
}
for (int row = 0; row < this.ySize; row++)
{
cellData[row][0].hasBeenVisited = true;
cellData[row][this.xSize].hasBeenVisited = true;
}
}
private void generateMaze(int x, int y)
{
// Set current cell as visited
cellData[x][y].hasBeenVisited = true;
// While there are unvisited neighbors
while (!cellData[x][y+1].hasBeenVisited || !cellData[x+1][y].hasBeenVisited || !cellData[x][y-1].hasBeenVisited || !cellData[x-1][y].hasBeenVisited)
{
// Select a random neighbor
while (true)
{
int r = randomGenerator.nextInt(4);
if (r == 0 && !cellData[x][y+1].hasBeenVisited)
{
cellStack.push(cellData[x][y]);
cellData[x][y].hasNorthWall = false;
cellData[x][y+1].hasSouthWall = false;
generateMaze(x, y + 1);
break;
}
else if (r == 1 && !cellData[x+1][y].hasBeenVisited)
{
cellStack.push(cellData[x][y]);
cellData[x][y].hasEastWall = false;
cellData[x+1][y].hasWestWall = false;
generateMaze(x+1, y);
break;
}
else if (r == 2 && !cellData[x][y-1].hasBeenVisited)
{
cellStack.push(cellData[x][y]);
cellData[x][y].hasSouthWall = false;
cellData[x][y-1].hasNorthWall = false;
generateMaze(x, y-1);
break;
}
else if (r == 3 && !cellData[x-1][y].hasBeenVisited)
{
cellStack.push(cellData[x][y]);
cellData[x][y].hasWestWall = false;
cellData[x-1][y].hasEastWall = false;
generateMaze(x-1, y);
break;
}
}
}
// There are no unvisited neighbors
tempCell = cellStack.pop();
generateMaze(tempCell.xPos, tempCell.yPos);
}
// Begin generating maze at top left corner
private void generateMaze()
{
generateMaze(1,1);
}
}
Cell.java
package mazeMaker;
public class Cell
{
public boolean isCurrentCell;
public boolean hasBeenVisited;
public boolean hasNorthWall;
public boolean hasSouthWall;
public boolean hasEastWall;
public boolean hasWestWall;
public int xPos;
public int yPos;
}
cellData[this.ySize][col].hasBeenVisited = true;
You have initialized your cellData as cellData[20][30], but in the above-mentioned line you are calling cellData[30][col]. Instead of 30, the first bracket should have a value from 0 to 19, because the row size is 20.
My guess is from this code in initBoundries()
// Initialize the border cells as visited so we don't go out of bounds
for (int col = 0; col < this.xSize; col++)
{
cellData[0][col].hasBeenVisited = true;
cellData[this.ySize][col].hasBeenVisited = true;
}
this.ySize after initialize (from constructor) has value 30.

replacing green pixels in a picture using java

I have a picture and I am trying to remove all the green pixels. How do I do this using simple java and a 2D array?
So far, my code looks like this:
public void removeGreen() {
Picture pic = new Picture("IMG_7320.JPG");
Pixel pixel = null;
for (int row = 0; row < pic.getHeight(); row++) {
for (int col = 0; col < pic.getWidth(); col++) {
pixel = getPixel(row,col);
pixel.getColor();
if(pixel.getRed() < 40 & pixel.getBlue() < 160 & pixel.getGreen() > 220) {
Color white = new Color(255,255,255);
pixel.setColor(white);
}
}
}
}
(Right now I am trying only replacing the green pixel with a white pixel because I'm not sure how to remove the pixel altogether.)
And the code in my main method that I am using to test the removeGreen() method,looks like this:
//method to test removeGreen
public static void testRemoveGreen() {
Picture me = new Picture("IMG_7320.JPG");
me.explore();
me.removeGreen();
me.explore();
}
So, my code now looks like this:
public void removeGreen(Picture pic) {
for (int row = 0; row < pic.getHeight(); row++) {
for (int col = 0; col < pic.getWidth(); col++) {
Pixel pixel = pic.getPixel(row,col);
if((pixel.getRed() < 40) && (pixel.getBlue() < 160) && (pixel.getGreen() > 220)) {
Color white = new Color(255,255,255);
pixel.setColor(white);
}
}
}
}
and my main method is still the same. I still do not understand why the method is not working properly.
The following makes clear that the passed pic parameter is altered.
public static void removeGreen(Picture pic) {
for (int row = 0; row < pic.getHeight(); row++) {
for (int col = 0; col < pic.getWidth(); col++) {
Pixel pixel = pic.getPixel(row,col);
if (pixel.getRed() < 40 && pixel.getBlue() < 160
&& pixel.getGreen() > 220) {
pixel.setColor(Color.white);
}
}
}
}
The && is a short-cut AND: x && y() will not call y is x is false.
Mind the pic.getPixel. If removeGreen is intended as method of Picture, remove the parameter and the static keyword, and pic..
public static void testRemoveGreen() {
Picture me = new Picture("IMG_7320.JPG");
me.explore();
removeGreen(me);
me.explore();
}
As the .jpg, JPEG, format does not have transparency, some colour like white is needed to be shown as "background."

Logic check for a 10*10 game

I am doing a game called 1010! Probably some of you have heard of it. Bascially I encouter some trouble when writing the Algorithm for clearance.
The rule is such that if any row or any column is occupied, then clear row and column respectively.
The scoring is such that each move gains a+10*b points. a is the number of square in the input piece p and b is the total number of row&column cleared.
To start, I create a two dimensional Array board[10][10], poulate each elements in the board[][] with an empty square.
In the class of Square, it has public void method of unset()-> "empty the square" & boolean status() -> "judge if square is empty"In the class of piece, it has int numofSquare -> "return the number of square in each piece for score calculation"
In particular, I don't know how to write it if both row and column are occupied as they are inter-cross each other in an two dimensional array.
It fail the test under some condition, in which some of the squares are not cleared but they should have been cleared and I am pretty sure is the logic problem.
My thinking is that:
Loop through squares in first row and first column, record the number of square that are occupied (using c and r); if both are 10, clear row&column, otherwise clear row or column or do nothing.
reset the c &r to 0, loop through square in the second row, second column…
update score.
Basically the hard part is that if I seperate clear column and clear row algorithm ,I will either judge row or column first then clear them . However, as every column contains at least one square belong to the row, and every row contains at least one square belong to the column, there will be mistake when both row and column are full.
Thanks for help.
import java.util.ArrayList;
public class GameState{
public static final int noOfSquares = 10;
// the extent of the board in both directions
public static final int noOfBoxes = 3;
// the number of boxes in the game
private Square[][] board; // the current state of the board
private Box[] boxes; // the current state of the boxes
private int score; // the current score
// initialise the instance variables for board
// all squares and all boxes are initially empty
public GameState()
{
getboard();
score = 0;
board = new Square[10][10];
for(int i =0;i<board.length;i++){
for(int j =0;j<board[i].length;j++){
board[i][j] = new Square();
}
}
boxes = new Box[3];
for(int k =0;k<boxes.length;k++){
boxes[k] = new Box();
}
}
// return the current state of the board
public Square[][] getBoard()
{
return board;
}
// return the current score
public int getScore()
{
return score;
}
// place p on the board with its (notional) top-left corner at Square x,y
// clear columns and rows as appropriate
int r =0;
int c = 0;
int rowandcolumn = 0;
for (int row=0;row<10;row++){
for (int column=0;column<10;column++) {
if (board[row][column].status() == true){
c = c + 1;
if( c == 10 ) {
rowandcolumn = rowandcolumn + 1;
for(int z=0;z<10;z++){
board[row][z].unset(); //Clear column
}
}
}
if (board[column][row].status() == true){
r = r + 1;
if( r == 10) {
rowandcolumn = rowandcolumn + 1;
for(int q=0;q<10;q++){
board[q][row].unset(); //Clear row
}
}
}
}
r=0; //reset
c=0;
}
score = score + p.numberofBox()+10*rowandcolumn;
}
how about this
void Background::liquidate(int &score){
int arr_flag[2][10]; //0 is row,1 is column。
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 10; j++)
{
arr_flag[i][j] = 1;
}
}
//column
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (arr[i][j].type == 0)
{
arr_flag[0][i] = 0;
break;
}
}
}
//row
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (arr[j][i].type == 0)
{
arr_flag[1][i] = 0;
break;
}
}
}
//clear column
for (int i = 0; i < 10; i++)
{
if (arr_flag[0][i] == 1)
{
for (int j = 0; j < 10; j++)
{
arr[i][j].Clear();
}
}
}
//clear row
for (int i = 0; i < 10; i++)
{
if (arr_flag[1][i] == 1)
{
for (int j = 0; j < 10; j++)
{
arr[j][i].Clear();
}
}
}
}
I tried to write somme code for the idea I posted
// place p on the board with its (notional) top-left corner at Square x,y
// clear columns and rows as appropriate
int r =0;
int c = 0;
int rowandcolumn = 0;
int row=FindFirstRow();
int column=FindFirstColumn();
if(row!=-1 && column!=-1)
{
rowandcolumn++;
//actions here: row found and column found
//clear row and column
clearRow(row);
clearColumn(column);
}
else if(row!=-1)
{
//only row is found
//clear row
clearRow(row);
}
else if(column!=-1)
{
//only column is found
//clear column
clearColumn(column);
}
else
{
//nothing is found
}
public void clearRow(int row)
{
for(int i=0; i<10;i++)
{
board[row][i].unset();
}
}
public void clearColumn(int column)
{
for(int i=0; i<10;i++)
{
board[i][column].unset();
}
}
//this method returns the first matching row index. If nothing is found it returns -1;
public int FindFirstRow()
{
for (int row=0;row<10;row++)
{
int r=0;
for (int column=0;column<10;column++)
{
if (board[row][column].status() == true)
{
r = r + 1;
if( r == 10)
{
//row found
return row;
}
}
}
r=0; //reset
}
//nothing found
return -1;
}
//this method returns the first matching column index. If nothing is found it returns -1;
public int FindFirstColumn()
{
for (int column=0;column<10;column++)
{
int c=0;
for (int row=0;row<10;row++)
{
if (board[row][column].status() == true)
{
c = c + 1;
if( c == 10 )
{
//matching column found
return column;
}
}
}
c=0; //reset
}
//nothing found
return -1;
}

Recursive method for solving sudoku

I'm currently studying my second programming course in java and have a problem with an assignment that I need to complete to pass the course. Basically it's about writing a program that recursively solves sudoku with backtracking. This is the algorithm I came up with. I used a 9x9 array to represent the grid which in the beginning is filled with zeroes. checkFill checks whether it's possible to insert a number (var) into a position[i][j]. The problem is that it only solves sudoku partially it always returns false(no solution) and some cells still contain zeroes. What am I doing wrong here?
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Sudoku {
private int[][] sudoku;
private JFrame frame;
private JFormattedTextField[][] sudokuSquares;
private JButton solve, clear;
public Sudoku() {
sudoku = new int[9][9];
frame = new JFrame("Sudoku Solver");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel northPanel = new JPanel();
northPanel.setLayout(new GridLayout(9, 9));
northPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
sudokuSquares = new JFormattedTextField[9][9];
Font font1 = new Font("SansSerif", Font.BOLD, 20);
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
sudokuSquares[i][j] = new JFormattedTextField();
sudokuSquares[i][j].setHorizontalAlignment(JTextField.CENTER);
sudokuSquares[i][j].setFont(font1);
sudokuSquares[i][j].setBorder(BorderFactory.createEtchedBorder(javax.swing.border.EtchedBorder.RAISED));
northPanel.add(sudokuSquares[i][j]);
}
}
setColor();
frame.add(northPanel, BorderLayout.NORTH);
JPanel southPanel = new JPanel();
solve = new JButton("Solve");
solve.addActionListener(new SolveButtonListener());
clear = new JButton("Clear");
clear.addActionListener(new ClearButtonListener());
southPanel.add(clear);
southPanel.add(solve);
frame.add(southPanel, BorderLayout.SOUTH);
frame.setResizable(false);
frame.setSize(280, 330);
frame.setVisible(true);
}
private void solveSudoku() {
boolean hasSolution = solve(0, 0);
if(hasSolution) {
JOptionPane.showMessageDialog(frame, "Sudoku has been successfully solved");
} else {
JOptionPane.showMessageDialog(frame, "Sudoku has no solution");
}
}
private class SolveButtonListener implements ActionListener {
#Override
/**
* Checks input for errors and outputs the sudoku matrix after it's been solved.
*/
public void actionPerformed(ActionEvent e) {
String s;
setColor();
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
s = sudokuSquares[i][j].getText();
if(s.isEmpty()) {
s = "0";
} else if(s.length() > 1 || !Character.isDigit(s.charAt(0)) || s.charAt(0) == '0') {
sudokuSquares[i][j].setBackground(Color.RED);
JOptionPane.showMessageDialog(frame, "Invalid entry! Please enter a number between 1-9");
return;
}
sudoku[i][j] = Integer.parseInt(s);
}
}
solveSudoku();
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
sudokuSquares[i][j].setText(String.valueOf(sudoku[i][j]));
}
}
}
}
private class ClearButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
sudokuSquares[i][j].setText("");
}
}
setColor();
}
}
/**
* Sets the background color of sudoku cells
*/
private void setColor() {
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
if((i / 3 < 1 || i / 3 >= 2) && (j / 3 < 1 || j / 3 >= 2) ||
(i / 3 >= 1 && i / 3 < 2) && (j / 3 >= 1 && j / 3 < 2)) {
sudokuSquares[i][j].setBackground(new Color(220, 220, 220));
} else {
sudokuSquares[i][j].setBackground(Color.WHITE);
}
}
}
}
/**
* Solves the sudoku through recursive technique
* #param i row
* #param j column
* #return returns true if the sudoku has a solution, returns false otherwise
*/
private boolean solve(int i, int j) {
if(i > 8) {
return true;
}
if(sudoku[i][j] == 0) {
for(int var = 1; var < 10; var++) {
if(checkFill(i, j, var)) {
sudoku[i][j] = var;
if(j >= 8) {
solve(i + 1, 0);
} else {
solve(i, j+1);
}
}
}
} else {
if(j >= 8) {
solve(i + 1, 0);
} else {
solve(i, j+1);
}
}
return false;
}
/**
* Checks if var could be inserted at position [i][j]
* #param i row
* #param j column
* #param var number to be checked for possible insertion
* #return
*/
private boolean checkFill(int i, int j, int var) {
for(int a = 0; a < 9; a++) {
if(sudoku[a][j] == var) {
return false;
}
}
for(int a = 0; a < 9; a++) {
if(sudoku[i][a] == var) {
return false;
}
}
int tempI = (i / 3) * 3;
int tempJ = (j / 3) * 3;
for(int a = 0; a < 3; a++) {
for(int b = 0; b < 3; b++) {
if(sudoku[tempI + a][tempJ + b] == var)
return false;
}
}
return true;
}
}
So anyone got any ideas?
It seems that your program simply checks if a given number can fit into a given slot, checks the row, column, and local 3x3 grid, and places it there permanently if those three checks pass.
This is problematic since most sudoku will not yield a single possibility on such simple checks.
You'll need to make a list of possible values for each spot, then start using techniques such as double pairs to solve it out further.
Since it's a computer and it's fast, after you use some of these techniques you can start brute forcing it.
A different approach would be to translate the sudoku problem into a SAT formula, feed it into a SAT solver, and translate the solution back into a sudoku solution. There are very advanced SAT solvers these days that can handle 9x9 sudoku very quickly. Here is a more in-depth explanation.
In your solve method, you test whether sudoku[i][j]==0 before you make a change. That means that once you place a number, whether it's right or wrong, you never change it. You need to be able to back out of errant moves.
You can find a simplified java Sudoku program here.http://www.heimetli.ch/ffh/simplifiedsudoku.html
if you can share you full source code including the method checkFill, we can assist you in debuging further

Categories