Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //Hydreigon_Lord
- //Kahoot Simulation
- #include <iostream>
- #include <fstream>
- #include <random>
- #include <cmath>
- #include <string>
- #include <vector>
- #include <iomanip>
- using namespace std;
- /* Global constants */
- const double QUESTION_TIME_LIMIT = 20;
- const double MEDIAN_KNOWLEDGE = 0.7;
- const double MEDIAN_SPEED = 0.7;
- const double MEDIAN_ACCURACY = 0.95;
- const string PLAYER_FILENAME = "players.txt";
- mt19937_64* en; //Pointer to global random engine
- exponential_distribution<>* expdist; //Pointer to an exponential distribution to be used as needed
- /* Generates a random double in the [0,1) range. Equivalent to randRoot(0.5). */
- double randDec()
- {
- return generate_canonical<double, 64>(*en);
- }
- /* Generates a random double in the [0,1) range on a distribution with the median specified. */
- double randRoot(double median)
- {
- double p = -log2(abs(median - 0.5) + 0.5);
- double r = generate_canonical<double, 64>(*en);
- if (median < 0.5)
- return 1 - pow(1 - r, p);
- else
- return pow(r, p);
- }
- /* Class to represent players in the simulation. */
- class Player
- {
- public:
- /* Constructors */
- Player() : score(0), streak(0)
- {
- numPlayers++;
- knowledge = randRoot(MEDIAN_KNOWLEDGE);
- speed = randRoot(MEDIAN_SPEED);
- accuracy = randRoot(MEDIAN_ACCURACY);
- name = "Player " + to_string(numPlayers);
- }
- Player(string n) : Player()
- {
- name = n;
- }
- /* Getters */
- unsigned getScore() const
- {
- return score;
- }
- unsigned getStreak() const
- {
- return streak;
- }
- unsigned getCorrect() const
- {
- unsigned c = 0;
- for (bool r: responses)
- c += r;
- return c;
- }
- unsigned getIncorrect() const
- {
- return getNumQuestions() - getCorrect() - getTimeouts();
- }
- unsigned getTimeouts() const
- {
- unsigned timeouts = 0;
- for (double t: responseTimes) {
- if (t < 0) {
- timeouts++;
- }
- }
- return timeouts;
- }
- unsigned getNumQuestions() const
- {
- return (unsigned)responses.size();
- }
- bool getResponseAt(unsigned questionNum) const
- {
- return responses[questionNum - 1];
- }
- double getResponseTimeAt(unsigned questionNum) const
- {
- return responseTimes[questionNum - 1];
- }
- unsigned getRankAt(unsigned questionNum) const
- {
- return ranks[questionNum - 1];
- }
- double getAvgResponseTime() const
- {
- unsigned count = 0;
- double sum = 0;
- for (double t: responseTimes) {
- if (t > -1) {
- count++;
- sum += t;
- }
- }
- if (count == 0) //The player timed out on every question
- return -1;
- return sum / count;
- }
- int getLastRankChange() const
- {
- unsigned s = (unsigned)ranks.size();
- if (s < 2) {
- return 0;
- }
- return ranks[s - 2] - ranks[s - 1];
- }
- double getKnowledge() const
- {
- return knowledge;
- }
- double getSpeed() const
- {
- return speed;
- }
- double getAccuracy() const
- {
- return accuracy;
- }
- string getName() const
- {
- return name;
- }
- /* Setter */
- void setName(string n)
- {
- name = n;
- }
- void addRank(unsigned r)
- {
- ranks.push_back(r);
- }
- /* This method updates this player's statistics according to the outcome of a random question with the specified time limit in seconds. */
- void simQuestion(double timeLimit)
- {
- //Figure out how long it took for the player to answer
- expdist = new exponential_distribution<>(speed);
- double responseTime = (*expdist)(*en) * timeLimit / 10;
- delete expdist;
- responseTimes.push_back((responseTime <= timeLimit) ? responseTime : -1);
- //Determine whether or not the player was correct
- bool isCorrect;
- if (responseTime > timeLimit) { //timeout
- isCorrect = false;
- } else if (randDec() < knowledge && randDec() < accuracy) {
- isCorrect = true;
- } else { //guessing or misclick
- isCorrect = randDec() < 0.25;
- }
- responses.push_back(isCorrect);
- //Update the player's score and answer streak
- if (isCorrect) {
- score += (int)round(500 + 100 * (streak > 5 ? 5 : streak) + 500 * (1 - responseTime / timeLimit));
- streak++;
- } else {
- streak = 0;
- }
- }
- private:
- static int numPlayers;
- /* Player characteristics */
- double knowledge, speed, accuracy;
- string name;
- /* Player statistics */
- unsigned score, streak;
- vector<bool> responses;
- vector<double> responseTimes;
- vector<unsigned> ranks;
- };
- int Player::numPlayers = 0;
- int main()
- {
- //Seed random engine with a non-deterministic device if possible
- random_device rd;
- seed_seq seq{rd(), rd(), rd(), rd(), rd(), rd(), rd(), rd()};
- en = new mt19937_64(seq);
- int playerCount, questionCount;
- Player* players;
- vector<string> playerNames;
- //Try to read the player list from the players.txt file
- //The file should contain one player name per line
- fstream fin(PLAYER_FILENAME, ios::in);
- if (fin) {
- cout << "File " << PLAYER_FILENAME << " found. Reading players from file...\n";
- while (!fin.eof()) {
- string n;
- getline(fin, n);
- playerNames.push_back(n);
- }
- playerCount = (int)playerNames.size();
- cout << "Successfully read file.\n";
- fin.close();
- } else {
- cout << "File " << PLAYER_FILENAME << " not found. How many players? ";
- cin >> playerCount;
- }
- players = new Player[playerCount];
- if (playerNames.size() == playerCount) {
- for (int i = 0; i < playerCount; i++) {
- players[i].setName(playerNames[i]);
- }
- }
- //Get the number of questions
- cout << "How many questions? ";
- cin >> questionCount;
- //Ask the user for two files to output the game report to
- string filename;
- cout << "Enter the filename for the game report: ";
- cin >> filename;
- fstream report_out(filename, ios::out);
- report_out << "*** Kahoot Game Report ***\n\n";
- if (!report_out) {
- cout << "Error writing to file " << filename << endl;
- exit(1);
- }
- cout << "Enter the filename for the detailed report: ";
- cin >> filename;
- fstream detail_out(filename, ios::out);
- detail_out << "*** Kahoot Detailed Report ***\n\n";
- if (!detail_out) {
- cout << "Error writing to file " << filename << endl;
- exit(2);
- }
- cout << "Successfully opened files for writing.\n";
- //Simulate the game
- report_out << left << setprecision(5) << fixed << showpoint;
- detail_out << left << setprecision(5) << fixed << showpoint;
- //Report player stats
- report_out << "\n=== Player Stats ===\n";
- detail_out << "\n=== Player Stats ===\n";
- detail_out << setw(40) << "Name" << setw(10) << "Knowledge" << setw(10) << "Speed" << setw(10) << "Accuracy" << setw(10) << "Stat Total" << endl;
- detail_out << string(80, '-') << endl;
- double topKnowledge = -1, topSpeed = -1, topAccuracy = -1, topStatTotal = -1;
- string topKnowledgePlayer = "", topSpeedPlayer = "", topAccuracyPlayer = "", topStatTotalPlayer = "";
- double lowKnowledge = 2, lowSpeed = 2, lowAccuracy = 2, lowStatTotal = 7;
- string lowKnowledgePlayer = "", lowSpeedPlayer = "", lowAccuracyPlayer = "", lowStatTotalPlayer = "";
- for (int p = 0; p < playerCount; p++) {
- detail_out << setw(40) << players[p].getName();
- detail_out << setw(10) << players[p].getKnowledge();
- if (players[p].getKnowledge() > topKnowledge) {
- topKnowledge = players[p].getKnowledge();
- topKnowledgePlayer = players[p].getName();
- }
- if (players[p].getKnowledge() < lowKnowledge) {
- lowKnowledge = players[p].getKnowledge();
- lowKnowledgePlayer = players[p].getName();
- }
- detail_out << setw(10) << players[p].getSpeed();
- if (players[p].getSpeed() > topSpeed) {
- topSpeed = players[p].getSpeed();
- topSpeedPlayer = players[p].getName();
- }
- if (players[p].getSpeed() < lowSpeed) {
- lowSpeed = players[p].getSpeed();
- lowSpeedPlayer = players[p].getName();
- }
- detail_out << setw(10) << players[p].getAccuracy();
- if (players[p].getAccuracy() > topAccuracy) {
- topAccuracy = players[p].getAccuracy();
- topAccuracyPlayer = players[p].getName();
- }
- if (players[p].getAccuracy() < lowAccuracy) {
- lowAccuracy = players[p].getAccuracy();
- lowAccuracyPlayer = players[p].getName();
- }
- double statTotal = players[p].getKnowledge() + players[p].getSpeed() + players[p].getAccuracy();
- detail_out << setw(10) << statTotal;
- if (statTotal > topStatTotal) {
- topStatTotal = statTotal;
- topStatTotalPlayer = players[p].getName();
- }
- if (statTotal < lowStatTotal) {
- lowStatTotal = statTotal;
- lowStatTotalPlayer = players[p].getName();
- }
- detail_out << endl;
- }
- report_out << topKnowledgePlayer << " has the highest Knowledge stat, at " << topKnowledge << ".\n";
- report_out << topSpeedPlayer << " has the highest Speed stat, at " << topSpeed << ".\n";
- report_out << topAccuracyPlayer << " has the highest Accuracy stat, at " << topAccuracy << ".\n";
- report_out << topStatTotalPlayer << " has the highest stat total, at " << topStatTotal << ".\n";
- report_out << lowKnowledgePlayer << " has the lowest Knowledge stat, at " << lowKnowledge << ".\n";
- report_out << lowSpeedPlayer << " has the lowest Speed stat, at " << lowSpeed << ".\n";
- report_out << lowAccuracyPlayer << " has the lowest Accuracy stat, at " << lowAccuracy << ".\n";
- report_out << lowStatTotalPlayer << " has the lowest stat total, at " << lowStatTotal << ".\n";
- report_out << setprecision(3);
- detail_out << setprecision(3);
- for (int q = 1; q <= questionCount; q++) {
- //Write the question header to the reports
- cout << "Simulating question " << q << " of " << questionCount << ".\n";
- report_out << "\n=== Question " << q << " of " << questionCount << " ===\n";
- detail_out << "\n=== Question " << q << " of " << questionCount << " ===\n";
- detail_out << setw(8) << "Rank" << setw(40) << "Name" << setw(8) << "Time" << setw(8) << "Correct" << setw(8) << "Score" << setw(8) << "Streak" << endl;
- detail_out << string(80, '-') << endl;
- //Simulate the question
- int numCorrect = 0;
- for (int p = 0; p < playerCount; p++) {
- players[p].simQuestion(QUESTION_TIME_LIMIT);
- if (players[p].getResponseAt(q)) {
- numCorrect++;
- }
- }
- //Sort the player array (selection sort is used here)
- for (int i = 0; i < playerCount - 1; i++) {
- int maxScore = players[i].getScore();
- int maxIndex = i;
- for (int j = i + 1; j < playerCount; j++) {
- if (players[j].getScore() > maxScore) {
- maxScore = players[j].getScore();
- maxIndex = j;
- }
- }
- Player temp = players[i];
- players[i] = players[maxIndex];
- players[maxIndex] = temp;
- }
- //Update player ranks
- unsigned lastScore = players[0].getScore();
- unsigned lastRank = 1;
- players[0].addRank(1);
- for (int p = 1; p < playerCount; p++) {
- if (players[p].getScore() == lastScore) {
- players[p].addRank(lastRank);
- } else {
- lastScore = players[p].getScore();
- lastRank = p + 1;
- players[p].addRank(lastRank);
- }
- }
- //Report the question results in detail
- for (int p = 0; p < playerCount; p++) {
- detail_out << setw(8) << players[p].getRankAt(q);
- detail_out << setw(40) << players[p].getName();
- double time = players[p].getResponseTimeAt(q);
- if (time < 0) {
- detail_out << setw(8) << "Timeout";
- } else {
- detail_out << setw(8) << time;
- }
- detail_out << setw(8) << (players[p].getResponseAt(q) ? 'Y' : 'N');
- detail_out << setw(8) << players[p].getScore();
- detail_out << setw(8) << players[p].getStreak();
- detail_out << endl;
- }
- detail_out << string(80, '-') << endl;
- //Report the rank changes and point differentials in detail
- int highestClimb = 0, highestFall = 0;
- string highestClimbPlayer = "", highestFallPlayer = "";
- for (int p = 0; p < playerCount; p++) {
- detail_out << players[p].getName() << " ";
- if (q > 1) {
- int rankChange = players[p].getLastRankChange();
- if (rankChange > 0) {
- detail_out << "moved up " << rankChange << " rank" << (rankChange > 1 ? "s" : "");
- } else if (rankChange < 0) {
- detail_out << "moved down " << -rankChange << " rank" << (rankChange < -1 ? "s" : "");
- } else {
- detail_out << "did not change rank";
- }
- if (rankChange > highestClimb) {
- highestClimb = rankChange;
- highestClimbPlayer = players[p].getName();
- }
- if (-rankChange > highestFall) {
- highestFall = -rankChange;
- highestFallPlayer = players[p].getName();
- }
- detail_out << " and ";
- }
- if (p == 0) {
- detail_out << "is in the lead";
- } else {
- unsigned diff = players[p - 1].getScore() - players[p].getScore();
- if (diff == 0) {
- detail_out << "is tied with " << players[p - 1].getName();
- } else {
- detail_out << "is " << diff << " point" << (diff > 1 ? "s" : "") << " behind " << players[p - 1].getName();
- }
- }
- detail_out << ".\n";
- }
- //Report the correct answer rate and the top and bottom 5
- report_out << numCorrect << " out of " << playerCount << " players answered this question correctly.\n";
- if (playerCount <= 10) {
- for (int p = 0; p < playerCount; p++) {
- report_out << players[p].getRankAt(q) << ". " << players[p].getName() << " " << players[p].getScore() << endl;
- }
- } else {
- for (int p = 0; p < 5; p++) {
- report_out << players[p].getRankAt(q) << ". " << players[p].getName() << " " << players[p].getScore() << endl;
- }
- report_out << "...\n";
- for (int p = playerCount - 5; p < playerCount; p++) {
- report_out << players[p].getRankAt(q) << ". " << players[p].getName() << " " << players[p].getScore() << endl;
- }
- }
- //Report the highest climber and highest faller
- if (highestClimb > 0) {
- report_out << highestClimbPlayer << " is the highest climber, having moved up " << highestClimb << " rank" << (highestClimb > 1 ? "s" : "") << ".\n";
- }
- if (highestFall > 0) {
- report_out << highestFallPlayer << " is the highest faller, having moved down " << highestFall << " rank" << (highestFall > 1 ? "s" : "") << ".\n";
- }
- //Report interesting answer streaks
- unsigned highestStreak = 0, perfectStreaks = 0;
- string highestStreakPlayer = "", comebackPlayer = "";
- for (int p = 0; p < playerCount; p++) {
- if (players[p].getStreak() > highestStreak) {
- highestStreak = players[p].getStreak();
- highestStreakPlayer = players[p].getName();
- }
- if (players[p].getStreak() == q) {
- perfectStreaks++;
- }
- if (q > 3 && players[p].getStreak() == 3) {
- comebackPlayer = players[p].getName();
- }
- }
- if (perfectStreaks > 1) {
- report_out << perfectStreaks << " players are still perfect at answer streak " << q << ".\n";
- } else if (perfectStreaks == 1) {
- report_out << highestStreakPlayer << " is still perfect at answer streak " << q << ".\n";
- } else if (highestStreak > 0) {
- report_out << highestStreakPlayer << " has the highest answer streak, at " << highestStreak << ".\n";
- } else {
- report_out << "No-one has an answer streak.\n";
- }
- if (!comebackPlayer.empty()) {
- report_out << "With an answer streak of 3, " << comebackPlayer << " could be staging a comeback.\n";
- }
- }
- //Report the final results
- report_out << "\n=== Final Results ===\n";
- cout << left << setw(8) << "Rank" << setw(40) << "Name" << setw(8) << "Score" << setw(8) << "Correct" << endl;
- report_out << setw(8) << "Rank" << setw(40) << "Name" << setw(8) << "Score" << setw(8) << "Correct" << endl;
- cout << string(64, '-') << endl;
- report_out << string(64, '-') << endl;
- for (int p = 0; p < playerCount; p++) {
- cout << setw(8) << players[p].getRankAt(questionCount);
- cout << setw(40) << players[p].getName();
- cout << setw(8) << players[p].getScore();
- cout << setw(8) << players[p].getCorrect();
- cout << endl;
- report_out << setw(8) << players[p].getRankAt(questionCount);
- report_out << setw(40) << players[p].getName();
- report_out << setw(8) << players[p].getScore();
- report_out << setw(8) << players[p].getCorrect();
- report_out << endl;
- }
- //Report detailed statistics
- detail_out << "\n=== Final Statistics ===\n";
- detail_out << setw(8) << "Rank" << setw(40) << "Name" << setw(questionCount > 7 ? questionCount + 1 : 8) << "Answers" << setw(8) << "AvgTime" << setw(8) << "Timeout" << setw(8) << "Corr%" << endl;
- detail_out << string(questionCount > 7 ? questionCount + 73 : 80, '-') << endl;
- for (int p = 0; p < playerCount; p++) {
- detail_out << setw(8) << players[p].getRankAt(questionCount);
- detail_out << setw(40) << players[p].getName();
- string answers = "";
- for (int q = 1; q <= questionCount; q++) {
- if (players[p].getResponseTimeAt(q) < 0) {
- answers += "T";
- } else if (players[p].getResponseAt(q)) {
- answers += "Y";
- } else {
- answers += "N";
- }
- }
- detail_out << setw(questionCount > 7 ? questionCount + 1 : 8) << answers;
- double avgResponseTime = players[p].getAvgResponseTime();
- if (avgResponseTime < 0) {
- detail_out << setw(8) << "N/A";
- } else {
- detail_out << setw(8) << avgResponseTime;
- }
- detail_out << setw(8) << players[p].getTimeouts();
- if (avgResponseTime >= 0) {
- detail_out << setw(8) << 100.0 * players[p].getCorrect() / (questionCount - players[p].getTimeouts());
- }
- detail_out << endl;
- }
- //Cleanup
- delete en;
- delete[] players;
- return 0;
- }
Add Comment
Please, Sign In to add comment