一个简单的控制台小游戏,用链表实现贪吃蛇。代码不到两百行,压缩一下可以在100行以内。通过链表,指针,光标控制,代码还是非常优雅的
很多同学刚学C语言,不知道能干嘛。其实C语言什么都能干,只不过可能比较复杂,但深入学习C语言,对于底层数据结构和算法原理学习极有好处。
以后再学习其他语言,学习速度跟坐飞机一样。
比如这个贪吃蛇小程序,实现方式有很多,数组也行,这里用链表,主要是为了练习链表和指针的使用。
一、控制光标
想让控制台成为游戏画面,printf作为输出,得先实现指定位置printf
头文件#include
代码:
COORD coord;
HANDLE ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
//获取控制台缓冲区句柄
coord.X = x;
coord.Y = y;
//设置光标位置
SetConsoleCursorPosition(ConsoleHandle, coord);
printf("*");
使用以上代码就可以在(x,y)位置输出,控制台左上角坐标为(0,0).
二、定时贪吃蛇每走一步,需要间隔一段时间,所以需要定时。
curTime = GetTickCount();
if (curTime - lastTime > 300)
{
if (move(dire, &head, food, &tail) == -1)
break;
lastTime = curTime;
}
move函数是贪吃蛇的移动函数,GetTickCount()获取当前时间,当前时间和上次时间只差大于300ms就移动一次。
你也可以用一个宏定义,或者从键盘输出控制间隔时间也就是控制速度(难度)。
三、获取方向头文件:#include
代码:
if (_kbhit())
{
switch (_getch())
{
case 'w':
dire = 'w'; break;
case 's':
dire = 's'; break;
case 'a':
dire = 'a'; break;
case 'd':
dire = 'd'; break;
default:break;
}
}
_kbhit()函数检测键盘是否有按下,有则返回1,无则返回0;_getch()函数获取键盘值。
我们用w,s,a,d分别表示上,下,左,右。
四、构造贪吃蛇typedef struct {
int x;
int y;
}POS;
typedef struct BODY {
POS pos;
struct BODY* next;
}BODY;
BODY* head = malloc(sizeof(BODY));
BODY* body = malloc(sizeof(BODY));
BODY* tail = malloc(sizeof(BODY));
BODY* food = malloc(sizeof(BODY));
int lastTime;
int curTime;
head->pos.x = w_max / 2;
head->pos.y = h_max / 2;
body->pos.x = w_max / 2 + 1;
body->pos.y = h_max / 2;
tail->pos.x = w_max / 2 + 2;
tail->pos.y = h_max / 2;
head->next = body;
body->next = tail;
tail->next = NULL;
每一节身体,都有两部分内容,pos坐标,next指针,指向下一节的位置。初始化贪吃蛇为三节,head头,body身体,tail尾巴。
五、移动没有吃到食物的移动:
移动位置根据方向确定。
首先检测新的位置在不在身体上,也就是链表的遍历,在这个遍历过程中,我们把尾巴的前一个位置pretail记录下来。
移动过程其实就是把尾巴变成新的头,尾巴的前一节身体变成尾巴,新的位置变为头,原来的头变成头的下一节。
吃到食物的移动:
其实就是食物的位置变成了头结点。
六、完整代码如果编译不通过,只要将 malloc(sizeof(BODY))改为(BODY*)malloc(sizeof(BODY))。
自己可以加一下界面,比如开始和gameover,以及速度选择,地图大小选择。
tips:如果要做失败从头再来的功能记得释放malloc出来的空间。
#include#include #include #include #define H 52//长 #define W 102//宽 #define h_max (H-1) #define w_max (W-1) #define h_min 1 #define w_min 1 typedef struct { int x; int y; }POS; typedef struct BODY { POS pos; struct BODY* next; }BODY; void produceFood(BODY* food) { food->pos.x = rand() % (W - 2) + 1; food->pos.y = rand() % (H - 2) + 1; show_point(food->pos.x, food->pos.y); } int show_point(int x, int y) { COORD coord; HANDLE ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); coord.X = x; coord.Y = y; //设置光标位置 SetConsoleCursorPosition(ConsoleHandle, coord); printf("*"); return 0; } int clear_point(int x, int y) { COORD coord; HANDLE ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台缓冲区句柄 coord.X = x; coord.Y = y; //设置光标位置 SetConsoleCursorPosition(ConsoleHandle, coord); printf(" "); return 0; } int move(char dire, BODY** head, BODY* food, BODY** tail) { BODY newhead; BODY* search; BODY* pretail; pretail = NULL; int pretail_flag = 0; newhead.pos.x = (*head)->pos.x; newhead.pos.y = (*head)->pos.y; switch (dire) { case 'w':newhead.pos.y--; break; case 's':newhead.pos.y++; break; case 'a':newhead.pos.x--; break; case 'd':newhead.pos.x++; break; default:break; } if (newhead.pos.y >= h_max || newhead.pos.y < h_min || newhead.pos.x < w_min || newhead.pos.x >= w_max)//越界则退出 return -1; if (newhead.pos.x == food->pos.x && newhead.pos.y == food->pos.y) //吃到食物 { BODY* newHead = malloc(sizeof(BODY));//新申请一个空间并加入贪吃蛇头部 newHead->pos.x = newhead.pos.x; newHead->pos.y = newhead.pos.y; newHead->next = *head; *head = newHead; produceFood(food);//吃了食物要产生一个新的食物 return 0; } else//没吃到食物则按方向前进(其实就是尾巴变成新的头部,尾巴的前一个变尾巴),同时需看新的位置是否在身体上 { search = *head; pretail_flag = 0; while (1) { if(pretail_flag==0) { pretail = search; if ((pretail->next)->next == NULL) pretail_flag = 1; } if (search->pos.x == newhead.pos.x && search->pos.y == newhead.pos.y) return -1; if (search->next == NULL) break; search = search->next; } clear_point((*tail)->pos.x, (*tail)->pos.y); show_point(newhead.pos.x, newhead.pos.y); //没有返回-1说明未碰撞,尾巴变新的头,pretail变新尾巴 pretail->next = NULL; (*tail)->next = *head; (*tail)->pos.x = newhead.pos.x; (*tail)->pos.y = newhead.pos.y; *head = *tail; *tail = pretail; return 0; } } void show()//画出背景 { COORD coord; //获取控制台缓冲区句柄 HANDLE ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); //设置光标位置 for (int i = 0; i < W; i++) { coord.X = i; coord.Y = 0; SetConsoleCursorPosition(ConsoleHandle, coord); printf("#"); coord.Y = H - 1; SetConsoleCursorPosition(ConsoleHandle, coord); printf("#"); } for (int i = 0; i < H; i++) { coord.X = 0; coord.Y = i; SetConsoleCursorPosition(ConsoleHandle, coord); printf("#"); coord.X = W - 1; SetConsoleCursorPosition(ConsoleHandle, coord); printf("#"); } } int main() { srand((unsigned)time(NULL)); //初始化 BODY* head = malloc(sizeof(BODY)); BODY* body = malloc(sizeof(BODY)); BODY* tail = malloc(sizeof(BODY)); BODY* food = malloc(sizeof(BODY)); int lastTime; int curTime; head->pos.x = w_max / 2; head->pos.y = h_max / 2; body->pos.x = w_max / 2 + 1; body->pos.y = h_max / 2; tail->pos.x = w_max / 2 + 2; tail->pos.y = h_max / 2; head->next = body; body->next = tail; tail->next = NULL; lastTime = GetTickCount(); // char dire;//0,1,2,3,上下左右 dire = 'w'; show(); show_point(head->pos.x, head->pos.y); show_point(body->pos.x, body->pos.y); show_point(tail->pos.x, tail->pos.y); produceFood(food); while (1) { if (_kbhit()) { switch (_getch()) { case 'w': dire = 'w'; break; case 's': dire = 's'; break; case 'a': dire = 'a'; break; case 'd': dire = 'd'; break; default:break; } } curTime = GetTickCount(); if (curTime - lastTime > 300) { if (move(dire, &head, food, &tail) == -1) break; lastTime = curTime; } } return 0; }



