掃雷:我的Java之路--第一個小作品
這學期開始學Java,也許是為了工作,也許是對程式設計有了一點點興趣,所以學的稍微有一點用心。而老師也只講了一些基本的語法和操作,其他的都是自己一步步摸索來的,所以想寫一點點東西來讓以後的自己看看。
廢話不多說,這學期的java實踐課作業是做一個掃雷遊戲。下面逐一講解。
設計思路—-
(一)
首先設計一個二維陣列,用於存放雷和周圍八個格子雷的個數,再定義兩個一維陣列分別存放雷的X和Y座標,佈雷,記錄周圍八個格子雷的個數。
我使用的是eclipse
新建Java專案Boom
新建package:boom
新建Class:Block
程式碼:
package boom;
/*
* 設計一個二維陣列,用於存放雷和周圍八個格子雷的個數,
* 再定義兩個一維陣列分別存放雷的X和Y座標,
* 佈雷,記錄周圍八個格子雷的個數。
*/
import java.util.Random;
public class Block {
protected int[][] Array;//用於存放雷和周圍雷的個數
protected int[] ArrayTestX;//用於存放雷的X座標
protected int[] ArrayTestY;//用於存放雷的Y座標
protected static int m;//用於記錄已產生雷的個數,並對即將要產生的雷做限制條件
protected static int s;//用於記錄要產生雷的個數
private static int t = 0;//記錄周圍雷的個數
public Block(int x,int y){
m = 0;
s = 0;//此處必須置零
Array = new int[x][y];
switch(Array.length)
{
case 9:
ArrayTestX = new int[10];
ArrayTestY = new int[10];
s = 10 ;
break;
case 16:
ArrayTestX = new int[40];
ArrayTestY = new int[40];
s = 40;
break;
case 30:
ArrayTestX = new int[99];
ArrayTestY = new int[99];
s = 99;
break;
}//選則遊戲不同難度所對應的不同陣列大小
for(int i = 0;i < Array.length;i++){
for(int j = 0;j < Array.length;j++){
Array[i][j] = 0;
}
}//產生平面陣列
Random a = new Random();
for(;m < s;m++){
Array[(ArrayTestX[m] = a.nextInt(Array.length))][(ArrayTestY[m] = a.nextInt(Array.length))] = -1;//隨機產生雷點,並賦值-1
if((Judge() && m > 0)){
m--;
}//判斷雷點是否重複
}//產生雷點
for (int i = 0; i < Array.length; i++) {
for (int j = 0; j < Array.length; j++) {
t = 0;
if (Array[i][j] == -1) {
} else {
if ((i - 1 >= 0) && (j - 1 >= 0)) {
if (Array[i - 1][j - 1] == -1) {
t++;
}
}
if (i - 1 >= 0) {
if (Array[i - 1][j] == -1) {
t++;
}
}
if ((i - 1 >= 0) && (j + 1 < Array.length)) {
if (Array[i - 1][j + 1] == -1) {
t++;
}
}
if (j - 1 >= 0) {
if (Array[i][j - 1] == -1) {
t++;
}
}
if (j + 1 < Array.length) {
if (Array[i][j + 1] == -1) {
t++;
}
}
if ((i + 1 < Array.length) && (j - 1 >= 0)) {
if (Array[i + 1][j - 1] == -1) {
t++;
}
}
if (i + 1 < Array.length) {
if (Array[i + 1][j] == -1) {
t++;
}
}
if ((i + 1 < Array.length) && (j + 1 < Array.length)) {
if (Array[i + 1][j + 1] == -1) {
t++;
}
}
Array[i][j] = t;
}
}
}//遍歷周圍八個格子,記錄雷的個數並賦值給當前位置
}
private boolean Judge(){
for(int i = 0;i < m;i++){
for(int j = i;j < m;j++){
if((ArrayTestX[j] == ArrayTestX[j+1]) && (ArrayTestY[j] == ArrayTestY[j+1])){
return true;
}
}
}
return false;
}//此方法用於判斷已產生的雷的雷點是否重複。
public void fun(){
for(int i = 0;i < Array.length;i++){
for(int j = 0;j < Array.length;j++){
System.out.print(Array[i][j]);
}
}
}//此方法打印出平面陣列
}
(二)
建立一個頂層容器JFrame,上面新增一個JPanel——JpMain,使用BorderLayout佈局North為重新開始按鈕,Center為雷區(我在這裡新建了一個物件,把雷區進行了封裝),South為選擇難度區域(任然使用一個JPanel,上面新增JRadioButton單選器),廢話不多說,下面給出我的頂層容器程式碼:
1.在Boom專案下新建Class,命名為SuperJpanel。
程式碼:
package boom;
/*
* 新增頂層容器
* 設定雷區
* 設定選擇難度按鈕,及其觸發事件。
* 設定根據難度不同調整容器大小。
*/
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.net.URL;
import java.util.LinkedList;
import java.util.Queue;
import boom.Block;
import javax.swing.*;
public class SuperJpanel extends JFrame{
private static int X = 9;
private static int Y = 9;//用與對選擇難度時做中間變數
private JPanel JpSouth = new JPanel();
private Block block = new Block(X,Y);
private CenterJpanel JpCenter;//初始化一個CenterJpanel物件,為雷區
private JPanel JpMain ;//在頂層容器中新增一個JPanel
private JButton JbRestart;//新建一個重新開始按鈕
private JRadioButton[] jrb = {
new JRadioButton("初級",true),new JRadioButton("中級"),new JRadioButton("高階")
};//新建選擇按鈕
private ButtonGroup bg = new ButtonGroup();
public SuperJpanel(){
String UI = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
try {
UIManager.setLookAndFeel(UI);
} catch(Exception e){
e.printStackTrace();
}//設定整個面板顯示格式
JbRestart = new JButton("重新開始") ;
JpCenter = new CenterJpanel(block.Array, block.ArrayTestX, block.ArrayTestY);
JpMain = new JPanel();
JpMain.setLayout(new BorderLayout());//設定佈局方式為BorderLayout佈局
JbRestart.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO 自動生成的方法存根
if (e.getSource() == JbRestart) {
JpCenter.clear(block.Array);
JpMain.remove(JpCenter);//先移除Center區域,
block = new Block(block.Array.length,block.Array.length);//新生成一個不同的陣列作為雷區
JpCenter = new CenterJpanel(block.Array, block.ArrayTestX, block.ArrayTestY);//將雷區新增到Center中
JpCenter.first(block.Array);//讓雷區顯示第一張卡片
JpMain.add(JpCenter,BorderLayout.CENTER);//將Center新增到定層容器中
JpMain.revalidate();//重新整理整個面板
}
}
});//重新開始按鈕觸發事件處理方式
for(int i = 0;i < 3;i ++){
JpSouth.add(jrb[i]);
bg.add(jrb[i]);
jrb[i].addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
// TODO 自動生成的方法存根
if(e.getSource() == jrb[0]){//初級按鈕
X = 9;
Y = 9;//設定對應雷區的X,Y軸的長度
JpCenter.clear(block.Array);
JpMain.remove(JpCenter);//任然移除Center
block = new Block(X,Y);//新建一個初級的雷區
JpCenter = new CenterJpanel(block.Array, block.ArrayTestX, block.ArrayTestY);
JpCenter.first(block.Array);//翻到第一塊卡片
choose(X);//呼叫選擇方法來設定整個面板的大小
JpMain.add(JpCenter,BorderLayout.CENTER);
JpMain.revalidate();
}
if(e.getSource() == jrb[1]){
X = 16;
Y = 16;
JpCenter.clear(block.Array);
JpMain.remove(JpCenter);
block = new Block(X,Y);
JpCenter = new CenterJpanel(block.Array, block.ArrayTestX, block.ArrayTestY);
JpCenter.first(block.Array);
choose(X);
JpMain.add(JpCenter,BorderLayout.CENTER);
JpMain.revalidate();
}
if(e.getSource() == jrb[2]){
X = 30;
Y = 30;
JpCenter.clear(block.Array);
JpMain.remove(JpCenter);
block = new Block(X,Y);
JpCenter = new CenterJpanel(block.Array, block.ArrayTestX, block.ArrayTestY);
JpCenter.first(block.Array);
choose(X);
JpMain.add(JpCenter,BorderLayout.CENTER);
JpMain.revalidate();
}
}
});//難度選擇按鈕觸發事件
}
JpMain.add(JpCenter,BorderLayout.CENTER);
JpMain.add(JbRestart,BorderLayout.NORTH);
JpMain.add(JpSouth,BorderLayout.SOUTH);//將三個區域新增進主面板中
this.add(JpMain);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//設定關閉結束
this.setTitle("掃雷");
this.setBounds(100, 100, 200, 300);//設定初始時頂層容器大小
this.setResizable(false);//設定使用者不可自己調整大小
this.setVisible(true);
}
public void choose(int x){
switch(x){
case 9:
this.setBounds(100, 100, 200, 300);
break;
case 16:
this.setBounds(100, 100, 300, 380);
break;
case 30:
this.setBounds(100, 100, 500, 580);
break;
}
}//根據不同難度設定不同頂層容器大小
}
(三)
建立一個物件——雷區物件的初始化,整個雷區使用一個JPanel包含,這個JPanel使用GridLayout佈局方式,雷區有多少個格子就新增多少個JPanel,使用卡片佈局方式,每個JPanel中有兩個卡片,第一個卡片為按鈕,第二張卡片為JLabel——用於顯示雷,周圍雷的個數。
新建Class命名為CenterJpanel
注意:我在這裡遇到的問題最多,首先我的想法是將遍歷周圍八個陣列封裝成方法,再遞迴呼叫,最後發現因為遞迴層次太多極其容易卡死,所以不得不另想辦法,最後想到了使用佇列,把每個0的點放入佇列,直到判斷到不為0的地方
程式碼:
package boom;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.*;
public class CenterJpanel extends JPanel{
private JPanel[][] jp;//有多少格子就對應產生多少JPanel
private JLabel[][] jl;//設定第二層卡片顯示的數字,雷
private JButton[][] jb;//設定第一層卡片顯示的按鈕
public CenterJpanel(int[][] Array,int[] ArrayTestX,int [] ArrayTestY){//根據傳入陣列的不同產生不同的雷區
this.setLayout(new GridLayout(Array.length,Array.length,0,0));//設定雷區佈局為網格佈局
jp = new JPanel[Array.length][Array.length];
jl = new JLabel[Array.length][Array.length];
jb = new JButton[Array.length][Array.length];
for(int i = 0;i < Array.length;i++){
for(int j = 0;j < Array.length;j++){
jp[i][j] = new JPanel();
jp[i][j].setLayout(new CardLayout());
jb[i][j] = new JButton();
jb[i][j].setBorder(BorderFactory.createRaisedBevelBorder());
jp[i][j].add(jb[i][j],"card1");//新增按鈕到第一層卡片
jb[i][j].addActionListener(new ActionListener() {//設定按鈕觸發事件
@Override
public void actionPerformed(ActionEvent e) {
// TODO 自動生成的方法存根
for (int i = 0; i < Array.length; i++) {
for (int j = 0; j < Array[i].length; j++) {
if (e.getSource() == jb[i][j]) {
if (Array[i][j] == -1) {
for (int x = 0; x < ArrayTestX.length; x++) {
CardLayout cl = (CardLayout) jp[ArrayTestX[x]][ArrayTestY[x]]
.getLayout();
cl.show(jp[ArrayTestX[x]][ArrayTestY[x]],
"card2");
}//若為雷,則遊戲結束,顯示所有雷(因為A讓raytestX,Y分別對應記錄了雷的位置,直接遍歷就OK)
for (int m = 0; m < Array.length; m++) {
for (int n = 0; n < Array[m].length; n++) {
jb[m][n].setEnabled(false);
}
}//並且使整個雷區按鈕不可再觸發
} else if (Array[i][j] == 0) {//若為0,則顯示周圍八個格子除雷以外的每個格子的的周圍八個雷的個數,若有為0的再遍歷
CardLayout cl = (CardLayout) jp[i][j].getLayout();
cl.show(jp[i][j], "card2");
Ergodic(Array,i, j);//遍歷周圍八個格子。
} else {
CardLayout cl = (CardLayout) jp[i][j].getLayout();
cl.show(jp[i][j], "card2");
}//若不為0也不為雷則顯示周圍雷的個數
}
}
}
}
});
jl[i][j] = new JLabel();//設定第二層卡片
jl[i][j].setBorder(BorderFactory.createLoweredBevelBorder());//設定JLabel顯示為凹下去,便於美觀
jl[i][j].setFont(new java.awt.Font("微軟雅黑", 1, 14));//設定字型
if(Array[i][j] == -1){
URL imgURL = SuperJpanel.class.getResource("b.png");
ImageIcon icon = new ImageIcon(imgURL);
jl[i][j].setIcon(icon); ;
}//若為雷則顯示地雷圖片
else if(Array[i][j] == 0){
jl[i][j].setText("");
}//若為0則什麼也不顯示
else{
if(Array[i][j] == 1){
jl[i][j].setForeground(Color.BLUE);
}
if(Array[i][j] == 2){
jl[i][j].setForeground(Color.GREEN);
}
if(Array[i][j] == 3){
jl[i][j].setForeground(Color.RED);
}
if(Array[i][j] == 4){
jl[i][j].setForeground(Color.cyan);
}
if(Array[i][j] == 5){
jl[i][j].setForeground(Color.yellow);
}
jl[i][j].setText(Integer.toString(Array[i][j]));
}//若為周圍雷的個數則設定每個數字的顏色不同
jp[i][j].add(jl[i][j],"card2");
this.add(jp[i][j]);
}
}
}
public void Ergodic(int[][] Array,int i, int j) {
Queue<Integer> qi = new LinkedList<Integer>();
Queue<Integer> qj = new LinkedList<Integer>();
if ((i - 1 >= 0) && (j - 1 >= 0)) {
if ((Array[i - 1][j - 1] == 0)) {
CardLayout cl = (CardLayout) jp[i - 1][j - 1].getLayout();
cl.show(jp[i - 1][j - 1], "card2");
qi.offer(i - 1);
qj.offer(j - 1);
Array[i - 1][j - 1] = -2;
} else if ((Array[i - 1][j - 1] != -1)) {
CardLayout cl = (CardLayout) jp[i - 1][j - 1].getLayout();
cl.show(jp[i - 1][j - 1], "card2");
}
}
if (i - 1 >= 0) {
if ((Array[i - 1][j] == 0)) {
CardLayout cl = (CardLayout) jp[i - 1][j].getLayout();
cl.show(jp[i - 1][j], "card2");
qi.offer(i - 1);
qj.offer(j);
Array[i - 1][j] = -2;
} else if ((Array[i - 1][j] != -1)) {
CardLayout cl = (CardLayout) jp[i - 1][j].getLayout();
cl.show(jp[i - 1][j], "card2");
}
}
if ((i - 1 >= 0) && (j + 1 < Array.length)) {
if ((Array[i - 1][j + 1] == 0)) {
CardLayout cl = (CardLayout) jp[i - 1][j + 1].getLayout();
cl.show(jp[i - 1][j + 1], "card2");
qi.offer(i - 1);
qj.offer(j + 1);
Array[i - 1][j + 1] = -2;
} else if ((Array[i - 1][j + 1] != -1)) {
CardLayout cl = (CardLayout) jp[i - 1][j + 1].getLayout();
cl.show(jp[i - 1][j + 1], "card2");
}
}
if (j - 1 >= 0) {
if ((Array[i][j - 1] == 0)) {
CardLayout cl = (CardLayout) jp[i][j - 1].getLayout();
cl.show(jp[i][j - 1], "card2");
qi.offer(i);
qj.offer(j - 1);
Array[i][j - 1] = -2;
} else if ((Array[i][j - 1] != -1)) {
CardLayout cl = (CardLayout) jp[i][j - 1].getLayout();
cl.show(jp[i][j - 1], "card2");
}
}
if (j + 1 < Array.length) {
if ((Array[i][j + 1] == 0)) {
CardLayout cl = (CardLayout) jp[i][j + 1].getLayout();
cl.show(jp[i][j + 1], "card2");
qi.offer(i);
qj.offer(j + 1);
Array[i][j + 1] = -2;
} else if ((Array[i][j + 1] != -1)) {
CardLayout cl = (CardLayout) jp[i][j + 1].getLayout();
cl.show(jp[i][j + 1], "card2");
}
}
if ((i + 1 < Array.length) && (j - 1 >= 0)) {
if ((Array[i + 1][j - 1] == 0)) {
CardLayout cl = (CardLayout) jp[i + 1][j - 1].getLayout();
cl.show(jp[i + 1][j - 1], "card2");
qi.offer(i + 1);
qj.offer(j - 1);
Array[i + 1][j - 1] = -2;
} else if ((Array[i + 1][j - 1] != -1)) {
CardLayout cl = (CardLayout) jp[i + 1][j - 1].getLayout();
cl.show(jp[i + 1][j - 1], "card2");
}
}
if (i + 1 < Array.length) {
if ((Array[i + 1][j] == 0)) {
CardLayout cl = (CardLayout) jp[i + 1][j].getLayout();
cl.show(jp[i + 1][j], "card2");
qi.offer(i + 1);
qj.offer(j);
Array[i + 1][j] = -2;
} else if ((Array[i + 1][j] != -1)) {
CardLayout cl = (CardLayout) jp[i + 1][j].getLayout();
cl.show(jp[i + 1][j], "card2");
}
}
if ((i + 1 < Array.length) && (j + 1 < Array.length)) {
if ((Array[i + 1][j + 1] == 0)) {
CardLayout cl = (CardLayout) jp[i + 1][j + 1].getLayout();
cl.show(jp[i + 1][j + 1], "card2");
qi.offer(i + 1);
qj.offer(j + 1);
Array[i + 1][j + 1] = -2;
} else if ((Array[i + 1][j + 1] != -1)) {
CardLayout cl = (CardLayout) jp[i + 1][j + 1].getLayout();
cl.show(jp[i + 1][j + 1], "card2");
}
}//遍歷八個格子,分別判斷每個格子屬性,若為0則顯示,再將此點放入佇列,更改數值(以免進入無線迴圈。)
while ((qi.peek() != null) && (qj.peek() != null)) {
int a = qi.poll();
int b = qj.poll();
if ((a - 1 >= 0) && (b - 1 >= 0)) {
if ((Array[a - 1][b - 1] == 0)) {
CardLayout cl = (CardLayout) jp[a - 1][b - 1].getLayout();
cl.show(jp[a - 1][b - 1], "card2");
qi.offer(a - 1);
qj.offer(b - 1);
Array[a - 1][b - 1] = -2;
} else if ((Array[a - 1][b - 1] != -1)) {
CardLayout cl = (CardLayout) jp[a - 1][b - 1].getLayout();
cl.show(jp[a - 1][b - 1], "card2");
}
}
if (a - 1 >= 0) {
if ((Array[a - 1][b] == 0)) {
CardLayout cl = (CardLayout) jp[a - 1][b].getLayout();
cl.show(jp[a - 1][b], "card2");
qi.offer(a - 1);
qj.offer(b);
Array[a - 1][b] = -2;
} else if ((Array[a - 1][b] != -1)) {
CardLayout cl = (CardLayout) jp[a - 1][b].getLayout();
cl.show(jp[a - 1][b], "card2");
}
}
if ((a - 1 >= 0) && (b + 1 < Array.length)) {
if ((Array[a - 1][b + 1] == 0)) {
CardLayout cl = (CardLayout) jp[a - 1][b + 1].getLayout();
cl.show(jp[a - 1][b + 1], "card2");
qi.offer(a - 1);
qj.offer(b + 1);
Array[a - 1][b + 1] = -2;
} else if ((Array[a - 1][b + 1] != -1)) {
CardLayout cl = (CardLayout) jp[a - 1][b + 1].getLayout();
cl.show(jp[a - 1][b + 1], "card2");
}
}
if (b - 1 >= 0) {
if ((Array[a][b - 1] == 0)) {
CardLayout cl = (CardLayout) jp[a][b - 1].getLayout();
cl.show(jp[a][b - 1], "card2");
qi.offer(a);
qj.offer(b - 1);
Array[a][b - 1] = -2;
} else if ((Array[a][b - 1] != -1)) {
CardLayout cl = (CardLayout) jp[a][b - 1].getLayout();
cl.show(jp[a][b - 1], "card2");
}
}
if (b + 1 < Array.length) {
if ((Array[a][b + 1] == 0)) {
CardLayout cl = (CardLayout) jp[a][b + 1].getLayout();
cl.show(jp[a][b + 1], "card2");
qi.offer(a);
qj.offer(b + 1);
Array[a][b + 1] = -2;
} else if ((Array[a][b + 1] != -1)) {
CardLayout cl = (CardLayout) jp[a][b + 1].getLayout();
cl.show(jp[a][b + 1], "card2");
}
}
if ((a + 1 < Array.length) && (b - 1 >= 0)) {
if ((Array[a + 1][b - 1] == 0)) {
CardLayout cl = (CardLayout) jp[a + 1][b - 1].getLayout();
cl.show(jp[a + 1][b - 1], "card2");
qi.offer(a + 1);
qj.offer(b - 1);
Array[a + 1][b - 1] = -2;
} else if ((Array[a + 1][b - 1] != -1)) {
CardLayout cl = (CardLayout) jp[a + 1][b - 1].getLayout();
cl.show(jp[a + 1][b - 1], "card2");
}
}
if (a + 1 < Array.length) {
if ((Array[a + 1][b] == 0)) {
CardLayout cl = (CardLayout) jp[a + 1][b].getLayout();
cl.show(jp[a + 1][b], "card2");
qi.offer(a + 1);
qj.offer(b);
Array[a + 1][b] = -2;
} else if ((Array[a + 1][b] != -1)) {
CardLayout cl = (CardLayout) jp[a + 1][b].getLayout();
cl.show(jp[a + 1][b], "card2");
}
}
if ((a + 1 < Array.length) && (b + 1 < Array.length)) {
if ((Array[a + 1][b + 1] == 0)) {
CardLayout cl = (CardLayout) jp[a + 1][b + 1].getLayout();
cl.show(jp[a + 1][b + 1], "card2");
qi.offer(a + 1);
qj.offer(b + 1);
Array[a + 1][b + 1] = -2;
} else if ((Array[a + 1][b + 1] != -1)) {
CardLayout cl = (CardLayout) jp[a + 1][b + 1].getLayout();
cl.show(jp[a + 1][b + 1], "card2");
}
}
}
}//佇列中再分別判斷每個點的周圍的八個格子,若仍為0,則再放入佇列。之道遍歷到邊界或者不為0的點
public void clear(int[][] Array){
for(int i = 0;i < Array.length;i ++){
for(int j = 0;j < Array.length;j ++){
jp[i][j].remove(jl[i][j]);
jp[i][j].remove(jb[i][j]);
this.remove(jp[i][j]);
}
}
}//此方法清除每個JPanel上的卡片
public void first(int[][] Array){
for(int i = 0;i < Array.length;i ++){
for(int j = 0;j < Array.length;j ++){
CardLayout cl = (CardLayout) jp[i][j].getLayout();
cl.first(jp[i][j]);
}
}
}//此方法顯示卡片一
}
(四)
新建Class ——BoomMain 勾選產生主函式
主函式,直接初始化一個SuperJpanel物件就是
程式碼:
package boom;
public class BoomMain{
public static void main(String[] args) {
// TODO 自動生成的方法存根
new SuperJpanel();
//Block1 b = new Block1(16,16);
//b.fun();
}
}
需要整個工程的請說明