【学习笔记】“STL演讲比赛流程管理系统”作业总结

1. 作业内容:

1.1 比赛规则

  • 学校举行一场演讲比赛,共有12个人参加。比赛共两轮,第一轮为淘汰赛,第二轮为决赛。
  • 比赛方式:分组比赛,每组6个人;选手每次要随机分组,进行比赛
  • 每名选手都有对应的编号,如 10001 ~ 10012 
  • 第一轮分为两个小组,每组6个人。 整体按照选手编号进行抽签后顺序演讲。
  • 当小组演讲完后,淘汰组内排名最后的三个选手,前三名晋级,进入下一轮的比赛。
  • 第二轮为决赛,前三名胜出
  •  每轮比赛过后需要显示晋级选手的信息

1.2 程序功能

  • 开始演讲比赛:完成整届比赛的流程,每个比赛阶段需要给用户一个提示,用户按任意键后继续下一个阶段

  • 查看往届记录:查看之前比赛前三名结果,每次比赛都会记录到文件中,文件用.csv后缀名保存

  • 清空比赛记录:将文件中数据清空

  • 退出比赛程序:可以退出当前程序

 图1. 老师程序运行时的初始界面


2. 作业复盘

写该文章时,还没有参看老师的标准答案。是按照1.1及1.2的内容,然后“复刻”老师的程序打印输出内容完成的。

在受标准答案“影响”之前,把自己的作业过程记录于此。之后再与标准答案做对比总结。

项目结构如图2:

 图2 项目结构

首先说主函数(SCMS_main.cpp),具有如图1所示的结构和功能,那么和之前的作业内容一样,我也使用了switch语句,通过用户输入来选择不同的功能。

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

//SCMS means Speech Contest Management System
using namespace std;

int Cround = 1; // Used to record contest round

int main()
{
	srand((unsigned int)time(NULL)); // This is a random seed
	SCMCmanager scmc;
	

	int userSelect = 0;
	while (true)
	{
		scmc.showMenu();
		cout << "Please enter your choice:" << endl;
		cin >> userSelect;
		switch (userSelect)
		{
		case 1: // 1. start speech competition and save result in csv file
			Cround = scmc.startContest(Cround);
			break;
		case 2:
			scmc.viewPreResult();
			break;
		case 3:
			scmc.clearResult();
			break;
		case 0:
		{
			scmc.exitSystem();
			break;
		}
		default:
			break;
		}
	}

	system("pause");

	return 0;
}

而每个case下的功能实现,我都统一放在“功能管理”的文件下,我起名叫SCMS_management.cpp,函数统一注册在SCMS_ management.h文件下( = =专业说法是啥子),下面详细展开每个功能。

2.1 显示菜单(showMenu)

这个功能算是最基本最简单的功能了,按照老师的内容照猫画虎就可以。没啥说的。

// function showMenu
void SCMCmanager::showMenu()
{
	cout << "****************************************************" << endl;
	cout << "********** Welcome to the Speech Contest! **********" << endl;
	cout << "********** 1.Start the Speech Contest **************" << endl;
	cout << "********** 2.View previous competition records *****" << endl;
	cout << "********** 3.Clear game records ********************" << endl;
	cout << "********** 0.Exit system. **************************" << endl;
	cout << "****************************************************" << endl;
}

2.2 退出系统(exitSystem)

对应主函数中case 0的选项,一个很简单的功能,没啥说的X2。

void SCMCmanager::exitSystem()
{
	cout << "Welcome to use next time." << endl;
	system("pause");
	system("cls");
	exit(0);
}

2.3 比赛开始(startContest)**重点**

对应主函数中case1的选项,在我写这个作业时,这个功能是我认为项目中最复杂的功能。

对我的实现思路进行复盘:

比赛开始,我通过createPlayer函数创建12名选手并进行抽签(打乱)。

选手是我定义的一个自定义数据类型Player类。如下面的代码段所示。Player首先继承了一个抽象类,AbsPlayer类(现在看这一步有些脱裤子放P……)。Player类中重载了Player类的构造函数。

// AbsPlayer.h 
// 这是我定义的一个抽象类
#pragma once
#include<iostream>
#include<string>

using namespace std;

class AbstractPlayer
{
public:

	string m_name;
	int m_ID;
	float m_sccore;
};
// ----------------------------------
// Player.h 继承抽象的AbsPlayer类
#pragma once
#include<iostream>
#include"AbsPlayer.h"

using namespace std;

class Player : public AbstractPlayer
{
public:
	Player(int id,string name);

};
// ----------------------------------
// Player.cpp 具体实现
#include "Player.h"

