C++ 演讲比赛流程管理系统(四)开始演讲比赛功能的具体实现:成员属性添加和初始化、选手创建、开始比赛流程、抽签、开始比赛、显示晋级选手、保存分数

1、开始演讲比赛

1.1 成员属性添加和初始化

根据上一篇的功能分析,需要在speechManager.h中添加成员属性和初始化成员属性的函数声明,并在speechManager.cpp中写初始化成员属性函数的实现。

实现步骤:

  1. 首先,第一轮比赛有12个人,对应12个编号;第二比赛有6人,对应6个编号;最后有3个人胜出,因此可以准备3个vector容器存放这三组编号。也可以不用3个vector容器,用1个大容器来嵌套3个小容器,通过大容器来访问到3个小容器,可以后续改进
  2. 其次,仅存编号是不够的,在显示选手信息时至少需要展示选手的姓名和得分,而且选手编号和选手的信息是对应的。因此可以使用map容器(对组)来存放选手编号和选手的信息(姓名和得分),由于选手编号不可能重复,采用map容器即可,不需要multimap容器。
  3. 然后,还需要需要一个参数来记录比赛的当前轮数。

以上3步这些都需要添加在流程管理系统中,属于speechManager的成员属性

  1. 接着,还需要初始化以上成员属性,也就是把容器都置空和比赛轮数置1。初始化时,在speechManager.h头文件写初始化的函数声明, void initSpeech();;在peechManager.cpp源文件中写函数的实现。
  2. 最后,在构造函数中调用初始化成员属性函数

代码展示:

  • 在speechManager.h中添加成员属性和初始化成员属性函数 initSpeech()
	//初始化成员属性
	void initSpeech();

	//成员属性
	vector<int> v1;//第一轮比赛的12名选手编号
	vector<int> v2;//第二轮比赛的6名选手编号 第一轮晋级6名选手编号
	vector<int> vfinal;//最后胜出的3名选手编号
	map<int, Speaker> m_Speaker;//存放选手编号和选手信息(姓名 分数)
	int m_index;//记录当前比赛轮数
  • 在speechManager.cpp中实现void initSpeech();
//构造函数
SpeechManager::SpeechManager()
{
	this->initSpeech();
}

//初始化成员属性
void SpeechManager::initSpeech()
{
	//容器置空 比赛轮数置1
	this->v1.clear();
	this->v2.clear();
	this->vfinal.clear();

	//初始化比赛轮数
	this->m_index = 1;
}

1.2 选手的具体创建

上面做好了选手类创建以及选手信息的存放等准备工作之后,接下来实现12名选手的具体创建,包括选手编号、姓名、初试分数等。

实现步骤:

  1. 在speechManager.h中提供开始比赛的的成员函数 void createSpeaker();
  2. 在speechManager.cpp中实现void createSpeaker();
  3. SpeechManager类的构造函数中调用void createSpeaker();
  4. 基于SCL的演讲比赛流程管理系统.cpp中的main函数中测试,在创建完管理对象后,测试12名选手是否初始化

代码展示:

  • speechManager.cpp
//构造函数
SpeechManager::SpeechManager()
{
	this->initSpeech();	//成员属性初始化

	this->createSpeaker();	//创建12名选手
}

//12名选手的具体创建
void SpeechManager::createSpeaker()
{
	string nameseed = "ABCDEFGHIJKL";
	for (int i = 0; i < nameseed.size(); i++)
	{
		string name = "选手";
		name += nameseed[i];

		//具体选手信息初始化
		Speaker speaker;
		speaker.m_Name = name;
		for (int j = 0; j < 2; j++)
		{
			speaker.m_Score[j] = 0;
		}

		//创建选手编号 v1存放第一轮选手信息
		this->v1.push_back(10001 + i);

		this->m_Speaker.insert(make_pair(10001 + i, speaker));
	}
}

