Website: Code road online

WeChat: c91374286

Many people learn to program and never write their own mini-games.

This, more or less, should be a lack of fun in programming.
And the people who have not written, nothing more than this kind.
1. Disdain. I’m awesome. It’s a waste of time.
2. No. I scratch my cheek, but I have nowhere to do it.
3. Unwilling. I’m only interested in money. I’m not interested in writing games.
Don’t know how to judge, don’t want to judge, everyone has their own world view, outlook on life, values.
So, for those who are interested but have no place to start, this article will restore the thinking process and development principle of the small game “Snake” step by step.

Draw a box

A complex thing, broken down into N simple steps, one by one, this is the tao of programming.
So, step one, what do we do? Draw a box.
As anyone who has ever played snake knows, snakes actually move around a square or rectangle.
So, the first step is to define this area of activity.

And here, we have a name for this area, snake- Grassland, where snakes steal eggs to eat.

*/ * {padding: 0; border: 0; margin: 0; outline: 0; box-sizing: border-box; } /* Snake - Grassland */. Snake - Grassland {width: 300px; /* width */ height: 300px; /* height */ background-color: #52af4a; /* Green grass */}Copy the code

<! Snake Meadow -- Structure --><div class="snake-grassland"></div>Copy the code



I’m sure a lot of people have a lot of thoughts going through their heads when they write about Snake.
What does a snake look like?
What do snake eggs look like?
How to design a beautiful snake activity area?
Should I float div or position?
How do you calculate if you hit a wall?
How does the snake increase its body length after eating the eggs?
How to control the snake’s automatic movement?
How do you steer the snake?
And so on… And so on… And so on.
Here, I tell you, these things, don’t think! Don’t think about it! Don’t think about it!
Because I’m writing this course for people who don’t know how to do it or who don’t have ideas.
So, what are the characteristics of this type of student?
Their programming ideas, programming skills, vision and experience are in the early stages of accumulation.
All of a sudden N problems arise to be dealt with, it is difficult to clear logic and law, and then come to a solution.
So instead of thinking about N problems at once, focus on solving the first one.
What if there are problems in the back? We’ll talk about it when we get there. Is the so-called, today has wine today drunk, tomorrow sorrow to tomorrow sorrow.
Put the problems that can’t be solved later and solve the ones that can be solved first.
This reminds me of when I was in junior high school. What should I do if I couldn’t write the math answers? First write a “solution:” bai, get 2 points again!

A grid

One classroom, 60 chairs, that only seats 60 people. One for each, no interference, fair and just.
A family of four, that dinner is four pairs of chopsticks, more than a pair, that must be next door Lao Wang.
So, step two, you should draw the grid.
What’s with the grid? Snake eggs!
To make a poached egg, you can’t just open the gas stove and beat the egg. You must put the pot first.

*/ * {padding: 0; border: 0; margin: 0; outline: 0; box-sizing: border-box; } /* Snake - Grassland */. Snake - Grassland {width: 300px; /* width */ height: 300px; /* height */ background-color: #52af4a; .snake- range.line {height:10; } /* Range-style */.snake. Cell {width: 10%; /* width < 10 */ height: 100%; /* Height = 100 of the line element height */ float: left; /* border-right: 1px solid # 42A03a; /* border-bottom: 1px solid # 42A03a; /* * * */Copy the code

<! -- Grid structure -- 10 lines, too much code, only 2 lines -->
<div class="snake-grassland">
    <! -- Line 1 -->
    <div class="line">
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
    </div>
    <! -- Line 2 -->
    <div class="line">
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
    </div>
    <! -- Line 3 -->.</div>Copy the code



Here are a few things to think about.
1. Why use line to specify a row? Why not draw the same grid layout if all cells float directly?
2. Why use percentages for line height and grid width instead of pixels?
3. Why is the grid positioned by float? Isn’t this bug-prone and not recommended? And you did, and you didn’t clear the float.
4. Why not use absolute positioning? Wouldn’t it be easier to set the hierarchy with z-index when placing snake eggs this way?
5. Why use a div as a grid element instead of a SPAN?
Now, to answer the above questions, but I hope that when you see the above questions, you can think about them first.
Things happen, there is always a scientific reason for it. Otherwise, it is superstition, it is metaphysics. (Although CSS itself is metaphysical…)
Answer 1: Write code in a logical way. This is a two-dimensional plane game, just like the table arrangement in the classroom. You can’t have 100 tables lined up and floating, one next to the other. This can achieve the same effect, but they are still two-dimensional and one-dimensional in nature, and the movement of the snake in the back, increasing its length, is difficult to calculate. So, it makes sense to use rows and columns, rows and columns to locate the eggs and snakes that follow.
Answer 2: Why use percentages instead of pixels? To use pixels, you need to know the width of the grass, divide by 10, get a decimal, and write that number. Percentages do not need to know the width of the grass, do not need to calculate, save time.
Answer 3: Why use float layouts? Because, simple. Why not clear the float? The grass has been fixed at 300px * 300px, and the floating grid inside will not affect the grass, so there is no need to clear the floating.
Answer 4: Why are cells not absolutely positioned? Absolute positioning requires calculating the distance above each cell, the distance to the left. Classmate, 100 grid, you go to calculate try. Float with no need to calculate, save time.
Answer 5: Why is the grid layout div instead of SPAN? It’s personal. You can use anything you want, but they’re boxes. Is there any difference between sleeping on the bed and on the floor? There’s not much difference. They both sleep.
So, to sum up. When we’re writing code, when we’re developing projects, we need to be lazy, use time-saving techniques, to write code, to develop projects. Because time is the most precious thing.

Draw an egg

That’s right. I’m finally gonna paint an egg.
At this point, I’m sure some of you will start to struggle.
How do I draw an egg?
Where?
How to put?
How do you lay new eggs if a snake eats you?
How do you put new eggs?
Where do you put the new eggs?
All right, let’s get over it.
Avenue to simple, please remember the front said, with the most time-saving method to draw the simplest egg!
The rest of the problem, don’t think about it, it’s a bottomless pit.

 /* 先重置一下 */
* {
    padding: 0;
    border: 0;
    margin: 0;
    outline: 0;
    box-sizing: border-box;
}
/* Snake Meadow - Style */
.snake-grassland {
    width: 300px; / * * / width
    height: 300px; / * * /
    background-color: #52af4a; /* Green grass */
}
 /* Line style */
.snake-grassland .line{
    height:10%;
}
 /* Grass grid - style */
.snake-grassland .cell{
    width: 10%; /* Width takes up 1/10 */
    height: 100%; /* Height counts for 1/10 */
    float: left; /* Each cell floats left */
    border-right: 1px solid #42a03a; /* Checkbox */
    border-bottom: 1px solid #42a03a; /* Checkbox */
}
 /* Which egg - style */
.snake-grassland .egg{
    width: 80%; /* Width of egg */
    height: 80%; /* Height of egg */
    margin: 10%; /* The outer spacing of the egg so that the egg is centered */
    border-radius: 50%; /* Make the egg round, after all, never seen a square egg */
    background-color: #ffc107; /* Make the egg yellow, of course, you want a red egg, green egg, also ok */
}
            Copy the code

 <! -- HTML -- structure -->
<div class="snake-grassland">
    <! -- Line 1 -->
    <div class="line">
        <div class="cell">
            <div class="egg"></div>
        </div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
        <div class="cell"></div>
    </div>
    <! -- Line 2 -->.</div>Copy the code



Very simple, directly put a yellow egg in the first grid.
Although this step is simple, it has profound implications.
1. It ends egg-laying disorder, right? There’s nothing to worry about. Just put it on the first grid.
2. It gives us an intuitive view of what an egg looks like when placed in a grid. So, let’s go ahead and write the code without worrying about the style of the egg.

Random put eggs

After the previous step, we laid an egg. But, you know, we can’t just keep having eggs in the first box, right? It has to be placed randomly in any cell.
So, at this point, it’s time to start writing the javascript code we all love to hate.
First of all, all javascript in this article is native JS code.
So, how do you randomly place snake eggs?
Let’s list the knowledge points and ideas first.
1. The random function math.random () generates the abscissa and ordinate values in the range according to the number of rows and columns of the grid, and the combination of the two can uniquely locate a grid position.
2. Obtain the corresponding grid according to the row and column values generated by the random function.
3. Place the egg in the corresponding grid.
Did you write the code directly? No, think about it.
For example, how do you generate random numbers?
Those of you who know me know that I often use a four-word phrase: mind programming.
What does that mean?
For example, to this step now, many young students, directly start to generate random number, and then go to the element!
Bullshit! Do you know the structure of the element? Do you know how elements are nested, how to get that element by random number?
So, one line of code, three errors, is not unreasonable.
So let’s look at the structure.



Now you know how to get it? Is there an element to take?
The X-axis, to the left of line, is the subscript of the line element in the children array below the grass element.
On the Y-axis, is not the end of the children array containing the cell element below the corresponding line element?
Isn’t it much more accurate to observe with the naked eye than to use your mind to blind the cat and kill the mouse?
Unfortunately, there are still so many people who use mind programming heavily.
Here, want to learn how to program? I’ll teach you.
At this point, a lot of people might find the main character and run away.
It’s not good. It’s not good.
What else is there to do? Don’t you want to know the meaning, the function, the usage of the other attributes except children?
I think that year but the meaning of each attribute, role, a Baidu to search, for understanding and using native javascript, is a very big benefit.
All right, back to the subject. If you look at our grid structure, it’s a row, column layout, all the elements of the grid
So, we definitely need two random numbers. The first one produces the X-axis, which is the x-coordinate, which is the row number.
The second one produces the Y-axis, which is the y-coordinate, which is the column number.
So, we’ll just generate two random numbers from 0 to 9, which correspond to each of the 10 and 10 rows.
So, why 0 to 9, not 1 to 10?
Because the starting index of the array is 0.
———- Smart dividing line ———-
Next, don’t be impatient, write the random number first.

// The random x-coordinate of the egg, i.e. the number of rows
var eggX = Math.floor(Math.random() * 10)Copy the code

Let’s see how it can take values between 0 and 9 (including 0 and 9).
How do you analyze it? It’s easy. Open the Rookie tutorial (runoob.com).



Find this function.
Can’t find? Go home and graze your cattle.
Look at the red circle. Needless to say, that’s the point.
It returns the value between [0,1].
Remember middle school, high school, on/off?
Brackets mean contained, and parentheses mean not contained.
That is, math.random () generates values from 0 to 0.999… A number between 99.
If the generated random number is multiplied by 1, the minimum value is 0 * 1 = 0, the maximum value is 0.999… 999 * 1 = 0.999… 999
Is it all right? If so, go home and herd your cattle.
Magnify 1 10 times. The minimum is still 0. What’s the maximum? 9.999… 999
Let’s magnify 1 100 times. The minimum is still 0. What’s the maximum? 99.99… 999
Okay, let’s stop. It’s time to analyze Math.floor.
Old school, open up rookie tutorial (runoob.com)



See yet? Returns the largest integer less than or equal to x.
What you mean?
Math.floor(1.6) returns 1
Math.floor(9.999… 999) returns 9!!
So that gives us a value between zero and nine.
Here’s the problem.
Why do we do that?
Did you get the 3.16736237 element of the array? No, the index of the array must be an integer!
Random number perfect end, congratulations to this step of the students, and a small step forward.
One small step for you. It’s really just one small step. The real programming hasn’t started yet.
And then we can get that random cell, based on the random number.

 // Grass element
var grasslandElement = document.querySelector("#snake-grassland");
// The random x-coordinate of the egg, i.e. the number of rows
var eggX = Math.floor(Math.random() * 10);
// The random y-coordinate of the egg, i.e. the number of columns
var eggY = Math.floor(Math.random() * 10);
// Random grid elements, where eggs need to be placed
// From the children line array below the lawn element
var eggElement = grasslandElement.children[eggX].children[eggY];
                            Copy the code



As you can see, perfect!
What can we do next?
I’m going to put an egg on this random grid.

// Grass element
var grasslandElement = document.querySelector("#snake-grassland");
// The random x-coordinate of the egg, i.e. the number of rows
var eggX = Math.floor(Math.random() * 10);
// The random y-coordinate of the egg, i.e. the number of columns
var eggY = Math.floor(Math.random() * 10);
// Random grid elements, where eggs need to be placed
// From the children line array below the lawn element
var eggElement = grasslandElement.children[eggX].children[eggY];
// Add an egg to the random grid element
eggElement.innerHTML = '<div class="egg"></div>';Copy the code



It wasn’t easy to write the code, but the egg was finished.
At this point, it was 1 a.m.
Education by heart is not easy, painful and happy.
If you find this article useful to you, please add me to wechat C91374286
Explore programming, and friends in the programming community, communicate, help each other, improve.

Draw the head of a snake at random

Leading man number one, finally on stage.
So before we draw protagonist number one, let’s summarize what we’ve been thinking.
Reduce complexity to simplicity: break down complex logic into simple logic.
Quick fix: Color, style, location, size, all can be changed at any time, they are not the core elements of early development. Don’t spend too much time on it. Don’t even spend time on it. To learn, save time.
Many people don’t know what programming thinking is, and I am often asked. I don’t know. I don’t know.
A case is a good way to illustrate, and if I didn’t answer it clearly before, this case should be enough to illustrate part of it.
How do you draw a snake?
So this is something to think about, it’s a little bit different, and this is where you really start to think logically.
Programs, we know, are made up of data structures plus algorithms.
Their relationship, to use the snake example, is that the composition of the snake is a data structure. Snakes can move and eat eggs. It’s an algorithm.
In other words, the materials that make up the robot are structures. Robots walk, walk, jump, jump, are algorithms.
In javascript, there are two most common structures: arrays and objects.
So, what is the structure of the snake? The answer is, in terms of a combination of the two.
First, let’s look at the characteristics of snakes.
Head first, tail last. The order is deterministic, fixed, and when you eat the egg, the tail will grow, not the head.
And to be able to represent a snake like this, obviously, an array is a good place to be, because you have a fixed order of elements in an array, and you can add elements later, and after you add elements, the order is fixed.

// Snake element array
var snakeArray = [];Copy the code

The structure of the snake is determined. What’s next?
Locate the snake.
Understand the generation of the egg in front, phase must not say more here.
But? As we know, there is always one egg, there are never 2 eggs at the same time, and there are never more than one egg in a row (of course, you can do that, but that’s outside the scope of this tutorial).
The snake? Its body parts are connected. You can’t have your head in the first line, your body in the second line, and your body in the eighth line. Right? It doesn’t make sense.
So, what do we need to do? Remember what we said earlier? We’re going to use a combination of arrays and objects to locate the snake. So, each element of the snake’s head and body has its own horizontal and vertical coordinates. So, how do I represent this? As follows.

// Snake element array
var snakeArray = [{x:0.y:0}];
                            Copy the code

For convenience, we’ll start with just the head of the snake. So, the first element of the array is the location of the snake’s head.
As you can see, it’s an object. Objects in arrays, coordinates in objects, isn’t that elegant? That’s the power of data structures.
If you don’t feel that power, let’s take a counter example, and use a two-layer array.

// Snake element array
var snakeArray = [[0.0]].Copy the code

The first element of the array is still an array, and the 0th element of the array is the horizontal axis, and the first element is the vertical axis.
Which one is better than the one above?
It’s obviously the one up there. The code is written for people. It’s written for people. The more intuitive, the more accessible the code, the better.
All right, let’s figure out where the head of the snake is, just like an egg, generated by two random numbers. (What if the head of the snake and the egg are in the same position? Cold mix, if you don’t understand this part of this tutorial, quick battle, one by one, the enemy will block, cover the ground, that can go home to cattle.
The so-called trade-off, trade-off. In a way, it doesn’t mean giving up, but doing the most important things first and then dealing with the edges.

 // Grass element
var grasslandElement = document.querySelector("#snake-grassland-3");
// The random x-coordinate of the egg, i.e. the number of rows
var eggX = Math.floor(Math.random() * 10);
// The random y-coordinate of the egg, i.e. the number of columns
var eggY = Math.floor(Math.random() * 10);
// Random grid element, obtained by the children line array corresponding to the random number below the grass element
var eggElement = grasslandElement.children[eggX].children[eggY];
// Add an egg to the random grid element
eggElement.innerHTML = '<div class="egg"></div>';
// The horizontal coordinate of the snake head
var snakeX = Math.floor(Math.random() * 10);
// The ordinate of the snake's head
var snakeY = Math.floor(Math.random() * 10);
// Snake element array
var snakeArray = [{ x: snakeX, y: snakeY }];
// Spawn snake
snakeArray.forEach(function(item, index) {
    // Random grid element representing snake position, same as above
    var snakeElement = grasslandElement.children[item.x].children[item.y];
    / / generated snake
    if (index === 0) {
        snakeElement.innerHTML = '<div class="snake snake-head"></div>';
    } else {
        snakeElement.innerHTML = '<div class="snake snake-body"></div>'; }});Copy the code



The snake moved upward

Each time you refresh this page, you will see the egg and the head of the snake in different positions
So what happens next? Control the snake’s movement, of course, so that the snake can eat the egg.
Here, we use the arrow keys on the keyboard to control the snake’s movement.
Before we start coding, it’s important to remember that you don’t want to program.
There are at least two issues that need to be sorted out first.
1. How to monitor keyboard pressing?
2. How to determine which key to press?
The solution is simple: experiment.

 // Listen for keyboard press, print press events
document.addEventListener('keydown'.function(event){
    console.dir(event);
});Copy the code



When we press the up arrow key.
As you can see, there are multiple properties that specify the value of the key being pressed.
So which one to choose? To make this more intuitive, here we select the key property.
Remember, don’t eat all the fat in one bite, we control the snake to move up first, and then, control it to move in all directions.
Before we start coding, we need to think about one thing.
How do I move up?
We know that the position of the snake depends on the x and y axes.
So, if I move up, what I want to change is the value of x, right? Or is it the value of y?
Well, in our conventional wisdom, it must be changing y.
But remember how we drew the grid?
X is the row, y is the column of this row.
If I move up, is that the same thing as saying that I have a smaller number of rows (from top to bottom, row 0 to 9)?
So, how do you set it up? Is x equal to x minus 1, right.
The number of rows has gone down by 1, or x minus is equal to 1
So, the implementation code is as follows.


// Listen for keyboard pressing
document.addEventListener('keydown'.function(event){
    // If you press up
    if(event.key === 'ArrowUp'){
        snakeHead.x -= 1;
    }
    // Spawn snake
    snakeArray.forEach(function(item, index) {
        // Random grid element representing snake position, same as above
        var snakeElement = grasslandElement.children[item.x].children[item.y];
        // Generate a snake - the first element of the array is the snake's head
        if (index === 0) {
            snakeElement.innerHTML = '<div class="snake snake-head"></div>';
        } else {
            snakeElement.innerHTML = '<div class="snake snake-body"></div>'; }}); });Copy the code



It can be found that when the arrow key is pressed, the snake head moves, but the original snake head is not cleared from the previous position, so multiple residual shadows are caused.
So, the next step is to delete the previous snake before producing the next one.
How do I delete it? The principle is simple.
Both snakes and their bodies have a common snake class name. Find all current snake class names and delete them.

 // Listen for keyboard pressing
document.addEventListener('keydown'.function(event){
    console.dir(event);
    // If you press up
    if(event.key === 'ArrowUp'){
        snakeHead.x -= 1;
    }
    // Delete the last snake
    grasslandElement.querySelectorAll(".snake").forEach(function(item) {
        item.parentElement.removeChild(item);
    });
    // Spawn snake
    snakeArray.forEach(function(item, index) {
        // Random grid element representing snake position, same as above
        var snakeElement = grasslandElement.children[item.x].children[item.y];
        // Generate a snake - the first element of the array is the snake's head
        if (index === 0) {
            snakeElement.innerHTML = '<div class="snake snake-head"></div>';
        } else {
            snakeElement.innerHTML = '<div class="snake snake-body"></div>'; }}); });Copy the code

One thing to note here is that when we delete, we use the parentElement attribute.
What does that mean? Indicates that the current snake element is a parent element, which is the cell class name element.
Why use it? Because, in native JS, you add a child element, or you remove a child element.
Both reference to the element’s parent. (Of course, there may be a way to delete the element itself, students to study it).



All right, that’s it for the ascending snake.

The snake moves up and down, side to side

Remember what we said earlier? Don’t bite the fat one at a time, take one step at a time, steady and accurate.
We’ve got the move up, the other three directions, and that’s easy.

// The up direction key controls the snake to move up
document.addEventListener("keydown".function(event) {
    console.dir(event);
    // Snake head, the 0th element of the snake array
    var snakeHead = snakeArray[0];
    // If you press up
    if (event.key === "ArrowUp") {
        snakeHead.x -= 1;
    }
    // If you press down
    if (event.key === "ArrowDown") {
        snakeHead.x += 1;
    }
    // If you press left
    if (event.key === "ArrowLeft") {
        snakeHead.y -= 1;
    }
    // If you press right
    if (event.key === "ArrowRight") {
        snakeHead.y += 1;
    }
    // Delete the last snake
    grasslandElement.querySelectorAll(".snake").forEach(function(item) {
        item.parentElement.removeChild(item);
    });
    // Spawn snake
    snakeArray.forEach(function(item, index) {
        // Random grid element representing snake position, same as above
        var snakeElement = grasslandElement.children[item.x].children[item.y];
        // Generate a snake - the first element of the array is the snake's head
        if (index === 0) {
            snakeElement.innerHTML = '<div class="snake snake-head"></div>';
        } else {
            snakeElement.innerHTML = '<div class="snake snake-body"></div>'; }}); });Copy the code

Here, I’m just going to show you the listening part of the code, but I won’t show you the previous one, so that the code is not too long.



In the picture above, you see an interesting problem.
How did the egg disappear?
In our code, only delete the snake, not delete the snake eggs?
This is because the grid that the snake passes through is occupied by the snake type.
The egg is replaced with the snake element when we pass the egg grid, and then, when we press the next direction, the previous position of the snake is deleted.
So, the egg is gone.
Notice that it’s not being eaten, it’s being covered with snakes.
So, next, it’s about how snakes eat eggs.

The snake eating the eggs

It’s time to think again.
How do snakes eat eggs?
Remember our snake structure?

// The horizontal coordinate of the snake head
var snakeX = Math.floor(Math.random() * 10);
// The ordinate of the snake's head
var snakeY = Math.floor(Math.random() * 10);
// Snake element array - currently there is only one snake head
var snakeArray = [{ x: snakeX, y: snakeY }];Copy the code

See yet? The snake itself is an array, and the head and body of the snake are each element of the array. This element is an object that records the abscissa and ordinate positions of the head and body
So if we want to add the body of the snake, do we just add the body element to the array?
So, what a snake looks like when it’s full.

var snakeArray = [{ x: x1, y: y1 },{ x: x2, y: y2 },{ x: x3, y: y3 }... {x: xn, y: yn }];Copy the code

The title of our section is snakes Eat Eggs. But, we’ve been talking about what happens when a snake eats an egg. So, how does a snake eat an egg?
This is easy enough. When the coordinates of the snake’s head and the egg match, the snake eats the egg.
Although simple to say, but, need to consider the place, is more subtle.
What’s the problem?
After the snake eats the egg, how to put a body element into the tail?
Array push({x:n,y:m}).
Is it? too yang to simple
I just want to ask you a question, how do I get the x-coordinate and y-coordinate of the appended body part?
So, it’s not that simple.
Let’s look at the code.

// Grass element
var grasslandElement = document.querySelector("#snake-grassland");
// The random x-coordinate of the egg, i.e. the number of rows
var eggX = Math.floor(Math.random() * 10);
// The random y-coordinate of the egg, i.e. the number of columns
var eggY = Math.floor(Math.random() * 10);
// Random grid elements, where eggs need to be placed
// From the children line array below the lawn element
var eggElement = grasslandElement.children[eggX].children[eggY];
// Add an egg to the random grid element
eggElement.innerHTML = '<div class="egg"></div>';
// The horizontal coordinate of the snake head
var snakeX = Math.floor(Math.random() * 10);
// The ordinate of the snake's head
var snakeY = Math.floor(Math.random() * 10);
// Snake element array - currently there is only one snake head
var snakeArray = [{ x: snakeX, y: snakeY }];
// Spawn snake
snakeArray.forEach(function(item, index) {
    // Random grid element representing snake position, same as above
    var snakeElement = grasslandElement.children[item.x].children[item.y];
    // Generate a snake - the first element of the array is the snake's head
    if (index === 0) {
        snakeElement.innerHTML = '<div class="snake snake-head"></div>';
    } else {
        snakeElement.innerHTML = '<div class="snake snake-body"></div>'; }});// The up direction key controls the snake to move up
document.addEventListener("keydown".function(event) {
    // Snake head, the 0th element of the snake array
    var snakeHead = snakeArray[0];
    // If you press up
    if (event.key === "ArrowUp") {
        snakeHead.x -= 1;
        // Whether the snake eats the egg
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // The egg is eaten and regenerated into one
            eggX = Math.floor(Math.random() * 10);
            // The random y-coordinate of the egg, i.e. the number of columns
            eggY = Math.floor(Math.random() * 10);
            // Random grid element, obtained by the children line array corresponding to the random number below the grass element
            eggElement = grasslandElement.children[eggX].children[eggY];
            // Add an egg to the random grid element
            eggElement.innerHTML = '<div class="egg"></div>';
            // Snake's tail - the most important element in the snake array
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x - 1.y: snakeTail.y }); }}// If you press down
    if (event.key === "ArrowDown") {
        snakeHead.x += 1;
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // The egg is eaten and regenerated into one
            eggX = Math.floor(Math.random() * 10);
            // The random y-coordinate of the egg, i.e. the number of columns
            eggY = Math.floor(Math.random() * 10);
            // Random grid element, obtained by the children line array corresponding to the random number below the grass element
            eggElement = grasslandElement.children[eggX].children[eggY];
            // Add an egg to the random grid element
            eggElement.innerHTML = '<div class="egg"></div>';
            // Snake's tail - the most important element in the snake array
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x + 1.y: snakeTail.y }); }}// If you press left
    if (event.key === "ArrowLeft") {
        snakeHead.y -= 1;
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // The egg is eaten and regenerated into one
            eggX = Math.floor(Math.random() * 10);
            // The random y-coordinate of the egg, i.e. the number of columns
            eggY = Math.floor(Math.random() * 10);
            // Random grid element, obtained by the children line array corresponding to the random number below the grass element
            eggElement = grasslandElement.children[eggX].children[eggY];
            // Add an egg to the random grid element
            eggElement.innerHTML = '<div class="egg"></div>';
            // Snake's tail - the most important element in the snake array
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x,
                y: snakeTail.y + 1}); }}// If you press right
    if (event.key === "ArrowRight") {
        snakeHead.y += 1;
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // The egg is eaten and regenerated into one
            eggX = Math.floor(Math.random() * 10);
            // The random y-coordinate of the egg, i.e. the number of columns
            eggY = Math.floor(Math.random() * 10);
            // Random grid element, obtained by the children line array corresponding to the random number below the grass element
            eggElement = grasslandElement.children[eggX].children[eggY];
            // Add an egg to the random grid element
            eggElement.innerHTML = '<div class="egg"></div>';
            // Snake's tail - the most important element in the snake array
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x,
                y: snakeTail.y - 1}); }}// Delete the last snake
    grasslandElement.querySelectorAll(".snake").forEach(function(item) {
        item.parentElement.removeChild(item);
    });
     // Spawn snake
    snakeArray.forEach(function(item, index) {
        // Random grid element representing snake position, same as above
        var snakeElement = grasslandElement.children[item.x].children[item.y];
        // Generate a snake - the first element of the array is the snake's head
        if (index === 0) {
            snakeElement.innerHTML = '<div class="snake snake-head"></div>';
        } else {
            snakeElement.innerHTML = '<div class="snake snake-body"></div>'; }}); });Copy the code

