Can Gravity Guy Make It?
Snails, 15 bytes
Try it online?
^
\ n\ ,=\#r}+~
0. ^
is an option that requires the pattern to start at the top left.
\
: match spacen
: Turn 90 degrees in either direction\ ,
: match space zero or more times=\#
check that there is a#
in front of usr
: set the direction to right}+
: do all of the preceding one or more times~
match a cell which is out of the grid bounds
Perl, 93 89 81 77 76 75 74 bytes
Includes +2 for -0p
Run with the input pattern (with all lines space padded to the same length) on STDIN:
gravity.pl < gravity.txt
gravity.pl
:
#!/usr/bin/perl -0p
/
/;$n=".{@-}";s/#$n\K( $n)*\b |(^|w)([w ]$n)*\K $n#/w|$&/es&&redo;$_=m;w
This file based version needs the final newline, so that is really 75 bytes. But the commandline based version doesn't need that extra newline so this counts as 74 bytes:
perl -0pe '/
/;$n=".{@-}";s/#$n\K( $n)*\b |(^|w)([w ]$n)*\K $n#/w|$&/es&&redo;$_=m;w' < gravity.txt
Explanation:
This will construct a string with a w
in each positions gravity guy can reach.
So for the second to last truthy example it will construct:
#########
##### ####
#wwwww#wwww#
#w # #w ##ww#
wwwww wwwwwww#ww#
##### #### w#ww
wwwwwwwwww#
#########
So gravity guy can make it if and only if there is a w
in the last column.
The string will be constructed by replacing one reachable space by w
in each round.
Each replacement will be of the form
s/prefix \K space postfix/ w | $& /e
which will demand that the space is preceded by prefix and followed by postfix but replace only the space by w
without needing lots of advanced grouping.
Assume $n
contains a regex that will progress just enough that the left and right sides are exactly below each other. Then the relevant regexes are:
/^( $n)*\K $n#/ From the first position drop down as long as you
encounter spaces until you encounter a #.
This puts gravity guy on his starting platform
/#$n\K( $n)*\b / A space immediately below a # and if you (optionally)
go down further from there (as as the descent is
over spaces) you get to a space that follows a word
boundary. The only way to get a word boundary is if
there is a w in front of that space. This selects the
position gravity guy ends up on if starting from that
w and gravity is up
/w([w ]$n)*\K $n#/ A w followed by a space (or w) and if you go down from
there as long as it is over spaces (or w) you finally
end up on a space directly above a #. This handles the
gravity down case. The test uses "space or w" instead
of just space to handle this case:
#
ww
#x
#
Position x is currently a space and must be replaced by a
w but the gravity up regex has already put a w directly
after the w gravity guy takes off from. So for gravity
down we must handle w as if it is still a space. This
is not needed for gravity up because regex always matches
starting at the earliest possible character, so
gravity up matches before gravity down
With that out of the way the program is easy:
#!/usr/bin/perl -0p Slurp all of STDIN into $_, at the end print $_
/\n/ Match the first newline (needed to measure the row
length)
$n=".{@-}" $n effectively becomes rowlength-1 times ".". This
will be the regex that goes one step down a column
s/#$n\K( $n)*\b |(^|w)([w ]$n)*\K $n#/w|$&/es
This is the 3 regexes shown above combined. The s
modifier is needed so the . in $n also matches newline
&&redo Keep looping as long as w's keep getting added
$_=m;w\n; Check if the last column contains a w: He made it!
The \n; at the end is not written. These 2 bytes sneakily
come from the -p option for the ; and the -e option
for the \n
JavaScript (ES6), 174 bytes
a=>[...a[0]].map((_,i)=>[...t].map(x=>s[x]<'#'&&g(s.indexOf('#',x),-1)&&g(s.lastIndexOf('#',x),1),s=a.map(s=>s[i]),t=new Set),t=new Set([0]),g=(i,d)=>i<0||t.add(i+d))&&t.size
Takes a horizontal array of strings and returns the number of exit points. Transposing the array costs me 29 bytes. Ungolfed:
function gravity(array) {
var set = new Set;
set.add(0); // starting point
for (var i = 0; i < array[0].length; i++) {
var s = array.map(s => s[i]); // transpose array
var old = set;
set = new Set;
for (var x of old) {
if (s[x] == '#') continue; // hit wall
var j = s.indexOf('#', x); // try downward gravity
if (j >= 0) set.add(j - 1);
j = s.lastIndexOf('#', x); // try upward gravity
if (j >= 0) set.add(j + 1);
}
}
return set.size;
}