Example: Sudoku v3
Here is a partial solution to writing a Sudoku program. It has a very clunky
user interface using text boxes and doesn't do any checking.
It's divided into three classes:
Sudoku.java
is main JFrame subclass, which has most of the user interface code.
It also creates a new model object.
SudokuBoardDisplay.java
is a subclass of JComponent, and it
used for the graphical display of the sudoku board. Its constructor is
passed a reference to the model created in the main program.
It interrogates the model to determine with to draw.
SudokuModel.java
contains the model, the logic,
of the game. It knows nothing about the user interface, and could equally
well be used with a console or web interface.
Main JFrame subclass
These source files can be copied and pasted into your IDE from
http://www.leepoint.net/notes-java/GUI/examples/sudoku/ex-sudoku-v3.html.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
// File : gui/sudoku/sudoku3/Sodoku.java
// Description: Initializes and displays board.
// Uses text components to make move.
// Author : Fred Swartz - 21 Sep 2006 - Placed in public domain.
// Enhancements needed:
// * Use mouse for input instead of text fields.
// * Check for legal board positions on initialization and move.
// * Don't allow initial cell values to be changed.
package sudoku3;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Sudoku extends JFrame {
///
private static final String INITIAL_BOARD =
"8156....4/" +
"6...75.8./" +
"....9..../" +
"9...417../" +
".4.....2./" +
"..623...8/" +
"....5..../" +
".5.91...6/" +
"1....7895";
//=================================================================== fields
private SudokuModel _sudokuLogic = new SudokuModel(INITIAL_BOARD);
private SudokuBoardDisplay _sudokuBoard = new SudokuBoardDisplay(_sudokuLogic);
private JTextField _rowTF = new JTextField(2);
private JTextField _colTF = new JTextField(2);
private JTextField _valTF = new JTextField(2);
//============================================================== constructor
public Sudoku() {
// 1... Create/initialize components
JButton moveBtn = new JButton("Move");
JPanel controlPanel = new JPanel();
controlPanel.setLayout(new FlowLayout());
controlPanel.add(new JLabel("Row (1-9):"));
controlPanel.add(_rowTF);
controlPanel.add(new JLabel("Col (1-9):"));
controlPanel.add(_colTF);
controlPanel.add(new JLabel("Val:"));
controlPanel.add(_valTF);
controlPanel.add(moveBtn);
//... Add listener
moveBtn.addActionListener(new MoveListener());
// 2... Create content panel, set layout
JPanel content = new JPanel();
content.setLayout(new BorderLayout());
// 3... Add the components to the content panel.
content.add(controlPanel, BorderLayout.NORTH);
content.add(_sudokuBoard, BorderLayout.CENTER);
// 4... Set this window's attributes, and pack it.
setContentPane(content);
setTitle("Sudoku 3");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false); // Don't let user resize it.
pack();
setLocationRelativeTo(null); // Center it.
}
///////////////////////////////////////////////////////////// MoveListener
class MoveListener implements ActionListener {
public void actionPerformed(ActionEvent ae) {
try {
//... Translate row and col to zero-based indexes.
int row = Integer.parseInt(_rowTF.getText().trim()) - 1;
int col = Integer.parseInt(_colTF.getText().trim()) - 1;
int val = Integer.parseInt(_valTF.getText().trim());
if (_sudokuLogic.isLegalMove(row, col, val)) {
_sudokuLogic.setVal(row, col, val);
_sudokuBoard.repaint();
} else {
JOptionPane.showMessageDialog(null, "Illegal row, col, or value.");
}
} catch (NumberFormatException nfe) {
JOptionPane.showMessageDialog(null, "Please enter numeric values.");
}
}
}
//===================================================================== main
public static void main(String[] args) {
new Sudoku().setVisible(true);
}
}
|
Board drawing component
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
// File : gui/sudoku/sudoku3/SudokuBoardDisplay.java
// Purpose: Draws a Sudoku board give a model.
// Author : Fred Swartz - 23 Sep 2006 - Placed in public domain.
package sudoku3;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JComponent;
///////////////////////////////////////////////////////////// SudokuBoardDisplay
public class SudokuBoardDisplay extends JComponent {
//================================================================ constants
private static final int CELL_PIXELS = 50; // Size of each cell.
private static final int PUZZLE_SIZE = 9; // Number of rows/cols
private static final int SUBSQUARE = 3; // Size of subsquare.
private static final int BOARD_PIXELS = CELL_PIXELS * PUZZLE_SIZE;
private static final int TEXT_OFFSET = 15; // Fine tuning placement of text.
private static final Font TEXT_FONT = new Font("Sansserif", Font.BOLD, 24);
//================================================================ fields
private SudokuModel _model; // Set in constructor.
//============================================================== constructor
public SudokuBoardDisplay(SudokuModel model) {
setPreferredSize(new Dimension(BOARD_PIXELS + 2, BOARD_PIXELS + 2));
setBackground(Color.WHITE);
_model = model;
}
//=========================================================== paintComponent
@Override public void paintComponent(Graphics g) {
//... Draw background.
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.BLACK);
drawGridLines(g);
drawCellValues(g);
}
//============================================================ drawGridLines
// Separate method to simlify paintComponent.
private void drawGridLines(Graphics g) {
//... Draw grid lines. Terminates on <= to get final line.
for (int i = 0; i <= PUZZLE_SIZE; i++) {
int acrossOrDown = i * CELL_PIXELS;
//... Draw at different x's from y=0 to y=BOARD_PIXELS.
g.drawLine(acrossOrDown, 0, acrossOrDown, BOARD_PIXELS);
//... Draw at different y's from x=0 to d=BOARD_PIXELS.
g.drawLine(0, acrossOrDown, BOARD_PIXELS, acrossOrDown);
//... Draw a double line for subsquare boundaries.
if (i % SUBSQUARE == 0) {
acrossOrDown++; // Move one pixel and redraw as above
g.drawLine(acrossOrDown, 0, acrossOrDown, BOARD_PIXELS);
g.drawLine(0, acrossOrDown, BOARD_PIXELS, acrossOrDown);
}
}
}
//=========================================================== drawCellValues
// Method to simplify paintComponent.
private void drawCellValues(Graphics g) {
g.setFont(TEXT_FONT);
for (int i = 0; i < PUZZLE_SIZE; i++) {
int yDisplacement = (i+1) * CELL_PIXELS - TEXT_OFFSET;
for (int j = 0; j < PUZZLE_SIZE; j++) {
if (_model.getVal(i, j) != 0) {
int xDisplacement = j * CELL_PIXELS + TEXT_OFFSET;
g.drawString("" + _model.getVal(i, j), xDisplacement, yDisplacement);
}
}
}
}
}
|
The model
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
// File : gui/sudoku/sudoku3/SudokuModel.java
// Purpose: Represents the "logic" of a Sudoku game.
// Author : Fred Swartz - 23 Sep 2006 - Placed in public domain.
package sudoku3;
//////////////////////////////////////////////////////////////////// SudokuModel
public class SudokuModel {
//================================================================ constants
private static final int BOARD_SIZE = 9;
//=================================================================== fields
private int[][] _board;
//============================================================== constructor
public SudokuModel() {
_board = new int[BOARD_SIZE][BOARD_SIZE];
}
//============================================================== constructor
public SudokuModel(String initialBoard) {
this(); // Call no parameter constructor first.
initializeFromString(initialBoard);
}
//===================================================== initializeFromString
public void initializeFromString(final String boardStr) {
clear(); // Clear all values from the board.
int row = 0;
int col = 0;
//... Loop over every character.
for (int i = 0; i < boardStr.length(); i++) {
char c = boardStr.charAt(i);
if (c >= '1' && c <='9') {
if (row > BOARD_SIZE || col > BOARD_SIZE) {
throw new IllegalArgumentException("SudokuModel: "
+ " Attempt to initialize outside 1-9 "
+ " at row " + (row+1) + " and col " + (col+1));
}
_board[row][col] = c - '0'; // Translate digit to int.
col++;
} else if (c == '.') {
col++;
} else if (c == '/') {
row++;
col = 0;
} else {
throw new IllegalArgumentException("SudokuModel: Character '" + c
+ "' not allowed in board specification");
}
}
}
//============================================================== islegalMove
public boolean isLegalMove(int row, int col, int val) {
return row>=0 && row<BOARD_SIZE && col>=0 && col<BOARD_SIZE
&& val>0 && val<=9 && _board[row][col]==0;
}
//=================================================================== setVal
public void setVal(int r, int c, int v) {
_board[r][c] = v;
}
//=================================================================== getVal
public int getVal(int row, int col) {
return _board[row][col];
}
//===================================================================== clear
public void clear() {
for (int row = 0; row < BOARD_SIZE; row++) {
for (int col = 0; col < BOARD_SIZE; col++) {
setVal(row, col, 0);
}
}
}
}
|