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


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
第2节：进制大转盘 — Turtle 进制展示
面向7-10岁少儿编程教学
=========================================
画一个"进制大转盘"，展示同一个数字在不同进制下的表示
4个扇区分别代表：二进制、八进制、十进制、十六进制
"""

import turtle
import time

# ========== 全局设置 ==========
SCREEN_W = 900
SCREEN_H = 700
CENTER_X = 0
CENTER_Y = 40
RADIUS = 200

# 颜色方案
COLORS = {
    "binary": ("#FF6B6B", "#FFE0E0"),      # 红-二进制
    "octal": ("#4ECDC4", "#E0F7F4"),       # 青-八进制
    "decimal": ("#45B7D1", "#D6F0F8"),     # 蓝-十进制
    "hex": ("#96CEB4", "#E8F5EE"),         # 绿-十六进制
}

SCHEMES = {
    "binary": {"name": "二进制", "short": "BIN", "base": 2, "prefix": "0b"},
    "octal": {"name": "八进制", "short": "OCT", "base": 8, "prefix": "0o"},
    "decimal": {"name": "十进制", "short": "DEC", "base": 10, "prefix": ""},
    "hex": {"name": "十六进制", "short": "HEX", "base": 16, "prefix": "0x"},
}

# ========== 初始化画布 ==========
screen = turtle.Screen()
screen.setup(SCREEN_W, SCREEN_H)
screen.bgcolor("white")
screen.title("进制大转盘 — 四种进制对比")
screen.tracer(0)

# ========== 创建海龟 ==========
pen = turtle.Turtle()
pen.speed(0)
pen.hideturtle()


def draw_sector(angle_start, angle_end, radius, color, label, value_str):
    """
    画一个扇区（从 angle_start 到 angle_end）
    label: 进制名称
    value_str: 该进制下的数字表示
    """
    # 画扇区背景
    pen.penup()
    pen.goto(CENTER_X, CENTER_Y)
    pen.setheading(angle_start)
    pen.pendown()
    pen.fillcolor(color[1])  # 浅色背景
    pen.begin_fill()
    pen.pencolor(color[0])
    pen.pensize(2)
    pen.forward(radius)
    pen.left(90)
    # 画弧
    steps = 30
    angle_step = (angle_end - angle_start) / steps
    for _ in range(steps):
        pen.forward(2 * 3.14159 * radius / 360 * (angle_end - angle_start) / steps)
        pen.left(angle_step)
    pen.goto(CENTER_X, CENTER_Y)
    pen.end_fill()

    # 在扇区中间写进制名
    mid_angle = (angle_start + angle_end) / 2
    mid_rad = mid_angle * 3.14159 / 180
    label_r = radius * 0.35
    label_x = CENTER_X + label_r * (mid_rad ** 0).__class__.__name__ == "float" or True
    # 简单计算
    import math
    label_x = CENTER_X + label_r * math.cos(mid_rad)
    label_y = CENTER_Y + label_r * math.sin(mid_rad)
    pen.penup()
    pen.goto(label_x, label_y - 8)
    pen.color(color[0])
    pen.write(label, align="center", font=("Arial", 18, "bold"))

    # 在扇区靠外位置写数值
    val_r = radius * 0.65
    val_x = CENTER_X + val_r * math.cos(mid_rad)
    val_y = CENTER_Y + val_r * math.sin(mid_rad)
    pen.goto(val_x, val_y - 12)
    pen.color("black")
    pen.write(value_str, align="center", font=("Courier", 16, "bold"))

    # 在扇区最外写进制缩写
    short_r = radius * 0.88
    short_x = CENTER_X + short_r * math.cos(mid_rad)
    short_y = CENTER_Y + short_r * math.sin(mid_rad)
    pen.goto(short_x, short_y - 6)
    pen.color(color[0])
    pen.write(label.split("\n")[0] if "\n" in label else label[:3],
              align="center", font=("Arial", 11, "normal"))


def draw_title(number):
    """画标题"""
    pen.penup()
    pen.goto(0, CENTER_Y + RADIUS + 50)
    pen.color("navy")
    pen.write(f"数字 {number} 的四种进制表示",
              align="center", font=("Arial", 24, "bold"))

    pen.goto(0, CENTER_Y + RADIUS + 20)
    pen.color("gray")
    pen.write("看看同一个数字在不同进制中的样子吧！",
              align="center", font=("Arial", 13, "normal"))


def draw_center_circle():
    """画转盘中心圆"""
    pen.penup()
    pen.goto(CENTER_X, CENTER_Y - 25)
    pen.pendown()
    pen.fillcolor("white")
    pen.begin_fill()
    pen.circle(25)
    pen.end_fill()
    pen.penup()
    pen.goto(CENTER_X, CENTER_Y - 8)
    pen.color("navy")
    pen.write("进制", align="center", font=("Arial", 12, "bold"))


def draw_number_system_circle(number):
    """
    画进制大转盘
    4个扇区：二进制(0-90)、八进制(90-180)、十进制(180-270)、十六进制(270-360)
    """
    pen.clear()

    # 标题
    draw_title(number)

    # 计算各进制表示
    values = {}
    for key, scheme in SCHEMES.items():
        base = scheme["base"]
        if base == 10:
            values[key] = str(number)
        else:
            # 生成进制字符串（不含前缀）
            prefix = scheme["prefix"]
            if base == 2:
                val = bin(number)[2:]
            elif base == 8:
                val = oct(number)[2:]
            elif base == 16:
                val = hex(number)[2:].upper()
            else:
                val = str(number)
            values[key] = val

    # 画4个扇区
    sectors = [
        ("binary", 90, 0, SCHEMES["binary"]["name"]),
        ("octal", 180, 90, SCHEMES["octal"]["name"]),
        ("decimal", 270, 180, SCHEMES["decimal"]["name"]),
        ("hex", 360, 270, SCHEMES["hex"]["name"]),
    ]

    import math
    for key, start, end, name in sectors:
        mid = (start + end) / 2
        mid_rad = mid * math.pi / 180
        label_r = RADIUS * 0.35
        val_r = RADIUS * 0.65
        short_r = RADIUS * 0.88

        # 画扇区背景
        pen.penup()
        pen.goto(CENTER_X, CENTER_Y)
        pen.setheading(end)
        pen.pendown()
        pen.pencolor(COLORS[key][0])
        pen.fillcolor(COLORS[key][1])
        pen.pensize(2)
        pen.begin_fill()
        pen.forward(RADIUS)
        pen.left(90)
        # 画弧
        arc_angle = start - end
        if arc_angle < 0:
            arc_angle += 360
        steps = 36
        for i in range(steps + 1):
            frac = i / steps
            a = end + frac * arc_angle
            pen.goto(
                CENTER_X + RADIUS * math.cos(a * math.pi / 180),
                CENTER_Y + RADIUS * math.sin(a * math.pi / 180)
            )
        pen.goto(CENTER_X, CENTER_Y)
        pen.end_fill()

        # 写进制名（内圈）
        label_x = CENTER_X + label_r * math.cos(mid_rad)
        label_y = CENTER_Y + label_r * math.sin(mid_rad)
        pen.penup()
        pen.goto(label_x, label_y - 8)
        pen.color(COLORS[key][0])
        pen.write(name, align="center", font=("Arial", 18, "bold"))

        # 写数值（中圈）
        val_x = CENTER_X + val_r * math.cos(mid_rad)
        val_y = CENTER_Y + val_r * math.sin(mid_rad)
        pen.goto(val_x, val_y - 10)
        pen.color("black")
        display_val = values[key]
        if key == "binary":
            pen.write(display_val, align="center",
                      font=("Courier", 15, "bold"))
        else:
            pen.write(display_val, align="center",
                      font=("Courier", 18, "bold"))

        # 写进制缩写（外圈）
        short_x = CENTER_X + short_r * math.cos(mid_rad)
        short_y = CENTER_Y + short_r * math.sin(mid_rad)
        pen.goto(short_x, short_y - 6)
        pen.color(COLORS[key][0])
        pen.write(f"[{SCHEMES[key]['short']}]", align="center",
                  font=("Arial", 11, "normal"))

    # 画中心圆
    pen.penup()
    pen.goto(CENTER_X, CENTER_Y - 25)
    pen.pendown()
    pen.fillcolor("white")
    pen.pencolor("lightgray")
    pen.begin_fill()
    pen.circle(25)
    pen.end_fill()
    pen.penup()
    pen.goto(CENTER_X, CENTER_Y - 8)
    pen.color("navy")
    pen.write("进制", align="center", font=("Arial", 12, "bold"))

    # 底部信息
    pen.penup()
    pen.goto(0, CENTER_Y - RADIUS - 50)
    pen.color("gray")
    pen.write("二进制(BIN) | 八进制(OCT) | 十进制(DEC) | 十六进制(HEX)",
              align="center", font=("Arial", 12, "normal"))

    pen.goto(0, CENTER_Y - RADIUS - 75)
    pen.color("gray")
    pen.write("A=10  B=11  C=12  D=13  E=14  F=15",
              align="center", font=("Arial", 11, "normal"))

    screen.update()


def show_number_animation(numbers):
    """依次展示多个数字的进制表示"""
    for num in numbers:
        draw_number_system_circle(num)
        time.sleep(1.5)


def hex_color_demo():
    """展示十六进制在颜色中的应用"""
    pen.clear()
    pen.penup()
    pen.goto(0, 250)
    pen.color("navy")
    pen.write("十六进制颜色编码展示", align="center",
              font=("Arial", 22, "bold"))
    pen.goto(0, 215)
    pen.color("gray")
    pen.write("网页颜色用 #RRGGBB 格式，每种颜色用2位十六进制 (00-FF)",
              align="center", font=("Arial", 13, "normal"))

    # 展示几种颜色
    colors_to_show = [
        ("#FF0000", "红色", 255, 0, 0),
        ("#00FF00", "绿色", 0, 255, 0),
        ("#0000FF", "蓝色", 0, 0, 255),
        ("#FFFF00", "黄色", 255, 255, 0),
        ("#FF00FF", "紫色", 255, 0, 255),
        ("#00FFFF", "青色", 0, 255, 255),
        ("#FFFFFF", "白色", 255, 255, 255),
        ("#000000", "黑色", 0, 0, 0),
    ]

    x_start = -300
    y_start = 150
    box_w = 75
    box_h = 50

    for idx, (hex_code, name, r, g, b) in enumerate(colors_to_show):
        col = idx % 4
        row = idx // 4
        x = x_start + col * (box_w + 20)
        y = y_start - row * (box_h + 60)

        # 颜色方块
        pen.penup()
        pen.goto(x, y)
        pen.pendown()
        pen.pencolor("black")
        pen.fillcolor(hex_code)
        pen.begin_fill()
        for _ in range(2):
            pen.forward(box_w)
            pen.right(90)
            pen.forward(box_h)
            pen.right(90)
        pen.end_fill()

        # 颜色名称
        pen.penup()
        pen.goto(x + box_w // 2, y - 18)
        pen.color("black")
        pen.write(name, align="center", font=("Arial", 12, "bold"))

        # 十六进制
        pen.goto(x + box_w // 2, y - 35)
        pen.color("dimgray")
        pen.write(hex_code, align="center", font=("Courier", 11, "normal"))

        # RGB十进制
        pen.goto(x + box_w // 2, y - 50)
        pen.color("gray")
        pen.write(f"RGB({r},{g},{b})", align="center",
                  font=("Courier", 9, "normal"))

    # 底部说明
    pen.penup()
    pen.goto(0, y_start - 170)
    pen.color("navy")
    pen.write("FF(16) = 255(10)  —  每种颜色分量用1个字节(0-255)表示",
              align="center", font=("Arial", 13, "normal"))

    screen.update()


def show_ascii_demo():
    """展示ASCII字符的十六进制"""
    chars = [
        ("A", 65, "0x41"),
        ("B", 66, "0x42"),
        ("Z", 90, "0x5A"),
        ("a", 97, "0x61"),
        ("0", 48, "0x30"),
        ("9", 57, "0x39"),
        ("!", 33, "0x21"),
        ("?", 63, "0x3F"),
    ]

    pen.clear()
    pen.penup()
    pen.goto(0, 250)
    pen.color("navy")
    pen.write("ASCII字符的十六进制编码", align="center",
              font=("Arial", 22, "bold"))

    x_start = -250
    y_start = 180

    for idx, (char, dec, hex_code) in enumerate(chars):
        col = idx % 4
        row = idx // 4
        x = x_start + col * 130
        y = y_start - row * 100

        pen.penup()
        pen.goto(x, y)
        pen.color("darkorange")
        pen.write(f"'{char}'", align="center", font=("Arial", 36, "bold"))

        pen.goto(x, y - 35)
        pen.color("black")
        pen.write(f"十进制: {dec}", align="center",
                  font=("Arial", 12, "normal"))

        pen.goto(x, y - 55)
        pen.color("crimson")
        pen.write(f"十六进制: {hex_code}", align="center",
                  font=("Courier", 14, "bold"))

    pen.goto(0, y_start - 210)
    pen.color("gray")
    pen.write("电脑中每个字符都有一个唯一的数字编码，常用十六进制表示",
              align="center", font=("Arial", 12, "normal"))

    screen.update()


# ========== 入口 ==========
if __name__ == "__main__":
    # 展示进制大转盘
    demo_numbers = [5, 10, 15, 16, 31, 100, 255]

    for num in demo_numbers:
        draw_number_system_circle(num)
        time.sleep(2)

    # 展示颜色编码
    time.sleep(1)
    hex_color_demo()
    time.sleep(2)

    # 展示ASCII
    show_ascii_demo()

    # 交互提示
    pen.penup()
    pen.goto(0, -300)
    pen.color("gray")
    pen.write("点击画面退出  |  观察不同进制下同一个数字的表示差异",
              align="center", font=("Arial", 12, "normal"))
    screen.update()

    screen.exitonclick()
