GameStatistics.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. * GameStatistics.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "GameStatistics.h"
  12. #include "../CPlayerState.h"
  13. #include "../constants/StringConstants.h"
  14. #include "CGameState.h"
  15. #include "TerrainHandler.h"
  16. #include "StartInfo.h"
  17. #include "../mapObjects/CGHeroInstance.h"
  18. #include "../mapObjects/CGTownInstance.h"
  19. #include "../mapObjects/CGObjectInstance.h"
  20. #include "../mapObjects/MiscObjects.h"
  21. #include "../mapping/CMap.h"
  22. VCMI_LIB_NAMESPACE_BEGIN
  23. void StatisticDataSet::add(StatisticDataSetEntry entry)
  24. {
  25. data.push_back(entry);
  26. }
  27. StatisticDataSetEntry StatisticDataSet::createEntry(const PlayerState * ps, const CGameState * gs)
  28. {
  29. StatisticDataSetEntry data;
  30. data.day = gs->getDate(Date::DAY);
  31. data.player = ps->color;
  32. data.team = ps->team;
  33. data.isHuman = ps->isHuman();
  34. data.status = ps->status;
  35. data.resources = ps->resources;
  36. data.numberHeroes = ps->heroes.size();
  37. data.numberTowns = ps->towns.size();
  38. data.numberArtifacts = Statistic::getNumberOfArts(ps);
  39. data.armyStrength = Statistic::getArmyStrength(ps);
  40. data.income = Statistic::getIncome(gs, ps);
  41. data.mapVisitedRatio = Statistic::getMapVisitedRatio(gs, ps->color);
  42. return data;
  43. }
  44. std::string StatisticDataSet::toCsv()
  45. {
  46. std::stringstream ss;
  47. auto resources = std::vector<EGameResID>{EGameResID::GOLD, EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS};
  48. ss << "Day" << ";";
  49. ss << "Player" << ";";
  50. ss << "Team" << ";";
  51. ss << "IsHuman" << ";";
  52. ss << "Status" << ";";
  53. ss << "NumberHeroes" << ";";
  54. ss << "NumberTowns" << ";";
  55. ss << "NumberArtifacts" << ";";
  56. ss << "ArmyStrength" << ";";
  57. ss << "Income" << ";";
  58. ss << "MapVisitedRatio";
  59. for(auto & resource : resources)
  60. ss << ";" << GameConstants::RESOURCE_NAMES[resource];
  61. ss << "\r\n";
  62. for(auto & entry : data)
  63. {
  64. ss << entry.day << ";";
  65. ss << GameConstants::PLAYER_COLOR_NAMES[entry.player] << ";";
  66. ss << entry.team.getNum() << ";";
  67. ss << entry.isHuman << ";";
  68. ss << (int)entry.status << ";";
  69. ss << entry.numberHeroes << ";";
  70. ss << entry.numberTowns << ";";
  71. ss << entry.numberArtifacts << ";";
  72. ss << entry.armyStrength << ";";
  73. ss << entry.income << ";";
  74. ss << entry.mapVisitedRatio;
  75. for(auto & resource : resources)
  76. ss << ";" << entry.resources[resource];
  77. ss << "\r\n";
  78. }
  79. return ss.str();
  80. }
  81. //calculates total number of artifacts that belong to given player
  82. int Statistic::getNumberOfArts(const PlayerState * ps)
  83. {
  84. int ret = 0;
  85. for(auto h : ps->heroes)
  86. {
  87. ret += (int)h->artifactsInBackpack.size() + (int)h->artifactsWorn.size();
  88. }
  89. return ret;
  90. }
  91. // get total strength of player army
  92. si64 Statistic::getArmyStrength(const PlayerState * ps)
  93. {
  94. si64 str = 0;
  95. for(auto h : ps->heroes)
  96. {
  97. if(!h->inTownGarrison) //original h3 behavior
  98. str += h->getArmyStrength();
  99. }
  100. return str;
  101. }
  102. // get total gold income
  103. int Statistic::getIncome(const CGameState * gs, const PlayerState * ps)
  104. {
  105. int percentIncome = gs->getStartInfo()->getIthPlayersSettings(ps->color).handicap.percentIncome;
  106. int totalIncome = 0;
  107. const CGObjectInstance * heroOrTown = nullptr;
  108. //Heroes can produce gold as well - skill, specialty or arts
  109. for(const auto & h : ps->heroes)
  110. {
  111. totalIncome += h->valOfBonuses(Selector::typeSubtype(BonusType::GENERATE_RESOURCE, BonusSubtypeID(GameResID(GameResID::GOLD)))) * percentIncome / 100;
  112. if(!heroOrTown)
  113. heroOrTown = h;
  114. }
  115. //Add town income of all towns
  116. for(const auto & t : ps->towns)
  117. {
  118. totalIncome += t->dailyIncome()[EGameResID::GOLD];
  119. if(!heroOrTown)
  120. heroOrTown = t;
  121. }
  122. /// FIXME: Dirty dirty hack
  123. /// Stats helper need some access to gamestate.
  124. std::vector<const CGObjectInstance *> ownedObjects;
  125. for(const CGObjectInstance * obj : heroOrTown->cb->gameState()->map->objects)
  126. {
  127. if(obj && obj->tempOwner == ps->color)
  128. ownedObjects.push_back(obj);
  129. }
  130. /// This is code from CPlayerSpecificInfoCallback::getMyObjects
  131. /// I'm really need to find out about callback interface design...
  132. for(const auto * object : ownedObjects)
  133. {
  134. //Mines
  135. if ( object->ID == Obj::MINE )
  136. {
  137. const auto * mine = dynamic_cast<const CGMine *>(object);
  138. assert(mine);
  139. if (mine->producedResource == EGameResID::GOLD)
  140. totalIncome += mine->getProducedQuantity();
  141. }
  142. }
  143. return totalIncome;
  144. }
  145. double Statistic::getMapVisitedRatio(const CGameState * gs, PlayerColor player)
  146. {
  147. double visible = 0.0;
  148. double numTiles = 0.0;
  149. for(int layer = 0; layer < (gs->map->twoLevel ? 2 : 1); layer++)
  150. for(int y = 0; y < gs->map->height; ++y)
  151. for(int x = 0; x < gs->map->width; ++x)
  152. {
  153. TerrainTile tile = gs->map->getTile(int3(x, y, layer));
  154. if(tile.blocked && (!tile.visitable))
  155. continue;
  156. if(gs->isVisible(int3(x, y, layer), player))
  157. visible++;
  158. numTiles++;
  159. }
  160. return visible / numTiles;
  161. }
  162. const CGHeroInstance * Statistic::findBestHero(CGameState * gs, const PlayerColor & color)
  163. {
  164. std::vector<ConstTransitivePtr<CGHeroInstance> > &h = gs->players[color].heroes;
  165. if(h.empty())
  166. return nullptr;
  167. //best hero will be that with highest exp
  168. int best = 0;
  169. for(int b=1; b<h.size(); ++b)
  170. {
  171. if(h[b]->exp > h[best]->exp)
  172. {
  173. best = b;
  174. }
  175. }
  176. return h[best];
  177. }
  178. std::vector<std::vector<PlayerColor>> Statistic::getRank(std::vector<TStat> stats)
  179. {
  180. std::sort(stats.begin(), stats.end(), [](const TStat & a, const TStat & b) { return a.second > b.second; });
  181. //put first element
  182. std::vector< std::vector<PlayerColor> > ret;
  183. std::vector<PlayerColor> tmp;
  184. tmp.push_back( stats[0].first );
  185. ret.push_back( tmp );
  186. //the rest of elements
  187. for(int g=1; g<stats.size(); ++g)
  188. {
  189. if(stats[g].second == stats[g-1].second)
  190. {
  191. (ret.end()-1)->push_back( stats[g].first );
  192. }
  193. else
  194. {
  195. //create next occupied rank
  196. std::vector<PlayerColor> tmp;
  197. tmp.push_back(stats[g].first);
  198. ret.push_back(tmp);
  199. }
  200. }
  201. return ret;
  202. }
  203. VCMI_LIB_NAMESPACE_END