11. Korytnačky (turtle)

Dnes sa naučíme v Pythone pracovať trochu s inou grafikou ako sa pracovalo pomocou tkinter. Napr.

import tkinter
canvas = tkinter.Create()                    # vytvor grafickú plochu
canvas.pack()                                # zobraz ju do okna
g.create_oval(100, 50, 150, 80, fill='red')  # nakresli červenú elipsu
...

Pri programovaní takýchto úloh sme počítali s tým, že:

  • počiatočný bod (0, 0) je v ľavom hornom rohu grafickej plochy

  • x-ová súradnica ide vpravo vodorovne po hornej hrane grafickej plochy

  • y-ová súradnica ide nadol zvislo po ľavej hrane grafickej plochy: smerom nadol sú kladné y-ové hodnoty, smerom nahor idú záporné hodnoty súradníc

  • všetky kreslené útvary sú umiestnené absolútne k bodu (0, 0)

  • otáčanie útvaru o nejaký uhol, resp. rozmiestňovanie bodov na kružnici môže byť dosť náročné a väčšinou vyžaduje použitie math.sin() a math.cos()

Korytnačia grafika

Korytnačka je grafické pero (grafický robot), ktoré si okrem pozície v grafickej ploche (pos()) pamätá aj smer natočenia (heading()). Korytnačku vytvárame podobne ako v tkinter grafickú plochu:

>>> import turtle
>>> t = turtle.Turtle()
>>> t.pos()
(0.00,0.00)
>>> t.heading()
0.0

Príkaz t = turtle.Turtle() vytvorí grafickú plochu a v jej strede korytnačku s natočením na východ. Volanie t.pos() vráti momentálnu pozíciu korytnačky (0, 0) a t.heading() vráti uhol 0.

Pre grafickú plochu korytnačej grafiky platí:

  • súradná sústava má počiatok v strede grafickej plochy

  • x-ová súradnica ide vpravo vodorovne od počiatku

  • y-ová súradnica ide nahor zvislo od počiatku: smerom nahor sú kladné y-ové hodnoty, smerom nadol idú záporné hodnoty súradníc

  • smer natočenia určujeme v stupňoch (nie v radiánoch) a v protismere otáčania hodinových ručičiek:

    • na východ je to 0

    • na sever je to 90

    • na západ je to 180

    • na juh je to 270

  • pozícia a smer korytnačky je vizualizovaná malým čiernym trojuholníkom - keď sa bude korytnačka hýbať alebo otáčať, bude sa hýbať tento trojuholník.

Základnými príkazmi sú forward(dĺžka), ktorý posunie korytnačku v momentálnom smere o zadanú dĺžku a right(uhol), ktorý otočí korytnačku o zadaný uhol vpravo, napr.

>>> import turtle
>>> t = turtle.Turtle()
>>> t.forward(100)
>>> t.right(90)
>>> t.forward(100)
>>> t.right(90)
>>> t.forward(100)
>>> t.right(90)
>>> t.forward(100)

nakreslí štvorec so stranou 100. Častejšie budeme používať skrátené zápisy týchto príkazov: fd(), rt() a lt() (je skratkou príkazu left(), teda otočenie vľavo).

mainloop()

Takýto spôsob postupného testovania korytnačích príkazov a programov s korytnačkami nemusí fungovať mimo IDLE. Niektorí z vás už máte skúsenosti z iných vývojových prostredí, že pri práci s grafikou musíte na záver programu pripísať canvas.mainloop(). Pri práci s korytnačou grafikou Python tiež využíva tkinter, preto aj v tomto prípade musíme na záver programu zadať napr.

turtle.mainloop()

Pripadne môžeme namiesto tohto zapísať

turtle.exitonclick()

čo bude označovať, že grafické okno sa automaticky zatvorí po kliknutí niekam do okna.

Zapíšme funkciu, pomocou ktorej korytnačka nakreslí štvorec:

import turtle

def stvorec(dlzka):
    for i in range(4):
        t.fd(dlzka)
        t.rt(90)

t = turtle.Turtle()
stvorec(100)
t.lt(70)
t.fd(80)
stvorec(50)
_images/11_1.png

Program nakreslí dva rôzne štvorce - druhý je posunutý a trochu otočený.

Aby sa nám ľahšie experimentovalo s korytnačkou, nemusíme stále reštartovať shell z programového režimu (napr. klávesom F5). Keď máme vytvorenú korytnačku t, stačí zmazať kresbu pomocou:

>>> t.clear()     # zmaže grafickú plochu a korytnačku nechá tam, kde sa momentálne nachádza

alebo pri zmazaní plochy aj inicializuje korytnačku v strede plochy otočenú na východ:

>>> t.reset()     # zmaže grafickú plochu a inicializuje korytnačku

a teraz znovu kreslíme už do prázdnej plochy.

Korytnačka má pero, ktorým kreslí pri svojom pohybe po grafickej ploche. Toto pero môžeme zdvihnúť (pen up) - odteraz sa pohybuje bez kreslenia, alebo spustiť (pen down) - opäť bude pri pohybe kresliť. Na to máme dva príkazy penup() alebo pendown(), prípadne ich skratky pu() alebo pd(). Predchádzajúci príklad doplníme o dvíhanie pera:

