这学期开始学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();
}
}
需要整个工程的请说明