21
\$\begingroup\$

In the United States, the two opposing directions of traffic on a road are separated by a dashed yellow line if passing is allowed and two solid yellow lines if passing is not allowed.

road line rules graphic

(Just one side can be dashed to allow passing on that side, and yellow lines can mean other things like center or reversible lanes, but we aren't concerned with any of those cases.)

Write a program that takes in a run-length encoded string of P for passing and N for no passing, and prints an ASCII version of the corresponding road. Except for the center line, the road always has the same pattern, which can be easily inferred from the examples below.

There will be a positive decimal number before each P and N in the input string. This number defines the length of the passing or no passing region of the current part of the road.

Examples

An input of 12N would produce 12 columns of no passing road (center line all =):

____________


============

____________

An input of 12P would produce 12 columns of passing road (center line - repeating):

____________


- - - - - - 

____________

Passing and no passing can then be combined, e.g. 4N4P9N7P1N1P2N2P would produce:

______________________________


====- - =========- - - -=-==- 

______________________________

These are 4 no passing columns, then 4 passing, then 9 no passing, etc.

Note that a passing zone always starts with a dash (-) on the leftmost side, not a space (). This is required.

Details

  • The input will never have two N zones or two P zones in a row. e.g. 4P5P will never occur.
  • You don't need to support letters without a leading positive number. Plain P will always be 1P, plain N will always be 1N.
  • There may be trailing spaces as long as they do not extend beyond the final column of the road. There may be one optional trailing newline.
  • Instead of a program, you may write a function that takes in the run-length encoded string and prints or returns the ASCII road.
  • Takes input in any standard way (stdin, command line, function arg).

The shortest code in bytes wins. Tiebreaker is earlier post.

\$\endgroup\$
  • \$\begingroup\$ Does the road have to be asymmetrical, or is it allowed to print 4 spaces of road on each side of the line? \$\endgroup\$ – orlp Apr 27 '15 at 13:09
  • \$\begingroup\$ @orlp If you're asking if the road can be wider than 5 rows, then no. If you're asking if space characters can be put in the empty lines above or below the center line, then yes as long as they hold with detail bullet 3. \$\endgroup\$ – Calvin's Hobbies Apr 27 '15 at 14:36
  • \$\begingroup\$ Let me ask by example, is either of these a valid output? gist.github.com/orlp/0e0eae16d6e1fcda5e9b \$\endgroup\$ – orlp Apr 27 '15 at 14:43
  • \$\begingroup\$ @orlp Neither is. \$\endgroup\$ – Calvin's Hobbies Apr 27 '15 at 14:47

13 Answers 13

5
\$\begingroup\$

CJam, 38 bytes

"_  - _":N3'=t:P;q~]2/e~z'
*"--"/"- "*

How it works

We first assign the correct road column to variables N and P and then simply evaluate the input string. This leaves a pair of the length and the column on stack. We group them up, run an RLD on it to get the full columns, transpose to join them and then finally, convert the continuous -- to -.

:_  - _":N                    e# This is the no passing column. We assign it to N
          3'=t:P              e# Replace the '-' in N with '=" and assign it to P
                q~]2/         e# Read the input, evaluate it and then group it in pairs
                     e~       e# Run a run-length-decoder on the pairs
                       z'
*                             e# Transpose and join with new lines.
 "--"/                        e# Split on two continuous occurrence of -
      "- "*                   e# Join by an alternate "- "

Try it online here

\$\endgroup\$
6
\$\begingroup\$

JavaScript (ES6), 114

Using template strings, the 5 linefeeds are significant are must be counted.

f=s=>(b=(s=s.replace(/(\d+)(.)/g,(x,n,b)=>(b<'P'?'=':'- ').repeat(n).slice(0,n))).replace(/./g,'_'))+`


${s}

`+b
\$\endgroup\$
5
\$\begingroup\$

rs, 252 chars

Although this might not count because I added the convergence operator as a ripoff of Martin Büttner's Retina an hour ago...I'm not really here to compete anyway. It's just fun making a regex-based solution for this.