import turtle

def stvorec(dlzka):
    for i in range(4):
        t.fd(dlzka)
        t.rt(90)

t = turtle.Turtle()
stvorec(100)
t.pu()
t.lt(70)
t.fd(80)
t.pd()
stvorec(50)
_images/11_2.png

Napíšme funkciu posun(), ktorá presunie korytnačku na náhodnú pozíciu v ploche a dá jej aj náhodný smer:

import turtle
from random import randrange

def posun():
    t.pu()
    t.setpos(randrange(-300, 300), randrange(-300, 300))
    t.seth(randrange(360))
    t.pd()

def stvorec(dlzka):
    for i in range(4):
        t.fd(dlzka)
        t.rt(90)

t = turtle.Turtle()
for i in range(10):
    posun()
    stvorec(30)
_images/11_3.png
  • funkcia na náhodné pozície nakreslí 10 malých štvorcov

  • použili sme tu dva nové príkazy: setpos(x, y), ktorá presunie korytnačku na novú pozíciu a seth(uhol) (skratka z setheading()), ktorá otočí korytnačku do daného smeru

Grafickému peru korytnačky môžeme meniť hrúbku a farbu:

  • príkaz pencolor(farba) zmení farbu pera - odteraz bude korytnačka všetko kresliť touto farbou, až kým ju opäť nezmeníme

  • príkaz pensize(hrúbka) zmení hrúbku pera (celé kladné číslo) - odteraz bude korytnačka všetko kresliť touto hrúbkou, až kým ju opäť nezmeníme

V nasledovnom príklade uvidíme aj príkaz turtle.delay(), ktorým môžeme urýchliť (alebo spomaliť) pohyb korytnačky (rýchlosť turtle.delay(0) je najrýchlejšia, turtle.delay(10) je pomalšia - parameter hovorí počet milisekúnd, ktorým sa zdržuje každé kreslenie):

import turtle
from random import randrange

def stvorec(dlzka):
    for i in range(4):
        t.fd(dlzka)
        t.rt(90)

def trojuholnik(dlzka):
    for i in range(3):
        t.fd(dlzka)
        t.rt(120)

def posun():
    t.pu()
    t.setpos(randrange(-300, 300), randrange(-300, 300))
    t.seth(randrange(360))
    t.pd()

turtle.delay(0)
t = turtle.Turtle()
t.pensize(5)
for i in range(20):
    posun()
    if randrange(2):
        t.pencolor('red')
        stvorec(30)
    else:
        t.pencolor('blue')
        trojuholnik(30)
_images/11_4.png

Program na náhodné pozície umiestni červené štvorce alebo modré trojuholníky. Zrejme korytnačka je v globálnej premennej t (v hlavnom mennom priestore) a teda na ňu vidia všetky naše funkcie.

Ďalší príklad predvedie funkciu, ktorá nakreslí ľubovoľný rovnostranný n-uholník a tiež príkaz clear(), ktorý zmaže nakreslený obrázok, aby sa mohol kresliť ďalší už v prázdnej grafickej ploche:

import turtle

def n_uholnik(n, d):
    for i in range(n):
        t.fd(d)
        t.lt(360 / n)

t = turtle.Turtle()
for n in range(3, 16):
    t.clear()
    n_uholnik(n, 100)
_images/11_5.png
  • ak by sme vyhodili príkaz clear(), mohli by sme v jednej kresbe vidieť všetky n-uholníky

Pomocou n-uholníkov môžeme nakresliť aj kružnicu (napr. ako 36-uholník s malou dĺžkou strany), ale aj len časti kružníc, napr. 18 strán z 36-uholníka nakreslí polkruh, a 9 strán nakreslí štvrťkruh.

Nasledovný príklad najprv definuje oblúk (štvrťkruh), potom lupen (dva priložené štvrťkruhy) a nakoniec kvet ako n lupeňov:

import turtle

def obluk(d):
    for i in range(9):
        t.fd(d)
        t.rt(10)

def lupen(d):
    for i in 1, 2:
        obluk(d)
        t.rt(90)

def kvet(n, d):
    for i in range(n):
        lupen(d)
        t.rt(360 / n)

turtle.delay(0)
t = turtle.Turtle()
kvet(10, 20)
_images/11_6.png

Vyfarbenie útvaru

Útvary, ktoré nakreslí korytnačka sa dajú aj vyfarbiť. Predpokladajme, že korytnačka nakreslí nejaký útvar (napr. štvorec), a potom ho môže farbou výplne vyfarbiť. Princíp fungovania vypĺňania nejakou farbou je takýto:

  • na začiatok postupnosti korytnačích príkazov, ktoré definujú obrys útvaru, umiestnime príkaz begin_fill()

  • na koniec tejto postupnosti dáme príkaz end_fill(), ktorý vyfarbí nakreslený útvar farbou výplne

  • farbu výplne meníme príkazom fillcolor(farba) (na začiatku je nastavená čierna farba)

  • ak nakreslíme krivku, ktorá netvorí uzavretý útvar, pri vypĺňaní sa táto uzavrie

