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

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

5 точки общо

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

  1class InvalidToneError(Exception):
  2    def __init__(self, tone):
  3        self._message = f"Invalid tone: '{tone}'."
  4        super().__init__(self._message)
  5
  6
  7class InvalidIntervalOperationError(Exception):
  8    def __init__(self, message="Invalid interval operation."):
  9        self._message = message
 10        super().__init__(self._message)
 11
 12
 13class Tone:
 14    TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
 15
 16    def __init__(self, name):
 17        if name not in self.TONES:
 18            raise InvalidToneError(name)
 19        self.name = name
 20
 21    def __repr__(self):
 22        return self.name
 23
 24    def __str__(self):
 25        return self.name
 26
 27    def __eq__(self, other):
 28        if isinstance(other, Tone):
 29            return self.name == other.name
 30        return False
 31
 32    def __hash__(self):
 33        return hash(self.name)
 34
 35    def __add__(self, other):
 36        if isinstance(other, Tone):
 37            return Chord(self, other)
 38        elif isinstance(other, Interval):
 39            return Tone(self.calculate_new_tone(self, other))
 40        else:
 41            raise TypeError("Invalid operation")
 42
 43    def __sub__(self, other):
 44        if isinstance(other, Interval):
 45            inverted_interval = Interval(-other.semitones)
 46            return Tone(self.calculate_new_tone(self, inverted_interval))
 47        elif isinstance(other, Tone):
 48            return self.calculate_interval(self, other)
 49        else:
 50            raise TypeError("Invalid operation")
 51
 52    @staticmethod
 53    def calculate_new_tone(tone, interval):
 54        index = Tone.TONES.index(tone.name)
 55        new_index = (index + interval.semitones) % len(Tone.TONES)
 56        return Tone.TONES[new_index]
 57
 58    @staticmethod
 59    def calculate_interval(tone1, tone2):
 60        index1 = Tone.TONES.index(tone1.name)
 61        index2 = Tone.TONES.index(tone2.name)
 62        semitones = (index2 - index1) % len(Tone.TONES)
 63        return Interval(semitones)
 64
 65
 66class Interval:
 67    _interval_names = {
 68        0: "unison",
 69        1: "minor 2nd",
 70        2: "major 2nd",
 71        3: "minor 3rd",
 72        4: "major 3rd",
 73        5: "perfect 4th",
 74        6: "diminished 5th",
 75        7: "perfect 5th",
 76        8: "minor 6th",
 77        9: "major 6th",
 78        10: "minor 7th",
 79        11: "major 7th"
 80    }
 81
 82    def __init__(self, semitones):
 83        if not isinstance(semitones, int):
 84            raise InvalidIntervalOperationError("Interval must be an integer.")
 85        self.semitones = semitones % 12
 86
 87    def __str__(self):
 88        return self._interval_names.get(self.semitones, "unknown")
 89
 90    def __add__(self, other):
 91        if isinstance(other, Interval):
 92            return Interval(self.semitones + other.semitones)
 93        raise InvalidIntervalOperationError("Can only add another Interval.")
 94
 95    def __neg__(self):
 96        return Interval(-self.semitones)
 97
 98
 99class Chord:
100    def __init__(self, *tones):
101        unique_tones = set(tones)
102        if len(unique_tones) < 2:
103            raise ValueError("Chord must contain at least two unique tones.")
104        self.tones = unique_tones
105        self.root = min(unique_tones, key=lambda tone: Tone.TONES.index(tone.name))
106
107    def __str__(self):
108        ordered_tones = sorted(self.tones, key=lambda tone: Tone.TONES.index(tone.name))
109        return "-".join(tone.name for tone in ordered_tones)
110
111    def __add__(self, other):
112        if isinstance(other, Tone):
113            return Chord(*(self.tones | {other}))
114        elif isinstance(other, Chord):
115            return Chord(*(self.tones | other.tones))
116        raise TypeError("Invalid operation.")
117
118    def __sub__(self, other):
119        if isinstance(other, Tone):
120            if other not in self.tones:
121                raise ValueError(f"Tone {other} not in the chord.")
122            new_tones = self.tones - {other}
123            if len(new_tones) < 2:
124                return None
125            return Chord(*new_tones)
126
127        raise TypeError("Invalid operation.")
128
129    def transposed(self, interval):
130        transposed_tones = [Tone(tone.calculate_new_tone(tone, interval)) for tone in self.tones]
131        return Chord(*transposed_tones)
132
133    def calculate_interval(self, tone1, tone2):
134        return Tone.calculate_interval(tone1, tone2)
135
136    def is_minor(self):
137        return any(self.calculate_interval(self.root, tone).semitones == 3 for tone in self.tones)
138
139    def is_major(self):
140        return any(self.calculate_interval(self.root, tone).semitones == 4 for tone in self.tones)
141
142    def is_power_chord(self):
143        has_minor_third = any(self.calculate_interval(self.root, tone).semitones == 3 for tone in self.tones)
144        has_major_third = any(self.calculate_interval(self.root, tone).semitones == 4 for tone in self.tones)
145        return not (has_minor_third or has_major_third)

EFFFFF...FF.......E..FFF...F...E.EFF.
======================================================================
ERROR: test_chord_not_enough_tones (test.TestBasicChordFunctionality.test_chord_not_enough_tones)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 79, in test_chord_not_enough_tones
Chord(a_sharp)
File "/tmp/solution.py", line 103, in __init__
raise ValueError("Chord must contain at least two unique tones.")
ValueError: Chord must contain at least two unique tones.

======================================================================
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 93, in __add__
raise InvalidIntervalOperationError("Can only add another Interval.")
solution.InvalidIntervalOperationError: Can only add another Interval.

======================================================================
ERROR: test_subtract_tone_from_chord_error (test.TestOperations.test_subtract_tone_from_chord_error)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 292, in test_subtract_tone_from_chord_error
c5_chord - a
~~~~~~~~~^~~
File "/tmp/solution.py", line 121, in __sub__
raise ValueError(f"Tone {other} not in the chord.")
ValueError: Tone A not in the chord.

======================================================================
ERROR: test_tone_addition_same_tone (test.TestOperations.test_tone_addition_same_tone)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 167, in test_tone_addition_same_tone
c + another_c
~~^~~~~~~~~~~
File "/tmp/solution.py", line 37, in __add__
return Chord(self, other)
^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 103, in __init__
raise ValueError("Chord must contain at least two unique tones.")
ValueError: Chord must contain at least two unique tones.

======================================================================
FAIL: test_chord_ordering (test.TestBasicChordFunctionality.test_chord_ordering)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 65, in test_chord_ordering
self.assertEqual(str(f_sixth_ninth_chord), "F-A-C-E")
AssertionError: 'C-E-F-A' != 'F-A-C-E'
- C-E-F-A
+ F-A-C-E

======================================================================
FAIL: test_chord_str (test.TestBasicChordFunctionality.test_chord_str)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 60, in test_chord_str
self.assertEqual(str(a_major_chord), "A-C#-E")
AssertionError: 'C#-E-A' != 'A-C#-E'
- C#-E-A
+ A-C#-E

======================================================================
FAIL: test_chord_tone_repetition (test.TestBasicChordFunctionality.test_chord_tone_repetition)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 74, in test_chord_tone_repetition
self.assertEqual(str(a_minor_chord), "A-C-E")
AssertionError: 'C-E-A' != 'A-C-E'
- C-E-A
+ A-C-E

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

======================================================================
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_transposed_negative_overflow (test.TestBasicChordFunctionality.test_transposed_negative_overflow)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 152, in test_transposed_negative_overflow
self.assertEqual(str(result_chord), "G#-B-D#")
AssertionError: 'D#-G#-B' != 'G#-B-D#'
- D#-G#-B
+ G#-B-D#

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

======================================================================
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

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

======================================================================
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.005s

FAILED (failures=13, errors=4)

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