# Draw a Bumpy String

(Inspired by this challenge.)

Let's say we have a string ABBCBA. We can say that there is a rise between A and B, for B follows A; we can say that there is a run between B and B, for nothing changes; and finally we can say there is a fall between C and B. We can draw a graph like this:

             A   B   B   C   B   A
Rising:        o       o
Continuing:        o
Falling:                   o   o


Without the labels, and minimizing whitespace:

o o
o
oo


This is the expected output for input ABBCBA.

You may use any non-whitespace character to replace o in the output. Further, each column may optionally have an extra space between them, like so:

o   o
o
o o


The input will consist of at least three characters. The string will consist entirely of uppercase letters, but you may instead use lowercase letters.

## Test cases

TEST CASE
LINE 1
LINE 2
LINE 3

HELLOWORLD
o oo o
o
o    o oo

TESTCASE
oo  o

o  oo o

EXAMINATION
o o o o o

o o o o o

ZSILENTYOUTH
o ooo o

oo o   o oo

ABC
oo

ABCBA
oo

oo

• Could there be a space between each consecutive os, or does the output have to be compact? Sep 25 '16 at 0:04
• @JHM Sure, that's fine. Sep 25 '16 at 0:05
• Also, does the output have to be a string, or does it only need to look similar to the example? Sep 25 '16 at 0:06
• @JHM What do you ahve in mind? Sep 25 '16 at 0:06
• The code I have in mind generates a grid. Sep 25 '16 at 0:07

# Mathematica, 938368 64 bytes

(uses 0, not O)

