C language beginners project 200 lines of code, using a two-dimensional array to realize the snake game

Keywords: C Visual Studio

C language beginner project: a two-dimensional array to realize the greedy Snake game

This article is for c Language beginners, so I try to use the most basic syntax to realize the whole game, and only use a two-dimensional array. The implementation process will be explained in detail below.
A two-dimensional array can be directly printed into a plane to form the background of the whole game. We can control the movement of snakes and the generation of food by changing the content of the array.
First establish the whole game framework
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.h>
#define ROW 10 / / number of rows
#define COL 25 / / number of columns
#define SPEED 100 / / this is actually the refresh rate, so the lower the speed, the faster it will be. It will be referenced later
int a[ROW][COL];
int main(void)
{
    char play;
	do
	{
		game();
		printf("1:continue\n");
		printf("2:exit\n");
		play = getch();
	} while (play=='1');
	printf("exit\n");
	return 0;
}
The two-dimensional array is established as a global variable to initialize all the numbers of the array to 0. Then we can use different numbers in the array to represent different things. For example, we initialize the border of the array to-2,-2 It represents the wall
	for (int i = 0; i < ROW; i++)//Generate walls
		for (int j = 0; j < COL; j++)
			if (i == 0 || j == 0 || i == ROW - 1 || j == COL - 1)
				a[i][j] = -2;

The digital processing of snakes is very important, which involves the movement of snakes. In fact, the movement of snakes can be understood as moving the tail to the front of the snake. If the snake wants to move up, it is to move the tail to the top of the head. Similarly, moving to the right, that is, moving the tail to the right of the head. I understand this movement mode, You can create the value of the snake in the array. Make the value of the head 1, and then the body block is 2, 3, and until the value of the tail. We initialize the snake with three sections, so the number of tails is 3 (of course, you can also initialize other values).

	a[ROW-2][1] = 3;//Generative snake
	a[ROW-2][2] = 2;//Generative snake
	a[ROW-2][3] = 1;//Generate snake head
	int tailmax = 3;//The length of the snake depends on this value

Food generation is relatively simple. Take the random number within the range of the array. If it is an inappropriate position, reset it and set the value of food to - 1

void toeat(int a[ROW][COL])//Produce food
{
	while (1)
	{
		int x = rand() % ROW;
		int y = rand() % COL;
		if (x != 0 && y != 0/*Not on the border*/ && a[x][y] == 0/*Can only be generated in empty areas*/)
		{
			a[x][y] = -1;
			break;
		}
	}
}

s
Now the basic processing of the array is completed. There are four kinds of values in the array. One is - 2, indicating the wall, one is - 1, indicating food, and one is 0, indicating the blank area. The remaining positive numbers all represent the snake, and the snake head is 1. Then you can write the print function

void print(int a[ROW][COL])//Print
{
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			if ((i == 0 || i == ROW - 1) && j != 0 && j != COL - 1)//Upper and lower boundary
				printf("-");
			else if ((j == 0 || j == COL - 1) && i != 0 && i != ROW - 1)//Left and right boundary
				printf("|");
			else if ((i == 0 || i == ROW - 1) && (j == 0 || j == COL - 1))//Four corners
				printf("+");
			else if (a[i][j] == 0)//white space
				printf(" ");
			else if (a[i][j] == 1)//Snake head, I use the letter 'O'
				printf("O");
			else if (a[i][j] == -1)//food
				printf("$");
			else
				printf("*");//The rest are positive numbers, which are snake bodies
		}
		printf("\n");
	}
}

The first three if alternatives may be a little scary, because I want to create a (slightly) beautiful boundary. If you are too troublesome, you can directly make the boundary a character, as follows

void print(int a[ROW][COL])//Print
{
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
		    if(a[i][j]==-2)
		        printf("*");
			else if (a[i][j] == 0)
				printf(" ");
			else if (a[i][j] == 1)
				printf("O");
			else if (a[i][j] == -1)
				printf("$");
			else
				printf("*");
		}
		printf("\n");
	}
}

After all these are handled, we can move the snake. We are used to using wsad to represent up, down, left and right (not). First of all, how does it mean that a snake moves up once?

