一、需求:俺是歌手挑战赛比赛规则如下
- 目前共计12名选手报名参赛,选手编号为1-12号
- 比赛分为A和B两组,每组6人,选手随机抽签进行分组
- 比赛共两轮赛事,第一轮为淘汰赛,第二轮为决赛,淘汰赛中每组前三名晋级下一轮,决赛按最终得分诞生冠亚季军
- 十个评委进行打分,最终得分为去掉一个最高分和一个最低分之后的平均分
- 每轮赛事结束后都要显示晋级的选手信息及其得分
- 将每赛季的冠亚季军的最终得分信息进行保存

二、大致思路
1,创建管理类和选手类
 管理类:由于实现比赛的具体流程,成员属性为所有参赛选手容器、第一轮晋级的选手容器、进入决胜局的选手容器、选手编号和具体对应的选手容器(用于根据选手编号来找到具体的选手信息)
 选手类:用于创建选手,成员属性为姓名和成绩,其中成绩为数组存放比赛的成绩
 2,开始比赛
 第一轮比赛:抽签、比赛、显示晋级结果
 第二轮比赛:抽签、比赛、显示最终结果、保存冠亚季军信息(选手编号和最终得分)
 3,查看记录
 读取记录、查看记录
 4,清空记录

 看完这张图大概就知道该项目的框架了
 主函数中通过switch case语句共四个功能:开始比赛、查看记录、清空记录、退出系统
 项目共涉及到两个类,选手类(Player)和管理类(manager)
 管理类中核心是开始比赛函数(startCompetition()),其负责调用各个模块函数进行拼接实现
三、具体步骤实现
1,新建C++空项目

2,创建管理类
管理类主要负责界面的交互和比赛的全部流程
 创建管理类:Manager.h和Manager.cpp
①目前项目大致结构

②Manager.h
构造函数、析构函数、界面显示用户交互函数show_Menu()的定义
#pragma once
class Manager 
{
public:
	Manager();//构造函数声明,并需要在类外进行实现,空实现就行
	void show_Menu();//用于用户的页面交互显示函数
	//后续添加函数均由此处开始添加
	
	~Manager();//析构函数,并需要在类外进行实现,空实现就行
};
③Manager.cpp
界面显示用户交互函数show_Menu()的实现
#include "Manager.h"
#include <iostream>
void Manager::show_Menu() 
{
	std::cout << "********************************************" << std::endl;
	std::cout << "*************  欢迎参加按时歌手挑战赛 ************" << std::endl;
	std::cout << "*************  1.开始吼歌比赛  *************" << std::endl;
	std::cout << "*************  2.查看往届记录  *************" << std::endl;
	std::cout << "*************  3.清空比赛记录  *************" << std::endl;
	std::cout << "*************  0.退出比赛程序  *************" << std::endl;
	std::cout << "********************************************" << std::endl;
	std::cout << std::endl;
}
//后续对应头文件函数的实现均有此处进行添加
Manager::Manager() //构造函数空实现
{
}
Manager::~Manager() //析构函数空实现
{
}
④main.cpp
调用Manager管理类的界面显示用户交互函数show_Menu()
#include "Manager.h"
#include <iostream>
int main(int argc, char* agrv[])
{
	Manager manager;//所有的功能都放在Manager类中
	manager.show_Menu();
	
	system("pause");
	
	return 0;
}
⑤运行效果

3,比赛系统框架搭建
①Manager.h
新添加退出系统函数exit_System()的声明
	void exit_System();//退出系统
②Manager.cpp
退出系统函数exit_System()的实现
void Manager::exit_System() 
{
	std::cout << "Welcome to next use! Bye~" << std::endl;
	system("pause");
	exit(0); //退出系统
}
③main.cpp
通过swith case获取用户的输入,进入对应选择的功能
 将退出系统函数exit_System()放在case0分支下
 调用Manager管理类的退出系统函数exit_System()
#include "Manager.h"
#include <iostream>
int main(int argc, char* agrv[])
{
	Manager manager;
	int selection = 0;//接受用户的选择
	while (true) 
	{
		manager.show_Menu(); //显示菜单
		std::cout << "Please input your selection:" << std::endl;
		std::cin >> selection;
		switch (selection)
		{
		case 1: //开始比赛
			break;
		case 2: //查看记录
			break;
		case 3: //清空记录
			break;
		case 0: // 退出系统
			manager.exit_System();//调用管理类中的退出系统函数
			break;
		default: //按下其他键
			system("cls"); //清屏
			break;
		}
	}
	system("pause");
	return 0;
}
④运行效果

4,创建选手类
①目前项目大致结构
这里不需要实现,只需一个类就行,故定义选手类Player
 
②Player.h
选手类中成员属性为姓名和每轮比赛的得分,成员方法为对应的set和get方法
 选手信息包括选手姓名和每轮比赛的最终得分(共两轮比赛)
#pragma once
#include <iostream>
#include <string>
class Player
{
public:
	std::string getName() //拿到选手姓名
	{
		return this->name_;
	}
	double* getScores() //拿到最终得分
	{
		return this->scores_;
	}
	void setName(std::string name) //设置选手姓名
	{
		this->name_ = name;
	}
	void setScores(double arr[]) //给选手打分
	{
		this->scores_[0] = arr[0];
		this->scores_[1] = arr[1];
	}
private:
	std::string name_; //选手姓名
	double scores_[2]; //两轮成绩
};
5,管理类中添加成员属性和成员方法
①Manager.h
添加管理类的成员属性:进入淘汰赛、半决赛、总决赛比赛的选手容器、比赛回合、选手编号和选手信息绑定容器
 添加管理类的成员方法:比赛初始化函数initCompetition()的声明
	std::vector<int> v_knockout;  //存放第一轮淘汰比赛的选手的id编号 12人
	std::vector<int> v_semifinal; //存放进入第二轮半决赛的选手的id编号 6人
	std::vector<int> v_finals;    //存放进入第三轮总决赛的选手的id编号 3人
	std::map<int, Player> id_player; //选手编号和选手信息绑定
	int round_; //比赛轮数
	void initCompetition(); //比赛初始化
