package sudoku;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.EventListener;
import javax.swing.*;
/**
* The SudokuPanel class currently draws the Sudoku Board and allows for text entry. All menu items work as advertised.
*
* @author Kyle Wilson
* @version 30 November 2006
*
*
* code for menu bar adapted from Scribble Program example by Xiaoping Jia Copyright 2002
*
*/
public class SudokuPanel extends JPanel implements ActionListener
{
/**
* Constructs a SudokuPanel object and initializes a SudokuPanelListener to act as a MouseListener
*
*/
public SudokuPanel() {
super();
sBoard = new int[9][9];
setLayout(new GridLayout(9,9));
sudokuBoard = new SudokuTextField[9][9];
Font font = new Font("Monospace", Font.BOLD, 24);
for(int i=0; i<sudokuBoard.length; i++) {
for(int j=0; j<sudokuBoard[i].length; j++) {
sudokuBoard[i][j] = new SudokuTextField(j,i);
sudokuBoard[i][j].setFont(font);
sudokuBoard[i][j].addActionListener(this);
add(sudokuBoard[i][j]);
sBoard[i][j] = -1;
}
}
}
/**
* The actionPerformed method is called whenever the user hits enter after entering text into
* a SudokuTextField box. It will first update sBoard to any changes in sudokuBoard, then check
* for duplicates, then update the GUI to any duplicates.
*/
public void actionPerformed(ActionEvent evt)
{
updatesBoard();
checkDuplicate();
repaint();
}
/**
* Updates all the sBoard entries to reflect their current status. An empty or invalid cell is filled with -1 by default.
*
*/
private void updatesBoard()
{
for(int i = 0; i < 9; i++)
{
for(int j = 0; j < 9; j++)
{
try
{
int testint = Integer.parseInt(sudokuBoard[i][j].getText());
if(testint > 9 || testint < 1)
{
JOptionPane.showMessageDialog(null, "Sudoku numbers must be between 1 and 9", "Invalid Entry",
JOptionPane.ERROR_MESSAGE);
sBoard[i][j] = -1;
}
else
sBoard[i][j] = testint;
}
catch(NumberFormatException e)
{
sBoard[i][j] = -1;
}
catch(NullPointerException e)
{
sBoard[i][j] = -1;
}
}
}
}
/**
* Checks for duplicates in every row, column, and blocks.
* note: algorithm revised as of version 2006.11.30
*/
private void checkDuplicate()
{
for(int i = 0; i < 9; i++)
for(int j = 0; j < 9; j++)
sudokuBoard[i][j].duplicate = false;
for(int a = 0; a < 9; a++)
{
for(int i = 0; i < 9; i++)
{
for(int j = 0; j < 9; j++)
{
if(i != j && sBoard[a][i] == sBoard[a][j] && sBoard[a][i] != -1 && sBoard[a][j] != -1)
{
sudokuBoard[a][i].duplicate = true;
sudokuBoard[a][j].duplicate = true;
}
if(i != j && sBoard[i][a] == sBoard[j][a] && sBoard[i][a] != -1 && sBoard[j][a] != -1)
{
sudokuBoard[i][a].duplicate = true;
sudokuBoard[j][a].duplicate = true;
}
}
}
}
for(int i = 0; i < 9; i++)
{
for(int j = 0; j < 9; j++)
{
int myx = i - (i % 3);
int myy = j - (j % 3);
for(int x=myx; x<myx+3; x++) {
for(int y=myy; y<myy+3; y++) {
if(x!=i && y!=j && sBoard[x][y] == sBoard[i][j] && sBoard[x][y] != -1) {
sudokuBoard[i][j].duplicate = true;
}
}
}
}
}
}
private void updateSudokuBoard()
{
for(int i = 0; i<9;i++)
for(int j=0;j<9;j++)
{
if(sBoard[i][j] != -1)
sudokuBoard[i][j].setText(sBoard[i][j] + "");
else
sudokuBoard[i][j].setText("");
}
}
/**
* Clears the current Sudoku Puzzle and leave an empty board.
*
*/
public void newFile() {
for(int i = 0; i < 9; i++)
for(int j = 0; j < 9; j++)
{ sudokuBoard[i][j].setText("");}
updatesBoard();
checkDuplicate();
repaint();
}
/**
* Opens a saved Sudoku puzzle.
* @param filename - The name of the file to open.
*/
public void openFile(String filename) {
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename));
sBoard = (int[][]) in.readObject();
in.close();
updateSudokuBoard();
repaint();
} catch (IOException e1) {
System.out.println("Unable to open file: " + filename);
} catch (ClassNotFoundException e2) {
System.out.println(e2 + "oh no");
}
}
/**
* Saves a sudoku puzzle currently in progress.
* @param filename - the name to save the sudoku puzzle under.
*/
public void saveFile(String filename) {
updatesBoard();
try {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filename));
out.writeObject(sBoard);
out.close();
System.out.println("Save Sudoku to " + filename);
} catch (IOException e) {
System.out.println("Unable to write file: " + filename);
}
}
/**
* Extension of the JTextField class that provides a boolean value
* to specify whether the SudokuTextField is a duplicate and values
* for the x and y location in the sudokuBoard grid.
* @author kyle
*
*/
class SudokuTextField extends JTextField {
int x,y;
String value;
boolean duplicate;
/**
* default constructor
* @param x x position in the sudokuBoard grid
* @param y y position in the sudokuBoard grid
*/
public SudokuTextField(int x, int y) {
this.x = x;
this.y = y;
duplicate = false;
setOpaque(false);
}
/**
* accessor method to modify duplicate
* @param dupe value to set duplicate to.
*/
public void setDuplicate(boolean dupe)
{
duplicate = dupe;
}
/**
* standard update method.
*/
public void update(Graphics g) {
paint(g);
}
/**
* Draws the border rectangle for each SudokuTextField box which makes up the board
* and also will outline the box in red if the entry is a duplicate.
*/
public void paint(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0,0,getSize().width,getSize().height);
if(duplicate){
g.setColor(Color.RED);
g.drawLine(0,0,getSize().width-1,0);
g.drawLine(0,0,0,getSize().height-1);
}
else
g.setColor(Color.BLACK);
g.drawLine(getSize().width-1,0,getSize().width-1,getSize().height);
g.drawLine(0,getSize().height-1,getSize().width,getSize().height-1);
if(x == 3 || x == 6) {
g.drawLine(0,0,0,getSize().width);
}
if(y == 3 || y == 6) {
g.drawLine(0,0,getSize().width,0);
}
super.paintComponent(g);
}
}
protected EventListener listener;
protected int x, y;
protected SudokuTextField[][] sudokuBoard; //the board of SudokuTextField objects; what the user sees.
protected int[][] sBoard; //int sudokuBoard representation, used for duplicate checking
}