Napr. nakreslíme farebné štvorce v jednom rade:

import turtle

def stvorec(d):
    for i in range(4):
        t.fd(d)
        t.rt(90)

turtle.delay(0)
t = turtle.Turtle()
t.fillcolor('red')
for i in range(5):
    t.begin_fill()
    stvorec(50)
    t.end_fill()
    t.pu()
    t.fd(60)
    t.pd()
_images/11_7.png

Nakreslíme kvet zložený z farebných lupeňov (každý lupeň inou farbou):

import turtle

def obluk(d):
    for i in range(9):
        t.fd(d)
        t.rt(10)

def lupen(d):
    for i in 1, 2:
        obluk(d)
        t.rt(90)

def kvet(d, farby):
    for f in farby:
        t.fillcolor(f)
        t.begin_fill()
        lupen(d)
        t.end_fill()
        t.rt(360 / len(farby))

turtle.delay(0)
t = turtle.Turtle()
kvet(20, ['red', 'blue', 'yellow', 'magenta', 'green', 'orange', 'violet'])
_images/11_8.png

Špirály

Rôzne špirály môžeme kresliť tak, že opakujeme kreslenie stále sa zväčšujúcich čiar a za každým sa otočíme o pevný uhol, napr.

import turtle

t = turtle.Turtle()
t.lt(30)
for i in range(3, 300, 3):
    t.fd(i)
    t.rt(90)
_images/11_9.png

Program nakreslí štvorcovú špirálu.

S uhlami môžete experimentovať, napr.

import turtle
from random import randrange

turtle.delay(0)
t = turtle.Turtle()
while True:
    uhol = randrange(30, 170)
    print('spirala s uhlom', uhol)
    for i in range(3, 300, 3):
        t.fd(i)
        t.rt(uhol)
    t.reset()
_images/11_10.png

Tento program kreslí špirály s rôznymi náhodne generovanými uhlami. Zároveň do textovej plochy vypisuje informáciu o uhle momentálne kreslenej špirály.

Zaujímavé špirály vznikajú, keď nemeníme dĺžku čiar ale uhol, napr.

import turtle

turtle.delay(0)
t = turtle.Turtle()
for uhol in range(1, 2000):
    t.fd(8)
    t.rt(uhol)
_images/11_11.png

Tu môžeme vyskúšať rôzne malé zmeny uhla, o ktorý sa mení kreslenie čiar útvaru:

import turtle

turtle.delay(0)
t = turtle.Turtle()
for uhol in range(1, 2000):
    t.fd(8)
    t.rt(uhol + 0.1)
_images/11_12.png

Vyskúšajte rôzne iné zmeny uhla v príkaze t.rt().

Zhrnutie užitočných metód

metóda

variant

význam

príklad

forward(d)

fd

choď dopredu

t.fd(100); t.fd(-50)

back(d)

backward, bk

cúvaj

t.bk(50); t.bk(-10)

right(u)

rt

otoč sa vpravo

t.rt(90); t.rt(-120)

left(u)

lt

otoč sa vľavo

t.lt(90); t.lt(-45)

penup()

pu, up

zdvihni pero

t.pu()

pendown()

pd, down

spusti pero

t.pd()

setpos(x, y)

setposition, goto

choď na pozíciu

t.setpos(50, 70)

pos()

position

zisti pozíciu korytnačky

t.pos()

xcor()

zisti x-ovú súradnicu

t.xcor()

ycor()

zisti y-ovú súradnicu

t.ycor()

heading()

zisti uhol korytnačky

t.heading()

setheading(u)

seth

nastav uhol korytnačky

t.seth(120)

pensize(h)

width

nastav hrúbku pera

t.pensize(5)

pencolor(f)

nastav farbu pera

t.pencolor('red')

pencolor()

zisti farbu pera

t.pencolor()

fillcolor(f)

nastav farbu výplne

t.fillcolor('blue')

fillcolor()

zisti farbu výplne

t.fillcolor()

color(f1, f2)

nastav farbu pera aj výplne

t.color('red'); t.color('blue', 'white')

color()

zisti farbu pera aj výplne

t.color()

reset()

zmaž kresbu a inicializuj korytnačku

t.reset()

clear()

zmaž kresbu

t.clear()

begin_fill()

začiatok budúceho vyfarbenia

t.begin_fill()

end_fill()

koniec vyfarbenia

t.end_fill()

Globálne korytnačie funkcie

Modul turtle má ešte tieto funkcie, ktoré robia globálne nastavenia a zmeny (majú vplyv na všetky korytnačky):

  • turtle.delay(číslo) - vykonávanie korytnačích metód sa spomalí na zadaný počet milisekúnd (štandardne je 10)

    • každú jednu korytnačku môžeme ešte individuálne zrýchľovať alebo spomaľovať pomocou t.speed(číslo), kde číslo je od 0 do 10 (0 najrýchlejšie, štandardne je 3)

  • turtle.tracer(číslo) - zapne alebo vypne priebežné zobrazovanie zmien v grafickej ploche (štandardne je číslo 1):

    • turtle.tracer(0) - vypne zobrazovanie zmien, t. j. teraz je vykresľovanie veľmi rýchle bez pozdržiavania, ale zatiaľ žiadnu zmenu v grafickej ploche nevidíme

    • turtle.tracer(1) - zapne zobrazovanie zmien, t. j. teraz je vykresľovanie už pomalé (podľa nastavených turtle.delay() a t.speed()), lebo vidíme všetky zmeny kreslenia v grafickej ploche

  • turtle.bgcolor(farba) - zmení farbu pozadia grafickej plochy, pričom všetky kresby v ploche ostávajú bez zmeny

