使用纯C实现贪吃蛇小游戏,欢迎小伙伴探讨使用纯C实现的其他算法。感兴趣的伙伴可以自己也试着写一下,如果纯C不太容易,使用其他面向对象的语言实现会轻松不少。可使用上/下/左/右键或A/S/D/W键控制贪吃蛇移动方向,先上一张程序跑起来的状态图:
使用C来写贪吃蛇比我想象的要容易,想着10余年前自己玩的游戏,应该需要几千行代码实现,事实上就400行左右,贪吃蛇的核心代码更少,不足100行,这一点心里还是有点落差。使用C++实现代码应该还要少不少。实现的算法逻辑非常简单,就直接贴代码啦。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ROW 20 // 域行数(高y)
#define COL 40 // 域列数(宽x)
#define SUCCESS 0
#define FAILURE -1
#define NAME_LENGTH 32 // 名字最大长度
#define MOVE_SPEED 10 // 移动速度(每秒移动的次数,10:正常,100:变态)
#define DEBUG 0 // 调试模式,0:关闭调试,other:打开调试
#if DEBUG
#define print_error() do { \
printf("Error: %s, %d\n", __FUNCTION__, __LINE__); \
} while(0)
#else
#define print_error()
#endif
typedef char int8;
typedef unsigned char uint8;
typedef short int int16;
typedef unsigned short int uint16;
typedef long int int32;
typedef unsigned long int uint32;
typedef struct {
// 坐标位置
int16 x;
int16 y;
} pos_t;
typedef struct node_t {
// 蛇的节点位置
pos_t pos;
struct node_t *next;
struct node_t *prev;
} snake_node_t;
typedef enum {
// 移动方向
UP = 0,
DOWN,
LEFT,
RIGHT,
DIR_BUTT
} dir_t;
typedef struct {
// 蛇信息
int32 id;
int8 name[NAME_LENGTH];
int32 speed;
int32 length;
dir_t dir;
snake_node_t *head;
snake_node_t *tail;
} snake_t;
typedef struct {
// 域信息
pos_t food;
snake_t snake;
} field_t;
int8 initial(field_t *field); // 域初始化
int8 create_snake(int32 id, int8 *name, int32 speed, pos_t head, int32 length, dir_t dir, snake_t *snake); // 创建蛇
int8 collision(pos_t *pos, snake_t *snake); // 碰撞检测
int8 get_food(field_t *field); // 获取食物
void get_move_dir(snake_t *snake); // 获取移动方向
void free_field(snake_t *snake); // 销毁
void set_pos(int32 x, int32 y); // 设置光标位置
void hide(void); // 隐藏光标
void snake_step(snake_t *snake); // 蛇移动一步
int8 snake_grow_up(snake_t *snake); // 蛇吃到食物
int8 snake_move(field_t *field); // 蛇移动
void display_init(field_t *field); // 显示初始化
void display(field_t *field); // 显示
void snake_debug(field_t *field); // 调试打印
/*******************************************************************************
* 函 数 名:main
* 函数功能:主函数
* 输 入:void
* 输 出:int:是否异常
* 说 名:none
*******************************************************************************/
int main(void) // 主函数
{
field_t field;
initial(&field);
#if (!DEBUG)
display_init(&field);
#endif
while(1) {
#if DEBUG
snake_debug(&field);
#endif
get_move_dir(&(field.snake));
if (snake_move(&field) != SUCCESS) {
break;
}
get_food(&field);
#if (!DEBUG)
display(&field);
#endif
Sleep(1000 / field.snake.speed);
}
free_field(&field.snake);
return 0;
}
/*******************************************************************************
* 函 数 名:initial
* 函数功能:域初始化
* 输 入:field:域信息
* 输 出:int8:是否成功
* 说 名:none
*******************************************************************************/
int8 initial(field_t *field) // 域初始化
{
// 长度3不能比起始位置(pos_t){ 2, 0 }的x值2超过1
// 多条贪吃蛇尽量只修改id,name,起始位置(pos_t){ 2, 0 }的y值
create_snake(2020, "CX19970", MOVE_SPEED, (pos_t){
2, 0 }, 3, RIGHT, &(field->snake));
field->food.x = COL / 2;
field->food.y = ROW / 2;
return SUCCESS;
}
/*******************************************************************************
* 函 数 名:create_snake
* 函数功能:创建蛇
* 输 入:id:序号
name:蛇的名字
speed:移动速度
head:蛇头位置
length:蛇的长度
dir:蛇的移动方向
snake:蛇信息
* 输 出:int8:是否成功
* 说 名:create_snake(2020, "moon", MOVE_SPEED, (pos_t){ 2, 0 }, 3, RIGHT, &(field->snake));
长度3不能比起始位置(pos_t){ 2, 0 }的x值2超过1,
多条贪吃蛇尽量只修改id,name,起始位置(pos_t){ 2, 0 }的y值。
*******************************************************************************/
int8 create_snake(int32 id, int8 *name, int32 speed, pos_t head, int32 length, dir_t dir, snake_t *snake) // 创建蛇
{
int32 i, len;
len = strlen((char *)name);
if ((len >= NAME_LENGTH) || (length < 1) ||
(head.x >= COL) || (head.y >= ROW) || (length > head.x + 1)) {
print_error();
return FAILURE;
}
snake->id = id;
memcpy(snake->name, name, len);
snake->name[len] = '\0';
snake->speed = speed;
snake->length = length;
snake->dir = dir;
snake_node_t *p = NULL;
p = (snake_node_t *)malloc(sizeof(snake_node_t));
if (p == NULL) {
print_error();
return FAILURE;
}
snake->head = p;
snake->tail = p;
p->next = NULL;
p->prev = NULL;
snake->head->pos.x = head.x;
snake->head->pos.y = head.y;
for (i = 1; i < length; i++) {
p = (snake_node_t *)malloc(sizeof(snake_node_t));
if (p == NULL) {
print_error();
return FAILURE;
}
snake->tail->next = p;
p->prev = snake->tail;
p->next = NULL;
snake->tail = p;
p->pos.x = head.x - i;
p->pos.y = head.y;
}
return SUCCESS;
}
/*******************************************************************************
* 函 数 名:collision
* 函数功能:碰撞检测
* 输 入:pos:待检测位置,snake:蛇信息
* 输 出:int8:0未发生碰撞,1发生碰撞
* 说 名:none
*******************************************************************************/
int8 collision(pos_t *pos, snake_t *snake) // 碰撞检测
{
snake_node_t *p = NULL;
if ((pos->x < 0) || (pos->y < 0) || (pos->x > COL - 1) || (pos->y > ROW - 1)) {
// 越界
return 1;
}
p = snake->head; // 蛇体
while (p != NULL) {
if ((pos->x == p->pos.x) && (pos->y == p->pos.y)) {
return 1;
}
p = p->next;
}
return 0;
}
/*******************************************************************************
* 函 数 名:get_food
* 函数功能:获取食物
* 输 入:field:域信息
* 输 出:int8:是否成功
* 说 名:none
*******************************************************************************/
int8 get_food(field_t *field) // 获取食物
{
pos_t pos;
if ((field->food.y != -1) || (field->food.x != -1)) {
// 已经存在食物
return SUCCESS;
}
srand((unsigned)time(NULL));
pos.x = rand() % COL;
pos.y = rand() % ROW;
while (collision(&pos, &(field->snake))) {
pos.x = rand() % COL;
pos.y = rand() % ROW;
}
field->food.x = pos.x;
field->food.y = pos.y;
return SUCCESS;
}
/*******************************************************************************
* 函 数 名:get_move_dir
* 函数功能:获取移动方向
* 输 入:snake:蛇信息
* 输 出:void
* 说 名:none
*******************************************************************************/
void get_move_dir(snake_t *snake) // 获取移动方向
{
int8 ch;
if (!_kbhit()) {
return;
}
ch = _getch();
if ((ch == 72) || (ch == 'w') || (ch == 'W')) {
if (snake->dir != DOWN) {
snake->dir = UP;
}
} else if ((ch == 80) || (ch == 's') || (ch == 'S')) {
if (snake->dir != UP) {
snake->dir = DOWN;
}
} else if ((ch == 75) || (ch == 'a') || (ch == 'A')) {
if (snake->dir != RIGHT) {
snake->dir = LEFT;
}
} else if ((ch == 77) || (ch == 'd') || (ch == 'D')) {
if (snake->dir != LEFT) {
snake->dir = RIGHT;
}
}
}
/*******************************************************************************
* 函 数 名:free_field
* 函数功能:销毁
* 输 入:snake:蛇信息
* 输 出:void
* 说 名:none
*******************************************************************************/
void free_field(snake_t *snake) // 销毁
{
snake_node_t *p = snake->tail;
snake_node_t *tail = NULL;
while (p != NULL) {
tail = p->prev;
free(p);
p = tail;
}
}
/*******************************************************************************
* 函 数 名:set_pos
* 函数功能:设置光标位置
* 输 入:x/y:坐标位置
* 输 出:void
* 说 名:none
*******************************************************************************/
void set_pos(int32 x, int32 y) // 设置光标位置
{
COORD pos;
HANDLE output;
pos.X = x;
pos.Y = y;
output = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(output, pos);
}
/*******************************************************************************
* 函 数 名:hide
* 函数功能:隐藏光标
* 输 入:snake:蛇信息
* 输 出:void
* 说 名:none
*******************************************************************************/
void hide(void) // 隐藏光标
{
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cursor_info = {
1, 0};
SetConsoleCursorInfo(hout, &cursor_info);
}
/*******************************************************************************
* 函 数 名:snake_step
* 函数功能:蛇移动一步
* 输 入:void
* 输 出:void
* 说 名:none
*******************************************************************************/
void snake_step(snake_t *snake) // 蛇移动一步
{
snake_node_t *p = NULL;
p = snake->tail;
snake->tail = snake->tail->prev;
snake->tail->next = NULL;
p->next = snake->head;
p->prev = NULL;
snake->head->prev = p;
snake->head = p;
}
/*******************************************************************************
* 函 数 名:snake_grow_up
* 函数功能:蛇吃到食物
* 输 入:snake:蛇信息
* 输 出:int8:是否成功
* 说 名:none
*******************************************************************************/
int8 snake_grow_up(snake_t *snake) // 蛇吃到食物
{
snake_node_t *p = NULL;
p = (snake_node_t *)malloc(sizeof(snake_node_t));
if (p == NULL) {
print_error();
return FAILURE;
}
p->next = snake->head;
snake->head->prev = p;
p->prev = NULL;
snake->head = p;
return SUCCESS;
}
/*******************************************************************************
* 函 数 名:snake_move
* 函数功能:蛇移动
* 输 入:field:域信息
* 输 出:int8:是否成功
* 说 名:none
*******************************************************************************/
int8 snake_move(field_t *field) // 蛇移动
{
dir_t dir;
snake_t *snake;
pos_t new_pos;
dir = field->snake.dir;
snake = &(field->snake);
if ((dir == UP) && (snake->head->pos.y < ROW - 1)) {
new_pos.x = snake->head->pos.x;
new_pos.y = snake->head->pos.y + 1;
} else if ((dir == DOWN) && (snake->head->pos.y > 0)) {
new_pos.x = snake->head->pos.x;
new_pos.y = snake->head->pos.y - 1;
} else if ((dir == LEFT) && (snake->head->pos.x > 0)) {
new_pos.x = snake->head->pos.x - 1;
new_pos.y = snake->head->pos.y;
} else if ((dir == RIGHT) && (snake->head->pos.x < COL - 1)) {
new_pos.x = snake->head->pos.x + 1;
new_pos.y = snake->head->pos.y;
} else {
print_error();
return FAILURE;
}
if (collision(&new_pos, snake)) {
print_error();
return FAILURE;
} else {
if((new_pos.x == field->food.x) && (new_pos.y == field->food.y)) {
snake_grow_up(snake);
snake->length++;
snake->head->pos.x = new_pos.x;
snake->head->pos.y = new_pos.y;
field->food.x = -1;
field->food.y = -1;
} else {
snake_step(snake);
snake->head->pos.x = new_pos.x;
snake->head->pos.y = new_pos.y;
}
}
return SUCCESS;
}
/*******************************************************************************
* 函 数 名:display_init
* 函数功能:显示初始化
* 输 入:field:域信息
* 输 出:void
* 说 名:none
*******************************************************************************/
void display_init(field_t *field) // 显示初始化
{
int8 temp[128];
int32 i, row, col;
snake_node_t *p = NULL;
row = ROW + 5;
col = COL + 4;
hide();
sprintf(temp, "mode con cols=%d lines=%d", (int)col, (int)row);
system((char *)temp);
set_pos(0, 0);
for (i = 0; i < col; i++) {
printf("=");
}
printf("\n");
set_pos((col - 14) / 2, 1);
printf("王 氏 贪 吃 蛇\n");
set_pos(0, 2);
for (i = 0; i < col; i++) {
printf("#");
}
printf("\n");
for (i = 0; i < ROW; i++) {
set_pos(0, ROW - 1 - i + 3);
printf("# ");
p = field->snake.head;
while (p != NULL) {
if (p->pos.y != i) {
p = p->next;
continue;
}
set_pos(p->pos.x + 2, ROW - 1 - p->pos.y + 3);
if ((p->pos.x == field->snake.head->pos.x) && (p->pos.y == field->snake.head->pos.y)) {
printf("*");
} else if ((p->pos.x == field->snake.tail->pos.x) && (p->pos.y == field->snake.tail->pos.y)) {
printf("+");
} else if ((p->pos.x == field->food.x) && (p->pos.y == field->food.y)) {
printf("$");
} else {
printf("=");
}
p = p->next;
}
set_pos(COL + 2, i + 3);
printf(" #\n");
}
set_pos(0, ROW + 3);
for (i = 0; i < col; i++) {
printf("#");
}
set_pos(0, ROW + 4);
printf("length: ");
display(field);
}
/*******************************************************************************
* 函 数 名:snake_debug
* 函数功能:调试打印
* 输 入:field:域信息
* 输 出:void
* 说 名:none
*******************************************************************************/
void snake_debug(field_t *field) // 调试打印
{
snake_node_t *p = NULL;
printf("food: (x:%d, y:%d) \n", (int)field->food.x, (int)field->food.y);
printf("snake: \n");
printf(" |--id: %d\n", (int)field->snake.id);
printf(" |--name: %s\n", (char *)field->snake.name);
printf(" |--speed: %d\n", (int)field->snake.speed);
printf(" |--length: %d\n", (int)field->snake.length);
printf(" |--dir: %d (0:U, 1:D, 2:L, 3:R)\n", (int)field->snake.dir);
printf(" |--head: ");
p = field->snake.head;
while (p != NULL) {
printf("(x:%d, y:%d)->", (int)p->pos.x, (int)p->pos.y);
p = p->next;
}
printf("NULL\n");
printf(" |--tail: ");
p = field->snake.tail;
while (p != NULL) {
printf("(x:%d, y:%d)->", (int)p->pos.x, (int)p->pos.y);
p = p->prev;
}
printf("NULL\n\n");
}
/*******************************************************************************
* 函 数 名:display
* 函数功能:显示
* 输 入:field:域信息
* 输 出:void
* 说 名:none
*******************************************************************************/
void display(field_t *field) // 显示
{
static int16 prev_food_x = -1;
static int16 prev_food_y = -1;
static int16 prev_head_x = -1;
static int16 prev_head_y = -1;
static int16 prev_tail_x = -1;
static int16 prev_tail_y = -1;
if (prev_food_x != -1 && prev_food_y != -1) {
set_pos(prev_food_x + 2, ROW - 1 - prev_food_y + 3);
printf(" ");
}
set_pos(field->food.x + 2, ROW - 1 - field->food.y + 3);
printf("$");
prev_food_x = field->food.x;
prev_food_y = field->food.y;
if (prev_head_x != -1 && prev_head_y != -1) {
set_pos(prev_head_x + 2, ROW - 1 - prev_head_y + 3);
printf("=");
}
set_pos(field->snake.head->pos.x + 2, ROW - 1 - field->snake.head->pos.y + 3);
printf("*");
prev_head_x = field->snake.head->pos.x;
prev_head_y = field->snake.head->pos.y;
if (prev_tail_x != -1 && prev_tail_y != -1) {
set_pos(prev_tail_x + 2, ROW - 1 - prev_tail_y + 3);
printf(" ");
}
set_pos(field->snake.tail->pos.x + 2, ROW - 1 - field->snake.tail->pos.y + 3);
printf("+");
prev_tail_x = field->snake.tail->pos.x;
prev_tail_y = field->snake.tail->pos.y;
set_pos(8, ROW + 4);
printf("%d", (int)field->snake.length);
}