From the above, we know that as long as we find the head and tail coordinates and put the tail above the head, but the snake is a series of continuous numbers, which must be processed in the array. After obtaining the head and tail coordinates, we can first make the tail disappear, that is, set the coordinates to 0, and then add 1 to all the snake body numbers including the snake head, so that the original snake head number becomes 2, Now that there is no snake head, we can set the number above the original snake head (if the head is a[i][j], the number above the head is a[i-1][j]) to 1, the new snake head will be generated, and the value of the snake's new tail will not change. In this way, we simply realize the operation of moving the snake up one grid, and the conversion code is as follows

	int handx, handy;//Snakehead coordinates
	int tailx, taily;//Snake tail coordinates
			for(int i=0;i<ROW;i++)
			for(int j=0;j<COL;j++)
				if (a[i][j] == 1)
				{
					handx = i;
					handy = j;
				}//Find head coordinates
		for(int i=0;i<ROW;i++)
			for(int j=0;j<COL;j++)
				if (a[i][j] == tailmax)
				{
					tailx = i;
					taily = j;
				}//To find the tail coordinates, the previous tailmax is used here
		if (a[handx - 1][handy] == 0)//Judge whether the front is blank and can walk
		{
				a[tailx][taily] = 0;//Snake tail is set to 0
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2)//Exclude walls, food, blanks
							a[i][j]++;//The snake's body and head are increased by 1
				a[handx - 1][handy] = 1;//Set new snakehead
		}

Using this logic, we can easily realize the process of eating food. If the value of the forward direction is - 1, that is, food, add 1 to all the values of the whole snake, head and tail, and then set the value of food to 1 to form a new snake head

			else if (a[handx - 1][handy] == -1)//Judge food
			{
				for (int i = 0;i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2&&a[i][j]!=-5)
							a[i][j]++;
				a[handx - 1][handy] = 1;
				tailmax++;//The snake grows longer, so the value of the tail should be increased by 1
				toeat(a);.//Generate a new food

The front of the snake is not only blank and food, but also the wall and snake body. If you continue to move forward, the game will end

			else 
			gameover = 1;//Later use

We can move once, and we can move in four directions by slightly modifying the parameters of the two-dimensional array. However, the movement of the snake is a continuous process. We should use the cycle to realize continuous movement, but the simple cycle is very fast, so the snake will move quickly. In order to avoid this situation, we need to use the Sleep function in windows.h, namely Sleep(m), The value of M is the millisecond of pause. We call the previously defined SPEED, and Sleep(SPEED) can control the SPEED of snake movement.
In addition, because the snake is moving and the program is running all the time, we must input the instructions of wsad to control the snake. It is impossible to input the instructions once the snake moves. It is too troublesome, so we need to learn a new function_ kbhit(), which determines whether your keyboard has entered. If not, it returns 0, and if yes, it returns a non-0 value. Put it at the back of each circular movement, and then use getct to directly read the input value without echo, so as to operate the snake.
A complete movement is as follows

while(1)
{
		if(move=='w')//Take moving up as an example
		{
			if (a[handx - 1][handy] == 0)//Judge walkability
			{
				a[tailx][taily] = 0;
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2)
							a[i][j]++;
				a[handx - 1][handy] = 1;
			}
			else if (a[handx - 1][handy] == -1)//Judge food
			{
				for (int i = 0;i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2&&a[i][j]!=-5)
							a[i][j]++;
				a[handx - 1][handy] = 1;
				tailmax++;
				toeat(a);
			}
			else gameover = 1;//Judge game failure
			break;
		system("cls");//Clear previous screen
		print(a);//Print a new situation, so refresh the basic implementation of the snake's dynamic process
		Sleep(SPEED);//Stop it
		if (_kbhit())//Judge whether there is input without skipping directly
			{
				char cao = getch();//Accept with another variable to avoid accepting garbage values other than wsad
				if (cao == 'w' || cao == 's' || cao == 'a' || cao == 'd')
					move = cao;
			}
		}
}		

