SWUST 2021 —— 数据结构课程设计 贪吃蛇
#include <stdio.h> #include <stdlib.h> #include <windows.h> #include <conio.h> #include <time.h> const int Width = 50; const int Height = 25; int Speed = 200;//全局-记录速度 int Point;//全局-记录分数 int Level = 1; int Count = 1; int Number = 1; typedef struct Snake { int x, y; Snake* prev; Snake* next; }Snake, * Snakee; typedef struct Food { int x, y; bool isEat; }Food; //获取蛇坐标 void gotoxySnake(Snakee snake) { COORD position = { snake->x,snake->y }; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), position); } //获取实体坐标 void gotoxyEntity(int x, int y) { COORD position = { x,y }; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), position); } Snakee snake_move(Snakee snake, int dir, Food& food); void snake_grow(Snakee snake, int x, int y); void free_snake(Snakee snake); bool snake_die_itself(Snakee snake); bool snake_die_edge(int x, int y); bool snake_eat(int s_x, int s_y, int f_x, int f_y); int snake_get_dir(int old_dir); Snakee init_snake(); void generate_food(Food& food, Snakee* snake); bool food_is_eat(Food& food, Snakee* snake); void level_add(); void speed_add(); void init_game(Snakee snake, Food& food); int main() { HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO CursorInfo; GetConsoleCursorInfo(handle, &CursorInfo);//获取控制台光标信息 CursorInfo.bVisible = false; //隐藏控制台光标 SetConsoleCursorInfo(handle, &CursorInfo);//设置控制台光标状态 Snakee snake; Food food; snake = init_snake(); int new_dir = 75;//向左 init_game(snake, food); while (true) { if (_kbhit()) { new_dir = snake_get_dir(new_dir); } if (snake) { snake = snake_move(snake, new_dir, food); } if (food.isEat == true) { generate_food(food, &snake); } } } void init_game(Snakee snake, Food& food) { Snakee p = snake; do { gotoxySnake(p), printf("O"); p = p->next; } while (p != snake); gotoxyEntity(Width, Height / 4), printf(" Point: %d", Point); gotoxyEntity(Width, Height / 4 + Height / 8), printf(" Speed: %d", Speed); gotoxyEntity(Width, (Height / 4) - Height / 8), printf(" Level: %d", Level); gotoxyEntity(Width + 20, Height), printf("By 5121095644 周麟志"); gotoxyEntity(Width + 20, Height/3), printf(" 本人博客园 "); gotoxyEntity(Width + 20, (Height/2)), printf("https://www.cnblogs.com/lightWh1te/"); for (int i = Width; i < Width + Width / 3; i++) { gotoxyEntity(i, Height / 2), printf("*"); gotoxyEntity(i, 1), printf("*"); gotoxyEntity(i, Height), printf("*"); } for (int i = 1; i <= Width; i++) { gotoxyEntity(i, 1), printf("*"); gotoxyEntity(i, Height), printf("*"); } for (int i = 1; i <= Height; i++) { gotoxyEntity(1, i), printf("*"); gotoxyEntity(Width, i), printf("*"); gotoxyEntity(Width + Width / 3, i), printf("*"); } food.isEat = false; food.x = 10; food.y = 10; gotoxyEntity(food.x, food.y), printf("#"); } void generate_food(Food& food, Snakee* snake) { srand(time(NULL)); bool generate = true; if (generate == true) { generate = false; food.x = (rand() % (Width - 2 - 5 + 1)) + 5; food.y = (rand() % (Height - 2 - 5 + 1)) + 5; if (food_is_eat(food, snake)) { generate = true; } } food.isEat = false; gotoxyEntity(food.x, food.y); printf("#"); } bool food_is_eat(Food& food, Snakee* snake) { Snakee p = *snake;//这里获得了一级指针 food.isEat = true; if (food.x == p->x && food.y == p->y) { return true; } else return false; } void snake_grow(Snakee snake, int x, int y) { Snakee head = (Snakee)malloc(sizeof(Snake)); head->prev = snake->prev; head->next = snake; head->x = x; head->y = y; snake->prev->next = head; snake->prev = head; } Snakee init_snake() { Snakee head = (Snakee)malloc(sizeof(Snake)); head->x = 36; head->y = 5; head->next = head;//双向循环链表初始化 head->prev = head; for (int i = 36; i < 40; i++) { snake_grow(head, i, 5); } return head; } int snake_get_dir(int old_dir) { int new_dir = old_dir; if (_kbhit()) { _getch(); new_dir = _getch();//第二次getch才是实际字符 if (abs(new_dir - old_dir == 2) || abs(new_dir - old_dir) == 8) { new_dir = old_dir; } } return new_dir; } Snakee snake_move(Snakee snake, int dir, Food& food) { Snakee head; head = (Snakee)malloc(sizeof(Snake)); head->prev = snake->prev; head->next = snake; head->x = snake->x; head->y = snake->y; snake->prev->next = head; snake->prev = head; Snakee p = head->prev; int tail_x = p->x; int tail_y = p->y; gotoxySnake(p); printf(" "); p->prev->next = p->next; p->next->prev = p->prev; free(p); int new_dir = snake_get_dir(dir); if (dir) { switch (new_dir) { case 72://上 head->y--; break; case 77://右 head->x++; break; case 75://左 head->x--; break; case 80://下 head->y++; break; default: break; } } if (snake_die_edge(head->x, head->y) == true || snake_die_itself(snake) == true) { system("cls"); gotoxyEntity((Width + 10) / 2, (Height) / 2); printf("Game Over\n"); free_snake(snake); return 0; } if (snake_eat(head->x, head->y, food.x, food.y) == true) { food.isEat = true; Snakee tail = (Snakee)malloc(sizeof(Snake)); tail->prev = head->prev; tail->next = head; tail->x = tail_x; tail->y = tail_y; head->prev->next = tail; head->prev = tail; } gotoxySnake(head), printf("O"); Sleep(Speed); return head; } bool snake_die_edge(int x, int y) { if (x == 1 || x == Width || y == 1 || y == Height) { return true; } else return false; } bool snake_die_itself(Snakee snake) { Snakee t; t = snake->next->next; do { if (snake->x == t->x && snake->y == t->y) { return true; } t = t->next; } while (t != snake); return false; } bool snake_eat(int s_x, int s_y, int f_x, int f_y) { if (Number % 3 == 0) { Level++; Number = 1; gotoxyEntity(Width, (Height / 4) - Height / 8), printf(" Level: %d", Level); speed_add(); } if (s_x == f_x && s_y == f_y) { Count++; Number++; Point += 10; gotoxyEntity(Width, Height / 4), printf(" Point: %d", Point); return true; } else return false; } void free_snake(Snakee snake) { Snakee p = snake; do { Snakee t = p; p = p->next; free(t); } while (p != snake); } void speed_add() {//速度逻辑 Speed -= 20; gotoxyEntity(Width, Height / 4 + Height / 8), printf(" Speed: %d", Speed); }
V1.0
-
建模:程序界面是一个二维平面图
-
蛇:蛇的身体可以看作是链表的节点,当蛇吃到食物时,就增加一节链表长度++;
-
食物:随机生成
-
蛇的移动:取得上下左右键的ASCII码的键值,通过switch语句响应相应的键值对,实现蛇的方向获取
-
闪烁问题:通过局部打印空格与蛇身节点的移动,来实现局部更新可避免全局绘制造成的闪烁问题,也能提高性能
-
获取实体:获取游戏一个实体的坐标需要得到当前窗口的句柄,并重写gotoxy()函数,在绘制时也要注意绘制重合的问题
-
结构:把相应的功能模块化,结构化,封装成一个个函数,像蛇逻辑有自噬死亡,撞墙死亡,吃食,变长等
-
交互:在相应的自定义逻辑函数中调用各自的函数,主函数则主要写下简短的循环框架,直到游戏结束
- Windows窗口