Oh, my god, the amount of code suddenly increased.
However, if you look closely, some of the code logic is the same.
There may be doubts, let alone, first see the effect.



There should be a lot of questions.
Answer them one by one.
Before solving the problem, we can see that as shown in the picture above, we have realized that snakes can eat eggs and grow their bodies.
However, the BUG is also very obvious, the body does not follow the head.
And secondly, as we’ve said before, snakes have bodies. It’s not that simple.
Why not? Just look at how much code has increased.
What exactly has been added?
The body increases in different ways depending on the d-pad.
In other words, if I eat an egg on the left side, my body will end up on the right side of my head.
Press the right arrow key and the body will increase to the left of the head.
Press the up arrow button to increase the underside of your head.
Press the arrow key and the body will increase the upper side of the head.
In short, the body never appears in front of, to the left and to the right of, the direction in which the head moves, only to the rear.
Small physical increase, also contains a little bit of strategy. NICE.
———- clever dividing line ———
Moving on, what other features have we added?
You can’t run out of food when the snake’s finished, can you?
So, every time you eat an egg, you have another egg.
The logic is obvious, but there’s a lot of repetitive code, and we’ll talk about optimization later.
So, there’s only one important thing left. The body doesn’t follow the head!

Body with the head

At this point, it’s not just a matter of writing code, it’s a matter of programming thinking, thinking strategies, the art of debugging code.
How do I put it?
First, we need to locate the problem.
Isn’t the problem already fixed? The body doesn’t follow the head!
Yes, generally speaking, yes. But!
Can you determine which code needs to be changed, which doesn’t, and which logic needs to be rearranged in order for the body to follow the head?
Therefore, to solve the problem, can not be an armchair strategist, to have a specific analysis, specific judgment.
A lot of people just don’t have that, so it’s hard to learn code and even harder to write it.
All right, let’s analyze it.
Snake eat eggs, eggs randomly generated, with the body does not follow the head BUG, of course, is irrelevant.
Secondly, every time after eating the egg, according to the direction of movement, the body element is added, here is also no flaw.
So, what’s left? There’s something wrong with the way it’s moving!
What’s the problem?
If you look at the code, every time you move and press the arrow button, it’s just the head of the snake plus 1, minus 1, minus 1, plus 1, right?
What about the body parts? The storm is still and steady.
So, we need to get the body parts moving, too.
Also, follow the head of the snake.
How do you follow the snake’s head?
Let me give you an example.
You need to queue for a train ticket.
A lot of people, all in a row, is this just like me and our snake?
Suppose the first person is the head of the snake, and the first person buys a ticket and walks around, is the seat occupied by the second person?
And then what? The person behind, and so on, the person behind, takes the place of the person before.
So, what do we do? As the head moves, let the body parts take up the previous position one by one.