To judge the four moving directions, we use the switch statement inside the while loop to judge the value of move and determine the moving direction,
At the same time, set a value int gameover=1 to judge whether the game is over. The specific codes are as follows

	while (!gameover)
	{
		for(int i=0;i<ROW;i++)
			for(int j=0;j<COL;j++)
				if (a[i][j] == 1)
				{
					handx = i;
					handy = j;
				}//Find head coordinates
		for(int i=0;i<ROW;i++)
			for(int j=0;j<COL;j++)
				if (a[i][j] == tailmax)
				{
					tailx = i;
					taily = j;
				}//Find tail coordinates
		switch (move)
		{
		case'w':
			if (a[handx - 1][handy] == 0)//Judge walkability
			{
				a[tailx][taily] = 0;
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2)
							a[i][j]++;
				a[handx - 1][handy] = 1;
			}
			else if (a[handx - 1][handy] == -1)//Judge food
			{
				for (int i = 0;i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2&&a[i][j]!=-5)
							a[i][j]++;
				a[handx - 1][handy] = 1;
				tailmax++;
				toeat(a);
			}
			else gameover = 1;
			break;
		case's':
			if (a[handx + 1][handy] == 0)
			{
				a[tailx][taily] = 0;
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2 && a[i][j] != -5)
							a[i][j]++;
				a[handx + 1][handy] = 1;
			}
			else if (a[handx + 1][handy] == -1)
			{
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2 && a[i][j] != -5)
							a[i][j]++;
				a[handx + 1][handy] = 1;
				tailmax++;
				toeat(a);
			}
			else gameover = 1;
			break;
		case'a':
			if (a[handx][handy-1] == 0)
			{
				a[tailx][taily] = 0;
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2 && a[i][j] != -5)
							a[i][j]++;
				a[handx][handy - 1] = 1;
			}
			else if (a[handx][handy-1] == -1)
			{
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2 && a[i][j] != -5)
							a[i][j]++;
				a[handx][handy - 1] = 1;
				tailmax++;
				toeat(a);
			}
			else gameover = 1;
			break;
		case'd':
			if (a[handx][handy + 1] == 0)
			{
				a[tailx][taily] = 0;
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2 && a[i][j] != -5)
							a[i][j]++;
				a[handx][handy + 1] = 1;
			}
			else if (a[handx][handy + 1] == -1)
			{
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2 && a[i][j] != -5)
							a[i][j]++;
				a[handx][handy + 1] = 1;
				tailmax++;
				toeat(a);
			}
			else gameover = 1;
			break;
		}
		system("cls");
		print(a);
		Sleep(SPEED);
		if (_kbhit())//Judge whether there is input
		{
			char cao = getch();
			if (cao == 'w' || cao == 's' || cao == 'a' || cao == 'd')
				move = cao;
		}
	}
	printf("YOU DIE!\n");
	printf("score:%d", tailmax);//Just use the tailmax value to judge the snake length, indicating the score
}