Tvar korytnačky

Korytnačkám môžeme meniť ich tvar - momentálne je to malý trojuholník.

Príkaz shape() zmení tvar na jeden s preddefinovaných tvarov (pre korytnačku t):

t.shape('arrow')        # tvarom korytnačky bude šípka
t.shape('turtle')       # tvarom korytnačky bude korytnačka
t.shape('circle')       # tvarom korytnačky bude kruh
t.shape('square')       # tvarom korytnačky bude štvorec
t.shape('triangle')     # tvarom korytnačky bude trojuholník
t.shape('classic')      # tvarom korytnačky bude hrot šípky

Default tvar je 'classic'.

Príkaz shapesize() nastavuje zväčšenie tvaru a hrúbku obrysu tvaru (pre korytnačku t):

t.shapesize(sirka, vyska, hrubka)

Mohli ste si všimnúť, že keď korytnačke zmeníte farbu pera, zmení sa obrys jej tvaru. Podobne, keď sa zmení farba výplne, tak sa zmení aj výplň tvaru korytnačky. Napr.

import turtle

turtle.delay(0)
t = turtle.Turtle()
t.shape('turtle')
t.shapesize(5, 5, 8)
t.color('darkgreen', 'green')
for i in range(90):
    t.fd(5)
    t.rt(4)
_images/11_13.png

V tomto príklade sa nastaví korytnačke zväčšený tvar a pomaly nakreslí kružnicu (90-uholník).

Zobrazovanie tvaru korytnačky môžeme skryť príkazom hideturtle() (skratka ht()) a opätovné zobrazovanie zapnúť príkazom showturtle() (skratka st()).

Náhodné prechádzky

Náhodnými prechádzkami budeme nazývať taký pohyb korytnačky, pri ktorom sa korytnačka veľa-krát náhodne otočí a prejde nejakú malú vzdialenosť. Napr.

import turtle
from random import randrange

turtle.delay(0)
t = turtle.Turtle()
for i in range(10000):
    t.seth(randrange(360))
    t.fd(10)
_images/11_14.png

Po čase odíde z grafickej plochy - upravíme tak, aby nevyšla z nejakej oblasti, napr.

import turtle
from random import randrange

turtle.delay(0)
t = turtle.Turtle()
t.pensize(5)
t.pencolor('blue')
for i in range(10000):
    t.seth(randrange(360))
    t.fd(10)
    if t.xcor()**2 + t.ycor()**2 > 50**2:
        t.fd(-10)
_images/11_15.png

Príkaz if nedovolí korytnačke vzdialiť sa od (0,0) viac ako 50 - počítali sme tu vzdialenosť korytnačky od počiatku.

Môžeme využiť metódu distance(), ktorá vráti vzdialenosť korytnačky od nejakého bodu alebo inej korytnačky:

import turtle
from random import randrange

turtle.delay(0)
t = turtle.Turtle()
turtle.bgcolor('navy')
t.pensize(5)
t.pencolor('yellow')
for i in range(10000):
    t.seth(randrange(360))
    t.fd(10)
    if t.distance(20, 0) > 50 or t.distance(50, 0) < 50:
        t.fd(-10)
_images/11_16.png

Korytnačka sa teraz pohybuje v oblasti, ktorá ma tvar mesiaca: nesmie vyjsť z prvého kruhu a zároveň vojsť do druhého.

Tvar stráženej oblasti môže byť definovaný aj zložitejšou funkciou, napr.

import turtle
from random import randrange

def fun(pos):
    x, y = pos            # pos je dvojica súradníc
    if abs(x - 30) + abs(y) < 50:
        return False
    return abs(x + 30) + abs(y) > 50

turtle.delay(0)
t = turtle.Turtle()
t.speed(0)
t.pensize(5)
for i in range(10000):
    t.seth(randrange(360))
    if t.distance(0, 0) < 30:
        t.pencolor('green')
    else:
        t.pencolor('red')
    t.fd(5)
    if fun(t.pos()):       # funkcia fun stráži nejakú oblasť
        t.fd(-5)
_images/11_17.png

Okrem stráženia oblasti tu meníme farbu pera podľa nejakej podmienky

Viac korytnačiek

Doteraz sme pracovali len s jednou korytnačkou (vytvorili sme ju pomocou t = turtle.Turtle()). Korytnačiek ale môžeme vytvoriť ľubovoľne veľa. Aby rôzne korytnačky mohli využívať tú istú kresliacu funkciu (napr. stvorec()) musíme globálnu premennú t vo funkcii prerobiť na parameter:

import turtle

