Skip to content

Commit 65a3f40

Browse files
kytrinyxkotp
authored andcommitted
Sync scale-generator with problem-specifications
This brought in a slew of changes that required updating the sample solution. The TL;DR is that the last character pattern passed to the interval was being ignored before, when it should not be.
1 parent 2e132ee commit 65a3f40

File tree

5 files changed

+167
-73
lines changed

5 files changed

+167
-73
lines changed
Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,68 @@
11
# Instructions
22

3-
Given a tonic, or starting note, and a set of intervals, generate
4-
the musical scale starting with the tonic and following the
5-
specified interval pattern.
3+
## Chromatic Scales
64

7-
Scales in Western music are based on the chromatic (12-note) scale. This
8-
scale can be expressed as the following group of pitches:
5+
Scales in Western music are based on the chromatic (12-note) scale.
6+
This scale can be expressed as the following group of pitches:
97

10-
A, A#, B, C, C#, D, D#, E, F, F#, G, G#
8+
> A, A, B, C, C, D, D, E, F, F, G, G
119
12-
A given sharp note (indicated by a #) can also be expressed as the flat
13-
of the note above it (indicated by a b) so the chromatic scale can also be
14-
written like this:
10+
A given sharp note (indicated by a ♯) can also be expressed as the flat of the note above it (indicated by a ♭) so the chromatic scale can also be written like this:
1511

16-
A, Bb, B, C, Db, D, Eb, E, F, Gb, G, Ab
12+
> A, B♭, B, C, D♭, D, E♭, E, F, G♭, G, A♭
1713
18-
The major and minor scale and modes are subsets of this twelve-pitch
19-
collection. They have seven pitches, and are called diatonic scales.
20-
The collection of notes in these scales is written with either sharps or
21-
flats, depending on the tonic. Here is a list of which are which:
14+
The major and minor scale and modes are subsets of this twelve-pitch collection.
15+
They have seven pitches, and are called diatonic scales.
16+
The collection of notes in these scales is written with either sharps or flats, depending on the tonic (starting note).
17+
Here is a table indicating whether the flat expression or sharp expression of the scale would be used for a given tonic:
2218

23-
No Sharps or Flats:
24-
C major
25-
a minor
19+
| Key Signature | Major | Minor |
20+
| ------------- | --------------------- | -------------------- |
21+
| Natural | C | a |
22+
| Sharp | G, D, A, E, B, F♯ | e, b, f♯, c♯, g♯, d♯ |
23+
| Flat | F, B♭, E♭, A♭, D♭, G♭ | d, g, c, f, b♭, e♭ |
2624

27-
Use Sharps:
28-
G, D, A, E, B, F# major
29-
e, b, f#, c#, g#, d# minor
25+
Note that by common music theory convention the natural notes "C" and "a" follow the sharps scale when ascending and the flats scale when descending.
26+
For the scope of this exercise the scale is only ascending.
3027

31-
Use Flats:
32-
F, Bb, Eb, Ab, Db, Gb major
33-
d, g, c, f, bb, eb minor
28+
### Task
3429

35-
The diatonic scales, and all other scales that derive from the
36-
chromatic scale, are built upon intervals. An interval is the space
37-
between two pitches.
30+
Given a tonic, generate the 12 note chromatic scale starting with the tonic.
3831

39-
The simplest interval is between two adjacent notes, and is called a
40-
"half step", or "minor second" (sometimes written as a lower-case "m").
41-
The interval between two notes that have an interceding note is called
42-
a "whole step" or "major second" (written as an upper-case "M"). The
43-
diatonic scales are built using only these two intervals between
44-
adjacent notes.
32+
- Shift the base scale appropriately so that all 12 notes are returned starting with the given tonic.
33+
- For the given tonic, determine if the scale is to be returned with flats or sharps.
34+
- Return all notes in uppercase letters (except for the `b` for flats) irrespective of the casing of the given tonic.
4535

46-
Non-diatonic scales can contain other intervals. An "augmented second"
47-
interval, written "A", has two interceding notes (e.g., from A to C or Db to E)
48-
or a "whole step" plus a "half step". There are also smaller and larger
49-
intervals, but they will not figure into this exercise.
36+
## Diatonic Scales
37+
38+
The diatonic scales, and all other scales that derive from the chromatic scale, are built upon intervals.
39+
An interval is the space between two pitches.
40+
41+
The simplest interval is between two adjacent notes, and is called a "half step", or "minor second" (sometimes written as a lower-case "m").
42+
The interval between two notes that have an interceding note is called a "whole step" or "major second" (written as an upper-case "M").
43+
The diatonic scales are built using only these two intervals between adjacent notes.
44+
45+
Non-diatonic scales can contain other intervals.
46+
An "augmented second" interval, written "A", has two interceding notes (e.g., from A to C or D♭ to E) or a "whole step" plus a "half step".
47+
There are also smaller and larger intervals, but they will not figure into this exercise.
48+
49+
### Task
50+
51+
Given a tonic and a set of intervals, generate the musical scale starting with the tonic and following the specified interval pattern.
52+
53+
This is similar to generating chromatic scales except that instead of returning 12 notes, you will return N+1 notes for N intervals.
54+
The first note is always the given tonic.
55+
Then, for each interval in the pattern, the next note is determined by starting from the previous note and skipping the number of notes indicated by the interval.
56+
57+
For example, starting with G and using the seven intervals MMmMMMm, there would be the following eight notes:
58+
59+
Note | Reason
60+
--|--
61+
G | Tonic
62+
A | M indicates a whole step from G, skipping G♯
63+
B | M indicates a whole step from A, skipping A♯
64+
C | m indicates a half step from B, skipping nothing
65+
D | M indicates a whole step from C, skipping C♯
66+
E | M indicates a whole step from D, skipping D♯
67+
F♯ | M indicates a whole step from E, skipping F
68+
G | m indicates a half step from F♯, skipping nothing

exercises/practice/scale-generator/.meta/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"blurb": "Generate musical scales, given a starting note and a set of intervals. ",
2+
"blurb": "Generate musical scales, given a starting note and a set of intervals.",
33
"authors": [
44
"fluxusfrequency"
55
],

exercises/practice/scale-generator/.meta/example.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ def chromatic
1414
end
1515

1616
def interval(pattern)
17-
last_index = 0
18-
scale = pattern.each_char.with_object([]) do |c, collector|
19-
collector << reorder_chromatic_scale[last_index]
20-
last_index += ASCENDING_INTERVALS.index(c) + 1
17+
index = 0
18+
pattern.each_char.with_object([reorder_chromatic_scale[index]]) do |char, scale|
19+
index = (index + (ASCENDING_INTERVALS.index(char) + 1)) % 12
20+
scale << reorder_chromatic_scale[index]
2121
end
2222
end
2323

exercises/practice/scale-generator/.meta/tests.toml

Lines changed: 92 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,52 +10,127 @@
1010
# is regenerated, comments can be added via a `comment` key.
1111

1212
[10ea7b14-8a49-40be-ac55-7c62b55f9b47]
13-
description = "Chromatic scale with sharps"
13+
description = "Chromatic scales -> Chromatic scale with sharps"
1414

1515
[af8381de-9a72-4efd-823a-48374dbfe76f]
16-
description = "Chromatic scale with flats"
16+
description = "Chromatic scales -> Chromatic scale with flats"
17+
18+
[7195998a-7be7-40c9-8877-a1d7949e061b]
19+
description = "Scales with specified intervals -> Simple major scale"
20+
reimplements = "6f5b1410-1dd7-4c6c-b410-6b7e986f6f1e"
21+
22+
[fe853b97-1878-4090-b218-4029246abb91]
23+
description = "Scales with specified intervals -> Major scale with sharps"
24+
reimplements = "13a92f89-a83e-40b5-b9d4-01136931ba02"
25+
26+
[d60cb414-cc02-4fcb-ad7a-fc7ef0a9eead]
27+
description = "Scales with specified intervals -> Major scale with flats"
28+
reimplements = "aa3320f6-a761-49a1-bcf6-978e0c81080a"
29+
30+
[77dab9b3-1bbc-4f9a-afd8-06da693bcc67]
31+
description = "Scales with specified intervals -> Minor scale with sharps"
32+
reimplements = "63daeb2f-c3f9-4c45-92be-5bf97f61ff94"
33+
34+
[5fa1728f-5b66-4b43-9b7c-84359b7069d4]
35+
description = "Scales with specified intervals -> Minor scale with flats"
36+
reimplements = "616594d0-9c48-4301-949e-af1d4fad16fd"
37+
38+
[f3f1c353-8f7b-4a85-a5b5-ae06c2645823]
39+
description = "Scales with specified intervals -> Dorian mode"
40+
reimplements = "390bd12c-5ac7-4ec7-bdde-4e58d5c78b0a"
41+
42+
[5fe14e5a-3ddc-4202-a158-2c1158beb5d0]
43+
description = "Scales with specified intervals -> Mixolydian mode"
44+
reimplements = "846d0862-0f3e-4f3b-8a2d-9cc74f017848"
45+
46+
[e6307799-b7f6-43fc-a6d8-a4834d6e2bdb]
47+
description = "Scales with specified intervals -> Lydian mode"
48+
reimplements = "7d49a8bb-b5f7-46ad-a207-83bd5032291a"
49+
50+
[7c4a95cd-ecf4-448d-99bc-dbbca51856e0]
51+
description = "Scales with specified intervals -> Phrygian mode"
52+
reimplements = "a4e4dac5-1891-4160-a19f-bb06d653d4d0"
53+
54+
[f476f9c9-5a13-473d-bb6c-f884cf8fd9f2]
55+
description = "Scales with specified intervals -> Locrian mode"
56+
reimplements = "ef3650af-90f8-4ad9-9ef6-fdbeae07dcaa"
57+
58+
[87fdbcca-d3dd-46d5-9c56-ec79e25b19f4]
59+
description = "Scales with specified intervals -> Harmonic minor"
60+
reimplements = "70517400-12b7-4530-b861-fa940ae69ee8"
61+
62+
[b28ecc18-88db-4fd5-a973-cfe6361e2b24]
63+
description = "Scales with specified intervals -> Octatonic"
64+
reimplements = "37114c0b-c54d-45da-9f4b-3848201470b0"
65+
66+
[a1c7d333-6fb3-4f3b-9178-8a0cbe043134]
67+
description = "Scales with specified intervals -> Hexatonic"
68+
reimplements = "496466e7-aa45-4bbd-a64d-f41030feed9c"
69+
70+
[9acfd139-0781-4926-8273-66a478c3b287]
71+
description = "Scales with specified intervals -> Pentatonic"
72+
reimplements = "bee5d9ec-e226-47b6-b62b-847a9241f3cc"
73+
74+
[31c933ca-2251-4a5b-92dd-9d5831bc84ad]
75+
description = "Scales with specified intervals -> Enigmatic"
76+
reimplements = "dbee06a6-7535-4ab7-98e8-d8a36c8402d1"
1777

1878
[6f5b1410-1dd7-4c6c-b410-6b7e986f6f1e]
19-
description = "Simple major scale"
79+
description = "Scales with specified intervals -> Simple major scale"
80+
include = false
2081

2182
[13a92f89-a83e-40b5-b9d4-01136931ba02]
22-
description = "Major scale with sharps"
83+
description = "Scales with specified intervals -> Major scale with sharps"
84+
include = false
2385

2486
[aa3320f6-a761-49a1-bcf6-978e0c81080a]
25-
description = "Major scale with flats"
87+
description = "Scales with specified intervals -> Major scale with flats"
88+
include = false
2689

2790
[63daeb2f-c3f9-4c45-92be-5bf97f61ff94]
28-
description = "Minor scale with sharps"
91+
description = "Scales with specified intervals -> Minor scale with sharps"
92+
include = false
2993

3094
[616594d0-9c48-4301-949e-af1d4fad16fd]
31-
description = "Minor scale with flats"
95+
description = "Scales with specified intervals -> Minor scale with flats"
96+
include = false
3297

3398
[390bd12c-5ac7-4ec7-bdde-4e58d5c78b0a]
34-
description = "Dorian mode"
99+
description = "Scales with specified intervals -> Dorian mode"
100+
include = false
35101

36102
[846d0862-0f3e-4f3b-8a2d-9cc74f017848]
37-
description = "Mixolydian mode"
103+
description = "Scales with specified intervals -> Mixolydian mode"
104+
include = false
38105

39106
[7d49a8bb-b5f7-46ad-a207-83bd5032291a]
40-
description = "Lydian mode"
107+
description = "Scales with specified intervals -> Lydian mode"
108+
include = false
41109

42110
[a4e4dac5-1891-4160-a19f-bb06d653d4d0]
43-
description = "Phrygian mode"
111+
description = "Scales with specified intervals -> Phrygian mode"
112+
include = false
44113

45114
[ef3650af-90f8-4ad9-9ef6-fdbeae07dcaa]
46-
description = "Locrian mode"
115+
description = "Scales with specified intervals -> Locrian mode"
116+
include = false
47117

48118
[70517400-12b7-4530-b861-fa940ae69ee8]
49-
description = "Harmonic minor"
119+
description = "Scales with specified intervals -> Harmonic minor"
120+
include = false
50121

51122
[37114c0b-c54d-45da-9f4b-3848201470b0]
52-
description = "Octatonic"
123+
description = "Scales with specified intervals -> Octatonic"
124+
include = false
53125

54126
[496466e7-aa45-4bbd-a64d-f41030feed9c]
55-
description = "Hexatonic"
127+
description = "Scales with specified intervals -> Hexatonic"
128+
include = false
56129

57130
[bee5d9ec-e226-47b6-b62b-847a9241f3cc]
58-
description = "Pentatonic"
131+
description = "Scales with specified intervals -> Pentatonic"
132+
include = false
59133

60134
[dbee06a6-7535-4ab7-98e8-d8a36c8402d1]
61-
description = "Enigmatic"
135+
description = "Scales with specified intervals -> Enigmatic"
136+
include = false

exercises/practice/scale-generator/scale_generator_test.rb

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,90 +17,90 @@ def test_chromatic_scales_chromatic_scale_with_flats
1717
def test_scales_with_specified_intervals_simple_major_scale
1818
skip
1919
scale = Scale.new("C")
20-
assert_equal %w[C D E F G A B], scale.interval("MMmMMMm")
20+
assert_equal %w[C D E F G A B C], scale.interval("MMmMMMm")
2121
end
2222

2323
def test_scales_with_specified_intervals_major_scale_with_sharps
2424
skip
2525
scale = Scale.new("G")
26-
assert_equal ["G", "A", "B", "C", "D", "E", "F#"], scale.interval("MMmMMMm")
26+
assert_equal ["G", "A", "B", "C", "D", "E", "F#", "G"], scale.interval("MMmMMMm")
2727
end
2828

2929
def test_scales_with_specified_intervals_major_scale_with_flats
3030
skip
3131
scale = Scale.new("F")
32-
assert_equal %w[F G A Bb C D E], scale.interval("MMmMMMm")
32+
assert_equal %w[F G A Bb C D E F], scale.interval("MMmMMMm")
3333
end
3434

3535
def test_scales_with_specified_intervals_minor_scale_with_sharps
3636
skip
3737
scale = Scale.new("f#")
38-
assert_equal ["F#", "G#", "A", "B", "C#", "D", "E"], scale.interval("MmMMmMM")
38+
assert_equal ["F#", "G#", "A", "B", "C#", "D", "E", "F#"], scale.interval("MmMMmMM")
3939
end
4040

4141
def test_scales_with_specified_intervals_minor_scale_with_flats
4242
skip
4343
scale = Scale.new("bb")
44-
assert_equal %w[Bb C Db Eb F Gb Ab], scale.interval("MmMMmMM")
44+
assert_equal %w[Bb C Db Eb F Gb Ab Bb], scale.interval("MmMMmMM")
4545
end
4646

4747
def test_scales_with_specified_intervals_dorian_mode
4848
skip
4949
scale = Scale.new("d")
50-
assert_equal %w[D E F G A B C], scale.interval("MmMMMmM")
50+
assert_equal %w[D E F G A B C D], scale.interval("MmMMMmM")
5151
end
5252

5353
def test_scales_with_specified_intervals_mixolydian_mode
5454
skip
5555
scale = Scale.new("Eb")
56-
assert_equal %w[Eb F G Ab Bb C Db], scale.interval("MMmMMmM")
56+
assert_equal %w[Eb F G Ab Bb C Db Eb], scale.interval("MMmMMmM")
5757
end
5858

5959
def test_scales_with_specified_intervals_lydian_mode
6060
skip
6161
scale = Scale.new("a")
62-
assert_equal ["A", "B", "C#", "D#", "E", "F#", "G#"], scale.interval("MMMmMMm")
62+
assert_equal ["A", "B", "C#", "D#", "E", "F#", "G#", "A"], scale.interval("MMMmMMm")
6363
end
6464

6565
def test_scales_with_specified_intervals_phrygian_mode
6666
skip
6767
scale = Scale.new("e")
68-
assert_equal %w[E F G A B C D], scale.interval("mMMMmMM")
68+
assert_equal %w[E F G A B C D E], scale.interval("mMMMmMM")
6969
end
7070

7171
def test_scales_with_specified_intervals_locrian_mode
7272
skip
7373
scale = Scale.new("g")
74-
assert_equal %w[G Ab Bb C Db Eb F], scale.interval("mMMmMMM")
74+
assert_equal %w[G Ab Bb C Db Eb F G], scale.interval("mMMmMMM")
7575
end
7676

7777
def test_scales_with_specified_intervals_harmonic_minor
7878
skip
7979
scale = Scale.new("d")
80-
assert_equal %w[D E F G A Bb Db], scale.interval("MmMMmAm")
80+
assert_equal %w[D E F G A Bb Db D], scale.interval("MmMMmAm")
8181
end
8282

8383
def test_scales_with_specified_intervals_octatonic
8484
skip
8585
scale = Scale.new("C")
86-
assert_equal ["C", "D", "D#", "F", "F#", "G#", "A", "B"], scale.interval("MmMmMmMm")
86+
assert_equal ["C", "D", "D#", "F", "F#", "G#", "A", "B", "C"], scale.interval("MmMmMmMm")
8787
end
8888

8989
def test_scales_with_specified_intervals_hexatonic
9090
skip
9191
scale = Scale.new("Db")
92-
assert_equal %w[Db Eb F G A B], scale.interval("MMMMMM")
92+
assert_equal %w[Db Eb F G A B Db], scale.interval("MMMMMM")
9393
end
9494

9595
def test_scales_with_specified_intervals_pentatonic
9696
skip
9797
scale = Scale.new("A")
98-
assert_equal ["A", "B", "C#", "E", "F#"], scale.interval("MMAMA")
98+
assert_equal ["A", "B", "C#", "E", "F#", "A"], scale.interval("MMAMA")
9999
end
100100

101101
def test_scales_with_specified_intervals_enigmatic
102102
skip
103103
scale = Scale.new("G")
104-
assert_equal ["G", "G#", "B", "C#", "D#", "F", "F#"], scale.interval("mAMMMmm")
104+
assert_equal ["G", "G#", "B", "C#", "D#", "F", "F#", "G"], scale.interval("mAMMMmm")
105105
end
106106
end

0 commit comments

Comments
 (0)