#include <bits/stdc++.h>
int dy[] = {1, 0, -1, 0};
int dx[] = {0, 1, 0, -1};
int ny, nx;
using namespace std;
long long gcd(long long a, long long b) { return b ? gcd(b, a % b) : a; }
long long lcm(long long m, long long n) {
  if ((0 == m) || (0 == n)) return 0;
  return ((m / gcd(m, n)) * n);
}
long long llpow(long long x, long long y) {
  long long ans = 1;
  for (int i = 0, i_len = (y); i < i_len; ++i) ans *= x;
  return ans;
}
int ctoi(char c) {
  if (c >= '0' && c <= '9') {
    return c - '0';
  }
  return 0;
}
class UnionFind {
 public:
  vector<long long> par;
  vector<long long> siz;
  UnionFind(long long sz_) : par(sz_), siz(sz_, 1LL) {
    for (long long i = 0; i < sz_; ++i) par[i] = i;
  }
  void init(long long sz_) {
    siz.assign(sz_, 1LL);
    par.resize(sz_);
    for (long long i = 0; i < sz_; ++i) par[i] = i;
  }
  long long root(long long x) {
    while (par[x] != x) {
      x = par[x] = par[par[x]];
    }
    return x;
  }
  bool merge(long long x, long long y) {
    x = root(x);
    y = root(y);
    if (x == y) return false;
    if (siz[x] < siz[y]) swap(x, y);
    siz[x] += siz[y];
    par[y] = x;
    return true;
  }
  bool issame(long long x, long long y) { return root(x) == root(y); }
  long long size(long long x) { return siz[root(x)]; }
};
template <class T>
inline bool chmin(T &a, T b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}
template <class T>
inline bool chmax(T &a, T b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}
template <typename Container>
bool exist_in(const Container &c, const typename Container::value_type &v) {
  return (c.end() != std::find(c.begin(), c.end(), v));
}
vector<long long> decom;
void decompositPrime(long long n) {
  long long a = 2;
  while (n >= a * a) {
    if (n % a == 0) {
      decom.push_back(a);
      n /= a;
    } else {
      a++;
    }
  }
  decom.push_back(n);
}
int ctoi_alpha(char c) {
  if (c >= 'a' && c <= 'z') {
    return c - 'a';
  }
  return 0;
}
const int MOD = 1000000007;
vector<vector<int>> calcNext(const string &S) {
  int n = (int)S.size();
  vector<vector<int>> res(n + 1, vector<int>(26, n));
  for (int i = n - 1; i >= 0; --i) {
    for (int j = 0; j < 26; ++j) res[i][j] = res[i + 1][j];
    res[i][S[i] - 'a'] = i;
  }
  return res;
}
void add(long long &a, long long b) {
  a += b;
  if (a >= MOD) a -= MOD;
}
int H, W;
char S[21][21];
int max_dist = 0;
int bfs() {
  for (int sx = 0, sx_len = (H); sx < sx_len; ++sx) {
    for (int sy = 0, sy_len = (W); sy < sy_len; ++sy) {
      queue<pair<int, int>> que;
      int cnt[H][W];
      for (int i = 0, i_len = (H); i < i_len; ++i)
        for (int j = 0, j_len = (W); j < j_len; ++j) {
          if (S[i][j] == '#') continue;
          cnt[i][j] = 1145141919;
        }
      que.push(pair<int, int>(sx, sy));
      cnt[sx][sy] = 0;
      while (que.size()) {
        pair<int, int> p = que.front();
        que.pop();
        for (int i = 0, i_len = (4); i < i_len; ++i) {
          int nx = p.first + dx[i];
          int ny = p.second + dy[i];
          if (0 <= nx && nx < H && 0 <= ny && ny < W && S[nx][ny] != '#' &&
              cnt[nx][ny] == 1145141919) {
            que.push(pair<int, int>(nx, ny));
            cnt[nx][ny] = cnt[p.first][p.second] + 1;
          }
        }
      }
      int dist_goal = 0;
      for (int i = 0, i_len = (H); i < i_len; ++i) {
        for (int j = 0, j_len = (W); j < j_len; ++j) {
          if (S[i][j] != '#') dist_goal = max(dist_goal, cnt[i][j]);
        }
      }
      max_dist = max(max_dist, dist_goal);
    }
  }
  return max_dist;
}
int main() {
  cin >> H >> W;
  for (int i = 0, i_len = (H); i < i_len; ++i)
    for (int j = 0, j_len = (W); j < j_len; ++j) cin >> S[i][j];
  cout << bfs() << endl;
  return 0;
}
