Sisällysluettelo

Python esi­merk­ke­jä


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Python esimerkkejä > Python esimerkkejä ()

Python esi­merk­ke­jä

Tu­tus­tum­me aluk­si muu­ta­maan pie­neen esi­merk­ki­oh­jel­maan. Pi­kai­sel­la vil­kai­sul­la oh­jel­moin­nin aloit­te­li­ja ei saane sel­vää, miten ne toi­mi­vat, mutta koh­tuul­li­sel­la pon­nis­te­lul­la ja ko­kei­lul­la ne opet­ta­vat pal­jon oh­jel­moin­nis­ta.

Esi­merk­ki: Fibonnacin luvut

Jos olet kiin­nos­tu­nut ma­te­ma­tii­kas­ta ja tie­dät, mitä ovat fibonnacin luvut sinua saattaa hetken säväyttää seuraava ohjelma. Ellet tunne fibonnacin lukuja, harjoitus tuntunee tyhmältä, mutta tee se silti.

            # Määritellään funktio fib(n), jolla on yksi argumentti, n
            def fib(n):
            if n == 0:    # Jos n on yhtäsuuri kuin nolla,
            return 0  # funktio saa arvon 0
            elif n == 1:  # muuten, jos n on yhtäsuuri kuin 1
            return 1  # funktio saa arvon 1
            else:         # muissa tapauksissa funktio saa arvokseen
            return fib(n-1) + fib(n-2)  # kahden edeltävän fibonnacin luvun summan

            # Pääohjelma
            print(fib(5))
        

Python-kielisestä funk­tios­ta fib(n) saattaa tunnistaa fibonnacin luvun määritelmän, vaikka merkintätapa onkin eri kuin matematiikan kirjoissa.

Käyn­nis­tä spyder, kopio ylläoleva sen editointi-ikkunaan allaolevan kuvan mukaisesti ja tallenna tiedostoon fibonnacci.py. Suorita ohjelma painamalla F5. Samall F5-painallus tuo ohjelmamodulin funktion järjestelmän tietoon niin, että voit kutsua sitä myös konsolilta — kirjoittamalla esimerkiksi fib(12) kuvan mukaisesti spyder-ohjelmointiympäristön oikeassa alalaidassa olevan konsoli-ikkunan komentoriville. (Ilman F5-painallusta saat funktion konsolilta kutsuttavaksi kirjoittamalla konsolille from fibonnacci import fib.)

https://heikki-valisuo.fi/kuva/templates/xml/Programming/PythonOpetus/spyder_figs/spyder_fibo.png
 

Yleen­sä tieto­kone suo­rit­taa kir­joit­ta­ma­si oh­jel­man sil­män­rä­päyk­ses­sä, mutta jos in­nos­tuit ko­kei­le­maan jon­kin ison fibonnacin luvun las­ke­mis­ta, kyl­läs­tyit ken­ties odot­te­le­maan vas­taus­ta. Spyderin kon­so­li-ik­ku­nan oi­keas­sa ylä­kul­mas­sa on pu­nai­nen neliö, josta oh­jel­man voi py­säyt­tää, mi­kä­li se tun­tuu jää­neen "ju­miin".

Yleen­sä oh­jel­ma jää "ju­miin" oh­jel­moin­ti­vir­heen takia, mutta nyt "ju­mit­tu­mi­nen" joh­tuu siitä, että fibonnacin luvun mää­rit­tä­mi­nen on moni­mut­kai­nen lasku­teh­tä­vä. 35:tä suu­rem­mat fibonnacci luvut osoit­tau­tui­vat minun ko­neel­la­ni niin hi­taik­si las­kea, etten jak­sa­nut odot­taa tu­los­ta.

Hakukoneile "fibonnacci complexity" jos sinua jäi mie­ti­tyt­tä­mään, miksi noin pie­nen muu­ta­man rivin funk­tion arvon las­ke­mi­nen voi olla lii­kaa te­hok­kaal­le tieto­ko­neel­le.


Pie­niä Python-kielisiä esi­merk­ke­jä

Ei kan­na­ta pe­läs­tyä, vaik­ka seu­raa­vat oh­jel­man­pät­kät näyt­tä­vät vai­kea­sel­koisilta. Oh­jel­moin­tia tun­te­mat­to­mal­le oh­jel­ma­koodi näyt­tä­nee täy­sin kä­sit­tä­mät­tö­mäl­tä. Myös oh­jel­moin­tia tun­te­van on usein vai­kea saada sel­vää toi­sen kir­joit­ta­mas­ta oh­jel­mas­ta. Vasta har­jaan­tu­nut oh­jel­moi­ja, joka tun­tee käy­tet­tä­vän oh­jel­moin­ti­kie­len, voi lukea jon­kun muun te­ke­mää oh­jel­maa ilman suu­rem­pia vai­keuk­sia.

Python--ohjelmaa lu­kies­sa on hyvä tie­tää, että ri­vien si­sen­nys mää­rää, mihin ylem­pään ko­ko­nai­suu­teen rivi kuu­luu. Aivan va­sem­mas­ta lai­das­ta al­ka­vat rivit kuu­lu­vat oh­jel­man pää­ta­sol­le. Yllä funk­tio fib(n) mää­ri­tel­tiin siis pää­ta­sol­le, koska def fib(n): alkaa rivin alusta. Sitä seuraavat sisennetyt rivit kuuluvat funktion määrittelyyn — ne kertovat, mitä funktio tekee.

Seu­raa­vaa esi­merk­kiä ei kan­na­ta yrit­tää ym­mär­tää yh­del­lä vil­kai­sul­la. Lataa allaolevan oh­jel­man koodi kehitysympäristöön ja kokeile kuten yllä fibonnaccin lukuja laskevaa funktiota.

