// The very basic /////////////////////////////////////////////////////////////
#include <bits/stdc++.h>
using namespace std;
#define ALL_H 1

// Print elements of containers using << operator /////////////////////////////
// pair
template<typename T1, typename T2> ostream& operator<<(ostream& s, const pair<T1, T2>& p) {
  return s << "(" << p.first << ", " << p.second << ")";}
// vector
template<typename T> ostream& operator<<(ostream& s, const vector<T>& v) {
  for (auto &elem : v) {
    s << elem;
    if (&elem != &v.back()) s << " ";
  }
  return s;
}
// 2 dimentional vector
template<typename T> ostream& operator<<(ostream& s, const vector< vector<T> >& vv) {
  for (auto &elem : vv) {
    s << elem;
    if (&elem != &vv.back()) s << endl;
  }
  return s;
}
// map
template<typename T1, typename T2> ostream& operator<<(ostream& s, const map<T1, T2>& m) {
  s << "{" << endl;
  for (auto &elem: m) {
    s << " " << elem.first << " : " << elem.second << endl;
  }
  s << "}";
  return s;
}
//set
template<typename T> ostream& operator<<(ostream& s, const set<T>& m) {
  s << "{ ";
  for (auto &elem: m) {
    s << elem << " ";
  }
  s << "}";
  return s;
}

// Convenient macros //////////////////////////////////////////////////////////

// types
#define ll long long
#define ld long double
#define int ll
#define Pi pair<int, int>
#define Graph vector<vector<int>>
#define WeightGraph vector<vector<Pi>>

// other
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define all(x) (x).begin(),(x).end()
#define perm(c) sort(all(c));for(bool c##p=1;c##p;c##p=next_permutation(all(c)))

#define MOD 1000000007
#define INF LLONG_MAX/100

// update max or min //////////////////////////////////////////////////////////

template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return 1; } return 0; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return 1; } return 0; }

// gcd
int gcd(int a,int b){return b?gcd(b,a%b):a;}

// direction
const int dx[4] = {1, 0, -1, 0};
const int dy[4] = {0, 1, 0, -1};


// Debug it by D(x) ///////////////////////////////////////////////////////////
#define DEBUG 0
#if DEBUG
#define D(x) x
#else
#define D(x)
#endif

///////////////////////////////////////////////////////////////////////////////
//                             Start Coding Here                             //
///////////////////////////////////////////////////////////////////////////////
// must init
int H, W;
vector<string> field;
vector<vector<int>> dist;

void bfs(int sh, int sw){
  queue<Pi> q;
  q.emplace(sh, sw);
  dist[sh][sw] = 0;
  while(!q.empty()) {
    int h = q.front().first;
    int w = q.front().second; q.pop();
    rep(dir, 4) {
      int nh = h + dx[dir];
      int nw = w + dy[dir];
      // Out of range condition
      D(cout << "here2" << endl);
      D(cout << nh << " " << H << endl);
      D(cout << nw << " " << W << endl);
      if (nh < 0 || nh >= H || nw < 0 || nw >= W) continue;
      // Wall condition
      D(cout << "here3" << endl);
      if (field[nh][nw] == '#') continue;
      // Seen(dist) condition
      D(cout << "here4" << endl);
      if (dist[nh][nw] != INF) continue;
      q.emplace(nh, nw);

      dist[nh][nw] = dist[h][w] + 1;
    }
  }
};

signed main() {
  cin >> H >> W;
  field.resize(H);
  rep(i, H) cin >> field[i];
  int ans = -1;
  rep(sh, H) rep(sw, W) {
    if (field[sh][sw] == '#') continue;
    dist.assign(H, vector<int>(W, INF));
    D(cout << dist << endl);
    bfs(sh, sw);
    D(cout << "here1" << endl);
    D(cout << dist << endl);
    rep(i, H) rep(j, W) {
      if (dist[i][j] == INF) continue;
      else chmax(ans, dist[i][j]);
    }
  }
  cout << ans << endl;
}



Measured memory_bytes: 768