在 Java 中使用 JPanel 的数独板

Posted

技术标签:

【中文标题】在 Java 中使用 JPanel 的数独板【英文标题】:Sudoku Board Using JPanels in Java 【发布时间】:2012-10-04 20:53:48 【问题描述】:

我知道已经有几篇关于数独相关问题的帖子,但我不确定其中是否有我正在寻找的内容......

我正在尝试使用 JPanels 和 JTextfields 在 Java 中构建一个空的数独板。我还需要使用另一个 JPanel 在右侧创建一个菜单。

棋盘本身是一个 9 x 9 的正方形,分成 9 个 3x3 的正方形。请注意,每个较小的正方形都由比常规正方形间边界更重的边界隔开。每个方块都是一个文本字段。编写程序,使文本字段中没有任何内容。用户可以根据需要在文本字段中输入,如果输入,数字将显示出来。侧面有四个按钮,可让您解决、获得新谜题、获得提示或重置谜题。

任何想法都会很棒。我无法理解如何嵌套 for 循环来创建板。这是我的代码...

    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);


【问题讨论】:

数组索引通常从 0 开始;) 请查看example,了解当前问题。 【参考方案1】:

首先,我会使用某种模型来控制“虚拟”板中的值,这会将逻辑与 UI 分开,并允许更改其中一个而不会对另一个产生不利影响。

我会为模型提供适当的事件,以允许在模型更改时更新 UI,并为每个字段提供根据需要更新模型的方法。

然后我会将问题简化为最小的概念组件,即子板,并生成 UI 以尽可能抽象的方式表示它。这允许重复使用并有助于调试,就像一块板有问题一样,您可以在一个地方修复它。

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);
                
            
        
    

更新

要将文本字段限制为仅允许输入数值,您可以查看JTextField limiting character amount input and accepting numeric only 了解一些想法

已更新(使用二维数组)

这是一个使用 2D 数组的实现,它还对子板进行分组,以便每个 3x3 字段的网格都有自己的板...

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);
                
            
        

    

或者,如果您想尝试将所有字段保留在一个***引用中,您可以执行类似...

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);
            
        
    


单类更新

好吧,与其子类化,不如简单地使用几种方法来创建板的每个单独部分,您可以从中重复调用...

记住、减少和重用。

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);
                
            
        
    

【讨论】:

这看起来非常令人印象深刻,但我们在课堂上还没有那么先进。它只是 jpanels 上的一个二维文本框数组,右侧有一个菜单。 更新为使用 2D 数组并在每个 3x3 网格周围应用一块板:P 感谢您的所有帮助。现在,当我尝试实现 main 方法时,出现错误: JFrame frame = new SudBoard(); (“无法将 SudBoard 转换为 JFrame。”) SubBoard 是 JPanel 而不是 JFrame,它们是不同的类。您需要将SubBoard 添加到JFrame,就像我的Sudoku 类在它的构造函数中所做的那样 请看本页底部的代码。我需要把这一切都放在一个班级里,所以我试图附加你所拥有的。我的课程名为“SudBoard”,我在底部有 main 方法。【参考方案2】:

我看到的几件事:

我不认为你现在想要 9x9 JPanels,而是 9x9 JTextFields。您可能需要 3x3 JPanels,这样您就可以使每个部分的边框更粗。明确地列出这些而不是尝试循环执行可能更容易。

您的循环计数器(和数组索引)应该从 0 开始,而不是 1。按照现在的方式,循环只会执行 8 次。

您将要跟踪每一行、每一列以及每个 3x3 子组中的值。行和列很简单,就像您在 2D 数组中一样。您可能会考虑使用另一个数组来保存每个 3x3 区域中的值。这样可以在您需要时更轻松地扫描这些值,并且如果您走这条路,可能有助于将值放置在较小的 3x3 JPanels 中。

【讨论】:

以上是关于在 Java 中使用 JPanel 的数独板的主要内容,如果未能解决你的问题,请参考以下文章

如何用独特的解决方案生成数独板

Java问题中的蛮力数独求解器算法

Java中的数独求解器,使用回溯和递归

数独生成器的递归解决方案

LeetCode题目--有效的数独(python/Java实现)

java刷题--36有效的数独