#include <bits/stdc++.h>
using namespace std;
inline int toInt(string s) {
  int v;
  istringstream sin(s);
  sin >> v;
  return v;
}
template <class T>
inline string toString(T x) {
  ostringstream sout;
  sout << x;
  return sout.str();
}
template <class T>
inline T sqr(T x) {
  return x * x;
}
const double EPS = 1e-10;
const double PI = acos(-1.0);
const int INF = (int)1000000007;
const long long MOD = (long long)1000000007;
const long long INF2 = (long long)100000000000000000;
int ans = 1;
void grid_bfs(vector<string> &s, char start, const string &wall = "#") {
  const int vx[] = {0, 1, 0, -1}, vy[] = {1, 0, -1, 0};
  vector<vector<int> > min_cost(s.size(), vector<int>(s[0].size(), -1));
  queue<pair<int, int> > que;
  for (int i = 0; i < s.size(); i++) {
    for (int j = 0; j < s[i].size(); j++) {
      if (s[i][j] == start) {
        que.emplace(i, j);
        min_cost[i][j] = 0;
      }
    }
  }
  while (!que.empty()) {
    auto p = que.front();
    que.pop();
    for (int i = 0; i < 4; i++) {
      int ny = p.first + vy[i], nx = p.second + vx[i];
      if (nx < 0 || ny < 0 || nx >= s[0].size() || ny >= s.size()) continue;
      if (min_cost[ny][nx] != -1) continue;
      if (wall.find(s[ny][nx]) != string::npos) continue;
      min_cost[ny][nx] = min_cost[p.first][p.second] + 1;
      ans = max(ans, min_cost[ny][nx]);
      que.emplace(ny, nx);
    }
  }
}
int main() {
  int h, w;
  cin >> h >> w;
  vector<string> tb(h);
  for (int i = 0; i < h; i++) {
    cin >> tb[i];
  }
  for (int i = 0; i < h; i++) {
    for (int j = 0; j < w; j++) {
      char tmp = tb[i][j];
      tb[i][j] = 's';
      grid_bfs(tb, 's', "#");
      tb[i][j] = tmp;
    }
  }
  cout << ans << endl;
  return 0;
}
