From e91044a004b2f71094f17d97a1859e4172b1d725 Mon Sep 17 00:00:00 2001 From: Robosturm Date: Fri, 6 Oct 2023 17:20:18 +0200 Subject: [PATCH] removed neural network --- CMakeLists.txt | 9 +- ai/heavyai.cpp | 1848 --------------------- ai/heavyai.h | 678 -------- ai/heavyai/heavyai.cpp | 34 + ai/heavyai/heavyai.h | 23 + ai/heavyai/heavyaiproduction.cpp | 610 ------- ai/heavyai/heavyaitraining.cpp | 131 -- ai/neuralnetwork/neural/edge.cpp | 47 - ai/neuralnetwork/neural/edge.h | 55 - ai/neuralnetwork/neural/layer.cpp | 218 --- ai/neuralnetwork/neural/layer.h | 102 -- ai/neuralnetwork/neural/neuralnetwork.cpp | 182 -- ai/neuralnetwork/neural/neuralnetwork.h | 121 -- ai/neuralnetwork/neural/neuron.cpp | 259 --- ai/neuralnetwork/neural/neuron.h | 89 - coreengine/metatyperegister.cpp | 3 +- game/gamemap.cpp | 8 +- gameinput/basegameinputif.cpp | 2 +- 18 files changed, 64 insertions(+), 4355 deletions(-) delete mode 100644 ai/heavyai.cpp delete mode 100644 ai/heavyai.h create mode 100644 ai/heavyai/heavyai.cpp create mode 100644 ai/heavyai/heavyai.h delete mode 100644 ai/heavyai/heavyaiproduction.cpp delete mode 100644 ai/heavyai/heavyaitraining.cpp delete mode 100644 ai/neuralnetwork/neural/edge.cpp delete mode 100644 ai/neuralnetwork/neural/edge.h delete mode 100644 ai/neuralnetwork/neural/layer.cpp delete mode 100644 ai/neuralnetwork/neural/layer.h delete mode 100644 ai/neuralnetwork/neural/neuralnetwork.cpp delete mode 100644 ai/neuralnetwork/neural/neuralnetwork.h delete mode 100644 ai/neuralnetwork/neural/neuron.cpp delete mode 100644 ai/neuralnetwork/neural/neuron.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fb0ee51a5..7db907af1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -726,20 +726,13 @@ set(${PROJECT_NAME}_SRCS ai/coreai_predefinedai.cpp ai/proxyai.cpp ai/proxyai.h ai/normalai.cpp ai/normalai.h - ai/heavyai.cpp ai/heavyai.h - ai/heavyai/heavyaiproduction.cpp - ai/heavyai/heavyaitraining.cpp + ai/heavyai/heavyai.cpp ai/heavyai/heavyai.h ai/influencefrontmap.cpp ai/influencefrontmap.h ai/dummyai.h ai/dummyai.cpp ai/aiprocesspipe.h ai/aiprocesspipe.cpp # production system ai/productionSystem/simpleproductionsystem.h ai/productionSystem/simpleproductionsystem.cpp - # neuralnetwork - ai/neuralnetwork/neural/edge.cpp ai/neuralnetwork/neural/edge.h - ai/neuralnetwork/neural/layer.cpp ai/neuralnetwork/neural/layer.h - ai/neuralnetwork/neural/neuralnetwork.cpp ai/neuralnetwork/neural/neuralnetwork.h - ai/neuralnetwork/neural/neuron.cpp ai/neuralnetwork/neural/neuron.h # decision tree ai/decisiontree/leaf.cpp ai/decisiontree/leaf.h ai/decisiontree/decisionnode.cpp ai/decisiontree/decisionnode.h diff --git a/ai/heavyai.cpp b/ai/heavyai.cpp deleted file mode 100644 index 77f74fa01..000000000 --- a/ai/heavyai.cpp +++ /dev/null @@ -1,1848 +0,0 @@ -#include - -#include "coreengine/qmlvector.h" -#include "coreengine/gameconsole.h" -#include "coreengine/globalutils.h" - -#include "ai/heavyai.h" - -#include "game/player.h" -#include "game/gameaction.h" -#include "game/gamemap.h" - -#include "resource_management/unitspritemanager.h" - -const qint32 HeavyAi::minSiloDamage = 7000; -const char* const HeavyAi::NeuralNetworkFileEnding = ".net"; -const char* const HeavyAi::NeuralNetworkPath = "aidata/heavy/"; -const char* const HeavyAi::NeuralNetworkNames[] = {"Production", - "ACTION_FIRE", - "ACTION_CAPTURE", - "ACTION_UNLOADING", - "WAIT_DISTANCE_MULTIPLIER"}; -// normally i'm not a big fan of this but else the function table gets unreadable -using namespace std::placeholders; - -HeavyAi::HeavyAi(GameMap* pMap, QString type, GameEnums::AiTypes aiType) - : CoreAI(pMap, aiType, type), - m_scoreInfos({ -{ACTION_CAPTURE, std::bind(&HeavyAi::scoreCapture, this, _1, _2, _3)}, -{ACTION_FIRE, std::bind(&HeavyAi::scoreFire, this, _1, _2, _3)}, -{ACTION_JOIN, std::bind(&HeavyAi::scoreJoin, this, _1, _2, _3)}, -{ACTION_MISSILE, std::bind(&HeavyAi::scoreMissile, this, _1, _2, _3)}, -{ACTION_LOAD, std::bind(&HeavyAi::scoreLoad, this, _1, _2, _3)}, -{ACTION_UNLOAD, std::bind(&HeavyAi::scoreUnload, this, _1, _2, _3)}, -{ACTION_WAIT, std::bind(&HeavyAi::scoreWait, this, _1, _2, _3)}, - }), - m_InfluenceFrontMap(pMap, m_IslandMaps), - m_timer(this) -{ -#ifdef GRAPHICSUPPORT - setObjectName("HeavyAi"); -#endif - Interpreter::setCppOwnerShip(this); - setupJsThis(this); - - m_timer.setSingleShot(false); - connect(&m_timer, &QTimer::timeout, this, &HeavyAi::process, Qt::QueuedConnection); - - m_iniData = { // General - {"MinActionScore", "General", &m_minActionScore, 0.2f, 0.0f, 10.0f}, - {"ActionScoreVariant", "General", &m_actionScoreVariant, 0.05f, 0.01f, 0.5f}, - {"StealthDistanceMultiplier", "General", &m_stealthDistanceMultiplier, 2.0f, 1.0f, 10.0f}, - {"AlliedDistanceModifier", "General", &m_alliedDistanceModifier, 5.0f, 1.0f, 10.0f}, - {"MaxMovementpoints", "General", &m_maxMovementpoints, 20.0f, 20.0f, 20.0f}, - {"MaxProductionTurnRange", "General", &m_maxProductionTurnRange, 4.0f, 1.0f, 10.0f}, - {"MaxFirerange", "General", &m_maxFirerange, 10.0f, 10.0f, 10.0f}, - {"PrimaryEnemyMultiplier", "General", &m_primaryEnemyMultiplier, 1.2f, 1.0f, 10.0f}, - {"MaxLoadingPlace", "General", &m_maxLoadingPlace, 4.0f, 1.0f, 10.0f}, - {"NotAttackableDamage", "General", &m_notAttackableDamage, 30.0f, 1.0f, 45.0f}, - {"OwnUnitProtection", "General", &m_notAttackableDamage, 5.0f, 1.0f, 10.0f}, - {"EnemyUnitThread", "General", &m_notAttackableDamage, 5.0f, 1.0f, 10.0f}, - {"MaxVision", "General", &m_notAttackableDamage, 10.0f, 10.0f, 10.0f}, - {"MaxUnitValue", "General", &m_maxUnitValue, 40000.0f, 40000.0f, 40000.0f}, - {"MaxScore", "General", &m_maxScore, 10.0f, 0.0f, 10.0f}, - {"MaxTerrainDefense", "General", &m_maxTerrainDefense, 15.0f, 15.0f, 15.0f}, - {"MaxCapturePoints", "General", &m_maxCapturePoints, 20.0f, 20.0f, 20.0f}, - {"MinSameIslandDistance", "General", &m_minSameIslandDistance, 3.0f, 3.0f, 3.0f}, - {"MinUnitHealth", "General", &m_minUnitHealth, 3.0f, 0.0f, 10.0f}, - {"SlowUnitSpeed", "General", &m_slowUnitSpeed, 2.0f, 2.0f, 2.0f}, - {"UsedCapturePointIncrease", "General", &m_usedCapturePointIncrease, 1.5f, 1.5f, 1.5f}, - }; - if (m_pMap != nullptr) - { - m_currentTargetedPfsTargets.reserve(m_pMap->getMapHeight() * m_pMap->getMapWidth()); - } - if (m_pMap != nullptr && - !m_pMap->getSavegame()) - { - loadIni("heavy/" + m_aiName.toLower() + ".ini"); - } - loadNeuralNetworks(aiType); - CONSOLE_PRINT("Creating heavy ai", GameConsole::eDEBUG); -} - -void HeavyAi::loadNeuralNetworks(GameEnums::AiTypes aiType) -{ - bool randomize = static_cast(aiType) - GameEnums::AiTypes_Heavy >= 3; - loadNeuralNetwork(NeuralNetworkNames[NeuralNetworks::Production], m_neuralNetworks[NeuralNetworks::Production], static_cast(BuildingEntryMaxSize), BuildingEntryMaxSize / 4, randomize); - loadNeuralNetwork(NeuralNetworkNames[NeuralNetworks::ActionFire], m_neuralNetworks[NeuralNetworks::ActionFire], static_cast(AttackInfoMaxSize), AttackInfoMaxSize / 4, randomize); - loadNeuralNetwork(NeuralNetworkNames[NeuralNetworks::ActionCapture], m_neuralNetworks[NeuralNetworks::ActionCapture], static_cast(CaptureInfoMaxSize), CaptureInfoMaxSize / 4, randomize); - loadNeuralNetwork(NeuralNetworkNames[NeuralNetworks::ActionUnloading], m_neuralNetworks[NeuralNetworks::ActionUnloading], static_cast(UnloadingInfoMaxSize), UnloadingInfoMaxSize / 4, randomize); - // todo - loadNeuralNetwork(NeuralNetworkNames[NeuralNetworks::WaitDistanceMultiplier], m_neuralNetworks[NeuralNetworks::WaitDistanceMultiplier], 5, 5, randomize, WaitTargetTypesMaxSize); -} - -void HeavyAi::loadNeuralNetwork(QString netName, spNeuralNetwork & network, qint32 inputVectorSize, qint32 netDepth, bool randomize, qint32 outputSize) -{ - network = MemoryManagement::create(); - network->setNetworkName(netName); - QString baseName = NeuralNetworkPath + netName + m_aiName + NeuralNetworkFileEnding; - QStringList searchFiles; - // make sure to overwrite existing js stuff - for (qint32 i = 0; i < Settings::getInstance()->getMods().size(); i++) - { - searchFiles.append(QString(oxygine::Resource::RCC_PREFIX_PATH) + Settings::getInstance()->getMods().at(i) + baseName); - searchFiles.append(Settings::getInstance()->getUserPath() + Settings::getInstance()->getMods().at(i) + baseName); - } - searchFiles.append("resources/" + baseName); - searchFiles.append(QString(oxygine::Resource::RCC_PREFIX_PATH) + "/resources/" + baseName); - bool found = false; - for (qint32 i = 0; i < searchFiles.size(); i++) - { - if (QFile::exists(searchFiles[i])) - { - QFile file(searchFiles[i]); - file.open(QIODevice::ReadOnly); - QDataStream stream(&file); - stream.setVersion(QDataStream::Version::Qt_6_5); - network->deserializeObject(stream); - qint32 inputSize = network->getInputSize(); - if (inputSize > inputVectorSize) - { - network->extend(inputSize - inputVectorSize, randomize); - } - found = true; - break; - } - } - if (!found) - { - QMap parameters; - parameters.insert(Layer::LAYER_PARAMETER_TYPE, static_cast(Layer::LayerType::INPUT)); - parameters.insert(Layer::LAYER_PARAMETER_ACTIVATION, static_cast(Neuron::ActivationFunction::Step)); - parameters.insert(Layer::LAYER_PARAMETER_SIZE, static_cast(inputVectorSize)); - network->addLayer(parameters); - parameters.insert(Layer::LAYER_PARAMETER_TYPE, static_cast(Layer::LayerType::STANDARD)); - for (qint32 i = 0; i < netDepth; ++i) - { - network->addLayer(parameters); - } - parameters.insert(Layer::LAYER_PARAMETER_TYPE, static_cast(Layer::LayerType::OUTPUT)); - parameters[Layer::LAYER_PARAMETER_SIZE] = outputSize; - network->addLayer(parameters); - network->autogenerate(); - QFile file("resources/" + baseName); - file.open(QIODevice::WriteOnly | QIODevice::Truncate); - QDataStream stream(&file); - stream.setVersion(QDataStream::Version::Qt_6_5); - network->serializeObject(stream); - } -} - -void HeavyAi::process() -{ - m_pBuildings = m_pPlayer->getSpBuildings(); - m_pBuildings->randomize(); - m_pEnemyBuildings = m_pPlayer->getSpEnemyBuildings(); - m_pEnemyBuildings->randomize(); - if (m_pause) - { - setupTurn(m_pBuildings); - m_timer.start(1000); - return; - } - else - { - m_timer.stop(); - } - spQmlVectorUnit pUnits(m_pPlayer->getSpUnits()); - spQmlVectorUnit pEnemyUnits(m_pPlayer->getSpEnemyUnits()); - qint32 index = -1; - FunctionType flareType = FunctionType::Undefined; - getFunctionType(CoreAI::ACTION_FLARE, flareType, index); - FunctionType ooziumType = FunctionType::Undefined; - getFunctionType(CoreAI::ACTION_HOELLIUM_WAIT, ooziumType, index); - FunctionType explodeType = FunctionType::Undefined; - getFunctionType(CoreAI::ACTION_EXPLODE, explodeType, index); - - if (useBuilding(m_pBuildings, pUnits)){} - else if (useCOPower(pUnits, pEnemyUnits)) - { - m_usedPredefinedAi = false; - } - else - { - setupTurn(m_pBuildings); - m_turnMode = GameEnums::AiTurnMode_DuringDay; - if (CoreAI::buildCOUnit(m_pUnits)){} - // use core ai here for the start - else if (flareType != FunctionType::JavaScript && CoreAI::moveFlares(m_pUnits)){} - else if (ooziumType != FunctionType::JavaScript && CoreAI::moveOoziums(m_pUnits, m_pEnemyUnits)){} - else if (explodeType != FunctionType::JavaScript && CoreAI::moveBlackBombs(m_pUnits, m_pEnemyUnits)){} - // actual heavy ai impl - else if (!selectActionToPerform()) - { - if (m_aiStep < AISteps::moveToTargets) - { - m_aiStep = AISteps::moveToTargets; - scoreMoveToTargets(); - } - if (!selectActionToPerform()) - { - if (m_aiStep < AISteps::moveTransporters) - { - m_aiStep = AISteps::moveTransporters; - scoreMoveToTargets(); - } - if (!selectActionToPerform()) - { - if (!buildUnits(m_pBuildings, m_pUnits, m_pEnemyUnits, m_pEnemyBuildings)) - { - endTurn(); - } - } - } - } - } -} - -void HeavyAi::endTurn() -{ - m_aiStep = AISteps::moveUnits; - m_turnMode = GameEnums::AiTurnMode_EndOfDay; - m_pUnits.reset(); - m_pEnemyUnits.reset(); - m_usedPredefinedAi = false; - spQmlVectorUnit pUnits(m_pPlayer->getSpUnits()); - spQmlVectorUnit pEnemyUnits(m_pPlayer->getSpEnemyUnits()); - if (useCOPower(pUnits, pEnemyUnits)){} - else - { - // end the turn for the player - m_turnMode = GameEnums::AiTurnMode_StartOfDay; - finishTurn(); - } -} - -bool HeavyAi::selectActionToPerform() -{ - AI_CONSOLE_PRINT("HeavyAi selecting action to be performed", GameConsole::eDEBUG); - float bestScore = std::numeric_limits::lowest(); - qint32 index = -1; - qint32 pos = 0; - for (const auto & ownUnit : qAsConst(m_ownUnits)) - { - if (ownUnit.m_action.get() != nullptr && - ownUnit.m_score > bestScore) - { - if (ownUnit.pUnit->getTerrain() == nullptr || - ownUnit.pUnit->getHp() <= 0) - { - oxygine::handleErrorPolicy(oxygine::error_policy::ep_show_error, "invalid unit found"); - continue; - } - bestScore = ownUnit.m_score; - index = pos; - } - ++pos; - } - if (index >= 0) - { - auto & unit = m_ownUnits[index]; - QPoint target = unit.m_action->getTarget(); - AI_CONSOLE_PRINT("HeavyAi selected action " + unit.m_action->getActionID() + " to be performed with score " + QString::number(bestScore), GameConsole::eDEBUG); - m_updatePoints.push_back(target); - m_updatePoints.push_back(unit.m_action->getActionTarget()); - if (target != unit.pUnit->Unit::getPosition()) - { - oxygine::handleErrorPolicy(oxygine::error_policy::ep_show_error, "HeavyAi::selectActionToPerform action error"); - unit.m_action.reset(); - unit.m_score = 0; - return false; - } - if (unit.captureTarget.x() >= 0) - { - m_planedCaptureTargets.push_back(unit.captureTarget); - } - emit sigPerformAction(unit.m_action); - unit.m_action.reset(); - unit.m_score = 0; - unit.captureTarget = QPoint(-1, -1); - return true; - } - return false; -} - -void HeavyAi::setupTurn(const spQmlVectorBuilding & buildings) -{ - qint32 cost = 0; - m_pPlayer->getSiloRockettarget(2, 3, cost); - m_missileTarget = (cost >= minSiloDamage); - bool startOfTurn = (m_pUnits.get() == nullptr); - if (m_pEnemyUnits.get() == nullptr) - { - m_pEnemyUnits = m_pPlayer->getSpEnemyUnits(); - m_pEnemyUnits->randomize(); - initUnits(m_pEnemyUnits, m_enemyUnits, true); - } - if (m_pUnits.get() == nullptr) - { - m_pUnits = m_pPlayer->getSpUnits(); - initUnits(m_pUnits, m_ownUnits, false); - } - if (startOfTurn) - { - AI_CONSOLE_PRINT("HeavyAi initial start of turn calculation", GameConsole::eDEBUG); - createIslandMaps(); - - // create influence map at the start of the turn - m_InfluenceFrontMap.reset(); - m_InfluenceFrontMap.setOwner(m_pPlayer); - m_InfluenceFrontMap.addBuildingInfluence(); - for (auto & unit : m_ownUnits) - { - m_InfluenceFrontMap.addUnitInfluence(unit.pUnit.get(), unit.pUnitPfs.get(), unit.movementPoints); - } - for (auto & unit : m_enemyUnits) - { - m_InfluenceFrontMap.addUnitInfluence(unit.pUnit.get(), unit.pUnitPfs.get(), unit.movementPoints); - } - m_InfluenceFrontMap.updateOwners(); - m_InfluenceFrontMap.calculateGlobalData(); - AI_CONSOLE_PRINT("HeavyAi front lines created", GameConsole::eDEBUG); - findHqThreads(buildings); - AI_CONSOLE_PRINT("HeavyAi initial scoring actions for units", GameConsole::eDEBUG); - for (auto & unit : m_ownUnits) - { - scoreActions(unit); - } - } - else - { - updateUnits(); - } -} - -void HeavyAi::createIslandMaps() -{ - UnitSpriteManager* pUnitSpriteManager = UnitSpriteManager::getInstance(); - qint32 unitCount = pUnitSpriteManager->getCount(); - for (qint32 i = 0; i < unitCount; i++) - { - QString unitId = pUnitSpriteManager->getID(i); - createIslandMap(pUnitSpriteManager->getMovementType(unitId), unitId); - } -} - -void HeavyAi::initUnits(spQmlVectorUnit & pUnits, std::vector & units, bool enemyUnits) -{ - units.clear(); - for (auto & pUnit : pUnits->getVector()) - { - addNewUnitToUnitData(units, pUnit.get(), enemyUnits); - } -} - -void HeavyAi::addNewUnitToUnitData(std::vector & units, Unit* pUnit, bool enemyUnits) -{ - if (pUnit != nullptr) - { - MoveUnitData data; - data.pUnit = pUnit->getSharedPtrFromWeak(); - data.pUnitPfs = MemoryManagement::create(m_pMap, pUnit); - data.movementPoints = data.pUnit->getMovementpoints(data.pUnit->getPosition()); - data.pUnitPfs->setMovepoints(data.movementPoints * 2); - data.pUnitPfs->setIgnoreEnemies(UnitPathFindingSystem::CollisionIgnore::OnlyNotMovedEnemies); - data.pUnitPfs->explore(); - if (!enemyUnits) - { - updateCaptureBuildings(data); - } - units.push_back(data); - } -} - -void HeavyAi::updateUnits() -{ - updateUnits(m_ownUnits, m_pUnits, false); - updateUnits(m_enemyUnits, m_pEnemyUnits, true); - m_updatePoints.clear(); -} - -void HeavyAi::updateUnits(std::vector & units, spQmlVectorUnit & pUnits, bool enemyUnits) -{ - - if (m_pMap == nullptr) - { - return; - } - qint32 i = 0; - while (i < units.size()) - { - if (units[i].pUnit->getHp() <= 0 || - units[i].pUnit->getTerrain() == nullptr) - { - units.erase(units.cbegin() + i); - } - else - { - if (units[i].pUnit->getHasMoved()) - { - units[i].m_action.reset(); - units[i].m_score = 0; - } - ++i; - } - } - i = 0; - while (i < pUnits->size()) - { - if (pUnits->at(i)->getHp() <= 0 || - pUnits->at(i)->getTerrain() == nullptr) - { - pUnits->removeAt(i); - } - else - { - ++i; - } - } - std::vector updated; - spQmlVectorPoint pPoints = GlobalUtils::getSpCircle(1, 5); - for (auto & updatePoint : m_updatePoints) - { - qint32 i2 = 0; - while (i2 < units.size()) - { - if (!GlobalUtils::contains(updated, i2)) - { - if (qAbs(updatePoint.x() - units[i2].pUnit->Unit::getX()) + - qAbs(updatePoint.y() - units[i2].pUnit->Unit::getY()) <= - units[i2].pUnit->getMovementpoints(QPoint(units[i2].pUnit->Unit::getX(), units[i2].pUnit->Unit::getY())) + 2) - { - units[i2].pUnitPfs = MemoryManagement::create(m_pMap, units[i2].pUnit.get()); - units[i2].pUnitPfs->setIgnoreEnemies(UnitPathFindingSystem::CollisionIgnore::OnlyNotMovedEnemies); - units[i2].pUnitPfs->explore(); - if (!enemyUnits) - { - updateCaptureBuildings(units[i2]); - scoreActions(units[i2]); - } - updated.push_back(i2); - } - ++i2; - } - else - { - ++i2; - } - - } - if (!enemyUnits) - { - for (auto & point : pPoints->getVector()) - { - QPoint pos = point + m_updatePoints[i]; - if (m_pMap->onMap(pos.x(), pos.y())) - { - bool found = false; - Unit* pUnit = m_pMap->getTerrain(pos.x(), pos.y())->getUnit(); - if (pUnit != nullptr && - pUnit->getOwner() == m_pPlayer) - { - for (const auto & unit : units) - { - if (unit.pUnit.get() == pUnit) - { - found = true; - break; - } - } - if (!found) - { - pUnits->append(pUnit); - addNewUnitToUnitData(units, pUnit, enemyUnits); - } - } - } - } - } - } -} - -void HeavyAi::updateCaptureBuildings(MoveUnitData & unitData) -{ - if (unitData.actions.contains(ACTION_CAPTURE)) - { - unitData.m_capturePoints.clear(); - GameAction action(ACTION_CAPTURE, m_pMap); - action.setTarget(QPoint(unitData.pUnit->Unit::getX(), unitData.pUnit->Unit::getY())); - auto targets = unitData.pUnitPfs->getAllNodePointsFast(unitData.movementPoints + 1); - for (const auto & target : targets) - { - action.setMovepath(QVector(1, target), 0); - if (action.canBePerformed()) - { - unitData.m_capturePoints.append(target); - } - } - } -} - -void HeavyAi::findHqThreads(const spQmlVectorBuilding & buildings) -{ - AI_CONSOLE_PRINT("Searching for HQ Threads", GameConsole::eDEBUG); - std::vector hqPositions; - for (auto & pBuilding : buildings->getVector()) - { - if (pBuilding->getBuildingID() == CoreAI::BUILDING_HQ) - { - hqPositions.push_back(QVector3D(pBuilding->Building::getX(), pBuilding->Building::getY(), 1)); - } - } - for (auto & enemy : m_enemyUnits) - { - if (isCaptureTransporterOrCanCapture(enemy.pUnit.get())) - { - constexpr qint32 dayDistance = 3; - QPoint pos = enemy.pUnit->getMapPosition(); - qint32 movementPoints = enemy.pUnit->getMovementpoints(enemy.pUnit->getMapPosition()); - for (const auto & hqPos : hqPositions) - { - if (GlobalUtils::getDistance(pos, QPoint(hqPos.x(), hqPos.y())) <= dayDistance * movementPoints) - { - TargetedUnitPathFindingSystem pfs = TargetedUnitPathFindingSystem(m_pMap, enemy.pUnit.get(), hqPositions, &m_MoveCostMap); - pfs.setIgnoreEnemies(UnitPathFindingSystem::CollisionIgnore::OnlyNotMovedEnemies); - pfs.setMovepoints(movementPoints * dayDistance); - pfs.setAbortOnCostExceed(true); - pfs.setFast(true); - pfs.setUseBasecosts(true); - pfs.explore(); - QPoint targetFields = pfs.getReachableTargetField(movementPoints * 3); - if (targetFields.x() >= 0) - { - // todo maybe we need to save more data here - enemy.m_threadLevel = ThreadLevel::Hq; - enemy.m_hqThread = targetFields; - } - } - } - } - } -} - -bool HeavyAi::isCaptureTransporterOrCanCapture(Unit* pUnit) -{ - bool canCapture = false; - if (pUnit->getActionList().contains(CoreAI::ACTION_CAPTURE)) - { - canCapture = true; - } - else if (pUnit->getLoadedUnitCount() > 0) - { - for (auto & pLoadedUnit : pUnit->getLoadedUnits()) - { - if (isCaptureTransporterOrCanCapture(pLoadedUnit.get())) - { - canCapture = true; - break; - } - } - } - return canCapture; -} - -void HeavyAi::scoreActions(MoveUnitData & unit) -{ - AI_CONSOLE_PRINT("HeavyAi::scoreActions", GameConsole::eDEBUG); - if (!unit.pUnit->getHasMoved() && - isUsingUnit(unit.pUnit.get()) && - unit.pUnit->getAiMode() == GameEnums::GameAi_Normal) - { - if (unit.pUnit->getTerrain() == nullptr || - unit.pUnit->getHp() <= 0) - { - oxygine::handleErrorPolicy(oxygine::error_policy::ep_show_error, "invalid unit found"); - unit.m_action.reset(); - unit.m_score = 0; - return; - } - std::vector scoreInfos; - float bestScore = m_minActionScore - 1.0; - unit.actions = unit.pUnit->getActionList(); - auto moveTargets = unit.pUnitPfs->getAllNodePointsFast(unit.movementPoints + 1); - if (m_aiStep >= AISteps::moveToTargets || - unit.actions.contains(ACTION_CAPTURE)) - { - prepareWaitPfs(unit, unit.actions); - } - for (const auto & action : unit.actions) - { - if (isScoringAllowed(action, unit.actions)) - { - FunctionType type = FunctionType::CPlusPlus; - qint32 index = -1; - getFunctionType(action, type, index); - if (type != FunctionType::Undefined) - { - mutateActionForFields(unit, moveTargets, action, type, index, - bestScore, scoreInfos); - } - } - } - if (scoreInfos.size() > 0) - { - qint32 item = GlobalUtils::randInt(0, scoreInfos.size() - 1); - unit.m_score = scoreInfos[item].m_score; - unit.m_action = scoreInfos[item].m_gameAction; - unit.captureTarget = scoreInfos[item].m_captureTarget; - } - else - { - unit.m_score = 0.0f; - unit.m_action.reset(); - } - m_currentTargetedPfs.reset(); - } -} - -bool HeavyAi::isScoringAllowed(const QString & action, const QStringList & actions) -{ - bool ret = false; - if (m_aiStep >= AISteps::moveTransporters) - { - ret = true; - } - else if (m_aiStep >= AISteps::moveToTargets && - m_secondActions.contains(action)) - { - ret = true; - } - else if (!m_secondActions.contains(action) || - (action == ACTION_WAIT && actions.contains(ACTION_CAPTURE))) - { - ret = true; - } - return ret; -} - -void HeavyAi::mutateActionForFields(MoveUnitData & unitData, const std::vector & moveTargets, - QString action, FunctionType type, qint32 index, - float & bestScore, std::vector & scoreInfos) -{ - AI_CONSOLE_PRINT("HeavyAi::mutateActionForFields " + action, GameConsole::eDEBUG); - for (const auto & target : moveTargets) - { - auto path = unitData.pUnitPfs->getPathFast(target.x(), target.y()); - qint32 costs = unitData.pUnitPfs->getCosts(path); - bool mutate = true; - std::vector stepPosition; - stepPosition.reserve(40); - std::vector maxStepOtions; - maxStepOtions.reserve(40); - std::vector baseData(static_cast(BasicFieldInfo::BasicFieldInfoMaxSize)); - spGameAction pDummy = MemoryManagement::create(m_pMap); - pDummy->setActionID(action); - pDummy->setMovepath(path, costs); - pDummy->setTarget(QPoint(unitData.pUnit->Unit::getX(), unitData.pUnit->Unit::getY())); - getBasicFieldInputVector(pDummy, baseData); - while (mutate) - { - spGameAction pAction = MemoryManagement::create(m_pMap); - pAction->setActionID(action); - pAction->setMovepath(path, costs); - pAction->setTarget(QPoint(unitData.pUnit->Unit::getX(), unitData.pUnit->Unit::getY())); - qint32 step = 0; - if (pAction->canBePerformed()) - { - ScoreData data; - data.m_gameAction = pAction; - mutate = mutateAction(data, unitData, baseData, type, index, step, stepPosition, maxStepOtions); - if (data.m_score > m_maxScore) - { - data.m_score = m_maxScore; - } - if (data.m_score > m_minActionScore) - { - if (data.m_score > bestScore) - { - bestScore = data.m_score; - qint32 i = 0; - while (i < scoreInfos.size()) - { - if (scoreInfos[i].m_score < bestScore - m_actionScoreVariant) - { - scoreInfos.erase(scoreInfos.cbegin() + i); - } - else - { - ++i; - } - } - scoreInfos.push_back(data); - } - else if (bestScore - m_actionScoreVariant <= data.m_score) - { - scoreInfos.push_back(data); - } - } - } - // stop mutating? - if (stepPosition.size() == 0) - { - break; - } - } - } -} - -bool HeavyAi::mutateAction(ScoreData & data, MoveUnitData & unitData, std::vector & baseData, FunctionType type, qint32 functionIndex, qint32 & step, - std::vector & stepPosition, std::vector & maxStepOtions) -{ - bool ret = false; - if (data.m_gameAction->isFinalStep()) - { - switch (type) - { - case FunctionType::JavaScript: - { - Interpreter* pInterpreter = Interpreter::getInstance(); - QJSValueList args({m_jsThis, - JsThis::getJsThis(data.m_gameAction.get())}); - QJSValue erg = pInterpreter->doFunction(m_aiName, data.m_gameAction->getActionID(), args); - if (erg.isNumber()) - { - data.m_score = erg.toNumber(); - } - break; - } - case FunctionType::CPlusPlus: - { - m_scoreInfos[functionIndex].callback(data, unitData, baseData); - break; - } - case FunctionType::Undefined: - { - CONSOLE_PRINT("Undefined function type", GameConsole::eDEBUG); - break; - } - default: - { - oxygine::handleErrorPolicy(oxygine::ep_show_error, "HeavyAi::mutateAction invalid calculation type"); - } - } - ret = true; - } - else - { - QString stepType = data.m_gameAction->getStepInputType(); - if (stepType == GameAction::INPUTSTEP_MENU) - { - spMenuData pData = data.m_gameAction->getMenuStepData(); - if (pData->validData()) - { - const auto & enableList = pData->getEnabledList(); - const auto & costList = pData->getCostList(); - const auto & actionList = pData->getActionIDs(); - qint32 nextStep = getNextMutateStep(step, stepPosition, maxStepOtions, actionList.size()); - for (qint32 i = nextStep; i < actionList.size(); ++i) - { - if (enableList[i]) - { - CoreAI::addMenuItemData(data.m_gameAction, actionList[i], costList[i]); - stepPosition[step] = i; - ++step; - ret = mutateAction(data, unitData, baseData, type, functionIndex, step, stepPosition, maxStepOtions); - break; - } - } - } - } - else if (stepType == GameAction::INPUTSTEP_FIELD) - { - spMarkedFieldData pData = data.m_gameAction->getMarkedFieldStepData(); - QVector* pFields = pData->getPoints(); - qint32 maxOptions = 1; - if (data.m_gameAction->getActionID() != ACTION_MISSILE) - { - maxOptions = pFields->size(); - } - qint32 nextStep = getNextMutateStep(step, stepPosition, maxStepOtions, maxOptions); - if (pData->getAllFields()) - { - // special case for missile - if (nextStep == 0 && - data.m_gameAction->getActionID() == ACTION_MISSILE) - { - qint32 cost = 0; - QPoint rocketTarget = m_pPlayer->getSiloRockettarget(2, 3, cost); - CoreAI::addSelectedFieldData(data.m_gameAction, rocketTarget); - ++step; - ret = mutateAction(data, unitData, baseData, type, functionIndex, step, stepPosition, maxStepOtions); - } - else - { - Interpreter* pInterpreter = Interpreter::getInstance(); - QJSValueList args({m_jsThis, - JsThis::getJsThis(data.m_gameAction.get())}); - QString func = data.m_gameAction->getActionID() + "GetBestField"; - QJSValue erg = pInterpreter->doFunction(m_aiName, func, args); - QPoint target = erg.toVariant().toPoint(); - CoreAI::addSelectedFieldData(data.m_gameAction, target); - ++step; - ret = mutateAction(data, unitData, baseData, type, functionIndex, step, stepPosition, maxStepOtions); - } - } - else - { - for (qint32 i = nextStep; i < pFields->size(); ++i) - { - CoreAI::addSelectedFieldData(data.m_gameAction, (*pFields)[i]); - stepPosition[step] = i; - ++step; - ret = mutateAction(data, unitData, baseData, type, functionIndex, step, stepPosition, maxStepOtions); - break; - } - } - } - else - { - CONSOLE_PRINT("Uknown action step type: " + stepType, GameConsole::eERROR); - } - } - return ret; -} - -qint32 HeavyAi::getNextMutateStep(qint32 step, std::vector & stepPosition, std::vector & maxStepOtions, qint32 maxOptions) -{ - qint32 nextStep = 0; - if (step < stepPosition.size()) - { - if (step + 1 < stepPosition.size()) - { - if (stepPosition[step + 1] + 1 < maxStepOtions[step + 1]) - { - nextStep = stepPosition[step]; - } - else - { - nextStep = ++stepPosition[step]; - // reset mutation for following steps - stepPosition.resize(step + 1); - maxStepOtions.resize(step + 1); - } - } - else - { - nextStep = ++stepPosition[step]; - // reset mutation for following steps - stepPosition.resize(step + 1); - maxStepOtions.resize(step + 1); - } - maxStepOtions[step] = maxOptions; - } - else - { - stepPosition.push_back(0); - maxStepOtions.push_back(maxOptions); - } - return nextStep; -} - -void HeavyAi::getFunctionType(const QString & action, FunctionType & type, qint32 & index) -{ - Interpreter* pInterpreter = Interpreter::getInstance(); - index = -1; - type = FunctionType::Undefined; - if (pInterpreter->exists(m_aiName, action)) - { - type = FunctionType::JavaScript; - } - else - { - for (qint32 i = 0; i < m_scoreInfos.size(); ++i) - { - if (m_scoreInfos[i].m_actionId == action) - { - type = FunctionType::CPlusPlus; - index = i; - break; - } - } - } -} - -void HeavyAi::getBasicFieldInputVector(spGameAction & action, std::vector & data) -{ - Unit* pMoveUnit = action->getTargetUnit(); - QPoint moveTarget = action->getActionTarget(); - getBasicFieldInputVector(pMoveUnit, moveTarget, action->getCosts(), - pMoveUnit->getMovementpoints(action->getTarget()), data); -} - -void HeavyAi::getBasicFieldInputVector(Unit* pMoveUnit, QPoint & moveTarget, double moveCosts, double movementPoints, std::vector & data) -{ - - if (m_pMap != nullptr) - { - bool fogOfWar = m_pMap->getGameRules()->getFogMode() != GameEnums::Fog_Off; - Terrain* pTerrainTarget = m_pMap->getTerrain(moveTarget.x(), moveTarget.y()); - Building* pBuildingTarget = pTerrainTarget->getBuilding(); - qint32 playerId = m_pPlayer->getPlayerID(); - const double highestInfluece = m_InfluenceFrontMap.getTotalHighestInfluence(); - const auto * info = m_InfluenceFrontMap.getInfluenceInfo(moveTarget.x(), moveTarget.y()); - double notMovedUnitCount = 0; - double protectionValue = 0; - double ownValue = pMoveUnit->getCoUnitValue(); - for (const auto & pUnit : qAsConst(m_ownUnits)) - { - if (pUnit.pUnit->getHasMoved()) - { - ++notMovedUnitCount; - } - qint32 cost = pUnit.pUnitPfs->getTargetCosts(moveTarget.x(), moveTarget.y()); - if (cost >= 0 && cost <= movementPoints) - { - protectionValue += pUnit.pUnit->getCoUnitValue(); - } - } - double threadValue = 0; - for (const auto & pUnit : qAsConst(m_ownUnits)) - { - qint32 cost = pUnit.pUnitPfs->getTargetCosts(moveTarget.x(), moveTarget.y()); - if (cost >= 0 && cost <= movementPoints) - { - threadValue += pUnit.pUnit->getCoUnitValue(); - } - } - data[BasicFieldInfo::OwnInfluenceValue] = static_cast(info->getOwnInfluence()) / highestInfluece; - data[BasicFieldInfo::EnemyInfluenceValue] = static_cast(info->getEnemyInfluence()) / highestInfluece; - data[BasicFieldInfo::MoveTurnProgress] = notMovedUnitCount / static_cast(m_ownUnits.size()); - spQmlVectorPoint pCircle = GlobalUtils::getSpCircle(1, 1); - double wallCount = 0; - for (auto & circlePos : pCircle->getVector()) - { - QPoint pos = circlePos; - pos += moveTarget; - if (m_pMap->onMap(pos.x(), pos.y())) - { - Unit* pUnit = m_pMap->getTerrain(pos.x(), pos.y())->getUnit(); - if (pUnit != nullptr && - m_pPlayer->isAlly(pUnit->getOwner())) - { - if (pUnit->getHasMoved() || - pUnit->getOwner() != m_pPlayer) - { - ++wallCount; - } - else - { - wallCount += 0.5; - } - } - } - else - { - ++wallCount; - } - } - constexpr double adjustantTileCount = 4; - data[BasicFieldInfo::WallCount] = wallCount / adjustantTileCount; - if (pMoveUnit->isStatusStealthed()) - { - data[BasicFieldInfo::VisionHide] = 1; - } - else if (fogOfWar) - { - data[BasicFieldInfo::VisionHide] = (pTerrainTarget->getVisionHide(nullptr) == true) ? 1 : -1; - } - data[BasicFieldInfo::UsedMovement] = moveCosts / movementPoints; - data[BasicFieldInfo::UnitHealth] = pMoveUnit->getHp() / Unit::MAX_UNIT_HP; - if (threadValue > ownValue * m_enemyUnitThread) - { - data[BasicFieldInfo::EnemyThread] = 1; - } - else - { - data[BasicFieldInfo::EnemyThread] = threadValue / (ownValue * m_enemyUnitThread); - } - if (threadValue > ownValue * m_ownUnitProtection) - { - data[BasicFieldInfo::OwnProtection] = 1; - } - else - { - data[BasicFieldInfo::OwnProtection] = protectionValue / (ownValue * m_ownUnitProtection); - } - data[BasicFieldInfo::VisionRange] = static_cast(pMoveUnit->getVision(moveTarget)) / m_maxVision; - data[BasicFieldInfo::TerrainDefense] = static_cast(pMoveUnit->getTerrainDefense(moveTarget.x(), moveTarget.y())) / m_maxTerrainDefense; - if (pBuildingTarget != nullptr && - pBuildingTarget->getActionList().contains(CoreAI::ACTION_BUILD_UNITS)) - { - bool enemy = m_pPlayer->isEnemy(pBuildingTarget->getOwner()); - bool allied = m_pPlayer->isAlly(pBuildingTarget->getOwner()); - bool neutral = (pBuildingTarget->getOwner() == nullptr); - if (!neutral) - { - if (enemy) - { - data[BasicFieldInfo::TerrainDefense] = -1; - } - else if (allied) - { - data[BasicFieldInfo::TerrainDefense] = 1; - } - else - { - oxygine::handleErrorPolicy(oxygine::ep_show_error, "HeavyAi::getBasicFieldInputVector unknown building ownership"); - } - } - } - const auto & frontOwners = info->getFrontOwners(); - if (GlobalUtils::contains(frontOwners, playerId)) - { - data[BasicFieldInfo::FrontTile] = 1; - } - else if (frontOwners.size() > 0) - { - data[BasicFieldInfo::FrontTile] = -1; - } - data[BasicFieldInfo::IsCoUnit] = (pMoveUnit->getUnitRank() < 0); - } -} - -void HeavyAi::scoreCapture(ScoreData & data, MoveUnitData & unitData, std::vector & baseData) -{ - baseData.resize(baseData.size() + static_cast(CaptureInfoMaxSize - CaptureInfoStart), 0); - Building* pBuilding = data.m_gameAction->getMovementBuilding(); - auto target = data.m_gameAction->getActionTarget(); - baseData[CaptureInfoIsHq] = (pBuilding->getBuildingID() == CoreAI::BUILDING_HQ); - baseData[CaptureInfoIsComTower] = (pBuilding->getBuildingID() == "TOWER"); - baseData[CaptureInfoCaptureOptions] = 1.0 / static_cast(unitData.m_capturePoints.size()); - if (pBuilding->getActionList().contains(ACTION_BUILD_UNITS)) - { - baseData[CaptureInfoProductionPotential] = static_cast(pBuilding->getConstructionList().size()) / static_cast(m_pPlayer->getBuildList().size()); - } - double uniqueCounter = 0; - for (const auto & unit : qAsConst(m_ownUnits)) - { - if (unit.m_capturePoints.contains(target)) - { - ++uniqueCounter; - } - } - baseData[CaptureInfoUniqueCaptureBuilding] = 1.0 / uniqueCounter; - double remainingPoints = m_maxCapturePoints; - if (target == data.m_gameAction->getTarget()) - { - remainingPoints -= unitData.pUnit->getCapturePoints(); - } - double captureRate = unitData.pUnit->getCaptureRate(target); - if (captureRate > remainingPoints) - { - baseData[CaptureInfoRemainingDays] = 1.0; - } - else - { - baseData[CaptureInfoRemainingDays] = captureRate / remainingPoints; - } - auto score = m_neuralNetworks[NeuralNetworks::ActionCapture]->predict(baseData); - data.m_score = score[0]; -} - -void HeavyAi::scoreFire(ScoreData & data, MoveUnitData & unitData, std::vector & baseData) -{ - - baseData.resize(baseData.size() + static_cast(AttackInfoMaxSize - AttackInfoStart), 0); - if (m_pMap != nullptr) - { - data.m_gameAction->startReading(); - Unit* pAttacker = data.m_gameAction->getTargetUnit(); - double attackerHp = pAttacker->getHp() * Unit::MAX_UNIT_HP; - double attackerUnitValue = pAttacker->getCoUnitValue(); - qint32 x = data.m_gameAction->readDataInt32(); - qint32 y = data.m_gameAction->readDataInt32(); - Terrain* pTerrain = m_pMap->getTerrain(x, y); - Unit* pDefUnit = pTerrain->getUnit(); - auto damageData = calcUnitDamage(data.m_gameAction, QPoint(x, y)); - if (damageData.x() < 0) - { - return; - } - else if (pDefUnit != nullptr) - { - double defenderUnitHp = pDefUnit->getHp() * Unit::MAX_UNIT_HP; - double defenderUnitValue = pDefUnit->getCoUnitValue(); - double atkDamage = damageData.x(); - if (atkDamage > defenderUnitHp) - { - atkDamage = defenderUnitHp; - } - double defDamage = damageData.width(); - if (defDamage > attackerHp) - { - defDamage = attackerHp; - } - static constexpr double maxDamage = Unit::MAX_UNIT_HP * Unit::MAX_UNIT_HP; - baseData[AttackInfo::AttackDealingHpDamage] = atkDamage / defenderUnitHp; - baseData[AttackInfo::AttackReceavingHpDamage] = defDamage / attackerHp; - baseData[AttackInfo::AttackDealingAbsolutDamage] = atkDamage / maxDamage; - baseData[AttackInfo::AttackReceicingAbsolutDamage] = defDamage / maxDamage; - baseData[AttackInfo::AttackDealingFundsDamage] = baseData[AttackInfo::AttackDealingHpDamage] * defenderUnitValue / m_maxUnitValue; - baseData[AttackInfo::AttackReceicingFundsDamage] = baseData[AttackInfo::AttackReceavingHpDamage] * attackerUnitValue / m_maxUnitValue; - if (pDefUnit->getActionList().contains(CoreAI::ACTION_CAPTURE)) - { - double captureDays = static_cast(pDefUnit->getCaptureRate(pDefUnit->Unit::getPosition())) / (m_maxCapturePoints - static_cast(pDefUnit->getCapturePoints())); - if (captureDays > 1.0) - { - captureDays = 1.0; - } - baseData[AttackInfo::AttackRemainingCaptureDays] = captureDays; - } - for (const auto & enemy : m_enemyUnits) - { - if (enemy.pUnit.get() == pDefUnit) - { - baseData[AttackInfo::HqThread] = static_cast(enemy.m_threadLevel) / static_cast(ThreadLevel::Max); - break; - } - } - } - else if (isAttackOnTerrainAllowed(pTerrain, damageData.x())) - { - Building* pBuilding = pTerrain->getBuilding(); - double atkDamage = damageData.x(); - double hp = pTerrain->getHp(); - if (hp <= 0 && pBuilding != nullptr) - { - hp = pBuilding->getHp(); - } - if (atkDamage > hp) - { - atkDamage = hp; - } - baseData[AttackInfo::AttackDealingHpDamage] = atkDamage / hp; - baseData[AttackInfo::AttackDealingAbsolutDamage] = atkDamage / hp; - } - else - { - return; - } - } - auto score = m_neuralNetworks[NeuralNetworks::ActionFire]->predict(baseData); - data.m_score = score[0]; -} - -void HeavyAi::scoreJoin(ScoreData & data, MoveUnitData & unitData, std::vector & baseData) -{ - Unit* pJoinUnit = data.m_gameAction->getMovementTarget(); - float ret = -m_maxScore; - if (pJoinUnit != nullptr && - pJoinUnit->getHasMoved() && - pJoinUnit->getCapturePoints() > 0) - { - // simple code to give the ai the option to join capture - float resultHp = pJoinUnit->getHpRounded() + unitData.pUnit->getHpRounded(); - float malus = 0.0f; - if (resultHp > Unit::MAX_UNIT_HP) - { - malus = resultHp - Unit::MAX_UNIT_HP; - } - else - { - malus = (Unit::MAX_UNIT_HP - resultHp) * 2.0f; - } - ret = m_maxScore - malus / Unit::MAX_UNIT_HP * m_maxScore; - } - data.m_score = ret; -} - -void HeavyAi::scoreMissile(ScoreData & data, MoveUnitData & unitData, std::vector & baseData) -{ - float ret = -m_maxScore; - bool fireSilos = hasMissileTarget(); - if (fireSilos || - baseData[EnemyInfluenceValue] >= baseData[OwnInfluenceValue]) - { - ret = m_maxScore; - } - data.m_score = ret; -} - -void HeavyAi::scoreLoad(ScoreData & data, MoveUnitData & unitData, std::vector & baseData) -{ - float ret = -m_maxCapturePoints; - // we got a transporter is it a good one - Unit* pTransporter = data.m_gameAction->getMovementTarget(); - if (pTransporter != nullptr && - !pTransporter->getHasMoved()) - { - if (unitData.actions.contains(ACTION_CAPTURE)) - { - // score gets better for infantries with low movement - ret = m_maxScore / unitData.movementPoints; - } - else - { - qint32 ownValue = 0; - qint32 enemyValue = 0; - qint32 island = getIsland(unitData.pUnit.get()); - m_IslandMaps[island]->getValueOnIsland(getIslandIndex(unitData.pUnit.get()), ownValue, enemyValue); - std::vector ignoreList = {data.m_gameAction->getActionTarget()}; - qint32 targets = getNumberOfTargetsOnIsland(ignoreList); - std::vector transporterTargets; - appendTransporterTargets(unitData.pUnit.get(), m_pUnits, transporterTargets); - // Is it a useful transporter at all? - if (CoreAI::contains(transporterTargets, data.m_gameAction->getActionTarget())) - { - if (targets == 0) - { - ret = m_maxScore * unitData.unitCosts / m_maxUnitValue; - } - else - { - float unitValue = unitData.unitCosts; - float actionRatio = static_cast(targets) / static_cast(m_currentTargetedPfs->getTargets().size()); - if (ownValue - unitValue > enemyValue || - actionRatio < 0.6f) - { - ret = m_maxScore; - } - else - { - ret = m_maxScore * actionRatio; - if (ownValue > enemyValue) - { - ret *= static_cast(enemyValue) / static_cast(ownValue); - } - else - { - ret -= m_maxScore * (1 - static_cast(ownValue) / static_cast(enemyValue)); - } - } - } - } - } - } - data.m_score = ret; -} - -void HeavyAi::scoreUnload(ScoreData & data, MoveUnitData & unitData, std::vector & baseData) -{ - // we got a transporter is it a good one - Unit* pTransporter = data.m_gameAction->getTargetUnit(); - if (pTransporter != nullptr && - !pTransporter->getHasMoved() && - m_aiStep >= AISteps::moveTransporters) - { - static double halfPercent = 0.5; - double loadingCount = pTransporter->getLoadedUnitCount(); - double variableCount = data.m_gameAction->getVariableCount(); - double unloadingCount = static_cast(static_cast(variableCount - 1.0) / 3.0); - double score = 0; - auto targetPath = m_currentTargetedPfs->getTargetPathFast(); - double maxBonusScore = m_actionScoreVariant * loadingCount; - double unloadingPercent = unloadingCount / loadingCount; - if (targetPath.size() > 0 && targetPath[0] == m_currentTargetedPfs->getTarget()) - { - score += maxBonusScore * unloadingPercent; - } - else - { - score += maxBonusScore * unloadingPercent * halfPercent; - } - const double maxBonus = m_maxScore * halfPercent - 1; - if (score > maxBonus) - { - score = maxBonus; - } - scoreWaitGeneric(data, unitData, baseData); - data.m_score += score; - } - else - { - data.m_score = m_minActionScore - 1.0; - } -} - -qint32 HeavyAi::getNumberOfTargetsOnIsland(const std::vector & ignoreList) -{ - qint32 ret = 0; - - if (m_pMap != nullptr) - { - const auto & targets = m_currentTargetedPfs->getTargets(); - for (const auto & point : targets) - { - if (!GlobalUtils::contains(ignoreList, QPoint(point.x(), point.y()))) - { - Unit* pUnit = m_pMap->getTerrain(point.x(), point.y())->getUnit(); - if (pUnit == nullptr || - m_pPlayer->isEnemyUnit(pUnit)) - { - ++ret; - } - } - } - } - return ret; -} - -void HeavyAi::scoreMoveToTargets() -{ - AI_CONSOLE_PRINT("HeavyAi scoring wait actions if needed", GameConsole::eDEBUG); - for (auto & unit : m_ownUnits) - { - if (!unit.pUnit->getHasMoved() && - unit.m_action.get() == nullptr) - { - QStringList & actions = unit.actions; - prepareWaitPfs(unit, actions); - std::vector scoreInfos; - float bestScore = m_minActionScore - 1.0; - auto moveTargets = unit.pUnitPfs->getAllNodePointsFast(unit.movementPoints + 1); - for (const auto & action : qAsConst(actions)) - { - FunctionType type = FunctionType::CPlusPlus; - qint32 index = -1; - getFunctionType(action, type, index); - if (type != FunctionType::Undefined) - { - mutateActionForFields(unit, moveTargets, action, type, index, - bestScore, scoreInfos); - } - } - if (scoreInfos.size() > 0) - { - qint32 item = GlobalUtils::randInt(0, scoreInfos.size() - 1); - unit.m_score = scoreInfos[item].m_score; - unit.m_action = scoreInfos[item].m_gameAction; - } - else - { - unit.m_score = 0.0f; - unit.m_action.reset(); - } - m_currentTargetedPfs.reset(); - } - } -} - -void HeavyAi::prepareWaitPfs(MoveUnitData & unitData, const QStringList & actions) -{ - if (m_currentTargetedPfs.get() == nullptr) - { - m_currentTargetedPfsTargets.clear(); - getMoveTargets(unitData, actions, m_currentTargetedPfsTargets); - m_currentTargetedPfs = MemoryManagement::create(m_pMap, unitData.pUnit.get(), m_currentTargetedPfsTargets, &m_MoveCostMap); - m_currentTargetedPfs->explore(); - } -} - -void HeavyAi::getMoveTargets(MoveUnitData & unit, const QStringList & actions, std::vector & targets) -{ - if (m_pMap != nullptr) - { - qint32 mapWidth = m_pMap->getMapWidth(); - qint32 mapHeight = m_pMap->getMapHeight(); - qint32 unitIslandIdx = getIslandIndex(unit.pUnit.get()); - qint32 minFirerange = unit.pUnit->getMinRange(unit.pUnit->getPosition()); - qint32 maxFirerange = unit.pUnit->getMaxRange(unit.pUnit->getPosition()); - spQmlVectorPoint pTargetFields = GlobalUtils::getSpCircle(minFirerange, maxFirerange); - - std::vector input; - // todo create input vector - m_neuralNetworks[NeuralNetworks::WaitDistanceMultiplier]->predict(input); - - Unit* pUnit = unit.pUnit.get(); - if (isUsingUnit(pUnit)) - { - bool canRefill = false; - if (actions.contains(ACTION_SUPPORTALL_RATION) || - actions.contains(ACTION_SUPPORTALL_RATION) || - actions.contains(ACTION_SUPPORTSINGLE_REPAIR) || - actions.contains(ACTION_SUPPORTSINGLE_FREEREPAIR) || - actions.contains(ACTION_SUPPORTSINGLE_SUPPLY)) - { - canRefill = true; - } - bool canSupport = false; - for (const auto & action : actions) - { - if (action.startsWith(ACTION_SUPPORTSINGLE) || - action.startsWith(ACTION_SUPPORTALL)) - { - canSupport = true; - break; - } - } - addLoadingTargets(pUnit, unit.actions, targets); - addUnloadTargets(pUnit, targets); - addCustomTargets(pUnit); - for (qint32 x = 0; x < mapWidth; ++x) - { - for (qint32 y = 0; y < mapHeight; ++y) - { - if (unit.pUnit->canMoveOver(x, y) && - onSameIsland(unitIslandIdx, unit.pUnit->Unit::getX(), unit.pUnit->Unit::getY(), x, y)) - { - Terrain* pTerrain = m_pMap->getTerrain(x, y); - addCaptureTargets(actions, pTerrain, targets); - addAttackTargets(pUnit, pTerrain, pTargetFields.get(), targets); - addCaptureTransporterTargets(pUnit, actions, pTerrain, targets); - addTransporterTargets(pUnit, pTerrain, targets); - if (canRefill) - { - addRefillTargets(x, y, targets); - } - if (canSupport) - { - addSupportTargets(x, y, targets); - } - // currently using predefined ai - // addFlareTargets(); - // addOoziumTargets(); - // addBlackbombTargets(); - } - } - } - } - else - { - addRepairTargets(unit.pUnit.get(), targets); - } - } -} - -void HeavyAi::addCustomTargets(Unit* pUnit) -{ - Interpreter* pInterpreter = Interpreter::getInstance(); - const char* const CB_NAME = "addCustomTargets"; - if (pInterpreter->exists(m_aiName, CB_NAME)) - { - QJSValueList args({m_jsThis, - JsThis::getJsThis(pUnit)}); - pInterpreter->doFunction(m_aiName, CB_NAME, args); - } -} - -void HeavyAi::addRepairTargets(Unit* pUnit, std::vector& targets) -{ - bool requiresRefuel = needsRefuel(pUnit); - if (requiresRefuel || pUnit->getHp() < m_minUnitHealth) - { - appendRepairTargets(pUnit, m_pBuildings, targets); - if (requiresRefuel) - { - appendTransporterTargets(pUnit, m_pUnits, targets); - } - } -} - -void HeavyAi::addCustomTarget(qint32 x, qint32 y, qint32 priority) -{ - if (priority >= 1) - { - if (m_pMap->onMap(x, y)) - { - m_currentTargetedPfsTargets.push_back(QVector3D(x, y, priority)); - } - else - { - CONSOLE_PRINT("Position in addCustomTarget x: " + QString::number(x) + " y: " + QString::number(y) + " isn't on the map", GameConsole::eERROR); - } - } - else - { - CONSOLE_PRINT("Priority: " + QString::number(priority) + " in addCustomTarget is not greater than 1", GameConsole::eERROR); - } -} - -void HeavyAi::addCaptureTargets(const QStringList & actions, - Terrain* pTerrain, std::vector& targets) -{ - if (actions.contains(ACTION_CAPTURE) || - actions.contains(ACTION_MISSILE)) - { - qint32 x = pTerrain->Terrain::getX(); - qint32 y = pTerrain->Terrain::getY(); - Building* pBuilding = pTerrain->getBuilding(); - if (pBuilding != nullptr&& - pBuilding->getTerrain()->getUnit() == nullptr && - pBuilding->isEnemyBuilding(m_pPlayer)) - { - qint32 captureDistanceModifier = 0; - if (pBuilding->isCaptureBuilding()) - { - captureDistanceModifier = m_neuralNetworks[NeuralNetworks::WaitDistanceMultiplier]->output(WaitTargetTypes_Capture); - } - else if (pBuilding->isMissile()) - { - captureDistanceModifier = m_neuralNetworks[NeuralNetworks::WaitDistanceMultiplier]->output(WaitTargetTypes_Silo); - } - else - { - return; - } - if (captureDistanceModifier < 1) - { - captureDistanceModifier = 1; - } - if (GlobalUtils::contains(m_planedCaptureTargets, QPoint(x, y))) - { - captureDistanceModifier *= m_usedCapturePointIncrease; - } - QVector3D possibleTarget(x, y, captureDistanceModifier); - if (!GlobalUtils::contains(targets, possibleTarget)) - { - targets.push_back(possibleTarget); - m_possibleCaptureTargets.push_back(QPoint(x, y)); - } - } - } -} - -void HeavyAi::addAttackTargets(Unit* pUnit, Terrain* pTerrain, QmlVectorPoint* pTargetFields, std::vector & targets) -{ - - if (m_pMap != nullptr) - { - qint32 x = pTerrain->Terrain::getX(); - qint32 y = pTerrain->Terrain::getY(); - for (auto & target : pTargetFields->getVector()) - { - qint32 targetX = target.x() + x; - qint32 targetY = target.y() + y; - if (m_pMap->onMap(targetX, targetY)) - { - Terrain* pTargetTerrain = m_pMap->getTerrain(targetX, targetY); - Unit* pEnemy = pTargetTerrain->getUnit(); - if (pEnemy != nullptr && - pUnit->isAttackable(pEnemy, true)) - { - Unit* pTargetUnit = pTerrain->getUnit(); - float alliedmultiplier = 1.0f; - if (pTargetUnit != nullptr && - pTargetUnit->getOwner()->checkAlliance(m_pPlayer) == GameEnums::Alliance_Friend) - { - alliedmultiplier = m_alliedDistanceModifier; - } - qint32 stealthMalus = 0; - bool terrainHide = false; - if (pEnemy->isStatusStealthedAndInvisible(m_pPlayer, terrainHide)) - { - stealthMalus = m_stealthDistanceMultiplier; - if (terrainHide) - { - stealthMalus /= 2; - } - } - qint32 attackDistanceModifier = m_neuralNetworks[NeuralNetworks::WaitDistanceMultiplier]->output(WaitTargetTypes_Enemy); - if (attackDistanceModifier < 1) - { - attackDistanceModifier = 1; - } - QVector3D possibleTarget(x, y, (stealthMalus + attackDistanceModifier) * alliedmultiplier); - if (!GlobalUtils::contains(targets, possibleTarget)) - { - targets.push_back(possibleTarget); - } - } - else if (m_enableNeutralTerrainAttack) - { - Building* pBuilding = pTargetTerrain->getBuilding(); - if ((pBuilding != nullptr && - pBuilding->getHp() > 0 && - m_pPlayer->isEnemy(pBuilding->getOwner()) && - pUnit->isEnvironmentAttackable(pBuilding->getBuildingID())) || - (isAttackOnTerrainAllowed(pTerrain, pUnit->getEnvironmentDamage(pTerrain->getID())))) - { - Unit* pTargetUnit = pTerrain->getUnit(); - float alliedmultiplier = 1.0f; - if (pTargetUnit != nullptr && - pTargetUnit->getOwner()->checkAlliance(m_pPlayer) == GameEnums::Alliance_Friend) - { - alliedmultiplier = m_alliedDistanceModifier; - } - qint32 attackDistanceModifier = m_neuralNetworks[NeuralNetworks::WaitDistanceMultiplier]->output(WaitTargetTypes_Enemy); - QVector3D possibleTarget(x, y, (attackDistanceModifier) * alliedmultiplier); - if (!GlobalUtils::contains(targets, possibleTarget)) - { - targets.push_back(possibleTarget); - } - } - } - } - } - } -} - -void HeavyAi::addCaptureTransporterTargets(Unit* pUnit, const QStringList & actions, - Terrain* pTerrain, std::vector& targets) -{ - if (actions.contains(ACTION_CAPTURE) || - actions.contains(ACTION_MISSILE)) - { - qint32 x = pTerrain->Terrain::getX(); - qint32 y = pTerrain->Terrain::getY(); - Unit* pTransporter = pTerrain->getUnit(); - if (pTransporter != nullptr && - pTransporter->getOwner() == m_pPlayer && - pTransporter->canTransportUnit(pUnit)) - { - qint32 distanceModifier = m_neuralNetworks[NeuralNetworks::WaitDistanceMultiplier]->output(WaitTargetTypes_CaptureLoad); - if (distanceModifier < 1) - { - distanceModifier = 1; - } - QVector3D possibleTarget(x, y, distanceModifier); - if (!GlobalUtils::contains(targets, possibleTarget)) - { - targets.push_back(possibleTarget); - } - } - } -} - -void HeavyAi::addTransporterTargets(Unit* pUnit, Terrain* pTerrain, std::vector& targets) -{ - qint32 x = pTerrain->Terrain::getX(); - qint32 y = pTerrain->Terrain::getY(); - Unit* pTransporter = pTerrain->getUnit(); - if (pTransporter != nullptr && - pTransporter->getOwner() == m_pPlayer && - pTransporter->canTransportUnit(pUnit)) - { - qint32 distanceModifier = m_neuralNetworks[NeuralNetworks::WaitDistanceMultiplier]->output(WaitTargetTypes_Transport); - if (distanceModifier < 1) - { - distanceModifier = 1; - } - QVector3D possibleTarget(x, y, distanceModifier); - if (!GlobalUtils::contains(targets, possibleTarget)) - { - targets.push_back(possibleTarget); - } - } -} - -void HeavyAi::addUnloadTargets(Unit* pUnit, std::vector& targets) -{ - if (pUnit->getLoadedUnitCount() > 0) - { - // can one of our units can capture buildings? - bool captureFound = false; - bool attackFound = false; - qint32 distanceModifier = m_neuralNetworks[NeuralNetworks::WaitDistanceMultiplier]->output(WaitTargetTypes_Unload); - if (distanceModifier < 1) - { - distanceModifier = 1; - } - - for (auto & pLoaded : pUnit->getLoadedUnits()) - { - QStringList actions = pLoaded->getActionList(); - if (!captureFound && actions.contains(ACTION_CAPTURE)) - { - // todo - // appendUnloadTargetsForCapturing(pUnit, pUnits, m_pEnemyBuildings, targets, distanceModifier); - captureFound = true; - } - if (!attackFound && actions.contains(ACTION_FIRE)) - { - appendUnloadTargetsForAttacking(pUnit, m_pEnemyUnits, targets, 1, distanceModifier); - attackFound = true; - } - if (attackFound && captureFound) - { - break; - } - } - // if not find closest unloading field - if (targets.size() == 0) - { - appendUnloadTargetsForAttacking(pUnit, m_pEnemyUnits, targets, 3, distanceModifier); - } - if (targets.size() == 0) - { - appendNearestUnloadTargets(pUnit, m_pEnemyUnits, m_pEnemyBuildings, targets, distanceModifier); - } - } -} - -void HeavyAi::addLoadingTargets(Unit* pUnit, const QStringList & actions, std::vector& targets) -{ - if (pUnit->getLoadingPlace() > 0 && - pUnit->getLoadedUnitCount() == 0) - { - qint32 distanceModifier = m_neuralNetworks[NeuralNetworks::WaitDistanceMultiplier]->output(WaitTargetTypes_Load); - if (distanceModifier < 1) - { - distanceModifier = 1; - } - // we need to move to a loading place - std::vector targets; - appendCaptureTargets(actions, pUnit, m_pEnemyBuildings, targets, distanceModifier); - appendLoadingTargets(pUnit, m_pUnits, m_pEnemyUnits, m_pEnemyBuildings, false, false, targets, false, distanceModifier); - if (targets.size() == 0) - { - appendLoadingTargets(pUnit, m_pUnits, m_pEnemyUnits, m_pEnemyBuildings, true, false, targets, false, distanceModifier); - } - } -} - -void HeavyAi::addRefillTargets(qint32 posX, qint32 posY, std::vector & targets) -{ - qint32 distanceModifier = m_neuralNetworks[NeuralNetworks::WaitDistanceMultiplier]->output(WaitTargetTypes_Refill); - if (distanceModifier < 1) - { - distanceModifier = 1; - } - QVector3D possibleTarget(posX, posY, distanceModifier); - if (!GlobalUtils::contains(targets, possibleTarget)) - { - bool found = false; - spQmlVectorPoint circle = GlobalUtils::getSpCircle(1, 1); - for (const auto circlePos : circle->getVector()) - { - qint32 x = posX + circlePos.x(); - qint32 y = posY + circlePos.y(); - if (m_pMap->onMap(x, y)) - { - Unit* pSupplyUnit = m_pMap->getTerrain(x, y)->getUnit(); - if (pSupplyUnit != nullptr && - pSupplyUnit->getOwner() == m_pPlayer && - needsRefuel(pSupplyUnit)) - { - found = true; - break; - } - } - } - if (found) - { - targets.push_back(possibleTarget); - } - } -} - -void HeavyAi::addSupportTargets(qint32 posX, qint32 posY, std::vector & targets) -{ - qint32 distanceModifier = m_neuralNetworks[NeuralNetworks::WaitDistanceMultiplier]->output(WaitTargetTypes_Support); - if (distanceModifier < 1) - { - distanceModifier = 1; - } - QVector3D possibleTarget(posX, posY, distanceModifier); - if (!GlobalUtils::contains(targets, possibleTarget)) - { - bool found = false; - spQmlVectorPoint circle = GlobalUtils::getSpCircle(1, 1); - for (const auto circlePos : circle->getVector()) - { - qint32 x = posX + circlePos.x(); - qint32 y = posY + circlePos.y(); - if (m_pMap->onMap(x, y)) - { - Unit* pSupportUnit = m_pMap->getTerrain(x, y)->getUnit(); - if (pSupportUnit != nullptr && - pSupportUnit->getOwner() == m_pPlayer && - !needsRefuel(pSupportUnit)) - { - found = true; - break; - } - } - } - if (found) - { - targets.push_back(possibleTarget); - } - } -} - -void HeavyAi::scoreWait(ScoreData & data, MoveUnitData & unitData, std::vector & baseData) -{ - if (unitData.pUnit->getLoadedUnitCount() > 0 && - m_aiStep < AISteps::moveTransporters) - { - // don't wait with transporter units till we score unloading - data.m_score = m_minActionScore - 1.0; - } - else - { - scoreWaitGeneric(data, unitData, baseData); - } -} - -void HeavyAi::scoreWaitGeneric(ScoreData & data, MoveUnitData & unitData, std::vector & baseData) -{ - float score = m_minActionScore - 1; - auto targetPath = m_currentTargetedPfs->getTargetPathFast(); - QPoint target = data.m_gameAction->getActionTarget(); - if (GlobalUtils::contains(m_possibleCaptureTargets, target)) - { - data.m_captureTarget = target; - } - float movePathSize = data.m_gameAction->getMovePathLength() - 1; - if (movePathSize > 0) - { - double distance = getDistanceToMovepath(targetPath, target); - score += m_maxScore * 0.3 / distance; - score += m_maxScore * 0.3 * movePathSize / static_cast(unitData.movementPoints); - } - // todo calculate - data.m_score = score; -} - -qint32 HeavyAi::getDistanceToMovepath(const std::vector & targetPath, const QPoint & target) const -{ - qint32 distance = std::numeric_limits::max(); - if (!GlobalUtils::contains(targetPath, target)) - { - for (const auto & path : qAsConst(targetPath)) - { - qint32 newDistance = GlobalUtils::getDistance(path, target) + 1; - if (newDistance < distance) - { - distance = newDistance; - } - } - } - else - { - distance = 1; - } - return distance; -} - -bool HeavyAi::isPrimaryEnemy(Player* pPlayer) const -{ - return pPlayer == m_pPrimaryEnemy; -} - -bool HeavyAi::isPrimaryEnemy(Unit* pUnit) const -{ - return isPrimaryEnemy(pUnit->getOwner()); -} - -bool HeavyAi::isPrimaryEnemy(Building* pBuilding) const -{ - return isPrimaryEnemy(pBuilding->getOwner()); -} - -bool HeavyAi::isUsingUnit(Unit* pUnit) const -{ - return !needsRefuel(pUnit) && pUnit->getHp() >= m_minUnitHealth; -} diff --git a/ai/heavyai.h b/ai/heavyai.h deleted file mode 100644 index 572da01e0..000000000 --- a/ai/heavyai.h +++ /dev/null @@ -1,678 +0,0 @@ -#pragma once -#include -#include - -#include "ai/coreai.h" -#include "ai/influencefrontmap.h" -#include "ai/targetedunitpathfindingsystem.h" -#include "ai/neuralnetwork/neural/neuralnetwork.h" - -class GameMap; -class HeavyAi; -using spHeavyAi = std::shared_ptr; - -class HeavyAi final : public CoreAI -{ - Q_OBJECT - /** - * @brief The BuildingEntry enum - */ - enum BuildingEntry - { - DirectUnitRatio , - IndirectUnitRatio , - InfantryUnitRatio , - TransportUnitRatio , - TotalBuildingRatio , - SupplyUnitRatio , - SupplyRequiredRatio , - EnemyRatio , - ProductionUsage , - DayProgression , - LocalUnitData , - BasicAttackRange = LocalUnitData, - CaptureUnit , - Movementpoints , - MapMovementpoints, - FondsUsage , - FireRange , - Flying , - LoadingPotential , - OwnInfluence, - HighestEnemyInfluence, - DealingFundsDamage , - DealingHpDamage , - ReceivingFundsDamge , - ReceivingHpDamage , - CapturePotential , - CanAttackImmuneUnitRatio, - UnitsToTransportRatio, - RequiredUnitsToTransportRatio, - MovementPotential, - VisionPotential, - MaxUnitValue, - BuildingEntryMaxSize, - }; - - enum BasicFieldInfo - { - OwnInfluenceValue, // 0 - EnemyInfluenceValue, // 1 - // the higher the more units haven't moved yet - MoveTurnProgress, // 2 - // amount of adjustant tiles blocked by an allied unit - WallCount, // 3 - // if the unit is potentially stealthed at this tile - VisionHide, // 4 - // how much movement points getting here will be consumed - UsedMovement, // 5 - EnemyThread, // 6 - OwnProtection, // 7 - UnitHealth, // 8 - VisionRange, // 9 - TerrainDefense, // 10 - ProductionOwner, // 11 - FrontTile, // 12 - IsCoUnit, // 13 - BasicFieldInfoMaxSize, - }; - - enum AttackInfo - { - AttackInfoStart = BasicFieldInfoMaxSize, - AttackDealingHpDamage = AttackInfoStart, - AttackReceavingHpDamage, - AttackDealingAbsolutDamage, - AttackReceicingAbsolutDamage, - AttackDealingFundsDamage, - AttackReceicingFundsDamage, - AttackRemainingCaptureDays, - HqThread, - AttackInfoMaxSize, - }; - - enum CaptureInfo - { - CaptureInfoStart = BasicFieldInfoMaxSize, - CaptureInfoIsHq = CaptureInfoStart, - CaptureInfoIsComTower, - CaptureInfoProductionPotential, - CaptureInfoRemainingDays, - CaptureInfoCaptureOptions, - CaptureInfoUniqueCaptureBuilding, - CaptureInfoMaxSize, - }; - - enum UnloadingInfo - { - UnloadingInfoStart = BasicFieldInfoMaxSize, - UnloadingInfoUnloadingPercent = UnloadingInfoStart, - UnloadingInfoMovementPercent, - UnloadingInfoMaxSize, - }; - - enum WaitTargetTypes - { - WaitTargetTypes_Capture, - WaitTargetTypes_Silo, - WaitTargetTypes_Enemy, - WaitTargetTypes_Terrain, - WaitTargetTypes_CaptureLoad, - WaitTargetTypes_Transport, - WaitTargetTypes_Load, - WaitTargetTypes_Unload, - WaitTargetTypes_Refill, - WaitTargetTypes_Support, - WaitTargetTypesMaxSize, - }; - - // flare / explode - - // place watermine / disable mine - // support repair and ration actions - // wait / stealth / unstealth - // build waterplane - - enum NeuralNetworks - { - Production, - ActionFire, - ActionCapture, - ActionUnloading, - WaitDistanceMultiplier, - NeuralNetworksMax, - }; - - struct ScoreData - { - float m_score{0}; - spGameAction m_gameAction; - QPoint m_captureTarget{-1, -1}; - }; - -public: - enum class FunctionType - { - JavaScript, - CPlusPlus, - Undefined, - }; - - struct UnitBuildData - { - bool enabled{true}; - QString unitId; - qint32 cost{0}; - std::vector unitBuildingDataInput = std::vector(static_cast(BuildingEntryMaxSize)); - }; - struct BuildingData - { - Building* m_pBuilding; - spGameAction m_action; - float m_score{0}; - qint32 m_selectedData{-1}; - QVector buildingDataInput; - }; - - explicit HeavyAi(GameMap* pMap, QString type, GameEnums::AiTypes aiType); - virtual ~HeavyAi() = default; - - void loadNeuralNetwork(QString netName, spNeuralNetwork & network, qint32 inputVectorSize, qint32 netDepth, bool randomize, qint32 outputSize = 1); - - /*******************************************************************/ - // processing section for js scripts use the following functions - /*******************************************************************/ - /** - * @brief getInfluenceFrontMap - * @return - */ - Q_INVOKABLE InfluenceFrontMap* getInfluenceFrontMap(); - /** - * @brief addCustomTarget - * @param x - * @param y - * @param priority - */ - Q_INVOKABLE void addCustomTarget(qint32 x, qint32 y, qint32 priority); - /*******************************************************************/ - // training section - /*******************************************************************/ - /** - * @brief getMaxNeuralNetworks amount of neural networks owned by the heavy ai - * @return - */ - Q_INVOKABLE qint32 getMaxNeuralNetworks() - { - return static_cast(NeuralNetworksMax); - } - /** - * @brief saveNeuralNetwork saves the network under the given name - * @param network - */ - Q_INVOKABLE void saveNeuralNetwork(qint32 network); - /** - * @brief getNeuralNetworkName - * @param network - * @return - */ - Q_INVOKABLE QString getNeuralNetworkName(qint32 network); - /** - * @brief mutateNeuralNetwork - * @param network - * @param mutationChance - * @return - */ - Q_INVOKABLE void mutateNeuralNetwork(qint32 network, double mutationChance, double mutationRate = 0.1f); - /** - * @brief getMinActionScore - * @return - */ - Q_INVOKABLE double getMinActionScore() const; - /** - * @brief setMinActionScore - * @param newMinActionScore - */ - void setMinActionScore(double newMinActionScore); - /** - * @brief setDisabled - * @param network - * @param disabled - */ - void setDisabled(qint32 network, bool disabled); - /** - * @brief loadNeuralNetworks - */ - Q_INVOKABLE void loadNeuralNetworks(GameEnums::AiTypes aiType); - /** - * @brief combineAi - * @param aisToUse - */ - Q_INVOKABLE void combineAi(QStringList aisToUse); - /*******************************************************************/ - // debugging section - /*******************************************************************/ - /** - * @brief toggleAiPause for debugging - */ - Q_INVOKABLE void toggleAiPause(); - /** - * @brief showFrontMap for debugging visualization - */ - Q_INVOKABLE void showFrontMap(); - /** - * @brief showFrontLines for debugging visualization - */ - Q_INVOKABLE void showFrontLines(); - /** - * @brief hideFrontMap for debugging visualization - */ - Q_INVOKABLE void hideFrontMap(); - /** - * @brief showUnitPfs - * @param enemy - * @param index - */ - Q_INVOKABLE void showUnitPfs(bool enemy, qint32 index); - /** - * @brief showIslandMap - * @param unitId - */ - Q_INVOKABLE void showIslandMap(QString unitId); - /** - * @brief showIslandMap - * @param unitId - */ - Q_INVOKABLE void hideIslandMap(QString unitId); -public slots: - /** - * @brief process - */ - virtual void process() override; - -protected: - /** - * @brief scoreActions - * @param unit - */ - void scoreActions(MoveUnitData & unit); - /** - * @brief prepareWaitPfs - * @param unitData - * @param actions - */ - void prepareWaitPfs(MoveUnitData & unitData, const QStringList & actions); -private: - void setupTurn(const spQmlVectorBuilding & buildings); - void endTurn(); - void createIslandMaps(); - void initUnits(spQmlVectorUnit & pUnits, std::vector & units, bool enemyUnits); - void addNewUnitToUnitData(std::vector & units, Unit* pUnit, bool enemyUnits); - void updateUnits(); - void updateUnits(std::vector & units, spQmlVectorUnit & pUnits, bool enemyUnits); - void updateCaptureBuildings(MoveUnitData & unitData); - void findHqThreads(const spQmlVectorBuilding & buildings); - bool isCaptureTransporterOrCanCapture(Unit* pUnit); - void mutateActionForFields(MoveUnitData & unit, const std::vector & moveTargets, - QString action, FunctionType type, qint32 index, - float & bestScore, std::vector & scoreInfos); - bool mutateAction(ScoreData & data, MoveUnitData & MoveUnitData, std::vector & baseData, FunctionType type, qint32 functionIndex, - qint32 & step, std::vector & stepPosition, std::vector & maxStepOtions); - qint32 getNextMutateStep(qint32 step, std::vector & stepPosition, std::vector & maxStepOtions, qint32 maxOptions); - /** - * @brief scoreWait - * @param unit - */ - void scoreMoveToTargets(); - /** - * @brief performAction - */ - bool selectActionToPerform(); - /** - * @brief scoreCapture - * @param action - * @return - */ - void scoreCapture(ScoreData & data, MoveUnitData & unitData, std::vector & baseData); - /** - * @brief scoreFire - * @param action - * @return - */ - void scoreFire(ScoreData & data, MoveUnitData & unitData, std::vector & baseData); - /** - * @brief scoreJoin - * @param action - * @param unitData - * @param baseData - * @return - */ - void scoreJoin(ScoreData & data, MoveUnitData & unitData, std::vector & baseData); - /** - * @brief scoreMissile - * @param action - * @param unitData - * @param baseData - * @return - */ - void scoreMissile(ScoreData & data, MoveUnitData & unitData, std::vector & baseData); - /** - * @brief scoreLoad - * @param action - * @param unitData - * @param baseData - * @return - */ - void scoreLoad(ScoreData & data, MoveUnitData & unitData, std::vector & baseData); - /** - * @brief scoreUnload - * @param data - * @param unitData - * @param baseData - */ - void scoreUnload(ScoreData & data, MoveUnitData & unitData, std::vector & baseData); - /** - * @brief getMoveTargets - * @param unit - * @param targets - */ - void getMoveTargets(MoveUnitData & unit, const QStringList & actions, std::vector & targets); - /** - * @brief scoreWait - * @param action - * @return - */ - void scoreWait(ScoreData & data, MoveUnitData & unitData, std::vector & baseData); - /** - * @brief scoreWaitGeneric - * @param data - * @param unitData - * @param baseData - */ - void scoreWaitGeneric(ScoreData & data, MoveUnitData & unitData, std::vector & baseData); - /** - * @brief addCaptureTargets - * @param pUnit - * @param actions - * @param pEnemyBuildings - * @param targets - */ - void addCaptureTargets(const QStringList & actions, - Terrain* pTerrain, std::vector& targets); - /** - * @brief addAttackTargets - * @param pUnit - * @param pTargetFields - * @param targets - */ - void addAttackTargets(Unit* pUnit, Terrain* pTerrain, QmlVectorPoint* pTargetFields, std::vector & targets); - /** - * @brief addTransporterTargets - */ - void addTransporterTargets(Unit* pUnit, Terrain* pTerrain, std::vector& targets); - /** - * @brief addCaptureTransporterTargets - */ - void addCaptureTransporterTargets(Unit* pUnit, const QStringList & actions, - Terrain* pTerrain, std::vector& targets); - /** - * @brief addUnloadTargets - * @param pUnit - * @param pEnemyUnits - * @param targets - */ - void addUnloadTargets(Unit* pUnit, std::vector& targets); - /** - * @brief addLoadingTargets - * @param pUnit - * @param targets - */ - void addLoadingTargets(Unit* pUnit, const QStringList & actions, std::vector& targets); - /** - * @brief addCustomTargets - * @param pUnit - */ - void addCustomTargets(Unit* pUnit); - /** - * @brief addRepairTargets - * @param pUnit - * @param targets - */ - void addRepairTargets(Unit* pUnit, std::vector& targets); - /** - * @brief addRefillTargets - * @param posX - * @param posY - * @param targets - */ - void addRefillTargets(qint32 posX, qint32 posY, std::vector & targets); - /** - * @brief addSupportTargets - * @param posX - * @param posY - * @param targets - */ - void addSupportTargets(qint32 posX, qint32 posY, std::vector & targets); - /** - * @brief getBasicFieldInputVector - * @param action - * @param data - */ - void getBasicFieldInputVector(spGameAction & action, std::vector & data); - /** - * @brief getBasicFieldInputVector - * @param pMoveUnit - * @param moveTarget - * @param moveCosts - * @param movepoints - * @param data - */ - void getBasicFieldInputVector(Unit* pMoveUnit, QPoint & moveTarget, double moveCosts, double movepoints, std::vector & data); - /** - * @brief getFunctionType - * @param action - * @param type - * @param index - */ - void getFunctionType(const QString & action, FunctionType & type, qint32 & index); - /** - * @brief getProductionInputVector - * @param pBuilding - * @param pUnit - */ - void getProductionInputVector(Building* pBuilding, Unit* pUnit, UnitBuildData & data, const std::vector & immuneUnits, qint32 movementPoints); - /** - * @brief buildUnits - * @return - */ - bool buildUnits(spQmlVectorBuilding & pBuildings, spQmlVectorUnit & pUnits, - spQmlVectorUnit & pEnemyUnits, spQmlVectorBuilding & pEnemyBuildings); - /** - * @brief scoreUnitBuilding - */ - void scoreUnitBuildings(spQmlVectorBuilding & pBuildings, spQmlVectorUnit & pUnits, - spQmlVectorUnit & pEnemyUnits, spQmlVectorBuilding & pEnemyBuildings); - /** - * @brief scoreBuildingProductionData - */ - void scoreBuildingProductionData(BuildingData & building); - /** - * @brief getGlobalBuildInfo - * @param pBuildings - * @param pUnits - * @param pEnemyUnits - * @param pEnemyBuildings - * @return - */ - std::vector getGlobalBuildInfo(spQmlVectorBuilding & pBuildings, spQmlVectorUnit & pUnits, - spQmlVectorUnit & pEnemyUnits, spQmlVectorBuilding & pEnemyBuildings, - std::vector> & transportTargets); - /** - * @brief createUnitBuildData - * @param building - */ - void createUnitBuildData(BuildingData & building, std::vector & data, qint32 funds, const std::vector & immuneUnits, - const std::vector> & transportTargets, spQmlVectorBuilding & pEnemyBuildings); - /** - * @brief UpdateUnitBuildData - * @param unitData - * @param data - * @param funds - */ - void updateUnitBuildData(BuildingData & building, std::vector & data, qint32 funds); - /** - * @brief updateUnitBuildData - * @param unitData - * @param data - * @param funds - */ - void updateUnitBuildData(UnitBuildData &unitData, std::vector &data, qint32 funds); - /** - * @brief calculateUnitProductionDamage - * @param pBuilding - * @param pUnit - * @param data - */ - void calculateUnitProductionDamage(Building* pBuilding, Unit* pUnit, qint32 movementPoints, QPoint position, UnitBuildData & data, const std::vector & immuneUnits); - /** - * @brief getBaseDamage - * @param pAttacker - * @param pDefender - */ - float getBaseDamage(Unit* pAttacker, Unit* pDefender); - /** - * @brief getProductionScoreMultiplier - * @param position - * @param target - * @param movementPoints - * @return - */ - float getProductionScoreMultiplier(QPoint position, QPoint target, qint32 movementPoints); - /** - * @brief getImmuneUnits - * @param pUnits - * @param pEnemyUnits - * @param immuneUnits - */ - void getImmuneUnits(spQmlVectorUnit & pUnits, spQmlVectorUnit & pEnemyUnits, std::vector & immuneUnits); - /** - * @brief getTransportInputVector - * @param building - * @param transportTargets - * @param unitData - * @param pEnemyBuildings - * @param data - */ - void getTransportInputVector(Building* pBuilding, Unit* pUnit, const std::vector> & transportTargets, - spQmlVectorBuilding & pEnemyBuildings, qint32 movementPoints, UnitBuildData & data); - /** - * @brief isPrimaryEnemy - * @param pPlayer - * @return - */ - bool isPrimaryEnemy(Player* pPlayer) const; - /** - * @brief isPrimaryEnemy - * @param pUnit - * @return - */ - bool isPrimaryEnemy(Unit* pUnit) const; - /** - * @brief isPrimaryEnemy - * @param pBuilding - * @return - */ - bool isPrimaryEnemy(Building* pBuilding) const; - /** - * @brief isScoringAllowed - * @param action - * @param actions - * @return - */ - bool isScoringAllowed(const QString & action, const QStringList & actions); - /** - * @brief getNumberOfTargetsOnIsland - * @param ignoreList - * @return - */ - qint32 getNumberOfTargetsOnIsland(const std::vector & ignoreList); - /** - * @brief getDistanceToMovepath - * @param targetPath - * @param target - */ - qint32 getDistanceToMovepath(const std::vector & targetPath, const QPoint & target) const; - /** - * @brief isUsingUnit - * @param pUnit - * @return - */ - bool isUsingUnit(Unit* pUnit) const; -private: - // function for scoring a function - using scoreFunction = std::function & baseData)>; - struct ScoreInfo - { - QString m_actionId; - scoreFunction callback; - }; - const std::vector m_scoreInfos; - std::vector m_enemyUnits; - std::vector m_ownUnits; - std::vector m_updatePoints; - std::vector m_BuildingData; - std::vector m_planedCaptureTargets; - InfluenceFrontMap m_InfluenceFrontMap; - spQmlVectorUnit m_pUnits = spQmlVectorUnit(); - spQmlVectorBuilding m_pBuildings = spQmlVectorBuilding(); - spQmlVectorUnit m_pEnemyUnits = spQmlVectorUnit(); - spQmlVectorBuilding m_pEnemyBuildings = spQmlVectorBuilding(); - Player* m_pPrimaryEnemy{nullptr}; - QTimer m_timer; - bool m_pause{false}; - QStringList m_secondActions - { - ACTION_WAIT, - ACTION_LOAD, - ACTION_STEALTH, - ACTION_UNSTEALTH, - }; - QStringList m_thirdActions - { - ACTION_UNLOAD, - }; - spTargetedUnitPathFindingSystem m_currentTargetedPfs; - std::vector m_possibleCaptureTargets; - std::vector m_currentTargetedPfsTargets; - - double m_minActionScore{0.2}; - double m_actionScoreVariant{0.05}; - double m_stealthDistanceMultiplier{2.0}; - double m_alliedDistanceModifier{5.0}; - double m_maxMovementpoints{15.0}; - double m_maxFirerange{10}; - double m_maxProductionTurnRange{4}; - double m_maxVision{10}; - double m_primaryEnemyMultiplier{1.2}; - double m_maxLoadingPlace{4}; - double m_notAttackableDamage{20.0}; - double m_ownUnitProtection{5}; - double m_enemyUnitThread{5}; - double m_maxUnitValue{40000.0}; - double m_maxScore{10.0f}; - double m_maxTerrainDefense{15.0}; - double m_maxCapturePoints = 20; - double m_earlyGameDays{6.0f}; - double m_usedCapturePointIncrease{1.5f}; - double m_minUnitHealth{3}; - - // storable stuff - QList m_neuralNetworks{static_cast(NeuralNetworksMax)}; - - // static constants - static const qint32 minSiloDamage; - static const char* const NeuralNetworkNames[]; - static const char* const NeuralNetworkFileEnding; - static const char* const NeuralNetworkPath; -}; - -Q_DECLARE_INTERFACE(HeavyAi, "HeavyAi"); diff --git a/ai/heavyai/heavyai.cpp b/ai/heavyai/heavyai.cpp new file mode 100644 index 000000000..4ca5f1f89 --- /dev/null +++ b/ai/heavyai/heavyai.cpp @@ -0,0 +1,34 @@ + +#include "coreengine/qmlvector.h" +#include "coreengine/gameconsole.h" +#include "coreengine/globalutils.h" + +#include "ai/heavyai/heavyai.h" + +#include "game/player.h" +#include "game/gameaction.h" +#include "game/gamemap.h" + +#include "resource_management/unitspritemanager.h" + +HeavyAi::HeavyAi(GameMap* pMap, QString type, GameEnums::AiTypes aiType) + : CoreAI(pMap, aiType, type) +{ +#ifdef GRAPHICSUPPORT + setObjectName("HeavyAi"); +#endif + Interpreter::setCppOwnerShip(this); + setupJsThis(this); + + if (m_pMap != nullptr && + !m_pMap->getSavegame()) + { + loadIni("heavy/" + m_aiName.toLower() + ".ini"); + } + CONSOLE_PRINT("Creating heavy ai", GameConsole::eDEBUG); +} + +void HeavyAi::process() +{ + +} diff --git a/ai/heavyai/heavyai.h b/ai/heavyai/heavyai.h new file mode 100644 index 000000000..771539c4e --- /dev/null +++ b/ai/heavyai/heavyai.h @@ -0,0 +1,23 @@ +#pragma once + +#include "ai/coreai.h" + +class GameMap; +class HeavyAi; +using spHeavyAi = std::shared_ptr; + +class HeavyAi final : public CoreAI +{ + Q_OBJECT +public: + + explicit HeavyAi(GameMap* pMap, QString type, GameEnums::AiTypes aiType); + virtual ~HeavyAi() = default; +public slots: + /** + * @brief process + */ + virtual void process() override; +}; + +Q_DECLARE_INTERFACE(HeavyAi, "HeavyAi"); diff --git a/ai/heavyai/heavyaiproduction.cpp b/ai/heavyai/heavyaiproduction.cpp deleted file mode 100644 index f108680d6..000000000 --- a/ai/heavyai/heavyaiproduction.cpp +++ /dev/null @@ -1,610 +0,0 @@ -#include "ai/heavyai.h" - -#include "coreengine/qmlvector.h" -#include "coreengine/gameconsole.h" -#include "coreengine/globalutils.h" - -#include "game/player.h" -#include "game/gameaction.h" -#include "game/gamemap.h" - -#include "resource_management/movementtablemanager.h" - -// code for building units is here - -bool HeavyAi::buildUnits(spQmlVectorBuilding & pBuildings, spQmlVectorUnit & pUnits, - spQmlVectorUnit & pEnemyUnits, spQmlVectorBuilding & pEnemyBuildings) -{ - if (m_aiStep < AISteps::buildUnits) - { - m_productionSystem.onNewBuildQueue(pBuildings.get(), pUnits.get(), pEnemyUnits.get(), pEnemyBuildings.get()); - } - m_aiStep = AISteps::buildUnits; - bool executed = false; - if (m_productionSystem.buildUnit(pBuildings.get(), pUnits.get(), pEnemyUnits.get(), pEnemyBuildings.get(), executed)) - { - return executed; - } - scoreUnitBuildings(pBuildings, pUnits, pEnemyUnits, pEnemyBuildings); - double bestScore = std::numeric_limits::lowest(); - qint32 index = -1; - for (qint32 i = 0; i < m_BuildingData.size(); ++i) - { - if (m_BuildingData[i].m_action.get() != nullptr && - m_BuildingData[i].m_score > bestScore) - { - bestScore = m_BuildingData[i].m_score; - index = i; - } - } - if (index >= 0) - { - auto & item = m_BuildingData[index]; - CONSOLE_PRINT("HeavyAi::buildUnits " + item.buildingDataInput[item.m_selectedData].unitId + " with scored value " + QString::number(bestScore), GameConsole::eDEBUG); - m_updatePoints.push_back(item.m_action->getTarget()); - emit sigPerformAction(item.m_action); - item.m_action.reset(); - item.m_score = 0; - if (item.buildingDataInput[item.m_selectedData].unitBuildingDataInput[BuildingEntry::CanAttackImmuneUnitRatio] > 0 || - item.buildingDataInput[item.m_selectedData].unitBuildingDataInput[BuildingEntry::UnitsToTransportRatio] > 0) - { - // reset data - m_BuildingData.clear(); - } - return true; - } - else - { - CONSOLE_PRINT("HeavyAi::buildUnits build no units highest value was: " + QString::number(bestScore), GameConsole::eDEBUG); - } - return false; -} - -void HeavyAi::scoreUnitBuildings(spQmlVectorBuilding & pBuildings, spQmlVectorUnit & pUnits, - spQmlVectorUnit & pEnemyUnits, spQmlVectorBuilding & pEnemyBuildings) -{ - std::vector> transportTargets; - std::vector data = getGlobalBuildInfo(pBuildings, pUnits, pEnemyUnits, pEnemyBuildings, transportTargets); - std::vector immuneUnits; - getImmuneUnits(pUnits, pEnemyUnits, immuneUnits); - - - // do the actual scoring - qint32 funds = m_pPlayer->getFunds(); - for (auto & building : m_BuildingData) - { - if (building.buildingDataInput.size() == 0) - { - createUnitBuildData(building, data, funds, immuneUnits, transportTargets, pEnemyBuildings); - } - else - { - updateUnitBuildData(building, data, funds); - } - scoreBuildingProductionData(building); - } -} - -void HeavyAi::scoreBuildingProductionData(HeavyAi::BuildingData & building) -{ - double bestScore = 0.0; - std::vector bestItems; - std::vector scores; - building.m_action.reset(); - building.m_score = 0; - qint32 coCount = m_pPlayer->getCoCount(); - qint32 maxCoCount = m_pPlayer->getMaxCoCount(); - for (qint32 i = 0; i < building.buildingDataInput.size(); ++i) - { - if (building.buildingDataInput[i].enabled) - { - qint32 x = building.m_pBuilding->Building::getX(); - qint32 y = building.m_pBuilding->Building::getY(); - auto score = m_neuralNetworks[NeuralNetworks::Production]->predict(building.buildingDataInput[i].unitBuildingDataInput); - double value = score[0] * BaseGameInputIF::getUnitBuildValue(building.buildingDataInput[i].unitId); - if (m_pPlayer->getCoCount() > 0) - { - Unit dummy(building.buildingDataInput[i].unitId, m_pPlayer, false, m_pMap); - dummy.setVirtuellX(x); - dummy.setVirtuellY(y); - double bonusScore = 0; - for (qint32 co = 0; co < maxCoCount; ++co) - { - bonusScore += getAiCoUnitMultiplier(m_pPlayer->getCO(co), &dummy); - } - double modifier = value * (1 + bonusScore / (CO::MAX_CO_UNIT_VALUE * coCount)); - if (bonusScore > 0) - { - value += modifier; - } - else - { - value -= modifier; - } - // todo boost co indirect / direct ratio - } - if (value > m_maxScore) - { - value = m_maxScore; - } - if (value > bestScore && value >= m_minActionScore) - { - bestScore = value; - qint32 i2 = 0; - while (i2 < bestItems.size()) - { - if (scores[i2] < bestScore - m_actionScoreVariant) - { - scores.erase(scores.cbegin() + i2); - bestItems.erase(bestItems.cbegin() + i2); - } - else - { - ++i2; - } - } - scores.push_back(bestScore); - bestItems.push_back(i); - } - else if (bestScore - m_actionScoreVariant <= value) - { - bestItems.push_back(i); - scores.push_back(value); - } - } - } - if (bestItems.size() > 0) - { - qint32 item = GlobalUtils::randInt(0, bestItems.size() - 1); - building.m_score = scores[item]; - building.m_selectedData = bestItems[item]; - building.m_action = MemoryManagement::create(CoreAI::ACTION_BUILD_UNITS, m_pMap); - building.m_action->setTarget(QPoint(building.m_pBuilding->Building::getX(), building.m_pBuilding->Building::getY())); - CoreAI::addMenuItemData(building.m_action, building.buildingDataInput[building.m_selectedData].unitId, building.buildingDataInput[building.m_selectedData].cost); - } -} - -void HeavyAi::createUnitBuildData(BuildingData & building, std::vector & data, qint32 funds, const std::vector & immuneUnits, - const std::vector> & transportTargets, spQmlVectorBuilding & pEnemyBuildings) -{ - // create new - MovementTableManager* pMovementTableManager = MovementTableManager::getInstance(); - spTerrain pDummyTerrain = Terrain::createTerrain(GameMap::PLAINS, -1, -1, "", m_pMap); - spGameAction pAction = MemoryManagement::create(ACTION_BUILD_UNITS, m_pMap); - qint32 x = building.m_pBuilding->Building::getX(); - qint32 y = building.m_pBuilding->Building::getY(); - pAction->setTarget(QPoint(x, y)); - if (pAction->canBePerformed()) - { - // we're allowed to build units here - spMenuData pData = pAction->getMenuStepData(); - if (pData->validData()) - { - auto enableList = pData->getEnabledList(); - auto actionIds = pData->getActionIDs(); - auto costs = pData->getCostList(); - for (qint32 i = 0; i < pData->getActionIDs().size(); i++) - { - if (enableList[i]) - { - UnitBuildData unitData; - unitData.unitId = actionIds[i]; - unitData.cost = costs[i]; - Unit dummy(unitData.unitId, m_pPlayer, false, m_pMap); - dummy.setVirtuellX(x); - dummy.setVirtuellY(y); - spTerrain pDummyTerrain = Terrain::createTerrain(GameMap::PLAINS, -1, -1, "", m_pMap); - qint32 baseMovementCost = pMovementTableManager->getBaseMovementPoints(dummy.getMovementType(), pDummyTerrain.get(), pDummyTerrain.get(), &dummy); - if (baseMovementCost < 0) - { - baseMovementCost = 1; - } - qint32 movementPoints = dummy.getMovementpoints(QPoint(x, y)) / baseMovementCost; - getProductionInputVector(building.m_pBuilding, &dummy, unitData, immuneUnits, movementPoints); - getTransportInputVector(building.m_pBuilding, &dummy, transportTargets, pEnemyBuildings, movementPoints, unitData); - updateUnitBuildData(unitData, data, funds); - building.buildingDataInput.append(unitData); - } - } - } - } -} - -void HeavyAi::updateUnitBuildData(BuildingData & building, std::vector & data, qint32 funds) -{ - GameAction action = GameAction(ACTION_BUILD_UNITS, m_pMap); - action.setTarget(QPoint(building.m_pBuilding->Building::getX(), building.m_pBuilding->Building::getY())); - if (action.canBePerformed()) - { - spMenuData pData = action.getMenuStepData(); - if (pData->validData()) - { - // update - auto enableList = pData->getEnabledList(); - auto actionIds = pData->getActionIDs(); - for (auto & unitData : building.buildingDataInput) - { - for (qint32 i = 0; i < enableList.size(); ++i) - { - if (unitData.unitId == actionIds[i]) - { - unitData.enabled = enableList[i]; - break; - } - } - updateUnitBuildData(unitData, data, funds); - } - } - else - { - building.buildingDataInput.clear(); - } - } - else - { - building.buildingDataInput.clear(); - } -} - -void HeavyAi::updateUnitBuildData(UnitBuildData &unitData, std::vector &data, qint32 funds) -{ - for (qint32 i = 0; i < BuildingEntry::LocalUnitData; ++i) - { - unitData.unitBuildingDataInput[i] = data[i]; - } - if (funds > 0) - { - unitData.unitBuildingDataInput[BuildingEntry::FondsUsage] = static_cast(unitData.cost) / static_cast(funds); - } - else - { - unitData.unitBuildingDataInput[BuildingEntry::FondsUsage] = 0; - } -} - -void HeavyAi::getProductionInputVector(Building* pBuilding, Unit* pUnit, UnitBuildData & data, const std::vector & immuneUnits, qint32 movementPoints) -{ - - if (m_pMap != nullptr) - { - QStringList actionList = pUnit->getActionList(); - if (pUnit->getBaseMaxRange() > 1) - { - data.unitBuildingDataInput[BuildingEntry::BasicAttackRange] = 1; - } - else - { - data.unitBuildingDataInput[BuildingEntry::BasicAttackRange] = -1; - } - if (actionList.contains(ACTION_CAPTURE)) - { - data.unitBuildingDataInput[BuildingEntry::CaptureUnit] = 1; - } - else - { - data.unitBuildingDataInput[BuildingEntry::CaptureUnit] = -1; - } - QPoint position(pBuilding->Building::getX(), pBuilding->Building::getY()); - const auto * influenceInfo = m_InfluenceFrontMap.getInfluenceInfo(position.x(), position.y()); - double highestInfluence = m_InfluenceFrontMap.getTotalHighestInfluence(); - data.unitBuildingDataInput[BuildingEntry::Movementpoints] = static_cast(movementPoints) / static_cast(m_maxMovementpoints); - data.unitBuildingDataInput[BuildingEntry::VisionPotential] = pUnit->getVision(position) / m_maxVision; - data.unitBuildingDataInput[BuildingEntry::MapMovementpoints] = movementPoints / static_cast(m_pMap->getMapHeight() * m_pMap->getMapWidth()); - data.unitBuildingDataInput[BuildingEntry::FireRange] = static_cast(pUnit->getMaxRange(position)) / static_cast(m_maxFirerange); - data.unitBuildingDataInput[BuildingEntry::Flying] = (pUnit->useTerrainDefense() == false) ? 1 : -1; - data.unitBuildingDataInput[BuildingEntry::LoadingPotential] = static_cast(pUnit->getLoadingPlace()) / 4.0; - data.unitBuildingDataInput[BuildingEntry::OwnInfluence] = static_cast(influenceInfo->getOwnInfluence()) / highestInfluence; - data.unitBuildingDataInput[BuildingEntry::HighestEnemyInfluence] = static_cast(influenceInfo->getEnemyInfluence()) / highestInfluence; - qint32 islandIdx = getIslandIndex(pUnit); - qint32 island = m_IslandMaps[islandIdx]->getIsland(position.x(), position.y()); - qint32 islandSize = m_IslandMaps[islandIdx]->getIslandSize(island); - double maxIslandSize = movementPoints * movementPoints; - if (islandSize > maxIslandSize) - { - data.unitBuildingDataInput[BuildingEntry::MovementPotential] = 1; - } - else if (maxIslandSize > 0) - { - data.unitBuildingDataInput[BuildingEntry::MovementPotential] = islandSize / maxIslandSize; - } - calculateUnitProductionDamage(pBuilding, pUnit, movementPoints, position, data, immuneUnits); - } -} - -void HeavyAi::getTransportInputVector(Building* pBuilding, Unit* pUnit, const std::vector> & transportTargets, - spQmlVectorBuilding & pEnemyBuildings, qint32 movementPoints, UnitBuildData & data) -{ - if (data.unitBuildingDataInput[BuildingEntry::LoadingPotential] > 0) - { - QPoint position(pBuilding->Building::getX(), pBuilding->Building::getY()); - std::vector targets; - std::vector loadingUnits = appendLoadingTargets(pUnit, m_pUnits, m_pEnemyUnits, pEnemyBuildings, false, true, targets, true); - std::vector transporterUnits; - for (qint32 i2 = 0; i2 < transportTargets.size(); i2++) - { - if (!GlobalUtils::contains(transporterUnits, std::get<0>(transportTargets[i2]))) - { - transporterUnits.push_back(std::get<0>(transportTargets[i2])); - } - } - double transporterCount = 0; - for (const auto & unit : qAsConst(loadingUnits)) - { - if (canTransportToEnemy(pUnit, unit, m_pEnemyUnits, pEnemyBuildings)) - { - double scoreMultiplier = getProductionScoreMultiplier(position, unit->Unit::getMapPosition(), movementPoints); - transporterCount += scoreMultiplier; - } - } - double requiredTransporterCount = 0; - for (const auto & unit : qAsConst(transporterUnits)) - { - double scoreMultiplier = getProductionScoreMultiplier(position, unit->Unit::getMapPosition(), movementPoints); - requiredTransporterCount += scoreMultiplier; - } - data.unitBuildingDataInput[BuildingEntry::RequiredUnitsToTransportRatio] = requiredTransporterCount / static_cast(transportTargets.size() + 1); - data.unitBuildingDataInput[BuildingEntry::UnitsToTransportRatio] = transporterCount / static_cast(m_pUnits->size() + 1); - } -} - -void HeavyAi::calculateUnitProductionDamage(Building* pBuilding, Unit* pUnit, qint32 movementPoints, QPoint position, UnitBuildData & data, const std::vector & immuneUnits) -{ - - if (m_pMap != nullptr) - { - qint32 mapWidth = m_pMap->getMapWidth(); - qint32 mapHeight = m_pMap->getMapHeight(); - qint32 unitValue = pUnit->getCoUnitValue(); - bool canCapture = (data.unitBuildingDataInput[BuildingEntry::CaptureUnit] > 0); - double fundsDamage = 0.0; - double possibleFundsDamage = 0.0; - double hpDamage = 0.0; - double possibleHpDamage = 0.0; - double captureRate = 0.0; - double maxCaptureRate = 0.0; - double counterFundsDamage = 0.0; - double counterhpDamage = 0.0; - double counterAttackCount = 0.0; - double immuneUnit = 0; - for (qint32 x = 0; x < mapWidth; ++x) - { - for (qint32 y = 0; y < mapHeight; ++y) - { - Terrain* pTerrain = m_pMap->getTerrain(x, y); - Unit* pEnemyUnit = pTerrain->getUnit(); - Building* pBuilding = pTerrain->getBuilding(); - double scoreMultiplier = getProductionScoreMultiplier(position, QPoint(x, y), movementPoints); - if (pEnemyUnit != nullptr && - m_pPlayer->isEnemyUnit(pEnemyUnit)) - { - double unitMulitpler = scoreMultiplier; - if (isPrimaryEnemy(pEnemyUnit)) - { - unitMulitpler *= m_primaryEnemyMultiplier; - } - double dmg = getBaseDamage(pUnit, pEnemyUnit); - double enemyHp = pEnemyUnit->getHp(); - double enemyHpRatio = enemyHp / Unit::MAX_UNIT_HP; - if (dmg >= 0) - { - double enemyUnitValue = pEnemyUnit->getCoUnitValue(); - double damageMultiplier = dmg / (enemyHp * Unit::MAX_UNIT_HP); - fundsDamage += unitMulitpler * enemyUnitValue * damageMultiplier; - possibleFundsDamage += unitMulitpler * enemyUnitValue; - hpDamage += unitMulitpler * damageMultiplier; - possibleHpDamage += unitMulitpler * enemyHp / Unit::MAX_UNIT_HP; - if (GlobalUtils::contains(immuneUnits, pEnemyUnit)) - { - ++immuneUnit; - } - } - dmg = getBaseDamage(pEnemyUnit, pUnit) * enemyHpRatio; - if (dmg >= 0) - { - static constexpr double maxDamage = Unit::MAX_UNIT_HP * Unit::MAX_UNIT_HP; - counterFundsDamage += unitMulitpler * unitValue * dmg / maxDamage; - counterhpDamage += unitMulitpler * dmg / maxDamage; - ++counterAttackCount; - } - } - if (canCapture && - pBuilding != nullptr && - m_pPlayer->isEnemy(pBuilding->getOwner()) && - pBuilding->isCaptureOrMissileBuilding(true)) - { - double buildingMulitpler = 1; - if (isPrimaryEnemy(pBuilding)) - { - buildingMulitpler = m_primaryEnemyMultiplier; - scoreMultiplier *= m_primaryEnemyMultiplier; - } - if (pBuilding->getActionList().contains(ACTION_BUILD_UNITS)) - { - qint32 buildSize = pBuilding->getConstructionList().size(); - captureRate += scoreMultiplier * buildSize; - maxCaptureRate += buildSize * buildingMulitpler; - } - else - { - captureRate += buildingMulitpler; - maxCaptureRate += buildingMulitpler; - } - } - } - } - if (possibleHpDamage > 0) - { - data.unitBuildingDataInput[BuildingEntry::DealingHpDamage] = hpDamage / possibleHpDamage; - } - if (possibleFundsDamage > 0) - { - data.unitBuildingDataInput[BuildingEntry::DealingFundsDamage] = fundsDamage / possibleFundsDamage; - } - if (counterAttackCount > 0 && unitValue > 0) - { - data.unitBuildingDataInput[BuildingEntry::ReceivingFundsDamge] = counterFundsDamage / (counterAttackCount * unitValue); - data.unitBuildingDataInput[BuildingEntry::ReceivingHpDamage] = counterhpDamage / counterAttackCount; - } - if (maxCaptureRate > 0) - { - data.unitBuildingDataInput[BuildingEntry::CapturePotential] = captureRate / maxCaptureRate; - } - if (immuneUnits.size() > 0) - { - data.unitBuildingDataInput[BuildingEntry::CanAttackImmuneUnitRatio] = immuneUnit / static_cast(immuneUnits.size()); - } - data.unitBuildingDataInput[BuildingEntry::MaxUnitValue] = unitValue / m_maxUnitValue; - } -} - -float HeavyAi::getProductionScoreMultiplier(QPoint position, QPoint target, qint32 movementPoints) -{ - float scoreMultiplier = 1.0f; - qint32 distance = GlobalUtils::getDistance(position, target); - double turnDistance = distance / static_cast(movementPoints); - if (turnDistance > m_maxProductionTurnRange) - { - scoreMultiplier = m_maxProductionTurnRange / turnDistance; - } - return scoreMultiplier; -} - -float HeavyAi::getBaseDamage(Unit* pAttacker, Unit* pDefender) -{ - float dmg = pAttacker->getBaseDamage(pDefender); - dmg *= pAttacker->getHp() / Unit::MAX_UNIT_HP; - // cap damage - if (dmg > pDefender->getHp() * Unit::MAX_UNIT_HP) - { - dmg = pDefender->getHp() * Unit::MAX_UNIT_HP; - } - return dmg; -} - -std::vector HeavyAi::getGlobalBuildInfo(spQmlVectorBuilding & pBuildings, spQmlVectorUnit & pUnits, - spQmlVectorUnit & pEnemyUnits, spQmlVectorBuilding & pEnemyBuildings, - std::vector> & transportTargets) -{ - std::vector data(BuildingEntryMaxSize, 0.0); - - if (m_pMap != nullptr) - { - UnitCountData countData; - GetOwnUnitCounts(m_ownUnits, pUnits, pEnemyUnits, pEnemyBuildings, countData); - double count = pUnits->size() + 1; - data[DirectUnitRatio] = static_cast(countData.directUnits) / count; - data[IndirectUnitRatio] = static_cast(countData.indirectUnits) / count; - data[InfantryUnitRatio] = static_cast(countData.infantryUnits) / count; - data[TransportUnitRatio] = static_cast(countData.transporterUnits) / count; - data[SupplyUnitRatio] = static_cast(countData.supplyUnits) / count; - data[SupplyRequiredRatio] = static_cast(countData.supplyNeededUnits) / count; - if (m_pMap->getCurrentDay() > m_earlyGameDays) - { - data[DayProgression] = 1.0 / static_cast(m_pMap->getCurrentDay() - m_earlyGameDays); - } - else - { - data[DayProgression] = 1.0; - } - double enemeyCount = 0; - double playerCount = 0; - for (qint32 i = 0; i < m_pMap->getPlayerCount(); i++) - { - if (!m_pMap->getPlayer(i)->getIsDefeated()) - { - playerCount++; - if (m_pPlayer->isEnemy(m_pMap->getPlayer(i))) - { - enemeyCount++; - } - } - } - if (pEnemyBuildings->size() + pBuildings->size() > 0) - { - data[TotalBuildingRatio] = pBuildings->size() / static_cast(pBuildings->size() + pEnemyBuildings->size()); - } - if (playerCount > 0) - { - data[EnemyRatio] = static_cast(enemeyCount) / playerCount; - } - double unusedCount = 0; - float funds = m_pPlayer->getFunds(); - for (qint32 i = 0; i < pBuildings->size(); i++) - { - Building* pBuilding = pBuildings->at(i); - if (pBuilding->isProductionBuilding()) - { - if (pBuilding->getTerrain()->getUnit() == nullptr) - { - bool found = false; - for (auto & building : m_BuildingData) - { - if (building.m_pBuilding == pBuilding) - { - unusedCount++; - found = true; - break; - } - } - if (!found) - { - auto buildList = pBuilding->getConstructionList(); - for (auto & unitId : buildList) - { - Unit dummy(unitId, m_pPlayer, false, m_pMap); - if (m_pPlayer->getCosts(unitId, pBuilding->getPosition()) < funds && dummy.hasWeapons()) - { - BuildingData newData; - newData.m_pBuilding = pBuilding; - m_BuildingData.push_back(newData); - unusedCount++; - break; - } - } - } - } - else - { - for (qint32 i = 0; i < m_BuildingData.size(); ++i) - { - if (m_BuildingData[i].m_pBuilding == pBuilding) - { - m_BuildingData.erase(m_BuildingData.cbegin() + i); - break; - } - } - } - } - } - if (m_BuildingData.size() > 0) - { - data[ProductionUsage] = static_cast(unusedCount) / static_cast(m_BuildingData.size()); - } - } - return data; -} - -void HeavyAi::getImmuneUnits(spQmlVectorUnit & pUnits, spQmlVectorUnit & pEnemyUnits, std::vector & immuneUnits) -{ - immuneUnits.clear(); - for (qint32 i2 = 0; i2 < pEnemyUnits->size(); i2++) - { - bool attackable = false; - for (qint32 i = 0; i < pUnits->size(); i++) - { - Unit* pUnit = pUnits->at(i); - Unit* pEnemyUnit = pEnemyUnits->at(i2); - auto dmg = getBaseDamage(pUnit, pEnemyUnit); - - if (dmg > m_notAttackableDamage || dmg >= pEnemyUnit->getHp()) - { - attackable = true; - break; - } - } - if (!attackable) - { - immuneUnits.push_back(pEnemyUnits->at(i2)); - } - } -} diff --git a/ai/heavyai/heavyaitraining.cpp b/ai/heavyai/heavyaitraining.cpp deleted file mode 100644 index d09b788c2..000000000 --- a/ai/heavyai/heavyaitraining.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "ai/heavyai.h" -#include "coreengine/gameconsole.h" -#include "coreengine/globalutils.h" - -// code for debugging and neuro evolution is here - -QString HeavyAi::getNeuralNetworkName(qint32 network) -{ - if (network >= 0 && network < m_neuralNetworks.size()) - { - return m_neuralNetworks[network]->getNetworkName(); - } - else - { - CONSOLE_PRINT("HeavyAi::getNeuralNetworkName invalid index " + QString::number(network), GameConsole::eDEBUG); - } - return ""; -} - -void HeavyAi::mutateNeuralNetwork(qint32 network, double mutationChance, double mutationRate) -{ - if (network >= 0 && network < m_neuralNetworks.size()) - { - m_neuralNetworks[network]->mutateAllWeights(mutationChance, mutationRate); - } - else - { - CONSOLE_PRINT("HeavyAi::mutateNeuralNetwork invalid index " + QString::number(network), GameConsole::eDEBUG); - } -} - -void HeavyAi::setDisabled(qint32 network, bool disabled) -{ - m_neuralNetworks[network]->setDisabled(disabled); -} - -void HeavyAi::combineAi(QStringList aisToUse) -{ - CONSOLE_PRINT("HeavyAi::combineAi", GameConsole::eDEBUG); - for (qint32 i = 0; i < NeuralNetworksMax; ++i) - { - qint32 item = GlobalUtils::randInt(0, aisToUse.length() - 1); - QString netName = NeuralNetworkNames[i]; - QString targetName = QString("resources/") + NeuralNetworkPath + netName + m_aiName + NeuralNetworkFileEnding; - QFile::remove(targetName); - QFile::copy(QString("resources/") + NeuralNetworkPath + netName + aisToUse[item] + NeuralNetworkFileEnding, targetName); - } -} - -void HeavyAi::saveNeuralNetwork(qint32 network) -{ - if (network >= 0 && network < m_neuralNetworks.size()) - { - QFile file(QString("resources/") + NeuralNetworkPath + NeuralNetworkNames[network] + m_aiName + NeuralNetworkFileEnding); - file.open(QIODevice::WriteOnly | QIODevice::Truncate); - QDataStream stream(&file); - stream.setVersion(QDataStream::Version::Qt_6_5); - m_neuralNetworks[network]->serializeObject(stream); - } - else - { - CONSOLE_PRINT("HeavyAi::saveNeuralNetwork invalid index " + QString::number(network), GameConsole::eDEBUG); - } -} - -void HeavyAi::toggleAiPause() -{ - m_pause = !m_pause; -} - -void HeavyAi::showFrontMap() -{ - m_InfluenceFrontMap.show(); -} - -void HeavyAi::showFrontLines() -{ - m_InfluenceFrontMap.showFrontlines(); -} - -void HeavyAi::hideFrontMap() -{ - m_InfluenceFrontMap.hide(); -} - -void HeavyAi::showUnitPfs(bool enemy, qint32 index) -{ - if (enemy) - { - if (index >= 0 && index < m_enemyUnits.size()) - { - m_InfluenceFrontMap.showPfs(m_enemyUnits[index].pUnitPfs.get()); - } - } - else - { - if (index >= 0 && index < m_ownUnits.size()) - { - m_InfluenceFrontMap.showPfs(m_ownUnits[index].pUnitPfs.get()); - } - } -} - -void HeavyAi::showIslandMap(QString unitId) -{ - Unit unit(unitId, m_pPlayer, false, m_pMap); - qint32 unitIslandIdx = getIslandIndex(&unit); - m_IslandMaps[unitIslandIdx]->show(); -} - -void HeavyAi::hideIslandMap(QString unitId) -{ - Unit unit(unitId, m_pPlayer, false, m_pMap); - qint32 unitIslandIdx = getIslandIndex(&unit); - m_IslandMaps[unitIslandIdx]->show(); -} - -double HeavyAi::getMinActionScore() const -{ - return m_minActionScore; -} - -void HeavyAi::setMinActionScore(double newMinActionScore) -{ - m_minActionScore = newMinActionScore; -} - -InfluenceFrontMap* HeavyAi::getInfluenceFrontMap() -{ - return &m_InfluenceFrontMap; -} diff --git a/ai/neuralnetwork/neural/edge.cpp b/ai/neuralnetwork/neural/edge.cpp deleted file mode 100644 index ff1127bd1..000000000 --- a/ai/neuralnetwork/neural/edge.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "ai/neuralnetwork/neural/edge.h" -#include "ai/neuralnetwork/neural/neuron.h" - -Edge::Edge(Neuron *nextNeuron, Neuron* previousNeuron, double weight) - : m_nextNeuron(nextNeuron), - m_previousNeuron(previousNeuron), - m_weight(weight) -{ -} - -Neuron* Edge::nextNeuron() const -{ - return m_nextNeuron; -} - -Neuron* Edge::previousNeuron() const -{ - return m_previousNeuron; -} - -double Edge::weight() -{ - return m_weight; -} - -void Edge::propagate(double neuron_output) -{ - m_nextNeuron->addAccumulated(neuron_output * m_weight); -} - -void Edge::alterWeight(double w) -{ - m_weight = w; -} - -void Edge::serializeObject(QDataStream& pStream) const -{ - pStream << getVersion(); - pStream << m_weight; -} - -void Edge::deserializeObject(QDataStream& pStream) -{ - qint32 version = 0; - pStream >> version; - pStream >> m_weight; -} diff --git a/ai/neuralnetwork/neural/edge.h b/ai/neuralnetwork/neural/edge.h deleted file mode 100644 index 9710adb95..000000000 --- a/ai/neuralnetwork/neural/edge.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef EDGE_H -#define EDGE_H - -#include - -#include "coreengine/fileserializable.h" - -class NeuralNetwork; -class Neuron; -class Layer; -class Edge; - -using spNeuron = std::shared_ptr; -using spEdge = std::shared_ptr; - -//Edge between two neurons -class Edge final : public FileSerializable -{ -public: - static constexpr const char* const getTypeName() - { - return "Edge"; - } - Edge(Neuron* nextNeuron, Neuron* previousNeuron, double weight); - virtual ~Edge() = default; - /** - * @brief serialize stores the object - * @param pStream - */ - virtual void serializeObject(QDataStream& pStream) const override; - /** - * @brief deserialize restores the object - * @param pStream - */ - virtual void deserializeObject(QDataStream& pStream) override; - /** - * @brief getVersion version of the file - * @return - */ - virtual qint32 getVersion() const override - { - return 1; - } - Neuron* nextNeuron() const; - Neuron* previousNeuron() const; - double weight(); - void propagate(double neuron_output); - void alterWeight(double w); -private: - Neuron* m_nextNeuron = nullptr; - Neuron* m_previousNeuron = nullptr; - double m_weight = 0.0; -}; - -#endif // EDGE_H diff --git a/ai/neuralnetwork/neural/layer.cpp b/ai/neuralnetwork/neural/layer.cpp deleted file mode 100644 index 7c7105eb4..000000000 --- a/ai/neuralnetwork/neural/layer.cpp +++ /dev/null @@ -1,218 +0,0 @@ -#include "ai/neuralnetwork/neural/layer.h" -#include "coreengine/memorymanagement.h" - -const char* const Layer::LAYER_PARAMETER_SIZE = "SIZE"; -const char* const Layer::LAYER_PARAMETER_TYPE = "TYPE"; -const char* const Layer::LAYER_PARAMETER_ACTIVATION = "ACTIVATION"; - -Layer::Layer(qint32 id_layer, NeuralNetwork* net, QMap & parameters) -{ - m_id_layer = id_layer; - m_net = net; - m_parameters = parameters; - m_type = static_cast(static_cast(parameters[LAYER_PARAMETER_TYPE])); - m_activation = static_cast(static_cast(m_parameters[LAYER_PARAMETER_ACTIVATION])); - initLayer(); -} - -void Layer::extend(quint32 count, bool randomize) -{ - qint32 size = m_parameters[Layer::LAYER_PARAMETER_SIZE]; - if (m_type == LayerType::STANDARD || m_type == LayerType::INPUT) - { - for (qint32 i_neuron = 0; i_neuron < count; ++i_neuron) - { - m_neurons.insert(m_neurons.cbegin(), MemoryManagement::create(size + i_neuron, this, m_activation, false)); - } - } - if (m_previousLayer != nullptr) - { - for(auto & n1 : m_previousLayer->m_neurons) - { - for(qint32 i = size; i < size + count; ++i) - { - auto & n2 = m_neurons[i]; - if(!n2->isBias()) - { - n1->addNext(n2, randomize); - } - } - } - } - m_parameters[Layer::LAYER_PARAMETER_SIZE] += count; -} - -int Layer::getId() const -{ - return m_id_layer; -} - -void Layer::initLayer() -{ - m_neurons.clear(); - - if (m_type == LayerType::STANDARD || m_type == LayerType::INPUT) - { - m_parameters[LAYER_PARAMETER_SIZE] += 1; // bias for the next layer - m_neurons = std::vector(static_cast(m_parameters[LAYER_PARAMETER_SIZE])); - for (qint32 i_neuron = 0; i_neuron < m_parameters[LAYER_PARAMETER_SIZE]; ++i_neuron) - { - bool isBias = i_neuron == (m_neurons.size() - 1); - m_neurons[i_neuron] = MemoryManagement::create(i_neuron, this, m_activation, isBias); - } - } - else if (m_type == LayerType::OUTPUT) - { - m_neurons.reserve(static_cast(m_parameters[LAYER_PARAMETER_SIZE])); - for (qint32 i_neuron = 0; i_neuron < m_parameters[LAYER_PARAMETER_SIZE]; ++i_neuron) - { - m_neurons.push_back(MemoryManagement::create(i_neuron, this, m_activation)); - } - } -} - -void Layer::clean() -{ - for(auto & n : m_neurons) - { - n->clean(); - } -} - -void Layer::trigger() -{ - for(auto & n : m_neurons) - { - n->trigger(); - } -} - - -void Layer::connectComplete(Layer *next) -{ - next->setPreviousLayer(this); - for(auto & n1 : m_neurons) - { - for(auto & n2 : next->m_neurons) - { - if(!n2->isBias()) - { - n1->addNext(n2); - } - } - } -} - -std::vector Layer::output() -{ - std::vector outputs; - outputs.reserve(m_neurons.size()); - for(auto & n : m_neurons) - { - outputs.push_back(n->output()); - } - return outputs; - -} - -void Layer::randomizeAllWeights(double abs_value) -{ - for(auto & neuron : m_neurons) - { - neuron->randomizeAllWeights(abs_value); - } -} - -void Layer::mutateAllWeights(double mutationChance, double maxWeight, double mutationRate) -{ - for(auto & neuron : m_neurons) - { - neuron->mutateAllWeights(mutationChance, maxWeight, mutationRate); - } -} - -void Layer::setAccumulated(qint32 neuron, double value) -{ - m_neurons[neuron]->setAccumulated(value); -} - -Neuron* Layer::getNeuron(qint32 id) -{ - for(auto & neuron : m_neurons) - { - if (neuron->getNeuronId() == id) - { - return neuron.get(); - } - } - return nullptr; -} - -QString Layer::toString() -{ - QString str = "layer: " + QString::number(m_id_layer) + "\n"; - for(auto & neuron : m_neurons) - { - str += neuron->toString() + "\n"; - } - return str; -} - -const QMap& Layer::getParameters() const -{ - return m_parameters; -} - -Layer::LayerType Layer::getType() const -{ - return m_type; -} - -Neuron::ActivationFunction Layer::getActivation() const -{ - return m_activation; -} - -Layer *Layer::getPreviousLayer() const -{ - return m_previousLayer; -} - -void Layer::setPreviousLayer(Layer *previousLayer) -{ - m_previousLayer = previousLayer; -} - -void Layer::serializeObject(QDataStream& pStream) const -{ - pStream << getVersion(); - pStream << m_id_layer; - pStream << m_type; - pStream << m_activation; - pStream << m_parameters; - pStream << static_cast(m_neurons.size()); - for (const auto & neuron : m_neurons) - { - neuron->serializeObject(pStream); - } -} - -void Layer::deserializeObject(QDataStream& pStream) -{ - m_neurons.clear(); - qint32 version = 0; - pStream >> version; - pStream >> m_id_layer; - pStream >> m_type; - pStream >> m_activation; - pStream >> m_parameters; - qint32 size = 0; - pStream >> size; - for (qint32 i = 0; i < size; ++i) - { - spNeuron pNeuron = MemoryManagement::create(i, this, m_activation, false); - pNeuron->deserializeObject(pStream); - m_neurons.push_back(pNeuron); - } -} - diff --git a/ai/neuralnetwork/neural/layer.h b/ai/neuralnetwork/neural/layer.h deleted file mode 100644 index 39d4b8fba..000000000 --- a/ai/neuralnetwork/neural/layer.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef LAYER_H -#define LAYER_H - -#include -#include - -#include "ai/neuralnetwork/neural/neuron.h" - -class NeuralNetwork; -class Layer; - -using spLayer = std::shared_ptr; -//Layer of the network -class Layer final : public FileSerializable -{ -public: - static constexpr const char* const getTypeName() - { - return "Layer"; - } - static const char* const LAYER_PARAMETER_SIZE; - static const char* const LAYER_PARAMETER_TYPE; - static const char* const LAYER_PARAMETER_ACTIVATION; - enum class LayerType - { - STANDARD = 0, //Standard layer : fully connected perceptrons - OUTPUT, // Output : No bias neuron - INPUT, // Input: Standard input (output of neurons is outputRaw() ) - SOFTMAX //K-Class Classification Layer - - }; - Layer(qint32 id_layer, NeuralNetwork* net, QMap & parameters); - virtual ~Layer() = default; - /** - * @brief extend - * @param count - * @param randomize - */ - void extend(quint32 count, bool randomize); - - qint32 getId() const; - - void initLayer(); - - void clean(); - - void trigger(); - - void connectComplete(Layer* next); - - std::vector output(); - - void randomizeAllWeights(double abs_value); - void mutateAllWeights(double mutationChance, double maxWeight, double mutationRate = 0.1f); - void setAccumulated(qint32 neuron, double value); - QString toString(); - - const QMap& getParameters() const; - - LayerType getType() const; - - Neuron::ActivationFunction getActivation() const; - - NeuralNetwork* getNet() const { return m_net; } - - Layer *getPreviousLayer() const; - void setPreviousLayer(Layer *previousLayer); - /** - * @brief getNeuron - * @param id - * @return - */ - Neuron* getNeuron(qint32 id); - /** - * @brief serialize stores the object - * @param pStream - */ - virtual void serializeObject(QDataStream& pStream) const override; - /** - * @brief deserialize restores the object - * @param pStream - */ - virtual void deserializeObject(QDataStream& pStream) override; - /** - * @brief getVersion version of the file - * @return - */ - virtual qint32 getVersion() const override - { - return 1; - } -private: - NeuralNetwork* m_net{nullptr}; - Layer* m_previousLayer{nullptr}; - qint32 m_id_layer; - std::vector m_neurons; - LayerType m_type; - Neuron::ActivationFunction m_activation; - QMap m_parameters; -}; - -#endif // LAYER_H diff --git a/ai/neuralnetwork/neural/neuralnetwork.cpp b/ai/neuralnetwork/neural/neuralnetwork.cpp deleted file mode 100644 index 67f9c2e07..000000000 --- a/ai/neuralnetwork/neural/neuralnetwork.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include "ai/neuralnetwork/neural/neuralnetwork.h" -#include "ai/neuralnetwork/neural/layer.h" - -#include "coreengine/memorymanagement.h" -#include "coreengine/interpreter.h" - -NeuralNetwork::NeuralNetwork(double maxWeight) - : m_maxWeight(maxWeight) -{ - Interpreter::setCppOwnerShip(this); -} - -void NeuralNetwork::autogenerate(bool randomize) -{ - connectComplete(); - if(randomize) - { - randomizeAllWeights(); - } -} - -void NeuralNetwork::addLayer(QMap & parameters) -{ - m_configuration.append(parameters); - m_layers.push_back(MemoryManagement::create(m_layers.size(), this, parameters)); -} - -void NeuralNetwork::extend(quint32 count, bool randomize) -{ - for(qint32 i = 0; i < m_layers.size(); ++i) - { - m_configuration[i][Layer::LAYER_PARAMETER_SIZE] += count; - m_layers[i]->extend(count, randomize); - } -} - -void NeuralNetwork::clean() -{ - for(spLayer & l : m_layers) - { - l->clean(); - } -} - -void NeuralNetwork::setInput(const std::vector & in) -{ - clean(); - for(qint32 i = 0; i < in.size(); ++i) - { - double value = in[i]; - m_layers[0]->setAccumulated(i, value); - } -} - -void NeuralNetwork::trigger() -{ - for (spLayer & l : m_layers) - { - l->trigger(); - } -} - -std::vector NeuralNetwork::output() -{ - auto ret = (m_layers.back())->output(); - if (m_disabled) - { - for (auto & item : ret) - { - item = 0; - } - } - return ret; -} - -double NeuralNetwork::output(qint32 index) -{ - if (m_disabled) - { - return 0; - } - else - { - return m_layers.back()->output()[index]; - } - -} - -void NeuralNetwork::connectComplete() -{ - for(qint32 i_layer = 0; i_layer < m_layers.size() - 1; ++i_layer) - { - m_layers[i_layer]->connectComplete(m_layers[i_layer + 1].get()); - } -} - -void NeuralNetwork::randomizeAllWeights() -{ - for(qint32 i_layer = 0; i_layer < m_layers.size() - 1; ++i_layer) - { - m_layers[i_layer]->randomizeAllWeights(m_maxWeight); - } -} - -bool NeuralNetwork::getDisabled() const -{ - return m_disabled; -} - -void NeuralNetwork::setDisabled(bool newDisabled) -{ - m_disabled = newDisabled; -} - -void NeuralNetwork::mutateAllWeights(double mutationChance, double mutationRate) -{ - for(qint32 i_layer = 0; i_layer < m_layers.size() - 1; ++i_layer) - { - m_layers[i_layer]->mutateAllWeights(mutationChance, m_maxWeight, mutationRate); - } -} - -const QString &NeuralNetwork::getNetworkName() const -{ - return m_networkName; -} - -void NeuralNetwork::setNetworkName(const QString &newNetworkName) -{ - m_networkName = newNetworkName; -} - -QString NeuralNetwork::toString() -{ - QString s = "NeuralNetwork"; - s.push_back('\n'); - for(spLayer & l : m_layers) - { - s += l->toString(); - } - return s; -} -std::vector NeuralNetwork::predict(const std::vector& in) -{ - setInput(in); - trigger(); - return output(); -} - -void NeuralNetwork::serializeObject(QDataStream& pStream) const -{ - pStream << getVersion(); - pStream << m_fitness; - pStream << m_configuration; - pStream << m_maxWeight; - pStream << static_cast(m_layers.size()); - for (const auto & layer : m_layers) - { - layer->serializeObject(pStream); - } -} - -void NeuralNetwork::deserializeObject(QDataStream& pStream) -{ - qint32 version = 0; - pStream >> version; - pStream >> m_fitness; - pStream >> m_configuration; - pStream >> m_maxWeight; - qint32 size = 0; - pStream >> size; - for (qint32 i = 0; i < size; ++i) - { - spLayer layer = MemoryManagement::create(i, this, m_configuration[i]); - if (i > 0) - { - layer->setPreviousLayer(m_layers[i - 1].get()); - } - layer->deserializeObject(pStream); - m_layers.push_back(layer); - } -} diff --git a/ai/neuralnetwork/neural/neuralnetwork.h b/ai/neuralnetwork/neural/neuralnetwork.h deleted file mode 100644 index b6fbb43bd..000000000 --- a/ai/neuralnetwork/neural/neuralnetwork.h +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef NEURALNETWORK_H -#define NEURALNETWORK_H - -#include -#include -#include -#include - -#include "ai/neuralnetwork/neural/layer.h" - -class NeuralNetwork; -class Neuron; -class Layer; -using spNeuralNetwork = std::shared_ptr; - -class NeuralNetwork final : public QObject, public FileSerializable -{ - Q_OBJECT -public: - - NeuralNetwork(double maxWeight = 1.0); - ~NeuralNetwork()= default; - /** - * @brief serialize stores the object - * @param pStream - */ - virtual void serializeObject(QDataStream& pStream) const override; - /** - * @brief deserialize restores the object - * @param pStream - */ - virtual void deserializeObject(QDataStream& pStream) override; - /** - * @brief getVersion version of the file - * @return - */ - virtual qint32 getVersion() const override - { - return 1; - } - /** - * @brief addLayer adds a layer to the neural network - * @param parameters - * TYPE (Standard=0, OUTPUT=1, INPUT=2) - * SIZE - * ACTIVATION (LINEAR=0, SIGMOID=1, RELU=2) - */ - void addLayer(QMap & parameters); - /** - * @brief extend - * @param count - * @param randomize - */ - void extend(quint32 count, bool randomize); - /** - * @brief autogenerate connects all neurons and can be used to randomize all weights - * @param randomize - */ - void autogenerate(bool randomize = true); - /** - * @brief predicts the score for the input calls - * @param in - * @return - */ - std::vector predict(const std::vector& in); - /** - * @brief output - * @return the last predicted output - */ - std::vector output(); - /** - * @brief NeuralNetwork::output - * @param index - * @return - */ - double output(qint32 index); - /** - * @brief toString for debugging - * @return - */ - QString toString(); - /** - * @brief getNetworkName - * @return - */ - const QString &getNetworkName() const; - /** - * @brief setNetworkName - * @param newNetworkName - */ - void setNetworkName(const QString &newNetworkName); - /** - * @brief mutateNeuralNetwork - * @param mutationChance - */ - void mutateAllWeights(double mutationChance, double mutationRate = 0.1f); - qint32 getInputSize() - { - return m_configuration[0][Layer::LAYER_PARAMETER_SIZE]; - } - bool getDisabled() const; - void setDisabled(bool newDisabled); - -private: - void setInput(const std::vector & in); - void trigger(); - void clean(); - void connectComplete(); - void randomizeAllWeights(); -private: - std::vector m_layers; - double m_fitness; - QVector> m_configuration; - double m_maxWeight = 1; - QString m_networkName; - bool m_disabled{false}; -}; - -Q_DECLARE_INTERFACE(NeuralNetwork, "NeuralNetwork"); - -#endif // NEURALNETWORK_H diff --git a/ai/neuralnetwork/neural/neuron.cpp b/ai/neuralnetwork/neural/neuron.cpp deleted file mode 100644 index 429677440..000000000 --- a/ai/neuralnetwork/neural/neuron.cpp +++ /dev/null @@ -1,259 +0,0 @@ -#include "3rd_party/oxygine-framework/oxygine/core/oxygine.h" - -#include "ai/neuralnetwork/neural/neuron.h" -#include "ai/neuralnetwork/neural/layer.h" -#include "coreengine/memorymanagement.h" -#include "coreengine/globalutils.h" - -Neuron::Neuron(qint32 id_neuron, Layer* layer, ActivationFunction function, bool is_bias) - : m_layer(layer), - m_id_neuron(id_neuron), - m_activation_function(function), - m_is_bias(is_bias) -{ - if(m_layer->getType() == Layer::LayerType::INPUT) - { - m_activation_function = ActivationFunction::LINEAR; - } - else if (m_layer->getType() == Layer::LayerType::OUTPUT) - { - m_activation_function = ActivationFunction::LINEAR; - } - else if (m_is_bias) - { - m_activation_function = ActivationFunction::Bias; - } -} - -void Neuron::trigger() -{ - double value = output(); - for (spEdge & e : m_next) - { - e->propagate(value); - } -} - -double Neuron::output() -{ - double ret = 0; - switch (m_activation_function) - { - case ActivationFunction::Bias: - { - ret = 1; - break; - } - case ActivationFunction::Limited: - { - if (m_accumulated > 1) - { - ret = 1; - } - else if (m_accumulated < -1) - { - ret = -1; - } - else - { - ret = m_accumulated; - } - break; - } - case ActivationFunction::LINEAR: - { - ret = m_accumulated; - break; - } - case ActivationFunction::RELU: - { - ret = GlobalUtils::relu(m_accumulated); - break; - } - case ActivationFunction::SIGMOID: - { - ret = GlobalUtils::sigmoid(m_accumulated); - break; - } - case ActivationFunction::Step: - { - if (m_accumulated > 0) - { - ret = 1; - } - else - { - ret = -1; - } - break; - } - default: - { - oxygine::handleErrorPolicy(oxygine::error_policy::ep_show_error, "Neuron::output invalid output function defined"); - } - } - return ret; -} - -void Neuron::clean() -{ - m_accumulated = 0; -} - -void Neuron::addAccumulated(double v) -{ - m_accumulated += v; -} - -void Neuron::addNext(spNeuron & n, bool random) -{ - if (random) - { - m_next.push_back(MemoryManagement::create(n.get(), this, GlobalUtils::randDouble(-1, 1))); - } - else - { - m_next.push_back(MemoryManagement::create(n.get(), this, 0)); - } - n->addPrevious(m_next.back()); -} - -void Neuron::addNext(spEdge & e) -{ - m_next.push_back(e); -} - -void Neuron::addPrevious(spEdge & e) -{ - m_previous.push_back(e); -} - -int Neuron::getNeuronId() const -{ - return m_id_neuron; -} - -void Neuron::setAccumulated(double v) -{ - - m_accumulated = v; -} - -void Neuron::randomizeAllWeights(double abs_value) -{ - for(spEdge & e : m_next) - { - e->alterWeight(GlobalUtils::randDouble(-abs_value, abs_value)); - } -} - -void Neuron::mutateAllWeights(double mutationChance, double maxWeight, double mutationRate) -{ - for(spEdge & e : m_next) - { - if (GlobalUtils::randDouble(0, 1) < mutationChance) - { - float value = e->weight(); - if (qAbs(value) <= 0.005f) - { - qint32 rand = GlobalUtils::randInt(-1, 1); - if (rand == 0) - { - e->alterWeight(0.0f); - } - else if (rand > 0) - { - e->alterWeight(0.0075f); - } - else if (rand < 0) - { - e->alterWeight(-0.0075f); - } - } - else - { - qint32 rand = GlobalUtils::randInt(0, 1); - if (rand == 0) - { - e->alterWeight(value + value * mutationRate); - } - else - { - e->alterWeight(value - value * mutationRate); - } - } - } - if (e->weight() < -maxWeight) - { - e->alterWeight(-maxWeight); - } - else if (e->weight() > maxWeight) - { - e->alterWeight(maxWeight); - } - } -} - -QString Neuron::toString() -{ - QString weights; - for(spEdge & e : m_next) - { - weights.append(QString::number(e->weight()) + ","); - } - QString str = "[" + QString::number(m_layer->getId()) + "," + QString::number(m_id_neuron) + "]" + "("+ weights +")"; - return str; -} - -bool Neuron::isBias() const -{ - return m_is_bias; -} - -void Neuron::serializeObject(QDataStream& pStream) const -{ - pStream << getVersion(); - pStream << m_id_neuron; - pStream << m_accumulated; - pStream << m_threshold; - pStream << m_activation_function; - pStream << m_is_bias; - // store the connection to the previous layer - pStream << static_cast(m_previous.size()); - for (const auto & previous : m_previous) - { - pStream << previous->previousNeuron()->getNeuronId(); - previous->serializeObject(pStream); - } -} - -void Neuron::deserializeObject(QDataStream& pStream) -{ - qint32 version = 0; - pStream >> version; - pStream >> m_id_neuron; - pStream >> m_accumulated; - pStream >> m_threshold; - pStream >> m_activation_function; - pStream >> m_is_bias; - qint32 size = 0; - pStream >> size; - for (qint32 i = 0; i < size; ++i) - { - // read the connection to the previous layer - qint32 id = 0; - pStream >> id; - Neuron* previous = nullptr; - if (m_layer->getPreviousLayer() != nullptr) - { - previous = m_layer->getPreviousLayer()->getNeuron(id); - } - spEdge pEdge = MemoryManagement::create(this, previous, 0); - pEdge->deserializeObject(pStream); - m_previous.push_back(pEdge); - if (previous != nullptr) - { - previous->addNext(pEdge); - } - } -} diff --git a/ai/neuralnetwork/neural/neuron.h b/ai/neuralnetwork/neural/neuron.h deleted file mode 100644 index c63526786..000000000 --- a/ai/neuralnetwork/neural/neuron.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef NEURON_H -#define NEURON_H - -#include - -#include "ai/neuralnetwork/neural/edge.h" - -#include "coreengine/fileserializable.h" - -class Layer; - -class Neuron; -using spNeuron = std::shared_ptr; - -class Neuron final : public FileSerializable -{ -public: - static constexpr const char* const getTypeName() - { - return "Neuron"; - } - - enum class ActivationFunction - { - LINEAR, - SIGMOID, - RELU, - Step, - Limited, - Bias, - }; - - Neuron(qint32 id_neuron, Layer* layer, ActivationFunction function = ActivationFunction::LINEAR, bool is_bias = false); - ~Neuron() = default; - - void trigger(); - double output(); - void clean(); - void addAccumulated(double v); - void addNext(spNeuron & n, bool random = true); - void addNext(spEdge & e); - void addPrevious(spEdge & e); - qint32 getNeuronId() const; - - void setAccumulated(double v); - - void randomizeAllWeights(double abs_value); - void mutateAllWeights(double mutationChance, double maxWeight, double mutationRate = 0.1f); - - QString toString(); - - bool isBias() const; - const Layer* getLayer() const - { - return m_layer; - } - /** - * @brief serialize stores the object - * @param pStream - */ - virtual void serializeObject(QDataStream& pStream) const override; - /** - * @brief deserialize restores the object - * @param pStream - */ - virtual void deserializeObject(QDataStream& pStream) override; - /** - * @brief getVersion version of the file - * @return - */ - virtual qint32 getVersion() const override - { - return 1; - } -private: - -private: - Layer* m_layer = nullptr; - qint32 m_id_neuron = 0; - double m_accumulated = 0.0; - - double m_threshold = 0.0; - std::vector m_next; - std::vector m_previous; - ActivationFunction m_activation_function; - bool m_is_bias = false; -}; - -#endif // NEURON_H diff --git a/coreengine/metatyperegister.cpp b/coreengine/metatyperegister.cpp index 5988c75f9..b28f9d0d1 100644 --- a/coreengine/metatyperegister.cpp +++ b/coreengine/metatyperegister.cpp @@ -11,7 +11,7 @@ #include "coreengine/audiomanager.h" #include "ai/productionSystem/simpleproductionsystem.h" -#include "ai/heavyai.h" +#include "ai/heavyai/heavyai.h" #include "ai/normalai.h" #include "ai/influencefrontmap.h" @@ -197,7 +197,6 @@ void MetaTypeRegister::registerInterfaceData() qmlRegisterInterface("InfluenceInfo", 1); qmlRegisterInterface("NormalAi", 1); - qmlRegisterInterface("NeuralNetwork", 1); qmlRegisterInterface("NetworkGame", 1); qmlRegisterInterface("PlayerSelection", 1); qmlRegisterInterface("COSpriteManager", 1); diff --git a/game/gamemap.cpp b/game/gamemap.cpp index c7592e67d..67705ca10 100644 --- a/game/gamemap.cpp +++ b/game/gamemap.cpp @@ -1769,10 +1769,10 @@ void GameMap::deserializer(QDataStream& pStream, bool fast) pStream >> m_recordFile; pStream >> m_replayActionCount; } - if (m_headerInfo.m_Version > 13) - { - m_Variables.deserializeObject(pStream); - } + } + if (m_headerInfo.m_Version > 13) + { + m_Variables.deserializeObject(pStream); } if (showLoadingScreen) { diff --git a/gameinput/basegameinputif.cpp b/gameinput/basegameinputif.cpp index 7d1df4777..9674e0b25 100644 --- a/gameinput/basegameinputif.cpp +++ b/gameinput/basegameinputif.cpp @@ -6,7 +6,7 @@ #include "ai/veryeasyai.h" #include "ai/proxyai.h" #include "ai/normalai.h" -#include "ai/heavyai.h" +#include "ai/heavyai/heavyai.h" #include "ai/dummyai.h" #include "game/gamemap.h"