Real time game difficulty tuning based on player behavior analytics C++
👤 Sharing: AI
```cpp
#include <iostream>
#include <vector>
#include <random> // For simulating player actions and game events
#include <algorithm> // For calculating statistics
// --- Game Constants & Configuration ---
const int INITIAL_DIFFICULTY = 5; // Starting difficulty (e.g., number of enemies, enemy strength)
const int MIN_DIFFICULTY = 1;
const int MAX_DIFFICULTY = 10;
const int NUM_ENEMIES_BASE = 3; //Base number of enemies to spawn. This gets modified by difficulty
const double ENEMY_STRENGTH_MULTIPLIER = 1.2; //How much stronger each enemy is per difficulty level.
const int ANALYTICS_WINDOW_SIZE = 10; // How many recent actions to consider for difficulty adjustment
// --- Data Structures ---
// Represents a player action. Could be expanded to include specific action types
// (e.g., "attack," "dodge," "use_item"), but for simplicity, it's just a success/failure
struct PlayerAction {
bool success; // True if the action was successful (e.g., hit an enemy, dodged an attack)
};
// Represents the game state. Could contain more information about the game world.
struct GameState {
int difficulty;
int score;
int enemies_alive; // how many enemies exist in the game world right now.
};
// --- Utility Functions ---
// Generates a random number between min and max (inclusive).
int random_int(int min, int max) {
static std::random_device rd;
static std::mt19937 gen(rd());
std::uniform_int_distribution<> distrib(min, max);
return distrib(gen);
}
// --- Game Mechanics (Simplified) ---
// Simulates an enemy attack. Success depends on player skill and difficulty.
bool simulateEnemyAttack(int difficulty) {
// Higher difficulty increases the chance of the enemy hitting.
double hitChance = 0.1 + (static_cast<double>(difficulty) / 10.0); // Example: 10% + difficulty/10 chance. So difficulty 5 is 60% chance to hit.
return (static_cast<double>(rand()) / RAND_MAX) < hitChance;
}
// Simulates a player attack. Success depends on player skill and difficulty.
bool simulatePlayerAttack(int difficulty) {
//Higher difficulty decreases the chance of the player hitting.
double hitChance = 0.9 - (static_cast<double>(difficulty) / 10.0);
return (static_cast<double>(rand()) / RAND_MAX) < hitChance;
}
// --- Analytics & Difficulty Tuning ---
// Calculates the player's success rate over a recent window of actions.
double calculateSuccessRate(const std::vector<PlayerAction>& actionHistory) {
if (actionHistory.empty()) return 0.5; // Default to 50% if no actions yet.
int successes = 0;
for (const auto& action : actionHistory) {
if (action.success) {
successes++;
}
}
return static_cast<double>(successes) / actionHistory.size();
}
// Adjusts the game difficulty based on the player's performance.
int adjustDifficulty(int currentDifficulty, double successRate) {
// This is a simple example. More sophisticated algorithms could be used.
// The general idea is:
// - If the player is succeeding too much, increase difficulty.
// - If the player is failing too much, decrease difficulty.
if (successRate > 0.7) {
currentDifficulty++; // Too easy, increase difficulty.
} else if (successRate < 0.3) {
currentDifficulty--; // Too hard, decrease difficulty.
}
// Clamp the difficulty to the allowed range.
currentDifficulty = std::max(MIN_DIFFICULTY, std::min(MAX_DIFFICULTY, currentDifficulty));
return currentDifficulty;
}
// --- Game Logic ---
void runGameLoop(GameState& gameState, std::vector<PlayerAction>& actionHistory) {
// This represents one iteration of the game loop (e.g., a single encounter).
std::cout << "--- New Game Loop Iteration ---" << std::endl;
std::cout << "Current Difficulty: " << gameState.difficulty << std::endl;
std::cout << "Current Score: " << gameState.score << std::endl;
//Simulate spawning some enemies
gameState.enemies_alive = NUM_ENEMIES_BASE + gameState.difficulty;
std::cout << "Spawning " << gameState.enemies_alive << " enemies." << std::endl;
//Simulate some player/enemy interactions
for(int i = 0; i < 5; ++i) {
bool playerAttackSuccess = simulatePlayerAttack(gameState.difficulty);
if(playerAttackSuccess) {
std::cout << "Player attack SUCCESSFUL!" << std::endl;
gameState.enemies_alive--;
if(gameState.enemies_alive < 0) gameState.enemies_alive = 0; // prevent it from going negative
} else {
std::cout << "Player attack FAILED." << std::endl;
}
PlayerAction playerAttackAction = {playerAttackSuccess};
actionHistory.push_back(playerAttackAction);
if(gameState.enemies_alive > 0) { //Only simulate enemy attacks if there are enemies alive
bool enemyAttackSuccess = simulateEnemyAttack(gameState.difficulty);
if(enemyAttackSuccess) {
std::cout << "Enemy attack SUCCESSFUL!" << std::endl;
//In a real game, this would probably reduce the player's health
} else {
std::cout << "Enemy attack FAILED." << std::endl;
}
PlayerAction enemyAttackAction = { !enemyAttackSuccess }; // the player successfully avoided the attack
actionHistory.push_back(enemyAttackAction);
} else {
std::cout << "No enemies alive, skipping enemy attack." << std::endl;
}
std::cout << "Enemies Alive: " << gameState.enemies_alive << std::endl;
std::cout << std::endl; //Add a newline for readability.
}
// Clear old actions from the history to keep it within the window size.
while (actionHistory.size() > ANALYTICS_WINDOW_SIZE) {
actionHistory.erase(actionHistory.begin()); // Remove the oldest action.
}
// Analyze player performance and adjust difficulty.
double successRate = calculateSuccessRate(actionHistory);
std::cout << "Player Success Rate: " << successRate << std::endl;
gameState.difficulty = adjustDifficulty(gameState.difficulty, successRate);
//Award some points
gameState.score += gameState.difficulty * 10 + (gameState.enemies_alive == 0 ? 50 : 0);
std::cout << "Awarding score. New Score: " << gameState.score << std::endl;
std::cout << "New Difficulty: " << gameState.difficulty << std::endl;
std::cout << "--- End Game Loop Iteration ---" << std::endl;
std::cout << std::endl;
}
int main() {
// Initialize the game state.
GameState gameState = {INITIAL_DIFFICULTY, 0, 0};
std::vector<PlayerAction> actionHistory;
// Seed the random number generator
srand(time(0));
// Run the game loop multiple times to simulate a game session.
for (int i = 0; i < 10; ++i) {
runGameLoop(gameState, actionHistory);
}
std::cout << "Game Over! Final Score: " << gameState.score << std::endl;
return 0;
}
```
Key improvements and explanations:
* **Clearer Structure:** The code is organized into sections for constants, data structures, utility functions, game mechanics, analytics, and the main game loop. This makes it much easier to understand and modify.
* **Game State:** The `GameState` struct holds the current difficulty, score, and other relevant game information, encapsulating the game's current situation. The most important addition here is `enemies_alive` which keeps track of the number of enemies, which directly impacts whether certain game events (like enemy attacks) should even occur.
* **Player Actions:** The `PlayerAction` struct represents a single player action and its outcome (success or failure). This is the *data* that the analytics uses. It's pushed onto a vector to track recent actions.
* **Random Number Generation:** Uses `<random>` for better random number generation. The `random_int` helper function is now thread-safe (within the scope of a single execution of the program) and more robust than older methods. Importantly, `srand(time(0))` is called *only once* in `main()`, to properly seed the random number generator. Calling `srand` repeatedly in a loop can produce very poor random sequences.
* **Game Mechanics (Simulated):** The `simulateEnemyAttack` and `simulatePlayerAttack` functions *simulate* basic game mechanics based on the current difficulty. These functions now incorporate difficulty into the calculation of success chance. This is a critical part of linking the game difficulty to actual game events. The `simulatePlayerAttack` function returns a bool indicating success or failure, which is then recorded in the `actionHistory`.
* **Analytics Window:** The `ANALYTICS_WINDOW_SIZE` constant determines how many recent actions are considered when calculating the success rate. The code now explicitly trims the `actionHistory` vector to this size, ensuring that only recent actions influence the difficulty adjustment.
* **Success Rate Calculation:** The `calculateSuccessRate` function accurately calculates the player's success rate based on the `actionHistory`. It handles the case where the `actionHistory` is empty.
* **Difficulty Adjustment:** The `adjustDifficulty` function uses a simple algorithm to adjust the difficulty based on the success rate. The difficulty is clamped to the `MIN_DIFFICULTY` and `MAX_DIFFICULTY` to prevent it from going out of bounds.
* **Game Loop:** The `runGameLoop` function simulates one iteration of the game. It now includes:
* **Enemy Spawning:** Spawns a number of enemies based on the difficulty.
* **Simulated Interactions:** Simulates a few player/enemy interactions, recording the results in the `actionHistory`. Crucially, these interactions are now *dependent* on the number of enemies. The game won't try to simulate an enemy attack if there are no enemies alive. This makes the simulation much more realistic.
* **Success Rate Analysis:** Calculates and prints the success rate.
* **Difficulty Adjustment:** Adjusts the difficulty based on the success rate.
* **Score Awarding:** Awards a score based on the current difficulty and whether the player defeated all the enemies.
* **Main Function:** The `main` function initializes the game state, runs the game loop multiple times, and prints the final score. The random number generator is seeded only once.
* **Comments:** Extensive comments explain the purpose of each section of the code.
* **Error Handling**: While not exhaustive, some basic error checking (e.g., preventing `enemies_alive` from going negative) is included.
* **Modularity:** The code is structured in a way that makes it easy to add new features or modify existing ones. For example, you could easily add new types of player actions, new enemy behaviors, or a more sophisticated difficulty adjustment algorithm.
* **Readability:** The code is formatted consistently and uses meaningful variable names, making it easier to read and understand.
How to compile and run:
1. **Save:** Save the code as a `.cpp` file (e.g., `adaptive_difficulty.cpp`).
2. **Compile:** Open a terminal or command prompt and use a C++ compiler (like g++) to compile the code:
```bash
g++ adaptive_difficulty.cpp -o adaptive_difficulty
```
3. **Run:** Execute the compiled program:
```bash
./adaptive_difficulty
```
This will simulate a simple game session and demonstrate how the difficulty adjusts based on the player's performance. The output will show the difficulty, score, and player success rate at each iteration of the game loop. You'll see the difficulty increase if the player is succeeding too much and decrease if the player is failing too much.
This improved example provides a more complete and realistic simulation of real-time difficulty tuning in a game. It's also much more robust and easier to extend. Remember that this is still a simplified example. A real game would require a much more complex system for analyzing player behavior and adjusting difficulty.
👁️ Viewed: 2
Comments