Player::Player(int id,string name)
{
	this->m_name = name;
	this->m_ID = id;
}

 按照重载的Player构造创建选手后(他们有了名字和编号),将他们放在一个保存选手的vector容器中,容器起名vPlayer。随后使用random_shuffle算法将容器内12名选手顺序打乱,这个步骤就是完成抽签过程。随后将打乱后的vPlayer数组进行返回方便后续使用。

这里记录自己做作业过程中存在的问题:

  1. 在创建vPlayer数组时需要开辟在堆区而不是栈区,因为它是一个函数内部的局部变量,如果想在函数外继续使用就需要这么做。第一次写时我将vector直接创建成局部变量,在return后发现函数体外vector的size为0.(我的个人理解,依然一知半解)
  2. 在vector循环推入数据时,我错误的把循环条件写成了vector.size(),应该使用capacity,才是我开辟的vector的大小。
  3. 在打乱vector时,我首先以为针对自定义数据类型需要制定一个打乱方式,比如根据选手的ID进行打乱,后来发现自定义数据类型可以直接使用random_shuffle打乱,好耶。

createPlayer函数如下:

// Create player
// mark,注意返回值的方式,以及vector应该开辟在堆区而不是栈区
vector<Player> * SCMCmanager::createPlayer()
{
	vector<Player> *vPlayer = new vector<Player>;
	vPlayer->reserve(12);
	string nameseed = "ABCDEFGHIJKL";
	int PlayerID = 10001;
	for (int i = 0; i < vPlayer->capacity(); i++) // mark,错误的使用了size和capacity
	{
		string PlayerName = "Player ";
		PlayerName += nameseed[i];
		Player p = Player(PlayerID, PlayerName);
		vPlayer->push_back(p);
		PlayerID++;
	}
	random_shuffle(vPlayer->begin(), vPlayer->end());// mark, ramdom_shuffle对于自定义数据类型可以直接使用
	return vPlayer;
	// test
	//for (vector<Player>::iterator it = vPlayer->begin(); it != vPlayer->end(); it++)
	//{
	//	cout << "Name:" << it->m_name << " ID:" << it->m_ID << endl;
	//}
	
}

完成了选手创建和抽签后就是打印信息,告诉用户比赛选手已经抽签完毕,并且抽签后的比赛顺序是什么。

这里说一下我的代码中Cround这个参数,它是用于记录比赛轮数的参数。是定义在主函数中的一个全局变量。怎么把它传递给主函数呢?我的想法是只能return出去……( = =return是让我玩“明白”了)所有比赛结束,继续重置为1.(可能这么说还是不清晰,之所以写了这个参数是因为老师的演示中,每次比赛分为2轮,系统会自动打印第1轮比赛巴拉巴拉……第2轮又巴拉巴拉……所以我加了这个参数,写死的话很low?)

随后比赛正式开始,要把打乱顺序后的12个选手分到2个小组中。思前想后,我又引入了分组函数,gruoping。一样的配方一样的味道,传入一个待分组的vector数组,不妨就叫V。然后在这个函数中创建2个新的分组,叫做G1,G2吧(G2在S赛的表现还是……扯远了),还是开辟在堆区。然后通过遍历,把待分类的数组V前6个传入G1,后6个传入G2(有个板凳队员了,ヾ(✿゚▽゚)ノ)。然后!我TM不知道如何return两个参数,然后我就构造了一个保存vector的vector(套娃达成!),把它return出去了……

grouping函数如下:

vector<vector<Player>> * SCMCmanager::grouping(vector<Player> * vp)
{
	vector<Player> *groupOne = new vector<Player>;
	vector<Player> *groupTwo = new vector<Player>;
	for (int i = 0; i < 12; i++)
	{
		if (i < 6)
		{
			groupOne->push_back(vp->at(i));
		}
		else
		{
			groupTwo->push_back(vp->at(i));
		}
	}

	vector<vector<Player>> * vOfTwoGroup = new vector<vector<Player>>;
	vOfTwoGroup->push_back(*groupOne);
	vOfTwoGroup->push_back(*groupTwo);
	return vOfTwoGroup;
	// test
	//for (auto it = gruopOne->begin(); it != gruopOne->end(); it++)
	//{
	//	cout << it->m_ID << " ";
	//}
	//cout << endl;
	//for (auto it = gruopTwo->begin(); it != gruopTwo->end(); it++)
	//{
	//	cout << it->m_ID << " ";
	//}

}

把12个选手分了组之后,就比赛打分, 按照分数降序排列。我写了一个scorePlayer函数。

