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

文章目录

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

灵活轻巧的java接口自动化测试实战

前言 无论是自动化测试还是自动化部署&#xff0c;撸码肯定少不了&#xff0c;所以下面的基于java语言的接口自动化测试&#xff0c;要想在业务上实现接口自动化&#xff0c;前提是要有一定的java基础。 如果没有java基础&#xff0c;也没关系。这里小编也为大家提供了一套jav…

CHS_01.1.4+操作系统体系结构 一

CHS_01.1.4操作系统体系结构 一 操作系统的体系结构 也就是操作系统的内核应该怎么设计这样一个问题操作系统的内核 在这个小节中 我们会学习 操作系统的体系结构 也就是操作系统的内核应该怎么设计这样一个问题 那这个小节的内容我们只需要做简要的了解就可以了 我们考试中常考…

数模学习day08-拟合算法

这里拟合算法可以和差值算法对比 引入 插值和拟合的区别 与插值问题不同&#xff0c;在拟合问题中不需要曲线一定经过给定的点。拟 合问题的目标是寻求一个函数&#xff08;曲线&#xff09;&#xff0c;使得该曲线在某种准则下与所 有的数据点最为接近&#xff0c;即曲线拟…

手把手教你从阿里云容器仓库拉取镜像

如何从阿里云容器镜像仓库拉取镜像 背景&#xff0c;我的服务器无法访问外网&#xff0c;那么在需要使用一些容器镜像的时候就无法从阿里云容器镜像仓库拉取镜像&#xff0c;那怎么办呢&#xff0c;坦白讲&#xff0c;我比较菜&#xff0c;我也不是做开发的&#xff0c;我是做…

1.9.。。

1 有道云笔记 2 .cpp #include "mywidget.h" #include "ui_mywidget.h"myWidget::myWidget(QWidget *parent) :QWidget(parent),ui(new Ui::myWidget) {ui->setupUi(this);this->setWindowTitle("原神");this->setStyleSheet("…

JVM主要的几种垃圾回收算法

1、Java 为什么要实现自动内存管理 &#xff1f; 简化开发过程&#xff1a;通过内存自动管理可以避免手动分配和释放内存的麻烦&#xff0c;减少了内存泄漏和内存错误的风险&#xff0c;让研发能更专注于业务逻辑&#xff0c;不必纠结于内存管理的细节。 提高开发效率&#xff…

2024年第九届机器学习技术国际会议(ICMLT 2024) 即将召开

2024年第九届机器学习技术国际会议&#xff08;ICMLT 2024&#xff09;将于2024年5月24-26日在挪威奥斯陆举行。ICMLT 2024旨在讨论机器学习技术领域的最新研究技术现状和前沿趋势&#xff0c;为来自世界各地的科学家、工程师、实业家、学者和其他专业人士提供一个互动和交流的…

cosmos及特定应用程序的区块链

特定应用程序的区块链,简单来说&#xff0c;一个区块链就是一个专门的应用程序。为了实现某一特定的去中心化应用而专门实现一个区块链。 传统的用智能合约构建去中心化应用不行吗&#xff1f; 灵活性不足&#xff1a;智能合约本质上受到虚拟机本身的限制。例如&#xff0c;以…

uniapp自定义封装只有时分秒的组件,时分秒范围选择

说实话&#xff0c;uniapp和uview的关于只有时分秒的组件实在是不行。全是日历&#xff0c;但是实际根本就不需要日历这玩意。百度了下&#xff0c;终于看到了一个只有时分秒的组件。原地址&#xff1a;原地址&#xff0c;如若侵犯请联系我删除 <template><view clas…

BlogPark测试报告

目录 一&#xff0c;项目背景 二&#xff0c;项目功能 三&#xff0c;测试计划 3.1 测试用例的设计 3.2 功能测试 1.正常登录 2.正常写博客测试 &#xff08;输入完整的标题和内容&#xff09; 3.发布博客之后跳转到详情页观察是否有刚发布的博客 4.删除博客观察列表的…

会计报名照片怎么压缩?这几种方法一定要会

会计师报名考试的时候&#xff0c;在上传证件照信息时通常会对图片大小、格式、尺寸、还有照片dpi分辨率以及照片背景颜色都有具体的要求&#xff0c;小编在这边建议考生提前了解好报名照片的要求&#xff0c;避免照片审核失败&#xff0c;那么会计报名照片怎么压缩呢&#xff…

基于springboot在线考试系统源码和论文

网络的广泛应用给生活带来了十分的便利。所以把在线考试管理与现在网络相结合&#xff0c;利用java技术建设在线考试系统&#xff0c;实现在线考试的信息化。则对于进一步提高在线考试管理发展&#xff0c;丰富在线考试管理经验能起到不少的促进作用。 在线考试系统能够通过互…

ISIS基本概率与配置(HCIP完整版)

目录 一、ISIS协议基础 1、ISIS概述&#xff08;认识ISIS&#xff09; 2、ISIS的应用 4、ISIS的工作过程 5、ISIS路由器的类型 6、ISIS区域 7、ISIS报文 8、ISIS基础配置 9、进程号&#xff1a; 10、NET地址 11、ISIS邻居关系 二、邻居表分析 1、ISIS邻居表字段解析…

MVC设计模式和与三层架构的关系

MVC设计模式和与三层架构的关系 MVC是一种设计模式&#xff0c;将软件按照模型、视图、控制器来划分&#xff1a; M&#xff1a;Model&#xff0c;模型层&#xff0c;指工程中的JavaBean&#xff0c;作用是处理数据 JavaBean分为两类&#xff1a; 一类称为数据承载Bean&#x…

JavaSec基础 反射修改Final修饰的属性及绕过高版本反射限制

反射重拾 半年没碰java了 先写点基础回忆一下 反射弹计算器 public class Test {public static void main(String[] args) throws Exception {Class<?> clazz Class.forName("java.lang.Runtime");clazz.getDeclaredMethod("exec", String.cla…

记录汇川:H5U与Fctory IO测试5

主程序&#xff1a; 子程序&#xff1a; IO映射 子程序&#xff1a; 自动程序 Fctory IO配置&#xff1a; 触摸屏如下&#xff1a; 实际动作如下&#xff1a; Fctory IO测试5

力扣:18.四数之和

一、做题链接&#xff1a;18. 四数之和 - 力扣&#xff08;LeetCode&#xff09; 二、题目分析 1.做这一道题之前本博主建议先看上一篇《三数之和》 2.题目分析 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重…

java连接池、C3P0、Druid德鲁伊连接池技术

java线程池 连接池C3P0Druid 连接池 概念&#xff1a;其实就是一个容器(集合)&#xff0c;存放数据库连接的容器。当系统初始化好后&#xff0c;容器被创建&#xff0c;容器中会申请一些连接对象&#xff0c;当用户来访问数据库时&#xff0c;从容器中获取连接对象&#xff0c…

信息论与编码期末复习——计算题+基础汇总(二)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;V…

微信小程序连接数据库与WXS的使用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《微信小程序开发实战》。&#x1f3af;&#x1f3a…