#include <bits/stdc++.h>

using namespace std;

#define REP(i,a,b) for(int i=a;i<(int)b;i++)
#define rep(i,n) REP(i,0,n)
#define all(c) (c).begin(), (c).end()
#define zero(a) memset(a, 0, sizeof a)
#define minus(a) memset(a, -1, sizeof a)
#define watch(a) { std::cout << #a << " = " << a << "\n"; }
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; }

typedef long long ll;
int const inf = INT_MAX / 2;

const int dy[] = {-1,0,1,0};
const 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;
    rep(k, 4) {
      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);
  rep(i, H) cin >> F[i];
  int ans = 0;
  rep(i, H) rep(j, W) {
    if (F[si][sj] == '#') { continue; }
    ans = max(ans, get_max_distance(F, H, W, i, j));
  }
  cout << ans << endl;
}
