Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added useful metrics #168

Merged
merged 8 commits into from Dec 6, 2018
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions game_engine/core/HaliteImpl.cpp
Expand Up @@ -141,6 +141,8 @@ void HaliteImpl::run_game() {
for (game.turn_number = 1; game.turn_number <= constants.MAX_TURNS; game.turn_number++) {
Logging::set_turn_number(game.turn_number);
game.logs.set_turn_number(game.turn_number);
// Used to track the current turn number inside Event::update_stats
game.game_statistics.turn_number = game.turn_number;
Logging::log([turn_number = game.turn_number]() {
return "Starting turn " + std::to_string(turn_number);
}, Logging::Level::Debug);
Expand Down Expand Up @@ -499,10 +501,17 @@ void HaliteImpl::update_player_stats() {
if (!player.terminated && player.can_play) {
if (player_can_play(player)) { // Player may have died during this turn, in which case do not update final turn
player_stats.last_turn_alive = game.turn_number;

// Calculate carried_at_end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Watch out for mixed spaces/tabs here.)

player_stats.carried_at_end = 0;
for (const auto &[_entity_id, location] : player.entities) {
player_stats.carried_at_end += game.store.get_entity(_entity_id).energy;
}
}
player_stats.turn_productions.push_back(player.energy);
player_stats.turn_deposited.push_back(player.total_energy_deposited);
player_stats.number_dropoffs = player.dropoffs.size();
player_stats.ships_peak = std::max(player_stats.ships_peak, (long)player.entities.size());
for (const auto &[_entity_id, location] : player.entities) {
const dimension_type entity_distance = game.map.distance(location, player.factory);
if (entity_distance > player_stats.max_entity_distance)
Expand Down
16 changes: 12 additions & 4 deletions game_engine/core/Statistics.cpp
Expand Up @@ -50,18 +50,24 @@ void to_json(nlohmann::json &json, const PlayerStatistics &stats) {
FIELD_TO_JSON(random_id),
FIELD_TO_JSON(rank),
FIELD_TO_JSON(last_turn_alive),
FIELD_TO_JSON(last_turn_ship_spawn),
{"final_production", final_production},
FIELD_TO_JSON(total_production),
FIELD_TO_JSON(max_entity_distance),
FIELD_TO_JSON(number_dropoffs),
FIELD_TO_JSON(interaction_opportunities),
FIELD_TO_JSON(ships_captured),
FIELD_TO_JSON(ships_given),
//FIELD_TO_JSON(ships_captured),
//FIELD_TO_JSON(ships_given),
FIELD_TO_JSON(ships_spawned),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we're at it, we should stop writing unused fields - ships_captured, ships_given, total_mined_from_captured, and perhaps interaction_opportunities.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually specifically advised against that on discord. But after thinking about it some more the risk seems very minimal and while the benefit is also fairly small it probably is good on balance.

Since you're obviously in favor, I'm fine with them going as well.

FIELD_TO_JSON(ships_peak),
FIELD_TO_JSON(self_collisions),
FIELD_TO_JSON(all_collisions),
FIELD_TO_JSON(dropoff_collisions),
FIELD_TO_JSON(total_mined),
FIELD_TO_JSON(total_bonus),
FIELD_TO_JSON(total_mined_from_captured),
//FIELD_TO_JSON(total_mined_from_captured),
FIELD_TO_JSON(total_dropped),
FIELD_TO_JSON(carried_at_end),
{"mining_efficiency", mining_efficiency},
FIELD_TO_JSON(halite_per_dropoff),
{"average_entity_distance", average_distance}};
Expand All @@ -74,7 +80,9 @@ void to_json(nlohmann::json &json, const PlayerStatistics &stats) {
*/
void to_json(nlohmann::json &json, const GameStatistics &stats) {
json = {FIELD_TO_JSON(number_turns),
FIELD_TO_JSON(player_statistics)};
FIELD_TO_JSON(player_statistics),
FIELD_TO_JSON(execution_time),
FIELD_TO_JSON(map_total_halite)};
}

}
10 changes: 10 additions & 0 deletions game_engine/core/Statistics.hpp
Expand Up @@ -16,12 +16,15 @@ struct PlayerStatistics {
unsigned int random_id; /**< Random number assigned to player for use in ties. */
long rank{}; /**< The rank of the player (1 = highest) */
long last_turn_alive{}; /**< The last turn the player remained alive */
long last_turn_ship_spawn{}; /**< The last turn the player spawned a ship */
std::vector<energy_type> turn_productions{}; /**< Production granted to player each turn, turn 1 at front of vector. */
std::vector<energy_type> turn_deposited{}; /**< Running total of how much halite has been collected by this player each turn. */
energy_type total_production{}; /**< Production granted to player each turn, turn 1 at front of vector. */
energy_type total_mined{}; /**< Total amount extracted from map, including energy not deposited (lost due to collision or some other means), but not including bonuses from inspiration. */
energy_type total_bonus{}; /**< Total halite collected from inspiration bonuses, including energy not deposited (lost due to collision or some other means). */
energy_type total_mined_from_captured{}; /**< Total amount mined, including energy not deposited (lost due to collision or some other means) and bonuses from inspiration, by ships captured from other players. */
energy_type total_dropped{}; /**< Total amount of halite lost due collisions. */
energy_type carried_at_end{}; /**< Amount of halite on ships last frame. */
dimension_type max_entity_distance{}; /**< The maximum distance any entity traveled from the player's factory. */
dimension_type total_distance{}; /**< The total distance of all entities over all turns from factory. */
dimension_type total_entity_lifespan{}; /**< Total lifespan of all entities (ie 1 entity for 10 turns plus 1 for 300 = 310. */
Expand All @@ -31,6 +34,9 @@ struct PlayerStatistics {
long ships_given{}; /**< The number of ships captured from this player. */
long self_collisions{}; /**< The number of ships involved in collisions with allied ships. */
long all_collisions{}; /**< The number of ships involved in collisions with any ships, allied or not. Note there may be overlap with self_collisions if a 3+ ship collision occurs. */
long dropoff_collisions{}; /**< The number of ships involved in collisions with allied ships over a friendly dropoff. */
long ships_spawned{}; /**< The number of ships spawned. */
long ships_peak{}; /**< The maximum number of ship spawned at the same time. */
std::unordered_map<Location, energy_type> halite_per_dropoff{}; /**< The amount of halite collected at each dropoff. */

/**
Expand Down Expand Up @@ -61,6 +67,10 @@ struct PlayerStatistics {
struct GameStatistics {
std::vector<PlayerStatistics> player_statistics; /**< Statistics for each player. */
unsigned long number_turns{}; /**< Total number of turns that finished before game ends. */
energy_type map_total_halite{}; /**< Total halite available at the start. */
long long execution_time{}; /**< Execution time of the game in ms. */

unsigned long turn_number{}; /**< Used to track last_turn_ship_spawn */

/**
* Convert game statistics to json format
Expand Down
13 changes: 12 additions & 1 deletion game_engine/main.cpp
Expand Up @@ -22,6 +22,7 @@ constexpr auto SEPARATOR = '/';
constexpr auto JSON_INDENT_LEVEL = 4;

int main(int argc, char *argv[]) {
auto engine_start = std::chrono::system_clock::now();
auto &constants = hlt::Constants::get_mut();

using namespace TCLAP;
Expand Down Expand Up @@ -149,6 +150,12 @@ int main(int argc, char *argv[]) {
hlt::Replay replay{game_statistics, map_parameters.num_players, map_parameters.seed, map};
Logging::log("Map seed is " + std::to_string(map_parameters.seed));

for (const auto &row : map.grid) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Mixed tabs and spaces here too.)

for (const auto &cell : row) {
game_statistics.map_total_halite += cell.energy;
}
}

hlt::Halite game(map, networking_config, game_statistics, replay);
game.run_game(bot_commands, snapshot);

Expand All @@ -175,6 +182,8 @@ int main(int argc, char *argv[]) {
static constexpr size_t MAX_DATE_STRING_LENGTH = 25;
char time_string[MAX_DATE_STRING_LENGTH];
std::strftime(time_string, MAX_DATE_STRING_LENGTH, "%Y%m%d-%H%M%S%z", localtime);

game_statistics.execution_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - engine_start).count();

if (!no_replay_switch.getValue()) {
// Output gamefile. First try the replays folder; if that fails, just use the straight filename.
Expand Down Expand Up @@ -245,9 +254,11 @@ int main(int argc, char *argv[]) {
results["terminated"][to_string(player_id)] = player.terminated;
}
}


results["execution_time"] = game_statistics.execution_time;
results["map_width"] = map_width;
results["map_height"] = map_height;
results["map_total_halite"] = game_statistics.map_total_halite;
results["map_seed"] = seed;
std::ostringstream stream;
stream << type;
Expand Down
17 changes: 16 additions & 1 deletion game_engine/replay/GameEvent.cpp
Expand Up @@ -29,6 +29,14 @@ void SpawnEvent::to_json(nlohmann::json &json) const {
FIELD_TO_JSON(energy)};
}

void SpawnEvent::update_stats(const Store &store, const Map &map,
GameStatistics &stats) {
(void)map;

stats.player_statistics.at(owner_id.value).ships_spawned++;
stats.player_statistics.at(owner_id.value).last_turn_ship_spawn = stats.turn_number;
}

/**
* Convert capture event to json format
* @param[out] json JSON to be filled by spawn event
Expand Down Expand Up @@ -60,8 +68,15 @@ void CollisionEvent::update_stats(const Store &store, const Map &map,
ordered_id_map<Player, int> ships_involved;
for (const auto &ship_id : ships) {
const auto &entity = store.get_entity(ship_id);
stats.player_statistics.at(entity.owner.value).all_collisions++;
auto &player_stats = stats.player_statistics.at(entity.owner.value);

ships_involved[entity.owner]++;
player_stats.all_collisions++;
player_stats.total_dropped += entity.energy;

if (map.at(location).owner == entity.owner) { // There is a friendly dropoff
player_stats.dropoff_collisions++;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should dropoff_collisions really count collisions over all dropoffs? It seems like only counting collisions over the player's own dropoffs might be more useful.

}
for (const auto &[player_id, num_ships] : ships_involved) {
// Increment self-collision to account for uncounted ship
Expand Down
2 changes: 2 additions & 0 deletions game_engine/replay/GameEvent.hpp
Expand Up @@ -73,6 +73,8 @@ class SpawnEvent : public BaseEvent {
SpawnEvent(Location location, energy_type energy, Player::id_type owner_id, Entity::id_type id) :
BaseEvent(location), energy(energy), owner_id(owner_id), id(id) {};
~SpawnEvent() override = default;

virtual void update_stats(const Store &store, const Map &map, GameStatistics &stats) override;
};

/** An event for entity captures */
Expand Down