注意:
在初始化选手的分数时,注意m_Score是一个数组,用for循环实现每个选手的两个分数都置0;
speaker是一个map容器,选手编号和选手信息放入这个map中,key是编号10001+i,value是具体选手

  • 基于SCL的演讲比赛流程管理系统.cpp
	SpeechManager sm;
	//测试12名选手的初始化状态
	for (map<int, Speaker>::iterator it = sm.m_Speaker.begin(); it != sm.m_Speaker.end(); it++)
	{
		cout << "选手编号:" << it->first << " 选手姓名:" << it->second.m_Name 
			<< " 选手第一轮分数:" << it->second.m_Score[0] << endl;
	}

测试效果:
在这里插入图片描述

1.3 开始比赛流程 成员函数添加

实现步骤:

  • 在speechManager.h中提供开始比赛的的成员函数 void startSpeech();
  • 该函数功能是主要控制比赛的流程
  • 在speechManager.cpp中将startSpeech的先写入空实现,可以先将整个比赛的流程伪代码写到函数中,把框架搭起来
//开始比赛 整个比赛流程的控制函数
void SpeechManager::startSpeech()
{
	//第一轮比赛开始

	//抽签

	//比赛
	
	//显示晋级结果

	//第二轮比赛开始

	//抽签

	//比赛

	//显示晋级结果

	//保存分数
}

1.3 选手抽签 成员函数添加

实现步骤:

  • 在speechManager.h中提供开始比赛的的成员函数 void speechDraw();
  • 该函数功能是实现两轮比赛前选手的比赛顺序抽签
  • 在speechManager.cpp中写speechDraw的具体实现
  • 抽签就是对选手的编号顺序打乱,主要用到random_shuffle这个函数,然后再分组显示选手的抽签顺序
  • 要对两轮比赛的选手都进行抽签,用m_index 这个成员属性来控制第几轮比赛的选手进行抽签
  • 最后在void SpeechManager::startSpeech()中加上选手抽签成员函数,并在基于SCL的演讲比赛流程管理系统.cpp中测试

代码展示:

//开始比赛 整个比赛流程的控制函数
void SpeechManager::startSpeech()
{
	//第一轮比赛开始
	//抽签
	this->speechDraw();
	//比赛

	//显示晋级结果

	//第二轮比赛开始

	//抽签

	//比赛

	//显示晋级结果

	//保存分数
}

//抽签
void SpeechManager::speechDraw()
{
	//第一轮

	cout << "第 " << this->m_index << " 轮比赛选手正在抽签" << endl;
	cout << string(40, '-') << endl;
	cout << "抽签后第一轮演讲比赛选手比赛顺序:" << endl;
	if (this->m_index == 1)
	{
		//第一轮比赛抽签 v1容器元素打乱 第一轮选手编号
		random_shuffle(v1.begin(), v1.end());
		//显示打乱之后的选手编号
		int n = int(v1.size()) + 1;
		int half = n / 2;
		cout << "第一组选手:" << endl;
		vector<int>::iterator it = v1.begin();
		while (n-- && n > half)
		{
			cout << *it << "  ";
			it++;
		}
		cout << endl << endl;
		cout << "第二组选手:" << endl;
		while (n--)
		{
			cout << *it << "  ";
			it++;
		}
		cout << endl << string(40, '-') << endl;
		system("pause");
		cout << endl;
	}
	else
	{
		//第二轮比赛抽签 v2容器元素打乱 第二轮选手编号
		random_shuffle(v2.begin(), v2.end());
		//显示打乱之后的选手编号 这6人直接一组
		for (vector<int>::iterator it = v2.begin(); it != v2.end(); it++)
		{
			cout << *it << "  ";
		}
		cout << endl;
		cout << endl << string(40, '-') << endl;
		system("pause");
		cout << endl;
	}
}

在这里插入图片描述

1.4 开始比赛 成员函数添加

