fun main(args: Array<String>) {
    data class XY(val x: Int, val y: Int)
    val (h, w) = readLine()?.split(" ")?.map(String::toInt) ?: return
    val maze: List<MutableList<Char>> = 0.until(h).map {
        readLine()?.toMutableList() ?: return
    }
    val queue = LinkedBlockingDeque<List<XY>>()
    var max = 0
    0.until(h).forEach { y ->
        0.until(w).forEach { x ->
            if (maze[y][x] != '#') {
                val mazeCopy = maze.map { it.toMutableList() }
                mazeCopy[y][x] = '#'
                queue.offer(listOf(XY(x, y)))
                while (queue.isNotEmpty()) {
                    val xy = queue.poll()
                    val length = xy.size
                    if (max < length) {
                        max = length
                    }
                    val last = xy.last()
                    val xx = last.x
                    val yy = last.y
                    // 上
                    val up = yy - 1
                    if (up >= 0 && mazeCopy[up][xx] != '#') {
                        mazeCopy[up][xx] = '#'
                        queue.offer(xy + listOf(XY(xx, up)))
                    }
                    // 左
                    val left = xx - 1
                    if (left >= 0 && mazeCopy[yy][left] != '#') {
                        mazeCopy[yy][left] = '#'
                        queue.offer(xy + listOf(XY(left, yy)))
                    }
                    // 右
                    val right = xx + 1
                    if (right < w && mazeCopy[yy][right] != '#') {
                        mazeCopy[yy][right] = '#'
                        queue.offer(xy + listOf(XY(right, yy)))
                    }
                    // 下
                    val down = yy + 1
                    if (down < h && mazeCopy[down][xx] != '#') {
                        mazeCopy[down][xx] = '#'
                        queue.offer(xy + listOf(XY(xx, down)))
                    }
                }
            }
        }
    }
    println(max - 1)
}
