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

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

8 точки общо

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

  1CHROMATIC_SCALE = {
  2    "C": 0,
  3    "C#": 1,
  4    "D": 2,
  5    "D#": 3,
  6    "E": 4,
  7    "F": 5,
  8    "F#": 6,
  9    "G": 7,
 10    "G#": 8,
 11    "A": 9,
 12    "A#": 10,
 13    "B": 11,
 14}
 15
 16
 17INTERVALS = {
 18    "0": "unison",
 19    "1": "minor 2nd",
 20    "2": "major 2nd",
 21    "3": "minor 3rd",
 22    "4": "major 3rd",
 23    "5": "perfect 4th",
 24    "6": "diminished 5th",
 25    "7": "perfect 5th",
 26    "8": "minor 6th",
 27    "9": "major 6th",
 28    "10": "minor 7th",
 29    "11": "major 7th",
 30}
 31
 32
 33class Tone:
 34    def __init__(self, name):
 35        self.name = name
 36
 37    def __str__(self):
 38        return self.name
 39
 40    def __add__(self, other):
 41        if isinstance(other, Tone):
 42            return Chord(self, other)
 43        elif isinstance(other, Interval):
 44            new_index = (CHROMATIC_SCALE[self.name] + other.interval) % 12
 45        key = next((k for k, v in CHROMATIC_SCALE.items() if v == new_index), None)
 46        new_tone = Tone(key)
 47        return new_tone
 48
 49    def __sub__(self, other):
 50        if isinstance(other, Tone):
 51            interval = Interval(
 52                abs(CHROMATIC_SCALE[self.name] - CHROMATIC_SCALE[other.name]))
 53            return interval
 54        elif isinstance(other, Interval):
 55            new_index = abs(CHROMATIC_SCALE[self.name] + 12 - other.interval) % 12
 56            key = next((k for k, v in CHROMATIC_SCALE.items() if v == new_index), None)
 57            new_tone = Tone(key)
 58            return new_tone
 59
 60    # Извиква се ако __add__ не успее да извърши операцията +
 61    def __radd__(self, other):
 62        raise TypeError("Invalid operation")
 63
 64    # Извиква се ако __sub__ не успее да извърши операцията -
 65    def __rsub__(self, other):
 66        raise TypeError("Invalid operation")
 67
 68
 69class Interval:
 70    def __init__(self, interval):
 71        self.interval = interval % 12
 72
 73    def __str__(self):
 74        return INTERVALS[str(self.interval)]
 75
 76    def __add__(self, other):
 77        new_interval = Interval(self.interval + other.interval)
 78        return new_interval
 79
 80    # Предефинира унарния оператор -
 81    def __neg__(self):
 82        return Interval(-self.interval)
 83
 84
 85class Chord:
 86    def __init__(self, *args):
 87        self.unique_tones = []
 88        for item in args:
 89            if self.__tone_is_unique__(item):
 90                self.unique_tones.append(item)
 91        if len(self.unique_tones) <= 1:
 92            raise TypeError("Cannot have a chord made of only 1 unique tone")
 93        self.unique_tones = self.sort_relative_to_root(self.unique_tones[0])
 94
 95    def __tone_is_unique__(self, tone):
 96        # Проверка дали тонът вече съществува в уникалните тонове
 97        for item in self.unique_tones:
 98            if str(item) is str(tone):
 99                return False