①Manager.cpp
管理类的成员方法:比赛初始化函数initCompetition()的实现,并在构造函数中进行调用比赛初始化函数
void Manager::initCompetition() // 比赛初始化函数
{
	this->v_knockout.clear();//第一轮淘汰比赛的选手的id编号清空
	this->v_semifinal.clear();//进入第二轮半决赛的选手的id编号清空
	this->v_finals.clear();//存放进入第三轮总决赛的选手的id编号清空
	this->id_player.clear();//编号id和选手信息绑定容器清空
	this->round_ = 1;//第一回合
}
6,管理类中添加成员方法——添加选手
①Manager.h
创建选手函数createPlayer()声明
	void createPlayer(); //创建比赛选手
②Manager.cpp
创建选手函数createPlayer()实现
 选手姓名为A-L共十二位,成绩初始化都为0,将编号id和选手信息进行绑定,并将编号id(1-12)放入淘汰比赛的选手的id编号容器中(v_knockout),在构造函数中进行调用创建选手函数
void Manager::createPlayer() //创建比赛选手
{
	std::string nameList = "ABCDEFGHIJKL"; //十二个选手name分别为A、B...
	for (int i = 0; i < nameList.size(); i++) 
	{
		std::string name = "Player";
		name += nameList[i];
		Player player;
		player.setName(name); //初始化设置参赛选手的姓名
		for (int j = 0; j < 2; j++) 
		{
			double arr[2] = { 0,0 }; //初始化设置参赛选手的每轮比赛得分
			player.setScores(arr);
		}
		this->v_knockout.push_back(i+1);//v_knockout为淘汰赛参加的12名选手,给选手编号赋值1-12
		
		//每个map中存放key为id,value为选手信息
		this->id_player.insert(std::make_pair(i+1, player));//编号分别为1,2,...,11,12
	}
}
③main.cpp
调用测试函数show_id_palyer_info(),用于显示id和player信息绑定是否成功
 测试完之后没问题即可注释掉show_id_palyer_info(manager.id_player);
#include "Manager.h"
#include <iostream>
#include<iomanip>
void show_id_palyer_info(std::map<int, Player> id_palyer) //显示id和player信息绑定是否成功
{
	//std::setw(2) << std::setfill('0')为了将个位数的id补为两位,比如01,02,03...
	for (std::map<int, Player>::iterator it = id_palyer.begin(); it != id_palyer.end(); it++)
	{
		std::cout << "Player id is: " <<std::setw(2) << std::setfill('0') << (*it).first
			<< " ,name is: " << it->second.getName()
			<< " ,round 1 scores is: " << it->second.getScores()[0]
			<< " ,round 2 scores is: " << it->second.getScores()[1]
			<< std::endl;
	}
}
int main(int argc, char* agrv[])
{
	Manager manager;
	int selection = 0;//接受用户的选择
	//测试一下id和player信息绑定是否成功
	show_id_palyer_info(manager.id_player);
	while (true) 
	{
		manager.show_Menu(); //显示菜单
		
		std::cout << "Please input your selection:" << std::endl;
		std::cin >> selection;
		switch (selection)
		{
		case 1: //开始比赛
			break;
		case 2: //查看记录
			break;
		case 3: //清空记录
			break;
		case 0: // 退出系统
			manager.exit_System();//调用管理类中的退出系统函数
			break;
		default: //按下其他键
			system("cls"); //清屏
			break;
		}
	}
	system("pause");
	return 0;
}
④运行效果

7,管理类中添加流程控制函数——startCompetition()
startCompetition()开始比赛这个成员方法流程:
 第一轮比赛:抽签、比赛、显示晋级结果
 第二轮比赛:抽签、比赛、显示最终结果、保存分数
 因为第二轮比赛完了之后就全部结束了
 每个过程都用一个独立的函数进行实现
Ⅰ抽签功能
这一步完成开始比赛startCompetition()中的比赛抽签功能competitionDraw()
 比赛抽签思路:第一回合(淘汰赛)12名参赛选手比赛id编号依次为1-12,先随机抽取,然后前6个为A组,后6位选手为B组进行抽签
①Manager.h
开始比赛成员方法startCompetition()的声明
	void startCompetition();//开始比赛
	void competitionDraw(); //比赛抽签
②Manager.cpp
startCompetition()开始比赛成员方法具体实现,这里列了下框架结构,具体的每个步骤均由每个函数进行实现
void Manager::startCompetition() //开始比赛成员方法实现
{
	//第一轮比赛
	//1、抽签
	this->competitionDraw();
	//2、比赛
	//3、显示晋级结果
	//第二轮比赛
	//1、抽签
	//2、比赛
	//3、显示最终结果
	//4、保存分数
}
void Manager::competitionDraw() 
{
	std::cout << "Rounds :" << this->round_ << " ,player is drawing..." << std::endl;
	std::cout << "The order of the draw is as follows: " << std::endl;
	if (this->round_ == 1) // 第一轮淘汰赛,参赛12人,分两组,每组前三晋级
	{
		std::random_shuffle(v_knockout.begin(), v_knockout.end()); //第一轮比赛的参赛选手容器为v_knockout
		
		std::vector<int>::iterator it_a, it_m, it_b;
		it_a = v_knockout.begin();
		it_m = v_knockout.begin();
		it_b = v_knockout.end();
		int m = v_knockout.size()/2; //6
		
		for (it_a = v_knockout.begin(); it_a != v_knockout.end(); it_a++)
		{
			std::cout << (*it_a) << " ";
		}
		std::cout << std::endl;
		std::cout << "-----------------------------" << std::endl;
		while (m--) 
		{
			it_m++;
		}
		std::cout << "A group is: " << std::endl;
		for (it_a = v_knockout.begin(); it_a != it_m; it_a++) 
		{
			std::cout << (*it_a) << " ";
		}
		std::cout << std::endl;
		std::cout << "B group is: " << std::endl;
		for (it_b = it_m; it_b != v_knockout.end(); it_b++)
		{
			std::cout << (*it_b) << " ";
		}
		std::cout << std::endl;
		
	}
	else // 第二轮半决赛,参赛6人,前三晋级
	{
		std::random_shuffle(v_semifinal.begin(), v_semifinal.end());//目前半决赛还没有加入选手,因为淘汰赛还没有开始进行
		for (std::vector<int>::iterator it = v_semifinal.begin(); it != v_semifinal.end(); it++) 
		{
			std::cout<<"(*it)"<<std::endl;
		}
		std::cout << std::endl;
	}
	system("pause");
	std::cout << std::endl;
}
③main.cpp
开始比赛成员方法startCompetition()放在主函数中的case1分支下
#include "Manager.h"
#include <iostream>
#include<iomanip>
void show_id_palyer_info(std::map<int, Player> id_palyer) //显示id和player信息绑定是否成功
{
	//std::setw(2) << std::setfill('0')为了将个位数的id补为两位,比如01,02,03...
	for (std::map<int, Player>::iterator it = id_palyer.begin(); it != id_palyer.end(); it++)
	{
		std::cout << "Player id is: " <<std::setw(2) << std::setfill('0') << (*it).first
			<< " ,name is: " << it->second.getName()
			<< " ,round 1 scores is: " << it->second.getScores()[0]
			<< " ,round 2 scores is: " << it->second.getScores()[1]
			<< std::endl;
	}
}
int main(int argc, char* agrv[])
{
	Manager manager;
	int selection = 0;//接受用户的选择
	//测试一下id和player信息绑定是否成功
	//show_id_palyer_info(manager.id_player);
	while (true) 
	{
		manager.show_Menu(); //显示菜单
		
		std::cout << "Please input your selection:" << std::endl;
		std::cin >> selection;
		switch (selection)
		{
		case 1: //开始比赛
			manager.startCompetition(); //调用开始比赛成员方法
			break;
		case 2: //查看记录
			break;
		case 3: //清空记录
			break;
		case 0: // 退出系统
			manager.exit_System();//调用管理类中的退出系统函数
			break;
		default: //按下其他键
			system("cls"); //清屏
			break;
		}
	}
	system("pause");
	return 0;
}
④运行效果