def stvorec(tu, velkost):
    for i in range(4):
        tu.fd(velkost)
        tu.rt(90)

t = turtle.Turtle()
stvorec(t, 100)

Vytvorme ďalšiu korytnačku a necháme ju tiež kresliť štvorce tou istou funkciou:

t1 = turtle.Turtle()
t1.lt(30)
for i in range(5):
    stvorec(t1, 50)
    t1.lt(72)

Kým sme vytvárali funkcie, ktoré pracovali len pre jednu korytnačku, nemuseli sme ju posielať ako parameter. Ak ale budeme potrebovať funkcie, ktoré by mali pracovať pre ľubovoľné ďalšie korytnačky, vytvoríme vo funkcii nový parameter (najčastejšie ako prvý parameter funkcie) a ten bude v tele funkcie zastupovať tú korytnačku, ktorú do funkcie pošleme.

V ďalšom príklade vyrobíme 3 korytnačky: 2 sa pohybujú po nejakej stálej trase a tretia sa vždy nachádza presne v strede medzi nimi (ako keby bola v strede gumenej nite):

import turtle

def posun(t, pos):     # pos je pozícia v tvare (x,y)
    t.pu()
    t.setpos(pos)
    t.pd()

def stred(k1, k2):
    x = (k1.xcor()+k2.xcor()) / 2
    y = (k1.ycor()+k2.ycor()) / 2
    return (x, y)

turtle.delay(0)
t1 = turtle.Turtle()
posun(t1, (-100, -100))
t2 = turtle.Turtle()
posun(t2, (200, 100))
t3 = turtle.Turtle()
posun(t3, stred(t1, t2))
t3.pencolor('red')

while True:
    t1.fd(4)
    t1.rt(3)
    t2.fd(3)
    t2.lt(2)
    t3.setpos(stred(t1, t2))
_images/11_18.png

Zoznam korytnačiek

Do premennej typu zoznam postupne priradíme vygenerované korytnačky, pričom každú presunieme na inú pozíciu (všetky ležia na x-ovej osi) a nastavíme jej iný smer:

import turtle

turtle.delay(0)
zoznam = []
for i in range(60):
    t = turtle.Turtle()
    t.pu()
    t.setpos(-300 + 10*i, 0)
    t.pd()
    t.seth(i * 18)
    zoznam.append(t)

Necháme ich kresliť rovnaké kružnice:

import turtle

turtle.delay(0)
zoznam = []
for i in range(60):
    t = turtle.Turtle()
    t.pu()
    t.setpos(-300 + 10*i, 0)
    t.pd()
    t.seth(i * 18)
    zoznam.append(t)

for t in zoznam:
    for i in range(24):
        t.fd(20)
        t.lt(15)
_images/11_19.png

Takto kreslila jedna za druhou: ďalšia začala kresliť až vtedy, keď predchádzajúca skončila.

Pozmeňme to tak, aby všetky kreslili naraz:

import turtle

turtle.delay(0)
zoznam = []
for i in range(60):
    t = turtle.Turtle()
    t.pu()
    t.setpos(-300 + 10*i, 0)
    t.pd()
    t.seth(i * 18)
    zoznam.append(t)

for i in range(24):
    for t in zoznam:
        t.fd(20)
        t.lt(15)

Tu sme zmenili len poradie for-cyklov.

V ďalšom príklade vygenerujeme všetky korytnačky v počiatku súradnej sústavy ale s rôznymi smermi a necháme ich prejsť dopredu rovnakú vzdialenosť:

import turtle

turtle.delay(0)
zoznam = []
for i in range(60):
    zoznam.append(turtle.Turtle())
    zoznam[-1].seth(i * 6)

for t in zoznam:
    t.fd(200)
_images/11_20.png

Všimnite si, ako pracujeme so zoznamom korytnačiek (zoznam[-1] označuje posledný prvok zoznamu, t. j. naposledy vygenerovanú korytnačku).

Korytnačky sa naháňajú

Na náhodných pozíciách vygenerujeme n korytnačiek a potom ich necháme, nech sa naháňajú podľa takýchto pravidiel:

  • každá sa otočí smerom k nasledovnej (prvá k druhej, druhá k tretej, …, n-tá k prvej)

  • každá prejde stotinu vzdialenosti k nasledovnej

import turtle
from random import randrange

n = 8
t = []
turtle.delay(0)
for i in range(n):
    nova = turtle.Turtle()
    nova.pu()
    nova.setpos(randrange(-300, 300), randrange(-300, 300))
    nova.pencolor(f'#{randrange(256**3):06x}')
    nova.pensize(3)
    nova.pd()
    t.append(nova)

while True:
    for i in range(n):
        j = (i+1) % n               # index nasledovnej
        uhol = t[i].towards(t[j])
        t[i].seth(uhol)
        vzdialenost = t[i].distance(t[j])
        t[i].fd(vzdialenost / 100)
_images/11_21.png

Využili sme novú metódu towards(), ktorá vráti uhol otočenia k nejakému bodu alebo k pozícii inej korytnačky.

Trochu pozmeníme: okrem prejdenia 1/10 vzdialenosti k nasledovnej nakreslí aj celú spojnicu k nasledovnej:

import turtle
from random import randrange

turtle.delay(0)
while True:
    turtle.bgcolor('black')
    n = randrange(3, 9)
    t = []
    for i in range(n):
        nova = turtle.Turtle()
        nova.speed(0)
        nova.pu()
        nova.setpos(randrange(-300, 300), randrange(-300, 300))
        nova.pencolor(f'#{randrange(256**3):06x}')
        nova.pd()
        nova.ht()
        t.append(nova)

    for k in range(100):
        for i in range(n):
            j = (i+1) % n                # index nasledovnej
            uhol = t[i].towards(t[j])
            t[i].seth(uhol)
            vzdialenost = t[i].distance(t[j])
            t[i].fd(vzdialenost)
            t[i].fd(vzdialenost/10 - vzdialenost)

    for tt in t:
        tt.clear()
_images/11_22.png

Po dokreslení, obrázok zmaže a začne kresliť nový. Uvedomte si, že každý ďalší prechod while-cyklu vytvorí nové a nové korytnačky a tie pôvodné staré tam ostávajú, hoci majú skrytý tvar a teda ich nevidíme. Ak by sme naozaj chceli zrušiť korytnačky z grafickej plochy, tak namiesto záverečného cyklu:

for tt in t:
    tt.clear()

by sme mali zapísať:

turtle.getscreen().clear()


Cvičenia

