Julia, 90 characters
s->all(cumsum(map(i->i∈"!|.\":"?-1:i∈"()_@\$&",mapfoldl(collect,hcat,split(s,"
")))').>-1)
Unlike the original solution (below), this uses mathematics to determine the solution. mapfoldl(collect,hcat,split(s,"\n"))
(written above with \n
replaced with an actual newline to save characters) converts the string into a 2d array of characters. map(i->i∈"!|.\":"?-1:i∈"()_@\$&",...)
creates an array of numbers, with 1 if the character is a cloud, -1 if the character is rain, and 0 otherwise.
cumsum(...')
calculates the cumulative sums of the rows (would normally be written cumsum(...,2)
, but since we don't care about orientation from this point on, transposing only costs one character), and then all(... .>-1)
checks for a negative number - negatives will only occur if a rain character appears without being preceded by a cloud character.
Julia, 139 136 characters
s->(t=join(mapfoldl(i->split(i,""),.*,split(s,"
")),"
");while t!=(t=replace(t,r"[()_@$&](.*?)[!|.\":]",s"\g<1>"))end;∩("!|.\":",t)==[])
This function first transposes the text so that rows become columns and vice-versa. Note that newlines are present in the code in the form of actual newlines, to save one character per instance.
The function then iteratively replaces cloud/droplet pairs with spaces, and once all such pairs are removed, it returns true if there are any droplets remaining and false otherwise.
r"[()_@$&](.*?)[!|.\":]"
- this is a regex that will match cloud/droplet pairs in a lazy manner, with group 1 containing everything between cloud and droplet. Then s"\g<1>"
tells it to remove the matched cloud and droplets, but keep the stuff in between (necessary as it may contain clouds) - the \g<1>
is whatever was matched in group 1 of the regex. ∩("!|.\":",t)==[]
will generate the intersection of the droplet characters with the final string, and if it's empty, then none of the droplet characters are present, and it's raining.