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

"""
=================================================
  07_列表推导式.py
  【魔法压缩饼干】—— 用一句话生成列表！
  面向7-10岁少儿编程教学
=================================================
"""

import turtle
import math
import random

# =================================================
# 第1关：基本语法 —— 复制与加工
# 口诀：方括号里写公式，for字前面是表达式
# =================================================
def demo_basic():
    """基本列表推导式演示"""
    print("=" * 40)
    print("第1关：基本语法 —— 复制与加工")
    print("=" * 40)

    # 复制数字（最简单！）
    数字 = [i for i in range(1, 6)]
    print("复制数字:", 数字)  # [1, 2, 3, 4, 5]

    # 加工数字（翻倍）
    翻倍 = [i * 2 for i in range(1, 6)]
    print("翻倍数字:", 翻倍)  # [2, 4, 6, 8, 10]

    # 算出平方
    平方 = [i ** 2 for i in range(1, 6)]
    print("平方数:", 平方)  # [1, 4, 9, 16, 25]

    # 字符串操作
    名字 = ["小明", "小红", "小刚"]
    欢迎 = [n + "同学" for n in 名字]
    print("字符串处理:", 欢迎)  # ['小明同学', '小红同学', '小刚同学']

    # 对每个数加10
    结果 = [x + 10 for x in [5, 10, 15]]
    print("每个加10:", 结果)  # [15, 20, 25]


# =================================================
# 第2关：带条件的列表推导式 —— 筛子过滤
# 口诀：for画图if过滤，一句生成新列表
# =================================================
def demo_filter():
    """带条件的列表推导式演示"""
    print("\n" + "=" * 40)
    print("第2关：带条件的列表推导式 —— 筛子过滤")
    print("=" * 40)

    # 筛出偶数
    偶数 = [i for i in range(1, 11) if i % 2 == 0]
    print("1到10中的偶数:", 偶数)  # [2, 4, 6, 8, 10]

    # 筛出奇数
    奇数 = [i for i in range(1, 11) if i % 2 == 1]
    print("1到10中的奇数:", 奇数)  # [1, 3, 5, 7, 9]

    # 偶数翻倍
    翻倍偶数 = [i * 2 for i in range(1, 11) if i % 2 == 0]
    print("偶数翻倍:", 翻倍偶数)  # [4, 8, 12, 16, 20]

    # 选出能被3整除的数
    倍数3 = [i for i in range(10, 21) if i % 3 == 0]
    print("10到20中能被3整除:", 倍数3)  # [12, 15, 18]

    # 字符串长度过滤
    同学 = ["小明", "张伟", "李小美", "王"]
    长名字 = [n for n in 同学 if len(n) > 2]
    print("长度大于2的名字:", 长名字)  # ['李小美']


# =================================================
# 第3关：嵌套for循环 —— 双层压缩
# 口诀：想要多层怎么办？for字再写一个for
# =================================================
def demo_nested():
    """嵌套列表推导式演示"""
    print("\n" + "=" * 40)
    print("第3关：嵌套for循环 —— 双层压缩")
    print("=" * 40)

    # 生成坐标点
    坐标 = [(x, y) for x in range(3) for y in range(3)]
    print("3x3坐标网格:", 坐标)

    # 展平二维列表
    矩阵 = [[1, 2], [3, 4], [5, 6]]
    展开 = [i for 行 in 矩阵 for i in 行]
    print("展平二维列表:", 展开)  # [1, 2, 3, 4, 5, 6]

    # 带条件的嵌套（去掉对角线）
    坐标_过滤 = [(x, y) for x in range(3) for y in range(3) if x != y]
    print("去掉对角线的坐标:", 坐标_过滤)

    # 九九乘法表里的算式
    乘法算式 = [f"{x}x{y}={x*y}" for x in range(1, 4) for y in range(1, 4)]
    print("小九九（1-3）:", 乘法算式)

    # 颜色与形状组合
    颜色 = ["红", "蓝", "绿"]
    形状 = ["圆", "方"]
    组合 = [c + s for c in 颜色 for s in 形状]
    print("颜色形状组合:", 组合)


