#include <bits/stdc++.h>
using namespace std;
template <class T1, class T2>
inline bool minimize(T1& a, T2 b) {
  return b < a && (a = b, 1);
}
template <class T1, class T2>
inline bool maximize(T1& a, T2 b) {
  return a < b && (a = b, 1);
}
template <class T, class V>
istream& operator>>(istream& ist, pair<T, V>& p) {
  return ist >> p.first >> p.second;
}
template <class T>
ostream& operator<<(ostream& ost, pair<T, T>& p) {
  return ost << p.first << ", " << p.second;
}
template <class T>
istream& operator>>(istream& ist, vector<T>& vs) {
  for (auto& e : vs) ist >> e;
  return ist;
}
int const inf = INT_MAX / 2;
int dy[] = {-1, 0, 1, 0};
int dx[] = {0, -1, 0, 1};
bool in_field(int y, int x, int H, int W) {
  return 0 <= y && y < H && 0 <= x && x < W;
}
int get_max_distance(vector<string> const& F, int H, int W, int si, int sj) {
  queue<pair<int, int>> q;
  q.emplace(si, sj);
  vector<vector<int>> dist(H, vector<int>(W, inf));
  dist[si][sj] = 0;
  int ans = 0;
  while (!q.empty()) {
    const auto p = q.front();
    q.pop();
    const auto y = p.first;
    const auto x = p.second;
    for (int k = 0; k < (int)4; k++) {
      const auto ny = y + dy[k];
      const auto nx = x + dx[k];
      if (!in_field(ny, nx, H, W)) {
        continue;
      }
      if (F[ny][nx] == '#') {
        continue;
      }
      if (dist[ny][nx] < inf) {
        continue;
      }
      dist[ny][nx] = dist[y][x] + 1;
      ans = max(ans, dist[ny][nx]);
      q.emplace(ny, nx);
    }
  }
  return ans;
}
int main() {
  int H, W;
  cin >> H >> W;
  vector<string> F(H);
  for (int i = 0; i < (int)H; i++) cin >> F[i];
  int ans = 0;
  for (int i = 0; i < (int)H; i++)
    for (int j = 0; j < (int)W; j++) {
      ans = max(ans, get_max_distance(F, H, W, i, j));
    }
  cout << ans << endl;
}
