import java.util.*
fun main () {
    val (h, w) = readLine()!!.split(" ").map { it.toInt() }
    val maze = MutableList(h) {
        readLine()!!.toString().toCharArray()
    }
    val dx = listOf(1, 0, -1, 0)
    val dy = listOf(0, 1, 0, -1)

    fun bfs(sx: Int, sy: Int, gx: Int, gy: Int): Int {
        if (maze[sy][sx] == '#') return 0
        if (maze[gy][gx] == '#') return 0
        var d: MutableList<MutableList<Int>> = mutableListOf()
        var queue = ArrayDeque<Pair<Int, Int>>()
        for (i in 0 until h) {
            d.add(mutableListOf())
            for (j in 0 until w) {
                d[i].add(10000)
            }
        }
        queue.add(Pair(sy, sx))
        d[sy][sx] = 0
        //println("$sx, $sy, $gx, $gy")
        while (queue.size > 0) {
            var p = queue.removeLast()
            //println(p)
            if(p.first == gy && p.second == gx) break
            for (i in 0 until 4) {
                val nx = p.second + dx[i]
                val ny = p.first + dy[i]
                if (nx in 0 until w &&  ny in 0 until h && maze[ny][nx] == '.' && d[ny][nx] == 10000) {
                    queue.push(Pair(ny, nx))
                    d[ny][nx] = d[p.first][p.second] + 1

                    //println(d[ny][nx].toString()+ " " + ny.toString() + "," + nx.toString())
                }
            }
        }
        //println(d)
        return d[gy][gx]
    }

    var res_max = 0
    for(i in 0 until w){
        for(j in 0 until h){
            for(k in 0 until w) {
                for (l in 0 until h) {
                    if(i == k || j == l)continue
                    if(maze[j][i] == '#' && maze[l][k] == '#')continue
                    val res = bfs(i, j, k, l)
                    if (res_max < res) res_max = res
                }
            }
        }
    }
    println(res_max)
}