100        return True
101
102    def sort_relative_to_root(self, root):
103        # Получаване на индекса на основния тон (root) в хроматичната скала
104        root_index = CHROMATIC_SCALE[str(root)]
105
106        # Подреждане на тоновете спрямо root-а
107        sorted_tones = sorted(self.unique_tones, key=lambda tone: (CHROMATIC_SCALE[str(tone)] - 
108                                                                   CHROMATIC_SCALE[str(root)])% 12,)
109
110        # Връщане на подредените тонове като нов списък
111        return sorted_tones
112
113    def __str__(self):
114        return "-".join(str(tone) for tone in self.unique_tones)
115
116    def __add__(self, other):
117        # Ако other е тон
118        if isinstance(other, Tone):
119            # Добавяме тона към акорда
120            if self.__tone_is_unique__(other):
121                combined_tones = self.unique_tones + [other]
122                new_chord = Chord(*combined_tones)
123                new_chord.unique_tones = new_chord.sort_relative_to_root(self.unique_tones[0])
124                return new_chord
125        # Ако other е акорд
126        elif isinstance(other, Chord):
127            # Обединяване на уникалните тонове от двата акорда
128            combined_tones = self.unique_tones + [
129                tone for tone in other.unique_tones if self.__tone_is_unique__(tone)]
130            new_chord = Chord(*combined_tones)
131            new_chord.unique_tones = new_chord.sort_relative_to_root(self.unique_tones[0])
132            return new_chord
133
134    def __sub__(self, tone):
135        if str(tone) in str(self):
136            # Създаваме нов списък без посочения тон
137            new_tones = [t for t in self.unique_tones if str(t) != str(tone)]
138
139            # Проверяваме, че новият акорд има поне два уникални тона
140            if len(new_tones) >= 2:
141                return Chord(*new_tones)
142            else:
143                # Ако след премахването има по-малко от два тона, хвърляме грешка
144                raise TypeError("Cannot have a chord made of only 1 unique tone")
145        else:
146            raise TypeError(f"Cannot remove tone {str(tone)} from chord {str(self)}")
147
148    # На всеки от тоновете добавяме стойността на интервала
149    def transposed(self, interval):
150        transposed_tones = [
151            tone + interval if interval.interval >= 0 else tone - abs(interval) for tone in self.unique_tones]
152        new_chord = Chord(*transposed_tones)
153        return new_chord
154
155    def is_minor(self):
156        root = self.unique_tones[0]
157        for item in self.unique_tones:
158            interval = abs(CHROMATIC_SCALE[str(root)] - CHROMATIC_SCALE[str(item)])
159            if INTERVALS[str(interval)] is "minor 3rd":
160                return True
161        return False
162
163    def is_major(self):
164        root = self.unique_tones[0]
165        for item in self.unique_tones:
166            interval = abs(CHROMATIC_SCALE[str(root)] - CHROMATIC_SCALE[str(item)])
167            if INTERVALS[str(interval)] is "major 3rd":
168                return True
169        return False
170
171    def is_power_chord(self):
172        return not self.is_major() and not self.is_minor()

/tmp/solution.py:159: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
if INTERVALS[str(interval)] is "minor 3rd":
/tmp/solution.py:167: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
if INTERVALS[str(interval)] is "major 3rd":
....FFF...........E...E............F.
======================================================================
ERROR: 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 210, in test_add_interval_to_tone_left_side_error
Interval(2) + g
~~~~~~~~~~~~^~~
File "/tmp/solution.py", line 77, in __add__
new_interval = Interval(self.interval + other.interval)
^^^^^^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'interval'

======================================================================
ERROR: 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 267, in test_add_tone_to_chord_existing_tone
result_chord = result_chord + Tone("G#")
~~~~~~~~~~~~~^~~~~~~~~~~~
File "/tmp/solution.py", line 62, in __radd__
raise TypeError("Invalid operation")
TypeError: Invalid operation

======================================================================
FAIL: test_is_major (test.TestBasicChordFunctionality.test_is_major)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 107, in test_is_major
self.assertTrue(a_major_chord.is_major())
AssertionError: False is not true

======================================================================
FAIL: test_is_minor (test.TestBasicChordFunctionality.test_is_minor)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 90, in test_is_minor
self.assertTrue(a_minor_chord.is_minor())
AssertionError: False is not true

======================================================================
FAIL: test_is_power_chord (test.TestBasicChordFunctionality.test_is_power_chord)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 116, in test_is_power_chord
self.assertFalse(a_minor_chord.is_power_chord())
AssertionError: True is not false

======================================================================
FAIL: test_tone_subtraction_inverse (test.TestOperations.test_tone_subtraction_inverse)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 178, in test_tone_subtraction_inverse
self.assertEqual(str(perfect_4th), "perfect 4th")
AssertionError: 'perfect 5th' != 'perfect 4th'
- perfect 5th
? ^
+ perfect 4th
? ^

----------------------------------------------------------------------
Ran 37 tests in 0.003s

FAILED (failures=4, errors=2)

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