Realization of 2048 games in JAVA

preface

In 2014, Gabriele Cirulli spent the weekend writing the program of 2048, which was just fun. He wants to create a different visual effect from his own animation.

The game is implemented in java language, using swing technology for interface processing, and the design idea is object-oriented.

Main demand

Every time you control all blocks to move in the same direction, two blocks with the same number collide and merge into their sum. After each operation, a 2 or 4 will be randomly generated in the blank square. Finally, you will win if you get a "2048" block. If all 16 squares are filled and the adjacent squares are different, that is, they cannot be moved, the game will end.

Main design

1. Game panel generation display

2. Block design

3. Keyboard monitoring, direction keys to control the movement of numbers

4. Digital mobile logic algorithm processing

5. The number adds up to 2048 and the game wins

Function screenshot

The game begins

Movement effect

code implementation

Interface layout class

public class Game2048View implements ActionListener{
	Block[] blocks;		//block
	JPanel myJPanel;	//Main panel
	JPanel jp1,jp2;		//SubPanel
//	int moveFlag; 				//  Number of cumulative moves
	boolean numFlag;		// Used to determine whether new numbers can be added
	JLabel scroeValue;		//Show score
	
	public Game2048View(JFrame myJFrame){
		blocks=new Block[16];
//		moveFlag=0;
		numFlag=true;
		this.myJPanel=(JPanel)myJFrame.getContentPane();///Get content panel
		setJp1();
		myJPanel.add(jp1,BorderLayout.NORTH);
		setJp2();
		myJPanel.add(jp2, BorderLayout.CENTER);
		myJFrame.addKeyListener(new Game2048Logic(this,myJFrame,blocks,numFlag,scroeValue));
	}
	
	public void addc(JPanel jp1,Component component, GridBagConstraints gbc,int gridwidth,int gridheight, int weightx,int weighty,int gridx,int gridy) {
		//This method is used to add a control to the container
		gbc.gridwidth=gridwidth;		//This method is to set the number of grids occupied by the component level. If it is 0, it means that the component is the last one in the line
		gbc.gridheight=gridheight;		//This method is to set the number of grids occupied by the vertical component
		gbc.weightx=weightx;				//This method sets the horizontal stretching range of the component. If it is 0, it means no stretching. If it is not 0, it will stretch as the window increases, between 0 and 1
		gbc.weighty=weighty;				//This method sets the vertical stretching range of the component. If it is 0, it means no stretching. If it is not 0, it will stretch as the window increases, between 0 and 1
		gbc.gridx=gridx;
		gbc.gridy=gridy;
		gbc.fill=GridBagConstraints.BOTH;
		jp1.add(component,gbc);
	}
	
	public void setJp1() {
		GridBagLayout gbLayout=new GridBagLayout();
		jp1=new JPanel(gbLayout);
		
		JPanel Jtitle=new JPanel();		
		JLabel title=new JLabel("2048");
		title.setFont(new Font("font", Font.PLAIN, 45));//Type, style, size
		title.setHorizontalAlignment(JLabel.LEFT);//jLabel text left-right alignment attribute setting method and alignment
		Jtitle.add(title);
		jp1.add(Jtitle);
		
		JPanel Jscroe=new JPanel(new GridLayout(2, 1));//new GridLayout(2, 1) is the grid layout style. The parameters "2" and "1" are "rows" and "columns" of the grid respectively.
		JLabel scroe=new JLabel("Scroe");
		scroe.setFont(new Font("font", Font.PLAIN, 16));
		scroe.setHorizontalAlignment(JLabel.CENTER);
		scroeValue=new JLabel("0");
		scroeValue.setFont(new Font("font", Font.PLAIN, 16));
		scroeValue.setHorizontalAlignment(JLabel.CENTER);
		Jscroe.add(scroe);
		Jscroe.add(scroeValue);
		jp1.add(Jscroe);
		
		JPanel Jnoite=new JPanel();
		JLabel noite=new JLabel("Move the direction key and accumulate the number to 2048");
		noite.setFont(new Font("font", Font.PLAIN, 14));
		noite.setHorizontalAlignment(JLabel.LEFT);
		Jnoite.add(noite);
		jp1.add(Jnoite);
		
		JPanel JnewGame=new JPanel();
		JButton newGame=new JButton("New Game");
		newGame.setHorizontalAlignment(JButton.CENTER);
		newGame.addActionListener(this);
		JnewGame.add(newGame);
		jp1.add(JnewGame);
				
		GridBagConstraints gbc=new GridBagConstraints();		
		addc(jp1, Jtitle, gbc, 3, 2, 60, 60, 0, 0);
		addc(jp1, Jscroe, gbc, 0, 2, 40, 60, 3, 0);
		addc(jp1, Jnoite, gbc, 3, 1, 60, 40, 0, 2);
		addc(jp1, JnewGame, gbc, 0, 1, 40, 40, 3, 2);
	}
	
