# Sinusoidal text

Goal: Write a program or function which prints an input string in a sinusoidal shape.

### The ASCII sinusoid

Here is one period of the sinusoid:

         .......
...       ...
..             ..
.                 .
.                   .
.                     .
.                       .                       .
.                     .
.                   .
.                 .
..             ..
...       ...
.......


Note that there is exactly one dot on each column.

• Each character in the input string will replace a dot in the shape above, from left to right.
• Spaces in the input have to be outputted like normal characters, in place of a dot.
• The starting character corresponds to the leftmost dot in the figure above.
• This is only one period, inputs can be longer than the number of dots above.

### Input

• Inputs are ASCII strings that contain only characters between ASCII decimal 32 (space) and ASCII decimal 126 (Tilde ~).
• Inputs will always be one line only (no linebreaks).
• Inputs can be taken via STDIN, function parameters, command line arguments, or anything similar.

### Output

• Output must be printed exactly like they are in the test cases given.
• Trailing spaces on lines are allowed as long as the length of the line with those trailing spaces does not exceed the length of the longest line (the one with the last character on it).

### Test cases

• Input: .................................................

Output:

         .......
...       ...
..             ..
.                 .
.                   .
.                     .
.                       .                       .
.                     .
.                   .
.                 .
..             ..
...       ...
.......

• Input: Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

Output:

         ng Puzz                                         ion and                                         siasts                                          stratio
mmi       les                                   est        an                                   thu       and                                   egi       n r
ra              &                               qu             sw                               en              c                                r             eq
g                                                                 e                                               o                             o                 u
o                   C                           a                   r                           e                   d                           n                   i
r                     o                                                                         l                     e                                               r
P                       d                       s                       s                       z                                               ,                       e
e                     i                         i                     z                         g                     e                         d
t                   u                           o                   e                           .
G                 e                             e                 p                             l                 r
ol             ng                                f             g                                fe              f
f S       cha                                   or        min                                   rs.       00%
tack Ex                                         program                                          It's 1

• Input: Short text.

Output:

         t.
tex
t
r
o
h
S

• Input: The quick brown fox jumps over the lazy dog

Output:

          brown
ick       fox
qu              j
u
e                   m
h                     p
T                       s

o
v
er
th       dog
e lazy


### Scoring

This is , so the shortest program or function in bytes wins.

• This is what I was thinking of Jul 26, 2015 at 12:52
• Oh I see it is indeed somewhat similar. Jul 26, 2015 at 12:56
• Incidentally, your wave is not quite sinusoidal. (Naturally I tried using a sin function to reproduce it but the positions are a little off.) Jul 27, 2015 at 11:09
• @DavidZ That doesn't surprise me, I eyeballed the ASCII shape. Can you even get something sinusoidal without any "gap" in the columns (i.e. exactly one dot per column)? Jul 27, 2015 at 11:11
• I spent a couple minutes entertaining myself by moving the scroll bar on test case 2's output back and forth really fast. Jul 27, 2015 at 16:41

## Python 2, 156 bytes

l=map(int,"654322111%08d1122345"%1);l+=[12-c for c in l]
def f(t):
h=len(t);o=bytearray(' '*h+'\n')*13;i=0
for c in t:o[i-~h*l[i%48]]=c;i+=1
print o[:-1]


## Explanation

• The whole code simply makes a block of spaces (o) and replaces the right spaces with the letters of the input t.

• The variable l stores a list of offsets from the top. So that the nth character of t should be on line l[n].

• The bytearray o serves as a mutable string, since strings are immutable in python.

• -~h is the same as h+1 but saves space because I don't need parentheses.

# Pyth, 59 bytes (57 characters)

Xjb.sC.>V+R*12\ Xz\ C9*+-L12K+JsM._+6jC\཈2tP_JKlz]*dlzC9d


Demonstration.

A binary lookup table is encoded inside ཈, which has value 3912. This is converted to binary, giving [1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0]. This is treated as the differences between consecutive heights. By prepending a 6, forming all prefixes and mapping each to its sum, the first quarter of the wave is generated.

sM._+6jC\཈2 evaluates to [6, 7, 8, 9, 10, 10, 11, 11, 11, 12, 12, 12, 12] as described above. Then, the code concatenates on the reverse of this string to form the first half of the wave, and then subtracts it from 12 to give the entire wave.

Then, we form lines of each input character followed by 12 spaces. This line is rotated to the right by the wave height parameter corresponding to that location, and then the lines are transposed and joined on newlines.

Then, we strip off leading and trailing blank lines. However, we can't strip off leading or trailing blank lines that have spaces from the input. This is implemented by replacing spaces in the input with tabs (C9), which can't be in the input, stripping blank lines, and turning the tabs back into spaces.

