views:

2340

answers:

12

The challenge

The shortest code by character count, that will output musical notation based on user input.

Input will be composed of a series of letters and numbers - letters will represent the name of the note and the number will represent the length of the note. A note is made of 4 vertical columns. The note's head will be a capital O, stem, if present will be 3 lines tall, made from the pipe character |, and the flag(s) will be made from backward slash \.

Valid note lengths are none, 1/4 of a note, 1/8 of a note, 1/16 of a note and 1/32 of a note.

       |    |\    |\    |\
       |    |     |\    |\
       |    |     |     |\
 O    O    O     O     O
 1   1/4  1/8  1/16   1/32

Notes are places on the Staff, according to their note name:

  ----

D ----
C     
B ----
A     
G ----
F     
E ----

All input can be assumed to be valid and without errors - Each note separated with a white space on a single line, with at least one valid note.

Test cases

Input:
    B B/4 B/8 B/16 B/32 G/4 D/8 C/16 D B/16
Output:
                              |\               
    --------------------------|---|\--------
          |   |\  |\  |\      |   |\      |\
    ------|---|---|\--|\-----O----|--O----|\
          |   |   |   |\  |      O        | 
    -O---O---O---O---O----|--------------O--
                          |                 
    ---------------------O------------------

    ----------------------------------------


Input:
    E/4 F/8 G/16 A/32 E/4 F/8 G/16 A/32 
Output:

    --------------------------------

    --------------|\--------------|\
              |\  |\          |\  |\ 
    ------|\--|\--|\------|\--|\--|\
      |   |   |  O    |   |   |  O  
    --|---|--O--------|---|--O------
      |  O            |  O          
    -O---------------O--------------


Input:
    C E/32 B/8 A/4 B F/32 B C/16
Output:

    ------------------------------|\
              |\                  |\
    ----------|---|---------------|-
     O        |   |              O   
    ---------O----|--O----|\-O------
          |\     O        |\        
    ------|\--------------|\--------
          |\             O           
    -----O--------------------------

Code count includes input/output (i.e full program).

+15  A: 

C89 (186 characters)

#define P,putchar(
N[99];*n=N;y;e=45;main(q){for(;scanf(" %c/%d",n,n+1)>0;n
+=2);for(;y<11;q=y-(75-*n++)%7 P+q-4?e:79)P*n&&q<4&q>0?
124:e)P*n++/4>>q&&q?92:e))*n||(e^=13,n=N,y++P+10))P+e);}

Half-note support (+7 characters)

#define P,putchar(
N[99];*n=N;y;e=45;main(q){for(;scanf(" %c/%d",n,n+1)>0;n
+=2);for(;y<11;q=y-(75-*n++)%7 P+q-4?e:v<4?79:64)P*n&&q<4&q>0?
124:e)P*n++/4>>q&&q?92:e))*n||(e^=13,n=N,y++P+10))P+e);}
strager
Seems like it needs EOF to finish input. Works if I execute it as `echo 'E/4 F/8 G/16 A/32 E/4 F/8 G/16 A/32' | ./notes`
LiraNuna
Will likely crash if you give it more than 50 notes...
Chris Dodd
@Chris, it's common for golf entries to be 'fragile'.
LiraNuna
What does y++P(10) mean?
gnibbler
@gnibbler: Because of `#define P,putchar`, it means `y++,putchar(10)`.
ephemient
smacks forehead, thanks ephemient
gnibbler
I used the same trick to shave enough bytes off my own answer :)
gnibbler
+6  A: 

F#, 458 chars

Reasonably short, and still mostly readable:

let s=Array.init 10(fun _->new System.Text.StringBuilder())
System.Console.ReadLine().Split([|' '|])
|>Array.iter(fun n->
for i in 0..9 do s.[i].Append(if i%2=1 then"----"else"    ")
let l=s.[0].Length
let i=68-int n.[0]+if n.[0]>'D'then 7 else 0
s.[i+3].[l-3]<-'O'
if n.Length>1 then
 for j in i..i+2 do s.[j].[l-2]<-'|'
 for j in i..i-1+(match n.[2]with|'4'->0|'8'->1|'1'->2|_->3)do s.[j].[l-1]<-'\\')