	public void setJp2() {
		addBlock();
		initBlock();
		initBlock();
	}
	
	/**
	 * Add box
	 */
	public void addBlock(){
		jp2=new JPanel();
		/*
		 * setLayout Is to set the current component as a streaming layout Components are arranged from left to right in the form. If they are arranged to the end of the line, they are arranged in a new line 
		 * GridLayout(int rows, int cols, int hgap, int vgap)
			Creates a grid layout with a specified number of rows and columns.
			rows - The rows has a value representing any number of rows
			cols - The cols has a value representing any number of columns
			hgap - Horizontal spacing
			vgap - Vertical spacing
		 */
		jp2.setLayout(new GridLayout(4, 4, 5, 5));
		for (int i = 0; i < blocks.length; i++) {
			blocks[i]=new Block();
			blocks[i].setHorizontalAlignment(JLabel.CENTER);// Opaque label
			blocks[i].setOpaque(true);// Set control opacity
			jp2.add(blocks[i]);
		}
	}
	
	/**
	 * Initialization block
	 */
	public void initBlock(){
		while (numFlag) {
			int index=(int) (Math.random()*16);
			if (blocks[index].getText().trim().equals("")) {
				blocks[index].setValue("2");
				break;
			} else {
				continue;
			}	
		}
	}
	
	/**
	 * Get the height of the first sub panel
	 */
	public int getMyJPanelHeidth() {
		return jp1.getSize().height;
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// Method stub automatically generated by TODO
		newGame();
	}
	
	/**
	 * Restart the game
	 */	
	public void newGame() {
		for (int i = 0; i < blocks.length; i++) {			
			blocks[i].setValue("");			
		}
		numFlag=true;
		scroeValue.setText("0");
		initBlock();
		initBlock();
	}
}

Business logic class

public class Game2048Logic implements KeyListener{
	Block[] blocks;
	boolean numFlag;		// Used to determine whether new numbers can be added
	JLabel scroeValue;		//Show score
	int blocksarr[]=new int[4];		//Save values in a row / column box
	JFrame myJFrame;
	int scroe=0;
	Game2048View game2048View;
	
	//Initialize keyboard events
	public Game2048Logic(Game2048View game2048View, JFrame myJFrame, Block[] blocks,boolean numFlag,JLabel scroeValue) {
		// Constructor stub automatically generated by TODO
		this.blocks=blocks;
		this.numFlag=numFlag;
		this.scroeValue=scroeValue;
		this.myJFrame=myJFrame;
		this.game2048View=game2048View;
	}
	
	//Initialize button event
	public Game2048Logic() {
		// Constructor stub automatically generated by TODO
	}
	
	public boolean getnumFlag() {
		return numFlag;
	}

	@Override
	public void keyPressed(KeyEvent e) {
		// Method stub automatically generated by TODO
		int[] blocksarr=getBlock();
		switch (e.getKeyCode()) {
		case KeyEvent.VK_UP:
			colBlock("up");
			hasEmptyBlock();
			if (Arrays.equals(blocksarr, getBlock())) {
			} else {
				refershBlock();
			}			
			isGameFail("up");
			break;
		case KeyEvent.VK_DOWN:
			colBlock("down");
			hasEmptyBlock();
			if (Arrays.equals(blocksarr, getBlock())) {
			} else {
				refershBlock();
			}	
			isGameFail("down");
			break;
		case KeyEvent.VK_LEFT:
			rowBlock("left");
			hasEmptyBlock();
			if (Arrays.equals(blocksarr, getBlock())) {
			} else {
				refershBlock();
			}	
			isGameFail("left");
			break;
		case KeyEvent.VK_RIGHT:
			rowBlock("right");
			hasEmptyBlock();
			if (Arrays.equals(blocksarr, getBlock())) {
			} else {
				refershBlock();
			}	
			isGameFail("right");
			break;
		default:
			break;
		}
		scroeValue.setText(""+scroe);
		win();
	}
	
