# Problem Description
This is a pattern generation problem where we need to create a square matrix of characters ('+' and '-') based on an input parameter N. The matrix size is 3N × 3N, and the characters are arranged in 3×3 sub-squares following a specific pattern. The goal is to return this matrix as a list of strings, where each string represents one row of the matrix.

# Visual Facts
1. Each matrix has dimensions of 3N × 3N:
   - N=1: 3×3 matrix
   - N=2: 6×6 matrix
   - N=3: 9×9 matrix

2. Only two symbols are used: '+' and '-'

3. Symbols are always grouped in 3×3 blocks (sub-squares)

4. For N=1:
   - All cells contain '+'

5. For N=2:
   - Upper-left and lower-right 3×3 blocks contain '+'
   - Upper-right and lower-left 3×3 blocks contain '-'

6. For N=3:
   - Corner blocks (3×3) contain '+'
   - Center block contains '+'
   - Remaining blocks contain '-'

# Visual Patterns
1. Block Pattern:
   - The matrix is divided into N×N blocks, where each block is 3×3
   - Each 3×3 block contains identical symbols (all '+' or all '-')

2. Alternation Pattern:
   - Adjacent blocks always contain opposite symbols
   - If a block contains '+', its neighboring blocks (horizontally and vertically) contain '-'

3. Size Rule:
   - Final matrix size = 3N × 3N
   - Number of 3×3 blocks = N × N

4. Block Assignment Rule:
   - The upper-left conner block always contains '+'


# Solution
from typing import List

def solution(N: int) -> List[str]:
    """
    Generates a character matrix of size N x N based on the pattern shown in the image.

    Parameters:
    N (int): The size of the matrix (both the number of rows and columns).

    Returns:
    List[str]: The generated character matrix, where each row is a string.
    """
    matrix = []
    for i in range(N * 3):
        row = []
        for j in range(N * 3):
            if (i // 3 + j // 3) % 2 == 0:
                row.append('+')
            else:
                row.append('-')
        matrix.append(''.join(row))
    return matrix


# Test Script
def test():
    test_cases = [
        (1, ['+++', 
             '+++', 
             '+++']),
        (2, ['+++---', 
             '+++---', 
             '+++---', 
             '---+++', 
             '---+++', 
             '---+++']),
        (3, ['+++---+++', 
             '+++---+++', 
             '+++---+++', 
             '---+++---', 
             '---+++---', 
             '---+++---', 
             '+++---+++', 
             '+++---+++', 
             '+++---+++']),
        (4, ['+++---+++---', 
             '+++---+++---', 
             '+++---+++---', 
             '---+++---+++', 
             '---+++---+++', 
             '---+++---+++', 
             '+++---+++---', 
             '+++---+++---', 
             '+++---+++---', 
             '---+++---+++', 
             '---+++---+++', 
             '---+++---+++']),
        (5, ['+++---+++---+++', 
             '+++---+++---+++', 
             '+++---+++---+++', 
             '---+++---+++---', 
             '---+++---+++---', 
             '---+++---+++---', 
             '+++---+++---+++', 
             '+++---+++---+++', 
             '+++---+++---+++', 
             '---+++---+++---', 
             '---+++---+++---', 
             '---+++---+++---', 
             '+++---+++---+++', 
             '+++---+++---+++', 
             '+++---+++---+++'])
    ]
    for N, expected_output in test_cases:
        try:
            output = solution(N)
        except Exception as e:
            error_message = (
                f"An exception occurred while running the test case:\n"
                f"`N`: {N}\n\n"
                f"Exception: '{str(e)}'"
            )
            assert False, error_message

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

test()