# Java Notes

# 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:

1. `Sudoku.java` is main JFrame subclass, which has most of the user interface code. It also creates a new model object.
2. `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.
3. `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);
                }
            }
        }
    }