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

news2024/11/20 1:33:34

文章目录

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

1 开发历程

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

2 开发思路

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

3 使用介绍

由于整个项目只有一个文件,使用g++ -o snake main.cpp命令编译源文件,就可以得到一个名为snakeexe可运行程序,接着在exe同目录下打开cmd使用下面两个命令来执行程序:

  • snake start,开始游戏,w、s、a、d四个按键(不分大小写)控制蛇的移动方向,若蛇碰到墙壁或者自身,则游戏结束,并将计分存入名为rank.txt的文件中。
  • snake rank,查看历史前五得分记录。

4 源文件代码

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1371143.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

0-1背包问题-例题

题目摘自《卡码网》46题 题意理解 m种材料——对应m物品 大小问n的行李箱——对应大小为n的背包 所以该问题是一个0-1背包问题&#xff0c;采用动态规划的一般思路来解题。 解题思路&#xff1a; 动规五部曲&#xff1a; &#xff08;1&#xff09;定义二维dp数组&#xff0c;明…

把图表题注编号由“一.1”改为“1.1“ (方法一)

前置设置&#xff1a; 手打章节标题&#xff0c;“绪论”&#xff0c;“ 相关理论和技术方法”。给章节标题设置样式 “标题一”&#xff0c;设置为一级标题。打开导航窗格&#xff0c;可以不开&#xff0c;我纯粹是为了操作方便。 1、选中第一章的标题--“绪论”&#xff0c;…

WhatsApp营销是什么?

WhatsApp营销是什么&#xff1f; WhatsApp营销是一种信息和服务营销&#xff0c;是外贸企业的营销利器。WhatsApp可用于售前咨询、售后服务以及品牌宣传等多种场景&#xff0c;跨境企业可通过WhatsApp快速链接目标客户&#xff0c;提供7x24h全天候客户支持,以建立良好的客户关…

UltraScale 和 UltraScale+ 生成已加密文件和已经过身份验证的文件

注释 &#xff1a;如需了解更多信息&#xff0c;请参阅《使用加密和身份验证确保 UltraScale/UltraScale FPGA 比特流的安全》 (XAPP1267)。 要生成加密比特流&#xff0c;请在 Vivado IDE 中打开已实现的设计。在主工具栏中&#xff0c;依次选择“Flow” → “Bitstream Setti…

源码|redis7.2.2|sds

文章目录 前言Type && EncodingsdsencodingcreateStringObjectcreateEmbeddedStringObject总结 createRawStringObject总结 createStringObjectFromLongDouble总结 createStringObjectFromLongLongWithOptions总结 相关操作sdscatlen总结 阈值44sds VS C字符串 前言 从…

微信这一功能全面升级,万万没想到

大家好&#xff0c;我是小悟 兄弟们&#xff0c;今天要说的是&#xff0c;复用已认证公众号资料快速注册已认证小程序的时代一去不复还了。 再从公众号快速注册小程序入口去注册小程序&#xff0c;注册后也是未认证的状态&#xff0c;需要单独付费认证。通过该方式注册的小程序…

案例:自定义下拉刷新动画(ArkTS)

文章目录 介绍相关概念完整示例代码结构解读 自定义下拉组件自定义刷新动画总结 介绍 本篇Codelab主要介绍组件动画animation属性设置。当组件的某些通用属性变化时&#xff0c;可以通过属性动画实现渐变效果&#xff0c;提升用户体验。 本Codelab使用的display接口处于mock阶…

私域新手必看:这可能是你最快速的入门指南!

一、认知层&#xff1a;为什么必须做私域&#xff1f; 了解这个问题&#xff0c;必须得观察一下中国现状。根据中国互联网络信息中心&#xff08;CNNIC&#xff09;发布第48次《中国互联网络发展状况统计报告》显示&#xff0c;截至 2021 年 6 月&#xff1a; 而典型电商平台的…

Transformers 2023年度回顾 :从BERT到GPT4

人工智能已成为近年来最受关注的话题之一&#xff0c;由于神经网络的发展&#xff0c;曾经被认为纯粹是科幻小说中的服务现在正在成为现实。从对话代理到媒体内容生成&#xff0c;人工智能正在改变我们与技术互动的方式。特别是机器学习 (ML) 模型在自然语言处理 (NLP) 领域取得…

