This is obviously the right language for the task. :^D
s:({c<L>}{c<R>}0[(<R> <L>)(<L> <R>)_?])%{30}
c:0 *$
Matches the entire input if it is a valid snake; fails to match if it's not. Try it here!
Explanation
SnakeEx is a 2-D pattern matching language. A program consists of a list of definitions for "snakes," which crawl around the input matching characters, changing directions, and spawning other snakes. In our program, we define two snakes, s
and c
.
We'll start with c
because it's simpler. Its definition is 0 *$
, which should be quite readable if you know regex: match 0
, followed by zero or more spaces, followed by the edge of the grid. The main catch here: this matching can proceed in any direction. We're going to use c
both upward and downward from the snake, to verify that there are no extra 0
s in each column.
Now for the main snake, s
. It takes the form (...)%{30}
, which means "match the contents of the parentheses 30 times"--once for each 0
in the snake. So far, so good. What goes inside the parentheses?
{c<L>}
This spawns a new c
snake, turned left 90 degrees. The direction is relative to the s
snake's direction, so the new snake moves toward the top of the grid (the main snake is moving toward the right). The c
snake checks that the current grid cell is a 0
and that every cell above it is a space. If it fails, the whole match fails. If it succeeds, we continue with
{c<R>}
which does the same thing, only turned right (toward the bottom of the grid).
Note that these spawns don't affect the position of the match pointer in the main snake. They're a bit like lookaheads in regex. (Maybe here we could call them "lookbesides"?) So after verifying that we're pointing to a 0
and the rest of the column contains only spaces, we need to actually match the 0
:
0
Now the match pointer is on the character to the right of the 0
. We need to check three different options: the snake angles down, the snake angles up, or the snake goes straight. For this, we can use an OR expression:
[...]
Inside our OR, we have three possibilities:
(<R> <L>)
Turn right, match a space, and turn left again (snake angles down).
(<L> <R>)
Turn left, match a space, and turn right again (snake angles up).
_?
Match zero or one underscores. Since there are no underscores in the input, this will always be an empty match (snake goes straight).
After matching one of the above three options, the match pointer should be pointing to the 0
in the next column, ready to match the parenthesized expression again.
truthy/falsey
rather thantrue/false
? \$\endgroup\$