C++ 实现简陋版贪吃蛇

 wall.h 文件:

#pragma once

#include <iostream>
using namespace std;

// 创建游戏墙壁的类
class Wall
{
public:
	// 利用枚举设置墙的宽和高
	enum
	{
		ROW = 26,
		COL = 26
	};
	// 声明初始化墙壁函数
	void InitWall();
	// 声明设置当前所在位置的符号的函数 
	void setWall(int x, int y, char c);	//参数1:x轴坐标,参数2:y轴坐标,参数3:要设置的符号
	// 声明获取当前位置的符号 要返回当前所在位置的符号
	char getWall(int x, int y);	//参数1:x轴坐标,参数2:y轴坐标
	// 声明 画墙壁 函数
	void drawWall();
private:
	// 声明游戏的二维数组
	char gameArray[ROW][COL];

};

wall.cpp 文件 :

#include "wall.h"
// 初始化游戏的墙壁
void Wall::InitWall()
{
	// 利用二维数组初始化游戏墙壁
	// 给游戏的二维数组初始化
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			// 设置墙壁
			if (i == 0 || j == 0 || i == ROW - 1 || j == COL - 1)
			{
				// 将该位置设置成墙壁
				gameArray[i][j] = '*';
			}
			else
			{
				// 将该位置设置成空格
				gameArray[i][j] = ' ';
			}
		}
	}
}
// 设置当前所在位置的符号
void Wall::setWall(int x, int y, char c)
{
	// 设置当前位置的符号
	gameArray[x][y] = c;
}
// 获取当前所在位置的符号
char Wall::getWall(int x, int y)
{
	// 直接返回当前所在的位置 
	return gameArray[x][y];
}
// 画游戏墙壁
void Wall::drawWall()
{
	// 给游戏的二维数组初始化
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			// 输出二维数组
	        cout << gameArray[i][j] << ' ';
		}
		// 显示一些提示
		if (i == 9)
		{
			cout << "Welcome game";
		}
		if (i == 11)
		{
			cout << "a : ←";
		}
		if (i == 13)
		{
			cout << "s : ↓";
		}
		if (i == 15)
		{
			cout << "d : →";
		}
		if (i == 17)
		{
			cout << "w : ↑";
		}
		cout << endl;
	}	
}

food.h 文件:

#pragma once

#include <iostream>
#include "wall.h"
using namespace std;

// 创建食物的类
class Food
{
public:
	// 构造函数
	Food(Wall & tempWall);

	// 初始化食物
	void InitFood();	 
public:
	// 维护住 Wall 类
	Wall& wall;	// 注意要用引用的方式
private:
	// 声明食物的坐标
	int FoodX;	// x轴坐标
	int FoodY;	// y轴坐标

};

 food.cpp 文件:

#include "food.h"
#include <windows.h>
void gotoxy2(HANDLE hOut2, int x, int y)//其中x,y是与正常理解相反的,注意区分
{
	COORD pos;
	pos.X = x;             //横坐标
	pos.Y = y;            //纵坐标
	SetConsoleCursorPosition(hOut2, pos);
}//光标定位函数
HANDLE hOut2 = GetStdHandle(STD_OUTPUT_HANDLE);

// 构造函数  用初始化列表的方式给墙类的对象初始化 
Food::Food(Wall& tempWall) : wall(tempWall)
{
	// 给食物的坐标初始化
	this->FoodX = rand() % (Wall::ROW - 1) + 1;
	this->FoodY = rand() % (Wall::COL - 1) + 1;
}
// 初始化食物
void Food::InitFood()
{
	// 添加随机函数种子
	srand((unsigned int)time(NULL));
	// 循环生成食物 当成功生成食物则退出循环
	while (1)
	{
		// 给食物的坐标 生成随机数 
		this->FoodX = rand() % (Wall::ROW - 1) + 1;
		this->FoodY = rand() % (Wall::COL - 1) + 1;

		// 设置食物不产生在蛇的身上,或是墙上
		if (wall.getWall(this->FoodX, this->FoodY) == ' ')
		{
			// 调用Wall 类的成员函数 setWall设置食物
			wall.setWall(this->FoodX, this->FoodY, '#');
			gotoxy2(hOut2, this->FoodY * 2, this->FoodX);
			cout << '#';
			// 成功生成食物则退出循环 
			break;
		}	
	}
}


snake.h 文件:

#pragma once

#include <iostream>
#include "wall.h"
#include "food.h"
using namespace std;

// 创建蛇的类
class Snake
{
public:
	// 构造函数
	Snake(Wall & tempWall, Food &tempFood);
public:
	// 枚举移动方向
	enum
	{
		LEFT = 'a',		// 向左
		DOWN = 's',		// 向下
		RIGHT = 'd',	// 向右
		UP = 'w'		// 向上
	};
	// 声明获取分数变量
	int score;
	// 声明速度变量
	int speed;
	// 创建蛇的节点结构体
	struct Node
	{
		int x,			// 蛇的横坐标
			y;			// 蛇的纵坐标
		Node* pNext;	//指向下一个节点的指针
	};
	// 声明 初始化蛇 函数
	void InitSnake();
	// 添加节点
	void addNode(int x, int y);
	// 销毁节点
	void destroyNode();
	// 删除节点
	void delNode();
	// 声明蛇的移动 函数, 注意这里是要判断是否移动成功
	bool SnakeMove(char key);
public:
	// 维护住 Wall 类
	Wall& wall;	//注意这要用引用的方式创建类的对象
	// 维护住 Food 类
	Food& food;	// 注意要用引用的方式创建类的对对象	
private:
	// 创建头节点指针
	Node* pHead;
};


 snake.cpp 文件:

