Bootstrap

Turtle——绘图演示脚本案例

一、演示案例脚本列表

以下是本篇文章案例介绍列表,如果你想学习turtle绘图,请看turtle库(海龟绘图)介绍与使用

名称描述相关特性
bytedesign复杂的传统海龟绘图模式tracer()delay()update()
chaos绘制Verhulst动态模型,演示通过计算机的运算可能会生成令人惊叹的结果世界坐标系
clock绘制模拟时钟显示本机的当前时间海龟作为表针,ontimer()
colormixer试验 r, g, b 颜色模式ondrag()当鼠标拖动
forest绘制3棵广度优先树随机化
fractalcurves绘制Hilbert&Koch曲线递归
lindenmayer文化数学 (印度装饰艺术)L-系统
minimal_hanoi汉诺塔矩形海龟作为汉诺盘 (shape()shapesize())
nim玩经典的“尼姆”游戏,开始时有三堆小棒,与电脑对战。海龟作为小棒,事件驱动 (鼠标, 键盘)
paint超极简主义绘画程序onclick()当鼠标点击
peace初级技巧海龟: 外观与动画
penrose非周期性地使用风筝和飞镖形状铺满平面stamp()印章
planet_and_moon模拟引力系统复合开关,Vec2D
rosette一个来自介绍海龟绘图的维基百科文章的图案clone()undo()
round_dance两两相对并不断旋转舞蹈的海龟复合形状,clone()shapesize()tilt()get_shapepoly()update
sorting_animate动态演示不同的排序方法简单对齐, 随机化
tree一棵 (图形化的) 广度优先树 (使用生成器)clone()克隆
two_canvases简单设计两块画布上的海龟
yinyang另一个初级示例circle()画圆

二、演示脚本案例代码

2.1 bytedesign

from time import perf_counter as clock
from turtle import Turtle, mainloop


class Designer(Turtle):

    def design(self, homePos, scale):
        self.up()
        for i in range(5):
            self.forward(64.65 * scale)
            self.down()
            self.wheel(self.position(), scale)
            self.up()
            self.backward(64.65 * scale)
            self.right(72)
        self.up()
        self.goto(homePos)
        self.right(36)
        self.forward(24.5 * scale)
        self.right(198)
        self.down()
        self.centerpiece(46 * scale, 143.4, scale)
        self.getscreen().tracer(True)

    def wheel(self, initpos, scale):
        self.right(54)
        for i in range(4):
            self.pentpiece(initpos, scale)
        self.down()
        self.left(36)
        for i in range(5):
            self.tripiece(initpos, scale)
        self.left(36)
        for i in range(5):
            self.down()
            self.right(72)
            self.forward(28 * scale)
            self.up()
            self.backward(28 * scale)
        self.left(54)
        self.getscreen().update()

    def tripiece(self, initpos, scale):
        oldh = self.heading()
        self.down()
        self.backward(2.5 * scale)
        self.tripolyr(31.5 * scale, scale)
        self.up()
        self.goto(initpos)
        self.setheading(oldh)
        self.down()
        self.backward(2.5 * scale)
        self.tripolyl(31.5 * scale, scale)
        self.up()
        self.goto(initpos)
        self.setheading(oldh)
        self.left(72)
        self.getscreen().update()

    def pentpiece(self, initpos, scale):
        oldh = self.heading()
        self.up()
        self.forward(29 * scale)
        self.down()
        for i in range(5):
            self.forward(18 * scale)
            self.right(72)
        self.pentr(18 * scale, 75, scale)
        self.up()
        self.goto(initpos)
        self.setheading(oldh)
        self.forward(29 * scale)
        self.down()
        for i in range(5):
            self.forward(18 * scale)
            self.right(72)
        self.pentl(18 * scale, 75, scale)
        self.up()
        self.goto(initpos)
        self.setheading(oldh)
        self.left(72)
        self.getscreen().update()

    def pentl(self, side, ang, scale):
        if side < (2 * scale): return
        self.forward(side)
        self.left(ang)
        self.pentl(side - (.38 * scale), ang, scale)

    def pentr(self, side, ang, scale):
        if side < (2 * scale): return
        self.forward(side)
        self.right(ang)
        self.pentr(side - (.38 * scale), ang, scale)

    def tripolyr(self, side, scale):
        if side < (4 * scale): return
        self.forward(side)
        self.right(111)
        self.forward(side / 1.78)
        self.right(111)
        self.forward(side / 1.3)
        self.right(146)
        self.tripolyr(side * .75, scale)

    def tripolyl(self, side, scale):
        if side < (4 * scale): return
        self.forward(side)
        self.left(111)
        self.forward(side / 1.78)
        self.left(111)
        self.forward(side / 1.3)
        self.left(146)
        self.tripolyl(side * .75, scale)

    def centerpiece(self, s, a, scale):
        self.forward(s)
        self.left(a)
        if s < (7.5 * scale):
            return
        self.centerpiece(s - (1.2 * scale), a, scale)


def main():
    t = Designer()
    t.speed(0)
    t.hideturtle()
    t.getscreen().delay(0)
    t.getscreen().tracer(0)
    at = clock()
    t.design(t.position(), 2)
    et = clock()
    return "runtime: %.2f sec." % (et - at)


if __name__ == '__main__':
    msg = main()
    print(msg)
    mainloop()

2.2 chaos

from turtle import *

N = 80


def f(x):
    return 3.9 * x * (1 - x)


def g(x):
    return 3.9 * (x - x ** 2)


def h(x):
    return 3.9 * x - 3.9 * x * x


def jumpto(x, y):
    penup();
    goto(x, y)


def line(x1, y1, x2, y2):
    jumpto(x1, y1)
    pendown()
    goto(x2, y2)