Mi­kä­li et ole ins­tal­loi­nut pylab-modulia, laita kom­men­tik­si rivit, jois­sa vii­ta­taan pylabiin. Eli laita ky­seis­ten ri­vien al­kuun #-merk­ki. Voit myös yrit­tää ins­tal­loi­da pylab-modulin ko­men­nol­la pip install pylab tai jos käytät Python3:a komennollapip3 install pylab. Jos se aiheuttaa virheilmoituksia, jätä sikseen vähäksi aikaa. Emme tarvitse pylabia tästä eteenpäin.

            # -*- coding: utf-8 -*-

            import pylab

            # lasketaan kokonaislukujen 1..k summa for-silmukan avulla
            # tulostetaan joka kierroksella välitulos
            # huom. Pythonin range(k) antaa luvut 0:sta (k-1):een.

            def summa(k):
            s = 0
            for i in range(k+1):
            s = s+(i)
            print("i: ", i, "s: ", s)
            return s


            # lasketaan kokonaiskukujen 1..k rekursiivisella funktiolla.
            # Rekursiossa funktio kutsuu itseään, kunnes lopetusehto
            # (tässä tapauksessa k == 0) toteutuu.
            def summaR(k):
            print("summaR ", k)
            if k == 0:
            return 0
            return k+summaR(k-1)


            # Esimerkkejä eri tavoista saada tulostettua jotain konsolille
            def mjonot():
            s = "Tämä on merkkijono "
            m = 53
            print(s)
            print("luku ja merkkijono ", m, str(m))
            print("kaksi merkkijonoa yhdistetty + merkillä: ")
            print(s+str(m))
            m = 120
            n = 15
            print(m+n)
            print(str(m)+str(n))
            print(str(m)+' '+str(n))


            # Esimerkki siitä, miten lukea konsolilta.
            # Input tuottaa merkkijonon, joten muutamme sen int()-funktiolla
            # kokonaisluvuksi
            def kysymys():
            N = input("anna luku: ")
            print('luku on:', N, type(N))
            S = summa(int(N))
            print("lukujen 1..", N, " summa on: ", S)
            print("lukujen 1.."+str(N)+" summa on: "+str(S))


            # piirretään funktion y = x**3 kuvaaja
            # Muodostetaan listat/taulukot xx ja yy toisiaan vastaavilla
            # x ja y arvoille.
            # loppu hoituu pylab-funktioilla
            # huom. Pythonin range(i,j) antaa luvut i:stä (j-1):een.

            def kuvaaja():
            xx = [i for i in range(-10, 11)]
            yy = [i**3 for i in range(-10, 11)]

            print("xx:")
            print(xx)
            print("yy:")
            print(yy)

            pylab.plot(xx, yy)
            pylab.title('y = x**3')
            pylab.ylabel("x**3")
            pylab.xlabel("x")
            pylab.savefig('y_x3.png')
            pylab.show()

            # Huom. Merkkijoissa \n aiheuttaa rivinvaihdon
            print('\npääohjelma\n')

            print("summa(5):")
            s = summa(5)
            print(s)
            print("\nsummaR(5): ")
            s = summaR(5)
            print(s)

            print("\nmerkkijonot")
            mjonot()

            kysymys()

            print("\nkuvaaja:")
            kuvaaja()

            # voit kutsua funktiota konsolilta

        

Ylläolevan oh­jel­man koodi

