HeyWeGo小组《Java程序设计》
2015—2016年学期团队项目总结
题目简介
一个简单的扫雷小游戏,在12*12的方格盘上,首先可以设定雷的个数,然后点击开始程序就会随机布雷,开始游戏后如果点到雷就会显示游戏结束,如果没有,会出现数字表示周围一圈雷的个数,以此推理当扫出所有雷将显示游戏胜利。
游戏项目规划:
- 确定游戏中方块格子的个数
- 确定游戏中地雷的个数(初始10个),完成布雷
- 计算每个方块周围的雷数,在方块周围本身没有地雷的情况下,统计周围地雷的个数
- 若周围没有地雷则翻开周围的空白方格,直到周围有附近地雷的格子
- 设计一个检测是否挖完所有的雷的方法
- 设定左键点击为翻开
- 设定右键点开为标记
团队分工
- 20145336 张子扬(对代码的进度进行策划,实现代码的运行,对主程序和模块进行划分及分析)
- 20145321 曾子誉(收集资料,实现程序界面,对一些分支进行细节处理)
- 20145235 方浩南(进行代码的编写)
- 20145225 唐振远(实验代码检查错误,并对代码进行下一步的完善)
- 20145331魏澍琛(发现不足,查找资料,改善体验)
代码实现及注释:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Frame
extends JFrame {
JTextField text;
JLabel nowBomb, setBomb;
int BombNum, BlockNum; //当前雷数,当前方块数
int rightBomb, restBomb, restBlock; //找到的地雷数,剩余雷数,剩余方块数 JButton start = new JButton(" 开始 ");//初始化一个开始的按钮
JPanel MenuPanel = new JPanel();//新建一个区域,作为菜单
JPanel bombPanel = new JPanel();//新建一个区域,作为雷区
Bomb[][] bombButton;//2维数组,防地雷 JPanel c;
BorderLayout borderLayout1 = new BorderLayout();//布局管理器边界布局
GridLayout gridLayout1 = new GridLayout();//布局一个网格布局
public Frame() {
try {
setDefaultCloseOperation(EXIT_ON_CLOSE);//关闭操作并结束进程
jbInit();//一般用于对程序进行初始化
}
catch (Exception exception) {//异常处理
exception.printStackTrace();
}
} private void jbInit() throws Exception {
c = (JPanel) getContentPane();//设定一个空间
setTitle("扫雷");//标题为扫雷
c.setBackground(Color.WHITE);//背景颜色为白色
MenuPanel.setBackground(Color.GRAY);//菜单颜色为灰色
c.setLayout(borderLayout1);
setSize(new Dimension(700, 700));//界面大小700*700
setResizable(false); BlockNum = 144;//设定方块数144个
BombNum = 10;//设定初始地雷数10个
text = new JTextField("10 ", 3);//设定一个字符框长度为3
nowBomb = new JLabel("当前雷数" + ":" + BombNum);
setBomb = new JLabel("设置地雷数");//标明解释
start.addActionListener(new Frame1_start_actionAdapter(this)); MenuPanel.add(setBomb);
MenuPanel.add(text);
MenuPanel.add(start);
MenuPanel.add(nowBomb);//添加菜单元素
c.add(MenuPanel, java.awt.BorderLayout.SOUTH);//给界面下方的界面设定布局 bombPanel.setLayout(gridLayout1);
gridLayout1.setColumns( (int) Math.sqrt(BlockNum));//纵列设定
gridLayout1.setRows( (int) Math.sqrt(BlockNum));//横排设定
bombButton = new Bomb[ (int) Math.sqrt(BlockNum)][ (int) Math.sqrt(BlockNum)];
for (int i = 0; i < (int) Math.sqrt(BlockNum); i++) {
for (int j = 0; j < (int) Math.sqrt(BlockNum); j++) {//嵌套for循环表示所有的格子
bombButton[i][j] = new Bomb(i, j); bombButton[i][j].setFont(new Font("", Font.PLAIN, 14));//设计字体显示效果 bombButton[i][j].setForeground(Color.white);//背景颜色为白色
bombButton[i][j].addMouseListener(new Bomb_mouseAdapter(this));
bombButton[i][j].addActionListener(new Bomb_actionAdapter(this));
bombPanel.add(bombButton[i][j]);
}
}
c.add(bombPanel, java.awt.BorderLayout.CENTER);//给界面中间的界面设定布局 startBomb();
} /* 开始按钮 */ public void start_actionPerformed(ActionEvent e) {
int num=Integer.parseInt(text.getText().trim());//调用Integer.parseInt()把一个String转换成一个Double基本类型的值并赋值给了num
if (num >= 5 && num < 50) {
BombNum = num;//如果当前雷数在5-50之间
startBomb();//可以开始布雷。
}
else if (num < 5) {//如果当前雷数在5以下
JOptionPane.showMessageDialog(null, "您设置的地雷数太少了,请重设!", "错误",
JOptionPane.ERROR_MESSAGE);//消息提示框显示错误。
num=10;
BombNum = num;//自动将雷数改为10.
}
else {
JOptionPane.showMessageDialog(null, "您设置的地雷数太多了,请重设!", "错误",
JOptionPane.ERROR_MESSAGE);//同上,消息提示框提醒输入有误。
num=10;
BombNum = num;
}
}
/* 开始,布雷 */ public void startBomb() {
nowBomb.setText("当前雷数" + ":" + BombNum);//利用setText设置现在的雷数。
for (int i = 0; i < (int) Math.sqrt(BlockNum); i++) {//由按钮组成的二维数组,i表示行,j表示列,构成了一个方形网格
for (int j = 0; j < (int) Math.sqrt(BlockNum); j++) {//循环条件判断isRight的值,如果为false循环否则退出
bombButton[i][j].isBomb = false;//当前按钮是否为雷
bombButton[i][j].isClicked = false;//是否已被点击
bombButton[i][j].isRight = false;//是否被(标注)正确
bombButton[i][j].BombFlag = 0;//是否被标注为雷
bombButton[i][j].BombRoundCount = 9;//周围
bombButton[i][j].setEnabled(true);
bombButton[i][j].setText("");
bombButton[i][j].setFont(new Font("", Font.PLAIN, 14));//设置字体大小
bombButton[i][j].setForeground(Color.BLUE);//设置颜色为蓝色
rightBomb = 0;//正确标出的雷数为0
restBomb = BombNum;
restBlock = BlockNum - BombNum;
}
} for (int i = 0; i < BombNum; ) {
int x = (int) (Math.random() * (int) (Math.sqrt(BlockNum) - 1));
int y = (int) (Math.random() * (int) (Math.sqrt(BlockNum) - 1)); if (bombButton[x][y].isBomb != true) {
bombButton[x][y].isBomb = true;
i++;
}
}
CountRoundBomb();
} /* 计算方块周围雷数 */ public void CountRoundBomb() {
for (int i = 0; i < (int) Math.sqrt(BlockNum); i++) {//开方 障碍数
for (int j = 0; j < (int) Math.sqrt(BlockNum); j++) {
int count = 0;//当需要检测的单元格本身无雷的情况下,统计周围的地雷个数 if (bombButton[i][j].isBomb != true) {//如果不是雷
for (int x = i - 1; x < i + 2; x++) {//从左边到右,一个三个
for (int y = j - 1; y < j + 2; y++) {
if ( (x >= 0) && (y >= 0)
&& (x < ( (int) Math.sqrt(BlockNum)))
&& (y < ( (int) Math.sqrt(BlockNum)))) {//因前面x=i-1,所以排除超出边界的情况
if (bombButton[x][y].isBomb == true) {//如果是雷
count++;//加一
}
}
}
}
bombButton[i][j].BombRoundCount = count;//设置该Bomb环绕数的值
}
}
}
} /* 是否挖完了所有的雷 */ public void isWin() {
restBlock = BlockNum - BombNum;
for (int i = 0; i < (int) Math.sqrt(BlockNum); i++) {
for (int j = 0; j < (int) Math.sqrt(BlockNum); j++) {
if (bombButton[i][j].isClicked == true) {
restBlock--;
}
}
} if (rightBomb == BombNum || restBlock == 0) {
JOptionPane.showMessageDialog(this, "您挖完了所有的雷,您胜利了!", "胜利",
JOptionPane.INFORMATION_MESSAGE);
startBomb();
}
} /** 当选中的位置为空,则翻开周围的地图* */ public void isNull(Bomb ClickedButton) {
int i, j;
i = ClickedButton.num_x; //点击的方块,周围地图
j = ClickedButton.num_y; for (int x = i - 1; x < i + 2; x++) { //从左边到右边,一至三个
for (int y = j - 1; y < j + 2; y++) {
if ( ( (x != i) || (y != j)) && (x >= 0) && (y >= 0)
&& (x < ( (int) Math.sqrt(BlockNum))) //因为设定x=i-1,所以排除超出边界的情况
&& (y < ( (int) Math.sqrt(BlockNum)))) {
if (bombButton[x][y].isBomb == false //如果方块中不是雷
&& bombButton[x][y].isClicked == false //如果方块未被点击
&& bombButton[x][y].isRight == false) { //如果方块未被标记
turn(bombButton[x][y]);
} //当前位置为空,周围地图被翻开
}
}
}
} /* 翻开 */ public void turn(Bomb ClickedButton) {
ClickedButton.setEnabled(false);
ClickedButton.isClicked = true; //点击方块为空,不是地雷
if (ClickedButton.BombRoundCount > 0) { //如果方块地雷环绕数>0
ClickedButton.setText(ClickedButton.BombRoundCount + ""); //标记数目
}
else {
isNull(ClickedButton); //否则,代替Null
}
} /* 左键点击 */
public void actionPerformed(ActionEvent e) {
if ( ( (Bomb) e.getSource()).isClicked == false //如果方块未被点击
&& ( (Bomb) e.getSource()).isRight == false) { //如果方块标未被记
if ( ( (Bomb) e.getSource()).isBomb == false) { //如果方块不是地雷
turn( ( (Bomb) e.getSource()));
isWin(); //当挖完雷,胜利
} else {
for (int i = 0; i < (int) Math.sqrt(BlockNum); i++) {
for (int j = 0; j < (int) Math.sqrt(BlockNum); j++) {
if (bombButton[i][j].isBomb == true) { //如果踩到雷
bombButton[i][j].setText("b"); //地雷方块上显示“b”
}
}
}
( (Bomb) e.getSource()).setForeground(Color.RED); //显示红色
( (Bomb) e.getSource()).setFont(new Font("", Font.BOLD, 20));
( (Bomb) e.getSource()).setText("X"); //显示符号“X”
JOptionPane.showMessageDialog(this, "你踩到地雷了,按确定重来", "踩到地雷", 2);
startBomb();
}
}
} /* 右键点击 */
public void mouseClicked(MouseEvent e) {
Bomb bombSource = (Bomb) e.getSource();
boolean right = SwingUtilities.isRightMouseButton(e); if ( (right == true) && (bombSource.isClicked == false)) {
bombSource.BombFlag = (bombSource.BombFlag + 1) % 3; //计算右键标记数及对应得出符号
if (bombSource.BombFlag == 1) { //如果计算得1,标记显示F
if (restBomb > 0) {
bombSource.setForeground(Color.RED); //标记显示红色
bombSource.setText("F"); //标记符号“F”
bombSource.isRight = true; //如果方块被标记
restBomb--; //那么当前地雷数减1
}
else {
bombSource.BombFlag = 0;
}
}
else if (bombSource.BombFlag == 2) { //如果计算得2,标记显示Q
restBomb++; //在F减1的基础上地雷数加1
bombSource.setText("Q"); //标记符号“Q”
bombSource.isRight = false; //方块未被标记地雷
}
else {
bombSource.setText("");
} if (bombSource.isBomb == true) { //如果方块是雷
if (bombSource.BombFlag == 1) { //如果所在方块计算得1,即标记F
rightBomb++;
}
else if (bombSource.BombFlag == 2) { //如果所在方块计算得2,即标记Q
rightBomb--;
}
}
nowBomb.setText("当前雷数" + ":" + restBomb);
isWin();
}
} public static void main(String[] args) {
Frame frame = new Frame();
frame.setVisible(true);
}
} class Frame1_start_actionAdapter
implements ActionListener {
private Frame adaptee;
Frame1_start_actionAdapter(Frame adaptee) {
this.adaptee = adaptee;
} public void actionPerformed(ActionEvent e) {
adaptee.start_actionPerformed(e);
}
} ////////////////////////////
class Bomb
extends JButton {
int num_x, num_y; //第几号方块
int BombRoundCount; //周围雷数
boolean isBomb; //是否为雷
boolean isClicked; //是否被点击
int BombFlag; //测试雷标记
boolean isRight; //是否点击右键 public Bomb(int x, int y) {
num_x = x;
num_y = y;//累的位置,获得是参数值,嵌套了两个for循环
BombFlag = 0;//是否已经插旗了
BombRoundCount = 9;//环绕数
isBomb = false;//是雷
isClicked = false;//被点击
isRight = false;//是真的
}
} class Bomb_actionAdapter
implements ActionListener {
private Frame adaptee;
Bomb_actionAdapter(Frame adaptee) {
this.adaptee = adaptee;
} public void actionPerformed(ActionEvent e) {
adaptee.actionPerformed(e);
}
} class Bomb_mouseAdapter
extends MouseAdapter {
private Frame adaptee;
Bomb_mouseAdapter(Frame adaptee) {
this.adaptee = adaptee;
} public void mouseClicked(MouseEvent e) {
adaptee.mouseClicked(e);
}
}
运行结果截图:
初始界面:
设置雷数为10,开始游戏,会显示数字:
踩到雷,游戏结束:
两种标记:
扫完雷,游戏胜利:
分数的分配
张子扬 30分
方浩南 27分
唐振远 23分
曾子誉 20分
团队贡献
张子扬:小组组长,负责给组员分配任务,掌握代码进度。对代码的架构进行划分,并协助方浩南完成部分代码。并完成图形界面和雷数的布控。
方浩南:负责编写部分前期代码,完成对周围雷数的计算、剩余雷数的计算和翻开的操作。
唐振远:负责编写后期代码,完成左右键的点击操作,并完成代码的检查,更正。
曾子誉:查阅相关资料,协助唐振远同学完成相关代码。并对代码进行后期完善,改善用户体验。
代码托管链接
http://git.oschina.net/Zziyang/Frame/tree/master
总结
通过团队项目,我们认识到了合作的重要性,紧密的合作能够提高我们的能力。代码测试过程中出现很多错误,但经过互相的合作和探讨,加以改进,便可以成功运行。 小组各组员的课程总结已发到个人博客。