for x in s do printfn"%s"(x.ToString())

With brief commentary:

// create 10 stringbuilders that represent each line of output
let s=Array.init 10(fun _->new System.Text.StringBuilder())
System.Console.ReadLine().Split([|' '|])
// for each note on the input line
|>Array.iter(fun n->
// write the staff
for i in 0..9 do s.[i].Append(if i%2=1 then"----"else"    ")
// write note (math so that 'i+3' is which stringbuilder should hold the 'O')
let l=s.[0].Length
let i=68-int n.[0]+if n.[0]>'D'then 7 else 0
s.[i+3].[l-3]<-'O'
// if partial note
if n.Length>1 then
 // write the bar
 for j in i..i+2 do s.[j].[l-2]<-'|'
 // write the tails if necessary
 for j in i..i-1+(match n.[2]with|'4'->0|'8'->1|'1'->2|_->3)do s.[j].[l-1]<-'\\')
// print output
for x in s do printfn"%s"(x.ToString())
Brian
thanks for the commented version :)
Zeus
Your `F` should have `#` after it.
strager
+23  A: 

Perl, 126 characters (115/122 with switches)

Perl in 239 226 218 216 183 180 178 172 157 142 136 133 129 128 126 chars

This 126 character solution in Perl is the result of a lengthy collaboration between myself and A. Rex.

@o=($/)x10;$/=$";map{m[/];$p=4+(5-ord)%7;
$_.=--$p?!($p&~3)*$'?16<$p*$'?"  |\\":"  | ":$/x4:" O  ",
$|--&&y@ @-@for@o}<>;print@o

A. Rex also proposes a solution to run with the perl -ap switch. With 111(!) characters in this solution plus 4 strokes for the extra command-line switch, this solution has a total score of 115.

$\="$:
"x5;$p=4+(5-ord)%7,s#..##,$\=~s#(.)\K$#--$p?
$_*!($p&~3)?"$1|".(16<$p*$_?"\\":$1).$1:$1x4:O.$1x3#gemfor@F

The first newline in this solution is significant.

Or 122 characters embedding the switches in the shebang line:

#!perl -ap
$\="$:
"x5;$p=4+(5-ord)%7,s#..##,$\=~s#(.)\K$#--$p?$_*!($p&~3)?"$1|".(16<$p*$_?
"\\":$1).$1:$1x4:O.$1x3#gemfor@F

(first two newlines are significant).

Half-notes can be supported with an additional 12 chars:

@o=($/)x10;$/=$";map{m[/];$p=4+(5-ord)%7;
$_.=--$p?!($p&~3)*$'?16<$p*$'?"  |\\":"  | ":$/x4:$'>2?" @  ":" O  ",
$|--&&y@ @-@for@o}<>;print@o
mobrule
split//,"D3C4B5A6G7F8E9" -> "D3C4B5A6G7F8E9"=~/./g
Kinopiko
@Kinopiko, it's generally frowned upon to perform optimizations on other people's solutions. You can write a comment (as you did), but please don't edit answers unless they say it's okay.
strager
Oh, sorry, someone else edited it before me though.
Kinopiko
grep -> map for one less character.
Kinopiko
Good 'ol barewords.
Robert P
172 chars. Oh dear.
strager
For those par 5 fancy text formatting holes, Perl is definitely a 1-wood.
mobrule
I'd seriously would like to a read a lengthy step-by-step article of the process the Perl hacker's mind goes through when creating something like this.
Esko
A. Rex
I'm in awe. This is insane.
strager
A. Rex
146! Nice move getting rid of the `$c`ounter variable I had!
mobrule
`($L)=m:\d+:g` instead of `s@.*/@@;$L=$_` for 142
mobrule
A. Rex
@A. Rex, thanks, I'm going to borrow your `$'` club!
mobrule
+1. I had a feeling that perl would fit in a tweet
gnibbler
Esko: you start with full-sized code that works, then think of tricks to reduce the size. The important point is to get it working first before golfing it.
Kinopiko
@Kinopiko, Really? I do the reverse with my solutions.
strager
You golf it then debug? Crikey!
Kinopiko
Arg..I just got ruby down to 136 and you move the goalpost again!
gnibbler
A. Rex
@A. Rex - I thought the script broke when I got rid of the `|$`, but I guess not.
mobrule
@mobrule regarding `|$`: It depends on block structure. Both `$ however, they're local to the current block, so depending on that, the code might still work. (You need this for the `/\d+/` actually.) Incidentally, for some versions of the code, `/../` worked fine as a pattern (using `$'`) regardless of block structure because it successfully matched no matter what.
A. Rex
A. Rex
@A. Rex: by my count, that's actually 122, unfortunately. However, you can save yourself a few more: remove the `#!perl -ap\n` from the script (-11), and add `-ap ` (+4) to the command line. Gives a total savings of 7, and a final script of 115.
Robert P
I don't know if I should allow switches. Not because I'm evil, but because I can do something like `gcc -DA=somecode`
LiraNuna
@Robert P: You're right that it's 122 -- oops. @LiraNuna: This coincides with how it's scored in Perl golf competitions. I don't know if you should allow it here (you're the MC!), although I think it'd be cool to see what other languages can do with switches and every time I've participated I wanted switches. I do think it's remarkable that I can get savings even if I have to write the `#!perl ` though.
A. Rex
@LiraNuna, the perl golf guys used to count any characters past "perl" including the whitespace as part of the solution
gnibbler
By the way, if people don't know: if you have `#!perl [switches]` on the first line of a Perl program, you don't actually need the shell to interpret the switches. Perl will read them and apply them itself. So even if command-line switches aren't allowed, shebang ones definitely are -- it's part of the language you get when you just run `perl`.
A. Rex
That `-a` would be nice to have in other languages though for only 3 strokes!
gnibbler
Thing is, we have two solutions that can win, depend on those rules. I think it would be fair to use the 122 chars solution (shebang used) as the answer, because I find it fair - especially since we count *"The shortest code by character count"*
LiraNuna
Just wait until I get 114 chars. Then we won't have to worry about this. =]
strager
+1  A: 

C -- 293 characters

Still needs more compression, and it takes the args on the command line instead of reading them...

i,j,k,l;main(c,v)char **v;{char*t;l=4*(c-1)+2;t=malloc(10*l)+1;for(i=0;i<10;i
++){t[i*l-1]='\n';for(j=0;j<l;j++)t[i*l+j]=i&1?'-':' ';}t[10*l-1]=0;i=1;while
(--c){j='G'-**++v;if(j<3)j+=7;t[j*l+i++]='O';if(*++*v){t[--j*l+i]='|';t[--j*l
+i]='|';t[--j*l+i]='|';if(*++*v!='4'){t[j++*l+i+1]='\\';if(**v!='8'){t[j++*l+
i+1]='\\';if(**v!='1'){t[j++*l+i+1]='\\';}}}}i+=3;}puts(t);}

edit: fixed the E

edit: down to 293 characters, including the newlines...

#define X t[--j*l+i]='|'
#define Y t[j++*l+i+1]=92
i,j,k,l;main(c,v)char**v;{char*t;l=4*(c-1)+2;t=malloc(10*l)+1;for(i=10;i;)t[--i*
l-1]=10,memset(t+i*l,i&1?45:32,l-1);t[10*l-1]=0;for(i=1;--c;i+=3)j=71-**++v,j<3?
j+=7:0,t[j*l+i++]=79,*++*v?X,X,X,*++*v-52?Y,**v-56?Y,**v-49?Y:0:0:0:0;puts(t);}
Chris Dodd
Wrong position for note `E`.
LiraNuna
Wow, nice work. Also, one of the rare times I found the perl implementation more readable ;)
ldigas
@ldigas, Haha! +1 to your comment. =]
strager
+10  A: 

159 Ruby chars