# =================================================
# 第4关：turtle绘图 —— 星空点点
# 用列表推导式生成100个随机星星
# =================================================
def draw_star_sky():
    """用列表推导式画出星空"""
    t = turtle.Turtle()
    t.speed(0)
    t.hideturtle()
    turtle.bgcolor("black")

    # 用列表推导式生成100个随机坐标
    星星坐标 = [(random.randint(-300, 300), random.randint(-300, 300)) for _ in range(80)]

    # 用列表推导式生成随机颜色（RGB模式）
    星星颜色 = [(random.random(), random.random(), random.random()) for _ in range(80)]

    # 用列表推导式生成随机大小
    星星大小 = [random.randint(3, 12) for _ in range(80)]

    # 画星星
    for i in range(80):
        t.penup()
        t.goto(星星坐标[i][0], 星星坐标[i][1])
        t.color(星星颜色[i])
        t.pendown()
        t.dot(星星大小[i])

    # 画一个月亮（大圆）
    t.penup()
    t.goto(200, 200)
    t.color("yellow")
    t.pendown()
    t.dot(50)

    t.penup()
    t.goto(-250, -250)
    t.color("white")
    t.write("星空（列表推导式生成）", font=("Arial", 16, "normal"))

    turtle.done()


# =================================================
# 第5关：turtle绘图 —— 彩色螺旋
# 用列表推导式生成螺旋坐标和渐变色
# =================================================
def draw_spiral():
    """用列表推导式画出彩色螺旋"""
    t = turtle.Turtle()
    t.speed(0)
    t.hideturtle()
    t.width(2)

    turtle.bgcolor("black")

    # 用列表推导式生成螺旋的坐标
    螺旋点 = [(15 * a * math.cos(math.radians(a)),
                15 * a * math.sin(math.radians(a)))
               for a in range(0, 720, 5)]

    # 用列表推导式生成渐变色
    颜色 = [((a / 720) % 1, 0.5, 1 - (a / 720) % 1) for a in range(0, 720, 5)]

    # 画螺旋
    for i in range(len(螺旋点)):
        t.color(颜色[i])
        t.goto(螺旋点[i][0], 螺旋点[i][1])

    turtle.done()


# =================================================
# 第6关：turtle绘图 —— 彩色棋盘格
# 用嵌套列表推导式生成格子坐标和颜色
# =================================================
def draw_checkerboard():
    """用列表推导式画出彩色棋盘"""
    t = turtle.Turtle()
    t.speed(0)
    t.hideturtle()

    # 用嵌套列表推导式生成所有棋盘格子坐标
    格子 = [(x * 50, y * 50) for x in range(-4, 4) for y in range(-4, 4)]

    # 用列表推导式判断奇偶来决定颜色（三色棋盘）
    颜色表 = [("red" if (x + y) % 3 == 0 else "blue" if (x + y) % 3 == 1 else "yellow")
              for x in range(8) for y in range(8)]

    # 画棋盘
    for i in range(len(格子)):
        t.penup()
        t.goto(格子[i][0], 格子[i][1])
        t.fillcolor(颜色表[i])
        t.pendown()
        t.begin_fill()
        for _ in range(4):
            t.forward(50)
            t.right(90)
        t.end_fill()

    # 写标题
    t.penup()
    t.goto(-180, -230)
    t.write("彩色棋盘（列表推导式生成）", font=("Arial", 14, "bold"))

    turtle.done()


# =================================================
# 第7关：turtle绘图 —— 多边形派对
# 用列表推导式生成多种多边形的顶点坐标
# =================================================
def draw_polygon_party():
    """用列表推导式画出多个多边形"""
    t = turtle.Turtle()
    t.speed(0)
    t.hideturtle()

    turtle.title("多边形派对 —— 列表推导式")

    # 不同的多边形：三角形(3边)、正方形(4边)、五边形(5边)...
    边数列表 = [3, 4, 5, 6, 8, 10]
    颜色列表 = ["red", "orange", "gold", "green", "blue", "purple"]
    半径列表 = [40, 50, 60, 70, 80, 90]

    for idx in range(len(边数列表)):
        边数 = 边数列表[idx]
        半径 = 半径列表[idx]

        # 用列表推导式生成多边形的顶点坐标
        顶点 = [(半径 * math.cos(math.radians(360 * i / 边数 - 90)),
                  半径 * math.sin(math.radians(360 * i / 边数 - 90)))
                 for i in range(边数)]

        # 移动到这个多边形的起始位置（中心偏移）
        x_offset = (idx - 2.5) * 100
        y_offset = 0

        # 画多边形
        t.penup()
        t.goto(顶点[0][0] + x_offset, 顶点[0][1] + y_offset)
        t.color(颜色列表[idx])
        t.pendown()
        t.begin_fill()
        for 点 in 顶点[1:]:
            t.goto(点[0] + x_offset, 点[1] + y_offset)
        t.goto(顶点[0][0] + x_offset, 顶点[0][1] + y_offset)
        t.end_fill()

    # 写标题
    t.penup()
    t.goto(-250, -130)
    t.color("black")
    t.write("多边形派对（列表推导式生成顶点）", font=("Arial", 14, "bold"))

    turtle.done()