• @FryAmTheEggman Fixed, at the cost of 16 bytes. Jul 28, 2015 at 0:18

## Java, 219209 199 bytes

void p(char[]s){int r=6,c;String t="";for(;r>-7;r--,t+='\n')for(c=0;c<s.length;c++)t+=(s(c%48)==r?s[c]:' ');System.out.println(t);}int s(int a){return a<4?a:a<6?4:a<9?5:a<15?6:a<24?s(24-a):-s(a-24);}


I'm still a newbie here, and hope that it is compliant to the rules to introduce a sub-function (when the bytes of this function are counted, of course). If not, I'll try to convert the sin function into some clever array lookup...

public class SinusText
{
public static void main(String[] args)
{
SinusText s = new SinusText();
s.p(".................................................".toCharArray());
s.p("Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.".toCharArray());
s.p("Short text.".toCharArray());
s.p("The quick brown fox jumps over the lazy dog".toCharArray());
}
void p(char[]s){int r=6,c;String t="";for(;r>-7;r--,t+='\n')for(c=0;c<s.length;c++)t+=(s(c%48)==r?s[c]:' ');System.out.println(t);}int s(int a){return a<4?a:a<6?4:a<9?5:a<15?6:a<24?s(24-a):-s(a-24);}
}

• For some string-based challenges it also saves if you take input as a char[]. Here, it would get rid of the () on length and eliminate charAt() as well. If I'm reading it right, you can also use print() instead of println() to save a couple more. Jul 27, 2015 at 5:21
• @Geobits These are the degrees of freedom that I was not aware of. The task description talks about a "String", so I thought that it had to be "THE" String representation of the respective language. ... Jul 27, 2015 at 8:35
• Yea, I asked about it on meta some time back. Here's a link for reference: meta.codegolf.stackexchange.com/q/2214/14215 Jul 27, 2015 at 8:37
• Thanks, it's 209 then. (Maybe I'll try to squeeze out a few more bytes later. The "sin" function still looks too verbose...) Jul 27, 2015 at 8:41
• Hmm, not a huge improvement, but you can cut 10 by doing the whole thing modulus 48. Change the end to ...a<24?s(24-a):-s(a-24); and call it with s(c%48). Jul 27, 2015 at 14:28

# Perl 5 + -lF -Mbigint -M5.10.0, 97 bytes

Coming back to this after a while it's nice to see how much my approach to this kind of thing has improved, -123 bytes!

## JavaScript, 251243224220 217

Really simple implementation: it uses a string of characters to represent the y-position of each character on the wave (offset by a, which is ASCII code 97). It then iterates through all possible rows; if the y-value of the current row is the same as the y-position on the wave, it writes a character from the string. There's also a cleanup at the end to remove the row if it turned out to be completely blank.

Note that the output will appear wonky in the alert() window if it's not using a monospaced font, you can change it to console.log() to verify the output is correct.

s=prompt(o=[])
for(y=i=0;y<13;++y){o[i]=""
for(x=0;x<s.length;++x)o[i]+=y=="gfedccbbbaaaaaaabbbccdefghijkklllmmmmmmmlllkkjih".charCodeAt(x%48)-97?s[x]:" "
if(o[i++].trim().length<1)o.splice(--i,1)}


EDIT1: ++ and -- exist.

EDIT2: Blank line removal is now done in the same loop as the rest, saving 17 characters. Didn't need those brackets either, for an extra 2 characters.

EDIT3: No need to declare the waveform as a variable, saving 4 characters.

EDIT4: As pointed out by Dom Hastings in the comments, the byte count included the carriage return as well as the newline character, updated the byte counts for all revisions to exclude the carriage return.

EDIT5: Saved 3 bytes courtesy of Dom Hastings. I haven't implemented the o.splice fix as this fails to remove the blank lines (on my end, at least).

