using System;
using System.Linq;//リストの使用
using System.Collections.Generic;
using System.Text;//テキストの高速出力に必要
class Program
{
	static void Main()
	{
    string[] input = Console.ReadLine().Split(' ');
		int h = int.Parse(input[0]);
		int w = int.Parse(input[1]);
		int[,] maze = new int[h,w];
    long answer = 0;
    for(int i = 0; i < h; i++)
    {
      string s = Console.ReadLine();
      for(int j = 0; j < w; j++)
      {
        if(s[j] == '.') maze[i,j] = 1;
        else maze[i,j] = 2;
      }
    }

    for(int i = 0; i < h; i++)
    {
      for(int j = 0; j < w; j++)
      {
        if(maze[i,j] == 1)
        {
          maze[i,j] = 0;
          long[,] answers = Maze(maze);

          for(int a = 0; a < h; a++)
          {
            for(int b = 0; b < w; b++)
            {
              if(answers[a,b] != long.MaxValue)
              {
                answer = Math.Max(answer, answers[a,b]);
              }
            }
          }
          maze[i,j] = 1;
        }
      }
    }
    
		Console.WriteLine(answer);
	}

  static long[,] Maze(int[,] mazes)
  {//迷路の最短距離をO(HW)で返す。
  //引数は0が複数可のスタート、1が道、2が壁。
  //スタートには0、壁を含む到達不可能な地点にはlong.MaxValueを返す。
  //実装では周囲に壁を追加している。
    long[] VectorArrow = new long[8]{0,1,0,-1,1,0,-1,0};//上下左右に進む為のベクトル
    long r = mazes.GetLength(0);
    long c = mazes.GetLength(1);
    long[,] distances = new long[r,c];//最短距離
    long[,] wallPositions = new long[r+2,c+2];//壁は1
    int n = 0;//スタート地点の個数
    for(int i = 0; i < r; i++)
    {
      for(int j = 0; j < c; j++)
      {
        if(mazes[i,j] == 2) wallPositions[i+1,j+1] = 1;
        else if(mazes[i,j] == 0) n++;
      }
    }
    
    long[][] startPosition = new long[n][];//スタート地点の個数と、xy座標
    int startCount = 0;
    Queue<long[]> que = new Queue<long[]>();//幅優先探索用のキュー
    for(int i = 0; i < n; i++)
    {
      startPosition[i] = new long[2];
    }
    for(int i = 0; i < r; i++)
    {
      for(int j = 0; j < c; j++)
      {
        if(mazes[i,j] == 0)
        {
          startPosition[startCount][0] = i;
          startPosition[startCount][1] = j;
          que.Enqueue(startPosition[startCount]);
          startCount++;
        }
      }
    }

    for(int i = 0; i < r+2; i++)//周囲を壁で埋める
    {
      wallPositions[i,0] = 1;
      wallPositions[i,c+1] = 1;
    }
    for(int j = 0; j < c+2; j++)
    {
      wallPositions[0,j] = 1;
      wallPositions[r+1,j] = 1;
    }
    
    for(long i = 0; i < r; i++)
    {
      for(long j = 0; j < c; j++)
      {
        distances[i,j] = long.MaxValue;//距離を無限と仮定
      }
    }

    for(int i = 0; i < n; i++)
    {
      distances[startPosition[i][0], startPosition[i][1]] = 0;//スタート地点までの距離は0
    }
    long poppedMemoX = 0;
    long poppedMemoY = 0;
    while(que.Count > 0)
    {
      poppedMemoX = que.Peek()[0];
      poppedMemoY = que.Peek()[1];
      que.Dequeue();
    
      for(long partnerNum = 0; partnerNum < 4; partnerNum++)//上下左右に進みたい
      {
        long aimX = poppedMemoX+VectorArrow[2*partnerNum];
        long aimY = poppedMemoY+VectorArrow[2*partnerNum+1];
        if(wallPositions[aimX+1, aimY+1] == 1) continue;//壁なら無視
        if(distances[aimX, aimY] != long.MaxValue) continue;//距離が確定済みなら無視
        distances[aimX, aimY] = distances[poppedMemoX, poppedMemoY] + 1;//距離の加算
        que.Enqueue(new long[2]{aimX, aimY});
      }
    }

    return distances;  
  }
  
}

Measured memory_bytes: 15440