#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#define ll long long
#define pii pair<int,int>
#define pll pair<ll int,ll int>
#define pb push_back
#define sbit __builtin_popcount
#define vec vector <int>
#define ub upper_bound
#define lb lower_bound
#define bs binary_search
#define bg begin()
#define en end()
#define endl "\n"
#define ff first
#define ss second
#define all(x) x.bg,x.en
#define sz(a) (int)a.size()
#define min3(a,b,c) min(a,min(b,c))
#define max3(a,b,c) max(a,max(b,c))
#define sp(x) setprecision(x)
#define fill(a,b) memset(a,b,sizeof(a))
#define fr(i,n) for(int i=0;i<n;i++)
#define rp(i,a,b) for(int i=a;i<b;i++)
#define rr(i,a,b) for(int i=a;i>=b;i--)
#define ft(it,x) for(auto it : x)
#define dbg(x) cerr<<#x<<" "<<x<<endl;
#define dbga(arr,n) cerr<<#arr<<": "; rep(tt,0,n)cerr<<arr[tt]<<" "; cerr<<endl;
#define mapi map<int,int>
#define mapl map<ll int,ll int>
#define val_at(x) find_by_order(x)
#define idx_of(x) order_of_key(x)
#define scni(x) scanf("%d",&x)
#define prt(x) printf("%d\n",x)
#define test(t) int t;cin>>t;while(t--)
#define fast ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> myset;
const int mod=1e9+7;
const int mex=1e6+1;
inline ll power(ll a,ll b)
{
    ll ans=1,temp=a;
    while(b!=0)
    {
        if(b&1)
        ans=ans*temp;
        ans=ans%mod;
        temp=temp*temp;
        temp=temp%mod;
        b/=2;
    }
    return ans;
}
int pr[mex];
vec prime;
inline void seive()
{
    rp(i,2,mex) pr[i]=1;
    for(int i=2;i*i<mex;i++)
    {
        if(pr[i])
        for(int j=i*i;j<mex;j+=i)
        pr[j]=0;
    }
    vec prime;
    rp(i,2,mex)
    {
        if(pr[i]) prime.pb(i);
    }
}
int n,m;
int d1[4]={0,1,0,-1},d2[4]={-1,0,1,0};
char a[21][21];
bool check(int x,int y)
{
    if(x<0 || x>=n || y<0 || y>=m || a[x][y]=='#') return false;
    return true;
}
int dij(int x,int y)
{
    bool vis[n][m];
    int dist[n][m];
    fr(i,n) fr(j,m) dist[i][j]=INT_MAX;
    fill(vis,0);
    dist[x][y]=0;
    multiset < pair < int , pii > > s;
    s.insert({0,{x,y}});
    while(!s.empty()){

        pair <int , pii> p = *s.begin();
        s.erase(s.begin());

        int x1=p.ss.ff,y1=p.ss.ss;
        if(vis[x1][y1]) continue;
        vis[x1][y1] = true;
        fr(i,4)
        {
            int x2=x1+d1[i];
            int y2=y1+d2[i];
            if(!check(x2,y2)) continue;
            if(dist[x1][y1] + 1 < dist[x2][y2]  )
            {
                dist[x2][y2] = dist[x1][y1] + 1;
                s.insert({dist[x2][y2],{x2,y2}});   
            }
        }
    }
    int ans=-1;
    fr(i,n){
        fr(j,m){
            if(dist[i][j]!=INT_MAX)
            {
                ans=max(dist[i][j],ans);
            }
        }
    }
    return ans;
}
int main()
{
    fast
    cin>>n>>m;
    fr(i,n) fr(j,m) cin>>a[i][j];
    int ans1=-1;
    fr(i,n)
    {
        fr(j,m)
        {
            int ans=dij(i,j);
            ans1=max(ans,ans1);
        }
    }
    cout<<ans1;
}