一、团队课程设计博客链接:
https://www.cnblogs.com/luomeili/p/10280310.html
二、个人负责模块或任务说明:
模块:文件操作 Minefield 类实现
三、自己的代码提交记录截图
四、自己负责模块或任务详细说明
1.Minefield 类实现
Minefield 是我们主要的算法实现模块。在正式开始扫雷游戏时,看见的是如下界面:一格一格的蓝色格子表示可点击区域。
点击后,分三个情况:1)标记该区域为雷(右击)2)该区域是点击到雷,游戏结束 3)该区域无雷,显示区域九宫格范围内的雷的个数。(此处有 9 种情况)4) 游戏提前结束,显示所有未被点击的雷。下图囊括四种情况:
选项区域:
这里涉及到的保存进度,下条中讲解。
对于以上游戏功能,我们是这样实现的。
先根据所选等级,初始化一个 map 数组,数组的行数和列数取决于等级。然后用 Math.random () 数结合循环语句和判断语句生成,将类的区域置 9。其余位置元素值属于 0~8,分别表示以当前位置为中心的九宫格中雷的数目。至此,map 数组生成完毕。由于游戏界面中,真正显示出来的状态有 13 种(0~8 九个数字九种,标记该位置为雷、该位置是被点击的雷、游戏成功后未被点击的雷、当前可点击区域各一种),用 map 数组来控制游戏界面的显示会有冲突,所以此处引入 hiddenmap 数组,元素数值范围为 0~12,分别表示上述 13 种情况。对于游戏界面,其实是一个 JButton 数组,由于界面的美观性,用 hiddenmap 数组值为每个按钮分配图片,根据 hiddenmap 数组值分配对应的功能图片。
生成雷:
生成 map 数组其他位置的数字:
for (int i = 0; i < getWidth(); i++)
for (int j = 0; j < getLength(); j++) {
if (map[i][j] != 9) // 只对不为雷的区域进行雷数判断
{
int number = 0;
if (i == 0) {
if (j == 0) {
if (map[i][j + 1] == 9)
number++;
if (map[i + 1][j] == 9)
number++;
if (map[i + 1][j + 1] == 9)
number++;
} else if (j == getLength() - 1) {
if (map[i][j - 1] == 9)
number++;
if (map[i + 1][j] == 9)
number++;
if (map[i + 1][j - 1] == 9)
number++;
} else {
if (map[i][j - 1] == 9)
number++;
if (map[i][j + 1] == 9)
number++;
if (map[i + 1][j - 1] == 9)
number++;
if (map[i + 1][j] == 9)
number++;
if (map[i + 1][j + 1] == 9)
number++;
}
}
if (i == getWidth() - 1) {
if (j == 0) {
if (map[i][j + 1] == 9)
number++;
if (map[i - 1][j] == 9)
number++;
if (map[i - 1][j + 1] == 9)
number++;
} else if (j == getLength() - 1) {
if (map[i][j - 1] == 9)
number++;
if (map[i - 1][j] == 9)
number++;
if (map[i - 1][j - 1] == 9)
number++;
} else {
if (map[i][j - 1] == 9)
number++;
if (map[i][j + 1] == 9)
number++;
if (map[i - 1][j - 1] == 9)
number++;
if (map[i - 1][j] == 9)
number++;
if (map[i - 1][j + 1] == 9)
number++;
}
}
if (i != 0 && i != (getWidth() - 1)) {
if (j == 0) {
if (map[i - 1][j + 1] == 9)
number++;
if (map[i][j + 1] == 9)
number++;
if (map[i + 1][j + 1] == 9)
number++;
if (map[i - 1][j] == 9)
number++;
if (map[i + 1][j] == 9)
number++;
}
if (j == getLength() - 1) {
if (map[i - 1][j - 1] == 9)
number++;
if (map[i][j - 1] == 9)
number++;
if (map[i + 1][j - 1] == 9)
number++;
if (map[i - 1][j] == 9)
number++;
if (map[i + 1][j] == 9)
number++;
}
}
if ((i != 0) && (j != 0) && (i != getWidth() - 1) && (j != getLength() - 1)) { // 不在边缘的情况
// 单位九宫格内的雷数
for (int n = i - 1; n <= i + 1; n++)
for (int m = j - 1; m <= j + 1; m++)
if (map[n][m] == 9)
number++;
}
map[i][j] = number;
}
}
}
所有过程中我们的操作都是对我们的 hiddenmap 做修改,我们的 map 是我们的真实雷区不做改动,接下来讲讲核心的具体实现。
所以我们的 hiddenmap 刚开始都是初始化为 10,当进行第一次点击的时候,如果 hiddenmap 下面对应的 map 对应的数字是 0,则需要展开所有为 0 的区域,所以我们这里对 hiddenmap 进行了递归搜索为 0 的区域并为 hiddenmap 标注上去,以显示空白区域,我们的做法是对当前为 0 的方块检查它的上下左右不为 9 的方块,给它标注出来,然后对上下左右递归,直到遍历整个区域,因为我们直对当前为 0 的方块进行递归,所以不会使我们的递归遍历整个图,只会遍历当前区域。这个是 findzero 方法的具体思想。
public void findZero(int i, int j) {
if (hiddenmap[i][j] != 0) {
if (map[i][j] == 0) {
hiddenmap[i][j] = 0;
if (i == 0) {
if (j == 0) {
if (map[i][j + 1] != 0 && map[i][j + 1] != 9)
hiddenmap[i][j + 1] = map[i][j + 1];
if (map[i + 1][j] != 0 && map[i + 1][j] != 9)
hiddenmap[i + 1][j] = map[i + 1][j];
}
else if (j == length - 1) {
if (map[i][j - 1] != 0 && map[i][j - 1] != 9)
hiddenmap[i][j - 1] = map[i][j - 1];
if (map[i + 1][j] != 0 && map[i + 1][j] != 9)
hiddenmap[i + 1][j] = map[i + 1][j];
}
else {
if (map[i][j - 1] != 0 && map[i][j - 1] != 9)
hiddenmap[i][j - 1] = map[i][j - 1];
if (map[i + 1][j] != 0 && map[i + 1][j] != 9)
hiddenmap[i + 1][j] = map[i + 1][j];
if (map[i][j + 1] != 0 && map[i][j + 1] != 9)
hiddenmap[i][j + 1] = map[i][j + 1];
}
}
if (i == width - 1) {
if (j == 0) {
if (map[i][j + 1] != 0 && map[i][j + 1] != 9)
hiddenmap[i][j + 1] = map[i][j + 1];
if (map[i - 1][j] != 0 && map[i - 1][j] != 9)
hiddenmap[i - 1][j] = map[i - 1][j];
} else if (j == length - 1) {
if (map[i - 1][j] != 0 && map[i - 1][j] != 9)
hiddenmap[i - 1][j] = map[i - 1][j];
if (map[i][j - 1] != 0 && map[i][j - 1] != 9)
hiddenmap[i][j - 1] = map[i][j - 1];
} else {
if (map[i][j + 1] != 0 && map[i][j + 1] != 9)
hiddenmap[i][j + 1] = map[i][j + 1];
if (map[i - 1][j] != 0 && map[i - 1][j] != 9)
hiddenmap[i - 1][j] = map[i - 1][j];
if (map[i][j - 1] != 0 && map[i][j - 1] != 9)
hiddenmap[i][j - 1] = map[i][j - 1];
}
}
if (j == 0) {
if (i != 0 && i != width - 1) {
if (map[i - 1][j] != 0 && map[i - 1][j] != 9)
hiddenmap[i - 1][j] = map[i - 1][j];
if (map[i + 1][j] != 0 && map[i + 1][j] != 9)
hiddenmap[i + 1][j] = map[i + 1][j];
if (map[i][j + 1] != 0 && map[i][j + 1] != 9)
hiddenmap[i][j + 1] = map[i][j + 1];
}
}
if (j == length - 1) {
if (i != 0 && i != width - 1) {
if (map[i - 1][j] != 0 && map[i - 1][j] != 9)
hiddenmap[i - 1][j] = map[i - 1][j];
if (map[i + 1][j] != 0 && map[i + 1][j] != 9)
hiddenmap[i + 1][j] = map[i + 1][j];
if (map[i][j - 1] != 0 && map[i][j - 1] != 9)
hiddenmap[i][j - 1] = map[i][j - 1];
}
}
if (i != 0 && i != width - 1 && j != 0 && j != length - 1) {
if (map[i][j + 1] != 0 && map[i][j + 1] != 9)
hiddenmap[i][j + 1] = map[i][j + 1];
if (map[i + 1][j] != 0 && map[i + 1][j] != 9)
hiddenmap[i + 1][j] = map[i + 1][j];
if (map[i][j - 1] != 0 && map[i][j - 1] != 9)
hiddenmap[i][j - 1] = map[i][j - 1];
if (map[i - 1][j] != 0 && map[i - 1][j] != 9)
hiddenmap[i - 1][j] = map[i - 1][j];
}
if (j >= 1)
findZero(i, j - 1);
if (i >= 1)
findZero(i - 1, j);
if (j <= getLength() - 2)
findZero(i, j + 1);
if (i <= getWidth() - 2)
findZero(i + 1, j);
}
}
}
setButton 函数为 JButton [][] 数组每个位置放置图片:
public void setButton(JButton button, int i, int j) {
if (minefield.getHiddenMap()[i][j] == 0)
button.setIcon(new ImageIcon("whilt.png"));
if (minefield.getHiddenMap()[i][j] == 1)
button.setIcon(new ImageIcon("whilt-1.png"));
if (minefield.getHiddenMap()[i][j] == 2)
button.setIcon(new ImageIcon("whilt-2.png"));
if (minefield.getHiddenMap()[i][j] == 3)
button.setIcon(new ImageIcon("whilt-3.png"));
if (minefield.getHiddenMap()[i][j] == 4)
button.setIcon(new ImageIcon("whilt-4.png"));
if (minefield.getHiddenMap()[i][j] == 5)
button.setIcon(new ImageIcon("whilt-5.png"));
if (minefield.getHiddenMap()[i][j] == 6)
button.setIcon(new ImageIcon("whilt-6.png"));
if (minefield.getHiddenMap()[i][j] == 7)
button.setIcon(new ImageIcon("whilt-7.png"));
if (minefield.getHiddenMap()[i][j] == 8)
button.setIcon(new ImageIcon("whilt-8.png"));
if (minefield.getHiddenMap()[i][j] == 9)
button.setIcon(new ImageIcon("boom.png"));
if (minefield.getHiddenMap()[i][j] == 10)
button.setIcon(new ImageIcon("blue.png"));
if (minefield.getHiddenMap()[i][j] == 11)
button.setIcon(new ImageIcon("red.png"));
if (minefield.getHiddenMap()[i][j] == 12)
button.setIcon(new ImageIcon("redboom.png"));
}
2.. 文件操作
我们引入了文件保存的机制,为了不用保存过多的参数,而且不希望一个一个量地保存,我们把所有的操作需要用到的数据都保存到了 minefield 类里面,包含我们整个扫雷模块的数据,因为学习过文件处理,我们以 object 的形式可以把所有类都保存起来,再以同样的方式读取,并强制转换类型为 minefield 子类,就可以恢复我们之前保存的数据,所以我们就引入了文件保存机制,能使用户保存他的上一局未完成的游戏,我们会以用户名的形式创建一个同名的 file 来保存,文件保存在当前目录下。当然,如果没有上一局记录的话,就找不到我们的记录文件,我们会默认打开一个初始化的界面。
读取上局文件:
if (file.isNewOne() == false) {
try {
readFile = new ObjectInputStream(new FileInputStream(file.getFileName()));
this.minefield = (Minefield) readFile.readObject();
readFile.close();
if (minefield.isBoom() == true) {
boom.play();
upset.play();
} else {
playGame.play();
}
} catch (FileNotFoundException e) { //不存在上局时,自动生成一局
this.minefield = new Minefield(file.getWidth(), file.getLength(), file.getLambnumber());
playGame.play();
// JOptionPane.showMessageDialog(null, "您还未开始游戏,不存在上局哦,即将为您自动生成上局!");
// e.printStackTrace();
} catch (IOException e) {
// e.printStackTrace();
}
}
保存进度:
item3.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
try {
ObjectOutputStream dateSave = new ObjectOutputStream(new FileOutputStream(getFileName()));
dateSave.writeObject(minefield);
dateSave.close();
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
五、课程设计感想
通过这次课程设计,我大大提高了自己的自主学习能力,俗话说 “师傅领进门,修行在个人”,要完成如此复杂的课程设计,仅靠老师上课教授的知识是远远不够的,需要我们自己去多加学习。在学习中,还应学会提问的方法,遇文图时不要慌张,要仔细往根源去找问题,不要一有问题就寻求老师同学帮忙,要有自主解决问题的能力。