您的位置:首页 > 编程语言 > C语言/C++

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

2016-09-07 00:04 489 查看
分析贪吃蛇游戏,蛇的身体在吃到食物之后增长,因此可以用链表来储存蛇身体的节点。

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

//

#include "stdafx.h"

#include <windows.h>

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#include <time.h>

#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;

}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: