求助!!!C语言版的贪吃蛇问题,谢谢。

swustpeng 2018-06-06 04:07:50
自己写的C语言版的贪吃蛇,我想知道为什么不能立马掉头转弯回去,而是空一行。别人写的可以。
我的如下图,不知道为什么,头疼。代码贴在下面了。谢谢!

相应符号我做了替换



#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<conio.h>
#include<time.h>

#define Wid 40
#define Hei 20
int speed;//蛇的速度
//蛇的节点
typedef struct Snake_node{
COORD pos;
struct Snake_node *next;
struct Snake_node *prev;
}Snake,*PSnake;
PSnake Head;//蛇的头指针
PSnake Tail;//蛇的尾指针

struct Snake_pos{
COORD pos;
int dir;
}HeadPos;//蛇头即将移动的方向和坐标

COORD Food;//食物坐标
int score;//积分记录
//光标定位
void GetPosition(COORD pos)
{
HANDLE hout=GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hout,pos);
}
//游戏玩家积分等级输出
void GameLevel()
{
COORD pos_1={Wid+2,11};
GetPosition(pos_1);
if(score>=70)
{
printf("当前游戏等级:恭喜你登顶了");
}
else if(score>=60)
{
printf("当前游戏等级:最强蛇人2颗心");
}
else if(score>=55)
{
printf("当前游戏等级:最强蛇人1颗心");
}
else if(score>=50)
{
printf("当前游戏等级:一代宗师3颗心");
}
else if(score>=45)
{
printf("当前游戏等级:一代宗师2颗心");
}
else if(score>=40)
{
printf("当前游戏等级:一代宗师1颗心");
}
else if(score>=36)
{
printf("当前游戏等级:炉火纯青3颗心");
}
else if(score>=32)
{
printf("当前游戏等级:炉火纯青2颗心");
}
else if(score>=28)
{
printf("当前游戏等级:炉火纯青1颗心");
}
else if(score>=24)
{
printf("当前游戏等级:马马虎虎3颗星");
}
else if(score>=19)
{
printf("当前游戏等级:马马虎虎2颗星");
}
else if(score>=16)
{
printf("当前游戏等级:马马虎虎1颗星");
}
else if(score>=13)
{
printf("当前游戏等级:初学乍练3颗星");
}
else if(score>=10)
{
printf("当前游戏等级:初学乍练2颗星");
}
else if(score>=0)
{
printf("当前游戏等级:初学乍练1颗星");
}
}
//添加蛇的头节点
void AddSnakeBody(COORD pos)
{
GetPosition(pos);
printf("蛇");
PSnake q=(PSnake)malloc(sizeof(Snake));
q->pos=pos;
if(Head==NULL&Tail==NULL)
{
Head=Tail=q;
q->next=NULL;
}
else
{
q->next=Head;
Head->prev=q;
Head=q;
}

COORD pos_1={Wid+2,2};
GetPosition(pos_1);
printf(" ");
GetPosition(pos_1);
printf("蛇头位置:%d行 %d列",pos.Y,pos.X/2);
}
//删掉蛇的尾节点
void ReduceSnakeTail()
{
GetPosition(Tail->pos);
printf(" ");
Tail=Tail->prev;
free(Tail->next);
Tail->next=NULL;
}
//蛇的移动
void MoveSnake(COORD pos)
{
AddSnakeBody(pos);
ReduceSnakeTail();
}
//方向控制
void ChangeDir()
{
COORD pos_1={Wid+2,4};
if(_kbhit())
{
switch(_getch())
{
case 72:
if(HeadPos.dir!=1)
{
HeadPos.dir=0;
}
break;
case 80:
if(HeadPos.dir!=0)
{
HeadPos.dir=1;
}
break;
case 75:
if(HeadPos.dir!=3)
{
HeadPos.dir=2;
}
break;
case 77:
if(HeadPos.dir!=2)
{
HeadPos.dir=3;
}
break;
case 'a':
pos_1.Y=8;
GetPosition(pos_1);
system("pause");
GetPosition(pos_1);
printf(" ");
GetPosition(pos_1);
printf("按a键暂停游戏");
break;
case '5':
speed=50;
GetPosition(pos_1);
printf("当前模式:噩梦");
break;
case '4':
speed=100;
GetPosition(pos_1);
printf("当前模式:困难");
break;
case '3':
speed=150;
GetPosition(pos_1);
printf("当前模式:一般");
break;
case '2':
speed=200;
GetPosition(pos_1);
printf("当前模式:简单");
break;
case '1':
speed=400;
GetPosition(pos_1);
printf("当前模式:蜗牛");
break;
}
}
switch (HeadPos.dir)
{
case 0:
HeadPos.pos.Y--;
break;
case 1:
HeadPos.pos.Y++;
break;
case 2:
HeadPos.pos.X-=2;
break;
case 3:
HeadPos.pos.X+=2;
break;
}
}
//划出游戏边框
void DrawBorder()
{
COORD pos;
for(pos.Y=0;pos.Y<=Hei-1;pos.Y++)
{
GetPosition(pos);
for(pos.X=0;pos.X<=Wid-2;pos.X+=2)
{
if(pos.Y==0||pos.Y==Hei-1||pos.X==0||pos.X==Wid-2)
{
printf("墙");
}
else
{
printf(" ");
}
}
pos.X=0;
}


COORD pos_1={Wid+2,4};
GetPosition(pos_1);
printf("模式选择提示:按键1~5分别对应傻瓜、简单、一般、困难、噩梦");
pos_1.Y++;
GetPosition(pos_1);
printf("游戏过程中,模式可以随意切换,请君量力而行!");
pos_1.Y++;
GetPosition(pos_1);
printf("当前模式:一般");
speed=150;
pos_1.Y=8;
GetPosition(pos_1);
printf("按a键暂停游戏");
pos_1.Y=0;
GetPosition(pos_1);
printf("贪吃蛇游戏");
pos_1.Y=12;
GetPosition(pos_1);
printf("想要知道最高等级是什么吗?那就继续玩游戏吧!");
pos_1.Y=Hei-3;
GetPosition(pos_1);
printf("开发者信息:xxxxxx");

}
//游戏结束判断
int GameOver()
{
if(HeadPos.pos.X<=0||HeadPos.pos.X>=Wid-2||HeadPos.pos.Y<=0||HeadPos.pos.Y>=Hei-1)
{
return 1;
}
PSnake q=Head;
while(q!=NULL)
{
if(HeadPos.pos.X==q->pos.X&&HeadPos.pos.Y==q->pos.Y)
{
return 1;
}
q=q->next;
}
return 0;
}
//食物和积分信息输出
void FoodInfo()
{
COORD pos={Wid+2,1};
GetPosition(pos);
printf(" ");
GetPosition(pos);
printf("食物位置:%d行 %d列",Food.Y,Food.X/2);
pos.Y=9;
GetPosition(pos);
printf("当前得分:%d",score++);
}
//初始化食物
int InitFood()
{
srand((unsigned) time(NULL));
while(1)
{
Food.X=(rand()%(Wid/2-2)+1)*2;
Food.Y=rand()%(Hei-2)+1;
int flag=0;
PSnake q=Head;
while(q!=NULL)
{
if(Food.X==q->pos.X&&Food.Y==q->pos.Y)
{
flag=1;
break;
}
q=q->next;
}
if(flag==0)
{
break;
}
}

GetPosition(Food);
printf("食");
GameLevel();
FoodInfo();
}
//考虑到内存问题,链表空间释放(重新开始游戏需要用到)
void DestroySnakeNode()
{
while(Tail!=Head)
{
Tail=Tail->prev;
free(Tail->next);
}
free(Tail);
}
//游戏数据初始化
void InitGame()
{
system("color 0E");
DrawBorder();
HeadPos.pos.X=Wid/2;
HeadPos.pos.Y=Hei/2;
HeadPos.dir=0;
Head=NULL;
Tail=NULL;
AddSnakeBody(HeadPos.pos);
InitFood();
score=0;
}
//游戏运行控制
void RunGame()
{
while(1)
{
Sleep(speed);
ChangeDir();
if(GameOver())
{
break;
}
else if(HeadPos.pos.X==Food.X&&HeadPos.pos.Y==Food.Y)
{
AddSnakeBody(HeadPos.pos);
InitFood();
}
else
{
MoveSnake(HeadPos.pos);
}
}
COORD pos={0,Hei};
GetPosition(pos);
printf("很遗憾,你撞墙了或者自食自己,游戏结束!\n");
}
//游戏重新开始
int RestarGame()
{
int restar;
printf("你是最棒的!是否重新挑战游戏?\n");
printf("0.否 1.是\n");
printf("请选择(按回车键确认):");
scanf("%d",&restar);
if(restar==1)
{
system("cls");
DestroySnakeNode();
return 1;
}
else if(restar==0)
{
printf("游戏退出!\n");
return 0;
}
else
{

printf("选择无效!默认退出!\n");
return 0;
}
}


