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


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
第1节：二进制探秘 — Turtle 二进制位演示
面向7-10岁少儿编程教学
=========================================
用图形化的方块展示一个数字的二进制表示
每个位画一个方块，填充=1（亮），空白=0（灭）
"""

import turtle
import time

# ========== 全局设置 ==========
SCREEN_W = 800
SCREEN_H = 500
BOX_SIZE = 60          # 每个位方块的大小
BOX_GAP = 10           # 方块之间的间距
BITS = 8               # 显示8个二进制位
START_X = -(BITS * (BOX_SIZE + BOX_GAP)) // 2  # 居中起点
START_Y = 80

# 颜色方案
COLOR_ON = "deepskyblue"    # 1 = 点亮
COLOR_OFF = "lightgray"     # 0 = 熄灭
COLOR_BG = "white"
COLOR_TEXT = "navy"
COLOR_TITLE = "darkorange"

# ========== 初始化画布 ==========
screen = turtle.Screen()
screen.setup(SCREEN_W, SCREEN_H)
screen.bgcolor(COLOR_BG)
screen.title("二进制探秘 — Turtle 二进制位演示")
screen.tracer(0)  # 关闭自动刷新，手动控制

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


def draw_bit_box(x, y, value, label=""):
    """
    在 (x, y) 位置画一个二进制位方块
    value: 0 或 1
    label: 位权标签（1, 2, 4, 8, ...）
    """
    pen.penup()
    pen.goto(x, y)

    # 画方块
    pen.pendown()
    if value == 1:
        pen.fillcolor(COLOR_ON)
    else:
        pen.fillcolor(COLOR_OFF)
    pen.begin_fill()
    for _ in range(4):
        pen.forward(BOX_SIZE)
        pen.right(90)
    pen.end_fill()

    # 在方块内写数字（0或1）
    pen.penup()
    pen.goto(x + BOX_SIZE // 2, y + BOX_SIZE // 2 - 10)
    pen.color("white" if value == 1 else "dimgray")
    pen.write(str(value), align="center",
              font=("Arial", 20, "bold"))

    # 在方块下方写位权标签
    if label:
        pen.goto(x + BOX_SIZE // 2, y - 25)
        pen.color(COLOR_TEXT)
        pen.write(label, align="center",
                  font=("Arial", 12, "normal"))


def draw_title(text):
    """在屏幕顶部绘制标题"""
    pen.penup()
    pen.goto(0, START_Y + BOX_SIZE + 40)
    pen.color(COLOR_TITLE)
    pen.write(text, align="center",
              font=("Arial", 22, "bold"))


def draw_info(text, y_offset=-150):
    """在屏幕底部绘制信息文字"""
    pen.penup()
    pen.goto(0, y_offset)
    pen.color(COLOR_TEXT)
    pen.write(text, align="center",
              font=("Arial", 14, "normal"))


def draw_decimal_label(number, y_offset=-120):
    """显示十进制数字"""
    pen.penup()
    pen.goto(0, y_offset)
    pen.color("crimson")
    pen.write(f"十进制: {number}", align="center",
              font=("Arial", 16, "bold"))


def draw_binary_label(bin_str, y_offset=-180):
    """显示二进制字符串"""
    pen.penup()
    pen.goto(0, y_offset)
    pen.color("darkgreen")
    pen.write(f"二进制: {bin_str}", align="center",
              font=("Arial", 16, "bold"))


def draw_bit_labels():
    """在方块顶部标记位序号"""
    for i in range(BITS):
        x = START_X + i * (BOX_SIZE + BOX_GAP)
        pen.penup()
        pen.goto(x + BOX_SIZE // 2, START_Y + BOX_SIZE + 5)
        bit_pos = BITS - 1 - i  # 从右往左
        pen.color("purple")
        pen.write(f"位{bit_pos}", align="center",
                  font=("Arial", 10, "normal"))


def draw_weight_labels():
    """在方块底部标记位权值"""
    for i in range(BITS):
        x = START_X + i * (BOX_SIZE + BOX_GAP)
        weight = 1 << (BITS - 1 - i)  # 2的幂
        pen.color("dimgray")
        pen.penup()
        pen.goto(x + BOX_SIZE // 2, START_Y - 30)
        if weight >= 1024:
            pen.write(f"{weight}", align="center",
                      font=("Arial", 9, "normal"))
        else:
            pen.write(f"{weight}", align="center",
                      font=("Arial", 11, "normal"))


def draw_connection_lines(bin_bits):
    """画连接线：标记哪些位相加得到结果"""
    pen.penup()
    pen.goto(START_X - 20, START_Y + BOX_SIZE // 2)
    pen.color("orange", "lightyellow")
    pen.pendown()
    pen.begin_fill()
    for _ in range(2):
        pen.forward(10)
        pen.right(90)
        pen.forward(BOX_SIZE)
        pen.right(90)
    pen.end_fill()
    pen.penup()
    pen.goto(START_X - 15, START_Y + BOX_SIZE // 2 - 8)
    pen.color("darkorange")
    pen.write("=", align="center", font=("Arial", 16, "bold"))


def show_binary(number):
    """
    主函数：显示一个十进制数的二进制表示
    """
    pen.clear()

    # 计算二进制位
    bits = []
    for i in range(BITS - 1, -1, -1):
        bit = (number >> i) & 1
        bits.append(bit)

    # 构建二进制字符串
    bin_str = "".join(str(b) for b in bits)
    # 去掉前导0的简洁版
    bin_short = bin(number)[2:]

    # ---------- 绘制标题 ----------
    draw_title(f"数字 {number} 的二进制表示")

    # ---------- 绘制位标签 ----------
    draw_bit_labels()

    # ---------- 绘制位权标签 ----------
    draw_weight_labels()

    # ---------- 绘制每个二进制位 ----------
    for i in range(BITS):
        x = START_X + i * (BOX_SIZE + BOX_GAP)
        value = bits[i]
        weight = 1 << (BITS - 1 - i)
        draw_bit_box(x, START_Y, value, str(weight))

    # ---------- 绘制信息 ----------
    decimal_sum = sum(
        (bits[BITS - 1 - i] << i) for i in range(BITS)
    )
    draw_decimal_label(f"十进制: {number}", y_offset=-120)
    draw_binary_label(f"二进制: {bin_short} (完整: {bin_str})",
                      y_offset=-160)

    # 底部提示
    draw_info("蓝色 = 1 (亮)  灰色 = 0 (灭)  从右往左位权: 1,2,4,8,16,32,64,128",
              y_offset=-210)

    screen.update()


def animate_bits(number):
    """动画效果：逐位点亮"""
    bits = []
    for i in range(BITS - 1, -1, -1):
        bit = (number >> i) & 1
        bits.append(bit)

    pen.clear()
    draw_title(f"数字 {number} — 逐位展示")

    # 先画空盒子
    for i in range(BITS):
        x = START_X + i * (BOX_SIZE + BOX_GAP)
        draw_bit_box(x, START_Y, 0, str(1 << (BITS - 1 - i)))
    screen.update()
    time.sleep(0.5)

    # 逐位点亮
    for i in range(BITS):
        x = START_X + i * (BOX_SIZE + BOX_GAP)
        # 重新绘制这个位
        pen.penup()
        pen.goto(x, START_Y)
        pen.pendown()
        if bits[i] == 1:
            pen.fillcolor(COLOR_ON)
        else:
            pen.fillcolor(COLOR_OFF)
        pen.begin_fill()
        for _ in range(4):
            pen.forward(BOX_SIZE)
            pen.right(90)
        pen.end_fill()

        # 数字
        pen.penup()
        pen.goto(x + BOX_SIZE // 2, START_Y + BOX_SIZE // 2 - 10)
        pen.color("white" if bits[i] == 1 else "dimgray")
        pen.write(str(bits[i]), align="center",
                  font=("Arial", 20, "bold"))

        screen.update()
        time.sleep(0.3)

    # 显示结果
    bin_short = bin(number)[2:]
    draw_decimal_label(number, y_offset=-120)
    draw_binary_label(f"二进制: {bin_short}", y_offset=-160)
    screen.update()


def interactive_mode():
    """交互模式：让用户输入数字查看二进制"""
    draw_info("请输入一个0-255之间的数字（在终端输入）", y_offset=-240)
    screen.update()

    try:
        num = int(turtle.textinput("二进制探秘", "请输入一个0-255之间的数字:"))
        if 0 <= num <= 255:
            show_binary(num)
        else:
            draw_info("数字超出范围（0-255），请重新运行程序", y_offset=-240)
            screen.update()
    except (ValueError, TypeError):
        draw_info("输入无效，请重新运行程序输入整数", y_offset=-240)
        screen.update()


# ========== 入口 ==========
if __name__ == "__main__":
    # 示例数字 — 修改这个值来演示不同数字
    demo_number = 5

    # 先展示逐位动画
    animate_bits(demo_number)
    time.sleep(1.5)

    # 再展示完整结果
    show_binary(demo_number)

    # 底部提示交互
    draw_info("点击画面退出  |  可修改代码中的 demo_number 来展示其他数字",
              y_offset=-240)
    screen.update()

    # 点击退出
    screen.exitonclick()
