#include <bits/stdc++.h>
using namespace std;
char wall = '#';
vector<tuple<long long, long long, long long>> dfs(
    vector<vector<char>> maze, pair<long long, long long> start) {
  vector<tuple<long long, long long, long long>> res;
  queue<tuple<long long, long long, long long>> q;
  long long M = -1;
  vector<vector<long long>> already_went(
      maze.size(), vector<long long>(maze.at(0).size(), 0));
  tuple<long long, long long, long long> temp;
  get<0>(temp) = start.first;
  get<1>(temp) = start.second;
  get<2>(temp) = 0;
  q.push(temp);
  while (!q.empty()) {
    tuple<long long, long long, long long> check = q.front();
    q.pop();
    if (get<2>(check) > M) {
      M = get<2>(check);
      res = {make_tuple(get<0>(check), get<1>(check), M)};
    }
    if (get<2>(check) == M) {
      res.push_back(make_tuple(get<0>(check), get<1>(check), M));
    }
    if (maze.at(get<0>(check) + 1).at(get<1>(check)) != wall &&
        already_went.at(get<0>(check) + 1).at(get<1>(check)) != 1) {
      get<0>(temp) = get<0>(check) + 1;
      get<1>(temp) = get<1>(check);
      get<2>(temp) = get<2>(check) + 1;
      q.push(temp);
      already_went.at(get<0>(check) + 1).at(get<1>(check)) = 1;
    }
    if (maze.at(get<0>(check)).at(get<1>(check) + 1) != wall &&
        already_went.at(get<0>(check)).at(get<1>(check) + 1) != 1) {
      get<0>(temp) = get<0>(check);
      get<1>(temp) = get<1>(check) + 1;
      get<2>(temp) = get<2>(check) + 1;
      q.push(temp);
      already_went.at(get<0>(check)).at(get<1>(check) + 1) = 1;
    }
    if (maze.at(get<0>(check) - 1).at(get<1>(check)) != wall &&
        already_went.at(get<0>(check) - 1).at(get<1>(check)) != 1) {
      get<0>(temp) = get<0>(check) - 1;
      get<1>(temp) = get<1>(check);
      get<2>(temp) = get<2>(check) + 1;
      q.push(temp);
      already_went.at(get<0>(check) - 1).at(get<1>(check)) = 1;
    }
    if (maze.at(get<0>(check)).at(get<1>(check) - 1) != wall &&
        already_went.at(get<0>(check)).at(get<1>(check) - 1) != 1) {
      get<0>(temp) = get<0>(check);
      get<1>(temp) = get<1>(check) - 1;
      get<2>(temp) = get<2>(check) + 1;
      q.push(temp);
      already_went.at(get<0>(check)).at(get<1>(check) - 1) = 1;
    }
  }
  return res;
}
int main() {
  long long H, W;
  cin >> H >> W;
  vector<vector<char>> maze(H + 2, vector<char>(W + 2, '#'));
  pair<long long, long long> start;
  for (long long i = 1; i < H + 1; ++i) {
    for (long long j = 1; j < W + 1; ++j) {
      cin >> maze.at(i).at(j);
      if (maze.at(i).at(j) == '.') {
        start = {i, j};
      }
    }
  }
  vector<vector<bool>> already(H + 2, vector<bool>(W + 2, false));
  vector<tuple<long long, long long, long long>> now = {
      make_tuple(start.first, start.second, 0)};
  long long ans = 0;
  while (now.size() != 0) {
    vector<tuple<long long, long long, long long>> next;
    for (long long i = 0; i < now.size(); ++i) {
      vector<tuple<long long, long long, long long>> temp =
          dfs(maze, {get<0>(now.at(i)), get<1>(now.at(i))});
      for (long long j = 0; j < temp.size(); ++j) {
        ans = max(ans, get<2>(temp.at(j)));
        if (already.at(get<0>(temp.at(j))).at(get<1>(temp.at(j)))) {
          continue;
        }
        already.at(get<0>(temp.at(j))).at(get<1>(temp.at(j))) = true;
        next.push_back(temp.at(j));
      }
    }
    now = next;
  }
  cout << ans << endl;
}