int main()
{
while(1)
{
InitGame();
RunGame();
if(RestarGame())
{
continue;
}
break;
}
return 0;
}
...全文
2848 5 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
赵4老师 2018-06-09
  • 打赏
  • 举报
回复
学习一下#3楼的代码。
RWYQ阿伟 2018-06-08
  • 打赏
  • 举报
回复
我大概看了下,就结果而言,我还是不懂。
通过调试我发现不管两次按键时间多么接近,在循环的时候中间总是会出现一次按键识别不了,
比如我一打开就输入 左 下。
_getchar()
两次循环的值:75、80
可是实际上是:75、-32、80
中间多了一次是-32,然后多走了一步。
我简单修改了下RunGame()

void RunGame()
{
while (1)
{
///////////////////////////////
COORD pos_1 = { Wid + 2,4 };
char a = 0;
if (_kbhit())
{
a = _getch();
if (a == -32)
{
continue;
}

}

switch (a)
{
case 72:
if (HeadPos.dir != 1)
{
HeadPos.dir = 0;//上
}
break;
case 80:
if (HeadPos.dir != 0)
{
HeadPos.dir = 1;//下
}
break;
case 75:
if (HeadPos.dir != 3)
{
HeadPos.dir = 2;//左
}
break;
case 77:
if (HeadPos.dir != 2)
{
HeadPos.dir = 3;//右
}
break;
case 'a':
pos_1.Y = 8;
GetPosition(pos_1);
system("pause");
GetPosition(pos_1);
printf(" ");
GetPosition(pos_1);
printf("按a键暂停游戏");
break;
case '5':
speed = 50;
GetPosition(pos_1);
printf("当前模式:噩梦");
break;
case '4':
speed = 100;
GetPosition(pos_1);
printf("当前模式:困难");
break;
case '3':
speed = 150;
GetPosition(pos_1);
printf("当前模式:一般");
break;
case '2':
speed = 200;
GetPosition(pos_1);
printf("当前模式:简单");
break;
case '1':
speed = 1000;
GetPosition(pos_1);
printf("当前模式:蜗牛");
break;
}

switch (HeadPos.dir)
{
case 0:
HeadPos.pos.Y--;
break;
case 1:
HeadPos.pos.Y++;
break;
case 2:
HeadPos.pos.X -= 2;
break;
case 3:
HeadPos.pos.X += 2;
break;
}
///////////////////////////////

if (GameOver())
{
break;
}
else if (HeadPos.pos.X == Food.X&&HeadPos.pos.Y == Food.Y)
{
AddSnakeBody(HeadPos.pos);
InitFood();
}
else
{
MoveSnake(HeadPos.pos);
}

Sleep(speed);
}
COORD pos = { 0,Hei };
GetPosition(pos);
printf("很遗憾,你撞墙了或者自食自己,游戏结束!\n");
}

