package main

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

var sc = bufio.NewScanner(os.Stdin)

func init() {
	sc.Split(bufio.ScanWords)
}

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

type Pos struct{
	cnt int
	x int
	y int
}

func main() {
	var h, w int
	fmt.Scanf("%d %d", &h, &w)
	s := make([][]int, h)
	for i := 0; i < h; i++ {
		s[i] = make([]int, w)
		var ss string
		fmt.Scanf("%s", &ss)
		for j, c := range ss {
			if c == '#' {
				s[i][j] = -1
			}
		}
	}

	max := 0
	for i := 0; i < h; i++ {
		for j := 0; j < w; j++ {
			sc := make([][]int, h)
			for k := 0; k < h; k++ {
				sc[k] = make([]int, w)
				copy(sc[k], s[k])
			}
			q := []Pos{{
				cnt: 0,
				x:   j,
				y:   i,
			}}
			length := mostDepth(sc, h, w, q)
			if length > max {
				max = length
			}
		}
	}

	fmt.Printf("%d", max)
}

func Max(x ...int) int {
	max := x[0]
	for _, v := range x {
		if v > max {
			max = v
		}
	}
	return max
}

func mostDepth(m [][]int, mi, mj int, q []Pos) (int) {
	max := 0
	for len(q) > 0 {
		v := q[0]
		q = q[1:]

		if v.y < 0 || v.y >= mi {
			continue
		}

		if v.x < 0 || v.x >= mj {
			continue
		}

		if m[v.y][v.x] != 0 {
			continue
		}

		max = v.cnt

		m[v.y][v.x] = v.cnt

		q = append(q, Pos{
			cnt: v.cnt +1,
			x:   v.x + 1,
			y:   v.y,
		})
		q = append(q, Pos{
			cnt: v.cnt +1,
			x:   v.x - 1,
			y:   v.y,
		})
		q = append(q, Pos{
			cnt: v.cnt +1,
			x:   v.x,
			y:   v.y + 1,
		})
		q = append(q, Pos{
			cnt: v.cnt +1,
			x:   v.x,
			y:   v.y - 1,
		})
	}
	return max
}
