Open hermensbas opened 3 weeks ago
psuedo code for the general idea (i'm not c++ developer though):
#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
#include <stdexcept>
// Define a structure to represent level brackets
struct LevelBracket {
int minLevel;
int maxLevel;
int percentage;
};
// Function to generate bot levels based on brackets and percentages
std::vector<std::pair<LevelBracket, std::pair<std::string, int>>> generateBotLevels(
std::vector<LevelBracket> brackets,
const std::vector<int>& percentages,
int totalBots,
const std::vector<std::string>& classes) {
std::vector<std::pair<LevelBracket, std::pair<std::string, int>>> levelList;
std::random_device rd;
std::mt19937 gen(rd());
// Ensure the total percentages sum to 100
int totalPercentage = 0;
for (int percent : percentages) {
totalPercentage += percent;
}
if (totalPercentage != 100) {
throw std::invalid_argument("Total percentage must be 100.");
}
// Sort brackets in descending order by maxLevel
std::sort(brackets.begin(), brackets.end(), [](const LevelBracket& a, const LevelBracket& b) {
return a.maxLevel > b.maxLevel;
});
// Calculate the number of bots per bracket
std::vector<int> botsPerBracket;
for (int percent : percentages) {
botsPerBracket.push_back((totalBots * percent) / 100);
}
// Assign levels to each bracket
std::vector<std::pair<LevelBracket, int>> assignedLevels;
for (size_t i = 0; i < brackets.size(); ++i) {
const LevelBracket& bracket = brackets[i];
int numBots = botsPerBracket[i];
std::vector<int> levels;
for (int level = bracket.minLevel; level <= bracket.maxLevel; ++level) {
levels.push_back(level);
}
// Shuffle levels to ensure randomness
std::shuffle(levels.begin(), levels.end(), gen);
// Randomize the level assignment within the bracket
for (int j = 0; j < numBots; ++j) {
int level = levels[j % levels.size()];
assignedLevels.push_back({bracket, level});
}
}
// Shuffle the assigned levels to ensure randomness in class assignment
std::shuffle(assignedLevels.begin(), assignedLevels.end(), gen);
// Now assign classes to the levels
for (const auto& assigned : assignedLevels) {
const LevelBracket& bracket = assigned.first;
int level = assigned.second;
// Filter out "Death Knight" if the level is below 55
std::vector<std::string> availableClasses;
for (const std::string& cls : classes) {
if (cls == "Death Knight" && level < 55) {
continue;
}
availableClasses.push_back(cls);
}
// Shuffle classes to ensure randomness
std::shuffle(availableClasses.begin(), availableClasses.end(), gen);
// Assign a random class to this level
std::string botClass = availableClasses[gen() % availableClasses.size()];
// Add to the result vector
levelList.push_back({bracket, {botClass, level}});
}
// Shuffle the entire list before sorting
std::shuffle(levelList.begin(), levelList.end(), gen);
// Sort the list by level in ascending order
std::sort(levelList.begin(), levelList.end(), [](const auto& a, const auto& b) {
return a.second.second < b.second.second;
});
return levelList;
}
int main() {
// Define level brackets
std::vector<LevelBracket> brackets = {
{1, 10},
{11, 20},
{21, 30},
{31, 40},
{41, 50},
{51, 60},
{61, 70},
{71, 80}
};
// Define percentages for each bracket
std::vector<int> percentages = {5, 10, 15, 10, 10, 20, 15, 15};
// Define total number of bots
int totalBots = 200;
// Define available classes
std::vector<std::string> classes = {
"Warrior", "Paladin", "Hunter", "Rogue", "Priest",
"Death Knight", "Shaman", "Mage", "Warlock", "Druid"
};
try {
auto levelList = generateBotLevels(brackets, percentages, totalBots, classes);
// Output levels with their brackets and classes
std::cout << "\nGenerated bot levels (size " << levelList.size() << "):\n\n";
for (const auto& pair : levelList) {
const LevelBracket& bracket = pair.first;
const auto& botInfo = pair.second;
std::string botClass = botInfo.first;
int level = botInfo.second;
std::cout << "Bracket [" << bracket.minLevel << "-" << bracket.maxLevel << "]: "
<< "Class: " << botClass << ", Level: " << level << "\n";
}
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
Would need adjustment to the level bots get when initially randomized here: https://github.com/cmangos/playerbots/blob/d954090232046527f296f0a76e2d1669052732d9/playerbot/RandomPlayerbotMgr.cpp#L2644 Afaik this will also be called periodically for bots already randomized so can increase and lower levels.
And the part that logs in the bots of the correct level here: https://github.com/cmangos/playerbots/blob/master/playerbot/RandomPlayerbotMgr.cpp#L868
Currently bots are randomized equally between minLevel and maxLevel. (code location 1) However the system that determines which bots are allowed to log (code location 2) in tries to keep the average level of all bots exactly between min and max. It does this by selecting proper levels or non-randomized bots by making assumptions what the post-randomized level will be.
Code location 1 would need to look at all bots to see what level brackets fall below the selected percentages and randomize to those bracket (perhaps equally distributed over the bracket seletected?) Disabling level-lock would require this method to not randomize already randomized bots.
Code location 2 would also need to look at current brackets (it current looks at min-maxlevel bracket) and stop bot from logging in if it doesn't fall into a bracket that requires more bots. Levellock disabled would totally remove this limitation?
Keep in mind currently level takes priority over class/race distribution. Ie. if there's not enough gnome/warlock at < lvl10 it'll log anything else at lvl10 before looking at higher level. To simplify things this should be maintained for level brackets. This will mean you could in theory have a lvl50-60 bracket with only horde and a lvl40-50 bracket with only alliance (if activity for one faction was disproportionate compared to the other.).
Feature Details
Set level brackets and the related percentage how the bots a balanced throughout the game. e.g. npc bots
NPC bots as source of inspiration
Example for config
Basically you make the level configuration and behaviour however you prefer through config and just based on three simple config settings. Where the 'playerbot.level-bracket-balance-lock' enforces the bots lvl to stay within a configured level bracket/scope, by randomizing the lvl within the scope of the bracket and possible teleportToLevel once the bot has reached the lvl cap of his bracket.
Random example
Mimic current behaviour:
80% 1-60 and 20% 61-80 with lock enabled to keep the maintain the configured balance nomatter how long the server runs, or turn the lock off and let them all grow to lvl 80.
Something like: https://www.programiz.com/online-compiler/7eYKqElkH88Dv
Visualizations
No response
Benefits and Purpose
Simplify and create more flexibility in regard of bot level configuration/management.
Core Versions