def coosys():
    line(-1, 0, N + 1, 0)
    line(0, -0.1, 0, 1.1)


def plot(fun, start, color):
    pencolor(color)
    x = start
    jumpto(0, x)
    pendown()
    dot(5)
    for i in range(N):
        x = fun(x)
        goto(i + 1, x)
        dot(5)


def main():
    reset()
    setworldcoordinates(-1.0, -0.1, N + 1, 1.1)
    speed(0)
    hideturtle()
    coosys()
    plot(f, 0.35, "blue")
    plot(g, 0.35, "green")
    plot(h, 0.35, "red")
    # Now zoom in:
    for s in range(100):
        setworldcoordinates(0.5 * s, -0.1, N + 1, 1.1)
    return "Done!"


if __name__ == "__main__":
    main()
    mainloop()

2.3 clock

from datetime import datetime
from turtle import *


def jump(distanz, winkel=0):
    penup()
    right(winkel)
    forward(distanz)
    left(winkel)
    pendown()


def hand(laenge, spitze):
    fd(laenge * 1.15)
    rt(90)
    fd(spitze / 2.0)
    lt(120)
    fd(spitze)
    lt(120)
    fd(spitze)
    lt(120)
    fd(spitze / 2.0)


def make_hand_shape(name, laenge, spitze):
    reset()
    jump(-laenge * 0.15)
    begin_poly()
    hand(laenge, spitze)
    end_poly()
    hand_form = get_poly()
    register_shape(name, hand_form)


def clockface(radius):
    reset()
    pensize(7)
    for i in range(60):
        jump(radius)
        if i % 5 == 0:
            fd(25)
            jump(-radius - 25)
        else:
            dot(3)
            jump(-radius)
        rt(6)


def setup():
    global second_hand, minute_hand, hour_hand, writer
    mode("logo")
    make_hand_shape("second_hand", 125, 25)
    make_hand_shape("minute_hand", 130, 25)
    make_hand_shape("hour_hand", 90, 25)
    clockface(160)
    second_hand = Turtle()
    second_hand.shape("second_hand")
    second_hand.color("gray20", "gray80")
    minute_hand = Turtle()
    minute_hand.shape("minute_hand")
    minute_hand.color("blue1", "red1")
    hour_hand = Turtle()
    hour_hand.shape("hour_hand")
    hour_hand.color("blue3", "red3")
    for hand in second_hand, minute_hand, hour_hand:
        hand.resizemode("user")
        hand.shapesize(1, 1, 3)
        hand.speed(0)
    ht()
    writer = Turtle()
    # writer.mode("logo")
    writer.ht()
    writer.pu()
    writer.bk(85)


def wochentag(t):
    wochentag = ["星期一", "星期二", "星期三",
                 "星期四", "星期五", "星期六", "星期日"]
    return wochentag[t.weekday()]


def datum(z):
    monat = ["一月", "二月", "三月", "四月", "五月", "六月",
             "七月", "八月", "九月", "十月", "十一月", "十二月"]
    j = z.year
    m = monat[z.month - 1]
    t = z.day
    return "%d年 %s %d日" % (j, m, t)


def tick():
    t = datetime.today()
    sekunde = t.second + t.microsecond * 0.000001
    minute = t.minute + sekunde / 60.0
    stunde = t.hour + minute / 60.0
    try:
        tracer(False)
        writer.clear()
        writer.home()
        writer.forward(65)
        writer.write(wochentag(t),
                     align="center", font=("Courier", 14, "bold"))
        writer.back(150)
        writer.write(datum(t),
                     align="center", font=("Courier", 14, "bold"))
        writer.forward(85)
        second_hand.setheading(6 * sekunde)
        minute_hand.setheading(6 * minute)
        hour_hand.setheading(30 * stunde)
        tracer(True)
        ontimer(tick, 100)
    except Terminator:
        pass


def main():
    tracer(False)
    setup()
    tracer(True)
    tick()
    return "EVENTLOOP"


if __name__ == "__main__":
    mode("logo")
    msg = main()
    print(msg)
    mainloop()

2.4 colormixer

运行后使用鼠标拖拉海龟可改变窗口背景颜色。

from turtle import Screen, Turtle, mainloop


class ColorTurtle(Turtle):
    def __init__(self, x, y):
        Turtle.__init__(self)
        self.shape("turtle")
        self.resizemode("user")
        self.shapesize(3, 3, 5)
        self.pensize(10)
        self._color = [0, 0, 0]
        self.x = x
        self._color[x] = y
        self.color(self._color)
        self.speed(0)
        self.left(90)
        self.pu()
        self.goto(x, 0)
        self.pd()
        self.sety(1)
        self.pu()
        self.sety(y)
        self.pencolor("gray25")
        self.ondrag(self.shift)

    def shift(self, x, y):
        self.sety(max(0, min(y, 1)))
        self._color[self.x] = self.ycor()
        self.fillcolor(self._color)
        setbgcolor()


def setbgcolor():
    screen.bgcolor(red.ycor(), green.ycor(), blue.ycor())


def main():
    global screen, red, green, blue
    screen = Screen()
    screen.delay(0)
    screen.setworldcoordinates(-1, -0.3, 3, 1.3)

    red = ColorTurtle(0, .5)
    green = ColorTurtle(1, .5)
    blue = ColorTurtle(2, .5)
    setbgcolor()

    writer = Turtle()
    writer.ht()
    writer.pu()
    writer.goto(1, 1.15)
    writer.write("拖拉海龟!", align="center", font=("Arial", 30, ("bold", "italic")))
    return "EVENTLOOP"


if __name__ == "__main__":
    msg = main()
    print(msg)
    mainloop()

2.5 forest

