package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

var sc = bufio.NewScanner(os.Stdin)

func nextInt() int {
	sc.Scan()
	i, err := strconv.Atoi(sc.Text())
	if err != nil {
		panic(err)
	}
	return i
}

func nextString() string {
	sc.Scan()
	return sc.Text()
}

func nextStringLine(n int) []string {
	s := make([]string, n)
	for i := 0; i < n; i++ {
		s[i] = nextString()
	}
	return s
}

type Pair struct {
	i int
	j int
}

const INF = 100100100

func main() {
	sc.Split(bufio.ScanWords)
	H, W := nextInt(), nextInt()

	S := make([][]string, H)
	for i := 0; i < H; i++ {
		S[i] = make([]string, W)
		str := nextString()
		for j := 0; j < W; j++ {
			S[i][j] = string(str[j])
		}
	}

	DI := [4]int{-1, 0, 1, 0}
	DJ := [4]int{0, -1, 0, 1}

	distMax := 0

	for si := 0; si < H; si++ {
		for sj := 0; sj < W; sj++ {
			// init queue
			queue := []Pair{{si, sj}}

			// init dist
			dist := make([][]int, H)
			for i := 0; i < H; i++ {
				dist[i] = make([]int, W)
				for j := 0; j < W; j++ {
					dist[i][j] = INF
				}
			}
			dist[si][sj] = 0

			for len(queue) > 0 {
				p := queue[0]
				queue = queue[1:]

				for k := 0; k < 4; k++ {
					di := p.i + DI[k]
					dj := p.j + DJ[k]
					if di < 0 || dj < 0 || di > H-1 || dj > W-1 {
						continue
					}
					if S[di][dj] == "#" {
						continue
					}
					if dist[di][dj] == INF {
						queue = append(queue, Pair{di, dj})
						dist[di][dj] = dist[p.i][p.j] + 1
					}
				}
			}
			max := getMax(dist, H, W)
			if max > distMax {
				distMax = max
			}
		}
	}
	fmt.Println(distMax)
}

func getMax(list [][]int, h, w int) int {
	max := 0
	for i := 0; i < h; i++ {
		for j := 0; j < w; j++ {
			if max < list[i][j] && list[i][j] != INF {
				max = list[i][j]
			}
		}
	}
	return max
}
