栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JAVA扫雷(无JButton)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JAVA扫雷(无JButton)

JAVA扫雷 无JButton
  • 介绍
  • 基本思路
  • 主要代码
  • 代码地址


介绍
  1. 可以双击排雷;
  2. 无JButton,纯绘制;
  3. 游戏结束后显示历次时间排行榜;
  4. 第一步至少至少可以翻开九个格子,利于开局;
  5. 游戏难度易于调节,只需要改格子宽度和格子雷数之比即可,格子数量会根据屏幕高宽和格子宽度自动计算;
基本思路
  1. 雷分布情况、每个格子翻开情况以及被标记情况均用二维数组存储;
  2. 系统在第一次翻开格子之后才开始布雷,要保证第一个格子及其周围八个格子不是雷;
  3. 每次鼠标事件,根据点击的相对位置来判断点中格子的坐标;
  4. 每次重绘,对于每个格子,根据其不同的状态在相应位置绘制不同的内容;
  5. 在最外圈单独设置一层非雷格子,在计算每个格子的周围雷数时不用考虑特殊情况,在绘制面板时,只需要绘制红色部分的格子即可,如下图;
主要代码

Panel.java

public class Panel extends JPanel implements KeyListener{
	private BufferedImage image;
	private Graphics2D g2d;// 画笔
	public double timer = 0;// 计时器
	// 雷分布,周围雷数,翻开状态,被玩家标记状态
	private int[][] mine,mineAround,isOpen,marked;
	private static final int cellWidth = 90;// 格子宽度
	private static final int difficulty = 6;// 难度系数(格子数与雷数比值)
	private static final int barHeight = 50;// 顶栏高度
	private static int xNum,yNum;// 横纵向格子数
	private static int mineNum;// 雷数
	private int leftMine = mineNum;// 剩余雷数
	private int clickX,clickY,backHeight,backWidth;// 当前鼠标点中的格子坐标,面板高宽
	public  boolean start,finish,win;// 是否开始,是否结束,结束后是否胜利
	private Image mineImg;// 雷图片
	
