#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <math.h>
#include <assert.h>
#include <vector>
#include <queue>
#include <string>
#include <map>
#include <set>

using namespace std;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
static const long double EPS = 1e-9;
static const long double PI = acos(-1.0);

#define REP(i, n) for (int i = 0; i < (int)(n); i++)
#define FOR(i, s, n) for (int i = (s); i < (int)(n); i++)
#define FOREQ(i, s, n) for (int i = (s); i <= (int)(n); i++)
#define FORIT(it, c) for (__typeof((c).begin())it = (c).begin(); it != (c).end(); it++)
#define MEMSET(v, h) memset((v), h, sizeof(v))

struct Point {
  int x, y, cost;
  Point() {;}
  Point(int x, int y, int cost) : x(x), y(y), cost(cost) {;}
};

int w, h;
char field[600][600];
int springDist[600][600];
int goalDist[600][600];
const int dx[4] = { 1, 0, -1, 0 };
const int dy[4] = { 0, 1, 0, -1 };

int sx, sy;

inline bool Movable(int x, int y) {
  if (x < 0 || x >= w || y < 0 || y >= h) { return false; }
  if (field[y][x] != '.') { return false; }
  return true;
}

void CalcDist() {
  MEMSET(springDist, -1);
  MEMSET(goalDist, -1);
  queue<Point> que;
  REP(y, h) {
    REP(x, w) {
      if (field[y][x] == '*') {
        REP(dir, 4) {
          int nx = x + dx[dir];
          int ny = y + dy[dir];
          if (!Movable(nx, ny)) { continue; }
          que.push(Point(nx, ny, 1));
        }
      }
    }
  }
  while (!que.empty()) {
    Point p = que.front();
    que.pop();
    if (springDist[p.y][p.x] != -1) { continue; }
    springDist[p.y][p.x] = p.cost;
    REP(dir, 4) {
      int nx = p.x + dx[dir];
      int ny = p.y + dy[dir];
      if (!Movable(nx, ny)) { continue; }
      que.push(Point(nx, ny, p.cost + 1));
    }
  }

  REP(y, h) {
    REP(x, w) {
      if (field[y][x] == 'g') { que.push(Point(x, y, 0)); }
    }
  }
  while (!que.empty()) {
    Point p = que.front();
    que.pop();
    if (goalDist[p.y][p.x] != -1) { continue; }
    goalDist[p.y][p.x] = p.cost;
    REP(dir, 4) {
      int nx = p.x + dx[dir];
      int ny = p.y + dy[dir];
      if (!Movable(nx, ny)) { continue; }
      que.push(Point(nx, ny, p.cost + 1));
    }
  }
}

long double ToGoal(int x, int y, long double E) {
  long double ret = 1e+100;
  if (goalDist[y][x] != -1) { ret = min(ret, (long double)goalDist[y][x]); }
  if (springDist[y][x] != -1) { ret = min(ret, springDist[y][x] + E); }
  //cout << x << " " << y << endl;
  assert(goalDist[y][x] != -1 || springDist[y][x] != -1);
  return ret;
}

//long double vs[510 * 510];
long double calc(long double E) {
  long double nE = 0.0;
  int cnt = 0;
  //priority_queue<long double> que;
  REP(y, h) {
    REP(x, w) {
      if (field[y][x] != '.') { continue; }
      //vs[cnt++] = ToGoal(x, y, E);
      //que.push(-ToGoal(x, y, E));
      nE += ToGoal(x, y, E);
      cnt++;
    }
  }
  //sort(vs, vs + cnt);
  //REP(i, cnt - 1) {
    //nE += vs[i];
    //long double l = que.top();
    //que.pop();
    //long double r = que.top();
    //que.pop();
    //que.push(l + r);
  //}
  return nE / cnt;
}

int main() {
  while (scanf("%d %d", &w, &h) > 0) {
    REP(y, h) {
      scanf("%s", field[y]);
      REP(x, w) {
        if (field[y][x] == 's') {
          sx = x;
          sy = y;
          field[y][x] = '.';
        }
      }
    }
    CalcDist();
    long double left = 1.0;
    long double right = 1e+10;
    if (springDist[sy][sx] != -1) {
      REP(iter, 80) {
        long double mid = (left + right) / 2.0;
        if (calc(mid) > mid) {
          left = mid;
        } else {
          right = mid;
        }
      }
    }
    printf("%.10Lf\n", ToGoal(sx, sy, left));
  }
}

Measured memory_bytes: 4820