1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-22 22:13:35 +02:00

BattleAI: retreat

This commit is contained in:
Andrii Danylchenko 2022-10-14 11:24:29 +03:00
parent e2b373a784
commit 82a9f82e1c
11 changed files with 168 additions and 4 deletions

View File

@ -17,6 +17,7 @@
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/spells/CSpellHandler.h"
#include "../../lib/spells/ISpellMechanics.h"
#include "../../lib/battle/BattleStateInfoForRetreat.h"
#include "../../lib/CStack.h" // TODO: remove
// Eventually only IBattleInfoCallback and battle::Unit should be used,
// CUnitState should be private and CStack should be removed completely
@ -728,13 +729,31 @@ void CBattleAI::print(const std::string &text) const
boost::optional<BattleAction> CBattleAI::considerFleeingOrSurrendering()
{
if(cb->battleCanSurrender(playerID))
BattleStateInfoForRetreat bs;
bs.canFlee = cb->battleCanFlee();
bs.canSurrender = cb->battleCanSurrender(playerID);
bs.ourSide = cb->battleGetMySide();
bs.ourHero = cb->battleGetMyHero();
bs.enemyHero = cb->battleGetFightingHero(!bs.ourSide);
for(auto stack : cb->battleGetAllStacks(false))
{
if(stack->alive())
{
if(stack->side == bs.ourSide)
bs.ourStacks.push_back(stack);
else
bs.enemyStacks.push_back(stack);
}
}
if(cb->battleCanFlee())
if(!bs.canFlee || !bs.canSurrender)
{
return boost::none;
}
return boost::none;
return cb->makeSurrenderRetreatDecision(bs);
}

View File

@ -19,6 +19,7 @@
#include "../../lib/serializer/CTypeList.h"
#include "../../lib/serializer/BinarySerializer.h"
#include "../../lib/serializer/BinaryDeserializer.h"
#include "../../lib/battle/BattleStateInfoForRetreat.h"
#include "AIGateway.h"
#include "Goals/Goals.h"
@ -488,6 +489,23 @@ void AIGateway::showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositio
NET_EVENT_HANDLER;
}
boost::optional<BattleAction> AIGateway::makeSurrenderRetreatDecision(
const BattleStateInfoForRetreat & battleState)
{
LOG_TRACE(logAi);
NET_EVENT_HANDLER;
double fightRatio = battleState.getOurStrength() / (double)battleState.getEnemyStrength();
if(fightRatio < 0.3 && battleState.canFlee)
{
return BattleAction::makeRetreat(battleState.ourSide);
}
return boost::none;
}
void AIGateway::init(std::shared_ptr<Environment> env, std::shared_ptr<CCallback> CB)
{
LOG_TRACE(logAi);

View File

@ -166,6 +166,7 @@ public:
void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override;
void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor) override;
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions) override;
boost::optional<BattleAction> makeSurrenderRetreatDecision(const BattleStateInfoForRetreat & battleState) override;
void battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side) override;
void battleEnd(const BattleResult * br) override;

View File

@ -387,3 +387,9 @@ bool CBattleCallback::battleMakeTacticAction( BattleAction * action )
sendRequest(&ma);
return true;
}
boost::optional<BattleAction> CBattleCallback::makeSurrenderRetreatDecision(
const BattleStateInfoForRetreat & battleState)
{
return cl->playerint[getPlayerID().get()]->makeSurrenderRetreatDecision(battleState);
}

View File

@ -32,6 +32,7 @@ struct CPackForServer;
class IBattleEventsReceiver;
class IGameEventsReceiver;
struct ArtifactLocation;
class BattleStateInfoForRetreat;
VCMI_LIB_NAMESPACE_END
@ -48,6 +49,7 @@ public:
//battle
virtual int battleMakeAction(const BattleAction * action) = 0;//for casting spells by hero - DO NOT use it for moving active stack
virtual bool battleMakeTacticAction(BattleAction * action) = 0; // performs tactic phase actions
virtual boost::optional<BattleAction> makeSurrenderRetreatDecision(const BattleStateInfoForRetreat & battleState) = 0;
};
class IGameActionCallback
@ -104,6 +106,7 @@ public:
CBattleCallback(boost::optional<PlayerColor> Player, CClient *C);
int battleMakeAction(const BattleAction * action) override;//for casting spells by hero - DO NOT use it for moving active stack
bool battleMakeTacticAction(BattleAction * action) override; // performs tactic phase actions
boost::optional<BattleAction> makeSurrenderRetreatDecision(const BattleStateInfoForRetreat & battleState) override;
#if SCRIPTING_ENABLED
scripting::Pool * getContextPool() const override;

View File

