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;
}