实现步骤:

  • 在speechManager.h中提供比赛的的成员函数 void speechContest();
  • 在speechManager.cpp中实现成员函数 void speechContest();
  • 最后在void SpeechManager::startSpeech()中加上开始比赛成员函数,并在基于SCL的演讲比赛流程管理系统.cpp中测试

代码展示:

void SpeechManager::speechContest()
{
	cout << string(15, '*') << " 第 " << this->m_index << " 轮比赛正式开始 " << string(15, '*') << endl;

	int num = 0;//统计当前参赛选手的个数
	//保存当前组选手成绩 后续打印和决出晋级和胜出选手 key是成绩 value是编号 key降序排序
	multimap<double, int, greater<double>> mspeech;

	//第几轮比赛 获取当前参赛选手  选手容器获取v1还是v2
	vector<int>vspeech;
	if (this->m_index == 1)
	{
		vspeech = v1;
	}
	else
	{
		vspeech = v2;
	}
	//比赛 给每个选手打分
	for (vector<int>::iterator it = vspeech.begin(); it != vspeech.end(); it++)
	{
		num++;
		deque<double> d;//存10个分数
		for (int i = 0; i < 10; i++)
		{
			double score = (rand() % 401 + 600) / 10.f;//打分 随机出小数
			//cout << score << "  ";//测试
			d.push_back(score);//1个选手的所有得分
		}
		//cout << endl;//测试 换行

		//计算当前选手的总分和平均分 成绩降序排序  谓词头文件#include<functional>
		sort(d.begin(), d.end(), greater<double>());
		d.pop_back();//去掉最低分
		d.pop_front();//去掉最高分
		double sum = accumulate(d.begin(), d.end(), 0.0f);
		double avg = sum / (double)d.size();
		平均分测试
		//cout << "编号:" << *it << "  姓名:" << this->m_Speaker[*it].m_Name << "  成绩:" << avg << endl;

		//存当前选手的成绩  *it选手编号 this->m_index-1 第一轮比赛就是第一个分数 第二轮比赛就是第二个分数
		this->m_Speaker[*it].m_Score[this->m_index-1] = avg;

		//保存当前选手分数 key是成绩 value是编号 key降序排序
		mspeech.insert(make_pair(avg, *it));

		//每一组输出 分数打印 选出晋级选手和胜出选手 
		if (num % 6 == 0)//每6名选手一组 输出一组选手成绩  num / 6 表示第几组
		{
			cout << "第 " << num / 6 << " 组选手" << endl;
			for (multimap<double, int, greater<double>>::iterator it = mspeech.begin(); it != mspeech.end(); it++)
			{
				cout << "编号:" << it->second	<< "  姓名:" << this->m_Speaker[it->second].m_Name << "  成绩:" << it->first << endl;
			}
			//前三名 
			int count = 0;
			for (multimap<double, int, greater<double>>::iterator it = mspeech.begin(); it != mspeech.end() && count < 3; it++, count++)
			{
				if (this->m_index == 1)//第一轮比赛
				{
					v2.push_back(it->second);//晋级选手
				}
				else//第二轮比赛
				{
					vfinal.push_back(it->second);
				}
			}
			mspeech.clear();//一组处理完清空 否则下一组处理时会保留上一组信息
		}
	}

	//比赛结束
	cout << string(15, '*') << " 第 " << this->m_index << " 轮比赛结束     " << string(15, '*') << endl;
	cout << endl;
	system("pause"); 
	cout << endl;
}

//开始比赛 整个比赛流程的控制函数
void SpeechManager::startSpeech()
{
	//第一轮比赛开始

	//抽签
	this->speechDraw();
	//比赛
	this->speechContest();
	//显示晋级结果

	//第二轮比赛开始
	this->m_index++;
	//抽签
	speechDraw();
	//比赛
	this->speechContest();
	//显示晋级结果
	
	//保存分数
}

说明: 代码主要分为两大块:一个是计算最终成绩,另一个是成绩的保存和打印,以及选出晋级选手

