[C + + learning notes] III. greedy snake project

Article directory

1 greedy snake logic statement

1.1 creating a snake object

Before creating the snake object, you should print a map of demo, after all, you are going to print it with the console.

const int N = 30;  	//Map width
const int M = 81;  	//Length of map
char map[N][M];		//Map array

Map generate map

void Map()
{
	int i, j;
	for (i = 0; i < N; i++)
	{
		for (j = 0; j < M - 1; j++)
		{
			if (i == 0 || i == N - 1 || j == 0 || j == M - 2)
				map[i][j] = '*';
			else
				map[i][j] = ' ';
		}
		map[i][j] = '\n';
	}
	map[i][j] = '\0';
}

Common dos commands under the console:
system("cls"); for screen clearing
system("pause"); pause
Sleep function needs to add related header file, shortcut key Alt + Enter ()
Windows.h header files under some compilers

void Show_map()
{
	system("cls");		
	printf("%s", map);
	Sleep(100);
}

Create a snake node to store the connection between the snake head and the snake body

struct Node
{
	char x;
	char y;
	struct Node* next;
};

Of course, there are many ways about snake objects. You can type the function directly, and the structure is OK. The example here is just a snake implementation.

/*
Features: - create Snake:
		@
Description:
Example: s.Create_snake(s1);
*/
void Snake::Create_snake(Node* snake)
{
	srand((unsigned)time(NULL));

	snake->x = rand() % 78 + 1;
	snake->y = rand() % 26 + 1;
	//snake->x = 40;
	//snake->y = 16;
	char _x = snake->x;
	char _y = snake->y;
	while (snake_length--[--snakeNum])
	{
		//snake->next = (Node*)malloc(sizeof(Node));
		snake->next = new Node;
		if (!(snake->next))
		{
			cerr << "Failed to apply for snake node" << endl;
		}
		else
		{
			//Cerr < < successfully applied for snake node < < endl;
			snake = snake->next;
			snake->x = _x;
			snake->y = ++_y;
		}
	}
	snake->next = NULL;
	delete (snake->next);
	snakeNum++;
	//free(snake->next);	
}

1.2 Snake Movement

The code is relatively simple, so it will not be explained. The movement here does not refer to the movement of control
Note: local variable, private member variable, often preceded by 'U' or 'm': access permission and action space

/*
Features: - Snake move:
		@
Description:
Example: s.moveUp(s1);;
*/
void Snake::Move_snake(Node* snake)
{
	static char  _x, _y, _x1, _y1;
	_x = snake->x;
	_y = snake->y;
	while (snake = snake->next)
	{
		_x1 = snake->x;
		_y1 = snake->y;
		snake->x = _x;
		snake->y = _y;
		_x = _x1;
		_y = _y1;
	}
}

This is the real control of movement. In fact, it doesn't need to be separated when it's done. Just put it in the switch. Continue passing snake nodes

//Move up 'w'
void Snake::moveUp(Node* snake)
{
	snake->y--;
}
//Move down's'
void Snake::moveDown(Node* snake)
{
	snake->y++;
}
//Move 'a' left
void Snake::moveLeft(Node* snake)
{
	snake->x--;
}
//Move right'd '
void Snake::moveRight(Node* snake)
{
	snake->x++;
}

1.3 positioning of snake body

Here's how the growth of the snake's body works. The bottom moves with the skull

/*
Functions: - body position of Snake growth:
		@
Description:
Example:
*/
void Snake::Growth(char ch, Node* snake)
{
	char _x = snake->x;
	char _y = snake->y;
	snake = snake->next;
	switch (ch)
	{
	case 'w':
		snake->x = _x;
		snake->y = ++_y;
		break;
	case 's':
		snake->x = _x;
		snake->y = --_y;
		break;
	case 'a':
		snake->x = ++_x;
		snake->y = _y;
		break;
	case 'd':
		snake->x = --_x;
		snake->y = _y;
		break;
	default:
		break;
	}
	snake->next = NULL;
}

1.4 hiring snake

The above logic of rolling snake needs to be printed on the map

/*
Functions: - hire Snake:
		@
Description:
Example:
*/
void Snake::Entry_snake(Node* snake)
{
	Map();
	map[snake->y][snake->x] = '@';
	while (snake = snake->next)
		map[snake->y][snake->x] = '+';
	map[food.y][food.x] = '$';
}

1.5 test the normal operation of snake object

Now test whether the snake is moving normally. Run at will to confirm whether there is any problem with the snake

void SnakeTest()
{
	Snake s;
	Node* s1 = new Node;
	//Node* s2 = new Node;
	s.Map();
	s.Create_snake(s1);

	//s.Create_snake(s2);
	//s.Entry_snake(s2);
	s.Refresh_Food(s1);
	for (int i = 0; i < 3; i++)
	{
		s.Move_snake(s1);
		s.moveUp(s1);
		s.Entry_snake(s1);
		Sleep(100);
		s.Show_map();
	}
	for (int i = 0; i < 3; i++)
	{
		s.Move_snake(s1);
		s.moveDown(s1);
		s.Entry_snake(s1);
		Sleep(100);
		s.Show_map();
	}
	for (int i = 0; i < 3; i++)
	{
		s.Move_snake(s1);
		s.moveLeft(s1);
		s.Entry_snake(s1);
		Sleep(100);
		s.Show_map();
	}
	for (int i = 0; i < 3; i++)
	{
		s.Move_snake(s1);
		s.moveRight(s1);
		s.Entry_snake(s1);
		Sleep(100);
		s.Show_map();
	}
	delete s1;
}

Because it's still in demo, it's all plugged into public temporarily

#ifndef __SNAKE_H
#define __SNAKE_H

