### BUGGY CODE ###

from fractions import gcd
# from datetime import date, timedelta
# from heapq import *
import heapq
import math
from collections import defaultdict, Counter, deque
from bisect import *
import itertools
import fractions
import sys
sys.setrecursionlimit(10 ** 7)
MOD = 10 ** 9 + 7
# input = sys.stdin.readline

def make_divisors(n):
    divisors = []
    for i in range(1, int(n**0.5)+1):
        if n % i == 0:
            divisors.append(i)
            if i != n // i:
                divisors.append(n//i)

    divisors.sort()
    return divisors





def main():
    
    # n, m = map(int, input().split())
    # ps = [list(input().split()) for i in range(m)]

    # c = 1
    # ac, wa = 0, 0
    # d = defaultdict(int)
    # for i in range(m):
    #     if ps[i][1] == "AC" and d[int(ps[i][0])] == 0:
    #         ac += 1
    #         d[int(ps[i][0])] = 1
    #     elif ps[i][1] == "WA" and d[int(ps[i][0])] == 0:
    #         wa += 1
    # print(ac,wa)
            
    
    h, w = map(int, input().split())
    s = [input() for i in range(h)]



    l = []
    for i in range(h):
        for j in range(w):
            l.append((i, j))

    maze = s
    def Dijkstra_search(sx, sy, gx, gy, c):
        H = h
        W = w
        visited = [[False for i in range(W)] for j in range(H)]
        d = [[float("inf") for i in range(W)] for j in range(H)]  # now cost
        d[sx][sy] = 0

        queue = []
        heapq.heappush(queue, (0, (sx, sy)))
        while len(queue):
            cost, (x, y) = heapq.heappop(queue)
            if x == gx and y == gy:
                break
                return cost

            if visited[x][y]:
                continue
            visited[x][y] = True

            for dx, dy in [[1, 0], [0, 1], [-1, 0], [0, -1]]:
                nx = x + dx
                ny = y + dy
                if nx >= 0 and ny >= 0 and nx < H and ny < W:
                    if visited[nx][ny] or maze[nx][ny] == "#":
                        continue

                    vd = 1 + cost
                    if d[nx][ny] > vd:
                        d[nx][ny] = vd
                        heapq.heappush(queue, (vd, (nx, ny)))

        return d[gx][gy]
    
    # ans = 0
    # for ll in itertools.combinations(l, 2):
    #     d = deque()
    #     d.append(ll[0])
    #     visited = [[0 for i in range(w)] for j in range(h)]
    #     while len(d) != 0:
    #         x, y = d.pop()
    #         if x == ll[1][0] and y == ll[1][1]:
    #             break
    #         for i, j in [[1, 0], [-1, 0], [0, 1], [0, -1]]:
    #             nx = x + i
    #             ny = y + j
    #             if nx < 0 or ny < 0 or nx > h - 1 or ny > w - 1:
    #                 continue
    #             if s[nx][ny] == "#":
    #                 continue
    #             if visited[nx][ny] != 0:
    #                 continue
    #             d.append((nx, ny))
    #             visited[nx][ny] =visited[x][y] + 1
        

    #     print(ll, visited, visited[ll[1][0]][ll[1][1]])
    #     ans = max(visited[ll[1][0]][ll[1][1]], ans)
    ans = 0
    for v in itertools.combinations(l, 2):
        if s[v[0][0]][v[0][1]] == "#" or s[v[1][0]][v[1][1]] == "#":
            continue
        #print(Dijkstra_search(v[0][0], v[0][1], v[1][0], v[1][1], 1))
        ans = max(ans , Dijkstra_search(v[0][0],v[0][1],v[1][0],v[1][1],1))
    print(ans)
                


                
        




    


if __name__ == '__main__':
    main()


### FIXED CODE ###

from fractions import gcd
# from datetime import date, timedelta
# from heapq import *
import heapq
import math
from collections import defaultdict, Counter, deque
from bisect import *
import itertools
import fractions
import sys
sys.setrecursionlimit(10 ** 7)
MOD = 10 ** 9 + 7
# input = sys.stdin.readline

def make_divisors(n):
    divisors = []
    for i in range(1, int(n**0.5)+1):
        if n % i == 0:
            divisors.append(i)
            if i != n // i:
                divisors.append(n//i)

    divisors.sort()
    return divisors





def main():
    
    # n, m = map(int, input().split())
    # ps = [list(input().split()) for i in range(m)]

    # c = 1
    # ac, wa = 0, 0
    # d = defaultdict(int)
    # for i in range(m):
    #     if ps[i][1] == "AC" and d[int(ps[i][0])] == 0:
    #         ac += 1
    #         d[int(ps[i][0])] = 1
    #     elif ps[i][1] == "WA" and d[int(ps[i][0])] == 0:
    #         wa += 1
    # print(ac,wa)
            
    
    h, w = map(int, input().split())
    s = [input() for i in range(h)]



    l = []
    for i in range(h):
        for j in range(w):
            l.append((i, j))

    maze = s
    def Dijkstra_search(sx, sy, c):
        H = h
        W = w
        visited = [[False for i in range(W)] for j in range(H)]
        d = [[float("inf") for i in range(W)] for j in range(H)]  # now cost
        d[sx][sy] = 0

        queue = []
        heapq.heappush(queue, (0, (sx, sy)))
        while len(queue):
            cost, (x, y) = heapq.heappop(queue)


            if visited[x][y]:
                continue
            visited[x][y] = True

            for dx, dy in [[1, 0], [0, 1], [-1, 0], [0, -1]]:
                nx = x + dx
                ny = y + dy
                if nx >= 0 and ny >= 0 and nx < H and ny < W:
                    if visited[nx][ny] or maze[nx][ny] == "#":
                        continue

                    vd = 1 + cost
                    if d[nx][ny] > vd:
                        d[nx][ny] = vd
                        heapq.heappush(queue, (vd, (nx, ny)))

        ret = 0
        for i in range(h):
            for j in range(w):
                if s[i][j] == "#":
                    continue
                ret = max(d[i][j],ret)
        return ret
    
    # ans = 0
    # for ll in itertools.combinations(l, 2):
    #     d = deque()
    #     d.append(ll[0])
    #     visited = [[0 for i in range(w)] for j in range(h)]
    #     while len(d) != 0:
    #         x, y = d.pop()
    #         if x == ll[1][0] and y == ll[1][1]:
    #             break
    #         for i, j in [[1, 0], [-1, 0], [0, 1], [0, -1]]:
    #             nx = x + i
    #             ny = y + j
    #             if nx < 0 or ny < 0 or nx > h - 1 or ny > w - 1:
    #                 continue
    #             if s[nx][ny] == "#":
    #                 continue
    #             if visited[nx][ny] != 0:
    #                 continue
    #             d.append((nx, ny))
    #             visited[nx][ny] =visited[x][y] + 1
        

    #     print(ll, visited, visited[ll[1][0]][ll[1][1]])
    #     ans = max(visited[ll[1][0]][ll[1][1]], ans)
    ans = 0
    for v in l:
        if s[v[0]][v[1]] == "#":
            continue
        #print(Dijkstra_search(v[0][0], v[0][1], v[1][0], v[1][1], 1))
        ans = max(ans , Dijkstra_search(v[0],v[1],1))
    print(ans)
                


                
        




    


if __name__ == '__main__':
    main()