package main

import (
	"bufio"
	"fmt"
	"math"
	"os"
	"strconv"
	"strings"
	"unsafe"
)

func main() {
	HW := sc.nextInts()
	H := HW[0]
	W := HW[1]

	maze := make([][]string, H)
	for i := 0; i < H; i++ {
		t := strings.Split(sc.next(), "")
		maze[i] = make([]string, W)
		copy(maze[i], t)
	}

	dH := []int{1, 0, -1, 0}
	dW := []int{0, 1, 0, -1}

	var ans int
	for i := 0; i < H; i++ {
		for j := 0; j < W; j++ {
			// for i := 2; i < 3; i++ {
			// for j := 4; j < 5; j++ {
			if maze[i][j] == "#" {
				continue
			}
			pair := Pair{first: i, second: j}
			queue := Queue{}
			queue.Push(pair)
			dist := make(map[Pair]int)
			dist[pair] = 1

			for !queue.Empty() {
				v := queue.Pop()
				// fmt.Println("v", v, v.first, v.second, "maze", maze[v.first][v.second], maze[1][0])

				for di := 0; di < 4; di++ {
					first := v.first + dH[di]
					second := v.second + dW[di]
					if first < 0 || first >= H || second < 0 || second >= W {
						continue
					}
					if maze[first][second] != "#" {
						tmpPair := Pair{first, second}
						if dist[tmpPair] == 0 {
							dist[tmpPair] = dist[v] + 1
							queue.Push(tmpPair)
						}
					}
				}

				// upH := v.first + 1
				// loH := v.first - 1
				// upW := v.second + 1
				// loW := v.second - 1

				// if upH < H && maze[upH][v.second] != "#" {
				// 	tmpPair := Pair{first: upH, second: v.second}
				// 	if dist[tmpPair] == 0 {
				// 		dist[tmpPair] = dist[v] + 1
				// 		queue.Push(tmpPair)
				// 	}
				// }

				// if loH >= 0 && maze[loH][v.second] != "#" {
				// 	tmpPair := Pair{first: loH, second: v.second}
				// 	if dist[tmpPair] == 0 {
				// 		dist[tmpPair] = dist[v] + 1
				// 		queue.Push(tmpPair)
				// 	}
				// }

				// if upW < H && maze[v.first][upW] != "#" {
				// 	tmpPair := Pair{first: v.first, second: upW}
				// 	if dist[tmpPair] == 0 {
				// 		dist[tmpPair] = dist[v] + 1
				// 		queue.Push(tmpPair)
				// 	}
				// }

				// if loW >= 0 && maze[v.first][loW] != "#" {
				// 	tmpPair := Pair{first: v.first, second: loW}
				// 	if dist[tmpPair] == 0 {
				// 		dist[tmpPair] = dist[v] + 1
				// 		queue.Push(tmpPair)
				// 	}
				// }
			}
			var tmpMax int
			// fmt.Println(dist)
			for _, v := range dist {
				if tmpMax < v {
					tmpMax = v
					// fmt.Println(tmpMax)
				}
			}

			if dist[pair] == 0 {
				continue
			}

			if tmpMax > ans {
				ans = tmpMax
			}
		}
	}
	fmt.Println(ans - 1)
}

/* template functions */
var (
	sc scanner
)

func init() {
	sc = scanner{
		buf: make([]string, 0, 0),
		cur: 0,
		r:   bufio.NewReader(os.Stdin),
	}
}

type scanner struct {
	buf []string
	cur int
	r   *bufio.Reader
}

func (s *scanner) readln() {
	rbuf := make([]byte, 0, 0)
	for {
		line, prefix, err := s.r.ReadLine()
		if err != nil {
			panic(err)
		}
		rbuf = append(rbuf, line...)
		if prefix == false {
			break
		}
	}
	s.cur = 0
	s.buf = strings.Split(*(*string)(unsafe.Pointer(&rbuf)), " ")
}
func (s *scanner) isFull() bool {
	if s.cur == len(s.buf) {
		return true
	}
	return false
}
func (s *scanner) resetCur() {
	s.cur = 0
}
func (s *scanner) next() string {
	if s.cur == 0 {
		s.readln()
	}
	res := s.buf[s.cur]
	s.cur++
	if s.isFull() {
		s.resetCur()
	}
	return res
}
func (s *scanner) nexts() []string {
	s.readln()
	s.resetCur()
	return s.buf
}
func (s *scanner) nextInt() int {
	if s.cur == 0 {
		s.readln()
	}
	res, _ := strconv.Atoi(s.buf[s.cur])
	s.cur++
	if s.isFull() {
		s.resetCur()
	}
	return res
}
func (s *scanner) nextInts() []int {
	s.readln()
	res := make([]int, len(s.buf))
	for i := range s.buf {
		res[i], _ = strconv.Atoi(s.buf[i])
	}
	s.resetCur()
	return res
}
func (s *scanner) nextFloat() float64 {
	if s.cur == 0 {
		s.readln()
	}
	res, _ := strconv.ParseFloat(s.buf[s.cur],
		64)
	s.cur++
	if s.isFull() {
		s.resetCur()
	}
	return res
}
func (s *scanner) nextFloats() []float64 {
	s.readln()
	res := make([]float64, len(s.buf))
	for i := range s.buf {
		res[i], _ = strconv.ParseFloat(s.buf[i],
			64)
	}
	s.resetCur()
	return res
}
func digits(x int) int {
	return len(strconv.Itoa(x))
}
func powInt(x, p int) (result int) {
	result = 1
	for i := 0; i < p; i++ {
		result *= x
	}
	return
}
func max(x, y int) int {
	return int(math.Max(float64(x), float64(y)))
}
func min(x, y int) int {
	return int(math.Min(float64(x), float64(y)))
}
func abs(x int) int {
	return int(math.Abs(float64(x)))
}
func varDump(value ...interface{}) {
	for _, v := range value {
		fmt.Fprintf(os.Stderr, "%#v\n", v)
	}
}
func yes() {
	fmt.Println("Yes")
}
func no() {
	fmt.Println("No")
}

// Pair a
type Pair struct {
	first  int
	second int
}

// Queue a
type Queue struct {
	v []Pair
}

// Push for queue
func (q *Queue) Push(v Pair) {
	q.v = append(q.v, v)
}

// Pop for queue
func (q *Queue) Pop() Pair {
	r := q.v[0]
	q.v = q.v[1:]
	return r
}

// Front for queue
func (q Queue) Front() Pair {
	return q.v[0]
}

// Empty return true if empty
func (q Queue) Empty() bool {
	return len(q.v) == 0
}


Measured memory_bytes: 5504