Row[Column@Insert[{,},0,2-#]&/@Sign@Differences@LetterNumber@#]&


# Explanation

LetterNumber@#


Gets the position in the alphabet of each characters of the input.

Sign@Differences@


Takes the difference between each consecutive elements, and takes the sign (-1 for negative/falling, 0 for 0/continuing, 1 for positive/rising)

Insert[{,},0,2-#]&


Inserts a 0 in a list of two Nulls, in the first position if rising, middle if continuing, and third position if falling.

Row[Column@ ... ]


Formats the output.

If the output could look different from the one in the question, the above code could be shortened to 41 bytes:

ListPlot@*Sign@*Differences@*LetterNumber


... which creates something like this (for "ABBCBA"):

• How does the 41 byte look? Sep 24 '16 at 23:54
• @ConorO'Brien please see edit. Sep 24 '16 at 23:56

# MATL, 15, 14 bytes

dZSqtQtQv~79*c


Try it online!

Explanation:

They say a picture is worth a thousand words, so here is a beta online interpreter that shows you the value on top of the stack live as it updates. Note that it's still in beta, so you might need to hit run several times.

So first, we call dZS. d gives us the difference between each consecutive element, and ZS gives us the sign (-1, 0, or 1) of each element. So with 'HELLOWORLD' as input, after the first step we'll have:

-1  1  0  1  1 -1  1 -1 -1


Now, we just use q to decrement this and get:

-2  0 -1  0  0 -2  0 -2 -2


And then two times we duplicate the top of the stack and increment the array (tQ) After this we'll have

-2  0 -1  0  0 -2  0 -2 -2
-1  1  0  1  1 -1  1 -1 -1
0   2  1  2  2  0  2  0  0


Now all of the '0's are where we want to output a character. So, we join these three arrays into a matrix (v), and logically negate it (~). Then we multiply every value in the matrix by the ASCII value of 'O', (79*) and display it as a string with c.

• Once you have the vector [-1, 1, 0, 1,...], you could use them as the row indices of a sparse matrix with column indices [1,2,3,4,...], then convert it to a full matrix. Sep 25 '16 at 3:44
• OK nevermind, tried that suggestion, doesn't seem to save anything Sep 25 '16 at 4:13
• @NickAlger Thanks for the tip anyway! Out of curiosity, could I see what you came up with? Sep 25 '16 at 4:14
• Sure. The following is 19 chars, though it could probably be improved a few, dZS2+tn:tnZ?XPg79*c Sep 25 '16 at 4:23
• Got it to 16 with a couple optimizations, dZSqq_tn:lZ?79*c Sep 25 '16 at 4:33

f w=[do(e,y)<-zip w$tail w;max" "['o'|b e y]|b<-[(<),(==),(>)]]  Returns a list of three strings, representing the lines of output. Contains no subliminal messages. dianne saved three bytes by using do notation and max instead of a list comprehension and last. • Great, it contains no subliminal messages! What are those? Sep 25 '16 at 0:27 • ['o'|b e y].. Sep 25 '16 at 14:53 • Yes, my master Wait what's going on? Mar 3 '17 at 16:35 ## CJam, 19 bytes l2ew{:-g)S3*0t}%zN*  Uses 0 instead of o. Try it online! ### Explanation l e# Read input. 2ew e# Get all pairs of consecutive letters. { e# Map this block over the pairs... :- e# Compute the difference between the two letters. g e# Signum. Gives -1 for rises, 1 for falls, 0 otherwise. ) e# Increment. Gives 0 for rises, 2 for falls, 1 otherwise. Call this i. S3* e# Push a string with three spaces. 0t e# Replace the i'th space (zero-based) with a zero. }% z e# Transpose. N* e# Join with linefeeds.  # Jelly, 11 bytes OIṠ“ o ”ṙZY  ### How it works OIṠ“ o ”ṙZY Main link. Argument: s (string) O Ordinal; replace all characters with their code points. I Increments; compute the differences of consecutive code points. Ṡ Sign function. “ o ”ṙ Rotate that string -1, 0, or 1 unit(s) to the left. Z Zip; transpose rows and columns. Y Join, separating by linefeeds.  # Python 2, 76 71 bytes lambda s:[''.join(' o'[cmp(*x)==n]for x in zip(s,s[1:]))for n in-1,0,1]  Thanks to @xnor for notifying me that returning a list of strings is allowed. Test it on Ideone. • You're allowed to output a list of three strings, which lets you do a lambda. – xnor Sep 25 '16 at 5:41 • I am? That changes everything. Sep 25 '16 at 5:54 • I asked in the comments because Lynn's Haskell answer was doing it. – xnor Sep 25 '16 at 6:03 ## JavaScript (ES6), 96958987 82 bytes 2 bytes saved by using 0 instead of o, as suggested by Conor O'Brien 2 6 bytes saved thanks to ETHproductions let f = s=>[1,0,-1].map(k=>s.replace(/./g,(c,i)=>i--?(c>s[i])-(c<s[i])-k&&' ':'')).join  console.log(f("HELLOWORLD")); console.log(f("EXAMINATION")); • Since you can use any character, does replacing 'o' with 0 help any? Sep 25 '16 at 0:17 • @ConorO'Brien - Indeed, it does. ;) Sep 25 '16 at 0:19 • I think s=>[1,0,-1].map(k=>[...s].map(c=>(r=p?(c>p)-(c<p)-k&&' ':'',p=c,r),p=0).join).join\n  would work, saving 2 bytes. Sep 25 '16 at 1:27 • You can save another byte by grabbing the previous character each time instead of manually keeping track of it: s=>[1,0,-1].map(k=>[...s].map((c,i)=>(p=s[i-1])?(c>p)-(c<p)-k&&' ':'').join).join\n . s.replace will also save you several bytes over [...s].map().join(). Sep 25 '16 at 1:36 # Perl, 47 bytes Includes +1 for -p Give input on STDIN: bumpy.pl <<< ABBCBA  bumpy.pl: #!/usr/bin/perl -p$_ x=3;s%.%/\G(.)(.)/?$2cmp$1^$.&&$":--$.>0%eg  # MATL, 16 14 Bytes dZSqq_tn:79Z?c  Try it online! This grew out of a discussion of DJMCMahem's answer. Even though this answer is 2 characters longer the same length, the method is somewhat different so it may be of independent interest. Thanks to Luis Mendo for a suggestion saving 2 bytes (see comments) Explanation: 'dZS' gets a vector where each entry is the sign of the differences between sucessive characters, then 'qq_' decrements each entry by two and flips the sign, so now if the character increases it is 1, if it stays the same 2, and if it decreases 3. For example, dZSqq_ applied to 'HELLOWORLD' creates the vector [3 1 2 1 1 3 1 3 3]  Next, 't' makes a copy of the previous vector on the stack, then 'n:' places the vector [1,2,3,4,...] on the stack as well. Then '79' places the value 79 on the stack. The value 79 is chosen because it is the number for the unicode character 'o', which will be our output later. (Thanks to Luis Mendo for the idea to put the value 79 here rather than later) tn:79 applied to [3 1 2 1 1 3 1 3 3] creates the following items: [3 1 2 1 1 3 1 3 3] <-- first item on the stack [1 2 3 4 5 6 7 8 9] <-- second item on the stack 79 <-- third item on the stack  At this point we have precisely the row indices, column indices, and nonzero value of a sparse matrix that has the value 79 wherever we want the output character, and 0 wherever we want to output whitespace. We take these three items off the stack and create this sparse matrix with MATL's sparse matrix command 'Z?'. That is, dZSqq_tn:79 Z? applied to 'HELLOWORLD' outputs the following: [0 79 0 79 79 0 79 0 0 ] [0 0 79 0 0 0 0 0 0 ] <-- 3-by-n sparse matrix [79 0 0 0 0 79 0 79 79]  All that is left is to convert the matrix from numbers to unicode characters, which is done by the command 'c'. The 79's become 'o', and the 0's become spaces: dZSqq_tn:79Z?c applied to 'HELLOWORLD' outputs: [ o o o o ] [ o ] <-- 3-by-n sparse matrix of characters. [o o o o]  The resulting matrix of chars is then implicitly displayed. • You can directly use 79 as the nonzero value for the sparse matrix, thus saving two bytes. Also, I think this is the first time that sparse matrices are used in a MATL answer :-) Sep 25 '16 at 12:46 • @LuisMendo Thanks! I edited the post to make the change you suggest Sep 25 '16 at 18:37 # PHP, 95 Bytes for($b[1]=$b[0]=$b[-1]=" ";($s=$argv[1])[++$i];)$b[$s[$i-1]<=>$s[$i]][$i]=8;echo join("\n",$b);


1.Create an array of strings with the index -1 to 1 alternative $b=array_fill(-1,3," "); 2.Fill the strings dependent by the spaceship operator and position of the input 3.Output join the array with a new line First Way 111 Bytes for($o=" ";$i<$l=strlen($s=$argv[1])-1;)$o[$l*(1+($s[$i]<=>$s[$i+1]))+$i++]=8;echo join("\n",str_split($o,$l));  Use the spaceship operator <=> spaceship operator • If you encode your program in Latin-1, ~õ is a handy shortcut for "\n". No, seriously! – Lynn Sep 25 '16 at 0:37 • Same thing for " ", which can be ~ß. Example. You want to set your browser encoding to Latin-1 when viewing these. – Lynn Sep 25 '16 at 0:40 • @Lynn or ~³~†~‘~‘ Thank You for the idea. I will prefer unicode Sep 25 '16 at 11:20 # JavaScript (ES6), 81 bytes s=>[s,s,s].map(f=([c,...s],n)=>(p=s[0])?((c<p)-(c>p)+n-1&&" ")+f(s,n):"").join   Written from scratch, though it was heavily inspired by @Arnauld's answer. Uses recursion to calculate the contents of each row. # Ruby, 66 64 bytes ->s{(-1..1).map{|n|s.gsub(/.(?=(.))/){"o "[n+($1<=>$&)]}.chop}}  See it on eval.in: https://eval.in/649503 # Java 7, 158 156 bytes String c(char[]z){String a,b,c=a=b="";for(char i=1,q=z[0],o=79,s=32,x;i<z.length;a+=(x=z[i])>q?o:s,b+=x==q?o:s,c+=x<q?o:s,q=z[i++]);return a+"\n"+b+"\n"+c;}  2 bytes saved thanks to @Frozn. Ungolfed & test cases: Try it here. class M{ static String c(char[] z){ String a, b, c = a = b = ""; for(char i = 1, q = z[0], o = 79, s = 32, x; i < z.length; a += (x = z[i]) > q ? o : s, b += x == q ? o : s, c += x < q ? o : s, q = z[i++]); return a + "\n" + b + "\n" + c; } public static void main(String[] a){ print("HELLOWORLD"); print("TESTCASE"); print("EXAMINATION"); print("ZSILENTYOUTH"); print("ABC"); print("ABCBA"); print("ABBCBA"); print("UVVWVVUVVWVVUVVW"); } static void print(String s){ System.out.println(c(s.toCharArray())); System.out.println("-------------------------"); } }  Output:  O OO O O O O OO ------------------------- OO O O OO O ------------------------- O O O O O O O O O O ------------------------- O OOO O OO O O OO ------------------------- OO ------------------------- OO OO ------------------------- O O O OO ------------------------- O O O O O O O O O O O O O O O -------------------------  • I'm not sure whether this works but a,b,c=b=a="" would be shorter. Sep 26 '16 at 14:30 • @Frozn Thanks, edited. It indeed works. PS: you could have checked yourself in the ideone by forking it. ;) Sep 26 '16 at 14:51 • You're right! I'm always overlooking the links and starting up eclipse just for that isn't worth it :) Sep 26 '16 at 15:25 # Clora (20 bytes) <IN?o ;=IN?o ;>IN?o Explanation: There are 3 Clora programs, one for each output line. ### First program, <IN?o Check if current input char I is smaller < than the next char N. Save the result in a global flag. Check the flag result ? and if is true, output o, else a blank space (yes, there is a blank space there. All other programs, follow the same rule and are separated by ;, every program is executed and receives the input as argument. You can test it by yourself including clora.js and executing it with (function() { var x = new Clora('<IN?o ;=IN?o ;>IN?o '); x.execute('EXAMINATION', function(r) { console.log(r) }) })();  • This seems to be strictly non-competing, as it was created after this challenge. This looks like an interesting lang though! Sep 26 '16 at 18:36 # Pyth, 21 bytes jCmX*3\ h._d0-M.:CMz2  A program that takes input of an unquoted string on STDIN and prints the result. This uses a similar idea to @MartinEnder's CJam answer. How it works jCmX*3\ h._d0-M.:CMz2 Program. Input: z CMz Map ordinal over z, yielding the code-points of the characters .: 2 Yield all length-2 sublists of that -M Map subtraction over that m Map the following over that with variable d: ._d Yield the sign of d h Increment that (i) *3\ Yield string literal of 3 spaces, " " X 0 Replace the space at index i with 0 C Transpose that j Join that on newlines Implicitly print  # PHP 7, 8180 77 bytes Note: uses Windows-1252 encoding for($x=2;~$x--;print~õ)for($a=$argn;$c=$a[$$x+1];)echoc<=>a[$$x++]^$x?~ß:o;


Run like this:

echo HELLOWORLD | php -nR 'for($x=2;~$x--;print"\n")for($a=$argn;$c=$a[$$x+1];)echoc<=>a[$$x++]^$x?" ":o;';echo  # Explanation Iterates over lines (numbered 1, 0, -1). Then iterates over the input string for every line. When the result of spaceship comparison equals the line number, output an o, otherwise, output a space. After every line, print a newline. # Tweaks • Stop iterating when $x is -1, which we can find by binary negation (result 0). Saves a byte compared to adding 1 (or 2 with pre-increment).
• Saved 3 bytes by using \$argn
• You forgot to add -d error_reporting=30709 to your byte count. Sep 26 '16 at 19:29
• @Titus Why in the world would I need to add that to the byte count? It's only so PHP Notices (which are ignorable) are not printed! Sep 27 '16 at 7:31
• Could also add 2>/dev/null, but that will get rid of ALL errors, including fatal Sep 27 '16 at 7:35
• Oh, and a reference Sep 27 '16 at 7:54
• Something like If you get warnings, set the default value with .... Please excuse my pedantery; I didn´t decode that value. Sep 27 '16 at 12:23

Lua 326 303 bytes tl=0 s=io.read() o1,o2,o3="","","" t={} for i=1,#s do t[i]=s:sub(i,i) tl=tl+1 end for v=1,tl-1 do if t[v]t[v+1] then o1=o1.." " o2=o2.." " o3=o3.."o" end end print(o1.."\n"..o2.."\n"..o3)

An ungolfed version

tl = 0 --set the tables length to 0
s = io.read() --Get the string from input
o1,o2,o3="","","" --Set the 3 output rows to empty strings
t = {} --Make a table for the string to be sent into
for i = 1, #s do --Loop from 1 to the length of the string
t[i] = s:sub(i, i) --Set the I-th term in the table to the I-th character in the string
tl = tl+1 --Add 1 to the table length
end --End the loop
for v=1,tl-1, 1 do --Loop from 1 to the tables length - 1, incrementing by 1
if t[v] < t[v+1] then --Lua supports greater than less than and equals to with charactes, so this if statement detects if the string is rising
o1=o1.."o" --Adds an o to the end of the first line of output
o2=o2.." " --Adds a space to the second line
o3=o3.." " --Adds a space to the third line
elseif t[v] == t[v+1] then --Detects if the string is continuing
o1=o1.." " --Adds a space to the first line
o2=o2.."o" --Adds an o to the second line
o3=o3.." " --Adds a space to the third line
elseif t[v] > t[v+1] then --Detects if string is falling
o1=o1.." " --Adds a space to the first line
o2=o2.." " --Adds a space to the second line
o3=o3.."o" --Adds an o to the third line
end --Ends the if statement
end --Ends the loop
print(o1.."\n"..o2.."\n"..o3) --Prints the output

• I think you can golf out some whitespace from, say, t1 = 0? to t1=0? And similar places. Sep 26 '16 at 21:41
• I will fix that now Sep 26 '16 at 22:07

## R, 114 bytes

v=y=z=rep(" ",length(x<-diff(utf8ToInt(scan(,"")))));v[x>0]="#";y[x==0]="#";z[x<0]="#";cat(v,"\n",y,"\n",z,sep="")

3. Then replace the white space vectors with # if the differences are >0, ==0 or <0.