fun main(args: Array<String>) {
    val dx = listOf(1, 0, -1, 0)
    val dy = listOf(0, 1, 0, -1)
    val (H, W) = listOfInt()
    val maze = Array<String>(H) { next() }
    var ans = 0
    for (sh in 0 until H) {
        for (sw in 0 until W) {
            if (maze[sh][sw] == '#') continue
            var Q = Queue<Triple<Int, Int, Int>>()
            var visit = Array<IntArray>(H) { IntArray(W) { -1 } }
            Q.enqueue(Triple(sh, sw, 0))
            visit[sh][sw] = 0
            while (!Q.isEmpty()) {
                val (h, w, k) = Q.dequeue()!!
                visit[h][w] = k
                ans = Math.max(ans, k)
                for (n in 0 .. 3) {
                    val nh = h + dx[n]
                    val nw = w + dy[n]
                    if (nh < 0 || nh >= H || nw < 0 || nw >= W) continue
                    if (maze[nh][nw] == '#') continue
                    if (visit[nh][nw] != -1) continue
                    Q.enqueue(Triple(nh, nw, k + 1))
                }
            }
        }
    }
    println(ans)
}
fun next() = readLine()!!
fun listOfString() = next().split(" ")
fun listOfInt(delta: Int = 0) = listOfString().map { Integer.parseInt(it) + delta }
class Queue<T> {
    val elements = mutableListOf<T>()
    fun isEmpty() = elements.isEmpty()
    fun enqueue(item: T) = elements.add(item)
    fun dequeue() = if (!isEmpty()) elements.removeAt(0) else null
    override fun toString(): String = elements.toString()
}