Although I wrote about walking the maze in the past, many people reported that they did not find the code and would not deploy, and did not understand the principle. This time, I wrote the optimization in more detail and put the code on github. Taking advantage of 520,521, I can put some pictures to the goddess!
cause
look at the rendering ( at the end of the article) (online computer try address http://biggsai.com/maze.html ):
Project github address: https://github.com/javasmall/mazegame :
As a programmer, he is often busy with his work, is often busy with coding and rarely takes care of his girlfriend in his spare time, and is often complained of as a scumbag.
However, scumbags, but found that 520 has arrived. Damn it, 520? !
We often regret what we said when we were impulsive. I prepared a woolen yarn and didn’t prepare anything, but I can’t cheat other girls casually (although I’ve cheated 😭).
Habitually open Taobao, and stroll around to see if there is anything suitable. I think it’s the 19th today, is it going to be cold?
I thought that there was a Meituan remedy that could be delivered. When I opened the Meituan, I found that it was all about eating and drinking. It was over, it was really cold.
In the middle of the night, I was confessing the big things I had said. I can't talk nonsense if I'm not capable. I can't do anything all night.
Wait, one more night!
At this moment, my eyes suddenly brightened. For the programmer, he can create his romance in one night!
Open all kinds of search engines, blog searches: programmers 520 confession artifact code as expected! There is something, hahaha ctrl+c
, ctrl+v
I know too well, romance is coming!
I searched for a few codes and found that it was so awesome and I could still run, but I found a lot of similarities. I couldn't find the original author, but I admired the author's talent (saving grace) very much in my heart.
At this time, a sentence she said to me for a long time emerged again in my mind: "I don't care at all" ! These may indeed be very old-fashioned and pediatrics, no, I will do it in my own way! I want to have my own characteristics.
I wondered how to use familiar data structures and algorithms to make something interesting.
Recalling the data structures and algorithms I know: linked lists, queues, stacks, graphs, dfs, bfs, and collected , Dijkstra, Prime...
I used to vaguely remember that someone used Java Swing to cooperate and search algorithms to draw a maze. Can I write a little game about walking the maze? But I searched and found that there are no HTML and JavaScript versions, but Swing is basically no use except for certain scenarios. I want a different one. Although I am not familiar with JavaScript and HTML, I should be able to, come on 💪 !
analysis
I need a design from 0 to 1 to implement a maze game, but there must be difficulties and obstacles in it, but any problem can be separated step by step, and then broken and merged one by one.
For a small game of walking the maze, I thought for a moment that I might need to master the following knowledge:
- Understand a maze generation algorithm with a path from the initial position to the end position
- Draw a chessboard on canvas with JavaScript (initial state of the maze)
- Use the maze generation algorithm to generate a maze (to erase the lines that need to be erased)
- Use JavaScript, event monitoring and canvas drawing to achieve block movement (not out of bounds and not passing through walls should be considered)
thinking process of is probably 160a7375a313d9:
- Draw a single line -> draw multiple lines -> rub the line (to form a maze) -> block movement, movement constraints (no out of bounds and no wall penetration) -> complete the game .
Line drawing (checkerboard)
I have never touched html+js (canvas) before, but I have learned Java Swing and written gomoku games and card flipping games. It’s a bit similar to this. There is a canvas canvas in html, which can be painted on. Some things and declare some monitoring (keyboard monitoring).
For this canvas, Java Swing, QT and other painting gallery, if you use it for drawing, to make it clear you painted something is actually a line, not the actual property, you just need the data in a programming language Structure and algorithm to operate the canvas, so that the content of the canvas is correct for the data structure or algorithm you write reasonable display .
So for the maze, each line has no attribute , only the position x,y
. When you operate this canvas, it may be different from our accustomed aspect thinking. When designing lines or points, you must be able to calculate Infer where these points and lines are, and operate according to this position when drawing lines, rubbing lines, and moving squares so that the entire maze is completely unified visual effect of 160a7375a315ff.
The steps for drawing a chessboard are also very simple. First try to draw a line, figure out the size of each square in the maze and the total number of squares in advance, and then draw the horizontal and vertical directions in the order of the starting (upper left) coordinates. Lines (the positions of the starting and ending points between parallel lines are regular), and finally a visual maze can be realized.
<!DOCTYPE html>
<html>
<head>
<title>MyHtml.html</title>
</head>
<body>
<canvas id="mycanvas" width="600px" height="600px"></canvas>
</body>
<script type="text/javascript">
var chessboradsize=14;//棋盘大小
var chess = document.getElementById("mycanvas");
var context = chess.getContext('2d');
function drawChessBoard(){//绘画
for(var i=0;i<chessboradsize+1;i++){
context.strokeStyle='gray';//可选区域
context.moveTo(15+i*30,15);//垂直方向画15根线,相距30px;
context.lineTo(15+i*30,15+30*chessboradsize);
context.stroke();
context.moveTo(15,15+i*30);//水平方向画15根线,相距30px;棋盘为14*14;
context.lineTo(15+30*chessboradsize,15+i*30);
context.stroke();
}
}
drawChessBoard();//绘制棋盘
</script>
</html>
Achieve effect
Draw a maze
How to generate a random maze? What's going on? I fell into a dumbfounded look again.
Because we want a maze, we need a connected path between the exit and the entrance of the maze. It is difficult to know what algorithm to use to generate the maze without studying. At this time, a familiar voice came from the ear corners: Use and search together (disjoint collection) .
What is the connection between a maze and a disjoint set? (rule)
and search set ( disjoint set ) in the previous data structure and algorithm series. Its main function is the merger and search of forests: the unconnected pass and search can quickly combine two forests. Merge, and be able to quickly query whether two nodes are in the same forest (collection)!
The random maze generation is also using this idea: when each square is not connected, it is a checkerboard square , each maze grid is relatively independent, which is its initial state; later generations may require several Neighboring nodes are connected (combined into a set), and this node may or may not be connected to its neighbors. We can achieve the support of its underlying data structure and
Here, we treat each grid as a collection element, and each collection and the surrounding walls are to prove whether it is directly connected. We want to realize the entire random maze by connecting a part of the grid (wiping part of the wall).
The specific ideas are: (mainly understand and check the collection)
1 : Define the basic classes and methods that do not want to intersect ( search,union
etc.)
2 : array initialization, each array element is a set, the value is -1
3 : Randomly find a grid (one-dimensional data needs to be converted to two-dimensional, which is a bit troublesome), find a random wall (that is, find the top, bottom, left and right of the grid), and determine whether the grid is out of bounds is a legitimate Lattice.
- Specifically generate a random number m (less than the total number of grids in the maze)
- The one-dimensional random number m is transformed into the horizontal and vertical two-dimensional position p of the maze, specifically:
[m/length, m% length] where length represents the number of rows or columns of the maze.
- Randomly find a position q
[m/length+1,m%length] or
[m/length-1,m%length] or
[m/length,m%length+1] or
[m/length, m%length-1]
- Judge whether it is out of range, if it is out of range, search again, otherwise proceed to the next step.
4 : Determine whether the two grids p and q (this time convert the two-dimensional coordinates into its one-dimensional array number) are in a set ( and search set to find ). If it is, go back to the third step to find it again, if it is not, then dig out the wall.
5 : a bit cumbersome to dig out the wall (merge) , even if the two squares are not connected, you need to judge its kind of wall (up and down or left and right isolation) by location, and then accurately locate the wall by calculation The start and end points are then erased (a lot of details need to be considered).
6 : Repeat the above work until the first (1,1) and (n,n) connection stops to get a complete maze. Although random numbers are used to find squares to find walls, the data efficiency and results are quite good.
It is necessary to clarify the relationship between one-dimensional and two-dimensional arrays. One dimension is real data, and the operations of merge, search, set, and merge are performed. The conversion to two-dimensional is more to find the location. Understand the transformation!
Note : To avoid confusion, figure out the address of the array and the location of the logical matrix. The array starts from 0, logically judge by yourself, don't get confused!
You may ask why this algorithm can finally generate a starting and ending connected maze, because our termination is based on it. If it is not connected, it will allow a random connection in the maze to connect to a maze that is not connected, and this possibility It is very limited, so the maze connection can not be satisfied in the worst case, and then random points are found to make the maze look more symmetrical.
main logic of
while(search(0)!=search(aa*aa-1))//主要思路
{
var num = parseInt(Math.random() * aa*aa );//产生一个小于196的随机数
var neihbour=getnei(num);
if(search(num)==search(neihbour)){continue;}
else//不在一个上
{
isling[num][neihbour]=1;isling[neihbour][num]=1;
drawline(num,neihbour);//划线
union(num,neihbour);
}
}
Then the previous code is
<!DOCTYPE html>
<html>
<head>
<title>MyHtml.html</title>
</head>
<body>
<canvas id="mycanvas" width="600px" height="600px"></canvas>
</body>
<script type="text/javascript">
var chessboradSize=14;
var chess = document.getElementById("mycanvas");
var context = chess.getContext('2d');
var tree = [];//存放是否联通
var isling=[];//判断是否相连
for(var i=0;i<chessboradSize;i++){
tree[i]=[];
for(var j=0;j<chessboradSize;j++){
tree[i][j]=-1;//初始值为0
}
} for(var i=0;i<chessboradSize*chessboradSize;i++){
isling[i]=[];
for(var j=0;j<chessboradSize*chessboradSize;j++){
isling[i][j]=-1;//初始值为0
}
}
function drawChessBoard(){//绘画
for(var i=0;i<chessboradSize+1;i++){
context.strokeStyle='gray';//可选区域
context.moveTo(15+i*30,15);//垂直方向画15根线,相距30px;
context.lineTo(15+i*30,15+30*chessboradSize);
context.stroke();
context.moveTo(15,15+i*30);//水平方向画15根线,相距30px;棋盘为14*14;
context.lineTo(15+30*chessboradSize,15+i*30);
context.stroke();
}
}
drawChessBoard();//绘制棋盘
function getnei(a)//获得邻居号 random
{
var x=parseInt(a/chessboradSize);//要精确成整数
var y=a%chessboradSize;
var mynei=new Array();//储存邻居
if(x-1>=0){mynei.push((x-1)*chessboradSize+y);}//上节点
if(x+1<14){mynei.push((x+1)*chessboradSize+y);}//下节点
if(y+1<14){mynei.push(x*chessboradSize+y+1);}//有节点
if(y-1>=0){mynei.push(x*chessboradSize+y-1);}//下节点
var ran=parseInt(Math.random() * mynei.length );
return mynei[ran];
}
function search(a)//找到根节点
{
if(tree[parseInt(a/chessboradSize)][a%chessboradSize]>0)//说明是子节点
{
return search(tree[parseInt(a/chessboradSize)][a%chessboradSize]);//不能压缩路径路径压缩
}
else
return a;
}
function value(a)//找到树的大小
{
if(tree[parseInt(a/chessboradSize)][a%chessboradSize]>0)//说明是子节点
{
return tree[parseInt(a/chessboradSize)][a%chessboradSize]=value(tree[parseInt(a/chessboradSize)][a%chessboradSize]);//不能路径压缩
}
else
return -tree[parseInt(a/chessboradSize)][a%chessboradSize];
}
function union(a,b)//合并
{
var a1=search(a);//a根
var b1=search(b);//b根
if(a1==b1){}
else
{
if(tree[parseInt(a1/chessboradSize)][a1%chessboradSize]<tree[parseInt(b1/chessboradSize)][b1%chessboradSize])//这个是负数(),为了简单减少计算,不在调用value函数
{
tree[parseInt(a1/chessboradSize)][a1%chessboradSize]+=tree[parseInt(b1/chessboradSize)][b1%chessboradSize];//个数相加 注意是负数相加
tree[parseInt(b1/chessboradSize)][b1%chessboradSize]=a1; //b树成为a树的子树,b的根b1直接指向a;
}
else
{
tree[parseInt(b1/chessboradSize)][b1%chessboradSize]+=tree[parseInt(a1/chessboradSize)][a1%chessboradSize];
tree[parseInt(a1/chessboradSize)][a1%chessboradSize]=b1;//a所在树成为b所在树的子树
}
}
}
function drawline(a,b)//划线,要判断是上下还是左右
{
var x1=parseInt(a/chessboradSize);
var y1=a%chessboradSize;
var x2=parseInt(b/chessboradSize);
var y2=b%chessboradSize;
var x3=(x1+x2)/2;
var y3=(y1+y2)/2;
if(x1-x2==1||x1-x2==-1)//左右方向的点 需要上下划线
{
context.strokeStyle = 'white';
context.clearRect(29+x3*30, y3*30+16,2,28);
}
else
{
context.strokeStyle = 'white';
context.clearRect(x3*30+16, 29+y3*30,28,2);
}
}
while(search(0)!=search(chessboradSize*chessboradSize-1))//主要思路
{
var num = parseInt(Math.random() * chessboradSize*chessboradSize );//产生一个小于196的随机数
var neihbour=getnei(num);
if(search(num)==search(neihbour)){continue;}
else//不在一个上
{
isling[num][neihbour]=1;isling[neihbour][num]=1;
drawline(num,neihbour);//划线
union(num,neihbour);
}
}
</script>
</html>
In this way, one step further from victory, the effect is achieved:
Block movement
The method I used for this part is not (the key is that I don’t know how to move 160a7375a31c83), but a grid-by- grid jump, that is, when we go to the next grid, the current grid will be wiped off. Draw another square in the grid. The square is selected because the square is easier to erase and draw calculations, and it can be erased accurately according to the pixel size. Of course, those who are familiar with JavaScript can get a little guy in and play.
In addition, pay attention to when moving. It cannot pass through the wall or cross the boundary . So how to judge? It's easy to handle. Before moving the target square, we judge whether it is directly connected to , pay attention to direct connection instead of China Unicom (it is very possible to go around in a circle but can not directly pass over, so here and check the path ), if it is not directly connected, then no operation is performed, otherwise the block is moved.
In addition, the monitoring of events can be obtained by checking it up and down by yourself on Baidu. Adding buttons to monitor some events and completing the entire game is not the most important thing.
In order to enrich the playability of the game, the method is encapsulated and the level can be set (just change the size of the maze), so that the level can be cleared.
Conclusion
try online address http://biggsai.com/maze.html , the code can go to GitHub https://github.com/javasmall/mazegame : download or to bigsai
public No reply: maze you can get ! After downloading the project, modify the picture and what you want to say, put it on your own server, and you can show it to the goddess.
Avoid eating dog food, the dynamic picture picture has been changed (the leaderboard function has been castrated):
The author's front-end capabilities and algorithm capabilities are limited, and the writing may not be particularly good, please forgive me! Of course, the author welcomes to progress and learn together with people who love to learn! Welcome to follow the author's public : 160a7375a31dfd bigsai , welcome to follow and like! Crab!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。