C语言: 贪吃蛇
标签: C语言 贪吃蛇
by 小威威
这个贪吃蛇是作业来着,所以按照作业的要求做的,只在12*12的区域移动。在放代码之前,我要说一下我在做贪吃蛇的时候遇到的非常隐蔽的BUG:就是已经实现了贪吃蛇,在吃食物的时候,有的时候程序会出现异常,即贪吃蛇.exe遇到问题需要退出。其实,这主要是栈溢出导致的。那哪里栈溢出了呢?
你可以检查一下生成随机食物那一部分代码,你是否用了递归?
如果有,那问题就出在这了。解决方法就是将递归改成while循环,注意将srand((unsigned)time(NULL))放在循环外。
为什么要这样做呢?因为随机数字时以秒为单位产生,即一秒产生一组随机数字,然而,倘若你用递归,产生的随机数字不符合要求而再次调用此函数时,用的时间远远小于1s,随机数字并没有改变,即是此时的随机数依旧是不符合的所以它会继续递归,最终导致栈溢出(因为CPU执行的速度很快,不到1s就已经把栈填满了)
我的代码没有改成while循环,确实有点瑕疵,不过为了赶交作业嘛~
还有一个问题要说明一下:就是一定要自己思考,争取自己把贪吃蛇做出来。遇到不懂的就去查,而不是一味看别人的代码。比如:我想实现清屏,我就去查清屏函数怎么用,而不是看别人的贪吃蛇代码照搬照用。再如,别人的代码中有kbhit()和getch(),而我们想要的是读取键盘输入的值,只需要getch(),根本不需要kbhit()。你就不能照抄别人的,把kbhit()也给写上去,因为你不知道kbhit究竟有什么用。当你用getch()函数编写出贪吃蛇代码,发现只能通过按键来实现贪吃蛇移动,不能实现贪吃蛇自动移动,我们就会想到,怎样让我的贪吃蛇在我没有按键盘时保持原来的方向继续移动,通过查询我们就知道了kbhit()函数就是发挥这个作用,我们也就理所当然的将这个函数用到我们的代码中去。还有其他小细节阿如怎么产生随机食物阿,rand()后的%有什么作用阿…这些都要通过思考,转变成自己的知识,再应用到贪吃蛇代码,如此,当你完成了贪吃蛇代码后,你就会觉得收获颇丰,而不是吐槽这份作业是多么多么地坑!!!
下面放代码:
# include <stdio.h>
# include <stdlib.h>
# include <conio.h>
# include <windows.h>
# include <time.h>
# define SNAKE_MAX_LENGTH 20
# define SNAKE_HEAD 'H'
# define SNAKE_BODY 'X'
# define BLANK_CELL ' '
# define SNAKE_FOOD '$'
# define WALL_CELL '*'
void snakemove(int, int); // Achieve the snake's movement
void put_money(void); // Achieve the random money occurring
void output(void); // Print the array of map
void gameover(void); // Stop the game and print "game over"
char map[12][13] = {
"************",
"*XXXXH *",
"* *",
"* *",
"* *",
"* *",
"* *",
"* *",
"* *",
"* *",
"* *",
"************"
}; // This is the map
int snakeX[SNAKE_MAX_LENGTH] = {1, 2, 3, 4, 5}; // Define this array to instruct the colunmn coordinate of snake
int snakeY[SNAKE_MAX_LENGTH] = {1, 1, 1, 1, 1}; // Define this array to instruct the row coordinate of snake
int snakeLength = 5; // The original length of snake
int money_exist = 0; // Used to judged the money's existion
int eatfood = 0; // Used to judged whether the snake has eaten the food
int main(void) {
output(); // Print the mao
while(snakeLength <= SNAKE_MAX_LENGTH-1) { // Set the loop to achieve entering messages more times
int ch; // The valuable we set to store the input value
if (kbhit()) // Judge whether the user has hit the keyboard
ch = getch(); // Enter the message without hit the "ENTER"
switch(ch) { // According to the message they enter, choose the respectly way to perform
case 'w':
case 'W':
snakemove(0, -1); // Call the function to move the snake
break;
case 's':
case 'S':
snakemove(0, 1); // Call the function to move the snake
break;
case 'a':
case 'A':
snakemove(-1, 0); // Call the function to move the snake
break;
case 'd':
case 'D':
snakemove(1, 0); // Call the function to move the snake
break;
default:
break;
}
Sleep(500); // To pause the for 0.5 seconds.
system("CLS"); // To empty all the thing in the screen
output(); // Print the map
}
system("CLS"); // To empty all the thing in the screen
printf("You are so great~\n"); // When you pass it will be print
return 0;
}
void snakemove(int dx, int dy) {
if (map[snakeY[snakeLength-1]+dy][snakeX[snakeLength-1]+dx] == WALL_CELL
|| map[snakeY[snakeLength-1]+dy][snakeX[snakeLength-1]+dx] == SNAKE_BODY)
gameover(); // If the next step is the wall or itself,it means game over
for (int i = 0; i < snakeLength; i++) {
map[snakeY[i]][snakeX[i]] = BLANK_CELL; // Empty the snake in the map first
}
for (int i = 0; i < snakeLength-1; i++) {
if (map[snakeY[snakeLength-1]+dy][snakeX[snakeLength-1]+dx] == SNAKE_FOOD) {
snakeLength++; // Jugde if the next step is the money and perform respect ways
money_exist = 0; // Set the value to 0
snakeX[snakeLength-1] = snakeX[snakeLength-2]+dx;
snakeY[snakeLength-1] = snakeY[snakeLength-2]+dy;
map[snakeY[snakeLength-1]][snakeX[snakeLength-1]] = SNAKE_HEAD;
// Let the location of food becomes the head of snake
eatfood = 1; // Set the value to 1 means it has eaten the food
map[snakeY[snakeLength-2]][snakeX[snakeLength-2]] = SNAKE_BODY;
// Change the ordinary head to the part of body
}
if (eatfood == 0) { // If the next step is not food, move every part of the body to the former one
snakeX[i] = snakeX[i+1];
snakeY[i] = snakeY[i+1];
map[snakeY[i]][snakeX[i]] = SNAKE_BODY; // Move to the former one
if (i == snakeLength-2) { // Set the first one as the head of snake
snakeX[i+1] = snakeX[i+1] + dx;
snakeY[i+1] = snakeY[i+1] + dy;
map[snakeY[i+1]][snakeX[i+1]] = SNAKE_HEAD;
}
} else {
eatfood = 0; // Successfully add the length of snake and perform this statement
}
}
if (money_exist == 0) { // Judge whether the money have been put
put_money();
money_exist = 1;
}
if (money_exist == 0) { // In case of the fail of putting the money
put_money();
money_exist = 1;
}
}
void put_money(void) { // Define the function of put_money
srand((unsigned)time(NULL)); // Set the random seed
int dx, dy, ran; // ran means the random number
while (1) {
if ((ran = rand()%10) > 2) { // Creat the correct coordinate
dx = ran;
break;
}
}
while (1) {
if ((ran = rand()%10) > 2) { // Creat the correct coordinate
dy = ran;
break;
}
}
if (map[dx][dy] != SNAKE_BODY && map[dx][dy] != SNAKE_HEAD) // In case of the incorret location
map[dx][dy] = SNAKE_FOOD;
else // If creat the incorrect location that perform this statement
map[2][2] = SNAKE_FOOD;
}
void output(void) { // Define the funtion to output the map
for (int i = 0; i < 12; i++) {
printf("\n");
for (int j = 0; j < 12; j++) {
printf("%c", map[i][j]);
}
}
}
void gameover(void) { // Define the function of gave over
system("CLS");
printf("game over!");
exit(1); // exit the program
}
以上内容皆为本人观点,欢迎大家提出批评和指导,我们一起探讨!