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

int main() {
    int64_t H, W;
    cin >> H >> W;
    vector<string> S(H);
    for (int64_t i = 0; i < H; i++) {
        cin >> S[i];
    }

    struct Element {
        int64_t x, y, cost;
        bool operator<(const Element& rhs) const {
            return cost < rhs.cost;
        }
        bool operator>(const Element& rhs) const {
            return cost > rhs.cost;
        }
    };

    int64_t dx[] = { 0, 1, 0, -1 };
    int64_t dy[] = { 1, 0, -1, 0 };

    int64_t ans = -1;
    for (int64_t sx = 0; sx < W; sx++) {
        for (int64_t sy = 0; sy < H; sy++) {
            if (S[sy][sx] == '#') {
                continue;
            }

            int64_t copy_sx = sx, copy_sy = sy;
            //(sx, sy)からダイクストラ
            {
                vector<vector<int64_t>> cost(H, vector<int64_t>(W, INT_MAX));
                priority_queue<Element, vector<Element>, greater<Element>> pq;

                cost[sy][sx] = 0;
                pq.push({ sx, sy, 0 });
                while (!pq.empty()) {
                    Element t = pq.top();
                    pq.pop();
                    if (t.cost > cost[t.y][t.x]) {
                        continue;
                    }

                    for (int64_t i = 0; i < 4; i++) {
                        int64_t nx = t.x + dx[i];
                        int64_t ny = t.y + dy[i];

                        if (nx < 0 || nx >= W || ny < 0 || ny >= H || S[ny][nx] == '#') {
                            continue;
                        }

                        int64_t new_cost = t.cost + 1;
                        if (new_cost < cost[ny][nx]) {
                            cost[ny][nx] = new_cost;
                            pq.push({ nx, ny, new_cost });
                        }
                    }
                }

                int64_t max_cost = -1;
                for (int64_t i = 0; i < H; i++) {
                    for (int64_t j = 0; j < W; j++) {
                        if (S[i][j] == '#') {
                            continue;
                        }
                        if (cost[i][j] > max_cost) {
                            max_cost = cost[i][j];
                            sx = j;
                            sy = i;
                        }
                    }
                }
            }

            //もう一回ダイクストラ
            {
                vector<vector<int64_t>> cost(H, vector<int64_t>(W, INT_MAX));
                priority_queue<Element, vector<Element>, greater<Element>> pq;

                cost[sy][sx] = 0;
                pq.push({ sx, sy, 0 });
                while (!pq.empty()) {
                    Element t = pq.top();
                    pq.pop();
                    if (t.cost > cost[t.y][t.x]) {
                        continue;
                    }

                    for (int64_t i = 0; i < 4; i++) {
                        int64_t nx = t.x + dx[i];
                        int64_t ny = t.y + dy[i];

                        if (nx < 0 || nx >= W || ny < 0 || ny >= H || S[ny][nx] == '#') {
                            continue;
                        }

                        int64_t new_cost = t.cost + 1;
                        if (new_cost < cost[ny][nx]) {
                            cost[ny][nx] = new_cost;
                            pq.push({ nx, ny, new_cost });
                        }
                    }
                }

                for (int64_t i = 0; i < H; i++) {
                    for (int64_t j = 0; j < W; j++) {
                        if (S[i][j] == '#') {
                            continue;
                        }
                        if (cost[i][j] > ans) {
                            ans = cost[i][j];
                            sx = j;
                            sy = i;
                        }
                    }
                }
            }

            sx = copy_sx;
            sy = copy_sy;
        }
    }
    cout << ans << endl;
}

Measured memory_bytes: 256