I'm trying to find out how to calculate the 'weight choices' based on the most frequent number in an array for AI to choose a particular number.
For instance, I have this function which calculates the most common number in an array and allows the AI to choose a particular option to make the player lose.
int GameManager::ComputerChoice(int playerChoices)
{
int arraySize = sizeof(m_playerChoices) / sizeof(m_playerChoices[0]);
int maxCount = 0;
int mostFrequentNumber;
int computerChoice = 0;
if (m_playerChoices[9] == 0)
{
// Array used to store the player choices upto 10 times
m_playerChoices[m_index] = playerChoices;
m_index ;
}
else
{
for (int i = 0; i < arraySize; i )
{
int count = 0;
for (int j = 0; j < arraySize; j )
{
if (m_playerChoices[i] == m_playerChoices[j])
{
count ;
}
}
if (count > maxCount)
{
maxCount = count;
mostFrequentNumber = m_playerChoices[i];
}
}
int weight = rand() % 100 1;
switch (mostFrequentNumber)
{
case 1:
if (weight <= 50)
{
computerChoice = 2;
}
else if (weight >= 51 && weight <= 75)
{
computerChoice = 1;
}
else
{
computerChoice = 3;
}
break;
case 2:
if (weight <= 50)
{
computerChoice = 3;
}
else if (weight >= 51 && weight <= 75)
{
computerChoice = 1;
}
else
{
computerChoice = 2;
}
break;
case 3:
if (weight <= 50)
{
computerChoice = 1;
}
else if (weight >= 51 && weight <= 75)
{
computerChoice = 2;
}
else
{
computerChoice = 3;
}
break;
}
return computerChoice;
}
}
As it stands now, if the most frequent number is 1, there will be a 50% chance that 2 will be chosen. Ideally, it should only have a 50% chance to occur if the array is full of 1s.
What I am trying to achieve is if 1 is the most frequent number and only appears 5 times (for example), then the computer choice shouldn't have a 50% to be chosen but should remain the highest possible choice. With this, the weighting shouldn't exceed 50% nor drop below 25%.
Does anyone have any solutions for this? I haven't been able to work it out. Appreciate the help.
P.S: I know the function is probably written very badly, just trying to get something as a prototype/skeleton function.
CodePudding user response:
This looks like the perfect place for a std::discrete_distribution
.
Walkthrough:
#include <algorithm>
#include <iostream>
#include <map>
#include <random>
#include <vector>
// A seeded pseudo random number generator:
static std::mt19937 gen(std::random_device{}());
int main() {
Say you have all the player choices in a vector
. You seem to have them in an array, but I'll use a vector
since it's easier to handle.
// filled with some example choices:
std::vector<int> m_playerChoices{10,10,11,10,22,22,10};
I'd start by making a histogram so that you get the count of each choice:
std::map<int, int> hist;
for(int choice : m_playerChoices) {
hist[choice];
}
Then we extract the choices and counts from the histogram and put in two separate vector
s. One containing all the unique choices and a corresponding one containing the count of each, called weights
.
std::vector<int> unique_choices;
std::vector<int> weights;
for(auto[choice, count] : hist) {
unique_choices.push_back(choice);
weights.push_back(count);
}
The weights
are then used to create a discrete_distribution
:
std::discrete_distribution<int> dist(weights.begin(), weights.end());
You can now call the pseudo random number generator and use this discrete distribution to get one of the unique numbers and the probability for each will be exactly according to the count of each choice that you started out with:
for(int i = 0; i < 100; i) {
std::cout << unique_choices[dist(gen)] << '\n';
}
}