import re

# Define patterns using regular expressions
patterns = {
    'five': re.compile('11111'),
    'block_five': re.compile('211111|111112'),
    'four': re.compile('011110'),
    'block_four': re.compile('10111|11011|11101|211110|211101|211011|210111|011112|101112|110112|111012'),
    'three': re.compile('011100|011010|010110|001110'),
    'block_three': re.compile('211100|211010|210110|001112|010112|011012'),
    'two': re.compile('001100|011000|000110|010100|001010'),
}

# Define shapes with associated scores
shapes = {
    'FIVE': 5,
    'BLOCK_FIVE': 50,
    'FOUR': 4,
    'FOUR_FOUR': 44,  # Double four
    'FOUR_THREE': 43,  # Four with an open three
    'THREE_THREE': 33,  # Double three
    'BLOCK_FOUR': 40,
    'THREE': 3,
    'BLOCK_THREE': 30,
    'TWO_TWO': 22,  # Double two
    'TWO': 2,
    'NONE': 0
}

# Initialize a performance record
performance = {
    'five': 0,
    'block_five': 0,
    'four': 0,
    'block_four': 0,
    'three': 0,
    'block_three': 0,
    'two': 0,
    'none': 0,
    'total': 0
}

# Function to detect shapes on the board
def get_shape(board, x, y, offset_x, offset_y, role):
    """
    Detect shape at a given board position.
    :param board: The game board.
    :param x: X-coordinate.
    :param y: Y-coordinate.
    :param offset_x: X-direction offset for scanning.
    :param offset_y: Y-direction offset for scanning.
    :param role: Current player's role.
    :return: A tuple of shape, self count, opponent count, and empty count.
    """
    opponent = -role
    empty_count = 0
    self_count = 1
    opponent_count = 0
    shape = shapes['NONE']

    # Skip empty nodes
    if (
        board[x + offset_x + 1][y + offset_y + 1] == 0
        and board[x - offset_x + 1][y - offset_y + 1] == 0
        and board[x + 2 * offset_x + 1][y + 2 * offset_y + 1] == 0
        and board[x - 2 * offset_x + 1][y - 2 * offset_y + 1] == 0
    ):
        return [0, self_count, opponent_count, empty_count]
    # Check for 'two' pattern
    for i in range(-3, 4):
        if i == 0:
            continue
        nx, ny = x + i * offset_x, y + i * offset_y
        current_role = board.get((nx, ny))
        if current_role is None:
            continue
        if current_role == 2:
            opponent_count += 1
        elif current_role == role:
            self_count += 1
        elif current_role == 0:
            empty_count += 1

    if self_count == 2:
        if opponent_count == 0:
            return shapes['TWO'], self_count, opponent_count, empty_count
        else:
            return shapes['NONE'], self_count, opponent_count, empty_count

    # Reset counts and prepare string for pattern matching
    empty_count, self_count, opponent_count = 0, 1, 0
    result_string = '1'

    # Build result string for pattern matching
    for i in range(1, 6):
        nx = x + i * offset_x + 1
        ny = y + i * offset_y + 1
        currentRole = board[nx][ny]
        if currentRole == 2:
            result_string += '2'
        elif currentRole == 0:
            result_string += '0'
        else:
            result_string += '1' if currentRole == role else '2'
        if currentRole == 2 or currentRole == opponent:
            opponent_count += 1
            break
        if currentRole == 0:
            empty_count += 1
        if currentRole == role:
            self_count += 1
    
    for i in range(1, 6):
        nx = x - i * offset_x + 1
        ny = y - i * offset_y + 1
        currentRole = board[nx][ny]
        if currentRole == 2:
            result_string = '2' + result_string
        elif currentRole == 0:
            result_string = '0' + result_string
        else:
            result_string = '1' if currentRole == role else '2' + result_string
        if currentRole == 2 or currentRole == opponent:
            opponent_count += 1
            break
        if currentRole == 0:
            empty_count += 1
        if currentRole == role:
            self_count += 1

    # Check patterns and update performance
    for pattern_key, shape_key in [('five', 'FIVE'), ('four', 'FOUR'), ('block_four', 'BLOCK_FOUR'),
                                   ('three', 'THREE'), ('block_three', 'BLOCK_THREE'), ('two', 'TWO')]:
        if patterns[pattern_key].search(result_string):
            shape = shapes[shape_key]
            performance[pattern_key] += 1
            performance['total'] += 1
            break
    ## 尽量减少多余字符串生成
    if self_count <= 1 or len(result_string) < 5:
        return shape, self_count, opponent_count, empty_count

    return shape, self_count, opponent_count, empty_count

