改朝换代游戏(基于数字游戏2048逻辑代码)

什么2048数字游戏?

《2048》,是一款益智小游戏,这款游戏是由年仅19岁的意大利程序员加布里勒希鲁尼(Gabriele Cirulli)开发出来的,官方版本只能在网页上或通过其移动网站运行。

在传统版本中,有16个格子,初始时会有两个格子上安放了两个数字2,每次可以选择上下左右其中一个方向去滑动,每滑动一次,所有的数字方块都会往滑动的方向靠拢外,系统也会在空白的地方随即出现一个数字方块,相同数字的方块在靠拢、相撞时会相加。

在改朝换代版本中,数字置换成中国自夏开始的各个朝代,就是两个夏拼成一个商,两个商拼成一个周……每次碰撞后便生成下一个朝代,以此类推直到中华人民共和国完成通关。(本程序涉及的朝代只有夏、商、周、秦、汉、隋、唐、宋、元、明、清,如果需要更完整的朝代更迭可自行添加)

【代码如下】

首先我们定义一个App类,在主函数中创建一个MainFrame的实例,而MainFrame在创建实例时会执行它的构造方法。

package com;
public class App {
	public static void main(String[] args){
		new MainFrame();
	}
}

在MainFrame的构造方法中,调用了 initFrame()用于初始化窗体,调用了initData()用于初始化数据,调用了initMenu()用于初始化菜单,调用了paintView()用于初始化游戏界面,调用了playMusic()用于循环播放背景音乐,最后再添加键盘监听,使得用户在按下↑、↓、←、→键时程序能够有所反应。(注意:在playMusic()函数中关于背景音乐的打开路径和paintView()函数中关于游戏界面图片的打开路径根据自身存放位置考虑,建议使用相对路径。而且背景音乐的格式不能是mp3格式,可以用wav格式。)

package com;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.SourceDataLine;
import javax.swing.*;
import java.util.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;

public class MainFrame extends JFrame implements KeyListener,ActionListener{
	int[][] datas=new int[4][4];
	
	/*
	int[][] datas={//失败数组
			{2,4,8,4},
			{16,32,64,8},
			{128,2,256,2},
			{512,8,1024,64}
	};*/
	int loseflag=1;		//是否展现失败图片的开关:1关闭,0开启
	int score=0;		//得分
	String theme="flower";//图片类别
	//创建JMenuItem对象
	JMenuItem item1=new JMenuItem("flower");
	JMenuItem item2=new JMenuItem("gold");
	JMenuItem item3=new JMenuItem("blackWhite");
	/**构造方法*/
	public MainFrame(){
		//初始化窗体
		 initFrame();
		 //初始化数据
		 initData();
		 //初始化菜单
		 initMenu();
		//绘制界面
		 paintView(); 
		//为窗体添加键盘监听
		 this.addKeyListener(this);
		//设置窗体可见
		setVisible(true);
		//播放背景音乐
		playMusic();
	}

	public void playMusic() {// 背景音乐播放 
    	try {
    		AudioInputStream ais = AudioSystem.getAudioInputStream(new File("music//1.wav"));    //绝对路径
    		AudioFormat aif = ais.getFormat();
    		final SourceDataLine sdl;
    		DataLine.Info info = new DataLine.Info(SourceDataLine.class, aif);
    		sdl = (SourceDataLine) AudioSystem.getLine(info);
    		sdl.open(aif);
    		sdl.start();
    		FloatControl fc = (FloatControl) sdl.getControl(FloatControl.Type.MASTER_GAIN);
    		// value可以用来设置音量,从0-2.0
    		double value = 2;
    		float dB = (float) (Math.log(value == 0.0 ? 0.0001 : value) / Math.log(10.0) * 20.0);
    		fc.setValue(dB);
    		int nByte = 0;
    		final int SIZE = 1024 * 64;
    		byte[] buffer = new byte[SIZE];
    		while (nByte != -1) {
    			nByte = ais.read(buffer, 0, SIZE);
    			sdl.write(buffer, 0, nByte);
    		}
    		sdl.stop();
 
    	} catch (Exception e) {
    		e.printStackTrace();
    	}
    }
	
	public void initMenu() {
		//创建JMenuBar对象
		JMenuBar menuBar=new JMenuBar();
		//创建JMenu栏目对象
		JMenu menu=new JMenu("换肤");
		menuBar.add(menu);
		menu.add(item1);
		menu.add(item2);
		menu.add(item3);
		
		//注册监听
		item1.addActionListener(this);
		item2.addActionListener(this);
		item3.addActionListener(this);
		//给窗体设置菜单
		setJMenuBar(menuBar);
		}
	