Ⅱ比赛功能
上一步完成了选手比赛抽签功能competitionDraw()
接下来开始实现比赛功能competitionContest()
 比赛主要是十位评委对选手进行打分,去掉一个最低分和最高分求解平均分为选项最终成绩
 可以参考博文:STL经典案例(一)——评委打分
①Manager.h
开始比赛函数competitionContest()声明
	void competitionContest(); //开始比赛
②Manager.cpp
比赛开始函数competitionContest()实现
void Manager::competitionContest() //开始比赛函数实现
{
	std::cout << "The round is: " << this->round_ << " starting----------------------" << std::endl;
	//存放临时容器,存放分组后的每个小组成员的成绩,double为每个人的最终得分,int为选手的id编号
	std::multimap<double, int, std::greater<int>>groupScore;//内建函数std::greater<int>,从大到小排序
	int groupnumber = 0;
	std::vector <int>v_competitor;//实际的参赛选手容器
	if (this->round_ == 1)//淘汰赛每组的前三名晋级 
	{
		v_competitor = v_knockout;//淘汰赛
	}
	else 
	{
		v_competitor = v_semifinal;//半决赛
	}
	for (std::vector<int>::iterator it = v_competitor.begin(); it != v_competitor.end(); it++) //遍历选手,评委打分,这里的it为选手的id编号
	{
		groupnumber++;//小组成员自增
		//使用双端数组deque接收十位评委打分,到时候排完序直接可以去除最高分和最低分
		std::deque<double> scores;
		for (int i = 0; i < 10; i++) 
		{
			double score = (rand() % 401 + 600) / 10.f;//rand() % 401 == [0,401)
			scores.push_back(score); 
			//std::cout << score << " ";//查看十位评委给选手的具体打分情况
		}
		//std::cout << std::endl;
		sort(scores.begin(), scores.end(), std::greater<double>());//降序排列,内建函数greater从大到小排序
		scores.pop_front();//去除最高分
		scores.pop_back();//去除最低分
		double score_sum = std::accumulate(scores.begin(), scores.end(), 0.0f);//容器内所有的值进行累加
		double score_avg = score_sum / (double)scores.size();//平均分
		//测试输出一下平均分
		/*
		std::cout << "Player id is: " << *it
			<< " ,name is: " << this->id_player[*it].getName()
			<< " ,aver is: " << score_avg;
        */
		//把平均分放入到map容器id_player中,按id编号得到选手信息,然后再放入到选手信息中的得分数组里即可
		double arr[2] = {0};
		int i = this->round_ - 1;
		arr[i] = score_avg;
		this->id_player[*it].setScores(arr);
		//将每个选手的最终得分存放到临时小组容器中
		groupScore.insert(std::make_pair(score_avg, *it));//key为最终成绩,value为选手的id编号
		//每个小组取出最终得分前三名
		if (groupnumber % 6 == 0)
		{
			if (groupnumber == 6 && this->round_==1)std::cout << "Group A Ranking is :" << std::endl;
			if (groupnumber == 12 && this->round_ == 1)std::cout << "Group B Ranking is :" << std::endl;
			if (groupnumber == 6 && this->round_ == 2)std::cout << "Outstanding Group Ranking is :" << std::endl;
			for (std::multimap<double, int, std::greater<double>>::iterator it = groupScore.begin(); it != groupScore.end(); it++) 
			{
				std::cout << "Player id is: " << it->second
					<< " ,name is: " << this->id_player[it->second].getName()
					<< " ,score is: " << this->id_player[it->second].getScores()[this->round_ - 1]
					<< std::endl;
			}
			//获取每组的前三名,晋级
			int count = 0;
			for (std::multimap<double, int, std::greater<double>>::iterator it = groupScore.begin(); it != groupScore.end(); it++)
			{
				if (count < 3) //前三名晋级
				{
					count++;
					if (this->round_ == 1)//淘汰赛晋级半决赛的选手id编号
					{
						v_semifinal.push_back((*it).second);//key为最终成绩,value为选手id编号
					}
					else //半决赛晋级决赛的选手id编号
					{
						v_finals.push_back((*it).second);//key为最终成绩,value为选手id编号
					}
				}
			}
			groupScore.clear();//小组容器清空,避免后续的追加
			std::cout << std::endl;
		}
	}
	//std::cout << std::endl;
	std::cout << "-----------------Round " << this->round_ << " is over!--------------" << std::endl;
	system("pause");
}
③运行效果

Ⅲ显示比赛分数功能
上一步完成了比赛功能competitionContest()
 已经将十二名选手进行分组,并且每组六人,也都按最终成绩进行了排序
接下来将这每组的最终得分最高的前三名选手进行晋级并显示晋级选手的最终得分
 显示比赛分数函数showScore()
①Manage.h
显示比赛分数函数showScore()声明
	void showScore(); //显示比赛结果
