Penerjemah Skor Musik

11

Diberi skor musik ascii, Anda harus dapat menampilkan not dan panjang korespondensinya. Skor akan berisi antara 5 dan 15 catatan inklusif, dan ditranskripsikan pada stave. Paranada terdiri dari lima garis horizontal yang terdiri dari - (minus) karakter yang dipisahkan oleh garis spasi. Intinya di paranada setara dengan catatan 'E'. Garis spasi tepat di atas garis bawah menunjukkan 'F', dan dari nada yang lebih tinggi bahwa 'E' di bawahnya. Ini berlanjut seperti di bawah ini. Perhatikan bahwa catatan hanya naik ke 'G' sebelum mulai lagi di 'A'. Lihat di bawah:

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

Perhatikan bahwa huruf-huruf tersebut tidak termasuk dalam input. Catatan dilapiskan di atas paranada menggunakan karakter ao (huruf kecil) untuk menunjukkan 'kepala catatan'. Head note ini menunjukkan frekuensi not, dan karenanya representasi alfabetis seperti di atas. Misalnya, catatan yang ditempatkan pada skor seperti di bawah ini menunjukkan 'A':

----

----

----
o   
----

----

Sebuah nada, seperti 'A' di atas, disebut 'seluruh nada' dan akan dimainkan untuk satu ketukan utuh. Durasi lain dapat diindikasikan dengan memasukkan 'batang' naik dari nada, dan antara nol dan tiga 'bendera'. Batang terdiri dari tiga | (pipa, atau batang vertikal) karakter ditumpuk tepat di atas kepala catatan. Batang tanpa bendera dianggap sebagai 'not seperempat', dan bermain untuk seperempat ketukan. Bendera adalah \ (garis miring terbalik) karakter, dan tangan di sisi kanan batang. Setiap batang membagi dua waktu not dimainkan. Panjang setiap not akan menjadi salah satu dari yang berikut: seluruh not, not seperempat, not kedelapan, not keenam belas atau not ketiga puluh detik. Beginilah setiap jenis not akan mencari A:

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

----|---|\--|\--|\--
    |   |   |\  |\
----|---|---|---|\--
o   o   o   o   o
--------------------

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

Menyatukan lebih dari satu not memberi Anda skor. Setiap catatan dapat dianggap sebagai lebar empat karakter, dengan catatan berada di kolom pertama dari setiap blok empat karakter. Sebagai contoh :

    |\             
----|\--|\----------
    |\  |       |\  
----o---|---o---|\--
|       o       |   
|---------------o---
|                   
o-------------------

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

Contoh di atas berisi catatan berikut, secara berurutan: not seperempat 'G', not ketiga puluh detik 'D', not kedelapan 'C', seluruh not 'D' dan not keenam belas 'B'. Setiap catatan dalam output Anda harus dalam format huruf / panjang, di mana huruf adalah AG dan panjang adalah fraksi dari panjang catatan jika dibandingkan dengan seluruh catatan. Sebagai pengecualian, panjang dan / karakter tidak boleh dicetak jika catatan adalah keseluruhan catatan. Setiap catatan dalam output Anda harus dipisahkan oleh satu ruang. Karenanya, untuk skor di atas, kode Anda harus menampilkan yang berikut:

G/4 D/32 C/8 D B/16
  • Catatan akan berada dalam kisaran berikut: EFGABCDE F. Perhatikan bahwa hanya huruf yang perlu dicetak, oktaf diabaikan.
  • Perhatikan bahwa jumlah baris input bervariasi dari 9 hingga 12, karena catatan dengan waktu seperempat atau kurang pada saluran D atau lebih tinggi akan membutuhkan lebih banyak baris untuk ditampilkan sepenuhnya.
  • Tidak ada setengah catatan dalam kasus ini.

Kode terpendek menang (spasi putih tidak masuk hitungan).

Sunting: Memperbaiki kesalahan spasi dalam satu input.

Beberapa input sampel:

        |\                    
----|\--|-------------------
|\  |   |                   
|---|---o---------------o---
|   o               |\      
o---------------|\--|\------
            |\  |\  |\      
