#include <iostream>
#include <string>
#include <vector>
#include <tuple>
#include <queue>

auto
distance (
    std::vector<std::string> maze,  // copy
    std::size_t max_r,
    std::size_t max_c,
    std::size_t start_r,
    std::size_t start_c)
{
    using T = std::tuple<std::size_t, std::size_t, int>;
    std::queue<T> que;
    auto distance = 0;
    que.push({start_r, start_c, 0});
    maze[start_r][start_c] = '#';
    while ( !que.empty() ) {
      std::size_t cur_r;
      std::size_t cur_c;
      int depth;
      std::tie(cur_r, cur_c, depth) = que.front();
      distance = depth;
      que.pop();

      // Go up
      if ( 0 <= cur_r - 1 && maze[cur_r - 1][cur_c] == '.' ) {
        que.push({cur_r - 1, cur_c, depth + 1});
        maze[cur_r - 1][cur_c] = '#';
      }
      // Go right
      if ( cur_c + 1 < max_c && maze[cur_r][cur_c + 1] == '.' ) {
        que.push({cur_r, cur_c + 1, depth + 1});
        maze[cur_r][cur_c + 1] = '#';
      }
      // Go down
      if ( cur_r + 1 < max_r && maze[cur_r + 1][cur_c] == '.' ) {
        que.push({cur_r + 1, cur_c, depth + 1});
        maze[cur_r + 1][cur_c] = '#';
      }
      // Go left
      if ( 0 <= cur_c - 1 && maze[cur_r][cur_c - 1] == '.' ) {
        que.push({cur_r, cur_c - 1, depth + 1});
        maze[cur_r][cur_c - 1] = '#';
      }
    }
    return distance;
}

int
main ()
{
    int h, w;
    std::cin >> h >> w;

    std::vector<std::string> maze(h);
    for (auto & row : maze) {
      std::cin >> row;
    }

    auto d = 0;
    for (std::size_t i = 0; i < h; ++i) {
      for (std::size_t j = 0; j < w; ++j) {
        if ( maze[i][j] == '.' ) {
          d = std::max(d, distance(maze, h, w, i, j));
        }
      }
    }

    std::cout << d << std::endl;
}