// Grass element
var grasslandElement = document.querySelector("#snake-grassland");
// The random x-coordinate of the egg, i.e. the number of rows
var eggX = Math.floor(Math.random() * 10);
// The random y-coordinate of the egg, i.e. the number of columns
var eggY = Math.floor(Math.random() * 10);
// Random grid elements, where eggs need to be placed
// From the children line array below the lawn element
var eggElement = grasslandElement.children[eggX].children[eggY];
// Add an egg to the random grid element
eggElement.innerHTML = '<div class="egg"></div>';
// The horizontal coordinate of the snake head
var snakeX = Math.floor(Math.random() * 10);
// The ordinate of the snake's head
var snakeY = Math.floor(Math.random() * 10);
// Snake element array - currently there is only one snake head
var snakeArray = [{ x: snakeX, y: snakeY }];
// Spawn snake
snakeArray.forEach(function(item, index) {
    // Random grid element representing snake position, same as above
    var snakeElement = grasslandElement.children[item.x].children[item.y];
    // Generate a snake - the first element of the array is the snake's head
    if (index === 0) {
        snakeElement.innerHTML = '<div class="snake snake-head"></div>';
    } else {
        snakeElement.innerHTML = '<div class="snake snake-body"></div>'; }});// The up direction key controls the snake to move up
