<commit_before>#include <algorithm>
#include <deque>
#include <ppl.h>
#include <random>
#include <vector>
#include <Windows.h>

#include "conio.h"
#include "Card.h"

using namespace std;

static void PopulateDeck(vector<Card*>& Deck, const bool Joker = false);
static void DealCards(deque<Card*>& PlayerDeck, deque<Card*>& ComDeck, const vector<Card*>& CardDeck);
static void CombineDecks(deque<Card*>& Deck, vector<Card*>& ToAdd);
static void TieBreaker(deque<Card*>& PlayerDeck, deque<Card*>& ComDeck, vector<Card*>& TempDeck);

int main()
{
	cout << "Enter the number of games to simulate: ";
	unsigned iterations;
	cin >> iterations;

	// Grab # of logical cores
	unsigned threads = thread::hardware_concurrency();
	cout << "Enter number of threads (detected " << threads << "): ";
	cin >> threads;

	// Ugly windows performace tracking
	LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds;
	LARGE_INTEGER Frequency;
	
	QueryPerformanceFrequency(&Frequency);
	QueryPerformanceCounter(&StartingTime);

	// Stats (Playerwins, Computerwins, Draws)
	unsigned Stats[] = { 0, 0, 0 };

	// Calculate the workload for each thread
	vector<int> workload;

	unsigned extra = iterations % threads;
	unsigned perIteration = (iterations - extra) / threads;

	for (unsigned i = 0; i < threads; i++)
	{
		if (extra > 0)
		{
			workload.push_back(perIteration + 1);
			extra--;
		}
		else
			workload.push_back(perIteration);
	}

	Concurrency::parallel_for(0, (int)threads, [&](int i)
	{
		// Set up the Deck
		vector<Card*> MasterDeck;	// shared_ptr adds ~50% overhead vs dumb pointers
		PopulateDeck(MasterDeck);

		auto engine = default_random_engine{};
		
		for (int j = 0; j < workload[i]; j++)
		{
			shuffle(MasterDeck.begin(), MasterDeck.end(), engine);

			// Set up the Player and Computer's decks
			deque<Card*> PlayerDeck;	
			deque<Card*> ComputerDeck;
			vector<Card*> TempDeck;

			DealCards(PlayerDeck, ComputerDeck, MasterDeck);
		
			// Main game loop
			while (PlayerDeck.size() > 0 && ComputerDeck.size() > 0)
			{
				Card * PlayerCard = PlayerDeck[0];
				PlayerDeck.pop_front();

				Card * ComputerCard = ComputerDeck[0];
				ComputerDeck.pop_front();

				TempDeck.push_back(PlayerCard);
				TempDeck.push_back(ComputerCard);

				if (PlayerCard->Value() > ComputerCard->Value())
					CombineDecks(PlayerDeck, TempDeck);
				else if (ComputerCard->Value() > PlayerCard->Value())
					CombineDecks(ComputerDeck, TempDeck);
				else
					TieBreaker(PlayerDeck, ComputerDeck, TempDeck);
			}

			if (PlayerDeck.size() == 0 && ComputerDeck.size() == 0) // Tie
				Stats[2]++;
			else if (ComputerDeck.size() == 0) // Player Win
				Stats[0]++;
			else // Computer win
				Stats[1]++;
		}
		// Clean up
		for (unsigned i = 0; i < MasterDeck.size(); i++)
			delete MasterDeck[i];
	});

	// Compute perf results
	QueryPerformanceCounter(&EndingTime);
	ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart;

	ElapsedMicroseconds.QuadPart *= 1000000;
	ElapsedMicroseconds.QuadPart /= Frequency.QuadPart;
	
	cout << endl << "Player wins: " << Stats[0] << endl
		<< "Computer wins: " << Stats[1] << endl
		<< "Draws: " << Stats[2] << endl;

	cout << endl << "Completed in: ";

	// Format and output
	if (ElapsedMicroseconds.QuadPart < 1000)
		cout << ElapsedMicroseconds.QuadPart << " us" << endl;
	else if (ElapsedMicroseconds.QuadPart < 1e+6)
	{
		double ms = (double)ElapsedMicroseconds.QuadPart / 1000.0;
		cout << ms << " ms" << endl;
	}
	else
	{
		double seconds = (double)ElapsedMicroseconds.QuadPart / (double)1e+6;
		cout << seconds << " s" << endl;
	}

	cout << "Average time per game: "
		<< (double)ElapsedMicroseconds.QuadPart / (double)iterations << " us" << endl;

	_getch();

    return 0;
}

