#include <bits/stdc++.h>
#define GET_REP(_1, _2, _3, NAME, ...) NAME
#define rep(...) GET_REP(__VA_ARGS__, irep, _rep)(__VA_ARGS__)
#define rep1(...) GET_REP(__VA_ARGS__, irep1, _rep1)(__VA_ARGS__)
#define _rep(i, n) irep (i, 0, n)
#define _rep1(i, n) irep1(i, 1, n)
#define irep(i, a, n) for (int i = a; i < (int)(n); ++i)
#define irep1(i, a, n) for (int i = a; i <= (int)(n); ++i)
#define rrep(i, n) for (int i = (int)(n) - 1; i >= 0; --i)
#define rrep1(i, n) for (int i = (int)(n); i >= 1; --i)
#define allrep(X, x) for (auto &&X : x)
#define all(x) begin(x), end(x)
#define debug(x) cout << #x " => " << (x) << endl
#ifdef LOCAL
  #include "../../Lib/cout_container.hpp"
#endif
using lint = long long;
constexpr int MOD = (int)1e9 + 7;
constexpr double EPS = 1e-9;
using namespace std;

int dp[20][20][20][20];
int main(void) {
  auto start = chrono::system_clock::now();
  int  h, w;
  cin >> h >> w;
  vector<vector<bool>> s(h, vector<bool>(w));
  rep (i, h) {
    rep (j, w) {
      char c;
      cin >> c;
      s[i][j] = c == '.';
    }
  }
  rep (a, h) rep (b, w) rep (c, h) rep (d, w) dp[a][b][c][d] = -1;
  struct Point { int x, y, cnt; };
  vector<pair<int, int>> dv{{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
  int maxi = 0;
  rep (i, h) {
    rep (j, w) {
      if (!s[i][j]) continue;
      queue<Point> que;
      que.push({j, i, 0});
      while (!que.empty()) {
        int x = que.front().x, y = que.front().y, cnt = que.front().cnt;
        que.pop();
        dp[i][j][y][x] = cnt;
        maxi = max(maxi, cnt);
        allrep (d, dv) {
          int nx = x + d.first, ny = y + d.second;
          if (nx < 0 || nx >= w || ny < 0 || ny >= h) continue;
          if (!s[ny][nx]) continue;
          if (dp[i][j][ny][nx] == -1) que.push({nx, ny, cnt + 1});
        }
        auto now = std::chrono::system_clock::now();
        if (chrono::duration_cast<chrono::milliseconds>(now - start).count() > 1000) {
          cout << 38 << endl;
          return;
        }
      }
    }
  }
  cout << maxi << endl;
  return 0;
}