L.I.S.T.

  1. Napíš funkciu bodka(velkost, farba), ktorá na pozícii korytnačky nakreslí farebnú bodku danej veľkosti.

    • bodku bude kresliť takto: zmení hrúbku a farbu pera na parametrami zadané hodnoty a potom nakreslí čiaru dĺžky 0

    • funkciu otestuj nakreslením 100 bodiek na náhodných pozíciách, pričom v pomere 1:2 budú červené veľkosti 50 ku modrým veľkosti 30 (v cykle generuj náhodné čísla randrange(3) a pri 0 kresli väčšie červené a inak menšie modré)

    • v tomto testovacom programe nahraď volanie tvojej funkcie bodka(...) na korytnačí príkaz t.dot(...) - mal by si dostať rovnaký výsledok (môžeš poštudovať help(t.dot))

    _images/11_c01.png

  1. Napíš funkciu terc(pocet), ktorá pomocou korytnačieho t.dot(...) nakreslí zadaný počet rôzne veľkých bodiek: najmenšia má veľkosť 15, každá ďalšia je o 15 väčšia. Najväčšia bodka nech je modrá, menšia žltá a takto sa ďalej striedajú tieto dve farby na všetky bodky.

    • otestuj napr. terc(40)

    _images/11_c02.png

  1. Napíš funkciu stvorec(velkost), pomocou ktorej korytnačka nakreslí náhodne zafarbený štvorec danej veľkosti

    • otestuj nakreslením 10 štvorcov v rade vedľa seba s náhodnými veľkosťami strán od 20 do 50

    _images/11_c03.png

  1. Iste vieš, ako sa dá nakresliť domček jedným ťahom. Domček, ktorý sa skladá zo štvorca s oboma uhlopriečkami, na ktorom je rovnostranný trojuholník ako strecha. Napíš funkciu domcek(velkost), pomocou ktorej korytnačka nakreslí takýto domček. Môžeš predpokladať, že kresliť začína v ľavom dolnom rohu štvorca otočená na východ a skončí v pravom dolnom tiež otočená na východ. Vo funkcii používaj iba príkazy t.forward() (zrejme bude 8 volaní tejto funkcie) a otáčanie pomocou t.left() a t.right().

    • potom môžeš otestovať:

      for d in 100, 50, 80, 60, 120:
          domcek(d)
      
    _images/11_c04.png

  1. Napíš funkciu kosostvorec(velkost, farba), pomocou ktorej korytnačka nakreslí kosoštvorec s danou veľkosťou strany. Vnútorný uhol nech je 45 stupňov. Korytnačka začína aj končí pri vrchole, ktorý má týchto 45 stupňov. Vnútro kosoštvorca vyfarbi danou farbou.

    • otestuj nakreslením 8 kosoštvorcov, ktoré majú jeden spoločný vrchol; týmto kosoštvorcom pravidelne striedaj dve farby výplne 'tan' a 'tomato'

    _images/11_c05.png

  1. Napíš funkciu polkruznica(velkost, smer), pomocou ktorej korytnačka nakreslí 18 strán z pravidelného 36-uholníka so stranou danej veľkosti. Parameter smer určuje, či sa pri kreslení korytnačka otáča vľavo (smer=True) alebo vpravo (smer=False).

    • otestuj nakreslením 10 polkružníc tak, aby z toho vznikli vlnky (veľkosť nech je napr. 3)

    _images/11_c06.png

  1. Na rovnakom princípe, ako bola funkciu polkruznica z predchádzajúceho príkladu, napíš funkciu polkruh(velkost, smer). Korytnačka teraz vnútro nakresleného oblúku vyfarbí náhodnou farbou.

    • otestuj podobne ako predchádzajúci príklad, ale po nakreslení 10 farebných polkruhov dokresli ďalších 10 tak, aby sa vlnky doplnili do farebných kruhov - zrejme každý z takýchto kruhov sa bude skladať z dvoch rôzne zafarbených polovíc

    _images/11_c07.png

  1. Napíš dve funkcie: trojuholnik(velkost) a rad_trojuholnikov(n, velkost):

    • funkcia trojuholnik(velkost) nakreslí rovnostranný trojuholník, ktorý bude vyplnený náhodnou farbou - korytnačka sa pri kreslení trojuholníka otáča vľavo

    • funkcia rad_trojuholnikov(n, velkost) nakreslí vedľa seba n trojuholníkov - všetky ležia na jednej podstave (jedna ich strana leží na jednej spoločnej priamke)

    • pomocou týchto funkcií nakresli takúto 10-poschodovú pyramídu: v dolnom rade je 10 trojuholníkov, nad nimi je ich 9, nad nimi 8 atď. v najvyššom rade je už len jeden trojuholník, použi funkciu rad_trojuholnikov, pričom po každom rade sa korytnačka presunie na začiatok radu, prejde po šikmej hrane o rad vyššie a pokračuje s radom trojuholníkov, ktorý je o jedna kratší

    _images/11_c08.png

  1. Napíš funkciu slnko(pocet, velkost), pomocou ktorej korytnačka nakreslí slnko s daným počtom lúčov. Každý lúč je úsečka danej veľkosti a hrúbky 10. Okrem lúčov nakresli kruh danej veľkosti (priemer) - môžeš použiť korytnačí príkaz t.dot(...). Na kreslenie lúčov aj slnka použi farbu 'gold'.

    _images/11_c09.png

  1. Napíš funkciu bodky(n, m), ktorá nakreslí n radov bodiek (t.dot(...)) po m v každom rade. Stredy bodiek v radoch aj stĺpcoch sú vo vzdialenosti 30. Všetky bodky majú farbu 'salmon' a náhodnú veľkosť od 20 do 35.

    • otestuj napr. bodky(10, 15)

    _images/11_c10.png

  1. Napíš funkciu cikcak(dlzka), ktorá bude takto kresliť cikcakovú čiaru:

    • otočí sa vľavo 60

    • kým nie je dlzka menšia ako 5, ide dopredu 5, otočí sa vpravo 120, ide dopredu 5, otočí sa o 120 vľavo a zmenší dlzka o 5

    • otočí sa späť o 60 vpravo

    • ak je dlzka nenulová, prejde ešte túto zvyšnú vzdialenosť

    • otestuj túto funkciu kreslením napr. trojuholníkovej špirály z prednášky, kde miesto obyčajných čiar použiješ cikcakové

    _images/11_c11.png

  1. (trochu matematiky) Budeš kresliť obrázok, ktorý demonštruje Pytagorovu vetu: súčet štvorcov nad odvesnami sa rovná štvorcu nad preponou:

    • napíš funkciu pytagoras(prepona, uhol), ktorá nakresli štvorec nad preponou, vypočíta dĺžky oboch odvesien a nakreslí oba štvorce

      • uhol je vnútorný uhol pravouhlého trojuholníka

      • uvedom si, že dĺžky odvesien môžeš počítať ako prepona * cos(uhol) a prepona * sin(uhol), daj pozor na radiány

    • funkcia potom vypíše obsah štvorca nad preponou a obsahy oboch štvorcov nad odvesnami

    • pri kreslení štvorcov použi funkciu na kreslenie náhodne vyfarbeného štvorca

    • môžeš dostať napr. takýto výstup:

      >>> pytagoras(150, 17)
      stvorec nad preponou = 22500
      stvorec nad 1. odvesnou = 20576.672691244217
      stvorec nad 2. odvesnou = 1923.3273087557814
      sucet = 22500.0
      
    _images/11_c12.png

  1. (trochu matematiky) Napíš funkciu troj(rameno, uhol), ktorá nakreslí rovnoramenný trojuholník s danou dĺžkou ramena a uhlom, ktorý zvierajú tieto dve ramená. V tomto vrchole začne aj skonči kresliť. Trojuholník vyplň náhodnou farbou.

    • otestuj nakreslením 36 takýchto trojuholníkov s ramenom 300 a s uhlom 10, po každom trojuholníku sa otoč o 10 stupňov

    _images/11_c13.png

  1. Napíš funkciu prechadzka(), ktorá zabezpečí náhodnú prechádzku:

    • nastav korytnačke farbu čiar modrú a hrúbku 10

    • korytnačka sa náhodne otáča a ide vzdialenosť 10

    • ak sa vzdiali od bodu (90, 0) viac ako 100, cúvne 10

    • toto opakuj 10000-krát

    _images/11_c14.png

  1. Predchádzajúci príklad s náhodnou prechádzkou oprav tak, aby sa okrem testu na vzdialenosť od bodu (90, 0), kontrolovalo, či jej vzdialenosť od (80, 0) nie je menej ako 50 - aj vtedy treba zabezpečiť cúvnutie.

    • uvedom si, že korytnačka by sa mala teraz pohybovať v medzikruží: vo vnútri kruhu so stredom (90, 0) a s polomerom 100 a ale nie v kruhu so stredom (80, 0) a s polomerom 50

    _images/11_c15.png

  1. Napíš dve funkcie vyrob(n) a smerom(zoznam):

    • prvá funkcia vyrob(n) vytvorí na náhodných pozíciách n korytnačiek, každej nastaví náhodnú farbu pera a hrúbku pera 5; každú korytnačku ešte natočí smerom k bodu (x, y), kde x a y sú nejaké globálne premenné (napr. x, y = 0, -300); všetky tieto korytnačky vloží do zoznamu a tento zoznam vráti (return) ako výsledok funkcie

    • druhá funkcia smerom(zoznam) dostáva zoznam korytnačiek a nechá postupne všetky korytnačky z tohto zoznamu presúvať k bodu (x, y); teda bude 50-krát robiť toto:

      • každá korytnačka si vypočíta vzdialenosť k bodu (x, y) a prejde (forward) desatinu tejto vzdialenosti

    • otestujte napr.

      >>> x, y = 0, -300
      >>> smerom(vyrob(100))
      
    _images/11_c16.png