②Manage.cpp
显示比赛分数函数showScore()实现
void Manager::showScore() //显示得分函数
{
	std::cout << "Round " << this->round_ << " Promoted Players is: " << std::endl;
	std::vector<int> player;
	if (this->round_ == 1) //第一轮淘汰赛比赛
	{
		player = this->v_semifinal;//进入半决赛的选手名单
	}
	else 
	{
		player = this->v_finals;//进入决赛的选手名单
	}
	for (std::vector<int>::iterator it = player.begin(); it != player.end(); it++) 
	{
		std::cout << "Player id is: " << (*it) << " ,name is: " << this->id_player[*it].getName()
			<< " ,scores is: " << this->id_player[*it].getScores()[this->round_ - 1] << std::endl;
	}
	std::cout<<std::endl;
	system("pause");//按任意键继续
	system("cls");//清屏
	this->show_Menu();//重新显示一下菜单
}
③运行效果

Ⅳ第二轮比赛+抽签+比赛+显示比赛分数功能复用
只需要将控制比赛轮次的成员属性round_自加即可
①Manage.cpp
在流程控制函数中将比赛轮次的成员属性round_自加实现第二轮比赛
 第二轮比赛和第一轮比赛一样,都是抽签competitionDraw()、比赛competitionContest()、显示晋级名单showScore()
 函数复用即可
void Manager::startCompetition() //开始比赛成员方法实现
{
	//第一轮比赛
	//1、抽签
	this->competitionDraw();
	//2、比赛
	this->competitionContest();
	//3、显示晋级结果
	this->showScore();
	//第二轮比赛
	this->round_++;
	//1、抽签
	this->competitionDraw();
	//2、比赛
	this->competitionContest();
	//3、显示最终结果
	this->showScore();
	//4、保存分数
}
②运行效果

Ⅴ保存冠亚季军选手的信息
①Manage.h
保存冠亚季军选手的信息函数savePlayerInfo()声明
	void savePlayerInfo();//保存冠亚季军选手的信息
②Manage.cpp
保存冠亚季军选手的信息函数savePlayerInfo()实现
void Manager::savePlayerInfo() 
{
	std::ofstream ofs;
	ofs.open("./compepiption.csv", std::ios::out | std::ios::app);
	for (std::vector<int>::iterator it = v_finals.begin(); it != v_finals.end(); it++) 
	{
		ofs << (*it) << "," << id_player[*it].getScores()[1] << ",";
	}
	ofs << std::endl;
	ofs.close();
	std::cout << "Already saved! " << std::endl;
}
③运行效果

⑥总结
就此流程控制函数startCompetition()所包含的所有流程均已全部实现
 
8,管理类中添加查看往届比赛记录成员方法——读取冠亚季军选手信息
读取往届冠亚季军选手信息函数loadPlayerInfo()
 上一步已经实现了保存冠亚季军选手的信息,现在开始读取这些信息
 首先需要判断保存的文件是否存在,其次通过比赛届数与冠亚季军选手的信息通过map进行关联
①Manage.h
读取往届冠亚季军选手信息函数loadPlayerInfo()声明
	void loadPlayerInfo();//读取往届冠亚季军选手信息
	bool fileIsEmpty;//判断文件是否为空
	std::map<int, std::vector<std::string>> past_records;//存放往届记录的容器
②Manage.cpp
读取往届冠亚季军选手信息函数loadPlayerInfo()实现
 因为这些是往届的比赛记录,故放在构造函数中,程序运行就执行
void Manager::loadPlayerInfo() 
{
	std::ifstream ifs("./compepiption.csv", std::ios::in);//读取文件
	
	//文件不存在,打开失败
	if (!ifs.is_open()) //打开失败
	{
		this->fileIsEmpty = true;//文件不存在,标志位设为true
		std::cout << "file is not exist!" << std::endl;
		ifs.close();
		return;
	}
	//文件存在,但文件为空
	char ch;
	ifs >> ch;//从文件中读一个字符
	if (ifs.eof()) //若文件读了一个文件后,就到结尾了
	{
		this->fileIsEmpty = true;//文件为空,标志位设为true
		std::cout << "file is empty!" << std::endl;
		ifs.close();
		return;
	}
	//重新若能走到这里,表示文件存在且不为空
	this->fileIsEmpty = false;
	ifs.putback(ch);//因为之前取出一个字符验证文件是否为空,现在就需把这个字符再读回来
	std::string info;//存放比赛记录的信息,主要是冠亚季军的编号和最终成绩六个逗号分隔的字符串,是完整的全部信息
	int index = 0;//第几届的比赛
	while (ifs >> info) 
	{
		//std::cout << info << std::endl;
		std::vector<std::string> data;
		int pos = -1;//标志位
		int start = 0;//起始位
		
		while (true) 
		{
			//数据格式:10, 86.675, 9, 81.3, 7, 78.55,
			pos = info.find(",", start);//从全部的信息中按逗号查找,从start位置开始查找
			if (pos == -1) //没找到返回pos为-1,找到了返回pos为具体的位置
			{
				break;//没找到,或者找完了
			}
			//取字符串函数substr,参数一:起始位置;参数二;截取长度
			std::string temp = info.substr(start, pos - start);//pos为逗号位置,start为起始位置,相减就是我们要分割长度
			data.push_back(temp);//将分割后的结果放入data容器中
			start = pos + 1;//查找下一个
		}
		this->past_records.insert(std::make_pair(index, data));//将比赛的届数和冠军季军亚军的信息和成绩进行绑定
		index++;//比赛届数
	}
	ifs.close();
	//小测试
	for (std::map<int, std::vector<std::string>>::iterator it = past_records.begin(); it != past_records.end(); it++) 
	{
		std::cout << "Round is: " << (*it).first+1 << " ,Champion No." << it->second[0]
			<< " ,Final Score is: " << it->second[1] << std::endl;
	}
}
Manager::Manager() //构造函数
{
	this->initCompetition(); //调用比赛初始化函数
	this->createPlayer();    //调用创建选手函数
	this->loadPlayerInfo();//加载往届比赛记录
}
③运行效果

9,管理类中添加查看往届比赛记录成员方法——显示冠亚季军选手信息
显示往届冠亚季军选手信息函数showPastCompetitionRecord()
 上一步读取完冠亚季军选手的信息,接下来开始进行显示
