# Problem Description
This is a path-tracking problem where an object (blue dot) moves through a grid with the following rules:
- The object starts from a given point and moves right (column++) initially
- When it steps on a black block, it changes direction to the next clockwise direction 
- The object continues this pattern until it exits the grid
- We need to count how many black blocks the object encounters before exiting

# Visual Facts
1. Grid Properties:
   - Each example shows a grid with numbered rows and columns (1-indexed)
   - Different grid sizes are shown (4x6, 5x4, 6x6, 7x6)
   - Each grid has a blue dot marking the start position

2. Movement Indicators:
   - Orange arrows show the path of movement
   - Movement alternates between downward and rightward directions
   - Path always starts with downward movement

3. Black Blocks:
   - Black squares represent a turning point
   - Some black blocks are on the path, others are not
   - Black blocks appear in different patterns in each example

4. Starting Points:
   - All examples show the start point marked with a blue dot
   - Starting positions vary (e.g., (1,2), (1,1))

# Visual Patterns
1. Movement Rules:
   - Path always starts with rightward movement from the starting point
   - Direction changes when encounter a black block
   - Direction changes are always in a clockwise direction
      - (right->down, down->left, left->up, up->right)

2. Exit Conditions:
   - The path terminates when reaching any grid boundary

# Solution
from typing import Tuple

def solution(m: int, n: int, start_point: Tuple[int,int], black_blocks: list[Tuple[int, int]]) -> int:     
    """
    Calculates the number of black blocks that can be traversed
    
    Parameters:
        m (int): Number of rows in the matrix
        n (int): Number of columns in the matrix
        start_point (Tuple[int,int]): Starting coordinates (1-indexed)
        black_blocks (list[Tuple[int, int]]): List of coordinates representing blocked positions (1-indexed)

    Returns:
        int: Number of black blocks that can be traversed
    """
    class Movement:
        def __init__(self):
            self.state = 0
            self.directions = [ (0,1), (1,0), (0,-1), (-1,0) ] # right, down, left, up

        def move(self, point):
            x, y = point
            return (x + self.directions[self.state][0], y + self.directions[self.state][1])
        def change_state(self):
            self.state = (self.state+ 1) % 4

    movement = Movement()
    i = start_point[0]-1
    j = start_point[1]-1
    num_blocks = 0
    while i >= 0 and i < m and j >= 0 and j < n:
        if (i+1, j+1) in black_blocks:
            movement.change_state()
            i, j = movement.move((i, j))
            num_blocks += 1
        else:
            i, j = movement.move((i, j))

    return num_blocks





# Test Script
def test():
    test_cases = [
       (4,6, (1,2),[(1,5),(4,5), (3, 2)],2),
       (5,4,(1,1),[(2,1),(3,3),(1,4)],1),
       (6,4,(1,1),[(1,4), (2,1),(3,3)],1),
       (6,6,(1,1),[(1,5),(2,2),(2,4),(2,6),(5,2),(5,5)],5),
       (7,6,(1,1),[(1,6),(2,4),(5,4),(7,3),(7,6)],3)
    ]

    for m, n, start_point, black_blocks, expected_output in test_cases:
        try:
            output = solution(m, n, start_point, black_blocks)
        except Exception as e:
            error_message = (
                f"An exception occurred while running the test case:\n"
                f"`m`: {m}\n"
                f"`n`: {n}\n"
                f"`start_point`: {start_point}\n"
                f"`black_blocks`: {black_blocks}\n\n"
                f"Exception: '{str(e)}'"
            )
            assert False, error_message

        if output != expected_output:
            error_message = (
                f"The following Test Case failed:\n"
                f"`m`: {m}\n"
                f"`n`: {n}\n" 
                f"`start_point`: {start_point}\n"
                f"`black_blocks`: {black_blocks}\n\n"
                f"Actual Output: {output}\n"
                f"Expected Output: {expected_output}"
            )
            assert False, error_message

test()