#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
struct __INIT{__INIT(){cin.tie(0);ios::sync_with_stdio(false);cout<<fixed<<setprecision(15);}} __init;
#define max3(a,b,c) max(a,max(b,c))
#define min3(a,b,c) min(a,min(b,c))
#define MOD 1000000007
#define INF (1<<30)
#define LINF (lint)(1LL<<56)
#define endl "\n"
#define rep(i,n) for(int (i)=0;(i)<(n);(i)++)
#define reprev(i,n) for(int (i)=(n-1);(i)>=0;(i)--)
#define Flag(x) (1<<(x))
#define Flagcount(x) __builtin_popcount(x)
#define pint pair<int,int>
#define pdouble pair<double,double>
#define plint pair<lint,lint>
typedef unsigned long long int ull;
typedef long long lint;

int main(void){
    int H,W;
    cin >> H >> W;
    char S[H][W];
    rep(i,H) cin >> S[i];
    int ans=0;
    rep(i,H) rep(j,W){
        if(S[i][j]=='#') continue;
        int dis[H][W]={};
        rep(k,H) rep(l,W) dis[k][l]=INF;
        dis[i][j]=0;
        queue<pint> Q;
        bool reach[H][W]={};
        Q.push(pint(i,j));
        while(!Q.empty()){
            pint p=Q.front();
            Q.pop();
            reach[p.first][p.second]=true;
            if(p.first>0 && S[p.first-1][p.second]!='#' && reach[p.first-1][p.second]==false) Q.push(pint(p.first-1,p.second)),dis[p.first-1][p.second]=dis[p.first][p.second]+1;
            if(p.first<H-1 && S[p.first+1][p.second]!='#' && reach[p.first+1][p.second]==false) Q.push(pint(p.first+1,p.second)),dis[p.first+1][p.second]=dis[p.first][p.second]+1;
            if(p.second>0 && S[p.first][p.second-1]!='#' && reach[p.first][p.second-1]==false) Q.push(pint(p.first,p.second-1)),dis[p.first][p.second-1]=dis[p.first][p.second]+1;
            if(p.second<W-1 && S[p.first][p.second+1]!='#' && reach[p.first][p.second+1]==false) Q.push(pint(p.first,p.second+1)),dis[p.first][p.second+1]=dis[p.first][p.second]+1;
        }
        rep(k,H) rep(l,W) if(dis[k][l]!=INF) ans=max(ans,dis[k][l]);
    }
    cout << ans << endl;
}