const int N = 30;  //Map width
const int M = 81;  //Length of map
//const int N = 20; / / width of map
//const int M = 61; / / length of map
//Structure of snake node
struct Node
{
	char x;
	char y;
	struct Node* next;
};

//Structure of food
struct Food
{
	char x;
	char y;
};

class Snake
{
public:
	Snake();
	Snake(int);
	~Snake();

	void Create_snake(Node*);	//Create snake
	void Move_snake(Node*);		//Mobile snake
	void Growth(char, Node*);	//Position the new body

	void moveUp(Node*);			//Upward movement
	void moveDown(Node*);		//Downward movement
	void moveLeft(Node*);		//Move to the left
	void moveRight(Node*);		//Move to the right

	void Entry_snake(Node*);	//Entry of snake

	void Refresh_Food(Node*);	//Refresh food

	void Map();					//Snake map
	void Show_map();			//Output function

	int* snake_length;			//Length of snake
	int snakeNum = 1;			//Number of snakes / / no two snakes yet

	Food food;					//Define the structure of food

private:

};
#endif // !__SNAKE_H

2 food

2.1 food structure

Food is simple, just two coordinates

//Structure of food
struct Food
{
	char x;
	char y;
};

2.2 update food

Snakes update when they eat

/*
Features: - update food:
		@
Description:
Example:
*/
void Snake::Refresh_Food(Node* snake)
{
	food.x = rand() % 78 + 1;
	food.y = rand() % 28 + 1;
	while (snake)
	{
		if (food.x == snake->x && food.y == snake->y)
		{
			Refresh_Food(snake);
		}	
		snake = snake->next;
	}
}

3 Game

I'm afraid of chaos. I'll pull out a game class directly. There's only one game in it
1) : snake initialization
2) : snake control
3) Judge whether the snake is dead

3.1 initialization of snake

All initialization has to do is create the snake, start refreshing a food, and refresh the map

/*
Features: - Construction: Game()
		@
Description:
Example:
*/
Game::Game()
{
	snake = new Node;
	s.Map();
	s.Create_snake(snake);

	s.Refresh_Food(snake);

	s.Entry_snake(snake);
	Sleep(100);
	s.Show_map();
}

3.2 snake control

Get the key value of the keyboard and judge it.
Don't try to use goto. It's just convenient. You can use it directly without much code

/*
Functions: - Snake control:
		@
Description:
Example:
*/
void Game::Control()
{
	while (!_kbhit());
	char c = _getch();
	char nextC;
z:	s.Move_snake(snake);
	switch (c)
	{
	case 'w':	s.moveUp(snake);		break;	//Upward movement
	case 's':	s.moveDown(snake);		break;	//Downward movement
	case 'a':	s.moveLeft(snake);		break;	//Move to the left
	case 'd':	s.moveRight(snake);		break;	//Move to the right
	default:							break;
	}
	died(c);
	s.Entry_snake(snake);	//Hiring snake
	s.Show_map();			//Update map
	if (_kbhit())
	{
		nextC = _getch();
		if (nextC == 'w' || nextC == 'a' || nextC == 'd' || nextC == 's')
		{
			if ((nextC == 'w' && c == 's') || (nextC == 's' && c == 'w') || (nextC == 'a' && c == 'd') || (nextC == 'd' && c == 'a'))
				nextC = c;
			else
				c = nextC;
		}
	}
	else
		nextC = c;

	//Bite and die
	goto z;
}

3.3 judge whether the snake is still alive

How did the snake die?
Bite yourself?
Hit a wall and die?
Is there any other way to die?
It's a single person, don't think too much. It's faster for two people to make the engine. Unit processing is much simpler than this.
Paste code directly

/*
Function: - judge the survival of Snake:
		@
Description:
Example:
*/
void Game::died(char c)
{
	Node* _snake = snake;
	if ((snake->x == s.food.x) && (snake->y == s.food.y))
	{
		while (_snake->next)
			_snake = _snake->next;
		_snake->next = (Node*)malloc(sizeof(Node));
		s.Growth(c, _snake);
		s.snake_length++;
		if (s.snake_length[s.snakeNum - 1] == (N - 2) * (M - 3))
		{
			fprintf(stderr, "Congratulations on your success, you are so amazing!!!");
			exit(EXIT_FAILURE);
		}
		s.Refresh_Food(snake);
		return;
	}
	if ((snake->x == 0) || (snake->x == (M - 2)) || (snake->y == 0) || (snake->y == (N - 1)))
	{
		fprintf(stderr, "Why are you hitting the wall");
			exit(EXIT_FAILURE);
	}
	while (_snake = _snake->next)
	{
		if (snake->x == _snake->x && snake->y == _snake->y)
		{
			fprintf(stderr, "you TMD What are you doing? You can't go back");
				exit(EXIT_FAILURE);
		}
	}
}

Header file of Game class

#ifndef __GAME_H
#define __GAME_H

#include "Snake.h"

class Game
{
public:
	Game();
	~Game();
	void Control();
	void died(char c);

private:
	Snake s;
	Node* snake;
};
#endif // !__GAME_H

Finally, add the following related contents to the main function

void GameTest();

int main()
{
	GameTest();
	//cout << "time = " << (double)(clock()) / CLOCKS_PER_SEC << "s" << endl;
	//_CrtDumpMemoryLeaks();
	system("pause");
	return 0;
}

void GameTest()
{
	Game g1;
	while (1)
	{
		g1.Control();
	}
}

The operation results are as follows:

wsad: control up, down, left and right
Run successfully, don't panic, I've sorted out two copies of my own source code for reference, all of which can run normally
Here's the source code of greedy snake
Greedy Snake source code

Published 3 original articles, praised 0 and visited 25
Private letter follow

Tags: Windows Mobile

Posted on Wed, 29 Jan 2020 01:26:34 -0800 by skalooky