from random import randrange
from time import perf_counter as clock
from turtle import Turtle, colormode, tracer, mainloop


def symRandom(n):
    return randrange(-n, n + 1)


def randomize(branchlist, angledist, sizedist):
    return [(angle + symRandom(angledist),
             sizefactor * 1.01 ** symRandom(sizedist))
            for angle, sizefactor in branchlist]


def randomfd(t, distance, parts, angledist):
    for i in range(parts):
        t.left(symRandom(angledist))
        t.forward((1.0 * distance) / parts)


def tree(tlist, size, level, widthfactor, branchlists, angledist=10, sizedist=5):
    if level > 0:
        lst = []
        brs = []
        for t, branchlist in list(zip(tlist, branchlists)):
            t.pensize(size * widthfactor)
            t.pencolor(255 - (180 - 11 * level + symRandom(15)),
                       180 - 11 * level + symRandom(15),
                       0)
            t.pendown()
            randomfd(t, size, level, angledist)
            yield 1
            for angle, sizefactor in branchlist:
                t.left(angle)
                lst.append(t.clone())
                brs.append(randomize(branchlist, angledist, sizedist))
                t.right(angle)
        for x in tree(lst, size * sizefactor, level - 1, widthfactor, brs,
                      angledist, sizedist):
            yield None


def start(t, x, y):
    colormode(255)
    t.reset()
    t.speed(0)
    t.hideturtle()
    t.left(90)
    t.penup()
    t.setpos(x, y)
    t.pendown()


def doit1(level, pen):
    pen.hideturtle()
    start(pen, 20, -208)
    t = tree([pen], 80, level, 0.1, [[(45, 0.69), (0, 0.65), (-45, 0.71)]])
    return t


def doit2(level, pen):
    pen.hideturtle()
    start(pen, -135, -130)
    t = tree([pen], 120, level, 0.1, [[(45, 0.69), (-45, 0.71)]])
    return t


def doit3(level, pen):
    pen.hideturtle()
    start(pen, 190, -90)
    t = tree([pen], 100, level, 0.1, [[(45, 0.7), (0, 0.72), (-45, 0.65)]])
    return t


# 这里有 3 个树生成器:
def main():
    p = Turtle()
    p.ht()
    tracer(75, 0)
    u = doit1(6, Turtle(undobuffersize=1))
    s = doit2(7, Turtle(undobuffersize=1))
    t = doit3(5, Turtle(undobuffersize=1))
    a = clock()
    while True:
        done = 0
        for b in u, s, t:
            try:
                b.__next__()
            except:
                done += 1
        if done == 3:
            break

    tracer(1, 10)
    b = clock()
    return "runtime: %.2f sec." % (b - a)


if __name__ == '__main__':
    main()
    mainloop()

2.6 fractalcurves

from time import sleep, perf_counter as clock
from turtle import *


class CurvesTurtle(Pen):
    def hilbert(self, size, level, parity):
        if level == 0:
            return
        # 旋转并绘制与大曲线奇偶校验相反的第一条子曲线
        self.left(parity * 90)
        self.hilbert(size, level - 1, -parity)
        # 绘制与大曲线相同的第二条子曲线的接口
        self.forward(size)
        self.right(parity * 90)
        self.hilbert(size, level - 1, parity)
        # 第三条子曲线
        self.forward(size)
        self.hilbert(size, level - 1, parity)
        # 第四条子曲线
        self.right(parity * 90)
        self.forward(size)
        self.hilbert(size, level - 1, -parity)
        # 需要最后一圈才能使最终从大正方形面向外
        self.left(parity * 90)

    def fractalgon(self, n, rad, lev, dir):
        import math

        # 如果 dir = 1 向外转
        # 如果 dir = -1 向内转
        edge = 2 * rad * math.sin(math.pi / n)
        self.pu()
        self.fd(rad)
        self.pd()
        self.rt(180 - (90 * (n - 2) / n))
        for i in range(n):
            self.fractal(edge, lev, dir)
            self.rt(360 / n)
        self.lt(180 - (90 * (n - 2) / n))
        self.pu()
        self.bk(rad)
        self.pd()

    def fractal(self, dist, depth, dir):
        if depth < 1:
            self.fd(dist)
            return
        self.fractal(dist / 3, depth - 1, dir)
        self.lt(60 * dir)
        self.fractal(dist / 3, depth - 1, dir)
        self.rt(120 * dir)
        self.fractal(dist / 3, depth - 1, dir)
        self.lt(60 * dir)
        self.fractal(dist / 3, depth - 1, dir)


def main():
    ft = CurvesTurtle()

    ft.reset()
    ft.speed(0)
    ft.ht()
    ft.getscreen().tracer(1, 0)
    ft.pu()

    size = 6
    ft.setpos(-33 * size, -32 * size)
    ft.pd()

    ta = clock()
    ft.fillcolor("red")
    ft.begin_fill()
    ft.fd(size)

    ft.hilbert(size, 6, 1)

    # 框架
    ft.fd(size)
    for i in range(3):
        ft.lt(90)
        ft.fd(size * (64 + i % 2))
    ft.pu()
    for i in range(2):
        ft.fd(size)
        ft.rt(90)
    ft.pd()
    for i in range(4):
        ft.fd(size * (66 + i % 2))
        ft.rt(90)
    ft.end_fill()
    tb = clock()
    res = "Hilbert: %.2fsec. " % (tb - ta)

    sleep(1)

    ft.reset()
    ft.speed(0)
    ft.ht()
    ft.getscreen().tracer(1, 0)

    ta = clock()
    ft.color("black", "blue")
    ft.begin_fill()
    ft.fractalgon(3, 250, 4, 1)
    ft.end_fill()
    ft.begin_fill()
    ft.color("red")
    ft.fractalgon(3, 200, 4, -1)
    ft.end_fill()
    tb = clock()
    res += "Koch: %.2fsec." % (tb - ta)
    return res


