A combination lock is a type of padlock in which a sequence of numbers is used to open and close the lock. The sequence may be entered using a set of independently rotating discs with inscribed numerals. The discs directly interact with the locking mechanism, following the principle outlined using the following illustrations.

combinatieslot: schijven
Exploded view of the rotating discs. The notches on the discs correspond to the numerals in the correct combination. In this case, the combination is 9-2-4.
combinatieslot: open
The discs are mounted on one side of the lock, which may in turn be attached to the end of a chain or cable. The other side of the lock, or the other end of the cable, has a pin with several protruding teeth.
combinatieslot: gesloten
When the toothed pin is inserted and the discs are rotated to an incorrect combination, the inner faces of the discs block the pin from being extracted.

Assignment

Write a class CombinationLock which can be used to initiate objects that represent a combination lock having $$d \in \mathbb{N}_0$$ rotating discs. Each disc is inscribed with the integer series 0, 1, …, $$m$$. There is a single correct combination to which the discs must be set in order to open the lock. The objects of the class CombinationLock must at least support the following methods:

Example

>>> lock = CombinationLock((9, 2, 4))
>>> lock
CombinationLock((9, 2, 4), maxvalue=9)
>>> print(lock)
0-0-0
>>> lock.open()
False
>>> lock.rotate(1, 2)
>>> print(lock)
0-2-0
>>> lock.rotate(2, 5)
>>> print(lock)
0-2-5
>>> lock.open()
False
>>> lock.rotate([2, 0], 9)
>>> print(lock)
9-2-4
>>> lock.open()
True

>>> lock = CombinationLock([14, 13, 2, 7, 6], maxvalue=16)
>>> lock
CombinationLock((14, 13, 2, 7, 6), maxvalue=16)
>>> print(lock)
0-0-0-0-0
>>> lock.rotate([0, 2, 4], 6)
>>> print(lock)
6-0-6-0-6
>>> lock.rotate([1, 3, 5], 13)
Traceback (most recent call last):
AssertionError: invalid disc
>>> print(lock)
6-0-6-0-6
>>> lock.rotate([1, 3, 2], 13)
>>> print(lock)
6-13-2-13-6
>>> lock.rotate([0, 3], 8)
>>> print(lock)
14-13-2-4-6
>>> lock.open()
False
>>> lock.rotate(3, 3)
>>> print(lock)
14-13-2-7-6
>>> lock.open()
True

>>> lock = CombinationLock([1, 2, 3, 4, 5], maxvalue=4)
Traceback (most recent call last):
AssertionError: invalid combination

>>> lock = CombinationLock([])
Traceback (most recent call last):
AssertionError: invalid combination