	/**该方法用于初始化数据--对datas数组进行初始化*/
 	public void initData() {
		createNum();
		createNum();
	}
	
	/**此方法用于初始化窗体*/
 	public void initFrame() {
		setDefaultCloseOperation(3);//调用成员方法,设置Java程序随着窗口关闭而关闭
		setSize(514,538);//调用成员方法,设置窗口大小(宽,高)
		setLocationRelativeTo(null);//调用成员方法,设置窗口处于显示器中间位置
		setAlwaysOnTop(true);//调用成员方法,设置窗口置顶
		setTitle("改朝换代");//调用成员方法,设置窗口标题
	}
	
	/**此方法用于绘制界面内容*/
	public void paintView() {
		//移除掉界面内所有内容
		getContentPane().removeAll();
		
		//界面得分内容
		JLabel scoreLabel=new JLabel("得分:"+score);
		scoreLabel.setBounds(50,20,100,20);
		getContentPane().add(scoreLabel);
		
		if(loseflag==0) {
			JLabel loseLabel=new JLabel(new ImageIcon("image\\"+theme+"\\fail.png"));
			loseLabel.setBounds(50,50,400,300);
			getContentPane().add(loseLabel);
		}
		
		for(int i=0;i<4;i++) {
			for(int j=0;j<4;j++) {
				JLabel image=new JLabel(new ImageIcon("image\\"+theme+"\\"+datas[i][j]+".png"));
				image.setBounds(50+100*j,50+100*i,100,100);
				getContentPane().add(image);
			}
		}
		JLabel image=new JLabel(new ImageIcon("image\\"+theme+"\\background.png"));
		image.setBounds(40,40,420,420);
		getContentPane().add(image);
		
		//刷新界面的方法
		getContentPane().repaint();
	}
	
