| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- /*
- * 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"
- #include "../CPlayerState.h"
- #include "../constants/StringConstants.h"
- #include "CGameState.h"
- #include "TerrainHandler.h"
- #include "CHeroHandler.h"
- #include "StartInfo.h"
- #include "../mapObjects/CGHeroInstance.h"
- #include "../mapObjects/CGTownInstance.h"
- #include "../mapObjects/CGObjectInstance.h"
- #include "../mapObjects/MiscObjects.h"
- #include "../mapping/CMap.h"
- VCMI_LIB_NAMESPACE_BEGIN
- void StatisticDataSet::add(StatisticDataSetEntry entry)
- {
- data.push_back(entry);
- }
- StatisticDataSetEntry StatisticDataSet::createEntry(const PlayerState * ps, const CGameState * gs)
- {
- StatisticDataSetEntry data;
- data.day = gs->getDate(Date::DAY);
- data.player = ps->color;
- data.team = ps->team;
- data.isHuman = ps->isHuman();
- data.status = ps->status;
- data.resources = ps->resources;
- data.numberHeroes = ps->heroes.size();
- data.numberTowns = ps->towns.size();
- data.numberArtifacts = Statistic::getNumberOfArts(ps);
- data.armyStrength = Statistic::getArmyStrength(ps, true);
- data.income = Statistic::getIncome(gs, ps);
- data.mapVisitedRatio = Statistic::getMapVisitedRatio(gs, ps->color);
- data.obeliskVisited = Statistic::getObeliskVisited(gs, ps->team);
- data.mightMagicRatio = Statistic::getMightMagicRatio(ps);
- data.numMines = Statistic::getNumMines(gs, ps);
- return data;
- }
- std::string StatisticDataSet::toCsv()
- {
- std::stringstream ss;
- auto resources = std::vector<EGameResID>{EGameResID::GOLD, EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS};
- ss << "Day" << ";";
- ss << "Player" << ";";
- ss << "Team" << ";";
- ss << "IsHuman" << ";";
- ss << "Status" << ";";
- ss << "NumberHeroes" << ";";
- ss << "NumberTowns" << ";";
- ss << "NumberArtifacts" << ";";
- ss << "ArmyStrength" << ";";
- ss << "Income" << ";";
- ss << "MapVisitedRatio" << ";";
- ss << "ObeliskVisited" << ";";
- ss << "MightMagicRatio";
- for(auto & resource : resources)
- ss << ";" << GameConstants::RESOURCE_NAMES[resource];
- for(auto & resource : resources)
- ss << ";" << GameConstants::RESOURCE_NAMES[resource] + "Mines";
- ss << "\r\n";
- for(auto & entry : data)
- {
- ss << entry.day << ";";
- ss << GameConstants::PLAYER_COLOR_NAMES[entry.player] << ";";
- ss << entry.team.getNum() << ";";
- ss << entry.isHuman << ";";
- ss << (int)entry.status << ";";
- ss << entry.numberHeroes << ";";
- ss << entry.numberTowns << ";";
- ss << entry.numberArtifacts << ";";
- ss << entry.armyStrength << ";";
- ss << entry.income << ";";
- ss << entry.mapVisitedRatio << ";";
- ss << entry.obeliskVisited << ";";
- ss << entry.mightMagicRatio;
- for(auto & resource : resources)
- ss << ";" << entry.resources[resource];
- for(auto & resource : resources)
- ss << ";" << entry.numMines[resource];
- ss << "\r\n";
- }
- return ss.str();
- }
- std::vector<const CGMine *> Statistic::getMines(const CGameState * gs, const PlayerState * ps)
- {
- std::vector<const CGMine *> tmp;
- /// FIXME: Dirty dirty hack
- /// Stats helper need some access to gamestate.
- std::vector<const CGObjectInstance *> ownedObjects;
- for(const CGObjectInstance * obj : gs->map->objects)
- {
- if(obj && obj->tempOwner == ps->color)
- ownedObjects.push_back(obj);
- }
- /// This is code from CPlayerSpecificInfoCallback::getMyObjects
- /// I'm really need to find out about callback interface design...
- for(const auto * object : ownedObjects)
- {
- //Mines
- if ( object->ID == Obj::MINE )
- {
- const auto * mine = dynamic_cast<const CGMine *>(object);
- assert(mine);
- tmp.push_back(mine);
- }
- }
- return tmp;
- }
- //calculates total number of artifacts that belong to given player
- int Statistic::getNumberOfArts(const PlayerState * ps)
- {
- int ret = 0;
- for(auto h : ps->heroes)
- {
- ret += (int)h->artifactsInBackpack.size() + (int)h->artifactsWorn.size();
- }
- return ret;
- }
- // get total strength of player army
- si64 Statistic::getArmyStrength(const PlayerState * ps, bool withTownGarrison)
- {
- si64 str = 0;
- for(auto h : ps->heroes)
- {
- if(!h->inTownGarrison || withTownGarrison) //original h3 behavior
- str += h->getArmyStrength();
- }
- return str;
- }
- // get total gold income
- int Statistic::getIncome(const CGameState * gs, const PlayerState * ps)
- {
- int percentIncome = gs->getStartInfo()->getIthPlayersSettings(ps->color).handicap.percentIncome;
- int totalIncome = 0;
- const CGObjectInstance * heroOrTown = nullptr;
- //Heroes can produce gold as well - skill, specialty or arts
- for(const auto & h : ps->heroes)
- {
- totalIncome += h->valOfBonuses(Selector::typeSubtype(BonusType::GENERATE_RESOURCE, BonusSubtypeID(GameResID(GameResID::GOLD)))) * percentIncome / 100;
- if(!heroOrTown)
- heroOrTown = h;
- }
- //Add town income of all towns
- for(const auto & t : ps->towns)
- {
- totalIncome += t->dailyIncome()[EGameResID::GOLD];
- if(!heroOrTown)
- heroOrTown = t;
- }
- for(const CGMine * mine : getMines(gs, ps))
- {
- if (mine->producedResource == EGameResID::GOLD)
- totalIncome += mine->getProducedQuantity();
- }
- return totalIncome;
- }
- double Statistic::getMapVisitedRatio(const CGameState * gs, PlayerColor player)
- {
- double visible = 0.0;
- double numTiles = 0.0;
- for(int layer = 0; layer < (gs->map->twoLevel ? 2 : 1); layer++)
- for(int y = 0; y < gs->map->height; ++y)
- for(int x = 0; x < gs->map->width; ++x)
- {
- TerrainTile tile = gs->map->getTile(int3(x, y, layer));
- if(tile.blocked && (!tile.visitable))
- continue;
- if(gs->isVisible(int3(x, y, layer), player))
- visible++;
- numTiles++;
- }
-
- return visible / numTiles;
- }
- const CGHeroInstance * Statistic::findBestHero(CGameState * gs, const PlayerColor & color)
- {
- std::vector<ConstTransitivePtr<CGHeroInstance> > &h = gs->players[color].heroes;
- 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];
- }
- std::vector<std::vector<PlayerColor>> Statistic::getRank(std::vector<TStat> stats)
- {
- std::sort(stats.begin(), stats.end(), [](const TStat & a, const TStat & b) { return a.second > b.second; });
- //put first element
- std::vector< std::vector<PlayerColor> > ret;
- std::vector<PlayerColor> tmp;
- tmp.push_back( stats[0].first );
- ret.push_back( tmp );
- //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
- std::vector<PlayerColor> tmp;
- tmp.push_back(stats[g].first);
- ret.push_back(tmp);
- }
- }
- return ret;
- }
- int Statistic::getObeliskVisited(const CGameState * gs, const TeamID & t)
- {
- if(gs->map->obelisksVisited.count(t))
- return gs->map->obelisksVisited.at(t);
- else
- return 0;
- }
- double Statistic::getMightMagicRatio(const PlayerState * ps)
- {
- double numMight = 0;
- for(auto h : ps->heroes)
- if(h->type->heroClass->affinity == CHeroClass::EClassAffinity::MIGHT)
- numMight++;
- return numMight / ps->heroes.size();
- }
- std::map<EGameResID, int> Statistic::getNumMines(const CGameState * gs, const PlayerState * ps)
- {
- std::map<EGameResID, int> tmp;
- for(auto & res : EGameResID::ALL_RESOURCES())
- tmp[res] = 0;
- for(const CGMine * mine : getMines(gs, ps))
- tmp[mine->producedResource]++;
-
- return tmp;
- }
- VCMI_LIB_NAMESPACE_END
|