①Manage.h
显示往届冠亚季军选手信息函数showPastCompetitionRecord()声明
	void showPastCompetitionRecord();//显示往届比赛记录
②Manage.cpp
显示往届冠亚季军选手信息函数showPastCompetitionRecord()实现
void Manager::showPastCompetitionRecord() //显示往届比赛记录
{
	for (int i = 0; i < this->past_records.size(); i++) //i表示第几届比赛
	{
		std::cout << "Round is: " << i + 1 << ", "
			<< "Champion No." << this->past_records[i][0] << ", Final Score is: " << this->past_records[i][1] << "; "
			<< "Runner-up No." << this->past_records[i][2] << ", Final Score is: " << this->past_records[i][3] << "; "
			<< "Third Place No." << this->past_records[i][4] << ", Final Score is: " << this->past_records[i][5] << "; "
			<< std::endl;
	}
}
③main.cpp
通过swith case获取用户的输入,进入对应选择的功能
 将查看往届记录函数showPastCompetitionRecord()放在case2分支下
 调用Manager管理类的查看往届记录函数showPastCompetitionRecord()
#include "Manager.h"
#include <iostream>
#include<iomanip>
void show_id_palyer_info(std::map<int, Player> id_palyer) //显示id和player信息绑定是否成功
{
	//std::setw(2) << std::setfill('0')为了将个位数的id补为两位,比如01,02,03...
	for (std::map<int, Player>::iterator it = id_palyer.begin(); it != id_palyer.end(); it++)
	{
		std::cout << "Player id is: " <<std::setw(2) << std::setfill('0') << (*it).first
			<< " ,name is: " << it->second.getName()
			<< " ,round 1 scores is: " << it->second.getScores()[0]
			<< " ,round 2 scores is: " << it->second.getScores()[1]
			<< std::endl;
	}
}
int main(int argc, char* agrv[])
{
	Manager manager;
	int selection = 0;//接受用户的选择
	//测试一下id和player信息绑定是否成功
	//show_id_palyer_info(manager.id_player);
	while (true) 
	{
		manager.show_Menu(); //显示菜单
		
		std::cout << "Please input your selection:" << std::endl;
		std::cin >> selection;
		switch (selection)
		{
		case 1: //开始比赛
			manager.startCompetition(); //调用开始比赛成员方法
			break;
		case 2: //查看记录
			manager.showPastCompetitionRecord();
			break;
		case 3: //清空记录
			break;
		case 0: // 退出系统
			manager.exit_System();//调用管理类中的退出系统函数
			break;
		default: //按下其他键
			system("cls"); //清屏
			break;
		}
	}
	system("pause");
	return 0;
}
④运行效果

10,清空记录功能
通过swith case获取用户的输入,进入对应选择的功能
 将清空往届比赛记录函数clearPastCompetitionRecord()放在case4分支下
 调用Manager管理类的清空往届比赛记录函数clearPastCompetitionRecord()
用户确认清空记录,通过truncf方式打开文件,该方式若存在则删除文件重新创建
 清空记录之后还需要对容器进行初始化
②Manage.cpp
void Manager::clearPastCompetitionRecord() //清空往届比赛记录
{
	std::cout << "Are you sure you want to empty past competition record files?" << std::endl;
	std::cout << "1,Yes." << std::endl;
	std::cout << "2,No." << std::endl;
	int user_selection;
	std::cin >> user_selection;
	if (user_selection == 1) 
	{
		std::ofstream ofs("./compepiption.csv", std::ios::trunc);
		ofs.close();
		this->competitionReset();//重置初始化容器、重新创建12名选手、重新加载往届记录
		std::cout << "clear is successful" << std::endl;
	}
	system("pause");
	system("cls");
}
11,完善系统细节
Ⅰ显示冠亚季军选手信息功能——showPastCompetitionRecord()
若往届没有冠亚季军记录,则进行提示
①Manage.cpp
void Manager::showPastCompetitionRecord() //显示往届比赛记录
{
	if (this->fileIsEmpty) //若文件不存在或为空则提示用户
	{
		std::cout << "The file does not exist or the record is empty." << std::endl;
	}
	else //文件存在或不为空
	{
		for (int i = 0; i < this->past_records.size(); i++) //i表示第几届比赛
		{
			std::cout << "Round is: " << i + 1 << ", "
				<< "Champion No." << this->past_records[i][0] << ", Final Score is: " << this->past_records[i][1] << "; "
				<< "Runner-up No." << this->past_records[i][0] << ", Final Score is: " << this->past_records[i][1] << "; "
				<< "Third Place No." << this->past_records[i][0] << ", Final Score is: " << this->past_records[i][1] << "; "
				<< std::endl;
		}
	}
}
②运行效果

Ⅱ保存冠亚季军选手的信息——savePlayerInfo()
若刚开始比赛记录为空,保存了冠亚季军选手信息之后,需要更新文件为空的标志位
①Manage.cpp
void Manager::savePlayerInfo() 
{
	std::ofstream ofs;
	ofs.open("./compepiption.csv", std::ios::out | std::ios::app);//以追加的方式写入文件,若不存在则创建
	for (std::vector<int>::iterator it = v_finals.begin(); it != v_finals.end(); it++) 
	{
		ofs << (*it) << "," << id_player[*it].getScores()[1] << ",";
	}
	ofs << std::endl;
	ofs.close();
	std::cout << "Already saved! " << std::endl;
	this->fileIsEmpty = false;
}
Ⅲ流程控制函数——startCompetition()
比赛完之后,需要重置比赛,并且获取记录
①Manage.h
void competitionReset();//重置比赛
②Manage.cpp
void Manager::competitionReset() 
{
	//重置比赛,获取记录
	this->initCompetition();//初始化容器
	this->createPlayer();//重新创建12名参赛选手
	this->loadPlayerInfo();//加载往届的比赛记录
}
Ⅳ初始化时需要初始化往届比赛记录容器——initCompetition()
初始化的时候需要清空之前的比赛记录容器
①Manage.cpp
void Manager::initCompetition() // 比赛初始化函数
{
	this->v_knockout.clear();//第一轮淘汰比赛的选手的id编号清空
	this->v_semifinal.clear();//进入第二轮半决赛的选手的id编号清空
	this->v_finals.clear();//存放进入第三轮总决赛的选手的id编号清空
	this->id_player.clear();//编号id和选手信息绑定容器清空
	this->round_ = 1;//第一回合
	this->past_records.clear();//往届比赛记录容器初始化
}
在开始比赛(startCompetition())中进行调用
void Manager::startCompetition() //开始比赛成员方法实现
{
	//第一轮比赛
	//1、抽签
	this->competitionDraw();
	//2、比赛
	this->competitionContest();
	//3、显示晋级结果
	this->showScore();
	//第二轮比赛
	this->round_++;
	//1、抽签
	this->competitionDraw();
	//2、比赛
	this->competitionContest();
	//3、显示最终结果
	this->showScore();
	//4、保存分数
	this->savePlayerInfo();
	this->competitionReset();//重新初始化
	std::cout << "Completion of the session!" << std::endl;
	system("pause");
	system("cls");
}
Ⅴ添加随机数种子——main.cpp
头文件:#include <ctime>
 主函数最前面添加:std::srand((unsigned int)time(NULL));
