Page 1 of 1

Sudoku III - Basic GUI A universal Sudoku Grid Rate Topic: -----

#1 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8327
  • View blog
  • Posts: 31,857
  • Joined: 06-March 08

Posted 20 October 2010 - 08:26 PM

The following class will let you build a basic Sudoku grid GUI
The class GridGuiOne.java is a Jpanel using a GridLayout
For a 9x9 Sudoku it will be composed by a 3x3 GridLayout into where the 9 regions will be inserted into 3 rows and 3 columns.
Each region will be also a GridLayout 3x3 for the 3 rows and 3 columns of each region.

So one big JPanel which the class extends into which 9 JPanels created within the class.

The constructor receives an instance of the Grid class to build all the panels from the cells contained in the region[] RowColReg array.

The main() method can be used as unit test to test the panel. It builds a JFrame and put the GridGuiOne into it.


import javax.swing.*;
import java.awt.*;
/*
 * A Basic JPanel to display the cells
 */
public class GridGuiOne extends JPanel {

	private static final long serialVersionUID = 1L;

	// Constructor receives an instance of class Grid as parameter
	GridGuiOne(Grid grid) {
		
		// get the regions from the Grid
		RowColReg[] region = grid.getRegion();
		// get the size to get the number of row and column in each region
		// and the total of regions per row/column
		int size = (int) Math.sqrt((double) region.length);
		
		// this (the main panel) will contain size X size regions
		setLayout(new GridLayout(size, size));
		// now we will build the other panels (one for each region)
		for(int i = 0; i < region.length; i++) {
			JPanel p = new JPanel(new GridLayout(size, size));
			p.setBorder(BorderFactory.createLineBorder(Color.BLACK));
			// get the cells from that region
			Cell[] cell = region[i].getCells();
			// add the cell to the grid
			for(int j = 0; j < cell.length; j++) {
				p.add(cell[j]);
			}
			// then add this panel of a region to the big grid
			add(p);
		}
	}
	
	/*
	 * To test the Grid
	 */
	public static void main(String[] atgs) {
		// create a JFrame to hold the Grid
		JFrame frame = new JFrame("First GUI demo");
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		// Build a Grid of size 9
		Grid grid = new Grid(9);
		// load the grid with predefined problem
		Problems.setProblem3(grid);
		// ask the Grid to react to keyboard event
		grid.registerForKeyboardEvent();
		// build a GridGuiOne from that Grid object
		GridGuiOne ggo = new GridGuiOne(grid);
		// add the Grid to the Frame
		frame.add(ggo);
		// make it visible
		frame.setVisible(true);
		frame.pack();
	}
}



You can play with the cursor to move from one the to another.
You can enter a value on cells, if the value you enter is valid for that cell.

The Problem class has been upgraded to add a new problem.
The Cell class has been updated to remove the focus listener if no key listener are installed.

Problems.java

/**
 * A class that contains, for test purpose, the setters for different
 * Sudoku problems
 */
public class Problems {

	/*
	 * private method called by the others to load a problem
	 */
	private static void load(Grid grid, int[][] val) {
		// clear grid because if it already contains something
		// the setOriginalProblemValue might reject to set a cell to a value already there
		for(int i = 0; i < val.length; i++) {
			for(int j = 0; j < val[i].length; j++) {
				grid.setCellValue(i, j, 0);	
			}
		}
		// load with new problem
		for(int i = 0; i < val.length; i++) {
			for(int j = 0; j < val[i].length; j++) {
				grid.setOriginalProblemValue(i, j, val[i][j]);	
			}
		}
	}
	// preregistered problem 1	
	public static void setProblem1(Grid grid) {
		int[][] val = {
				{0, 0, 0, 0, 0, 7, 0, 0, 0},
				{3, 0, 0, 0, 6, 0, 0, 5, 0},
				{7, 0, 0, 9, 2, 0, 0, 1, 0},
				{0, 8, 0, 0, 3, 0, 0, 0, 1},
				{0, 0, 0, 0, 0, 0, 5, 0, 6},
				{9, 0, 0, 6, 0, 8, 0, 0, 0},
				{0, 0, 0, 0, 1, 3, 6, 0, 0},
				{0, 0, 0, 5, 0, 0, 1, 2, 0},
				{0, 9, 0, 0, 0, 0, 7, 0, 3}
		};
		// loat it with my values
		load(grid, val);
	}

