今天也要努力学习,争取考上杭电!
今天帮朋友写了贪吃蛇,我自己都没写过,还要骂骂咧咧帮别人写。
本来是写了一个版本,结果所有功能实现以后,发现了一个bug!!!这个bug是啥呢,就是我的蛇紧急转弯时候,如果身子挨在一起,就会在头尾在同一直线时 断掉。哈哈哈哈,让我想到了有的蛇砍掉头还能蹦跶两下…不过后面改掉了。
如果有要写贪吃蛇的大一朋友们,可以参考一下这个思路:
1、实现动态的效果,肯定是要不停清屏、输出的
2、蛇头要有一个移动方向,如果不人为干预的话,那就应该遵循旧的移动方向
3、如果从键盘键入了一个方向,键入的方向和当前方向相反,就是无效的键入
4、不需要一直键入,而是想键入就键入,那么应该使用非阻塞的getch(kbhit()和getch()配合使用)
5、蛇的下一个位置,判断当前蛇头按方向的下一个坐标是不是墙、某一节身子、食物、空白
6、是墙、某一节身子的话,就结束了
7、是空白的话,就可以走,那么空白的坐标变成头的,原来的头变成身子,尾巴向前移
8、是食物的话,就可以走,那么食物的坐标变成头的,原来的头变成身子,而尾巴不需要动
9、尾巴的判断,把尾巴的最后一节变为空(通常情况下,一个尾巴肯定是线性的,与倒数第二节尾巴相连,所以我的bug就是出现在这里,如果只是判断到一个相连的就不判断了,那么两个相邻的时候就出问题了,后面我的思路是改用了单链表,存储每一节点的坐标,这样甚至就不用判断尾巴了,但是消耗的空间和原来只用两个变量存储尾坐标相比多了一个长度为蛇身的链表)
10、食物生成,就是生成一对随机数做坐标,随机数范围在地图合理范围内,如果生成的坐标是身子什么的从新生成一对再
11、至于数据存储,为了不来回传参,我选择了用的全局变量,因为真的用了好多控制变量
我的代码中,地图是用了一个二维字符数组,蛇身体坐标用的单链表实现
下面给列一下会用的一些不常用的函数及功能,可以参考,具体原型自行查吧:
1、gotoxy(int x, int y):将光标移动到控制台的某个坐标,这个要看你的环境,没有的话自己写或者下库
2、kbhit():检查当前是否有键入,没有返回0 , 头文件:conio.h
3、getch():无回显,自动读取,不需要按回车!可以搭配kbhit()使用,头文件:conio.h
然后,就是方向键,键入是返回两个值,第一个值是-32,说明是方向键,这时候再读取第二个判断是啥方向
大概就写完了,明天也要努力学习考杭电!
代码实现:
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <time.h>
#define MAP_HEIGTH 30
#define MAP_WIDTH 60
typedef struct snake{
int x;
int y;
struct snake *next;
}snake,*psnake;
char map[MAP_HEIGTH][MAP_WIDTH+1];
psnake sh,st; //蛇头 蛇尾指针,但蛇尾是链表头指针
int h_x,h_y,t_x,t_y; //头坐标 尾坐标
int point = 75; //方向值,初始向左 ←75 →77 ↑72 ↓ 80
char get_p,get_key; //key用来接收键入,方向键返回两个值,第二个用p接收
int food = 0; //食物数目
int sleep = 30; //休眠时间
int length = 5; //蛇长
void gotoxy(int x, int y) {
//DEVC环境需要下库graphics.h
//所以自己写了
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(handle, pos);
}
void insert_head(int x, int y){
//更新蛇头
psnake h = (psnake)malloc(sizeof(snake));
h -> x = x;
h -> y = y;
h -> next = st;
sh -> next = h;
sh = h;
}
void del_tail(){
//删除蛇尾坐标
psnake del = st;
st = st -> next;
free(del);
sh -> next = st;
}
void ini_snake(){
int i;
h_x = (MAP_WIDTH - 5) / 2;
h_y = MAP_HEIGTH / 2;
t_x = h_x + 5;
t_y = h_y;
map[h_y][h_x] = 'O';
//初始蛇头坐标存储
sh = (psnake)malloc(sizeof(snake));
st = sh;
sh -> x = h_x;
sh -> y = h_y;
sh -> next = st;
psnake q = sh;
for(i = h_x + 1;i <= t_x;i++){
map[h_y][i] = '+';
//初始蛇身子坐标存储
psnake p = (psnake)malloc(sizeof(snake));
p -> x = i;
p -> y = h_y;
p -> next = q;
q = p;
sh -> next = p;
st = p;
}
}
void produce_food(){
//始终保证地图中有5个食物
// 1 <= x <= MAP_WIDTH-2
// 1 <= y <= MAP_HEIGTH-2
srand(time(NULL));
while(food != 5){
int x = (int)rand() % (MAP_WIDTH-2) +1; //[1,58]
int y = (int)rand() % (MAP_HEIGTH-2) +1; //[1,28]
if(map[y][x] == 'O' || map[y][x] == '+' || map[y][x] == '#'){
//等于身体重新生成,边界不在生成范围
continue;
}else{
map[y][x] = '#';
food++;
}
}
}
void ini_map(){
int i,j;
map[0][0] = '*';
map[0][MAP_WIDTH-1] = '*';
map[MAP_HEIGTH-1][0] = '*';
map[MAP_HEIGTH-1][MAP_WIDTH-1] = '*';
for(i = 1;i < MAP_WIDTH-1;i++){
map[0][i] = '*';
map[MAP_HEIGTH-1][i] = '*';
}
map[0][MAP_WIDTH] = '\0';
map[MAP_HEIGTH-1][MAP_WIDTH] = '\0';
for(i =1;i < MAP_HEIGTH-1;i++){
map[i][0] = '*';
for(j = 1;j < MAP_WIDTH-1;j++){
map[i][j] = ' ';
}
map[i][MAP_WIDTH-1] = '*';
map[i][MAP_WIDTH] = '\0';
}
ini_snake();
produce_food();
}
void load_map(){
int i;
printf("\n");
for(i = 0;i < MAP_HEIGTH; i++){
printf(" %s\n",map[i]);
}
return;
}
int check_next(){
int flag = 0; //判断是否是食物
int next_x,next_y; //下一个的坐标
switch(point){ //头的下一个按方向判断
case 75: //左
next_x = h_x - 1, next_y = h_y;
break;
case 77: //右
next_x = h_x + 1, next_y = h_y;
break;
case 72: //上
next_x = h_x, next_y = h_y - 1;
break;
case 80: //下
next_x = h_x, next_y = h_y + 1;
break;
}
if(map[next_y][next_x] == '*' || map[next_y][next_x] == '+') return 0; //撞到墙或自己
else{
if(map[next_y][next_x] == '#') flag = 1; //下一个是食物,则尾不变
map[next_y][next_x] = 'O'; //下一个变成新的头
map[h_y][h_x] = '+'; //当前头变成身体
h_y = next_y; //更新头坐标
h_x = next_x; //更新头坐标
insert_head(next_x,next_y); //更新头坐标
}
if(flag){
food--; //食物减少
length++;
if(sleep > 10 && length % 10 == 0) sleep = sleep - 2 ;
return 1; //如果是食物,则尾不变
}
t_y = st -> y;
t_x = st -> x;
map[t_y][t_x] = ' ';
del_tail();
return 1;
}
void load_end(){
system("color 04");
gotoxy((MAP_WIDTH-9)/2,MAP_HEIGTH/2);
printf("GAME OVER !");
}
void monitor_key(){
//kbhit判断缓冲区有没有键入
//getch无回显读取一个字符
if(kbhit()){ //有键入
get_key = getch();
if(get_key == -32){
//方向键返回两个值,需要读取两次
get_p = getch();
if(get_p != (152-point)){
//相反方向相加=152
//相反方向无效
point = get_p;
}
}
}
}
void start(){
int YoN = 1;
ini_map();
system("color 70");
while(YoN){
system("CLS");
if(food != 5) produce_food();
load_map(); //加载界面
gotoxy(27,32);
printf("Length : %d",length);;
Sleep(sleep);
monitor_key();
YoN = check_next();
}
load_end();
}
int main()
{
system("mode con cols=65 lines=35");
gotoxy((MAP_WIDTH-10)/2,MAP_HEIGTH/2);
printf("Level 1-9\n");
gotoxy((MAP_WIDTH-2)/2,MAP_HEIGTH/2+2);
scanf("%d",&sleep);
sleep=(10-sleep)*10;
start();
while(1){};
}