#include <bits/stdc++.h>
using namespace std;
template <typename T>
istream &operator>>(istream &is, vector<T> &vec) {
  for (T &x : vec) is >> x;
  return is;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, pair<T, U> &pair_var) {
  os << "(" << pair_var.first << ", " << pair_var.second << ")";
  return os;
}
template <typename T>
ostream &operator<<(ostream &os, const vector<T> &vec) {
  os << "{";
  for (int i = 0; i < vec.size(); i++) {
    os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
  }
  os << "}\n   ";
  return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, map<T, U> &map_var) {
  os << "{";
  for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
    os << *itr;
    itr++;
    if (itr != map_var.end()) os << ", ";
    itr--;
  }
  os << "}";
  return os;
}
template <typename T>
ostream &operator<<(ostream &os, set<T> &set_var) {
  os << "{";
  for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
    os << *itr;
    itr++;
    if (itr != set_var.end()) os << ", ";
    itr--;
  }
  os << "}";
  return os;
}
void dump_func() { cerr << endl; }
template <class Head, class... Tail>
void dump_func(Head &&head, Tail &&...tail) {
  cerr << head;
  if (sizeof...(Tail) > 0) {
    cerr << ", ";
  }
  dump_func(std::move(tail)...);
}
int H, W;
vector<pair<int, int>> dirs = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
vector<vector<bool>> maze;
int bfs(pair<int, int> start, pair<int, int> goal) {
  queue<pair<int, int>> q = queue<pair<int, int>>();
  queue<int> move_q = queue<int>();
  set<pair<int, int>> visited = set<pair<int, int>>();
  q.push(start);
  move_q.push(0);
  while (!q.empty()) {
    auto c = q.front();
    auto move = move_q.front();
    if (c == goal) {
      return move;
    }
    visited.insert(c);
    q.pop();
    move_q.pop();
    for (auto dir : dirs) {
      int dx = dir.first;
      int dy = dir.second;
      int x = c.first;
      int y = c.second;
      int next_x = x + dx;
      int next_y = y + dy;
      if (next_x >= 0 && next_x < H && next_y >= 0 && next_y < W &&
          maze[next_x][next_y] &&
          visited.find({next_x, next_y}) == visited.end()) {
        q.push({next_x, next_y});
        move_q.push(move + 1);
      }
    }
  }
  return -1;
}
int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cin >> H >> W;
  maze = vector<vector<bool>>(H, vector<bool>(W));
  for (int h = 0, h_len = (H); h < h_len; ++h) {
    string s;
    cin >> s;
    for (int w = 0, w_len = (W); w < w_len; ++w) {
      if (s[w] == '#') {
        maze[h][w] = false;
      } else {
        maze[h][w] = true;
      }
    }
  }
  int max_move = 0;
  for (int hs = 0, hs_len = (H); hs < hs_len; ++hs) {
    for (int ws = 0, ws_len = (W); ws < ws_len; ++ws) {
      for (int hg = 0, hg_len = (H); hg < hg_len; ++hg) {
        for (int wg = 0, wg_len = (W); wg < wg_len; ++wg) {
          if (maze[hs][ws] && maze[hg][wg]) {
            int move = bfs({hs, ws}, {hg, wg});
            max_move = max(max_move, move);
          }
        }
      }
    }
  }
  cout << max_move << endl;
}