------------|\--|\--o-------
            |\  o           
------------o---------------

Output: B / 8 C / 8 D / 8 E / 32 F / 32 G / 32 D


----------------o-------------------
                                o   
------------o-----------------------
                            o       
--------o---------------------------
                        o           
----o-------------------------------
                    o               
o-----------------------------------

Output: EGBDFFACE


            |\                  
            |\                  
            |\                  
------------o-------|-----------
|               o   |   |\      
|---|\--------------|---|\------
|   |               o   |\      
o---|---|\--------------o---|\--
    o   |\                  |\  
--------|\------------------|---
        o                   o   
--------------------------------

Output: B / 4 A / 8 F / 32 F / 32 EC / 4 B / 32 F / 16

Neil
sumber
Mengapa spasi tidak dihitung?
JB
@ J: Sehingga orang tidak akan merasa cenderung untuk mengirimkan program satu baris lama tanpa spasi.
Neil
1
Adalah konvensional untuk menghitung spasi putih tetapi untuk tidak menghitung baris baru yang hanya ada untuk menjaga entri di bawah lebar yang wajar. Script users George melakukan ini dengan beberapa bahasa (termasuk c).
dmckee --- ex-moderator kitten
2
@Neil yah sekarang semua yang saya ingin kirim adalah program Whitespace.
JB
@Neil, ya, tapi kemudian Anda mendapatkan orang-orang pintar yang menulis solusi yang benar-benar bertele-tele, mengemasnya ke dalam string spasi putih, dan memasukkan kode sandi-dan-exec: codegolf.stackexchange.com/questions/3203/meta-golf-challenge/…
Stanby

Jawaban:

6

Javascript, 284.279.278.225.221 , 220 Karakter (termasuk spasi yang diperlukan)

One-liner ( biola uji ):

function a(c){b='',d=c.split('\n');for(e=0;d[0][e++];){f=0;for(i=0;g=d[i++];){h=g[e-1];if(h=='o')b+=(b?' ':'')+String.fromCharCode((d.length+4-i)%7+65);if(h=='|')f=f||4;if(g[e]&&g[e]=='\\')f*=2;}if(f)b+='/'+f;}return b;}

Dapat dibaca ( tes biola ):

function getNotes(input){
    out='',lines=input.split('\n');

    for(col=0;lines[0][col++];){
        time=0;
        for(i=0;line=lines[i++];){
            char=line[col-1];
            if(char=='o')out+=(out?' ':'')+String.fromCharCode((lines.length+4-i)%7+65);
            if(char=='|')time=time||4;
            if(line[col]&&line[col]=='\\')time*=2;
        }
        if(time)out+='/'+time;
    }
    return out;
}
Briguy37
sumber
1
Dengan menghapus yang tidak perlu ;dan melakukan beberapa trik, Anda bisa membuatnya lebih pendek. function a(c){b='',d=c.split('\n');for(e=0;d[0][e++];){for(i=f=0;g=d[i++];){h=g[e-1];if(h=='o')b+=(b?' ':'')+String.fromCharCode((d.length+4-i)%7+65);if(h=='|')f=f||4;f*=1+(g[e]=='\\');}if(f)b+='/'+f}return b}(209 karakter)
JiminP
4

Perl, 103 karakter

(108 jika Anda menghitung karakter spasi putih yang diperlukan)

