/*---> 16 January 2020 <--- > 12:19:48 <---*/
// #pragma GCC optimize("Ofast")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
// #pragma GCC optimize("unroll-loops")
// #pragma comment(linker, "/stack:200000000")
#pragma GCC optimize("O3")
//#pragma GCC target ("sse4")
#include <bits/stdc++.h>
//#include <ext/pb_ds/tree_policy.hpp>
//#include <ext/pb_ds/assoc_container.hpp>
//using namespace __gnu_pbds;
using namespace std;
//template <class T> using Tree = tree<T, null_type, less<T>, rb_tree_tag,tree_order_statistics_node_update>;
#define int long long
#define F first
#define S second
#define mod 1000000007
#define inf (int)1e15+5
#define sz(x) (int)x.size()
#define PI 3.141592653589793238510
#define all(x) (x).begin(),(x).end()
#define rall(x) (x).rbegin(),(x).rend()
#define __ ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define vi vector<int>
#define vpii vector<pair<int,int> > 
#define vvi vector<vector<int> >
#define PRINT_TIME cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s." <<endl;
#define sim template < class c
#define ris return * this
#define dor > debug & operator <<
#define eni(x) sim > typename   enable_if<sizeof dud<c>(0) x 1, debug&>::type operator<<(c i) {
sim > struct rge { c b, e; };
sim > rge<c> range(c i, c j) { return rge<c>{i, j}; }
sim > auto dud(c* x) -> decltype(cerr << *x, 0);
sim > char dud(...);
struct debug {
#ifdef LOCAL
~debug() { cerr << endl; }
eni(!=) cerr << boolalpha << i; ris; }
eni(==) ris << range(begin(i), end(i)); }
sim, class b dor(pair < b, c > d) {
  ris << "(" << d.first << ", " << d.second << ")";
}
sim dor(rge<c> d) {
  *this << "[";
  for (auto it = d.b; it != d.e; ++it)
    *this << ", " + 2 * (it == d.b) << *it;
  ris << "]";
}
#else
sim dor(const c&) { ris; }
#endif
};
#define imie(...) " [" << #__VA_ARGS__ ": " << (__VA_ARGS__) << "] "
typedef long double ld;
typedef pair<int,int> pii;
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
const int N=21;
int n,m;
int di[]={1,0,-1,0};
int dj[]={0,1,0,-1};
int dp[N][N];
char gg[N][N];
bool valid(int i,int j){
	return (i>=0 && j>=0 && i<n && j<m && gg[i][j]!='#');
}
int ei,ej;
int solve(int i,int j){
	queue<pair<int,pii> > qq;
	qq.push({0,{i,j}});
	vector<vector<bool> > vis(n+1,vector<bool> (m+1));
	// vis[i][j]=true;
	while(!qq.empty()){
		pair<int,pii> cur=qq.front();
		qq.pop();
		if(cur.S.F==ei && cur.S.S==ej)
			return cur.F;
		if(vis[cur.S.F][cur.S.S])
			continue;
		vis[cur.S.F][cur.S.S]=1;
		for(int k=0;k<4;k++){
			int ni=cur.S.F+di[k],nj=cur.S.S+dj[k];
			if(valid(ni,nj) && !vis[ni][nj]){
				qq.push({cur.F+1,{ni,nj}});
				// vis[ni][nj]=1;
			}
		}
	}
	return inf;
}
int32_t main(){__
	cin>>n>>m;
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			cin>>gg[i][j];
		}
	}
	int ans=0;
	for(int si=0;si<n;si++){
		for(int sj=0;sj<m;sj++){
			for(ei=0;ei<n;ei++){
				for(ej=0;ej<m;ej++){
					if(gg[si][sj]!='#' && gg[ei][ej]!='#')
					{	
						int val=solve(si,sj);
						if(val<inf)
						ans=max(ans,val);
					}

				}
			}
		}
	}
	cout<<ans<<"\n";
return 0;
}

Measured memory_bytes: 256