数据结构与算法分析期末大作业(贪吃蛇)(管理员系统)(排行榜)

         这个项目是作为数据结构与算法分析课程期末项目大作业写的,要求是使用多种所学的算法和结构,所以选题和命令有一些限制。在写代码上也是往所要求的内容靠拢。代码部分分成三个部分,贪吃蛇游戏、管理员系统;排行榜系统三个部分。

        管理员系统有账号和密码登录,密码和账号放入分离链接散列表中。游戏的结果:得分、用户名、耗时等能够通过游戏结束后写入文件,也可以通过管理员验证通过后自行输入。之后会通过文件读取的方式从文件中读出数据,放入结构体数组中,使用结构体快排为数组排序。使用二分法为数据显示其排名(当用户结束游戏时,会在已排序好的结构体数组中找出其排名)。

        代码总量约700行,其中游戏部分的代码较多参考CSDN中的一篇博客,更新了一些操作,但本质没有修改。但是在写博客时没法找到这个记录。致歉。

        头文件中第一行时万能头文件

头文件:

#include<bits/stdc++.h>
#include<Windows.h>
typedef struct list_node* list_position;
//定义链表
struct list_node {
	int password; //密码
	list_position next;//链表中的指向下一个单元的指针
};
typedef struct table_node* table_position;
//定义散列表
struct table_node {
	int table_size;//哈希表的大小
	//用指针表示的数组;数组中的每一个元素都用来做链表的起始点
	list_position* arr;
};

//记录
struct HistoryRecord {
	char name[64]; //用户名
	//string会出错?奇怪的原因
	clock_t costtime; // 花费的时间
	int score; // 得分
};//文件中记录的数据结构体

extern struct HistoryRecord NowRecord;//声明结构体NowRecord用于存放一次游戏的结果
extern struct HistoryRecord* UserAllRecord;//全局变量,全部记录
extern table_position t;


void  start();//起始界面
void game_snack();//游戏部分
void Administrator(int AdminNum, table_position t);//管理员权限
int Limitint();//限制int类型输入
void PrintfRanking(int a);//显示排行榜前几位(自选 )
void Game_start();//游戏开始
void Account();//管理员判断
int main_ShowRanking(int grade);//返回排名
void insert(int Admin_Num, table_position p, int password);//管理员密码插入
void fintoArr();//读取文件内容到结构体数组并快排 
void InitAccout();//管理员初始化
void Writetofin(struct HistoryRecord NowRecord1);//将结果写入文本
void welcometogame();//欢迎界面
void Myprintf();//打印初始界面
void gotoxy(int x, int y);//传入x y 修改控制台光标位置
void color(int col);//调用API改变控制台字体颜

 main.cpp:

#define _CRT_SECURE_NO_WARNINGS
#include"Gluttony.h"
using namespace std;

int main() {
	InitAccout();//初始化一次就完成了,while循环时,避免再次初始化管理员信息
	int a = 1;
	while (a==1)//第二次之后输入任意数推出程序
	{
		start();
		cout << "输入1重新开始" << endl << "输入任意键退出" << endl;
		a= Limitint();
	}
}

管理员验证

#include"Gluttony.h"
#include<Windows.h>
using namespace std;

