#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
using namespace std;
template <class T>
inline bool chmax(T& a, T b) {
  if (a < b) {
    a = b;
    return 1;
  }
  return 0;
}
template <class T>
inline bool chmin(T& a, T b) {
  if (a > b) {
    a = b;
    return 1;
  }
  return 0;
}
struct io {
  io() {
    ios::sync_with_stdio(false);
    cin.tie(0);
  }
};
const long long int MOD = 1000000007;
const double EPS = 1e-9;
const int INF = INT_MAX;
const long long int LLINF = 1LL << 60;
struct Corr {
  int x;
  int y;
  int depth;
};
queue<Corr> q;
int bfs(vector<string> v, int gy, int gx) {
  vector<vector<bool> > ispass(v.size(), vector<bool>(v[0].size(), false));
  int dx[] = {1, 0, -1, 0};
  int dy[] = {0, -1, 0, 1};
  int res = INF;
  while (!q.empty()) {
    Corr now = q.front();
    q.pop();
    for (int i = 0; i < 4; i++) {
      Corr next = {now.x + dx[i], now.y + dy[i], now.depth + 1};
      if (next.x >= v[0].size() || next.x < 0 || next.y >= v.size() ||
          next.y < 0)
        continue;
      if (ispass[next.y][next.x]) continue;
      if (v[next.y][next.x] == '#') continue;
      if (next.y == gy && next.x == gx) {
        res = min(res, now.depth + 1);
        continue;
      }
      ispass[next.y][next.x] = 1;
      q.push(next);
    }
  }
  return (res == INF) ? 0 : res;
}
int main(void) {
  int h, w;
  cin >> h >> w;
  vector<string> v(h);
  for (int i = 0; i < (h); ++i) cin >> v[i];
  int ans = 0;
  for (int i = 0; i < (h); ++i) {
    for (int j = 0; j < (w); ++j) {
      for (int k = 0; k < (h); ++k) {
        for (int l = 0; l < (w); ++l) {
          if (i == k && j == l) {
            continue;
          }
          if (v[i][j] == '#' || v[k][l] == '#') continue;
          Corr c = {j, i, 0};
          q.push(c);
          int t = bfs(v, k, l);
          ans = max(ans, t);
        }
      }
    }
  }
  cout << (ans) << '\n';
}