四、完整项目
完整项目结构:
 
1,选手类——Player.h
 
#pragma once
#include <iostream>
#include <string>
class Player
{
public:
	std::string getName() //拿到选手姓名
	{
		return this->name_;
	}
	double* getScores() //拿到最终得分
	{
		return this->scores_;
	}
	void setName(std::string name) //设置选手姓名
	{
		this->name_ = name;
	}
	void setScores(double arr[]) //给选手打分
	{
		this->scores_[0] = arr[0];
		this->scores_[1] = arr[1];
	}
private:
	std::string name_; //选手姓名
	double scores_[2]; //两轮成绩
};
2,管理类——Manager.h
 
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <deque>
#include <algorithm>//sort排序算法
#include <functional>//内建函数greater从大到小排序,用于sort排序算法指定排序规则
#include <numeric>//容器求和函数accumulate
#include <fstream>
#include "Player.h"
class Manager 
{
public:
	Manager();//构造函数
	void show_Menu();//用于用户的页面交互显示函数
	void exit_System();//退出系统
	std::vector<int> v_knockout;  //存放第一轮淘汰比赛的选手的id编号 12人
	std::vector<int> v_semifinal; //存放进入第二轮半决赛的选手的id编号 6人
	std::vector<int> v_finals;    //存放进入第三轮总决赛的选手的id编号 3人
	std::map<int, Player> id_player; //选手编号和选手信息绑定
	int round_ = 1; //比赛轮数
	void initCompetition(); //比赛初始化
	void createPlayer(); //创建比赛选手
	void startCompetition();//开始比赛
	void competitionDraw(); //比赛抽签
	void competitionContest(); //开始比赛
	void showScore(); //显示比赛结果
	void savePlayerInfo();//保存冠亚季军选手的信息
	void loadPlayerInfo();//读取往届冠亚季军选手信息
	bool fileIsEmpty;//判断文件是否为空
	std::map<int, std::vector<std::string>> past_records;//存放往届记录的容器
	void showPastCompetitionRecord();//显示往届比赛记录
	void competitionReset();//重置比赛
	void clearPastCompetitionRecord();//清空往届比赛记录
	~Manager();//析构函数
};
3,管理类实现——Manager.cpp
 
