利用java開發(fā)簡易版掃雷游戲
1.簡介
學(xué)了幾周的Java,閑來無事,寫個(gè)乞丐版的掃雷,加強(qiáng)一下Java基礎(chǔ)知識(shí)。
2.編寫過程
編寫這個(gè)游戲,一共經(jīng)歷了三個(gè)階段,編寫了三個(gè)版本的游戲代碼。
第一版:完成了掃雷游戲的基本雛形,實(shí)現(xiàn)了游戲的基本功能,游戲運(yùn)行在cmd黑窗口中,以字符繪制游戲界面,無圖形化窗口,通過控制臺(tái)輸入字符完成游戲控制。代碼放置在一個(gè)java文件中,代碼的可讀性以及可擴(kuò)展性都比較差。
第二版:在第一版實(shí)現(xiàn)基本功能的基礎(chǔ)之上,對(duì)游戲代碼進(jìn)行重構(gòu),根據(jù)各部分的功能創(chuàng)建多個(gè)類,增加代碼注釋,提高代碼的可讀性以及可擴(kuò)展性。
第三版:在第二版重構(gòu)代碼的基礎(chǔ)之上給游戲增加了圖形化界面,將用戶從控制臺(tái)輸入命令控制游戲變?yōu)橥ㄟ^鼠標(biāo)左右鍵點(diǎn)擊操作控制游戲。
3.游戲運(yùn)行邏輯
游戲運(yùn)行邏輯圖(第一版代碼):
游戲運(yùn)行邏輯圖(第二版代碼):
以上兩個(gè)游戲流程圖的運(yùn)行是建立在從控制臺(tái)讀取數(shù)據(jù)的基礎(chǔ)之上的,兩者的執(zhí)行邏輯大體相同,其本質(zhì)區(qū)別在于修改游戲數(shù)據(jù)的時(shí)機(jī)不相同。前者是在通關(guān)判斷之前修改數(shù)據(jù),后者實(shí)在通關(guān)判斷之后。兩者在運(yùn)行期間并沒有什么區(qū)別,但是當(dāng)玩家完成掃雷之后最后的畫面打印就會(huì)出現(xiàn)問題,即游戲畫面中最后一個(gè)進(jìn)行操作的坐標(biāo)點(diǎn)的字符的顯示狀態(tài),在發(fā)生改變之前就會(huì)終止程序。通過對(duì)修改游戲數(shù)據(jù)以及通關(guān)判斷這兩個(gè)操作的執(zhí)行順序進(jìn)行調(diào)整,即可修正這一顯示錯(cuò)誤。
游戲運(yùn)行邏輯圖(第三版代碼):
這個(gè)運(yùn)行流程圖是基于第三版加了圖形化界面之后的游戲代碼,游戲控制流程與控制臺(tái)輸入控制的流程基本相同,只是將從控制臺(tái)讀取用戶輸入變成了監(jiān)聽用戶的鼠標(biāo)左鍵與右鍵的點(diǎn)擊事件。并且,從控制臺(tái)讀取數(shù)據(jù)時(shí)要保證游戲結(jié)束前一直進(jìn)行讀取,因此需要設(shè)置while(true)循環(huán)以進(jìn)行實(shí)現(xiàn),游戲結(jié)束使用break跳出循環(huán)。而在圖形化界面中,使用的是事件監(jiān)聽器,事件監(jiān)聽器在游戲結(jié)束前持續(xù)監(jiān)聽用戶的鼠標(biāo)點(diǎn)擊事件,在游戲結(jié)束的彈窗彈出的同時(shí)移除監(jiān)聽器,結(jié)束鼠標(biāo)對(duì)游戲的控制。
4.游戲相關(guān)數(shù)據(jù)存儲(chǔ)與讀取
以10x10x3的三維數(shù)組存儲(chǔ)每個(gè)坐標(biāo)點(diǎn)的信息(包括行號(hào)、列號(hào)、是否是地雷、當(dāng)前顯示的符號(hào)(未操作?、插旗#、地雷*)、周圍地雷數(shù))。
一維數(shù)組的下標(biāo)表示行號(hào),二維數(shù)組的下標(biāo)表示列號(hào),三維數(shù)組中存的第一個(gè)數(shù)據(jù)表示的是該位置是否是地雷(0-不是,1-是),第二個(gè)數(shù)據(jù)表示該位置當(dāng)前顯示的符號(hào)(0-?,1-#,2-*,3-顯示地雷數(shù)),第三個(gè)數(shù)據(jù)表示該位置周圍的地雷數(shù)。例如:
array[0][9][0]=1;表示將第0行第9列設(shè)置為地雷 array[0][9][1]=1;表示將第0行第9列的顯示字符設(shè)置為'#' array[0][9][2]=0;表示將第0行第9列位置周圍的地雷數(shù)設(shè)置為0
5.游戲代碼
5.1 第一版
第一版的游戲代碼寫在一個(gè)類中,主要的作用是實(shí)現(xiàn)基本的游戲功能。代碼如下:
import java.util.Random; import java.util.Scanner; /** * 掃雷游戲 * @author zjl * */ public class MineSweeper { public static void main(String[] args) { Scanner input = new Scanner(System.in); //初始化游戲數(shù)據(jù) int[][][] gameData = init(); while (true) { //打印游戲信息 showInfo(); //打印游戲框 showWin(gameData); //踩中地雷結(jié)束游戲 //由于踩中地雷會(huì)把所有標(biāo)記變成'*',所以只需要判斷0行0列的顯示標(biāo)記是不是'*'就行了 if (gameData[0][0][1] == 2) { System.out.println("踩中地雷,游戲結(jié)束!"); break; } //通關(guān)結(jié)束游戲 if (missionAccomplished(gameData)) { System.out.println("恭喜通關(guān)!"); break; } //讀取控制臺(tái)數(shù)據(jù)并對(duì)游戲數(shù)據(jù)數(shù)組進(jìn)行修改 gameData = readAndChangeData(input,gameData); } } /** * 打印提示信息 */ private static void showInfo() { printBlank(25); System.out.println("*******************************************************\n" + "\t\t 游戲信息\n" + "游戲名稱:掃雷\n" + "游戲版本:1.0\n" + "游戲操作:1.輸入行號(hào)及列號(hào)來選中要翻開的\'?\'進(jìn)行操作,可\n" + "\t 以選擇插旗(#)或者直接翻開.\n" + "\t 2.如果翻開'*'則表示地雷,則游戲結(jié)束;如果翻開\n" + "\t 的是數(shù)字,則表示該格周圍的地雷數(shù).\n" + "\t 3.標(biāo)記出全部地雷,并且沒有\(zhòng)'?\',則闖關(guān)成功,游戲\n" + "\t 結(jié)束.\n" + "*******************************************************\n\n"); } /** * 打印游戲框 */ private static void showWin(int[][][] gameData) { System.out.println(" 0 1 2 3 4 5 6 7 8 9\n" + " ***********************"); //遍歷游戲框中的每個(gè)坐標(biāo),讀取并打印顯示符號(hào) for (int i = 0; i < 10; i++) { System.out.print(i + " * "); for (int j = 0; j < 10; j++) { //讀取展示的符號(hào) char sign; switch (gameData[i][j][1]) { case 1: sign = '#'; break; case 2: sign = '*'; break; case 3: sign = (char)(gameData[i][j][2] + 48); break; default: sign = '?'; break; } //打印符號(hào) System.out.print(sign + " "); } System.out.println("*"); } System.out.println(" ***********************"); } /** * 打印空白行 */ private static void printBlank(int blankNum) { for (int i = 0; i < blankNum; i++) { System.out.println(""); } } /** * 隨機(jī)生成地雷坐標(biāo) */ private static int[][] createMineCoord() { //定義二維數(shù)組 int[][] mineCoordArray = new int[20][2]; Random random = new Random(); //將生成的隨機(jī)坐標(biāo)存入數(shù)組中 for (int i = 0; i < 20; i++) { for (int j = 0; j < 2; j++) { //生成0~9范圍內(nèi)的隨機(jī)數(shù) int randomNumber = random.nextInt(10); mineCoordArray[i][j] = randomNumber; } } return mineCoordArray; } /** * 初始化游戲數(shù)據(jù) */ private static int[][][] init(){ //創(chuàng)建大小為10*10*3的三維數(shù)組(默認(rèn)初始值為0) int[][][] gameData = new int[10][10][3]; //生成隨機(jī)的地雷坐標(biāo),并將其存入游戲數(shù)據(jù)數(shù)組中 int[][] mineCoordArray = createMineCoord(); for (int[] mineCoord : mineCoordArray) { int row = mineCoord[0]; int col = mineCoord[1]; gameData[row][col][0] = 1; } //計(jì)算每格周圍地雷數(shù)并將其存入游戲數(shù)據(jù)數(shù)組中 //循環(huán)遍歷每個(gè)坐標(biāo) for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { //遍歷當(dāng)前坐標(biāo)周圍的8個(gè)坐標(biāo) for (int aroundRow = i-1; aroundRow <= i+1; aroundRow++) { //行號(hào)超范圍則跳過 if (aroundRow < 0 || aroundRow > 9) { continue; } for (int aroundCol = j-1; aroundCol <= j+1; aroundCol++) { //列號(hào)超范圍則跳過 if (aroundCol < 0 || aroundCol > 9) { continue; } //排除本身坐標(biāo)點(diǎn) if ((gameData[aroundRow][aroundCol][0] == 1) && (!(aroundRow == i && aroundCol == j))) { gameData[i][j][2] += 1; } } } } } return gameData; } /** * 從控制臺(tái)讀取數(shù)據(jù),并對(duì)游戲的數(shù)據(jù)數(shù)組進(jìn)行修改 * @param input */ private static int[][][] readAndChangeData(Scanner input,int[][][] gameData) { //定義在循環(huán)外部,以方便后續(xù)使用 int row; int col; printBlank(12); //讀取輸入 //設(shè)置循環(huán)來讀取行號(hào),當(dāng)輸入的行號(hào)不在范圍內(nèi)時(shí),會(huì)一直提示玩家 while (true) { System.out.print("請(qǐng)輸入行號(hào):"); row = input.nextInt(); if (row >= 0 && row <= 9) { break; } else { System.out.println("輸入的行號(hào)不符合規(guī)范!"); } } //設(shè)置循環(huán)來讀取列號(hào),當(dāng)輸入的行號(hào)不在范圍內(nèi)時(shí),會(huì)一直提示玩家 while(true) { System.out.print("請(qǐng)輸入列號(hào):"); col = input.nextInt(); if (col >= 0 && col <= 9) { break; } else { System.out.println("輸入的列號(hào)不符合規(guī)范!"); } } //設(shè)置循環(huán),防止玩家輸入不能識(shí)別的字符 while (true) { System.out.print("標(biāo)記(B)還是直接翻開(F):"); String sign = input.next(); //如果翻開的是炸彈,直接把所有標(biāo)記變成'*',并返回結(jié)束游戲 if (sign.equalsIgnoreCase("f")) { if (gameData[row][col][0] == 1) { for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { gameData[i][j][1] =2; } } break; } } //修改數(shù)據(jù) if (gameData[row][col][1] != 3) {//gameData[row][col][1] == 3 表示已被翻開,翻開的坐標(biāo)點(diǎn)不能再被操作 if (sign.equalsIgnoreCase("b")) { gameData[row][col][1] = 1; } else if (sign.equalsIgnoreCase("f")) { //如果翻開的不是炸彈,則顯示其周圍地雷數(shù) if (gameData[row][col][0] != 1) { gameData[row][col][1] = 3; } } else { System.out.println("輸入不符合要求,請(qǐng)重新輸入!"); continue; } } break; } return gameData; } /** * 通關(guān)判斷 * @return */ private static boolean missionAccomplished(int[][][] gameDate) { //坐標(biāo)點(diǎn)總數(shù) int totalSite = 10 * 10; //統(tǒng)計(jì)地雷數(shù)與非地雷數(shù) int mineSigned = 0; int noMineOpen = 0; //遍歷游戲數(shù)據(jù)數(shù)組 for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { //通關(guān)條件 //1、翻開非地雷的位置 if (gameDate[i][j][0] == 0 && gameDate[i][j][1] == 3) { noMineOpen++; } //2、地雷位置標(biāo)記 if (gameDate[i][j][0] == 1 && gameDate[i][j][1] == 1) { mineSigned++; } } } if (totalSite == (noMineOpen + mineSigned)) { return true; } return false; } }
5.2 第二版
這一版的代碼實(shí)在第一版的基礎(chǔ)上對(duì)代碼進(jìn)行重構(gòu),根據(jù)功能對(duì)代碼進(jìn)行分類,將其放入不同的類中,增加代碼的可讀性與可維護(hù)性、可擴(kuò)展性。代碼一共分為五個(gè)類:主程序類、設(shè)置類、地雷類、控制類、顯示類。各部分代碼各司其職,共同作用,共同完成游戲運(yùn)行。
主程序類,游戲程序入口:
import java.util.Scanner; /** * 掃雷游戲 * @author zjl * */ public class MineSweeper { /** * 游戲運(yùn)行主程序 * @param args */ public static void main(String[] args) { Scanner input = new Scanner(System.in); //初始化游戲數(shù)據(jù) int[][][] gameData = GameControl.init(); while (true) { //打印游戲信息 Show.gameInfo(); //打印游戲框 Show.gameBoard(gameData); //踩中地雷結(jié)束游戲 //由于踩中地雷會(huì)把所有標(biāo)記變成'*',所以只需要判斷0行0列的顯示標(biāo)記是不是'*'就行了 if (gameData[0][0][Settings.SIGN_DATA] == Settings.MINE_SIGN_DATA) { System.out.println("踩中地雷,游戲結(jié)束!"); break; } //通關(guān)結(jié)束游戲 if (GameControl.missionAccomplished(gameData)) { System.out.println("恭喜通關(guān)!"); break; } //讀取控制臺(tái)數(shù)據(jù)并對(duì)游戲數(shù)據(jù)數(shù)組進(jìn)行修改 GameControl.readAndChangeData(input,gameData); } } }
設(shè)置類,游戲相關(guān)設(shè)置數(shù)據(jù):
/** * 定義游戲初始數(shù)據(jù)的類 * @author zjl * */ public class Settings { //定義游戲界面參數(shù) /** * 游戲界面的行數(shù) */ public static final int ROW_SIZE = 10; /** * 游戲界面的列數(shù) */ public static final int COL_SIZE = 10; /** * 兩個(gè)游戲界面之間的默認(rèn)空白行數(shù) */ public static final int DEFAULT_BLANK = 20; /** * 地雷數(shù) */ public static final int MINE_NUM = 20; /** * 確定地雷位置所需要的坐標(biāo)數(shù),由于是在平面內(nèi),所以只需要設(shè)置橫縱坐標(biāo),值為2 */ public static final int MINE_SITE_NUM = 2; /** * 地雷行坐標(biāo)在地雷數(shù)組中的下標(biāo) */ public static final int MINE_ROW_IN_ARRAY = 0; /** * 地雷列坐標(biāo)在地雷數(shù)組中的下標(biāo) */ public static final int MINE_COL_IN_ARRAY = 1; /** * 每個(gè)坐標(biāo)點(diǎn)中存儲(chǔ)數(shù)據(jù)的數(shù)組的大小 */ public static final int DATA_SIZE = 3; //定義每個(gè)坐標(biāo)點(diǎn)中存儲(chǔ)數(shù)據(jù)的數(shù)組中數(shù)值的含義 /** * 數(shù)組中存放地雷信息的位置 */ public static final int MINE_DATA = 0; /** * 表示不是地雷,為默認(rèn)值 */ public static final int IS_NOT_MINE = 0; /** * 表示是地雷 */ public static final int IS_MINE = 1; /** * 數(shù)組中存放符號(hào)信息的位置 */ public static final int SIGN_DATA = 1; /** * 表示初始符號(hào),即'?' */ public static final int INIT_SIGN_DATA = 0; /** * 表示插旗符號(hào),即'#' */ public static final int FLAG_SIGN_DATA = 1; /** * 表示地雷符號(hào),即'*' */ public static final int MINE_SIGN_DATA = 2; /** * 表示當(dāng)前位置已翻開,即應(yīng)該顯示當(dāng)前位置的地雷數(shù) */ public static final int MINE_NUM_SIGN_DATA = 3; /** * 數(shù)組中存放坐標(biāo)點(diǎn)周圍地雷數(shù)的位置 */ public static final int AROUND_MINE_DATA = 2; //游戲符號(hào) /** * 初始符號(hào)'?' */ public static final char INIT_SIGN = '?'; /** * 插旗符號(hào)'#' */ public static final char FLAG_SIGN = '#'; /** * 地雷符號(hào)'*' */ public static final char MINE_SIGN = '*'; /** * 在ASCII碼表中整數(shù)48~57代表字符0~9,設(shè)置一個(gè)增量值,將數(shù)字轉(zhuǎn)換為字符 */ public static final int ASCII_ADD = 48; //定義玩家在控制臺(tái)輸入的操縱符 /** * 表示翻開操縱的符號(hào) */ public static final String OPEN_OPERATION = "F"; /** * 表示插旗操作的符號(hào) */ public static final String FLAG_OPERATION = "B"; /** * 游戲信息 */ public static final String INFORMATION = "**************************************************\n" + "\t\t 游戲信息\n" + "游戲名稱:掃雷\n" + "游戲版本:2.0\n" + "游戲操作:1.輸入行號(hào)及列號(hào)來選中要翻開的\'?\'進(jìn)行操作,可\n" + "\t 以選擇插旗(#)或者直接翻開.\n" + "\t 2.如果翻開'*'則表示地雷,則游戲結(jié)束;如果翻開\n" + "\t 的是數(shù)字,則表示該格周圍的地雷數(shù).\n" + "\t 3.標(biāo)記出全部地雷,并且沒有\(zhòng)'?\',則闖關(guān)成功,游戲\n" + "\t 結(jié)束.\n" + "**************************************************\n\n"; }
地雷類,生成隨機(jī)的地雷坐標(biāo)數(shù)據(jù):
import java.util.Random; /** * 有關(guān)地雷的類 * @author zjl * */ public class Mine { /** * 隨機(jī)生成地雷坐標(biāo) */ public static int[][] createMineCoord() { //定義二維數(shù)組 int[][] mineCoordArray = new int[Settings.MINE_NUM][Settings.MINE_SITE_NUM]; Random random = new Random(); //將生成的隨機(jī)坐標(biāo)存入數(shù)組中 for (int i = 0; i < Settings.MINE_NUM; i++) { for (int j = 0; j < Settings.MINE_SITE_NUM; j++) { //生成行坐標(biāo)隨機(jī)數(shù),并將其放入數(shù)組 if (j == Settings.MINE_ROW_IN_ARRAY) { mineCoordArray[i][j] = random.nextInt(Settings.ROW_SIZE); } //生成列坐標(biāo)隨機(jī)數(shù),并將其放入數(shù)組 if (j == Settings.MINE_COL_IN_ARRAY) { mineCoordArray[i][j] = random.nextInt(Settings.COL_SIZE); } } } return mineCoordArray; } }
控制類,控制游戲進(jìn)程,以及游戲數(shù)據(jù):
import java.util.Scanner; /** * 關(guān)于游戲相關(guān)控制的類 * @author zjl * */ public class GameControl { /** * 初始化游戲數(shù)據(jù) */ public static int[][][] init(){ //創(chuàng)建存儲(chǔ)游戲相關(guān)數(shù)據(jù)的三維數(shù)組(默認(rèn)初始值為0) int[][][] gameData = new int[Settings.ROW_SIZE][Settings.COL_SIZE][Settings.DATA_SIZE]; //生成隨機(jī)的地雷坐標(biāo),并將其存入游戲數(shù)據(jù)數(shù)組中 int[][] mineCoordArray = Mine.createMineCoord(); for (int[] mineCoord : mineCoordArray) { int row = mineCoord[Settings.MINE_ROW_IN_ARRAY]; int col = mineCoord[Settings.MINE_COL_IN_ARRAY]; gameData[row][col][Settings.MINE_DATA] = Settings.IS_MINE; } //計(jì)算每格周圍地雷數(shù)并將其存入游戲數(shù)據(jù)數(shù)組中 //循環(huán)遍歷每個(gè)坐標(biāo) for (int i = 0; i < Settings.ROW_SIZE; i++) { for (int j = 0; j < Settings.COL_SIZE; j++) { //遍歷當(dāng)前坐標(biāo)周圍的8個(gè)坐標(biāo) for (int aroundRow = i-1; aroundRow <= i+1; aroundRow++) { //行號(hào)超范圍則跳過 if (aroundRow < 0 || aroundRow > Settings.ROW_SIZE-1) { continue; } for (int aroundCol = j-1; aroundCol <= j+1; aroundCol++) { //列號(hào)超范圍則跳過 if (aroundCol < 0 || aroundCol > Settings.COL_SIZE-1) { continue; } //排除本身坐標(biāo)點(diǎn) if ((gameData[aroundRow][aroundCol][Settings.MINE_DATA] == Settings.IS_MINE) && (!(aroundRow == i && aroundCol == j))) { gameData[i][j][Settings.AROUND_MINE_DATA] += 1; } } } } } return gameData; } /** * 從控制臺(tái)讀取數(shù)據(jù),并對(duì)游戲的數(shù)據(jù)數(shù)組進(jìn)行修改 * @param input */ public static void readAndChangeData(Scanner input,int[][][] gameData) { //定義在循環(huán)外部,以方便后續(xù)使用 int row; int col; //讀取輸入 //設(shè)置循環(huán)來讀取行號(hào),當(dāng)輸入的行號(hào)不在范圍內(nèi)時(shí),會(huì)一直提示玩家 while (true) { System.out.print("請(qǐng)輸入行號(hào):"); row = input.nextInt(); if (row >= 0 && row <= Settings.ROW_SIZE-1) { break; } else { System.out.println("輸入的行號(hào)不符合規(guī)范!"); } } //設(shè)置循環(huán)來讀取列號(hào),當(dāng)輸入的行號(hào)不在范圍內(nèi)時(shí),會(huì)一直提示玩家 while(true) { System.out.print("請(qǐng)輸入列號(hào):"); col = input.nextInt(); if (col >= 0 && col <= Settings.COL_SIZE-1) { break; } else { System.out.println("輸入的列號(hào)不符合規(guī)范!"); } } //設(shè)置循環(huán),防止玩家輸入不能識(shí)別的字符 while (true) { System.out.print("標(biāo)記(B)還是直接翻開(F):"); String sign = input.next(); //如果翻開的是炸彈,直接把所有標(biāo)記變成'*',并返回結(jié)束游戲 if (sign.equalsIgnoreCase(Settings.OPEN_OPERATION)) { if (gameData[row][col][Settings.MINE_DATA] == Settings.IS_MINE) { for (int i = 0; i < Settings.ROW_SIZE; i++) { for (int j = 0; j < Settings.COL_SIZE; j++) { gameData[i][j][Settings.SIGN_DATA] =Settings.MINE_SIGN_DATA; } } break; } } //修改數(shù)據(jù) if (gameData[row][col][Settings.SIGN_DATA] != Settings.MINE_NUM_SIGN_DATA) {//相等表示已被翻開,翻開的坐標(biāo)點(diǎn)不能再被操作 if (sign.equalsIgnoreCase(Settings.FLAG_OPERATION)) { gameData[row][col][Settings.SIGN_DATA] = Settings.FLAG_SIGN_DATA; } else if (sign.equalsIgnoreCase(Settings.OPEN_OPERATION)) { //如果翻開的不是炸彈,則顯示其周圍地雷數(shù) if (gameData[row][col][Settings.MINE_DATA] == Settings.IS_NOT_MINE) { gameData[row][col][Settings.SIGN_DATA] = Settings.MINE_NUM_SIGN_DATA; } } else { System.out.println("輸入不符合要求,請(qǐng)重新輸入!"); continue; } } break; } } /** * 通關(guān)判斷 * @return */ public static boolean missionAccomplished(int[][][] gameDate) { //坐標(biāo)點(diǎn)總數(shù) int totalSite = Settings.ROW_SIZE * Settings.COL_SIZE; //統(tǒng)計(jì)地雷數(shù)與非地雷數(shù) int mineSigned = 0; int noMineOpen = 0; //遍歷游戲數(shù)據(jù)數(shù)組 for (int i = 0; i < Settings.ROW_SIZE; i++) { for (int j = 0; j < Settings.COL_SIZE; j++) { //通關(guān)條件 //1、翻開非地雷的位置 if (gameDate[i][j][Settings.MINE_DATA] == Settings.IS_NOT_MINE && gameDate[i][j][Settings.SIGN_DATA] == Settings.MINE_NUM_SIGN_DATA) { noMineOpen++; } //2、地雷位置標(biāo)記 if (gameDate[i][j][Settings.MINE_DATA] == Settings.IS_MINE && gameDate[i][j][Settings.SIGN_DATA] == Settings.FLAG_SIGN_DATA) { mineSigned++; } } } //當(dāng)翻開的的坐標(biāo)數(shù)加上標(biāo)記的地雷數(shù)等于坐標(biāo)點(diǎn)總數(shù)的時(shí)候,返回true表示可以結(jié)束游戲 if (totalSite == (noMineOpen + mineSigned)) { return true; } //條件不滿足,游戲繼續(xù) return false; } }
顯示類,對(duì)游戲的相關(guān)畫面進(jìn)行打?。?/p>
/** * 展示游戲相關(guān)畫面的類 * @author zjl * */ public class Show { /** * 打印提示信息 */ public static void gameInfo() { //打印空白行,作用是使展現(xiàn)在控制臺(tái)的圖形刷新 printSign(Settings.DEFAULT_BLANK,"\n"); System.out.println(Settings.INFORMATION); } /** * 打印一行指定的圖形 */ public static void printSign(int num,String sign) { for (int i = 0; i < num; i++) { System.out.print(sign); } } /** * 打印游戲框 */ public static void gameBoard(int[][][] gameData) { //打印游戲上邊框 printSign(4, " "); for (int i = 0; i < Settings.COL_SIZE; i++) { printSign(1, i+" "); } printSign(1, "\n *"); printSign(Settings.COL_SIZE+1, "**"); printSign(1, "\n"); //遍歷游戲框中的每個(gè)坐標(biāo),讀取并打印顯示符號(hào) for (int i = 0; i < Settings.ROW_SIZE; i++) { System.out.print(i + " * "); for (int j = 0; j < Settings.COL_SIZE; j++) { //讀取展示的符號(hào) char sign; switch (gameData[i][j][Settings.SIGN_DATA]) { case Settings.FLAG_SIGN_DATA: sign = Settings.FLAG_SIGN; break; case Settings.MINE_SIGN_DATA: sign = Settings.MINE_SIGN; break; case Settings.MINE_NUM_SIGN_DATA: //將數(shù)組中存的整型數(shù)值通過ASCII碼轉(zhuǎn)為字符型表示 sign = (char)(gameData[i][j][Settings.AROUND_MINE_DATA] + Settings.ASCII_ADD); break; default: sign = Settings.INIT_SIGN; break; } //打印符號(hào) System.out.print(sign + " "); } System.out.println("*"); } //打印游戲下邊框 printSign(2, " "); printSign(Settings.COL_SIZE+1, "**"); printSign(1, "*\n"); } }
5.3 第三版
在第二版的基礎(chǔ)上,去除了顯示類,增加了圖片類、圖形界面類、事件監(jiān)聽器類。
游戲運(yùn)行主程序類:
/** * 掃雷游戲主程序類 * @author zjl * */ public class MineSweeper { /** * 游戲運(yùn)行主程序 * @param args */ public static void main(String[] args) { int[][][] gameData = new int[Settings.ROW_SIZE][Settings.COL_SIZE][Settings.DATA_SIZE]; //創(chuàng)建游戲控制類對(duì)象 GameDataController controller = new GameDataController(gameData); //初始化游戲數(shù)據(jù) controller.init(); //繪制游戲界面 new Graphic(controller); } }
設(shè)置類,提供游戲運(yùn)行相關(guān)這是數(shù)據(jù):
/** * 定義游戲初始數(shù)據(jù)的類 * @author zjl * */ public class Settings { //定義游戲界面參數(shù) /** * 游戲界面的行數(shù) */ public static final int ROW_SIZE = 10; /** * 游戲界面的列數(shù) */ public static final int COL_SIZE = 10; /** * 地雷數(shù) */ public static final int MINE_NUM = 20; /** * 確定地雷位置所需要的坐標(biāo)數(shù),由于是在平面內(nèi),所以只需要設(shè)置橫縱坐標(biāo),值為2 */ public static final int MINE_SITE_NUM = 2; /** * 地雷行坐標(biāo)在地雷數(shù)組中的下標(biāo) */ public static final int MINE_ROW_IN_ARRAY = 0; /** * 地雷列坐標(biāo)在地雷數(shù)組中的下標(biāo) */ public static final int MINE_COL_IN_ARRAY = 1; /** * 每個(gè)坐標(biāo)點(diǎn)中存儲(chǔ)數(shù)據(jù)的數(shù)組的大小 */ public static final int DATA_SIZE = 3; //定義每個(gè)坐標(biāo)點(diǎn)中存儲(chǔ)數(shù)據(jù)的數(shù)組中數(shù)值的含義 /** * 數(shù)組中存放地雷信息的位置 */ public static final int MINE_DATA = 0; /** * 表示不是地雷,為默認(rèn)值 */ public static final int IS_NOT_MINE = 0; /** * 表示是地雷 */ public static final int IS_MINE = 1; /** * 數(shù)組中存放符號(hào)信息的位置 */ public static final int SIGN_DATA = 1; /** * 表示初始符號(hào) */ public static final int INIT_SIGN_DATA = 0; /** * 表示插旗符號(hào) */ public static final int FLAG_SIGN_DATA = 1; /** * 表示地雷符號(hào) */ public static final int MINE_SIGN_DATA = 2; /** * 表示當(dāng)前位置已翻開,即應(yīng)該顯示當(dāng)前位置的地雷數(shù) */ public static final int MINE_NUM_SIGN_DATA = 3; /** * 數(shù)組中存放坐標(biāo)點(diǎn)周圍地雷數(shù)的位置 */ public static final int AROUND_MINE_DATA = 2; //定義游戲框尺寸數(shù)據(jù) /** * 圖片邊長 */ public static final int IMAGE_SIZE = 60; /** * 游戲窗口在屏幕上的x位置 */ public static final int FRAME_X = 400; /** * 游戲窗口在屏幕上的y位置 */ public static final int FRAME_Y = 150; /** * 游戲窗口的寬度 */ public static final int FRAME_WIDTH = IMAGE_SIZE * ROW_SIZE; /** * 游戲窗口的高度 */ public static final int FRAME_HEIGHT =IMAGE_SIZE * COL_SIZE; /** * 游戲網(wǎng)格線的寬度 */ public static final int BORDER_WIDTH = 1; /** * 游戲窗口標(biāo)題欄高度 */ public static final int TITLE_HEIGHT = 23; //彈窗數(shù)據(jù) /** * 彈窗標(biāo)題 */ public static final String DIALOG_TITLE = "提示"; /** * 彈窗提示語,踩雷 */ public static final String DIALOG_DEFEAT = "踩雷,游戲結(jié)束!"; /** * 彈窗提示語,通關(guān) */ public static final String DIALOG_VECTORY = "恭喜通關(guān)!"; }
圖片類,提供游戲相關(guān)圖片:
import javax.swing.ImageIcon; /** * 這是一個(gè)用于提供游戲所需圖片的類,在游戲運(yùn)行時(shí),將相關(guān)的圖片加載到圖形界面上 * @author zjl * */ public class Image{ /** * ImageIcon類型的常量,表示地雷圖片 */ public static final ImageIcon IMAGE_MINE = new ImageIcon("img\\mine.png"); /** * ImageIcon類型的常量,表示旗幟圖片 */ public static final ImageIcon IMAGE_FLAG = new ImageIcon("img\\flag.png"); /** * ImageIcon類型的常量,表示失敗時(shí)的表情圖片 */ public static final ImageIcon IMAGE_DEFEAT = new ImageIcon("img\\defeat.png"); /** * ImageIcon類型的常量,表示通關(guān)時(shí)的表情圖片 */ public static final ImageIcon IMAGE_VECTORY = new ImageIcon("img\\vectory.png"); /** * 這是一個(gè)用于返回?cái)?shù)字圖片的靜態(tài)方法, * 通過傳入的參數(shù)來獲取表示對(duì)應(yīng)數(shù)字的圖片, * 返回的圖片上的數(shù)字表示某位置周圍存在的地雷數(shù) * @param mineNum-int類型,表示傳入地雷數(shù)量的參數(shù) * @return image-ImageIcon類型的返回值,返回的圖片上的數(shù)字與參數(shù)mineNum對(duì)應(yīng) */ public static ImageIcon getImageByNum(int mineNum) { ImageIcon image = new ImageIcon("img\\"+mineNum+".png"); return image; } }
地雷類,生成游戲中的類的相關(guān)數(shù)據(jù):
import java.util.Random; /** * 這是一個(gè)用于生成隨機(jī)地雷坐標(biāo)的類。 * 由于生成的是偽隨機(jī)數(shù),因此生成的地雷坐標(biāo)可能重復(fù),所以實(shí)際游戲中的地雷數(shù)量并不固定。 * @author zjl * */ public class Mine { /** * 隨機(jī)生成坐標(biāo)數(shù)據(jù),為游戲提供隨機(jī)的地雷坐標(biāo)數(shù)據(jù)。 * @return mineCoordArray-int類型的二維數(shù)組,存儲(chǔ)的是生成的地雷的坐標(biāo)數(shù)據(jù) */ public static int[][] createMineCoord() { //定義二維數(shù)組 int[][] mineCoordArray = new int[Settings.MINE_NUM][Settings.MINE_SITE_NUM]; Random random = new Random(); //將生成的隨機(jī)坐標(biāo)存入數(shù)組中 for (int i = 0; i < Settings.MINE_NUM; i++) { for (int j = 0; j < Settings.MINE_SITE_NUM; j++) { //生成行坐標(biāo)隨機(jī)數(shù),并將其放入數(shù)組 if (j == Settings.MINE_ROW_IN_ARRAY) { mineCoordArray[i][j] = random.nextInt(Settings.ROW_SIZE); } //生成列坐標(biāo)隨機(jī)數(shù),并將其放入數(shù)組 if (j == Settings.MINE_COL_IN_ARRAY) { mineCoordArray[i][j] = random.nextInt(Settings.COL_SIZE); } } } return mineCoordArray; } }
游戲控制類,提供用于游戲控制的相關(guān)方法:
import javax.swing.JLabel; /** * 這是一個(gè)用于游戲數(shù)據(jù)控制的類, * @author zjl * */ public class GameDataController { /** * 私有的成員變量,用于存儲(chǔ)在構(gòu)造方法中接收到的游戲數(shù)據(jù) */ private int[][][] gameData; /** * 這是本類的一個(gè)有參構(gòu)造方法,通過傳入游戲數(shù)據(jù)來構(gòu)造一個(gè)游戲數(shù)據(jù)控制器 * @param gameData-存儲(chǔ)游戲相關(guān)數(shù)據(jù)的三維數(shù)組 */ public GameDataController(int[][][] gameData) { this.gameData = gameData; } /** * 初始化游戲數(shù)據(jù) */ public void init(){ //將地雷數(shù)據(jù)存入三維游戲數(shù)組中 int[][] mineCoordArray = Mine.createMineCoord(); for (int[] mineCoord : mineCoordArray) { int row = mineCoord[Settings.MINE_ROW_IN_ARRAY]; int col = mineCoord[Settings.MINE_COL_IN_ARRAY]; gameData[row][col][Settings.MINE_DATA] = Settings.IS_MINE; } //計(jì)算每格周圍地雷數(shù)并將其存入游戲數(shù)據(jù)數(shù)組中 calcAroundNum(); } /** * 游戲通關(guān)判斷 * @return 返回boolean類型的true或false,true表示游戲通關(guān) */ public boolean missionAccomplished() { //坐標(biāo)點(diǎn)總數(shù) int totalSite = Settings.ROW_SIZE * Settings.COL_SIZE; //統(tǒng)計(jì)地雷數(shù)與非地雷數(shù) int mineSigned = 0; int noMineOpen = 0; //遍歷游戲數(shù)據(jù)數(shù)組 for (int i = 0; i < Settings.ROW_SIZE; i++) { for (int j = 0; j < Settings.COL_SIZE; j++) { //通關(guān)條件 //1、翻開非地雷的位置 if (gameData[i][j][Settings.MINE_DATA] == Settings.IS_NOT_MINE && gameData[i][j][Settings.SIGN_DATA] == Settings.MINE_NUM_SIGN_DATA) { noMineOpen++; } //2、地雷位置標(biāo)記 if (gameData[i][j][Settings.MINE_DATA] == Settings.IS_MINE && gameData[i][j][Settings.SIGN_DATA] == Settings.FLAG_SIGN_DATA) { mineSigned++; } } } //當(dāng)翻開的的坐標(biāo)數(shù)加上標(biāo)記的地雷數(shù)等于坐標(biāo)點(diǎn)總數(shù)的時(shí)候,返回true表示可以結(jié)束游戲 if (totalSite == (noMineOpen + mineSigned)) { return true; } //條件不滿足,游戲繼續(xù) return false; } /** * 用于鼠標(biāo)左擊時(shí)的游戲控制操作,即翻開所點(diǎn)擊的位置 * @param x-表示點(diǎn)擊位置在游戲框上的x軸坐標(biāo) * @param y-表示點(diǎn)擊位置在游戲框上的y軸坐標(biāo) * @param labels-表示用于放置圖片的標(biāo)簽的集合 */ public void leftClick(int x, int y, JLabel[][] labels) { if (gameData[y][x][Settings.SIGN_DATA] == Settings.INIT_SIGN_DATA) { if (gameData[y][x][Settings.MINE_DATA] == Settings.IS_MINE) { //如果翻開的是地雷,顯示彈窗提示結(jié)束游戲 labels[y][x].setIcon(Image.IMAGE_MINE); Graphic.showDialog(false); }else { //如果當(dāng)前位置未被翻開,則翻開當(dāng)前位置,修改游戲數(shù)據(jù)及顯示圖片 gameData[y][x][Settings.SIGN_DATA] = Settings.MINE_NUM_SIGN_DATA; int aroundMineNum = gameData[y][x][Settings.AROUND_MINE_DATA]; labels[y][x].setIcon(Image.getImageByNum(aroundMineNum)); } } } /** * 用于鼠標(biāo)右擊時(shí)的游戲控制操作,即插旗與取消插旗 * @param x-表示點(diǎn)擊位置在游戲框上的x軸坐標(biāo) * @param y-表示點(diǎn)擊位置在游戲框上的y軸坐標(biāo) * @param labels-表示用于放置圖片的標(biāo)簽的集合 */ public void rightClick(int x, int y, JLabel[][] labels) { if (gameData[y][x][Settings.SIGN_DATA] == Settings.INIT_SIGN_DATA) { //如果當(dāng)前位置未被翻開,則修改相應(yīng)數(shù)據(jù),并將其顯示為插旗 gameData[y][x][Settings.SIGN_DATA] = Settings.FLAG_SIGN_DATA; labels[y][x].setIcon(Image.IMAGE_FLAG); } else if (gameData[y][x][Settings.SIGN_DATA] == Settings.FLAG_SIGN_DATA) { //如果該位置已被插旗,則修改相應(yīng)數(shù)據(jù),并將其恢復(fù)初始狀態(tài) gameData[y][x][Settings.SIGN_DATA] = Settings.INIT_SIGN_DATA; labels[y][x].setIcon(null); } } /** * 計(jì)算每個(gè)位置周圍的地雷數(shù),并將算出的結(jié)果存入到三維游戲數(shù)組中 */ private void calcAroundNum() { for (int i = 0; i < Settings.ROW_SIZE; i++) { for (int j = 0; j < Settings.COL_SIZE; j++) { //遍歷當(dāng)前坐標(biāo)周圍的8個(gè)坐標(biāo) for (int aroundRow = i-1; aroundRow <= i+1; aroundRow++) { //行號(hào)超范圍則跳過 if (aroundRow < 0 || aroundRow > Settings.ROW_SIZE-1) { continue; } for (int aroundCol = j-1; aroundCol <= j+1; aroundCol++) { //列號(hào)超范圍則跳過 if (aroundCol < 0 || aroundCol > Settings.COL_SIZE-1) { continue; } //排除本身坐標(biāo)點(diǎn) if ((gameData[aroundRow][aroundCol][Settings.MINE_DATA] == Settings.IS_MINE) && (!(aroundRow == i && aroundCol == j))) { gameData[i][j][Settings.AROUND_MINE_DATA] += 1; } } } } } } }
繪制圖形化界面類,生成顯示游戲的圖形化界面:
import java.awt.Color; import java.awt.GridLayout; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; /** * 繪制掃雷的圖形界面的類,提供用于繪制界面以及用于獲取相關(guān)對(duì)象的方法 * @author zjl * */ public class Graphic { /** * 定義JFrame類型的的靜態(tài)屬性frame */ private static JFrame frame; /** * 定義GameListener類型的游戲事件監(jiān)聽器gameListener */ private static GameListener gameListener; /** * 定義用于存儲(chǔ)JLabel類型數(shù)據(jù)的二維數(shù)組labels */ private JLabel[][] labels = new JLabel[Settings.ROW_SIZE][Settings.COL_SIZE]; /** * 定義JLabel類型的屬性label */ private JLabel label; /** * 定義游戲控制器 */ private GameDataController controller; /** * 初始化游戲圖形界面中窗口容器的相關(guān)設(shè)置 */ static { frame = new JFrame("掃雷2.0"); //將frame的布局管理器設(shè)置為GridLayout frame.setLayout(new GridLayout(Settings.ROW_SIZE, Settings.COL_SIZE)); //設(shè)置frame的位置、大小、可見性,設(shè)置窗體大小不可更改以及關(guān)閉按鈕的功能 frame.setBounds(Settings.FRAME_X, Settings.FRAME_Y, Settings.FRAME_WIDTH, Settings.FRAME_HEIGHT); frame.setVisible(true); frame.setResizable(false); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } /** * 這是該類的一個(gè)有參構(gòu)造方法,用于創(chuàng)建Graphic對(duì)象。 * 該構(gòu)造方法調(diào)用本類中的draw()方法,繪制游戲的圖形化界面 * @param controller-接收主函數(shù)中傳入的游戲參數(shù)控制器對(duì)象,后續(xù)將其傳遞到事件監(jiān)聽器中,使監(jiān)聽器能夠修改游戲數(shù)據(jù) */ public Graphic(GameDataController controller) { this.controller = controller; draw(); } /** * 繪制游戲的圖形化界面,并在游戲窗口上添加事件監(jiān)聽器 */ private void draw() { //通過循環(huán)創(chuàng)建label,并將其加入到frame中 for (int i = 0; i < Settings.ROW_SIZE; i++) { for (int j = 0; j < Settings.COL_SIZE; j++) { frame.add(label = new JLabel()); labels[i][j] = label; //設(shè)置label的邊框?qū)傩? label.setBorder(BorderFactory.createLineBorder(Color.BLACK, Settings.BORDER_WIDTH)); } } //創(chuàng)建事件監(jiān)聽器,監(jiān)聽鼠標(biāo)點(diǎn)擊在frame上的位置,并將監(jiān)聽器添加到frame上 gameListener = new GameListener(labels, controller); frame.addMouseListener(gameListener); } /** * 繪制游戲結(jié)束時(shí)的彈窗。 * 根據(jù)傳入的參數(shù)判斷游戲是因?yàn)椴鹊降乩锥Y(jié)束還是因?yàn)橥P(guān)而結(jié)束,從而繪制不同效果的彈窗 * @param result-boolean類型的參數(shù),表示游戲是因?yàn)橥P(guān)結(jié)束還是因?yàn)椴壤捉Y(jié)束 */ public static void showDialog(boolean result) { int option; String message; ImageIcon image; //判斷游戲的結(jié)束原因,并進(jìn)行相應(yīng)的賦值操作 if (result) { message = Settings.DIALOG_VECTORY; image = Image.IMAGE_VECTORY; } else { message = Settings.DIALOG_DEFEAT; image = Image.IMAGE_DEFEAT; } //彈窗出現(xiàn)表示游戲結(jié)束,此時(shí)應(yīng)移除窗體上的事件監(jiān)聽器 frame.removeMouseListener(gameListener); //根據(jù)相關(guān)參數(shù)繪制彈窗 option = JOptionPane.showConfirmDialog(null, message, Settings.DIALOG_TITLE, JOptionPane.CANCEL_OPTION,JOptionPane.INFORMATION_MESSAGE,image); /* 根據(jù)彈窗上的按鈕點(diǎn)擊結(jié)果判斷是否關(guān)閉游戲退出程序。 * 只有在點(diǎn)擊確定時(shí)才會(huì)結(jié)束程序,點(diǎn)擊取消并不會(huì)推出游戲, * 而是停留在游戲結(jié)束時(shí)的畫面,但是不能進(jìn)行游戲操作, * 點(diǎn)擊關(guān)閉窗口即可退出程序 */ if (option != JOptionPane.CANCEL_OPTION) { System.exit(0); } } }
事件監(jiān)聽器類,用于提供監(jiān)聽鼠標(biāo)點(diǎn)擊事件的監(jiān)聽器:
import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.ImageIcon; import javax.swing.JLabel; /** * 定義游戲相關(guān)的事件監(jiān)聽器 * @author zjl * */ public class GameListener extends MouseAdapter { /** * 私有的成員變量,用以接收構(gòu)造事件監(jiān)聽器時(shí)傳入的JLabel數(shù)組 */ private JLabel[][] labels; /** * 私有的成員變量,用以接收構(gòu)造事件監(jiān)聽器時(shí)傳入的游戲數(shù)據(jù)控制器對(duì)象 */ private GameDataController controller; /** * 這是本類一個(gè)有參構(gòu)造方法,用于根據(jù)傳入的參數(shù)構(gòu)造事件監(jiān)聽器對(duì)象 * @param labels-這是一個(gè)JLabel數(shù)組,為后續(xù)改變相應(yīng)的圖像顯示提供容納圖片的JLabel組件 * @param controller-游戲數(shù)據(jù)控制器對(duì)象,用于改變相應(yīng)游戲數(shù)據(jù) */ public GameListener(JLabel[][] labels, GameDataController controller) { this.labels = labels; this.controller = controller; } /** * 重寫MouseAdapter類中的mouseClicked方法,添加響應(yīng)鼠標(biāo)操作的邏輯代碼 */ @Override public void mouseClicked(MouseEvent e) { //對(duì)鼠標(biāo)點(diǎn)擊點(diǎn)的坐標(biāo)進(jìn)行計(jì)算可得到label在數(shù)組中的下標(biāo) int x = (e.getX()-Settings.BORDER_WIDTH)/Settings.IMAGE_SIZE; int y = (e.getY()-Settings.TITLE_HEIGHT)/Settings.IMAGE_SIZE; //區(qū)分鼠標(biāo)左擊右擊事件 if (e.getButton() == MouseEvent.BUTTON1) {//鼠標(biāo)左擊,進(jìn)行的操作為翻開當(dāng)前位置 controller.leftClick(x, y, labels); } else if (e.getButton() == MouseEvent.BUTTON3) {//鼠標(biāo)右擊,進(jìn)行的操作為插旗與取消插旗 controller.rightClick(x, y, labels); } //通關(guān)判斷 if (controller.missionAccomplished()) { //通關(guān)則顯示彈窗并移除監(jiān)聽器 Graphic.showDialog(true); } } }
6.部分代碼思路
6.1 生成隨機(jī)的地雷坐標(biāo)
創(chuàng)建Random類的對(duì)象,使用相關(guān)方法,生成地雷行坐標(biāo)與列坐標(biāo)的隨機(jī)數(shù)值,使用二維數(shù)組存儲(chǔ)坐標(biāo)點(diǎn)數(shù)據(jù)。由于沒有做去重處理,因此有概率生成多個(gè)相同的坐標(biāo),所以地雷數(shù)最多為設(shè)置的生成數(shù),最少為1(概率極低)。
生成隨機(jī)坐標(biāo)的代碼如下:
public static int[][] createMineCoord() { //定義二維數(shù)組 int[][] mineCoordArray = new int[Settings.MINE_NUM][Settings.MINE_SITE_NUM]; Random random = new Random(); //將生成的隨機(jī)坐標(biāo)存入數(shù)組中 for (int i = 0; i < Settings.MINE_NUM; i++) { for (int j = 0; j < Settings.MINE_SITE_NUM; j++) { //生成行坐標(biāo)隨機(jī)數(shù),并將其放入數(shù)組 if (j == Settings.MINE_ROW_IN_ARRAY) { mineCoordArray[i][j] = random.nextInt(Settings.ROW_SIZE); } //生成列坐標(biāo)隨機(jī)數(shù),并將其放入數(shù)組 if (j == Settings.MINE_COL_IN_ARRAY) { mineCoordArray[i][j] = random.nextInt(Settings.COL_SIZE); } } } return mineCoordArray; }
6.2 測試地雷生成
代碼如下:
import java.util.Random; public class Test { public static void main(String[] args) { int[][][] gameData = init(); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { System.out.print("("); for (int k= 0; k < 3; k++) { System.out.print(gameData[i][j][k]); if (k < 2) { System.out.print(","); } } System.out.print(")"); } System.out.println(); } } /** * 初始化游戲數(shù)據(jù) * @return */ private static int[][][] init(){ //創(chuàng)建大小為10*10*3的三維數(shù)組,并賦初值(默認(rèn)初始值為0) int[][][] gameData = new int[10][10][3]; //生成隨機(jī)的地雷坐標(biāo),并將其存入游戲數(shù)據(jù)數(shù)組中 int[][] mineCoordArray = createMineCoord(); for (int[] mineCoord : mineCoordArray) { int row = mineCoord[0]; int col = mineCoord[1]; gameData[row][col][0] = 1; } //計(jì)算每格周圍地雷數(shù)并將其存入游戲數(shù)據(jù)數(shù)組中 return gameData; } }
運(yùn)行結(jié)果如下:
將其轉(zhuǎn)化為圖像形式就是:
6.3 計(jì)算每格周圍的地雷數(shù)目
思路:遍歷目標(biāo)坐標(biāo)點(diǎn)周圍的8個(gè)坐標(biāo)點(diǎn),每當(dāng)發(fā)現(xiàn)一個(gè)地雷,則目標(biāo)坐標(biāo)點(diǎn)的游戲數(shù)據(jù)數(shù)組中的統(tǒng)計(jì)地雷的數(shù)值加1。
實(shí)現(xiàn)代碼:
private void calcAroundNum() { for (int i = 0; i < Settings.ROW_SIZE; i++) { for (int j = 0; j < Settings.COL_SIZE; j++) { //遍歷當(dāng)前坐標(biāo)周圍的8個(gè)坐標(biāo) for (int aroundRow = i-1; aroundRow <= i+1; aroundRow++) { //行號(hào)超范圍則跳過 if (aroundRow < 0 || aroundRow > Settings.ROW_SIZE-1) { continue; } for (int aroundCol = j-1; aroundCol <= j+1; aroundCol++) { //列號(hào)超范圍則跳過 if (aroundCol < 0 || aroundCol > Settings.COL_SIZE-1) { continue; } //排除本身坐標(biāo)點(diǎn) if ((gameData[aroundRow][aroundCol][Settings.MINE_DATA] == Settings.IS_MINE) && (!(aroundRow == i && aroundCol == j))) { gameData[i][j][Settings.AROUND_MINE_DATA] += 1; } } } } } }
測試運(yùn)行結(jié)果如下:
將其轉(zhuǎn)換為圖像表示:
7.游戲運(yùn)行畫面
7.1 踩中地雷
第一、二版:
第三版:
7.2 通關(guān)游戲
第一、二版:
第三版:
到此這篇關(guān)于利用java開發(fā)丐版掃雷游戲的文章就介紹到這了,更多相關(guān)java開發(fā)掃雷游戲內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于SpringBoot獲取IOC容器中注入的Bean(推薦)
本文通過實(shí)例代碼給大家詳解了springboot獲取ioc容器中注入的bean問題,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2018-05-05SpringBoot大學(xué)心理服務(wù)系統(tǒng)實(shí)現(xiàn)流程分步講解
本系統(tǒng)主要論述了如何使用JAVA語言開發(fā)一個(gè)大學(xué)生心理服務(wù)系統(tǒng) ,本系統(tǒng)將嚴(yán)格按照軟件開發(fā)流程進(jìn)行各個(gè)階段的工作,采用B/S架構(gòu),面向?qū)ο缶幊趟枷脒M(jìn)行項(xiàng)目開發(fā)2022-09-09從java反編譯及字節(jié)碼角度探索分析String拼接字符串效率
這篇文章主要介紹了從java反編譯及字節(jié)碼角度探索分析String拼接字符串效率,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12JVM性能調(diào)優(yōu)之運(yùn)行時(shí)參數(shù)小結(jié)
jvm是java的運(yùn)行環(huán)境,在jvm中有很多的參數(shù)可以進(jìn)行設(shè)置,本文主要介紹了JVM性能調(diào)優(yōu)之運(yùn)行時(shí)參數(shù)小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下2024-04-04Java并發(fā)底層實(shí)現(xiàn)原理學(xué)習(xí)心得
本片文章是學(xué)習(xí)Java并發(fā)底層實(shí)現(xiàn)原理的一篇知識(shí)心得,對(duì)大家學(xué)習(xí)這個(gè)方便的知識(shí)很有幫助,一起參考下。2018-01-01java String[]字符串?dāng)?shù)組自動(dòng)排序的簡單實(shí)現(xiàn)
下面小編就為大家?guī)硪黄猨ava String[]字符串?dāng)?shù)組自動(dòng)排序的簡單實(shí)現(xiàn)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-09-09