扫雷,是一款耐玩有意思的小游戏。但是通过代码实现对于新手来说并不算简单。本人是一个新手,通过这几天摸索、分析设计,也算基本能做出来了,当然有部分功能尚未实现,如果有人能指出错误,那就更好了。下面来分享一下学习的历程。博文有点长,如果只想要源码的,可以直接拉到最下面。
效果图:
扫雷核心算法
ps:本人算法跟数据结构涉及不算多,所以有些算法虽然能实现功能,但是对于系统内存占用会比较大。如果有人对于扫雷算法有更好的实现方法,欢迎指出。
1.随机生成雷:
雷的表示:可以用二维数组的下标表示,所以可以用随机数Math.random()方法来生成随机横纵坐标,random()方法是随机生成0~1之间的小数,但是不会生成0和1。所以random()*16就表示随机生成0~16之间的浮点数,不包括0和16。
因为坐标是整数类型,所以生成的随机横纵坐标需要强制转换为int类型 有一点需要注意的是,强制转换类型并不是四舍五入转换,例如:
random()*30随机生成一个数的值为29.99999999999,转换为会变为29。
/**
* @param mineNum:表示要生成雷的总数
* @param row:表示随机雷的横坐标的int类型
* @param col:表示随机雷的纵坐标的int类型
* @param EMPTY:值为0,表示该组件尚未设置任何东西
* @param MINE:值为1,表示该组件已经设置为雷
*/
private void addMine() {
for (int mineNum = 1; mineNum <= MINE_NUMBER;) {
int row = (int) (Math.random() * 16);
int col = (int) (Math.random() * 30);
// 为了避免有相同位置的雷的生成,需要简单判断一下
if (map[row][col] == EMPTY) {
map[row][col] = MINE;
mineLab[row][col].setIcon(img(9));//设置雷的图标
mineNum++;
}
}
}
2.统计每个按钮周围8个格子的雷的总数:
原理如同下图:(i,j)就是我们要统计的点,周围8个格子的坐标就是下图,通过图片我们可以发现,每一行的都是横坐标累加,纵坐标不变。每一列都是横坐标不变,纵坐标累加。所以就可以用两层for循环实现历遍周围八个格的功能了。有一点需要注意的是,如果(i,j)这个点位于边界,那么就有可能发生越界,比如(i,j)是(0,0),那么i-1=-1,数组就会报错,因为数组下标不能为负数,或者大于长度。所以,每次循环都要判断一下
if (i >= 0 && j >= 0 && i < 16 && j < 30) 是否为真
/**
* 计算每个格周围八个格的雷的总数量的方法,并返回int类型的雷的数量值
* @param i:传入按钮的横坐标
* @param j:传入按钮的纵坐标
*/
private int mN(int i, int j) {
int count = 0; // 雷的数量
int row = i; //保存固定的横坐标
int col = j; //保存固定的纵坐标
for (int m = -1; m < 2; m++) {
i = row; //使得每次循环的i的初始坐标不变
i += m;
for (int n = -1; n < 2; n++) {
j = col; //同上
j += n;
if (i >= 0 && j >= 0 && i < 16 && j < 30) {
if (map[i][j] == MINE) {
count++;
}
}
}
}
return count;
}
3.空雷区自动翻开:
private void isBlank(int i, int j) {
if (mN(i, j) == 0) {
map[i][j] = CHECKED;
int row = i;
int col = j;
for (int m = -1; m < 2; m++) {
i = row;
i += m;
for (int n = -1; n < 2; n++) {
j = col;
j += n;
if (i >= 0 && j >= 0 && i < 16 && j < 30) {
if (mN(i, j) == 0) {
if (map[i][j] != MINE && map[i][j] != CHECKED) {
showLab(i, j);
isBlank(i, j);
}
} else {
showLab(i, j);
}
}
}
}
}
}
4.统计剩余雷数
/**
* 判断剩余雷数
*/
private void isClean() {
int mineFound = MINE_NUMBER_H;
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
if (mineFalg[i][j] == IS_FLAG) {
mineFound--;
}
}
}
mineText.setText("" + mineFound);
if (mineFound == 0) {
flag = true;
showMine();
}
}
完整代码:
package view;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import<