c/c++ 入门之控制台上实现贪吃蛇

分析贪吃蛇游戏,蛇的身体在吃到食物之后增长,因此可以用链表来储存蛇身体的节点。

// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include
#include
#include
#include
#include
#define GAME_WIN_WIDTH 15
#define GAME_WIN_HEIGHT 15
#define DIRECTION_UP 'w'
#define DIRECTION_DOWN 's'
#define DIRECTION_LEFT 'a'
#define DIRECTION_RIGHT 'd'

//定义二维向量
struct vector2
{
 int x;
 int y;
};

//定义贪吃蛇身体节点
struct node {
 node *next;
 vector2 pos;//节点位置
};
enum MapFlag
{
 NONE = 0,//空
 BODY, //蛇身体
 FOOD, //食物
 WALL //墙壁
};
enum GameState
{
 showStartBG = 1,
 start,
 playing,
 over,
 showOverBG
};

//定义贪吃蛇结构
struct Snake {
 node * head;   //贪吃蛇头部节点
 int currentDirection; //当前移动方向
 vector2 food;
 vector2 sceneSize; //背景地图总大小
 vector2 offset;  //蛇可活动范围相对地图偏移值
 int mapBackground[GAME_WIN_WIDTH + 2][GAME_WIN_HEIGHT + 2]; //背景地图
};

//为蛇添加头部节点
node* createHeader(int x, int y, node * body) {
 node * head = new node;
 head->next = body;
 head->pos.x = x;
 head->pos.y = y;
 return head;
}

//为贪吃蛇进行数据初始化
void initSnake(Snake& s) {
 s.currentDirection = DIRECTION_DOWN;
 s.food = { 3,4 };
 s.head = createHeader(5, 5, NULL);
 s.offset = { 1,1 };
 s.sceneSize = { GAME_WIN_WIDTH + 2 ,GAME_WIN_HEIGHT + 2 };
 memset(s.mapBackground, 0, s.sceneSize.x * s.sceneSize.y * sizeof(int));
 for (int i = 0; i < s.sceneSize.x; i++) {
  s.mapBackground[i][0] = MapFlag::WALL;
  s.mapBackground[i][s.sceneSize.y - 1] = MapFlag::WALL;
 }
 for (int i = 0; i < s.sceneSize.y; i++) {
  s.mapBackground[0][i] = MapFlag::WALL;
  s.mapBackground[s.sceneSize.x - 1][i] = MapFlag::WALL;
 }
}


//删除蛇尾
void deleteTail(node * head) {
 do
 {
  if (!head->next) {
   return;
  }
  if (!head->next->next) {
   delete head->next;
   head->next = NULL;
  }
 } while (head = head -> next);
}
void deleteSnake(node * head) {
 node* p = NULL;
 while (head) {
  p = head;
  head = head->next;
  delete p;
 }
}


node * onMove(int direction, node* head) {
 node *p = nullptr;
 switch (direction)
 {
 case DIRECTION_DOWN:
  p = createHeader(head->pos.x, head->pos.y + 1, head);
  break;
 case DIRECTION_UP:
  p = createHeader(head->pos.x, head->pos.y - 1, head);
  break;
 case DIRECTION_LEFT:
  p = createHeader(head->pos.x - 1, head->pos.y, head);
  break;
 case DIRECTION_RIGHT:
  p = createHeader(head->pos.x + 1, head->pos.y , head);
  break;
 default:
  break;
 }
 if (p) {
  deleteTail(p);
 }
 return p;
}

vector2 getNextPos(Snake s) {
 node *head = s.head;
 vector2 nextPos;
 switch (s.currentDirection)
 {
 case DIRECTION_DOWN:
  nextPos = { head->pos.x, head->pos.y + 1 };
  break;
 case DIRECTION_UP:
  nextPos = { head->pos.x, head->pos.y - 1 };
  break;
 case DIRECTION_LEFT:
  nextPos = { head->pos.x - 1, head->pos.y };
  break;
 case DIRECTION_RIGHT:
  nextPos = { head->pos.x + 1, head->pos.y };
  break;
 default:
  break;
 }
 return nextPos;
}
node* onEatFood(Snake &s) {
 vector2 pos = getNextPos(s);
 node * head = s.head;
 //---------随机重新生成食物---------
 int randomseed = 0;
 int adder = 0;
 int x, y;
 randomseed = LOWORD(time(0));
 adder = randomseed << 15;
 x = randomseed = ((randomseed) * 17 + adder) % GAME_WIN_WIDTH;
 y = (randomseed * 17 + adder) % GAME_WIN_HEIGHT;
 s.food.x = x;
 s.food.y = y;
 //---------------------------------
 return createHeader(pos.x, pos.y, head);
}
bool checkDir(int currentDir,int newDir) {
 switch (newDir)
 {
 case DIRECTION_DOWN:
  return currentDir != DIRECTION_UP;
  break;
 case DIRECTION_UP:
  return currentDir != DIRECTION_DOWN;
  break;
 case DIRECTION_LEFT:
  return currentDir != DIRECTION_RIGHT;
  break;
 case DIRECTION_RIGHT:
  return currentDir != DIRECTION_LEFT;
  break;
 default:
  break;
 }
 return false;
}
bool isDie(Snake s) {
 vector2 newHeadPos = getNextPos(s);
 node* head = s.head;
 if (newHeadPos.x < 0 || newHeadPos.y < 0) {
  return true;
 }
 if (s.mapBackground[newHeadPos.x + s.offset.x][newHeadPos.y + s.offset.y] == MapFlag::WALL) {
  return true;
 }
 do {
  if (head->pos.x == newHeadPos.x && head->pos.y == newHeadPos.y) {
   return true;
  }
 } while (head = head->next);
 return false;
}

