:
//移动蛇
void move_snake(int dir);
//像之前数组一样移动蛇
//通过将蛇头原来的坐标赋给第二节,原来的第二节赋给第三节,依次下去,完成蛇坐标的更新
void move_snake(int dir)
{
snake_node * p = NULL;
COORD last = snake_head->cor, current; //last与current用于之后蛇坐标的更新
int grow = 0;
switch (dir) {
case UP:
(snake_head->cor).Y--;
break;
case DOWN:
(snake_head->cor).Y++;
break;
case LEFT:
(snake_head->cor).X--;
break;
case RIGHT:
(snake_head->cor).X++;
break;
}
//蛇吃到食物
if (cor_cmp(snake_head->cor,food)) {
grow = 1;
//生成新的食物
food = generate_food();
}
for (p = snake_head->next; p != NULL; p = p->next) {
current = p->cor;
p->cor = last;
last = current;
}
gotoxy(snake_head->cor);
putchar('*'); //打印新的蛇头
if (grow) {
//找到蛇尾 p
p = snake_head;
while(p->next != NULL){
p = p->next;
}
p->next = (snake_node *)malloc(sizeof(snake_node)); //在蛇尾追加节点
p->next->cor = last;
p->next->next = NULL;
len++;
//打印新的食物
gotoxy(food);
putchar('$');
}else {
//这是为了避免当你把蛇绕成一个圈的时候(蛇头紧跟蛇尾,没咬到),清除蛇尾顺便也把蛇头清除掉了
if(!cor_cmp(snake_head->cor,last)){
gotoxy(last);
putchar(' ');
}
}
//避免光标一直跟着蛇尾(或食物 )
COORD foot = {0,17}; //将光标置于左下角
gotoxy(foot);
Sleep(500);
}
另一种更新坐标的方法是每次移动时申请一块新的内存空间存储移动后的蛇头坐标并让 snake_head 指针指向它,如果蛇不长节的话就释放蛇尾的内存空间,通俗来说就是添头去尾,这样就避免了蛇中间节点的移动过程(原来的蛇头自然而然成为第二节,原来第二节也成了第三节,而他们的坐标都没发生变化) 。
void move_snake(int dir)
{
snake_node * p,* t = NULL;
snake_node * newh = NULL; //新的蛇头
newh = (snake_node *)malloc(sizeof(snake_node)); //为新蛇头开辟内存空间
newh->cor = snake_head->cor; //把旧头的坐标赋给新头
newh->next = snake_head; //把新头的next指针指向旧头
snake_head = newh; //让头指针指向新开辟的内存空间
int grow = 0;
switch (dir) {
case UP:
(snake_head->cor).Y--;
break;
case DOWN:
(snake_head->cor).Y++;
break;
case LEFT:
(snake_head->cor).X--;
break;
case RIGHT:
(snake_head->cor).X++;
break;
}
//蛇吃到了食物
if (cor_cmp(snake_head->cor,food)) {
grow = 1;
//生成新的食物
food = generate_food();
}
gotoxy(snake_head->cor);
putchar('*');
//如果长节的话不再对链表进行操作
if (grow) {
len ++;
gotoxy(food);
putchar('$');
}
//不长节的话就得删除蛇尾
else {
//找到倒数第二节
p = snake_head;
while(p->next->next != NULL){
p = p->next;
}
//记录下最后一节的地址
t = p->next;
//将倒数第二节的next指针赋值为NULL
p->next = NULL;
//这是为了避免当你把蛇绕成一个圈的时候(蛇头紧跟蛇尾,没咬到),清除蛇尾顺便也把蛇头清除掉了
if(!cor_cmp(snake_head->cor,t->cor)){
gotoxy(t->cor);
putchar(' ');
}
free(t); //释放蛇尾的内存空间
}
//避免光标一直跟着蛇尾(或食物 )
COORD foot = {0,17}; //将光标置于左下角
gotoxy(foot);
Sleep(500);
}
在游戏结束之后,我们还得手动释放构造链表而申请的内存空间:
//释放申请的内存空间
void free_node(snake_node * n);
void free_node(snake_node * h){
snake_node * p = h,* q;
while(p != NULL){
q = p;
p = p->next;
free(q);
}
}
最后判断该蛇挂没挂:
//判断蛇死活的函数(判断了蛇是否撞到边界或者自食)
int isalive(void);
int isalive(void){
int self_eat = 0;
snake_node * p = NULL;
for (p = snake_head->next; p != NULL; p = p->next)
{
if (cor_cmp(p->cor, snake_head->cor))
{
self_eat = 1;
}
}
return ((snake_head->cor).X == 0 || (snake_head->cor).X == 16 || (snake_head->cor).Y == 0 || (snake_head)->cor.Y >= 16 || self_eat) 0 : 1;
}
根据以上的改进,我们顺利的利用 结构+链表 实现了贪吃蛇的小游戏,对前面的代码实现了优化。
没错!这篇博客还是参考自前面两篇博客的原作者的 《C语言实现贪吃蛇之结构链表篇 》。
这已经是第三篇参考(Copy)该作者的博客了,按理说也差不多了,再这样下去我都感觉自己有点小无耻了。
但毕竟是我感觉而已,本着坚持就是胜利的求知精神,我决定将无耻进行到底。