-
前言
我这次写的这个扫雷程序是java面向对象编程的结课作业,说实话一开始我没想到我会遇到这么多问题,也没想到这么一个小程序会让我有这么多的收获,从简陋的Demo1,到稍微完善的Demo2,再到现在有点样子的Demo3,感受颇多,在这里我想稍微花一点时间总结一下我的思路,和我在实际编写代码时遇到的问题。 -
整体思路
(有空再写) -
遇到的困难
(1)>遇到困难的地方主要是广搜那段算法,具体思路很简单,就是不停的搜索上下左右四个方向的方格,遇到(next)0的或是(now)0的并且(next)为除0以外数字的就把他压入队列中具体判断条件如下
if(gameMap4[next.x][next.y].equals("0")||(gameMap4[next.x][next.y].matches("[0-9]{1}")&&gameMap4[now.x])
前面都没问题,最坑爹(主要还是自己基础不牢)的地方就是Linklist的add()方法会覆盖了前面的所有元素,原因主要是如果我这样写代码
next.x = now.x+dir[i][0];next.y = now.y+dir[i][1];
那么至始至终我只有一个next对象,我每次add进队列的对象都是同一个next(都是相同地址),所以for循环里我next的属性值改变,之前所有的值都会被覆盖(同一个对象,同一个地址)所以正确代码应该这么写
next = new Grid(now.x+dir[i][0],now.y+dir[i][1]);
(每次for循环都创建y一个新的对象,新的地址,值就不会覆盖)
eg.如果觉得我解释的不够清楚可以参考https://bbs.youkuaiyun.com/topics/390774337(2)>广搜算法单独列出来:
public void bfsMap(int x,int y) { final int[][]dir = {{1,0},{0,1},{-1,0},{0,-1}}; class Grid{ int x; int y; public Grid(int x,int y) { this.x = x; this.y = y; } } Queue<Grid> q = new LinkedList<Grid>(); Grid now = new Grid(x,y); Grid next; vis[now.x][now.y] = 1;//点击过的点置1 q.add(now); while(!q.isEmpty()) { now = q.poll();//现在位置 System.out.println("nowx: "+now.x+" nowy "+now.y);//调试用 for(int i=0;i<4;i++) { //next.x = now.x+dir[i][0]; //next.y = now.y+dir[i][1]; next = new Grid(now.x+dir[i][0],now.y+dir[i][1]);//重点一定要注意!!!!! if(next.x>=0&&next.x<cols&&next.y>=0&&next.y<rows&&vis[next.x][next.y]!=1) { if(gameMap4[next.x][next.y].equals("0")||(gameMap4[next.x][next.y].matches("[0-9]{1}")&&gameMap4[now.x][now.y].equals("0"))) {//一定要注意了这里一定要用equals比较,用==比较的是地址(对象) //bfs队列 buttons[next.x][next.y].setBackground(Color.green); buttons[next.x][next.y].setText(gameMap4[next.x][next.y]); buttons[next.x][next.y].setEnabled(false); vis[next.x][next.y] = 1; System.out.println("map "+gameMap4[next.x][next.y]+" x: "+next.x+" y: "+next.y);//调试用 q.add(next); //调试用 } } } } }
}
```
package boom;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
import java.util.LinkedList;
import java.util.Queue;
class Map4 extends JPanel implements ActionListener,MouseListener{//创建地图(加按钮)
private static final long serialVersionUID = 1L;
private int rows;
private int cols;
private int boom;
private String[][] gameMap4;
int[][] vis;//用来判断那些点已经访问过
JFrame frame;
Font font;
JButton [][] buttons;
JTextArea text;
public Map4(int rows,int cols,int boom) {
this.rows = rows;
this.cols = cols;
this.boom = boom;
font = new Font("宋体",Font.BOLD,22);
UIManager.put("Button.font", font);
init();
//System.out.println(buttons.length);
}
private void init() {
buttons = new JButton[rows][cols];
gameMap4 = new String[rows][cols];
vis = new int[rows][cols];
text = new JTextArea();
frame = new JFrame();
addButtons();
addBoom();
mkMap();
}
public void addButtons(){
this.setLayout(new GridLayout(rows,cols));
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
JButton button = new JButton();
button.setBackground(Color.gray);
button.setOpaque(true);//设置button不透明
button.addMouseListener(this);
button.addActionListener(this);
buttons[i][j]=button;//将button对象赋给buttons数组,形成映射关系,以此来判断点击的是哪个按钮
this.add(button);
}
}
}
public void addBoom() {
Random rand=new Random();
int randRow,randCol;
for(int i=0;i<boom;i++){
randRow=rand.nextInt(rows);
randCol=rand.nextInt(cols);
if(gameMap4[randRow][randCol]== "X"){//如果布雷的地方已经有雷了那么i--
i--;
}
else{
gameMap4[randRow][randCol]="X";
//buttons[randRow][randCol].setText("*");
}
}
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
//有问题
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
if(e.getSource().equals(buttons[i][j])) {//重要!!!判断是哪个按钮点击
if(e.getButton()!=MouseEvent.BUTTON1){
buttons[i][j].setText("?");
buttons[i][j].setBackground(Color.WHITE);
}
}
}
}
}
public void actionPerformed(ActionEvent e) {
//buttons[randRow][randCol].setText("X");
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
if(e.getSource().equals(buttons[i][j])) {//重要!!!判断是哪个按钮点击
//System.out.println(i+" "+j);
if(gameMap4[i][j]=="X") {
buttons[i][j].setText("X");
buttons[i][j].setBackground(Color.red);
lose();
}
else {
buttons[i][j].setBackground(Color.green);
//gameMap4[i][j]="*";
//calcMap4(i,j);
buttons[i][j].setText(gameMap4[i][j]);
buttons[i][j].setEnabled(false);
bfsMap(i,j);
win();
}
}
}
}
}
public void lose() {
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
buttons[i][j].setEnabled(false);
}
}
text.setText("You are Dead");
}
public void win() {
int count = 0;
JDialog log = new JDialog(frame,"Congratulations!");
JTextArea area = new JTextArea("恭喜通关!");
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
if(vis[i][j]==1) {
count++;
}
}
}
if(count==rows*cols-boom)
{
System.out.println("count"+count);
text.setText("You are win");
area.setBackground(Color.RED);
log.add(area,"Center");
log.setBounds(320,320,100,100);
log.setVisible(true);
}
}
public void bfsMap(int x,int y) {
final int[][]dir = {{1,0},{0,1},{-1,0},{0,-1}};
class Grid{
int x;
int y;
public Grid(int x,int y) {
this.x = x;
this.y = y;
}
}
Queue<Grid> q = new LinkedList<Grid>();
Grid now = new Grid(x,y);
Grid next;
vis[now.x][now.y] = 1;//点击过的点置1
q.add(now);
while(!q.isEmpty()) {
/*
Iterator<Grid> iter = q.iterator();//调试用遍历
while(iter.hasNext()) {
Grid show = iter.next();
System.out.print("queue :"+"x "+show.x+" "+"y "+show.y);
}System.out.println();
*/
now = q.poll();//现在位置
System.out.println("nowx: "+now.x+" nowy "+now.y);//调试用
for(int i=0;i<4;i++) {
//next.x = now.x+dir[i][0];
//next.y = now.y+dir[i][1];//下一跳位置
next = new Grid(now.x+dir[i][0],now.y+dir[i][1]);//重点一定要注意!!!!!
if(next.x>=0&&next.x<cols&&next.y>=0&&next.y<rows&&vis[next.x][next.y]!=1) {
//System.out.println("map "+gameMap4[next.x][next.y]+" x: "+next.x+"y: "+next.y);//调试用
//System.out.println("bool "+gameMap4[next.x][next.y].equals("0"));//调试用
if(gameMap4[next.x][next.y].equals("0")||(gameMap4[next.x][next.y].matches("[0-9]{1}")&&gameMap4[now.x][now.y].equals("0"))) {//一定要注意了这里一定要用equals比较,用==比较的是地址(对象)
//bfs队列
//System.out.println("flag");//调试用
buttons[next.x][next.y].setBackground(Color.green);
buttons[next.x][next.y].setText(gameMap4[next.x][next.y]);
buttons[next.x][next.y].setEnabled(false);
vis[next.x][next.y] = 1;
System.out.println("map "+gameMap4[next.x][next.y]+" x: "+next.x+" y: "+next.y);//调试用
q.add(next);
//调试用
for(int r=0;r<rows;r++) {
for(int c=0;c<cols;c++) {
System.out.print(vis[r][c]);
}System.out.println();
}
}
}
}
}
}
public void mkMap() {
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
if(gameMap4[i][j]!="X") {
calcMap4(i,j);
}
}
}
//显示gameMap4方便调试
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
System.out.print(gameMap4[i][j]);
}System.out.println();
}
}
public void calcMap4(int x,int y) {
int count = 0;
class Grid{
int x;
int y;
public Grid(int x,int y) {
this.x = x;
this.y = y;
}
}
Grid now = new Grid(x,y);
Grid next;
final int[][]dir = {{1,0},{0,1},{-1,0},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}};
for(int i=0;i<8;i++) {
next = new Grid(now.x+dir[i][0],now.y+dir[i][1]);
if(next.x>=0&&next.x<cols&&next.y>=0&&next.y<rows) {
if(gameMap4[next.x][next.y]=="X") {
count++;
}
}
}
//System.out.println(count);
gameMap4[x][y] = String.valueOf(count);
//buttons[x][y].setText(String.valueOf(count));
}
}
class Diff3 extends JComboBox<String> implements ItemListener{
/**
*
*/
private static final long serialVersionUID = 1L;
String[] level = {"Easy","Normal","Difficult"};
private int rows;
private int cols;
private int boom;
GameFrame4 mf;
Boolean flag = true;
public Diff3(GameFrame4 f){
mf = f;
addItem("Choose the level");
for(String i:level) {
addItem(i);
}
this.addItemListener(this);
}
@Override
public void itemStateChanged(ItemEvent e) {
// TODO Auto-generated method stub
String selectedLevel = this.getSelectedItem().toString();
//selectedLevel = selectedLevel.substring(0, selectedLevel.indexOf("\n"));//不知道为啥会出现两个level所以在这里去掉一个
setLevel(selectedLevel);
System.out.println(rows+" "+cols);
//System.out.println(selectedLevel.substring(0,5));
}
public void setLevel(String level) {
//System.out.println(level);
if(level=="Easy") {
this.rows = 10;
this.cols = 10;
this.boom = 20;
}
else if(level=="Normal") {
this.rows = 15;
this.cols = 15;
this.boom = 30;
}
else if(level=="Difficult") {
this.rows = 20;
this.cols = 20;
this.boom = 100;
}
if(flag) {
GameFrame4 frame = new GameFrame4(cols,rows,boom);
flag = false;
}
System.out.print("flag");//为什么会运行两次???????
mf.dispose();
}
}
class MyMenuBar extends JMenuBar implements ActionListener//获取帮助(游戏规则,关于扫雷(开发者信息,退出等菜单(退出)
{
/**
*
*/
private static final long serialVersionUID = 1L;
JMenu help;
JMenu about;
JMenu exit;
JMenuItem rules;
JMenuItem ex;
JMenuItem aboutus;
public MyMenuBar() {
help = new JMenu("获取帮助");
about = new JMenu("关于扫雷");
exit = new JMenu("退出");
ex = new JMenuItem("exit");
aboutus = new JMenuItem("About us");
rules = new JMenuItem("rules");
ex.addActionListener(this);
rules.addActionListener(this);
aboutus.addActionListener(this);
exit.add(ex);
help.add(rules);
about.add(aboutus);
add(help);
add(about);
add(exit);
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getSource()==ex) {
System.exit(0);
}
else if(e.getSource()==rules) {
String ru = new String("当你随机点开一个方格的时候(如果上来就是炸弹\n,就只能重新开局咯)"
+ "会看到一个数字,那么扫雷\n的规律就是围绕着一个数字的九宫格里的炸弹数就\n是这个数字,"
+ "每个数字都会有自己的九宫格,这样\n结合起来分析就可以知道一个小白块是炸弹还是安\n全区域了");
JDialog r = new JDialog();
JTextArea label = new JTextArea(ru);
label.setLayout(new FlowLayout());
r.add(label);
r.setTitle("游戏规则");
r.setBounds(320,320,320,320);
r.setVisible(true);
}
else {
String ru = new String("作者:KolaFish\n制作日期:2018.12.29\n本扫雷游戏仅用作学习交流用途");
JDialog r = new JDialog();
JTextArea label = new JTextArea(ru);
label.setLayout(new FlowLayout());
r.add(label);
r.setTitle("关于我们");
r.setBounds(320,320,320,320);
r.setVisible(true);
}
}
}
class GameFrame4 extends JFrame implements ActionListener{//框架,有面板(地图),文本框,重置按钮,combobox
private static final long serialVersionUID = 1L;
private int[][] clear;
private int rows,cols;
Map4 m;
JButton reset;
JTextArea frametext;
JPanel pane;
Diff3 level;
MyMenuBar bar;
public GameFrame4(int rows,int cols,int boom) {
this.rows = rows;
this.cols = cols;
pane = new JPanel();
level = new Diff3(this);
bar = new MyMenuBar();
//System.out.println(rows+" "+cols+" "+boom);
m = new Map4(rows,cols,boom);
m.frame = this;
frametext = m.text;
init();
}
private void init() {
//JFrame.setDefaultLookAndFeelDecorated(true);
pane.setLayout(new FlowLayout());
pane.add(bar);
pane.add(level);
add(m);
addResetButton();
add(frametext,"South");
add(pane,"North");
setBounds(100,50,1000,1000);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void addResetButton(){
reset = new JButton("Reset");
reset.setBackground(Color.yellow);
reset.setOpaque(true);
reset.addActionListener(this);
pane.add(reset);
}
public void actionPerformed(ActionEvent e) {
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
clear = new int[rows][cols];
m.buttons[i][j].setEnabled(true);
m.buttons[i][j].setBackground(Color.gray);
m.buttons[i][j].setText(" ");
m.vis = clear;//清除vis
frametext.setText("");
}
}
}
}
public class BoomDemo4{
public static void main(String[] args) {
GameFrame4 frame = new GameFrame4(10,10,10);
}
}