import javax.swing.*; import javax.swing.table.DefaultTableModel; import java.awt.*; import java.awt.event.*; import java.sql.*; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.sql.Timestamp; import java.text.SimpleDateFormat; public class Sao_lei extends JFrame { private JButton[] box_x; // 格子 private JMenuItem mo_shi_1, mo_shi_2, mo_shi_3, viewLeaderboard; private String currentUsername = ""; // 当前用户名 private JPanel box_center; private JLabel label_nadu; // 难度标签 private JLabel label_timer; // 计时标签 private int rows, cols; // 地图的行数和列数 private int lei; // 地雷数量 private int[][] mineMap; // 用于存储地图信息的二维数组 private Timer timer; // 计时器 private int timeall; // 经过的时间 private int flagsPlaced; // 已放置的标记数量 private ImageIcon face; // 笑脸图片 private JButton label_face; // 笑脸按钮 private LeaderboardDialog leaderboardDialog; // 排行榜对话框 private static class Mysql_Set {//静态类//只能有一个实类(无需创建类实类)//减少内存//直接通过类名调用//避免多线程安全//不会被修改// private static final String URL = "jdbc:mysql://localhost:3306/saolei?useSSL=false&serverTimezone=UTC"; private static final String USER = "root"; private static final String PASSWORD = "254689"; public static void INIT_Mysql() { try{ Class.forName("com.mysql.cj.jdbc.Driver");//链接驱动包JDBC Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);//建立连接 String SQL_txt = "CREATE TABLE IF NOT EXISTS leaderboard (" +//创建 表 如果 不 存在 leaderboard// "id INT AUTO_INCREMENT PRIMARY KEY," +//id 整形 自增 主建(不能为NULL,不能重复)// "username VARCHAR(50) NOT NULL," +//username 字符串(50) 不能为空// "time_used INT NOT NULL," +//time_used 整形 不能为空// "difficulty VARCHAR(10) NOT NULL," +//difficulty 字符串(10) 不能为空// "completion_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP" +//completion_time 时间戳T 默认当前时间DC// ")";//结束 Statement stmt = conn.createStatement();//创建一个对象 stmt.execute(SQL_txt);//执行SQL语句 } catch (SQLException | ClassNotFoundException e) { JOptionPane.showMessageDialog(null, "数据库连接失败:", "错误", JOptionPane.ERROR_MESSAGE);//弹出错误信息框 } } public static void ADD_Mysql(String username, int timeUsed, String difficulty) {//插入用户数据 try { Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);//建立连接 String sql = "INSERT INTO leaderboard (username, time_used, difficulty) VALUES (?,?,?)";//插入 进入 排行榜 值(?占位符防止攻击) PreparedStatement pstmt = conn.prepareStatement(sql);//创建一个对象 pstmt.setString(1, username);//设置占位符 pstmt.setInt(2, timeUsed);//设置占位符 pstmt.setString(3, difficulty);//设置占位符 pstmt.executeUpdate();//执行SQL语句 } catch (SQLException e) { JOptionPane.showMessageDialog(null, "保存分数失败:" , "错误", JOptionPane.ERROR_MESSAGE); } } public static List GET_Mysql_Top(String difficulty, int limit) {//获取排行榜//返回泛型集合类型 List scores = new ArrayList<>();//定义数组列表Scores String sql = "SELECT username, time_used, completion_time FROM leaderboard " +//选择 1,2,3 来自 表// "WHERE difficulty = ? ORDER BY time_used ASC LIMIT ?";//哪里 难度 = ? 从 表 排序 用时 从低到高 限制 ?// try { Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);//建立连接 PreparedStatement pstmt = conn.prepareStatement(sql);//创建一个对象 pstmt.setString(1, difficulty);//设置占位符 pstmt.setInt(2, limit);//设置占位符 try (ResultSet rs = pstmt.executeQuery()) {//执行SQL语句 while (rs.next()) { scores.add(new ScoreRecord( rs.getString("username"), rs.getInt("time_used"), rs.getTimestamp("completion_time") )); } } } catch (SQLException e) { JOptionPane.showMessageDialog(null, "获取排行榜失败:" , "错误", JOptionPane.ERROR_MESSAGE); } return scores; } } // 排行榜记录类 private static class ScoreRecord { private String username; private int timeUsed; private Timestamp completionTime; public ScoreRecord(String username, int timeUsed, Timestamp completionTime) { this.username = username; this.timeUsed = timeUsed; this.completionTime = completionTime; } public String getUsername() { return username; } public int getTimeUsed() { return timeUsed; } public Timestamp getCompletionTime() { return completionTime; } public String getFormattedTime() { int minutes = timeUsed / 60; int seconds = timeUsed % 60; return String.format("%02d:%02d", minutes, seconds); } } // 排行榜对话框类 private class LeaderboardDialog extends JDialog { private JTable leaderboardTable; private JComboBox difficultyComboBox; private DefaultTableModel tableModel; private static final int MAX_SCORES = 10; public LeaderboardDialog(JFrame parent) { super(parent, "扫雷排行榜", true); // 创建表格模型 String[] columnNames = {"排名", "用户名", "用时", "完成时间"}; tableModel = new DefaultTableModel(columnNames, 0) { @Override public boolean isCellEditable(int row, int column) { return false; // 使表格不可编辑 } }; // 创建表格 leaderboardTable = new JTable(tableModel); leaderboardTable.getColumnModel().getColumn(0).setPreferredWidth(50); leaderboardTable.getColumnModel().getColumn(1).setPreferredWidth(150); leaderboardTable.getColumnModel().getColumn(2).setPreferredWidth(100); leaderboardTable.getColumnModel().getColumn(3).setPreferredWidth(200); // 创建难度选择下拉框 JPanel filterPanel = new JPanel(); filterPanel.add(new JLabel("难度:")); difficultyComboBox = new JComboBox<>(new String[]{"简单", "普通", "困难"}); filterPanel.add(difficultyComboBox); // 添加难度选择事件监听器 difficultyComboBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { loadLeaderboard((String) difficultyComboBox.getSelectedItem()); } }); // 创建主面板 JPanel mainPanel = new JPanel(new BorderLayout()); mainPanel.add(filterPanel, BorderLayout.NORTH); mainPanel.add(new JScrollPane(leaderboardTable), BorderLayout.CENTER); // 创建关闭按钮 JButton closeButton = new JButton("关闭"); closeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { dispose(); } }); JPanel buttonPanel = new JPanel(); buttonPanel.add(closeButton); mainPanel.add(buttonPanel, BorderLayout.SOUTH); // 设置对话框属性 getContentPane().add(mainPanel); setDefaultCloseOperation(DISPOSE_ON_CLOSE); setSize(500, 400); setLocationRelativeTo(parent); // 加载默认难度的排行榜 loadLeaderboard("简单"); } private void loadLeaderboard(String difficulty) { // 清空表格 tableModel.setRowCount(0); // 从数据库加载排行榜数据 List scores = Mysql_Set.GET_Mysql_Top(difficulty, MAX_SCORES); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 填充表格 for (int i = 0; i < scores.size(); i++) { ScoreRecord score = scores.get(i); tableModel.addRow(new Object[]{ i + 1, score.getUsername(), score.getFormattedTime(), dateFormat.format(score.getCompletionTime()) }); } } public void showLeaderboard(String difficulty) { difficultyComboBox.setSelectedItem(difficulty); loadLeaderboard(difficulty); setVisible(true); } } // 登录对话框类 private class LoginDialog extends JDialog { private JTextField usernameField; private JButton loginButton; private JButton cancelButton; private boolean loggedIn = false; private String username = ""; public LoginDialog(JFrame parent) { super(parent, "登录", true); // 创建面板 JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(5, 5, 5, 5); // 用户名标签 gbc.gridx = 0; gbc.gridy = 0; gbc.anchor = GridBagConstraints.WEST; panel.add(new JLabel("用户名:"), gbc); // 用户名输入框 usernameField = new JTextField(20); gbc.gridx = 1; gbc.gridy = 0; gbc.fill = GridBagConstraints.HORIZONTAL; panel.add(usernameField, gbc); // 按钮面板 JPanel buttonPanel = new JPanel(); loginButton = new JButton("登录"); cancelButton = new JButton("取消"); buttonPanel.add(loginButton); buttonPanel.add(cancelButton); gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.CENTER; panel.add(buttonPanel, gbc); // 添加事件监听器 loginButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (usernameField.getText().trim().isEmpty()) { JOptionPane.showMessageDialog(LoginDialog.this, "请输入用户名", "错误", JOptionPane.ERROR_MESSAGE); return; } username = usernameField.getText().trim(); loggedIn = true; dispose(); } }); cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { dispose(); } }); // 按下Enter键时登录 usernameField.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { loginButton.doClick(); } } }); // 设置对话框属性 getContentPane().add(panel); setDefaultCloseOperation(DISPOSE_ON_CLOSE); pack(); setLocationRelativeTo(parent); setResizable(false); } public boolean isLoggedIn() { return loggedIn; } public String getUsername() { return username; } } public Sao_lei() { // 初始化数据库 Mysql_Set.INIT_Mysql(); // 显示登录对话框 LoginDialog loginDialog = new LoginDialog(this); loginDialog.setVisible(true); if (loginDialog.isLoggedIn()) { currentUsername = loginDialog.getUsername(); } else { // 如果用户取消登录,使用默认用户名 currentUsername = "玩家"; } // 创建排行榜对话框 leaderboardDialog = new LeaderboardDialog(this); //=====================================大窗口 JFrame boxmax = new JFrame("扫雷 - " + currentUsername); boxmax.setSize(500, 600); boxmax.setLayout(new BorderLayout()); // 布局为 BorderLayout boxmax.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭操作 boxmax.setVisible(true); // 窗口可见 //====================================大窗口结束 //====================================顶部I JMenuBar topmenu = new JMenuBar(); JMenu menu1 = new JMenu("开始游戏"); topmenu.add(menu1); JMenu menu2 = new JMenu("难度选择"); topmenu.add(menu2); JMenu menu3 = new JMenu("排行榜"); topmenu.add(menu3); menu1.setFont(new Font("宋体", Font.PLAIN, 17)); menu2.setFont(new Font("宋体", Font.PLAIN, 17)); menu3.setFont(new Font("宋体", Font.PLAIN, 17)); mo_shi_1 = new JMenuItem("简单"); menu2.add(mo_shi_1); mo_shi_2 = new JMenuItem("普通"); menu2.add(mo_shi_2); mo_shi_3 = new JMenuItem("困难"); menu2.add(mo_shi_3); viewLeaderboard = new JMenuItem("查看排行榜"); menu3.add(viewLeaderboard); boxmax.setJMenuBar(topmenu); //=====================================顶部I //===========================================================顶部II JPanel box_tops = new JPanel(); box_tops.setLayout(new BorderLayout()); label_nadu = new JLabel("难度:未选择(选择难度后开始游戏)"); box_tops.add(label_nadu, BorderLayout.WEST); face = new ImageIcon("src/face.png"); face.setImage(face.getImage().getScaledInstance(50, 50, Image.SCALE_SMOOTH)); // 缩小笑脸按钮 label_face = new JButton(face); label_face.setBounds(0, 0, 50, 50); // 设置按钮的大小与图片一致 label_face.setBorderPainted(false); // 不绘制边框 label_face.setContentAreaFilled(false); // 不填充按钮区域 label_face.setFocusPainted(false); // 不绘制焦点边框 box_tops.add(label_face, BorderLayout.CENTER); label_timer = new JLabel("所用时间:00:00"); box_tops.add(label_timer, BorderLayout.EAST); boxmax.add(box_tops, BorderLayout.NORTH); //===========================================================顶部II //===========================================================中心区域 box_center = new JPanel(); JLabel game_text = new JLabel("《基于Java的扫雷》


" + "1. 先选择游戏难度
" + "2. 点击笑脸重置游戏
" + "3. 右键标记

" + "制作人员:
" + "🤵李 钊231040600226;
" + "🤵梁诗超231040600227;
" + "👰刘芮臣231040600229;
"); // 创建标签 game_text.setFont(new Font("宋体", Font.PLAIN, 25)); // 设置字体 game_text.setHorizontalAlignment(JLabel.CENTER); // 设置居中 box_center.add(game_text); // 添加到面板 boxmax.add(box_center, BorderLayout.CENTER); //============================================================中心区域结束 //===========================================================监听 mo_shi_1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { rows = 10; cols = 10; lei = 10; mo_shi_main(rows, cols, lei); label_nadu.setText("难度:简单"); } }); mo_shi_2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { rows = 16; cols = 16; lei = 40; mo_shi_main(rows, cols, lei); label_nadu.setText("难度:普通"); } }); mo_shi_3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { rows = 20; cols = 20; lei = 80; mo_shi_main(rows, cols, lei); label_nadu.setText("难度:困难"); } }); label_face.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // 检查是否已选择难度 if (!checkDifficultySelected()) { return; } timer.stop(); // 停止计时器 timeall = 0; // 重置计时器 Times(); // 更新计时器显示 mo_shi_main(rows, cols, lei); // 重新初始化游戏 face = new ImageIcon("src/face.png"); face.setImage(face.getImage().getScaledInstance(50, 50, Image.SCALE_SMOOTH)); // 缩小笑脸按钮 label_face.setIcon(face); label_face.repaint(); // 重新绘制面板 } }); // 排行榜菜单项监听器 viewLeaderboard.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // 显示排行榜对话框 if (label_nadu.getText().contains("未选择")) { leaderboardDialog.showLeaderboard("简单"); } else { String difficulty = label_nadu.getText().substring(3); leaderboardDialog.showLeaderboard(difficulty); } } }); //=============================================================监听结束 //====================================================================计时器 timer = new Timer(1000, new ActionListener() { public void actionPerformed(ActionEvent e) { timeall++; Times(); } }); //====================================================================计时器结束 } //==========================================================================模式函数 private void mo_shi_main(int rows, int cols, int lei) { box_center.removeAll(); // 清除原有组件 box_center.setLayout(new GridLayout(rows, cols)); // 设置新的布局 box_x = new JButton[rows * cols]; // 创建按钮数组 mineMap = generateMinesweeperMap(rows, cols, lei); // 生成地图 flagsPlaced = 0; // 重置标记数量 for (int i = 0; i < rows * cols; i++) { box_x[i] = new JButton(""); final int index = i; // 用于匿名类中访问 box_x[i].addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { revealCell(index / cols, index % cols); // 点击时揭示格子 } }); box_x[i].addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON3) { // 右键标记 markCell(index / cols, index % cols); } } }); box_center.add(box_x[i]); // 添加到面板 } box_center.revalidate(); // 重新验证布局 box_center.repaint(); // 重新绘制面板 timeall = 0; // 重置计时器 Times(); // 更新计时器显示 timer.start(); // 启动计时器 } //==========================================================================模式函数结束 //=========================================================================================生成扫雷地图 private int[][] generateMinesweeperMap(int rows, int cols, int lei) { int[][] map = new int[rows][cols]; Random random = new Random(); //1.定义二维数组,定义随机数生成器 //2.遍历总的雷的数量 //3.使用random.nextInt()方法随机生成0-rows-1的整数,传给r,作为行坐标 //4.使用random.nextInt()方法随机生成0-cols-1的整数,穿给c,作为列坐标 //5.判断二维数组中对应坐标的值是否为-1,如果不是-1,将其赋值为-1,并将lei_num加1 int lei_num = 0; while (lei_num < lei) { int r = random.nextInt(rows);//随机生成0-rows-1的整数 int c = random.nextInt(cols); if (map[r][c] != -1) { map[r][c] = -1; lei_num++; } } //1.遍历总的行和列 //2.判断到此处不为雷 //3.定义一个变量lei_num_count,记录周围雷的数量 //4.设置for循环-1到1的偏离值,通过行列加减遍历周围8个格子 //5.跳过自身 //6.新的偏离值,固定在全局中, //7.判断周围是否为雷,如果是,lei_num_count加1 //8.将lei_num_count赋值给二维数组对应坐标的值 for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { if (map[r][c] != -1) { int lei_num_count = 0;//记录周围雷的数量 for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (i == 0 && j == 0) continue; // 跳过自身 int newRow = r + i; int newCol = c + j; if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols && map[newRow][newCol] == -1) { lei_num_count++; } } } map[r][c] = lei_num_count; } } } return map; } //========================================================================================生成扫雷地图结束 // 揭示格子 private void revealCell(int row, int col) { if (row < 0 || row >= rows || col < 0 || col >= cols) return; // 超出范围 if (!box_x[row * cols + col].getText().isEmpty()) return; // 避免重复点击 if (mineMap[row][col] == -1) { box_x[row * cols + col].setText("💥"); // 地雷 box_x[row * cols + col].setBackground(Color.RED); timer.stop(); // 停止计时器 showGameOverDialog(); // 显示游戏结束对话框 } else { box_x[row * cols + col].setText(String.valueOf(mineMap[row][col])); box_x[row * cols + col].setEnabled(false); if (mineMap[row][col] == 0) { // 如果周围没有地雷,递归揭示周围格子 revealCell(row - 1, col); revealCell(row + 1, col); revealCell(row, col - 1); revealCell(row, col + 1); } } checkWinCondition(); // 检查是否胜利 } // 标记格子 private void markCell(int row, int col) { if (!box_x[row * cols + col].isEnabled()) return; // 已揭示的格子不能标记 if (box_x[row * cols + col].getText().isEmpty()) { box_x[row * cols + col].setText("🚩"); // 放置标记 flagsPlaced++; } else if (box_x[row * cols + col].getText().equals("🚩")) { box_x[row * cols + col].setText(""); // 移除标记 flagsPlaced--; } checkWinCondition(); // 检查是否胜利 } // 检查胜利条件 private void checkWinCondition() { int revealedCells = 0; // 已揭示的非雷格子数量 int flaggedMines = 0; // 已标记的雷格子数量 for (int i = 0; i < rows * cols; i++) { int row = i / cols; int col = i % cols; if (!box_x[i].isEnabled()) { // 格子已被揭示 if (mineMap[row][col] != -1) { // 非雷格子 revealedCells++; } } else if (box_x[i].getText().equals("🚩")) { // 格子已被标记 if (mineMap[row][col] == -1) { // 标记的格子是雷 flaggedMines++; } } } // 判断胜利条件 if (revealedCells == (rows * cols - lei) || flaggedMines == lei) { timer.stop(); // 停止计时器 // 获取当前难度 String difficulty = label_nadu.getText().substring(3); // 保存成绩到数据库 Mysql_Set.ADD_Mysql(currentUsername, timeall, difficulty); // 显示胜利消息 JOptionPane.showMessageDialog(this, "恭喜,你赢了!\n" + "用户名:" + currentUsername + "\n" + "难度:" + difficulty + "\n" + "时间:" + label_timer.getText(), "游戏胜利", JOptionPane.INFORMATION_MESSAGE); // 显示排行榜 leaderboardDialog.showLeaderboard(difficulty); } } // 显示游戏结束对话框 private void showGameOverDialog() { JOptionPane.showMessageDialog(this, "游戏结束!\n" + "用户名:" + currentUsername + "\n" + "难度:" + label_nadu.getText().substring(3) + "\n" + "时间:" + label_timer.getText(), "游戏结束", JOptionPane.INFORMATION_MESSAGE); // 笑脸按钮换为苦脸 face = new ImageIcon("src/noface.png"); face.setImage(face.getImage().getScaledInstance(50, 50, Image.SCALE_SMOOTH)); // 缩小笑脸按钮 label_face.setIcon(face); label_face.repaint(); // 重新绘制面板 } // 检查是否已选择难度 private boolean checkDifficultySelected() { if (label_nadu.getText().contains("未选择")) { JOptionPane.showMessageDialog(this, "请先选择游戏难度!", "提示", JOptionPane.WARNING_MESSAGE); return false; } return true; } // 更新计时器标签 private void Times() { int minutes = timeall / 60; int seconds = timeall % 60; label_timer.setText(String.format("所用时间:%02d:%02d", minutes, seconds)); } public static void main(String[] args) { new Sao_lei(); } }