warfactoryracine/docs/ai-framework.md
StillHammer 5e4235889a Add comprehensive AI, diplomacy, and save system documentation
- 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>
2025-10-06 22:39:36 +08:00

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 :

  1. Générer options disponibles dans le contexte actuel
  2. Scorer chaque option selon poids configurables et situation
  3. Choisir la meilleure option (highest score)
  4. 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 militaires
  • economie-logistique.md : Marché, pricing, supply chains
  • architecture-modulaire.md : Intégration modules, hot-reload
  • systeme-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";
}