document.addEventListener("keydown".function(event) {
    // Snake head, the 0th element of the snake array
    var snakeHead = snakeArray[0];
    // If you press up
    if (event.key === "ArrowUp") {
        // Incrementally move to occupy the previous position
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x - 1.y: snakeArray[index].y
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y }); }}); snakeArray = snakeArray2;// Whether the snake eats the egg
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // The egg is eaten and regenerated into one
            eggX = Math.floor(Math.random() * 10);
            // The random y-coordinate of the egg, i.e. the number of columns
            eggY = Math.floor(Math.random() * 10);
            // Random grid element, obtained by the children line array corresponding to the random number below the grass element
            eggElement = grasslandElement.children[eggX].children[eggY];
            // Add an egg to the random grid element
            eggElement.innerHTML = '<div class="egg"></div>';
            // Snake's tail - the most important element in the snake array
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x - 1.y: snakeTail.y }); }}// If you press down
    if (event.key === "ArrowDown") {
        // Incrementally move to occupy the previous position
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x + 1.y: snakeArray[index].y
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y }); }}); snakeArray = snakeArray2;if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // The egg is eaten and regenerated into one
            eggX = Math.floor(Math.random() * 10);
            // The random y-coordinate of the egg, i.e. the number of columns
            eggY = Math.floor(Math.random() * 10);
            // Random grid element, obtained by the children line array corresponding to the random number below the grass element
            eggElement = grasslandElement.children[eggX].children[eggY];
            // Add an egg to the random grid element
            eggElement.innerHTML = '<div class="egg"></div>';
            // Snake's tail - the most important element in the snake array
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x + 1.y: snakeTail.y }); }}// If you press left
    if (event.key === "ArrowLeft") {
        // Incrementally move to occupy the previous position
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x,
                    y: snakeArray[index].y - 1
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y }); }}); snakeArray = snakeArray2;if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // The egg is eaten and regenerated into one
            eggX = Math.floor(Math.random() * 10);
            // The random y-coordinate of the egg, i.e. the number of columns
            eggY = Math.floor(Math.random() * 10);
            // Random grid element, obtained by the children line array corresponding to the random number below the grass element
            eggElement = grasslandElement.children[eggX].children[eggY];
            // Add an egg to the random grid element
            eggElement.innerHTML = '<div class="egg"></div>';
            // Snake's tail - the most important element in the snake array
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x,
                y: snakeTail.y + 1}); }}// If you press right
    if (event.key === "ArrowRight") {
        // Incrementally move to occupy the previous position
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x,
                    y: snakeArray[index].y + 1
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y }); }}); snakeArray = snakeArray2;if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // The egg is eaten and regenerated into one
            eggX = Math.floor(Math.random() * 10);
            // The random y-coordinate of the egg, i.e. the number of columns
            eggY = Math.floor(Math.random() * 10);
            // Random grid element, obtained by the children line array corresponding to the random number below the grass element
            eggElement = grasslandElement.children[eggX].children[eggY];
            // Add an egg to the random grid element
            eggElement.innerHTML = '<div class="egg"></div>';
            // Snake's tail - the most important element in the snake array
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x,
                y: snakeTail.y - 1}); }}// Delete the last snake
    grasslandElement.querySelectorAll(".snake").forEach(function(item) {
        item.parentElement.removeChild(item);
    });
     // Spawn snake
    snakeArray.forEach(function(item, index) {
        // Random grid element representing snake position, same as above
        var snakeElement = grasslandElement.children[item.x].children[item.y];
        // Generate a snake - the first element of the array is the snake's head
        if (index === 0) {
            snakeElement.innerHTML = '<div class="snake snake-head"></div>';
        } else {
            snakeElement.innerHTML = '<div class="snake snake-body"></div>'; }}); });Copy the code



The transformation was successful.
Let’s see. What did we do?
The main one is this code.

// Incrementally move to occupy the previous position
var snakeArray2 = [];
snakeArray.forEach(function(item, index) {
    if (index === 0) {
        snakeArray2.push({
            x: snakeArray[index].x - 1.y: snakeArray[index].y
        });
    } else {
        snakeArray2.push({
            x: snakeArray[index - 1].x,
            y: snakeArray[index - 1].y }); }}); snakeArray = snakeArray2;Copy the code

So let’s see, what does this code do?
First of all, I’ve defined an array, snakeArray2.
Why define it? Used to save the data after the snake moved, and then replace the data before the snake moved.
Why do you do that? This is a story about value types and reference types, a story that we’re not going to tell here, otherwise we could write a whole book about this article.
In short, we’re on the right track: replace the old position with the new one, and the snake moves.
So how does the new position come about? From the snake itself, of course.
So, we iterate over the snake’s current array to generate new location data.
First of all, the head of the snake is a special element.
What’s the special method?
It has no elements in front of it, so it can’t use a placeholder to occupy the previous position, so it has to increase or decrease the x or y value depending on the direction of movement.
If we look closely, we can find that it is the same logic as when the body does not follow the head in front of us.
All that remains, then, is the body.
The body is simple, assign the position of the previous one to itself, end of story.

The snake moves automatically

Yeah, it’s over. It’s abrupt. It’s just amazing.
Now, coming to this section, the snake moves on its own.
Have you ever played a fully manual snake that doesn’t move automatically?
That’s the one up there. It’s a bit of a snake.
So, let’s make it a mainstream snake.
Automatic movement, easy to say the word: timer.
Yeah, that’s how it works. Snake moves.

// Grass element
var grasslandElement = document.querySelector("#snake-grassland");
// The random x-coordinate of the egg, i.e. the number of rows
var eggX = Math.floor(Math.random() * 10);
// The random y-coordinate of the egg, i.e. the number of columns
var eggY = Math.floor(Math.random() * 10);
// Random grid elements, where eggs need to be placed
// From the children line array below the lawn element
var eggElement = grasslandElement.children[eggX].children[eggY];
// Add an egg to the random grid element
eggElement.innerHTML = '<div class="egg"></div>';
// The horizontal coordinate of the snake head
var snakeX = Math.floor(Math.random() * 10);
// The ordinate of the snake's head
var snakeY = Math.floor(Math.random() * 10);
// Snake element array - currently there is only one snake head
var snakeArray = [{ x: snakeX, y: snakeY }];
// Spawn snake
snakeArray.forEach(function(item, index) {
    // Random grid element representing snake position, same as above
    var snakeElement = grasslandElement.children[item.x].children[item.y];
    // Generate a snake - the first element of the array is the snake's head
    if (index === 0) {
        snakeElement.innerHTML = '<div class="snake snake-head"></div>';
    } else {
        snakeElement.innerHTML = '<div class="snake snake-body"></div>'; }});// Automatically move to the right by default
