Домашни > Pitches love the D > Решения > Решението на Ивана Димитрова

Резултати
9 точки от тестове
0 точки от учител

9 точки общо

32 успешни теста
5 неуспешни теста
Код
Скрий всички коментари

  1TONE_NAMES  = (
  2    "C", 
  3    "C#", 
  4    "D", 
  5    "D#", 
  6    "E", 
  7    "F", 
  8    "F#", 
  9    "G", 
 10    "G#", 
 11    "A", 
 12    "A#", 
 13    "B"
 14    )
 15 
 16TONES_IN_OCTAVE = 12
 17 
 18INTERVAL_NAMES = (
 19        "unison", 
 20        "minor 2nd", 
 21        "major 2nd", 
 22        "minor 3rd", 
 23        "major 3rd",
 24        "perfect 4th",
 25        "diminished 5th", 
 26        "perfect 5th", 
 27        "minor 6th",
 28        "major 6th", 
 29        "minor 7th", 
 30        "major 7th"
 31        )
 32 
 33 
 34class Tone:
 35    def __init__(self, name):
 36        self.name = name
 37 
 38    def __str__(self):
 39        return self.name
 40 
 41    def __add__(self, other):
 42        if isinstance(other, Tone):
 43            return Chord(self, other)
 44        elif isinstance(other, Interval):
 45            return self._add_interval(other)
 46        else:
 47            raise TypeError("Invalid operation")
 48 
 49    def _add_interval(self, interval):
 50        """Collects a tone and an interval and returns a new tone."""
 51
 52        tone_index = TONE_NAMES.index(self.name)
 53        new_index = (tone_index + interval.halftone) % len(TONE_NAMES)
 54        return Tone(TONE_NAMES[new_index])
 55 
 56    def __sub__(self, other):
 57        if isinstance(other, Interval): 
 58            return self._sub_interval(other)
 59        if isinstance(other, Tone): 
 60            return self._sub_tone(other)
 61        else:
 62            raise TypeError("Invalid operation")
 63 
 64    def _sub_interval(self, interval):
 65        """Subtracts an interval from a tone and returns a new tone with a changed pitch."""
 66
 67        tone_index = TONE_NAMES.index(self.name)
 68        new_index = (tone_index - interval.halftone) % len(TONE_NAMES)
 69        return Tone(TONE_NAMES[new_index])
 70 
 71    def _sub_tone(self, other_tone):
 72        """Subtracts a tone from a tone and returns a space."""
 73
 74        tone_index = TONE_NAMES.index(self.name)
 75        other_tone_index = TONE_NAMES.index(other_tone.name)
 76        distance = (tone_index - other_tone_index) % len(TONE_NAMES)
 77
 78        return Interval(distance)
 79 
 80 
 81class Interval:
 82    def __init__(self, halftone):
 83        self.halftone = halftone % TONES_IN_OCTAVE
 84 
 85    def __add__(self, other):
 86        if isinstance(other, Interval):
 87            new_halftone = (self.halftone + other.halftone) % TONES_IN_OCTAVE
 88            return Interval(new_halftone)
 89 
 90    def __str__(self):
 91        return INTERVAL_NAMES[self.halftone]
 92 
 93    def __neg__(self):
 94        """Returns a new Interval object with a negative semitone value."""
 95        return Interval(-self.halftone)
 96 
 97 
 98class Chord:
 99    def __init__(self, base_tone, *other_tones):