4. Domáce zadanie

L.I.S.T.

Programovací jazyk Logo podobne ako my v Pythone riadi korytnačku v grafickej ploche. Syntax jazyka sa ale trochu líši od Pythonu. Nás z Loga budú zaujímať len týchto 8 príkazov:

  • fd 100 - zodpovedá pythonovskému t.fd(100), parametrom je celé alebo desatinné číslo

  • rt 45 - zodpovedá pythonovskému t.rt(45), parametrom je celé alebo desatinné číslo

  • lt 60 - zodpovedá pythonovskému t.lt(60), parametrom je celé alebo desatinné číslo

  • pu - zodpovedá pythonovskému t.pu(), príkaz je bez parametrov

  • pd - zodpovedá pythonovskému t.pd(), príkaz je bez parametrov

  • setpc 'red' - zodpovedá pythonovskému t.pencolor('red'), parametrom je znakový reťazec

  • setpw 5 - zodpovedá pythonovskému t.pensize(5), parametrom je celé číslo

  • repeat 4 [fd 50 rt 90] - označuje cyklus (bez premennej cyklu) s daným počtom opakovaní, pričom telom môže byť ľubovoľný logovský program, napr. tento cyklus sa môže preložiť takto:

    for _ in range(4):
        t.fd(50)
        t.rt(90)
    

    premennú cyklu budeme zapisovať identifikátorom _

Program v Logu môže byť naformátovaný úplne voľne, t.j. medzi príkazmi sú buď medzery alebo nové riadky a tak isto aj parametre sú od príkazov oddelené aspoň jednou medzerou alebo aj prázdnym riadkom.

Vašou úlohou bude napísať pythonovský skript s funkciou logo(), ktorý na vstupe dostane logovský program (textový súbor s príponou '.txt') a vyrobí z neho pythonovský skript (textový súbor s rovnakým menom ale s príponou '.py'), ktorým by sa nakreslil identický obrázok s logovským programom.

Napr. pre takýto vstupný súbor 'subor1.txt':

pu lt 30 fd 100 lt 60 setpc
'red' pd
fd 100 rt 120 fd
100    rt 120
     fd 100 rt
   60 setpc 'blue' fd 100 rt 120
fd 100

Váš program vygeneruje takýto pythonovský skript 'subor1.py':

import turtle
t = turtle.Turtle()
t.pu()
t.lt(30)
t.fd(100)
t.lt(60)
t.pencolor('red')
t.pd()
t.fd(100)
t.rt(120)
t.fd(100)
t.rt(120)
t.fd(100)
t.rt(60)
t.pencolor('blue')
t.fd(100)
t.rt(120)
t.fd(100)

Alebo pre súbor 'subor2.txt':

lt 90 pu fd 100 rt 30 pd

repeat 4 [
  fd 50
  lt 30
  repeat 3[fd 30 rt 120]lt 60
] rt 30 fd 70

dostaneme:

import turtle
t = turtle.Turtle()
t.lt(90)
t.pu()
t.fd(100)
t.rt(30)
t.pd()
for _ in range(4):
    t.fd(50)
    t.lt(30)
    for _ in range(3):
        t.fd(30)
        t.rt(120)
    t.lt(60)
t.rt(30)
t.fd(70)

Váš odovzdaný program s menom riesenie4.py musí začínať tromi riadkami komentárov:

# 4. zadanie: logo
# autor: Janko Hraško
# datum: 20.11.2018

Modul musí obsahovať definíciu funkcie:

def logo(meno_suboru):
    ...

Túto funkciu bude volať testovač s rôznymi logovskými súbormi.

Projekt riesenie4.py odovzdávajte (bez ďalších dátových súborov) na úlohový server https://list.fmph.uniba.sk/ najneskôr do 23:00 23. novembra, kde ho môžete nechať otestovať. Testovač bude spúšťať vašu funkciu s rôznymi textovými súbormi, ktoré si môžete stiahnuť z L.I.S.T.u. Odovzdať projekt aj ho testovať môžete ľubovoľný počet krát. Môžete zaň získať 10 bodov.