bool canEat(Snake s) {
 vector2 dest = getNextPos(s);
 return dest.x == s.food.x && dest.y == s.food.y;
}


void printfStartGame() {
 printf("-------------------------------------------------\n");
 printf("-------------------------------------------------\n");
 printf("-----                                       -----\n");
 printf("-----       press any key to start          -----\n");
 printf("-----                                       -----\n");
 printf("-------------------------------------------------\n");
 printf("-------------------------------------------------\n");
}
void printfGameOver() {
 printf("-------------------------------------------------\n");
 printf("-------------------------------------------------\n");
 printf("-----                                       -----\n");
 printf("-----             game over!!!              -----\n");
 printf("-----                                       -----\n");
 printf("-------------------------------------------------\n");
 printf("-------------------------------------------------\n");
}

void printGameScene(int *gameMap,Snake s , vector2 sceneSize,vector2 offset) {
 int tempMap[GAME_WIN_WIDTH + 2][GAME_WIN_HEIGHT + 2];
 memcpy(tempMap, gameMap, sceneSize.x * sceneSize.y * sizeof(int));

 node * head = s.head;
 do {
  tempMap[head->pos.x + offset.x][head->pos.y + offset.y] = MapFlag::BODY;
 } while (head = head->next);
 tempMap[s.food.x +offset.x][s.food.y + offset.y] = MapFlag::FOOD;
 
 for (int h = 0; h < sceneSize.y; h++) {
  for (int w = 0; w < sceneSize.x; w++) {
   switch (tempMap[w][h])
   {
   case MapFlag::NONE:
    printf(" ");
    break;
   case MapFlag::BODY:
    printf("*");
    break;
   case MapFlag::FOOD:
    printf("@");
    break;
   case MapFlag::WALL:
    printf("#");
    break;
   default:
    break;
   }
  }
  printf("\n");
 }

}


void renderScene(GameState st, Snake s) {
 system("cls");
 switch (st)
 {
 case showStartBG:
  printfStartGame();
  break;
 case start:
  printfStartGame();
  break;
 case playing:
  printGameScene((int*)s.mapBackground, s, s.sceneSize, s.offset);
  break;
 case over:
  printfGameOver();
  break;
 case showOverBG:
  printfGameOver();
  break;
 default:
  break;
 }
}
int main()
{
 char input;
 GameState  state = GameState::showStartBG;
 float speed = 2.0 / 1000; //step per msecond
 time_t lastFrame = 0;
 time_t laseMoveFrame = 0;
 time_t currentFrame = 0;
 Snake player;
 while (true)
 {
  lastFrame = currentFrame;
  currentFrame = GetTickCount64();

  if (_kbhit()) {
   input = _getch();
   switch (state)
   {
   case GameState::showStartBG:
    initSnake(player);
    renderScene(state, player);
    state = GameState::start;
    laseMoveFrame = currentFrame;
    break;
   case GameState::start:
    state = GameState:: playing;
    break;
   case GameState::playing:
    if (checkDir(player.currentDirection, input)) {
     player.currentDirection = input;
    } 
    break;
   case GameState::showOverBG:
    renderScene(state, player);
    state = GameState::over;
    break;
   case GameState::over:
    state = GameState::showStartBG;
    break;
   default:
    break;
   }
  }
  if (state == GameState::playing && (currentFrame - laseMoveFrame) * speed > 1) {
   laseMoveFrame = currentFrame;
   if (isDie(player)) {
    state = GameState::over;
   }
   else if(canEat(player))
   {
    player.head = onEatFood(player);
   }
   else
   {
    player.head = onMove(player.currentDirection, player.head);
   }
   renderScene(state, player);
  }

 }
    return 0;
}




你可能感兴趣的:(c/c++)