#include "Manager.h"
#include <iostream>
void Manager::show_Menu() 
{
	std::cout << "********************************************" << std::endl;
	std::cout << "*************  欢迎参加演讲比赛 ************" << std::endl;
	std::cout << "*************  1.开始演讲比赛  *************" << std::endl;
	std::cout << "*************  2.查看往届记录  *************" << std::endl;
	std::cout << "*************  3.清空比赛记录  *************" << std::endl;
	std::cout << "*************  0.退出比赛程序  *************" << std::endl;
	std::cout << "********************************************" << std::endl;
	std::cout << std::endl;
}
void Manager::exit_System() 
{
	std::cout << "Welcome to next use! Bye~" << std::endl;
	system("pause");
	exit(0); //退出系统
}
void Manager::initCompetition() // 比赛初始化函数
{
	this->v_knockout.clear();//第一轮淘汰比赛的选手的id编号清空
	this->v_semifinal.clear();//进入第二轮半决赛的选手的id编号清空
	this->v_finals.clear();//存放进入第三轮总决赛的选手的id编号清空
	this->id_player.clear();//编号id和选手信息绑定容器清空
	this->round_ = 1;//第一回合
	this->past_records.clear();//往届比赛记录容器初始化
}
void Manager::createPlayer() //创建比赛选手
{
	std::string nameList = "ABCDEFGHIJKL"; //十二个选手name分别为A、B、C、D...
	for (int i = 0; i < nameList.size(); i++) 
	{
		std::string name = "Player";
		name += nameList[i];
		Player player;
		player.setName(name); //初始化设置参赛选手的姓名
		for (int j = 0; j < 2; j++) 
		{
			double arr[2] = { 0,0 }; //初始化设置参赛选手的每轮比赛得分
			player.setScores(arr);
		}
		this->v_knockout.push_back(i+1);//v_knockout为淘汰赛参加的12名选手,给选手编号赋值1-12
		//每个map中存放key为id,value为选手信息
		this->id_player.insert(std::make_pair(i+1, player));//编号分别为1,2,...,11,12
	}
}
void Manager::startCompetition() //开始比赛成员方法实现
{
	//第一轮比赛
	//1、抽签
	this->competitionDraw();
	//2、比赛
	this->competitionContest();
	//3、显示晋级结果
	this->showScore();
	//第二轮比赛
	this->round_++;
	//1、抽签
	this->competitionDraw();
	//2、比赛
	this->competitionContest();
	//3、显示最终结果
	this->showScore();
	//4、保存分数
	this->savePlayerInfo();
	this->competitionReset();//重新初始化
	std::cout << "Completion of the session!" << std::endl;
	system("pause");
	system("cls");
}
void Manager::competitionReset() 
{
	//重置比赛,获取记录
	this->initCompetition();//初始化容器
	this->createPlayer();//重新创建12名参赛选手
	this->loadPlayerInfo();//加载往届的比赛记录
}
void Manager::competitionDraw() 
{
	std::cout << "Rounds :" << this->round_ << " ,player is drawing..." << std::endl;
	std::cout << "The order of the draw is as follows: " << std::endl;
	if (this->round_ == 1) // 第一轮淘汰赛,参赛12人,分两组,每组前三晋级
	{
		std::random_shuffle(v_knockout.begin(), v_knockout.end()); //第一轮比赛的参赛选手容器为v_knockout
		
		std::vector<int>::iterator it_a, it_m, it_b;
		it_a = v_knockout.begin();
		it_m = v_knockout.begin();
		it_b = v_knockout.end();
		int m = v_knockout.size()/2; //6
		
		for (it_a = v_knockout.begin(); it_a != v_knockout.end(); it_a++)
		{
			std::cout << (*it_a) << " ";
		}
		std::cout << std::endl;
		std::cout << "-----------------------------" << std::endl;
		while (m--) 
		{
			it_m++;
		}
		std::cout << "A group is: " << std::endl;
		for (it_a = v_knockout.begin(); it_a != it_m; it_a++) 
		{
			std::cout << (*it_a) << " ";
		}
		std::cout << std::endl;
		std::cout << "B group is: " << std::endl;
		for (it_b = it_m; it_b != v_knockout.end(); it_b++)
		{
			std::cout << (*it_b) << " ";
		}
		std::cout << std::endl;
		
	}
	else // 第二轮半决赛,参赛6人,前三晋级
	{
		std::random_shuffle(v_semifinal.begin(), v_semifinal.end());//目前半决赛还没有加入选手,因为淘汰赛还没有开始进行
		for (std::vector<int>::iterator it = v_semifinal.begin(); it != v_semifinal.end(); it++) 
		{
			std::cout << (*it) << " ";
		}
		std::cout << std::endl;
	}
	system("pause");
	std::cout << std::endl;
}
void Manager::competitionContest() //开始比赛函数实现
{
	std::cout << "The round is: " << this->round_ << " starting----------------------" << std::endl;
	//存放临时容器,存放分组后的每个小组成员的成绩,double为每个人的最终得分,int为选手的id编号
	std::multimap<double, int, std::greater<int>>groupScore;//内建函数std::greater<int>,从大到小排序
	int groupnumber = 0;
	std::vector <int>v_competitor;//实际的参赛选手容器
	if (this->round_ == 1)//淘汰赛每组的前三名晋级 
	{
		v_competitor = v_knockout;//淘汰赛
	}
	else 
	{
		v_competitor = v_semifinal;//半决赛
	}
	for (std::vector<int>::iterator it = v_competitor.begin(); it != v_competitor.end(); it++) //遍历选手,评委打分,这里的it为选手的id编号
	{
		groupnumber++;//小组成员自增
		//使用双端数组deque接收十位评委打分,到时候排完序直接可以去除最高分和最低分
		std::deque<double> scores;
		for (int i = 0; i < 10; i++) 
		{
			double score = (rand() % 401 + 600) / 10.f;//rand() % 401 == [0,401)
			scores.push_back(score); 
			//std::cout << score << " ";//查看十位评委给选手的具体打分情况
		}
		//std::cout << std::endl;
		sort(scores.begin(), scores.end(), std::greater<double>());//降序排列,内建函数greater从大到小排序
		scores.pop_front();//去除最高分
		scores.pop_back();//去除最低分
		double score_sum = std::accumulate(scores.begin(), scores.end(), 0.0f);//容器内所有的值进行累加
		double score_avg = score_sum / (double)scores.size();//平均分
		//测试输出一下平均分
		/*
		std::cout << "Player id is: " << *it
			<< " ,name is: " << this->id_player[*it].getName()
			<< " ,aver is: " << score_avg;
        */
		//把平均分放入到map容器id_player中,按id编号得到选手信息,然后再放入到选手信息中的得分数组里即可
		double arr[2] = {0};
		arr[this->round_ - 1] = score_avg;
		this->id_player[*it].setScores(arr);
		//将每个选手的最终得分存放到临时小组容器中
		groupScore.insert(std::make_pair(score_avg, *it));//key为最终成绩,value为选手的id编号
		//每个小组取出最终得分前三名
		if (groupnumber % 6 == 0)
		{
			if (groupnumber == 6)std::cout << "Group A Ranking is :" << std::endl;
			if (groupnumber == 12)std::cout << "Group B Ranking is :" << std::endl;
			for (std::multimap<double, int, std::greater<double>>::iterator it = groupScore.begin(); it != groupScore.end(); it++) 
			{
				std::cout << "Player id is: " << it->second
					<< " ,name is: " << this->id_player[it->second].getName()
					<< " ,score is: " << this->id_player[it->second].getScores()[this->round_ - 1]
					<< std::endl;
			}
			//获取每组的前三名,晋级
			int count = 0;
			for (std::multimap<double, int, std::greater<double>>::iterator it = groupScore.begin(); it != groupScore.end(); it++)
			{
				if (count < 3) //前三名晋级
				{
					count++;
					if (this->round_ == 1)//淘汰赛晋级半决赛的选手id编号
					{
						v_semifinal.push_back((*it).second);//key为最终成绩,value为选手id编号
					}
					else //半决赛晋级决赛的选手id编号
					{
						v_finals.push_back((*it).second);//key为最终成绩,value为选手id编号
					}
				}
			}
			groupScore.clear();//小组容器清空,避免后续的追加
			std::cout << std::endl;
		}
	}
	//std::cout << std::endl;
	std::cout << "-----------------Round " << this->round_ << " is over!--------------" << std::endl;
	system("pause");
}
void Manager::showScore() //显示得分函数
{
	std::cout << "Round " << this->round_ << " Promoted Players is: " << std::endl;
	std::vector<int> player;
	if (this->round_ == 1) //第一轮淘汰赛比赛
	{
		player = this->v_semifinal;//进入半决赛的选手名单
	}
	else 
	{
		player = this->v_finals;//进入决赛的选手名单
	}
	for (std::vector<int>::iterator it = player.begin(); it != player.end(); it++) 
	{
		std::cout << "Player id is: " << (*it) << " ,name is: " << this->id_player[*it].getName()
			<< " ,scores is: " << this->id_player[*it].getScores()[this->round_ - 1] << std::endl;
	}
	std::cout<<std::endl;
	system("pause");//按任意键继续
	system("cls");//清屏
	this->show_Menu();//重新显示一下菜单
}
void Manager::savePlayerInfo() 
{
	std::ofstream ofs;
	ofs.open("./compepiption.csv", std::ios::out | std::ios::app);//以追加的方式写入文件,若不存在则创建
	for (std::vector<int>::iterator it = v_finals.begin(); it != v_finals.end(); it++) 
	{
		ofs << (*it) << "," << id_player[*it].getScores()[1] << ",";
	}
	ofs << std::endl;
	ofs.close();
	std::cout << "Already saved! " << std::endl;
	this->fileIsEmpty = false;
}
void Manager::loadPlayerInfo() 
{
	std::ifstream ifs("./compepiption.csv", std::ios::in);//读取文件
	
	//文件不存在,打开失败
	if (!ifs.is_open()) //打开失败
	{
		this->fileIsEmpty = true;//文件不存在,标志位设为true
		//std::cout << "file is not exist!" << std::endl;
		ifs.close();
		return;
	}
	//文件存在,但文件为空
	char ch;
	ifs >> ch;//从文件中读一个字符
	if (ifs.eof()) //若文件读了一个文件后,就到结尾了
	{
		this->fileIsEmpty = true;//文件为空,标志位设为true
		//std::cout << "file is empty!" << std::endl;
		ifs.close();
		return;
	}
	//重新若能走到这里,表示文件存在且不为空
	this->fileIsEmpty = false;
	ifs.putback(ch);//因为之前取出一个字符验证文件是否为空,现在就需把这个字符再读回来
	std::string info;//存放比赛记录的信息,主要是冠亚季军的编号和最终成绩六个逗号分隔的字符串,是完整的全部信息
	int index = 0;//第几届的比赛
	while (ifs >> info) 
	{
		//std::cout << info << std::endl;
		std::vector<std::string> data;
		int pos = -1;//标志位
		int start = 0;//起始位
		
		while (true) 
		{
			//数据格式:10, 86.675, 9, 81.3, 7, 78.55,
			pos = info.find(",", start);//从全部的信息中按逗号查找,从start位置开始查找
			if (pos == -1) //没找到返回pos为-1,找到了返回pos为具体的位置
			{
				break;//没找到,或者找完了
			}
			//取字符串函数substr,参数一:起始位置;参数二;截取长度
			std::string temp = info.substr(start, pos - start);//pos为逗号位置,start为起始位置,相减就是我们要分割长度
			data.push_back(temp);//将分割后的结果放入data容器中
			start = pos + 1;//查找下一个
		}
		this->past_records.insert(std::make_pair(index, data));//将比赛的届数和冠军季军亚军的信息和成绩进行绑定
		index++;//比赛届数
	}
	ifs.close();
	/*
	//小测试
	for (std::map<int, std::vector<std::string>>::iterator it = past_records.begin(); it != past_records.end(); it++) 
	{
		std::cout << "Round is: " << (*it).first+1 << " ,Champion No." << it->second[0]
			<< " ,Final Score is: " << it->second[1] << std::endl;
	}
	*/
}
void Manager::showPastCompetitionRecord() //显示往届比赛记录
{
	if (this->fileIsEmpty) //若文件不存在或为空则提示用户
	{
		std::cout << "The file does not exist or the record is empty." << std::endl;
	}
	else //文件存在或不为空
	{
		for (int i = 0; i < this->past_records.size(); i++) //i表示第几届比赛
		{
			std::cout << "Round is: " << i + 1 << ", "
				<< "Champion No." << this->past_records[i][0] << ", Final Score is: " << this->past_records[i][1] << "; "
				<< "Runner-up No." << this->past_records[i][2] << ", Final Score is: " << this->past_records[i][3] << "; "
				<< "Third Place No." << this->past_records[i][4] << ", Final Score is: " << this->past_records[i][5] << "; "
				<< std::endl;
		}
	}
}
void Manager::clearPastCompetitionRecord() //清空往届比赛记录
{
	std::cout << "Are you sure you want to empty past competition record files?" << std::endl;
	std::cout << "1,Yes." << std::endl;
	std::cout << "2,No." << std::endl;
	int user_selection;
	std::cin >> user_selection;
	if (user_selection == 1) 
	{
		std::ofstream ofs("", std::ios::trunc);
		ofs.close();
		this->competitionReset();//重置初始化容器、重新创建12名选手、重新加载往届记录
		std::cout << "clear is successful" << std::endl;
	}
	system("pause");
	system("cls");
}
Manager::Manager() //构造函数
{
	this->initCompetition(); //调用比赛初始化函数
	this->createPlayer();    //调用创建选手函数
	this->loadPlayerInfo();//加载往届比赛记录
}
Manager::~Manager() //析构函数空实现
{
	//用于释放存放放在堆区的数据,我们这里没用到,故有没有无所谓
}
4,主函数——main.cpp
 
