diff --git a/Sao_lei.java b/Sao_lei.java new file mode 100644 index 0000000..b11688f --- /dev/null +++ b/Sao_lei.java @@ -0,0 +1,655 @@ +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(); + } +}