小美的蛋糕切割(美团2024届秋招笔试第一场编程真题)

题目分析&#xff1a;切蛋糕问题切记可以横着切&#xff0c;也可以竖着切。本题目需要快速求得两个矩形的和&#xff0c;可以用二维前缀和数组解决。然后枚举一刀切的位置。 #include <iostream> #include <algorithm> using namespace std; int a[1005][1005]; l…

Unity中URP下抓屏的 开启 和 使用

文章目录 前言一、抓屏开启1、Unity下开启抓屏2、Shader中开启抓屏 二、抓屏使用1、设置为半透明渲染队列&#xff0c;关闭深度写入2、申明纹理和采样器3、在片元着色器使用请添加图片描述 三、测试代码 前言 我们在这篇文章中看一下&#xff0c;URP下怎么开启抓屏。 一、抓屏…

DAY3 -Learning English

一、积累 1.谁在乎啊&#xff08;who gives a shit&#xff09; 例句&#xff1a;who gives a shit about what he think. 谁在乎他怎么想的? 2. 往事如烟|过去的都过去了(water under the bridge) The argument we had is now water under the bridge . 我们之前的争论已…

爬取国家法律法规数据库法律条文

代码展现&#xff1a; import requests import os filename 法律条文\\ if not os.path.exists(filename):os.mkdir(filename) url https://flk.npc.gov.cn/api/? data {type: flfg,searchType: title;vague,sortTr: f_bbrq_s;desc,gbrqStart: ,gbrqEnd: ,sxrqStart: ,sx…

【案例实战】业务稳定性运行之全链路混合压测

1.全链路压测开展步骤 &#xff08;1&#xff09;什么是全链路压测 全链路压测是指基于真实业务场景&#xff0c;通过模拟海量的用户请求&#xff0c;对整个后台服务进行压力测试&#xff0c;从而评估整个系统的性能水平。 对应用程序的整个技术栈进行完整的压力和性能测试&a…

Eureka切换Nacos时发现两个注册中心的解决方法

报错信息如下&#xff0c;意思是发现了两个注册中心 Field autoServiceRegistration in org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration required a single bean, but 2 were found: - nacosAutoServiceRegistration: defined…

6.OpenResty系列之深入理解(二)

1. 日志输出 vim /usr/local/openresty/nginx/conf/nginx.conf默认配置如下 #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info;#pid logs/nginx.pid;http {#log_format main $remote_addr - $remote_user [$time…

使用Nonebot编写QQ机器人

使用 NoneBot 这个工具&#xff0c;来编写 QQ 机器人。 安装基础软件 一、安装 NoneBot 库 直接使用 pip 安装即可 pip install nonebot二、安装酷Q 软件和 HTTP API 插件 酷Q 软件可以直接到官网下载&#xff0c;https://cqp.cc/b/news&#xff0c;或者可以到网盘下载&am…

TinyLog iOS v3.0接入文档

1.背景 为在线教育部提供高效、安全、易用的日志组件。 2.功能介绍 2.1 日志格式化 目前输出的日志格式如下&#xff1a; 日志级别/[YYYY-MM-DD HH:MM:SS MS] TinyLog-Tag: |线程| 代码文件名:行数|函数名|日志输出内容触发flush到文件的时机&#xff1a; 每15分钟定时触发…

SpringMVC 获取参数

文章目录 1、通过ServletAPI获取2、通过控制器方法的形参获取请求参数3、[RequestParam ](/RequestParam )4、[RequestHeader ](/RequestHeader )5、[CookieValue ](/CookieValue )6、通过POJO获取请求参数7、解决获取请求参数的乱码问题 1、通过ServletAPI获取 将HttpServlet…

HarmonyOS中的@ohos.promptAction 模块中弹框

在各种APP中会根据不同的业务场景显示不同的弹框情况&#xff0c;针对这些场景API中提示了那些弹框呢&#xff1f;今天就看下&#xff1a; 首先弹框分为模态弹框和非模态弹框&#xff0c;模态弹框必须用户点击反馈后进行下一步操作&#xff0c;非模态弹框只是告知用户信息&…