# =================================================
# 第8关：turtle绘图 —— 彩虹
# 用列表推导式生成彩虹的颜色和位置
# =================================================
def draw_rainbow():
    """用列表推导式画出彩虹"""
    t = turtle.Turtle()
    t.speed(0)
    t.hideturtle()
    t.width(8)

    turtle.bgcolor("lightblue")
    turtle.title("彩虹 —— 列表推导式")

    # 彩虹7种颜色
    彩虹颜色 = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"]

    # 用列表推导式生成每个弧线向上偏移的量
    偏移量 = [i * 18 for i in range(7)]

    # 用列表推导式生成每个弧线的半径
    半径列表 = [220 - i * 18 for i in range(7)]

    # 画彩虹
    for i in range(7):
        t.color(彩虹颜色[i])
        t.penup()
        t.goto(-半径列表[i], -100 + 偏移量[i])
        t.setheading(90)
        t.pendown()
        t.circle(半径列表[i], 180)

    # 画云朵
    def draw_cloud(x, y):
        t.penup()
        t.goto(x, y)
        t.color("white")
        t.begin_fill()
        t.circle(30)
        t.end_fill()
        t.penup()
        t.goto(x + 30, y + 10)
        t.begin_fill()
        t.circle(25)
        t.end_fill()
        t.penup()
        t.goto(x + 15, y + 25)
        t.begin_fill()
        t.circle(20)
        t.end_fill()

    # 画两朵云
    draw_cloud(-180, -50)
    draw_cloud(140, -30)

    # 画太阳
    t.penup()
    t.goto(180, 180)
    t.color("yellow")
    t.begin_fill()
    t.circle(40)
    t.end_fill()

    # 光线
    t.penup()
    t.goto(180, 220)
    t.color("yellow")
    t.width(3)
    for angle in range(0, 360, 30):
        t.setheading(angle)
        t.pendown()
        t.forward(20)
        t.penup()
        t.goto(180, 220)

    turtle.done()


# =================================================
# 第9关：turtle绘图 —— 万花筒
# 用列表推导式生成对称图案，展示嵌套推导式的威力
# =================================================
def draw_kaleidoscope():
    """用列表推导式画出万花筒图案"""
    t = turtle.Turtle()
    t.speed(0)
    t.hideturtle()
    t.width(2)

    turtle.bgcolor("black")
    turtle.title("万花筒 —— 嵌套列表推导式")

    # 用嵌套列表推导式生成所有花瓣的坐标
    # 第一层：8个方向（每45度一个）
    # 第二层：每个方向画3个花瓣（不同半径）
    花瓣数据 = [(r, a) for r in [60, 100, 140] for a in range(0, 360, 45)]

    # 用列表推导式生成颜色（每个花瓣不同色）
    def get_color(index, total):
        ratio = index / total
        return (ratio % 1, 0.3, 1 - ratio % 1)

    颜色数据 = [get_color(i, len(花瓣数据)) for i in range(len(花瓣数据))]

    for idx, (半径, 角度) in enumerate(花瓣数据):
        x = 半径 * math.cos(math.radians(角度))
        y = 半径 * math.sin(math.radians(角度))

        t.penup()
        t.goto(x, y)
        t.color(颜色数据[idx])
        t.pendown()

        # 在每个位置画一个小花
        for a in range(0, 360, 60):
            t.setheading(a)
            t.forward(半径 * 0.3)
            t.backward(半径 * 0.3)

        # 画个中心点
        t.dot(8)

    # 中心大圆
    t.penup()
    t.goto(0, -40)
    t.color("white", "purple")
    t.begin_fill()
    t.circle(40)
    t.end_fill()

    turtle.done()