table_position t;//创建
int hash(int val, int table_size) {
	return  val % table_size;
}
//初始化散列表
void init_table(int table_size,table_position& t) {
	//初始化表
	t = (table_position)malloc(sizeof(struct table_node));
	t->table_size = table_size;
	//初始化链表里的数组
	t->arr = (list_position*)malloc(table_size * sizeof(struct list_node));
	//初始化每个数组元素(每个链表)
	for (int i = 0; i < table_size; i++) {
		t->arr[i] = (list_position)malloc(sizeof(struct list_node));
		t->arr[i]->next = NULL;
	}
}
//判断一个arr[i]中是否有重复元素
int find1(int password1, int Admin_Num, table_position t ){
	//先计算出哈希码值
	int result = ::hash(Admin_Num, t->table_size);
	//将哈希码对应的链表取出来
	list_position list = t->arr[result];
	//因为链表头不放东西,所以从下一个开始找 
	list_position p = list->next;
	//开始找
	while (p)
	{
		if (p->password == password1)
			return 1;//有重复
		p = p->next;
	}
	return 0;//无重复
}
//插入,password密码。Admin 作为散列数组的序号。
void insert(int Admin_Num, table_position p,int password) {
	//首先判断password 是否存在,是,则不进行插入;反之插入

	if (!find1(password, Admin_Num, p)) {
		//创建一个新的链表节点
		list_position New1 = (list_position)malloc(sizeof(struct list_node));
		//将散列表中的链表拿来用
		int result = ::hash(Admin_Num, p->table_size);
		list_position origin = p->arr[result];
		//把这个链表节点new1插进 origin
		New1->next = origin->next;
		New1->password = password;
		origin->next = New1;
	}
	else {
		std::cout << "密码已存在,请不要重复输入" << endl;
		return;//发生重复,则不进行操作
	}
		
}
//判断管理员是否存在
int Admin_exit(int getsum,int arr[],int longA) {
	int* p = find(arr, arr + longA, getsum);//find函数判断管理员编号是否存在,返回地址
	if (*p == getsum) {
		return 1;
	}
	else
		return 0;
}
//打印出所有密码
void tra(table_position t) {
	list_position p;
	for (int i = 0; i < t->table_size; i++) {
		//抓取一个链表
		p = t->arr[i]->next;
		//遍历这个链表
		while (p) {
			printf("%d  ", p->password);
			p = p->next;
		}
		printf("NULL  \n");
	}
}
//打印我的管理员密码
void PrintfMypassword(int MyAdminNum, table_position My) {
	list_position p;
	//抓取一个链表
	p = My->arr[MyAdminNum]->next;
	//遍历这个链表
	while (p) {
		cout << p->password<<"  ";
		//printf("%d  ", p->password);
		p = p->next;
	}
	cout<<endl;
}
//管理员链表初始化
void InitAccout() {
	
	init_table(10, t);//初始化
	insert(0, t, 123456);
	insert(0, t, 111111);
	insert(3, t, 123456);
	insert(5, t, 123456);
	insert(7, t, 123456);//密码输入
}
//管理员判断,成功进入管理员界面
void Account() {

	int arr[] = { 0,3,5,7 };//四位管理员,编号0,3,5,7
	system("cls");
	puts("管理员验证,输入管理员账号:\n");
	//管理员账号
	int getsnum ;
	//管理员密码
	int password;
	int longA = sizeof(arr) / sizeof(int);
Zhanghao:
	getsnum= Limitint();//限制只能输入int类型的数值
	if (Admin_exit(getsnum, arr, longA)) {
		puts("输入密码\n");
	Password:
		password= Limitint();
		if (find1(password, getsnum, t)) {//判断密码是否对应
			puts("密码正确,进入管理员界面\n");
			Administrator(getsnum,t);//管理员界面
		}
		else {
			puts("密码错误,请重新输入\n");
			goto Password;
		}
	}
	else {
		puts("账号错误,请重新输入\n");
		goto Zhanghao;
	}
}
//管理员登录权限
void Administrator(int AdminNum, table_position t) {//传入管理员账号
	cout << "管理员" << AdminNum << "欢迎登录" << endl;
	if (0) {
	tabe1:	
		system("cls");
	}

	cout << "输入1为自己添加新密码" << endl;
	cout << "输入2添加成绩" << endl;
	cout << "输入3退出" << endl;
	int AdminInput = 0;

	cin >> AdminInput;
	switch (AdminInput)
	{
	case 1:
	{
		cout << "请输入新密码"<<endl;
		int password = 0;
		password=Limitint();		
		insert(AdminNum, t, password);
		cout << "现在你的密码有:" << endl;
		PrintfMypassword(AdminNum, t);
		system("pause");
		goto tabe1;
		break;
	}
	case 2: {
		struct HistoryRecord AdminRecord;
		cout << "输入成绩和用时\n";
		AdminRecord.score = Limitint();
		AdminRecord.costtime=Limitint();
		char AdminName[64]="管理员";
		char AdminName2[64];
		sprintf_s(AdminName2, "%d", AdminNum);//将int 型的AdminNum的内容放入char中
		strcat_s(AdminName, AdminName2);//组成 管理员+AdminNum
		strcpy_s(AdminRecord.name, AdminName);//放入结构体
		Writetofin(AdminRecord);
		cout << "成绩已成功添加" << endl;
		system("pause");
		goto tabe1;
		break;
	}
	case 3:
		break;//start();
	default: {
		cout << "输入错误,重新输入" << endl;
		Sleep(1000);
		goto tabe1;
		break;
	}
	}
}


