这个项目是作为数据结构与算法分析课程期末项目大作业写的,要求是使用多种所学的算法和结构,所以选题和命令有一些限制。在写代码上也是往所要求的内容靠拢。代码部分分成三个部分,贪吃蛇游戏、管理员系统;排行榜系统三个部分。
管理员系统有账号和密码登录,密码和账号放入分离链接散列表中。游戏的结果:得分、用户名、耗时等能够通过游戏结束后写入文件,也可以通过管理员验证通过后自行输入。之后会通过文件读取的方式从文件中读出数据,放入结构体数组中,使用结构体快排为数组排序。使用二分法为数据显示其排名(当用户结束游戏时,会在已排序好的结构体数组中找出其排名)。
代码总量约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);//结束时显示用户名、得分、排名、耗时等
}