if __name__ == '__main__':
    msg = main()
    print(msg)
    mainloop()

2.7 lindenmayer

from turtle import *


def replace(seq, replacementRules, n):
    for i in range(n):
        newseq = ""
        for element in seq:
            newseq = newseq + replacementRules.get(element, element)
        seq = newseq
    return seq


def draw(commands, rules):
    for b in commands:
        try:
            rules[b]()
        except TypeError:
            try:
                draw(rules[b], rules)
            except:
                pass


def main():
    def r():
        right(45)

    def l():
        left(45)

    def f():
        forward(7.5)

    snake_rules = {"-": r, "+": l, "f": f, "b": "f+f+f--f--f+f+f"}
    snake_replacementRules = {"b": "b+f+b--f--b+f+b"}
    snake_start = "b--f--b--f"

    drawing = replace(snake_start, snake_replacementRules, 3)

    reset()
    speed(3)
    tracer(1, 0)
    ht()
    up()
    backward(195)
    down()
    draw(drawing, snake_rules)

    from time import sleep
    sleep(3)

    def A():
        color("red")
        circle(10, 90)

    def B():
        from math import sqrt
        color("black")
        l = 5 / sqrt(2)
        forward(l)
        circle(l, 270)
        forward(l)

    def F():
        color("green")
        forward(10)

    krishna_rules = {"a": A, "b": B, "f": F}
    krishna_replacementRules = {"a": "afbfa", "b": "afbfbfbfa"}
    krishna_start = "fbfbfbfb"

    reset()
    speed(0)
    tracer(3, 0)
    ht()
    left(45)
    drawing = replace(krishna_start, krishna_replacementRules, 3)
    draw(drawing, krishna_rules)
    tracer(1)
    return "Done!"


if __name__ == '__main__':
    msg = main()
    print(msg)
    mainloop()

2.8 minimal_hanoi

from turtle import *


class Disc(Turtle):
    def __init__(self, n):
        Turtle.__init__(self, shape="square", visible=False)
        self.pu()
        self.shapesize(1.5, n * 1.5, 2)  # 正方形-->矩形
        self.fillcolor(n / 6., 0, 1 - n / 6.)
        self.st()


class Tower(list):
    # 河内塔,内置类型列表的子类

    def __init__(self, x):
        # 创建一个空塔。x 是 PEG 的 x 位置
        self.x = x

    def push(self, d):
        d.setx(self.x)
        d.sety(-150 + 34 * len(self))
        self.append(d)

    def pop(self):
        d = list.pop(self)
        d.sety(150)
        return d


def hanoi(n, from_, with_, to_):
    if n > 0:
        hanoi(n - 1, from_, to_, with_)
        to_.push(from_.pop())
        hanoi(n - 1, with_, from_, to_)


def play():
    onkey(None, "space")
    clear()
    try:
        hanoi(6, t1, t2, t3)
        write("结束!",
              align="center", font=("Courier", 16, "bold"))
    except Terminator:
        pass


def main():
    global t1, t2, t3
    ht();
    penup();
    goto(0, -225)  # 作家
    t1 = Tower(-250)
    t2 = Tower(0)
    t3 = Tower(250)
    # 制作 6 个圆盘塔
    for i in range(6, 0, -1):
        t1.push(Disc(i))
    # 准备用户界面
    write("按空格键开始游戏",
          align="center", font=("Courier", 16, "bold"))
    onkey(play, "space")
    listen()
    return "EVENTLOOP"


if __name__ == "__main__":
    msg = main()
    print(msg)
    mainloop()

2.9 nim

import random
import time
import turtle

SCREENWIDTH = 640
SCREENHEIGHT = 480

MINSTICKS = 7
MAXSTICKS = 31

HUNIT = SCREENHEIGHT // 12
WUNIT = SCREENWIDTH // ((MAXSTICKS // 5) * 11 + (MAXSTICKS % 5) * 2)

SCOLOR = (63, 63, 31)
HCOLOR = (255, 204, 204)
COLOR = (204, 204, 255)


def randomrow():
    return random.randint(MINSTICKS, MAXSTICKS)


def computerzug(state):
    xored = state[0] ^ state[1] ^ state[2]
    if xored == 0:
        return randommove(state)
    for z in range(3):
        s = state[z] ^ xored
        if s <= state[z]:
            move = (z, s)
            return move


def randommove(state):
    m = max(state)
    while True:
        z = random.randint(0, 2)
        if state[z] > (m > 1):
            break
    rand = random.randint(m > 1, state[z] - 1)
    return z, rand


class NimModel(object):
    def __init__(self, game):
        self.game = game

    def setup(self):
        if self.game.state not in [Nim.CREATED, Nim.OVER]:
            return
        self.sticks = [randomrow(), randomrow(), randomrow()]
        self.player = 0
        self.winner = None
        self.game.view.setup()
        self.game.state = Nim.RUNNING

    def move(self, row, col):
        maxspalte = self.sticks[row]
        self.sticks[row] = col
        self.game.view.notify_move(row, col, maxspalte, self.player)
        if self.game_over():
            self.game.state = Nim.OVER
            self.winner = self.player
            self.game.view.notify_over()
        elif self.player == 0:
            self.player = 1
            row, col = computerzug(self.sticks)
            self.move(row, col)
            self.player = 0

    def game_over(self):
        return self.sticks == [0, 0, 0]

    def notify_move(self, row, col):
        if self.sticks[row] <= col:
            return
        self.move(row, col)


