#include <iostream>
#include <vector>
#include <string>
#include <utility>
#include <climits>
#include <cmath>
#include <algorithm>

using namespace std;
using ll = long long;

int H, W;

typedef struct
{
    int x;
    int y;
} Position;

inline bool isSame(Position p, Position q)
{
    return p.x == q.x && p.y == q.y;
}

inline bool isNeighbor(Position p, Position q)
{
    int d_x = abs(p.x - q.x);
    int d_y = abs(p.y - q.y);

    return (d_x + d_y) == 1;
}

int main()
{
    cin >> H >> W;
    vector<Position> loads;
    cout << "a" << endl;

    // make load list
    for (size_t i = 0; i < H; i++)
    {
        for (size_t j = 0; j < W; j++)
        {
            char c;
            cin >> c;
            if (c == '.')
            {
                loads.push_back(Position{i, j});
            }
        }
    }

    vector<vector<int>> dp(loads.size(), vector<int>(loads.size(), INT_MAX));

    // initial cost
    for (size_t i = 0; i < loads.size(); i++)
    {
        for (size_t j = 0; j < loads.size(); j++)
        {
            Position pos1 = loads[i];
            Position pos2 = loads[j];
            if (isSame(pos1, pos2))
            {
                dp[i][j] = 0;
            }
            else if (isNeighbor(pos1, pos2))
            {
                dp[i][j] = 1;
            }
        }
    }

    for (size_t k = 0; k < loads.size(); k++)
    {
        for (size_t i = 0; i < loads.size(); i++)
        {
            for (size_t j = 0; j < loads.size(); j++)
            {
                if (dp[i][k] == INT_MAX || dp[k][j] == INT_MAX) {
                    continue;
                }
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
            }
        }
    }

    int ans = 0;
    for (size_t i = 0; i < loads.size(); i++)
    {
        for (size_t j = 0; j < loads.size(); j++)
        {
            if (dp[i][j] == INT_MAX){
                continue;
            }
            ans = max(ans, dp[i][j]);
        }
    }
    cout << ans << endl;
        
}