import java.io.BufferedReader
import java.io.InputStream
import java.io.InputStreamReader
import java.util.*

data class Point(val x: Int, val y: Int)

fun main(args: Array<String>) {
    val sc = FastScanner(System.`in`)

    var h = sc.nextInt()
    var w = sc.nextInt()

    val grid = Array(h) { sc.next() }

    var max = 0
    for (x1 in 0 until w) {
        for (y1 in 0 until h) {
            if(grid[y1][x1] =='#') continue


            val c = LinkedList<Pair<Point, Int>>()
            c.add(Point(x1, y1) to 0)
            val reached = mutableSetOf<Point>()

            while (c.size > 0) {
                val x = c.poll()
                val u = x.first
                val dist = x.second
                reached.add(u)

                if (max < dist) max = dist

                val dx = listOf(1, -1, 0, 0)
                val dy = listOf(0, 0, 1, -1)

                for (i in 0..3) {
                    val nx = u.x + dx[i]
                    val ny = u.y + dy[i]

                    if (reached.contains(Point(nx, ny))) continue

                    if (nx in 0 until grid[0].length && ny in 0 until grid.size && grid[ny][nx] == '.') {
                        c.add(Point(nx, ny) to dist + 1)
                    }
                }
            }
        }
    }
    println(max)
}

class FastScanner(s: InputStream) {
    private var st = StringTokenizer("")
    private val br = BufferedReader(InputStreamReader(s))

    fun next(): String {
        while (!st.hasMoreTokens()) st = StringTokenizer(br.readLine())

        return st.nextToken()
    }

    fun nextInt() = next().toInt()
    fun nextLong() = next().toLong()
    fun nextLine() = br.readLine()
    fun nextDouble() = next().toDouble()
    fun ready() = br.ready()
}
