You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 11:08:10