一、團隊課程設計博客鏈接:
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(); } } });
五、課程設計感想
通過這次課程設計,我大大提高了自己的自主學習能力,俗話說“師傅領進門,修行在個人”,要完成如此復雜的課程設計,僅靠老師上課教授的知識是遠遠不夠的,需要我們自己去多加學習。在學習中,還應學會提問的方法,遇文圖時不要慌張,要仔細往根源去找問題,不要一有問題就尋求老師同學幫忙,要有自主解決問題的能力。
