# Hidden stereogram message

You must generate an text stereogram according to an input string containing a paragraph of text, an empty line then the hidden message. The result will be displayed as a pair of paragraphs, one with different spacing causing the effect of it being raised up when viewed stereographically (An explanation can be found here).

### Example

Input:

I invented vegetarianism.  It is a diet involving no meat, just vegetables.  It is also common in cows - they are awesome.

vegetarianism. is awesome.


Output:

I      invented    I      invented
vegetarianism.    vegetarianism.
It   is a  diet    It  is  a  diet
involving    no    involving    no
meat,      just    meat,      just
vegetables.  It    vegetables.  It
is  also common    is  also common
in cows  - they    in cows  - they
are    awesome.    are   awesome.


### Bonuses

• Add option to choose between parallel and cross-eyed as user input (-20)
• Adjustable column width as user input (-50)

This is code golf, so shortest code after bonuses wins.

• This is the first time I see a stereogram with text. This is awesome. – Michael M. Mar 19 '14 at 15:56
• They don't work for me :/ (I have eye problems that inhibit stereographic vision) – David Wilkins Mar 19 '14 at 19:26
• Holy crap I just saw it... This is impressive! – WallyWest Mar 20 '14 at 10:57
• @Glenn Randers-Pehrson would you mind explaining why you added the [sorting] tag? – user12205 Mar 29 '14 at 15:06
• It was a mistake. I thought I was on another question, and cannot find a way to cancel. If it gets approved then I'll roll it back. – Glenn Randers-Pehrson Mar 29 '14 at 15:10

## Bash, sed: 228223197 (242 - 70) = 172

c=${5:-=};R=$c;L=;for f in r l;do
e="sed -e ";$e"$ d;s/$\| */ \n/g"$1>m
o=1;for w in $e"$ p;d" $1;do$e"$o,/^$w /s/^$w /$L$w$R /" m>n;o="/$c/" cp n m;done;tr -d \\n<n|fold -sw${2:-35}|$e"s/$c/ /g">$f L=$c;R=;done;pr -tmw${3:-80}${4:-l r}


If the script is in an executable file called "stereo", then type

stereo file.in [column_width [page_width ["r l"]]]


column_width is a number; 25-45 will work, default is 35.

page_width is a number, should be about twice the column_width, default is 80

For cross-eyed viewing, use "r l" as the 4th argument. Default is "l r" which sets up for parallel viewing.

EDIT: Rewrote to split the file into one word per line, then reassemble at the end. Note: reserves the "=" sign for its own use. Any "=" signs in the input file will become blanks.

EDIT: If your message has "=" signs in it, you can choose another symbol for the script to use, by supplying it as the 5th parameter.

Example

Input: vegetarianism.txt:

I invented vegetarianism.  It is a diet involving no meat, just
vegetables.  It is also common in cows - they are awesome.

vegetarianism. is awesome.


Result

./stereo vegetarianism.txt 32 72 "l r" : | expand (using the colon for its internal working symbol)

I invented  vegetarianism. It       I invented vegetarianism.  It
is a diet involving no meat,       is  a diet involving no meat,
just vegetables. It is also         just vegetables. It is also
common in cows - they are           common in cows - they are
awesome.                           awesome.


./stereo washington.txt 35 75 "l r" |expand

In a little district west of          In a little district west of
Washington Square the streets        Washington  Square the streets
have run crazy and broken             have run crazy and broken
themselves into small strips          themselves into small strips
called 'places'. These 'places'       called 'places'. These 'places'
make strange angles and curves.       make strange angles and curves.
One Street crosses itself a time      One Street crosses itself a time
or two. An artist once discovered     or two. An artist once discovered
a valuable possibility in this        a valuable possibility in this
street. Suppose a collector with a    street. Suppose a collector with a
bill for  paints, paper and canvas    bill for paints,  paper and canvas
should, in traversing this route,     should, in traversing this route,
suddenly meet  himself coming         suddenly meet himself  coming
back, without a cent having been      back, without a cent having been
paid on account!                      paid on account!


The "|expand" isn't necessary but when shifting the output by 4 places the TABs get handled incorrectly. It could be put into the script at a cost of 7 bytes.

## ImageMagick variation

Replacing the last line with a text-to-image ImageMagick command:

c=${6:-=};R=$c;L=;for f in r l;do
e="sed -e ";$e"$ d;s/$\| */ \n/g"$1>m
o=1;for w in $e"$ p;d" $1;do$e"$o,/^$w /s/^$w /$L$w$R /" m>n;o="/$c/" cp n m;done;tr -d \\n<n|fold -sw${2:-35}|$e"s/$c/ /g">$f L=$c;R=;done;
convert -border 10x30 label:@${4:-l} label:@${5:-r} +append show:


In this one, the "r" and "l" for cross-eyed versus parallel viewing are separate arguments:

./im_stereo vegetarianism.txt 40 80 l r =

(source: simplesystems.org)

# TeX 212

