# Problem Description
This is a lock rotation problem where:
- We have a circular lock with 5 positions (0, a, b, c, d) arranged in a circle
- The lock starts at position '0' (initial position)
- We need to calculate the total rotation angle needed to input a given password
- The lock can only rotate in one direction (clockwise based on the image)
- We need to reach each character in the password sequence in order
- The goal is to calculate the total cumulative angle of rotation

# Visual Facts
1. Layout Facts:
   - The lock has 5 character positions: 0, a, b, c, d
   - There's an initial position marked as '0'
   - The characters are arranged in a circular pattern
   - A pointer indicates the current position
   - The rotation direction is clockwise (shown by dotted arrows)

2. Position Facts:
   - Initial position is '0'
   - From '0', character positions are:
     * 'a' is at 315° clockwise
     * 'b' is at 225° clockwise
     * 'c' is at 135° clockwise
     * 'd' is at 45° clockwise

3. Example Movement Facts:
   - Shows movement from '' → 'a' → 'ad'
   - First rotation: 315° to reach 'a'
   - Second rotation: 90° from 'a' to 'd'

# Visual Patterns
1. Rotation Patterns:
   - The lock only rotates clockwise
   - To move between any two positions, take the shortest clockwise angle
   - Total angle is cumulative sum of all rotations

2. Movement Rules:
   - When moving between characters, calculate the clockwise angle needed
   - For consecutive characters, use relative angle between positions


# Solution
def solution(passwd: str) -> int:
    """
    Calculate the total angle needed to rotate the lock to unlock it. The lock can only rotate in one direction.

    Args:
        passwd (str): A string representing the passwd to unlock the lock, consisting of a sequence of
                        characters 'a', 'b', 'c', and 'd'. The passwd does not contain repeated adjacent characters.

    Returns:
        int: The total angle needed to spin the lock to unlock it, following the given passwd.
    """
    order = ['d', 'c', 'b', 'a']
    angle_per_step = 90  # 360 degrees divided by 4 positions
    original_angle = [45, 45+90, 45+180, 45+270]
    
    # Initialize total angle
    total_angle = original_angle[order.index(passwd[0])]
    
    # Start from the first character in the passwd
    for i in range(1, len(passwd)):
        # Find the current and previous positions
        current_position = order.index(passwd[i])
        previous_position = order.index(passwd[i-1])
        
        # Calculate the angle difference in clockwise direction
        steps = (current_position - previous_position) % 4
        total_angle += steps * angle_per_step
    
    return total_angle


# Test Script
def test():
    test_cases = [
        ('d', 45),
        ('b', 225),
        ('cb', 225),
        ('ad', 405),
        ('ac', 495),
        ('aca', 675),
        ('adba', 675),
        ('abcd', 1125),
        ('abcabcd', 1845),
        ('acbdacbd', 1485)
    ]

    for i, (passwd, expected_output) in enumerate(test_cases):
        try:
            output = solution(passwd)
        except Exception as e:
            error_message = (
                f"An exception occurred while running the test case:\n"
                f"`passwd`: {passwd}\n\n"
                f"Exception: '{str(e)}'"
            )
            assert False, error_message
            
        if output != expected_output:
            error_message = (
                f"The following Test Case failed:\n"
                f"`passwd`: {passwd}\n\n"
                f"Actual Output: {output}\n"
                f"Expected Output: {expected_output}"
            )
            assert False, error_message

test()