|
|
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<ScoreRecord> GET_Mysql_Top(String difficulty, int limit) {//获取排行榜//返回泛型集合类型
|
|
|
List<ScoreRecord> 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<String> 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<ScoreRecord> 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("<html>《基于Java的扫雷》<br><br><br>" +
|
|
|
"1. 先选择游戏难度<br>" +
|
|
|
"2. 点击笑脸重置游戏<br>" +
|
|
|
"3. 右键标记<br><br>" +
|
|
|
"制作人员:<br>" +
|
|
|
"🤵李 钊231040600226;<br>" +
|
|
|
"🤵梁诗超231040600227;<br>" +
|
|
|
"👰刘芮臣231040600229;<br></html>"); // 创建标签
|
|
|
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();
|
|
|
}
|
|
|
}
|