10
\$\begingroup\$

The Narrative

You are a bad musician. You never bothered to learn how to count rhythms. To remedy this shortcoming, you decide to write a program that will tell you how to count in any given time signature. You are still a bad musician, but you can at least pretend that you can count rhythms.

The Objective

Given a time signature, output a possible counting pattern for said time signature.

For those who don't know musical terms:
A measure is, essentially, a group of notes, useful for organizing music and making sure the musicians don't lose their place in the music.
A time signature is a set of numbers in a piece of music which tells you how many beats are in a measure, and which note-length is the beat. For example, a 3/4 time signature tells you that each measure has 3 beats, and the quarter note (4) is the beat. An 11/16 T.S. tells you that there are 11 sixteenth note beats (16) in one measure.
A counting pattern is a way to verbally express the division of a measure. For example, a song in 3/4 can simply be counted as "1 2 3". 11/16 can be counted as "1 la li 2 e & a 3 e & a" (this is more intuitive than counting to 11; I will define this later)
It's important to note that the bottom number of almost all T.S.'s are a power of 2. For this challenge, we will ignore irrational T.S.'s. We will also not look at T.S.'s with a beat greater than 16.

How to Generate a Counting Pattern:
Time signatures with a X/1, X/2, or X/4 can simply be counted with numbers (4/2 can be counted as "1 2 3 4").
T.S.'s with a X/8 or X/16 are a bit more tricky. These can be counted by grouping beats together to form larger beats (6/8, despite having 6 'beats', is very often treated as 2 beats with a triplet subdivision). For this challenge, beats should be grouped in groups of 1("1"), 2("1 &"), 3("1 la li"), or 4("1 e & a")
For X/8, groups of 3 should be prioritized over 2, but 1 should be avoided if possible. (For 7/8, even though 3+3+1 prioritizes 3's, 3+2+2 is better because it avoids 1)
For X/16, groups of 3 should be prioritized over 4, but 2 and 1 should be avoided if possible. (For 11/16, even though 3+3+3+2 prioritizes 3's, 3+4+4 is better because it avoids 2)
It is important to note that the ordering of the groups is not important, i.e. 3+2 and 2+3 are both acceptable groupings of 5/8. This would be important normally, but you are a bad musician.
Also note that T.S.'s 'larger' than 1/8 can be grouped entirely with 2's and 3's; T.S.'s 'larger' than 5/16 can be grouped entirely with 3's and 4's.

Whew! What a mouthful!

The Specs

You will take a time signature as input. this may be formatted within reason (use common sense!).
You will output an acceptable counting pattern. This will be a string of counts (1, la, &, etc.) separated by spaces. Your output should not include leading or trailing whitespace.
Test Cases:
To clarify, the 'numerator' is a positive integer, and the 'denominator' is 1, 2, 4, 8, or 16.
Again, note that some T.S.'s have multiple valid outputs.

"Input", "Output"
"1/1", "1"
"3/2", "1 2 3"
"4/4", "1 2 3 4"
"1/8", "1"
"10/8", "1 la li 2 la li 3 & 4 &"
"2/16", "1 &"
"5/16", "1 la li 2 &"
"29/16", "1 la li 2 la li 3 la li 4 la li 5 la li 6 la li 7 la li 8 e & a 9 e & a"

"Input", "Invalid Output"
"6/4", "1 la li 2 la li"
"7/8", "1 la li 2 la li 3"
"10/8", "1 & 2 & 3 & 4 & 5 &"
"12/16", "1 e & a 2 e & a 3 e & a"

This is , so the shortest answer in bytes wins!

\$\endgroup\$
9
  • 1
    \$\begingroup\$ For 10/8 is 1 & 2 & 3 & 4 & 5 & valid? \$\endgroup\$
    – ophact
    Commented May 30, 2022 at 11:38
  • 6
    \$\begingroup\$ "Your output should not include leading or trailing whitespace." I assume a single trailing newline is allowed, or not either? A lot of programming languages add a trailing newline by default, and disallowing this doesn't add much to the core of the challenge anyway. \$\endgroup\$ Commented May 30, 2022 at 15:31
  • 1
    \$\begingroup\$ @DeletedComment The 10/8 example is grouped as 3+3+2+2… maybe you misread? 2 groups of 2 look like ”1 & 2 &”, while 1 group of 4 looks like ”1 e & a”. Hope this helps! \$\endgroup\$
    – Nilster
    Commented May 30, 2022 at 17:09
  • 1
    \$\begingroup\$ Is a zero-indexed input allowed? So input would be 5/3 for 6/4 time \$\endgroup\$
    – jezza_99
    Commented May 31, 2022 at 1:34
  • 1
    \$\begingroup\$ @jezza_99 Sure, I’ll allow that \$\endgroup\$
    – Nilster
    Commented May 31, 2022 at 1:36

6 Answers 6

6
\$\begingroup\$

C (gcc), 372 354 308 301 299 260 240 237 233 bytes

#define p!printf(" %d%s"+!i++,i,a
#define q(x,y,z)(j=n%3-z/4)|n<z?p[x]),~j:p[y])+p[y])-z;
*a[]={""," &"," la li"," e & a"};f(n,d,i,j){for(i=0;d<5&&n--;)p[0]);n+=d-8?0:q(j,1,4)for(n+=d<16?0:n<2?p[0])-n:q(j=j?j+3:1,3,8)n>0;n-=3)p[2]);}

