基于ncurses的贪吃蛇持续移动功能故障排查求助
贪吃蛇游戏移动功能故障排查与修复
问题概述
实现贪吃蛇游戏时,将控制变量从var替换为dir后出现以下异常:
- 蛇无法按按下的方向(a/w/s/d)自动持续移动
- 按住按键时蛇能移动,但后续不再响应键盘输入
- 存在段错误
- 青蛙需触发按键才会生成
完整故障代码
#include <cctype> #include <cstdlib> #include <curses.h> #include <stdio.h> #include <wchar.h> #include <locale.h> #include <stdlib.h> #include <ncurses.h> #include <ctime> #include <time.h> #include <errno.h> #define N 20 #define M 40 int i,j,Field[N][M],x,y,gy,Head,Tail,Game,Frogs,a ,b,var,dir; void snakeInitialization(){ for(i=0;i<N;i++){ for(j=0;j<M;j++){ Field [i][j]=0; } } x=N/2; y=M/2; Head=5; Tail=1; gy=y; Game = 0; Frogs = 0; a = 0; b = 0; var = 0; for(i=0;i<Head;i++) { gy++; Field[x][gy-Head]=i+1; } } int kbhit(void) /* comment */ { int ch, r; // turn off getch() blocking and echo nodelay(stdscr, TRUE); noecho(); // check for input ch = getch(); if( ch == ERR) // no input r = FALSE; else // input { r = TRUE; ungetch(ch); } // restore block and echo nodelay(stdscr, FALSE); return(r); } int msleep(long msec) { struct timespec ts; int res; if (msec < 0) { errno = EINVAL; return -1; } ts.tv_sec = msec / 1000; ts.tv_nsec = (msec % 1000) * 1000000; do { res = nanosleep(&ts, &ts); } while (res && errno == EINTR); return res; } void print() { wchar_t RDC = 0x255D;//right down corner simbol wchar_t LDC = 0x255A;//left down corner unicode simbol wchar_t VW = 0x2551;//vertical wall simbol wchar_t HW = 0x2550;//Horizontal wall simbol wchar_t RUC = 0x2557;//right up corner simbol wchar_t LUC = 0x2554;//left up corner simbol wchar_t HS = 0x2593; wchar_t BS = 0x2591; wchar_t Frg = 0x1318F;//frog for (i=0;i<=M+1;i++) { if(i==0) { printw("%lc",LUC); } else if (i==M+1){ printw("%lc",RUC); } else{ printw("%lc",HW); } } printw("\n"); for(i=0;i<N;i++) { printw("%lc",VW); for (j=0;j<M;j++) { if(Field[i][j]==0) printw(" "); if(Field[i][j]>0&&Field[i][j]!=Head) printw("%lc",BS); if(Field[i][j]==Head) printw("%lc",HS); if(Field[i][j]==-1) printw("%lc",Frg); if(j==M-1) printw("%lc\n",VW); } } for (i=0;i<=M+1;i++) { if(i==0) { printw("%lc",LDC); } else if (i==M+1){ printw("%lc",RDC); } else{ printw("%lc",HW); } } } void ResetScreenPosition(){ move(0, 0); } void Random (){ srand(time(0)); a = 1 + rand() % 18; b = 1 + rand() % 38; if (Frogs == 0 && Field[a][b]==0) { Field[a][b] = -1; Frogs = 1; } } long int getch_noblock(){ if(!kbhit()) return getch(); else return -1; } void movement(){ var = getch_noblock(); var = tolower(var); if (var == 'd' || var == 'a' || var == 'w' || var == 's') { dir=var; } if (dir == 'd'){ y++; Head++; Field[x][y]=Head; } if (dir == 'a'){ y--; Head++; Field[x][y]=Head; } if (dir == 'w'){ x--; Head++; Field[x][y]=Head; } if (dir == 's'){ x++; Head++; Field[x][y]=Head; } } int main (int argc, char *argv[]) { setlocale(LC_CTYPE, ""); snakeInitialization(); // moving cursor, x = 20, y = 10 initscr(); while (Game == 0) { print(); ResetScreenPosition(); Random(); movement(); msleep(99); refresh(); } endwin(); return 0; }
核心故障函数(movement)
void movement(){ var = getch_noblock(); var = tolower(var); if (var == 'd' || var == 'a' || var == 'w' || var == 's') { dir=var; } if (dir == 'd'){ y++; Head++; Field[x][y]=Head; } if (dir == 'a'){ y--; Head++; Field[x][y]=Head; } if (dir == 'w'){ x--; Head++; Field[x][y]=Head; } if (dir == 's'){ x++; Head++; Field[x][y]=Head; } }
问题分析与修复方案
1. 输入读取逻辑完全反转
getch_noblock函数逻辑颠倒,导致有按键时返回-1,无按键时阻塞等待输入,这是蛇无法自动移动的核心原因:
// 修复后的getch_noblock long int getch_noblock(){ if(kbhit()) return getch(); else return -1; }
2. dir变量未初始化
全局变量dir未初始化,初始值为随机垃圾值,导致蛇初始状态无移动或乱走。在snakeInitialization中添加默认方向:
void snakeInitialization(){ // ... 原有代码 ... dir = 'd'; // 设置初始向右移动 }
3. 随机数种子重复初始化
Random函数每次调用都执行srand(time(0)),短时间内会生成相同随机坐标,且青蛙可能生成在蛇身上。将srand移到main开头仅初始化一次,并确保青蛙生成在空白区域:
// 修改后的Random函数 void Random (){ if (Frogs == 0) { do { a = 1 + rand() % 18; b = 1 + rand() % 38; } while(Field[a][b] != 0); Field[a][b] = -1; Frogs = 1; } } // main函数中添加 int main (int argc, char *argv[]) { srand(time(0)); // 仅初始化一次随机种子 setlocale(LC_CTYPE, ""); // ... 原有代码 ... }
4. 未处理蛇的尾巴逻辑
原代码仅添加头部,未清除尾巴,导致蛇无限变长最终越界触发段错误。修改movement函数,添加尾巴处理与碰撞检测:
// 修复后的movement函数 void movement(){ var = getch_noblock(); var = tolower(var); // 防止反向移动撞自己 if ((var == 'd' && dir != 'a') || (var == 'a' && dir != 'd') || (var == 'w' && dir != 's') || (var == 's' && dir != 'w')) { dir = var; } // 移动头部坐标 if (dir == 'd') y++; else if (dir == 'a') y--; else if (dir == 'w') x--; else if (dir == 's') x++; // 边界碰撞或撞自己则结束游戏 if(x < 0 || x >= N || y < 0 || y >= M || Field[x][y] > 0){ Game = 1; return; } Head++; Field[x][y] = Head; // 未吃到青蛙则缩短尾巴 if(Field[x][y] != -1){ for(int i=0; i<N; i++){ for(int j=0; j<M; j++){ if(Field[i][j] == Tail){ Field[i][j] = 0; Tail++; break; } } } } else { Frogs = 0; // 吃到青蛙,允许生成新的 } }
内容的提问来源于stack exchange,提问作者Linuxero
相关产品推荐
相关产品推荐