$i=0,s/\|\\/h /g,map$b[$i++].=$_,/./g for<>;/o/&&print chr 65+(4+length$')%7,/[h|]/&&"/".4*2**y/h//," "for@b

Dengan spasi putih untuk presentasi:

$i=0,
    s/\|\\/h /g,
    map $b[$i++]. = $_, /./g
  for <>;
/o/ && print chr 65 + (4 + length $') % 7,
             /[h|]/ && "/" . 4*2**y/h//,
             " "
  for @b

Perhatikan bahwa saya berasumsi bahwa semua baris memiliki panjang yang sama (sesuai versi revisi pertanyaan).

Versi ulang dengan penjelasan:

#!/usr/bin/env perl
# First transpose the list of lines into a list of columns.
my @b = ();               # @b[$i] will contain the characters in column $i
while (<>) {              # for each input line, do
    my $i = 0;            # start in column 0
    s/\|\\/h /g;          # replace '\|' by 'h ', to keep track of part notes in the first column
    foreach (/./g) {      # for each character, do
        $b[$i++] .= $_;   # append the character to the transposed matrix
    }
}
# Now process each column.
foreach (@b) {            # for each column, do
    if (/o/) {            # if it contains a note, then
        print chr(65 + (4 + length $') % 7);    # print the note pitch
        if (/[h|]/) {                           # if this is a part note (had |\ or just |)
            print "/", 4*2**y/h//;              # print /n where n = 2^(subdivision)
        }
        print " ";
    }
}

(solusi lama, lebih lama, disimpan karena mungkin menarik meskipun sedikit lebih lama)

Perl, 147 126 karakter

( 149 131 jika Anda menghitung spasi yang diperlukan)

$c=0,map{/o/?$h[$c]=E:/\\/?$d[$c-1]*=2:/\|/?$d[$c]||=4:++$h[$c];++$c}/./g for<>;print grep{s~$~/$d[$i++] ~;s~/ ~ ~;y/E-M/EFGA-F/}@h

Dengan spasi putih untuk presentasi:

$c = 0,
map { /o/ ? $h[$c]=E :
      /\\/ ? $d[$c-1]*=2 :
      /\|/ ? $d[$c]||=4 :
      ++$h[$c];
      ++$c
    } /./g for <>;
print grep {s~$~/$d[$i++] ~; s~/ ~ ~; y/E-M/EFGA-F/} @h

Ulang sedikit agar tidak terlalu banyak menyalahgunakan bahasa:

#!/usr/bin/perl
my @h;          # $h[$c] will contain the note in column $c, if any
my @d;          # $d[$c] will contain the note length (e.g. 4), if any
while (<>) {    # for each input line, do
    my $c = 0;  # column number
    foreach (split //) {   # for each character, do
        if (/o/) { $h[$c] = "E"; }      # o => it's a note; if this is the last line, it's E
        elsif (/\\/) { $d[$c-1] *= 2; } # \ => halve the duration of the note in the previous column
        elsif (/\|/) { $d[$c] ||= 4; }  # | => if this is the first | in the column, we have a quarter note
        else { ++$h[$c]; }              # anything else => bump the note by 1
        ++$c;
     }
}
for (my $i = 0; $i < @h; $i++) { # for each column, do
    $_ = $h[$i];                   # look up the potential note (or garbage if there is no note in this column)
    s~$~/$d[$i++] ~;               # append the duration and a space (or "/ " if there is no duration)
    s~/ ~ ~;                       # remove the spurious "/" if there is no duration
    if (y/E-M/EFGA-F/) {           # if it's a note (i.e. if it contains a letter E-M), then
                                   # fix the letter wraparound and then
        print $_;                    # print the note
    }
}

Perhatikan bahwa saya berasumsi bahwa semua garis memiliki panjang yang sama. Jika Anda ingin memperbolehkan baris yang lebih pendek, perbaikan yang jelas adalah menambahkan $_.=1x$c,di awal program, dengan biaya 9 karakter.

Saya memikirkan pendekatan lain untuk menghindari kata-kata panjang seperti splitdan mapdan membiarkan ruang melakukan lebih banyak pekerjaan, tetapi pelat dan tanda baca mengambil balas dendam mereka, dan saya hanya bisa membawanya ke sebuah rejan 130 (144 dengan spasi yang diperlukan).

sub p{$-[0]}
%a=qw(o $h[p]=E \ $d[&p-1]*=2 | $d[p]||=4 - ++$h[p]);
y/ /-/,s~.~$a{$&}~gee for<>;
print grep{s~$~/$d[$i++] ~;s~/ ~ ~;y/E-M/EFGA-F/}@h

Patch untuk mengatasi garis-garis yang belum selesai sedikit lebih aneh kali ini (apa, Anda pikir itu tidak akan aneh?). 139 karakter, 155 dengan spasi putih yang diperlukan.

sub p{$-[0]}
%a=qw(o $h[p]=E \ $d[&p-1]*=2 | $d[p]||=4 - ++$h[p]);
$_.=" "x p,y/
 /-/,s~.~$a{$&}~gee for<>;
print grep{s~$~/$d[$i++] ~;s~/ ~ ~;y/E-M/EFGA-F/}@h
Gilles 'SANGAT berhenti menjadi jahat'
sumber
2

Scala (2.9), 352 313 291 294 290 277 274 273 karakter

Jika semua fungsi yang diperlukan:

def m(s:String){var(x,y,z,l)=(0,1,s.count(_=='\n'),Array.fill(99)(0))
var n=l.clone
for(c<-s){if(c=='\n'){x=0;y+=1}
if(c=='\\')l(x-1)+=1
if(c=='|')l(x)+=1
if(c=='o')n(x)="EFGABCDEF"(z-y)
x+=1}
(n,l).zipped.map((x,y)=>if(x>0)print(x.toChar+(if(y>0)"/"+(4<<y-3)else"")+" "))}

Jika program lengkap diperlukan:

object M extends App{def m(s:String){var(x,y,z,l)=(0,1,s.count(_=='\n'),Array.fill(99)(0))
var n=l.clone
for(c<-s){if(c=='\n'){x=0;y+=1}
if(c=='\\')l(x-1)+=1
if(c=='|')l(x)+=1
if(c=='o')n(x)="EFGABCDEF"(z-y)
x+=1}
(n,l).zipped.map((x,y)=>if(x>0)print(x.toChar+(if(y>0)"/"+(4<<y-3)else"")+" "))}
m(io.Source.stdin.mkString)}
Gareth
sumber
Ada yang ruang kosong peralihan bar sampai akhir skor, meskipun saya tidak menyebutkan itu sehingga program harus bekerja terlepas. Jika garis dengan ruang kosong berakhir tiba-tiba, itu berarti tidak ada lagi masukan untuk dipertimbangkan untuk jalur itu. Hanya saja tidak harus crash .. :)
Neil
2

J - 108 karakter

exit echo}.,>,&.>/_4<@((a.{~32,65+7|4+i.&'o'),(>&0#('/',0":2^]))@((+/@(=&'\'))+2*'|'&e.))@;\|:|.[;._2]stdin''

Tidak Disatukan:

str =: stdin''
lines =: [;._2] str                          NB. split on the last character, the newline
rotated =: |: |. lines                       NB. lines reversed, then transposed
pitch =: 65 + 7 | 4 + i.&'o'                 NB. ord('A') + ( line.index('o') + 4 ) % 7
has_stem =: '|' & e.                         NB. '|' in line?
backslash_count =: (+/ @ (=&'\') )           NB. sum(char = '\\' for char in line)
denom_exp =: backslash_count + 2 * has_stem
fraction =: (>&0 # ('/', 0": 2 ^ ]))         NB. slash + 2^denom_exp, if denom_exp > 0
suffix =: fraction @ denom_exp
note_string =: (a. {~ 32,pitch) , suffix     NB. map(chr, (ord(' '), pitch)) + suffix
boxed_note_string =: < @ note_string @ ;     NB. box the string so it doesn't get padded
each_note_of_the =: boxed_note_string        NB. compute the note for a block of 4 lines
join_to_one_box =: , &. >
exit echo }. , > join_to_one_box / _4 each_note_of_the \ rotated
DCharness
sumber
2

Golf Python, 207 karakter.

import sys
a=[x[:-1]+' '*99 for x in sys.stdin]
for x in range(0,99,4):
 b=''.join((y[x:x+4] for y in a))+'o'
 c=2**(b.count('\\')+('|'in b)*2)
 print'FEDCBAGFE '[b.index('o')/4-len(a)+9]+('','/'+`c`)[c>1],

Saya sudah mulai kode golf dengan Python selama 2 hari dan saya menemukan bahwa hal-hal seperti import sys, sys.stdin.read, sys.stdout.writeyang ekspansif.

sinar
sumber
Jika Anda baru bermain golf di Python, Anda mungkin menemukan pertanyaan tips gofling python ini berguna.
Gareth