/
Binom
/
SudokuSolverAlise
Обзор
Документация
Войти
/
Binom
/
SudokuSolverAlise
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
SudokuGUI.java
492 строки
20 KB
Binom
upload files
23 ноя 2025, 12:16
23 ноя 2025, 12:16
a87f9bf
Код
Авторство
О чём код?
package org.example; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.AbstractDocument; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.DocumentFilter; import java.awt.*; import java.awt.event.*; import java.sql.Array; import java.util.ArrayList; import java.util.Map; import java.util.stream.Collectors; public class SudokuGUI extends JFrame { public static JTextField[][] cells = new JTextField[9][9]; public static JPanel[][] miniGrids = new JPanel[9][9]; private SudokuBoard board; private SudokuSolver solver; JTextField selectedCell = null; public boolean show = false; // Цвета для блоков private final Color DARK_BG = Color.LIGHT_GRAY; // Слегка затемнённый фон private final Color LIGHT_BG = Color.WHITE; // Обычный фон private final int BORDER_THICKNESS = 3; // Толщина границы блока 3x3 // Кнопки внизу private JButton solveButton; private JButton clearButton; private JButton hintButton; private final JButton[] btn = new JButton[9]; public SudokuGUI() { setTitle("Решатель Судоку"); setSize(600, 750); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setAlwaysOnTop(true); setLayout(new BorderLayout()); createMenu(); createGridPanel(); // Новая улучшенная сетка createBottomPanel(); board = new SudokuBoard(); solver = new SudokuSolver(); } private void createGridPanel() { JPanel gridPanel = new JPanel(new GridLayout(3, 3, 0, 0)); // 3x3 больших блока gridPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); for (int blockRow = 0; blockRow < 3; blockRow++) { for (int blockCol = 0; blockCol < 3; blockCol++) { JPanel block = createBlock(blockRow, blockCol); gridPanel.add(block); } } add(gridPanel, BorderLayout.CENTER); } private JPanel createBlock(int blockRow, int blockCol) { JPanel blockPanel = new JPanel(new GridLayout(3, 3, 0, 0)); boolean darkBackground = isDarkBlock(blockRow, blockCol); Color bgColor = darkBackground ? DARK_BG : LIGHT_BG; blockPanel.setBackground(bgColor); // Определяем, какие границы рисовать int top = (blockRow == 0) ? BORDER_THICKNESS : 0; int bottom = BORDER_THICKNESS; // Всегда рисуем нижнюю границу int left = (blockCol == 0) ? BORDER_THICKNESS : 0; int right = BORDER_THICKNESS; // Всегда рисуем правую границу blockPanel.setBorder(BorderFactory.createMatteBorder(top, left, bottom, right, Color.BLACK)); // Ячейки без границ for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { int row = blockRow * 3 + i; int col = blockCol * 3 + j; JPanel blockPanelCells = new JPanel(); blockPanelCells.setLayout(new OverlayLayout(blockPanelCells)); JTextField cell = new JTextField(); cell.setHorizontalAlignment(JTextField.CENTER); cell.setPreferredSize(new Dimension(50, 50)); cell.setFont(new Font("Arial", Font.BOLD, 40)); cell.setForeground(Color.BLUE); cell.setBackground(bgColor); cell.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1)); ((AbstractDocument) cell.getDocument()).setDocumentFilter(new DocumentSizeFilter(1, this, row, col)); cell.addCaretListener(e -> { if (!cell.getText().isEmpty()) { validateCell(row, col); } }); // Слушатель фокуса cell.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { selectedCell = (JTextField) e.getComponent(); // Находим индексы ячейки int row = -1, col = -1; for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { if (cells[i][j] == selectedCell) { row = i; col = j; break; } } } if (row == -1 || col == -1) return; showCandidates(); // Переключаем в режим ввода: показываем JTextField, скрываем сетку cells[row][col].setVisible(true); miniGrids[row][col].setVisible(false); // Даём фокус на JTextField cells[row][col].requestFocusInWindow(); } }); cell.addKeyListener(new KeyAdapter() { @Override public void keyTyped(KeyEvent e) { char c = e.getKeyChar(); if (!(Character.isDigit(c) && (c >= '1' && c <= '9') || Character.isISOControl(c))) { e.consume(); // Блокируем ввод неправильных символов // Обновляем модель board.getBoard()[row][col].setValue(Character.getNumericValue(c)); // Синхронизируем UI int[][] uiData = loadBoardFromUI(); board.updateFromArray(uiData); showCandidates(); // Снимаем фокус (чтобы переключиться в режим сетки) cell.transferFocus(); } } }); // Добавляем слушатель изменений ячейки cell.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { showCandidates(); } @Override public void removeUpdate(DocumentEvent e) { showCandidates(); } @Override public void changedUpdate(DocumentEvent e) { showCandidates(); } }); // Грид для возможных значений JPanel miniGrid = new JPanel(new GridLayout(3, 3, 0,0)); miniGrid.setBackground(bgColor); miniGrid.setPreferredSize(new Dimension(50, 50)); miniGrid.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1)); miniGrid.setOpaque(true); // Прозрачность для наложения miniGrid.setVisible(false); // Скрываем изначально cells[row][col] = cell; miniGrids[row][col] = miniGrid; blockPanelCells.add(miniGrid); blockPanelCells.add(cell); blockPanel.add(blockPanelCells); } } return blockPanel; } // Определяет, должен ли блок быть затемнён (углы и центр) private boolean isDarkBlock(int blockRow, int blockCol) { // Угловые блоки: (0,0), (0,2), (2,0), (2,2) if ((blockRow == 0 && blockCol == 0) || (blockRow == 0 && blockCol == 2) || (blockRow == 2 && blockCol == 0) || (blockRow == 2 && blockCol == 2)) { return true; } // Центральный блок: (1,1) if (blockRow == 1 && blockCol == 1) { return true; } return false; } private void createMenu() { JMenuBar menuBar = new JMenuBar(); // Меню "Файл" JMenu fileMenu = new JMenu("Файл"); JMenuItem saveItem = new JMenuItem("Сохранить доску"); JMenuItem loadItem = new JMenuItem("Загрузить доску"); fileMenu.add(saveItem); fileMenu.add(loadItem); menuBar.add(fileMenu); // Меню "Решение" JMenu solveMenu = new JMenu("Решение"); JMenuItem solveItem = new JMenuItem("Решить"); JMenuItem clearItem = new JMenuItem("Очистить доску"); JMenuItem hintItem = new JMenuItem("Показать"); JMenuItem showpossibleItem = new JMenuItem("Показывать возможные значения"); JMenuItem hidepossibleItem = new JMenuItem("Не показывать возможные значения"); solveMenu.add(solveItem); solveMenu.add(clearItem); solveMenu.add(hintItem); solveMenu.add(showpossibleItem); solveMenu.add(hidepossibleItem); menuBar.add(solveMenu); setJMenuBar(menuBar); // Назначаем слушатели saveItem.setName("save"); loadItem.setName("load"); solveItem.setName("solve"); clearItem.setName("clear"); hintItem.setName("hint"); showpossibleItem.setName("possible"); hidepossibleItem.setName("disable"); } private JPanel createNumberButtonsPanel() { JPanel buttonsPanel = new JPanel(); buttonsPanel.setLayout(new GridLayout(1, 9)); // 1 строка, 9 столбцов for (int i = 0; i < 9; i++) { btn[i] = getjButton(i + 1); buttonsPanel.add(btn[i]); } return buttonsPanel; } private JButton getjButton(int i) { JButton btn = new JButton(); btn.setForeground(Color.BLACK); btn.setFont(new Font("Aptos", Font.PLAIN, 40)); btn.setPreferredSize(new Dimension(50, 50)); btn.setText(String.valueOf(i)); btn.setFocusable(false); btn.setBackground(Color.WHITE); btn.setBorder(BorderFactory.createLineBorder(Color.WHITE)); return btn; } private void createBottomPanel() { JPanel bottomPanel = new JPanel(); bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS)); JPanel bottomUpPanel = new JPanel(); bottomUpPanel.setPreferredSize(new Dimension(50, 70)); bottomUpPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 40, 22)); JPanel bottomDownPanel = createNumberButtonsPanel(); solveButton = new JButton("Решить"); solveButton.setForeground(Color.BLUE); solveButton.setFont(new Font("Arial", Font.BOLD, 20)); clearButton = new JButton("Очистить"); clearButton.setForeground(Color.BLUE); clearButton.setFont(new Font("Arial", Font.BOLD, 20)); hintButton = new JButton("Показать"); hintButton.setForeground(Color.BLUE); hintButton.setFont(new Font("Arial", Font.BOLD, 20)); bottomUpPanel.add(solveButton); bottomUpPanel.add(clearButton); bottomUpPanel.add(hintButton); bottomPanel.add(bottomUpPanel); bottomPanel.add(bottomDownPanel); add(bottomPanel, BorderLayout.SOUTH); } // Геттеры для кнопок и меню-пунктов public JButton getSolveButton() { return solveButton; } public JButton getClearButton() { return clearButton; } public JButton getHintButton() { return hintButton; } public JButton[] getBtn() { return btn; } public void addMenuItemListener(String name, ActionListener listener) { JMenuBar menuBar = getJMenuBar(); for (int i = 0; i < menuBar.getMenuCount(); i++) { JMenu menu = menuBar.getMenu(i); for (int j = 0; j < menu.getItemCount(); j++) { JMenuItem item = menu.getItem(j); if (item != null && item.getName().equals(name)) { item.addActionListener(listener); } } } } public int[][] loadBoardFromUI() { int[][] values = new int[9][9]; for (int row = 0; row < 9; row++) { for (int col = 0; col < 9; col++) { String text = cells[row][col].getText().trim(); values[row][col] = text.isEmpty() ? 0 : Integer.parseInt(text); } } return values; } public void updateUIWithSolution(SudokuBoard board) { Cell[][] boardData = board.getBoard(); for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { int value = boardData[i][j].getValue(); cells[i][j].setText(value == 0 ? "" : String.valueOf(value)); } } } static class DocumentSizeFilter extends DocumentFilter { private final int maxLength; private final SudokuGUI gui; private int row, col; public DocumentSizeFilter(int maxLength, SudokuGUI gui, int row, int col) { this.maxLength = maxLength; this.gui = gui; this.row = row; this.col = col; } @Override public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException { if (fb.getDocument().getLength() + string.length() <= maxLength) { super.insertString(fb, offset, string, attr); gui.validateCell(row, col); // Запуск проверки после ввода } } @Override public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { if (fb.getDocument().getLength() + text.length() - length <= maxLength) { super.replace(fb, offset, length, text, attrs); gui.validateCell(row, col); // Запуск проверки после замены } } public void remove(FilterBypass fb, int offset, int length) throws BadLocationException { fb.remove(offset, length); gui.validateCell(row, col); // Проверка при удалении } } private void validateCell(int row, int col) { String value = cells[row][col].getText(); if (value.isEmpty()) { resetCellStyle(cells[row][col]); // Сбрасываем стиль для пустой ячейки return; } ArrayList<JTextField> conflicts = findConflicts(row, col, value); // Сбрасываем стили у всех ячеек (чтобы убрать старые красные метки) for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { resetCellStyle(cells[i][j]); } } // Подсвечиваем только конфликтующие for (JTextField cell : conflicts) { cell.setForeground(Color.RED); cell.setBorder(BorderFactory.createLineBorder(Color.RED, 2)); } // Если есть конфликты — подсвечиваем текущую ячейку if (!conflicts.isEmpty()) { cells[row][col].setForeground(Color.RED); cells[row][col].setBorder(BorderFactory.createLineBorder(Color.RED, 2)); } } // Вспомогательный метод для поиска конфликтов private ArrayList<JTextField> findConflicts(int row, int col, String value) { // ArrayList<JTextField> conflicts = new ArrayList<>(); int digit = Integer.parseInt(value); // Собираем конфликтующие ячейки ArrayList<JTextField> conflictCells = new ArrayList<>(); // Проверка строки for (int j = 0; j < 9; j++) { if (j != col && cells[row][j].getText().equals(value)) { conflictCells.add(cells[row][j]); } } // Проверка столбца for (int i = 0; i < 9; i++) { if (i != row && cells[i][col].getText().equals(value)) { conflictCells.add(cells[i][col]); } } // Проверка блока 3×3 int blockRow = (row / 3) * 3; int blockCol = (col / 3) * 3; for (int i = blockRow; i < blockRow + 3; i++) { for (int j = blockCol; j < blockCol + 3; j++) { if (i != row || j != col) { // Не сравнивать с собой if (cells[i][j].getText().equals(value)) { conflictCells.add(cells[i][j]); } } } } // Подсвечиваем конфликтующие ячейки красным for (JTextField cell : conflictCells) { cell.setForeground(Color.RED); cell.setBorder(BorderFactory.createLineBorder(Color.RED, 2)); } // Текущую ячейку тоже подсвечиваем, если есть конфликты if (!conflictCells.isEmpty()) { cells[row][col].setForeground(Color.RED); cells[row][col].setBorder(BorderFactory.createLineBorder(Color.RED, 2)); } else { // Если конфликтов нет — сбрасываем стили resetCellStyle(cells[row][col]); } return conflictCells; } private void resetCellStyle(JTextField cell) { cell.setForeground(Color.BLUE); cell.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1)); } public void showCandidates() { if (!show) { for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { miniGrids[i][j].setVisible(false); } } } else { int[][] uiData = loadBoardFromUI(); board.updateFromArray(uiData); board.updateHiddenSingles(); ArrayList<Integer> candidates; for (int row = 0; row < 9; row++) { for (int col = 0; col < 9; col++) { int value = board.getBoard()[row][col].getValue(); JPanel grid = miniGrids[row][col]; if (value == 0) { grid.removeAll(); grid.revalidate(); grid.repaint(); candidates = board.getPossibleValues(row, col); for (int num : candidates) { String text = (num != 0) ? String.valueOf(num) : " "; JLabel label = new JLabel(text); label.setFont(new Font("Arial", Font.PLAIN, 14)); label.setHorizontalAlignment(JLabel.CENTER); grid.add(label); miniGrids[row][col].setVisible(true); } } else { miniGrids[row][col].setVisible(false); } } } } } }