# This highway needs rain across both of its lanes

In this challenge you will take a an ascii art image of a highway like so

 |   |   |  |  |  |
|       |  |     |
|   |   |  |  |  |
|       |  |     |
|   |   |  |  |  |
|       |  |     |
|   |   |  |  |  |


And you need to put rain across its lanes like so

 |\\\|\\\|  |\\|\\|
|\\\\\\\|  |\\\\\|
|\\\|\\\|  |\\|\\|
|\\\\\\\|  |\\\\\|
|\\\|\\\|  |\\|\\|
|\\\\\\\|  |\\\\\|
|\\\|\\\|  |\\|\\|


A highway will be defined as an ascii art string containing only the characters |, and newlines. All rows of the image will have exactly the same number of characters. Every column will be one of the following

• Empty space, all spaces

• A line, all |

• A lane divider, alternating | and space

Furthermore there will always be a line before and after every lane divider. So

|    |   | |   |
|          |   |
|    |   | |   |


would be valid but

|     |     |
|  |  |
|     |     |


would not be since there is no line after the last lane divider.

And a lane divider will never be exactly next to a line. So

||   |
|    |
||   |


Is invalid but

|   ||  ||
|     | ||
|   ||  ||
|     | ||
|   ||  ||


is fine.

Additionally a highway must be at least two rows high and one column wide.

Your job is to write a program or function that takes a valid highway (as a string or list of lines) as input and outputs the highway with rain across its lanes.

To put rain across a highway's lanes you must replace all space characters that appear between two consecutive lines, where those lines have at least one lane divider between them, with \s. And leave all other characters the same.

So in the following diagram:

A   B   C   1   2  D
|   |   |       |  |
|   |   |   |      |
|   |   |       |  |
|   |   |   |      |


No rain should be put between A and B or B and C since there are no lane dividers between them, but rain should be put between C and D since lane dividers 1 and 2 are between them.

You may output an additional trailing newline, or add/remove trailing spaces from all lines equally if you wish.

This is so answers will be scored in bytes with fewer bytes being better.

## Test cases

Simple case

 |   |   |  |  |  |
|       |  |     |
|   |   |  |  |  |
|       |  |     |
|   |   |  |  |  |
|       |  |     |
|   |   |  |  |  |


 |\\\|\\\|  |\\|\\|
|\\\\\\\|  |\\\\\|
|\\\|\\\|  |\\|\\|
|\\\\\\\|  |\\\\\|
|\\\|\\\|  |\\|\\|
|\\\\\\\|  |\\\\\|
|\\\|\\\|  |\\|\\|


Extra line

|    |   | |   |
|          |   |
|    |   | |   |


|\\\\|\\\|\|   |
|\\\\\\\\\\|   |
|\\\\|\\\|\|   |


Unsynced lane dividers

   |    |      |
|        |  |
|    |      |
|        |  |
|    |      |


   |\\\\|\\\\\\|
|\\\\\\\\|\\|
|\\\\|\\\\\\|
|\\\\\\\\|\\|
|\\\\|\\\\\\|


|   |   |       |  |
|   |   |   |      |
|   |   |       |  |
|   |   |   |      |


|   |   |\\\\\\\|\\|
|   |   |\\\|\\\\\\|
|   |   |\\\\\\\|\\|
|   |   |\\\|\\\\\\|


|   | | |   |
|   |   |   |
|   | | |   |
|   |   |   |


|   |\|\|   |
|   |\\\|   |
|   |\|\|   |
|   |\\\|   |


Hard lane divider

|  |  |  |  |
|     |     |
|  |  |  |  |
|     |     |


|\\|\\|\\|\\|
|\\\\\|\\\\\|
|\\|\\|\\|\\|
|\\\\\|\\\\\|


|   ||  ||
|     | ||
|   ||  ||
|     | ||
|   ||  ||


|\\\||\\||
|\\\\\|\||
|\\\||\\||
|\\\\\|\||
|\\\||\\||


No lanes

|    |     |  |
|    |     |  |
|    |     |  |


|    |     |  |
|    |     |  |
|    |     |  |


One line

       |
|
|


       |
|
|


No lines








• Do we need to handle lanes of arbitrary width, or is it okay to assume a single lane is never more than 25 (or some other number) spaces wide? – Grimmy Mar 10 '20 at 16:46
• @Grimmy You should not assume that. But I do understand that some languages have limits on how much memory they can index, so it would probably be Ok to assume that you can index the entire string. – Wheat Wizard Mar 10 '20 at 16:56
• Are we allowed to accept and/or output line lists? – Jonathan Allan Mar 10 '20 at 21:33
• @JonathanAllan It seems like that is what many people are doing. I assume it is some sort of default or something. – Wheat Wizard Mar 10 '20 at 21:49
• I'm not sure it is, a list of characters may replace a string by default though. Maybe just specify that it is allowed :) – Jonathan Allan Mar 10 '20 at 22:00

# Jelly,  18  16 bytes

