#include <bits/stdc++.h>
using namespace std;

#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define rep1(i, n) for (int i = 1; i < (int)(n); i++)
#define FOREACH(element, vec_name) for (auto &(element) : (vec_name))
#define VECCIN(vec_name) for (auto &element : (vec_name)) cin >> element

#define ALL(obj) (obj).begin(), (obj).end()

#define repi(itr, ds) for (auto itr = ds.begin(); itr != ds.end(); itr++)

//dump出力
    // vector
    template <typename T>
    istream &operator>>(istream &is, vector<T> &vec)
    {
        for (T &x : vec)
            is >> x;
        return is;
    }
    // pair
    template <typename T, typename U>
    ostream &operator<<(ostream &os, pair<T, U> &pair_var)
    {
        os << "(" << pair_var.first << ", " << pair_var.second << ")";
        return os;
    }
    // vector
    template <typename T>
    ostream &operator<<(ostream &os, const vector<T> &vec)
    {
        os << "{";
        for (int i = 0; i < vec.size(); i++)
        {
            os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
        }
        os << "}";
        return os;
    }
    // map
    template <typename T, typename U>
    ostream &operator<<(ostream &os, map<T, U> &map_var)
    {
        os << "{";
        repi(itr, map_var)
        {
            os << *itr;
            itr++;
            if (itr != map_var.end())
                os << ", ";
            itr--;
        }
        os << "}";
        return os;
    }
    // set
    template <typename T>
    ostream &operator<<(ostream &os, set<T> &set_var)
    {
        os << "{";
        repi(itr, set_var)
        {
            os << *itr;
            itr++;
            if (itr != set_var.end())
                os << ", ";
            itr--;
        }
        os << "}";
        return os;
    }

    #define DUMPOUT cerr

    void dump_func()
    {
        DUMPOUT << endl;
    }
    template <class Head, class... Tail>
    void dump_func(Head &&head, Tail &&... tail)
    {
        DUMPOUT << head;
        if (sizeof...(Tail) > 0)
        {
            DUMPOUT << ", ";
        }
        dump_func(std::move(tail)...);
    }
    #ifdef DEBUG_
    #define DEB
    #define dump(...)                                                       \
        DUMPOUT << "  " << string(#__VA_ARGS__) << ": "                     \
                << "[" << to_string(__LINE__) << ":" << __FUNCTION__ << "]" \
                << endl                                                     \
                << "    ",                                                  \
            dump_func(__VA_ARGS__)
    #else
    #define DEB if (false)
    #define dump(...)
    #endif

using ll = long long;
using P =  pair<int,int>;

const int INF = 1e9;
const int MOD = 1e9+7;

ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }//最小公倍数
ll lcm(ll a, ll b) { return a / gcd(a, b) * b; }//最小公倍数

template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }

const int di[4] = {1,0,-1,0};//グリッドの探索
const int dj[4] = {0,1,0,-1};//グリッドの探索

int main()
{
    int h, w;
    cin >> h >> w;
    vector<string> s(h);
    rep(i, h) cin >> s[i];
    int ans = 0;
    rep(si, h) rep(sj, w)
    {
        if (s[si][sj] == '#')
            continue;
        vector<vector<int>> dist(h, vector<int>(w, INF));
        queue<P> q;
        auto update = [&](int i, int j, int x) {
            if (dist[i][j] != INF)
                return;
            dist[i][j] = x;
            q.push(P(i, j)); // .emplace(i,j)
        };
        update(si, sj, 0);
        while (!q.empty())
        {
            int i = q.front().first;
            int j = q.front().second;
            dump(1);
            q.pop();
            rep(dir, 4)
            {
                int ni = i + di[dir], nj = j + dj[dir];
                if (ni < 0 || ni >= h || nj < 0 || nj >= w)
                    continue;
                if (s[ni][nj] == '#')
                    continue;
                update(ni, nj, dist[i][j] + 1);
            }
        }
        rep(i, h) rep(j, w)
        {
            if (dist[i][j] == INF)
                continue;
            ans = max(ans, dist[i][j]);
        }
    }
    cout << ans << endl;
    return 0;
}

Measured memory_bytes: 256