Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sudoku Board Using JPanels in Java

I know there have been several posts already about Sudoku-related questions, but I am not sure any of them have exactly what I am looking for...

I am trying to build an empty Sudoku board in Java using JPanels and JTextfields. I also need to create a menu on the right hand side with another JPanel.

The board itself is a 9 x 9 square, divided into 9 3x3 squares. Note that each smaller square is set off by a heavier border than the regular intersquare borders. Each square is a text field. Write the program so that nothing is in a text field. Users can type in the text field if they want, and if they do, numbers will show up. On the side there are four buttons that allow you to solve, get a new puzzle, get a hint, or reset puzzle.

Any ideas would be great. I am having trouble understanding how to nest the for loops to create the board. Here is my code...

    import javax.swing.*;
    import javax.swing.border.Border;
    import java.awt.*;

    public class ArrayTest extends JFrame {

        public ArrayTest() {

    JPanel board = new JPanel(new GridLayout(9, 9));
    add(board);

    JPanel[][] squares = new JPanel[9][9];

    Border border = BorderFactory.createLineBorder(Color.BLACK);


    for (int row = 1; row < 9; row++) {

        for (int col = 1; col < 9; col++) {
            squares[row][col] = new JPanel();
            board.add(squares[row][col]);

        }

    }



    JPanel menu = new JPanel();
    menu.add(new JButton("Reset"));
    menu.add(new JButton("Hint"));
    menu.add(new JButton("Solve"));
    menu.add(new JButton("New Puzzle"));



    add(menu);

}
public static void main(String[] args) {
    // TODO Auto-generated method stub

    /** Create a frame and set its properties*/
    JFrame frame = new ArrayTest();
    frame.setTitle("Sudoku");
    frame.setSize(600, 600);
    frame.setLocationRelativeTo(null); //Center the frame
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);

}

}

like image 936
Adam Daniel Avatar asked Dec 21 '22 15:12

Adam Daniel


2 Answers

A couple of things I see:

  • I don't think you want 9x9 JPanels as you have now, but 9x9 JTextFields. You may want 3x3 JPanels so you can make the borders of each section bolder. It might be easier to just lay these out explicitly instead of trying to do it in a loop.

  • Your loop counters (and array indices) should start at 0, not 1. The way you have it now, the loops will only execute 8 times.

  • You are going to want to keep track of values in each row, column, and in each 3x3 sub-group. Rows and columns are easy the way you have it in a 2D array. You might consider another array of arrays that hold the values in each 3x3 area. This makes it easier to scan through these values when you need to, and might be useful for placing values in the smaller 3x3 JPanels, if you go that route.

like image 131
Bill the Lizard Avatar answered Dec 24 '22 02:12

Bill the Lizard


Firstly, I would use some kind of model to control the values within a "virtual" board, this will separate the logic from the UI and allow either to change without adversely effecting the other.

I would provide appropriate event with the model to allow the UI to be updated when the model changes as well as providing a means for each field to update the model as required.

I would then reduce the problem to it's smallest conceptional component, which would be the sub board, and generate the UI to represent it in the most abstract manner I could. This allows for re-use and helps with debugging, as if one board has an issue, then you can fix it in one place for all.

public class Sudoku {

    public static void main(String[] args) {
        new Sudoku();
    }