class Stick(turtle.Turtle):
    def __init__(self, row, col, game):
        turtle.Turtle.__init__(self, visible=False)
        self.row = row
        self.col = col
        self.game = game
        x, y = self.coords(row, col)
        self.shape("square")
        self.shapesize(HUNIT / 10.0, WUNIT / 20.0)
        self.speed(0)
        self.pu()
        self.goto(x, y)
        self.color("white")
        self.showturtle()

    def coords(self, row, col):
        packet, remainder = divmod(col, 5)
        x = (3 + 11 * packet + 2 * remainder) * WUNIT
        y = (2 + 3 * row) * HUNIT
        return x - SCREENWIDTH // 2 + WUNIT // 2, SCREENHEIGHT // 2 - y - HUNIT // 2

    def makemove(self, x, y):
        if self.game.state != Nim.RUNNING:
            return
        self.game.controller.notify_move(self.row, self.col)


class NimView(object):
    def __init__(self, game):
        self.game = game
        self.screen = game.screen
        self.model = game.model
        self.screen.colormode(255)
        self.screen.tracer(False)
        self.screen.bgcolor((240, 240, 255))
        self.writer = turtle.Turtle(visible=False)
        self.writer.pu()
        self.writer.speed(0)
        self.sticks = {}
        for row in range(3):
            for col in range(MAXSTICKS):
                self.sticks[(row, col)] = Stick(row, col, game)
        self.display("... 请稍等片刻 ...")
        self.screen.tracer(True)

    def display(self, msg1, msg2=None):
        self.screen.tracer(False)
        self.writer.clear()
        if msg2 is not None:
            self.writer.goto(0, - SCREENHEIGHT // 2 + 48)
            self.writer.pencolor("red")
            self.writer.write(msg2, align="center", font=("Courier", 18, "bold"))
        self.writer.goto(0, - SCREENHEIGHT // 2 + 20)
        self.writer.pencolor("black")
        self.writer.write(msg1, align="center", font=("Courier", 14, "bold"))
        self.screen.tracer(True)

    def setup(self):
        self.screen.tracer(False)
        for row in range(3):
            for col in range(self.model.sticks[row]):
                self.sticks[(row, col)].color(SCOLOR)
        for row in range(3):
            for col in range(self.model.sticks[row], MAXSTICKS):
                self.sticks[(row, col)].color("white")
        self.display("轮到你了!点击最左边的摇杆删除。")
        self.screen.tracer(True)

    def notify_move(self, row, col, maxspalte, player):
        if player == 0:
            farbe = HCOLOR
            for s in range(col, maxspalte):
                self.sticks[(row, s)].color(farbe)
        else:
            self.display(" ... 思考 ...         ")
            time.sleep(0.5)
            self.display(" ... 思考 ... aaah ...")
            farbe = COLOR
            for s in range(maxspalte - 1, col - 1, -1):
                time.sleep(0.2)
                self.sticks[(row, s)].color(farbe)
            self.display("轮到你了!点击最左边的摇杆删除。")

    def notify_over(self):
        if self.game.model.winner == 0:
            msg2 = "恭喜。你是赢家!!!"
        else:
            msg2 = "对不起,电脑是赢家。"
        self.display("要再次游戏,请按空格键。要离开,请按 ESC 键。", msg2)

    def clear(self):
        if self.game.state == Nim.OVER:
            self.screen.clear()


class NimController(object):

    def __init__(self, game):
        self.game = game
        self.sticks = game.view.sticks
        self.BUSY = False
        for stick in self.sticks.values():
            stick.onclick(stick.makemove)
        self.game.screen.onkey(self.game.model.setup, "space")
        self.game.screen.onkey(self.game.view.clear, "Escape")
        self.game.view.display("按空格键开始游戏")
        self.game.screen.listen()

    def notify_move(self, row, col):
        if self.BUSY:
            return
        self.BUSY = True
        self.game.model.notify_move(row, col)
        self.BUSY = False


class Nim(object):
    CREATED = 0
    RUNNING = 1
    OVER = 2

    def __init__(self, screen):
        self.state = Nim.CREATED
        self.screen = screen
        self.model = NimModel(self)
        self.view = NimView(self)
        self.controller = NimController(self)


def main():
    mainscreen = turtle.Screen()
    mainscreen.mode("standard")
    mainscreen.setup(SCREENWIDTH, SCREENHEIGHT)
    nim = Nim(mainscreen)
    return "EVENTLOOP"


if __name__ == "__main__":
    main()
    turtle.mainloop()

2.10 paint

from turtle import *


def switchupdown(x=0, y=0):
    if pen()["pendown"]:
        end_fill()
        up()
    else:
        down()
        begin_fill()


def changecolor(x=0, y=0):
    global colors
    colors = colors[1:] + colors[:1]
    color(colors[0])


def main():
    global colors
    shape("circle")
    resizemode("user")
    shapesize(.5)
    width(3)
    colors = ["red", "green", "blue", "yellow"]
    color(colors[0])
    switchupdown()
    onscreenclick(goto, 1)
    onscreenclick(changecolor, 2)
    onscreenclick(switchupdown, 3)
    return "EVENTLOOP"


if __name__ == "__main__":
    msg = main()
    print(msg)
    mainloop()

2.11 peace

from turtle import *


def main():
    peacecolors = ("red3", "orange", "yellow",
                   "seagreen4", "orchid4",
                   "royalblue1", "dodgerblue4")
    reset()
    Screen()
    up()
    goto(-320, -195)
    width(70)
    for pcolor in peacecolors:
        color(pcolor)
        down()
        forward(640)
        up()
        backward(640)
        left(90)
        forward(66)
        right(90)
    width(25)
    color("white")
    goto(0, -170)
    down()
    circle(170)
    left(90)
    forward(340)
    up()
    left(180)
    forward(170)
    right(45)
    down()
    forward(170)
    up()
    backward(170)
    left(90)
    down()
    forward(170)
    up()
    goto(0, 300)
    return "Done!"


if __name__ == "__main__":
    main()
    mainloop()

2.12 penrose

from math import cos, pi
from time import perf_counter as clock, sleep
from turtle import *

f = (5 ** 0.5 - 1) / 2.0  # (sqrt(5)-1)/2 -- 黄金比例
d = 2 * cos(3 * pi / 10)


def kite(l):
    fl = f * l
    lt(36)
    fd(l)
    rt(108)
    fd(fl)
    rt(36)
    fd(fl)
    rt(108)
    fd(l)
    rt(144)


def dart(l):
    fl = f * l
    lt(36)
    fd(l)
    rt(144)
    fd(fl)
    lt(36)
    fd(fl)
    rt(144)
    fd(l)
    rt(144)


def inflatekite(l, n):
    if n == 0:
        px, py = pos()
        h, x, y = int(heading()), round(px, 3), round(py, 3)
        tiledict[(h, x, y)] = True
        return
    fl = f * l
    lt(36)
    inflatedart(fl, n - 1)
    fd(l)
    rt(144)
    inflatekite(fl, n - 1)
    lt(18)
    fd(l * d)
    rt(162)
    inflatekite(fl, n - 1)
    lt(36)
    fd(l)
    rt(180)
    inflatedart(fl, n - 1)
    lt(36)


def inflatedart(l, n):
    if n == 0:
        px, py = pos()
        h, x, y = int(heading()), round(px, 3), round(py, 3)
        tiledict[(h, x, y)] = False
        return
    fl = f * l
    inflatekite(fl, n - 1)
    lt(36)
    fd(l)
    rt(180)
    inflatedart(fl, n - 1)
    lt(54)
    fd(l * d)
    rt(126)
    inflatedart(fl, n - 1)
    fd(l)
    rt(144)


def draw(l, n, th=2):
    clear()
    l = l * f ** n
    shapesize(l / 100.0, l / 100.0, th)
    for k in tiledict:
        h, x, y = k
        setpos(x, y)
        setheading(h)
        if tiledict[k]:
            shape("kite")
            color("black", (0, 0.75, 0))
        else:
            shape("dart")
            color("black", (0.75, 0, 0))
        stamp()


def sun(l, n):
    for i in range(5):
        inflatekite(l, n)
        lt(72)


def star(l, n):
    for i in range(5):
        inflatedart(l, n)
        lt(72)


def makeshapes():
    tracer(0)
    begin_poly()
    kite(100)
    end_poly()
    register_shape("kite", get_poly())
    begin_poly()
    dart(100)
    end_poly()
    register_shape("dart", get_poly())
    tracer(1)


def start():
    reset()
    ht()
    pu()
    makeshapes()
    resizemode("user")


def test(l=200, n=4, fun=sun, startpos=(0, 0), th=2):
    global tiledict
    goto(startpos)
    setheading(0)
    tiledict = {}
    tracer(0)
    fun(l, n)
    draw(l, n, th)
    tracer(1)
    nk = len([x for x in tiledict if tiledict[x]])
    nd = len([x for x in tiledict if not tiledict[x]])
    print("%d kites and %d darts = %d pieces." % (nk, nd, nk + nd))


def demo(fun=sun):
    start()
    for i in range(8):
        a = clock()
        test(300, i, fun)
        b = clock()
        t = b - a
        if t < 2:
            sleep(2 - t)


def main():
    mode("logo")
    bgcolor(0.3, 0.3, 0)
    demo(sun)
    sleep(2)
    demo(star)
    pencolor("black")
    goto(0, -200)
    pencolor(0.7, 0.7, 1)
    write("请稍等...",
          align="center", font=('Arial Black', 36, 'bold'))
    test(600, 8, startpos=(70, 117))
    return "Done"


if __name__ == "__main__":
    msg = main()
    mainloop()

2.13 planet_and_moon

from turtle import Shape, Turtle, mainloop, Vec2D as Vec

G = 8


class GravSys(object):
    def __init__(self):
        self.planets = []
        self.t = 0
        self.dt = 0.01

    def init(self):
        for p in self.planets:
            p.init()

    def start(self):
        for i in range(10000):
            self.t += self.dt
            for p in self.planets:
                p.step()


class Star(Turtle):
    def __init__(self, m, x, v, gravSys, shape):
        Turtle.__init__(self, shape=shape)
        self.penup()
        self.m = m
        self.setpos(x)
        self.v = v
        gravSys.planets.append(self)
        self.gravSys = gravSys
        self.resizemode("user")
        self.pendown()

    def init(self):
        dt = self.gravSys.dt
        self.a = self.acc()
        self.v = self.v + 0.5 * dt * self.a

    def acc(self):
        a = Vec(0, 0)
        for planet in self.gravSys.planets:
            if planet != self:
                v = planet.pos() - self.pos()
                a += (G * planet.m / abs(v) ** 3) * v
        return a

    def step(self):
        dt = self.gravSys.dt
        self.setpos(self.pos() + dt * self.v)
        if self.gravSys.planets.index(self) != 0:
            self.setheading(self.towards(self.gravSys.planets[0]))
        self.a = self.acc()
        self.v = self.v + dt * self.a


# 为行星创建复合黄色/蓝色形状
def main():
    s = Turtle()
    s.reset()
    s.getscreen().tracer(0, 0)
    s.ht()
    s.pu()
    s.fd(6)
    s.lt(90)
    s.begin_poly()
    s.circle(6, 180)
    s.end_poly()
    m1 = s.get_poly()
    s.begin_poly()
    s.circle(6, 180)
    s.end_poly()
    m2 = s.get_poly()
    planetshape = Shape("compound")
    planetshape.addcomponent(m1, "orange")
    planetshape.addcomponent(m2, "blue")
    s.getscreen().register_shape("planet", planetshape)
    s.getscreen().tracer(1, 0)
    # 设置重力系统
    gs = GravSys()
    sun = Star(1000000, Vec(0, 0), Vec(0, -2.5), gs, "circle")
    sun.color("yellow")
    sun.shapesize(1.8)
    sun.pu()
    earth = Star(12500, Vec(210, 0), Vec(0, 195), gs, "planet")
    earth.pencolor("green")
    earth.shapesize(0.8)
    moon = Star(1, Vec(220, 0), Vec(0, 295), gs, "planet")
    moon.pencolor("blue")
    moon.shapesize(0.5)
    gs.init()
    gs.start()
    return "Done!"


if __name__ == '__main__':
    main()
    mainloop()

2.14 rosette

from time import perf_counter as clock, sleep
from turtle import Screen, Turtle, mainloop


def mn_eck(p, ne, sz):
    turtlelist = [p]
    # 创建 NE-1 附加海龟
    for i in range(1, ne):
        q = p.clone()
        q.rt(360.0 / ne)
        turtlelist.append(q)
        p = q
    for i in range(ne):
        c = abs(ne / 2.0 - i) / (ne * .7)
        # 让那些ne海龟迈出一步
        # 并行:
        for t in turtlelist:
            t.rt(360. / ne)
            t.pencolor(1 - c, 0, c)
            t.fd(sz)


def main():
    s = Screen()
    s.bgcolor("black")
    p = Turtle()
    p.speed(0)
    p.hideturtle()
    p.pencolor("red")
    p.pensize(3)
    s.tracer(36, 0)
    at = clock()
    mn_eck(p, 36, 19)
    et = clock()
    z1 = et - at
    sleep(1)
    at = clock()
    while any(t.undobufferentries() for t in s.turtles()):
        for t in s.turtles():
            t.undo()
    et = clock()
    return "runtime: %.3f sec" % (z1 + et - at)


if __name__ == '__main__':
    msg = main()
    print(msg)
    mainloop()

2.15 round_dance

执行后按任意键停止动画。

from turtle import *


def stop():
    global running
    running = False


def main():
    global running
    clearscreen()
    bgcolor("gray10")
    tracer(False)
    shape("triangle")
    f = 0.793402
    phi = 9.064678
    s = 5
    c = 1
    # 创建复合形状
    sh = Shape("compound")
    for i in range(10):
        shapesize(s)
        p = get_shapepoly()
        s *= f
        c *= f
        tilt(-phi)
        sh.addcomponent(p, (c, 0.25, 1 - c), "black")
    register_shape("multitri", sh)
    # 创建舞者
    shapesize(1)
    shape("multitri")
    pu()
    setpos(0, -200)
    dancers = []
    for i in range(180):
        fd(7)
        tilt(-4)
        lt(2)
        update()
        if i % 12 == 0:
            dancers.append(clone())
    home()
    # 舞动
    running = True
    onkeypress(stop)
    listen()
    cs = 1
    while running:
        ta = -4
        for dancer in dancers:
            dancer.fd(7)
            dancer.lt(2)
            dancer.tilt(ta)
            ta = -4 if ta > 0 else 2
        if cs < 180:
            right(4)
            shapesize(cs)
            cs *= 1.005
        update()
    return "DONE!"


if __name__ == '__main__':
    print(main())
    mainloop()

2.16 sorting_animate

import random
from turtle import *


class Block(Turtle):

    def __init__(self, size):
        self.size = size
        Turtle.__init__(self, shape="square", visible=False)
        self.pu()
        self.shapesize(size * 1.5, 1.5, 2)  # 正方形-->矩形
        self.fillcolor("black")
        self.st()

    def glow(self):
        self.fillcolor("red")

    def unglow(self):
        self.fillcolor("black")

    def __repr__(self):
        return "Block size: {0}".format(self.size)


class Shelf(list):

    def __init__(self, y):
        "create a shelf. y is y-position of first block"
        self.y = y
        self.x = -150

    def push(self, d):
        width, _, _ = d.shapesize()
        # 通过底部边缘对齐块
        y_offset = width / 2 * 20
        d.sety(self.y + y_offset)
        d.setx(self.x + 34 * len(self))
        self.append(d)

    def _close_gap_from_i(self, i):
        for b in self[i:]:
            xpos, _ = b.pos()
            b.setx(xpos - 34)

    def _open_gap_from_i(self, i):
        for b in self[i:]:
            xpos, _ = b.pos()
            b.setx(xpos + 34)

    def pop(self, key):
        b = list.pop(self, key)
        b.glow()
        b.sety(200)
        self._close_gap_from_i(key)
        return b

    def insert(self, key, b):
        self._open_gap_from_i(key)
        list.insert(self, key, b)
        b.setx(self.x + 34 * key)
        width, _, _ = b.shapesize()
        # 通过底部边缘对齐块
        y_offset = width / 2 * 20
        b.sety(self.y + y_offset)
        b.unglow()


def isort(shelf):
    length = len(shelf)
    for i in range(1, length):
        hole = i
        while hole > 0 and shelf[i].size < shelf[hole - 1].size:
            hole = hole - 1
        shelf.insert(hole, shelf.pop(i))
    return


def ssort(shelf):
    length = len(shelf)
    for j in range(0, length - 1):
        imin = j
        for i in range(j + 1, length):
            if shelf[i].size < shelf[imin].size:
                imin = i
        if imin != j:
            shelf.insert(j, shelf.pop(imin))


def partition(shelf, left, right, pivot_index):
    pivot = shelf[pivot_index]
    shelf.insert(right, shelf.pop(pivot_index))
    store_index = left
    for i in range(left, right):  # 范围不包括末尾的值
        if shelf[i].size < pivot.size:
            shelf.insert(store_index, shelf.pop(i))
            store_index = store_index + 1
    shelf.insert(store_index, shelf.pop(right))  # 将枢轴移动到正确位置
    return store_index


def qsort(shelf, left, right):
    if left < right:
        pivot_index = left
        pivot_new_index = partition(shelf, left, right, pivot_index)
        qsort(shelf, left, pivot_new_index - 1)
        qsort(shelf, pivot_new_index + 1, right)


def randomize():
    disable_keys()
    clear()
    target = list(range(10))
    random.shuffle(target)
    for i, t in enumerate(target):
        for j in range(i, len(s)):
            if s[j].size == t + 1:
                s.insert(i, s.pop(j))
    show_text(instructions1)
    show_text(instructions2, line=1)
    enable_keys()


def show_text(text, line=0):
    line = 20 * line
    goto(0, -250 - line)
    write(text, align="center", font=("Courier", 16, "bold"))


def start_ssort():
    disable_keys()
    clear()
    show_text("选择排序")
    ssort(s)
    clear()
    show_text(instructions1)
    show_text(instructions2, line=1)
    enable_keys()


def start_isort():
    disable_keys()
    clear()
    show_text("插入排序")
    isort(s)
    clear()
    show_text(instructions1)
    show_text(instructions2, line=1)
    enable_keys()


def start_qsort():
    disable_keys()
    clear()
    show_text("快速排序")
    qsort(s, 0, len(s) - 1)
    clear()
    show_text(instructions1)
    show_text(instructions2, line=1)
    enable_keys()


def init_shelf():
    global s
    s = Shelf(-200)
    vals = (4, 2, 8, 9, 1, 5, 10, 3, 7, 6)
    for i in vals:
        s.push(Block(i))


def disable_keys():
    onkey(None, "s")
    onkey(None, "i")
    onkey(None, "q")
    onkey(None, "r")


def enable_keys():
    onkey(start_isort, "i")
    onkey(start_ssort, "s")
    onkey(start_qsort, "q")
    onkey(randomize, "r")
    onkey(bye, "space")


def main():
    getscreen().clearscreen()
    ht()
    penup()
    init_shelf()
    show_text(instructions1)
    show_text(instructions2, line=1)
    enable_keys()
    listen()
    return "EVENTLOOP"


instructions1 = "按 i 进行插入排序,按 S 进行选择排序,按 Q 进行快速排序"
instructions2 = "空格键退出,R 随机化"

if __name__ == "__main__":
    msg = main()
    mainloop()

2.17 tree

from time import perf_counter as clock
from turtle import Turtle, mainloop


def tree(plist, l, a, f):
    # plist 是笔列表 l 是分支的长度 a 是 2 个分支之间夹角的一半 f 是分支从一层缩短到另一层的系数
    if l > 3:
        lst = []
        for p in plist:
            p.forward(l)
            q = p.clone()
            p.left(a)
            q.right(a)
            lst.append(p)
            lst.append(q)
        for x in tree(lst, l * f, a, f):
            yield None


def maketree():
    p = Turtle()
    p.setundobuffer(None)
    p.hideturtle()
    p.speed(0)
    p.getscreen().tracer(30, 0)
    p.left(90)
    p.penup()
    p.forward(-210)
    p.pendown()
    t = tree([p], 200, 65, 0.6375)
    for x in t:
        pass


def main():
    a = clock()
    maketree()
    b = clock()
    return "done: %.2f sec." % (b - a)


if __name__ == "__main__":
    msg = main()
    print(msg)
    mainloop()

2.18 two_canvases

from turtle import TurtleScreen, RawTurtle, TK


def main():
    root = TK.Tk()
    cv1 = TK.Canvas(root, width=300, height=200, bg="#ddffff")
    cv2 = TK.Canvas(root, width=300, height=200, bg="#ffeeee")
    cv1.pack()
    cv2.pack()

    s1 = TurtleScreen(cv1)
    s1.bgcolor(0.85, 0.85, 1)
    s2 = TurtleScreen(cv2)
    s2.bgcolor(1, 0.85, 0.85)

    p = RawTurtle(s1)
    q = RawTurtle(s2)

    p.color("red", (1, 0.85, 0.85))
    p.width(3)
    q.color("blue", (0.85, 0.85, 1))
    q.width(3)

    for t in p, q:
        t.shape("turtle")
        t.lt(36)

    q.lt(180)

    for t in p, q:
        t.begin_fill()
    for i in range(5):
        for t in p, q:
            t.fd(50)
            t.lt(72)
    for t in p, q:
        t.end_fill()
        t.lt(54)
        t.pu()
        t.bk(50)

    return "EVENTLOOP"


if __name__ == '__main__':
    main()
    TK.mainloop()

2.19 yinyang

from turtle import *


def yin(radius, color1, color2):
    width(3)
    color("black", color1)
    begin_fill()
    circle(radius / 2., 180)
    circle(radius, 180)
    left(180)
    circle(-radius / 2., 180)
    end_fill()
    left(90)
    up()
    forward(radius * 0.35)
    right(90)
    down()
    color(color1, color2)
    begin_fill()
    circle(radius * 0.15)
    end_fill()
    left(90)
    up()
    backward(radius * 0.35)
    down()
    left(90)


def main():
    reset()
    yin(200, "black", "white")
    yin(200, "white", "black")
    ht()
    return "Done!"


if __name__ == '__main__':
    main()
    mainloop()
;