[Complete the snake game with C++ object-oriented thinking and STL]

1 Introduction

1.1 Purpose

I didn’t care much about the knowledge of STL in the past. Recently, I felt that there were too many places where STL was used in engineering codes at work. I didn’t understand it. , Then I thought that I could write a greedy snake to learn and sell now, and deepen the image.

1.2 Recommendations

It is recommended that students who are unfamiliar with STL can review STL before reading this article.

1.3 Development environment

Language: C++
Environment: Visual Studio 2022, Windows

2. Achievement display

No ink marks, first show the results.
insert image description here
insert image description here

3. Project requirements

1. Initialize the game, generate maps, snakes, and food
2. Snakes grow longer after eating food
3. Snakes cannot move in reverse 4.
Immediately produce new ones after food is eaten
5. Record the number of food eaten (score)
6 .Food cannot be generated on the snake body
7. The snake cannot hit itself
8. Set the difficulty of the game
9. If the snake hits the wall, the game ends directly

4. Detailed design

4.1 Overview

As mentioned earlier, it is written with object-oriented thinking, so it must generate snakes, food, and points. Why not maps, because I first use the method of drawing points, and then use points to draw square map. And you can also use dots to draw snakes and food.

Snakes:

insert image description here

Food category:
insert image description here

point class:
insert image description here

4.2 Initialization

4.2.1 Map

Before introducing how to initialize the map, let's first understand how to output a small square with code in the terminal.
That's right, just print out cout, but I want to print it out at a known coordinate?
Then you need to use cursor positioning:

void gotoxy(int x, int y) //光标定位
{
    
    
	COORD pos = {
    
     x,y };
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

Then just output the squares, use the symbols in the certain dog input method:
insert image description here
Then you can print the squares at specific coordinates, and then you can continue to print out maps with custom width and height.
需要强调一点,终端的光标的高 = 2 * 宽,所以我们方块的坐标想在终端输出(4,4)的位置,就需要在代码中用(8,4)来代替。

4.2.1 Snakes and food

Then let's continue to understand how to initialize the snake and food. The initialization must be generated with random coordinates, which requires the use of the rand() function, and then an integer in the interval [a,b] can be generated:

randNum = rand() % (b - a + 1) + a;

It's been so long since you can simply randomly spawn a snake head and food on the terminal.

For the whole snake, I use containers vector< pair< int, int > >to store coordinates, and for food, I use pair< int, int >

4.3 The body of the snake becomes longer after eating the food

First, you need to realize the movement logic of the snake: initialize the snake to move to the right, assuming the snake head coordinates (x, y), then add this coordinate (x-1, y) to the tail element of the vector, then delete the head element, and "插尾删头".

这里我们还需要定义一个oldSnack vector来打印空格,来抹去上一次蛇的身体,不然蛇是会一直覆盖,你会发现,蛇尾看起来没有被删除。

Then you can realize that the body of the snake becomes longer after eating food. This logic is by adding food coordinates to the tail element of the vector without deleting the head element, and “只插尾”.

4.4 Snakes cannot move in reverse

That is, if the moving direction of the snake is right, and I accidentally press the A key (left), the snake cannot move back.
A variable oldDirection needs to be defined to record the last moving direction to judge with the currently acquired direction.
insert image description here

4.5 Refresh after food is eaten

Refresh is very simple, just regenerate one, but how to judge whether the food is eaten, you can check whether the current coordinates of the snake head are equal to the food coordinates,
if not, it means that the snake has not been eaten, then continue to move the snake body, if it is eaten, regenerate food.
insert image description here
It is also here to record the number of food eaten. For myCount in the above screenshot, if you want to make a score, you can multiply it by a food score coefficient.

4.6 Food cannot spawn in snakes

Here you need to regenerate first, and then make a judgment.
insert image description here
Here we encapsulate the judgment whether the food is produced in the snake body, using find() in the STL algorithm:

bool IsFoodEqualSnack(vector<pair<int, int>> vec, pair<int, int> food)
{
    
    
	if (!vec.empty())
	{
    
    
		auto result = find(begin(vec), end(vec), food);
		if ( result != vec.end() )
		{
    
    
			return true;
		}
		else
		{
    
    
			return false;
		}
	}
	else
	{
    
    
		cout << "vector is empty" << endl;
		return false;
	}
	cout << endl;

}

4.7 Setting Game Difficulty

At present, the movement speed is controlled by Sleep(). The logic is that the length of the current snake minus the length at the time of initialization, and then take the remainder of 2, that is, every time two foods are eaten, the movement speed increases by one gear.

void setSpeed(Snack objSnack)
{
    
    
	Sleep( 1000 / speedLevel ); 
	if ( (objSnack.mV.size() - 3 != 0))
	{
    
    
		if ( (oldspeed != (objSnack.mV.size() - 3) % 2) &&
			 ((objSnack.mV.size() - 3) % 2 == 0) )
		{
    
    
			speedLevel++;
		}
	}
	oldspeed = (objSnack.mV.size() - 3) % 2;
}

4.8 A snake cannot hit itself

The logic we need here is that repeated elements cannot be included in the snake body vector. How can this be achieved?
It's very simple. Using the unique feature of the set container, we assign the snake body vector to a set container, and judge whether the size of the set is the same as the size of the snake body vector. It's up to me, GameOver.

bool Snack::isHitItself()
{
    
    
    mSet = {
    
     mV.begin(),mV.end() };
    if (mSet.size() != mV.size())//蛇头撞到了蛇身
    {
    
    
        return true;
    }
    else
    {
    
    
        return false;
    }
}

4.8 Snakes cannot hit walls

This is very simple, there is nothing to say.

bool Snack::isHitWall()
{
    
    
    if ( (mV.end()-1)->first == MAP_X *2  ||
         (mV.end() - 1)->first == 0       ||
         (mV.end() - 1)->second == MAP_Y  ||
         (mV.end() - 1)->second == 0)
    {
    
    
        return true;
    }
    else
    {
    
    
        return false;
    }
}

5. Flowchart

insert image description here

6. Code

If the environment is right, it can definitely run normally. 99.99% of the bugs have been solved during the development process, and there are no bugs anymore.
code download

7. Conclusion

The content of this article is completely original and is not allowed to be reproduced! ! !

Guess you like

Origin blog.csdn.net/qq_45458713/article/details/129410974