Oh­jel­ma kir­joit­taa kon­so­lil­le seu­raa­vaa, kun pai­nat F5:

            pääohjelma

            summa(5):
            i:  0 s:  0
            i:  1 s:  1
            i:  2 s:  3
            i:  3 s:  6
            i:  4 s:  10
            i:  5 s:  15
            15

            summaR(5):
            summaR  5
            summaR  4
            summaR  3
            summaR  2
            summaR  1
            summaR  0
            15

            merkkijonot
            Tämä on merkkijono
            luku ja merkkijono  53 53
            kaksi merkkijonoa yhdistetty + merkillä:
            Tämä on merkkijono 53
            135
            12015
            120 15

            anna luku: 4
            luku on: 4 <class 'str'>
            i:  0 s:  0
            i:  1 s:  1
            i:  2 s:  3
            i:  3 s:  6
            i:  4 s:  10
            lukujen 1.. 4  summa on:  10
            lukujen 1..4 summa on: 10

            kuvaaja:
            xx:
            [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
            yy:
            [-1000, -729, -512, -343, -216, -125, -64, -27, -8, -1, 0, 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
        

Oh­jel­man suo­ri­tuk­sen li­säk­si F5:näppäimen pai­na­mi­nen lataa oh­jel­man python jär­jes­tel­män työ­ti­laan niin, että voit kut­sua kut­sua funk­tioi­ta kon­so­lil­ta. Esi­mer­kik­si summa(3) laskee summan 0+1+2+3. Funktion koodin mukaisesti ensin asetetaan muuttujalle s arvo 0 ja sen jälkeen käydään foor-silmukassa läpi i:n arvot nollasta kolmeen ja joka kierroksella lisätään kukin i s:ään ja tulostetaan muuttujien arvot. Lopuksi funktio palauttaa s:n lopullisen arvon.

Ehkä ih­met­te­let for sil­mu­kas­sa käs­kyä range(k+1). Miksei range(k)? Kirjoita konsolin komentoriville range(3) niin saat listan [0,1,2]. Voit myös kirjoittaa range(1,3) niin saat listan [1,2]. Jostain syystä on nähty järkevästi, että range-käsky toimii noin.

Funtion summaR(k) näyttää, miten funktio voi kutsua itseään. Esimerkiksi lukujen yhdestä kolmeen summa on kolme plus lukujen yhdestä kahteen summa. Rekursiivisen funktion toimintaa voi olla vaikea hahmottaa, mutta yritä kuitenkin. Välitulostukset saattaavat auttaa.

Esi­mer­keis­sä tu­los­tuk­ses­ta ja syöt­tei­den lu­ke­mi­ses­ta kon­so­lil­ta ei ole hir­veäs­ti ym­mär­ret­tä­vää. Ne esit­te­le­vät eri vaih­to­eh­to­ja. Esi­merk­kien ko­men­not voi ko­pioi­da tar­vit­taes­sa omaan oh­jel­maan­sa.

Lo­puk­si piir­re­tään funk­tion

y = x 3

ku­vaa­ja. Python-kielessä "x po­tens­siin kolme" mer­ki­tään x**3. Piirtämisessä käytetään pylab-nimisen modulin funktiota plot, jolle pitää antaa kaksi argumenttia — luettelo x:n arvoista ja luettelo niitä vastaavista y:n arvoista.

https://heikki-valisuo.fi/kuva/templates/xml/Programming/PythonOpetus/y_x3.png
 

Pythonin olioi­ta

Seu­raa­va — ei aivan yksin­ker­tai­nen — esi­merk­ki esit­te­lee olio-oh­jel­moin­tia Pythonilla. Tämä opas olio-oh­jel­moin­tiin pythonilla kertoo lisää olio-ohjelmoinnista

Esi­mer­kis­sä kaup­piaat os­ta­vat ja myy­vät, mutta esi­merk­ki ei kui­ten­kaan ku­vas­ta to­del­lis­ta kau­pan­käyn­tiä. Siinä ei ole juuri muuta jär­keä kuin, että sii­hen on upo­tet­tu olio-oh­jel­moin­nin pe­rus­tei­ta.

            # -*- coding: utf-8 -*-
            """
            Created on Wed May 27 21:10:55 2015

            @author: heikki
            """

            # Määritellään, minkä tyyppisiä olioita ovat kauppiaat tässä ohjelmassa
            # Kullakin kauppiaalle on ominaisuudet varasto, kassa, tavaran yksikköhinta
            # ja nimi
            class kauppias():
            def __init__(self, kpl, Eur, hinta, nimi):
            self.varasto = kpl
            self.kassa = Eur
            self.hinta = hinta
            self.nimi = nimi

            # Kauppiaaseen liittyy metodi report, joka kertoo kauppiaan liiketoiminnan
            # tilan
            def report(self):
            print(self.nimi, " Varasto:",
            self.varasto, "Kassa: ", self.kassa)

            # Kauppiaalla on metodit osta ja myy
            # kun myyn, varastoni pienenee ja kassaan tulee rahaa.
            # Lisäksi kerron ostajalle ostoksen hinnan
            def myy(self, kpl):
            self.varasto -= kpl
            self.kassa += kpl*self.hinta
            return self.hinta*kpl

            # Python-kielessä a = a+b ei ole yhtälö vaan tarkoittaa, että a:n uusi arvo on
            # a:n vanha arvo lisättynä b:llä
            # operaatio a += b on sama kuin a = a+b. Vastaavasti a -= b

            # Kun ostan myyjältä, tämä kertoo ostokseni hinnan
            # Kassani pienee ja varastoni kasvaa
            def osta(self, kpl, Myyja):
            maksu = Myyja.myy(kpl)
            self.kassa -= maksu
            self.varasto += kpl


            # Pääohjelma (ohjelman suoritus) alkaa tästä.
            # "Luodaan" kaksi kauppiasta, A ja B. (Kauppiaita voisi olla vaikka sata)
            # Kauppias A: Varastoa 100 kpl, kassassa 100 euroa, myy hintaan 2€/kpl
            A = kauppias(100, 100, 2, 'Jaska')

            # Kauppias B: Varastoa 50 kpl, kassassa 200 euroa, myy hintaan 1€/kpl
            B = kauppias(50, 200, 1, 'Kalle')

            # Esimerkki siitä, miten kutsutaan olion metodia
            print("\nAlkutilanteen raportoinnit")
            A.report()
            B.report()

            print("\nA ostaa B:ltä 20 kpl B:n tarjoamaan edulliseen hintaan")
            A.osta(20, B)
            A.report()
            B.report()

            print("\nB ostaa A:lta 10 kpl A:n pyytämään kalliimpaan hintaan")
            B.osta(10, A)
            A.report()
            B.report()

        

Ylläolevan oh­jel­man koodi

Oh­jel­ma kir­joit­taa kon­so­lil­le seu­raa­vaa, kun pai­nat F5:

            Alkutilanteen raportoinnit
            Jaska  Varasto: 100 Kassa:  100
            Kalle  Varasto: 50 Kassa:  200

            A ostaa B:ltä 20 kpl B:n tarjoamaan edulliseen hintaan
            Jaska  Varasto: 120 Kassa:  80
            Kalle  Varasto: 30 Kassa:  220

            B ostaa A:lta 10 kpl A:n pyytämään kalliimpaan hintaan
            Jaska  Varasto: 110 Kassa:  100
            Kalle  Varasto: 40 Kassa:  200

        

Oh­jel­man toi­min­taa lie­nee vai­kea ym­mär­tää, mutta yritä silti. Aloi­ta koh­das­ta "pää­oh­jel­ma alkaa tästä". Aluk­si luo­daan kaksi olio­ta A ja B, jotka ovat tyyp­piä kaup­pias. Voit aja­tel­la, että olio on määrä­muo­toi­nen ar­kis­to­kort­ti — muis­ti­lappu, jol­lai­nen teh­dään jo­kai­ses­ta kaup­piaas­ta. Ylem­pä­nä oh­jel­ma­koo­dis­sa mää­rit­te­lyn class kaup­pias: sisällä funktion _init_ määrittely kertoo, mitä "arkistokorttiin" kirjoitetaan: Varaston määrä; kassan suuruus; yksikköhinta, jolla kauppias myy tavaroitaan sekä kauppiaan nimi.

Käs­kyl­lä A.report() pyydämme kauppiasta A kertomaan yrityksensä tilanne. Funktio report() on määritelty luokan (class) kauppias sisällä. (Luokan sisällä määriteltyjä funktioita kutsutaan olio-ohjelmoinnissa metodeiksi.) Vastaavasti käskyllä B.report() pyydämme samat tiedot kauppiaalta B.

Ei liene help­po ym­mär­tää, miksi käy­te­tään ter­me­jä class, luok­ka, me­to­di,– – ja siksi ter­meis­sä menee hel­pos­ti se­kai­sin. Paras ottaa mal­lia yksin­ker­tai­ses­ta esi­mer­kis­tä ja näin aluk­si unoh­taa ter­mien alku­perän päh­käi­ly.

Käs­kyl­lä A.osta(20,B) pyydämme A:ta ostamaan B:ltä 20 kappaletta. Metodit osta ja myy päivittävät varastojen ja kassojen tilanteet vastaamaan uutta tilannetta. Koeta selvittää, miten se tapahtuu.

Voit yrit­tää luoda vielä kol­man­nen kaup­piaan ja li­sä­tä osto­ta­pah­tu­mia. Jos on­nis­tut, si­nul­la ei enää ole kovin pal­joa opit­ta­vaa olio-oh­jel­moin­nin peri­aat­teis­ta.


Esi­merk­ke­jä funk­tioi­den kä­sit­te­lys­tä ja piir­te­lys­tä


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Python esimerkkejä > Rekursio, säikeet, synkronointi (2016-4-8)

Rekursio, säi­keet, synk­ro­noin­ti

Tein vuo­sia sit­ten oh­jel­man, joka piir­si puun re­kur­sii­vi­sel­la funk­tiol­la. Sen ok­sien väri ja pak­suus riip­pui etäi­syy­des­tä ty­ves­tä. Ok­sien kär­kiin piir­sin leh­den tai kukan. Li­sää­mäl­lä pik­kui­sen sa­tun­nai­suut­ta, pui­den piir­ty­mis­tä oli haus­ka kat­sel­la. On­nis­tuin jopa saa­maan puut huo­ju­maan. Jäin haa­vei­le­maan mai­se­mien luo­mi­ses­ta al­go­rit­mil­la, jossa olisi so­pi­vas­ti yh­dis­tet­ty­nä sään­nön­mu­kai­suut­ta ja sa­tun­nai­suut­ta. Vielä hie­nom­paa, jos jak­sai­si tehdä siitä kolmi­ulot­tei­sen niin, että sitä voisi zoo­mail­la ja kään­nel­lä. Niin pit­käl­le en pääs­syt, mutta on­nis­tuin­pa saa­maan puut piir­ty­mään vähän jou­he­vam­min kuin sil­loin kauan sit­ten.

puu kas­vaa

ja huo­juu


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Python esimerkkejä > Rekursio, säikeet, synkronointi > PuutPilvet.py ()

PuutPilvet.py

Seu­raa­va esi­merk­ki esit­te­lee, miten python-ohjelmointikielessä voi käyt­tää lis­to­ja, olioi­ta ja re­kur­sii­vi­sia funk­tioi­ta.

Oh­jel­ma piir­tää näy­töl­le nai­vis­ti­sen mai­se­man oh­jel­moin­ti­kie­len piir­tei­den ha­vain­nol­lis­ta­mi­sek­si. Esi­mer­kik­si pil­vet ja puun oksat ovat olioi­ta. Puu piir­re­tään re­kur­sii­vi­sel­la oh­jel­mal­la.

Wed Apr 2 20:09:00 2025


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Python esimerkkejä > Rekursio, säikeet, synkronointi > PuutPilvet.py > - ()

-

import pygame
import sys
from math import sin, cos, pi
import random


Musta = (0, 0, 0)  # Mustassa ei ole mitään valoa
Sin = (0, 0, 255)  # vain sinistä
Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Maasto = (100, 120, 10)
Kelt = (255, 255, 0)
Valk = (255, 255, 255)  # valkoisessa on kaikenväristä valoa
Tausta = (160, 200, 255)


MaxHaara = 10
LCoeff = 0.95
DKulma = 0.3
DS = 0.25
Ccos = 4.0


def screenxy(x, y):
    return (int(SKAALA*x+CXW/2.0), int(CYW-SKAALA*y))


def puuVari(Nhaara):
    r = 20 + (80-20)*Nhaara/MaxHaara
    g = 240 - (240-20)*Nhaara/MaxHaara
    return (r, g, 0)


class oksa:
    def __init__(self, paikka, alapuolinen, kulma, pituus, Nhaara):
        self.paikka = paikka  # Puun tyven sijainti
        self.kulma = kulma  # Oksanpätkän kulma alapuolella olevaan
        self.pituus = pituus
        self.paksuus = 2.0 + Nhaara
        self.Nhaara = Nhaara  # Monesko oksanpätkä latvasta lukien
        self.alapuolinen = alapuolinen  # Alapuolinen oksanpätkä
        self.vasenhaara = None
        self.oikeahaara = None


    def kasvata(self, Skulma):
        if self.Nhaara > 0 and Skulma < pi and Skulma > 0:
            # lisätään vielä ainakin yksi oksa kumpaankin suuntaan.
            # lasketaan lisättävälle oksanpätkälle pituus ...
            pituus = LCoeff*self.pituus + random.uniform(-DS, DS)
            + Ccos*(cos(Skulma) - 0.5)
            # ja asentokulma
            kulma = DKulma + random.uniform(-0.05, 0.05)
            # lisätään oksanpätkä ...
            self.vasenhaara = oksa(self.paikka, self, kulma,
                                   pituus, self.Nhaara-1)
            # ... ja jatketaan puun kasvatusta lisätystä oksanpätkästä ylöspäin
            self.vasenhaara.kasvata(Skulma+kulma)
            # kun vasenta haaraa on jatkettu latvaan asti,
            # ohjelman suoritus palaa tähän ja jatkaa oikeaa haaraa ylös.
            # Pituus ja kulma voisivat olla samat kuin vasemmallekin
            # lähtiessä, mutta vaihtelun vuoksi tehdään oikeasta haarasta
            # vähän erilainen
            pituus = LCoeff*self.pituus + random.uniform(-DS, DS)
            + Ccos*(cos(Skulma) - 0.5)
            kulma = - DKulma + random.uniform(-0.05, 0.05)
            self.oikeahaara = oksa(self.paikka, self, kulma,
                                   pituus, self.Nhaara-1)
            self.oikeahaara.kasvata(Skulma+kulma)
        else:
            # Lopuksi oksan päähän lisätään kukka
            self.vasenhaara = Kukka(self.pituus)


    def huoju(self, huojahdus):
        self.kulma = self.kulma+huojahdus/self.paksuus*self.pituus/7.0
        if self.vasenhaara is not None:
            self.vasenhaara.huoju(huojahdus)
        if self.oikeahaara is not None:
            self.oikeahaara.huoju(huojahdus)


    def piirra_w(self, x0, y0, kulma0, Wait, screen):
        kulma = self.kulma+kulma0
        if self.alapuolinen is None:
            (x0, y0) = self.paikka
        x = x0 + self.pituus*cos(kulma)
        y = y0 + self.pituus*sin(kulma)
        xy1 = screenxy(x, y)
        xy0 = screenxy(x0, y0)
        pygame.draw.line(screen, puuVari(self.Nhaara),
                         xy0, xy1, int(self.paksuus))
        if Wait:
            pygame.display.flip()
        if self.vasenhaara is not None:
            self.vasenhaara.piirra_w(x, y, kulma, Wait, screen)
        if self.oikeahaara is not None:
            self.oikeahaara.piirra_w(x, y, kulma, Wait, screen)
class Kukka:
    def __init__(self, pituus):
        # Kukan koko riippuu alapuolisen oksan pituudesta
        # Voisi riippua oksan paksuudesta tai olla vakio
        self.size = pituus/20.0
        self.colors = [Kelt, Sin, Pun, Kelt]
    # Piirretään kolme eriväristä ja -kokoista ympyrää päällekkäin.
    # Aloitetaan isoimmasta, ettei isompi peitä pienempäänsä.
    def piirra_w(self, x, y, kulma, Wait, screen):
        for i in range(3, 0, -1):
            xf, yf = screenxy(x-i*self.size, y+i*self.size)
            w = int(2*i*self.size*SKAALA)
            h = int(2*i*self.size*SKAALA)
            pygame.draw.ellipse(screen, self.colors[i], (xf, yf, w, h), 0)
        if Wait:
            pygame.display.flip()
    # Kukallakin pitää olla huoju-metodi, koska kutsuva ohjelma ei tiedä,
    # käsitteleekö se oksanpätkää vai kukkaa.
    def huoju(self, kulma):
        pass


class Pilvi:
    def __init__(self):
        xvasen = random.uniform(0.1, 0.9*CXW)
        ytop = random.uniform(0.1, 0.7*CYW)
        w = random.uniform(0.02*CXW, 0.2*CXW)
        h = max(0.2*w, random.uniform(0.02*CYW, 0.025*CYW))
        self.coords = pygame.Rect(int(xvasen), int(ytop), int(w), int(h))
    def liiku(self):
        # jos pilvi on ajautunut ulos peli-ikkunan oikeasta laidasta,
        # se siirretään peli-ikkunan vasempaan laitaan.
        if self.coords.left > CXW:
            self.coords.move_ip(-CXW-self.coords.width,
                                random.randrange(-1, 2))
        else:
            # move_ip siirtää pilveä satunnaisen hyppäyksen vasemmalle
            # ja lisäksi pikkuisen ylös tai alas
            max_speed = int(8-4*self.coords.y/CYW)
            self.coords.move_ip(random.randrange(0, max_speed),
                                random.randrange(-1, 2))
            # inflate_ip muuttaa pilven kokoa
            self.coords.inflate_ip(random.randrange(-1, 2),
                                   random.randrange(-1, 2))
            # ei anneta pilvien kasvaa rajatta eikä kutistua olemattomiin
            # pidetään pilven korkeus reilusti pienempänä kuin leveys
            self.coords.width = max(int(0.02*CXW),
                                    min(int(0.2*CXW), self.coords.width))
            self.coords.height = min(int(self.coords.width/5.0),
                                     max(int(0.02*CYW), self.coords.height))
    def piirra(self, screen):
        pygame.draw.ellipse(screen, Valk, self.coords, 0)
# Piirretään kaksi kukkulaa ja rajataan ne mustalla viivalla
def maasto(screen):
    pygame.draw.ellipse(screen, Maasto,
                        (0.4*CXW, 0.85*CYW, 0.9*CXW, CYW), 0)
    pygame.draw.ellipse(screen, Musta,
                        (0.4*CXW, 0.85*CYW, 0.9*CXW, CYW), 2)
    pygame.draw.ellipse(screen, Maasto,
                        (-0.1*CXW, 0.8*CYW, 0.8*CXW, 0.8*CYW), 0)
    pygame.draw.ellipse(screen, Musta,
                        (-0.1*CXW, 0.8*CYW, 0.8*CXW, 0.8*CYW), 2)
# Tarkistetaan, haluaako käyttäjä pysäyttää ohjelman.
# Tämä ei ole aivan tyylikäs tapa pysäyttää ohjelma.
# Ongelmana on reagoida keskeytyspyyntöön monikertaisen for-silmukan
# sisältä.
# En ole vielä opetellut try - exception rakenteen käyttöä.
def lopetus():
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            pygame.quit()
            sys.exit()
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pääohjelma alkaa tästä
# # # # # # # # # # # # # # # # # # # # # # # # # # #


def main():
    global CXW, CYW, XW, YW, SKAALA
    pygame.display.init()
    # Selvitetään näytön koko pikseleinä
    D_Info = pygame.display.Info()
    CXW = D_Info.current_w
    CYW = D_Info.current_h
    YW = 100.0  # Maiseman korkeus.
    XW = CXW/CYW*YW  # Maiseman leveys
    SKAALA = CYW/YW
    screen = pygame.display.set_mode((CXW, CYW))
    pygame.display.set_caption("puut")
    pygame.init()
    random.seed()
    screen.fill(Tausta)
    # Piirretään kukkulat
    maasto(screen)
    pygame.display.flip()
    # Luodaan sadan pilven lista
    pilvet = []
    for i in range(100):
        pilvet.append(Pilvi())
    # Piirretään pilvet yksi kerrallaan
    for pilvi in pilvet:
        pilvi.piirra(screen)
        pygame.display.flip()
        pygame.time.wait(50)
    # Luodaan "metsä" eli lista puista
    puut = []
    puut.append(oksa((-60, 15), None, pi/2.0+0.1, 5.0, MaxHaara-2))
    puut.append(oksa((50, 10), None, pi/2.0-0.1, 7.0, MaxHaara-1))
    puut.append(oksa((-10, 4), None, pi/2.0, 9.0, MaxHaara))
    # Kasvatetaan puut ja piirretään ne.
    # Wait = True eli päivitetään näyttö jokaisen
    # oksan piirtämisen jälkeen.
    for puu in puut:
        puu.kasvata(pi/2.0)
        puu.piirra_w(0, 2, 0.0, True, screen)
    pygame.time.wait(500)
    s = 0.0075
    while True:
        # käännytään keskeltä ääriasentoon ja takaisin sadalla s:n kokoisella
        # huojahduksella
        # Sitten sama toiseen ääriasentoon
        for j in range(2):
            for k in range(100):
                screen.fill(Tausta)
                for pilvi in pilvet:
                    pilvi.liiku()
                    pilvi.piirra(screen)
                maasto(screen)
                for puu in puut:
                    puu.huoju(s)
                    puu.piirra_w(0, 2, 0.0, False, screen)
                pygame.display.flip()
                lopetus()
                pygame.time.wait(10)
            s = -s  # Käännytään ääriasennosta takaisin
        s = -s  # Lähdetään keskeltä toiseen suuntaan kuin viimeksi
    pygame.quit()
if __name__ == "__main__":
    main()


Se­li­tyk­siä ylläolevaan

Allaolevat import käs­kyt tuo­vat tämän oh­jel­man käyt­töön val­mii­ta moduleita, joi­den sisältämista funk­tiois­ta saa tie­toa mm. webin python-oppaista. Gra­fiik­ka on to­teu­tet­tu pygame-modulin avul­la lä­hin­nä siksi, että satun tun­te­maan sen en­nes­tään.

import pygame
import sys
from math import sin, cos, pi
import random



Seu­raa­vil­la ri­veil­lä on glo­baa­le­ja va­kioi­ta, joita voi käyt­tää missä hy­vän­sä oh­jel­man osas­sa.

Pygame-modulissa värit esi­te­tään il­moit­ta­mal­la lu­vul­la vä­lil­tä 0..255 pu­nai­sen, vih­reän ja si­ni­sen värin osuus.

Musta = (0, 0, 0) <orange> #  Mustassa ei ole mitään valoa
</orange>Sin = (0, 0, 255) <orange> #  vain sinistä
</orange>Pun = (255, 0, 0)
Vihr = (0, 255, 0)
Maasto = (100, 120, 10)
Kelt = (255, 255, 0)
Valk = (255, 255, 255) <orange> #  valkoisessa on kaikenväristä valoa
</orange>Tausta = (160, 200, 255)



Seu­raa­vien pa­ra­met­rien mer­ki­tys sel­viää myö­hem­min.

Tyylikkässä oh­jel­mas­sa olisi graa­fi­nen käyt­tö­liit­ty­mä, joka se­lit­täi­si pa­ra­met­rien mer­ki­tyk­sen ja an­tai­si mah­dol­li­suu­den muut­taa nii­den ole­tus­ar­vo­ja. Ehkä jos­kus opet­te­len te­ke­mään käyt­tö­liit­ty­män tämän­ta­pai­siin oh­jel­miin.

MaxHaara = 10
LCoeff = 0.95
DKulma = 0.3
DS = 0.25
Ccos = 4.0



Funk­tio screenxy(x, y) las­kee, missä koh­taa näy­töl­lä on peli­maailman piste (x, y)



def screenxy(x, y):
    return (int(SKAALA*x+CXW/2.0), int(CYW-SKAALA*y))



Pui­den run­ko­jen väri vaih­tuu tyven tum­man­rus­keas­ta latva­ok­sien vaa­lean­vih­reään. Seu­raa­va funk­tio las­kee puun run­gon värin. Oh­jel­mas­sa puu kas­vaa vai­heit­tain kuin vuosi­kasvu ker­ral­laan. Nhaara ker­too, mo­nes­ko vuosi­kasvu lat­vas­ta lu­kien on vär­jät­tä­vä­nä. MaxHaara on vuosi­kas­vu­jen enim­mäis­määrä.



def puuVari(Nhaara):
    r = 20 + (80-20)*Nhaara/MaxHaara
    g = 240 - (240-20)*Nhaara/MaxHaara
    return (r, g, 0)



Puu koos­tuu ok­sik­si kut­su­tuis­ta pät­kis­tä. Oksan omi­nai­suu­det ovat pi­tuus, asen­non ker­to­va kulma, pak­suus ja tieto siitä, mo­nes­ko pätkä se on lat­vas­ta lu­kien. Ok­sal­la on myös tieto siitä, minkä ala­puo­li­sen oksan­pät­kän va­ras­sa se on ja mitkä oksan­pät­kät läh­te­vät siitä ylös­päin oi­keal­le ja va­sem­mal­le.



class oksa:
    def __init__(self, paikka, alapuolinen, kulma, pituus, Nhaara):
        self.paikka = paikka <orange> #  Puun tyven sijainti
</orange>        self.kulma = kulma <orange> #  Oksanpätkän kulma alapuolella olevaan
</orange>        self.pituus = pituus
        self.paksuus = 2.0 + Nhaara
        self.Nhaara = Nhaara <orange> #  Monesko oksanpätkä latvasta lukien
</orange>        self.alapuolinen = alapuolinen <orange> #  Alapuolinen oksanpätkä
</orange>        self.vasenhaara = None
        self.oikeahaara = None


Seu­raa­va funtio lisää puu­hun ty­ves­tä läh­tien oksan­pät­kän ker­ral­laan kun­nes pääs­tään lat­vaan asti tai kun­nes oksa kään­tyy maata kohti. Jat­ke­taan siis puun kas­vat­ta­mis­ta niin kauan kuin Nhaara > 0 ja ja kulma ho­ri­sont­tiin näh­den - Skulma - on pie­nem­pi kuin 180 as­tet­ta ja suu­rem­pi kuin 0. Tätä funk­tio­ta vähän muu­tet­tu­na voisi kut­sua suo­raan ylläolevassa __init__ funktiossa: self.vasenhaara = kas­va­ta(self, ...

Oksan­pät­kän asen­to­kulma on 0, kun oksa osoit­taa vaaka­suo­raan oi­keal­le ja asen­to­kulma on 180, kun oksa osoit­taa suo­raan va­sem­mal­le. Pythonissa kul­mat il­moi­te­taan ole­tus­ar­voi­ses­ti ra­diaa­nei­na, eli 180 as­tet­ta on pii ra­diaa­nia.

Uuden oksan­pät­kän pi­tuus voisi olla sama kuin sen ala­puo­lel­la ole­van­kin, mutta huvin vuok­si li­sä­sin puun kas­vul­la tai­pu­muk­sen, että ylä­puo­li­nen on hie­man ala­puo­lis­ta oksaa ly­hyem­pi (LCoeff < 1.0). Lisäksi oksa kasvaa sitä paremmin, mitä pystysuorempaan se on kasvamassa. (Termi Ccos*(cos(Skulma) - 0.5)) Lisäsin oksan pituuskasvuun myös hieman satunnaisuutta (random.uniform(-DS, DS)).

    def kasvata(self, Skulma):
        if self.Nhaara > 0 and Skulma < pi and Skulma > 0:
           <orange> #  lisätään vielä ainakin yksi oksa kumpaankin suuntaan.
</orange>           <orange> #  lasketaan lisättävälle oksanpätkälle pituus ...
</orange>            pituus = LCoeff*self.pituus + random.uniform(-DS, DS)
            + Ccos*(cos(Skulma) - 0.5)
           <orange> #  ja asentokulma
</orange>            kulma = DKulma + random.uniform(-0.05, 0.05)
           <orange> #  lisätään oksanpätkä ...
</orange>            self.vasenhaara = oksa(self.paikka, self, kulma,
                                   pituus, self.Nhaara-1)
           <orange> #  ... ja jatketaan puun kasvatusta lisätystä oksanpätkästä ylöspäin
</orange>            self.vasenhaara.kasvata(Skulma+kulma)

           <orange> #  kun vasenta haaraa on jatkettu latvaan asti,
</orange>           <orange> #  ohjelman suoritus palaa tähän ja jatkaa oikeaa haaraa ylös.
</orange>           <orange> #  Pituus ja kulma voisivat olla samat kuin vasemmallekin
</orange>           <orange> #  lähtiessä, mutta vaihtelun vuoksi tehdään oikeasta haarasta
</orange>           <orange> #  vähän erilainen
</orange>            pituus = LCoeff*self.pituus + random.uniform(-DS, DS)
            + Ccos*(cos(Skulma) - 0.5)
            kulma = - DKulma + random.uniform(-0.05, 0.05)

            self.oikeahaara = oksa(self.paikka, self, kulma,
                                   pituus, self.Nhaara-1)
            self.oikeahaara.kasvata(Skulma+kulma)

        else:
           <orange> #  Lopuksi oksan päähän lisätään kukka
</orange>            self.vasenhaara = Kukka(self.pituus)



huoju kääntää jokaista oksanpätkää parametristä huojahdus ja oksan paksuudesta ja pituudesta riippuvan kulman verran Huomaa, että meidän ei tarvitse tarkistaa, onko yläpuolinen haara oksanpätkä vai latvakukka. Riittää, että olioluokalle Kukka on sillekin määritelty metodi huoju.


    def huoju(self, huojahdus):
        self.kulma = self.kulma+huojahdus/self.paksuus*self.pituus/7.0
        if self.vasenhaara is not None:
            self.vasenhaara.huoju(huojahdus)
        if self.oikeahaara is not None:
            self.oikeahaara.huoju(huojahdus)



Piir­re­tään puu re­kur­sii­vi­ses­ti ty­ves­tä läh­tien oksan­pätkä ker­ral­laan. Tyveä piir­ret­täes­sä oksan­pät­kän ala­pään paik­ka (x0, y0) ) on tietysti puun tyven paikka, muussa tapauksessa funtion argumenttina saatava alapuolisen oksan yläpään paikka.

Oksan­pät­kän kulma ho­ri­sont­tiin näh­den on sen suh­teel­li­nen kulma ala­puo­li­seen ok­saan näh­den + ar­gu­ment­ti­na saa­ta­va kulma0, joka on alapuolisen oksan kulma horisonttiin nähden

Oksan­pät­kän ylä­pään paik­ka (x, y) saadaan trigonometrian avulla. Miten, sen ymmärtää, kun piirtää kuvan.

screenxy(x, y) laskee maiseman pistettä tt vastaavan pisteen pygamen peli-ikkunassa.

Wait määrää, päivitetäänkö peli-ikkuna näytöllä jokaisen oksanpätkän piirtämisen jälkeen.


    def piirra_w(self, x0, y0, kulma0, Wait, screen):
        kulma = self.kulma+kulma0
        if self.alapuolinen is None:
            (x0, y0) = self.paikka
        x = x0 + self.pituus*cos(kulma)
        y = y0 + self.pituus*sin(kulma)
        xy1 = screenxy(x, y)
        xy0 = screenxy(x0, y0)
        pygame.draw.line(screen, puuVari(self.Nhaara),
                         xy0, xy1, int(self.paksuus))
        if Wait:
            pygame.display.flip()
        if self.vasenhaara is not None:
            self.vasenhaara.piirra_w(x, y, kulma, Wait, screen)
        if self.oikeahaara is not None:
            self.oikeahaara.piirra_w(x, y, kulma, Wait, screen)


class Kukka:
    def __init__(self, pituus):
       <orange> #  Kukan koko riippuu alapuolisen oksan pituudesta
</orange>       <orange> #  Voisi riippua oksan paksuudesta tai olla vakio
</orange>        self.size = pituus/20.0
        self.colors = [Kelt, Sin, Pun, Kelt]

   <orange> #  Piirretään kolme eriväristä ja -kokoista ympyrää päällekkäin.
</orange>   <orange> #  Aloitetaan isoimmasta, ettei isompi peitä pienempäänsä.
</orange>    def piirra_w(self, x, y, kulma, Wait, screen):
        for i in range(3, 0, -1):
            xf, yf = screenxy(x-i*self.size, y+i*self.size)
            w = int(2*i*self.size*SKAALA)
            h = int(2*i*self.size*SKAALA)
            pygame.draw.ellipse(screen, self.colors[i], (xf, yf, w, h), 0)

        if Wait:
            pygame.display.flip()

   <orange> #  Kukallakin pitää olla huoju-metodi, koska kutsuva ohjelma ei tiedä,
</orange>   <orange> #  käsitteleekö se oksanpätkää vai kukkaa.
</orange>    def huoju(self, kulma):
        pass



Pil­vet ovat suora­kai­teen si­sään piir­ret­tä­viä el­lip­se­jä. Käy­tän ko­keek­si pygame-modulin olio­luok­kaa Rect, jonka siirtelyn ja koon muuntelun pitäisi olla helppoa ja ohjelman suorituksen kannalta tehokasta. (xvasen, ytop) on suorakaiteen vasen yläkulma ja (w, h) sen leveys ja korkeus.

Käy­tän pil­viä piir­täes­sä suo­raan peli-ik­ku­nan koor­di­naa­tis­toa, vaik­ka olisi ollut tyy­lik­kääm­pää käyt­tää samaa mai­se­man koor­di­naa­tis­toa kuin puita piir­täes­sä.

Pil­vet liik­ku­vat va­sem­mal­ta oi­keal­le.



class Pilvi:
    def __init__(self):
        xvasen = random.uniform(0.1, 0.9*CXW)
        ytop = random.uniform(0.1, 0.7*CYW)
        w = random.uniform(0.02*CXW, 0.2*CXW)
        h = max(0.2*w, random.uniform(0.02*CYW, 0.025*CYW))
        self.coords = pygame.Rect(int(xvasen), int(ytop), int(w), int(h))

    def liiku(self):
       <orange> #  jos pilvi on ajautunut ulos peli-ikkunan oikeasta laidasta,
</orange>       <orange> #  se siirretään peli-ikkunan vasempaan laitaan.
</orange>        if self.coords.left > CXW:
            self.coords.move_ip(-CXW-self.coords.width,
                                random.randrange(-1, 2))
        else:
           <orange> #  move_ip siirtää pilveä satunnaisen hyppäyksen vasemmalle
</orange>           <orange> #  ja lisäksi pikkuisen ylös tai alas
</orange>            max_speed = int(8-4*self.coords.y/CYW)
            self.coords.move_ip(random.randrange(0, max_speed),
                                random.randrange(-1, 2))
           <orange> #  inflate_ip muuttaa pilven kokoa
</orange>            self.coords.inflate_ip(random.randrange(-1, 2),
                                   random.randrange(-1, 2))
           <orange> #  ei anneta pilvien kasvaa rajatta eikä kutistua olemattomiin
</orange>           <orange> #  pidetään pilven korkeus reilusti pienempänä kuin leveys
</orange>            self.coords.width = max(int(0.02*CXW),
                                    min(int(0.2*CXW), self.coords.width))
            self.coords.height = min(int(self.coords.width/5.0),
                                     max(int(0.02*CYW), self.coords.height))

    def piirra(self, screen):
        pygame.draw.ellipse(screen, Valk, self.coords, 0)

<orange> #  Piirretään kaksi kukkulaa ja rajataan ne mustalla viivalla
</orange>def maasto(screen):
    pygame.draw.ellipse(screen, Maasto,
                        (0.4*CXW, 0.85*CYW, 0.9*CXW, CYW), 0)
    pygame.draw.ellipse(screen, Musta,
                        (0.4*CXW, 0.85*CYW, 0.9*CXW, CYW), 2)
    pygame.draw.ellipse(screen, Maasto,
                        (-0.1*CXW, 0.8*CYW, 0.8*CXW, 0.8*CYW), 0)
    pygame.draw.ellipse(screen, Musta,
                        (-0.1*CXW, 0.8*CYW, 0.8*CXW, 0.8*CYW), 2)


<orange> #  Tarkistetaan, haluaako käyttäjä pysäyttää ohjelman.
</orange><orange> #  Tämä ei ole aivan tyylikäs tapa pysäyttää ohjelma.
</orange><orange> #  Ongelmana on reagoida keskeytyspyyntöön monikertaisen for-silmukan
</orange><orange> #  sisältä.
</orange><orange> #  En ole vielä opetellut try - exception rakenteen käyttöä.
</orange>def lopetus():
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            pygame.quit()
            sys.exit()

<orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange><orange> #  Pääohjelma alkaa tästä
</orange><orange> #  # # # # # # # # # # # # # # # # # # # # # # # # # #
</orange>


Yleen­sä pe­leis­sä tai kuvia piir­rel­les­sä peli­maailman mitta­yk­si­köt kan­nat­taa erot­taa näy­tön mittatyksiköistä, koska oh­jel­man pitää toi­mia jär­ke­väs­ti eri ko­koi­sil­la näy­töil­lä. Voin esi­mer­kik­si si­joit­taa puun pis­tee­seen (-30, 5) riippumatta siitä, montako pikseliä peliruuden koko on näytöllä.

Tässä oh­jel­mas­sa peli­maailman kor­keus on kiin­ni­tet­ty sa­dak­si yk­si­kök­si ja le­veys so­vi­te­taan niin, että peli­maailman ja peli-ik­ku­nan kuva­suhde on sama.

Seu­raa­vas­sa on mää­ri­tel­ty peli-ik­ku­nan ja näy­tön ko­koon liit­ty­vät pa­ra­met­rit glo­baa­leik­si muut­tu­jik­si, jotta niitä voi käyt­tää missä hy­vän­sä oh­jel­man koh­das­sa. Glo­baa­le­ja muut­tu­jia on hou­kut­te­le­va käyt­tää eri­tyi­ses­ti oh­jel­maa ke­hit­täes­sä. Tur­val­li­sem­paa, mutta työ­lääm­pää, on vä­lit­tää funk­tioil­le kaik­ki tar­vit­ta­va tieto kutsu­pa­ra­met­rien kaut­ta.



def main():
    global CXW, CYW, XW, YW, SKAALA

    pygame.display.init()

   <orange> #  Selvitetään näytön koko pikseleinä
</orange>    D_Info = pygame.display.Info()
    CXW = D_Info.current_w
    CYW = D_Info.current_h

    YW = 100.0 <orange> #  Maiseman korkeus.
</orange>    XW = CXW/CYW*YW <orange> #  Maiseman leveys
</orange>    SKAALA = CYW/YW

    screen = pygame.display.set_mode((CXW, CYW))
    pygame.display.set_caption("puut")
    pygame.init()

    random.seed()

    screen.fill(Tausta)

   <orange> #  Piirretään kukkulat
</orange>    maasto(screen)
    pygame.display.flip()

   <orange> #  Luodaan sadan pilven lista
</orange>    pilvet = []
    for i in range(100):
        pilvet.append(Pilvi())

   <orange> #  Piirretään pilvet yksi kerrallaan
</orange>    for pilvi in pilvet:
        pilvi.piirra(screen)
        pygame.display.flip()
        pygame.time.wait(50)

   <orange> #  Luodaan "metsä" eli lista puista
</orange>    puut = []
    puut.append(oksa((-60, 15), None, pi/2.0+0.1, 5.0, MaxHaara-2))
    puut.append(oksa((50, 10), None, pi/2.0-0.1, 7.0, MaxHaara-1))
    puut.append(oksa((-10, 4), None, pi/2.0, 9.0, MaxHaara))

   <orange> #  Kasvatetaan puut ja piirretään ne.
</orange>   <orange> #  Wait = True eli päivitetään näyttö jokaisen
</orange>   <orange> #  oksan piirtämisen jälkeen.
</orange>    for puu in puut:
        puu.kasvata(pi/2.0)
        puu.piirra_w(0, 2, 0.0, True, screen)

    pygame.time.wait(500)

    s = 0.0075

    while True:
       <orange> #  käännytään keskeltä ääriasentoon ja takaisin sadalla s:n kokoisella
</orange>       <orange> #  huojahduksella
</orange>       <orange> #  Sitten sama toiseen ääriasentoon
</orange>        for j in range(2):
            for k in range(100):
                screen.fill(Tausta)
                for pilvi in pilvet:
                    pilvi.liiku()
                    pilvi.piirra(screen)
                maasto(screen)
                for puu in puut:
                    puu.huoju(s)
                    puu.piirra_w(0, 2, 0.0, False, screen)
                pygame.display.flip()
                lopetus()
                pygame.time.wait(10)
            s = -s <orange> #  Käännytään ääriasennosta takaisin
</orange>        s = -s <orange> #  Lähdetään keskeltä toiseen suuntaan kuin viimeksi
</orange>
    pygame.quit()

if __name__ == "__main__":

    main()



Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: PuutPilvet_0.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Python esimerkkejä > Rekursio, säikeet, synkronointi > synkronointi.py ()

synkronointi.py

Eri säi­keis­sä toi­mi­vien oh­jel­mien synk­ro­noin­ti

Wed Apr 2 20:09:20 2025


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Python esimerkkejä > Rekursio, säikeet, synkronointi > synkronointi.py > Synkronointi ()

Synk­ro­noin­ti

# Jokaisen pilven säikeessä on silmukka, jossa siirretään pilveä ja
# piirretään se
while not loppu:
    # siirretään pilveä
    self.siirra()
    # ja sitten piirretään pilvi uuteen paikkaan
    # Ennen kuin aletaan piirtää, anotaan lupaa kirjoittaa näytölle
    portti.acquire()
    # odotetaan, kunnes näyttöä päivittävä ohjelma antaa luvan
    portti.wait()
    # piirretään
    self.piirra()
    # vapautetaan varaus
    portti.release()
# Pääohjelman säikeessä on silmukka, joka päivittää näytön määrävälein
while not loppu:
    # Odotetaan, että portti vapautuu ja estetään sen jälkeen
    # muita ohittamasta porttia
    portti.acquire()
    pygame.display.flip()  # Päivitetään näyttö
    screen.fill(Tausta)  # Tyhjennetään "kangas"
    portti.notify_all()  # Ilmoitetaan muille säikeille, että portti vapautuu
    portti.release()  #
    # Odotellaan hetki, että muut säikeet ehtivät piirtää kankaalle jotain
    # uutta. DT on näytön päivitysväli
    time.sleep(DT)


Se­li­tyk­siä ylläolevaan

Tässä oh­jel­ma­ver­sios­sa kukin puu ja pilvi toi­mii eri säikessään. Oh­jel­moi­jan on hyvä aja­tel­la, että säi­kei­tä suo­ri­te­taan rin­nak­kain, koska kun­kin säi­keen suo­ri­tus ete­nee omaa tah­tiaan -- asynk­ro­ni­ses­ti. Multi­yti­mi­ses­sä pro­ses­so­ris­sa säi­keet voi­si­vat edetä ai­dos­ti rin­nak­kain, mutta ai­dos­ti rin­nak­kai­sen oh­jel­man te­ke­mi­nen vaa­tii vielä lisä­temp­pu­ja.

Jos säi­kei­den ha­lu­taan ot­ta­van tois­ten­sa suo­ri­tus­vai­heen huo­mioon, ne täy­tyy lait­taa vä­lit­tä­mään toi­sil­leen vies­te­jä. Seu­raa­vas­sa esi­te­tään yksi tapa synk­ro­noi­da säi­kei­den suo­ri­tus.

Jo­kai­sen puun ja pil­ven siir­te­lyyn ja piir­tä­mi­seen tar­vit­ta­va oh­jel­ma käyn­nis­te­tään omak­si säi­keek­seen. Ne piir­tä­vät yh­tei­sel­le "kan­kaal­le". Pääohjelmä siir­tää määrä­vä­lein "kan­kaan" si­säl­lön näyt­tö­muis­tiin ja pyyh­kii kan­kaan tyh­jäk­si. Ellei piir­tä­mi­siä ja näy­tön päi­vi­tys­tä synkroinoida, näy­töl­le tulee mitä sat­tuu.


<orange> #  Jokaisen pilven säikeessä on silmukka, jossa siirretään pilveä ja
</orange><orange> #  piirretään se
</orange>while not loppu:
   <orange> #  siirretään pilveä
</orange>    self.siirra()
   <orange> #  ja sitten piirretään pilvi uuteen paikkaan
</orange>   <orange> #  Ennen kuin aletaan piirtää, anotaan lupaa kirjoittaa näytölle
</orange>    portti.acquire()
   <orange> #  odotetaan, kunnes näyttöä päivittävä ohjelma antaa luvan
</orange>    portti.wait()
   <orange> #  piirretään
</orange>    self.piirra()
   <orange> #  vapautetaan varaus
</orange>    portti.release()


<orange> #  Pääohjelman säikeessä on silmukka, joka päivittää näytön määrävälein
</orange>while not loppu:
   <orange> #  Odotetaan, että portti vapautuu ja estetään sen jälkeen
</orange>   <orange> #  muita ohittamasta porttia
</orange>    portti.acquire()
    pygame.display.flip() <orange> #  Päivitetään näyttö
</orange>    screen.fill(Tausta) <orange> #  Tyhjennetään "kangas"
</orange>    portti.notify_all() <orange> #  Ilmoitetaan muille säikeille, että portti vapautuu
</orange>    portti.release() <orange> #
</orange>   <orange> #  Odotellaan hetki, että muut säikeet ehtivät piirtää kankaalle jotain
</orange>   <orange> #  uutta. DT on näytön päivitysväli
</orange>    time.sleep(DT)



Lataa tästä oh­jel­man tämän het­ki­nen ver­sio: synk­ro­noin­ti_0.py


Heikin pohteita > Ohjelmointia, matematiikkaa, fysiikkaa … > Python esimerkkejä > Rekursio, säikeet, synkronointi > PuutPilvetSaikeet.py ()

PuutPilvetSaikeet.py

Tämä on säikeistetty ver­sio edel­li­ses­tä oh­jel­mas­ta. Oh­jel­mas­sa on lis­to­ja, olioi­ta, re­kur­sii­vi­sia funk­tioi­ta ja säi­kei­tä. Oh­jel­ma piir­tää näy­töl­le nai­vis­ti­sen mai­se­man oh­jel­moin­ti­kie­len piir­tei­den ha­vain­nol­lis­ta­mi­sek­si. Esi­mer­kik­si pil­vet ja puut ovat omis­sa säi­keis­sään suo­ri­tet­ta­via olioi­ta. Puut luo­daan ja piir­re­tään re­kur­sii­vi­sel­la oh­jel­mal­la.

Wed Apr 2 20:19:12 2025