扫雷
《扫雷》是一款大众类的益智小游戏,于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。
扫雷在科技历史上也扮演了相似的角色。这个基于数字的逻辑谜题最早来自20世纪六七十年代,当时Jerimac Ratliff推出的名为“Cube”的游戏已经非常受人欢迎。几十年后的1992年,扫雷游戏被加入了Windows3.1,这并不是为了展示Windows是游戏操作系统专家,而是为了训练用户的鼠标左右键操作能力,让这些动作变得非常自然,并培养鼠标移动的速度和准确性。
Java简易版
首先会出现菜单界面选择1正常游玩,选择2退出。
选择1之后进入游玩界面,提示让你输入坐标,*表示未排除的位置,我们至于要输入两个坐标来选择你想要排除的区域,出现0表示周围8个格子未有雷,出现其他数字表示周围格子中共有多少个地雷,如果想要插旗则需要输入0 0,接着输入你想要插旗的位置,如果想取消插旗,输入0 1,接着输入想要取消插旗的位置。
代码实现
设置类似宏的变量
// 定义12行12列,实际扫雷的区域只有10*10
public static int ROW=12;public static int COL=12;
// 定义雷的个数
public static int NUM=20;
// 0为无炸弹,1为有炸弹
public static byte BOOM='1';
这里我们设置了棋盘大小设置10行10列,但是为了便于计算,我们设置横纵分别为12,便于埋雷等操作。自定义雷的个数,我们这里设置为20个,定义雷为字符1,无雷为字符0.
main方法
public static void main(String[] args) {
boolean quit=true;
while(quit){
menu();
Scanner sc=new Scanner(System.in);
int select=sc.nextInt();
switch(select){
case 1:
game();
break;
case 2:
quit=false;
break;
default:
System.out.println("输入有误,请重新输入!");
break;
}
}
}
首先设置一个死循环,加载出菜单也就是menu方法。
menu方法
public static void menu(){
System.out.println("####################");
System.out.println("##### 1.Play #####");
System.out.println("##### 2.Exit #####");
System.out.println("####################");
System.out.print("Please Select> ");
}
选择1进入游戏方法,选择2退出,选择其他提示输入错误。
game方法
public static void game(){
int[][] show_board=new int[ROW][COL];
int[][] mine_board=new int[ROW][COL];
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
show_board[i][j]='*';
mine_board[i][j]='0';
}
}
setMines(mine_board,ROW,COL);
int tot=(ROW-2)*(COL-2)-NUM;
int clear=0;
while(true){
showBoard(show_board,ROW,COL);
System.out.print("请输扫雷入坐标<x y> ");
Scanner sc=new Scanner(System.in);
int x=sc.nextInt();int y=sc.nextInt();
if((x==0&&y==0)||(x==0&&y==1)){
flag(show_board,x,y);
continue;
}
if(!(x>=1&&x<=ROW-2&&y>=1&&y<=COL-2)){
System.out.println("扫雷的位置有问题!");
continue;
}
if(show_board[x][y]!='*'){
System.out.println("扫雷的位置上已经排除!");
continue;
}
if(mine_board[x][y]=='1'){
System.out.println("不好意思,你被炸死了!");
break;
}
else{
int count=countMines(mine_board,x,y);
show_board[x][y]=count+'0';
clear++;
}
if (clear >= tot) {
System.out.println("恭喜你,游戏胜利!");
break;
}
}
}
首先我们设置两个棋盘,一个为我们埋雷的棋盘,一个为我们显示和操作的棋盘。接着对两个棋盘进行初始化,埋雷棋盘mine_board全部初始化为字符0,显示棋盘show_board全部初始化为字符*。接着我们进入埋雷方法。
setmines方法
public static void setMines(int[][]mine_board,int row,int col){
int i=0;
while (i<NUM){
Random random=new Random();//默认随机种子是系统时间
int x=random.nextInt(ROW-2)+1;
int y=random.nextInt(COL-2)+1;
if(mine_board[x][y]==BOOM){
continue;
}
mine_board[x][y]=BOOM;
i++;
}
}
埋雷运用random方法生成随机数,我们要的埋雷位置为10*10,但是棋盘为12*12,random生成随机数为左闭右开的区间,因此我们需要将生成的随机数-2+1得到我们需要的。埋完雷我们进入游戏主体,首先要进行棋盘的打印。
showBoard方法
public static void showBoard(int[][]board,int row,int col){
System.out.print(" ");
for (int i = 1; i <= row - 2; i++) {
System.out.printf("%4d", i);
}
System.out.println();
System.out.print(" ");
for (int k = 1; k <= col - 2; k++) {
System.out.print("----");
}
System.out.println();
for (int i = 1; i <= ROW - 2; i++) {
System.out.printf("%2d|", i);
for (int j = 1; j <= COL - 2; j++) {
System.out.printf(" %c |", board[i][j]);
}
System.out.println();
for (int k = 1; k <= COL - 2; k++) {
System.out.print("----");
}
System.out.println();
}
}
这里就比较复杂我们需要边调试边修改得到最终的棋盘,一行行的打印出来,最外层为坐标,内部为棋盘。打印完棋盘就可以开始扫雷了。
扫雷逻辑
System.out.print("请输扫雷入坐标<x y> ");
Scanner sc=new Scanner(System.in);
int x=sc.nextInt();int y=sc.nextInt();
if((x==0&&y==0)||(x==0&&y==1)){
flag(show_board,x,y);
continue;
}
if(!(x>=1&&x<=ROW-2&&y>=1&&y<=COL-2)){
System.out.println("扫雷的位置有问题!");
continue;
}
if(show_board[x][y]!='*'){
System.out.println("扫雷的位置上已经排除!");
continue;
}
if(mine_board[x][y]=='1'){
System.out.println("不好意思,你被炸死了!");
break;
}
else{
int count=countMines(mine_board,x,y);
show_board[x][y]=count+'0';
clear++;
}
if (clear >= tot) {
System.out.println("恭喜你,游戏胜利!");
break;
}
首先输入扫雷的坐标,判断位置是否合法,当你输入的坐标在mine_board棋盘中为字符1时你被炸死,变量tot为除了雷外剩余的空间总数,当我们扫完所有的剩余空间时即为游戏胜利。如果想要插旗或者取消插旗就输入0 0或0 1。
flag方法
public static void flag(int[][]board,int a,int b){
if(b==0){
System.out.print("请输入插旗坐标<x y> ");
Scanner sc=new Scanner(System.in);
int x=sc.nextInt();int y=sc.nextInt();
board[x][y]='$';
}
else{
while(true){
System.out.print("请输入取消插旗坐标<x y> ");
Scanner sc=new Scanner(System.in);
int i=sc.nextInt();int j=sc.nextInt();
if(board[i][j]=='$'){
board[i][j]='*';
break;
}
else{
System.out.println("输入坐标有误!");
continue;
}
}
}
}
如果是0 0进入插旗选项只需要再次输入想要插旗的坐标即可,将输入的坐标在show_board中换为$字符即可,0 1进入取消插旗,位置有棋即$则位置合法,只需要在shou_board中将$换为*。插旗或取消插旗完毕继续进入扫雷坐标输入。
cuntMines方法
public static int countMines(int[][]board,int x,int y){
return board[x - 1][y - 1] + board[x - 1][y]
+board[x - 1][y + 1] + board[x][y - 1]
+ board[x][y + 1] + board[x + 1][y - 1]
+board[x + 1][y] + board[x + 1][y + 1] - 8 *'0';
}
如果输入的扫雷的位置合法,就要进行周围雷数目的统计,然后显示在shou_board中,因为我们在min_board中设置无雷为0,有雷为1,而且我们设置棋盘时多设置了两行两列,我们只需要将周围八个格子的数字全部穷举相加由于是字符操作我们在减去8*字符‘0’,就得到了周围格子雷的数目。
至此,所有的逻辑已经通顺完毕,雷的数目棋盘的大小都可以进行自定义。得到最终效果。
总代码
import java.util.Random;
import java.util.Scanner;
public class MineSweep {
// 定义12行12列,实际扫雷的区域只有10*10
public static int ROW=12;public static int COL=12;
// 定义雷的个数
public static int NUM=20;
// 0为无炸弹,1为有炸弹
public static byte BOOM='1';
public static void menu(){
System.out.println("####################");
System.out.println("##### 1.Play #####");
System.out.println("##### 2.Exit #####");
System.out.println("####################");
System.out.print("Please Select> ");
}
public static void setMines(int[][]mine_board,int row,int col){
int i=0;
while (i<NUM){
Random random=new Random();//默认随机种子是系统时间
int x=random.nextInt(ROW-2)+1;
int y=random.nextInt(COL-2)+1;
if(mine_board[x][y]==BOOM){
continue;
}
mine_board[x][y]=BOOM;
i++;
}
}
public static void showBoard(int[][]board,int row,int col){
System.out.print(" ");
for (int i = 1; i <= row - 2; i++) {
System.out.printf("%4d", i);
}
System.out.println();
System.out.print(" ");
for (int k = 1; k <= col - 2; k++) {
System.out.print("----");
}
System.out.println();
for (int i = 1; i <= ROW - 2; i++) {
System.out.printf("%2d|", i);
for (int j = 1; j <= COL - 2; j++) {
System.out.printf(" %c |", board[i][j]);
}
System.out.println();
for (int k = 1; k <= COL - 2; k++) {
System.out.print("----");
}
System.out.println();
}
}
public static void flag(int[][]board,int a,int b){
if(b==0){
System.out.print("请输入插旗坐标<x y> ");
Scanner sc=new Scanner(System.in);
int x=sc.nextInt();int y=sc.nextInt();
board[x][y]='$';
}
else{
while(true){
System.out.print("请输入取消插旗坐标<x y> ");
Scanner sc=new Scanner(System.in);
int i=sc.nextInt();int j=sc.nextInt();
if(board[i][j]=='$'){
board[i][j]='*';
break;
}
else{
System.out.println("输入坐标有误!");
continue;
}
}
}
}
public static int countMines(int[][]board,int x,int y){
return board[x - 1][y - 1] + board[x - 1][y]
+board[x - 1][y + 1] + board[x][y - 1]
+ board[x][y + 1] + board[x + 1][y - 1]
+board[x + 1][y] + board[x + 1][y + 1] - 8 *'0';
}
public static void game(){
int[][] show_board=new int[ROW][COL];
int[][] mine_board=new int[ROW][COL];
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
show_board[i][j]='*';
mine_board[i][j]='0';
}
}
setMines(mine_board,ROW,COL);
int tot=(ROW-2)*(COL-2)-NUM;
int clear=0;
while(true){
showBoard(show_board,ROW,COL);
System.out.print("请输扫雷入坐标<x y> ");
Scanner sc=new Scanner(System.in);
int x=sc.nextInt();int y=sc.nextInt();
if((x==0&&y==0)||(x==0&&y==1)){
flag(show_board,x,y);
continue;
}
if(!(x>=1&&x<=ROW-2&&y>=1&&y<=COL-2)){
System.out.println("扫雷的位置有问题!");
continue;
}
if(show_board[x][y]!='*'){
System.out.println("扫雷的位置上已经排除!");
continue;
}
if(mine_board[x][y]=='1'){
System.out.println("不好意思,你被炸死了!");
break;
}
else{
int count=countMines(mine_board,x,y);
show_board[x][y]=count+'0';
clear++;
}
if (clear >= tot) {
System.out.println("恭喜你,游戏胜利!");
break;
}
}
}
public static void main(String[] args) {
boolean quit=true;
while(quit){
menu();
Scanner sc=new Scanner(System.in);
int select=sc.nextInt();
switch(select){
case 1:
game();
break;
case 2:
quit=false;
break;
default:
System.out.println("输入有误,请重新输入!");
break;
}
}
}
}
易错点
1.理清所有逻辑,所有不是雷的位置扫完才算胜利,而不是找到所有雷。
2.埋雷时计算好随机生成时的位置,否则容易越界。
3.计算周围雷的个数为字符操作,穷举完所有8个格子,在减去字符0乘以8.
总结
扫雷小游戏是我们初学者用来练手最合适不过的,逻辑不算太难,主要运用到方法的使用和数组的运用,慢慢理逻辑,其实并不难,又是一款经典的小游戏再合适不过了,加油!