#include <bits/stdc++.h>
#define REP(i, n) for(int i = 0, i##_len = (n); i < i##_len; ++i)
#define REPS(i, _s, n) for(int i = _s, i##_len = (n); i < i##_len; ++i)
#define ALL(x) (x).begin(), (x).end()
using namespace std;

#define repi(itr, ds) for(auto itr = ds.begin(); itr != ds.end(); itr++)
// vector
template <typename T>
istream &operator>>(istream &is, vector<T> &vec) {
  for(T &x : vec) is >> x;
  return is;
}
// pair
template <typename T, typename U>
ostream &operator<<(ostream &os, pair<T, U> &pair_var) {
  os << "(" << pair_var.first << ", " << pair_var.second << ")";
  return os;
}
// vector
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;
}
// map
template <typename T, typename U>
ostream &operator<<(ostream &os, map<T, U> &map_var) {
  os << "{";
  repi(itr, map_var) {
    os << *itr;
    itr++;
    if(itr != map_var.end()) os << ", ";
    itr--;
  }
  os << "}";
  return os;
}
// set
template <typename T>
ostream &operator<<(ostream &os, set<T> &set_var) {
  os << "{";
  repi(itr, set_var) {
    os << *itr;
    itr++;
    if(itr != set_var.end()) os << ", ";
    itr--;
  }
  os << "}";
  return os;
}

#define DUMPOUT cerr

void dump_func() { DUMPOUT << endl; }
template <class Head, class... Tail>
void dump_func(Head &&head, Tail &&... tail) {
  DUMPOUT << head;
  if(sizeof...(Tail) > 0) {
    DUMPOUT << ", ";
  }
  dump_func(std::move(tail)...);
}
#ifdef DEBUG_
#define DEB
#define dump(...)                                                             \
  DUMPOUT << "  " << string(#__VA_ARGS__) << ": "                             \
          << "[" << to_string(__LINE__) << ":" << __FUNCTION__ << "]" << endl \
          << "    ",                                                          \
      dump_func(__VA_ARGS__)
#else
#define DEB if(false)
#define dump(...)
#endif

typedef long long int ll;
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) {
  queue<pair<int, int>> q = queue<pair<int, int>>();
  queue<int> move_q = queue<int>();
  set<pair<int, int>> visited = set<pair<int, int>>();
  int max_move = 0;
  visited.insert(start);
  q.push(start);
  move_q.push(0);
  while(!q.empty()) {
    auto c = q.front();
    // dump(c);
    auto move = move_q.front();
    max_move = max(move, max_move);
    q.pop();
    move_q.pop();
    int x = c.first;
    int y = c.second;
    for(auto dir : dirs) {
      int dx = dir.first;
      int dy = dir.second;
      int next_x = x + dx;
      int next_y = y + dy;

      pair<int, int> next_p = {next_x, next_y};
      if(next_x >= 0 && next_x < H && next_y >= 0 && next_y < W && maze[next_x][next_y] && visited.find(next_p) == visited.end()) {
        visited.insert(next_p);
        q.push(next_p);
        move_q.push(move + 1);
      }
    }
  }
  return max_move;
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cin >> H >> W;
  maze = vector<vector<bool>>(H, vector<bool>(W));
  REP(h, H) {
    string s;
    cin >> s;
    REP(w, W) {
      if(s[w] == '#') {
        maze[h][w] = false;
      } else {
        maze[h][w] = true;
      }
    }
  }

  int ans = 0;
  REP(hs, H) {
    REP(ws, W) {
      if(maze[hs][ws]) {
        int max_move = bfs({hs, ws});
        ans = max(max_move, ans);
      }
    }
  }
  cout << ans << endl;
}


Measured memory_bytes: 256