- ai-framework.md: Unified decision framework with scoring system, RL integration, doctrines - systeme-diplomatique.md: Relations (shared), intentions (bilateral), threat, reliability systems - calcul-menace.md: Contextual threat calculation with sword & shield mechanics - systeme-sauvegarde.md: V1 save system with JSON, metachunks, module autonomy - module-versioning.md: Automatic MAJOR.MINOR.PATCH.BUILD versioning via CMake 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
38 KiB
AI Framework - Unified Decision System
Philosophie
Le système d'IA de Warfactory repose sur un framework unifié de prise de décision par scoring. Tous les types d'IA (tactique, opérationnelle, économique, diplomatique) utilisent le même pattern fondamental :
- Générer options disponibles dans le contexte actuel
- Scorer chaque option selon poids configurables et situation
- Choisir la meilleure option (highest score)
- Exécuter l'action correspondante
Ce pattern unifié permet :
- Cohérence : Même logique à travers tous les systèmes IA
- Configurabilité : Comportements ajustables via JSON
- Apprentissage : Reinforcement Learning ajuste les poids
- Modularité : Nouveaux types de décisions ajoutables facilement
- Testabilité : Chaque décision isolée et vérifiable
Architecture Globale
Hiérarchie des Modules IA
AI Framework
├── Core Decision System
│ ├── IDecision (interface de base)
│ ├── Decision Factory (création objets)
│ └── Scoring Engine (évaluation uniforme)
│
├── Helper Modules (services, pas de décisions)
│ ├── PathfinderModule (calcul chemins)
│ ├── AcquisitionModule (sélection cibles)
│ └── MovementModule (exécution mouvement)
│
├── Decision Modules (utilisent helpers)
│ ├── TacticalModule (combat temps-réel)
│ ├── OperationalModule (stratégie bataille)
│ ├── CompanyAIModule (business)
│ └── StateAIModule (politique)
│
└── Data Modules (consultés par tous)
├── DiplomacyModule (relations, traités)
└── EconomyModule (marché, prix)
Flux de Décision Type
OperationalModule
↓ (donne posture: "rush blindé")
TacticalModule
↓ (demande chemins pour flanking)
PathfinderModule → retourne 3 chemins possibles
↓ (demande cibles prioritaires)
AcquisitionModule → retourne 5 cibles scorées
↓ (choisit meilleure tactique)
TacticalModule → décision: flanking par chemin 2, cible 1
↓ (exécute mouvement)
MovementModule → unités se déplacent
Interface IDecision - Core Pattern
Définition
Toutes les décisions IA implémentent cette interface :
class IDecision {
public:
virtual ~IDecision() = default;
// 1. Générer toutes les options disponibles
virtual std::vector<Option> generateOptions() = 0;
// 2. Évaluer score d'une option dans le contexte
virtual float scoreOption(const Option& opt, const Context& ctx) = 0;
// 3. Choisir la meilleure option
virtual Option chooseBest() = 0;
// 4. Exécuter l'option choisie
virtual Action execute(const Option& opt) = 0;
// 5. Obtenir les poids configurés
virtual Weights getWeights() const = 0;
// 6. Définir nouveaux poids (pour RL)
virtual void setWeights(const Weights& weights) = 0;
};
Pattern d'Usage : Decision Objects Éphémères
Principe : New → Use → Delete
// Création décision éphémère
IDecision* decision = DecisionFactory::create(
"flanking_maneuver",
context,
weights
);
// Évaluation et choix
Option best = decision->chooseBest();
// Exécution
Action action = decision->execute(best);
// Destruction immédiate
delete decision;
Avantages :
- Pas d'état persistant : Chaque décision isolée
- Parallélisation : Évaluer N décisions simultanément sans conflits
- Hot-reload : Charger nouveaux types depuis modules .so
- Memory safety : Objets détruits après usage
Alternative avec smart pointers :
auto decision = DecisionFactory::createUnique("flanking_maneuver", ctx, weights);
Option best = decision->chooseBest();
Action action = decision->execute(best);
// Auto-delete à la sortie de scope
Structures de Données
Option
Représente une action possible :
struct Option {
std::string type; // "flanking", "frontal_assault", etc.
json parameters; // Paramètres spécifiques
float base_score; // Score de base (avant modifiers)
std::vector<Modifier> modifiers; // Bonus/malus contextuels
};
Context
État du monde pertinent pour la décision :
struct Context {
json world_state; // État général (terrain, météo, etc.)
json entities; // Entités pertinentes (unités, ennemis)
json objectives; // Objectifs actuels
json constraints; // Contraintes (budget, temps, etc.)
Doctrine* doctrine; // Doctrine appliquée (USSR, OTAN, etc.)
};
Weights
Poids configurables pour le scoring :
struct Weights {
std::map<std::string, float> values; // "aggression": 0.8, "caution": 0.3
float get(const std::string& key, float default_val = 0.5f) const {
auto it = values.find(key);
return (it != values.end()) ? it->second : default_val;
}
};
// Chargement depuis JSON
Weights Weights::fromJson(const json& data) {
Weights w;
for (auto& [key, val] : data.items()) {
w.values[key] = val.get<float>();
}
return w;
}
Système de Scoring Unifié
Formule Générale
float IDecision::scoreOption(const Option& opt, const Context& ctx) {
float score = opt.base_score;
// Application des poids
for (auto& [factor, importance] : weights.values) {
float factor_value = evaluateFactor(factor, opt, ctx);
score += factor_value * importance;
}
// Application des modifiers contextuels
for (auto& modifier : opt.modifiers) {
score *= modifier.multiplier;
}
// Normalisation (optionnelle)
return std::clamp(score, 0.0f, 1.0f);
}
Exemple Concret : PathfinderModule
Options : 5 chemins possibles vers objectif
class PathDecision : public IDecision {
std::vector<Option> generateOptions() override {
std::vector<Option> paths;
// Générer chemins alternatifs
for (auto& path : pathfinder->findAllPaths(start, end)) {
Option opt;
opt.type = "path";
opt.parameters["route"] = path.waypoints;
opt.parameters["distance"] = path.length;
opt.parameters["safety"] = path.threat_level;
opt.parameters["cover"] = path.cover_percentage;
opt.base_score = 0.5f;
// Modifiers contextuels
if (path.crosses_open_terrain) {
opt.modifiers.push_back({"open_terrain", 0.7f});
}
if (path.has_chokepoints) {
opt.modifiers.push_back({"chokepoint", 0.85f});
}
paths.push_back(opt);
}
return paths;
}
float scoreOption(const Option& opt, const Context& ctx) override {
float score = opt.base_score;
// Facteurs de scoring
float distance = opt.parameters["distance"];
float safety = opt.parameters["safety"];
float cover = opt.parameters["cover"];
// Application poids configurables
score += (1.0f - distance / max_distance) * weights.get("speed");
score += safety * weights.get("safety");
score += cover * weights.get("stealth");
// Modifiers
for (auto& mod : opt.modifiers) {
score *= mod.multiplier;
}
return score;
}
};
Configuration Poids (JSON) :
{
"pathfinder_weights": {
"speed": 0.6,
"safety": 0.3,
"stealth": 0.1
}
}
Usage :
auto decision = new PathDecision(start, end, context, weights);
Option bestPath = decision->chooseBest();
Action moveAction = decision->execute(bestPath);
delete decision;
Exemple : AcquisitionModule
Options : Cibles ennemies visibles
class AcquisitionDecision : public IDecision {
std::vector<Option> generateOptions() override {
std::vector<Option> targets;
for (auto& enemy : visibleEnemies) {
Option opt;
opt.type = "target";
opt.parameters["entity_id"] = enemy.id;
opt.parameters["threat"] = enemy.calculateThreat();
opt.parameters["vulnerability"] = enemy.calculateVulnerability();
opt.parameters["priority"] = enemy.strategic_value;
opt.base_score = 0.5f;
// Modifiers
if (enemy.is_wounded) {
opt.modifiers.push_back({"wounded", 1.2f});
}
if (enemy.has_cover) {
opt.modifiers.push_back({"covered", 0.8f});
}
targets.push_back(opt);
}
return targets;
}
float scoreOption(const Option& opt, const Context& ctx) override {
float score = opt.base_score;
float threat = opt.parameters["threat"];
float vulnerability = opt.parameters["vulnerability"];
float priority = opt.parameters["priority"];
// Poids doctrine
score += threat * weights.get("threat_priority");
score += vulnerability * weights.get("opportunism");
score += priority * weights.get("strategic_focus");
// Modifiers
for (auto& mod : opt.modifiers) {
score *= mod.multiplier;
}
return score;
}
};
Configuration :
{
"acquisition_weights": {
"threat_priority": 0.7,
"opportunism": 0.2,
"strategic_focus": 0.1
}
}
Modules Spécialisés
Helper Modules (Services)
Ces modules ne prennent pas de décisions, ils fournissent des services aux decision modules.
PathfinderModule
Responsabilité : Calculer chemins navigables entre deux points
Interface :
class PathfinderModule : public IModule {
public:
// Trouver meilleur chemin unique
Path findPath(Position start, Position end, PathConstraints constraints);
// Trouver tous les chemins alternatifs
std::vector<Path> findAllPaths(Position start, Position end, int max_paths = 5);
// Vérifier si chemin encore valide
bool validatePath(const Path& path);
};
struct Path {
std::vector<Position> waypoints;
float length;
float threat_level; // Exposition ennemie
float cover_percentage; // % du chemin avec couvert
bool crosses_open_terrain;
bool has_chokepoints;
};
Utilisé par : TacticalModule, OperationalModule
AcquisitionModule
Responsabilité : Identifier et scorer cibles potentielles
Interface :
class AcquisitionModule : public IModule {
public:
// Obtenir toutes les cibles visibles
std::vector<Target> getVisibleTargets(Unit observer);
// Scorer une cible selon contexte
float scoreTarget(Target target, Context ctx, Weights weights);
// Filtrer cibles selon critères
std::vector<Target> filterTargets(std::vector<Target> targets, FilterCriteria criteria);
};
struct Target {
EntityId id;
Position position;
float threat; // Dangerosité
float vulnerability; // Facilité élimination
float strategic_value; // Importance stratégique
bool is_wounded;
bool has_cover;
};
Utilisé par : TacticalModule
MovementModule
Responsabilité : Exécuter déplacements d'unités
Interface :
class MovementModule : public IModule {
public:
// Déplacer unité selon chemin
void moveUnit(Unit unit, Path path);
// Déplacer formation
void moveFormation(std::vector<Unit> units, Path path, FormationType formation);
// Vérifier si mouvement possible
bool canMove(Unit unit, Position target);
};
Utilisé par : TacticalModule
Decision Modules
Ces modules prennent des décisions en utilisant les helpers.
TacticalModule
Responsabilité : Décisions combat temps-réel (secondes/minutes)
Décisions prises :
- Choix tactique (flanking, frontal assault, suppression, retreat)
- Sélection cibles
- Positionnement unités
- Utilisation couvert
- Timing engagements
Dépendances :
- PathfinderModule (chemins)
- AcquisitionModule (cibles)
- MovementModule (exécution)
Exemple Décision :
class FlankingDecision : public IDecision {
std::vector<Option> generateOptions() override {
std::vector<Option> options;
// Pour chaque flanc possible
for (auto& flankPosition : identifyFlankPositions(enemy)) {
// Demander chemins au PathfinderModule
auto paths = pathfinder->findAllPaths(myPosition, flankPosition);
for (auto& path : paths) {
// Demander cibles à AcquisitionModule
auto targets = acquisition->getVisibleTargets(myUnit);
Option opt;
opt.type = "flanking";
opt.parameters["path"] = path;
opt.parameters["flank_side"] = flankPosition.side; // "left"/"right"
opt.parameters["targets"] = targets;
opt.base_score = 0.6f; // Flanking généralement bon
// Modifiers
if (path.crosses_open_terrain) {
opt.modifiers.push_back({"exposed_approach", 0.7f});
}
if (targets.size() > 3) {
opt.modifiers.push_back({"many_targets", 1.2f});
}
options.push_back(opt);
}
}
return options;
}
float scoreOption(const Option& opt, const Context& ctx) override {
// Récupérer poids doctrine
Doctrine* doctrine = ctx.doctrine;
float flanking_preference = doctrine->tactical_weights["flanking"];
float score = opt.base_score * flanking_preference;
// Facteurs tactiques
Path path = opt.parameters["path"];
score += path.cover_percentage * weights.get("safety");
score -= path.threat_level * weights.get("caution");
// Modifiers
for (auto& mod : opt.modifiers) {
score *= mod.multiplier;
}
return score;
}
};
Configuration Poids :
{
"tactical_weights": {
"safety": 0.4,
"caution": 0.3,
"aggression": 0.7
}
}
OperationalModule
Responsabilité : Choix stratégie/posture bataille (heures/jours)
Décisions prises :
- Posture opérationnelle (rush blindé, raid cavalerie, défense en profondeur)
- Allocation forces entre secteurs
- Timing offensives
- Objectifs prioritaires
Dépendances :
- TacticalModule (exécution tactique)
- DiplomacyModule (contexte stratégique)
Exemple Décision :
class OperationalPostureDecision : public IDecision {
std::vector<Option> generateOptions() override {
std::vector<Option> postures;
// Postures disponibles selon doctrine
for (auto& [posture_name, posture_weight] : doctrine->operational_weights) {
Option opt;
opt.type = "posture";
opt.parameters["name"] = posture_name;
opt.base_score = posture_weight;
// Évaluer faisabilité
if (posture_name == "rush_blinde" && terrain.is_open) {
opt.modifiers.push_back({"favorable_terrain", 1.3f});
}
if (posture_name == "infiltration" && units.has_infantry) {
opt.modifiers.push_back({"suitable_units", 1.2f});
}
postures.push_back(opt);
}
return postures;
}
Action execute(const Option& opt) override {
std::string posture = opt.parameters["name"];
// Donner directive au TacticalModule
tacticalModule->setPosture(posture);
// Ajuster poids tactiques selon posture
if (posture == "rush_blinde") {
tacticalModule->adjustWeights({
{"flanking", 1.5f}, // Boost flanking
{"caution", 0.5f} // Réduire caution
});
}
return Action{posture, opt.parameters};
}
};
Doctrines Opérationnelles :
// doctrines/ussr.json
{
"name": "USSR Deep Battle",
"operational_weights": {
"deep_battle": 0.8,
"overwhelming_assault": 0.9,
"mobile_defense": 0.4,
"raid_cavalry": 0.3,
"infiltration": 0.2
},
"tactical_weights": {
"frontal_assault": 0.8,
"flanking": 0.5,
"combined_arms": 0.7,
"retreat_threshold": 0.2
}
}
// doctrines/nato.json
{
"name": "NATO AirLand Battle",
"operational_weights": {
"airland_battle": 0.9,
"maneuver_warfare": 0.8,
"deep_strike": 0.7,
"overwhelming_assault": 0.4,
"mobile_defense": 0.6
},
"tactical_weights": {
"frontal_assault": 0.3,
"flanking": 0.9,
"combined_arms": 0.9,
"retreat_threshold": 0.6
}
}
CompanyAIModule
Responsabilité : Décisions business (jours/semaines)
Décisions prises :
- Investissements R&D
- Production (quoi produire, combien)
- Prix et stratégie commerciale
- Lobbying états
- Acquisitions/expansions
Dépendances :
- EconomyModule (prix marché, demande)
- DiplomacyModule (relations, influence)
Exemple Décision :
class CompanyInvestmentDecision : public IDecision {
std::vector<Option> generateOptions() override {
std::vector<Option> investments;
// R&D options
for (auto& tech : availableTechnologies) {
Option opt;
opt.type = "research";
opt.parameters["tech_id"] = tech.id;
opt.parameters["cost"] = tech.research_cost;
opt.parameters["expected_profit"] = estimateProfit(tech);
opt.base_score = 0.5f;
if (tech.is_breakthrough) {
opt.modifiers.push_back({"breakthrough", 1.5f});
}
investments.push_back(opt);
}
// Production expansion
Option expand;
expand.type = "expand_production";
expand.parameters["cost"] = calculate_expansion_cost();
expand.parameters["capacity_increase"] = 0.3f;
expand.base_score = 0.4f;
investments.push_back(expand);
// Lobbying
for (auto& state : states) {
float influence = diplomacy->getInfluence(company_id, state.id);
if (influence < 0.8f) {
Option lobby;
lobby.type = "lobbying";
lobby.parameters["state_id"] = state.id;
lobby.parameters["cost"] = calculateLobbyCost(state);
lobby.parameters["influence_gain"] = 0.1f;
lobby.base_score = 0.3f;
investments.push_back(lobby);
}
}
return investments;
}
float scoreOption(const Option& opt, const Context& ctx) override {
float score = opt.base_score;
if (opt.type == "research") {
float expected_profit = opt.parameters["expected_profit"];
float cost = opt.parameters["cost"];
float roi = expected_profit / cost;
score += roi * weights.get("innovation");
score -= (cost / company_budget) * weights.get("risk_aversion");
}
else if (opt.type == "lobbying") {
float influence_gain = opt.parameters["influence_gain"];
score += influence_gain * weights.get("political_focus");
}
for (auto& mod : opt.modifiers) {
score *= mod.multiplier;
}
return score;
}
};
Configuration Company :
{
"company_ai_weights": {
"innovation": 0.7,
"risk_aversion": 0.4,
"political_focus": 0.6,
"profit_maximization": 0.8,
"quality_focus": 0.6
}
}
StateAIModule
Responsabilité : Décisions étatiques (semaines/mois)
Décisions prises :
- Achats militaires
- Alliances diplomatiques
- Budgets sectoriels
- Objectifs stratégiques
Dépendances :
- DiplomacyModule (relations)
- EconomyModule (budget disponible)
- OperationalModule (besoins militaires)
Exemple Décision :
class StateMilitaryPurchaseDecision : public IDecision {
std::vector<Option> generateOptions() override {
std::vector<Option> purchases;
// Pour chaque Company
for (auto& company : companies) {
// Vérifier relation diplomatique
float relation = diplomacy->getRelation(state_id, company.id);
if (relation < 0.3f) continue; // Trop mauvaise relation
// Pour chaque véhicule disponible
for (auto& vehicle : company.catalog) {
Option opt;
opt.type = "purchase";
opt.parameters["company_id"] = company.id;
opt.parameters["vehicle_id"] = vehicle.id;
opt.parameters["price"] = vehicle.price;
opt.parameters["performance"] = vehicle.combat_rating;
opt.base_score = 0.5f;
// Modifiers
if (relation > 0.8f) {
opt.modifiers.push_back({"good_relations", 1.2f});
}
if (company.reputation > 0.9f) {
opt.modifiers.push_back({"trusted_brand", 1.1f});
}
purchases.push_back(opt);
}
}
return purchases;
}
float scoreOption(const Option& opt, const Context& ctx) override {
float score = opt.base_score;
float price = opt.parameters["price"];
float performance = opt.parameters["performance"];
float value = performance / price;
score += value * weights.get("cost_effectiveness");
score += performance * weights.get("quality_priority");
// Influence politique
CompanyId company = opt.parameters["company_id"];
float influence = diplomacy->getInfluence(company, state_id);
score += influence * weights.get("lobbying_susceptibility");
for (auto& mod : opt.modifiers) {
score *= mod.multiplier;
}
return score;
}
};
Configuration State :
{
"state_ai_weights": {
"cost_effectiveness": 0.6,
"quality_priority": 0.7,
"lobbying_susceptibility": 0.4,
"strategic_autonomy": 0.5
}
}
Decision Factory
Création Dynamique
class DecisionFactory {
public:
// Enregistrer types de décisions
static void registerDecision(const std::string& type, DecisionCreator creator);
// Créer décision par nom
static IDecision* create(
const std::string& type,
const Context& ctx,
const Weights& weights
);
// Créer avec smart pointer
static std::unique_ptr<IDecision> createUnique(
const std::string& type,
const Context& ctx,
const Weights& weights
);
private:
static std::map<std::string, DecisionCreator> registry;
};
// Type pour fonction de création
using DecisionCreator = std::function<IDecision*(const Context&, const Weights&)>;
Enregistrement Décisions
// Dans l'initialisation du module
void TacticalModule::initialize() {
DecisionFactory::registerDecision("flanking", [](auto ctx, auto weights) {
return new FlankingDecision(ctx, weights);
});
DecisionFactory::registerDecision("frontal_assault", [](auto ctx, auto weights) {
return new FrontalAssaultDecision(ctx, weights);
});
DecisionFactory::registerDecision("retreat", [](auto ctx, auto weights) {
return new RetreatDecision(ctx, weights);
});
}
Chargement depuis Modules .so
// Hot-reload de nouveaux types de décisions
void AIFramework::loadDecisionModule(const std::string& module_path) {
void* handle = dlopen(module_path.c_str(), RTLD_LAZY);
// Récupérer fonction d'enregistrement
auto registerFunc = (RegisterDecisionsFunc)dlsym(handle, "registerDecisions");
// Enregistrer les décisions du module
registerFunc(DecisionFactory::getInstance());
}
Reinforcement Learning Integration
Ajustement des Poids
Le système RL ajuste les poids selon résultats des décisions :
class RLEngine {
public:
// Enregistrer une décision et son outcome
void recordDecision(
const std::string& decision_type,
const Weights& weights_used,
const Option& chosen_option,
float outcome_reward // -1.0 à +1.0
);
// Ajuster poids selon historical performance
Weights adjustWeights(
const std::string& decision_type,
const Weights& current_weights,
float learning_rate = 0.1f
);
// Obtenir poids optimisés pour contexte
Weights getOptimizedWeights(
const std::string& decision_type,
const Context& ctx
);
private:
struct DecisionRecord {
Weights weights;
Option option;
float reward;
Context context;
};
std::map<std::string, std::vector<DecisionRecord>> history;
};
Exemple RL Tactique
// Enregistrer décision
auto decision = new FlankingDecision(ctx, current_weights);
Option best = decision->chooseBest();
Action action = decision->execute(best);
delete decision;
// Observer résultat combat
float outcome = evaluateCombatOutcome(); // -1.0 (défaite) à +1.0 (victoire)
// Enregistrer pour RL
rlEngine->recordDecision("flanking", current_weights, best, outcome);
// Ajuster poids pour prochaine fois
Weights new_weights = rlEngine->adjustWeights("flanking", current_weights);
saveWeights("tactical_flanking_weights.json", new_weights);
Poids Initiaux (Manuels)
Important : En V1, pas de training automatique. Poids configurés manuellement dans JSON.
// config/ai/tactical_initial_weights.json
{
"flanking": {
"safety": 0.4,
"caution": 0.3,
"aggression": 0.7,
"speed": 0.6
},
"frontal_assault": {
"safety": 0.2,
"caution": 0.1,
"aggression": 0.9,
"overwhelming_force": 0.8
},
"retreat": {
"safety": 0.9,
"caution": 0.8,
"preservation": 0.9
}
}
Le RL ajuste ces valeurs au fil du temps selon expérience.
Performance et Optimisation
Object Pooling
Pour éviter new/delete fréquents :
class DecisionPool {
public:
template<typename T>
T* acquire(const Context& ctx, const Weights& weights) {
if (pool.empty()) {
return new T(ctx, weights);
}
T* obj = pool.back();
pool.pop_back();
obj->reset(ctx, weights);
return obj;
}
template<typename T>
void release(T* obj) {
pool.push_back(obj);
}
private:
std::vector<IDecision*> pool;
};
Usage :
auto decision = decisionPool->acquire<FlankingDecision>(ctx, weights);
Option best = decision->chooseBest();
decisionPool->release(decision);
Parallélisation
Évaluer plusieurs décisions simultanément :
std::vector<Option> evaluateDecisionsParallel(
const std::vector<std::string>& decision_types,
const Context& ctx,
const Weights& weights
) {
std::vector<std::future<Option>> futures;
for (auto& type : decision_types) {
futures.push_back(std::async(std::launch::async, [&]() {
auto decision = DecisionFactory::createUnique(type, ctx, weights);
return decision->chooseBest();
}));
}
std::vector<Option> results;
for (auto& future : futures) {
results.push_back(future.get());
}
return results;
}
Performance Targets
- TacticalModule : 60 décisions/seconde (temps-réel combat)
- OperationalModule : 10 décisions/seconde (moins fréquent)
- CompanyAI : 1 décision/seconde (batch processing)
- StateAI : 0.1 décision/seconde (décisions rares)
Intégration Architecture Modulaire
AI Framework comme Module
class AIFrameworkModule : public IModule {
public:
void initialize() override {
// Charger configurations
loadDoctrines("config/doctrines/");
loadWeights("config/ai/");
// Enregistrer décisions
registerAllDecisions();
// Initialiser RL engine
rlEngine = new RLEngine();
}
json process(const json& input) override {
// Traiter requêtes de décision
std::string decision_type = input["decision_type"];
Context ctx = Context::fromJson(input["context"]);
Weights weights = getWeights(decision_type);
auto decision = DecisionFactory::createUnique(decision_type, ctx, weights);
Option best = decision->chooseBest();
json output;
output["chosen_option"] = best.toJson();
return output;
}
void shutdown() override {
delete rlEngine;
}
};
Communication avec Autres Modules
TacticalModule demande décision :
// TacticalModule process()
json request;
request["decision_type"] = "flanking";
request["context"] = currentContext.toJson();
json response = sendToModule("ai_framework", request);
Option chosen = Option::fromJson(response["chosen_option"]);
// Exécuter
executeOption(chosen);
OperationalModule configure TacticalModule :
// OperationalModule choisit posture
auto decision = new OperationalPostureDecision(ctx, weights);
Option posture = decision->chooseBest();
// Envoyer directive à TacticalModule
json directive;
directive["posture"] = posture.parameters["name"];
directive["weight_adjustments"] = calculateAdjustments(posture);
sendToModule("tactical", directive);
Configuration et Données
Structure Fichiers Config
config/
├── ai/
│ ├── tactical_weights.json
│ ├── operational_weights.json
│ ├── company_weights.json
│ └── state_weights.json
├── doctrines/
│ ├── ussr.json
│ ├── nato.json
│ ├── china.json
│ └── guerrilla.json
└── rl/
├── learning_rates.json
└── reward_functions.json
Exemple Configuration Complète
config/ai/tactical_weights.json :
{
"flanking": {
"safety": 0.4,
"caution": 0.3,
"aggression": 0.7,
"speed": 0.6,
"stealth": 0.5
},
"frontal_assault": {
"safety": 0.2,
"caution": 0.1,
"aggression": 0.9,
"overwhelming_force": 0.8,
"speed": 0.4
},
"suppression": {
"safety": 0.6,
"rate_of_fire": 0.9,
"ammunition_conservation": 0.3,
"area_denial": 0.8
},
"retreat": {
"safety": 0.9,
"caution": 0.8,
"preservation": 0.9,
"speed": 0.7,
"covering_fire": 0.6
}
}
config/doctrines/ussr.json (complet) :
{
"name": "USSR Deep Battle Doctrine",
"description": "Emphasis on overwhelming force, combined arms, and continuous offensive operations",
"operational_weights": {
"deep_battle": 0.8,
"overwhelming_assault": 0.9,
"echeloned_attack": 0.7,
"mobile_defense": 0.4,
"raid_cavalry": 0.3,
"infiltration": 0.2,
"attritional_warfare": 0.6
},
"tactical_weights": {
"frontal_assault": 0.8,
"flanking": 0.5,
"combined_arms": 0.7,
"artillery_preparation": 0.9,
"retreat_threshold": 0.2,
"counterattack": 0.7,
"urban_combat": 0.6
},
"resource_priorities": {
"armor_quantity": 0.9,
"artillery": 0.8,
"infantry_mass": 0.7,
"air_support": 0.5,
"logistics": 0.6
},
"special_characteristics": {
"accepts_high_casualties": true,
"prefers_night_operations": true,
"emphasizes_political_officers": true
}
}
config/doctrines/nato.json (complet) :
{
"name": "NATO AirLand Battle Doctrine",
"description": "Deep strike, maneuver warfare, and combined arms with heavy air integration",
"operational_weights": {
"airland_battle": 0.9,
"maneuver_warfare": 0.8,
"deep_strike": 0.7,
"defensive_depth": 0.6,
"overwhelming_assault": 0.4,
"mobile_defense": 0.6,
"decapitation_strike": 0.7
},
"tactical_weights": {
"frontal_assault": 0.3,
"flanking": 0.9,
"combined_arms": 0.9,
"artillery_preparation": 0.6,
"retreat_threshold": 0.6,
"air_strikes": 0.9,
"precision_engagement": 0.8
},
"resource_priorities": {
"armor_quality": 0.9,
"air_superiority": 0.9,
"precision_munitions": 0.8,
"C4ISR": 0.9,
"logistics": 0.8
},
"special_characteristics": {
"accepts_high_casualties": false,
"prefers_technology_advantage": true,
"emphasizes_initiative": true
}
}
Évolution Future
Version 2 : Apprentissage Actif
- Auto-training : RL s'entraîne via simulations automatiques
- Meta-learning : Adaptation rapide à nouvelles situations
- Transfer learning : Connaissances transférées entre doctrines
Version 3 : Comportements Émergents
- Genetic algorithms : Companies/States évoluent génération par génération
- Multi-agent RL : Plusieurs IA apprennent en compétition/coopération
- Adaptive doctrines : Doctrines mutent selon expérience
Version 4 : Neural Networks
- Deep RL : Réseaux neurones pour décisions complexes
- Pattern recognition : Reconnaître situations similaires historiques
- Predictive modeling : Anticiper actions adversaires
Références Croisées
systeme-militaire.md: Combat, véhicules, doctrines militaireseconomie-logistique.md: Marché, pricing, supply chainsarchitecture-modulaire.md: Intégration modules, hot-reloadsysteme-sauvegarde.md: Persistence états IA, poids RL
Exemples Pratiques
Exemple Complet : Décision Tactique
// Contexte bataille
Context ctx;
ctx.world_state = {
{"terrain", "open_plains"},
{"weather", "clear"},
{"time_of_day", "dawn"}
};
ctx.entities = {
{"my_units", {{"tanks", 10}, {"ifv", 5}}},
{"enemy_units", {{"tanks", 8}, {"infantry", 20}}}
};
ctx.objectives = {
{"primary", "secure_hill"},
{"secondary", "minimize_casualties"}
};
ctx.doctrine = doctrineManager->getDoctrine("nato");
// Charger poids
Weights weights = loadWeights("config/ai/tactical_weights.json", "flanking");
// Créer décision
auto decision = DecisionFactory::createUnique("flanking", ctx, weights);
// Générer et évaluer options
std::vector<Option> options = decision->generateOptions();
for (auto& opt : options) {
float score = decision->scoreOption(opt, ctx);
LOG_DEBUG("Option: {} - Score: {}", opt.type, score);
}
// Choisir meilleure
Option best = decision->chooseBest();
LOG_INFO("Chosen: {} (score: {})", best.type, best.base_score);
// Exécuter
Action action = decision->execute(best);
// Observer résultat et apprendre
float outcome = waitForBattleResult();
rlEngine->recordDecision("flanking", weights, best, outcome);
Exemple : Company AI Investment
// Contexte company
Context ctx;
ctx.world_state = {
{"market_demand", "high"},
{"tech_level", "gen3"},
{"competition", "moderate"}
};
ctx.entities = {
{"budget", 1000000},
{"reputation", 0.75},
{"research_capacity", 5}
};
Weights weights = companyProfile.weights; // Profil company unique
auto decision = new CompanyInvestmentDecision(ctx, weights);
Option investment = decision->chooseBest();
if (investment.type == "research") {
TechId tech = investment.parameters["tech_id"];
startResearch(tech);
}
else if (investment.type == "lobbying") {
StateId state = investment.parameters["state_id"];
float cost = investment.parameters["cost"];
lobbyState(state, cost);
}
delete decision;
Testing et Validation
Test Unitaire Decision
#ifdef TESTING
void testFlankingDecision() {
// Setup contexte test
Context ctx = createTestContext();
Weights weights = {{"safety", 0.5}, {"aggression", 0.8}};
// Créer décision
auto decision = new FlankingDecision(ctx, weights);
// Vérifier génération options
auto options = decision->generateOptions();
assert(options.size() > 0);
// Vérifier scoring
for (auto& opt : options) {
float score = decision->scoreOption(opt, ctx);
assert(score >= 0.0f && score <= 1.0f);
}
// Vérifier choix cohérent
Option best = decision->chooseBest();
assert(best.type == "flanking");
delete decision;
}
#endif
Benchmark Performance
void benchmarkDecisionSpeed() {
Context ctx = createBenchmarkContext();
Weights weights = loadWeights("tactical_weights.json", "flanking");
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 1000; i++) {
auto decision = new FlankingDecision(ctx, weights);
Option best = decision->chooseBest();
delete decision;
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "1000 decisions in " << duration.count() << "μs\n";
std::cout << "Average: " << (duration.count() / 1000.0f) << "μs per decision\n";
}