n=gets.split;9.downto(0){|p|m='- '[p%2,1];n.each{|t|r=(t[0]-62)%7;g=t[2..-1]
print m+(r==p ?'O'+m*2:p>=r&&g&&p<r+4?m+'|'+(g.to_i>1<<-p+r+5?'\\':m):m*3)}
puts}
yjerem
Specification has changed. Extra `-` or ` ` is not required at the end or beginning of the line (depending how you did the answer).
strager
yes I did just notice that. And got it down to 182 chars while I was at it :P
yjerem
I see 182 then refresh to find 159. Crazy!
strager
Darn, I knew ruby could go shorter, but you beat me to it and yours is even shorter
gnibbler
you can save one byte if you use '|'<< instead of '|'+ and change the '\\' to 92
gnibbler
+4  A: 

Lua, 307 Characters

b,s,o="\\",io.read("*l"),io.write for i=1,10 do for n,l in
s:gmatch("(%a)/?(%d*)")do x=n:byte() w=(x<69 and 72 or 79)-x
l=tonumber(l)or 1 d=i%2>0 and" "or"-"o(d..(i==w and"O"or
d)..(l>3 and i<w and i+4>w and"|"or d)..(l>7 and i==w-3
and b or l>15 and i==w-2 and b or l>31 and i==w-1 and b or
d))end o"\n"end
gwell
+14  A: 

Python 178 characters

The 167 was a false alarm, I forgot to suppress the stems on the whole notes.

R=raw_input().split()
for y in range(10):
 r=""
 for x in R:o=y-(5-ord(x[0]))%7;b=" -"[y&1]+"O\|";r+=b[0]+b[o==3]+b[-(-1<o<3and''<x[1:])]+b[2*(-1<o<":862".find(x[-1]))]
 print r

Python 167 characters (Broken)

No room for the evil eye in this one, although there are 2 filler characters in there, so I added a smiley. This technique takes advantage of the uniqueness of the last character of the note lengths, so lucky for me that there are no 1/2 notes or 1/64 notes

R=raw_input().split()
for y in range(10):
 r=""
 for x in R:o=y-(5-ord(x[0]))%7;b=" -"[y&1]+"O\|";r+=b[0]+b[o==3]+b[-(-1<o<3)]+b[2*(-1<o<":862".find(x[-1]))]
 print r

Python 186 characters <<o>>

Python uses the <<o>> evil eye operator to great effect here. The find() method returns -1 if the item is not found, so that is why D doesn't need to appear in the notes.


R=raw_input().split()
for y in range(10):
 r=""
 for x in R:o='CBAGFE'.find(x[0])+4;B=" -"[y%2];r+=B+(B,'O')[o==y]+(x[2:]and
y+4>o>y and"|"+(B,'\\')[int(x[2:])<<o>>6+y>0]or B*2)
 print r

11 extra bytes gives a version with half notes


R=raw_input().split()
for y in range(10):
 r=""
 for x in R:t='CBAGFE'.find(x[0])+4;l=x[2:];B=" -"[y%2];r+=B+(B,'@O'[l
in'2'])[t==y]+(l and y+4>t>y and"|"+(B,'\\')[int(l)>>(6+y-t)>0]or B*2)
 print r
$ echo B B/2 B/4 B/8 B/16 B/32 G/4 D/8 C/16 D B/16| python notes.py 
                              |\            
------------------------------|---|\--------
      |   |   |\  |\  |\      |   |\      |\
------|---|---|---|\--|\-----@----|--O----|\
      |   |   |   |   |\  |      @        | 
-O---O---@---@---@---@----|--------------@--
                          |                 
-------------------------@------------------

--------------------------------------------
gnibbler
Must use `O` not `o`. Great solution, though; time to beat it. =]
strager
ok, using big O now
gnibbler
Nice half notes!
mobrule
170, nice. `:)`
strager
`y-'CBAGFE'.find(x[0])` can be `y+1-(5-ord(x[0]))%7` to save 3 chars. Then you probably factor out the `+1`.
mobrule
@mobrule. Did as you suggested, took the +1 out which saves an extra byte overall, but now the smiley is gone.
gnibbler
But... But... `:(`
strager
Hmm..just noticed that i've broken the whole notes
gnibbler
I think you can save 2 characters by changing all the indented lines to be one line separated by semicolons, so you don't have to have a newline _and_ a space for indentation.
Chris Lutz
@Chris, I don't think I can, because the `for` causes it to be a syntax error
gnibbler
evil eye operator? :-D
Kugel
+4  A: 

