# Problem Description
This is a pipe connection validation problem where we need to verify if a given sequence of coordinates forms a valid path for water to flow by accessing connected pipe segments from left to right. The path must follow proper pipe connections and reach the rightmost column without any breaks or invalid connections.

# Visual Facts
1. Pipe Types and Their Symbols:
   - Horizontal pipe: "-"
   - down right curved pipe: "f"
   - down left curved: "t"
   - up right curved pipe: "l"
   - up left curved pipe: "j"

2. Grid Representation:
   - Uses a 2D array notation: `[[row1], [row2]]`
   - Examples show 2×5 grids
   - Green lines show the attempted paths
   - Red X marks show where paths get blocked

3. Example Outcomes:
   - Example 1: Successfully connects left-to-right
   - Example 2: Fails at mid-path due to misconnection
   - Example 3: Fails at the end due to improper terminal connection

4. Connection Documentation:
   - The top section shows individual pipe types
   - Middle section demonstrates valid pipe combinations
   - Each pipe segment is annotated with its symbol

# Visual Patterns
1. Connection Rules:
   - Each pipe type has specific entryfexit points:
     * "-": This block is accessible to left or right block.("-" at grid[i] connects grid[i-1], grid[i], and grid[i+1])
     * "f": This block is accessible to down or right block.("f" at grid[i][j] connects grid[i+1][j], grid[i][j], and grid[i][j+1])
     * "t": This block is accessible to down or left block.("t" at grid[i][j] connects grid[i][j-1], grid[i][j], and grid[i+1][j])
     * "l": This block is accessible to up or right block.("l" at grid[i][j] connects grid[i-1][j], grid[i][j], and grid[i][j+1])
     * "j": This block is accessible to up or left block.("j" at grid[i][j] connects grid[i-1][j], grid[i][j], and grid[i][j-1])


3. Path Validation Rules:
   - Path must start in leftmost column
   - Path must end in rightmost column
   - Each step must follow valid pipe connections
   - No breaks or misalignments allowed
   - Path coordinates must be sequential and adjacent

4. Failure Conditions:
   - Dead ends before reaching right edge (Example 2)
   - Mismatched connections between segments
   - Invalid terminal connections (Example 3)
   - Non-adjacent coordinates in path sequence


# Solution
from typing import List, Tuple

def solution(grid: List[List[str]], path: List[Tuple[int, int]]) -> bool:
    """
    Determines if the given path forms a valid connected route from the leftmost column to the rightmost column.

    Args:
        grid: A 2D list representing the grid, where each cell contains a string representing a segment ("-", "t", "f", "l", "j").
        path: A list of (row, col) coordinate tuples representing sequential steps in the path. Coordinates are 0-indexed integers.

    Returns:
        bool: True if the path forms a valid connected route from left to right, False otherwise.       
    """
    # Define the valid connections for each pipe type
    connections = {
        "-": {"left": (0, -1), "right": (0, 1)},
        "f": {"down": (1, 0), "right": (0, 1)},
        "t": {"left": (0, -1), "down": (1, 0)},
        "l": {"up": (-1, 0), "right": (0, 1)},
        "j": {"up": (-1, 0), "left": (0, -1)}
    }

    # Define the opposite directions for connection checking
    opposite_directions = {
        "left": "right",
        "right": "left",
        "up": "down",
        "down": "up"
    }

    # Check if the path starts in the leftmost column and ends in the rightmost column
    if not path:
        return False
    start = path[0]
    end = path[-1]
    if start[1] != 0 or end[1] != len(grid[0]) - 1:
        return False

    # Iterate through the path to check connectivity
    for i in range(len(path) - 1):
        current = path[i]
        next_step = path[i + 1]
        row, col = current
        next_row, next_col = next_step

        # Check if the current and next positions are within the grid
        if (row < 0 or row >= len(grid) or col < 0 or col >= len(grid[0]) or
            next_row < 0 or next_row >= len(grid) or next_col < 0 or next_col >= len(grid[0])):
            return False

        # Get the current and next pipe types
        current_pipe = grid[row][col]
        next_pipe = grid[next_row][next_col]

        # Determine the direction of movement
        delta_row = next_row - row
        delta_col = next_col - col
        direction = None
        if delta_row == -1 and delta_col == 0:
            direction = "up"
        elif delta_row == 1 and delta_col == 0:
            direction = "down"
        elif delta_row == 0 and delta_col == -1:
            direction = "left"
        elif delta_row == 0 and delta_col == 1:
            direction = "right"
        else:
            return False  # Invalid movement

        # Check if the current pipe allows movement in the direction
        if direction not in connections.get(current_pipe, {}):
            return False

        # Check if the next pipe allows movement in the opposite direction
        opposite_direction = opposite_directions.get(direction)
        if opposite_direction not in connections.get(next_pipe, {}):
            return False

    return True

# Test Script
def test():
    test_cases = [
        ([["-", "t", "f"], ["f", "l", "j"]],[(0,0),(0,1),(1,1),(1,2),(0,2)],True),
        ([["f", "t", "-"], ["j", "l", "j"]],[(0,0),(0,1),(1,1),(1,2),(0,2)],False),
        ([["-", "t", "f"], ["f", "l", "j"]],[(0,0),(0,1),(1,1)],False),
        ([["-", "-", "-"]], [(0,0),(0,1),(0,2)],True),
        ([["-", "t", "f"]], [(0,0),(0,1),(0,2)],False),
        ([["-", "t", "f"], ["-", "l", "-"], ["j", "-", "j"]],[(0,0),(0,1),(1,1),(1,2)], True),
        ([["f", "t", "-", "f", "t"], ["j", "l", "-", "j", "j"]],[(0,0),(0,1),(1,1),(1,2)],False),
        ([["-", "t", "t", "f", "-"], ["f", "l", "-", "j", "j"]], [(0,0),(0,1),(1,1),(1,2),(1,3),(0,3),(0,4)],True),
        ([["-", "t", "t", "f", "-"], ["f", "l", "-", "j", "-"]], [(0,0),(0,1),(1,1),(1,2),(1,3),(0,3),(0,4)],True),
        ([[ "-", "t", "t", "f", "-"], ["f", "l", "-", "l", "-"]], [(0,0),(0,1),(1,1),(1,2),(1,3),(0,3),(0,4)],False),
    ]

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

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

test()