ZḢ”\E?€ṣ”|»Ṁ$€K»  A monadic Link accepting a list of lines, each of which is a list of characters, which yields another, similar list. Try it online! Or see the test-suite. ### How? ZḢ”\E?€ṣ”|»Ṁ$€K» - Link: list of lists of characters (i.e. rows of text)
Z                - transpose - i.e. columns (each being: all spaces; all pipes; or a mix)
€          - for each (column):
?           -   if...
E            -   ...condition: all equal? - i.e. if not dashed
Ḣ               -   ...then: head - i.e. the unique character
”\             -   ...else: literal '\' character
”|       - literal '|' character
ṣ         - split at - i.e. highway chunks now contain spaces and '\' characters
€   - for each (chunk):

# Explanation

n⁶Zṣ1€oFẸḤƊ$€j1€W$Zị“|\ ”  Main Link
n⁶                         vectorized inequality to space; "|" becomes 1, " " becomes 0
Z                        transpose into a list of columns
ṣ1€                     split by [1, 1, ..., 1] (length of original; number of rows)
€              for each block (space between two lines)
o                    vectorized OR with
FẸḤ                 (flatten) (any) (double): 0 if there are only spaces; 2 if there are lane dividers
j             join on
1€W$[1, 1, ..., 1] wrapped ([[1, 1, ..., 1]]) Z zip back into original orientation ị“|\ ” index into "|\ "; 0s are spaces, 1s are lines, and 2s are the replaced spaces which become \  # Jelly, 25 bytes Zn⁶ṣoFẸḤƊ$€jW}
ç1€ị“|\ ”Z


Try it online!

I/O is a list of strings

# Explanation

Zn⁶ṣoFẸḤƊ$€jW} Primary Link (dyad); takes the input on the left and [1, 1, ..., 1] on the right Z zip the input into a list of columns n⁶ vectorized inequality to space; "|" becomes 1, " " becomes 0 ṣ split (on [1, 1, ..., 1]) € for each block (between lines) o vectorized or with FẸḤ (flatten) (any) (double): 0 if there are only spaces; 2 if there are lane dividers jW} join on ([1, 1, ..., 1] wrapped) (2,2-chain using }) ç1€ị“|\ ”Z Main Link ç call the primary link with (default left argument), 1€ [1, 1, ..., 1] ị“|\ ” index into "|\ " Z zip back into original orientation  # Haskell, 151 bytes a#b=a!!mod b(length a) z=zip[0..] s%_=[[(['\\'|b<'!'&&(s#(x-1)#y>'['||elem '\\'[a#(y-1),a#(y+1)])]++[b])!!0|(y,b)<-z a]|(x,a)<-z s] g x=foldl(%)x(x!!0)  Try it online! ## Explanation This implements the same algorithm as my python answer. It implements a simple automaton. At every step a space is replaced with an \ iff at least one of the following is true • There is a | or \ above it (indexing wraps around) • There is \ to the left or right of it. This means that rain starts at the gaps in the lane dividers and spreads until it hits |s. # Retina 0.8.2, 79 bytes +m(?<=(.)+) ((?<=(?(1).)^(?<-1>.)+\S.*¶.+|\\ )|(?=\\|.*¶(?<-1>.)+(?(1)$)\S))
\


Try it online! Same flood fill approach that @WheatWizard uses in his answers but using .NET balancing groups to detect vertical matches. Would save two bytes if the wind was blowing the other direction. Explanation:

+m


Repeatedly replace and turn on line detection.

(?<=(.)+)


Replace a space but count the number of preceding characters on that line.

((?<=...|...)|(?=...|...)))
\


Replace with a backslash if one of the four conditions is true.

The four conditions are:

(?(1).)^(?<-1>.)+\S.*¶.+


This space is below rain or a lane divider. The (?(1).) can only succeed if the characters are in the same column.

\\


This space is to the right of existing rain.

\\


This space is to the left of existing rain.

.*¶(?<-1>.)+(?(1)$)\S  This space is above rain or a lane divider. The (?(1)$) can only succeed if the characters are in the same column.

# Ruby, 144 bytes

->s{s.map{|l|((r=s[0]).size.times.map{|i|r[i]!=s[1][i]?[r.rindex(?|,i-1),r.index(?|,i+1)]:p}-[p]).map{|a,b|a.upto(b){|i|l[i]=?\\if l[i]<?!}};l}}


Try it online!

I thought I would try a different approach to the automaton. This finds lane dividers by checking for which indices the first two rows differ. It then goes through each of these indices and finds the previous and next line. It then fills in all space characters with the rain character.

This takes a list of strings for each row of the road.

### Golfy Tricks:

• Using l[i]<?! instead of l[i]==" ". This checks to see whether the character comes before ! (which only space does of the allowed charactersin this challenge).
• -[p] to remove all nil elements in an array.
• map instead of each.
• size.times instead of chars.each_index
• Using map with a condition (where failures result in nil and nils are then removed), rather than a select followed by a map.

### Explanation:

->s{s.map{|l|((r=s[0]).size.times.map{|i|r[i]!=s[1][i]?[r.rindex(?|,i-1),r.index(?|,i+1)]:p}-[p]).map{|a,b|a.upto(b){|i|l[i]=?\\if l[i]<?!}};l}}