static void TieBreaker(deque<Card*>& PlayerDeck, deque<Card*>& ComDeck, vector<Card*>& TempDeck)
{
	// If a player runs out of cards they loose
	if (PlayerDeck.size() == 0 || ComDeck.size() == 0)
		return;<commit_msg>Chrono<commit_after>#include <algorithm>
#include <chrono>
#include <deque>
#include <ppl.h>
#include <random>
#include <vector>

#include "conio.h"
#include "Card.h"

using namespace std;

static void PopulateDeck(vector<Card*>& Deck, const bool Joker = false);
static void DealCards(deque<Card*>& PlayerDeck, deque<Card*>& ComDeck, const vector<Card*>& CardDeck);
static void CombineDecks(deque<Card*>& Deck, vector<Card*>& ToAdd);
static void TieBreaker(deque<Card*>& PlayerDeck, deque<Card*>& ComDeck, vector<Card*>& TempDeck);

int main()
{
	cout << "Enter the number of games to simulate: ";
	unsigned iterations;
	cin >> iterations;

	// Grab # of logical cores
	unsigned threads = thread::hardware_concurrency();
	cout << "Enter number of threads (detected " << threads << "): ";
	cin >> threads;

	// Performace tracking
	typedef chrono::high_resolution_clock Clock;
	auto t1 = Clock::now();

	// Stats (Playerwins, Computerwins, Draws)
	unsigned Stats[] = { 0, 0, 0 };

	// Calculate the workload for each thread
	vector<int> workload;

	unsigned extra = iterations % threads;
	unsigned perIteration = (iterations - extra) / threads;

	for (unsigned i = 0; i < threads; i++)
	{
		if (extra > 0)
		{
			workload.push_back(perIteration + 1);
			extra--;
		}
		else
			workload.push_back(perIteration);
	}

	Concurrency::parallel_for(0, (int)threads, [&](int i)
	{
		// Set up the Deck
		vector<Card*> MasterDeck;	// shared_ptr adds ~50% overhead vs dumb pointers
		PopulateDeck(MasterDeck);

		auto engine = default_random_engine{};
		
		for (int j = 0; j < workload[i]; j++)
		{
			shuffle(MasterDeck.begin(), MasterDeck.end(), engine);

			// Set up the Player and Computer's decks
			deque<Card*> PlayerDeck;	
			deque<Card*> ComputerDeck;
			vector<Card*> TempDeck;

			DealCards(PlayerDeck, ComputerDeck, MasterDeck);
		
			// Main game loop
			while (PlayerDeck.size() > 0 && ComputerDeck.size() > 0)
			{
				Card * PlayerCard = PlayerDeck[0];
				PlayerDeck.pop_front();

				Card * ComputerCard = ComputerDeck[0];
				ComputerDeck.pop_front();

				TempDeck.push_back(PlayerCard);
				TempDeck.push_back(ComputerCard);

				if (PlayerCard->Value() > ComputerCard->Value())
					CombineDecks(PlayerDeck, TempDeck);
				else if (ComputerCard->Value() > PlayerCard->Value())
					CombineDecks(ComputerDeck, TempDeck);
				else
					TieBreaker(PlayerDeck, ComputerDeck, TempDeck);
			}

			if (PlayerDeck.size() == 0 && ComputerDeck.size() == 0) // Tie
				Stats[2]++;
			else if (ComputerDeck.size() == 0) // Player Win
				Stats[0]++;
			else // Computer win
				Stats[1]++;
		}
		// Clean up
		for (unsigned i = 0; i < MasterDeck.size(); i++)
			delete MasterDeck[i];
	});

	// Perf
	auto t2 = Clock::now();
	auto td = t2 - t1;

	cout << endl << "Player wins: " << Stats[0] << endl
		<< "Computer wins: " << Stats[1] << endl
		<< "Draws: " << Stats[2] << endl;

	cout << endl << "Completed in: ";

	// Format and output
	if (td.count() < 1000)
		cout << td.count() << " ns" << endl;
	else if (td.count() < 1e+6)
	{
		double us = (double)td.count() / 1000.0;
		cout << us << " us" << endl;
	}
	else if (td.count() < 1e+9)
	{
		double ms = (double)td.count() / 1e+6;
		cout << ms << " ms" << endl;
	}
	else
	{
		double seconds = (double)td.count() / 1e+9;
		cout << seconds << " s" << endl;
	}

	cout << "Average time per game: "
		<< (double)td.count() / (double)iterations / 1000.0 << " us" << endl;

	_getch();

    return 0;
}

static void TieBreaker(deque<Card*>& PlayerDeck, deque<Card*>& ComDeck, vector<Card*>& TempDeck)
{
	// If a player runs out of cards they loose
	if (PlayerDeck.size() == 0 || ComDeck.size() == 0)
		return;
{'fraction_non_alphanumeric': 0.1581022473386779, 'fraction_numerical': 0.013142331449599158, 'mean_word_length': 3.862619808306709, 'pattern_counts': {'":': 0, '<': 182, '<?xml version=': 0, '>': 75, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4978150', 'n_tokens_mistral': 3074, 'n_tokens_neox': 2663, 'n_words': 677}