文章目录
-
- [1 开发历程](#1 开发历程)
- [2 开发思路](#2 开发思路)
- [3 使用介绍](#3 使用介绍)
- [4 源文件代码](#4 源文件代码)
- [5 游戏截图](#5 游戏截图)
- [6 小结](#6 小结)
1 开发历程
游戏使用C++语言开发,是博主某个下午心血来潮的结果,后面又花了点时间加了计分,记录历史得分的功能。
2 开发思路
其实贪吃蛇主要难在蛇身的移动上,想像着蛇移动的模样,不禁让人想起链表这种数据结构,蛇的头带动蛇身的第二节,第二节带动第三节,每一节的移动方向会遗传给下一节,把握住这一点,难点就迎刃而解了。
3 使用介绍
由于整个项目只有一个文件,使用
g++ -o snake main.cpp
命令编译源文件,就可以得到一个名为snake
的exe
可运行程序,接着在exe同目录下打开cmd
使用下面两个命令来执行程序:
snake start
,开始游戏,w、s、a、d
四个按键(不分大小写)控制蛇的移动方向,若蛇碰到墙壁或者自身,则游戏结束,并将计分存入名为rank.txt的文件中。snake rank
,查看历史前五得分记录。
4 源文件代码
cpp
#include <iostream>
#include <cstring>
#include <fstream>
#include <set>
#include <windows.h>
#include <conio.h>
#include <time.h>
#define HEIGHT 15
#define WIDTH 35
#define INIT_FLUSHRATE 200
#define INIT_SCORE 0
#define BODY '@'
#define EMPTY ' '
#define FOOD '*'
#define EDGE '#'
#define RANKFILE "rank.txt"
using namespace std;
bool keyIsput = false;
enum class Orient {UP, DOWN, LEFT, RIGHT};
typedef struct node {
int row;
int col;
Orient direction;
struct node* next;
} Node;
typedef struct {
int frow;
int fcol;
} Food;
bool FoodInSnake(const int& r, const int& c, Node* q) {
while (q != NULL) {
if (q->row == r && q->col == c)return true;
q = q->next;
}
return false;
}
void printRank(){
ifstream fin(RANKFILE);
if(!fin){
cerr << RANKFILE << " does not exit or open file error!!";
exit(1);
}
if(fin.peek()==ifstream::traits_type::eof()){cout << "file is empty";exit(1);}
for(int i=0; i<5; ++i){
int tmp;
if(fin.eof())break;
fin >> tmp;
cout << "No." << i+1 << ": " << tmp << '\n';
}
}
class Snake {
private:
Node* snakeHead;
public:
static Orient tmp_1, tmp_2;
Snake(int r, int c, Orient ori) {
snakeHead = new Node;
snakeHead->row = r;
snakeHead->col = c;
snakeHead->direction = ori;
snakeHead->next = NULL;
}
Node* getHead() {
return snakeHead;
}
void setOrient(Orient ori) {
tmp_1 = snakeHead->direction;
snakeHead->direction = ori;
}
void move() {
Node* p = snakeHead;
while (p != NULL) {
if (p == snakeHead) {
if(keyIsput==false)
tmp_1 = p->direction;
}
else if (p != snakeHead) {
tmp_2 = p->direction;
p->direction = tmp_1;
tmp_1 = tmp_2;
}
switch (p->direction) {
case Orient::UP:(p->row)--; break;
case Orient::DOWN:(p->row)++; break;
case Orient::LEFT:(p->col)--; break;
case Orient::RIGHT:(p->col)++; break;
default:break;
}
p = p->next;
}
keyIsput = false;
}
void longer(int row, int col, Orient direction) {
Node* newbody = new Node;
newbody->row = row;
newbody->col = col;
newbody->direction = direction;
newbody->next = snakeHead;
snakeHead = newbody;
}
virtual ~Snake() {
Node *p = snakeHead, *q=NULL;
while (p != NULL) {
q = p->next;
delete p;
p = q;
}
}
};
class Board {
private:
Snake* s;
int playground[HEIGHT][WIDTH] = {0};
Food* food;
bool foodIsEaten;
bool gameOver;
int score;
int flushRate;
public:
Board(int sr=HEIGHT/2, int sc=WIDTH/2, Orient sori=Orient::RIGHT):
gameOver(false),foodIsEaten(false),score(INIT_SCORE),flushRate(INIT_FLUSHRATE){
s = new Snake(sr, sc, sori);
playground[sr][sc] = 1;
food = new Food;
while ((food->frow = rand() % HEIGHT) == sr);
while ((food->fcol = rand() % WIDTH) == sc);
playground[food->frow][food->fcol] = 2;
}
void refresh() {
s->move();
Node* shead = s->getHead();
if((gameOver = isCollision(shead)))return;
if (shead->row == food->frow && shead->col == food->fcol) {
foodIsEaten = true;
switch (shead->direction)
{
case Orient::UP: s->longer(shead->row - 1, shead->col, shead->direction); break;
case Orient::DOWN: s->longer(shead->row + 1, shead->col, shead->direction); break;
case Orient::LEFT: s->longer(shead->row, shead->col - 1, shead->direction); break;
case Orient::RIGHT: s->longer(shead->row, shead->col + 1, shead->direction); break;
default:
break;
}
}
if (foodIsEaten)
refreshFood();
memset(playground, 0, sizeof(playground));
Node* p = s->getHead();
while (p != NULL) {
playground[p->row][p->col] = 1;
p = p->next;
}
if(!foodIsEaten)playground[food->frow][food->fcol] = 2;
}
void printBoard() {
for (int i = 0; i < HEIGHT; ++i) {
for (int j = 0; j < WIDTH; ++j)
playground[i][j] == 1 ? cout << BODY : (playground[i][j]==2? cout << FOOD:cout << EMPTY);
i==0?cout << EDGE << " Your score: " << score << '\n':cout << EDGE << '\n';
}
for (int k = 0; k < WIDTH; ++k)cout << EDGE;
}
void setSnakeOrient(Orient ori) {
s->setOrient(ori);
}
void refreshFood() {
score++;if(flushRate>50)flushRate-=5;
Node* p = s->getHead();
while (FoodInSnake(food->frow = rand() % HEIGHT, food->fcol = rand() % WIDTH, p));
foodIsEaten = false;
}
void keyCheck() {
if (_kbhit()) {
keyIsput = true;
char key = _getch();
switch (key)
{
case 'w':case'W':setSnakeOrient(Orient::UP); break;
case 'a':case'A':setSnakeOrient(Orient::LEFT); break;
case 's':case'S':setSnakeOrient(Orient::DOWN); break;
case 'd':case'D':setSnakeOrient(Orient::RIGHT); break;
default:
break;
}
}
}
bool isCollision(Node*& head){
if(head->row<0 || head->row>=HEIGHT)return true;
if(head->col<0 || head->col>=WIDTH)return true;
Node* p = head;
while(p!=NULL){
if(p!=head && p->row==head->row && p->col==head->col)
return true;
p = p->next;
}
return false;
}
int getFlushRate() const{
return flushRate;
}
bool isGameOver(){
return gameOver;
}
void printEnd(){
ifstream fin(RANKFILE);
if(!fin){
fin.close();
ofstream fout(RANKFILE);
fout << score;fout.close();
}else{
set<int> r;int tmp;
r.insert(score);
while(!fin.eof()){
fin >> tmp;
r.insert(tmp);
}
fin.close();
ofstream fout(RANKFILE);
for(auto i=r.rbegin();i!=r.rend();++i)
++i==r.rend()?fout << *(--i):fout << *(--i) << '\n';
fout.close();
}
cout << "Game Over!!\n";
cout << "Your score is " << score << ".\n";
}
~Board() {
delete s;
delete food;
}
};
Orient Snake::tmp_1 = Orient::RIGHT, Snake::tmp_2 = Orient::RIGHT;
int main(int argc, char* argv[]) {
if(argc!=2 || (strcmp(argv[1], "start")!=0 && strcmp(argv[1], "rank")!=0)){
cerr << "Usage: \"snake start\" to begin game.\n";
cerr << " \"snake rank\" to look rank(1-5).";
return 1;
}
if(strcmp(argv[1], "rank")==0){printRank();return 0;}
srand((unsigned)time(NULL));
Board* gameboard = new Board();
while (!gameboard->isGameOver()) {
gameboard->printBoard();
Sleep(gameboard->getFlushRate());
system("cls");
gameboard->keyCheck();
gameboard->refresh();
}
gameboard->printEnd();
delete gameboard;
return 0;
}
5 游戏截图
6 小结
程序中使用了windows的库,所以暂时只支持win系统运行,这个游戏可以说是人人皆知,博主小时候特别喜欢这个游戏,现在动手实现了,感觉还是很不错的。再次感谢各位的阅读,希望可以帮到各位,喜欢的可以点赞支持一波,Thank you very much!!
后话:想学习C语言的,可以看看博主的C现代方法笔记的文章哦!