本文共 8771 字,大约阅读时间需要 29 分钟。
C编写的控制台应用程序(黑框框~~~)
实现了贪吃蛇的一些基本功能如 : 积分、等级加速、暂停等简单功能
下面我们开始吧~
首先是准备工作:
其中的 COORD是 windows里面定义的一个结构体用于存储二维坐标
原型如下:
typedef struct _COORD{ // 用于存储二维坐标的结构体
SHORT X;
SHORT Y;
}COORD, *PCOORD;
#include#include #include #include #include #include #define W 26//边界宽度#define L 61//边界长度#define UP 72#define LEFT 75//依此是键盘上下左右对应的ASCII#define RIGHT 77#define DOWN 80#define SNAKE 1#define FOOD 2#define BAR 3#define MIDX L/2-4 //打印 Game Over 的位置#define MIDY W/2-2 typedef struct Node{ COORD cor; struct Node* next;}node;int score = 0; //记录分数int delay = 200; //控制蛇移动速度int len = 1; //蛇长度int grade = 1; //等级node* head;//蛇头COORD food;COORD mid = {MIDX, MIDY};COORD info_score = {L, W/3}; //打印分数 的位置COORD info_grade = {L, W/3+8};//打印等级的位置
还需要准备一个定位光标位置的函数:(能够把光标移动到pt 所指定的二维坐标处)
void gotoxy(COORD pt) { HANDLE hout;// 句柄 hout = GetStdHandle(STD_OUTPUT_HANDLE);//定位到标准输出 SetConsoleCursorPosition(hout, pt); }下面由于要 比较坐标是否相等的方较多,可用函数表示:
int cor_cmp(COORD pt1, COORD pt2) { return (pt1.X == pt2.X && pt1.Y == pt2.Y); }int cor_cmp(COORD pt1, COORD pt2) { return (pt1.X == pt2.X && pt1.Y == pt2.Y); }
判断坐标是否在边界上:
int in_border(COORD pt) { return (pt.X == L-1 || pt.X == 0 || pt.Y == 0 || pt.Y == W-1); }
贪吃蛇的代码实现 主要分为五个部分:
1、初始化地图 2、获取移动方向 3、产生食物 4、移动和 局部更新(此方法没有选择完全更新整个地图) 5、判断游戏是否结束
一、初始化地图
void init_pos()//随机生成初始食物坐标和 蛇坐标 { srand((unsigned int)time(NULL)); do{ food.X = rand() % (L-20) + 10; food.Y = rand() % (W-5) + 5; head->cor.X = rand() % 60; head->cor.Y = rand() % 25; }while(cor_cmp(head->cor, food) || in_border(head->cor) || in_border(food)); }void init_game() { int i, j; head = (node *)malloc(sizeof(node));//先创建蛇头 然后初始化 head->next = NULL;//别忘记 置为NULL init_pos();//生成初始食物和蛇 for(i = 0; i < W; i++ ){ for(j = 0; j <= L; j++ ){ if(i == info_score.Y && j == info_score.X){//打印分数栏 printf("\t\tscore: %d",score); } if(i == info_grade.Y && j == info_grade.X){//打印等级栏 printf("\t\tgrade: %d",grade); } else if((i == 0 && j != L) || (i == W-1 && (j != L)) || j == 0 || j == L-1) putchar('#');//边界符号 else if(i == food.Y && j == food.X) putchar('@');//食物符号 else if(i == head->cor.Y && j == head->cor.X) putchar('$'); //蛇头符号 else putchar(' '); } putchar('\n'); } }
二、获取移动方向
注: kbhit()函数判断有无输入 , getch()读取输入但不回显(显示)
int get_dir(int old_dir) { int new_dir = old_dir; if(kbhit() != 0){//这里保证了 如果没有输入 蛇头的方向将保持不变 getch();//第一次返回只是扩展字符 第二次返回真正的方向 new_dir = getch(); if(len > 1 && (abs(new_dir-old_dir) == 2 || abs(new_dir-old_dir) == 8)) //根据键值差 保证蛇不能回头 (回头就死了) new_dir = old_dir; } return new_dir; //返回蛇头方向 }
三、产生食物(返回的是食物位置的结构体)
COORD generate_food() { srand((unsigned int)time(NULL));//随机数产生食物(放在循环外面 , 因为这是伪随机数) int in_snake = 0;// 标记 食物是否 和 蛇身坐标重合 node *p; COORD new_food; do{ new_food.X = rand() % 60; new_food.Y = rand() % 20; p = head; while(p != NULL){ if(cor_cmp(new_food, p->cor)) in_snake = 1; p = p->next; } }while(new_food.X == 0 || new_food.X == L-1 || new_food.Y == 0 || new_food.Y == W-1 || in_snake);//满足不在边界 不重合 才停止 return new_food; }
四、移动和 局部更新
void move_snake(int dir) { COORD last, current; last = head->cor;//记录当前蛇头的坐标 switch(dir){ case UP : head->cor.Y--; break; case DOWN : head->cor.Y++; break; case LEFT : head->cor.X--; break; case RIGHT : head->cor.X++; break; } node *p; int grow = 0; if(cor_cmp(food, head->cor)){ grow = 1; food = generate_food(); } p = head->next; while(p != NULL){//更新蛇身体 蛇头给第二节、 第二节 给第三节 依此进行 知道蛇尾 其中 结束时候last保存的是上一过程蛇尾的坐标 current = p->cor; p->cor = last; last = current; gotoxy(p->cor); putchar('*'); //蛇身体 用 * 表示 p = p->next; } gotoxy(head->cor);//光标定位到新的蛇头处 打印蛇头 putchar('$'); if(grow){ for(p = head; p->next != NULL; p = p->next); //找到 蛇尾 p->next = (node *)malloc(sizeof(node)); p->next->cor = last; p->next->next = NULL;// 不要忘记 置为NULL 否则 可能 找不到尾巴 gotoxy(food);//定位到新的食物的位置 打印食物 putchar('@'); len++; //蛇长度增加 score += 10;//累加分数 } else{//如果 没有吃到食物 把上一循环的 蛇尾 也就是last 位置 清空 (打印为空白) gotoxy(last); putchar(' '); } gotoxy(info_score);//定位到分数和等级 处 更新分数和等级 printf("\t\tscore: %d",score); if(len >= 5){ grade++; len -= 5; delay -= 30; gotoxy(info_grade); printf("\t\tgrade: %d",grade); } Sleep(delay);// 这个 包含在windows.h 控制蛇的速度 }五、判断游戏是否结束
int is_alive() { int eat_self = 0; //标记 是否自食 node *p; p = head; while(p->next != NULL){;//判断蛇头是否撞到自己 if(cor_cmp(head->cor, p->next->cor)) eat_self = 1; p = p->next; } return (head->cor.X == L-1 || head->cor.X == 0|| head->cor.Y == W-1 || head->cor.Y == 0 || eat_self == 1) ? 0 : 1; }六、其他(控制暂停的)
int is_key(int k)//判断是否是方向键 用于 判断是否是暂停 { return (k == UP || k == DOWN || k == RIGHT || k == LEFT); } void free(node* head)// 释放 空间 { node* p = head; node* q; while(p != NULL){ q = p->next; free(p); p = q; } }
完整实现 :
#include#include #include #include #include #include #define W 26#define L 61#define UP 72#define LEFT 75#define RIGHT 77#define DOWN 80#define SNAKE 1#define FOOD 2#define BAR 3#define MIDX L/2-4#define MIDY W/2-2 typedef struct Node{ COORD cor; struct Node* next;}node;int score = 0;int delay = 200;int len = 1;int grade = 1;node* head;COORD food;COORD mid = {MIDX, MIDY};COORD info_score = {L, W/3};COORD info_grade = {L, W/3+8};void gotoxy(COORD pt) { HANDLE hout; hout = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleCursorPosition(hout, pt); } int cor_cmp(COORD pt1, COORD pt2) { return (pt1.X == pt2.X && pt1.Y == pt2.Y); } int in_border(COORD pt) { return (pt.X == L-1 || pt.X == 0 || pt.Y == 0 || pt.Y == W-1); } void init_pos() { srand((unsigned int)time(NULL)); do{ food.X = rand() % (L-20) + 10; food.Y = rand() % (W-5) + 5; head->cor.X = rand() % 60; head->cor.Y = rand() % 25; }while(cor_cmp(head->cor, food) || in_border(head->cor) || in_border(food)); }void init_game() { int i, j; head = (node *)malloc(sizeof(node)); head->next = NULL; init_pos(); for(i = 0; i < W; i++ ){ for(j = 0; j <= L; j++ ){ if(i == info_score.Y && j == info_score.X){ printf("\t\tscore: %d",score); } if(i == info_grade.Y && j == info_grade.X){ printf("\t\tgrade: %d",grade); } else if((i == 0 && j != L) || (i == W-1 && (j != L)) || j == 0 || j == L-1) putchar('#'); else if(i == food.Y && j == food.X) putchar('@'); else if(i == head->cor.Y && j == head->cor.X) putchar('$'); else putchar(' '); } putchar('\n'); } } int get_dir(int old_dir) { int new_dir = old_dir; if(kbhit() != 0){ getch(); new_dir = getch(); if(len > 1 && (abs(new_dir-old_dir) == 2 || abs(new_dir-old_dir) == 8)) new_dir = old_dir; } return new_dir; } COORD generate_food() { srand((unsigned int)time(NULL)); int in_snake = 0; node *p; COORD new_food; do{ new_food.X = rand() % 60; new_food.Y = rand() % 20; p = head; while(p != NULL){ if(cor_cmp(new_food, p->cor)) in_snake = 1; p = p->next; } }while(new_food.X == 0 || new_food.X == L-1 || new_food.Y == 0 || new_food.Y == W-1 || in_snake); return new_food; } void move_snake(int dir) { COORD last, current; last = head->cor; switch(dir){ case UP : head->cor.Y--; break; case DOWN : head->cor.Y++; break; case LEFT : head->cor.X--; break; case RIGHT : head->cor.X++; break; } node *p; int grow = 0; if(cor_cmp(food, head->cor)){ grow = 1; food = generate_food(); } p = head->next; while(p != NULL){ current = p->cor; p->cor = last; last = current; gotoxy(p->cor); putchar('*'); p = p->next; } gotoxy(head->cor); putchar('$'); if(grow){ for(p = head; p->next != NULL; p = p->next); p->next = (node *)malloc(sizeof(node)); p->next->cor = last; p->next->next = NULL;// 不要忘记 置为NULL 否则 可能 找不到尾巴 gotoxy(food); putchar('@'); len++; score += 10; } else{ gotoxy(last); putchar(' '); } gotoxy(info_score); printf("\t\tscore: %d",score); if(len >= 5){ grade++; len -= 5; delay -= 30; gotoxy(info_grade); printf("\t\tgrade: %d",grade); } Sleep(delay); } int is_alive() { int eat_self = 0; node *p; p = head; while(p->next != NULL){ if(cor_cmp(head->cor, p->next->cor)) eat_self = 1; p = p->next; } return (head->cor.X == L-1 || head->cor.X == 0|| head->cor.Y == W-1 || head->cor.Y == 0 || eat_self == 1) ? 0 : 1; } int is_key(int k) { return (k == UP || k == DOWN || k == RIGHT || k == LEFT); } void free(node* head) { node* p = head; node* q; while(p != NULL){ q = p->next; free(p); p = q; } } int main() { int dir = UP; int old_dir =UP; init_game(); while(1){ if(is_key(dir)) old_dir = dir; dir = get_dir(dir); while(!is_key(dir)){ dir = get_dir(old_dir); } move_snake(dir); if(!is_alive()){ break; } } gotoxy(mid); printf("Game Over!"); getchar(); free(head); return 0; }
总之 、这是 一个 非常简易的 贪吃蛇 游戏 ,主要是练习指针 和 结构体的使用。