2024-07-27 02:11:26 +02:00
/*
* GameStatistics . 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 "GameStatistics.h"
2024-08-01 21:30:53 +02:00
# include "../CPlayerState.h"
# include "../constants/StringConstants.h"
2024-08-12 20:14:36 +02:00
# include "../VCMIDirs.h"
2024-08-01 21:30:53 +02:00
# include "CGameState.h"
2024-08-01 23:21:41 +02:00
# include "TerrainHandler.h"
2024-08-01 23:56:06 +02:00
# include "StartInfo.h"
2024-08-02 19:37:46 +02:00
# include "HighScore.h"
2024-08-01 22:36:32 +02:00
# include "../mapObjects/CGHeroInstance.h"
# include "../mapObjects/CGTownInstance.h"
# include "../mapObjects/CGObjectInstance.h"
# include "../mapObjects/MiscObjects.h"
# include "../mapping/CMap.h"
2024-08-03 19:53:05 +02:00
# include "../entities/building/CBuilding.h"
2025-07-05 13:51:27 +02:00
# include "../serializer/JsonDeserializer.h"
# include "../serializer/JsonUpdater.h"
2025-09-14 15:29:14 +02:00
# include "../entities/ResourceTypeHandler.h"
2024-08-03 19:53:05 +02:00
2024-07-27 02:11:26 +02:00
VCMI_LIB_NAMESPACE_BEGIN
void StatisticDataSet : : add ( StatisticDataSetEntry entry )
{
data . push_back ( entry ) ;
}
2025-05-20 21:40:05 +03:00
StatisticDataSetEntry StatisticDataSet : : createEntry ( const PlayerState * ps , const CGameState * gs , const StatisticDataSet & accumulatedData )
2024-08-01 21:30:53 +02:00
{
StatisticDataSetEntry data ;
2024-08-02 19:37:46 +02:00
HighScoreParameter param = HighScore : : prepareHighScores ( gs , ps - > color , false ) ;
HighScoreCalculation scenarioHighScores ;
scenarioHighScores . parameters . push_back ( param ) ;
scenarioHighScores . isCampaign = false ;
2025-03-03 15:19:38 +00:00
data . map = gs - > getMap ( ) . name . toString ( ) ;
2024-08-12 18:26:30 +00:00
data . timestamp = std : : time ( nullptr ) ;
2024-08-01 21:30:53 +02:00
data . day = gs - > getDate ( Date : : DAY ) ;
data . player = ps - > color ;
2024-08-14 02:49:54 +02:00
data . playerName = gs - > getStartInfo ( ) - > playerInfos . at ( ps - > color ) . name ;
2024-08-01 21:30:53 +02:00
data . team = ps - > team ;
2024-08-01 23:21:41 +02:00
data . isHuman = ps - > isHuman ( ) ;
data . status = ps - > status ;
2024-08-01 21:30:53 +02:00
data . resources = ps - > resources ;
2024-08-24 22:21:26 +00:00
data . numberHeroes = ps - > getHeroes ( ) . size ( ) ;
2024-08-02 19:37:46 +02:00
data . numberTowns = gs - > howManyTowns ( ps - > color ) ;
2024-08-01 22:36:32 +02:00
data . numberArtifacts = Statistic : : getNumberOfArts ( ps ) ;
2024-08-25 15:04:44 +00:00
data . numberDwellings = Statistic : : getNumberOfDwellings ( ps ) ;
2024-08-02 01:18:39 +02:00
data . armyStrength = Statistic : : getArmyStrength ( ps , true ) ;
2024-08-03 18:48:45 +02:00
data . totalExperience = Statistic : : getTotalExperience ( ps ) ;
2024-08-01 23:56:06 +02:00
data . income = Statistic : : getIncome ( gs , ps ) ;
2024-08-03 17:55:43 +02:00
data . mapExploredRatio = Statistic : : getMapExploredRatio ( gs , ps - > color ) ;
data . obeliskVisitedRatio = Statistic : : getObeliskVisitedRatio ( gs , ps - > team ) ;
2024-08-03 19:53:05 +02:00
data . townBuiltRatio = Statistic : : getTownBuiltRatio ( ps ) ;
data . hasGrail = param . hasGrail ;
2024-08-02 01:18:39 +02:00
data . numMines = Statistic : : getNumMines ( gs , ps ) ;
2024-08-02 19:37:46 +02:00
data . score = scenarioHighScores . calculate ( ) . total ;
2024-08-02 20:40:24 +02:00
data . maxHeroLevel = Statistic : : findBestHero ( gs , ps - > color ) ? Statistic : : findBestHero ( gs , ps - > color ) - > level : 0 ;
2025-05-20 21:40:05 +03:00
data . numBattlesNeutral = accumulatedData . accumulatedValues . count ( ps - > color ) ? accumulatedData . accumulatedValues . at ( ps - > color ) . numBattlesNeutral : 0 ;
data . numBattlesPlayer = accumulatedData . accumulatedValues . count ( ps - > color ) ? accumulatedData . accumulatedValues . at ( ps - > color ) . numBattlesPlayer : 0 ;
data . numWinBattlesNeutral = accumulatedData . accumulatedValues . count ( ps - > color ) ? accumulatedData . accumulatedValues . at ( ps - > color ) . numWinBattlesNeutral : 0 ;
data . numWinBattlesPlayer = accumulatedData . accumulatedValues . count ( ps - > color ) ? accumulatedData . accumulatedValues . at ( ps - > color ) . numWinBattlesPlayer : 0 ;
data . numHeroSurrendered = accumulatedData . accumulatedValues . count ( ps - > color ) ? accumulatedData . accumulatedValues . at ( ps - > color ) . numHeroSurrendered : 0 ;
data . numHeroEscaped = accumulatedData . accumulatedValues . count ( ps - > color ) ? accumulatedData . accumulatedValues . at ( ps - > color ) . numHeroEscaped : 0 ;
data . spentResourcesForArmy = accumulatedData . accumulatedValues . count ( ps - > color ) ? accumulatedData . accumulatedValues . at ( ps - > color ) . spentResourcesForArmy : TResources ( ) ;
data . spentResourcesForBuildings = accumulatedData . accumulatedValues . count ( ps - > color ) ? accumulatedData . accumulatedValues . at ( ps - > color ) . spentResourcesForBuildings : TResources ( ) ;
data . tradeVolume = accumulatedData . accumulatedValues . count ( ps - > color ) ? accumulatedData . accumulatedValues . at ( ps - > color ) . tradeVolume : TResources ( ) ;
data . eventCapturedTown = accumulatedData . accumulatedValues . count ( ps - > color ) ? accumulatedData . accumulatedValues . at ( ps - > color ) . lastCapturedTownDay = = gs - > getDate ( Date : : DAY ) : false ;
data . eventDefeatedStrongestHero = accumulatedData . accumulatedValues . count ( ps - > color ) ? accumulatedData . accumulatedValues . at ( ps - > color ) . lastDefeatedStrongestHeroDay = = gs - > getDate ( Date : : DAY ) : false ;
data . movementPointsUsed = accumulatedData . accumulatedValues . count ( ps - > color ) ? accumulatedData . accumulatedValues . at ( ps - > color ) . movementPointsUsed : 0 ;
2024-08-01 21:30:53 +02:00
return data ;
}
2025-07-05 13:51:27 +02:00
void StatisticDataSetEntry : : serializeJson ( JsonSerializeFormat & handler )
{
handler . serializeString ( " map " , map ) ;
handler . serializeInt ( " timestamp " , timestamp ) ;
handler . serializeInt ( " day " , day ) ;
handler . serializeId ( " player " , player , PlayerColor : : CANNOT_DETERMINE ) ;
handler . serializeString ( " playerName " , playerName ) ;
2025-07-05 15:02:16 +02:00
handler . serializeInt ( " team " , team ) ;
2025-07-05 13:51:27 +02:00
handler . serializeBool ( " isHuman " , isHuman ) ;
2025-07-05 15:02:16 +02:00
handler . serializeEnum ( " status " , status , { " ingame " , " loser " , " winner " } ) ;
2025-07-05 13:51:27 +02:00
resources . serializeJson ( handler , " resources " ) ;
handler . serializeInt ( " numberHeroes " , numberHeroes ) ;
handler . serializeInt ( " numberTowns " , numberTowns ) ;
handler . serializeInt ( " numberArtifacts " , numberArtifacts ) ;
handler . serializeInt ( " numberDwellings " , numberDwellings ) ;
handler . serializeInt ( " armyStrength " , armyStrength ) ;
handler . serializeInt ( " totalExperience " , totalExperience ) ;
handler . serializeInt ( " income " , income ) ;
handler . serializeFloat ( " mapExploredRatio " , mapExploredRatio ) ;
handler . serializeFloat ( " obeliskVisitedRatio " , obeliskVisitedRatio ) ;
handler . serializeFloat ( " townBuiltRatio " , townBuiltRatio ) ;
handler . serializeBool ( " hasGrail " , hasGrail ) ;
{
auto zonesData = handler . enterStruct ( " numMines " ) ;
2025-09-14 21:12:51 +02:00
for ( auto & idx : LIBRARY - > resourceTypeHandler - > getAllObjects ( ) )
2025-09-16 01:52:47 +02:00
handler . serializeInt ( idx . toResource ( ) - > getJsonKey ( ) , numMines [ idx ] , 0 ) ;
2025-07-05 13:51:27 +02:00
}
handler . serializeInt ( " score " , score ) ;
handler . serializeInt ( " maxHeroLevel " , maxHeroLevel ) ;
handler . serializeInt ( " numBattlesNeutral " , numBattlesNeutral ) ;
handler . serializeInt ( " numBattlesPlayer " , numBattlesPlayer ) ;
handler . serializeInt ( " numWinBattlesNeutral " , numWinBattlesNeutral ) ;
handler . serializeInt ( " numWinBattlesPlayer " , numWinBattlesPlayer ) ;
handler . serializeInt ( " numHeroSurrendered " , numHeroSurrendered ) ;
handler . serializeInt ( " numHeroEscaped " , numHeroEscaped ) ;
spentResourcesForArmy . serializeJson ( handler , " spentResourcesForArmy " ) ;
spentResourcesForBuildings . serializeJson ( handler , " spentResourcesForBuildings " ) ;
tradeVolume . serializeJson ( handler , " tradeVolume " ) ;
handler . serializeBool ( " eventCapturedTown " , eventCapturedTown ) ;
handler . serializeBool ( " eventDefeatedStrongestHero " , eventDefeatedStrongestHero ) ;
handler . serializeInt ( " movementPointsUsed " , movementPointsUsed ) ;
}
void StatisticDataSet : : PlayerAccumulatedValueStorage : : serializeJson ( JsonSerializeFormat & handler )
{
handler . serializeInt ( " numBattlesNeutral " , numBattlesNeutral ) ;
handler . serializeInt ( " numBattlesPlayer " , numBattlesPlayer ) ;
handler . serializeInt ( " numWinBattlesNeutral " , numWinBattlesNeutral ) ;
handler . serializeInt ( " numWinBattlesPlayer " , numWinBattlesPlayer ) ;
handler . serializeInt ( " numHeroSurrendered " , numHeroSurrendered ) ;
handler . serializeInt ( " numHeroEscaped " , numHeroEscaped ) ;
spentResourcesForArmy . serializeJson ( handler , " spentResourcesForArmy " ) ;
spentResourcesForBuildings . serializeJson ( handler , " spentResourcesForBuildings " ) ;
tradeVolume . serializeJson ( handler , " tradeVolume " ) ;
handler . serializeInt ( " movementPointsUsed " , movementPointsUsed ) ;
handler . serializeInt ( " lastCapturedTownDay " , lastCapturedTownDay ) ;
handler . serializeInt ( " lastDefeatedStrongestHeroDay " , lastDefeatedStrongestHeroDay ) ;
}
void StatisticDataSet : : serializeJson ( JsonSerializeFormat & handler )
{
{
auto eventsHandler = handler . enterArray ( " data " ) ;
eventsHandler . syncSize ( data , JsonNode : : JsonType : : DATA_VECTOR ) ;
eventsHandler . serializeStruct ( data ) ;
}
{
auto eventsHandler = handler . enterStruct ( " accumulatedValues " ) ;
for ( auto & val : accumulatedValues )
eventsHandler - > serializeStruct ( GameConstants : : PLAYER_COLOR_NAMES [ val . first ] , val . second ) ;
}
}
2025-05-20 20:38:13 +03:00
std : : string StatisticDataSet : : toCsv ( std : : string sep ) const
2024-07-27 02:11:26 +02:00
{
std : : stringstream ss ;
2025-09-16 10:34:38 +02:00
auto resources = std : : vector < EGameResID > { EGameResID : : GOLD , EGameResID : : WOOD , EGameResID : : MERCURY , EGameResID : : ORE , EGameResID : : SULFUR , EGameResID : : CRYSTAL , EGameResID : : GEMS } ; //todo: configurable resource support
2024-08-01 21:30:53 +02:00
2024-08-14 21:01:37 +02:00
ss < < " Map " < < sep ;
ss < < " Timestamp " < < sep ;
ss < < " Day " < < sep ;
ss < < " Player " < < sep ;
ss < < " PlayerName " < < sep ;
ss < < " Team " < < sep ;
ss < < " IsHuman " < < sep ;
ss < < " Status " < < sep ;
ss < < " NumberHeroes " < < sep ;
ss < < " NumberTowns " < < sep ;
ss < < " NumberArtifacts " < < sep ;
ss < < " NumberDwellings " < < sep ;
ss < < " ArmyStrength " < < sep ;
ss < < " TotalExperience " < < sep ;
ss < < " Income " < < sep ;
ss < < " MapExploredRatio " < < sep ;
ss < < " ObeliskVisitedRatio " < < sep ;
ss < < " TownBuiltRatio " < < sep ;
ss < < " HasGrail " < < sep ;
ss < < " Score " < < sep ;
ss < < " MaxHeroLevel " < < sep ;
ss < < " NumBattlesNeutral " < < sep ;
ss < < " NumBattlesPlayer " < < sep ;
ss < < " NumWinBattlesNeutral " < < sep ;
ss < < " NumWinBattlesPlayer " < < sep ;
ss < < " NumHeroSurrendered " < < sep ;
ss < < " NumHeroEscaped " < < sep ;
ss < < " EventCapturedTown " < < sep ;
ss < < " EventDefeatedStrongestHero " < < sep ;
2024-08-03 20:47:20 +02:00
ss < < " MovementPointsUsed " ;
2024-08-01 21:30:53 +02:00
for ( auto & resource : resources )
2025-09-15 00:08:18 +02:00
ss < < sep < < resource . toResource ( ) - > getJsonKey ( ) ;
2024-08-02 01:18:39 +02:00
for ( auto & resource : resources )
2025-09-15 00:08:18 +02:00
ss < < sep < < resource . toResource ( ) - > getJsonKey ( ) + " Mines " ;
2024-08-03 19:53:05 +02:00
for ( auto & resource : resources )
2025-09-15 00:08:18 +02:00
ss < < sep < < resource . toResource ( ) - > getJsonKey ( ) + " SpentResourcesForArmy " ;
2024-08-03 19:53:05 +02:00
for ( auto & resource : resources )
2025-09-15 00:08:18 +02:00
ss < < sep < < resource . toResource ( ) - > getJsonKey ( ) + " SpentResourcesForBuildings " ;
2024-08-03 19:53:05 +02:00
for ( auto & resource : resources )
2025-09-15 00:08:18 +02:00
ss < < sep < < resource . toResource ( ) - > getJsonKey ( ) + " TradeVolume " ;
2024-08-01 21:30:53 +02:00
ss < < " \r \n " ;
2024-07-27 02:11:26 +02:00
for ( auto & entry : data )
{
2024-08-14 21:01:37 +02:00
ss < < entry . map < < sep ;
ss < < vstd : : getFormattedDateTime ( entry . timestamp , " %Y-%m-%dT%H:%M:%S " ) < < sep ;
ss < < entry . day < < sep ;
ss < < GameConstants : : PLAYER_COLOR_NAMES [ entry . player ] < < sep ;
ss < < entry . playerName < < sep ;
ss < < entry . team . getNum ( ) < < sep ;
ss < < entry . isHuman < < sep ;
ss < < static_cast < int > ( entry . status ) < < sep ;
ss < < entry . numberHeroes < < sep ;
ss < < entry . numberTowns < < sep ;
ss < < entry . numberArtifacts < < sep ;
ss < < entry . numberDwellings < < sep ;
ss < < entry . armyStrength < < sep ;
ss < < entry . totalExperience < < sep ;
ss < < entry . income < < sep ;
ss < < entry . mapExploredRatio < < sep ;
ss < < entry . obeliskVisitedRatio < < sep ;
ss < < entry . townBuiltRatio < < sep ;
ss < < entry . hasGrail < < sep ;
ss < < entry . score < < sep ;
ss < < entry . maxHeroLevel < < sep ;
ss < < entry . numBattlesNeutral < < sep ;
ss < < entry . numBattlesPlayer < < sep ;
ss < < entry . numWinBattlesNeutral < < sep ;
ss < < entry . numWinBattlesPlayer < < sep ;
ss < < entry . numHeroSurrendered < < sep ;
ss < < entry . numHeroEscaped < < sep ;
ss < < entry . eventCapturedTown < < sep ;
ss < < entry . eventDefeatedStrongestHero < < sep ;
2024-08-03 20:47:20 +02:00
ss < < entry . movementPointsUsed ;
2024-08-01 21:30:53 +02:00
for ( auto & resource : resources )
2024-08-14 21:01:37 +02:00
ss < < sep < < entry . resources [ resource ] ;
2024-08-02 01:18:39 +02:00
for ( auto & resource : resources )
2025-05-20 20:38:13 +03:00
ss < < sep < < entry . numMines . at ( resource ) ;
2024-08-03 19:53:05 +02:00
for ( auto & resource : resources )
2024-08-14 21:01:37 +02:00
ss < < sep < < entry . spentResourcesForArmy [ resource ] ;
2024-08-03 19:53:05 +02:00
for ( auto & resource : resources )
2024-08-14 21:01:37 +02:00
ss < < sep < < entry . spentResourcesForBuildings [ resource ] ;
2024-08-03 19:53:05 +02:00
for ( auto & resource : resources )
2024-08-14 21:01:37 +02:00
ss < < sep < < entry . tradeVolume [ resource ] ;
2024-08-01 21:30:53 +02:00
ss < < " \r \n " ;
2024-07-27 02:11:26 +02:00
}
return ss . str ( ) ;
}
2025-05-20 20:38:13 +03:00
std : : string StatisticDataSet : : writeCsv ( ) const
2024-08-12 20:14:36 +02:00
{
const boost : : filesystem : : path outPath = VCMIDirs : : get ( ) . userCachePath ( ) / " statistic " ;
boost : : filesystem : : create_directories ( outPath ) ;
const boost : : filesystem : : path filePath = outPath / ( vstd : : getDateTimeISO8601Basic ( std : : time ( nullptr ) ) + " .csv " ) ;
std : : ofstream file ( filePath . c_str ( ) ) ;
2024-08-14 21:01:37 +02:00
std : : string csv = toCsv ( " ; " ) ;
2024-08-12 20:14:36 +02:00
file < < csv ;
return filePath . string ( ) ;
}
2024-08-01 22:36:32 +02:00
//calculates total number of artifacts that belong to given player
int Statistic : : getNumberOfArts ( const PlayerState * ps )
{
int ret = 0 ;
2024-08-24 22:21:26 +00:00
for ( auto h : ps - > getHeroes ( ) )
2024-08-01 22:36:32 +02:00
{
2024-08-12 18:26:30 +00:00
ret + = h - > artifactsInBackpack . size ( ) + h - > artifactsWorn . size ( ) ;
2024-08-01 22:36:32 +02:00
}
return ret ;
}
2024-08-25 15:04:44 +00:00
int Statistic : : getNumberOfDwellings ( const PlayerState * ps )
{
int ret = 0 ;
for ( const auto * obj : ps - > getOwnedObjects ( ) )
if ( ! obj - > asOwnable ( ) - > providedCreatures ( ) . empty ( ) )
ret + = 1 ;
return ret ;
}
2024-08-01 22:36:32 +02:00
// get total strength of player army
2024-08-02 01:18:39 +02:00
si64 Statistic : : getArmyStrength ( const PlayerState * ps , bool withTownGarrison )
2024-08-01 22:36:32 +02:00
{
si64 str = 0 ;
2024-08-24 22:21:26 +00:00
for ( auto h : ps - > getHeroes ( ) )
2024-08-01 22:36:32 +02:00
{
2025-03-09 21:51:33 +00:00
if ( ! h - > isGarrisoned ( ) | | withTownGarrison ) //original h3 behavior
2024-08-01 22:36:32 +02:00
str + = h - > getArmyStrength ( ) ;
}
return str ;
}
2024-08-03 18:48:45 +02:00
// get total experience of all heroes
si64 Statistic : : getTotalExperience ( const PlayerState * ps )
{
si64 tmp = 0 ;
2024-08-24 22:21:26 +00:00
for ( auto h : ps - > getHeroes ( ) )
2024-08-03 18:48:45 +02:00
tmp + = h - > exp ;
return tmp ;
}
2024-08-01 22:36:32 +02:00
// get total gold income
2024-08-01 23:56:06 +02:00
int Statistic : : getIncome ( const CGameState * gs , const PlayerState * ps )
2024-08-01 22:36:32 +02:00
{
int totalIncome = 0 ;
//Heroes can produce gold as well - skill, specialty or arts
2025-03-13 19:42:18 +00:00
for ( const auto & object : ps - > getOwnedObjects ( ) )
totalIncome + = object - > asOwnable ( ) - > dailyIncome ( ) [ EGameResID : : GOLD ] ;
2024-08-01 22:36:32 +02:00
return totalIncome ;
}
2024-08-03 17:55:43 +02:00
float Statistic : : getMapExploredRatio ( const CGameState * gs , PlayerColor player )
2024-08-01 23:21:41 +02:00
{
2024-08-03 17:55:43 +02:00
float visible = 0.0 ;
float numTiles = 0.0 ;
2024-08-01 23:21:41 +02:00
2025-08-01 00:37:32 +02:00
for ( int layer = 0 ; layer < gs - > getMap ( ) . levels ( ) ; layer + + )
2025-03-03 15:19:38 +00:00
for ( int y = 0 ; y < gs - > getMap ( ) . height ; + + y )
for ( int x = 0 ; x < gs - > getMap ( ) . width ; + + x )
2024-08-01 23:21:41 +02:00
{
2025-03-03 15:19:38 +00:00
TerrainTile tile = gs - > getMap ( ) . getTile ( int3 ( x , y , layer ) ) ;
2024-08-01 23:21:41 +02:00
2024-07-15 07:41:53 +00:00
if ( tile . blocked ( ) & & ! tile . visitable ( ) )
2024-08-01 23:21:41 +02:00
continue ;
2025-05-14 15:50:13 +03:00
if ( gs - > isVisibleFor ( int3 ( x , y , layer ) , player ) )
2024-08-01 23:21:41 +02:00
visible + + ;
numTiles + + ;
}
2024-08-01 23:56:06 +02:00
return visible / numTiles ;
2024-08-01 23:21:41 +02:00
}
2024-08-02 19:38:33 +02:00
const CGHeroInstance * Statistic : : findBestHero ( const CGameState * gs , const PlayerColor & color )
2024-08-02 00:04:41 +02:00
{
2024-08-24 22:21:26 +00:00
const auto & h = gs - > players . at ( color ) . getHeroes ( ) ;
2024-08-02 00:04:41 +02:00
if ( h . empty ( ) )
return nullptr ;
//best hero will be that with highest exp
int best = 0 ;
for ( int b = 1 ; b < h . size ( ) ; + + b )
{
if ( h [ b ] - > exp > h [ best ] - > exp )
{
best = b ;
}
}
return h [ best ] ;
}
2024-08-03 17:55:43 +02:00
std : : vector < std : : vector < PlayerColor > > Statistic : : getRank ( std : : vector < std : : pair < PlayerColor , si64 > > stats )
2024-08-02 00:04:41 +02:00
{
2024-08-03 17:55:43 +02:00
std : : sort ( stats . begin ( ) , stats . end ( ) , [ ] ( const std : : pair < PlayerColor , si64 > & a , const std : : pair < PlayerColor , si64 > & b ) { return a . second > b . second ; } ) ;
2024-08-02 00:04:41 +02:00
//put first element
std : : vector < std : : vector < PlayerColor > > ret ;
2024-08-12 18:26:30 +00:00
ret . push_back ( { stats [ 0 ] . first } ) ;
2024-08-02 00:04:41 +02:00
//the rest of elements
for ( int g = 1 ; g < stats . size ( ) ; + + g )
{
if ( stats [ g ] . second = = stats [ g - 1 ] . second )
{
( ret . end ( ) - 1 ) - > push_back ( stats [ g ] . first ) ;
}
else
{
//create next occupied rank
2024-08-12 18:26:30 +00:00
ret . push_back ( { stats [ g ] . first } ) ;
2024-08-02 00:04:41 +02:00
}
}
return ret ;
}
2024-08-02 01:18:39 +02:00
int Statistic : : getObeliskVisited ( const CGameState * gs , const TeamID & t )
{
2025-03-03 15:19:38 +00:00
if ( gs - > getMap ( ) . obelisksVisited . count ( t ) )
return gs - > getMap ( ) . obelisksVisited . at ( t ) ;
2024-08-02 01:18:39 +02:00
else
return 0 ;
}
2024-08-03 17:55:43 +02:00
float Statistic : : getObeliskVisitedRatio ( const CGameState * gs , const TeamID & t )
2024-08-02 01:18:39 +02:00
{
2025-03-03 15:19:38 +00:00
if ( ! gs - > getMap ( ) . obeliskCount )
2024-08-03 17:55:43 +02:00
return 0 ;
2025-03-03 15:19:38 +00:00
return static_cast < float > ( getObeliskVisited ( gs , t ) ) / gs - > getMap ( ) . obeliskCount ;
2024-08-02 01:18:39 +02:00
}
std : : map < EGameResID , int > Statistic : : getNumMines ( const CGameState * gs , const PlayerState * ps )
{
std : : map < EGameResID , int > tmp ;
2025-09-14 15:29:14 +02:00
for ( auto & res : LIBRARY - > resourceTypeHandler - > getAllObjects ( ) )
2024-08-02 01:18:39 +02:00
tmp [ res ] = 0 ;
2025-03-13 19:42:18 +00:00
for ( const auto * object : ps - > getOwnedObjects ( ) )
{
//Mines
2025-04-21 18:41:01 +03:00
if ( object - > ID = = Obj : : MINE | | object - > ID = = Obj : : ABANDONED_MINE )
2025-03-13 19:42:18 +00:00
{
const auto * mine = dynamic_cast < const CGMine * > ( object ) ;
assert ( mine ) ;
tmp [ mine - > producedResource ] + + ;
}
}
2024-08-02 01:18:39 +02:00
return tmp ;
}
2024-08-03 19:53:05 +02:00
float Statistic : : getTownBuiltRatio ( const PlayerState * ps )
{
float built = 0.0 ;
float total = 0.0 ;
2024-08-24 22:21:26 +00:00
for ( const auto & t : ps - > getTowns ( ) )
2024-08-03 19:53:05 +02:00
{
2024-08-17 22:06:48 +03:00
built + = t - > getBuildings ( ) . size ( ) ;
2024-10-05 19:37:52 +00:00
for ( const auto & b : t - > getTown ( ) - > buildings )
2024-08-03 19:53:05 +02:00
if ( ! t - > forbiddenBuildings . count ( b . first ) )
total + = 1 ;
}
if ( total < 1 )
return 0 ;
return built / total ;
}
2024-07-27 02:11:26 +02:00
VCMI_LIB_NAMESPACE_END