#include "snake.h"
#include <windows.h>
void gotoxy1(HANDLE hOut1, int x, int y)//其中x,y是与正常理解相反的,注意区分
{
	COORD pos;
	pos.X = x;             //横坐标
	pos.Y = y;            //纵坐标
	SetConsoleCursorPosition(hOut1, pos);
}//光标定位函数
HANDLE hOut1 = GetStdHandle(STD_OUTPUT_HANDLE);
// 构造函数 利用初始化列表的方式给Wall 类的对象, Food的对象 初始化
Snake::Snake(Wall& tempWall, Food& tempFood) : wall(tempWall), food(tempFood)
{
	// 给头节点指针初始化
	this->pHead = NULL;
	// 初始化得分
	this->score = 0;
	// 初始化速度
	this->speed = 300;
}
// 初始化蛇
void Snake::InitSnake()
{
	// 保证初始时候是没有节点的
	destroyNode();
	// 添加节点,初始化蛇
	addNode(20, 10);
	addNode(20, 11);
	addNode(20, 12);
}
// 添加节点
void Snake::addNode(int x, int y)
{
	// 创建新节点 将新节点插入到链表中
	Node* pTemp = new Node;
	// 给节点成员赋值
	pTemp->x = x;
	pTemp->y = y;
	pTemp->pNext = NULL;
	if (NULL != pHead)
	{ 
		// 
		//pTemp->pNext = pHead;
		// 调用 Wall 类的设置当前位置函数,将头节点的位置设置成 = 
		wall.setWall(pHead->x, pHead->y, '=');
		gotoxy1(hOut1, pHead->y * 2, pHead->x);	// 更新光标位置
		cout << '=';
	}
	// 不管头是否为空,都将新节点赋给头节点 
	//pHead = pTemp;
	// 新来的节点指向头
	pTemp->pNext = pHead;
	// 头节点前移
	pHead = pTemp;

	// 重置头节点所在位置的符号
	wall.setWall(pHead->x, pHead->y, '@');
	gotoxy1(hOut1, pHead->y * 2, pHead->x);
	cout << '@';
}
// 销毁节点 注意这里是要释放所有的节点
void Snake::destroyNode()
{
	// 释放所有的节点
	// 记录头节点
	Node* temp = pHead;
	// 遍历
	while (NULL != pHead)
	{
		// 抠出头节点
		temp = pHead->pNext;
		// 将头节点的位置设置成 空格
		wall.setWall(pHead->x, pHead->y, ' ');
		// 释放头节点
		delete pHead;
		// 将头节点置空
		pHead = NULL;	
		// 给头节点重新赋值
		pHead = temp;		
	}	
}
// 删除节点 尾删除
void Snake::delNode()
{
	// 注意这里要有两个节点及以上节点才进行尾删除
	if (NULL == pHead || NULL == pHead->pNext)
		return;

#if 0
	// 尾删除方法一:
	// 记录头
	Node* temp = pHead;
	// 遍历找尾节点
	while (NULL != temp->pNext)
	{
		//  节点前移
		temp = temp->pNext;
	}
	// 将尾节点所在的位置符号设置空格
	wall.setWall(temp->x, temp->y, ' ');
	gotoxy1(hOut1, temp->y * 2, temp->x);
	cout << ' ';
	// 释放
	delete temp;
	// 将尾节点置成空
	temp = NULL;
#else
	// 尾删除方法二:
	// 双指针法,遍历
	Node* pPnext = pHead->pNext;	// 相对于蛇头的下一个节点, 要找的尾节点
	Node* pPre = pHead;				// 蛇头的节点,要找的尾节点的前一个节点
	// 遍历
	while (NULL != pPnext->pNext)
	{
		// 节点前移
		pPre = pPre->pNext; 
		pPnext = pPnext->pNext;
	}
	// 找到pPnext就是尾节点
	//  将尾节点设置所在的位置符号设置成空格
	wall.setWall(pPnext->x, pPnext->y, ' ');
	gotoxy1(hOut1, pPnext->y * 2, pPnext->x);
	cout << ' ';
	// 删除尾节点
	delete pPnext;
	// 将尾节点置空
	pPnext = NULL;
	// 将尾节点的前一个节点指向下一个节点的指针赋空
	pPre->pNext = NULL;
#endif
}
// 处理蛇的移动
bool Snake::SnakeMove(char key)
{
	// 获取蛇头的坐标
	int x = pHead->x;	// 获取蛇头的x轴坐标
	int y = pHead->y;	// 获取蛇头的y轴坐标	
	// 分类处理 用户按下的方向键
	switch (key)
	{
	case LEFT:	// 向左移
		y--;
		break;
	case RIGHT:	// 向右移
		y++;
		break;
	case UP:	// 向上移
		x--;
		break;
	case DOWN:	// 向下移
		x++;
		break;
	default:
		break;
	}
	// 判断是否继续游戏 注意一定要向进行判断是否撞墙或是吃到蛇身,
	//要不然成功移动蛇之后,以增加节点,初始坐标就变了,即使蛇撞到墙,当前蛇头就与墙的坐标不对等了
	if (wall.getWall(x, y) == '*' || wall.getWall(x, y) == '=')
	{
		gotoxy1(hOut1, Wall::COL*2, Wall::ROW - 5);
		// 蛇撞墙了,结束游戏
		cout << "Game Over!!!";	
		return false;
	}
	// 判断蛇是否成功吃到食物
	if (wall.getWall(x, y) == '#')
	{
		// 增加节点
		addNode(x, y);
		// 重新产生食物
		food.InitFood();
		// 得分+10
		score += 10;
		// 速度-20
		speed -= 20;
	}
	else
	{
		// 代表蛇移动成功
		// 先增加节点,后删除节点
		addNode(x, y);	// 增加节点
		delNode();		// 删节点
	}
	// 判断蛇是否移动成功 如果是这样写移动时,吃到食物,会隔一会才显示增长
	//if (wall.getWall(x, y) != '#' || wall.getWall(x, y) != '*')
	//{
	//	// 代表蛇移动成功
	//	// 先增加节点,后删除节点
	//	addNode(x, y);	// 增加节点
	//	delNode();		// 删节点
	//}
	return true;
}