一、计算最终成绩

  1. 首先提示用户,当前是第几轮比赛,并且用m_index来控制比赛轮数。要注意的是,m_index初始化为1,也就是第一轮比赛。第一轮比赛结束之后,m_index要+1,表示进行第二轮比赛。
  2. 由于抽签在比赛之前完成了,接下来只要取出抽签后的选手参加即可。由于两轮比赛的选手不一样,通过m_index来筛选当前参赛选手,并且用一个vector临时容器v_sc来存放当前的参赛选手编号,具体如下:
  • 当第一轮比赛,m_index=1时,取出v1容器中第一轮参赛选手编号,并v_sc=v1;
  • 当第二轮比赛,m_index=2时,取出v2容器中第二轮参赛选手编号,并v_sc=v2。
  1. 开始比赛后,通过遍历v_sc,逐个给每个选手打分,每个选手都有10个分数。为了计算最终成绩,每个选手的所有成绩都要临时保存,采用deque来存放选手的10个评委分,具体如下:
  • 遍历v_sc后,创建deque容器,通过for循环、rand()产生10个随机分数并逐个通过push_back存入deque,这里可以把分数输出做一个测试;
  • 要注意的是,为了产生带小数的分数,可以生成600-1000的整数再除以10,此时分数是double浮点型的;
  • 这里用deque容器是为了在降序排序后,方便两头操作,去掉最高最低分。
  1. 为了在显示成绩时直观地看到前三名,对分数进行降序排序,使用sort算法和谓词greater,然后通过pop_front和pop_back去掉最高、最低分。
  2. 使用accumalate计算当前选手的分数总和,计算平均分得到选手的最终成绩。要注意的是,计算平均分统计的是deque的元素个数,并为了保证数据统一,把deque.size()强转为double类型。

二、成绩的保存和打印,以及选出晋级选手

  1. 每个选手的最终成绩计算出来之后,需要保存在map容器m_Speaker中,key是int,编号;value是Speaker。
  2. 分组打印成绩,并且取出每组的前三名选手晋级,比赛轮数不同有不一样的处理,具体如下:
  • 第一轮比赛有两组,每组六人。按照成绩降序打印第一组的成绩。另外,取出前三名的选手晋级第二轮比赛,选手编号存放在v2容器中,第二组选手也做同样的处理;
  • 第二轮比赛只有六人,直接按照成绩降序打印成绩,并且取出前三名做为获胜选手,选手编号存放在vfinal容器中。
  1. 每组只有6人,因此可以通过v_sc的元素个数对6取模来控制成绩的打印、选手的晋级和胜出。
  2. 在遍历选手前定义一个统计数num,每遍历一个选手就num+1。这样可以保证第一轮比赛时,num是12,第一次对6取模就是第一组,第二次取模就是第二组;第二轮时,num是6。
  3. 打印分数时,当比赛是第一轮时,每一组(每组六名选手)的成绩都要显示,并且按照成绩降序显示成绩和信息。
  • 由于是分组打印选手成绩,因此用multimap容器m_sc来存放当前组的选手成绩和编号,key对应选手的成绩,value对应选手的编号。
  • 用multimap而不用map,是因为每个选手的编号和成绩是一组数据,而且有可能选手的成绩会相同,注意multimap的key值可以重复。
  • 为了后期成绩打印方便,成绩使用降序排序,m_sc加入谓词,谓词类型的double。
  • 保存分数时,通过选手编号索引到选手成绩的存放位置。注意,无论是第一轮还是第二轮比赛,m_sc都只有六祖成绩和编号。分别是第一轮第一组,六祖成绩;第一轮第二组,六祖成绩;第二轮比赛,六组成绩和编号。
  • 要注意的是,第一组取走前三名选手编号,通过一个统计数count来控制,获取前三名选手的编号。
  1. 一组选手比赛完之后,m_sc要清空,存放下一组选手的成绩,否则还会保留上一组的选手成绩。
  2. 最后提示用户比赛结束。
    在这里插入图片描述
    效果: 和下面的功能一起展示

