The challenge
The shortest code by character count to generate a wave from the input string.
A wave is generated by elevating (line-1) a higher character, and degrading (line+1) a lower character. Equal characters are kept on the same line (no elevating or degrading done).
Input is made of lower case characters and numbers only, letters are considered higher than numbers.
Test cases:
Input:
1234567890qwertyuiopasdfghjklzxcvbnm
Output:
z
l x v n
k c b m
j
h
g
y p s f
t u o a d
w r i
9 q e
8 0
7
6
5
4
3
2
1
Input:
31415926535897932384626433832795028841971693993751058209749445923078164062862
Output:
9 9 8 6 6
9 6 8 7 3 3 4 2 4 8 9 88
3 4 5 2 5 5 2 33 3 7 5 2 4 9 9 99 7
1 1 3 2 0 1 7 6 3 3 5 8 8 6
1 1 5 2 9 9 3 7 1 4 6 8
0 0 7 9 5 2 0 0 2 6
4 44 2
Code count includes input/output (i.e full program).
x86 machine code (37 bytes)
Hexdump:
6800B807BF8007BE8200B40EAC3C0D741338D8740A720481EF400181C7A000AB86C3EBE8C3
Run in MS-DOS with 50 line console, the input is taken from the command line.
E.g.
wave.com 1234567890qwertyuiopasdfghjklzxcvbnm
Update: Shaved off three bytes thanks to jrandomhacker
J
54 characters, if you let the interpreter handle input/output.
e=:|:@((#&' '@],[)"0[:(-<./)0,[:+/\[:(}:(>-<)}.)a.i.])
65 to explicitly read from stdin and write to stdout.
(|:((#&' '@],[)"0[:(-<./)0,[:+/\[:(}:(>-<)}.)a.&i.)~1!:1[3)1!:2[4
e '1234567890qwertyuiopasdfghjklzxcvbnm' z l x v n k c b m j h g y p s f t u o a d w r i 9 q e 8 0 7 6 5 4 3 2 1
e '31415926535897932384626433832795028841971693993751058209749445923078164062862' 9 9 8 6 6 9 6 8 7 3 3 4 2 4 8 9 88 3 4 5 2 5 5 2 33 3 7 5 2 4 9 9 99 7 1 1 3 2 0 1 7 6 3 3 5 8 8 6 1 1 5 2 9 9 3 7 1 4 6 8 0 0 7 9 5 2 0 0 2 6 4 44 2
NB. Look up ASCII codes ord =: a. i. ] ord 'p4ssw0rd' 112 52 115 115 119 48 114 100 NB. Going up? up =: }: < }. up ord 'p4ssw0rd' 0 1 0 1 0 1 0 NB. Going down? down =: }: > }. down ord 'p4ssw0rd' 1 0 0 0 1 0 1 NB. Combine to get ±1 updown =: }: (> - <) }. updown ord 'p4ssw0rd' 1 _1 0 _1 1 _1 1 NB. Start with 0, follow up with partial sums sum =: 0 , +/\ sum updown ord 'p4ssw0rd' 0 1 0 0 _1 0 _1 0 NB. Subtract the minimum to get sequence with base at 0 fix =: - <./ fix sum updown ord 'p4ssw0rd' 1 2 1 1 0 1 0 1 NB. For convenience, name this chain of functions d =: [: fix [: sum [: updown ord
NB. Make spaces before the characters push =: (#&' ' @ ] , [)"0 d push 'p4ssw0rd' p 4 s s w 0 r d NB. Turn it on its side |: push 'p4ssw0rd' w r p ss 0 d 4 NB. Combine into one named function… e =: |: @ push NB. …and inline everything e =: |:@((#&' '@],[)"0[:(-<./)0,[:+/\[:(}:(>-<)}.)a.i.])
The shortest code by character count to print a 'wave' from the input string.
Console.WriteLine("a 'wave' from the input string.");
Perl (94 characters)
144 characters originally by barnaba:
chop($_=<>);$l=length;push(@a," "x$l)for(1..$l*2);$l+=(ord $p<=>ord $_),substr($a[$l],$r++,1)=$p=$_ for(split //);/^\s+$/||print "$_\n" for(@a)
121 characters from optimization by Chris Lutz:
$_=<>;chop;$a[@a]=" "x$l for 1..($l=length)*2;$l+=$p cmp$_,substr($a[$l],$r++,1)=$p=$_ for split//;/\S/&&print"$_\n"for@a
94 characters from further optimization:
$_=<>;@a=($"x($l=y///c).$/)x(2*$l);s/./substr$a[$l+=$"cmp$&],"@-",1,$"=$&/ge;/\S/&&print for@a
Note that in traditional Perl golf, one usually adds the number of switches and the length of the code (which would help here by a few strokes), but here we're using stand-alone programs with no switches.
C on a VT100 terminal (76 characters)
This works in my test on FreeSBIE:
o;main(c){for(;(c=getchar())-10;o=c)printf("\33[1%c%c",c<o?66:c>o?65:71,c);}
But in order to see the output clearly, you have to run it with something like this:
clear ; printf "\n\n\n\n\n" ; echo the quick brown fox jumps over the lazy dog | ./a.out ; printf "\n\n\n\n\n"
Does this count?
Python (161 chars)
v,s="",raw_input()
m=n=len(s)
r=[' ']*n
q=[r[:]for i in range(2*n)]
for j,i in enumerate(s):
m+=(i<v)-(i>v)
q[m][j],v=i,i
for i in q:
if i!=r:print''.join(i)
I haven't done much to compress it yet though. Porting it to something with a spaceship operator now.
Ruby: 99 bytes
r,a,q,i=[],"",99,0
gets.chars{|x|t=r[q+=a<=>x]||=""
a=x
r[q]+=" "*(i-t.size)+x
i+=1}
puts r.compact
Uncompressed:
r,a,q,i = [],"",99,0
gets.chars { |x|
t = r[q+=a<=>x] ||= ""
a = x
r[q] += " "*(i-t.size)+x
i += 1
}
puts r.compact
PHP (138 characters)
<?for($lc=$i=$h=0;"\n"!=$c=fgetc(STDIN);$o[$h]=sprintf("%- {$i}s%s",@$o[$h],$lc=$c),$i++)$h+=$c<$lc?-1:$c>$lc;krsort($o);echo join($c,$o);
'Readable' version:
<?
for (
$last_ch = $i = $level = 0;
"\n" != $ch = fgetc(STDIN);
$out[$level] = sprintf("%- {$i}s%s", @$out[$level], $last_ch = $ch), $i++
)
$level += $ch < $last_ch ? -1 : $ch > $last_ch;
krsort($out);
echo join($ch,$out);
Python 2.x, now down to 156 chars:
s=raw_input()
R=range(len(s))
m=[0]
for i in R[1:]:m+=[m[-1]-cmp(s[i],s[i-1])]
for x in range(min(m),max(m)+1):print''.join(m[i]==x and s[i]or' 'for i in R)
Haskell, 215 characters. I'm posting this because I don't like Khoth's version at all. Just by writing in a reasonably functional style I ended up with a significantly shorter and IMO more readable program. I haven't actually tried to make it short apart from the variable names and spacing. Destructively updating an array might make it shorter than replicating spaces.
import Char
import List
main=getLine>>=(putStr.p)
p s=unlines$transpose[z++(y:x)|(m,y)<-zip n s,let(x,z)=splitAt m$replicate(maximum n)' ']
where o=map ord s
n=scanl(+)0$map signum$zipWith(-)(tail o)o
C89 (151 characters)
l[999][999];p;v=500;r;main(c){for(;(c=getchar())>0;
)l[v+=c>p,v-=c<p][++r]=*l[v]=p=c;for(v=999;v--;)for
(c=0;c++<=r;)*l[v]&&putchar(c<=r?32|l[v][c]:10);}
C#:
using System;
static class A
{
static void Main(string[] a)
{
var s=a[0];var r="";
int i=1,h=0,d=0,c=0,n=s.Length;
var m=new int[n];
m[0]=0;
for(;i<n;i++)
{
c+=Math.Sign(s[i]-s[i-1]);
h=(c>h)?c:h;
d=(c<d)?c:d;
m[i]=c;
}
for(;h>=d;h--)
{
for (c=0;c<n;c++)
r+=(m[c]==h)?s[c]:' ';
r+="\n";
}
Console.Write(r);
}
}
Weighs in at 287 compressed.
Perl, 85 chars with switches, 96 without
Invoke with -F// -an
switches
$q=$"x($n=@F);$,=$/;for(@F){/
/&&print@O;substr($O[$n+=$*cmp$_]|=$q,$i++,1)=$_;$*=$_}
There is a newline in between the 2nd and 3rd slash characters. Without switches you can do
$q=$"x($n=@C=split//,<>);$,=$/;for(@C){/
/&&print@O;substr($O[$n+=$*cmp$_]|=$q,$i++,1)=$_;$*=$_}
Haskell (285 characters):
heightDiff x y | x == y = 0
| x < y = -1
| True = 1
heights h (x:y:z)= (x,h):(heights (h+(heightDiff x y) ) (y:z))
heights h [y] = [(y,h)]
makech ((x,h):xs) i = (if i==h then x else ' '):makech xs i
makech [] _ = []
printAll xs = mapM_ (putStrLn . (makech xs)) [(minimum $ map snd xs)..(maximum $ map snd xs)]
main = getLine >>= (printAll . heights 0)
Some compression (260 characters):
a x y|x==y=0
|x<y= -1
|True=1
c h (x:y:z)=(x,h):(c(h+(a x y))(y:z))
c h [y]=[(y,h)]
d ((x,h):xs)i=(if i==h then x else ' '):d xs i
d [] _=[]
p xs = mapM_ (putStrLn .(d xs)) [(minimum $ map snd xs)..(maximum $ map snd xs)]
main = getLine >>= (p . c 0)
Perl, 88 characters
Now, edited to 88 characters:
$_=<>;
s/.(?=(.))/($"x40).$&.$"x(39+($1cmp$&))/ge;
@_=/.{80}/g;
{say map{chop||die}@_;redo}
Was:
$_=<>;
s/.(?=(.))/$&.$"x(79+($1cmp$&))/ge;
s/^.{40}|.{80}/$&\n/g;
print $& while /.$/gm || s/.$//gm * /\n/;
97 characters (omitting spaces). It's not that short, but I wonder if someone with more experience with PERL may be able to shorten it further. And also spot any bugs. The second line uses spaces to make a wave that falls vertically instead of horizontally, on an 80-width wrapping screen. The third line inserts linebreaks. The last line flips X/Y axes.
I'd originally wondered if the last two lines could be something like interleave(s/.{80}/g) where interleave interleaves an array of strings. But there seems not to be that function I'd hoped for. (Or is there in a library?)
A first shout in C#. The input must be supplied as first command linie argument.
using System;
using C = System.Console;
static class P
{
static void Main(string[] a)
{
var b = a[0];
var l = b.Length;
int y = 0, z = 0;
for (int i = 0; i < l - 1; i++)
{
y += Math.Sign(b[i] - b[i + 1]);
z = Math.Min(y, z);
}
y = 0;
for (int i = 0; i < l - 1; i++)
{
C.SetCursorPosition(i, y - z);
C.Write(b[i]);
y += Math.Sign(b[i] - b[i + 1]);
}
}
}
This yields 280 bytes in compressed from.
using System;using C=System.Console;static class P{static void Main(string[]a){var b=a[0];var l=b.Length;int y=0,z=0;for(int i=0;i<l-1;i++){y+=Math.Sign(b[i]-b[i+1]);z=Math.Min(y,z);}y=0;for(int i=0;i<l-1;i++){C.SetCursorPosition(i,y-z);C.Write(b[i]);y+=Math.Sign(b[i]-b[i+1]);}}}
Attempt number two with a different approach.
using System;
using System.Collections.Generic;
static class P
{
static void Main(string[] a)
{
var b = a[0] + "$";
var l = new List<string>();
var y = -1;
for (int i = 0; i < b.Length - 1; i++)
{
if ((y == -1) || (y == l.Count))
{
y = y < 0 ? 0 : y;
l.Insert(y, b.Substring(i, 1).PadLeft(i + 1));
}
else
{
l[y] = l[y].PadRight(i) + b[i];
}
y += Math.Sign(b[i] - b[i + 1]);
}
foreach (var q in l) Console.WriteLine(q);
}
}
The loop can further be rewritten to use a try/catch block.
for (int i = 0; i < b.Length - 1; i++)
{
try
{
l[y] = l[y].PadRight(i) + b[i];
}
catch
{
y = y < 0 ? 0 : y;
l.Insert(y, b.Substring(i, 1).PadLeft(i + 1));
}
y += Math.Sign(b[i] - b[i + 1]);
}
This yields slightly modified and compressed 321 bytes - a bit more then the first attempt, but much robuster.
using System;static class P{static void Main(string[]a){var b=a[0]+"$";var r=new System.Collections.Generic.List<string>();var y=-1;for(int i=0;i<b.Length-1;i++){try{r[y]=r[y].PadRight(i)+b[i];}catch{y=y<0?0:y;r.Insert(y,b[i].ToString().PadLeft(i+1));}y+=Math.Sign(b[i]-b[i+1]);}foreach(var l in r)Console.WriteLine(l);}}
PowerShell
Am sure this can be done with much less code, if anyone wants to edit that would be excellent. I'm leaving it readable.
$v = (Read-Host).ToCharArray()
$r = @(0)
for($i = 1; $i -lt $v.length; $i++) {
$p = $i - 1
$r += $r[$p] + [System.Math]::Sign($v[$i] - $v[$p])
$t = [System.Math]::Max($t, $r[$i])
$b = [System.Math]::Min($b, $r[$i])
}
for($i = $t; $i -ge $b; $i--) {
for($x = 0; $x -lt $r.length; $x ++) {
if($r[$x] -eq $i) {
$o += $v[$x]
}
else {
$o += " "
}
}
$o += "`n"
}
$o
F#, 242 characters:
let F(s:string)=(fun L->let a=Array.init(L*3)(fun _->Array.create L ' ')in Seq.fold(fun(r,p,c)n->let r=r+sign(int p-int n)in a.[r].[c]<-n;r,n,c+1)(L,s.[0],0)s;for r in a do if Array.exists((<>)' ')r then printfn"%s"(new string(r)))s.Length
With whitespace added for easier reading, it's
let F(s:string) =
(fun L->
let a = Array.init (L*3) (fun _ -> Array.create L ' ') in
Seq.fold (fun (r,p,c) n ->
let r = r + sign(int p-int n) in
a.[r].[c]<-n;
r, n, c+1)
(L, s.[0], 0)
s;
for r in a do
if Array.exists ((<>) ' ') r then
printfn "%s" (new string(r))
) s.Length
C (157 characters)
I'm stuck there for the time being. I don't think C will beat J on this one. Thanks to strager for helping trim 8 characters, though.
char*p,a[999][80];w,x,y=500;main(c){for(gets(memset(p=*a,32,79920));*p;
a[y][x++]=c=*p++)y+=*p<c,y-=*p>c;for(;++w<998;strspn(p," ")-79&&puts(p))
79[p=a[w]]=0;}
Formatted:
char *p, /* pointer to current character (1st) or line (2nd) */
a[999][80]; /* up to 998 lines of up to 79 characters */
w, x, y = 500; /* three int variables. y initialized to middle of array */
main(c){
for(gets(memset(p=*a, 32, 79920));
/* 999 * 80 = 79920, so the entire array is filled with space characters.
* memset() returns the value of its first parameter, so the above is
* a shortcut for:
*
* p = *a;
* memset(p, 32, 79920);
* gets(p);
*
* Incidentally, this is why I say "up to 998 lines", since the first
* row in the array is used for the input string.
*
* **** WARNING: Input string must not be more than 79 characters! ****
*/
*p;a[y][x++] = c = *p++) /* read from input string until end;
* put this char in line buffer and in prev
*/
y += *p < c, /* if this char < prev char, y++ */
y -= *p > c; /* the use of commas saves from using { } */
for(;++w < 998; /* will iterate from 1 to 998 */
strspn(p, " ") - 79 &&
/* strspn returns the index of the first char in its first parameter
* that's NOT in its second parameter, so this gets the first non-
* space character in the string. If this is the NULL at the end of
* the string (index 79), then we won't print this line (since it's blank).
*/
puts(p)) /* write the line out to the screen (followed by '\n') */
79[p = a[w]] = 0; /* same as "(p = a[y])[79] = 0",
* or "p = a[y], p[79] = 0", but shorter.
* Puts terminating null at the end of each line
*/
}
I didn't bother supporting input of more than 79 characters, since that would cause confusing wrap on most terminals.
A Java solution, not particularly compressed (now modified to read from stdin).
public class W
{
public static void main(String[] x)
{
String s = new java.util.Scanner(System.in).nextLine();
int i,j;
int t = s.length();
char[] b = s.toCharArray();
char[][] p = new char[2*t][t];
int q = t;
char v = b[0];
for (i=0; i<2*t; i++)
{
for (j=0; j<t; j++)
{
p[i][j] = ' ';
}
}
p[q][0] = v;
String z = new String(p[0]);
for (i=1; i<t; i++)
{
char c = b[i];
int d = (c == v) ? 0 : (c > v ? -1 : 1);
q += d;
p[q][i] = c;
v = c;
}
for (i=0; i<2*t; i++)
{
String n = new String(p[i]);
if (!n.equals(z))
{
System.out.println(n);
}
}
}
}
C# (564 characters of code)
using System;
class Program {
static void Main(string[] args) {
var input = args[0];
int min = 0, max = 0;
var heights = new int[input.Length];
for (var i = 1; i < input.Length; i++) {
heights[i] = heights[i-1] + (input[i] > input[i-1] ? 1 : (input[i] < input[i-1] ? -1 : 0));
min = Math.Min(min, heights[i]);
max = Math.Max(max, heights[i]);
}
for (var row = max; row >= min; row--, Console.WriteLine())
for (var col = 0; col < input.Length; col++)
Console.Write(heights[col] == row ? input[col] : ' ');
}
}
Compacted: (324 characters of code)
using System;class A{static void Main(string[] args){var I=args[0];int M=0,X=0;var H=new int[I.Length];for(var i=1;i<I.Length;i++){H[i]=H[i-1]+(I[i]>I[i-1]?1:(I[i]<I[i-1]?-1:0));M=Math.Min(M,H[i]);X=Math.Max(X,H[i]);}for(var r=X;r>=M;r--,Console.WriteLine())for(var c=0;c<I.Length;c++)Console.Write(H[c]==r?I[c]:' ');}}
Using tricks from comments (283 characters):
using System;class A{static void Main(string[] a){var I=a[0];int M=0,X=0,i=1,r,h,c=0,l=I.Length;var H=new int[l];for(;i<l;i++){h=H[i-1]+(I[i]>I[i-1]?1:(I[i]<I[i-1]?-1:0));H[i]=h;M=M<h?M:h;X=x>h?X:h;}for(r=X;r>=M;r--,Console.Write('\n'))for(;c<l;c++)Console.Write(H[c]==r?I[c]:' ');}}
Perl 5.10
159 characters, most "user friendly" version:
perl -nE'chop;@l=split//;$l{$_}=$l{$_-1}+($l[$_]cmp$l[$_-1])for 0..$#l;%e=();for$x(sort{$b<=>$a}grep{!$e{$_}++}values%l){say map{$l{$_}==$x?$l[$_]:" "}0..$#l}'
The following version is 153 characters, but you can only enter one line. To enter more than one, you have to restart the program. The rules are unclear as to whether or not this is allowed, but I figured it would be best to post both versions anyway:
perl -nE'chop;@l=split//;$l{$_}=$l{$_-1}+($l[$_]cmp$l[$_-1])for 0..$#l;for$x(sort{$b<=>$a}grep{!$e{$_}++}values%l){say map{$l{$_}==$x?$l[$_]:" "}0..$#l}'
And here is a 149 character version - this one is a script rather than a shell one-liner, and also works for only one line of input, but won't continue to accept input after that first line, which is probably a good thing:
$_=<>;chop;@l=split//;$l{$_}=$l{$_-1}+($l[$_]cmp$l[$_-1])for 0..$#l;for$x(sort{$b<=>$a}grep{!$e{$_}++}values%l){say map{$l{$_}==$x?$l[$_]:" "}0..$#l}
None of these are quite as short as the Perl solution already posted, but they certainly seem to beat Python and Ruby. And besides, There's More Than One Way To Do It.
F#, 235 characters
A completely different strategy saved a few chars compared to my other solution.
let F(s:string)=(fun L->let _,_,h=Seq.fold(fun(p,h,l)n->let r=h-sign(int n-int p)in n,r,r::l)(s.[0],0,[0])s in for r in Seq.min h..Seq.max h do printfn"%s"(new string(Array.init L (fun c->if r=h.[L-1-c]then s.[c]else ' '))))s.Length
With whitespace:
let F(s:string) =
(fun L->
let _,_,h = Seq.fold (fun (p,h,l) n ->
let r = h - sign(int n-int p) in
n,r,r::l) (s.[0],0,[0]) s in
for r in Seq.min h..Seq.max h do
printfn "%s" (new string(Array.init L (fun c ->
if r=h.[L-1-c] then s.[c] else ' ')))
) s.Length
C# 545 byte uncompressed
using System;
using System.Linq;
class Program{
static void Main(string[] b){
var s = b[0];
var t = new System.Collections.Generic.Dictionary<int, string>();
int y=0, p=0;
for (int i = 0; i < s.Length; i++){
y += Math.Sign(s[i] - p);
p = s[i];
if (!t.ContainsKey(y))
t.Add(y, "");
t[y] = t[y].PadRight(i) + s[i];
}
foreach (var v in t.OrderBy(a => -a.Key))
Console.WriteLine(v.Value);
}
}
Ruby: 109 bytes, counting newline characters!!
s=gets
r,a,q,i=[],s[0,1],99,0
s.chars{|x|q+=a<=>x
a=x
t=r[q]||=""
r[q]+=" "*(i-t.size)+x
i+=1}
puts r.compact
Uncompressed:
s = gets
r,a,q,i = [],s[0,1],99,0
s.chars { |x|
q += a<=>x
a = x
t = r[q] ||= ""
r[q] += " "*(i-t.size)+x
i += 1
}
puts r.compact
Groovy (195 chars)
test
s="1234567890qwertyuiopasdfghjklzxcvbnm"
short
=(s=~/./).collect{(char)it}
e=' ';x=0;l=[];u=[]
w.eachWithIndex({it,n->
if(l.size()>x){l[x]+=e*(n-u[x]-1)+it;u[x]=n}else{l+=e*n+it;u+=n}
if(w[n+1]>it)x++else x--;})
l.reverse().each({println it})
PHP: 108 chars
<?while(-1<$a=fgetc(STDIN)){$d+=$a<$b^-($a>$b);$r[$d].=' ';$r[$d][$k++]=$b=$a;}ksort($r);echo join("\n",$r);
Readable version:
<?
while(-1<$a=fgetc(STDIN)){
$d+=$a<$b^-($a>$b);
$r[$d].=' ';
$r[$d][$k++]=$b=$a;
}
ksort($r);
echo join("\n",$r);
Golfscript - 65 chars
' ': :c;1/:a,.+,{:N;a,a{:@c<+c@:c<-.N=[ c]\=}%.[n+'']\$-1= ==\;}%
Generate the wave line by line
{:N;a,a{:@c<+c@:c<-.N=[ c]\=}%
Filter out the blank lines
.[n+'']\$-1= ==\
ASL: 73
args1[,;{ch},1_]@1]o o>:><-0 0a:/+,/&-;{()@:'{" "`}}@;{};;{(){`}#`}" ":|P
I just translated the J solution into ASL.
XQuery
(257 bytes)
declare variable$i external;let$c:=string-to-codepoints($i),$h:= for$x at$p in$c
return sum(for$x in 1 to$p let$d:=$c[$x]-$c[$x -1]return(-1[$d>0],1[$d<0]))return
codepoints-to-string(for$p in min($h)to max($h),$x at$q
in($c,10)return(32[$h[$q]!=$p],$x)[1])
Since XQuery is purely declarative, I've had to fake the input as being passed in in an external variable. Here is the command line to run this with XQSharp:
xquery wave.xq !method=text i='1234567890qwertyuiopasdfghjklzxcvbnm'
If the string was passed in as the context item, then this could be reduced further, but setting the context item to a non-node value is not supported with all XQuery implementations (and not by the XQSharp command line tool):
let$c:=string-to-codepoints(.),$h:= for$x at$p in$c return sum(for$x in 1 to$p
let$d:=$c[$x]-$c[$x -1]return(-1[$d>0],1[$d<0]))return codepoints-to-string(for$p
in min($h)to max($h),$x at$q in($c,10)return(32[$h[$q]!=$p],$x)[1])
Just 228 bytes.
来源:https://stackoverflow.com/questions/1376077/code-golf-the-wave