    public Sudoku() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new SudokuBoard());
                frame.add(new MenuPane(), BorderLayout.AFTER_LINE_ENDS);
                frame.pack();
                frame.setVisible(true);
            }
        });
    }

    public class MenuPane extends JPanel {

        public MenuPane() {
            setBorder(new EmptyBorder(4, 4, 4, 4));
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 1;
            gbc.weightx = 1;
            gbc.fill = GridBagConstraints.HORIZONTAL;

            add(new JButton("Solve"), gbc);
            gbc.gridy++;
            add(new JButton("New"), gbc);
            gbc.gridy++;
            add(new JButton("Hint"), gbc);
            gbc.gridy++;
            add(new JButton("Reset"), gbc);
        }
    }

    public class SudokuBoard extends JPanel {

        public static final int ROWS = 3;
        public static final int COLUMNS = 3;

        private SubBoard[] subBoards;

        public SudokuBoard() {
            setBorder(new EmptyBorder(4, 4, 4, 4));
            subBoards = new SubBoard[ROWS * COLUMNS];
            setLayout(new GridLayout(ROWS, COLUMNS, 2, 2));
            for (int row = 0; row < ROWS; row++) {
                for (int col = 0; col < COLUMNS; col++) {
                    int index = (row * ROWS) + col;
                    SubBoard board = new SubBoard();
                    board.setBorder(new CompoundBorder(new LineBorder(Color.GRAY, 3), new EmptyBorder(4, 4, 4, 4)));
                    subBoards[index] = board;
                    add(board);
                }
            }
        }
    }

    public class SubBoard extends JPanel {

        public static final int ROWS = 9;
        public static final int COLUMNS = 9;

        private JTextField[] fields;

        public SubBoard() {
            setLayout(new GridLayout(ROWS, COLUMNS, 2, 2));
            fields = new JTextField[ROWS * COLUMNS];
            for (int row = 0; row < ROWS; row++) {
                for (int col = 0; col < COLUMNS; col++) {
                    int index = (row * COLUMNS) + col;
                    JTextField field = new JTextField(4);
                    fields[index] = field;
//                    field.setText(Integer.toString(index));
                    add(field);
                }
            }
        }
    }
}

UPDATED

To limit the text fields to only allow numeric values to be entered, you can take a look at JTextField limiting character amount input and accepting numeric only for some ideas

enter image description here

UPDATED (with 2D arrays)

Here's a implementation that uses 2D arrays, it also sub groups the sub boards so that each grid of 3x3 fields has it's own board...

enter image description here

public class Sudoku {

    public static void main(String[] args) {
        new Sudoku();
    }

    public Sudoku() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new SudokuBoard());
                frame.add(new MenuPane(), BorderLayout.AFTER_LINE_ENDS);
                frame.pack();
                frame.setVisible(true);
            }
        });
    }

    public class MenuPane extends JPanel {

        public MenuPane() {
            setBorder(new EmptyBorder(4, 4, 4, 4));
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 1;
            gbc.weightx = 1;
            gbc.fill = GridBagConstraints.HORIZONTAL;

            add(new JButton("Solve"), gbc);
            gbc.gridy++;
            add(new JButton("New"), gbc);
            gbc.gridy++;
            add(new JButton("Hint"), gbc);
            gbc.gridy++;
            add(new JButton("Reset"), gbc);

        }

    }

    public class SudokuBoard extends JPanel {

        public static final int ROWS = 3;
        public static final int COLUMNS = 3;

        private SubBoard[][] subBoards;

        public SudokuBoard() {
            setBorder(new EmptyBorder(4, 4, 4, 4));
            subBoards = new SubBoard[ROWS][COLUMNS];
            setLayout(new GridLayout(ROWS, COLUMNS, 2, 2));
            for (int row = 0; row < ROWS; row++) {
                for (int col = 0; col < COLUMNS; col++) {
                    int index = (row * ROWS) + col;
                    SubBoard board = new SubBoard();
                    board.setBorder(new CompoundBorder(new LineBorder(Color.GRAY, 3), new EmptyBorder(4, 4, 4, 4)));
                    subBoards[row][col] = board;
                    add(board);
                }
            }
        }

    }

    public class SubBoard extends JPanel {

        public SubBoard() {
            setLayout(new GridLayout(3, 3, 2, 2));

            for (int index = 0; index < 3*3; index++) {
                add(new ChildBoard(3, 3));
            }

        }
    }

    public class ChildBoard extends JPanel {

        private JTextField[][] fields;

        public ChildBoard(int rows, int cols) {
            setBorder(new LineBorder(Color.LIGHT_GRAY));
            setLayout(new GridLayout(rows, cols, 2, 2));
            fields = new JTextField[rows][cols];
            for (int row = 0; row < rows; row++) {
                for (int col = 0; col < cols; col++) {
                    JTextField field = new JTextField(4);
                    fields[row][col] = field;
                    add(field);
                }
            }
        }

    }
}

or, if you want to try and keep all the fields in a single top level reference you could do something like...

public class SubBoard extends JPanel {

    private JTextField[][] fields;