函数思路也很简单,传入一个有6个队员的待打分数组,遍历队员,对每个队员随机打10次分,然后求平均分。为了分数不至于太离谱,每个人都可以保底60分。打完分之后,就是对vector数组中自定义的数据进行排序。必须告诉编译器排序的规则。我使用了谓词来完成这样的步骤。

排序规则如下(对于这种使用还是不熟练,查阅了很多资料):

// Greatersort.h
#pragma once
#include<iostream>
#include"Player.h"

using namespace std;
class GreaterSort
{
public:
	bool operator()(const Player & p1, const Player & p2);
};
// ---------------------------
// Greatersort.cpp
#include "GreaterSort.h"

bool GreaterSort::operator()(const Player & p1, const Player & p2)
{
	return (p1.m_sccore > p2.m_sccore);
}

scorePlayer函数如下:

// Give socre to each players
vector<Player> * SCMCmanager::scorePlayer(vector<Player> * vp)
{
	for (int i = 0; i < vp->size(); i++)
	{
		//queue<float> qScore;
		float score = 0;
		float finalScore = 0;
		for (int j = 0; j < 10; j++)
		{
			score = (float)(rand() % 41 + 60);
			//qScore.push(score);
			finalScore += score;
		}

		finalScore = finalScore / 10;
		vp->at(i).m_sccore = finalScore;
	}
	// mark,对自定义数据类型的sort排序还不熟练
	sort(vp->begin(), vp->end(), GreaterSort());
	for (vector<Player>::iterator it = vp->begin(); it != vp->end(); it++)
	{
		cout << "ID:" << (*it).m_ID << "  Name:" << (*it).m_name
			<< "  Socre:" << (*it).m_sccore << endl;
	}
	cout << endl;
	return vp;
}

 完成了选手打分排序,下面就是每队的前三名晋级,继续第二轮的PK。我就创建了一个groupOne的vector,把每个队伍的前三名推入(2X3 6个人)。

然后打印信息,第一轮比赛结束,告知用户第一轮的晋级者是哪些(遍历groupOne就行)

随后第二轮比赛开始,流程和第一轮都是一样的,复用了一些功能函数。直至第二轮比赛结束,也代表这一届的比赛彻底结束。需要保存比赛结果至CSV文件中,这一步也很简单。

我写了一个saveCSV函数,内容如下(FILENAME是自己define的)

void SCMCmanager::saveCSV(vector<Player> * vp)
{
	ofstream ofs;
	ofs.open(FILENAME, ios::app);
	for(vector<Player>::iterator it = vp->begin(); it != vp->end(); it++)
	{
		ofs << it->m_ID << "," << it->m_sccore << ",";
	}
	ofs << endl;
	ofs.close();
}

完成这一步之后,告诉用户比赛结果保存完毕!该清屏清屏,这个最难写的功能就搞定! 

startContest函数如下:

// Start Contest
int SCMCmanager::startContest(int Cround)
{
	vector<Player> * vPlayer = createPlayer();
	//cout << vPlayer->size() << endl;

	cout << "The " << Cround << " round players are drawing lots" << endl;
	cout << "------------------------" << endl;
	cout << "After drawing lots, the speech sequence is as follows:" << endl;
	for (vector<Player>::iterator it = vPlayer->begin(); it != vPlayer->end(); it++)
	{
		cout << it->m_ID << " ";
	}
	cout << endl;
	cout << "------------------------" << endl;
	system("pause");

	cout << "----------The " << Cround << " round officially begin----------" 
		<< endl;
	vector<vector<Player>> * vOfTwoGroup = grouping(vPlayer);
	cout << "The ranking of the first group is as follows:" << endl;
	vector<Player> * groupOne =  scorePlayer(&vOfTwoGroup->at(0));
	cout << "The ranking of the second group is as follows:" << endl;
	vector<Player> * groupTwo = scorePlayer(&vOfTwoGroup->at(1));
	// After round one,each gourp first three will put in this vecoter
	vector<Player>  * groupRoundOne = new vector<Player>; 
	groupRoundOne->reserve(6);
	for (int i = 0; i < 3; i++)
	{
		groupRoundOne->push_back(groupOne->at(i));
		groupRoundOne->push_back(groupTwo->at(i));
	}
	sort(groupRoundOne->begin(), groupRoundOne->end(), GreaterSort());
	cout << "----------The " << Cround << " round officially end------------"
		<< endl;
	system("pause");

	cout << "----------The " << Cround << " round contestants are as follows:-------"
		<< endl;
	for (vector<Player>::iterator it = groupRoundOne->begin(); it != groupRoundOne->end(); it++)
	{
		cout << "ID:" << (*it).m_ID << "  Name:" << (*it).m_name
			<< "  Socre:" << (*it).m_sccore << endl;
	}
	system("pause");
	system("cls");
	// round one finished....

	// round 2 start
	Cround++;
	cout << "The " << Cround << " round players are drawing lots" << endl;
	cout << "------------------------" << endl;
	cout << "After drawing lots, the speech sequence is as follows:" << endl;
	random_shuffle(groupRoundOne->begin(), groupRoundOne->end());
	for (vector<Player>::iterator it = groupRoundOne->begin(); it != groupRoundOne->end(); it++)
	{
		cout << it->m_ID << " ";
	}
	cout << endl;
	cout << "------------------------" << endl;
	system("pause");

	cout << "----------The " << Cround << " round officially begin------------"
		<< endl;
	cout << "The ranking of the first group is as follows:" << endl;
	groupRoundOne= scorePlayer(groupRoundOne);
	vector<Player>  * groupRoundTwo = new vector<Player>;
	groupRoundOne->reserve(3);
	for (int i = 0; i < 3; i++)
	{
		groupRoundTwo->push_back(groupRoundOne->at(i));
	}
	sort(groupRoundTwo->begin(), groupRoundTwo->end(), GreaterSort());
	cout << "----------The " << Cround << " round officially end------------"
		<< endl;
	system("pause");

	cout << "----------The " << Cround << " round contestants are as follows:-------"
		<< endl;
	for (vector<Player>::iterator it = groupRoundTwo->begin(); it != groupRoundTwo->end(); it++)
	{
		cout << "ID:" << (*it).m_ID << "  Name:" << (*it).m_name
			<< "  Socre:" << (*it).m_sccore << endl;
	}
	system("pause");
	system("cls");

	// after contest save file (*.csv)
	saveCSV(groupRoundTwo);
	cout << "Contest results saved" << endl;
	cout << "The competition is over!" << endl;
	system("pause");
	system("cls");

	Cround = 1;
	return Cround;
}

 2.4 查看比赛结果(viewPreResult)

这一步说白了就是读取本地的CSV文件内容,然后打印信息。

我的csv文件内容如下:

每行保存的按分数从高到低的选手ID,分数,每个数据“,”隔开. 

 这里遇到的难点就是如何读取csv文件,并按照逗号间隔开的问题。查阅了些资料,成功解决。

viewPreResult函数如下:

void SCMCmanager::viewPreResult()
{
	int Cround = 0;
	bool fileIsEmpty = false;
	ifstream ifs;
	ifs.open(FILENAME, ios::in);
	if (!ifs.is_open())
	{
		fileIsEmpty = true;
		cout << "File open filed." << endl;
		return;
	}
	if (ifs.eof())
	{
		cout << "File is empty!" << endl;
		fileIsEmpty = true;
		ifs.close();
		return;
	}
	// 如何读取CSV文件?
	string line;
	vector<vector<string>> strArray;
	while (getline(ifs, line))
	{
		Cround++;
		stringstream ss(line);
		string str;
		vector<string> lineArray;
		while (getline(ss, str, ','))
		{
			lineArray.push_back(str);
		}
		cout << "The " << Cround << " round ";
		cout << "ID of Champion:" << lineArray.at(0);
		cout << " Score:" << lineArray.at(1);
		cout << " ID of Second-place:" << lineArray.at(2);
		cout << " Score:" << lineArray.at(3);
		cout << " ID of Third-place:" << lineArray.at(4);
		cout << " Score:" << lineArray.at(5) << endl;
	}
	Cround = 0;
	system("pause");
	system("cls");

}

2.5 清空比赛结果(clearResult)

 代码很简单,做个用户确定输入的判断。然后通过trunc方式打开文件即可,这一步就会将文件内容清空。(本地文件还在,内容为空)

void SCMCmanager::clearResult()
{
	cout << "Do you really want to clear all the record?" << endl;
	cout << "1--YES  2--No" << endl;
	int userInput = 0;
	cin >> userInput;
	if (userInput == 1)
	{
		ofstream ofs;
		ofs.open(FILENAME, ios::trunc);
		ofs.close();
		cout << "Clear succeed!" << endl;
		system("pause");
		system("cls");
	}
	else if (userInput == 2)
	{
		cout << "Cancled.." << endl;
		system("pause");
		system("cls");
		return;
	}
	else
	{
		cout << "Wrong input.." << endl;
		system("pause");
		system("cls");
		return;
	}

}

至此,作业复盘完毕。

结果展示:

 

 

 

 

 

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值