如何让C语言贪吃蛇游戏实现跨编译器与平台兼容?
多平台适配的C语言贪吃蛇修改方案
我们小组为课程项目开发了一款基于C语言的贪吃蛇游戏,目前仅能在Code::Blocks平台运行。游戏使用了conio.h等非标准库,其中conio.h无法在Unix系统正常工作。我们希望修改代码使其适配更多平台与编译器,同时保留现有的kbhit、方向输入等逻辑,但作为编程新手,不清楚具体需要修改哪些内容。
核心修改点
- 替换非标准库conio.h的功能:
kbhit()和getch()是Windows平台conio.h特有的函数,我们用POSIX标准的termios接口实现跨平台的无缓冲按键检测和读取,兼容Windows和Unix-like系统(Linux、macOS等)。 - 跨平台清屏:Windows系统用
cls命令清屏,Unix-like系统用clear命令,通过条件编译自动识别系统并执行对应命令。 - 修复文件操作安全问题:原代码中打开
highscore.txt时未判断是否成功,添加错误处理避免文件不存在时程序崩溃。 - 优化内存管理:释放动态分配的内存前增加非空判断,避免野指针错误;添加内存分配失败的异常处理。
- 完善输入容错:增加地图尺寸输入的错误处理,默认使用中等尺寸。
适配后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> #include <ctype.h> // 跨平台实现kbhit和getch,替代conio.h #ifdef _WIN32 #include <conio.h> #else #include <termios.h> #include <fcntl.h> // 模拟Windows的kbhit(),检测是否有按键输入 int kbhit() { struct termios oldt, newt; int ch; int oldf; tcgetattr(STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); oldf = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); ch = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &oldt); fcntl(STDIN_FILENO, F_SETFL, oldf); if(ch != EOF) { ungetc(ch, stdin); return 1; } return 0; } // 模拟Windows的getch(),无回显读取按键 int getch() { struct termios oldt, newt; int ch; tcgetattr(STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); ch = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &oldt); return ch; } #endif void setup(); void draw(); void input(); void updateGame(); void logic(); void clearScreen(); void gotoxy(int x, int y); void newHighscore(); int gameover = 0; int x, y; int fruitx, fruity; int score; int height, width; char mode[10]; int snakeLength = 1; int* snakeBody = NULL; int dx = 0; int dy = 0; int highScore = 0; void setup() { printf("Enter Map Size (Small, Medium, Large): "); scanf("%s", mode); for (int i = 0; i < 10; i++){ mode[i] = tolower(mode[i]); } if (strcmp(mode, "small") == 0) { height = 10; width = 20; } else if (strcmp(mode, "medium") == 0) { height = 20; width = 40; } else if (strcmp(mode, "large") == 0) { height = 30; width = 60; } else { // 默认中等尺寸 height = 20; width = 40; strcpy(mode, "medium"); printf("Invalid input, using Medium size.\n"); sleep(1); } x = height / 2; y = width / 2; score = 0; // 释放之前的内存(如果存在) if (snakeBody != NULL) { free(snakeBody); } snakeBody = malloc(sizeof(int) * 2 * (height * width)); if (snakeBody == NULL) { printf("Memory allocation failed!\n"); exit(1); } snakeBody[0] = x; snakeBody[1] = y; srand(time(NULL)); do { fruitx = rand() % (height - 2) + 1; fruity = rand() % (width - 2) + 1; } while (fruitx == x || fruity == y); } void draw() { gotoxy(1, 1); printf("\033[?25l"); // 隐藏光标 for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if (i == 0 || i == height - 1 || j == 0 || j == width - 1){ printf("#"); } else if (i == fruitx && j == fruity) { printf("b"); } else if (i == x && j == y) { printf("O"); } else { int isBody = 0; for (int k = 1; k < snakeLength; k++) { if (i == snakeBody[2 * k] && j == snakeBody[2 * k + 1]) { printf("0"); isBody = 1; break; } } if (!isBody) { printf(" "); } } } printf("\n"); } if (strcmp(mode, "small") == 0) { printf("Score: %d Press X to Quit\n", score); } else if (strcmp(mode, "medium") == 0) { printf("Score: %d Press X to Quit\n", score); } else if (strcmp(mode, "large") == 0) { printf("Score: %d Press X to Quit\n", score); } fflush(stdout); } void input() { if (kbhit()) { switch (getch()) { case 'w': if (dx != 1) { dx = -1; dy = 0; } break; case 's': if (dx != -1) { dx = 1; dy = 0; } break; case 'a': if (dy != 1) { dx = 0; dy = -1; } break; case 'd': if (dy != -1) { dx = 0; dy = 1; } break; case 'x': gameover = 1; break; } } } void updateGame() { input(); x += dx; y += dy; for (int k = snakeLength - 1; k > 0; k--) { snakeBody[2 * k] = snakeBody[2 * (k - 1)]; snakeBody[2 * k + 1] = snakeBody[2 * (k - 1) + 1]; } snakeBody[0] = x; snakeBody[1] = y; } int FruitBody (int fruitx, int fruity) { for (int k = 0; k < snakeLength; k++) { if (fruitx == snakeBody[2 * k] && fruity == snakeBody[2 * k + 1]) { return 1; } } return 0; } void logic() { if (x == 0 || x == height - 1 || y == 0 || y == width - 1) { gameover = 1; return; } for (int k = 1; k < snakeLength; k++){ if (x == snakeBody[2 * k] && y == snakeBody[2 * k + 1]) { gameover = 1; return; } } if (x == fruitx && y == fruity) { score++; snakeLength++; do { fruitx = rand() % (height - 2) + 1; fruity = rand() % (width - 2) + 1; } while (fruitx == x || fruity == y || FruitBody(fruitx, fruity)); } } void clearScreen() { #ifdef _WIN32 system("cls"); #else system("clear"); #endif } void gotoxy(int x, int y) { printf("\033[%d;%dH", x, y); } void newHighscore () { FILE *highscore = fopen("highscore.txt", "r"); if (highscore == NULL) { // 文件不存在,初始化最高分0 highScore = 0; highscore = fopen("highscore.txt", "w"); if (highscore != NULL) { fprintf(highscore, "%d", highScore); fclose(highscore); } } else { fscanf(highscore, "%d", &highScore); fclose(highscore); if (score > highScore) { highscore = fopen("highscore.txt", "w"); if (highscore != NULL) { fprintf(highscore, "%d", score); fclose(highscore); highScore = score; } } } } int main() { char playAgain; do { setup(); clearScreen(); while (!gameover) { draw(); logic(); updateGame(); usleep(100000); } newHighscore(); printf("\033[?25h"); // 显示光标 clearScreen(); printf("Game Over! Final Score: %d High Score: %d\n", score, highScore); printf(" Play Again? (y/n) "); scanf(" %c", &playAgain); playAgain = tolower(playAgain); clearScreen(); gameover = 0; snakeLength = 1; dx = 0; dy = 0; } while (playAgain == 'y'); if (snakeBody != NULL) { free(snakeBody); } return 0; }
内容的提问来源于stack exchange,提问作者BaddieMuh
相关产品推荐
相关产品推荐

