// #ifdef DEBUG
// #define _GLIBCXX_DEBUG
// #endif
#include <iostream>
#include <iomanip>
#include <vector>
#include <valarray>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <stack>
#include <bitset>
#include <utility>
#include <numeric>
#include <algorithm>
#include <functional>
#include <complex>
#include <string>
#include <sstream>

#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cstring>

// these require C++11
#include <unordered_set>
#include <unordered_map>
#include <random>
#include <thread>
#include <chrono>
#include <tuple>

using namespace std;

#define int long long

#define all(c) c.begin(), c.end()
#define repeat(i, n) for (int i = 0; i < static_cast<int>(n); i++)
#define debug(x) #x << "=" << (x)

#ifdef DEBUG
#define dump(x) std::cerr << debug(x) << " (L:" << __LINE__ << ")" << std::endl
#else
#define dump(x) 
#endif

template<typename A,typename B>
ostream &operator<<(ostream&os,const pair<A,B>& p){
    os << "(" << p.first << "," << p.second << ")";
    return os;
}

typedef complex<double> point;

// template<typename T,std::size_t N>
// struct _v_traits {using type = std::vector<typename _v_traits<T,N-1>::type>;};
// template<typename T>
// struct _v_traits<T,1> {using type = std::vector<T>;};
// template<typename T,std::size_t N=1>
// using vec = typename _v_traits<T,N>::type;

template <typename T>
ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "[";
    for (const auto &v : vec) {
        os << v << ",";
    }
    os << "]";
    return os;
}

const int INF = 1000000000000000ll;

struct Info {
    int to_goal;
    int to_spring;
    bool is_start;
};

ostream &operator<<(ostream &os, const Info &i) {
    return os << "(" << i.to_goal << "," << i.to_spring << ")";
}

const char wall = '#';
const char spring = '*';
const char dot = '.';
const char start = 's';
const char goal = 'g';

const vector<int> dx = {-1,0,1,0};
const vector<int> dy = {0, 1,0,-1};
// 凸関数の極大な点をもとめる
template<typename F,typename T>
T ternary_search(F f,T left,T right,int try_cnt = 100){
    for(int i=0;i<try_cnt;i++){
        T l = (2*left + right) / 3;
        T r = (left + 2*right) / 3;
        if(f(l) < f(r)){
            left = l;
        }else{
            right = r;
        }
    }
    return (left+right)/2;
}

// 凹関数の極小な・を求める
template<typename F,typename T>
T ternary_search_concave(F f,T left,T right,int try_cnt=1000){
    return ternary_search([f](T x){return -f(x);},left,right);
}

// [0 ~ k)
long double calc_e(const vector<Info>& v,
                   const vector<int>& as,
                   const vector<int>& bs,
                   const int k){
    const int N = v.size();
    long double l = as[k];
    long double r = bs[N] - bs[k];
    return (l+r) / k;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    int W,H; cin >> W >> H;
    vector<string> field(H);
    for(string& s : field){
        cin >> s;
    }

    vector<vector<Info> > info(H,vector<Info>(W,Info{INF,INF,false}));
    for(int i=0;i<H;i++){
        for(int j=0;j<W;j++){
            if(field[i][j] == start) info[i][j].is_start = true;
        }
    }

    for(int to_finding_spring=0;to_finding_spring<2;to_finding_spring++){
        // value,y,x
        queue<tuple<int,int,int> > queue;
        set<tuple<int,int> > already;
        for(int i=0;i<H;i++){
            for(int j=0;j<W;j++){
                if((to_finding_spring and field[i][j] == spring) or
                   (!to_finding_spring and field[i][j] == goal)){
                    queue.emplace(0,i,j);
                }
            }
        }
        while(not queue.empty()){
            auto t = queue.front();
            queue.pop();
            int v = get<0>(t);
            int y = get<1>(t);
            int x = get<2>(t);
            if(already.find(make_tuple(y,x)) != already.end()){
                continue;
            }
            if(to_finding_spring && (field[y][x] == wall or field[y][x] == goal)){
                continue;
            }
            if(not to_finding_spring && (field[y][x] == wall or field[y][x] == spring)){
                continue;
            }

            already.insert(make_tuple(y,x));
            if(to_finding_spring){
                info[y][x].to_spring = v;
            }else{
                info[y][x].to_goal = v;
            }
            for(int i=0;i<4;i++){
                int nx = x + dx[i];
                int ny = y + dy[i];
                if(0 <= nx and nx < W and 0 <= ny and ny < H
                   and already.find(make_tuple(ny,nx)) == already.end()){
                    queue.emplace(v+1,ny,nx);
                }
            }
        }
    }

    vector<Info> dots;
    for(int i=0;i<H;i++){
        for(int j=0;j<W;j++){
            if(field[i][j] == dot || field[i][j] == start){
                dots.push_back(info[i][j]);
            }
        }
    }
    // 階段が近い → 遠い
    sort(dots.begin(),dots.end(),[](const Info& left,const Info& right){
            int l = left.to_goal - left.to_spring;
            int r = right.to_goal - right.to_spring;
            return l < r;
        });


    int si = find_if(dots.begin(),dots.end(),[](const Info& i){return i.is_start;}) - dots.begin();

    int N = dots.size();
    vector<int> as(N+1);
    vector<int> bs(N+1);

    for(int i=1;i<N+1;i++){
        as[i] = as[i-1] + dots[i-1].to_goal;
        bs[i] = bs[i-1] + dots[i-1].to_spring;
    }
    map<tuple<int,int>,long double> memo;
    auto f = [&](long double c){
        int k = 0;
        for(k=0;k<N;k++){
            if(dots[k].to_goal - dots[k].to_spring > c + 1e-9){
                break;
            }
        }
        if(si < k){
            return (long double)(as[si+1]-as[si]);
        }else{
            return (bs[si+1] - bs[si]) + calc_e(dots,as,bs,k);
        }
    };
    long double ls = 1e-15;
    long double rs = 1e15;
    auto r = ternary_search_concave(f,ls,rs);

    cout << fixed << setprecision(12);
    cout << f(r) << endl;
    return 0;
}

Measured memory_bytes: 22648