C++項目實戰之演講比賽流程管理系統

来源:https://www.cnblogs.com/sarexpine/archive/2023/08/18/17641686.html
-Advertisement-
Play Games

本文通過設計演講比賽流程管理系統,全面介紹了使用C++面向對象編程思想開發項目應用的過程,涵蓋了需求分析、系統架構設計、類的提取,以及採用多種STL容器配合演算法的具體實現。文中詳細展示了構建選手類、管理類,設計菜單界面與用戶交互、實現兩輪比賽流程的抽簽、評分模塊,並能夠完成記錄文件的讀寫與管理 ...


演講比賽流程管理系統

1. 演講比賽程式需求

1.1 比賽規則

  1. 學校舉行一場演講比賽,共有12個人參加。比賽共兩輪,第一輪為淘汰賽,第二輪為決賽

  2. 每名選手都有對應的編號,如 10001 ~ 10012

  3. 比賽方式:分組比賽,每組6個人

  4. 第一輪分為兩個小組,整體按照選手編號進行抽簽後順序演講

  5. 10個評委分別給每名選手打分,去除最高分和最低分,求得平均分為本輪選手的成績

  6. 當小組演講完後,淘汰組內排名最後的三個選手,前三名晉級,進入下一輪的比賽

  7. 第二輪為決賽,前三名勝出

  8. 每輪比賽過後需要顯示晉級選手的信息

1.2 程式功能

  • 開始演講比賽:完成整屆比賽的流程,每個比賽階段需要給用戶一個提示,用戶按任意鍵後繼續下一個階段
  • 查看往屆記錄:查看之前比賽前三名結果,每次比賽都會記錄到文件中,文件用.csv尾碼名保存
  • 清空比賽記錄:將文件中數據清空
  • 退出比賽程式:可以退出當前程式

2. 創建管理類

功能描述:

  1. 提供菜單界面與用戶交互

  2. 對演講比賽流程進行控制

  3. 與文件的讀寫交互

2.1創建文件

在頭文件和源文件的文件夾下分別創建 speechManager.hspeechManager.cpp 文件

2.2 頭文件實現

speechManager.h 中設計管理類

代碼如下:

#pragma once
class SpeechManager
{
public:

	//構造函數
	SpeechManager();

	//析構函數
	~SpeechManager();
};

2.3 源文件實現

speechManager.cpp 中將構造和析構函數空實現補全

#include "speechManager.h"

SpeechManager::SpeechManager()
{
}

SpeechManager::~SpeechManager()
{
}

至此演講管理類以創建完畢

3. 菜單功能

功能描述:與用戶的溝通界面

3.1 添加成員函數

在管理類 speechManager.h 中添加成員函數 void show_Menu();

//菜單界面
void show_Menu();

3.2 菜單功能實現

在管理類 speechManager.cpp 中實現 show_Menu() 函數

void SpeechManager::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;
}

3.3 測試菜單功能

在演講比賽流程管理系統.cpp中測試菜單功能

代碼:

#include <iostream>
#include "speechManager.h"

int main()
{
	SpeechManager spm;
	spm.show_Menu();

	return 0;
}
image-20230817105422542

菜單界面搭建完畢

4. 退出功能

4.1 提供功能介面

在main函數中提供分支選擇,提供每個功能介面

代碼:

#include <iostream>
#include "speechManager.h"

int main()
{
	SpeechManager spm;
	int choise = 0;

	while (true)
	{
		spm.show_Menu();
		std::cout << "請輸入選項: ";
		std::cin >> choise;

		switch (choise)
		{
		case 1:
			break;
		case 2:
			break;
		case 3:
			break;
		case 4:
			break;
		case 0:
			spm.exitSystem();
			break;
		default:
			break;
		}
	}

	return 0;
}

4.2 實現退出功能

speechManager.h 中提供退出系統的成員函數 void exitSystem();

//退出界面
void exitSystem();

speechManager.cpp 中提供具體的功能實現

void SpeechManager::exitSystem()
{
	std::cout << "~歡迎下次使用~" << std::endl;
	system("pause");
	exit(0);
}

4.3測試功能

在main函數分支 0 選項中,調用退出程式的介面

#include <iostream>
#include "speechManager.h"

int main()
{
	SpeechManager spm;
	int choise = 0;

	while (true)
	{
		spm.show_Menu();
		std::cout << "請輸入選項: ";
		std::cin >> choise;

		switch (choise)
		{
		case 1:
			break;
		case 2:
			break;
		case 3:
			break;
		case 4:
			break;
		case 0:
			spm.exitSystem();
			break;
		default:
			break;
		}
	}

	return 0;
}