I am using a typesetting system, not ASCII. The column width can be changed by changing 90pt in the fourth line, but I don't know if that's enough to qualify for the 50 bytes discount. The distance between the two copies of the text can be changed by changing the 9pt, also in the fourth line. The code can probably be made shorter. One can replace each newline by a single space, but not remove them completely.

\let\e\expandafter\read5to\t\read5to\.\def\a#1
{\def\~##1#1##2\a{\def\t{##1\hbox{\
#1\~{}}##2}\a}\e\~\t\a}\e\a\.{}\shipout\hbox
\let\~\ \def\ {}\vbox{\t}}\end.


After calling tex filename.tex in the terminal, the user is prompted to give the main text, then prompted again for a list of words to shift. No empty line in between. The (space-separated) list of words given in the second line should appear exactly as it is in the main text (punctuation is treated just like a letter would be, only spaces delimit words).

• I don't have a TeX compiler. Could I see a picture? – aebabis Mar 19 '14 at 18:19
• I meant adjustable by user input rather than editing the code, otherwise this bonus would apply to almost any code. – kitcar2000 Mar 19 '14 at 18:38

# Javascript 391 (441 - 50)

(My first code golf)

k=' ';Q='length';A=prompt().split(k);S=prompt().split(k);i=-1;M=25;L=[[]];j=0;R='';while(i++<A[Q]-1){if((j+A[i][Q])<M){if(S.indexOf(A[i])>-1){A[i]=(j?k+k:k)+A[i]}L[L[Q]-1].push(A[i]);j+=A[i][Q]+1}else{j=0;i--;L.push([])}}for(i=0;i<L[Q]-1;P(L[i++].join(C))){C=k;while(L[i].join(C+k)[Q]<M){C+=k}}P(L[i].join(k)+k);function P(a){while(a[Q]<M){a=a.replace(k,k+k)}R+=a;for(c in S){a=a.split(k+k+S[c]).join(k+S[c]+k)}R+=k+k+a+'\n'}console.log(R);


## Result

In    a  little  district   In    a  little  district
west    of     Washington   west    of    Washington
Square   the streets have   Square   the streets have
run    crazy  and  broken   run    crazy  and  broken
themselves    into  small   themselves    into  small
strips   called 'places'.   strips   called 'places'.
These     'places'   make   These     'places'   make
strange     angles    and   strange     angles    and
curves.     One    Street   curves.     One    Street
crosses  itself a time or   crosses  itself a time or
two.     An  artist  once   two.     An  artist  once
discovered    a  valuable   discovered    a  valuable
possibility     in   this   possibility     in   this
street.      Suppose    a   street.      Suppose    a
collector   with  a  bill   collector   with  a  bill
for   paints,  paper  and   for  paints ,  paper  and
canvas      should,    in   canvas      should,    in
traversing   this  route,   traversing   this  route,
suddenly   meet   himself   suddenly   meet  himself
coming    back, without a   coming    back, without a
cent  having been paid on   cent  having been paid on
account!                    account!


## Long code:

var arr = "In a little district west of Washington Square the streets have run crazy and broken themselves into small strips called 'places'. These 'places' make strange angles and curves. One Street crosses itself a time or two. An artist once discovered a valuable possibility in this street. Suppose a collector with a bill for paints, paper and canvas should, in traversing this route, suddenly meet himself coming back, without a cent having been paid on account!".split(' ');
var secret = "Washington paints himself".split(' ');
var i = -1;
var MAX_WIDTH = 25;
var lines = [[]];
var _l = 0;

var result = '';

while (i++ < arr.length - 1) {
if ((_l + arr[i].length) < MAX_WIDTH) {
if (secret.indexOf(arr[i]) > -1) {arr[i] = (_l?'  ':' ') + arr[i]}
lines[lines.length - 1].push(arr[i]);
_l += arr[i].length + 1;

} else {
_l = 0;
i--;
lines.push([]);
}
}

for (var i = 0; i < lines.length - 1; putText(lines[i++].join(chars))) {
// Align text
var chars = ' ';
while (lines[i].join(chars + ' ').length < MAX_WIDTH) {
chars += ' ';
}
}
putText(lines[i].join(' ') + ' ');
function putText(line) {
while (line.length < MAX_WIDTH) {
line = line.replace(' ', '  ');
}
// Make the illusion
result += line;
for (var val in secret) {
line = line.split('  '+secret[val]).join(' ' + secret[val] + ' ');
}
result += ('   ' + line) + '\n';
}
console.log(result);

• Nicely done. You can save a bunch (~17) if you add Q='length' and then replace things like A.length with A[Q]. – DocMax Mar 24 '14 at 5:33
• @DocMax thanks, that is a nice trick. I'm new in codegolf, thanks for your suggestion :) – TrungDQ Mar 24 '14 at 6:09
• "Washington paints himself"? – Joe Z. May 10 '14 at 19:35

# Javascript 493 (minimum expectations)