C 196 characters <<o>>

Borrowing a few ideas off strager. Interesting features include the n+++1 "triple +" operator and the <<o>> "evil eye" operator

#define P,putchar
N[99];*n=N;y;b;main(o){for(;scanf(" %c/%d",n,n+1)>0;n+=2);for(;y<11;)
n=*n?n:(y++P(10),N)P(b=y&1?32:45)P((o=10-(*n+++1)%7-y)?b:79)P(0<o&o<4&&*n?'|':b)
P(*n++<<o>>6&&0<o&o<4?92:b);}
gnibbler
Sweet Jeebus...
Chris Lutz
I managed to get 196 when SO was down but couldn't post. Your solution (well, modifications of mine) has hinted me at more optimizations. Thanks, but I'm gonna win this one. ;D
strager
Ahh, but will you have the evil eye? and the triple +? :)
gnibbler
Evil eye? Why the face?
mobrule
@gnibbler, Erm... No... =[
strager
`scanf(" %c/%d",n,n+1)+1` is not portable because by the standard `EOF` must be `< 0` (thus *may* be `-1`). Use `>0` like in my answer (or `>=0` if needed).
strager
@strager Thanks ;) are you going to update yours soon?
gnibbler
@gnibbler, Yes, I will by the end of today (EST). I really want to beat that Perl solution!
strager
you should explain the `n+++1` and the `<<o>>` operators and what they do (and mention if it is undefined/implementation defined behavior)
Evan Teran
It's really `*n+++1` and it is a combination of `n++` and `*n+1`, so give me whatever n is pointing to plus one and then increment n to point at the next character. The evil eye is `<<o>>6` so left shift by `o` and right shift by `6`. Standard C meaning, just unusual to see it.
gnibbler
@Evan Teran: more examples of that kind would be the 'goes forward operator': `while(x --> 0)`, which actually means `while(x-- > 0)`, and the 'cast to boolean operator': `!!integer`, which does what you think it does :)
LiraNuna
+4  A: 

168 characters in Perl 5.10

My original solution was 276 characters, but lots and lots of tweaking reduced it by more than 100 characters!

$_=<>;
y#481E-GA-D62 #0-9#d;
s#.(/(.))?#$"x(7+$&).O.$"x($k=10).($1?"|":$")x3 .$"x(10-$2)."\\"x$2.$"x(9-$&)#ge;
s#(..)*?\K (.)#-$2#g;
print$/while--$k,s#.{$k}\K.#!print$&#ge

If you have a minor suggestion that improves this, please feel free to just edit my code.

A. Rex
Nice! (Comment limit...)
strager
+20  A: 

Golfscript (112 characters)

' '%:A;10,{):y;A{2/.0~|1=~:r;0=0=5\- 7%
4y@--:q'  '' O'if-4q&!q*r*{16q/r<'|\\'
'| 'if}'  'if+{.32=y~&{;45}*}%}%n}%
strager
Must...beat...perl :)
gnibbler
@gnibbler, Exactly my thinking. =]
strager
Thanks for the explanation
gnibbler
Keep up the good work
gnibbler
Must ... beat ... GolfScript
mobrule
@gnibbler, Thanks. =] Still feels unoptimized, and this is my first golfscript.
strager
Jeesh, look at that big `and` out there. Isn't there an abbreviation for that?
mobrule
@mobrule, you should be quiet or he might notice he can use `*` instead of `and`
gnibbler
@gnibbler, Wow, didn't know you could do that with *. =] Nice.
strager
Nicely done, strager! I knew I shouldn't have used so many `$`'s ;-)
mobrule
that was quite the fight! set your engines for next golf ;)
LiraNuna
@mobrule, It was a great exercise. Lots of fun. Hopefully this'll be repeated with the next cold gold *sic*!
strager
@mobrule - Don't feel bad about a real language losing to something called "GolfScript."
Chris Lutz
@Lutz, Perl's a real language?
strager
+13  A: 