	public Panel(){
		// 初始化面板高宽,高度减去任务栏高度
		backHeight = Frame.screenHeight-100;
		backWidth = Frame.screenWidth;
		// 根据格子宽度计算横纵格子数
		xNum = backWidth/cellWidth;
		yNum = (backHeight-barHeight)/cellWidth;
		// 根据难度系数计算雷数
		mineNum = xNum*yNum/difficulty;
		// 雷图片
		mineImg = new ImageIcon("resource/mine.png").getImage();
		start = false;
		finish = false;
		win = false;
		mine = new int[xNum+2][yNum+2];
		mineAround = new int[xNum+2][yNum+2];
		isOpen = new int [xNum+2][yNum+2];
		marked = new int [xNum+2][yNum+2];
		// 初始化画布
		image = new BufferedImage(backWidth, backHeight, BufferedImage.TYPE_INT_BGR);
		// 初始化画笔
		g2d = image.createGraphics();
		// 初始化背景色
		g2d.setColor(new Color(200,200,200));
		g2d.fillRect(0, 0, backWidth, backHeight);
		// 初始化线条
		g2d.setColor(new Color(55,55,55));
		for (int i = 0; i <= yNum; i++) {
			g2d.drawLine(0, cellWidth*i+barHeight, xNum*cellWidth, cellWidth*i+barHeight);
		}
		for (int i = 0; i <= xNum; i++) {
			g2d.drawLine(cellWidth*i, barHeight, cellWidth*i, yNum*cellWidth+barHeight);
		}
		// 面板监听
		this.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				if(e.getClickCount()==2&&e.getButton()==e.BUTTON1) {
					DoblueClick(e);// 双击
				}else if(e.getButton()==e.BUTTON1){
					LeftClick(e);// 左键
				}else if(e.getButton()==e.BUTTON3){
					RightClick(e);// 右键
				}
			}
		});
		Score.init();// 读取分数
		new FreshThread(this).start();// 线程启动
	}
	
	protected void paintComponent(Graphics g){
		super.paintComponent(g);
		g.drawImage(image, 0, 0, this);//绘制画底
		// 绘制时间和剩余雷数
		g.setColor(Color.red);
		g.setFont(new Font(null,Font.BOLD,28));
		g.drawString(String.valueOf("时间:"+timer/1000+"s"), backWidth-300,40);
		g.drawString(String.valueOf("剩余雷数:"+leftMine+"个"), backWidth-600,40);
		
		g.setFont(new Font(null,Font.BOLD,cellWidth));
		for(int i = 1; i <= xNum; i++){
			for(int j = 1; j <= yNum; j++){
				// 当前格子起始绘图坐标
				int paintX = (i-1)*cellWidth+1;
				int paintY = (j-1)*cellWidth+barHeight+1;
				// 如果格子被翻开,则在对应位置绘制白色背景
				if(isOpen[i][j]==1){
					g.setColor(Color.white);
					g.fillRect(paintX, paintY, cellWidth-2,cellWidth-2);
					// 如果翻开的格子周围无雷,则不绘制周围雷数
					if(mineAround[i][j]!=0){
						if(mineAround[i][j]==1){
							g.setColor(Color.pink);
						}else if(mineAround[i][j]==2){
							g.setColor(Color.orange);
						}else if(mineAround[i][j]==3){
							g.setColor(Color.red);
						}else if(mineAround[i][j]==4){
							g.setColor(new Color(102,0,0));
						}else{// mineAround[i][j]==5,6,7,8
							g.setColor(Color.black);
						}
						g.drawString(String.valueOf(mineAround[i][j]), (i-1)*cellWidth+10, j*cellWidth+barHeight-7);
					}
				}
				// 如果格子被玩家标记,则绘制雷图
				if(marked[i][j]==1){
					g.drawImage(mineImg, paintX, paintY, cellWidth-1, cellWidth-1, null);
				}
			}
		}
		// 判断游戏是否胜利
		if(judgeWin()){
			//保存当前时间
			Score.addNewScore(timer/1000);
			win = true;
			finish = true;
		}
		// 如果游戏开始则计时器累加
		timer += start?FreshThread.FREASH:0;
	}
	
	// 获取每次鼠标点击格子的坐标,未点中格子则返回false
	public boolean mouseClickXY(MouseEvent e){
		for(int i = 1; i <= xNum; i++){
			for(int j = 1; j <= yNum; j++){
				if(new Rectangle((i-1)*cellWidth,(j-1)*cellWidth+barHeight,cellWidth,cellWidth).contains(e.getPoint())){
					clickX = i;
					clickY = j;
					return true;
				}
			}
		}
		return false;
	}
	
	public int getRandom(int sample){
		return (int)(Math.random()*sample+1);
	}
	
	// 计算每个格子周围的雷数/被玩家标记的雷数
	public int calNeibor(int i,int j,int arr[][]){
		int val = 0;
		val+=arr[i-1][j];
		val+=arr[i-1][j-1];
		val+=arr[i][j-1];
		val+=arr[i+1][j-1];
		val+=arr[i+1][j];
		val+=arr[i+1][j+1];
		val+=arr[i][j+1];
		val+=arr[i-1][j+1];
		return val;
	}
	
	public void openCell(int x,int y){
		// 不翻开被玩家标记的格子和已经翻开的格子
		if(isOpen[x][y]==0&&marked[x][y]==0){
			// 不翻开边界格子
			if(x!=0&&x!=xNum+1&&y!=0&&y!=yNum+1){
				// 翻开
				isOpen[x][y]=1;
				// 如果被翻开的格子周围无雷,则翻开其周围八个格子,以此类推
				if(mineAround[x][y]==0){
					openCell(x-1,y-1);
					openCell(x-1,y);
					openCell(x-1,y+1);
					openCell(x,y-1);
					openCell(x,y+1);
					openCell(x+1,y-1);
					openCell(x+1,y);
					openCell(x+1,y+1);
				}
			}
		}
	}
	
	// 布雷(第一次翻开之后)
	public void setMine(MouseEvent e){
		// 获取第一个格子的坐标
		if(mouseClickXY(e)){
			int index = 0;
			// 循环随机生成雷
			while(index
				int tempX = getRandom(xNum);
				int tempY = getRandom(yNum);
				boolean flag = true;
				// 判断生成的雷坐标是否位于第一个格子的周围,以及此位置是否已经有雷
				if(judgeMineSelect(tempX,tempY)||(mine[tempX][tempY]==1)){
					flag = false;
				}
				if(flag){
					mine[tempX][tempY]=1;
					index = index+1;
				}
			}
			// 计算非雷格子的周围雷数
			for(int i = 1; i <= xNum; i++){
				for(int j = 1; j <= yNum; j++){
					if(mine[i][j]==0){
						mineAround[i][j]=calNeibor(i,j,mine);
					}
				}
			}
			// 输出雷分布情况
			for(int j = 0 ;j
				for(int i = 0; i 
					System.out.print(mine[i][j]+" ");
				}
				System.out.println();
			}
			// 翻开第一个格子
			openCell(clickX,clickY);
			// 游戏开始
			start = true;
		}
	}
	
	// 布雷时,判断随机生成雷的位置是否为第一个格子或在第一个格子的周围
	public boolean judgeMineSelect(int tempX,int tempY){
		if(tempX==clickX-1||tempX==clickX||tempX==clickX+1){
			if(tempY==clickY-1||tempY==clickY||tempY==clickY+1){
				return true;
			}
		}
		return false;
	}
		
	public void LeftClick(MouseEvent e){
		if(mouseClickXY(e)){
			if(start){
				// 被玩家标记的格子无法点击
				if(isOpen[clickX][clickY]==0&&marked[clickX][clickY]==0){
					if(mine[clickX][clickY]==1){
						finish = true;
					}else{
						openCell(clickX,clickY);
					}		
				}
			}else{
				setMine(e);
			}
		}
	}
	public void RightClick(MouseEvent e){
		// 游戏未开始时不可标记
		if(mouseClickXY(e)&&start&&isOpen[clickX][clickY]==0){
			if(marked[clickX][clickY]==0){
				// 如果剩余雷数小于等于0,无法标记
				if(leftMine>0){
					marked[clickX][clickY]=1;
					leftMine-=1;
				}
			}else{
				marked[clickX][clickY]=0;
				leftMine+=1;
			}
		}
	}
	// 双击排雷
	public void DoblueClick(MouseEvent e){
		if(mouseClickXY(e)){
			int i = clickX;
			int j = clickY;
			if(start){
				if(isOpen[i][j]==1){
					// 计算周围被玩家标记的雷数
					int markedMine = calNeibor(i,j,marked);
					// 如果周围标记数大于周围雷数,游戏结束
					if(markedMine>mineAround[i][j]){
						finish = true;
					}else if(markedMine==mineAround[i][j]){
						if(marked[i-1][j-1]==mine[i-1][j-1]&&
							marked[i-1][j]==mine[i-1][j]&&
							marked[i-1][j+1]==mine[i-1][j+1]&&
							marked[i][j-1]==mine[i][j-1]&&
							marked[i][j+1]==mine[i][j+1]&&
							marked[i+1][j-1]==mine[i+1][j-1]&&
							marked[i+1][j]==mine[i+1][j]&&
							marked[i+1][j+1]==mine[i+1][j+1]
													){
							if(mine[i-1][j-1]==0)openCell(i-1,j-1);
							if(mine[i-1][j]==0)openCell(i-1,j);
							if(mine[i-1][j+1]==0)openCell(i-1,j+1);
							if(mine[i][j-1]==0)openCell(i,j-1);
							if(mine[i][j+1]==0)openCell(i,j+1);
							if(mine[i+1][j-1]==0)openCell(i+1,j-1);
							if(mine[i+1][j]==0)openCell(i+1,j);
							if(mine[i+1][j+1]==0)openCell(i+1,j+1);
						}else{
							finish = true;
						}
					}
				}
			}else{
				setMine(e);
			}
		}
	}
	// 判断是否胜利
	public boolean judgeWin(){
		// 如果雷未被玩家标记完,则未胜利
		if(leftMine>0){
			return false;
		}
		for(int i = 1; i <=xNum; i++){
			for(int j = 1; j <= yNum; j++){
				// 如果存在没被翻开也没被标记的格子,则未胜利
				if(isOpen[i][j]==0&&marked[i][j]==0){
					return false;
				}
			}
		}
		return true;
	}
	@Override
	public void keyTyped(KeyEvent e) {
	}
	@Override
	public void keyPressed(KeyEvent e) {
	}
	@Override
	public void keyReleased(KeyEvent e) {
	}
}
代码地址

gitee:https://gitee.com/meng-chenxu/java-minesweeping.

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/970271.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号