g=" ";l=prompt().split(g);r=l.slice();m=prompt().split(g);f=[];s=f.slice();w=0;n=0;a="";for(i=0;i<l.length;i++){if(l[i]==m[0]){m.shift();l[i]=g+r[i];r[i]+=g;}if(l[i].length+1>w)w=l[i].length+1;}while(l.length){f[f.length]="";s[s.length]="";while(l.length&&f[f.length-1].length+l[0].length<w){f[f.length-1]+=l[0]+g;s[s.length-1]+=r[0]+g;l.shift();r.shift();}f[f.length-1]+=g.repeat(w-f[f.length-1].length);}console.log(f,s);while(f.length){a+=f[0]+s[0]+"\n";f.shift();s.shift();}console.log(a);


This code sets up two arrays of lines (left and right), arranges them in a string and prints to f12 console.

This is just a minimum answer, not intended to win.

• 418 with JavaScript: L=b=>b.length;c=console.log;p=prompt;r=(l=p().split(g=" ")).slice(),m=p().split(g),s=(f=[]).slice(),n=w=a="";for(i=0;i<L(l);i++)l[i]==m[0]&&(m.shift(),l[i]=g+r[i],r[i]+=g),L(l[i])+1>w&&(w=L(l[i])+1);for(;L(l);){f[L(f)]="";for(s[L(s)]="";L(l)&&L(f[L(f)-1])+L(l[0])<w;)f[L(f)-1]+=l[0]+g,s[L(s)-1]+=r[0]+g,l.shift(),r.shift();f[L(f)-1]+=g.repeat(w-L(f[L(f)-1]))}for(c(f,s);L(f);)a+=f[0]+s[0]+"\n",f.shift(),s.shift();c(a) – WallyWest Mar 20 '14 at 11:42

# GolfScript 209 (279 -50 -20)

This is my first big GolfScript program. I wouldn't be surprised if there are optimizations to be had. Both the bonuses are supported; they are expected to be found after the message inputs, like:

"I invented vegetarianism.  It is a diet involving no meat, just vegetables.  It is also common in cows - they are awesome."

"vegetarianism. is awesome."

16  # column width
0   # view type, 1 for cross eyed (?)


If you've saved that file to input (and downloaded GolfScript) you can invoke the script with:

> cat input | ruby golfscript.rb


### Golfed

~{{\}}{{}}if:v;:w;n%~' '%\' '%[.]zip 0:c;{' '*}:s;{[.;]}:r;\{:x;{.c=0=x=}{1c+:c;}until.c<\1c+>[[x' 'v+' 'x v+]]\++}/zip{0:c;[[]]\{.,.c+w<{1c++:c;\)@r+r+}{:c;[r]+}if}/{.{,}%{+}*w\- 1$,1-.{1$1$/@@%@0:g;{3$3$g>+s\++1g+:g;}*\;\;}{[;.2/\2%1$s@@+s@)\;\]{+}*}if}%}%zip{{4s\++}*}%n*puts


### Ungolfed

~
#The program:

# Parameters, in reverse natural order

{{\}}{{}}if:v;   # view - truthy for parallel, falsey for cross-eyed
:w;         # col width

n%~         # split input on newlines

' '%\       # split secret message tokens
' '%        # split public message

[.]zip      # left and right

0:c;        # word count

{' '*}:s;   # spaces
{[.;]}:r;   # array of top

# for each secret word
\{

:x;       # word

{.c=0=x=}
{1c+:c;} until
# next public word is this private word

# splice edits
.c< \1c+> [[x' 'v+  ' 'x v+]]\ ++

}/
zip

# layout both messages
{

0:c;    # char count

[[]]\   # line accumulator

# split lines
{

.,.c+w<
# if enough room on line

#append to current line
{1c++:c;
\)@r+r+
}

#append as new line
{:c;
[r]+
}if

}/

# set lines
{

.{,}%{+}* # line chars
w\-       # space chars
1$,1- # gaps between words # if multi word .{ 1$1$# duplicate params /@@ # chars per space % # extra space gaps @ # load line 0:g; # current gap # join row { 3$3$# params g>+ # extra space s # space chars \++ # append 1g+:g; # update gap }* \;\; # drop params } # else one word { [ ; # drop gap count . # num spaces needed 2/\ # spaces per side 2% # extra space 1$s       # left space
@@+s      # right space

@)\;\     # word

]{+}*     # join

}if

}% # end line layout

}% # end message layout

zip

{{4s\++}*}%

n*

puts


# JavaScript 391

_='L=b=>b.length;c=console.log;p=prompt;r=(l*=" ")3m*),s=(f=[]3n=w=a52i=0;i<67i++)l/==m@&&(m!,l/=g+r/,r/8g),?>w&&(w=?72;67){9$]5‌​2:]56)&&%#)+64)<w;)#8l4+g,:-1]8r@+g,l!,r!;#8g.repeat(w-%#))}2c(f,s7%f7)a8$f4+s4+"‌​\\n",f!,s!;c(a)!.shift()#9-1]$??%L(*=p().split(g/[i]2for(3).slice(),4[0]5="";6%l7)‌​;8+=9f[%f):s[%s)?6/)+1@[$0]';for(Y in $='@?:98765432/*%$#!')with(_.split(\$[Y]))_=join(pop());eval(_)