diff options
Diffstat (limited to 'crawl-ref/source/dbg-maps.cc')
-rw-r--r-- | crawl-ref/source/dbg-maps.cc | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/crawl-ref/source/dbg-maps.cc b/crawl-ref/source/dbg-maps.cc new file mode 100644 index 0000000000..b8930b415e --- /dev/null +++ b/crawl-ref/source/dbg-maps.cc @@ -0,0 +1,475 @@ +/* + * File: dbg-maps.cc + * Summary: Map generation statistics/testing. + * Written by: Linley Henzell and Jesse Jones + */ + +#include "AppHdr.h" + +#include "dbg-maps.h" + +#ifdef DEBUG_DIAGNOSTICS +// Map statistics generation. + +static std::map<std::string, int> mapgen_try_count; +static std::map<std::string, int> mapgen_use_count; +static std::map<level_id, int> mapgen_level_mapcounts; +static std::map< level_id, std::pair<int,int> > mapgen_map_builds; +static std::map< level_id, std::set<std::string> > mapgen_level_mapsused; + +typedef std::map< std::string, std::set<level_id> > mapname_place_map; +static mapname_place_map mapgen_map_levelsused; +static std::map<std::string, std::string> mapgen_errors; +static std::string mapgen_last_error; + +static int mg_levels_tried = 0, mg_levels_failed = 0; +static int mg_build_attempts = 0, mg_vetoes = 0; + +void mapgen_report_map_build_start() +{ + mg_build_attempts++; + mapgen_map_builds[level_id::current()].first++; +} + +void mapgen_report_map_veto() +{ + mg_vetoes++; + mapgen_map_builds[level_id::current()].second++; +} + +static map_mask mg_MapMask; + +static bool _mg_region_flood(const coord_def &c, int region, bool flag) +{ + bool found_exit = false; + + mg_MapMask(c) = region; + + if (flag) + { + env.map(c).flags = 0; + set_terrain_mapped(c.x, c.y); + } + + const dungeon_feature_type ft = grd(c); + if (feat_is_travelable_stair(ft)) + found_exit = true; + + for (int yi = -1; yi <= 1; ++yi) + for (int xi = -1; xi <= 1; ++xi) + { + if (!xi && !yi) + continue; + + coord_def ci = c + coord_def(xi, yi); + if (!in_bounds(ci) || mg_MapMask(ci) || !dgn_square_is_passable(ci)) + continue; + + if (_mg_region_flood(ci, region, flag)) + found_exit = true; + } + return (found_exit); +} + +static bool _mg_is_disconnected_level() +{ + // Don't care about non-Dungeon levels. + if (you.level_type != LEVEL_DUNGEON + || (branches[you.where_are_you].branch_flags & BFLAG_ISLANDED)) + return (false); + + std::vector<coord_def> region_seeds; + + mg_MapMask.init(0); + + coord_def c; + int region = 0; + int good_regions = 0; + for (c.y = 0; c.y < GYM; ++c.y) + for (c.x = 0; c.x < GXM; ++c.x) + if (!mg_MapMask(c) && dgn_square_is_passable(c)) + { + if (_mg_region_flood(c, ++region, false)) + ++good_regions; + else + region_seeds.push_back(c); + } + + mg_MapMask.init(0); + for (int i = 0, size = region_seeds.size(); i < size; ++i) + _mg_region_flood(region_seeds[i], 1, true); + + return (good_regions < region); +} + +static bool mg_do_build_level(int niters) +{ + mesclr(); + mprf("On %s (%d); %d g, %d fail, %d err%s, %d uniq, " + "%d try, %d (%.2lf%%) vetos", + level_id::current().describe().c_str(), niters, + mg_levels_tried, mg_levels_failed, mapgen_errors.size(), + mapgen_last_error.empty()? "" + : (" (" + mapgen_last_error + ")").c_str(), + mapgen_use_count.size(), + mg_build_attempts, mg_vetoes, + mg_build_attempts? mg_vetoes * 100.0 / mg_build_attempts : 0.0); + + no_messages mx; + for (int i = 0; i < niters; ++i) + { + if (kbhit() && getch() == ESCAPE) + return (false); + + ++mg_levels_tried; + if (!builder(you.your_level, you.level_type)) + { + ++mg_levels_failed; + continue; + } + + for (int y = 0; y < GYM; ++y) + for (int x = 0; x < GXM; ++x) + { + switch (grd[x][y]) + { + case DNGN_SECRET_DOOR: + case DNGN_DETECTED_SECRET_DOOR: // paranoia + grd[x][y] = DNGN_CLOSED_DOOR; + break; + default: + break; + } + } + + { + unwind_bool wiz(you.wizard, true); + magic_mapping(1000, 100, true, true); + } + if (_mg_is_disconnected_level()) + { + extern std::vector<vault_placement> Level_Vaults; + std::string vaults; + for (int j = 0, size = Level_Vaults.size(); j < size; ++j) + { + if (j && !vaults.empty()) + vaults += ", "; + vaults += Level_Vaults[j].map.name; + } + + if (!vaults.empty()) + vaults = " (" + vaults + ")"; + + extern std::string dgn_Build_Method; + mprf(MSGCH_ERROR, + "Bad (disconnected) level on %s%s", + level_id::current().describe().c_str(), + vaults.c_str()); + FILE *fp = fopen("map.dump", "w"); + fprintf(fp, "Bad (disconnected) level (%s) on %s%s.\n\n", + dgn_Build_Method.c_str(), + level_id::current().describe().c_str(), + vaults.c_str()); + + // Mapping would only have mapped squares that the player can + // reach - explicitly map the full level. + coord_def c; + for (c.y = 0; c.y < GYM; ++c.y) + for (c.x = 0; c.x < GXM; ++c.x) + set_envmap_obj(c, grd(c)); + + dump_map(fp); + + return (false); + } + } + return (true); +} + +static std::vector<level_id> mg_dungeon_places() +{ + std::vector<level_id> places; + + for (int br = BRANCH_MAIN_DUNGEON; br < NUM_BRANCHES; ++br) + { + if (branches[br].depth == -1) + continue; + + const branch_type branch = static_cast<branch_type>(br); + for (int depth = 1; depth <= branches[br].depth; ++depth) + places.push_back( level_id(branch, depth) ); + } + + places.push_back(LEVEL_ABYSS); + places.push_back(LEVEL_LABYRINTH); + places.push_back(LEVEL_PANDEMONIUM); + places.push_back(LEVEL_PORTAL_VAULT); + + return (places); +} + +static bool mg_build_dungeon() +{ + const std::vector<level_id> places = mg_dungeon_places(); + + for (int i = 0, size = places.size(); i < size; ++i) + { + const level_id &lid = places[i]; + you.your_level = absdungeon_depth(lid.branch, lid.depth); + you.where_are_you = lid.branch; + you.level_type = lid.level_type; + if (you.level_type == LEVEL_PORTAL_VAULT) + you.level_type_tag = you.level_type_name = "bazaar"; + if (!mg_do_build_level(1)) + return (false); + } + return (true); +} + +static void mg_build_levels(int niters) +{ + mesclr(); + mprf("Generating dungeon map stats"); + + for (int i = 0; i < niters; ++i) + { + mesclr(); + mprf("On %d of %d; %d g, %d fail, %d err%s, %d uniq, " + "%d try, %d (%.2lf%%) vetos", + i, niters, + mg_levels_tried, mg_levels_failed, mapgen_errors.size(), + mapgen_last_error.empty()? "" + : (" (" + mapgen_last_error + ")").c_str(), + mapgen_use_count.size(), + mg_build_attempts, mg_vetoes, + mg_build_attempts? mg_vetoes * 100.0 / mg_build_attempts : 0.0); + + you.uniq_map_tags.clear(); + you.uniq_map_names.clear(); + init_level_connectivity(); + if (!mg_build_dungeon()) + break; + } +} + +void mapgen_report_map_try(const map_def &map) +{ + mapgen_try_count[map.name]++; +} + +void mapgen_report_map_use(const map_def &map) +{ + mapgen_use_count[map.name]++; + mapgen_level_mapcounts[level_id::current()]++; + mapgen_level_mapsused[level_id::current()].insert(map.name); + mapgen_map_levelsused[map.name].insert(level_id::current()); +} + +void mapgen_report_error(const map_def &map, const std::string &err) +{ + mapgen_last_error = err; +} + +static void _mapgen_report_available_random_vaults(FILE *outf) +{ + you.uniq_map_tags.clear(); + you.uniq_map_names.clear(); + + const std::vector<level_id> places = mg_dungeon_places(); + fprintf(outf, "\n\nRandom vaults available by dungeon level:\n"); + + for (std::vector<level_id>::const_iterator i = places.begin(); + i != places.end(); ++i) + { + fprintf(outf, "\n%s -------------\n", i->describe().c_str()); + mesclr(); + mprf("Examining random maps at %s", i->describe().c_str()); + mg_report_random_maps(outf, *i); + if (kbhit() && getch() == ESCAPE) + break; + fprintf(outf, "---------------------------------\n"); + } +} + +static void _check_mapless(const level_id &lid, std::vector<level_id> &mapless) +{ + if (mapgen_level_mapsused.find(lid) == mapgen_level_mapsused.end()) + mapless.push_back(lid); +} + +static void _write_mapgen_stats() +{ + FILE *outf = fopen("mapgen.log", "w"); + fprintf(outf, "Map Generation Stats\n\n"); + fprintf(outf, "Levels attempted: %d, built: %d, failed: %d\n", + mg_levels_tried, mg_levels_tried - mg_levels_failed, + mg_levels_failed); + + if (!mapgen_errors.empty()) + { + fprintf(outf, "\n\nMap errors:\n"); + for (std::map<std::string, std::string>::const_iterator i = + mapgen_errors.begin(); i != mapgen_errors.end(); ++i) + { + fprintf(outf, "%s: %s\n", + i->first.c_str(), i->second.c_str()); + } + } + + std::vector<level_id> mapless; + for (int i = BRANCH_MAIN_DUNGEON; i < NUM_BRANCHES; ++i) + { + if (branches[i].depth == -1) + continue; + + const branch_type br = static_cast<branch_type>(i); + for (int dep = 1; dep <= branches[i].depth; ++dep) + { + const level_id lid(br, dep); + _check_mapless(lid, mapless); + } + } + + _check_mapless(level_id(LEVEL_ABYSS), mapless); + _check_mapless(level_id(LEVEL_PANDEMONIUM), mapless); + _check_mapless(level_id(LEVEL_LABYRINTH), mapless); + _check_mapless(level_id(LEVEL_PORTAL_VAULT), mapless); + + if (!mapless.empty()) + { + fprintf(outf, "\n\nLevels with no maps:\n"); + for (int i = 0, size = mapless.size(); i < size; ++i) + fprintf(outf, "%3d) %s\n", i + 1, mapless[i].describe().c_str()); + } + + _mapgen_report_available_random_vaults(outf); + + std::vector<std::string> unused_maps; + for (int i = 0, size = map_count(); i < size; ++i) + { + const map_def *map = map_by_index(i); + if (mapgen_try_count.find(map->name) == mapgen_try_count.end() + && !map->has_tag("dummy")) + { + unused_maps.push_back(map->name); + } + } + + if (mg_vetoes) + { + fprintf(outf, "\n\nMost vetoed levels:\n"); + std::multimap<int, level_id> sortedvetos; + for (std::map< level_id, std::pair<int, int> >::const_iterator + i = mapgen_map_builds.begin(); i != mapgen_map_builds.end(); + ++i) + { + if (!i->second.second) + continue; + + sortedvetos.insert( + std::pair<int, level_id>( i->second.second, i->first )); + } + + int count = 0; + for (std::multimap<int, level_id>::reverse_iterator + i = sortedvetos.rbegin(); i != sortedvetos.rend(); ++i) + { + const int vetoes = i->first; + const int tries = mapgen_map_builds[i->second].first; + fprintf(outf, "%3d) %s (%d of %d vetoed, %.2f%%)\n", + ++count, i->second.describe().c_str(), + vetoes, tries, vetoes * 100.0 / tries); + } + } + + if (!unused_maps.empty()) + { + fprintf(outf, "\n\nUnused maps:\n\n"); + for (int i = 0, size = unused_maps.size(); i < size; ++i) + fprintf(outf, "%3d) %s\n", i + 1, unused_maps[i].c_str()); + } + + fprintf(outf, "\n\nMaps by level:\n\n"); + for (std::map<level_id, std::set<std::string> >::const_iterator i = + mapgen_level_mapsused.begin(); i != mapgen_level_mapsused.end(); + ++i) + { + std::string line = + make_stringf("%s ------------\n", i->first.describe().c_str()); + const std::set<std::string> &maps = i->second; + for (std::set<std::string>::const_iterator j = maps.begin(); + j != maps.end(); ++j) + { + if (j != maps.begin()) + line += ", "; + if (line.length() + j->length() > 79) + { + fprintf(outf, "%s\n", line.c_str()); + line = *j; + } + else + line += *j; + } + + if (!line.empty()) + fprintf(outf, "%s\n", line.c_str()); + + fprintf(outf, "------------\n\n"); + } + + fprintf(outf, "\n\nMaps used:\n\n"); + std::multimap<int, std::string> usedmaps; + for (std::map<std::string, int>::const_iterator i = + mapgen_try_count.begin(); i != mapgen_try_count.end(); ++i) + usedmaps.insert(std::pair<int, std::string>(i->second, i->first)); + + for (std::multimap<int, std::string>::reverse_iterator i = + usedmaps.rbegin(); i != usedmaps.rend(); ++i) + { + const int tries = i->first; + std::map<std::string, int>::const_iterator iuse = + mapgen_use_count.find(i->second); + const int uses = iuse == mapgen_use_count.end()? 0 : iuse->second; + if (tries == uses) + fprintf(outf, "%4d : %s\n", tries, i->second.c_str()); + else + fprintf(outf, "%4d (%4d): %s\n", uses, tries, i->second.c_str()); + } + + fprintf(outf, "\n\nMaps and where used:\n\n"); + for (mapname_place_map::iterator i = mapgen_map_levelsused.begin(); + i != mapgen_map_levelsused.end(); ++i) + { + fprintf(outf, "%s ============\n", i->first.c_str()); + std::string line; + for (std::set<level_id>::const_iterator j = i->second.begin(); + j != i->second.end(); ++j) + { + if (!line.empty()) + line += ", "; + std::string level = j->describe(); + if (line.length() + level.length() > 79) + { + fprintf(outf, "%s\n", line.c_str()); + line = level; + } + else + line += level; + } + if (!line.empty()) + fprintf(outf, "%s\n", line.c_str()); + + fprintf(outf, "==================\n\n"); + } + fclose(outf); +} + +void generate_map_stats() +{ + // We have to run map preludes ourselves. + run_map_preludes(); + mg_build_levels(SysEnv.map_gen_iters); + _write_mapgen_stats(); +} + +#endif // DEBUG_DIAGNOSTICS |