Commit eaa84ef1 authored by Georg Brandl's avatar Georg Brandl

#5923: update turtle module to version 1.1.

parent 51335296
""" turtle-example-suite:
tdemo_nim.py
Play nim against the computer. The player
who takes the last stick is the winner.
Implements the model-view-controller
design pattern.
"""
import turtle
import random
import time
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("... a moment please ...")
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("Your turn! Click leftmost stick to remove.")
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(" ... thinking ... ")
time.sleep(0.5)
self.display(" ... thinking ... aaah ...")
farbe = COLOR
for s in range(maxspalte-1, col-1, -1):
time.sleep(0.2)
self.sticks[(row, s)].color(farbe)
self.display("Your turn! Click leftmost stick to remove.")
def notify_over(self):
if self.game.model.winner == 0:
msg2 = "Congrats. You're the winner!!!"
else:
msg2 = "Sorry, the computer is the winner."
self.display("To play again press space bar. To leave press 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("Press space bar to start game")
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)
mainscreen = turtle.Screen()
mainscreen.mode("standard")
mainscreen.setup(SCREENWIDTH, SCREENHEIGHT)
def main():
nim = Nim(mainscreen)
return "EVENTLOOP!"
if __name__ == "__main__":
main()
turtle.mainloop()
""" turtle-example-suite:
tdemo_round_dance.py
(Needs version 1.1 of the turtle module that
comes with Python 3.1)
Dancing turtles have a compound shape
consisting of a series of triangles of
decreasing size.
Turtles march along a circle while rotating
pairwise in opposite direction, with one
exception. Does that breaking of symmetry
enhance the attractiveness of the example?
Press any key to stop the animation.
Technically: demonstrates use of compound
shapes, transformation of shapes as well as
cloning turtles. The animation is
controlled through update().
"""
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
# create compound shape
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)
# create dancers
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()
# dance
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()
......@@ -149,9 +149,12 @@ Turtle state
| :func:`shape`
| :func:`resizemode`
| :func:`shapesize` | :func:`turtlesize`
| :func:`shearfactor`
| :func:`settiltangle`
| :func:`tiltangle`
| :func:`tilt`
| :func:`shapetransform`
| :func:`get_shapepoly`
Using events
| :func:`onclick`
......@@ -187,9 +190,11 @@ Animation control
Using screen events
| :func:`listen`
| :func:`onkey`
| :func:`onkey` | :func:`onkeyrelease`
| :func:`onkeypress`
| :func:`onclick` | :func:`onscreenclick`
| :func:`ontimer`
| :func:`mainloop`
Settings and special methods
| :func:`mode`
......@@ -201,6 +206,10 @@ Settings and special methods
| :func:`window_height`
| :func:`window_width`
Input methods
| :func:`textinput`
| :func:`numinput`
Methods specific to Screen
| :func:`bye`
| :func:`exitonclick`
......@@ -1157,6 +1166,26 @@ Appearance
(5, 5, 8)
.. function:: shearfactor(self, shear=None):
:param shear: number (optional)
Set or return the current shearfactor. Shear the turtleshape according to
the given shearfactor shear, which is the tangent of the shear angle.
Do *not* change the turtle's heading (direction of movement).
If shear is not given: return the current shearfactor, i. e. the
tangent of the shear angle, by which lines parallel to the
heading of the turtle are sheared.
.. doctest::
>>> turtle.shape("circle")
>>> turtle.shapesize(5,2)
>>> turtle.shearfactor(0.5)
>>> turtle.shearfactor()
>>> 0.5
.. function:: tilt(angle)
:param angle: a number
......@@ -1194,10 +1223,19 @@ Appearance
>>> turtle.fd(50)
.. function:: tiltangle()
.. function:: tiltangle(angle=None)
Return the current tilt-angle, i.e. the angle between the orientation of the
turtleshape and the heading of the turtle (its direction of movement).
:param angle: a number (optional)
Set or return the current tilt-angle. If angle is given, rotate the
turtleshape to point in the direction specified by angle,
regardless of its current tilt-angle. Do *not* change the turtle's
heading (direction of movement).
If angle is not given: return the current tilt-angle, i. e. the angle
between the orientation of the turtleshape and the heading of the
turtle (its direction of movement).
Deprecated since Python 3.1
.. doctest::
......@@ -1209,6 +1247,46 @@ Appearance
45.0
.. function:: shapetransform(t11=None, t12=None, t21=None, t22=None)
:param t11: a number (optional)
:param t12: a number (optional)
:param t21: a number (optional)
:param t12: a number (optional)
Set or return the current transformation matrix of the turtle shape.
If none of the matrix elements are given, return the transformation
matrix as a tuple of 4 elements.
Otherwise set the given elements and transform the turtleshape
according to the matrix consisting of first row t11, t12 and
second row t21, 22. The determinant t11 * t22 - t12 * t21 must not be
zero, otherwise an error is raised.
Modify stretchfactor, shearfactor and tiltangle according to the
given matrix.
.. doctest::
>>> turtle.shape("square")
>>> turtle.shapesize(4,2)
>>> turtle.shearfactor(-0.5)
>>> turtle.shapetransform()
>>> (4.0, -1.0, -0.0, 2.0)
.. function:: get_shapepoly():
Return the current shape polygon as tuple of coordinate pairs. This
can be used to define a new shape or components of a compound shape.
.. doctest::
>>> turtle.shape("square")
>>> turtle.shapetransform(4, -1, 0, 2)
>>> turtle.get_shapepoly()
((50, -20), (30, 20), (-50, 20), (-30, -20))
Using events
------------
......@@ -1595,6 +1673,7 @@ Using screen events
.. function:: onkey(fun, key)
onkeyrelease(fun, key)
:param fun: a function with no arguments or ``None``
:param key: a string: key (e.g. "a") or key-symbol (e.g. "space")
......@@ -1613,6 +1692,25 @@ Using screen events
>>> screen.listen()
.. function:: onkeypress(fun, key=None):
:param fun: a function with no arguments or ``None``
:param key: a string: key (e.g. "a") or key-symbol (e.g. "space")
Bind *fun* to key-press event of key if key is given,
or to any key-press-event if no key is given.
Remark: in order to be able to register key-events, TurtleScreen
must have focus. (See method :func:`listen`.)
.. doctest::
>>> def f():
... fd(50)
...
>>> screen.onkey(f, "Up")
>>> screen.listen()
.. function:: onclick(fun, btn=1, add=None)
onscreenclick(fun, btn=1, add=None)
......@@ -1659,6 +1757,53 @@ Using screen events
>>> running = False
.. function:: mainloop()
Starts event loop - calling Tkinter's mainloop function.
Must be the last statement in a turtle graphics program.
Must *not* be used if a script is run from within IDLE in -n mode
(No subprocess) - for interactive use of turtle graphics. ::
>>> screen.mainloop()
Input methods
-------------
.. function:: textinput(title, prompt)
:param title: string
:param prompt: string
Pop up a dialog window for input of a string. Parameter title is
the title of the dialog window, propmt is a text mostly describing
what information to input.
Return the string input. If the dialog is canceled, return None. ::
>>> screen.textinput("NIM", "Name of first player:")
.. function:: numinput(self, title, prompt,
default=None, minval=None, maxval=None):
:param title: string
:param prompt: string
:param default: number (optional)
:param prompt: number (optional)
:param prompt: number (optional)
Pop up a dialog window for input of a number. title is the title of the
dialog window, prompt is a text mostly describing what numerical information
to input. default: default value, minval: minimum value for imput,
maxval: maximum value for input
The number input must be in the range minval .. maxval if these are
given. If not, a hint is issued and the dialog remains open for
correction.
Return the number input. If the dialog is canceled, return None. ::
>>> screen.numinput("Poker", "Your stakes:", 1000, minval=10, maxval=10000)
Settings and special methods
----------------------------
......@@ -2159,6 +2304,10 @@ The demoscripts are:
| | | as Hanoi discs |
| | | (shape, shapesize) |
+----------------+------------------------------+-----------------------+
| nim | play the classical nim game | turtles as nimsticks, |
| | with three heaps of sticks | event driven (mouse, |
| | against the computer. | keyboard) |
+----------------+------------------------------+-----------------------+
| paint | super minimalistic | :func:`onclick` |
| | drawing program | |
+----------------+------------------------------+-----------------------+
......@@ -2171,6 +2320,10 @@ The demoscripts are:
| planet_and_moon| simulation of | compound shapes, |
| | gravitational system | :class:`Vec2D` |
+----------------+------------------------------+-----------------------+
| round_dance | dancing turtles rotating | compound shapes, clone|
| | pairwise in opposite | shapesize, tilt, |
| | direction | get_polyshape, update |
+----------------+------------------------------+-----------------------+
| tree | a (graphical) breadth | :func:`clone` |
| | first tree (using generators)| |
+----------------+------------------------------+-----------------------+
......@@ -2204,6 +2357,32 @@ Changes since Python 2.6
This behaviour corresponds to a ``fill()`` call without arguments in
Python 2.6.
Changes since Python 3.0
========================
- The methods :meth:`Turtle.shearfactor`, :meth:`Turtle.shapetransform` and
:meth:`Turtle.get_shapepoly` have been added. Thus the full range of
regular linear transforms is now available for transforming turtle shapes.
:meth:`Turtle.tiltangle` has been enhanced in functionality: it now can
be used to get or set the tiltangle. :meth:`Turtle.settiltangle` has been
deprecated.
- The method :meth:`Screen.onkeypress` has been added as a complement to
:meth:`Screen.onkey` which in fact binds actions to the keyrelease event.
Accordingly the latter has got an alias: :meth:`Screen.onkeyrelease`.
- The method :meth:`Screen.mainloop` has been added. So when working only
with Screen and Turtle objects one must not additonally import
:func:`mainloop` anymore.
- Two input methods has been added :meth:`Screen.textinput` and
:meth:`Screen.numinput`. These popup input dialogs and return
strings and numbers respectively.
- Two example scripts :file:`tdemo_nim.py` and :file:`tdemo_round_dance.py`
have been added to the Demo directory (source distribution only). As usual
they can be viewed and executed within the demo viewer :file:`turtleDemo.py`.
.. doctest::
:hide:
......
This diff is collapsed.
......@@ -118,6 +118,9 @@ Installation
Library
-------
- Issue #5923: Update the ``turtle`` module to version 1.1, add two new
turtle demos in Demo/turtle.
- Issue #5692: In :class:`zipfile.Zipfile`, fix wrong path calculation when
extracting a file to the root directory.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment