#include <bits/stdc++.h>
using namespace std;
const int TABLEMAX = 510000;
const int MOD = 1000000007;
int fac[TABLEMAX];
int finv[TABLEMAX / 2], inv[TABLEMAX / 2];
const int ddx[8] = {0, 1, 1, 1, 0, -1, -1, -1};
const int ddy[8] = {1, 1, 0, -1, -1, -1, 0, 1};
const int dx[4] = {0, 1, 0, -1};
const int dy[4] = {-1, 0, 1, 0};
static const int NIL = -1;
vector<long long> primes;
vector<bool> isprime;
vector<long long> min_factor;
vector<long long> factor_list;
long long n, N, k, K, m, M;
template <class X>
void printArray(X *array, long long);
template <class X>
void printVector(vector<X> &v);
template <class X>
void printDimention(vector<vector<X>> &dv);
template <class X>
void printMap(X);
template <class X>
void printVecPair(vector<X> &v);
long long factor2(long long);
long long combination(long long, long long);
int factorial(int, int);
long long find_digits(long long);
int findSumOfDigits(int);
int degree(int);
int shishagonyu(double);
int roundUp(double);
int roundDown(double);
int roundOff(double);
long long gcd(long long, long long);
long long lcm(long long, long long);
void Eratosthenes(long long);
vector<pair<long long, long long>> prime_factorization(long long);
bool odd_even(int);
long long modinv(long long, long long);
long long modpow(long long, long long, long long);
void COMinit();
long long COM(long long, long long);
long long abs_ll(long long);
long long div_one(long long, long long);
template <class X>
void StringtoVec(string, vector<X> &v);
void get_factor_list(long long);
long long RepeatSquaring(long long N, long long P, long long M);
vector<vector<char>> S;
queue<pair<long long, long long>> Q;
vector<vector<long long>> d;
long long H, W;
long long res = 0;
void bfs() {
  pair<long long, long long> u;
  while (!Q.empty()) {
    u = Q.front();
    Q.pop();
    long long uy = u.first, ux = u.second;
    for (long long i = (0); i < (4); ++i) {
      long long next_y = uy + dy[i], next_x = ux + dx[i];
      if (next_y < 0 || H <= next_y || next_x < 0 || W < next_x) continue;
      if (S[next_y][next_x] == '#') continue;
      if (d[next_y][next_x] != 1000000000000) continue;
      d[next_y][next_x] = d[uy][ux] + 1;
      res = max(d[next_y][next_x], res);
      Q.push(make_pair(next_y, next_x));
    }
  }
}
void bfs_init(long long y, long long x) {
  d.assign(H, vector<long long>(W, 1000000000000));
  Q.push(make_pair(y, x));
  d[y][x] = 0;
  bfs();
}
int main(int argc, char const *argv[]) {
  cin.tie(0);
  ios::sync_with_stdio(false);
  cin >> H >> W;
  S.resize(H, vector<char>(W));
  d.resize(H, vector<long long>(W, 1000000000000));
  for (long long i = (0); i < (H); ++i) {
    for (long long j = (0); j < (W); ++j) {
      cin >> S[i][j];
    }
  }
  for (long long i = (0); i < (H); ++i) {
    for (long long j = (0); j < (W); ++j) {
      bfs_init(i, j);
    }
  }
  cout << res << endl;
  return 0;
}
template <class X>
void printArray(X array[], long long n) {
  for (long long i = (0); i < (n); ++i) {
    if (i) cout << " ";
    cout << array[i];
  }
  cout << endl;
}
template <class X>
void printVector(vector<X> &v) {
  for (long long i = (0); i < ((int)v.size()); ++i) {
    if (i) cout << " ";
    cout << v[i];
  }
  cout << endl;
}
template <class X>
void printDimention(vector<vector<X>> &dv) {
  for (long long i = (0); i < ((long long)dv.size()); ++i) {
    for (long long j = (0); j < ((long long)dv[i].size()); ++j) {
      if (j) cout << " ";
      cout << dv[i][j];
    }
    cout << endl;
  }
}
template <class X>
void printMap(X mp) {
  for (auto it : mp) {
    cout << it.first << " " << it.second << endl;
  }
}
template <class X>
void printVecPair(vector<X> &v) {
  for (long long i = (0); i < ((int)v.size()); ++i) {
    cout << v[i].first << " " << v[i].second << endl;
  }
  cout << endl;
}
long long factor2(long long num) {
  int cnt = 0;
  while (num % 2 == 0) {
    num /= 2;
    cnt++;
  }
  return cnt;
}
long long combination(long long n, long long r) {
  if (n == 0 || r == 0 || n < r) return 0;
  long long up = 1, down = 1;
  long long half = n / 2;
  if (r >= half) r = n - r;
  long long up_tmp = n, down_tmp = r;
  if (r == 0) return 1;
  for (long long i = (0); i < (r); ++i) {
    up *= up_tmp;
    up_tmp--;
    down *= down_tmp;
    down_tmp--;
  }
  return up / down;
}
int factorial(int n, int r) {
  if (n < r) {
    cout << "error" << endl;
    return 0;
  } else {
    int res = 1;
    while (r) {
      r--;
      res *= n;
      n--;
    }
    return res;
  }
}
long long find_digits(long long n) {
  int digit = 0;
  while (n) {
    n /= 10;
    digit++;
  }
  return digit;
}
int findSumOfDigits(int n) {
  int sum = 0;
  while (n > 0) {
    sum += n % 10;
    n /= 10;
  }
  return sum;
}
int degree(int n) {
  int count = 0;
  while (1) {
    if (count <= 0) break;
    count /= 2;
    count++;
  }
  return count;
}
int shishagonyu(double x) { return (int)(x < 0.0 ? x - 0.5 : x + 0.5); }
int roundUp(double n) {
  if (n >= 0) {
    return (int)n + 1;
  } else {
    return (int)n - 1;
  }
}
int roundDown(double n) { return (int)n; }
int roundOff(double n) {
  double decimal = 0;
  decimal = n - (int)n;
  if (decimal >= 0.5 || decimal <= -0.5) {
    return roundUp(n);
  } else {
    return roundDown(n);
  }
}
long long gcd(long long a, long long b) { return b ? gcd(b, a % b) : a; }
long long lcm(long long m, long long n) {
  if (m == 0 || n == 0) return 0;
  return ((m / gcd(m, n)) * n);
}
void Eratosthenes(long long n) {
  isprime.resize(n + 1, true);
  min_factor.resize(n + 1, -1);
  isprime[0] = isprime[1] = false;
  min_factor[0] = 0, min_factor[1] = 1;
  for (int i = 2; i < n; i++) {
    if (!isprime[i]) continue;
    primes.push_back(i);
    min_factor[i] = i;
    for (int j = i * 2; j <= n; j += i) {
      isprime[j] = false;
      if (min_factor[j] == -1) min_factor[i] = 1;
    }
  }
}
vector<pair<long long, long long>> prime_factorization(long long n) {
  vector<pair<long long, long long>> res;
  Eratosthenes(n);
  bool flg = false;
  if (n < 2) {
    cout << "Error" << endl;
    return res;
  }
  while (n != 1) {
    int prime = min_factor[n];
    int cnt = 0;
    while (min_factor[n] == prime) {
      cnt++;
      n /= prime;
    }
    res.push_back(make_pair(prime, cnt));
  }
  return res;
}
bool odd_even(int num) {
  bool judge;
  if (num % 2) {
    judge = true;
  } else {
    judge = false;
  }
  return judge;
}
long long modinv(long long a, long long m) {
  long long b = m, u = 1, v = 0;
  while (b) {
    long long t = a / b;
    a -= t * b;
    swap(a, b);
    u -= t * v;
    swap(u, v);
  }
  u %= m;
  if (u < 0) u += m;
  return u;
}
long long modpow(long long a, long long n, long long mod) {
  long long res = 1;
  while (n > 0) {
    if (n & 1) res = res * a % mod;
    a = a * a % mod;
    n >>= 1;
  }
  return res;
}
void COMinit() {
  fac[0] = fac[1] = 1;
  finv[0] = finv[1] = 1;
  inv[1] = 1;
  for (int i = 2; i < TABLEMAX; i++) {
    fac[i] = fac[i - 1] * i % MOD;
  }
  for (int i = 2; i < TABLEMAX / 2; i++) {
    inv[i] = MOD - inv[MOD % i] * (MOD / i) % MOD;
    finv[i] = finv[i - 1] * inv[i] % MOD;
  }
}
long long COM(long long n, long long k) {
  if (n < k) return 0;
  if (n < 0 || k < 0) return 0;
  return fac[n] * (finv[k] * finv[n - k] % MOD) % MOD;
}
long long abs_ll(long long a) {
  if (a >= 0) {
    return a;
  } else {
    return -a;
  }
}
long long div_one(long long res, long long div) {
  return (res + div - 1) / div;
}
template <class X>
void StringtoVec(string str, vector<X> &v) {
  long long num = stoll(str);
  long long it = str.size() - 1;
  v.resize(str.size());
  while (num) {
    v[it] = num % 10;
    num /= 10;
    it--;
    if (it < 0) {
      cout << "reference error" << endl;
      break;
    }
  }
}
void get_factor_list(long long n) {
  int div = 1, res;
  while (n >= div * div) {
    if (n % div == 0) {
      res = div;
      long long tmp = n / res;
      factor_list.push_back(res);
      if (tmp != 1) factor_list.push_back(tmp);
      div++;
    } else {
      div++;
    }
  }
  sort(factor_list.begin(), factor_list.end());
}
long long RepeatSquaring(long long N, long long P, long long M) {
  if (P == 0) return 1;
  if (P % 2 == 0) {
    long long tmp = RepeatSquaring(N, P / 2, M);
    return tmp * tmp % M;
  } else {
    return N * RepeatSquaring(N, P - 1, M) % M;
  }
}
