use std::io::stdin;
use std::cmp::{
    min,
    max,
};
use std::time::SystemTime;
use std::collections::HashMap;
use std::collections::VecDeque;
use std::collections::HashSet;

#[allow(dead_code)]
fn read_line() -> String {
    let mut s = String::new();
    stdin().read_line(&mut s).unwrap();
    s.trim().to_string()
}

#[allow(dead_code)]
fn read<T>() -> T
where T: std::str::FromStr
{
    read_line().parse::<T>().ok().unwrap()
}

#[allow(dead_code)]
fn read_vec<T>() -> Vec<T>
where T: std::str::FromStr
{
    read_line().split(" ").map(|n| n.parse::<T>().ok().unwrap()).collect()
}

#[allow(dead_code)]
fn read_chars() -> Vec<char>
{
    read_line().chars().collect()
}

#[allow(dead_code)]
fn print_time() -> () {
    println!("{:?}", SystemTime::now());
}

trait BinarySearchLeft<T> {
    fn binary_search_left(&self, x: &T) -> Result<usize, usize>;
}

impl<T> BinarySearchLeft<T> for Vec<T>
where T: std::cmp::Ord + std::fmt::Debug
{
    fn binary_search_left(&self, x: &T) -> Result<usize, usize> {
        let index = self.binary_search(x);
        match index {
            Ok(index) => {
                for i in (1..index+1).rev() {
                    if self[i] != self[i - 1] {
                        return Ok(i - 1)
                    }
                }
                
                Ok(index)
            },
            _ => index,
        }
    }
}

#[allow(unused_variables)]
fn main() {
    let nm: Vec<usize> = read_vec();
    let mut v = vec![];
    for _ in 0..nm[0] {
        v.push(read_chars());
    }

    let mut ans = 0;
    for _i in 0..nm[0] {
        for _j in 0..nm[1] {
            for _k in 0..nm[0] {
                for _l in 0..nm[1] {
                    if _i == _j && _k == _l {
                        continue;
                    }

                    if v[_i][_j] == '#' || v[_k][_l] == '#' {
                        continue;
                    }

                    let mut m = vec![vec![1000000; nm[1]]; nm[0]];
                    v[_i][_j] = 'S';
                    v[_k][_l] = 'G';

                    let mut s = S {
                        map: v.clone(),
                        nm: nm.clone(),
                        m: m,
                    };
                    s.f(_i, _j, 0);

                    let p = s.m[_k][_l];
                    if p != 1000000 {
                        ans = max(ans, p);
                    }

                    v[_i][_j] = '.';
                    v[_k][_l] = '.';
                }
            }
        }
    }
    println!("{}", ans);
}

struct S {
    map: Vec<Vec<char>>,
    nm: Vec<usize>,
    m: Vec<Vec<usize>>,
}

impl S {
    fn f(&mut self, i: usize, j: usize, c: usize) {
        if self.m[i][j] <= c {
            return;
        }
        if self.map[i][j] == 'G' {
            self.m[i][j] = c;
            return;
        }
        if self.map[i][j] == '#' {
            return;
        }
        self.m[i][j] = c;

        if i + 1 < self.nm[0] {
            self.f(i + 1, j, c + 1);
        }
        if i >= 1 {
            self.f(i - 1, j, c + 1);
        }
        if j + 1 < self.nm[1] {
            self.f(i, j + 1, c + 1);
        }
        if j >= 1 {
            self.f(i, j - 1, c + 1);
        }
    }
}