(\d+\D)/#\1
+(\d*)#(?:(((((((((9)|8)|7)|6)|5)|4)|3)|2)|1)|0)(?=\d*\D)/\1\1\1\1\1\1\1\1\1\1\2\3\4\5\6\7\8\9\10#
\d(?=\d*#N)/=
(^|(?<=\D))\d(?=\d*#P)/-
+(?<=-)\d\d(?=\d*#P)/ -
(?<=-)\d(?=\d*#P)/ 
#\D/
((?:(=|-| ))+)/A\1\n\n\n\1\n\nA\1\n
+(A_*)(.)/\1_
A/

I got line 2 from Martin's Retina answer for Programming Languages Through the Years.

Explanation

(\d+\D)/#\1
+(\d*)#(?:(((((((((9)|8)|7)|6)|5)|4)|3)|2)|1)|0)(?=\d*\D)/\1\1\1\1\1\1\1\1\1\1\2\3\4\5\6\7\8\9\10#

This does lots of magic. See the answer I linked above for more info.

Basically, with the input 4N4P9N7P1N1P2N2P, this will be the result:

4444#N4444#P999999999#N7777777#P1#N1#P22#N22#P

Next:

\d(?=\d*#N)/=

This replaces the numbers preceding the no-passing symbol (N) with the equal signs. The result with the previous input:

====#N4444#P=========#N7777777#P=#N1#P==#N22#P

This:

(^|(?<=\D))\d(?=\d*#P)/-

replaces the first number preceding a passing symbol (P) with the first dash. The result:

====#N-444#P=========#N-777777#P=#N-#P==#N-2#P

The next two lines continue the same pattern:

+(?<=-)\d\d(?=\d*#P)/ -
(?<=-)\d(?=\d*#P)/ 

The first line replaces the rest of the line with the dash-space pattern. The second one handles an odd number; it replaces the last dash followed by a single integer (such as -5) with a dash-space (-). Now, the output is:

====#N- - #P=========#N- - - -#P=#N-#P==#N- #P

Now things are starting to fall into place. The next line:

#\D/

just removes the #N and #P.

((?:(=|-| ))+)/A\1\n\n\n\1\n\nA\1\n
+(A_*)(.)/\1_

set up the underscores on the top and bottom to give:

A______________________________


====- - =========- - - -=-==- 

A______________________________

Lastly, we remove the A:

A/
\$\endgroup\$
2
\$\begingroup\$

Haskell, 165 bytes

k 'N'="="
k _="- "
d c=c>'/'&&c<':'
p[]=[]
p s=take(read$takeWhile d s)(cycle$k a)++p r where(a:r)=dropWhile d s
f s=unlines[q,"\n",p s,"",q]where q=map(\x->'_')$p s

Example run (f returns a string, so for better display print it):

*Main> putStr $ f "4N4P9N7P1N1P2N2P"
______________________________


====- - =========- - - -=-==- 

______________________________

How it works: p returns the middle line by recursively parsing the input string and concatenating the given number of symbols found by the lookup function k. The main function f joins a five element list with newlines, consisting of the top line (every char of the middle line replaced by _), a newline, the middle line, an empty line and the bottom line (same as top).

\$\endgroup\$
2
\$\begingroup\$

Python 3, 169 168 bytes. (167 with Python 2)

p,s='',str.split
for _ in s('N '.join(s('P '.join(s(input(),'P')),'N'))):
 v=int(_[:-1]);p+=['='*v,('- '*v)[:v]][_[-1]=='P']
l=len(p)
u='_'*l
print(u+'\n'*3+p+'\n\n'+u)

Fairly ungolfed:

p=''
for i in'N '.join('P '.join(input().split('P')).split('N')).split():

  v=int(i[:-1])         # Get the number from the input section

  if i[-1]=='N':        # Check the letter (last char) from the input section
      p+=('='*v)        # Repeat `=` the number from input (v)
  else:
      p+=('- '*v)[:v]   #Repeat `- ` v times, then take first v chars (half)
l=len(p)                #Get the length of the final line markings
print('_'*l+'\n\n\n'+p+'\n\n'+'_'*l)

print('_'*l                          # Print _ repeated the length of p
           +'\n\n\n'                 # 3 new lines
                    +p+              # print out p (the markings)
                       '\n\n'        # 2 new lines
                             +'_'*l) # Print _ repeated the length of p

for i in
        'N '.join(
                  'P '.join(
                            input().split('P'))
                                               .split('N'))
                                                           .split():
                            # Split the input into items of list at P
                  # Join together with P and ' '
                                                # Split at N...
         # Join with N and ' '
                                                           # Split at space
# Loop through produced list

Try it online here.

\$\endgroup\$
  • \$\begingroup\$ You forgot to update your byte count. \$\endgroup\$ – mbomb007 Apr 27 '15 at 20:15
  • \$\begingroup\$ @mbomb007 It didn't change the count :/ I can't get it below 169 atm \$\endgroup\$ – Tim Apr 27 '15 at 20:16
  • \$\begingroup\$ Putting p+=['='*v,('- '*v)[:v]][_[-1]=='P'] at the end of the previous line with a preceding semicolon saves one byte. \$\endgroup\$ – mbomb007 Apr 27 '15 at 20:18
  • \$\begingroup\$ Also, using Python 2 instead saves 1 byte on the print. \$\endgroup\$ – mbomb007 Apr 27 '15 at 20:25
  • \$\begingroup\$ @mbomb007 added them in :) I have a feeling python 2 it may be even shorter... But I'm not sure. \$\endgroup\$ – Tim Apr 27 '15 at 20:26
2
\$\begingroup\$

Python 2, 136 bytes

Surprisingly, importing re seems to be actually worthwhile here.

import re
s=""
for x,y in re.findall("(\d+)(.)",input()):s+=(("- ","==")[y=="N"]*int(x))[:int(x)]
t="_"*len(s);print t+"\n"*3+s+"\n"*2+t
\$\endgroup\$
2
\$\begingroup\$

PHP, 187 bytes

preg_match_all('/(\d+)(\w)/',$argv[1],$m,2);
$o='';
foreach($m as $p)
    $o.=str_replace('--','- ',str_repeat($p[2]<'P'?'=':'-',$p[1]));
$a=preg_replace('/./','_',$o);
echo("$a\n\n\n$o\n\n$a\n");

The code can stay on a single line; it is displayed here on multiple lines to be more readable (the whitespaces and newlines used for formatting were not counted).

Two bytes can be saved by not printing the trailing newline. Five more bytes can be saved by using real newline characters on the echo():

echo("$a


$o

$a");

Six additional bytes can be saved by omitting the initialization of $o ($o='';) but this will trigger a notice. The notice can be suppressed by running the script using the command line:

$ php -d error_reporting=0 <script_name> 4N4P9N7P1N1P2N2P

These brings it to 174 bytes.

\$\endgroup\$
2
\$\begingroup\$

Ruby, 137 135 bytes

Not the shortest I could come up with, but close to the nicest. Partly borrowed from Optimizer's answer.

require'scanf'
N='_  = _'
P='_  - _'
a=[]
scanf('%d%c'){|l,t|a+=[eval(t).chars]*l}
puts (a.shift.zip(*a).map(&:join)*?\n).gsub'--','- '

Ungolfed:

require 'scanf'

N = '_  = _'
P = '_  - _'
columns = [] # array of columns
# scan stdin for a number followed by a single char
scanf('%d%c') do |length, type|
  columns += [eval(type).chars] * length
done

# Convert to an array of rows, and join into a string
rows = columns.shift.zip(*columns).map(&:join)
str = rows * "\n" # join lines

# Replace '--' by '- ' and print
puts str.gsub(/--/, '- ')
\$\endgroup\$
  • \$\begingroup\$ You should be able to improve this by 2 bytes (and beat the python 2 answer) by changing the last line to (a.shift.zip(*a).map(&:join)*?\n).gsub'--','- '. \$\endgroup\$ – blutorange May 2 '15 at 22:26
1
\$\begingroup\$

C, 155 bytes

main(l,v,k,n,x,s,c)char*s,**v,c;{for(l=6;l--;puts(s))for(s=v[1];*s;s+=k)for(x=sscanf(s,"%d%c%n",&n,&c,&k);n--;)putchar(l%5?l^2?32:c^78?++x&1?45:32:61:95);}

More readable:

main(l,v,k,n,x,s,c)
    char*s,**v,c;
{
    for(l=6;l--;puts(s))
        for(s=v[1];*s;s+=k)
            for(x=sscanf(s,"%d%c%n",&n,&c,&k);n--;)
                putchar(l%5?l^2?32:c^78?++x&1?45:32:61:95);
}

The outer loop counts lines from 5 to 0.

The middle loop iterates over parts of the encoded string:

4N4P9N7P1N1P2N2P
4P9N7P1N1P2N2P
9N7P1N1P2N2P
7P1N1P2N2P
1N1P2N2P
1P2N2P
2N2P
2P
string is empty - exit

The inner loop decodes a part, like, 7P, and iterates the needed number of times (e.g. 7).

Each iteration prints one char. The value of the char is described by the code l%5?l^2?32:c^78?++x&1?45:32:61:95:

  • If line number is 5 or 0, print 95 (_)
  • Otherwise, if line number is not equal to 2, print a space
  • Otherwise, if the symbol is 'N', print 61 (=)
  • Otherwise, increase x by 1 (it was initialized to 2 by sscanf)
  • If odd, print 45 (-), else print 32 (space)
\$\endgroup\$
0
\$\begingroup\$

Scala, 163 bytes

(s:String)=>{val r=(("\\d+(P|N)"r) findAllIn(s) map(r=>{val l=r.init.toInt;if(r.last=='N')"="*l else ("- "*l).take(l)})).mkString;val k="_"*r.length;s"$k\n\n\n$r\n\n$k"}

First try, might be golfed some more.

\$\endgroup\$
0
\$\begingroup\$

Ruby, 94 bytes

Borrows the gsub'--','- ' idea from 14mRh4X0r's answer. I think that answer is more interesting, though, although this is shorter.

f=->x{n=?_*x.gsub!(/(\d+)(.)/){($2==?P??-:?=)*$1.to_i}.size
"#{n}


#{x.gsub'--','- '}

#{n}"}

Testing:

f=->x{n=?_*x.gsub!(/(\d+)(.)/){($2==?P??-:?=)*$1.to_i}.size
"#{n}


#{x.gsub'--','- '}

#{n}"}

puts f['4N4P9N7P1N1P2N2P']

Produces:

______________________________


====- - =========- - - -=-==- 

______________________________
\$\endgroup\$
0
\$\begingroup\$

let me include my matlab version

MATLAB (267 b)

function d=p(V,a),j=numel(V)-1;if (a==0),d=0;return; end,d=(V(a)-48+10*p(V,a-1))*(V(a)<64);fprintf('%c%.*s%c%.*s',(a>=j)*10,(a==j|a==1)*eval(strcat(regexprep(V,'[NP]','+'),48)),ones(99)*'_',(a<3)*10,(V(a+1)>64)*d,repmat((V(a+1)==78)*'=='+(V(a+1)==80)*'- ',[1 99]));end

input

An ascii-formatted string tailed by a space (since there is no end of chain '\0' in matlab

example V='12N13P '


output

pattern representation of the road

_________________________


============- - - - - - -

_________________________

function

the function must be called from its tail-1 (the empty character is removed)

example : p(V,numel(V)-1)

Simulation

try it online here

\$\endgroup\$
0
\$\begingroup\$

R, 132 bytes

Not greatly happy with this, but it was a bit of fun to do:) Tried to get rid of the multiple gsubs, but my efforts were in vain. I suspect there's a much better way to do this.

cat(rbind(nchar(a<-scan(,'',t=gsub('PN','P N',gsub('NP','N P',chartr('- =','PPN',scan(,'',sep='|')[4]))))),substring(a,1,1)),sep='')
  • scan gets the strings from STDIN and grabs the 4th one. Note that the empty lines require a space (or something) in them for scan to carry on getting the input.

    "====- - =========- - - -=-==- "

  • It replaces the =s with Ns, the - and with Ps.

    "NNNNPPPPNNNNNNNNNPPPPPPPNPNNPP"

  • Then it inserts a space between each NP and PN

    "NNNN PPPP NNNNNNNNN PPPPPPP N P NN PP"

  • The scan splits the string on spaces

    "NNNN" "PPPP" "NNNNNNNNN" "PPPPPPP" "N" "P" "NN" "PP"

  • The string length is then bound(rbind) with the first character of each string

    4 4 9 7 1 1 2 2
    "N" "P" "N" "P" "N" "P" "N" "P"

  • The array is then output using cat.

Test run

cat(rbind(nchar(a<-scan(,'',t=gsub('PN','P N',gsub('NP','N P',chartr('- =','PPN',scan(,'',sep='|')[4]))))),substring(a,1,1)),sep='')
1: ____________
2:  
3:  
4: ============
5:  
6: ____________
7: 
Read 6 items
Read 1 item
12N
> 
> cat(rbind(nchar(a<-scan(,'',t=gsub('PN','P N',gsub('NP','N P',chartr('- =','PPN',scan(,'',sep='|')[4]))))),substring(a,1,1)),sep='')
1: ____________
2:  
3:  
4: - - - - - - 
5:  
6: ____________
7: 
Read 6 items
Read 1 item
12P
> cat(rbind(nchar(a<-scan(,'',t=gsub('PN','P N',gsub('NP','N P',chartr('- =','PPN',scan(,'',sep='|')[4]))))),substring(a,1,1)),sep='')
1: ______________________________
2:  
3:  
4: ====- - =========- - - -=-==- 
5:  
6: ______________________________
7: 
Read 6 items
Read 8 items
4N4P9N7P1N1P2N2P
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.