import java.util.Scanner
import scala.collection.immutable.Queue

// '.': true
case class Input(maze: List[List[Boolean]]) {
  val w = maze(0).length
  val h = maze.length
  def bfs(from: (Int, Int), to: (Int, Int)): Int = {
    val mary = maze.map(_.toArray).toArray
    def neighbors(from: (Int, Int)): List[(Int, Int)] = {
      val (x, y) = from
      List((0,1),(0,-1),(1,0),(-1,0)).flatMap(
        { case (dx, dy) =>
          val (nx, ny) = (x+dx, y+dy)
          if (nx >= 0 && nx < h && ny >= 0 && ny < w) Some((nx,ny))
          else None
        }).filter({ case (x, y) => mary(x)(y) })
    }
    def aux(q: Queue[((Int, Int), Int)]): Int = q match {
      case (from, cnt) +: rst => {
        if (from == to) cnt
        else {
          val nb = neighbors(from)
          nb foreach({ case (x, y) => mary(x)(y) = false })
          aux(rst.enqueue(nb.map((_, cnt+1))))
        }
      }
      case _ => -1
    }
    aux(Queue((from, 0)))
  }
  def solved: Output = {
    val withIndex =
      maze.map(_.zipWithIndex).zipWithIndex
        .flatMap({ case (l, i) => l.map({ case (b, j) => (((i, j), b)) }) })
    val dots = withIndex.filter({ case (coord, b) => b }).map(_._1)
    val combs = dots.combinations(2)
    val ans = combs.map({ case List(from, to) => bfs(from, to) }).max
    Output(ans)
  }
}

object Input {
  def fromString(s: String): Input = {
    Input(s.split('\n').map(_.toCharArray().map(_ == '.').toList).toList)
  }
  def fromStdin(): Input = {
    val sc = new Scanner(System.in)
    val h = sc.nextInt()
    val w = sc.nextInt()
    sc.nextLine()
    fromString(List.fill(h)(sc.nextLine()).foldLeft("")({ case (acc, s) => s"${acc}${s}\n" }))
  }
}

case class Output(ans: Int) {
  override def toString(): String = {
    ans.toString()
  }

  def print() = println(this.toString())
}

object Main extends App {
  Input.fromStdin().solved.print()
}
