/*
	Failure is not the reason for you to stop trying 
	   it is actually a remainder that you should 
			KEEP GOING 
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios_base::sync_with_stdio(false);cin.tie(NULL);
#define pb push_back 
#define eb emplace_back
#define fn for(int i =0 ;i <n;i++)
#define fn1 for( int i =1;i<=n;i++)
#define fm for(int j =0 ;j <m;j++)
#define fm1 for(int j =1;j<=m;j++)
#define fi first
#define se second
#define E endl
#define MOD 1000000007
#define all(v) v.begin(),v.end()
#define rall(v) v.rbegin(),v.rend()
const int N = 1e2+5;
const int INF = 1e18L;
vector<pair<int,int>>v;
int n , m ;
class QItem { 
public: 
    int row; 
    int col; 
    int dist; 
    QItem(int x, int y, int w) 
        : row(x), col(y), dist(w) 
    { 
    } 
};
char ar[N][N];
int dp[N];
int minDistance( int sx , int sy, int dx , int dy) 
{ 
    QItem source(sx, sy, 0); 
  
    // To keep track of visited QItems. Marking 
    // blocked cells as visited. 
    bool visited[n][m]; 
    for (int i = 0; i < n; i++) { 
        for (int j = 0; j <m; j++) 
        { 
            if (ar[i][j] == '#') 
                visited[i][j] = true; 
            else
                visited[i][j] = false; 
  
            // Finding source 
          
        } 
    } 
  
    // applying BFS on matrix cells starting from source 
    queue<QItem> q; 
    q.push(source); 
    visited[source.row][source.col] = true; 
    while (!q.empty()) { 
        QItem p = q.front(); 
        q.pop(); 
  
        // Destination found; 
        if (p.col == dy && p.row==dx) 
            return p.dist; 
  
        // moving up 
        if (p.row - 1 >= 0 && 
            visited[p.row - 1][p.col] == false) { 
            q.push(QItem(p.row - 1, p.col, p.dist + 1)); 
            visited[p.row - 1][p.col] = true; 
        } 
  
        // moving down 
        if (p.row + 1 < n && 
            visited[p.row + 1][p.col] == false) { 
            q.push(QItem(p.row + 1, p.col, p.dist + 1)); 
            visited[p.row + 1][p.col] = true; 
        } 
  
        // moving left 
        if (p.col - 1 >= 0 && 
            visited[p.row][p.col - 1] == false) { 
            q.push(QItem(p.row, p.col - 1, p.dist + 1)); 
            visited[p.row][p.col - 1] = true; 
        } 
  
         // moving right 
        if (p.col + 1 < m && 
            visited[p.row][p.col + 1] == false) { 
            q.push(QItem(p.row, p.col + 1, p.dist + 1)); 
            visited[p.row][p.col + 1] = true; 
        } 
    }
    return -1;
}
signed main(){
	// #ifndef ONLINE_JUDGE
	// freopen("input.txt","r",stdin);
	// freopen("output.txt","w",stdout);
	// #endif
	IOS;
	int T=1;
	// cin >> T;
	while(T--){
		cin >> n >> m;
        fn{
            fm{
                cin >> ar[i][j];
                if(ar[i][j]=='.')v.pb({i,j});
            }
        }
        int ans=0;
         for(int i = 0;i < v.size();i++){
            for(int j =i;j<v.size();j++){	
                int tx  = v[j].fi;
               int ty = v[j].se;
                int cnt =minDistance(v[i].fi,v[i].se,tx,ty);
                ans = max(ans ,cnt);
            }
        }
        cout <<ans<<endl;
    }

	
	cerr << "Time elapsed : " << 1.0 * clock() / CLOCKS_PER_SEC << " sec \n";
	return 0;
}

Measured memory_bytes: 256