/**********************************************************
*snake.c
*a snake game
*
*Created by penhison
*Copyright 2017 penhison@gmail.com
*All right reserved
*
**********************************************************/
/**********************************************************
*changelog:
*
*2017/12/19 initial release
*2017/12/20 make map partial refresh
*2017/12/21 add player rank
*2017/12/23 add cheat mode
*
**********************************************************/
#include
#include
#include
#include
#include
#define MAP_LENGTH 40
#define MAP_HIGHT 30
#define BLANK_CELL ' '
#define WALL_CELL '*'
#define INIT_SNAKE_LENGTH 3
#define SNAKE_HEAD 'H'
#define SNAKE_BODY 'X'
#define SNAKE_FOOD '$'
#define UP (input == 'W' || input == 'w' || input == 72)
#define DOWN (input == 'S' || input == 's' || input == 80)
#define LEFT (input == 'A' || input == 'a' || input == 75)
#define RIGHT (input == 'D' || input == 'd' || input == 77)
#define PAUSE (input == ' ')
#define CHEAT (input == 'C' || input == 'c')
void initSnake();
void initFood();
void printMap();
void printMode();
void initGame();
void printSnake();
void printFood();
void printLength();
void productFood();
void eatFood();
int inputValid(char input);
void snakeControlMove(char input);
void snakeAutoMove();
void snakeMove();
int snakeDie();
void startGame();
void selectMode();
void playGame();
void gamePause();
void gameOver();
void restart();
void locateCursor(int x, int y);
void switchCursorVisible();
void inputName();
void getTime();
void initRank();
void ranking(FILE * fp, struct Player player[]);
void printRank(FILE * fp, struct Player player[]);
void rank();
void switchCheatMode();
void snakeSmartMove();
struct Snake {
int x[MAP_LENGTH * MAP_HIGHT + 1];
int y[MAP_LENGTH * MAP_HIGHT + 1];
int length;
int speed;
};
struct Food {
int x;
int y;
int exist;
};
struct Player {
char name[20];
char date[20];
int score;
};
struct Snake snake;
struct Food food;
struct Player currentPlayer;
char mode[20];
int cheat;
int main() {
startGame();
while(1) {
selectMode();
playGame();
gameOver();
restart();
}
return 0;
}
void startGame() {
printf("贪吃蛇游戏\n\n");
printf("方向键或WASD控制\n");
printf("长按加速\n");
printf("按空格键暂停\n\n");
printf("按任意键开始游戏\n");
getch();
inputName();
}
void selectMode() {
system("cls");
printf("请选择游戏模式:\n");
printf("1. easy\n");
printf("2. normal\n");
printf("3. hard\n");
printf("4. very hard\n");
printf("5. crazy\n");
printf("6. go die\n");
char input = getch();
switch (input) {
case '1':
snake.speed = 600;
strcpy(mode, "easy");
break;
case '2':
snake.speed = 300;
strcpy(mode, "normal");
break;
case '3':
snake.speed = 100;
strcpy(mode, "hard");
break;
case '4':
snake.speed = 50;
strcpy(mode, "very hard");
break;
case '5':
snake.speed = 20;
strcpy(mode, "crazy");
break;
case '6':
snake.speed = 10;
strcpy(mode, "go die");
break;
default:
snake.speed = 300;
strcpy(mode, "normal");
break;
}
return;
}
void playGame() {
initGame();
while (!snakeDie()) {
printSnake();
printFood();
printLength();
locateCursor(0, 1);
putchar(WALL_CELL);
locateCursor(0, MAP_HIGHT + 3);
snakeMove();
eatFood();
productFood();
}
}
void gamePause() {
system("pause");
locateCursor(0, MAP_HIGHT + 3);
printf(" ");
}
void gameOver() {
system("cls");
printf("\n\n\n\n Game over\n\n\n\n");
printf("你的蛇长:%d\n", snake.length);
printf("按空格键重玩\n\n");
rank();
}
void restart() {
char ch = 0;
while(ch != ' ') {
ch = getch();
}
}
void initGame() {
cheat = 0;
system("cls");
initSnake();
initFood();
printMap();
}
void initSnake() {
snake.length = INIT_SNAKE_LENGTH;
for (int i = 0; i <= snake.length; ++ i) {
snake.x[i] = snake.length + 1 - i;
snake.y[i] = 5;
}
}
void initFood() {
food.exist = 0;
productFood();
}
void printMap() {
printf("length:%d", snake.length);
printMode();
char map[MAP_HIGHT + 2][MAP_LENGTH + 2];
for (int i = 0; i < MAP_HIGHT + 2; ++ i) {
for (int j = 0; j < MAP_LENGTH + 2; ++ j) {
if (i == 0 || i == MAP_HIGHT + 1 || j == 0 || j == MAP_LENGTH + 1)
map[i][j] = WALL_CELL;
else map[i][j] = BLANK_CELL;
putchar(map[i][j]);
}
putchar('\n');
}
}
void printMode() {
int modeLength;
modeLength = strlen(mode);
for (int i = 0; i < 34 - modeLength; ++ i) {
putchar(' ');
}
printf("%s\n", mode);
}
void printSnake() {
locateCursor(snake.x[snake.length], snake.y[snake.length] + 1);
putchar(BLANK_CELL);
locateCursor(snake.x[0], snake.y[0] + 1);
putchar(SNAKE_HEAD);
locateCursor(snake.x[1], snake.y[1] + 1);
putchar(SNAKE_BODY);
}
void printFood() {
locateCursor(food.x, food.y + 1);
putchar(SNAKE_FOOD);
}
void printLength() {
locateCursor(7, 0);
printf("%d", snake.length);
}
void productFood() {
srand( (unsigned)time(NULL) );
while (!food.exist) {
food.x = rand() % MAP_LENGTH + 1;
food.y = rand() % MAP_HIGHT + 1;
food.exist = 1;
for (int i = 0; i < snake.length; ++ i) {
if (snake.x[i] == food.x && snake.y[i] == food.y) {
food.exist = 0;
break;
}
}
}
}
void eatFood() {
if (snake.x[0] == food.x && snake.y[0] == food.y) {
food.exist = 0;
snake.length += 1;
}
}
int inputValid(char input) {
if UP
if (snake.y[0] > snake.y[2]) return 0;
if DOWN
if (snake.y[0] < snake.y[2]) return 0;
if LEFT
if (snake.x[0] > snake.x[2]) return 0;
if RIGHT
if (snake.x[0] < snake.x[2]) return 0;
return 1;
}
void snakeControlMove(char input) {
if UP snake.y[0] -= 1;
if DOWN snake.y[0] += 1;
if LEFT snake.x[0] -= 1;
if RIGHT snake.x[0] += 1;
return;
}
void snakeAutoMove() {
if (cheat == 1) {
snakeSmartMove();
return;
}
if (snake.x[0] > snake.x[2]) snake.x[0] += 1;
if (snake.x[0] < snake.x[2]) snake.x[0] -= 1;
if (snake.y[0] > snake.y[2]) snake.y[0] += 1;
if (snake.y[0] < snake.y[2]) snake.y[0] -= 1;
}
void snakeMove() {
for (int i = snake.length; i > 0; -- i) {
snake.x[i] = snake.x[i - 1];
snake.y[i] = snake.y[i - 1];
}
char input = 0;
int start = clock();
while (clock() - start < snake.speed) {
if (!kbhit()) continue;
input = getch();
if (!UP && !DOWN && !LEFT && !RIGHT && !PAUSE && !CHEAT)
continue;
if PAUSE {
gamePause();
continue;
}
if CHEAT {
switchCheatMode();
continue;
}
if (!inputValid(input)) {
continue;
}
snakeControlMove(input);
return;
}
snakeAutoMove();
}
int snakeDie() {
if (snake.x[0] == 0 || snake.x[0] == MAP_LENGTH + 1
|| snake.y[0] == 0 || snake.y[0] == MAP_HIGHT + 1)
return 1;
for(int i = 1; i < snake.length; ++ i) {
if( (snake.x[0] == snake.x[i]) && (snake.y[0] == snake.y[i]) )
return 1;
}
return 0;
}
void locateCursor(int x, int y) {
COORD cor;
cor.X = x;
cor.Y = y;
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hout, cor);
}
void switchCursorVisible() {
HANDLE hout;
hout = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cci;
GetConsoleCursorInfo(hout, &cci);
cci.bVisible = 0;
SetConsoleCursorInfo(hout, &cci);
}
void rank() {
currentPlayer.score = snake.length;
getTime();
struct Player player[5];
FILE * fp;
fp = fopen(mode, "r");
if (fp == NULL) {
fclose(fp);
initRank();
fp = fopen(mode, "r");
}
ranking(fp, player);
printRank(fp, player);
fclose(fp);
}
void inputName() {
system("cls");
printf("Please input you name\n");
scanf("%s", currentPlayer.name);
switchCursorVisible();
}
void getTime() {
struct tm * date;
time_t currentTime;
time(¤tTime);
date = localtime(¤tTime);
strftime(currentPlayer.date, 20, "%Y/%m/%d %H:%M:%S", date);
}
void initRank() {
FILE * fp;
fp = fopen(mode, "w");
for (int i = 0; i < 5; ++ i) {
fprintf(fp, "%s\t%d\t", "anonymous", 0);
fprintf(fp, "%s %s\n", "2017/12/22", "00:00:00");
}
fclose(fp);
}
void ranking(FILE * fp, struct Player player[]) {
for (int i = 0; i < 5; ++ i) {
fscanf(fp, "%s\t%d\t", player[i].name, &player[i].score);
fgets(player[i].date, 20, fp);
}
for (int i = 0; i < 5; ++ i) {
if (currentPlayer.score >= player[i].score) {
for (int j = 4; j > i; -- j) {
strcpy(player[j].name, player[j - 1].name);
strcpy(player[j].date, player[j - 1].date);
player[j].score = player[j - 1].score;
}
strcpy(player[i].name, currentPlayer.name);
strcpy(player[i].date, currentPlayer.date);
player[i].score = currentPlayer.score;
break;
}
}
}
void printRank(FILE * fp, struct Player player[]) {
printf("\nrank:\n\n");
printf("%-10s %s\n\n", "player", "score");
for (int i = 0; i < 5; ++ i) {
printf("%-10s %5d %s\n", player[i].name, player[i].score,
player[i].date);
}
fclose(fp);
fp = fopen(mode, "w");
for (int i = 0; i < 5; ++ i) {
fprintf(fp, "%s\t%d\t%s\n", player[i].name, player[i].score,
player[i].date);
}
}
void switchCheatMode() {
cheat = !cheat;
if (cheat) {
locateCursor(16, 0);
printf("cheat mode");
}
else {
locateCursor(16, 0);
printf(" ");
}
return;
}
const int direction[4][2] = {{1, 0}, {0, 1}, {0, -1}, {-1, 0}};
#define f(x) (x->g + x->h)
#define abs(number) number > 0 ? number : -number
struct node {
char x;
char y;
char g;
char h;
char isBlock;
char inOpenTable;
char inCloseTable;
struct node * parent;
};
struct Result {
int x;
int y;
int found;
};
struct node mapNode[MAP_LENGTH + 2][MAP_HIGHT + 2];
struct node * openTable[1200];
struct node * closeTable[1200];
int openTableCount;
int closeTableCount;
void initMapNode() {
for (int i = 0; i < MAP_LENGTH + 2; ++ i) {
for (int j = 0; j < MAP_HIGHT + 2; ++ j) {
mapNode[i][j].x = i;
mapNode[i][j].y = j;
mapNode[i][j].g = 0;
mapNode[i][j].h = 0;
mapNode[i][j].inOpenTable = 0;
mapNode[i][j].inCloseTable = 0;
mapNode[i][j].parent = NULL;
if (i == 0 || i == MAP_LENGTH + 1 || j == 0 || j == MAP_HIGHT + 1)
mapNode[i][j].isBlock = 1;
else mapNode[i][j].isBlock = 0;
}
}
for (int i = 1; i < snake.length; ++ i) {
mapNode[snake.x[i]][snake.y[i]].isBlock = 1;
}
}
void swap(int x1, int x2) {
struct node * temp = openTable[x1];
openTable[x1] = openTable[x2];
openTable[x2] = temp;
}
void heapSort(int nodeIndex) {
int current = nodeIndex;
int parent = (current - 1) / 2;
int child = current * 2 + 1;
while (child < openTableCount) {
if ( (child + 1 < openTableCount) && (f(openTable[child + 1]) < f(openTable[child])) )
child++;
if (f(openTable[current]) <= f(openTable[child])) break;
else {
swap(current, child);
current = child;
child = current * 2 + 1;
}
}
if (current != nodeIndex) return;
while (current != 0) {
if (f(openTable[current]) >= f(openTable[parent])) break;
else {
swap(current, parent);
current = parent;
parent = (current - 1) / 2;
}
}
}
void insertToOpenTable(int x, int y, struct node * currentNode, struct node * endNode) {
if (!mapNode[x][y].isBlock && !mapNode[x][y].inCloseTable) {
if (mapNode[x][y].inOpenTable) {
if (mapNode[x][y].g > currentNode->g + 1) {
mapNode[x][y].g = currentNode->g + 1;
mapNode[x][y].parent = currentNode;
int i;
for (i = 0; i < openTableCount; ++ i) {
if (openTable[i]->x == x && openTable[i]->y == y) break;
}
heapSort(i);
}
}
else {
mapNode[x][y].g = currentNode->g + 1;
mapNode[x][y].h = abs(endNode->x - x) + abs(endNode->y - y);
mapNode[x][y].parent = currentNode;
mapNode[x][y].inOpenTable = 1;
openTable[openTableCount++] = &(mapNode[x][y]);
heapSort(openTableCount - 1);
}
}
}
void getNeighbors(struct node * currentNode, struct node * endNode) {
for (int i = 0; i < 4; ++ i) {
int tempx = currentNode->x + direction[i][0];
int tempy = currentNode->y + direction[i][1];
insertToOpenTable(tempx, tempy, currentNode, endNode);
}
}
struct Result search(int startX, int startY, int goalX, int goalY) {
struct Result result;
initMapNode();
struct node * startNode;
struct node * endNode;
struct node * currentNode;
openTableCount = 0;
closeTableCount = 0;
startNode = &(mapNode[startX][startY]);
endNode = &(mapNode[goalX][goalY]);
endNode->isBlock = 0;
openTable[openTableCount++] = startNode;
startNode->h = abs(startNode->x - endNode->x) + abs(startNode->y - endNode->y);
startNode->inOpenTable = 1;
while(1) {
currentNode = openTable[0];
openTable[0] = openTable[--openTableCount];
heapSort(0);
closeTable[closeTableCount++] = currentNode;
currentNode->inCloseTable = 1;
if (currentNode->x == endNode->x && currentNode->y == endNode->y) {
result.x = currentNode->parent->x;
result.y = currentNode->parent->y;
result.found = 1;
break;
}
getNeighbors(currentNode, endNode);
if (openTableCount == 0) {
result.found = 0;
break;
}
}
return result;
}
void wander() {
int nextX;
int nextY;
for (int i= 0; i < 4; ++ i) {
nextX = snake.x[0] + direction[i][0];
nextY = snake.y[0] + direction[i][1];
initMapNode();
if (mapNode[nextX][nextY].isBlock) continue;
if (search(snake.x[snake.length - 1], snake.y[snake.length - 1], nextX, nextY).found) {
snake.x[0] = nextX;
snake.y[0] = nextY;
return;
}
}
}
void snakeSmartMove() {
struct Result findSnakeHead;
struct Result findSnakeTail;
findSnakeHead = search(food.x, food.y, snake.x[0], snake.y[0]);
if (findSnakeHead.found == 1) {
findSnakeTail = search(snake.x[snake.length - 1], snake.y[snake.length - 1], findSnakeHead.x, findSnakeHead.y);
if (findSnakeTail.found == 1) {
snake.x[0] = findSnakeHead.x;
snake.y[0] = findSnakeHead.y;
return;
}
}
wander();
return;
}