	/**
	 * Vertical square movement processing function
	 */
	public void colBlock(String direction){
		int tmp1=0;
		int tmp2=0;
		int index=0;
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				if (blocks[tmp1].getText().trim().equals("")) {
					tmp1+=4;
					if (tmp1>=16) {
						break;
					} else {
						continue;
					}
				} else {
					blocksarr[index]=Integer.parseInt(blocks[tmp1].getText().trim());
					index+=1;
					tmp1+=4;
					if (tmp1>=16 || index>=4) {
						break;
					} else {
						continue;
					}
				}
			}
			switch (direction) {
			case "up":
				blocksarr=handleBlocksarr(blocksarr);
				break;
			case "down":
				blocksarr=reverseArr(handleBlocksarr(reverseArr(blocksarr)));
				break;
			default:
				break;
			}
			
			for (int n = 0; n < blocksarr.length; n++) {
				if (blocksarr[n]==0) {
					blocks[tmp2].setText("");
					blocks[tmp2].setBackground(Color.gray);
				} else {
					blocks[tmp2].setValue(blocksarr[n]+"");
				}
				tmp2+=4;
			}			
			index=0;
			tmp1=i+1;
			tmp2=i+1;
			//Empty array blockarr
			for (int n = 0; n < blocksarr.length; n++) {
				blocksarr[n]=0;				
			}
		}
	}
	
	/**
	 * Horizontal square movement processing function
	 */
	public void rowBlock(String direction) {
		int tmp1=0;
		int tmp2=0;
		int index=0;
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				if (blocks[tmp1].getText().trim().equals("")) {
					tmp1+=1;
					if (tmp1>=16) {
						break;
					} else {
						continue;
					}
				} else {
					blocksarr[index]=Integer.parseInt(blocks[tmp1].getText().trim());
					index+=1;
					tmp1+=1;
					if (tmp1>=16 || index>=4) {
						break;
					} else {
						continue;
					}
				}
			}
			
			switch (direction) {
			case "left":
				blocksarr=handleBlocksarr(blocksarr);
				break;
			case "right":
				blocksarr=reverseArr(handleBlocksarr(reverseArr(blocksarr)));
				break;
			default:
				break;
			}
			
			for (int n = 0; n < blocksarr.length; n++) {
				if (blocksarr[n]==0) {
					blocks[tmp2].setText("");
					blocks[tmp2].setBackground(Color.gray);
				} else {
					blocks[tmp2].setValue(blocksarr[n]+"");
				}
				tmp2+=1;
			}			
			index=0;
			//Empty array blockarr
			for (int n = 0; n < blocksarr.length; n++) {
				blocksarr[n]=0;				
			}
		}		
	}
	
	/**
	 * Process and return an array
	 */
	public int[] handleBlocksarr(int[] blocksarr) {
		int index=0;
		int[] result=new int[4];
		for (int i = 0; i < blocksarr.length; i++) {			
			//sort
			if (blocksarr[i]!=0) {
				result[index]=blocksarr[i];
				index++;
			} 
		}
		if (index==0 || index==1) {
			for (int i = index; i < result.length; i++) {
				result[i]=0;
			}
		} else {
			for (int i = 0; i < blocksarr.length; i++) {
				blocksarr[i]=0;
			}
			switch (index) {
			case 2:
				if (result[0]==result[1]) {
					blocksarr[0]=result[0]+result[1];
					scroe+=result[0]*2;
				} else {
					blocksarr=result;
				}				
				break;
			case 3:
				if (result[0]==result[1]) {
					blocksarr[0]=result[0]+result[1];
					scroe+=result[0]*2;
					blocksarr[1]=result[2];
				} else {
					if (result[1]==result[2]) {
						blocksarr[0]=result[0];
						blocksarr[1]=result[1]+result[2];
						scroe+=result[1]*2;
					} else {
						blocksarr=result;
					}
				}
				break;
			case 4:
				if (result[0]==result[1]) {
					blocksarr[0]=result[0]+result[1];	
					scroe+=result[0]*2;
					if (result[2]==result[3]) {
						blocksarr[1]=result[2]+result[3];
						scroe+=result[2]*2;
					} else {
						blocksarr[1]=result[2];
						blocksarr[2]=result[3];
					}
				} else {
					if (result[1]==result[2]) {
						blocksarr[0]=result[0];
						blocksarr[1]=result[1]+result[2];
						scroe+=result[1]*2;
						blocksarr[2]=result[3];
					} else {
						blocksarr[0]=result[0];
						blocksarr[1]=result[1];
						if (result[2]==result[3]) {
							blocksarr[2]=result[2]+result[3];
							scroe+=result[2]*2;
						} else {
							blocksarr=result;
						}
					}
				}
				break;
			default:
				break;
			}
			result=blocksarr;
		}		
		return result;	
	}
	
	/**
	 * Invert array eg: 45000 -- > 00054
	 */
	public int[] reverseArr(int[] arr) {
		int[] tmp=new int[arr.length];
		int index=arr.length-1;
		for (int i = 0; i < arr.length; i++) {
			tmp[index]=arr[i];
			index--;
		}
		return tmp;
	}
	
	/**
	 * Refresh the box and add a new 2 or 4
	 */
	public void refershBlock(){
		if (numFlag==false) {
		}
		while (numFlag) {
			int index=(int) (Math.random()*16);	
			if (blocks[index].getText().trim().equals("")) {
				if (Math.random()<0.8) {
					blocks[index].setValue("2");
				} else {
					blocks[index].setValue("4");
				}
				break;
			} else {
				continue;
			}	
		}
	}
	
	/**
	 * Determine whether there are empty squares
	 */
	public void hasEmptyBlock() {
		for (int i = 0; i < blocks.length; i++) {
			if (blocks[i].getText().trim().equals("")) {
				this.numFlag=true;
				break;
			} else {
				this.numFlag=false;
			}
		}
	}
	
	/**
	 * Judge whether the game fails
	 */
	public void isGameFail(String direction) {
		boolean result=true;	//true means failure and false means no failure
		int tmp=0;
		if (numFlag == false) { // Indicates that there are no empty squares
			switch (direction) {
			case "up":
			case "down":
				for (int i = 0; i < 4; i++) {
					tmp=i*4;
					for (int j = 0; j < 3; j++) {						
						if (blocks[tmp].getText().trim().equals(blocks[tmp+1].getText().trim())) {
							result = false;	//The game did not fail
							break;
						} else {
							tmp++;
						}
					}
					if (result==false) {
						break;
					}
				}
				break;
			case "left":
			case "right":
				for (int i = 0; i < 4; i++) {
					for (int j = 0; j < 3; j++) {						
						if (blocks[tmp].getText().trim().equals(blocks[tmp+4].getText().trim())) {
							result = false;	//The game did not fail
							break;
						} else {
							tmp+=4;
							if (tmp>=16) {
								break;
							} else {
								continue;
							}
						}
					}
					tmp=i+1;
					if (result==false) {
						break;
					}
				}
				break;
			default:
				break;
			}
		} else {
			result=false;
		}
		if (result==true) {
			JOptionPane.showMessageDialog( null , "Game Over",null , JOptionPane.ERROR_MESSAGE) ;
			game2048View.newGame();
		} else {
		}
	}
	
	/**
	 * Judge whether the game is successful, that is, the success is accumulated to 2048
	 */
	public void win() {
		for (int i = 0; i < blocks.length; i++) {
			if (blocks[i].getText().trim().equals("2048")) {
				JOptionPane.showMessageDialog( null , "YOU ARE WIN",null , JOptionPane.ERROR_MESSAGE) ;
				game2048View.newGame();
				break;
			} 
		}		
	}
	
	/**
	 * Get all the box contents for comparison
	 */
	public int[] getBlock() {
		int[] blocksarr=new int[16];
		for (int i = 0; i < blocks.length; i++) {
			if (blocks[i].getText().trim().equals("")) {
				blocksarr[i]=0;
			} else {
				blocksarr[i]=Integer.parseInt(blocks[i].getText().trim());
			}			
		}
		return blocksarr;
	}
	
	@Override
	public void keyReleased(KeyEvent e) {
		// Method stub automatically generated by TODO
	}

	@Override
	public void keyTyped(KeyEvent e) {
		// Method stub automatically generated by TODO
	}

}

summary

Through the implementation of the 2048 game, I have a further understanding of the relevant knowledge of swing and a deeper understanding of the language java than before.

Some basic syntax of java, such as data types, operators, program flow control and arrays, are better understood. The core of java is the idea of object-oriented. I finally realized something about this concept.

Source code acquisition

Download address of source code: portal ---- >

Like it. After following the blogger, chat privately and get it for free

Today is the 14th / 100th day of continuous writing.
You can follow me, praise me, comment on me and collect me.

Keywords: Java Game Development

Added by reece_1989 on Sun, 06 Mar 2022 02:19:27 +0200