Try it online!

-24 bytes thanks to ceilingcat

-46 bytes thanks to ceilingcat

-7 bytes thanks to ceilingcat

-2 bytes thanks to ceilingcat

-39 bytes thanks to both Juan Ignacio Díaz and ceilingcat

-20 bytes thanks to both Juan Ignacio Díaz and ceilingcat

-3 bytes thanks to ceilingcat

-4 bytes thanks to ceilingcat

\$\endgroup\$
4
  • 1
    \$\begingroup\$ It breaks for 1/16, 2/16, and 3/16... Everything else seems to be working fine though! \$\endgroup\$
    – Nilster
    Commented May 30, 2022 at 12:03
  • 2
    \$\begingroup\$ @Nilster fixed it \$\endgroup\$
    – badatgolf
    Commented May 30, 2022 at 13:26
  • 1
    \$\begingroup\$ Very nice! Until it’s beaten (or proven faulty) I’m marking this correct! \$\endgroup\$
    – Nilster
    Commented May 30, 2022 at 14:01
  • 2
    \$\begingroup\$ @ceilingcat you're crazy. I couldn't even understand what I wrote and you're still golfing it \$\endgroup\$
    – badatgolf
    Commented May 30, 2022 at 16:12
4
\$\begingroup\$

Jelly, 46 bytes

Œṗi@€o⁸Ṁ;ṢƊɗÞ:5×6Œ?Ɗ}Ḣị““&“la li“e & a”Ė¹Ƈ€K€K

A full program that accepts the beats and the note-length and prints the result

Try it online!

How?

Œṗi@€o⁸Ṁ;ṢƊɗÞ:5×6Œ?Ɗ}Ḣị“...”Ė¹Ƈ€K€K - Main Link: beats, B; note-length L
Œṗ                                  - all integer partitions of B
                    }               - using L:
                   Ɗ                -   last three links as a monad - f(L):
                                                             e.g. 1,2,4,8,16
             :5                     -     integer divide by 5  -> 0,0,0,1,3
               ×6                   -     multiply by six      -> 0,0,0,6,18
                 Œ?                 -     shortest permutation of [1..N] which has that
                                            lexicographic 1-based-index in a list of all
                                            permutaions of [1..N]
                                                               -> [],[],[],[3,2,1],[3,4,2,1]
                                                                  (our ordering)
            Þ                       - sort (the partitions) by:
           ɗ                        -   last three links as a dyad - f(Partition, Ordering)
    €                               -     for each (run-length, R) in the partition:
  i@                                -       first 1-indexed index of R in Ordering or 0
     o⁸                             -       logical OR with the Partition (vectorises)
                                              (replaces 0s with the larger, original Rs)
          Ɗ                         -       last three links as a monad - f(X):
       Ṁ                            -         maximum of X
         Ṣ                          -         sorted X
        ;                           -         concatenate
                     Ḣ              - head
                       “...”        - ["", "&", "la li", "e & a"]
                      ị             - index into
                            Ė       - enumerate (i.e. [A, B, ...] -> [[1,A],[2,B],...])
                               €    - for each:
                             ¹Ƈ     -   keep truthy (i.e. drop the "" entries)
                                K€  - join each with spaces
                                  K - join with spaces
                                    - implicit print
\$\endgroup\$
3
\$\begingroup\$

05AB1E, 52 bytes

5‹iLëÅœΣ4LRIg·†3†Rsk{}θε…e&aN>©š®D'&‚®…ÿ…å´à)ćè]˜ðý?

Two separated inputs \$a\$ and \$b\$, where \$b\$ is the beat and \$a\$ are the amount of notes in the measure.
If the input was as strict as the output, a trailing '/¡`© (+5 bytes) could be added and the I could be replaced with ®.

Try it online or verify all test cases or try it online with strict input format.

4LR®g·†3†R could alternatively be 4L14S.I®gè for the same byte-count: verify all test cases.

Explanation:

5‹i            # If the first (implicit) input is smaller than 5 (1, 2, or 4):
   L           #  Push a list in the range [1, second (implicit) input] 
ë              # Else (it's 8 or 16 instead):
 Ŝ            #  Get all lists of positive integers that sum to the second
               #  (implicit) input
   Σ           #  Sort it by:
    4LR        #   Push list [4,3,2,1]
       I       #   Push the second input again
        g      #   Pop and push its length
         ·     #   Double it
          †    #   Filter that value to the front
           3†  #   Then filter 3 to the front
             R #   And then reverse the list
               #   (we now have [1,4,2,3] for input=8
               #   and [1,2,4,3] for input=16)
    s          #  Swap so the current list
     k         #  Get the index of each in this list (or -1 if higher)
      {        #  Sort that list from lowest to highest
   }θ          #  After the sort: leave the last list
               #  (so `Σ...}θ` acted as a maximum-by builtin)
 ε             #  Map that resulting list to:
  …e&a         #   Push string "e&a"
      N>       #   Push the 1-based map-index
        ©      #   Store it in variable `®` (without popping)
         š     #   Convert the string to a list, and append it: 
               #    ["e","&","a",index]
  ®            #   Push index `®` again
  D            #   And again, with a duplicate
   '&‚        '#   Pair it with "&"
  ®            #   And yet again
   …ÿ…å´à      #   Push dictionary string "ÿ la li",
               #   where the `ÿ` is replaced with this index
  )            #   Wrap all values into a list
   ć           #   Extract the head; the current list of the map
    è          #   Modular 0-based index those into the list
]              # Close both the map and if-else statement
 ˜             # Flatten the list of lists
  ðý           # Join it with a space delimiter
    ?          # Explicitly print it without trailing newline

See this 05AB1E tip of mine (section How to use the dictionary?) to understand why …ÿ…å´à is "ÿ la li".

\$\endgroup\$
2
\$\begingroup\$

Charcoal, 70 bytes

NθNη⪫E⊟Φ…⟦Eθ¹E÷⁺²θ³⁻³‹ι﹪±θ³E÷θ³⁺³‹ι﹪θ³⟧⊕÷η⁸⁼θΣι⁺⊕κ§⪪“/^e)R⁼0⁷Vⅉ8⮌✳”,ι 

Try it online! Link is to verbose version of code. Explanation:

NθNη

Input the time signature.

⟦Eθ¹E÷⁺²θ³⁻³‹ι﹪±θ³E÷θ³⁺³‹ι﹪θ³⟧

Try to create three beat patterns: all single beats, as many groups of three as possible with the rest two, and as many groups of three as possible with the rest four.

…...⊕÷η⁸

Only consider the first or first two patterns depending on the denominator.

Φ...⁼θΣι

Only keep those patters that actually sum to the whole bar. (This prevents e.g. 5/16 from trying to use groups of three and four).

⪫E⊟...⁺⊕κ§⪪...,ι 

Take the last successful pattern and replace each group with its beat number and group suffix obtained by splitting the compressed string e & a,, &, la li, finally joining the groups with spaces.

\$\endgroup\$
2
\$\begingroup\$

Vyxal, 56 bytes

5<[ɾ|Ṅµ1k½d⁰LǔJ3Jvḟs;tṘ¨2›:£`e&a`fJ¥:\&"¥«cb|«"ṄWḣi;]fṄ₴

Try it Online!

Port of 05AB1E. Quite slow.

\$\endgroup\$
2
\$\begingroup\$

Python 2, 136 135 bytes

-1 byte thanks to Kevin Cruijssen

def f(m,b,r='',i=1):
 while m:k=b<5or[m,3-(m<5)+(6<m<9)*b/9][m>2+b/8];m-=k;r+=' '+`i`+[' e & a',' la li',' &',''][-k];i+=1
 print r[1:]

Try it online!

Takes input as f(m,b) where m is the numerator and b is the denominator

Explanation

while m:

Loop while m > 0

k=b<5or[m,3-(m<5)+(6<m<9)*b/9][m>2+b/8]

Determine the size of the next group. Essentially the logic is as follows:

  • b in [1,2,4] => 1
  • b == 8 and m == 4 => 2
  • b == 16 and m in [7,8] => 4
  • (b == 8 and m <= 3) or (b == 16 and m <= 4) => m
  • otherwise => 3
m-=k;r+=' '+`i`+[' e & a',' la li',' &',''][-k];i+=1

Decrease m by the group size. Append the following to r: First a space, then the current group index, then the appropriate suffix based on the group size. Increment the group index

print r[1:]

Output the constructed string, removing the leading space

\$\endgroup\$
1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.