使用c语言实现字符界面贪吃蛇。
亲测可以在Windows Terminal下运行。
直接上源码。
欢迎交流。
效果图
#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//每个位置的状态是什么
#define Nothing 0 //空
#define Wall 1 //墙
#define Food 2 //食物
#define Head 3 //蛇头
#define Body 4 //蛇身
//方向
#define Left 5 //左移
#define Right 6 //右移
#define Up 7 //上移
#define Down 8 //下移
HANDLE consoleHandle; // 操作控制台需要的一个变量
int w, h; // 高度,宽度,对应 y 和 x
int score = 0;//记录得分
int status[3000][120] = {{0}};//记录每个位置的状态是什么
int flag_death = 0;//判断是否死亡
int flag_q = 0;//判断是否退出游戏
//结构体:蛇头
struct snack_head
{
int len;
int x;
int y;
int dir;
//分别为蛇长(不包括蛇头),蛇头横坐标,纵坐标,方向
}snack;
//结构体,蛇身
struct snack_body
{
int x;
int y;
//蛇身中一节的横坐标
}body[10086];//用二维数组是一样的
//结构体,食物
struct FOOD
{
int x;
int y;
}food;
// 打印输入的一个字符
void mvaddch(int y, int x, char ch) { //y表示高度,x表示宽度
COORD co = (COORD) { .X = x, .Y = y };
SetConsoleCursorPosition(consoleHandle, co);
putchar(ch);
}
//打印一个字符串
void mvaddstr(int y, int x, char *s) {
int len = strlen(s);
for (int i = 0; i < len; ++i)
mvaddch(y, x + i, *(s + i));//y表示高度,x表示宽
}
// 清屏(全屏空格)
void ClearScr() {
for (int i = 1; i <= h; ++i)
for (int j = 1; j <= w; ++j)
mvaddch(i, j, ' ');
}
//开始游戏的面板
void EnterGame() {
mvaddstr(12, 50, " Welcome ! ");
mvaddstr(16, 50, "Press any key to start !");
}
//初始化面板
void PrintBoard() {//宽为30~90 高为1~28
for (int i = 1; i <= 28; ++i)
for (int j = 30; j <= 90; j++)
status[i][j] = Nothing;
for (int i = 30; i <= 90; ++i){
mvaddch(1, i, '-');
status[0][i] = Wall;
}
for (int i = 30; i <= 90; ++i) {
mvaddch(28, i, '-');
status[29][i] = Wall;
}
for (int i = 1; i <= 28; ++i) {
mvaddch(i, 29, '|');
status[i][28] = Wall;
}
for (int i = 1; i <= 28; ++i) {
mvaddch(i, 91, '|');
status[i][92] = Wall;
}
mvaddstr(12,94,"press 'p' to pause");
mvaddstr(13,94, "press 'space' to continue");
}
//打印得分
void PrintScore(int y,int x, int score_) {
COORD co = (COORD) {.X = x, .Y = y};
SetConsoleCursorPosition(consoleHandle, co);
printf("score:%d", score_);
}
//游戏失败时候的界面
void Game_Defeat() {
flag_death = 1;
PrintScore(h / 2 - 2, w / 2 - 3, score);
mvaddstr(h / 2 - 1, w / 2 - 3, "Good game!");
mvaddstr(h / 2, w / 2 - 10, "Press any key to restart.");
score = 0;
}
void InitSnack() {//初始化蛇
snack.len = 1;//蛇身初始长度为一节
snack.x = 60;
snack.y = 16;//y指的蛇头高度
snack.dir = Right;//蛇头的初始横纵坐标及方向
body[0].x = 59;
body[0].y = 16;//第一节蛇身的横纵坐标
}
void GenerateFood() {//随机产生一个食物,注意食物的那个位置在这之前必须是空的
srand((unsigned ) time(NULL));
do {
food.x = rand() % 59 + 31;
food.y = rand() % 26 + 2;
} while (status[food.y][food.x] != Nothing);
status[food.y][food.x] = Food;
mvaddch(food.y, food.x, '*');
}
//判断得分和是否结束,x,y分别指左移或右移的距离,如果吃到食物根据方向做相应的处理
void judge() {
if (snack.dir == Up) {
if (snack.x == food.x && snack.y - 1 == food.y) {
status[snack.y][snack.x] = Body;
status[food.y][food.x] = Head;
mvaddch(snack.y, snack.x, '#');
mvaddch(food.y, food.x, '@');
for (int i = snack.len; i >= 1; i--)
body[i] = body[i - 1];
body[0].x = snack.x;
body[0].y = snack.y;
snack.len++;
snack.y--;
score++;
GenerateFood();
} else if (status[snack.y - 1][snack.x] == Wall || status[snack.y - 1][snack.x] == Body || status[snack.y][snack.x - 1] == Wall || status[snack.y][snack.x+1] == Wall) {
Game_Defeat();
}
} else if (snack.dir == Down) {
if (snack.x == food.x && snack.y + 1 == food.y) {
status[snack.y][snack.x] = Body;
status[food.y][food.x] = Head;
mvaddch(snack.y, snack.x, '#');
mvaddch(food.y, food.x, '@');
for (int i = snack.len; i >= 1; i--)
body[i] = body[i - 1];
body[0].x = snack.x;
body[0].y = snack.y;
snack.len++;
snack.y--;
score++;
GenerateFood();
} else if (status[snack.y + 1][snack.x] == Wall || status[snack.y + 1][snack.x] == Body||status[snack.y][snack.x - 1] == Wall || status[snack.y][snack.x+1] == Wall) {
Game_Defeat();
}
} else if (snack.dir == Left) {
if (snack.x - 1 == food.x && snack.y == food.y) {
status[snack.y][snack.x] = Body;
status[food.y][food.x] = Head;
mvaddch(snack.y, snack.x, '#');
mvaddch(food.y, food.x, '@');
for (int i = snack.len; i >= 1; i--)
body[i] = body[i - 1];
body[0].x = snack.x;
body[0].y = snack.y;
snack.len++;
snack.x = snack.x - 1;
score++;
GenerateFood();
} else if (status[snack.y][snack.x - 1] == Wall||status[snack.y][snack.x - 1] == Body||status[snack.y+1][snack.x+1] == Wall||status[snack.y -1][snack.x]==Wall) {
Game_Defeat();
}
} else if (snack.dir == Right) {
if (snack.x + 1 == food.x && snack.y == food.y) {
status[snack.y][snack.x] = Body;
status[food.y][food.x] = Head;
mvaddch(snack.y, snack.x, '#');
mvaddch(food.y, food.x, '@');
for (int i = snack.len; i >= 1; i--)
body[i] = body[i - 1];
body[0].x = snack.x;
body[0].y = snack.y;
snack.len++;
snack.x = snack.x + 1;
score++;
GenerateFood();
} else if (status[snack.y][snack.x + 1] == Wall||status[snack.y][snack.x + 1] == Body||status[snack.y+1][snack.x+1] == Wall||status[snack.y -1][snack.x]==Wall) {
Game_Defeat();
}
}
}
void Clear_Snack() {//更新蛇之前先擦掉蛇
mvaddch(snack.y, snack.x,' ');
status[snack.y][snack.x] = Nothing;
for (int i = 0 ;i < snack.len; i++) {
mvaddch(body[i].y, body[i].x, ' ');
status[body[i].y][body[i].x] = Nothing;
}
}
void Draw_Snack() {//画蛇
status[snack.y][snack.x] = Head;
mvaddch(snack.y, snack.x, '@');
for (int i = 0; i < snack.len; i++) {
status[body[i].y][body[i].x] = Body;
mvaddch(body[i].y, body[i].x, '#');
}
}
void Dir_Change() {//根据输入调整方向
if (_kbhit()) {
switch (_getch()) {
case 'q':
case 'Q':
flag_q = 1;
break;
case 'W':
case 'w':
if (snack.dir != Down) {
snack.dir = Up;
break;
} else {
Game_Defeat();
break;
}
case 'S':
case 's':
if (snack.dir != Up) {
snack.dir = Down;
break;
} else {
Game_Defeat();
break;
}
case 'A':
case 'a':
if (snack.dir != Right) {
snack.dir = Left;
break;
} else {
Game_Defeat();
break;
}
case 'D':
case 'd':
if (snack.dir != Left) {
snack.dir = Right;
break;
} else {
Game_Defeat();
break;
}
case 'p':
case 'P':
while (1){
if (_getch() == ' ') {
break;
}
}
}
}
}
void MoveSnack() {//改变坐标
for (int i = snack.len - 1; i >= 0; i--) {
if ( i != 0) {
body[i] = body[i - 1];
} else {
body[i].x = snack.x;
body[i].y = snack.y;
}
}
switch (snack.dir) {
case Up:
snack.y--;
break;
case Down:
snack.y++;
break;
case Left:
snack.x = snack.x - 1;
break;
case Right:
snack.x = snack.x + 1;
break;
}
}
int main(){
consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); // 初始化这个操作器
CONSOLE_SCREEN_BUFFER_INFO csbi; // 屏幕的信息
GetConsoleScreenBufferInfo(consoleHandle, &csbi); // 获取屏幕信息
w = csbi.dwSize.X;
h = csbi.dwSize.Y; // 得到宽度高度
// 游戏里面,如果一直有输入的光标,就有点不好看。我们可以让它不显示
CONSOLE_CURSOR_INFO cci; // 光标信息
cci.dwSize = 100;
cci.bVisible = FALSE; // 不可见
SetConsoleCursorInfo(consoleHandle, &cci); // 将光标特性应用到控制台
// 到这里,闪烁的光标就消失了。
ClearScr();
EnterGame();//调出开始界面
getch();
ClearScr();
PrintBoard();
InitSnack();
GenerateFood();
while (1){
if (flag_q == 1) {
goto end;
}
MoveSnack();
Draw_Snack();
if (score <= 3){
Sleep(160);
} else if (score <= 7) {
Sleep(190);
} else if (score <= 12) {
Sleep(210);
} else {
Sleep(250);
}
PrintScore(10, 94, score);
Dir_Change();
if (flag_death == 1) {
flag_death = 0;
getch();
main();
}
judge();
if (flag_death == 1) {
flag_death = 0;
getch();
main();
}
Clear_Snack();
}
end :cci.bVisible = TRUE; // 可见
SetConsoleCursorInfo(consoleHandle, &cci); // 重新设置
}