| |

JSudoku

Yet another Sudoku program

Main interface

Down to the basics, here’s what I did:

  1. Generate the whole board with backtracking, randomizing the number I chose at each step and remember the choice, this assures that the board is random, while still keep the algorithm run in an acceptable time (it doesn’t have to regenerate a long track of numbers if it get into some sticky situation). Sudoku board have to keep up with a number of rules that make up a complex vector class, lost in there and you’re done. The other method to generate the board was to swap the rows/column/numbers, it’s safe and much faster but it took more time to implement, and it generates only a subclass of the possible Sudoku class, but it’s a good choice if you don’t want recursive calls in your program.
  2. Difficulty: I implemented a simple, but it’s not good for real application, I think. A good difficulty implementation should have taken humans’ deduction rules into consideration (you should know what are these these if you’ve played Sudoku before), and the algorithm should also make sure that with the given cells, only one solution is possible, this is a continuous generate-remove-check for solutions-remove loop and it’s complex to implement. So, I have only removed a number of cells based on difficulty, the more difficulty chosen, the less given cell there are. This implementation suffers from the above points.
  3. Build a usable Sudoku class’ interface: I deducted from coding that you need to store both the solution and the playing board in the class, provide board and solution get method, the set move method for the board should only accept valid moves, this way you only need to count the number of moves played to know if the player have won or not. This has the drawback that you can’t store invalid values in the board (to visually notify the player of their wrong moves), but it keeps your code clean of unnecessary checks.
  4. Build the interface: If you choose to manually generate the JFrame, JButton and uses a loop to create JTextField, it saves time to point-and-click creating the cells, but you will have to manually calculate the button and frame’s position to get a nice interface; the other way around, you’ll have to create JTextFields 81 times. I choose a hybrid approach: use the designer to design the form, add a JPanel to it, and then add the JTextField in the user interface’s constructor.
  5. Just for kicks: I add a key press handler into the text fields, the handler catches input, and if it’s not a valid number, not correct according to Sudoku rules or some other reason, the cell will be highlighted so the user can see it easily, the hint function is implemented in this way too: it compares what the user have to the solution and highlight the differences.

Creating text fields

    /** Creates new form SudokuInterface */
    public SudokuInterface() {
        initComponents();

        // Automatically arranges the cells into a grid-like layout
        jPanel1.setLayout(new GridLayout(Sudoku.ROWS, Sudoku.COLUMNS));
        fields = new JTextField[Sudoku.ROWS * Sudoku.COLUMNS];
        for (int i = 0; i < Sudoku.ROWS * Sudoku.COLUMNS; i++) {
            fields[i] = new JTextField(1);
            fields[i].setSize(66, 66);
            fields[i].setFont(cellFont);
            fields[i].setName(Integer.toString(i));
            fields[i].addKeyListener(cellInputHander);
            fields[i].setHorizontalAlignment(JTextField.CENTER);
            fields[i].setEnabled(false);
            jPanel1.add(fields[i]);
        }

        // First screen
        String intro = "JSudoku";
        for (int i = 1; i < intro.length() + 1; i++) {
            fields[4 * Sudoku.ROWS + i].setText(intro.charAt(i - 1) + "");
        }

    }

Handling key input

    private final KeyListener cellInputHander = new KeyListener() {

        /**
         * Controls, the user's input, let them input invalid data and display it
         * to avoid confusing the user (higher usability), but this won't be
         * reflected in the actual game, we have to keep track of which cells are
         * invalid, resulting in this complex implementation
         */
        public void keyTyped(KeyEvent e) {
            e.consume();
            int move = 0;
            String moveString = e.getKeyChar() + "";
            try {
                move = Integer.parseInt(moveString);
            } catch (Exception exception) {
                return;
            }
            // If the move is invalid, do nothing, the key won't appear
            int cellIndex = Integer.parseInt(e.getComponent().getName());
            if (!game.set(cellIndex, move)) {
                ((JTextField)e.getSource()).setForeground(cellIncorrectForeground);
                jLabel3.setText("You have just performed an invalid move");
                isInvalid[cellIndex] = true;
            } else {
                ((JTextField)e.getSource()).setForeground(cellEnabledForeground);
                jLabel3.setText("");
                isInvalid[cellIndex] = false;
            }
            ((JTextField)e.getSource()).setText(moveString);
            if (game.won()) {
                JOptionPane.showMessageDialog(rootPane, "You have solved the puzzle!", "Congratulation", JOptionPane.INFORMATION_MESSAGE);
                newGame(lastDifficulty);
            }
        }

        public void keyPressed(KeyEvent e) {

        }

        public void keyReleased(KeyEvent e) {

        }
    };

Move checking
Like I said, there’s plenty of room for improvement, so here’s the source code if you want to do just that 😉

Leave a Reply

Your email address will not be published. Required fields are marked *