	/**无法监听到上下左右按键,无需关注*/
	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}

	/**键盘按下时所触发的方法,在这个方法中区分出上下左右按键*/
	@Override
	public void keyPressed(KeyEvent e) {
		int KeyCode = e.getKeyCode();
		
		if(KeyCode==37) {
			//调用左移动的方法
			moveToLeft(1);
			createNum() ;
		}else if(KeyCode==38) {
			//调用上移动的方法
			moveToTop(1);
			createNum() ;
		}else if(KeyCode==39) {
			//调用右移动的方法
			moveToRight(1);
			createNum() ;
		}else if(KeyCode==40) {
			//调用下移动的方法
			moveToBottom(1);
			createNum() ;
		}
		//重新绘制界面
		paintView();
		//判断游戏是否结束
		check();
	}
	
	/**此方法用于处理一维数组的反转*/
	public void swap(int[] arr) {
		for(int start=0,end=arr.length-1;start<arr.length/2;start++,end--){
			int temp;
			temp=arr[start];
			arr[start]=arr[end];
			arr[end]=temp;
		}
	}
	
	/**此方法用于处理二维数组的反转*/
	public void horizontaSwap() {
		for(int i=0;i<datas.length;i++) {
			swap(datas[i]);
		}
	}
	
	/**此方法用于处理二维数组元素顺时针旋转*/
	public void clockwise() {
		int [][]arr=new int[4][4];
		for(int i=0;i<datas.length;i++) {
			for(int j=0;j<datas[i].length;j++){
				arr[j][3-i]=datas[i][j];
			}
		}
		datas=arr;
	}
	
	/**此方法用于处理二维数组元素逆时针旋转*/
	public void antilockwise() {
		int [][]arr=new int[4][4];
		for(int i=0;i<datas.length;i++) {
			for(int j=0;j<datas[i].length;j++){
				arr[3-j][i]=datas[i][j];
			}
		}
		datas=arr;
	}
	
	/**此方法用于处理数据左移动*/
	public void moveToLeft(int flag) {
		int[][]arr=new int[4][4];
		for(int i=0,n;i<datas.length;i++) {
			n=0;
			for(int j=0;j<datas[i].length;j++){
				if(datas[i][j]!=0) {
					arr[i][n]=datas[i][j];
					n++;
				}
			
			}
		}
		datas=arr;
		
		//合并元素后,后续元素前移,并在末尾补0
		for(int i=0;i<datas.length;i++) {
			for(int x = 0;x<3;x++) {
				if(datas[i][x]==datas[i][x+1]) {
					datas[i][x]*=2;	
					if(flag==1) {//判断是否真正左移
					score+=datas[i][x];		//计算得分
					}
					for(int j = x+1;j<3;j++) {//后续元素前移,并在末尾补0.
						datas[i][j]=datas[i][j+1];				
				}
					datas[i][3]=0;
				}
			}
		}		
	}
	
	/**此方法用于处理数据右移动*/
	public void moveToRight(int flag) {
		horizontaSwap();
		moveToLeft(flag);
		horizontaSwap();	
	}
	
	/**此方法用于处理数据上移动*/
	public void moveToTop(int flag) {
		antilockwise();
		moveToLeft(flag);
		clockwise();	
	}
	
	/**此方法用于处理数据下移动*/
	public void moveToBottom(int flag) {
		clockwise();
		moveToLeft(flag);
		antilockwise();
	}
	
	/**此方法用于拷贝二维数组的数据*/
	public void copyArray(int[][]src,int[][]dest) {
		for(int i=0;i<src.length;i++) {
			for(int j=0;j<src[i].length;j++) {
				dest[i][j]=src[i][j];
			}
		}
	}
	
	/**此方法用于整合四种移动的判定*/
	public void check() {
		if(checkLeft()==false&&checkRight()==false&&checkTop()==false&&checkBottom()==false) {
			loseflag = 0;
			paintView();
		}
	}
	
	/**此方法用于判断是否可以左移*/
	public boolean checkLeft() {
		//创建新数组用于备份原数组数据
		int[][] arr=new int[4][4];
		//将原数组数据拷贝到新数组中
		copyArray(datas,arr);
		//调用左移动方法,对原数组数据进行左移动
		moveToLeft(2);
		//false:不可以移动;true:可以移动
		boolean flag=false;
		//使用移动后的数据与原数组数据逐个进行比对,并使用flag变量记录
		lo:for(int i=0;i<datas.length;i++) {
			for(int j=0;j<datas[i].length;j++) {
				if(datas[i][j]!=arr[i][j]) {
					flag=true;
					break lo;
				}
			}
		}
		//恢复原数组数据
		copyArray(arr,datas);
		//返回结果信息
		return flag;
	}
	
	/**此方法用于判断是否可以右移*/
	public boolean checkRight() {
		int[][] arr=new int[4][4];
		copyArray(datas,arr);
		moveToRight(2);
		boolean flag=false;
		lo:for(int i=0;i<datas.length;i++) {
			for(int j=0;j<datas[i].length;j++) {
				if(datas[i][j]!=arr[i][j]) {
					flag=true;
					break lo;
				}
			}
		}
		copyArray(arr,datas);
		return flag;
	}

	/**此方法用于判断是否可以上移*/
	public boolean checkTop() {
		int[][] arr=new int[4][4];
		copyArray(datas,arr);
		moveToTop(2);
		boolean flag=false;
		lo:for(int i=0;i<datas.length;i++) {
			for(int j=0;j<datas[i].length;j++) {
				if(datas[i][j]!=arr[i][j]) {
					flag=true;
					break lo;
				}
			}
		}
		copyArray(arr,datas);
		return flag;
	}

	/**此方法用于判断是否可以下移*/
	public boolean checkBottom() {
		int[][] arr=new int[4][4];
		copyArray(datas,arr);
		moveToBottom(2);
		boolean flag=false;
		lo:for(int i=0;i<datas.length;i++) {
			for(int j=0;j<datas[i].length;j++) {
				if(datas[i][j]!=arr[i][j]) {
					flag=true;
					break lo;
				}
			}
		}
		copyArray(arr,datas);
		return flag;
	}
 	
	/**键盘松开时所触发的方法*/
	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}
	
	/**此方法用于从空白的位置,随机产生2号数字块*/
	public void createNum() {
		int[]arrayI=new int[16];
		int[]arrayJ=new int[16];
		int w=0;
		for(int i=0;i<datas.length;i++) {
			for(int j=0;j<datas[i].length;j++) {
				if(datas[i][j]==0) {
					arrayI[w]=i;
					arrayJ[w]=j;
					w++;
				}
			}
		}
		if(w!=0) {
			Random r=new Random();
			int index =r.nextInt(w);//生成[0,w)的随机数
			int x=arrayI[index];
			int y=arrayJ[index];
		    datas[x][y]=2;
		}
	}
	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getSource()==item1) {
			theme ="flower";		
		}else if(e.getSource()==item2) {
			theme ="gold";		
		}else if(e.getSource()==item3) {
			theme ="blackWhite";		
		}
		//重新绘制界面
		paintView();
	}

}

如果我们想要使程序在没有java环境下也可以运行,我们可以将程序用exe4j软件将Java程序打包转换为.exe的可执行文件。我使用的是Java12,exe4j8.0版本。具体打包过程可见如何将 java 项目打包成exe可执行文件_码猿小菜鸡的博客-CSDN博客

【打包成exe可执行文件后】 

 

 【游戏不同皮肤界面】

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值