5. 演講比賽功能

5.1 功能分析

比賽流程分析:

抽簽 → 開始演講比賽 → 顯示第一輪比賽結果 →

抽簽 → 開始演講比賽 → 顯示前三名結果 → 保存分數

5.2 創建選手類

選手類中的屬性包含:選手姓名、分數

頭文件中創建 speaker.h 文件,並添加代碼:

#pragma once
#include <iostream>

class Speaker
{
public:
	std::string m_Name;		//選手姓名
	double m_Score[2];		//選手分數,至多存放兩輪
};

5.3 比賽

5.3.1 成員屬性添加

speechManager.h 中添加屬性

	//容器1: 存放12位選手的編號
	std::vector<int> v1;

	//容器2: 存放6位選手的編號
	std::vector<int> v2;

	//容器3: 存放3位選手的編號
	std::vector<int> v_Victory;

	//容器4: 存放Speaker的編號和對應的選手
	std::map<int, Speaker> m_Speaker;

	//比賽輪數
	int m_Round;

5.3.2 初始化屬性

speechManager.h 中提供開始比賽的的成員函數 void initSpeech();

void initSpeaker();

speechManager.cpp 中實現開始比賽的的成員函數 void initSpeech();

void SpeechManager::initSpeaker()
{
	//清空容器
	this->v1.clear();
	this->v2.clear();
	this->v_Victory.clear();
	this->m_Speaker.clear();

	//第一輪比賽
	this->m_Round = 1;
}

SpeechManager 構造函數中調用void initSpeech();

SpeechManager::SpeechManager()
{
	//初始化
	this->initSpeaker();
}

5.3.3 創建選手

speechManager.h 中提供開始比賽的的成員函數 void createSpeaker();

//創建選手
void createSpeaker();

speechManager.cpp 中實現開始比賽的的成員函數 void createSpeaker();

void SpeechManager::createSpeaker()
{
	std::string nameSeed = "ABCDEFGHIJKL";
	for (int i = 0; i < nameSeed.size(); i++)
	{
		//初始化選手中的屬性
		Speaker sp;
		sp.m_Name = "選手";
		sp.m_Name += nameSeed[i];
		for (int j = 0; j < 2; j++)
		{
			sp.m_Score[j] = 0;
		}

		//將初始化後的選手放入容器中
		this->v1.push_back(i + 1000);
		this->m_Speaker.insert(std::pair<int, Speaker>(i + 1000, sp));
	}
}

SpeechManager類的 構造函數中調用 void createSpeaker();

SpeechManager::SpeechManager()
{
	//初始化
	this->initSpeaker();

	//創建選手
	this->createSpeaker();
}

測試 在main函數中,可以在創建完管理對象後,使用下列代碼測試12名選手初始狀態

for (std::map<int, Speaker>::iterator it = spm.m_Speaker.begin(); it != spm.m_Speaker.end(); it++)
{
    std::cout
        << it->first << " "
        << it->second.m_Name << " "
        << (*it).second.m_Score[0] << std::endl;
}

5.3.4 開始比賽成員函數添加

speechManager.h 中提供開始比賽的的成員函數 void startSpeech();

該函數功能是主要控制比賽的流程

//開始比賽 - 比賽流程式控制制
void startSpeech();

speechManager.cpp 中將 startSpeech 的空實現先寫入

我們可以先將整個比賽的流程 寫到函數中

void SpeechManager::startSpeech()
{
	//第一輪比賽
	//1、抽簽

	//2、比賽

	//3、顯示晉級結果

	//第二輪比賽

	//1、抽簽

	//2、比賽

	//3、顯示最終結果

	//4、保存分數
}

5.3.5 抽簽

功能描述:

正式比賽前,所有選手的比賽順序需要打亂,我們只需要將存放選手編號的容器 打亂次序即可

speechManager.h 中提供抽簽的的成員函數 void speechDraw();

//抽簽
void speechDraw();

speechManager.cpp 中實現成員函數 void speechDraw();

