import java.util.*

fun main(args: Array<String>) {
    val (row, col) = readLine()!!.split(" ").map { it.toInt() }
    val m = ArrayList<String>(row)
    repeat(row) {
        m.add(readLine()!!)
    }

    var best = 0
    (0 until row).forEach { r ->
        (0 until col).forEach { c ->
            if (m[r][c] == '.') {
                val temp = dijkstra(r, c, row, col, m)
                best = Math.max(best, temp)
            }
        }
    }
    println(best)
}

data class T(val cost: Int, val r: Int, val c: Int) : Comparable<T> {
    override fun compareTo(other: T): Int {
        return cost.compareTo(other.cost)
    }
}

fun dijkstra(r: Int, c: Int, row: Int, col: Int, m: List<String>): Int {
    val visit = ArrayList<BooleanArray>(row)
    repeat(row) { visit.add(BooleanArray(col) { false }) }
    val q = PriorityQueue<T>()
    q.add(T(0, r, c))

    var best = 0
    while (q.isNotEmpty()) {
        val now = q.poll()
        best = Math.max(best, now.cost)
        visit[now.r][now.c] = true
        if (now.r > 0 && m[now.r - 1][now.c] == '.' && !visit[now.r - 1][now.c]) { // LEFT
            q.add(T(now.cost + 1, now.r - 1, now.c))
        }
        if (now.r < row - 1 && m[now.r + 1][now.c] == '.' && !visit[now.r + 1][now.c]) { // RIGHT
            q.add(T(now.cost + 1, now.r + 1, now.c))
        }
        if (now.c > 0 && m[now.r][now.c - 1] == '.' && !visit[now.r][now.c - 1]) { // UP
            q.add(T(now.cost + 1, now.r, now.c - 1))
        }
        if (now.c < col - 1 && m[now.r][now.c + 1] == '.' && !visit[now.r][now.c + 1]) { // DOWN
            q.add(T(now.cost + 1, now.r, now.c + 1))
        }
    }
    return best
}