• Nice work! A few places you can save a couple more bytes: Replace: if(o[i++].trim().length<1)o.splice(--i,1) with o.splice(i-(t=!o[i++].match(/\s/)),t), for -4, s=prompt() o=[] with: s=prompt(o=[]), -1 and for(y=0,i=0;y<13;++y){o[i]="" with for(y=i=0;y<13;++y){o[i]="", -2. It's probably possible to combine your for loops too to save more... One last thing, it's worth noting as well that I only have 220 for your current byte count, so your 225 might be windows \r\n instead of just \n which I assume you can ignore (please correct me if I'm wrong)... Jul 27, 2015 at 14:53
• Good catch on the carriage return! Next time I won't trust Notepad++ so much :) Jul 27, 2015 at 16:05
• I think I have it reduced to 166. Can anyone else verify? I changed the array behavior to a log throughout the program. i used a short circuit instead of an if statement, and got rid of the brackets by putting the log at the end of the first for loop. for(s=prompt(),y=0;y<13;y++,v.trim()&&console.log(v))for(v="",x=0;x<s.length;x++)v+=y=="gfedccbbbaaaaaaabbbccdefghijkklllmmmmmmmlllkkjih".charCodeAt(x%48)-97?s[x]:" " Jul 28, 2015 at 21:25
• using falsy zero to replace == with subtraction, 165 char for(s=prompt(y=0);y<13;y++,v.trim()&&console.log(v))for(v="",x=0;x<s.length;x++)v+="gfedccbbbaaaaaaabbbccdefghijkklllmmmmmmmlllkkjih".charCodeAt(x%48)-97-y?" ":s[x] Jul 28, 2015 at 21:52
• I'm having trouble pasting it from my comment so heres a pastebin bit.ly/1VQgGXw 217->166 = 76% Jul 30, 2015 at 22:12

## Matlab, 133, 130 bytes

The one liner:

s=input('');y=ceil(5.6*sin(0:pi/24:pi-.1).^.9);l=[-y y]+7;n=numel(s);t=repmat(' ',13,n);for k=1:n;t(l(mod(k-1,48)+1),k)=s(k);end;t


And the expanded version:

function f(s)
y=ceil(5.6*sin(0:pi/24:pi-.1).^.9);l=[-y y]+7;  %// calculate the line number for each column position
n=numel(s);                                     %// number of character in input
t=repmat(' ',13,n);                             %// Create a blank canvas of whitespace characters
for k=1:n
t(l(mod(k-1,48)+1),k)=s(k);                 %// place each input character where it should be
end
t                                               %// force the output display


The one liner takes input from the console (stdin) and is 130 bytes. The expanded version replace the console input by a function definition (+1 byte) but is much more comfortable to use for the test case in a loop:

Description:

The line index of each character is calculated for a half period, then mirrored and concatenated to have a full period.
We create a blank background of whitespace character (same length as the input string. We place each character according to its position in the relevant line. If the input string is longer than one period, the mod (modulo) operator wraps that so we don't get out of bound when requesting the line number.

Test case:

Save the function version under textsine.m in your path, then run:

s = {'.................................................';...
'Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It''s 100% free, no registration required.';...
'Short text.';...
'The quick brown fox jumps over the lazy dog'};

for txtcase=1:4
textsine(s{txtcase,1})
end


will output:

t =

.......
...       ...
..             ..
.                 .
.                   .
.                     .
.                       .                       .
.                     .
.                   .
.                 .
..             ..
...       ...
.......

t =

ng Puzz                                         ion and                                         siasts                                          stratio
mmi       les                                   est        an                                   thu       and                                   egi       n r
ra              &                               qu             sw                               en              c                                r             eq
g                                                                 e                                               o                             o                 u
o                   C                           a                   r                           e                   d                           n                   i
r                     o                                                                         l                     e                                               r
P                       d                       s                       s                       z                                               ,                       e
e                     i                         i                     z                         g                     e                         d
t                   u                           o                   e                           .
G                 e                             e                 p                             l                 r
ol             ng                                f             g                                fe              f
f S       cha                                   or        min                                   rs.       00%
tack Ex                                         program                                          It's 1

t =

t.
tex
t
r
o
h
S

t =

brown
ick       fox
qu              j
u
e                   m
h                     p
T                       s

o
v
er
th       dog
e lazy


if you want to test the one liner version with input from stdin, your input has to be entered as one single string, so you'd have to enclose your input between ' characters. Example:

'Short text.'   %//   valid input
Short text.     %// INVALID input


Thanks Luis Mendo for shaving up 3 bytes ;-)

• @LuisMendo, thanks for the 3 bytes saved :-). I explained how to input a proper string so the simple s=input(''); would still work.
– Hoki
Jul 29, 2015 at 13:16
• s=input('');y=ceil(5.6*sinpi(0:1/24:.97).^.9);l=[-y y]+7;n=nnz(s);t=repmat(' ',13,n);for k=1:n;t(l(mod(k-1,48)+1),k)=s(k);end;t -3 bytes; nnz can be used instead of numel because input can't contain zeros, saves 2 bytes; sinpi(0:1/24:.97) saves one byte compared with sin(0:pi/24:pi-.1) Sep 28, 2020 at 21:13

# Scala 377 characters

first cut. probably can get a better formula to translate x to y

(s:String)⇒s.zipWithIndex.map(t⇒(t._1,t._2,t._2%48 match{
case i if i<5⇒6-i
case 5|19⇒2
case 6|7|8|16|17|18⇒1
case i if i<16⇒0
case i if i<29⇒i%20+2
case 29|43⇒10
case 30|31|32|40|41|42⇒11
case i if i<40⇒12
case i if i>43⇒10-i%44
})).groupBy(_._3).toSeq.map{case(y,xs)⇒(""→0/:xs.sortBy(_._2)){case((p,l),(c,x,_))⇒(p+" "*(x-l-1)+c)→x}._1→y}.sortBy(_._2).map(_._1).mkString("\n")


# Common Lisp, 205 bytes

(lambda(s &aux p v o)(dotimes(r 13)(setf o 0 p v v(round(*(/ 24 pi)(+(asin(-(/ r 6)1))pi))))(when p(map()(lambda(c)(princ(if(some(lambda(k)(<= p(mod k 48)(1- v)))(,o,(- 23 o)))c" "))(incf o))s)(terpri))))


## Remarks

Print the output line by line, computing the indices in the strings that should be printed using the inverse sine function asin. The output do not match exactly the expected inputs in the question, but since OP acknowledges that the example outputs are not real sine, I guess this is ok. At least, there is always only one character written for each column.

# Python 2, 172 bytes

This isn't as good as Alex L's answer, but it's pretty close. Takes input from standard input and works best in a .py file.

l=map(int,bin(9960000)[2:]);l+=[-c for c in l];s=6;o=[];i=9
for c in raw_input():b=[' ']*13;b[s]=c;o+=[b];s+=l[i%48];i+=1
print''.join(sum(zip(*o+['\n'*13])[::-1],())[:-1])


I decided to build the output transposed (each column is a row) and then transpose the result, since in python the transpose of a matrix is map(*m).

• l: The binary representation of 9960000 (after chopping off the "0b" from bin) is 100101111111101001000000. This is the "step" of the sine wave each column, starting at the very last character of the very lowest point. I copy this list, negate each number, and tack it onto the end of itself to form what is effectively a derivative of the function.
• s: This is the variable that keeps track of which row (column in the transpose) the next character gets inserted into.
• o: End output, transposed
• i: Keeps track of sine wave period. Starts at 9 since l is shifted slightly.

In the for loop, I create a list of 13 spaces (I was using bytearrays but lists of characters turn out to have a shorter print statement), then replace the sth character with the input character. Append b to the end of o, add the appropriate step to s, and increment i.

I had hoped the print statement would be as simple as \n'.join(*zip(o)), but no such luck. zip(*o+['\n'*13])[::-1] appends a column of newlines and then reverses and transposes the whole thing (without the reversing, the sine wave is upside down), sum(...,()) concatenates the tuples together into one tuple of characters, and then ''.join(...) concatenates the characters and prints it.

Other things I tried were making a 12-character array of spaces and inserting the new character into the appropriate place, and replacing l+=[-c for c in l]; with some sort of math with some sort of multiplication of 1 and -1 with the result of indexing into l, but nothing I could come up with ended up being shorter.

# Mathematica, 131 bytes

i=[input string];c=Characters@i;l=Length@c;StringJoin@Riffle[StringJoin@@@SparseArray[Table[{7-Round[6 Sin[.13(x-1)]],x},{x,l}]->c,{13,l}," "],"\n"]


That's 131 characters, including the three for i=foo;. That seemed a reasonable way to take the input; I could have put it straight into the definition of c and saved a few strokes, but that feels unfair.

It's pretty straightforward -- almost even readable. It splits the string into a list of characters, and then puts those characters into a sparse array at the positions determined from the Table (any spot in the array not given a character defaults to a space). The lines are assembled separately, then newlines sprinkled between them. The final StringJoin stitches it all up.

NB: Like some other solutions, this may not actually be valid because it produces a real sinusoid rather than the (beautiful) handcrafted example.

## Tests:

(*i=Programming Puzzles...*)
ng Puzz                                          on and                                          iasts                                           tration
mi       le                                     sti       a                                      us      and                                     is        r
am           s                                   e           ns                                  th            c                                 eg           eq
r               &                               qu              we                              en               o                               r               u
og                  C                                               r                                               d                            o                  ir
r                     o                          a                                               e                    e                          n                     e
P                       d                       s                       si                       l                       g                                               d
e                     i                          t                    zz                         o                     ,                         .
G                                               e                  u                            lf                 ee
o               ge                               f               p                               e               r
lf           an                                  or           g                                  rs            f
St      ch                                       p      min                                     .        0%
ack Ex                                          rogram                                          It's 10
(*i=.... ...*)
.......
..       ..
..           ..
.               .
..                 ..
.                     .
.                       .                       .
..                    .
.                  .
.               ..
..           ..
...      ..
......
(*i= Short text.*)
t.
ex
t
t
or
h
S

(*i=The quick...*)
brown
ck       fo
ui           x
q               j
e                  um
h                     p
T                       s
o
v
e
r            g
the      do
lazy
`