@ -12,6 +12,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/battle/BattleHex.cpp
${MAIN_LIB_DIR}/battle/BattleInfo.cpp
${MAIN_LIB_DIR}/battle/BattleProxy.cpp
${MAIN_LIB_DIR}/battle/BattleStateInfoForRetreat.cpp
${MAIN_LIB_DIR}/battle/CBattleInfoCallback.cpp
${MAIN_LIB_DIR}/battle/CBattleInfoEssentials.cpp
${MAIN_LIB_DIR}/battle/CCallbackBase.cpp
@ -241,6 +242,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/battle/BattleAttackInfo.h
${MAIN_LIB_DIR}/battle/BattleHex.h
${MAIN_LIB_DIR}/battle/BattleInfo.h
${MAIN_LIB_DIR}/battle/BattleStateInfoForRetreat.h
${MAIN_LIB_DIR}/battle/BattleProxy.h
${MAIN_LIB_DIR}/battle/CBattleInfoCallback.h
${MAIN_LIB_DIR}/battle/CBattleInfoEssentials.h

View File

@ -58,7 +58,9 @@ class CLoadFile;
class CSaveFile;
class BinaryDeserializer;
class BinarySerializer;
class BattleStateInfo;
struct ArtifactLocation;
class BattleStateInfoForRetreat;
#if SCRIPTING_ENABLED
namespace scripting
@ -93,7 +95,7 @@ public:
//pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id
virtual void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID)=0;
virtual void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID)=0;
virtual void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID)=0;
// Show a dialog, player must take decision. If selection then he has to choose between one of given components,
// if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called
@ -108,6 +110,11 @@ public:
virtual void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions){};
virtual boost::optional<BattleAction> makeSurrenderRetreatDecision(const BattleStateInfoForRetreat & battleState)
{
return boost::none;
}
virtual void saveGame(BinarySerializer & h, const int version) = 0;
virtual void loadGame(BinaryDeserializer & h, const int version) = 0;
};

View File

@ -105,6 +105,22 @@ BattleAction BattleAction::makeEndOFTacticPhase(ui8 side)
return ba;
}
BattleAction BattleAction::makeSurrender(ui8 side)
{
BattleAction ba;
ba.side = side;
ba.actionType = EActionType::SURRENDER;
return ba;
}
BattleAction BattleAction::makeRetreat(ui8 side)
{
BattleAction ba;
ba.side = side;
ba.actionType = EActionType::RETREAT;
return ba;
}
std::string BattleAction::toString() const
{
std::stringstream actionTypeStream;

View File

@ -40,6 +40,8 @@ public:
static BattleAction makeCreatureSpellcast(const battle::Unit * stack, const battle::Target & target, SpellID spellID);
static BattleAction makeMove(const battle::Unit * stack, BattleHex dest);
static BattleAction makeEndOFTacticPhase(ui8 side);
static BattleAction makeRetreat(ui8 side);
static BattleAction makeSurrender(ui8 side);
std::string toString() const;

View File

@ -0,0 +1,52 @@
/*
* BattleStateInfoForRetreat.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "BattleStateInfoForRetreat.h"
#include "Unit.h"
#include "CBattleInfoCallback.h"
#include "../CCreatureSet.h"
#include "../mapObjects/CGHeroInstance.h"
VCMI_LIB_NAMESPACE_BEGIN
BattleStateInfoForRetreat::BattleStateInfoForRetreat()
: canFlee(false), canSurrender(false), isLastTurnBeforeDie(false), ourStacks(), enemyStacks(), ourHero(nullptr), enemyHero(nullptr), ourSide(-1)
{
}
uint64_t getFightingStrength(std::vector<const battle::Unit *> stacks, const CGHeroInstance * hero = nullptr)
{
uint64_t result = 0;
for(const battle::Unit * stack : stacks)
{
result += stack->creatureId().toCreature()->AIValue * stack->getCount();
}
if(hero)
{
result = (uint64_t)(result * hero->getFightingStrength());
}
return result;
}
uint64_t BattleStateInfoForRetreat::getOurStrength() const
{
return getFightingStrength(ourStacks, ourHero);
}
uint64_t BattleStateInfoForRetreat::getEnemyStrength() const
{
return getFightingStrength(enemyStacks, enemyHero);
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,38 @@
/*
* BattleStateInfoForRetreat.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
VCMI_LIB_NAMESPACE_BEGIN
namespace battle
{
class Unit;
}
class CGHeroInstance;
class DLL_LINKAGE BattleStateInfoForRetreat
{
public:
bool canFlee;
bool canSurrender;
bool isLastTurnBeforeDie;
ui8 ourSide;
std::vector<const battle::Unit *> ourStacks;
std::vector<const battle::Unit *> enemyStacks;
const CGHeroInstance * ourHero;
const CGHeroInstance * enemyHero;
BattleStateInfoForRetreat();
uint64_t getOurStrength() const;
uint64_t getEnemyStrength() const;
};
VCMI_LIB_NAMESPACE_END