E=enumerate
F=lambda x,l,c=0,k=0:F(x[1:],l,-~c*(T:=x[0]==l),max(k,c+T))if x else k
def f(n,b):
B=[(x,y)for x,r in E(b)for y,_ in E(r)];D,q=map(lambda z:[(x,y)for x,y in B if z==b[x][y]],'#.');Q=[q.pop(0)];S=[Q[0]]
for x,y in Q:
for u in[(x+1,y),(x-1,y),(x,y-1),(x,y+1)]:
if(u in B)>(u in S)and'.'==b[u[0]][u[1]]:Q+=u,;S+=u,
return all((n+~x,n+~y)in D for x,y in D)*(not{*q}-{*S})*all(F(i,'.')>2and n>F(i,'#')for i in b)*all(F(i,'.')>2and n>F(i,'#')for i in zip(*b))
def to_board(s):
n=int(len(s)**.5);return n,[s[i:i+n]for i in range(0,len(s),n)]
TEST = """
`.........`|True|Valid grid
`#..............#`|True|Valid grid
`...#........#...`|True|Valid grid
`...#........#...`|True|Valid grid
`.........`|True|Valid grid
`#...#......#...#`|False|Valid grid
`.........................`|True|Valid grid
`##...#.............#...##`|True|Valid grid
`.................................................`|True|Valid grid
`........................#........................`|True|Valid grid
`....###.....##......##.....##......##.....###....`|True|Valid grid
`................................................................`|True|Valid grid
`##....####....##...........##......##...........##....####....##`|True|Valid grid
`...##.......#...........##.....##.....##...........#.......##...`|True|Valid grid
`#...............`|False|No 180 degree symmetry
`#..##..##..##..#`|False|2-letter entries, filled-in columns
`#........................`|False|No 180 degree symmetry
`.......#...###...#.......`|False|1-letter and 1-letter entries
`######....#....#....#....`|False|No 180 degree symmetry, filled-in column & row
`######...##...##...######`|False|Filled-in columns & rows
`...#......#......#......#......#......#......#...`|False|White squares not contiguous, filled-in column
`.................###....#....###.................`|False|1-letter entries
`...#......#...............##.....................`|False|No 180-degree symmetry
`....#.......#.......#........######........#.......#.......#....`|False|White squares not contiguous
`..#.........#.......#......##......#.......#.......#.........#..`|False|1-letter and 2-letter entries
`.#......#..............................................#......#.`|False|1-letter entries, white squares not contiguous
`...........................##......#............................`|False|No 180-degree symmetry
`####............................................................`|False|No 180-degree symmetry
`#......##......##......##......##......##......##......##......#`|False|Filled-in columns
"""
for i in filter(None, TEST.split('\n')):
a,b,_ = i.split('|')
assert f(*to_board(a[1:-1])) == eval(b)
print('tests passed!')
RT1lbnVtZXJhdGUKRj1sYW1iZGEgeCxsLGM9MCxrPTA6Rih4WzE6XSxsLC1+YyooVDo9eFswXT09bCksbWF4KGssYytUKSlpZiB4IGVsc2UgawpkZWYgZihuLGIpOgogQj1bKHgseSlmb3IgeCxyIGluIEUoYilmb3IgeSxfIGluIEUocildO0QscT1tYXAobGFtYmRhIHo6Wyh4LHkpZm9yIHgseSBpbiBCIGlmIHo9PWJbeF1beV1dLCcjLicpO1E9W3EucG9wKDApXTtTPVtRWzBdXQogZm9yIHgseSBpbiBROgogIGZvciB1IGluWyh4KzEseSksKHgtMSx5KSwoeCx5LTEpLCh4LHkrMSldOgogICBpZih1IGluIEIpPih1IGluIFMpYW5kJy4nPT1iW3VbMF1dW3VbMV1dOlErPXUsO1MrPXUsCiByZXR1cm4gYWxsKChuK354LG4rfnkpaW4gRCBmb3IgeCx5IGluIEQpKihub3R7KnF9LXsqU30pKmFsbChGKGksJy4nKT4yYW5kIG4+RihpLCcjJylmb3IgaSBpbiBiKSphbGwoRihpLCcuJyk+MmFuZCBuPkYoaSwnIycpZm9yIGkgaW4gemlwKCpiKSkKIApkZWYgdG9fYm9hcmQocyk6CgluPWludChsZW4ocykqKi41KTtyZXR1cm4gbixbc1tpOmkrbl1mb3IgaSBpbiByYW5nZSgwLGxlbihzKSxuKV0KClRFU1QgPSAiIiIKYC4uLi4uLi4uLmB8VHJ1ZXxWYWxpZCBncmlkCmAjLi4uLi4uLi4uLi4uLi4jYHxUcnVlfFZhbGlkIGdyaWQKYC4uLiMuLi4uLi4uLiMuLi5gfFRydWV8VmFsaWQgZ3JpZApgLi4uIy4uLi4uLi4uIy4uLmB8VHJ1ZXxWYWxpZCBncmlkCmAuLi4uLi4uLi5gfFRydWV8VmFsaWQgZ3JpZApgIy4uLiMuLi4uLi4jLi4uI2B8RmFsc2V8VmFsaWQgZ3JpZApgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLmB8VHJ1ZXxWYWxpZCBncmlkCmAjIy4uLiMuLi4uLi4uLi4uLi4uIy4uLiMjYHxUcnVlfFZhbGlkIGdyaWQKYC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi5gfFRydWV8VmFsaWQgZ3JpZApgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uIy4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLmB8VHJ1ZXxWYWxpZCBncmlkCmAuLi4uIyMjLi4uLi4jIy4uLi4uLiMjLi4uLi4jIy4uLi4uLiMjLi4uLi4jIyMuLi4uYHxUcnVlfFZhbGlkIGdyaWQKYC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi5gfFRydWV8VmFsaWQgZ3JpZApgIyMuLi4uIyMjIy4uLi4jIy4uLi4uLi4uLi4uIyMuLi4uLi4jIy4uLi4uLi4uLi4uIyMuLi4uIyMjIy4uLi4jI2B8VHJ1ZXxWYWxpZCBncmlkCmAuLi4jIy4uLi4uLi4jLi4uLi4uLi4uLi4jIy4uLi4uIyMuLi4uLiMjLi4uLi4uLi4uLi4jLi4uLi4uLiMjLi4uYHxUcnVlfFZhbGlkIGdyaWQKYCMuLi4uLi4uLi4uLi4uLi5gfEZhbHNlfE5vIDE4MCBkZWdyZWUgc3ltbWV0cnkKYCMuLiMjLi4jIy4uIyMuLiNgfEZhbHNlfDItbGV0dGVyIGVudHJpZXMsIGZpbGxlZC1pbiBjb2x1bW5zCmAjLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uYHxGYWxzZXxObyAxODAgZGVncmVlIHN5bW1ldHJ5CmAuLi4uLi4uIy4uLiMjIy4uLiMuLi4uLi4uYHxGYWxzZXwxLWxldHRlciBhbmQgMS1sZXR0ZXIgZW50cmllcwpgIyMjIyMjLi4uLiMuLi4uIy4uLi4jLi4uLmB8RmFsc2V8Tm8gMTgwIGRlZ3JlZSBzeW1tZXRyeSwgZmlsbGVkLWluIGNvbHVtbiAmIHJvdwpgIyMjIyMjLi4uIyMuLi4jIy4uLiMjIyMjI2B8RmFsc2V8RmlsbGVkLWluIGNvbHVtbnMgJiByb3dzCmAuLi4jLi4uLi4uIy4uLi4uLiMuLi4uLi4jLi4uLi4uIy4uLi4uLiMuLi4uLi4jLi4uYHxGYWxzZXxXaGl0ZSBzcXVhcmVzIG5vdCBjb250aWd1b3VzLCBmaWxsZWQtaW4gY29sdW1uCmAuLi4uLi4uLi4uLi4uLi4uLiMjIy4uLi4jLi4uLiMjIy4uLi4uLi4uLi4uLi4uLi4uYHxGYWxzZXwxLWxldHRlciBlbnRyaWVzCmAuLi4jLi4uLi4uIy4uLi4uLi4uLi4uLi4uLiMjLi4uLi4uLi4uLi4uLi4uLi4uLi4uYHxGYWxzZXxObyAxODAtZGVncmVlIHN5bW1ldHJ5CmAuLi4uIy4uLi4uLi4jLi4uLi4uLiMuLi4uLi4uLiMjIyMjIy4uLi4uLi4uIy4uLi4uLi4jLi4uLi4uLiMuLi4uYHxGYWxzZXxXaGl0ZSBzcXVhcmVzIG5vdCBjb250aWd1b3VzCmAuLiMuLi4uLi4uLi4jLi4uLi4uLiMuLi4uLi4jIy4uLi4uLiMuLi4uLi4uIy4uLi4uLi4jLi4uLi4uLi4uIy4uYHxGYWxzZXwxLWxldHRlciBhbmQgMi1sZXR0ZXIgZW50cmllcwpgLiMuLi4uLi4jLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLiMuLi4uLi4jLmB8RmFsc2V8MS1sZXR0ZXIgZW50cmllcywgd2hpdGUgc3F1YXJlcyBub3QgY29udGlndW91cwpgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uIyMuLi4uLi4jLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLmB8RmFsc2V8Tm8gMTgwLWRlZ3JlZSBzeW1tZXRyeQpgIyMjIy4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLmB8RmFsc2V8Tm8gMTgwLWRlZ3JlZSBzeW1tZXRyeQpgIy4uLi4uLiMjLi4uLi4uIyMuLi4uLi4jIy4uLi4uLiMjLi4uLi4uIyMuLi4uLi4jIy4uLi4uLiMjLi4uLi4uI2B8RmFsc2V8RmlsbGVkLWluIGNvbHVtbnMKIiIiCgoKZm9yIGkgaW4gZmlsdGVyKE5vbmUsIFRFU1Quc3BsaXQoJ1xuJykpOgoJYSxiLF8gPSBpLnNwbGl0KCd8JykKCWFzc2VydCBmKCp0b19ib2FyZChhWzE6LTFdKSkgPT0gZXZhbChiKQoKcHJpbnQoJ3Rlc3RzIHBhc3NlZCEnKQ==