	/*
	 * preregisterd problem 2
	 * this is kind of worst case for brute force
	 * no clue on top row where the only solution is 987654321 
	 * so a huge amount of iterations will be required
	 * this a good problem to test in the brute force with reverse in BruteForceGui.java
	 * (may be not with the GUI version it would take a while :-))
	 */
	public static void setProblem2(Grid grid) {
		int[][] val = {
				{0, 0, 0, 0, 0, 0, 0, 0, 0},
				{0, 0, 0, 0, 0, 3, 0, 8, 5},
				{0, 0, 1, 0, 2, 0, 0, 0, 0},
				{0, 0, 0, 5, 0, 7, 0, 0, 0},
				{0, 0, 4, 0, 0, 0, 1, 0, 0},
				{0, 9, 0, 0, 0, 0, 0, 0, 0},
				{5, 0, 0, 0, 0, 0, 0, 7, 3},
				{0, 0, 2, 0, 1, 0, 0, 0, 0},
				{0, 0, 0, 4, 0, 0, 0, 0, 9}
		};
		// loat it with my values
		load(grid, val);
	}
	
	/*
	 * and almost done Sudoku problem to see the BruteForce GUI in action
	 * the top part if almost done so the reversed method should be slower 
	 * on that one
	 */
	public static void setProblem3(Grid grid) {
		int[][] val = {
				{6, 1, 9, 2, 8, 4, 5, 7, 3},
				{3, 7, 0, 5, 1, 6, 4, 0, 9},
				{4, 5, 2, 0, 7, 0, 8, 0, 0},
				{7, 4, 5, 9, 0, 3, 1, 0, 0},
				{2, 0, 0, 8, 5, 1, 7, 9, 0},
				{8, 9, 1, 0, 2, 0, 3, 6, 0},
				{5, 0, 0, 6, 3, 0, 9, 0, 1},
				{0, 0, 3, 0, 0, 2, 6, 0, 7},
				{1, 0, 4, 0, 9, 0, 0, 0, 0}
		};
		// loat it with my values
		load(grid, val);
	}

	/*
	 * To unit test the whole thing
	 */
	public static void main(String[] args) {
		// create a Grid
		Grid grid = new Grid(9);
		// fill and print it
		setProblem1(grid);
		grid.printGrid();
		// fill and print it
		setProblem2(grid);
		grid.printGrid();
		// fill and print it
		setProblem3(grid);
		grid.printGrid();
	}
}




Cell.java

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

/*
 * A Sudoku cell as it might be used in a GUI make it a JLabel
 * by extending the JLabel class
 */
public class Cell extends JLabel implements FocusListener, MouseListener, KeyListener {
	
	private static final long serialVersionUID = 1L;
	// the font use for displaying the label
	private static Font labelFont, boldFont;
	// the background for the cell that hasfocus
	private static Color lightYellow = new Color(255, 255, 125);
	
	// at first invocation create our fonts
	static {
		JLabel l = new JLabel();          	// build a temp label to get standard font
		Font font = l.getFont();			// get the Font
		// make a font 2 times the original size (which will be considered bold)
		boldFont = font.deriveFont(font.getStyle(), (int) (font.getSize2D() * 2.0));
		// a smaller font for the cell not part of the original problem
		labelFont = boldFont.deriveFont(boldFont.getStyle() ^ Font.BOLD);	
	}
	// the String representation of the point 
	private String str;
	// the value of the cell
	private int value;
	// if the value is part of the original problem
	private boolean originalProblem;
	// the cell position
	private Point point;
	
	// an ArrayList to register the SudokuCellKbListener that want to be informed when a
	// a the keyboard is press
	private ArrayList<SudokuCellKbListener> listenerList;
	