100        unique_tones = [base_tone.name] 
101 
102        for tone in other_tones:
103            if tone.name not in unique_tones:
104                unique_tones.append(tone.name)
105 
106        if len(unique_tones) < 2:
107            raise TypeError("Cannot have a chord made of only 1 unique tone")
108 
109        self.base_tone = base_tone
110        self.tones = unique_tones 
111 
112    def get_tone_index(self, tone_name):
113        return TONE_NAMES.index(tone_name)
114 
115    def __str__(self):
116        root_index = self.get_tone_index(self.base_tone.name)
117        self.tones.sort(key = lambda current_tone: (TONE_NAMES.index(current_tone) - root_index) % TONES_IN_OCTAVE)
118        return "-".join(self.tones)
119 
120    def is_minor(self):
121        MINOR_INTERVAL = 3
122        root_index = self.get_tone_index(self.base_tone.name)
123        for tone_name in self.tones:
124            if (self.get_tone_index(tone_name) - root_index) % TONES_IN_OCTAVE == MINOR_INTERVAL:
125                return True
126        return False
127 
128    def is_major(self):
129        MAJOR_INTERVAL = 4 
130        root_index = self.get_tone_index(self.base_tone.name)
131        for tone_name in self.tones:
132            if (self.get_tone_index(tone_name) - root_index) % TONES_IN_OCTAVE == MAJOR_INTERVAL:
133                return True
134        return False
135 
136    def is_power_chord(self):
137        if self.is_minor() or self.is_major():
138            return False  
139        return True  
140 
141    def __add__(self, other):
142        """Matching a chord with a tone or with another chord."""
143 
144        if isinstance(other, Tone):
145            new_tones = self.tones + [other.name]
146            sorted_tones = sorted(set(new_tones), key=lambda t: TONE_NAMES.index(t))
147            return Chord(Tone(sorted_tones[0]), *[Tone(t) for t in sorted_tones[1:]])
148 
149        elif isinstance(other, Chord):
150            combined_tones = self.tones + other.tones
151            unique_tones = []
152            for tone in combined_tones:
153                if tone not in unique_tones:
154                    unique_tones.append(tone)
155            return Chord(Tone(unique_tones[0]), *[Tone(t) for t in unique_tones[1:]])
156 
157        else:
158            raise TypeError("Invalid operation") 
159 
160    def __sub__(self, tone):
161        if tone.name not in self.tones:
162            raise TypeError(f"Cannot remove tone {tone} from chord {self}")
163        
164        new_tones = [t for t in self.tones if t != tone.name]
165        if len(new_tones) < 2:
166            raise TypeError("Cannot have a chord made of only 1 unique tone")
167 
168        return Chord(self.base_tone, *[Tone(t) for t in new_tones])
169 
170    def transposed(self, interval):
171        """Transposes the chord by the given interval."""
172 
173        if not isinstance(interval, Interval):
174            raise TypeError("Expected an Interval object for transposition")
175 
176        transposed_tones = []
177        for tone_name in self.tones:
178            tone = Tone(tone_name)
179            if interval.halftone > 0:
180                transposed_tone = tone + interval
181            else:
182                transposed_tone = tone - Interval(abs(interval.halftone))
183            transposed_tones.append(transposed_tone)
184 
185        return Chord(*transposed_tones)

..................F..FFF...F.........
======================================================================
FAIL: test_add_interval_to_tone_left_side_error (test.TestOperations.test_add_interval_to_tone_left_side_error)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 209, in test_add_interval_to_tone_left_side_error
with self.assertRaises(TypeError) as err:
AssertionError: TypeError not raised

======================================================================
FAIL: test_add_tone_to_chord (test.TestOperations.test_add_tone_to_chord)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 254, in test_add_tone_to_chord
self.assertEqual(str(result_chord), "F-A-C-D")
AssertionError: 'C-D-F-A' != 'F-A-C-D'
- C-D-F-A
+ F-A-C-D

======================================================================
FAIL: test_add_tone_to_chord_existing_tone (test.TestOperations.test_add_tone_to_chord_existing_tone)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 270, in test_add_tone_to_chord_existing_tone
self.assertEqual(str(result_chord), "F-G#-C")
AssertionError: 'C-F-G#' != 'F-G#-C'
- C-F-G#
+ F-G#-C

======================================================================
FAIL: test_add_tone_to_chord_order (test.TestOperations.test_add_tone_to_chord_order)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 261, in test_add_tone_to_chord_order
self.assertEqual(str(result_chord), "F-G-A-C")
AssertionError: 'C-F-G-A' != 'F-G-A-C'
- C-F-G-A
? --
+ F-G-A-C
? ++

======================================================================
FAIL: test_subtract_interval_from_tone_left_side_error (test.TestOperations.test_subtract_interval_from_tone_left_side_error)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 235, in test_subtract_interval_from_tone_left_side_error
self.assertEqual(str(err.exception), INVALID_OPERATION)
AssertionError: "unsupported operand type(s) for -: 'Interval' and 'Tone'" != 'Invalid operation'
- unsupported operand type(s) for -: 'Interval' and 'Tone'
+ Invalid operation

----------------------------------------------------------------------
Ran 37 tests in 0.002s

FAILED (failures=5)

Дискусия
История
Това решение има само една версия.