# Problem Description
This is a circular lock problem where:
- We have a circular lock with 4 positions (a, b, c, d) and an initial position '0'
- Starting from position '0', we need to input a given password by rotating clockwise
- The goal is to calculate the total angle needed to rotate through all characters in the password
- The lock only rotates clockwise
- The password consists of characters a, b, c, d without adjacent repeating characters

# Visual Facts
1. Layout Facts:
   - The lock has 5 positions: initial '0' and characters a, b, c, d
   - The four characters are arranged in a circle at equal intervals
   - A pointer indicates current position
   - Rotation direction is clockwise (shown by dashed arrows)
   - Three states are shown: initial, after 315°, and after additional 90°

2. Position Facts:
   - From initial '0' position:
     * 'b' is at 45° clockwise
     * 'a' is at 135° clockwise
     * 'd' is at 225° clockwise
     * 'c' is at 315° clockwise

3. Example Sequence Facts:
   - Shows generation of password 'cb'
   - First rotation: 315° to reach 'c'
   - Second rotation: 90° to reach 'b'
   - Password boxes show progression: '' → 'c' → 'cb'

# Visual Patterns
1. Rotation Rules:
   - Lock rotates only clockwise
   - Each character is 90° apart from adjacent characters
   - Total angle is the sum of all rotations needed to reach each character in sequence

2. Movement Patterns:
   - To move between positions, calculate clockwise angle
   - When moving between consecutive password characters, use relative angle (90°, 180°, or 270°)
   - Initial movement is measured from '0' position to first character
   - To get from one character to the next, determine shortest clockwise angle


# 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 = ['b', 'a', 'd', 'c']
    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 = [
        ('b', 45),
        ('d', 225),
        ('ad', 225),
        ('cb', 405),
        ('ca', 495),
        ('cac', 675),
        ('cbdc', 675),
        ('cdab', 1125),
        ('cdacdab', 1845),
        ('cadbcadb', 1485)
    ]
    for passwd, expected_output in 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()