I'm building a musical trainer game where each after 12 pitch classes [C,CD,D,DE,E,F,FG,G,GA,A,AB,B] is represented by [0 thru 11], and a chord is a list of such numbers, so CEG would be [0,4,7]
Looking at the difference gives us the intervals: [4-0=4, 7-4=3] so [4,3], a minor third stacked on top of a major third in this case.
Suppose I have a complete list of all k-note chords, formed by maybe doing:
pitchClasses = [i for i in range(12)]
from itertools import combinations
deck = list( combinations(pitchClasses, k) )
How can I prune my deck so as to reject any chord containing an interval < p or > q?
Say for example I set p=3,q=4. then [C,D,G] gets excluded because it is [0,2,7] with gaps [2,5] and 2<3. Also 5>4 so it gets excluded on two counts.
Also [B,C] would get excluded because [11,0] -> gap of [1]
CEG wouldn't get excluded ; it has gaps [4,3]. There is also a gap of 7 between the G and wrapping round to the C, but this wouldn't constitute rejection.
It would be cleaner if a 3-note chord produces a 'dual' of 3 gaps. Because the wraparound gap might not be the biggest gap.
Then the smallest element can be compared with p, and the second-largest element can be compared with q.
I believe I would need to create the 'dual' gaps-list, order it smallest first, and then compare:
if W[0] < p or W[-2] > q:
# reject
I've done this in C# and it resulted in rather a lot of code.
Is there any way to do this concisely in Python?
So far I've found:
How can I find all the subsets of a set, with exactly n elements?
Python - Differences between elements of a list
And my code looks like this so far:
from random import shuffle
from itertools import combinations
pitchClasses = [i for i in range(12)]
deck = list( combinations(pitchClasses, notesInChord) )
def GapOk(chord,min_gap,max_gap):
gaps = [ ( 12 + chord[i+1]-chord[i] ) % 12 for i in range(len(chord)) ]
intervals = sorted( gaps )
return intervals[0] >= min_gap and intervals[-2] <= max_gap
deck_pruned = [ chord for chord in deck if GapOk(chord,min_gap,max_gap) ]
shuffle( deck_pruned )
Well, it looks as though I may have answered my own question. But I'm going to put it up anyway, because I'm very interested whether there is a better way of doing it. I may break the above code-block out into an answer later.