var arrow = "ArrowRight";
// Move the loop function
function MoveLoop (){
    // Snake head, the 0th element of the snake array
    var snakeHead = snakeArray[0];
    // If you press up
    if (arrow === "ArrowUp") {
        // Incrementally move to occupy the previous position
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x - 1.y: snakeArray[index].y
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y }); }}); snakeArray = snakeArray2;// Whether the snake eats the egg
        if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // The egg is eaten and regenerated into one
            eggX = Math.floor(Math.random() * 10);
            // The random y-coordinate of the egg, i.e. the number of columns
            eggY = Math.floor(Math.random() * 10);
            // Random grid element, obtained by the children line array corresponding to the random number below the grass element
            eggElement = grasslandElement.children[eggX].children[eggY];
            // Add an egg to the random grid element
            eggElement.innerHTML = '<div class="egg"></div>';
            // Snake's tail - the most important element in the snake array
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x - 1.y: snakeTail.y }); }}// If you press down
    if (arrow === "ArrowDown") {
        // Incrementally move to occupy the previous position
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x + 1.y: snakeArray[index].y
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y }); }}); snakeArray = snakeArray2;if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // The egg is eaten and regenerated into one
            eggX = Math.floor(Math.random() * 10);
            // The random y-coordinate of the egg, i.e. the number of columns
            eggY = Math.floor(Math.random() * 10);
            // Random grid element, obtained by the children line array corresponding to the random number below the grass element
            eggElement = grasslandElement.children[eggX].children[eggY];
            // Add an egg to the random grid element
            eggElement.innerHTML = '<div class="egg"></div>';
            // Snake's tail - the most important element in the snake array
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x + 1.y: snakeTail.y }); }}// If you press left
    if (arrow === "ArrowLeft") {
        // Incrementally move to occupy the previous position
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x,
                    y: snakeArray[index].y - 1
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y }); }}); snakeArray = snakeArray2;if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // The egg is eaten and regenerated into one
            eggX = Math.floor(Math.random() * 10);
            // The random y-coordinate of the egg, i.e. the number of columns
            eggY = Math.floor(Math.random() * 10);
            // Random grid element, obtained by the children line array corresponding to the random number below the grass element
            eggElement = grasslandElement.children[eggX].children[eggY];
            // Add an egg to the random grid element
            eggElement.innerHTML = '<div class="egg"></div>';
            // Snake's tail - the most important element in the snake array
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x,
                y: snakeTail.y + 1}); }}// If you press right
    if (arrow === "ArrowRight") {
        // Incrementally move to occupy the previous position
        var snakeArray2 = [];
        snakeArray.forEach(function(item, index) {
            if (index === 0) {
                snakeArray2.push({
                    x: snakeArray[index].x,
                    y: snakeArray[index].y + 1
                });
            } else {
                snakeArray2.push({
                    x: snakeArray[index - 1].x,
                    y: snakeArray[index - 1].y }); }}); snakeArray = snakeArray2;if (snakeHead.x === eggX && snakeHead.y === eggY) {
            // The egg is eaten and regenerated into one
            eggX = Math.floor(Math.random() * 10);
            // The random y-coordinate of the egg, i.e. the number of columns
            eggY = Math.floor(Math.random() * 10);
            // Random grid element, obtained by the children line array corresponding to the random number below the grass element
            eggElement = grasslandElement.children[eggX].children[eggY];
            // Add an egg to the random grid element
            eggElement.innerHTML = '<div class="egg"></div>';
            // Snake's tail - the most important element in the snake array
            var snakeTail = snakeArray[snakeArray.length - 1];
            snakeArray.push({
                x: snakeTail.x,
                y: snakeTail.y - 1}); }}// Delete the last snake
    grasslandElement.querySelectorAll(".snake").forEach(function(item) {
        item.parentElement.removeChild(item);
    });
     // Spawn snake
    snakeArray.forEach(function(item, index) {
        // Random grid element representing snake position, same as above
        var snakeElement = grasslandElement.children[item.x].children[item.y];
        // Generate a snake - the first element of the array is the snake's head
        if (index === 0) {
            snakeElement.innerHTML = '<div class="snake snake-head"></div>';
        } else {
            snakeElement.innerHTML = '<div class="snake snake-body"></div>'; }}); }// The up direction key controls the snake to move up
