# GESP等级：三级 | GESP Python 三级考点

"""
================================================================================
 练习3 ★★★ — 画板扩展（高级综合应用）
================================================================================

  练习目标：
    在挑战3的多色画板基础上，扩展更多功能：
      1. 颜色混合（set 交集/并集运算模拟）
      2. 图层管理（dict 管理多个 layer）
      3. 区域填充（BFS 泛洪，用 set 记录已访问）
      4. 画板导出/导入（dict → JSON 序列化）

  这是一个开放练习——只提供框架和提示，代码由你完成。
================================================================================
"""
import json

print("=" * 60)
print("练习3 ★★★ — 画板扩展")
print("=" * 60)

# ========== 扩展1：颜色混合 ==========
print("\n1. 颜色混合（set 运算模拟）")

# 用 set 表示一种颜色包含的"原色成分"
RED_SET = {"red"}
GREEN_SET = {"green"}
BLUE_SET = {"blue"}
YELLOW_SET = {"red", "green"}       # 红+绿=黄
CYAN_SET = {"green", "blue"}        # 绿+蓝=青
MAGENTA_SET = {"red", "blue"}       # 红+蓝=品红
WHITE_SET = {"red", "green", "blue"}

print(f"   红色成分：{RED_SET}")
print(f"   绿色成分：{GREEN_SET}")
print(f"   黄色成分：{YELLOW_SET}（红|绿）")
print(f"   黄色 ∩ 蓝色 = {YELLOW_SET & BLUE_SET}  → 无交集，不能混合")

# 混合函数
def mix_colors(c1: set, c2: set) -> set:
    """混合两种颜色（成分集合的并集）"""
    return c1 | c2, c1 & c2  # 并集=混合色，交集=共同成分

mixed, common = mix_colors(RED_SET, GREEN_SET)
print(f"   红 + 绿 → 成分：{mixed}，共同：{common}（即黄色）")

mixed, common = mix_colors(YELLOW_SET, BLUE_SET)
print(f"   黄 + 蓝 → 成分：{mixed}（白色），共同：{common}（绿色成分）")


# ========== 扩展2：图层管理 ==========
print("\n2. 图层管理（dict 管理多个画布层）")

# 每个图层是一个独立的 canvas（dict）
layers = {
    "background": {},
    "shapes": {},
    "details": {},
}
layer_order = ["background", "shapes", "details"]  # 控制渲染顺序

# 在不同图层上绘制
layers["background"][(0, 0)] = "blue"
layers["background"][(1, 0)] = "blue"
layers["shapes"][(0, 0)] = "red"       # 覆盖 background 的 (0,0)
layers["details"][(2, 2)] = "yellow"

# 合并渲染（下层被上层覆盖）
def composite(layers: dict, order: list) -> dict:
    """将多个图层合成为一张画布"""
    result = {}
    for layer_name in order:
        layer = layers.get(layer_name, {})
        result.update(layer)  # 后更新的覆盖先更新的
    return result

combined = composite(layers, layer_order)
print(f"   合并后画布：{combined}")
print(f"   (0,0) 最终颜色：{combined.get((0,0))}  （来自 shapes 层覆盖）")


# ========== 扩展3：泛洪填充（Flood Fill）==========
print("\n3. 泛洪填充（set 记录已访问）")

def flood_fill(canvas: dict, start: tuple, new_color: str,
               width: int, height: int) -> set:
    """
    从 start 位置开始泛洪填充。
    使用 set 记录已访问像素，避免重复。
    """
    if start not in canvas:
        return set()

    target_color = canvas[start]
    if target_color == new_color:
        return set()

    visited = set()
    stack = [start]

    while stack:
        x, y = stack.pop()
        if (x, y) in visited:
            continue
        if not (0 <= x < width and 0 <= y < height):
            continue
        if canvas.get((x, y)) != target_color:
            continue

        canvas[(x, y)] = new_color
        visited.add((x, y))

        # 四邻域入栈
        stack.extend([(x+1, y), (x-1, y), (x, y+1), (x, y-1)])

    return visited


# 测试泛洪填充
test_canvas = {
    (0, 0): "red", (1, 0): "red", (2, 0): "blue",
    (0, 1): "red", (1, 1): "red", (2, 1): "blue",
    (0, 2): "blue",(1, 2): "blue",(2, 2): "blue",
}
print(f"   填充前：")
for y in range(3):
    row = [test_canvas.get((x, y), "·") for x in range(3)]
    print(f"     {row}")

filled = flood_fill(test_canvas, (0, 0), "green", 3, 3)
print(f"   填充像素数：{len(filled)}")
print(f"   填充后：")
for y in range(3):
    row = [test_canvas.get((x, y), "·") for x in range(3)]
    print(f"     {row}")


# ========== 扩展4：画板序列化 ==========
print("\n4. 画板导出/导入（dict ↔ JSON）")

# 画板数据中 key 是 tuple，JSON 不支持 tuple 作为 key
# 需要序列化：将 (x,y) → "x,y"
def serialize(canvas_data: dict) -> str:
    """将画布序列化为 JSON 字符串"""
    serializable = {f"{x},{y}": color for (x, y), color in canvas_data.items()}
    return json.dumps(serializable, indent=2, ensure_ascii=False)


def deserialize(json_str: str) -> dict:
    """从 JSON 字符串恢复画布"""
    raw = json.loads(json_str)
    canvas_data = {}
    for key, color in raw.items():
        x_str, y_str = key.split(",")
        canvas_data[(int(x_str), int(y_str))] = color
    return canvas_data


# 测试序列化
sample_data = {(0, 0): "red", (1, 2): "blue", (3, 4): "green"}
serialized = serialize(sample_data)
print(f"   序列化：{serialized}")

restored = deserialize(serialized)
print(f"   反序列化：{restored}")
print(f"   数据一致：{sample_data == restored}")


# ========== 扩展5：分析画布颜色分布 ==========
print("\n5. 颜色分布分析（set + dict 综合）")

def analyze_canvas(canvas_data: dict) -> dict:
    """分析画布的颜色分布"""
    # 颜色出现次数
    color_freq = {}
    for color in canvas_data.values():
        color_freq[color] = color_freq.get(color, 0) + 1

    # 出现过的颜色集合
    colors_used = set(canvas_data.values())

    # 按区域分组（这里按 x 坐标分组）
    region_groups = {}
    for (x, y), color in canvas_data.items():
        region = f"x={x}"
        region_groups.setdefault(region, set()).add(color)

    return {
        "total_pixels": len(canvas_data),
        "unique_colors": len(colors_used),
        "color_palette": colors_used,
        "color_frequency": dict(sorted(color_freq.items(), key=lambda x: -x[1])),
        "regions": region_groups,
    }


analysis = analyze_canvas(sample_data)
print(f"   总像素：{analysis['total_pixels']}")
print(f"   唯一颜色：{analysis['unique_colors']}")
print(f"   颜色频次：{analysis['color_frequency']}")
print(f"   区域分组：{analysis['regions']}")


# ========== 总结 ==========
print()
print(">>> 练习3 核心技能 <<<")
print("✓ set 模拟颜色混合的数学基础（集合运算）")
print("✓ dict 管理多图层，支持合成与覆盖")
print("✓ set 在泛洪填充中做 visited 集合")
print("✓ dict → JSON 序列化需要处理 tuple key")
print("✓ 组合 set + dict 做画布多维分析")