    public SubBoard() {
        setLayout(new GridLayout(3, 3, 2, 2));

        fields = new JTextField[9][9];
        for (int row = 0; row < 9; row++) {
            for (int col = 0; col < 9; col++) {
                fields[row][col] = new JTextField(4);
            }
        }

        for (int row = 0; row < 3; row++) {
            for (int col = 0; col < 3; col++) {

                int startRow = row * 3;
                int startCol = col * 3;

                add(new ChildBoard(3, 3, fields, startRow, startCol));

            }
        }

    }
}

public class ChildBoard extends JPanel {

    public ChildBoard(int rows, int cols, JTextField[][] fields, int startRow, int startCol) {
        setBorder(new LineBorder(Color.LIGHT_GRAY));
        setLayout(new GridLayout(rows, cols, 2, 2));
        for (int row = 0; row < rows; row++) {
            for (int col = 0; col < cols; col++) {
                JTextField field = fields[startRow + row][startCol + col];
                fields[row][col] = field;
                add(field);
            }
        }
    }

}

UPDATED with single class

Okay, so rather then sub-classing, simple use a couple of methods to create each individual section of the board, from which you can make repeated calls to...

enter image description here

Remember, reduce and reuse.

public class Sudoku {

    public static void main(String[] args) {
        new Sudoku();
    }

    public Sudoku() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new SudokuBoard());
                frame.add(new MenuPane(), BorderLayout.AFTER_LINE_ENDS);
                frame.pack();
                frame.setVisible(true);
            }
        });
    }

    public class MenuPane extends JPanel {

        public MenuPane() {
            setBorder(new EmptyBorder(4, 4, 4, 4));
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 1;
            gbc.weightx = 1;
            gbc.fill = GridBagConstraints.HORIZONTAL;

            add(new JButton("Solve"), gbc);
            gbc.gridy++;
            add(new JButton("New"), gbc);
            gbc.gridy++;
            add(new JButton("Hint"), gbc);
            gbc.gridy++;
            add(new JButton("Reset"), gbc);

        }
    }

    public class SudokuBoard extends JPanel {

        public static final int GRID_ROWS = 3;
        public static final int GRID_COLUMNS = 3;
        public static final int BOARD_ROWS = 9;
        public static final int BOARD_COLUMNS = 9;
        private JTextField fields[][];

        public SudokuBoard() {
            setBorder(new EmptyBorder(4, 4, 4, 4));
            fields = new JTextField[GRID_ROWS * BOARD_ROWS][GRID_COLUMNS * BOARD_COLUMNS];

            setLayout(new GridLayout(GRID_ROWS, GRID_COLUMNS, 2, 2));
            for (int row = 0; row < GRID_ROWS; row++) {
                for (int col = 0; col < GRID_COLUMNS; col++) {
                    int startRow = row * GRID_ROWS;
                    int startCol = col * GRID_COLUMNS;
                    add(createBoard(fields, startRow, startCol));
                }
            }
        }

        protected JPanel createBoard(JTextField fiels[][], int startRow, int startCol) {
            JPanel panel = new JPanel(new GridLayout(3, 3, 2, 2));
            panel.setBorder(new CompoundBorder(new LineBorder(Color.DARK_GRAY, 2), new EmptyBorder(2, 2, 2, 2)));

            for (int row = 0; row < 3; row++) {
                for (int col = 0; col < 3; col++) {
                    int rowIndex = (startRow + row) * 3;
                    int colIndex = (startCol + col) * 3;
                    panel.add(createSubBoard(fields, rowIndex, colIndex));
                }
            }
            return panel;
        }

        protected JPanel createSubBoard(JTextField[][] fields, int startRow, int startCol) {
            JPanel panel = new JPanel(new GridLayout(3, 3, 2, 2));
            panel.setBorder(new CompoundBorder(new LineBorder(Color.GRAY, 2), new EmptyBorder(2, 2, 2, 2)));

            populateFields(fields, startRow, startCol);
            for (int row = 0; row < 3; row++) {
                for (int col = 0; col < 3; col++) {
                    panel.add(fields[row + startRow][col + startCol]);
                }
            }
            return panel;
        }

        protected void populateFields(JTextField[][] fields, int startRow, int startCol) {
            for (int row = startRow; row < startRow + 3; row++) {
                for (int col = startCol; col < startCol + 3; col++) {
                    fields[row][col] = new JTextField(4);
                }
            }
        }
    }
}
like image 27
MadProgrammer Avatar answered Dec 24 '22 01:12

MadProgrammer