document.addEventListener("keydown".function(event) {
    // Assign the global direction variable
    arrow = event.key;
    // Execute the sequential move function
    MoveLoop();
});
setInterval(function(){
    MoveLoop();
},500);Copy the code



Every half second, or 500 milliseconds, it moves.
How did you do that?
We extract the direction to a global variable, arrow, and use it to control the reverse of the movement.
Then, all the manual code in the monitor is extracted into an external function, which determines the direction of the snake’s movement according to the value of arrow.
Then, the timer goes off and the snake moves automatically.
There are very few changes in the code, but, I’m sure, a lot of people don’t think of them.
That’s programming thinking, getting to the heart of the problem.
We need to exercise this kind of thinking.
One of the purposes of this website is to train students’ programming thinking through case by case and in-depth analysis of articles.
Why do they do it this way?
I think many of you have heard the term programming mind, but it’s hard to understand and hard to make sense of.
Why is that? Because it’s a way of thinking and it’s hard to say.
So how can you share this kind of thinking?
Very simple.
No matter how Zhang SAN described it, Li Si always doubted it.
So what to do? Just take Leo out for a meal.
This is a very simple truth, many people do not understand, do not know.
How to learn programming? To understand from the principle, from the root.
I am Chen Suiyi, motto is: how to solve the problem, only code.
I like to share, like technology, like to make friends, currently committed to reducing the difficulty of learning programming, so that there is no difficult programming in the world.
Programming is easy. You just haven’t learned how to think about programming yet. I’ll teach you if you want to.