#include <bits/stdc++.h>
#pragma GCC optimize("O2")
#pragma GCC target("avx,avx2")
using namespace std;
using u64 = uint64_t;
using i64 = int64_t;
using u32 = uint32_t;
using vi = vector<int>;
using vi64 = vector<i64>;
int main(int argc, char *argv[]) {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int h, w;
  cin >> h >> w;
  vector<string> M;
  for (int _ = (0); _ < (h); ++_) {
    string s;
    cin >> s;
    M.push_back(s);
  }
  auto at = [&](int x, int y) {
    if (0 <= x && x < w && 0 <= y && y < h) {
      return M[y][x] == '.';
    }
    return false;
  };
  vector<pair<int, int>> roads;
  for (int i = (0); i < (h); ++i)
    for (int j = (0); j < (w); ++j) {
      if (at(j, i)) roads.push_back({j, i});
    }
  auto dist = [&](int aa, int bb, int cc, int dd) {
    deque<tuple<int, int, int>> Q;
    Q.push_back({0, aa, bb});
    set<pair<int, int>> seen;
    while (!Q.empty()) {
      int d, x, y;
      tie(d, x, y) = Q.front();
      Q.pop_front();
      if (x == cc && y == dd) return d;
      if (seen.count({x, y})) continue;
      seen.insert({x, y});
      if (at(x, y + 1)) Q.push_back({d + 1, x, y + 1});
      if (at(x, y - 1)) Q.push_back({d + 1, x, y - 1});
      if (at(x + 1, y)) Q.push_back({d + 1, x + 1, y});
      if (at(x - 1, y)) Q.push_back({d + 1, x - 1, y});
    }
    return -1;
  };
  int mx = 0;
  for (auto a : roads)
    for (auto b : roads) {
      mx = max(mx, dist(get<0>(a), get<1>(a), get<0>(b), get<1>(b)));
    }
  cout << mx << endl;
}
