I am building a Sudoku Solver visualizer that will solve the sudoku board and show the computer's steps as it tries each number in the available slots. I have been using the JFrame/JPanel framework to visualize this but have had problems updating the graphics to show the computer solving it and pausing the graphics in between each new attempt at each slot.
The solver works perfectly and correctly solves the board but I can only see the unsolved board and the solved board when I click the enter button.
Here is my class with the main method:
public class sudokuSolverVisualizer {
public static void main(String[] args) {
new GameFrame();
}
}
Here is the Game Frame class:
import javax.swing.*;
public class GameFrame extends JFrame {
GameFrame(){
this.add(new GamePanel());
this.setTitle("Sudoku Solver");
this.setResizable(false);
this.pack();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setVisible(true);
}
}
And here is my JPanel class that solves the sudoku board and updates the graphics:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
public class GamePanel extends JPanel implements ActionListener {
//region Variable Declaration
private static final int SIZE = 9;
static final int SCREEN_WIDTH = 270;
static final int SCREEN_HEIGHT = 270;
static final int UNIT_SIZE = SCREEN_WIDTH/SIZE;
static final int GAME_DELAY = 25;
static final int[][] board = {
{7, 0, 2, 0, 5, 0, 6, 0, 0},
{0, 0, 0, 0, 0, 3, 0, 0, 0},
{1, 0, 0, 0, 0, 9, 5, 0, 0},
{8, 0, 0, 0, 0, 0, 0, 9, 0},
{0, 4, 3, 0, 0, 0, 7, 5, 0},
{0, 9, 0, 0, 0, 0, 0, 0, 8},
{0, 0, 9, 7, 0, 0, 0, 0, 5},
{0, 0, 0, 2, 0, 0, 0, 0, 0},
{0, 0, 7, 0, 4, 0, 2, 0, 3}
};
static boolean solving = false;
static boolean solved = false;
static Timer timer;
//endregion
GamePanel(){
this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
this.setBackground(Color.lightGray);
this.setFocusable(true);
this.addKeyListener(new MyKeyAdapter());
startGame();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
draw(g);
}
public void startGame(){
timer = new Timer(GAME_DELAY, this);
timer.start();
}
private static void draw(Graphics g) {
if (solving){
solveBoard(board, g);
}
Graphics2D g2 = (Graphics2D) g;
for (int row = 0; row < SIZE; row++){
if (row % 3 == 0 && row != 0){
g2.setStroke(new BasicStroke(5));
} else {
g2.setStroke(new BasicStroke(1));
}
g2.draw(new Line2D.Float(0, row * UNIT_SIZE, SCREEN_WIDTH, row * UNIT_SIZE));
for (int column = 0; column < SIZE; column++){
if (column % 3 == 0 && column != 0){
g2.setStroke(new BasicStroke(5));
} else {
g2.setStroke(new BasicStroke(1));
}
g2.draw(new Line2D.Float(column * UNIT_SIZE, 0, column * UNIT_SIZE, SCREEN_HEIGHT));
if (solved){
g.setColor(Color.green);
}
g2.drawString(String.valueOf(board[row][column]), column * UNIT_SIZE + 12, row * UNIT_SIZE + 22);
g.setColor(Color.black);
}
}
}
private static boolean isInRow(int[][] board, int num, int row){
for (int i = 0; i < SIZE; i++){
if (board[row][i] == num){
return true;
}
}
return false;
}
private static boolean isInColumn(int[][] board, int num, int column){
for (int i = 0; i < SIZE; i++){
if (board[i][column] == num){
return true;
}
}
return false;
}
private static boolean isInBox(int[][] board, int num, int row, int column){
int rowStart = row - row % 3;
int columnStart = column - column % 3;
for (int i = rowStart; i < rowStart + 3; i++){
for (int j = columnStart; j < columnStart + 3; j++) {
if (board[i][j] == num) {
return true;
}
}
}
return false;
}
private static boolean isValidPlace(int[][] board, int num, int row, int column){
return !isInRow(board, num, row) &&
!isInColumn(board, num, column) &&
!isInBox(board, num, row, column);
}
private static boolean solveBoard(int[][] board, Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.black);
for (int row = 0; row < SIZE; row++) {
for (int column = 0; column < SIZE; column++) {
if (board[row][column] == 0) {
for (int num = 1; num <= SIZE; num++) {
if (isValidPlace(board, num, row, column)) {
board[row][column] = num;
drawElements(g2, row, column, true);
if (solveBoard(board, g)) {
return true;
}
else{
board[row][column] = 0;
drawElements(g2, row, column, false);
}
}
}
return false;
}
}
}
solving = false;
solved = true;
draw(g);
return true;
}
private static void drawElements(Graphics2D g2, int row, int column, boolean correct){
if (correct) {
g2.setColor(Color.green);
} else {
g2.setColor(Color.red);
}
g2.clearRect(column * UNIT_SIZE, row * UNIT_SIZE, UNIT_SIZE, UNIT_SIZE);
g2.drawString(String.valueOf(board[row][column]), column * UNIT_SIZE + 12, row * UNIT_SIZE + 22);
g2.drawRect(column * UNIT_SIZE, row * UNIT_SIZE, UNIT_SIZE, UNIT_SIZE);
g2.setColor(Color.black);
}
private static void printBoard(int[][] board){
for (int row = 0; row < SIZE; row++){
if (row % 3 == 0 && row != 0){
System.out.println("-------------------");
}
for (int column = 0; column < SIZE; column++){
if (column % 3 == 0 && column != 0){
System.out.print("|");
}
System.out.print(board[row][column] + " ");
}
System.out.println();
}
}
@Override
public void actionPerformed(ActionEvent e) {
if (solving) {
repaint();
}
}
public class MyKeyAdapter extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()){
case KeyEvent.VK_ENTER:
solving = true;
break;
}
}
}
}
If you could help me find a way to pause the graphics in between each attempt at each slot or somehow repaint the graphics every time it tries a number, that would be great!