#ifndef _VRE_APP_Snake_ #define _VRE_APP_Snake_ #include "assert.h" #include "string.h" #include "stdlib.h" #include "math.h" /* 注意坐标系,本程序假定屏幕原点在左 */ /* 上交,横向是X轴正轴,纵向是Y轴正轴 */ /* 程序中所用数组,列对应X轴,行对应Y轴*/ /* 是否使用系统控制的蛇跟控制的蛇竞争 */ #define SYSTEM_SNAKE /* 蛇体最大长度 */ #define SNAKE_MAX_LEN 15 /* 蛇体最初长度 */ /* 不能大于SNAKE_MAX_LEN */ #define SNAKE_INI_LEN 3 /* 蛇运动区域的宽度 */ #define GAME_REGION_WIDTH 24 /* 蛇运动区域的高度 */ #define GAME_REGION_HEIGHT 30 /* 画图的像素 */ #define SNAKE_UNIT_WIDTH 10 #ifdef SYSTEM_SNAKE /* 系统蛇咬用户蛇的概率 */ #define SYSTEM_SNAKE_BITE_FACTOR 0.001 /* 系统蛇吃食物的概率 */ #define SYSTEM_SNAKE_EAT_FACTOR 0.6 /* 系统蛇碰到障碍物会变短*/ #define SYSTEM_SNAKE_BLOCK_LOSS 3 /* 最多能吃的解药数 */ #define USER_SNAKE_ANTIDOTE_COUNT 10 #endif /* 枚举运动方向 */ typedef enum DIRECTION { /* 向左 */ DIRECTION_LEFT = -1, /* 向右 */ DIRECTION_RIGHT = 1, /* 向上 */ DIRECTION_UP = -2, /* 向下 */ DIRECTION_DOWN = 2, }DIRECTION; /* 蛇运动区域小格的状态 */ typedef enum REGION_STATE { /* 畅通 */ EMPTY = 0, /* 阻碍 */ BLOCK = 1, /* 食物 */ FOOD = 2, /* 解药 */ ANTIDOTE }REGION_STATE; /* 蛇前进一步的结果 */ typedef enum SNAKE_MOVE_RESULT { /* 还活着 */ ALIVE = 0, /* 撞墙而死 */ COLLISION_WALL_TODEAD = 1, /* 撞障碍物而死 */ COLLISION_BLOCK_TODEAD = 2, /* 通关 */ SUCCESS = 3, /* 长个 */ GROW_UP = 4, #ifdef SYSTEM_SNAKE /* 失败,系统的蛇先完成 */ FAIL = 6, /* 用户蛇被系统蛇毒死 */ BETTEN_TO_DEAD = 9, /* 被系统蛇毒了 */ BETTEN = 10, #endif }SNAKE_MOVE_RESULT; /* 蛇体组成部分 */ typedef struct tagLocation { int x; int y; DIRECTION direction; }SNAKE_NODE, LOCATION; /* 蛇运动区域数组 */ typedef struct tagGameRegion { REGION_STATE region[GAME_REGION_HEIGHT][GAME_REGION_WIDTH]; int foodX; int foodY; }GAME_REGION; /* 定义一条蛇 */ typedef struct tagSnake { /* 蛇体当前长度 */ int snakeLen; /* 蛇尾的位置 */ int snakeTail; /* 记录蛇体各个节点 */ SNAKE_NODE snakeBody[SNAKE_MAX_LEN]; /* 移动方向 */ DIRECTION moveDirection; /* 还有多少颗就解药 */ int antidoteCount; }SNAKE; #ifdef SYSTEM_SNAKE extern SNAKE* pSystemSnake; SNAKE_MOVE_RESULT SystemSnakeMove(SNAKE *pSnake, GAME_REGION *pGameRegion); #endif void Initialize(SNAKE *pSnake, GAME_REGION *pGameRegion); SNAKE_MOVE_RESULT SnakeMove(SNAKE *pSnake, GAME_REGION *pGameRegion, DIRECTION moveDirection); double GetDistance(int x1, int y1, int x2, int y2); void Produce(SNAKE *pSnake, GAME_REGION *pGameRegion, REGION_STATE state); /* 初始化,蛇被初始化为长度为SNAKE_INI_LEN,区域中有一个食物,在屏幕中间 */ void Initialize(SNAKE *pSnake, GAME_REGION *pGameRegion) { int iLoop = -1; //struct time sysTime; assert(pSnake); assert(pGameRegion); /* 初始化随机种子 */ srand(0); /* 初始化用户控制的蛇 */ pSnake->snakeLen = SNAKE_INI_LEN; pSnake->snakeTail = 0; pSnake->moveDirection = DIRECTION_DOWN; pSnake->antidoteCount = 5; for (iLoop = 0; iLoop < SNAKE_INI_LEN; iLoop++) { pSnake->snakeBody[iLoop].x = 0; pSnake->snakeBody[iLoop].y = iLoop; pSnake->snakeBody[iLoop].direction = DIRECTION_DOWN; } memset(pGameRegion, (int)EMPTY, sizeof(*pGameRegion)); pGameRegion->region[GAME_REGION_HEIGHT/2][GAME_REGION_WIDTH/2] = FOOD; #ifdef SYSTEM_SNAKE /* 初始化系统控制的蛇 */ assert(pSystemSnake); pSystemSnake->snakeLen = 1; pSystemSnake->snakeTail = 0; pSystemSnake->moveDirection = DIRECTION_UP; pSystemSnake->snakeBody[0].x = GAME_REGION_WIDTH - 1; pSystemSnake->snakeBody[0].y = GAME_REGION_HEIGHT - 1; pSystemSnake->snakeBody[0].direction = DIRECTION_UP; pSystemSnake->antidoteCount = 0; #endif } /* 以一定的概率产生一个障碍物 */ void Produce(SNAKE *pSnake, GAME_REGION *pGameRegion, REGION_STATE state) { int x = -1; int y = -1; int iLoop = -1; int snakeTail = -1; #ifdef SYSTEM_SNAKE int systemSnakeTail = pSystemSnake->snakeTail; #endif assert(pGameRegion); assert(pSnake); snakeTail = pSnake->snakeTail; if (state == BLOCK) { /* 每次有1/2的几率产生一个障碍物 */ if (rand()%2 == 1) return; } else if (state == ANTIDOTE) { /* 每次有1/3的几率产生一个解药 */ if (rand()%3 != 1) return; } else if (state == EMPTY) { return; } /* 产生一个障碍 */ while (1) { x = rand() % GAME_REGION_WIDTH; y = rand() % GAME_REGION_HEIGHT; /* 如果该位置原来就有食物,就换一个位置 */ if (pGameRegion->region[y][x] == FOOD) { continue; } /* 如果该位置在蛇体内,换一个位置 */ for (iLoop = 0; iLoop < pSnake->snakeLen; iLoop++) { if (pSnake->snakeBody[(snakeTail+iLoop)%SNAKE_MAX_LEN].y == y && pSnake->snakeBody[(snakeTail+iLoop)%SNAKE_MAX_LEN].x == x) { continue; } } /* 如果该位置离用户的蛇头位置太近,也换一个位置 */ if (GetDistance(pSnake->snakeBody[snakeTail+pSnake->snakeLen-1].x, pSnake->snakeBody[snakeTail+pSnake->snakeLen-1].y, x, y) < 2.828) { continue; } #ifdef SYSTEM_SNAKE /* 如果该位置在系统控制的蛇的体内,换一个位置 */ for (iLoop = 0; iLoop < pSystemSnake->snakeLen; iLoop++) { if (pSystemSnake->snakeBody[(systemSnakeTail+iLoop)%SNAKE_MAX_LEN].y == y && pSystemSnake->snakeBody[(systemSnakeTail+iLoop)%SNAKE_MAX_LEN].x == x) { continue; } } #endif pGameRegion->region[y][x] = state; if (state == FOOD) { pGameRegion->foodY = y; pGameRegion->foodX = x; } break; } } /* 蛇前进一步,并得到前今后的结果,或者安然无恙、或者长个、或者撞到墙、或者咬到自己 */ SNAKE_MOVE_RESULT SnakeMove(SNAKE *pSnake, GAME_REGION *pGameRegion, DIRECTION moveDirection) { int iLoop = 0; int snakeHead = -1; int snakeTail = -1; #ifdef SYSTEM_SNAKE int systemSnakeTail = -1; SNAKE_MOVE_RESULT systemSnakeMoveResult; #endif #ifdef SYSTEM_SNAKE systemSnakeMoveResult = SystemSnakeMove(pSnake, pGameRegion); if (BETTEN == systemSnakeMoveResult) { /* 用户蛇是被咬死的 */ if (pSnake->antidoteCount == 0) return BETTEN_TO_DEAD; Produce(pSnake, pGameRegion, ANTIDOTE); } /* 系统的蛇成功了 */ if (SUCCESS == systemSnakeMoveResult) return FAIL; #endif assert(pSnake); assert(pGameRegion); /* 在前进一步之前蛇肯定处于正常状态,不然上一步就该结束了。 */ if ((int)moveDirection == -(int)pSnake->moveDirection) { return ALIVE; } /* 根据蛇的移动方向前进一步 */ snakeHead = (pSnake->snakeTail + pSnake->snakeLen - 1) % SNAKE_MAX_LEN; switch (moveDirection) { case DIRECTION_DOWN: { pSnake->snakeBody[(snakeHead+1)%SNAKE_MAX_LEN].x = pSnake->snakeBody[snakeHead].x; pSnake->snakeBody[(snakeHead+1)%SNAKE_MAX_LEN].y = pSnake->snakeBody[snakeHead].y + 1; break; } case DIRECTION_UP: { pSnake->snakeBody[(snakeHead+1)%SNAKE_MAX_LEN].x = pSnake->snakeBody[snakeHead].x; pSnake->snakeBody[(snakeHead+1)%SNAKE_MAX_LEN].y = pSnake->snakeBody[snakeHead].y - 1; break; } case DIRECTION_RIGHT: { pSnake->snakeBody[(snakeHead+1)%SNAKE_MAX_LEN].x = pSnake->snakeBody[snakeHead].x + 1; pSnake->snakeBody[(snakeHead+1)%SNAKE_MAX_LEN].y = pSnake->snakeBody[snakeHead].y; break; } case DIRECTION_LEFT: { pSnake->snakeBody[(snakeHead+1)%SNAKE_MAX_LEN].x = pSnake->snakeBody[snakeHead].x - 1; pSnake->snakeBody[(snakeHead+1)%SNAKE_MAX_LEN].y = pSnake->snakeBody[snakeHead].y; break; } default:; } /* 设置蛇的运动方向 */ pSnake->moveDirection = moveDirection; /* 重新设置蛇的尾巴 */ snakeTail = pSnake->snakeTail = (pSnake->snakeTail + 1) % SNAKE_MAX_LEN; /* 重新设置蛇的头 */ snakeHead = (pSnake->snakeTail + pSnake->snakeLen - 1) % SNAKE_MAX_LEN; pSnake->snakeBody[snakeHead].direction = moveDirection; /* 蛇撞墙了,用蛇头判断就行了 */ if (pSnake->snakeBody[snakeHead].x == -1 || pSnake->snakeBody[snakeHead].x == GAME_REGION_WIDTH || pSnake->snakeBody[snakeHead].y == -1 || pSnake->snakeBody[snakeHead].y == GAME_REGION_HEIGHT) { /* 撞墙死的 */ if (pSnake->snakeLen == 1) { return COLLISION_WALL_TODEAD; } /* 撞墙之后,元气大伤,蛇头撞坏了*/ else { pSnake->snakeLen--; pSnake->moveDirection = pSnake->snakeBody[(pSnake->snakeTail + pSnake->snakeLen - 1) % SNAKE_MAX_LEN].direction; return ALIVE; } } /* 蛇撞到障碍物了 */ if (pGameRegion->region[pSnake->snakeBody[snakeHead].y][pSnake->snakeBody[snakeHead].x] == BLOCK) { /* 把障碍物吃了,元气大伤*/ pGameRegion->region[pSnake->snakeBody[snakeHead].y][pSnake->snakeBody[snakeHead].x] = EMPTY; if (pSnake->snakeLen > 1) { pSnake->snakeTail = (pSnake->snakeTail + 1) % SNAKE_MAX_LEN; pSnake->snakeLen -= 1; Produce(pSnake, pGameRegion, BLOCK); return ALIVE; } /* 撞障碍物死的 */ else { return COLLISION_BLOCK_TODEAD; } } /* 蛇遇到食物*/ if (pGameRegion->region[pSnake->snakeBody[snakeHead].y][pSnake->snakeBody[snakeHead].x] == FOOD) { pSnake->snakeLen++; /* 到了最大长度,成功通关了 */ if (pSnake->snakeLen == SNAKE_MAX_LEN) { return SUCCESS; } else { /* 将缩短的尾巴补回 */ pSnake->snakeTail--; if (pSnake->snakeTail == -1) pSnake->snakeTail = SNAKE_MAX_LEN - 1; pGameRegion->region[pSnake->snakeBody[snakeHead].y][pSnake->snakeBody[snakeHead].x] = EMPTY; return GROW_UP; } } #ifdef SYSTEM_SNAKE /* 蛇遇到解药了 */ if (pGameRegion->region[pSnake->snakeBody[snakeHead].y][pSnake->snakeBody[snakeHead].x] == ANTIDOTE) { if (pSnake->antidoteCount < USER_SNAKE_ANTIDOTE_COUNT) pSnake->antidoteCount++; pGameRegion->region[pSnake->snakeBody[snakeHead].y][pSnake->snakeBody[snakeHead].x] = EMPTY; Produce(pSnake, pGameRegion, ANTIDOTE); } #endif return ALIVE; } double GetDistance(int x1, int y1, int x2, int y2) { return sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); } #ifdef SYSTEM_SNAKE /* 一条法力高强的蛇,随机前进,不会咬到自己,遇到墙会从墙的另一段穿出,能吃食物 */ SNAKE_MOVE_RESULT SystemSnakeMove(SNAKE* pSnake, GAME_REGION *pGameRegion) { int iLoop = 0; int systemSnakeHead = -1; int systemSnakeTail = -1; int snakeHead = -1; int snakeTail = -1; DIRECTION moveDirection = DIRECTION_DOWN; double minFactor = 99999999999; double tryFoodDistance = -1; double trySnakeHeadDistance = -1; double tryFactor = -1; LOCATION tryLocation; LOCATION minLocation; assert(pSnake); assert(pSystemSnake); assert(pGameRegion); systemSnakeHead = (pSystemSnake->snakeTail + pSystemSnake->snakeLen -1) % SNAKE_MAX_LEN; snakeHead = (pSnake->snakeTail + pSnake->snakeLen - 1) % SNAKE_MAX_LEN; /* 尝试下方向 */ tryLocation.x = pSystemSnake->snakeBody[systemSnakeHead].x; tryLocation.y = (pSystemSnake->snakeBody[systemSnakeHead].y + 1) % GAME_REGION_HEIGHT; minLocation.x = tryLocation.x; minLocation.y = tryLocation.y; tryFoodDistance = GetDistance(tryLocation.x, tryLocation.y, pGameRegion->foodX, pGameRegion->foodY); trySnakeHeadDistance = GetDistance(tryLocation.x, tryLocation.y, pSnake->snakeBody[snakeHead].x, pSnake->snakeBody[snakeHead].y); tryFactor = SYSTEM_SNAKE_BITE_FACTOR * trySnakeHeadDistance + SYSTEM_SNAKE_EAT_FACTOR * tryFoodDistance; if (tryFactor < minFactor) { if (DIRECTION_UP != pSystemSnake->moveDirection) { moveDirection = DIRECTION_DOWN; minFactor = tryFactor; minLocation.x = tryLocation.x; minLocation.y = tryLocation.y; } } /* 尝试上方向 */ tryLocation.x = pSystemSnake->snakeBody[systemSnakeHead].x; tryLocation.y = pSystemSnake->snakeBody[systemSnakeHead].y - 1; if (tryLocation.y == -1) tryLocation.y = GAME_REGION_HEIGHT - 1; tryFoodDistance = GetDistance(tryLocation.x, tryLocation.y, pGameRegion->foodX, pGameRegion->foodY); trySnakeHeadDistance = GetDistance(tryLocation.x, tryLocation.y, pSnake->snakeBody[snakeHead].x, pSnake->snakeBody[snakeHead].y); tryFactor = SYSTEM_SNAKE_BITE_FACTOR * trySnakeHeadDistance + SYSTEM_SNAKE_EAT_FACTOR * tryFoodDistance; if (tryFactor < minFactor) { if (DIRECTION_DOWN != pSystemSnake->moveDirection) { moveDirection = DIRECTION_UP; minFactor = tryFactor; minLocation.x = tryLocation.x; minLocation.y = tryLocation.y; } } /* 尝试右方向 */ tryLocation.x = (pSystemSnake->snakeBody[systemSnakeHead].x + 1) % GAME_REGION_WIDTH; tryLocation.y = pSystemSnake->snakeBody[systemSnakeHead].y; tryFoodDistance = GetDistance(tryLocation.x, tryLocation.y, pGameRegion->foodX, pGameRegion->foodY); trySnakeHeadDistance = GetDistance(tryLocation.x, tryLocation.y, pSnake->snakeBody[snakeHead].x, pSnake->snakeBody[snakeHead].y); tryFactor = SYSTEM_SNAKE_BITE_FACTOR * trySnakeHeadDistance + SYSTEM_SNAKE_EAT_FACTOR * tryFoodDistance; if (tryFactor < minFactor) { if (DIRECTION_LEFT != pSystemSnake->moveDirection) { moveDirection = DIRECTION_RIGHT; minFactor = tryFactor; minLocation.x = tryLocation.x; minLocation.y = tryLocation.y; } } /* 尝试左方向 */ tryLocation.x = pSystemSnake->snakeBody[systemSnakeHead].x - 1; if (tryLocation.x == -1) tryLocation.x = GAME_REGION_WIDTH - 1; tryFoodDistance = GetDistance(tryLocation.x, tryLocation.y, pGameRegion->foodX, pGameRegion->foodY); trySnakeHeadDistance = GetDistance(tryLocation.x, tryLocation.y, pSnake->snakeBody[snakeHead].x, pSnake->snakeBody[snakeHead].y); tryFactor = SYSTEM_SNAKE_BITE_FACTOR * trySnakeHeadDistance + SYSTEM_SNAKE_EAT_FACTOR * tryFoodDistance; if (tryFactor < minFactor) { if (DIRECTION_RIGHT != pSystemSnake->moveDirection) { moveDirection = DIRECTION_LEFT; minFactor = tryFactor; minLocation.x = tryLocation.x; minLocation.y = tryLocation.y; } } /* 设置新的蛇头 */ pSystemSnake->moveDirection = moveDirection; pSystemSnake->snakeBody[(systemSnakeHead+1)%SNAKE_MAX_LEN].x = minLocation.x; pSystemSnake->snakeBody[(systemSnakeHead+1)%SNAKE_MAX_LEN].y = minLocation.y; pSystemSnake->snakeBody[(systemSnakeHead+1)%SNAKE_MAX_LEN].direction = moveDirection; snakeTail = pSnake->snakeTail; systemSnakeTail = pSystemSnake->snakeTail = (pSystemSnake->snakeTail + 1) % SNAKE_MAX_LEN; systemSnakeHead = (pSystemSnake->snakeTail + pSystemSnake->snakeLen - 1) % SNAKE_MAX_LEN; pSystemSnake->snakeBody[systemSnakeHead].direction = moveDirection; /* 判断系统控制的蛇有没有咬到用户控制的蛇 */ for (iLoop = 0; iLoop < pSnake->snakeLen; iLoop++) { if (pSnake->snakeBody[(snakeTail+iLoop)%SNAKE_MAX_LEN].y == pSystemSnake->snakeBody[systemSnakeHead].y && pSnake->snakeBody[(snakeTail+iLoop)%SNAKE_MAX_LEN].x == pSystemSnake->snakeBody[systemSnakeHead].x) { pSnake->antidoteCount--; return BETTEN; } } /* 判断系统控制的蛇有没有吃到障碍物,吃到的话,蛇的长度减4 */ if (pGameRegion->region[pSystemSnake->snakeBody[systemSnakeHead].y][pSystemSnake->snakeBody[systemSnakeHead].x] == BLOCK) { pGameRegion->region[pSystemSnake->snakeBody[systemSnakeHead].y][pSystemSnake->snakeBody[systemSnakeHead].x] = EMPTY; if (pSystemSnake->snakeLen > SYSTEM_SNAKE_BLOCK_LOSS) { pSystemSnake->snakeTail = (pSystemSnake->snakeTail + SYSTEM_SNAKE_BLOCK_LOSS) % SNAKE_MAX_LEN; pSystemSnake->snakeLen -= SYSTEM_SNAKE_BLOCK_LOSS; } Produce(pSnake, pGameRegion, BLOCK); return ALIVE; } /* 判断系统控制的蛇有没有吃到食物 */ if (pGameRegion->region[pSystemSnake->snakeBody[systemSnakeHead].y][pSystemSnake->snakeBody[systemSnakeHead].x] == FOOD) { /* 系统控制的蛇先长到最大长度 */ pSystemSnake->snakeLen++; if (pSystemSnake->snakeLen == SNAKE_MAX_LEN) return SUCCESS; else { /* 将缩短的尾巴补回 */ pSystemSnake->snakeTail--; if (pSystemSnake->snakeTail == -1) pSystemSnake->snakeTail = SNAKE_MAX_LEN - 1; pGameRegion->region[pSystemSnake->snakeBody[systemSnakeHead].y][pSystemSnake->snakeBody[systemSnakeHead].x] = EMPTY; Produce(pSnake, pGameRegion, FOOD); Produce(pSnake, pGameRegion, BLOCK); return ALIVE; } } /* 判断系统控制的蛇有没有吃到解药 */ if (pGameRegion->region[pSystemSnake->snakeBody[systemSnakeHead].y][pSystemSnake->snakeBody[systemSnakeHead].x] == ANTIDOTE) { pGameRegion->region[pSystemSnake->snakeBody[systemSnakeHead].y][pSystemSnake->snakeBody[systemSnakeHead].x] = EMPTY; Produce(pSnake, pGameRegion, ANTIDOTE); } return ALIVE; } #endif #endif