Solution of Staff

Although it looks like a tractor kind of problem, Staff allows for some quite elegant solutions.

Observation

The ASCII staff provided in the statement can be hard-coded and used in the solution. More concisely, it can be split horizontally into a series of sections. The treble cleff and final bar each make up one section. Each note occupies a 5-character section, with the @ situated exactly in the middle. This assures the right distance between consecutive notes.

All that's left is the printing. If the series described above is constructed appropriately, then it's a simple matter of executing multiple passes across the series, with each pass printing one row of the solution staff (much like an actual computer printer stamps sentences on a piece of paper, one row at a time).

C solution (Marius Gavrilescu)

    1 #include<stdio.h>
    2 #include<string.h>
    3 
    4 const char *notes = "C C#D D#E F F#G G#A A#B C2C2#"; // Notes in order. Note #i is at position i * 2 in the string
    5 const char *ex[] = {                                 // ! instead of \\ since escaped backslashes are ugly
    6 "----|-!-----------------------------------------------------------------------------+",
    7 "    |  }                                                                            |",
    8 "----|-/-----------------------------------------------------|----|------------------|",
    9 "    |/   4                                        |    |    |    |       (@) #(@)   |",
   10 "---/|-----------------------------------|----|----|----|----|----|--(@)--|----|-----|",
   11 "  / |    4                         |    |    |    |    |  (@) #(@)  |    |    |     |",
   12 "-{--|-!------------------|----|----|----|----|--(@)-#(@)------------|----|----|-----|",
   13 "  !_|_/        |    |    |    |    |  (@) #(@)                      |               |",
   14 "----|!---------|----|----|----|--(@)------------------------------------------------+",
   15 "    |_}        |    |  (@) #(@)                                                      ",
   16 "             (@) #(@)                                                                "};
   17 
   18 int v[255];
   19 
   20 void print(int l, int a, int b) {
   21     for(int i = a ; i < b ; i++)
   22         putchar(ex[l][i] == '!' ? '\\' : ex[l][i]); // Replace '!' with '\\'
   23 }
   24 
   25 int main() {
   26     int n;
   27     char note[10];
   28     scanf("%d ", &n);
   29     for(int i = 0 ; i < n ; i++){
   30         scanf("%s ", note);
   31         v[i] = (strstr(notes, note) - notes) / 2; // Index of the note
   32         v[i] = 12 + v[i] * 5;                     // Start position of the note
   33     }
   34 
   35     for (int l = 0; l < 11; l++) {                // For each line of ex...
   36         print(l, 0, 12);                          // ... print the clef and measure
   37         for(int i = 0; i < n; i++)
   38             print(l, v[i], v[i] + 5);             // ... print the note
   39         print(l, 82, 85);                         // ... print the final bar
   40         puts("");                                 // ... print a newline
   41     }
   42 
   43     return 0;
   44 }

Python solution (Sergiu Puscas)

    1 base = ['----|-\-----------------------------------------------------------------------------+',
    2         '    |  }                                                                            |',
    3         '----|-/-----------------------------------------------------|----|------------------|',
    4         '    |/   4                                        |    |    |    |       (@) #(@)   |',
    5         '---/|-----------------------------------|----|----|----|----|----|--(@)--|----|-----|',
    6         '  / |    4                         |    |    |    |    |  (@) #(@)  |    |    |     |',
    7         '-{--|-\------------------|----|----|----|----|--(@)-#(@)------------|----|----|-----|',
    8         '  \_|_/        |    |    |    |    |  (@) #(@)                      |               |',
    9         '----|\---------|----|----|----|--(@)------------------------------------------------+',
   10         '    |_}        |    |  (@) #(@)                                                      ',
   11         '             (@) #(@)                                                                ']
   12 
   13 link = {'clef': (0, 12),
   14         'C':    (12, 17),
   15         'C#':   (17, 22),
   16         'D':    (22, 27),
   17         'D#':   (27, 32),
   18         'E':    (32, 37),
   19         'F':    (37, 42),
   20         'F#':   (42, 47),
   21         'G':    (47, 52),
   22         'G#':   (52, 57),
   23         'A':    (57, 62),
   24         'A#':   (62, 67),
   25         'B':    (67, 72),
   26         'C2':   (72, 77),
   27         'C2#':  (77, 82),
   28         'bar':  (82, 85)}
   29 
   30 v = ['clef'] + [raw_input() for _ in range(0, input())] + ['bar']
   31 print '\n'.join([''.join([base[line][link[element][0]:link[element][1]] for element in v]) for line in range(0, len(base))])

The entire functionality only takes up two lines of code. All else is preparation.

Perl solution (Marius Gavrilescu)

    1 #!/usr/bin/perl
    2 use v5.14;
    3 use warnings;
    4 
    5 my @notes = qw/C C# D D# E F F# G G# A A# B C2 C2#/;         # List of notes
    6 my %notes = map {; $notes[$_] => 12 + $_ * 5 } 0 .. $#notes; # Hash from note name to start index
    7 
    8 my @ex = split "\n", <<'EOF';
    9 ----|-\-----------------------------------------------------------------------------+
   10     |  }                                                                            |
   11 ----|-/-----------------------------------------------------|----|------------------|
   12     |/   4                                        |    |    |    |       (@) #(@)   |
   13 ---/|-----------------------------------|----|----|----|----|----|--(@)--|----|-----|
   14   / |    4                         |    |    |    |    |  (@) #(@)  |    |    |     |
   15 -{--|-\------------------|----|----|----|----|--(@)-#(@)------------|----|----|-----|
   16   \_|_/        |    |    |    |    |  (@) #(@)                      |               |
   17 ----|\---------|----|----|----|--(@)------------------------------------------------+
   18     |_}        |    |  (@) #(@)
   19              (@) #(@)
   20 EOF
   21 $_ .= ' ' x 80 for @ex;                  # Append 80 spaces to each line of @ex
   22 
   23 <>;                                      # Read the first line and ignore it
   24 my @song = map { chomp; $notes{$_} } <>; # Song is an array of start indices
   25 
   26 for my $l (@ex) {                        # For each line of @ex...
   27     print substr $l, 0, 12;              # ...print the clef and measure
   28     print substr $l, $_, 5 for @song;    # ...print each note
   29     print substr $l, 82, 3;              # ...print the final bar
   30     say '';                              # ...print a newline
   31 }

Alternative solutions

Examples from the contest: 6930 (5.62 KB), 7208 (5.60 KB), 7390 (4.25 KB), 6655 (3.94 KB), 6924 (3.38 KB).

Questions?

Sponsors Gold