void SpeechManager::speechDraw()
{
	std::cout << "第" << this->m_Round << "輪比賽開始抽簽" << std::endl;
	std::cout << "=*=*=*=*=*=*=*=*=" << std::endl;
	std::cout << "抽簽後演講順序如下: " << std::endl;
	if (this->m_Round == 1)
	{
		std::random_shuffle(this->v1.begin(), this->v1.end());
		for (std::vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;
	}
	else
	{
		std::random_shuffle(this->v2.begin(), this->v2.end());
		for (std::vector<int>::iterator it = v2.begin(); it != v2.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;
	}
	std::cout << "=*=*=*=*=*=*=*=*=" << std::endl;
	system("pause");
	std::cout << std::endl;
}

startSpeech 比賽流程式控制制的函數中,調用抽簽函數

void SpeechManager::startSpeech()
{
	//第一輪比賽
	//1、抽簽
	this->speechDraw();

	//2、比賽

	//3、顯示晉級結果

	//第二輪比賽

	//1、抽簽

	//2、比賽

	//3、顯示最終結果

	//4、保存分數
}

5.3.6 開始比賽

speechManager.h 中提供比賽的的成員函數 void speechContest();

//打分
void speechContest();

speechManager.cpp 中實現成員函數 void speechContest();

void SpeechManager::speechContest()
{
	std::cout << "==========第" << this->m_Round << "輪比賽開始==========" << std::endl;

	//定義一個臨時存放需要比賽的選手的容器
	std::vector<int> v_Src;
	if (this->m_Round == 1)
	{
		v_Src = v1;
	}
	else
	{
		v_Src = v2;
	}

	//定義一個臨時存放平均分的容器
	std::multimap<double, int, std::greater<int>> groupScore;

	//定義一個分組變數,記錄人數
	//目的是讓每6個選手為1組
	int grouping = 0;

	//取出需要參賽的選手的編號
	for (std::vector<int>::iterator it = v_Src.begin(); it != v_Src.end(); it++)
	{
		++grouping;

		//定義評委打分容器,為每一組中的每一位選手打分
		//選用deque容器是因為饞它的pop_back和pop_front
		std::deque<double> scoring;

		for (std::vector<int>::iterator it = v_Src.begin(); it != v_Src.end(); it++)
		{
			double score = (rand() % 601 + 400) / 10.0f;	//(0+600~400+600) / 10 = 60~100
			scoring.push_back(score);
			//std::cout << score << " ";
		}
		//std::cout << std::endl;

		//打分結束後需要計算平均分
		std::sort(scoring.begin(), scoring.end(), std::greater<double>());
		scoring.pop_front();		//去掉一個最低分
		scoring.pop_back();			//去掉一個最高分
		double avg = std::accumulate(scoring.begin(), scoring.end(), 0.0f) / (double)scoring.size();

		//直接把平均分插入到m_Speaker的m_Score[m_Round-1]里,第一輪里12個人都有分數
		//Speaker sp = this->m_Speaker[*it];
		//sp.m_Score[this->m_Round - 1] = avg;
		this->m_Speaker[*it].m_Score[this->m_Round - 1] = avg;

		//將平均分放入到臨時的multimap容器里
		//其中multimap的鍵值對為 <平均分, 選手編號>
		groupScore.insert(std::multimap<double, int, std::greater<int>>::value_type(avg, *it));

		//如果是第6個人或第12個人,做一次分組
		if (grouping % 6 == 0)
		{
			std::cout << "第" << grouping / 6 << "小組比賽名次如下:" << std::endl;
			
			for (std::multimap<double, int, std::greater<int>>::iterator it = groupScore.begin();
				it != groupScore.end(); it++)
			{
				std::cout
					<< "選手編號: " << it->second << " "
					<< "選手姓名: " << this->m_Speaker[it->second].m_Name << " "
					<< "選手分數: " << this->m_Speaker[it->second].m_Score[this->m_Round - 1]
					<< std::endl;
			}

			int count_WinSpeaker = 0;
			//取每組的前三個成績排名靠前的
			for (std::multimap<double, int, std::greater<int>>::iterator it = groupScore.begin();
				it != groupScore.end() && count_WinSpeaker < 3;
				++it, ++count_WinSpeaker)
			{
				if (this->m_Round == 1)
				{
					v2.push_back(it->second);
				}
				else
				{
					v_Victory.push_back(it->second);
				}
				++count_WinSpeaker;
			}

			//每分6個人清空一次容器
			groupScore.clear();
			//美觀代碼
			if (grouping == 6) { std::cout << std::endl; }
		}
	}

	std::cout << "==========第" << this->m_Round << "輪比賽結束==========" << std::endl;
	++this->m_Round;
	system("pause");
}

startSpeech 比賽流程式控制制的函數中,調用比賽函數

void SpeechManager::startSpeech()
{
	//第一輪比賽
	//1、抽簽
	this->speechDraw();

	//2、比賽
	this->speechContest();

	//3、顯示晉級結果

	//第二輪比賽
	//1、抽簽

	//2、比賽

	//3、顯示最終結果

	//4、保存分數
}

5.3.7 顯示比賽分數

speechManager.h 中提供比賽的的成員函數 void showScore();

//顯示分數
void showScore();

speechManager.cpp 中實現成員函數 void showScore();

void SpeechManager::showScore()
{
	std::cout << "==========第" << this->m_Round << "輪比賽名次==========" << std::endl;

	std::vector<int> v;
	if (this->m_Round == 1)
	{
		v = v2;
	}
	else
	{
		v = v_Victory;
	}

	for (std::vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		std::cout
			<< "選手編號: " << *it << " "
			<< "選手姓名: " << this->m_Speaker[*it].m_Name << " "
			<< "選手分數: " << this->m_Speaker[*it].m_Score[this->m_Round - 1]
			<< std::endl;
	}

	system("pause");
	system("cls");
}

運行代碼,測試效果

image-20230817212724575

5.3.8 第二輪比賽

第二輪比賽流程同第一輪,只是比賽的輪是+1,其餘流程不變

startSpeech 比賽流程式控制制的函數中,加入第二輪的流程

void SpeechManager::startSpeech()
{
	//第一輪比賽
	//1、抽簽
	this->speechDraw();

	//2、比賽
	this->speechContest();

	//3、顯示晉級結果
	this->showScore();

	//第二輪比賽
	++this->m_Round;
	//1、抽簽
	this->speechDraw();

	//2、比賽
	this->speechContest();

	//3、顯示最終結果
	this->showScore();

	//4、保存分數
}
image-20230818005010269

5.4 保存分數

功能描述:將每次演講比賽的得分記錄到文件中

功能實現:

speechManager.h 中添加保存記錄的成員函數 void saveRecord();

//保存比賽分數
void saveRecord();

speechManager.cpp 中實現成員函數 void saveRecord();

void SpeechManager::saveRecord()
{
	std::ofstream ofs("speech.csv", std::ios::out | std::ios::app);

	for (std::vector<int>::iterator it = v_Victory.begin(); it != v_Victory.end(); it++)
	{
		ofs << *it << "," << this->m_Speaker[*it].m_Score[1] << ",";
	}
	ofs << std::endl;

	std::cout << "保存分數完成" << std::endl;
	std::cout << "本屆比賽正式結束!" << std::endl;
	this->fileIsEmpty = false;

	system("pause");
	system("cls");
}

startSpeech 比賽流程式控制制的函數中,最後調用保存記錄分數函數

void SpeechManager::startSpeech()
{
	//第一輪比賽
	//1、抽簽
	this->speechDraw();

	//2、比賽
	this->speechContest();

	//3、顯示晉級結果
	this->showScore();

	//第二輪比賽
	++this->m_Round;
	//1、抽簽
	this->speechDraw();

	//2、比賽
	this->speechContest();

	//3、顯示最終結果
	this->showScore();

	//4、保存分數
	this->saveRecord();
}
image-20230818011155645

至此,整個演講比賽功能製作完畢!

6. 查看記錄

6.1 讀取記錄分數

  1. speechManager.h 中添加保存記錄的成員函數 void loadRecord();

  2. 添加判斷文件是否為空的標誌 bool fileIsEmpty;

  3. 添加往屆記錄的容器map<int, vector<string>> m_Record;

其中 m_Record 中的 key 代表第幾屆,value 記錄具體的信息

SpeechManager 構造函數中調用獲取往屆記錄函數

//讀取記錄
void loadRecord();

//文件為空的標誌
bool fileIsEmpty;

//往屆記錄
map<int, vector<string>> m_Record;

speechManager.cpp 中實現成員函數 void loadRecord();

void SpeechManager::loadRecord()
{
	std::ifstream ifs("speech.csv", std::ios::in);

	if (!ifs.is_open())
	{
		//std::cout << "文件不存在" << std::endl;
		this->fileIsEmpty = true;
		ifs.close();
		return;
	}

	char ch;
	ifs >> ch;
	if (ifs.eof())
	{
		//std::cout << "文件為空" << std::endl;
		this->fileIsEmpty = true;
		ifs.close();
		return;
	}

	//文件不為空
	this->fileIsEmpty = false;
	ifs.putback(ch);

	std::string data;
	int round = 0;
	while (ifs >> data)
	{
		//定義獲取往屆比賽信息的容器
		std::vector<std::string> v;
		int pos = -1;		//,號的標誌位
		int start = 0;

		while (true)
		{
			//尋找 以","號分割的字元串,找不到就結束
			pos = data.find(",", start);
			if (pos == -1)
			{
				break;
			}

			//substr(起始位置, 結束位置)
			std::string temp = data.substr(start, pos - start);
			start = pos + 1;
			v.push_back(temp);
		}
		//將往屆的冠軍數據插入到容器中
		this->m_Record.insert(std::map<int, std::vector<std::string>>::value_type(round, v));
		++round;
	}

	ifs.close();
}

speechManager 構造函數中調用獲取往屆記錄函數

SpeechManager::SpeechManager()
{
	//初始化
	this->initSpeaker();

	//創建選手
	this->createSpeaker();

	//查看往屆記錄
	this->loadRecord();
}

6.2 查看記錄功能

speechManager.h 中添加保存記錄的成員函數 void showRecord();

//顯示往屆得分
void showRecord();

speechManager.cpp 中實現成員函數 void showRecord();

void SpeechManager::showRecord()
{
	if (this->fileIsEmpty)
	{
		std::cout << "文件不存在,記錄為空" << std::endl;
	}
	else
	{
		for (int i = 0; i < this->m_Record.size(); i++)
		{
			std::cout
				<< "=========第" << i + 1 << "屆=========" << "\n"
				<< "冠軍編號: " << this->m_Record[i][0] << " 得分: " << this->m_Record[i][1] << "\n"
				<< "亞軍編號: " << this->m_Record[i][2] << " 得分: " << this->m_Record[i][3] << "\n"
				<< "季軍編號: " << this->m_Record[i][4] << " 得分: " << this->m_Record[i][5] << "\n"
				<< "========================" << "\n"
				<< std::endl;
		}
	}
	system("pause");
	system("cls");
}

6.3 bug解決

目前程式中有幾處bug未解決:

  1. 查看往屆記錄,若文件不存在或為空,並未提示

    解決方式:在 showRecord 函數中,開始判斷文件狀態並加以判斷

  1. 若記錄為空或不存在,比完賽後依然提示記錄為空

    解決方式:saveRecord 中更新文件為空的標誌

  1. 比完賽後查不到本屆比賽的記錄,沒有實時更新

    解決方式:比賽完畢後,所有數據重置

  1. 在初始化時,沒有初始化記錄容器

    解決方式:initSpeech 中添加 初始化記錄容器

每次記錄都是一樣的

解決方式:在main函數一開始 添加隨機數種子

srand((unsigned int)time(NULL));

7. 清空記錄

7.1 清空記錄功能實現

speechManager.h 中添加保存記錄的成員函數 void clearRecord();

//清空記錄
void clearRecord();

speechManager.cpp 中實現成員函數 void clearRecord();

void SpeechManager::clearRecord()
{
	int choise = 0;
	std::cout << "確定要清空全部的數據嗎?" << std::endl;
	std::cout << "1.確定 ------ 0.返回" << std::endl;
	std::cin >> choise;
	
	if (choise == 1)
	{
		std::ofstream ofs("speech.csv", std::ios::trunc);

		this->initSpeaker();
		this->createSpeaker();
		this->loadRecord();

		std::cout << "清空完成!" << std::endl;
	}

	std::cout << "取消清空操作" << std::endl;

	system("pause");
	system("cls");
}

至此本案例結束!

源代碼及文件結構

├─頭文件
│      speaker.h
│      speechManager.h
│
├─源文件
│      main.cpp
│      speechManager.cpp

1. 演講者類 - Speaker

speaker.h

#pragma once
#include <iostream>

class Speaker
{
public:
	std::string m_Name;		//選手姓名
	double m_Score[2];		//選手分數,至多存放兩輪
};

2. 演講比賽管理類 - SpeechManager

speechManager.h

#pragma once
#include <iostream>
#include <vector>
#include <map>
#include <deque>
#include <algorithm>
#include <numeric>
#include <functional>
#include <fstream>
#include "speaker.h"

class SpeechManager
{
public:
	//容器1: 存放12位選手的編號
	std::vector<int> v1;

	//容器2: 存放6位選手的編號
	std::vector<int> v2;

	//容器3: 存放3位選手的編號
	std::vector<int> v_Victory;

	//容器4: 存放Speaker的編號和對應的選手
	std::map<int, Speaker> m_Speaker;

	//文件為空的標誌
	bool fileIsEmpty;

	//往屆記錄
	std::map<int, std::vector<std::string>> m_Record;

	//比賽輪數
	int m_Round;

	//構造函數
	SpeechManager();

	//菜單界面
	void show_Menu();

	//初始化容器
	void initSpeaker();

	//創建選手
	void createSpeaker();

	//抽簽
	void speechDraw();

	//打分
	void speechContest();

	//顯示分數
	void showScore();

	//保存比賽分數
	void saveRecord();

	//讀取記錄
	void loadRecord();

	//讀取往屆信息
	void showRecord();

	//開始比賽
	void startSpeech();

	//清空文件
	void clearRecord();

	//退出界面
	void exitSystem();

	//析構函數
	~SpeechManager();
};

speechManager.cpp

#include "speechManager.h"

SpeechManager::SpeechManager()
{
	//初始化
	this->initSpeaker();

	//創建選手
	this->createSpeaker();

	//查看往屆記錄
	this->loadRecord();
}

void SpeechManager::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 SpeechManager::initSpeaker()
{
	//清空容器
	this->v1.clear();
	this->v2.clear();
	this->v_Victory.clear();
	this->m_Speaker.clear();
	this->m_Record.clear();

	//第一輪比賽
	this->m_Round = 1;
}

void SpeechManager::exitSystem()
{
	std::cout << "~歡迎下次使用~" << std::endl;
	system("pause");
	exit(0);
}

void SpeechManager::createSpeaker()
{
	std::string nameSeed = "ABCDEFGHIJKL";
	for (int i = 0; i < nameSeed.size(); i++)
	{
		//初始化選手中的屬性
		Speaker sp;
		sp.m_Name = "選手";
		sp.m_Name += nameSeed[i];
		for (int j = 0; j < 2; j++)
		{
			sp.m_Score[j] = 0;
		}

		//將初始化後的選手放入容器中
		this->v1.push_back(i + 1000);
		this->m_Speaker.insert(std::pair<int, Speaker>(i + 1000, sp));
	}
}

void SpeechManager::speechDraw()
{
	std::cout << "第" << this->m_Round << "輪比賽開始抽簽" << std::endl;
	std::cout << "=*=*=*=*=*=*=*=*=" << std::endl;
	std::cout << "抽簽後演講順序如下: " << std::endl;
	if (this->m_Round == 1)
	{
		std::random_shuffle(this->v1.begin(), this->v1.end());
		for (std::vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;
	}
	else
	{
		std::random_shuffle(this->v2.begin(), this->v2.end());
		for (std::vector<int>::iterator it = v2.begin(); it != v2.end(); it++)
		{
			std::cout << *it << " ";
		}
		std::cout << std::endl;
	}
	std::cout << "=*=*=*=*=*=*=*=*=" << std::endl;
	system("pause");
	std::cout << std::endl;
}

void SpeechManager::speechContest()
{
	std::cout << "==========第" << this->m_Round << "輪比賽開始==========" << std::endl;

	//定義一個臨時存放需要比賽的選手的容器
	std::vector<int> v_Src;
	if (this->m_Round == 1)
	{
		v_Src = v1;
	}
	else
	{
		v_Src = v2;
	}

	//定義一個臨時存放平均分的容器
	std::multimap<double, int, std::greater<int>> groupScore;

	//定義一個分組變數,記錄人數
	//目的是讓每6個選手為1組
	int grouping = 0;

	//取出需要參賽的選手的編號
	for (std::vector<int>::iterator it = v_Src.begin(); it != v_Src.end(); it++)
	{
		++grouping;

		//定義評委打分容器,為每一組中的每一位選手打分
		//選用deque容器是因為饞它的pop_back和pop_front
		std::deque<double> scoring;

		for (std::vector<int>::iterator it = v_Src.begin(); it != v_Src.end(); it++)
		{
			double score = (rand() % 601 + 400) / 10.0f;	//(0+600~400+600) / 10 = 60~100
			scoring.push_back(score);
			//std::cout << score << " ";
		}
		//std::cout << std::endl;

		//打分結束後需要計算平均分
		std::sort(scoring.begin(), scoring.end(), std::greater<double>());
		scoring.pop_front();		//去掉一個最低分
		scoring.pop_back();			//去掉一個最高分
		double avg = std::accumulate(scoring.begin(), scoring.end(), 0.0f) / (double)scoring.size();

		//直接把平均分插入到m_Speaker的m_Score[m_Round-1]里,第一輪里12個人都有分數
		//Speaker sp = this->m_Speaker[*it];
		//sp.m_Score[this->m_Round - 1] = avg;
		this->m_Speaker[*it].m_Score[this->m_Round - 1] = avg;

		//將平均分放入到臨時的multimap容器里
		//其中multimap的鍵值對為 <平均分, 選手編號>
		groupScore.insert(std::multimap<double, int, std::greater<int>>::value_type(avg, *it));

		//如果是第6個人或第12個人,做一次分組
		if (grouping % 6 == 0)
		{
			std::cout << "第" << grouping / 6 << "小組比賽名次如下:" << std::endl;

			for (std::multimap<double, int, std::greater<int>>::iterator it = groupScore.begin();
				it != groupScore.end(); it++)
			{
				std::cout
					<< "選手編號: " << it->second << " "
					<< "選手姓名: " << this->m_Speaker[it->second].m_Name << " "
					<< "選手分數: " << this->m_Speaker[it->second].m_Score[this->m_Round - 1]
					<< std::endl;
			}

			int count_WinSpeaker = 0;
			//取每組的前三個成績排名靠前的
			for (std::multimap<double, int, std::greater<int>>::iterator it = groupScore.begin();
				it != groupScore.end() && count_WinSpeaker < 3;
				++it, ++count_WinSpeaker)
			{
				if (this->m_Round == 1)
				{
					v2.push_back(it->second);
				}
				else
				{
					v_Victory.push_back(it->second);
				}
			}

			//每分6個人清空一次容器
			groupScore.clear();
			//美觀代碼
			if (grouping == 6) { std::cout << std::endl; }
		}
	}

	std::cout << "==========第" << this->m_Round << "輪比賽結束==========" << std::endl;
	system("pause");
}

void SpeechManager::showScore()
{
	std::cout << "==========第" << this->m_Round << "輪比賽名次==========" << std::endl;

	std::vector<int> v;
	if (this->m_Round == 1)
	{
		v = v2;
	}
	else
	{
		v = v_Victory;
	}

	for (std::vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		std::cout
			<< "選手編號: " << *it << " "
			<< "選手姓名: " << this->m_Speaker[*it].m_Name << " "
			<< "選手分數: " << this->m_Speaker[*it].m_Score[this->m_Round - 1]
			<< std::endl;
	}

	system("pause");
	system("cls");
	this->show_Menu();
}

void SpeechManager::saveRecord()
{
	std::ofstream ofs("speech.csv", std::ios::out | std::ios::app);

	for (std::vector<int>::iterator it = v_Victory.begin(); it != v_Victory.end(); it++)
	{
		ofs << *it << "," << this->m_Speaker[*it].m_Score[1] << ",";
	}
	ofs << std::endl;

	std::cout << "保存分數完成" << std::endl;
	std::cout << "本屆比賽正式結束!" << std::endl;
	this->fileIsEmpty = false;

	system("pause");
	system("cls");
}

void SpeechManager::loadRecord()
{
	std::ifstream ifs("speech.csv", std::ios::in);

	if (!ifs.is_open())
	{
		//std::cout << "文件不存在" << std::endl;
		this->fileIsEmpty = true;
		ifs.close();
		return;
	}

	char ch;
	ifs >> ch;
	if (ifs.eof())
	{
		//std::cout << "文件為空" << std::endl;
		this->fileIsEmpty = true;
		ifs.close();
		return;
	}

	//文件不為空
	this->fileIsEmpty = false;
	ifs.putback(ch);

	std::string data;
	int round = 0;
	while (ifs >> data)
	{
		//定義獲取往屆比賽信息的容器
		std::vector<std::string> v;
		int pos = -1;		//,號的標誌位
		int start = 0;

		while (true)
		{
			//尋找 以","號分割的字元串,找不到就結束
			pos = data.find(",", start);
			if (pos == -1)
			{
				break;
			}

			//substr(起始位置, 結束位置)
			std::string temp = data.substr(start, pos - start);
			start = pos + 1;
			v.push_back(temp);
		}
		//將往屆的冠軍數據插入到容器中
		this->m_Record.insert(std::map<int, std::vector<std::string>>::value_type(round, v));
		++round;
	}

	ifs.close();
}

void SpeechManager::showRecord()
{
	if (this->fileIsEmpty)
	{
		std::cout << "文件不存在,記錄為空" << std::endl;
	}
	else
	{
		for (int i = 0; i < this->m_Record.size(); i++)
		{
			std::cout
				<< "=========第" << i + 1 << "屆=========" << "\n"
				<< "冠軍編號: " << this->m_Record[i][0] << " 得分: " << this->m_Record[i][1] << "\n"
				<< "亞軍編號: " << this->m_Record[i][2] << " 得分: " << this->m_Record[i][3] << "\n"
				<< "季軍編號: " << this->m_Record[i][4] << " 得分: " << this->m_Record[i][5] << "\n"
				<< "========================" << "\n"
				<< std::endl;
		}
	}
	system("pause");
	system("cls");
}

void SpeechManager::startSpeech()
{
	for (int i = 0; i < 2; i++)
	{
		//1、抽簽
		this->speechDraw();

		//2、比賽
		this->speechContest();

		//3、顯示晉級結果
		this->showScore();

		//第二輪比賽
		++this->m_Round;
	}
	//4、保存分數
	this->saveRecord();

	//當重覆進行插入操作時會報錯,因為初始化只有一次
	//當插入次數多了之後,容器溢出
	//所以每一次都需要初始化
	this->initSpeaker();
	this->createSpeaker();
	
	//重置比賽,獲取記錄
	//載入往屆記錄
	this->loadRecord();
}

void SpeechManager::clearRecord()
{
	int choise = 0;
	std::cout << "確定要清空全部的數據嗎?" << std::endl;
	std::cout << "1.確定 ------ 0.返回" << std::endl;
	std::cin >> choise;
	
	if (choise == 1)
	{
		std::ofstream ofs("speech.csv", std::ios::trunc);

		this->initSpeaker();
		this->createSpeaker();
		this->loadRecord();

		std::cout << "清空完成!" << std::endl;
	}

	std::cout << "取消清空操作" << std::endl;

	system("pause");
	system("cls");
}

SpeechManager::~SpeechManager()
{
}

3. Main函數主體

main.cpp

#include <iostream>
#include <algorithm>
#include "speechManager.h"

int main()
{
	SpeechManager spm;
	int choise = 0;

	srand((unsigned int)time(NULL));

	while (true)
	{
		spm.show_Menu();
		std::cout << "請輸入選項: " << std::endl;
		std::cin >> choise;

		switch (choise)
		{
		case 1:		//開始比賽
			spm.startSpeech();
			break;
		case 2:		//查看往屆比賽記錄
			spm.showRecord();
			break;
		case 3:		//清空文件
			spm.clearRecord();
			break;
		case 0:
			spm.exitSystem();
			break;
		default:
			system("cls");
			break;
		}
	}

	return 0;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ##### 1 JavaScript的引入方式 JavaScript, 是一門能夠運行在瀏覽器上的腳本語言. 簡稱JS. 首先, Javascript這個名字的由來就很有意思, 不少人認為Javascript和Java貌似很像. 容易想象成Java的腳本. 但其實不然, 兩者之間沒有任何關係. 純粹 ...
  • ##### 4 表格標簽 表格標簽也是一種複合標簽。由:table,tr,td,th,thead,tbody組合,由行和列組合成,行和列交叉的地方就是單元格。在HTML中使用table來定義表格。網頁的表格和辦公軟體裡面的xls一樣,都是有行有列的。HTML使用tr標簽定義行,使用td標簽定義列。 ...
  • 隨著互聯網的發展,PDF 文件在信息交流和文檔分享中起著重要的作用。通過在 Vue 組件中實現 PDF 預覽功能,我們可以為用戶提供便捷的內容閱讀體驗。 通過閱讀本文,讀者將瞭解如何在 Vue 中實現強大的 PDF 預覽功能,為用戶提供方便的閱讀體驗。無論你是剛入門的 Vue 開發者,還是有一定經... ...
  • ##### 2 超鏈接標簽 超鏈接是瀏覽者和伺服器的交互的主要手段,也叫超級鏈接或a鏈接,是網頁中指向一個目標的連接關係,這個目標可以是網頁、網頁中的具體位置、圖片、郵件地址、文件、應用程式等。 超鏈接是網頁中最重要的元素之一。一個網站的各個網頁就是通過超鏈接關聯起來的,用戶通過點擊超鏈接可以從一個 ...
  • ![](https://img2023.cnblogs.com/blog/3076680/202308/3076680-20230816164601035-688425072.png) # 1. 變化就是軟體的特性 ## 1.1. 變化保證天天有,存活保障無處尋 ## 1.2. 非每一款軟體每天都需 ...
  • 1. 概述 對於分散式系統,人們首先對現實中的分散式系統進行高層抽象,然後做出各種假設,發展了諸如CAP, FLP 等理論,提出了很多一致性模型,Paxos 是其中最璀璨的明珠。我們對分散式系統的時序,複製模式,一致性等基礎理論特別關註。 在共識演算法的基礎上衍生了選舉演算法,並且為分散式事務提供了部分 ...
  • ##一、前言 **工廠模式常見的詞:簡單工廠、工廠方法、抽象工廠。簡單工廠不屬於23種經典設計模式,但通常將它作為學習其他工廠模式的基礎。** ##二、簡單工廠 ###1、定義 **定義一個工廠類,它可以根據參數的不同返回不同類型的實例,被創建的實例通常都具有共同的父類。由於簡單的工廠模式中用於創建 ...
  • 背景: PyAV是一個用於音頻和視頻處理的Python庫,它提供了一個簡單而強大的介面,用於解碼、編碼、處理和分析各種音頻和視頻格式。PyAV基於FFmpeg多媒體框架,它本質上是FFmpeg 的Python綁定,因此可以利用FFmpeg的功能來處理各種多媒體任務。 基本概念: 編解碼器(Codec ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...