# Go through each character in the first row (saving the row in a variable for later use)
((r=s[0]).size.times.map
# Check if it's a line divider
r[i]!=s[1][i]
# If it is, save the nearest road lines
[r.rindex(?|,i-1),r.index(?|,i+1)]
# If it isn't save nil
p
# Remove all the nils
-[p]
# So we have all the indices of the lanes
((r=s[0]).size.times.map{|i|r[i]!=s[1][i]?[r.rindex(?|,i-1),r.index(?|,i+1)]:p}-[p])
# For each row in the road
->s{s.map{|l|
# Go through each index within the lane
.map{|a,b|a.upto(b)
# And set any empty charaters to rain
l[i]=?\\if l[i]<?!
# And then return the new rainy row
l


# Bash + Core utilities, 154152145142136 119 bytes

IFS=\

d=sed s/././g<<<$t sed -E ":l;s/([|%]$d|%) /\1%/;tl;s/ ($d[|%]|%)/%\1/;tl;"'y/%@/\\\ /'<<<$t@tr '
' @


Try it online!

Input on stdin, and output on stdout.

(The script has spurious output to stderr in one case, but that's considered acceptable.)

Here are the outputs for the test cases:

highway-1-simple
|\\\|\\\|  |\\|\\|
|\\\\\\\|  |\\\\\|
|\\\|\\\|  |\\|\\|
|\\\\\\\|  |\\\\\|
|\\\|\\\|  |\\|\\|
|\\\\\\\|  |\\\\\|
|\\\|\\\|  |\\|\\|

highway-2-extraline
|\\\\|\\\|\|   |
|\\\\\\\\\\|   |
|\\\\|\\\|\|   |

highway-3-unsyncedlanedividers
|\\\\|\\\\\\|
|\\\\\\\\|\\|
|\\\\|\\\\\\|
|\\\\\\\\|\\|
|\\\\|\\\\\\|

|   |   |\\\\\\\|\\|
|   |   |\\\|\\\\\\|
|   |   |\\\\\\\|\\|
|   |   |\\\|\\\\\\|

|   |\|\|   |
|   |\\\|   |
|   |\|\|   |
|   |\\\|   |

highway-6-hardlanedivider
|\\|\\|\\|\\|
|\\\\\|\\\\\|
|\\|\\|\\|\\|
|\\\\\|\\\\\|

|\\\||\\||
|\\\\\|\||
|\\\||\\||
|\\\\\|\||
|\\\||\\||

highway-8-nolanes
|    |     |  |
|    |     |  |
|    |     |  |

highway-9-oneline
|
|
|

highway-A-nolines

• tl;tl might be reducible to just tl. More tentatively, maybe some the global flags can be dropped too? What is the purpose of the fold? – user41805 Mar 12 '20 at 17:47
• @user41805 Whoops... I think I have a copy and paste error. (What I wrote works, but wasn't the fully golfed version.) Let me check it out. – Mitchell Spector Mar 12 '20 at 17:58
• @user41805 Yes, that was not the version that I had intended to post. It should be fixed now. Thanks for pointing it out. – Mitchell Spector Mar 12 '20 at 18:08

# Husk, 2120 15 bytes

-5 bytes thanks to Zgarb!

Tṁ?mσ' '\IV≠ġ▼T


Try it online! Takes input as a list of rows. Works by transposing rows and columns, splitting the columns into groups based on whether they have any spaces, adding rain to the column groups with lane divisions, recombining the column groups, and transposing back.

• ġo¬€'  can be ġΛI since only whitespace is falsy and all columns have the same height. – Zgarb Oct 16 '20 at 19:24
• Actually ġ▼ is even shorter. – Zgarb Oct 16 '20 at 19:25
• And I think V#≠ can be just V≠ since the lane dividers will be the only different columns in each group. – Zgarb Oct 16 '20 at 19:32

# Charcoal, 33 bytes

ＷＳ⊞υ⪫⪪ι ψＰ⪫υ¶Ｆ⊟υ¿⊖№ＫＤ²↓|ＫＫ«¤\↓¤\↗


Try it online! Link is to verbose version of code. Expects rectangular input with newline terminator. Explanation:

ＷＳ⊞υ⪫⪪ι ψ


Read the input but change spaces into nulls.

Ｐ⪫υ¶


Copy the input to the output; nulls turn into fillable spaces.

Ｆ⊟υ


Loop over the width of the input.

¿⊖№ＫＤ²↓|


Is this a lane divider? It isn't if it doesn't alternate between |s and empty cells.

ＫＫ


If it's not a divider then print the current character thus moving the cursor one step right. Simply moving right doesn't help because this needs to be a two-character Move command to avoid confusion with printing a lambda in a rightwards direction, and negating the condition doesn't help because when there are no lanes then Charcoal prints nulls instead of spaces for some inexplicable reason.

«¤\↓¤\↗


Fill the area with rain. Both the current cell and the cell below need to be checked as we don't know which one is empty. The cursor is then moved to the right of its original position.