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现代方法笔记的文章哦!

相关推荐
机智的叉烧3 分钟前
前沿重器[57] | sigir24:大模型推荐系统的文本ID对齐学习
人工智能·学习·机器学习
量子-Alex1 小时前
【多模态聚类】用于无标记视频自监督学习的多模态聚类网络
学习·音视频·聚类
轻口味1 小时前
命名空间与模块化概述
开发语言·前端·javascript
吉大一菜鸡1 小时前
FPGA学习(基于小梅哥Xilinx FPGA)学习笔记
笔记·学习·fpga开发
晓纪同学2 小时前
QT-简单视觉框架代码
开发语言·qt
威桑2 小时前
Qt SizePolicy详解:minimum 与 minimumExpanding 的区别
开发语言·qt·扩张策略
飞飞-躺着更舒服2 小时前
【QT】实现电子飞行显示器(简易版)
开发语言·qt
明月看潮生2 小时前
青少年编程与数学 02-004 Go语言Web编程 16课题、并发编程
开发语言·青少年编程·并发编程·编程与数学·goweb
明月看潮生2 小时前
青少年编程与数学 02-004 Go语言Web编程 17课题、静态文件
开发语言·青少年编程·编程与数学·goweb
Java Fans2 小时前
C# 中串口读取问题及解决方案
开发语言·c#