LilyPond - 244 bytes

Technically speaking, this doesn't adhere to the output specification, as the output is a nicely engraved PDF rather than a poor ASCII text substitute, but I figured the problem was just crying out for a LilyPond solution. In fact, you can remove the "\autoBeamOff\cadenzaOn\stemUp" to make it look even more nicely formatted. You can also add "\midi{}" after the "\layout{}" to get a MIDI file to listen to.

o=#(open-file"o""w")p=#ly:string-substitute
#(format o"~(~a"(p"2'1""2"(p"4'1""4"(p"6'1""6"(p"8'1""8"(p"/""'"(p"C""c'"(p"D""d'"(p" ""/1"(p"
"" "(ly:gulp-file"M")))))))))))#(close-port o)\score{{\autoBeamOff\cadenzaOn\stemUp\include"o"}\layout{}}

Usage: lilypond thisfile.ly

Notes:

  1. The input must be in a file named "M" in the same directory as the program.
  2. The input file must end in a newline. (Or save 9 bytes by having it end in a space.)
  3. The output is a PDF named "thisfile.pdf", where "thisfile.ly" is the name of the program.
  4. I tested this with LilyPond 2.12.2; other versions might not work.

I haven't done much in LilyPond, so I'm not sure this is the best way to do this, since it has to convert the input to LilyPond format, write it to an auxiliary file, and then read it in. I currently can't get the built-in LilyPond parser/evaluator to work. :(

Now working on an ASCII-output solution.... :)

KirarinSnow
Wow, just wow. Though the point is ASCII printing of patterns :). Still **wow**
LiraNuna
What is this I don't even
strager
I should mention that LilyPond is a music typesetting engine (http://lilypond.org/web). It has a built-in Scheme interpreter, but amazingly enough can still manage to beat Scheme on some golfing tasks (see http://stackoverflow.com/questions/1433263/decision-tree-code-golf/1584216#1584216)
KirarinSnow
+8  A: 

Ruby 136

n=gets;10.times{|y|puts (b=' -'[y&1,1])+n.split.map{|t|r=y-(5-t[0])%7
(r==3?'O':b)+(t[1]&&0<=r&&r<3?'|'<<(r<t[2,2].to_i/8?92:b):b+b)}*b}

Ruby 139 (Tweet)

n=gets;10.times{|y|puts (b=' -'[y&1,1])+n.split.map{|t|r=y-(5-t[0])%7
(r==3?'O':b)+(t[1]&&0<=r&&r<3?'|'<<(r<141>>(t[-1]&7)&3?92:b):b+b)}*b}

Ruby 143

n=gets.split;10.times{|y|puts (b=' -'[y&1,1])+n.map{|t|r=y-(5-t[0])%7;m=t[-1]
(r==3?'O':b)+(m<65&&0<=r&&r<3?'|'<<(r<141>>(m&7)&3?92:b):b+b)}*b}

Ruby 148

Here is another way to calculate the flags,
where m=ord(last character), #flags=1+m&3-(1&m/4)

and another way #flags=141>>(m&7)&3, that saves one more byte

n=gets.split;10.times{|y|b=' -'[y&1,1];n.each{|t|r=y-(5-t[0])%7;m=t[-1]
print b+(r==3?'O':b)+(m<65&&0<=r&&r<3?'|'<<(r<141>>(m&7)&3?92:b):b+b)}
puts}

Ruby 181

First try is a transliteration of my Python solution

n=gets.split;10.times{|y|r="";n.each{|x|o=y-(5-x[0])%7
r+=(b=" -"[y&1,1]+"O\\|")[0,1]+b[o==3?1:0,1]+b[-1<o&&o<3&&x[-1]<64?3:0,1]+b[-1<o&&o<(":862".index(x[-1]).to_i)?2:0,1]}
puts r}
gnibbler