起始界面

#define _CRT_SECURE_NO_WARNINGS

#include"Gluttony.h"

using namespace std;

void  start() {
	system("cls");
	//puts("输入1开始游戏\n");
	//puts("输入2进入管理员系统\n");
	//puts("输入3查看排行榜\n");
	welcometogame();
	int a = 0; 
	a = Limitint();
	switch (a)
	{
	case 1: {
		game_snack();
		break;
	}	
	case 2:{

		Account();
		break;
	}
	case 3: {
		system("cls");
		cout << "请输入数字来"<<endl << "查看排行榜前几位:" << endl;
		//显示排行榜前几位(自选 )
		int b = 0; 
		b = Limitint();
		PrintfRanking(b);
		//system("pause");
		break;
	}
	default:
		cout << "输入错误" << endl;
		break;
	}
}

//打印初始界面
void welcometogame()
{
	int choice;
	int x, y;
	int count1 = 0, count2 = 0;
	gotoxy(37, 10);
	color(11);
	printf("贪   吃   蛇   大   作   战");

	for (x = 20; x < 80; x++)
	{
		count2++;
		for (y = 17; y < 26; y++)
		{
			count1++;
			if (x == 20 || x == 79)
			{
				if (count1 % 2 == 0)
					color(9);
				else
					color(13);

				gotoxy(x, y);
				printf("|");
			}

			if (y == 17 || y == 25)
			{
				if (count2 % 2 == 0)
					color(9);
				else
					color(13);

				gotoxy(x, y);
				printf("-");
			}
		}

	}

	gotoxy(32, 19);
	color(13);
	cout << "1:开始游戏";

	gotoxy(58, 19);
	cout << "2:进入管理员系统";

	gotoxy(32, 23);
	cout << "3:查看排行榜";

	gotoxy(43, 26);
	cout << "请选择[1 2 3]:[ ]\b\b";
	color(7);
}

文件记录cpp:

#define _CRT_SECURE_NO_WARNINGS
#include"Gluttony.h"
using namespace std;
struct HistoryRecord* UserAllRecord;//全局变量,全部记录
int lineCnt = 0;//文件中的回车数目
bool cmp1(struct HistoryRecord A, struct HistoryRecord B) {
    return A.score > B.score;
}
//限制输入,只能输入int型
int Limitint() {

    int iInput;
    while (!(cin >> iInput)) {
        
        cin.clear();//清除错误标记,重新打开输入流,但是输入流中依旧保留着之前的不匹配的类型
        /*cin.sync();*///清楚cin缓存区的数据。
        while (cin.get() != '\n') {
            continue;
        }
        cout << "输入内容不合理,请重新输入" << endl;
    }

    return iInput;
}
//读取文件内容到结构体数组并快排。
void fintoArr() {

    int  n = 0; 
    lineCnt = 0;//将行数重新置0,防止再次调用lineCnt超过范围
    //之前错误就是因为lineCnt未置0,导致PrintfRanking()调用时,打印错误
    ifstream fin("score.txt", ios::in);//文件流1用于检索文件有几行内容
    ifstream in("score.txt", ios::in);//将内容写入结构体数组中
    if (!in.is_open())
    {
        cout << "Error: opening file fail" << endl;
        exit(1);
    }
    char c; 
    while (fin.get(c))
    {
        if (c == '\n')
            lineCnt++;
    }
    fin.close();
    UserAllRecord = new struct HistoryRecord[lineCnt + 1];//对于数组大小位置的结构体数组,用new分配内存空间
    while (!in.eof() && n <=  lineCnt)//enf()  end of file
    {
        in >> UserAllRecord[n].name >> UserAllRecord[n].score >> UserAllRecord[n].costtime;
        n++;
    }
    in.close();
    sort(UserAllRecord, UserAllRecord + lineCnt+1, cmp1);//快排

   // delete[] UserRecord;
   //cout << p[0].name << "的成绩是" << p[0].score<<"用时" << p[0].costtime;
}
//显示排行榜前几位(自选 )
void PrintfRanking(int a) {
    //cin >> a;
    fintoArr();

    //else
    for (int i = 0; i < lineCnt && i < a; i++)
    
        cout << "第" << i + 1 << "名 \tname:" << UserAllRecord[i].name << " \t得分:" << UserAllRecord[i].score << " \t时间(ms):" << UserAllRecord[i].costtime << endl;

    if (a >= lineCnt + 1) {
        cout << "现在只有" << lineCnt << "条成绩" << endl;
        cout << "已为您展示" << endl;
    }
      //  delete[]UserAllRecord;

}
//显示成绩是第几位 递归加二分法
int ShowRanking(int grade,int start,int end) {
    //基线条件
    if (start == end)
        return start;
    int middle = (start + end) / 2;
    if (UserAllRecord[middle].score < grade)
        return ShowRanking(grade, 0, middle-1);
    else if(UserAllRecord[middle].score > grade)
        return ShowRanking(grade, middle+1, end);
    else if (UserAllRecord[middle].score == grade)
        return middle;

}
//调用ShowRank;让Show Rank递归实现二分查找
int main_ShowRanking(int grade) {//返回排名
    fintoArr();
    int Myrank = 0;
    Myrank=ShowRanking(grade, 0, lineCnt );
    delete[]UserAllRecord;
    return Myrank + 1;
}

