C++实现简单贪吃蛇游戏

文章目录

    • [1 开发历程](#1 开发历程)
    • [2 开发思路](#2 开发思路)
    • [3 使用介绍](#3 使用介绍)
    • [4 源文件代码](#4 源文件代码)
    • [5 游戏截图](#5 游戏截图)
    • [6 小结](#6 小结)

1 开发历程

游戏使用C++语言开发,是博主某个下午心血来潮的结果,后面又花了点时间加了计分,记录历史得分的功能。

2 开发思路

其实贪吃蛇主要难在蛇身的移动上,想像着蛇移动的模样,不禁让人想起链表这种数据结构,蛇的头带动蛇身的第二节,第二节带动第三节,每一节的移动方向会遗传给下一节,把握住这一点,难点就迎刃而解了。

3 使用介绍

由于整个项目只有一个文件,使用g++ -o snake main.cpp命令编译源文件,就可以得到一个名为snakeexe可运行程序,接着在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现代方法笔记的文章哦!

相关推荐
Scott9999HH7 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
2401_841495647 小时前
【操作系统】进程同步与互斥实验报告
c++·算法·操作系统·进程·并发·同步·互斥
fqbqrr7 小时前
2607C++,soui与安卓
c++·soui
码智社8 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海8 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
FairGuard手游加固9 小时前
2026年7月份国产游戏审批信息
游戏
马优晨9 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
a11177610 小时前
2FA 验证码生成器(github登录验证 app)
笔记·学习
我的xiaodoujiao11 小时前
API 接口自动化测试详细图文教程学习系列32--Allure测试报告2
python·学习·测试工具·pytest