Crossword grid verification Validate a proposed crossword grid.

Entries should be full programs that simply test a proposed grid
to determine if it meets a set of conditions for making crossword
solvers happy.

Input

The input will be the name of a file representing the crossword
grid. The input filename may be passed as an argument, on the
standard input, or by other conventional means other than
hardcoding.

Grid file format: The first line consists of two white-space
separated integer constants M and N. Following that line are M
lines each consisting of N characters (plus a new line) selected
from [#A-Z ]. These characters are interpreted such that '#'
indicates a blocked square, ' ' a open square in the puzzle
with no known contents and any letter an open square whose
containing that letter.

Output

The program should produce no output on valid grids and exit with
normal termination state. If the proposed grid fails, the program
should produce a diagnostic error message and exit with a
abnormal termination state (i.e. not 0 on unix) if this is
supported by your execution environment. The error message should
indicate both which condition for validity is violated and the
location of the offending square; you are free to chose the means
of conveying these facts.

Conditions for validity

Valid grids will have no answers (across or down) that are only 1
character long (extra credit for making the minimum length a
input parameter), and will exhibit the usual symmetry. The usual
symmetry means the crossword remains the same after (three
equivalent descriptions of the same operation):


reflection through it's own center
reflection both vertically and horizontally
180 degree rotation


Test input and expected output

Passes:

5   5
#  ##
#    
  #  
    #
##  #


Fails on short answer:

5   5
## ##
#    
  #  
    #
## ##


Fails on symmetry:

5   5
#  ##
#    
  #  
#   #
##  #


Aside

This is the second of several crossword related challenges. I
plan to use a consistent set of file-formats throughout and to
build up a respectable suite of crossword related utilities in
the process. For instance a subsequent puzzle will call for
printing a ASCII version of the crossword based on the input and
output of this puzzle.

Previous challenges in this series:


Crossword numbering
Ruby - 215 207

t,*d=$&lt;.map &amp;:chop;n,d,e=t.size+1,(d*S=?#).gsub(/[^#]/,W=" "),-&gt;c,i{puts [c,i/n+1,i%n+1]*" ";exit 1}
0.upto(d.size-1){|i|d[i]==d[-i-1]||e[?R,i];d[i]==W&amp;&amp;(d[i-1]!=W&amp;&amp;d[i+1]!=W||d[i-n]!=W&amp;&amp;d[i+n]!=W)&amp;&amp;e[?L,i]}


Ungolfed:

h, *g = $&lt;.map &amp;:chop
w = h.size+1
g = (g*?#).gsub(/[^#]/," ")
error = -&gt;c,i{ puts [c,i/w+1,i% w+1]*" "; exit 1 }
0.upto(size-1) { |i|
        error[?R, i] if g[i] != g[-i-1]
        error[?L,i] if g[i]==" " &amp;&amp; (g[i-1]!=" " &amp;&amp; g[i+1]!=" " || g[i-w]!=" " &amp;&amp; g[i+w] != " ")
}


.

h, *g = $&lt;.map &amp;:chop


This basically removes the last char (line break) of each input line by calling the chop method on them, and returning an array of the results.

h takes the first element of this array and *g takes the rest. So we end up with the first line in h and the crossword grid lines in g.

g = (g*?#).gsub(/[^#]/," ")


g*?# joins (*) the array g with the "#" (?# is a character literal). This is the same as g.*("#"), or g.join("#"). Then every non # is replaced by a space.

For the symmetry check we just have to check if the char at every index is equals to the char at the opposite index in the string:

0.upto(g.size-1) { |i| if g[i] != g[g.size - i - 1]; error() }


In Ruby we can index strings from the end using negative indexes (starting from -1 instead of 0), so that g[-i-1] is the opposite of g[i] in the string. This saves a few chars:

0.upto(g.size-1) { |i| if g[i] != g[-i-1]; error() }


We can save a ; by using a conditional statement:

0.upto(g.size-1) { |i| error() if g[i] != g[-i-1] }


In the golfed version we can save a few more chars:

0.upto(g.size-1){|i|g[i]==g[-i-1]||error()}


In a previous version I used recursion for iterating over the string:

(f=-&gt;i{g[i]&amp;&amp;f[i+1]&amp;&amp;g[i]!=g[-i-1]&amp;&amp;error()})[0]


An out of bound access to g returns nil, so once g[i] returns nil, this stops the iteration.

Output format:

{ L | R } line-number column-number


L for length errors, and R for rotation error (so L 1 2 means length error at line 1, column 2)