//Utkarsh.25dec
#include <bits/stdc++.h>
#include <chrono>
#include <random>
#define ll long long int
#define ull unsigned long long int
#define pb push_back
#define mp make_pair
#define mod 1000000007
#define rep(i,n) for(ll i=0;i<n;i++)
#define loop(i,a,b) for(ll i=a;i<=b;i++)
#define vi vector <int>
#define vs vector <string>
#define vc vector <char>
#define vl vector <ll>
#define all(c) (c).begin(),(c).end()
#define max3(a,b,c) max(max(a,b),c)
#define min3(a,b,c) min(min(a,b),c)
using namespace std;
ll power(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll modInverse(ll a){return power(a,mod-2);}
const int N=30;
bool vis[N];
vector <int> adj[900];
char board[N+2][N+2];
map <pair<int,int>,int> indx;
int dist[900][900];
void solve()
{
    ll h,w;
    cin>>h>>w;
    int t=1;
    rep(i,N)
    {
        rep(j,N)
            board[i][j]='#';
    }
    for(int i=1;i<=h;i++)
    {
        string s;
        cin>>s;
        for(int j=0;j<s.length();j++)
        {
            board[i][j+1]=s[j];
            indx[mp(i,j+1)]=t++;
        }
    }
    for(int i=1;i<=h;i++)
    {
        for(int j=1;j<=w;j++)
        {
            if(board[i][j]!='.')
                continue;
            if(board[i-1][j]=='.')
            {
                adj[indx[mp(i,j)]].pb(indx[mp(i-1,j)]);
            }
            if(board[i+1][j]=='.')
            {
                adj[indx[mp(i,j)]].pb(indx[mp(i+1,j)]);
            }
            if(board[i][j-1]=='.')
            {
                adj[indx[mp(i,j)]].pb(indx[mp(i,j-1)]);
            }
            if(board[i][j+1]=='.')
            {
                adj[indx[mp(i,j)]].pb(indx[mp(i,j+1)]);
            }
        }
    }
    // return;
    for(int i=1;i<=h*w;i++)
    {
        for(int j=1;j<=h*w;j++)
        {
            dist[i][j]=1e5;
        }
    }
    // return;
    for(int i=1;i<=h*w;i++)
    {
        dist[i][i]=0;
        for(int j:adj[i])
            dist[i][j]=1;
    }
    for (int k = 1; k <= h*w; k++)  
    {  
        // Pick all vertices as source one by one  
        for (int i = 1; i <= h*w; i++)  
        {  
            // Pick all vertices as destination for the  
            // above picked source  
            for (int j = 1; j <= h*w; j++)  
            {  
                // If vertex k is on the shortest path from  
                // i to j, then update the value of dist[i][j]  
                if (dist[i][k] + dist[k][j] < dist[i][j])  
                    dist[i][j] = dist[i][k] + dist[k][j];  
            }  
        }  
    }  
    int ans=0;
    for(int i=1;i<=h;i++)
    {
        for(int j=1;j<=w;j++)
        {
            if(board[i][j]!='.')
                continue;
            for(int k=1;k<=h;k++)
            {
                for(int p=1;p<=w;p++)
                {
                    if(board[k][p]!='.')
                        continue;
                    ans=max(ans,dist[indx[mp(i,j)]][indx[mp(k,p)]]);
                }
            }
        }
    }
    cout<<ans<<'\n';
}
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int T=1;
    //cin>>T;
    int t=0;
    while(t++<T)
    {
        //cout<<"Case #"<<t<<":"<<' ';
        solve();
        //cout<<'\n';
    }
    cerr << "Time : " << 1000 * ((double)clock()) / (double)CLOCKS_PER_SEC << "ms\n";
}

Measured memory_bytes: 1792