game.cpp 文件:

#include <iostream>
#include "wall.h"
#include "snake.h"
#include "food.h"
#include <windows.h>
#include <conio.h>
using namespace std;
void gotoxy(HANDLE hOut, int x, int y)//其中x,y是与正常理解相反的,注意区分
{
	COORD pos;
	pos.X = x;             //横坐标
	pos.Y = y;            //纵坐标
	SetConsoleCursorPosition(hOut, pos);
}//光标定位函数
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//定义显示器句柄变量,并且这个只能在每个头文件中单独定义句柄和函数,否则无效

int main()
{
	// 创建Wall 类的对象
	Wall wall;
	// 调用初始化墙壁
	wall.InitWall();
	// 调用设置墙壁 测试是否成功画d出蛇
	wall.drawWall();
	// 调用设置墙壁
	//wall.drawWall();
	// 创建Food 类的对象
	Food food(wall);
	// 调用初始化食物
	food.InitFood();
	// 创建Snake 类的对象
	Snake snake(wall, food);
	// 调用Snake 的初始化成员函数
	snake.InitSnake();
	// 定义一个标识 判断是否继续游戏
	bool isDeath = false;
	// 定义一个记录前一次方向键的变量
	char preKey = NULL;
	gotoxy(hOut, Wall::COL * 2, Wall::ROW - 7);
	cout << "得分:" << snake.score;	
	while (!isDeath)
	{
		// 定义一个变量接受用户输入反向键
		char key = _getch();
		do
		{
			// 禁锢开始游戏时向左移动
			if (preKey == NULL && key == Snake::LEFT)
			{
				// 将preKey 赋给key 
				continue;
			}
			// 设置用户按下规定的4个方向键之外的键盘其他键时,将上一次的方向键赋给当前其他的键
			if (key == Snake::LEFT || key == Snake::RIGHT || key == Snake::UP || key == Snake::DOWN)
			{
				// 禁锢当前要移动的方向键和上一次方向键相反
				if (key == Snake::LEFT && preKey == Snake::RIGHT ||
					key == Snake::RIGHT && preKey == Snake::LEFT ||
					key == Snake::UP && preKey == Snake::DOWN ||
					key == Snake::DOWN && preKey == Snake::UP)
				{
					// 将上一次方向键赋给当前要一定的方向键
					key = preKey;
				}
				// 记录上一次的方向键
				preKey = key;
				// 判断蛇是否成功
				if (snake.SnakeMove(key) == true)
				{
					// 清屏
					//system("cls");
					// 重新画墙
					//wall.drawWall();
					// 休眠
					Sleep(snake.speed);
					gotoxy(hOut, Wall::COL * 2, Wall::ROW - 7);
					cout << "得分:" << snake.score;
				}
				else
				{
					// 退出
					isDeath = true;	// 将标志置成 true
					gotoxy(hOut, Wall::COL * 2, Wall::ROW - 3);
					break;
				}
			}
			else
			{
				// 将上一次的方向键赋给当前的key
				key = preKey;
			}
		} while (!_kbhit());
	}
	 测试是否成功将所有的节点全部销毁
	//snake.destroyNode();
	 调用设置墙壁 测试是否成功画出蛇
	//wall.drawWall();

	 测试是否成功删尾巴
	//snake.delNode();
	 调用设置墙壁 测试是否成功画出蛇
	//wall.drawWall();

	system("pause");
	return 0;
}

程序运行结果

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值