游戏:

#define _CRT_SECURE_NO_WARNINGS

#include<conio.h>
#include<Windows.h>
#include"Gluttony.h"
using namespace std;
#define LSIZE 70
#define WSIZE 26

int grade = 0;//分数
int speedSnake = 300;//控制速度
char Username1[64] = "";
struct HistoryRecord NowRecord;
struct snakeAddress//蛇的位置坐标
{
	int x;
	int y;
};
//蛇结构体
struct Snake
{
	struct snakeAddress snakeHead[1200];//蛇各部分坐标
	int snakeSize;//蛇身体的长度
}s, temps;
//食物结构体
struct snakeFood
{
	int x1;//食物的坐标
	int y1;
}food;
//枚举
enum moveDir
{
	top1 = 'W', bottom1 = 'S', left1 = 'A', right1 = 'D',
	top2 = 'w', bottom2 = 's', left2 = 'a', right2 = 'd'
};
//调用API改变控制台字体颜
void color(int col)
{

	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), col);
}
//传入x y 修改控制台光标位置
void gotoxy(int x, int y)
{
	COORD Position;
	Position.X = x;
	Position.Y = y;
	//调用API改变字体位置
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), Position);
}
//初始化蛇
void initSnake()
{
	s.snakeHead[0].y = WSIZE / 2;
	s.snakeHead[0].x = LSIZE / 2;

	s.snakeHead[1].y = WSIZE / 2;
	s.snakeHead[1].x = LSIZE / 2 + 1;
	s.snakeSize = 1;
}
//初始化食物
void initfood()
{
	food.x1 = rand() % (LSIZE - 3) + 2;//避免食物坐标初始化在边界上
	food.y1 = rand() % (WSIZE - 2) + 1;
}
//初始化蛇头的坐标
void initCursor()
{
	COORD coord;
	for (int i = 0; i < s.snakeSize; i++)
	{
		gotoxy(s.snakeHead[i].x, s.snakeHead[i].y);
		if (i == 0)
			putchar('@');//头
		else
			putchar('*');//蛇身
	}
	//初始化蛇尾的坐标
	gotoxy(temps.snakeHead[temps.snakeSize].x, temps.snakeHead[temps.snakeSize].y);
	putchar(' ');
	//生成食物的坐标
	gotoxy(food.x1, food.y1);
	printf("#");
	//初始化结束后的光标
	gotoxy(LSIZE, WSIZE);
	printf("\n");
}
//打印边界
void Myprintf() {
	for (int i = 0; i <= LSIZE; i += 2) {
		gotoxy(i, 0);
		printf("■");
		gotoxy(i, WSIZE);
		printf("■");
	}
	for (int j = 0; j <= WSIZE; j++) {
		gotoxy(0, j);
		printf("■");
		gotoxy(LSIZE, j);
		printf("■");
	}
}
//控制蛇
void controlSnake()
{
	char ch = 'A';
	//以蛇头和墙壁是否碰撞作为条件
	while (s.snakeHead[0].x < LSIZE && s.snakeHead[0].x > 1 && s.snakeHead[0].y < WSIZE && s.snakeHead[0].y > 0)//采用■ ,在宽度上占两个单位,
		//蛇的移动范围 在 x [2,lsize-1];y z[2,wsize]
	{	temps = s;
		initCursor();//更新坐标
		//initScreen();
		//蛇头和身体碰撞
		for (int i = 1; i < s.snakeSize+1; i++)
		{
			if (s.snakeHead[0].x == s.snakeHead[i].x && s.snakeHead[0].y == s.snakeHead[i].y)
				return;
		}
		//蛇遇到食物的时候
		if (s.snakeHead[0].x == (food.x1 ) && s.snakeHead[0].y == (food.y1 ))
		{
			s.snakeSize++;//蛇身增长
			initfood();//产生新的食物 
			if (grade <= 100) {//四个个if 控制难度
			grade += 15;//得分增加
			speedSnake -= 20;//速度
			}
			else if (grade <= 300) {
				grade += 20;
				speedSnake -= 15;
			}
			else if (grade <= 600) {
				grade += 30;
				speedSnake -= 5;
			}
			else
				grade += 40;
		}
		//移动蛇的位置
		if (_kbhit())//如果键盘有输入
			ch = _getch();
		switch (ch)//蛇进行移动,并对蛇和食物坐标更新
			//w/W 上 ;S/s 下 ;A/a 左 ;D/d 右移动 
		{
		case top2:
		case top1:
			s.snakeHead[0].y -= 1;
			for (int i = 1; i < s.snakeSize+1; i++)
			{
				s.snakeHead[i] = temps.snakeHead[i - 1];//蛇头部往指定方向移动后,蛇身坐标跟随移动
			}
			break;
		case bottom2:
		case bottom1:
			s.snakeHead[0].y += 1;
			for (int i = 1; i < s.snakeSize+1; i++)
			{
				s.snakeHead[i] = temps.snakeHead[i - 1];
			}
			break;
		case left2:		
		case left1:
			s.snakeHead[0].x -= 1;
			for (int i = 1; i < s.snakeSize+1; i++)
			{
				s.snakeHead[i] = temps.snakeHead[i - 1];
			}
			break;

		case right2:
		case right1:
			s.snakeHead[0].x += 1;
			for (int i = 1; i < s.snakeSize+1; i++)
			{
				s.snakeHead[i] = temps.snakeHead[i - 1];
			}
			break;

		default:
			break;
		}
		if (!_kbhit())//如果键盘有输入,当连续敲入时,不等待,直接行进至下一格,加速用
		    Sleep(speedSnake);
	}
}
//显示得分  
void Gameprintf(struct HistoryRecord NowRecord1) {

	cout <<NowRecord1.name<< "  得分:" << NowRecord1.score << "  时间:" 
		<< NowRecord1.costtime<<"ms" << endl;
	cout << "你的排名为:" << main_ShowRanking(NowRecord.score)<< endl;
}
//将结果写入文本
void Writetofin(struct HistoryRecord NowRecord1) {
	fstream f;
	f.open("score.txt", ios::out | ios::app);
	f << NowRecord1.name << " " << NowRecord1.score << " " 
		<< NowRecord1.costtime << "" << endl;
	f.close();
}
//游戏部分
void game_snack() {
	system("cls");
	color(13);
	cout << "输入用户名用于记录得分" << endl;
	cin >> Username1;
	cout << "用户 "<<Username1;
	system("pause");
	system("cls");
	color(7);//调色
	srand((unsigned int)time(NULL));//导入随机数种子
	
	initSnake();//初始化蛇
	initfood();//初始化食物
	initCursor();//初始化光标
	Myprintf();//初始化游戏界面
	clock_t start, end;//记录游戏时间
	start = clock();
	controlSnake();//控制,蛇的移动
	end = clock();
	clock_t costtime = end - start;//计时
	NowRecord.costtime = costtime;//将当前得分结构体初始化,方便调用
	strcpy(NowRecord.name, Username1);
	NowRecord.score = grade;
	Writetofin(NowRecord);//写入文件
	Gameprintf(NowRecord);//结束时显示用户名、得分、排名、耗时等
}

  • 4
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值