Java maze, the old programmer is here again
Introduction to the author
Author name: Ming Shiyin in programming world
Introduction: CSDN blog expert has been engaged in software development for many years and is proficient in Java and JavaScript. Bloggers also learn and grow step by step from scratch, know the importance of learning and accumulation, and like to fight and upgrade with ADC. Welcome to pay attention and look forward to learning, growing and taking off with you!
Series catalog
1. Java Tetris
2. Java Gobang games
3. The old Java programmer spent a day writing an airplane war
4. Java plants vs Zombies
5. The old Java programmer spent two days writing and reading
6. Java Xiaole (love elimination every day)
7. Java Snake games
8. Java minesweeping games
9. Java tank war
design sketch
Realization idea
1. Create a run window.
2. Create a menu.
3. Draw each unit of the maze.
4. Calculate the maze path through the algorithm and open the path to form a maze.
5. Draw the start and end points.
6. Add a keyboard event to control the movement of the starting point box.
7. Closure.
Maze algorithm (online reference)
- Take the starting point as the current maze cell and mark it as accessed
- When there are still unmarked labyrinth units, cycle
1). If the current maze unit has adjacent maze units that have not been accessed
(1). Randomly select an adjacent maze unit that is not accessed
(2). Stack the current maze unit
(3). Remove the walls between the current maze cell and the adjacent maze cell
(4). Mark the adjacent labyrinth unit and use it as the current labyrinth unit
2). If the current maze cell does not have an unreachable adjacent maze cell, and the stack is not empty
(1). The maze unit at the top of the stack comes out of the stack
(2). Make it the current maze unit
**This algorithm is called "depth first". In short, it starts from the starting point, looks for its upper, lower, left and right neighbors, and then walks one at random. When it can't get through, it returns to the previous step and continues to walk until all units are finished**
Relevant illustration
- The walls of each cell are divided into upper wall, right wall, lower wall and left wall. These walls are represented by an array with a length of 4. If the value of the element is true, it means that the wall exists, otherwise the wall does not exist. The subscript method of the array in the code determines whether the wall exists.
- Cells are created according to rows and columns, and double loops are used. Similar to tables, for example, if the second row is represented by i, it is 1, and the third column is represented by j, it is 2. The combination of elements in the second row and the third column is (1,2)
- Similarly, its upper neighbors are (0,2), right neighbors (1,3), lower neighbors (2,2), and left neighbors (1,1), that is, the upper and lower neighbors are i minus plus 1, and the left and right neighbors are j minus plus 1.
- The coordinates of the four points of the square are (x1,y1)(x2,y2)(x3,y3)(x4,y4) respectively. The formula for calculating the coordinates is (where start is the relative offset, in order to make some gaps on both sides of the maze):
//i represents row, j represents column, and h represents cell height //Upper left coordinate this.x1=start+j*h; this.y1=start+i*h; //Upper right coordinate this.x2=start+(j+1)*h; this.y2=start+i*h; //Lower right coordinate this.x3=start+(j+1)*h; this.y3=start+(i+1)*h; //Lower left coordinate this.x4=start+j*h; this.y4=start+(i+1)*h;
Calculate the coordinates. If the width and height of each square are 40, the coordinates of (1,2) this unit are as follows:
5. The wall is represented by an array of four elements. For example, if the array is: [true,true,true,true], the figure is as follows:
If the array is [false,true,true,true], the figure is:
6. If you want to connect with the neighbor on the right, what should you do? The current cell removes the right wall and the right cell removes the left wall, so it is connected.
That's it after removal, and so on
code implementation
create a window
First, create a game form class GameFrame, which is inherited to JFrame and used to display on the screen (window object). Each game has a window. Set the window title, size, layout, etc.
import javax.swing.JFrame; /** *Form class */ public class GameFrame extends JFrame { //Construction method public GameFrame(){ setTitle("maze");//Set title setSize(420, 470);//Set form size setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//The process exits after shutdown setLocationRelativeTo(null);//Center setResizable(false);//No enlargement is allowed //setVisible(true);// Set display form } }
Create panel container GamePanel inherit to JPanel
import javax.swing.JMenuBar; import javax.swing.JPanel; /* * Canvas class */ public class GamePanel extends JPanel{ private JMenuBar jmb = null; private GameFrame mainFrame = null; private GamePanel panel = null; private String gameFlag="start";//Game status //Construction method public GamePanel(GameFrame mainFrame){ this.setLayout(null); this.setOpaque(false); this.mainFrame=mainFrame; this.panel =this; } }
Create another Main class to start the window.
//Main class public class Main { public static void main(String[] args) { GameFrame frame = new GameFrame(); GamePanel panel = new GamePanel(frame); frame.add(panel); frame.setVisible(true); } }
Right click to execute the Main class, and the window is created
Create menu and menu options
create menu
private Font createFont(){ return new Font("Siyuan Song typeface",Font.BOLD,18); } //create menu private void createMenu() { //Create JMenuBar jmb = new JMenuBar(); //Get font Font tFont = createFont(); //Create game options JMenu jMenu1 = new JMenu("game"); jMenu1.setFont(tFont); //Create help options JMenu jMenu2 = new JMenu("help"); jMenu2.setFont(tFont); JMenuItem jmi1 = new JMenuItem("New game"); jmi1.setFont(tFont); JMenuItem jmi2 = new JMenuItem("sign out"); jmi2.setFont(tFont); //jmi1 jmi2 added to the menu item game jMenu1.add(jmi1); jMenu1.add(jmi2); JMenuItem jmi3 = new JMenuItem("Operation help"); jmi3.setFont(tFont); JMenuItem jmi4 = new JMenuItem("Victory conditions"); jmi4.setFont(tFont); //jmi13 jmi4 added to the menu item "game" jMenu2.add(jmi3); jMenu2.add(jmi4); jmb.add(jMenu1); jmb.add(jMenu2); mainFrame.setJMenuBar(jmb); //Add listening jmi1.addActionListener(this); jmi2.addActionListener(this); jmi3.addActionListener(this); jmi4.addActionListener(this); //Setting instruction jmi1.setActionCommand("restart"); jmi2.setActionCommand("exit"); jmi3.setActionCommand("help"); jmi4.setActionCommand("win"); }
Implement ActionListener and override the method actionPerformed
At this time, the GamePanel reports an error, and the actionPerformed method needs to be rewritten.
Implementation of actionPerformed method
@Override public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); System.out.println(command); UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("Siyuan Song typeface", Font.ITALIC, 18))); UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("Siyuan Song typeface", Font.ITALIC, 18))); if ("exit".equals(command)) { Object[] options = { "determine", "cancel" }; int response = JOptionPane.showOptionDialog(this, "Are you sure you want to exit", "", JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); if (response == 0) { System.exit(0); } }else if("restart".equals(command)){ restart(); }else if("help".equals(command)){ JOptionPane.showMessageDialog(null, "Move up, down, left and right through the keyboard", "Tips!", JOptionPane.INFORMATION_MESSAGE); }else if("win".equals(command)){ JOptionPane.showMessageDialog(null, "Move to the end to win", "Tips!", JOptionPane.INFORMATION_MESSAGE); } }
The GamePanel code at this time is as follows:
package main; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.UIManager; import javax.swing.plaf.FontUIResource; /* * Canvas class */ public class GamePanel extends JPanel implements ActionListener{ private JMenuBar jmb = null; private GameFrame mainFrame = null; private GamePanel panel = null; private String gameFlag="start";//Game status //Construction method public GamePanel(GameFrame mainFrame){ this.setLayout(null); this.setOpaque(false); this.mainFrame=mainFrame; this.panel =this; //create menu createMenu(); } private Font createFont(){ return new Font("Siyuan Song typeface",Font.BOLD,18); } //create menu private void createMenu() { //Create JMenuBar jmb = new JMenuBar(); //Get font Font tFont = createFont(); //Create game options JMenu jMenu1 = new JMenu("game"); jMenu1.setFont(tFont); //Create help options JMenu jMenu2 = new JMenu("help"); jMenu2.setFont(tFont); JMenuItem jmi1 = new JMenuItem("New game"); jmi1.setFont(tFont); JMenuItem jmi2 = new JMenuItem("sign out"); jmi2.setFont(tFont); //jmi1 jmi2 added to the menu item game jMenu1.add(jmi1); jMenu1.add(jmi2); JMenuItem jmi3 = new JMenuItem("Operation help"); jmi3.setFont(tFont); JMenuItem jmi4 = new JMenuItem("Victory conditions"); jmi4.setFont(tFont); //jmi13 jmi4 added to the menu item "game" jMenu2.add(jmi3); jMenu2.add(jmi4); jmb.add(jMenu1); jmb.add(jMenu2); mainFrame.setJMenuBar(jmb); //Add listening jmi1.addActionListener(this); jmi2.addActionListener(this); jmi3.addActionListener(this); jmi4.addActionListener(this); //Setting instruction jmi1.setActionCommand("restart"); jmi2.setActionCommand("exit"); jmi3.setActionCommand("help"); jmi4.setActionCommand("win"); } @Override public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); System.out.println(command); UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("Siyuan Song typeface", Font.ITALIC, 18))); UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("Siyuan Song typeface", Font.ITALIC, 18))); if ("exit".equals(command)) { Object[] options = { "determine", "cancel" }; int response = JOptionPane.showOptionDialog(this, "Are you sure you want to exit", "", JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); if (response == 0) { System.exit(0); } }else if("restart".equals(command)){ restart(); }else if("help".equals(command)){ JOptionPane.showMessageDialog(null, "Move up, down, left and right through the keyboard", "Tips!", JOptionPane.INFORMATION_MESSAGE); }else if("win".equals(command)){ JOptionPane.showMessageDialog(null, "Move to the end to win", "Tips!", JOptionPane.INFORMATION_MESSAGE); } } void restart(){ } }
Run it
Draw each unit of the maze
- Initialization related parameters
public final int ROWS=20;//that 's ok public final int COLS=20;//column public final int H=20;//Width and height of each piece Block[][] blocks = null;//A two-dimensional array that stores each cell
- Create a maze unit class (if you don't understand the coordinate calculation, you can turn it up and there is an illustration)
import java.awt.Graphics; import java.util.ArrayList; import java.util.List; /* * Maze unit class */ public class Block { private GamePanel panel = null; private int i=0;//Subscript i of two-dimensional array private int j=0;//Subscript j of two-dimensional array private int h=0;//Width and height private int start=6;//Offset pixel //4 vertex coordinates private int x1=0;//x1 coordinate private int y1=0;//y1 coordinate private int x2=0;//x2 coordinate private int y2=0;//y2 coordinates private int x3=0;//x3 coordinates private int y3=0;//y3 coordinates private int x4=0;//x4 coordinates private int y4=0;//y4 coordinates //Whether the upper, lower, left and right walls are displayed. true: display, false: hide boolean[] walls=new boolean[4]; private boolean visited=false;//Is it accessed //structure public Block(int i,int j,int h,GamePanel panel){ this.i=i; this.j=j; this.h=h; this.panel=panel; //Calculate the coordinates of the 4 vertices init(); } //Calculate the coordinates of the 4 vertices private void init() { //i for row and j for column //Upper left coordinate this.x1=start+j*h; this.y1=start+i*h; //Upper right coordinate this.x2=start+(j+1)*h; this.y2=start+i*h; //Lower right coordinate this.x3=start+(j+1)*h; this.y3=start+(i+1)*h; //Lower left coordinate this.x4=start+j*h; this.y4=start+(i+1)*h; //By default, the upper, lower, left and right walls are displayed walls[0]=true; walls[1]=true; walls[2]=true; walls[3]=true; } //Method of drawing indicator public void draw(Graphics g) { //Draw maze block drawBlock(g); } //Draw maze block private void drawBlock(Graphics g) { //Judge the upper, right, lower and left walls. If true, there will be walls, otherwise there will be no walls boolean top = walls[0]; boolean right = walls[1]; boolean bottom = walls[2]; boolean left = walls[3]; if(top){//Draw upper wall g.drawLine(x1, y1, x2, y2); } if(right){//Draw right wall g.drawLine(x2, y2, x3, y3); } if(bottom){//Draw the lower wall g.drawLine(x3, y3, x4, y4); } if(left){//Draw left wall g.drawLine(x4, y4, x1, y1); } } }
- Create the method createBlocks in the GamePanel class
//Create array contents private void createBlocks() { blocks = new Block[ROWS][COLS]; Block block ; for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { block = new Block(i, j,H,this); blocks[i][j]=block; } } }
- Call this method in the constructor.
//Construction method public GamePanel(GameFrame mainFrame){ this.setLayout(null); this.setOpaque(false); this.mainFrame=mainFrame; this.panel =this; //create menu createMenu(); //Create array contents createBlocks(); }
- Redraw the paint method in the GamePanel to draw these squares
public void paint(Graphics g) { super.paint(g); //Draw mesh drawBlock(g); } //Draw maze block private void drawBlock(Graphics g) { Block block ; for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { block = blocks[i][j]; if(block!=null){ block.draw(g); } } } }
Running, you can see that the squares are drawn
Calculate and get through the maze
- Add neighbor lookup method to each cell (in Block class)
//Find out whether the current cell has an unreachable neighbor cell public List<Block> findNeighbors() { //The neighbors are divided into upper, lower, left and right List<Block> res= new ArrayList<Block>();//Returned array Block top = this.getNeighbor(0,false); Block right = this.getNeighbor(1,false); Block bottom = this.getNeighbor(2,false); Block left = this.getNeighbor(3,false); if(top!=null){ res.add(top); } if(right!=null){ res.add(right); } if(bottom!=null){ res.add(bottom); } if(left!=null){ res.add(left); } return res;//Return neighbor array } //Get neighbors according to direction public Block getNeighbor(int type,boolean lose_visited) { Block neighbor; int ti=0,tj=0; if(type==0){ ti = this.i-1; tj = this.j; }else if(type==1){ ti = this.i; tj = this.j+1; }else if(type==2){ ti = this.i+1; tj = this.j; }else if(type==3){ ti = this.i; tj = this.j-1; } Block[][] blocks = panel.blocks; if(ti<0 || tj<0 || ti>=panel.ROWS || tj>=panel.COLS){//It's beyond the boundary neighbor = null; }else{ //First find the neighbor neighbor = blocks[ti][tj]; //Judge whether it is accessed. If it is accessed, null is returned if(neighbor.visited && !lose_visited){//lose_visited equals true to ignore access neighbor = null; } } return neighbor; }
- calculation
For the code written with the algorithm, the only thing to note is that I set a value unVisitedCount. The initial value is the number of all units. Whenever a unit is marked as accessed, this value decreases by 1. When the value is 0, the loop is terminated and the algorithm is ended.
//Calculation and processing of line private void computed(){ /* 1.Take the starting point as the current maze cell and mark it as accessed 2.When there are still unmarked labyrinth units, cycle 1).If the current maze unit has adjacent maze units that have not been accessed (1).Randomly select an adjacent maze unit that is not accessed (2).Stack the current maze unit (3).Remove the walls between the current maze cell and the adjacent maze cell (4).Mark the adjacent labyrinth unit and use it as the current labyrinth unit 2).If the current maze cell does not have an unreachable adjacent maze cell, and the stack is not empty (1).The maze unit at the top of the stack comes out of the stack (2).Make it the current maze unit */ Random random = new Random(); Stack<Block> stack = new Stack<Block>();//Stack Block current = blocks[0][0];//Take the first as the current cell current.setVisited(true);//Mark as accessed int unVisitedCount=ROWS*COLS-1;//Because the first one has been set to access List<Block> neighbors ;//Define neighbors Block next; while(unVisitedCount>0){ neighbors = current.findNeighbors();//Find neighbor collection (unreachable) if(neighbors.size()>0){//If the current maze unit has adjacent maze units that have not been accessed //Randomly select an adjacent maze unit that is not accessed int index = random.nextInt(neighbors.size()); next = neighbors.get(index); //Stack the current maze unit stack.push(current); //Remove the walls between the current maze cell and the adjacent maze cell this.removeWall(current,next); //Mark the adjacent labyrinth unit and use it as the current labyrinth unit next.setVisited(true); //If an access is marked, the counter is decremented by 1 unVisitedCount--;//Diminishing current = next; }else if(!stack.isEmpty()){//If the current maze cell does not have an unreachable adjacent maze cell, and the stack is not empty /* 1.The maze unit at the top of the stack comes out of the stack 2.Make it the current maze unit */ Block cell = stack.pop(); current = cell; } } }
- Remove wall
//Remove the walls between the current maze cell and the adjacent maze cell private void removeWall(Block current, Block next) { if(current.getI()==next.getI()){//Horizontal neighbor if(current.getJ()>next.getJ()){//The match is the left neighbor //If you are a neighbor on the left, remove your left wall and your neighbor's right wall current.walls[3]=false; next.walls[1]=false; }else{//The match is the neighbor on the right //If you have a neighbor on the right, remove your right wall and your neighbor's left wall current.walls[1]=false; next.walls[3]=false; } }else if(current.getJ()==next.getJ()){//Vertical neighbor if(current.getI()>next.getI()){//It matches the upper neighbor //If you are a neighbor above, you should remove your own upper wall and your neighbor's lower wall current.walls[0]=false; next.walls[2]=false; }else{//The match is the lower neighbor //If you are a neighbor below, remove your own lower wall and your neighbor's upper wall current.walls[2]=false; next.walls[0]=false; } } }
- Calling computed method in constructor
//Construction method public GamePanel(GameFrame mainFrame){ this.setLayout(null); this.setOpaque(false); this.mainFrame=mainFrame; this.panel =this; //create menu createMenu(); //Create array contents createBlocks(); //Calculation processing line computed(); }
- Operation effect
Draw start and end points
- Create Rect class
package main; import java.awt.Color; import java.awt.Graphics; //Start end box public class Rect { private int i=0;//Subscript i of two-dimensional array private int j=0;//Subscript j of two-dimensional array private int x=0;//x coordinate private int y=0;//y coordinate private int h=0;//Width and height private int start=6;//Offset pixel private String type="";//start end public Rect(int i,int j,int h,String type){ this.i=i; this.j=j; this.h=h; this.type=type; } //initialization private void init() { this.x=start+j*h+2; this.y=start+i*h+2; } //draw void draw(Graphics g){ //Calculate x and y coordinates init(); Color oColor = g.getColor(); if("start".equals(type)){//gules g.setColor(Color.red); }else{ g.setColor(Color.blue); } g.fillRect(x, y, h-3, h-3); g.setColor(oColor); } //move public void move(int type, Block[][] blocks,GamePanel panel) { //According to the current start square, the corresponding maze unit is obtained Block cur = blocks[this.i][this.j]; boolean wall = cur.walls[type];//Get the corresponding wall if(!wall){ //Get the cell corresponding to the moving block Block next = cur.getNeighbor(type,true); if(next!=null){ this.i = next.getI(); this.j = next.getJ(); panel.repaint(); //Judge that if I and j are equal to the end point, it means success if(this.i==panel.end.i && this.j==panel.end.j){ panel.gameWin(); } } } } public int getI() { return i; } public void setI(int i) { this.i = i; } public int getJ() { return j; } public void setJ(int j) { this.j = j; } }
- Create a method in the GamePanel class and call it in the structure.
//Creates a square that starts and ends private void createRects() { start = new Rect(0, 0, H, "start") ; end = new Rect(ROWS-1, COLS-1, H, "end") ; }
//Construction method public GamePanel(GameFrame mainFrame){ this.setLayout(null); this.setOpaque(false); this.mainFrame=mainFrame; this.panel =this; //create menu createMenu(); //Create array contents createBlocks(); //Calculation processing line computed(); //Creates a square that starts and ends createRects(); }
- Draw in the paint method
@Override public void paint(Graphics g) { super.paint(g); //Draw mesh drawBlock(g); //Draw start end direction drawRect(g); } //Draw start end box private void drawRect(Graphics g) { end.draw(g); start.draw(g); }
- Run it
Add keyboard mobile monitoring
- Create listening method
//Add keyboard monitor private void createKeyListener() { KeyAdapter l = new KeyAdapter() { //Press @Override public void keyPressed(KeyEvent e) { if(!"start".equals(gameFlag)) return ; int key = e.getKeyCode(); switch (key) { //Up case KeyEvent.VK_UP: case KeyEvent.VK_W: if(start!=null) start.move(0,blocks,panel); break; //towards the right case KeyEvent.VK_RIGHT: case KeyEvent.VK_D: if(start!=null) start.move(1,blocks,panel); break; //down case KeyEvent.VK_DOWN: case KeyEvent.VK_S: if(start!=null) start.move(2,blocks,panel); break; //towards the left case KeyEvent.VK_LEFT: case KeyEvent.VK_A: if(start!=null) start.move(3,blocks,panel); break; } } //release @Override public void keyReleased(KeyEvent e) { } }; //Add keyboard monitoring to the main frame mainFrame.addKeyListener(l); }
- Call in construction
//Construction method public GamePanel(GameFrame mainFrame){ this.setLayout(null); this.setOpaque(false); this.mainFrame=mainFrame; this.panel =this; //create menu createMenu(); //Create array contents createBlocks(); //Calculation processing line computed(); //Creates a square that starts and ends createRects(); //Add keyboard event listener createKeyListener(); }
- function
Finishing
At this point, the code has been basically completed. You can add methods such as game victory and restart
//restart void restart() { /*Parameter Reset 1.Game status 2.Labyrinth unit reset 3.Recalculate lines */ //1. Game status gameFlag="start"; //2. Labyrinth unit reset Block block ; for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { block = blocks[i][j]; if(block!=null){ block.setVisited(false); block.walls[0]=true; block.walls[1]=true; block.walls[2]=true; block.walls[3]=true; } } } //3. Calculation and processing line computed(); //Start square zeroing start.setI(0); start.setJ(0); //Repaint repaint(); }
When you see the big guys here, you can use your little hands to make a fortune, like + reply + collect. It would be better to [pay attention] to one wave.
Code acquisition method:
After [like] + [collection] + [attention] + [comment], add my home page wechat or chat with me privately, and I'll send it to you!
More wonderful
1. Java Tetris
2. Java Gobang games
3. The old Java programmer spent a day writing an airplane war
4. Java plants vs Zombies
5. The old Java programmer spent two days writing and reading
6. Java Xiaole (love elimination every day)
7. Java Snake games
8. Java minesweeping games
9. Java tank war
Related reading
1. Java Web Library Management System
2. JavaWeb student dormitory management system
3. JavaWeb online examination system
in addition
In order to help more Xiaobai advance from zero to advanced Java engineers, we got a set of "knowledge atlas of learning and growth of Java engineers" from CSDN officials, with a size of 870mm x 560mm. After unfolding, it is the size of a desk, which can also be folded into the size of a Book. The original is 129 yuan, and the current price is 29 yuan. First come, first served. Interested partners can learn about it!