The complete code is as follows

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.h>
#define ROW 10
#define COL 25
#define SPEED 100
int a[ROW][COL];
void toeat(int a[ROW][COL])//Produce food
{
	while (1)
	{
		int x = rand() % ROW;
		int y = rand() % COL;
		if (x != 0 && y != 0 && a[x][y] == 0)
		{
			a[x][y] = -1;
			break;
		}
	}
}
void print(int a[ROW][COL])//Print
{
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			if ((i == 0 || i == ROW - 1) && j != 0 && j != COL - 1)
				printf("-");
			else if ((j == 0 || j == COL - 1) && i != 0 && i != ROW - 1)
				printf("|");
			else if ((i == 0 || i == ROW - 1) && (j == 0 || j == COL - 1))
				printf("+");
			else if (a[i][j] == 0)
				printf(" ");
			else if (a[i][j] == 1)
				printf("O");
			else if (a[i][j] == -1)
				printf("$");
			else
				printf("*");
		}
		printf("\n");
	}
}
void game()
{
	system("cls");
	for (int i = 0; i < ROW; i++)//Reset the array, otherwise the corpse of the previous snake will appear when you restart the game, and even die when you hit it, and the amount of food will be increased by 1. It's inexplicably interesting. If you want to experience, you can comment on these three lines
		for (int j = 0; j < COL; j++)
			a[i][j] = 0;
	srand((unsigned int)time(NULL));
	for (int i = 0; i < ROW; i++)//Generate walls
		for (int j = 0; j < COL; j++)
			if (i == 0 || j == 0 || i == ROW - 1 || j == COL - 1)
				a[i][j] = -2;
	a[ROW-2][1] = 3;//Generative snake
	a[ROW-2][2] = 2;//Generative snake
	a[ROW-2][3] = 1;//Generate snake head
	print(a);
	toeat(a);
	char move='w';//The default is to move up first. You can also change to other directions
	int handx, handy;
	int tailx, taily;
	int tailmax = 3;//Snake length
	int gameover=0;//Determine whether the game is over
	printf("\nPress any key to cintinue");
	char flag = getch();//Press any key to start the game
	while (!gameover)
	{
		for(int i=0;i<ROW;i++)
			for(int j=0;j<COL;j++)
				if (a[i][j] == 1)
				{
					handx = i;
					handy = j;
				}//Find head coordinates
		for(int i=0;i<ROW;i++)
			for(int j=0;j<COL;j++)
				if (a[i][j] == tailmax)
				{
					tailx = i;
					taily = j;
				}//Find tail coordinates
		switch (move)
		{
		case'w':
			if (a[handx - 1][handy] == 0)//Judge walkability
			{
				a[tailx][taily] = 0;
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2)
							a[i][j]++;
				a[handx - 1][handy] = 1;
			}
			else if (a[handx - 1][handy] == -1)//Judge food
			{
				for (int i = 0;i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2&&a[i][j]!=-5)
							a[i][j]++;
				a[handx - 1][handy] = 1;
				tailmax++;
				toeat(a);
			}
			else gameover = 1;
			break;
		case's':
			if (a[handx + 1][handy] == 0)
			{
				a[tailx][taily] = 0;
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2 && a[i][j] != -5)
							a[i][j]++;
				a[handx + 1][handy] = 1;
			}
			else if (a[handx + 1][handy] == -1)
			{
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2 && a[i][j] != -5)
							a[i][j]++;
				a[handx + 1][handy] = 1;
				tailmax++;
				toeat(a);
			}
			else gameover = 1;
			break;
		case'a':
			if (a[handx][handy-1] == 0)
			{
				a[tailx][taily] = 0;
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2 && a[i][j] != -5)
							a[i][j]++;
				a[handx][handy - 1] = 1;
			}
			else if (a[handx][handy-1] == -1)
			{
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2 && a[i][j] != -5)
							a[i][j]++;
				a[handx][handy - 1] = 1;
				tailmax++;
				toeat(a);
			}
			else gameover = 1;
			break;
		case'd':
			if (a[handx][handy + 1] == 0)
			{
				a[tailx][taily] = 0;
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2 && a[i][j] != -5)
							a[i][j]++;
				a[handx][handy + 1] = 1;
			}
			else if (a[handx][handy + 1] == -1)
			{
				for (int i = 0; i < ROW; i++)
					for (int j = 0; j < COL; j++)
						if (a[i][j] != 0 && a[i][j] != -1 && a[i][j] != -2 && a[i][j] != -5)
							a[i][j]++;
				a[handx][handy + 1] = 1;
				tailmax++;
				toeat(a);
			}
			else gameover = 1;
			break;
		}
		system("cls");
		print(a);
		Sleep(SPEED);
		if (_kbhit())//Judge whether there is input
		{
			char cao = getch();
			if (cao == 'w' || cao == 's' || cao == 'a' || cao == 'd')
				move = cao;
		}
	}
	printf("YOU DIE!\n");
	printf("score:%d", tailmax);
}
int main(void)
{
    char play;
	do
	{
		game();
		printf("\n1:continue");
		printf("\n2:exit");
		play = getch();
	} while (play=='1');
	printf("\nexit");
	return 0;
}

The last problem: because this refresh method is used, the eyes may be uncomfortable. In addition, if the snake goes in the opposite direction, the game will end immediately (no one should do this).

Posted by Zeekar on Tue, 16 Nov 2021 08:38:38 -0800