# =================================================
# 第10关：turtle绘图 —— 函数图形
# 用列表推导式生成正弦波、心形线等数学图形
# =================================================
def draw_math_curves():
    """用列表推导式画出数学曲线"""
    t = turtle.Turtle()
    t.speed(0)
    t.hideturtle()
    t.width(2)

    turtle.bgcolor("black")
    turtle.title("数学曲线 —— 列表推导式")

    # ---- 正弦波 ----
    t.penup()
    t.goto(-300, 0)
    t.color("cyan")
    t.pendown()

    # 用列表推导式生成正弦波上的点
    正弦点 = [(x, 80 * math.sin(math.radians(x * 2))) for x in range(-300, 301, 3)]
    for 点 in 正弦点:
        t.goto(点[0], 点[1])

    # ---- 心形线 ----
    t.penup()
    t.goto(0, 0)
    t.color("red")
    t.pendown()

    # 用列表推导式生成心形线坐标
    # 参数方程: x = 16*sin^3(t), y = 13*cos(t) - 5*cos(2t) - 2*cos(3t) - cos(4t)
    心形点 = [(160 * math.sin(math.radians(t))**3,
               130 * math.cos(math.radians(t)) -
               50 * math.cos(math.radians(2*t)) -
               20 * math.cos(math.radians(3*t)) -
               10 * math.cos(math.radians(4*t)))
              for t in range(0, 361, 2)]

    # 移动到心形起点
    if 心形点:
        t.penup()
        t.goto(心形点[0][0] + 100, 心形点[0][1] - 50)
        t.pendown()
        for 点 in 心形点[1:]:
            t.goto(点[0] + 100, 点[1] - 50)

    # ---- 圆形点阵 ----
    # 用嵌套列表推导式生成圆环上的点阵
    圆环点 = [(120 * math.cos(math.radians(a)) + 50 * math.cos(math.radians(a * 3)),
               120 * math.sin(math.radians(a)) + 50 * math.sin(math.radians(a * 3)))
              for a in range(0, 360, 3)]

    t.penup()
    t.color("yellow")
    t.pendown()
    for 点 in 圆环点:
        t.goto(点[0] - 200, 点[1] - 80)
        t.dot(3)

    turtle.done()


# =================================================
# 主菜单 —— 选择要运行哪个演示
# =================================================
def main():
    """主函数：显示菜单并运行选择的演示"""
    print("""
    ====================================
      【魔法压缩饼干】列表推导式大冒险
    ====================================

    请选择要运行的演示：

    0 - 退出
    1 - 基本语法演示（控制台输出）
    2 - 条件过滤演示（控制台输出）
    3 - 嵌套推导演示（控制台输出）
    4 - 星空点点（turtle绘图）
    5 - 彩色螺旋（turtle绘图）
    6 - 彩色棋盘（turtle绘图）
    7 - 多边形派对（turtle绘图）
    8 - 彩虹（turtle绘图）
    9 - 万花筒（turtle绘图）
    10 - 数学曲线（turtle绘图）

    ====================================
    """)

    try:
        选择 = input("请输入数字 (0-10): ").strip()
    except KeyboardInterrupt:
        print("再见！下次继续学习列表推导式！")
        return

    print()

    if 选择 == "0":
        print("再见！下次继续学习列表推导式！")
    elif 选择 == "1":
        demo_basic()
    elif 选择 == "2":
        demo_filter()
    elif 选择 == "3":
        demo_nested()
    elif 选择 == "4":
        draw_star_sky()
    elif 选择 == "5":
        draw_spiral()
    elif 选择 == "6":
        draw_checkerboard()
    elif 选择 == "7":
        draw_polygon_party()
    elif 选择 == "8":
        draw_rainbow()
    elif 选择 == "9":
        draw_kaleidoscope()
    elif 选择 == "10":
        draw_math_curves()
    else:
        print("输入有误，请输入 0-10 之间的数字！")

    # 控制台演示结束后暂停
    if 选择 in ["1", "2", "3"]:
        input("\n按回车键继续...")


if __name__ == "__main__":
    main()
