完整源码
#include <iostream>
#include <algorithm>
#include <string>
#include <cstdlib>
#include <iomanip>
#include <list>
#include <ctime>
#include <windows.h>
//可以使用color(颜色代号修改输出字体颜色)
#define color(A) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),A)
#define W FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE
#define R FOREGROUND_INTENSITY|FOREGROUND_RED
#define G FOREGROUND_INTENSITY|FOREGROUND_GREEN
#define B FOREGROUND_INTENSITY|FOREGROUND_BLUE
#define Y FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN
#define P FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_BLUE
#define Q FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_BLUE
using namespace std;
struct general
{
string name;
int info[4];
};
list<general> l;//数据结构使用STL序列式容器中的list,储存武将结构体
list<general>::iterator it;//遍历list需要使用的迭代器变量
int cc,order4;
void Error()//输入不符合约定调用的报错函数
{
color(R);
cout<<"键入参数错误!!!请检查输入。"<<endl;
}
bool cmp(general x,general y)//武将链表排序调用sort,这是具体排序规则的cmp函数
{
if(cc==1)//cc==1姓名字典序排序
{
if(order4==1)
return x.name<y.name;//升序
else
return x.name>y.name;//降序
}
else
{
if(order4==1)
return x.info[cc-2]<y.info[cc-2];
else
return x.info[cc-2]>y.info[cc-2];
}
}
void Add_general()//初始化函数
{
srand((int)time(0));//随机数种子
color(R);
cout<<"请选择武将信息键入方式:1.自定义输入 2.系统大量随机输入"<<endl;color(W);
int order1;cin>>order1;
if(order1==1)
{
color(R);cout<<"开始自定义键入,依次键入武将姓名、年龄、武力值、智力值、体力值(以#结束):"<<endl;color(W);
while(1)//自定义循环输入
{
general temp1;
cin>>temp1.name;
if(temp1.name=="#")//以'#'结束输入
break;
cin>>temp1.info[0];cin>>temp1.info[1];cin>>temp1.info[2];cin>>temp1.info[3];
l.push_back(temp1);
}
}
else if(order1==2)
{
color(R);cout<<"请输入随机输入规模:"<<endl;color(W);
int num1;cin>>num1;
for(int i=0;i<num1;i++)//随机数输入
{
general p1;
int len1=rand()%10+3;//字符串长度随机3~12
char ch1[len1];
int j;
for(j=0;j<len1;j++)
{
if(rand()%2)//大小写随机
ch1[j]=rand()%26+65;//利用ASCII码由数字转字符
else
ch1[j]=rand()%26+97;
}
ch1[j]='\0';//需手动添加一维字符数组截止符'\0'
p1.name=ch1;//一维字符数组转string型
p1.info[0]=rand()%53+18;p1.info[1]=rand()%201;p1.info[2]=rand()%201;p1.info[3]=rand()%201;//各属性随机输入
l.push_back(p1);
}
color(R);cout<<"随机输入成功!"<<endl;
}
else
Error();
}
void Insert_Delete_general()
{
int order2;
while(1)
{
color(R);
cout<<"请选择:0.退出此项操作 1.插入武将(以#结束) 2.删除武将(姓名索引,以#结束)"<<endl;
color(W);
cin>>order2;
if(order2==0)
return;
else if(order2==1)
{
color(Y);cout<<"开始插入,依次键入武将姓名、年龄、武力值、智力值、体力值(以#结束):"<<endl;color(W);
while(1)
{
general temp2;
cin>>temp2.name;
if(temp2.name=="#")//以'#'结束插入
break;
cin>>temp2.info[0];cin>>temp2.info[1];cin>>temp2.info[2];cin>>temp2.info[3];
l.push_back(temp2);
}
}
else if(order2==2)
{
color(Y);cout<<"开始删除(姓名索引删除,以#结束):"<<endl;
while(1)
{
color(W);
int flag2=1;
string p2;cin>>p2;//姓名索引
if(p2=="#")
break;
for(it=l.begin();it!=l.end();)
{
if((*it).name==p2)
{
it=l.erase(it);
flag2=0;
}
else
it++;
}
if(flag2)
{
color(P);cout<<"武将库中未查找到此武将,删除失败!"<<endl;color(W);
}
else
{
color(P);cout<<"删除成功!"<<endl;color(W);
}
}
}
else
Error();
}
}
void Search_general()
{
color(R);cout<<"请选择查找索引方式:0.退出此次查找 1.姓名 2.年龄 3.武力值 4.智力值 5.体力值"<<endl;
while(1)
{
color(W);
int ff=1,flag3=1;
int order3;cin>>order3;
if(order3==0)
break;
else if(order3==1)
{
string p3;cin>>p3;
for(it=l.begin();it!=l.end();it++)
{
if((*it).name==p3)
{
if(ff)
{
color(G);cout<<"查找成功!武将信息如下:"<<endl;color(Q);
ff=0;
}
flag3=0;
color(P);cout<<left<<setw(12)<<(*it).name;color(Q);cout<<" "<<left<<setw(6)<<(*it).info[0]<<" "<<left<<setw(6)<<(*it).info[1]<<" "<<left<<setw(6)<<(*it).info[2]<<" "<<left<<setw(6)<<(*it).info[3]<<endl;
}
}
if(flag3)
{
color(P);
cout<<"武将库中未查找到此武将,查找失败!"<<endl;
}
}
else if(order3>=2&&order3<=5)
{
int q3;cin>>q3;
for(it=l.begin();it!=l.end();it++)
{
if((*it).info[order3-2]==q3)
{
if(ff)
{
color(G);cout<<"查找成功!武将信息如下:"<<endl;color(Q);
ff=0;
}
flag3=0;
color(P);cout<<left<<setw(12)<<(*it).name;color(Q);cout<<" "<<left<<setw(6)<<(*it).info[0]<<" "<<left<<setw(6)<<(*it).info[1]<<" "<<left<<setw(6)<<(*it).info[2]<<" "<<left<<setw(6)<<(*it).info[3]<<endl;
cout<<endl;
}
}
if(flag3)
{
color(P);
cout<<"武将库中未查找到具有此属性的武将,查找失败!"<<endl;
}
}
else
Error();
}
}
void Print_general()
{
char temp;
int sum=0;
color(R);cout<<"当前武将库信息如下(以#结束查看):"<<endl;
color(G);cout<<"姓名 年龄 武力 智力 体力"<<endl;
for(it=l.begin();it!=l.end();it++)
{
color(P);cout<<left<<setw(12)<<(*it).name;color(Q);cout<<" "<<left<<setw(6)<<(*it).info[0]<<" "<<left<<setw(6)<<(*it).info[1]<<" "<<left<<setw(6)<<(*it).info[2]<<" "<<left<<setw(6)<<(*it).info[3]<<endl;
sum++;
if(sum==50)//每一页输出都只有50组数据
{
sum=0;
system("pause");//输出50组后暂停程序
cin>>temp;
if(temp=='#')//当输入'#'后中止继续输出,其他任意字符继续输出
goto loop3;
system("cls");//清空上一页输出内容
//重新显示输出表头
color(R);cout<<"当前武将库信息如下(以#结束查看):"<<endl;
color(G);cout<<"姓名 年龄 武力 智力 体力"<<endl;
}
}
system("pause");//全部输出完成后暂停程序
loop3:;
}
void Sort_general()
{
color(R);cout<<"请输入排序索引:1.姓名 2.年龄 3.武力值 4.智力值 5.体力值"<<endl;
loop1:
color(W);
cin>>cc;
if(cc<1||cc>5)
{
Error();
goto loop1;
}
color(Y);cout<<"请输入排序方式:1.升序 2.降序"<<endl;
loop2:
color(W);
cin>>order4;
if(order4<1||order4>2)
{
Error();
goto loop2;
}
l.sort(cmp);//调用sort排序
Print_general();color(W);//排序后立即输出武将库数据
}
void Fuzzy_lookup()
{
color(R);cout<<"请选择模糊查找方式:1.姓名模糊查找 2.属性模糊查找"<<endl;color(W);
int gg=1,flag5=1,order5;cin>>order5;
if(order5==1)
{
color(R);cout<<"请输入武将姓名片段:"<<endl;color(W);
string p5;cin>>p5;
string::size_type idx;
for(it=l.begin();it!=l.end();it++)
{
idx=((*it).name).find(p5);//查找输入武将姓名片段是否是武将库某些武将姓名的字串
if(idx != string::npos )//如果是
{
if(flag5)
{
color(Y);cout<<"查找成功,可能武将如下:"<<endl;
color(G);cout<<"姓名 年龄 武力 智力 体力"<<endl;
flag5=0;
}
gg=0;
color(P);cout<<left<<setw(12)<<(*it).name;color(Q);cout<<" "<<left<<setw(6)<<(*it).info[0]<<" "<<left<<setw(6)<<(*it).info[1]<<" "<<left<<setw(6)<<(*it).info[2]<<" "<<left<<setw(6)<<(*it).info[3]<<endl;
}
}
system("pause");
if(gg)
{
color(P);
cout<<"武将库中未查找到姓名相似的武将,查找失败!"<<endl;
}
}
else if(order5==2)
{
color(R);cout<<"请输入查找的武将属性:1.年龄 2.武力值 3.智力值 4.体力值"<<endl;color(W);
int op;cin>>op;
if(op>=1&&op<=5)
{
color(R);cout<<"请输入要查找的区间:"<<endl;color(W);
int k1,k2;cin>>k1>>k2;//查找武将库中是否有属性在此区间的武将
for(it=l.begin();it!=l.end();it++)
{
if((*it).info[op-1]>=min(k1,k2)&&(*it).info[op-1]<=max(k1,k2))//调用max(),min(),输入区间时不用考虑两个范围树输入的先后顺序
{
if(flag5)
{
color(Y);cout<<"查找成功,可能武将如下:"<<endl;
color(G);cout<<"姓名 年龄 武力 智力 体力"<<endl;
flag5=0;
}
gg=0;
color(P);cout<<left<<setw(12)<<(*it).name;color(Q);cout<<" "<<left<<setw(6)<<(*it).info[0]<<" "<<left<<setw(6)<<(*it).info[1]<<" "<<left<<setw(6)<<(*it).info[2]<<" "<<left<<setw(6)<<(*it).info[3]<<endl;
}
}
system("pause");
if(gg)
{
color(P);
cout<<"武将库中未查找到属性在此区间的武将,查找失败!"<<endl;
}
}
else
Error();
}
else
Error();
}
void Animation()//字符动画函数
{
int col=120;
for(int i=0;i<90;i++)
{
system("cls");
cout<<setw(col)<<"***** ******* * *** *******"<<endl;
cout<<setw(col)<<"** * * * * * * "<<endl;
cout<<setw(col)<<" ** * *** *** * "<<endl;
cout<<setw(col)<<" ** * * * * * * "<<endl;
cout<<setw(col)<<"***** * * * * * * "<<endl;
col--;
}
cout << endl;
system("cls");
}
int main()
{
color(G);
for(int i=0;i<3;i++)
{
cout<<endl<<" 三国游戏武将系统正在启动.........";
Sleep(500);
system("cls");
Sleep(500);
}
Animation();
while(1)
{
color(Y);
cout<<"请键入操作:"<<endl;
cout<<" 0.退出游戏"<<endl<<" 1.初始化武将库"<<endl<<" 2.插入或删除武将"<<endl<<" 3.查找武将"<<endl<<" 4.武将排序"<<endl<<" 5.查看武将库"<<endl<<" 6.模糊查找"<<endl<<" 7.清空武将库"<<endl;
color(W);
int order;cin>>order;
switch(order)
{
case 0:color(R);cout<<"游戏结束......"<<endl;color(W);exit(0);//结束程序
case 1:Add_general();break;//武将库初始化
case 2:Insert_Delete_general();break;//插入或删除武将
case 3:Search_general();break;//查找武将
case 4:Sort_general();break;//武将排序
case 5:Print_general();break;//输出武将库信息
case 6:Fuzzy_lookup();break;//武将模糊查找
case 7:l.clear();color(R);cout<<"清空武将库成功!"<<endl;break;//清空武将库
default:Error();//输入报错
}
color(Y);cout<<"单次操作结束";
Sleep(1000);
system("cls");//每次输入过后清屏
}
return 0;
}
实验报告
题1:三国游戏开发
一、 问题描述
编写一组武将的排名程序,按照武力、智力、体力、年龄进行多条件排序,其中武力为主关键字,智力为 次关键字,体力为第三关键字,年龄为第四关键字。武力、智力、体力均按降序排序,年龄按升序排序。 数据值可任意输入,可用Excel来验证排序结果。
具体要求:
(1)从键盘输入若干个武将信息(要求支持不低于1000个),每个武将的信息包括姓名、年龄、武力值、智力值 和体力值。以#作为结束标记;可用结构体数组的形式来存储这一组武将。
(2)提示:可调用 C++标准库函数sort 来实现排序,#include
(3)表格化输出武将的信息,输出不支持中文。
二、 分析设计
数据结构定义:首先采用结构体来储存单个武将的信息,包括姓名、年龄、武力值、智力值、体力值等等,由于元素较多,所以采用结构体较为方便。其次,由于题目需要大量的插入、删除、排序、查找操作,而且数据量也较大,至少要容纳1000个数据。所以我选择STL序列式容器中的list来储存这些代表武将的结构体。这样主要的数据结构框架就确定了。
主体结构设计:
1.数据输入:对于武将信息的输入,我提供了两种选择。一种是由人主动从键盘按顺序一个一个键入武将的信息,以字符‘#’结束输入。这种比较繁琐,只能支持小范围数据的输入。当需要题目所要求的1000个武将以上时,手动键入就很麻烦,所以采用随机数输入。首先键入输入规模,然后声明一个一维字符数组,字符数组的长度由随机数rand()%10+3得到,其次每一个字符rand()%2指定大小写,rand()%26+65为ASII码转大写,rand()%26+97为转小写。
最后将随机得到的字符数组型字符串转为武将姓名的string型字符串。接着其他属性依次赋予随机值rand()%201,范围为0~200。这种随机输入规模达到5000,0000响应速度也挺快。
还有插入时的输入,插入采用了与初始化一样的自定义键入。
2.程序架构(如:循环、多重循环、递归等)
main函数为一个死循环,当输入不为0时一直执行,其中循环内主要结构为switch结构,根据输入的命令来执行不同的语句。为了让程序更有条理,提升可读性,每种不同的操作全由不同的函数来执行。然后只要一个一个实现函数的功能就行了。
3.函数设计
基本操作函数如下:
4.输出,
只需要设计一个Print_general()函数完成武将数据库的输出就行。
在上面也说了就是循环遍历list输出,而且每次只显示50组数据。
函数设计(如果有):
设计的函数为:
均为void型的函数,没有返回值。所具有的功能除了打印输出以外就是对list里的数据进行操作,而且为了优化程序,提高可读性和逻辑性将list以及迭代器指针的声明都放在main()函数之外,这样就是全局变量,无论什么函数都无需这些参数,直接可以操作。
具体实现:
Add_general()函数对武将库初始化。由if else结构来选择自定义键入与随机数规模键入,实现过程在上述数据输入环节已阐述。
Insert_Delete_general()函数进行插入和删除操作。一旦选择了插入则list只要一直push_back()就行了,删除操作则需要使用迭代器指针循环遍历list一直将符合要求的数据删除,并进行操作成功与否的反馈。
Search_general()函数进行武将的查找。根据输入的姓名string型字符串或者某个属性值运用迭代器指针循环遍历list来查找符合要求的数据并将其输出。
Sort_general()函数根据输入的操作命令对数据库的某一属性进行降序或者升序排列,其中具体的排列规则由list调用sort()函数,再由sort()调用cmp()函数实现。最后每次排序完都调用Print_general()函数进行数据库的输出打印。
Print_general()函数打印数据库。利用迭代器指针循环遍历list,将数据全部输出。而且由于要求输出信息的可读性,每一次都只输出50组数据,运用system(“pause”)暂停程序来实现,然后在下一组50个出现之前system(“cls”)进行清屏,增强输出的可读性,而且每次程序暂停时想要据需执行则除了’#’以外随便输入一个字符就行。但是当数据库数据过多时,可以随时键入’#’停止数据库的打印。
Fuzzy_lookup()函数实现模糊查找。分为两种方式。第一种只要输入武将姓名的子串就能将所有符合的数据输出,利用find()函数实现。第二种只要输入属性区间,那么迭代器指针遍历list将所有属性在此区间内的数据输出,而且使用了min()和max()函数,不用考虑输入区间上下限的顺序。
Error()函数当输入错误的不在约定之中的指令则调用此函数输出报错信息,并将程序返回重新输入。
color()是利用#definecolor(A) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),A)
来改变输出字体颜色,具体颜色由后面的A输入来决定。
分别代表白、红、绿、蓝、黄、粉、青。
具体实现代码如下(关键部分):
Add_general()
Insert_Delete_general()
Search_general()
Sort_general()
cmp()
Print_general()
Fuzzy_lookup()
Error()
三、 输入输出测试
1.输入规模测试
随机输入5000,0000组数据:
输入:
输出:不可能将5000,0000组数据全部截图,故只截取了50组。
2.自定义键入
初始化输入
查看武将库
插入武将
查看是否插入成功,查找武将
删除武将,查看是否完全删除干净
武将排序
以姓名字典序降序排列
以武力升序排列
查看武将库
模糊查找
姓名模糊查找
模糊区间查找
清空武将库
四、 分析与总结
本次实验的各种操作实现的难度不是很高,主要是需要的功能比较全面,代码编写行数较多,我编写完成后包括注释有389行。各种功能的实现比较繁琐,对于输出而定排版也比较繁琐,比如后面老师又提出了需要一部分一部分的输出,所以只用一个for循环肯定就是不行了,需要添加一些语句来部分部分地输出。不过幸好在开始编写的时候就已经预见到代码量,所以功能都决定使用函数实现,主函数的功能就只要通过switch选择结构去调用这些功能函数就行了。
下面来叙述我遇到的一些小麻烦以及解决方法。首先在编写初始化武将库的函数时,自定义键入比较简单,但是大规模随机输入过程中武将姓名的字符串随机输入是需要好好思考的。由于我的结构体储存姓名的是string型变量,所以我决定先随机生成一个一维字符数组再将其转换为string型字符串。首先长度利用随机数生成,其次每一个字符需要大小写随机,由于只有大写与小写两种情况,所以采用rand()%2来确定,再使用if else选择结构利用随机生成的数随机ASCII码来随机生成字符。再将生成的整个字符数组赋给string型姓名。但是编写后发现随机生成的字符串前面一部分不同,但有很多后面部分相同,而且长度也相同,第一想法就是随机失败,最后才发现是没有手动赋予字符数组终止符’\0’,导致string型读入字符数组上一次未被覆盖的部分。
还有就是需要注意list删除元素使用erase()时需要it=list.erase(),不然迭代器指针将会成为一个“野指针”。接着在编写排序函数时发现各种属性名称不同,排序也都有两种操作,升序和降序。如果每一个都编写特定的cmp函数的话需要编写10个不同的cmp函数。所以只能将武将结构体的各种属性声明成一个数组,当需要对哪种属性进行排序时直接通过命令输入的值来操作数组下标来完成cmp()的编写,在通过改变一个变量值,在cmp()里面添加一个if else选择结构就可以只用一个cmp函数完成排序操作了。大大减轻了工作量。
最后总结,这次实验需要的编程能力比较全面,有很多很多不同的功能,在平常的程序题当中,很多都只要1、2种功能,而这次实验有非常多功能需要实现。平时的编程积累就可以起到很大作用了,有很多功能我以前实现过,这次只需要将以前记录的相似的复制下来加以改动就行,减少了无谓的重复劳动。