1.5 显示比赛成绩 成员函数添加

实现步骤:

  • 在speechManager.h中提供比赛的的成员函数 void showScore();
  • 在speechManager.cpp中实现成员函数 void showScore();
  • 最后在void SpeechManager::startSpeech()中加上开始比赛成员函数,并在基于SCL的演讲比赛流程管理系统.cpp中测试

代码展示:

//显示比赛成绩
void SpeechManager::showScore()
{
	cout << endl << string(15, '*') << " 第 " << this->m_index << " 轮比赛晋级选手如下 " << string(15, '*') << endl;

	vector<int> vspeech;

	if (this->m_index == 1)
	{
		vspeech = v2;
	}
	else
	{
		vspeech = vfinal;
	}
	for (vector<int>::iterator it = vspeech.begin(); it != vspeech.end(); it++)
	{
		cout << "选手编号:" << *it << "  选手姓名:" << this->m_Speaker[*it].m_Name 
			 << "  选手成绩:" << this->m_Speaker[*it].m_Score[this->m_index - 1] << endl;
	}
	cout << endl;
	system("pause");
	system("cls");
	this->showMenu();
}

//开始比赛 整个比赛流程的控制函数
void SpeechManager::startSpeech()
{
	//第一轮比赛开始

	//抽签
	this->speechDraw();
	//比赛
	this->speechContest();
	//显示晋级结果
	this->showScore();
	//第二轮比赛开始
	this->m_index++;
	//抽签
	speechDraw();
	//比赛
	this->speechContest();
	//显示晋级结果
	this->showScore();
	//保存分数
}

注意点:
通过m_index来控制第几轮比赛的成绩的输出,根据不同的容器和比赛的轮数,做不同的输出,具体如下:

  • 如果m_index=1,第一轮比赛,获取第一轮比赛晋级的选手,在v2中;
  • 如果m_index=2,第二轮比赛,获取第二轮比赛晋级的选手,最终胜出选手,在vfinal中;
  • 选手成绩都存放在m_Speaker中,通过选手编号索引到选手的成绩;
  • 最后清屏并返回菜单界面。

效果:
在这里插入图片描述
在这里插入图片描述

1.6 保存分数函数添加

功能描述: 将每次演讲比赛的得分记录到文件中

实现步骤:

  • 在speechManager.h中添加保存记录的成员函数 void saveRecord();
  • 在speechManager.cpp中实现成员函数 void saveRecord();

代码展示:

//保存比赛记录
void SpeechManager::saveRecord()
{
	ofstream ofs;
	ofs.open("speech.csv", ios::out | ios::app);//追加的方式写文件

	//将每个选手都写进文件中
	for (vector<int>::iterator it = vfinal.begin(); it != vfinal.end(); it++)
	{
		//csv文件用逗号分割 m_Score[1]最终得分
		ofs << *it << "," << this->m_Speaker[*it].m_Score[1] << ",";
	}
	//关闭
	ofs.close();
	cout << "当届比赛记录已保存!" << endl;
}

//开始比赛 整个比赛流程的控制函数
void SpeechManager::startSpeech()
{
	//第一轮比赛开始

	//抽签
	this->speechDraw();
	//比赛
	this->speechContest();
	//显示晋级结果
	this->showScore();
	//第二轮比赛开始
	this->m_index++;
	//抽签
	speechDraw();
	//比赛
	this->speechContest();
	//显示晋级结果
	this->showScore();
	//保存分数
	this->saveRecord();

	cout << "本届比赛正式结束!" << endl;
	system("pause");
	system("cls");
	cout << endl;
}

注意点:

  • 用csv文件保存,csv文件用逗号分隔,每一届成绩保存完之后要换行
  • 文件内容保存时,用ios::app这个方式追加保存
  • 最后提示用户文件保存完毕

效果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值