	/*
	 * Constructor
	 */
	public Cell(int x, int y) {
		super("     ");
		point = new Point(x, y);
		str = String.format("(%02d,%02d)", x, y);
		// have the number centered
		setHorizontalAlignment(SwingConstants.CENTER);
		// make it opaque to it will have a background
		setOpaque(true);
		setBackground(Color.WHITE);
		// give it a border
		setBorder(BorderFactory.createLineBorder(Color.BLACK));
		// the font to use
		setFont(labelFont);
		// the arrayList for the listener of the keyboard
		listenerList = new ArrayList<SudokuCellKbListener>();
	}
	
	/*
	 * Returns the cell value
	 */
	public int getValue() {
		return value;
	}
	/*
	 * Returns cell position (mostly for debug purpose)
	 */
	public Point getPosition() {
		return point;
	}
	
	/*
	 * Reset a cell 
	 */
	private void reset(int val, boolean original) {
		value = val;
		// if value == 0 we put originalProblem to false and set the label at " "
		if(value == 0) {
			originalProblem = false;
			setText("     ");
			return;
		}
		originalProblem = original;
		setText("  " + Integer.toString(value, Character.MAX_RADIX).toUpperCase() + "  ");	
		// determine the font based on the type
		if(originalProblem)
			setFont(boldFont);
		else
			setFont(labelFont);
	}
	/*
	 * Set the value for an original problem
	 */
	public void setOriginalProblemValue(int value) {
		reset(value, true);
	}
	/*
	 * Set the value for a user guess or solver
	 */
	public void setValue(int value) {
		reset(value, false);
	}
	// getter for the type of value
	public boolean isOriginalProblem() {
		return originalProblem;
	}
	/*
	 * The method that overloads toString() that return the value 
	 */
	public String toString() {
		// for text display so we also pass the 0
		return Integer.toString(value, Character.MAX_RADIX).toUpperCase();	
	}
	/*
	 * To return the unique Id as a String
	 */
	public String getIdStr() {
		return str;
	}
	
	/*
	 * The two method invoked when the cell gain or loose focus
	 */
	@Override
	public void focusGained(FocusEvent e) {
		setBackground(lightYellow);
	}
	@Override
	public void focusLost(FocusEvent e) {
		setBackground(Color.WHITE);
	}

	/*
	 * The mouse listener to gain focus when a cell is cliked
	 * only pressed is used
	 */
	@Override
	public void mousePressed(MouseEvent e) {
		requestFocusInWindow();				// give focus to that cell
	}
	public void mouseClicked(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {}
	public void mouseExited(MouseEvent e) {}
	public void mouseReleased(MouseEvent e) {}

	/*
	 * to register to have the keyboard event relayed
	 */
	public void registerCellKbListener(SudokuCellKbListener listener) {
		// at the first call, when the arrayList is empty, register this as keyLlistener
		// to react to keyBoard key pressed and make the cells focusable
		if(listenerList.size() == 0) {
			// labels by default are not focusable make it focusable
			setFocusable(true);
			// and add them a focus listener so we can change the background color
			addFocusListener(this);
			// amouse listener to be able to select the cell by clicking over it with the mouse
			addMouseListener(this);
			addKeyListener(this);
			// disable the fact that a TAB will bring me to next JLabel
			setFocusTraversalKeysEnabled(false);
		}
		// add this listener to our list
		listenerList.add(listener);
	}
	/*
	 * The KeyListener methods
	 */
	@Override
	public void keyPressed(KeyEvent ev) {
		// pass through our list to prevent all the possible listener
		for(SudokuCellKbListener listener : listenerList) {
			listener.keyPressed(this, point.x , point.y, ev);
		}
	}
	public void keyReleased(KeyEvent e) {}
	public void keyTyped(KeyEvent e) {}
}


This post has been edited by pbl: 21 October 2010 - 04:48 PM


Is This A Good Question/Topic? 2
  • +

Page 1 of 1