static int moves_to_destination( const std::string &monster_type, const tripoint &start, const tripoint &end ) { REQUIRE( g->num_zombies() == 0 ); monster &test_monster = spawn_test_monster( monster_type, start ); // Get it riled up and give it a goal. test_monster.anger = 100; test_monster.set_dest( end ); test_monster.set_moves( 0 ); const int monster_speed = test_monster.get_speed(); int moves_spent = 0; for( int turn = 0; turn < 1000; ++turn ) { test_monster.mod_moves( monster_speed ); while( test_monster.moves >= 0 ) { int moves_before = test_monster.moves; test_monster.move(); moves_spent += moves_before - test_monster.moves; if( test_monster.pos() == test_monster.move_target() ) { g->remove_zombie( 0 ); return moves_spent; } } } g->remove_zombie( 0 ); // Return an unreasonably high number. return 100000; }
void check_lethality( std::string explosive_id, int range, float lethality, float epsilon ) { int num_survivors = 0; int num_subjects = 0; int num_wounded = 0; statistics<double> lethality_ratios; std::stringstream survivor_stats; int total_hp = 0; int average_hp = 0; float error = 0.0; float lethality_ratio = 0.0; do { // Clear map clear_map(); // Spawn some monsters in a circle. tripoint origin( 30, 30, 0 ); for( const tripoint monster_position : closest_tripoints_first( range, origin ) ) { if( rl_dist( monster_position, origin ) != range ) { continue; } num_subjects++; spawn_test_monster( "mon_zombie", monster_position ); } // Set off an explosion item grenade( explosive_id ); grenade.charges = 0; grenade.type->invoke( g->u, grenade, origin ); // see how many monsters survive std::vector<Creature *> survivors = g->get_creatures_if( []( const Creature & critter ) { return critter.is_monster(); } ); num_survivors += survivors.size(); for( Creature *survivor : survivors ) { survivor_stats << survivor->pos() << " " << survivor->get_hp() << ", "; num_wounded += ( survivor->get_hp() < survivor->get_hp_max() ) ? 1 : 0; total_hp += survivor->get_hp(); } if( num_survivors > 0 ) { survivor_stats << std::endl; average_hp = total_hp / num_survivors; } double survivor_ratio = static_cast<double>( num_survivors ) / num_subjects; lethality_ratio = 1.0 - survivor_ratio; lethality_ratios.add( lethality_ratio ); error = lethality_ratios.margin_of_error(); } while( lethality_ratios.n() < 5 || ( lethality_ratios.avg() + error > lethality && lethality_ratios.avg() - error < lethality ) ); INFO( "samples " << lethality_ratios.n() ); INFO( explosive_id ); INFO( "range " << range ); INFO( num_survivors << " survivors out of " << num_subjects << " targets." ); INFO( survivor_stats.str() ); INFO( "Wounded survivors: " << num_wounded ); INFO( "average hp of survivors: " << average_hp ); CHECK( lethality_ratio == Approx( lethality ).epsilon( epsilon ) ); }
/** * Simulate a player running from the monster, checking if it can catch up. **/ static int can_catch_player( const std::string &monster_type, const tripoint &direction_of_flight ) { REQUIRE( g->num_zombies() == 0 ); player &test_player = g->u; // Strip off any potentially encumbering clothing. std::list<item> temp; while( test_player.takeoff( test_player.i_at( -2 ), &temp ) ); test_player.setpos( { 65, 65, 0 } ); test_player.set_moves( 0 ); // Give the player a head start. const tripoint monster_start = { test_player.pos().x - (10 * direction_of_flight.x), test_player.pos().y - (10 * direction_of_flight.y), test_player.pos().z - (10 * direction_of_flight.z) }; monster &test_monster = spawn_test_monster( monster_type, monster_start ); // Get it riled up and give it a goal. test_monster.anger = 100; test_monster.set_dest( test_player.pos() ); test_monster.set_moves( 0 ); const int monster_speed = test_monster.get_speed(); const int target_speed = 100; int moves_spent = 0; std::vector<track> tracker; for( int turn = 0; turn < 1000; ++turn ) { test_player.mod_moves( target_speed ); while( test_player.moves >= 0 ) { test_player.setpos( test_player.pos() + direction_of_flight ); if( test_player.pos().x < SEEX * int(MAPSIZE / 2) || test_player.pos().y < SEEY * int(MAPSIZE / 2) || test_player.pos().x >= SEEX * (1 + int(MAPSIZE / 2)) || test_player.pos().y >= SEEY * (1 + int(MAPSIZE / 2)) ) { g->update_map( test_player ); wipe_map_terrain(); for( unsigned int i = 0; i < g->num_zombies(); ) { if( &g->zombie( i ) == &test_monster ) { i++; } else { g->remove_zombie( i ); } } } const int move_cost = g->m.combined_movecost( test_player.pos(), test_player.pos() + direction_of_flight, nullptr, 0 ); tracker.push_back({'p', move_cost, rl_dist(test_monster.pos(), test_player.pos()), test_player.pos()} ); test_player.mod_moves( -move_cost ); } test_monster.set_dest( test_player.pos() ); test_monster.mod_moves( monster_speed ); while( test_monster.moves >= 0 ) { int moves_before = test_monster.moves; test_monster.move(); tracker.push_back({'m', moves_before - test_monster.moves, rl_dist(test_monster.pos(), test_player.pos()), test_monster.pos()} ); moves_spent += moves_before - test_monster.moves; if( rl_dist( test_monster.pos(), test_player.pos() ) == 1 ) { INFO( tracker ); clear_map(); return turn; } else if( rl_dist( test_monster.pos(), test_player.pos() ) > 20 ) { INFO( tracker ); clear_map(); return -turn; } } } WARN( tracker ); clear_map(); return -1000; }