判断并把-32的值跳过,
然后效果差不多了。

我原以为是_kbhit()和_getch()这两个函数的特点,
可是我新建了一个项目,简单地搭建了类似的场景,测试结果却没有上面那种问题。
这个问题……搞不懂了。
我也是有点好奇,请路过的高手回答吧。
ooolinux 2018-06-07
  • 打赏
  • 举报
回复
总的来说,要考虑按键的时序和Sleep在主循环中的位置的关系。
赵4老师 2018-06-07
  • 打赏
  • 举报
回复
仅供参考:
#include <conio.h>
#include <windows.h>

void ConPrintAt(int x, int y, char *CharBuffer, int len)
{
   DWORD count;
   COORD coord = {x, y};
   HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
   SetConsoleCursorPosition(hStdOut, coord);
   WriteConsole(hStdOut, CharBuffer, len, &count, NULL);
}
void HideTheCursor()
{
   CONSOLE_CURSOR_INFO cciCursor;
   HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

   if(GetConsoleCursorInfo(hStdOut, &cciCursor))
   {
      cciCursor.bVisible = FALSE;
      SetConsoleCursorInfo(hStdOut, &cciCursor);
   }
}

void ShowTheCursor()
{
   CONSOLE_CURSOR_INFO cciCursor;
   HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

   if(GetConsoleCursorInfo(hStdOut, &cciCursor))
   {
      cciCursor.bVisible = TRUE;
      SetConsoleCursorInfo(hStdOut, &cciCursor);
   }
}
void GetWH(int *w,int *h) {
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    if (GetConsoleScreenBufferInfo(hStdOut, &csbi)) {
        *w=csbi.srWindow.Right;
        *h=csbi.srWindow.Bottom;
    } else {
        *w=80;
        *h=25;
    }
}
void ClearConsole()
{
   //Get the handle to the current output buffer...
   HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
   //This is used to reset the carat/cursor to the top left.
   COORD coord = {0, 0};
   //A return value... indicating how many chars were written
   //   not used but we need to capture this since it will be
   //   written anyway (passing NULL causes an access violation).
   DWORD count;
   //This is a structure containing all of the console info
   // it is used here to find the size of the console.
   CONSOLE_SCREEN_BUFFER_INFO csbi;
   //Here we will set the current color
   if(GetConsoleScreenBufferInfo(hStdOut, &csbi))
   {
      //This fills the buffer with a given character (in this case 32=space).
      FillConsoleOutputCharacter(hStdOut, (TCHAR) 32, csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
      FillConsoleOutputAttribute(hStdOut, csbi.wAttributes, csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
      //This will set our cursor position for the next print statement.
      SetConsoleCursorPosition(hStdOut, coord);
   }
}

int main() {
    unsigned short k;
    int x,y,w,h;
    char d;

    SetConsoleOutputCP(437);
    ClearConsole();
    GetWH(&w,&h);
    x=w/2;y=h/2;
    HideTheCursor();
    ConPrintAt(x,y,"O",1);
    d='o';
    while (1) {
        Sleep(50);
        if (kbhit()) {
            k=getch();
            if (27==k) break;//按Esc键退出
            if (0==k||0xe0==k) k|=getch()<<8;//非字符键
            switch (k) {
                case 0x48e0:case 0x04800://上
                    d='u';
                    if (y>0) {
                        ConPrintAt(x,y," ",1);
                        y--;
                        ConPrintAt(x,y,"O",1);
                    }
                break;
                case 0x50e0:case 0x05000://下
                    d='d';
                    if (y<h) {
                        ConPrintAt(x,y," ",1);
                        y++;
                        ConPrintAt(x,y,"O",1);
                    }
                break;
                case 0x4be0:case 0x04b00://左
                    d='l';
                    if (x>0) {
                        ConPrintAt(x,y," ",1);
                        x--;
                        ConPrintAt(x,y,"O",1);
                    }
                break;
                case 0x4de0:case 0x04d00://右
                    d='r';
                    if (x<w-1) {
                        ConPrintAt(x,y," ",1);
                        x++;
                        ConPrintAt(x,y,"O",1);
                    }
                break;
            }
//          cprintf("%04x pressed.\r\n",k);
        } else {
            switch (d) {
                case 'u':
                    if (y>0) {
                        ConPrintAt(x,y," ",1);
                        y--;
                        ConPrintAt(x,y,"O",1);
                    }
                break;
                case 'd':
                    if (y<h) {
                        ConPrintAt(x,y," ",1);
                        y++;
                        ConPrintAt(x,y,"O",1);
                    }
                break;
                case 'l':
                    if (x>0) {
                        ConPrintAt(x,y," ",1);
                        x--;
                        ConPrintAt(x,y,"O",1);
                    }
                break;
                case 'r':
                    if (x<w-1) {
                        ConPrintAt(x,y," ",1);
                        x++;
                        ConPrintAt(x,y,"O",1);
                    }
                break;
            }
        }
    }
    ClearConsole();
    ShowTheCursor();
    return 0;
}
yiyefangzhou24 2018-06-07
  • 打赏
  • 举报
回复
单步调试是程序员的基本技能,转弯的时候下个断点看一下
内容概要:本文档是2025年全国广播电视技术能手竞赛IPTV专业的竞赛内容大纲,旨在指导参赛选手准备比赛。竞赛分为理论和实操两大部分。理论部分涵盖基本知识、基础理论和专业知识,包括法律法规、技术标准、IPTV技术基础、信号传输与分发、网络和数据安全等。实操部分则涉及IPTV系统设备、仪器设备的操作使用和指标测量。具体内容包括IPTV系统架构、传输技术与指标、设备配置及维护、专业技能如测量工具使用、视音频指标测量、直播点播协议分析、播出网络性能测量、网络安全数据分析以及系统故障排查等。 适合人群:从事或有意从事IPTV相关工作的技术人员,尤其是有志于参加全国广播电视技术能手竞赛的专业人士。 使用场景及目标:①帮助参赛选手全面了解并掌握IPTV相关的法规文件和技术标准;②提升选手对IPTV系统架构、传输技术和设备配置的理解;③增强选手在实际操作中的测量、分析和故障排查能力;④确保选手能够熟练运用各种测量工具和分析软件进行视音频指标测量和网络安全数据分析。 阅读建议:由于文档内容详尽且专业性强,建议读者在学习过程中结合实际案例和操作练习,同时参考相关技术文献,以便更好地理解和掌握竞赛所需的知识和技能。

70,023

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