def count_shape(board, x, y, offset_x, offset_y, role):
    opponent = - role

    inner_empty_count = 0  # Number of empty positions inside the player's stones
    temp_empty_count = 0
    self_count = 0  # Number of the player's stones in the shape
    total_length = 0

    side_empty_count = 0  # Number of empty positions on the side of the shape
    no_empty_self_count = 0
    one_empty_self_count = 0

    # Right direction
    for i in range(1, 6):
        nx = x + i * offset_x + 1
        ny = y + i * offset_y + 1
        current_role = board[nx][ny]
        if current_role == 2 or current_role == opponent:
            break
        if current_role == role:
            self_count += 1
            side_empty_count = 0
            if temp_empty_count:
                inner_empty_count += temp_empty_count
                temp_empty_count = 0
            if inner_empty_count == 0:
                no_empty_self_count += 1
                one_empty_self_count += 1
            elif inner_empty_count == 1:
                one_empty_self_count += 1
        total_length += 1
        if current_role == 0:
            temp_empty_count += 1
            side_empty_count += 1
        if side_empty_count >= 2:
            break

    if not inner_empty_count:
        one_empty_self_count = 0

    return {
        'self_count': self_count,
        'total_length': total_length,
        'no_empty_self_count': no_empty_self_count,
        'one_empty_self_count': one_empty_self_count,
        'inner_empty_count': inner_empty_count,
        'side_empty_count': side_empty_count
    }

# Fast shape detection function
def get_shape_fast(board, x, y, offsetX, offsetY, role):
    if (
        board[x + offsetX + 1][y + offsetY + 1] == 0
        and board[x - offsetX + 1][y - offsetY + 1] == 0
        and board[x + 2 * offsetX + 1][y + 2 * offsetY + 1] == 0
        and board[x - 2 * offsetX + 1][y - 2 * offsetY + 1] == 0
    ):
        return [shapes['NONE'], 1]

    selfCount = 1
    totalLength = 1
    shape = shapes['NONE']

    leftEmpty = 0
    rightEmpty = 0
    noEmptySelfCount = 1
    OneEmptySelfCount = 1

    left = count_shape(board, x, y, -offsetX, -offsetY, role)
    right = count_shape(board, x, y, offsetX, offsetY, role)

    selfCount = left['self_count'] + right['self_count'] + 1
    totalLength = left['total_length'] + right['total_length'] + 1
    noEmptySelfCount = left['no_empty_self_count'] + right['no_empty_self_count'] + 1
    OneEmptySelfCount = max(
        left['one_empty_self_count'] + right['no_empty_self_count'],
        left['no_empty_self_count'] + right['one_empty_self_count'],
    ) + 1
    rightEmpty = right['side_empty_count']
    leftEmpty = left['side_empty_count']

    if totalLength < 5:
        return [shape, selfCount]

    if noEmptySelfCount >= 5:
        if rightEmpty > 0 and leftEmpty > 0:
            return [shapes['FIVE'], selfCount]
        else:
            return [shapes['BLOCK_FIVE'], selfCount]

    if noEmptySelfCount == 4:
        if (
            (rightEmpty >= 1 or right['one_empty_self_count'] > right['no_empty_self_count'])
            and (leftEmpty >= 1 or left['one_empty_self_count'] > left['no_empty_self_count'])
        ):
            return [shapes['FOUR'], selfCount]
        elif not (rightEmpty == 0 and leftEmpty == 0):
            return [shapes['BLOCK_FOUR'], selfCount]

    if OneEmptySelfCount == 4:
        return [shapes['BLOCK_FOUR'], selfCount]

    if noEmptySelfCount == 3:
        if (rightEmpty >= 2 and leftEmpty >= 1) or (rightEmpty >= 1 and leftEmpty >= 2):
            return [shapes['THREE'], selfCount]
        else:
            return [shapes['BLOCK_THREE'], selfCount]

    if OneEmptySelfCount == 3:
        if rightEmpty >= 1 and leftEmpty >= 1:
            return [shapes['THREE'], selfCount]
        else:
            return [shapes['BLOCK_THREE'], selfCount]

    if (noEmptySelfCount == 2 or OneEmptySelfCount == 2) and totalLength > 5:
        shape = shapes['TWO']

    return [shape, selfCount]

# Helper functions to check for specific shapes
def is_five(shape):
    # Checked
    return shape in [shapes['FIVE'], shapes['BLOCK_FIVE']]

def is_four(shape):
    # Checked
    return shape in [shapes['FOUR'], shapes['BLOCK_FOUR']]

# Function to get all shapes at a specific point
def get_all_shapes_of_point(shape_cache, x, y, role = None):
    # Checked
    roles = [role] if role else [1, -1]
    result = []
    for r in roles:
        for d in range(4):
            shape = shape_cache[r][d][x][y]
            if shape > 0:
                result.append(shape)
    return result


if __name__ == "__main__":
    pass