#include "Manager.h"
#include <iostream>
#include <iomanip>
#include <ctime>
void show_id_palyer_info(std::map<int, Player> id_palyer) //显示id和player信息绑定是否成功
{
	//std::setw(2) << std::setfill('0')为了将个位数的id补为两位,比如01,02,03...
	for (std::map<int, Player>::iterator it = id_palyer.begin(); it != id_palyer.end(); it++)
	{
		std::cout << "Player id is: " <<std::setw(2) << std::setfill('0') << (*it).first
			<< " ,name is: " << it->second.getName()
			<< " ,round 1 scores is: " << it->second.getScores()[0]
			<< " ,round 2 scores is: " << it->second.getScores()[1]
			<< std::endl;
	}
}
int main(int argc, char* agrv[])
{
	//添加随机数种子
	std::srand((unsigned int)time(NULL));
	Manager manager;
	int selection = 0;//接受用户的选择
	//测试一下id和player信息绑定是否成功
	//show_id_palyer_info(manager.id_player);
	while (true) 
	{
		manager.show_Menu(); //显示菜单
		
		std::cout << "Please input your selection:" << std::endl;
		std::cin >> selection;
		switch (selection)
		{
		case 1: //开始比赛
			manager.startCompetition(); //调用开始比赛成员方法
			break;
		case 2: //查看记录
			manager.showPastCompetitionRecord();
			break;
		case 3: //清空记录
			manager.clearPastCompetitionRecord();
			break;
		case 0: // 退出系统
			manager.exit_System();//调用管理类中的退出系统函数
			break;
		default: //按下其他键
			system("cls"); //清屏
			break;
		}
	}
	system("pause");
	return 0;
}
5,运行效果
1.开始比赛
 
 2.查看往届记录
 
 3.清空比赛记录
 
 0.退出比赛程序
 



















