#include<bits/stdc++.h>
#define int long long int 
#define hell 1000000007 
#define F first
#define re 15000000
#define S second
#define pb push_back
#define all(a)  (a).begin(),(a).end()
#define rep(i,a,b) for(int int i = a;i<b;i++)
#define pi 3.1415926536
#define Mod 998244353
#define endl '\n'
#define N 20
#define M 20
using namespace std;

vector<char> mat[21];
bool visited[20][20];
struct Node
{
  // (x, y) represents matrix cell coordinates
  // dist represent its minimum distance from the source
  int x, y, dist;
};

// Below arrays details all 4 possible movements from a cell
int row[] = { -1, 0, 0, 1 };
int col[] = { 0, -1, 1, 0 };

// Function to check if it is possible to go to position (row, col)
// from current position. The function returns false if (row, col)
// is not a valid position or has value 0 or it is already visited
bool isValid(int row, int col ,int h,int w)
{
  return (row >= 0) && (row < h) && (col >= 0) && (col < w)
    && mat[row][col]!='#' && !visited[row][col];
}

// Find Shortest Possible Route in a matrix mat from source
// cell (i, j) to destination cell (x, y)
int BFS(int i, int j, int x, int y ,int h,int w)
{
  // construct a matrix to keep track of visited cells
  
  // initially all cells are unvisited
  memset(visited, false, sizeof visited);
  
  // create an empty queue
  queue<Node> q;

  // mark source cell as visited and enqueue the source node
  visited[i][j] = true;
  q.push({i, j, 0});

  // stores length of longest path from source to destination
  int min_dist = INT_MAX;

  // run till queue is not empty  
  while (!q.empty())
  {
    // pop front node from queue and process it
    Node node = q.front();
    q.pop();
    
    // (i, j) represents current cell and dist stores its
    // minimum distance from the source
    int i = node.x, j = node.y, dist = node.dist;

    // if destination is found, update min_dist and stop
    if (i == x && j == y)
    {
      min_dist = dist;
      break;
    }

    // check for all 4 possible movements from current cell
    // and enqueue each valid movement
    for (int k = 0; k < 4; k++)
    {
      // check if it is possible to go to position
      // (i + row[k], j + col[k]) from current position
      if (isValid(i + row[k], j + col[k], h,w))
      {
        // mark next cell as visited and enqueue it
        visited[i + row[k]][j + col[k]] = true;
        q.push({ i + row[k], j + col[k], dist + 1 });
      }
    }
  }

  return min_dist;
}
int32_t main()
  {   
      ios_base::sync_with_stdio(false);
      cin.tie(0);
      cout.tie(0);            
      //std::setprecision(20);      
      int tests=1;
        //freopen("input.txt", "r", stdin);
        //cin>>tests;
     while(tests--) 
     { 
        int h,w;
        cin>>h>>w;
        for(int i = 0;i<19;i++)
          mat[i].clear();
        for(int i = 0;i<h;i++){
          for(int j = 0;j<w;j++){
            char c;
            cin>>c;
            mat[i].pb(c);
          }
        }
        // for(int i = 0;i<h;i++)
        // {
        //   for(int j = 0;j<w;j++)
        //     cout<<mat[i][j];
        //   cout<<endl;
        // }
        vector<pair<int,int> > v;
        for(int i = 0;i<h;i++){
          for(int j = 0;j<w;j++){
            if(mat[i][j]!='#')
               v.pb({i,j});
          }
        }
        int ans = 0;
        for(int i = 0;i<v.size();i++){
          for(int j = 0;j<v.size();j++){
            memset(visited,0,sizeof(visited));
            int get = BFS(v[i].F,v[i].S, v[j].F, v[j].S,h,w);
            if(get!= INT_MAX)
              ans = max(ans,get);
          }
        }
        cout<<ans;

     } 
}

Measured memory_bytes: 256