Näytetään tekstit, joissa on tunniste Koodauksen ABC - kurssi. Näytä kaikki tekstit
Näytetään tekstit, joissa on tunniste Koodauksen ABC - kurssi. Näytä kaikki tekstit

perjantai 20. maaliskuuta 2015

Koodauksen ABC: 14. oppitunti

Tällä kerralla lisäsimme pacman - peliin logiikan, jolla estämme pacmanin menemästä seinien läpi. Viime kerralla jäi implementoimatta testi, jolla toteamme onko pacman ns. seinäruudussa. Aloitimme siis kirjoittamalla funktion onko-seinä? Tässä funktiossa jouduimme ottamaan huomioon sen, että meidän seinälistassamme on vain ne seinäpalat, jotka sijaitsevat ns. keskellä, joten lisäsimme ehdot kentän ylä-, ala- ja sivureinoille.

;; onko-seinä? : paikka -> boolean
(define (onko-seinä? p1)
  (define x (paikka-x p1))
  (define y (paikka-y p1))
  (or (onko-kohdalla? p1 SEINÄ-LISTA)
      (<= x 0)
      (>= x (add1 PELIN-LEVEYS))
      (<= y 0)
      (>= y (add1 PELIN-KORKEUS)))) 


Varsinainen testi sille onko pacman seinäruudussa, toteutettiin apufunktion onko-kohdalla? avulla. Tämä funktio kirjoitettiin erikseen siksi, että samalla testillä voidaan havaita jatkossa myös namut.

;; onko-kohdalla? : paikka paikka-lista -> boolean
(define (onko-kohdalla? p1 lista)
  (ormap (lambda (p2) (paikka=? p1 p2)) lista))


Tässä käytimme apuna aikaisemmin kirjoittamaamme paikka=? predikaattia, jolla testattaan ovatko kaksi paikkaa samat. Testi tehdään listan jokaiselle alkiolle (verrataan niitä p1:teen), ja jos yksikin testi tuottaa arvoksi true, ormap palauttaa true. Lambda - lauseke tuossa koodin keskellä tuottaa funktion, jota "mäpätään" listan alkioihin. Vaikka koodin toimintaa oli taas vaikea selittää oppilaille, se oli onneksi lyhyt kirjoittaa.

Nyt kokeilimme koodin toimintaa. Ja kuten monesti aikaisemminkin, eihän se heti mennyt putkeen mutta pienen debugaamisen jälkeen suurin osa pacmaneista osasi nyt pysähtyä ennen seinää. Yksi jäi mystisesti jumittamaan ja toiselle ilmaantui erikoisia voimia välillä kävellä seinän läpi, kun sopivasti nuolinäppäimiä napsutteli.

Loppuhuipentumana lisäsimme namujen syömisen. Lisäsimme uuden ehdon päivitä-peli - funktion cond:iin, jossa testaamme olemmeko käytävällä ja namun kanssa samassa ruudussa (and (not seinä?) namu?). Tämä testi käyttää edellä tehtyä onko-kohdalla? -funktiota. Jos ollaan namun kohdalla, kutsutaan syö-namu -funktiota ja lisätään pisteitä yhdellä.

;; päivitä-peli : peli -> peli
(define (päivitä-peli tila)
  (define pacman (liikuta-hahmo tila))
  (define seinä? (onko-seinä? pacman))
  (define namu? (onko-kohdalla? pacman (peli-namut tila)))
  (cond
     [(and (not seinä?) namu?)
     (peli (paikka-x pacman)
           (paikka-y pacman)
           (hahmo-suunta tila)

           (syö-namu pacman (peli-namut tila))
           (peli-haamut tila)

           (add1 (peli-pisteet tila))
           (peli-elossa? tila))]

     ...

Syö-namu - funktio jäikin kurssin viimeiseksi tempuksi. Siinä poistetaan namulistasta pacman:in paikkaa vastaava namu. Ja jälleen käytimme paikka=? predikaattia. Lyhyt ja tehokas koodi ja yllättäen yksi oppilas muisti, että listasta poistettiin alkoita kutsumalla remove:a.

;; syö-namu : paikka paikka-lista -> paikka-lista
(define (syö-namu p namut)
  (remove p namut paikka=?))


Tämän jälkeen olikin vuorossa kuumeista debuggausta ja harmikseni emme saaneet kaikkia pacmanejä toimimaan ennen kuin tunti loppui. Kaikkein harmillisinta asiassa oli tietysti se, että tämä oli myös kurssin viimeinen tunti, joten projektin deadline tuli taas aivan liian aikaisin. Toisaalta ilo oli sitten sitäkin suurempi, kun ensimmäinen pacman ryhtyi syömään namuja labyrintissä.


Tämä oli ensimmäinen kokemukseni ohjelmointikurssin vetämisestä, ja taisi käydä niin että opettaja oppi kurssilla enemmän ohjelmointia kuin oppilaat. No, jostakin on aloitettava eikä tämä nyt aivan katastrofi ollut, vaikka aika loppuikin kesken. Ensivuonna suunnittelen kurssin kyllä hieman eri tavalla, tämä yhden vaikeahkon pelin vääntäminen yhdessä ei välttämättä ole se paras lähestymistapa, ehkä useampi pienempi peli omassa tahdissa voisi toimia paremmin? Hienoa tässä oli kuitenkin se, että oppilaat jaksoivat yrittää loppuun asti ja mielenkiinto pysyi yllä, joten jonkinlaista pitkäjänteisyyttä ja kärsivällisyyttä tässä varmasti tuli opittua sivutuotteena. Ja toivottavasti edes vähän myös ohjelmointia :-)

Jos haluat tutkia Racket-pacmanin koodia voit ladata sen tästä. Pelaamista voi kokeilla avaamalla  tiedoston DrRacketissa ja painamalla run.

Tässä analyysi kurssin plussista ja miinuksista:

PlussatMiinukset

  • Pelin koodaaminen motivoi pitkäjänteiseen työskentelyyn, motivaatio säilyi koko kurssin ajan ja jokainen uusi opeteltava ohjelmointitekniikka oli helppo perustella, koska ilman sitä peli ei olisi edennyt.

  • Yhden pelin tekeminen ei antanut tarpeeksi toistomahdollisuuksia (ei syntynyt rutiinia), ja uudet ohjelmointitekniikat unohtuivat nopeasti.

  • Pelin tekemisessä tuli monipuolisia ongelmia ratkaistavaksi. Kurssin myötä syntyi myös Racket - Turtle, jolla leikkiminen oli mukavaa vaihtelua.

  • Pacman oli hieman liian vaativa peli aloittelijoiden koodattavaksi, aika loppui kesken.

  • DrRacket toimi hyvin koko ajan. Työkalun oppiminen oli nopeaa ja tiedostojen tallentaminen kotihakemistoon ei tuottanut oppilaille ongelmia.

  • Debuggaamiseen meni lopussa paljon aikaa, koska emme testanneet funktioita kunnolla implementointivaiheessa.

  • Vaikka kaikki tekivät "samaa" peliä, jokaisella oli eri näköinen pacman sekä itse suunniteltu labyrintti. Tuli tunne "omasta" projektista.

  • Kurssi mentiin opettajavetoisesti ja kopioimalla opettajan mallikoodia. Vaikka ratkaisut keskusteltiin yhdessä ja koodin toiminta käytiin läpi, oppilaiden omaa ajattelua ja ongelmanratkaisua olisi voinut olla enemmän.
  • sunnuntai 8. maaliskuuta 2015

    Koodauksen ABC: 13. oppitunti

    Koodarit palasivat hiihtoloman ja toimintailtapäivän jälkeen sorvin ääreen ja jatkoimme pacmanien kanssa. Tällä tunnilla tekisimme suuren hyppäyksen pelkästä pacmanin liikuttelusta koko pelin pyörittämiseen, joten otimme käytöön uuden peli - structin. Tätä tarvittaisiin, jotta voisimme pitää muistissa mitkä namut on vielä syömättä, paljonko meillä on pisteitä koossa, missä haamut majailevat ja onko pacman edes elossa enää.

    ;; namut : paikka-lista
    ;; haamut : hahmo-lista 

    ;; pisteet : numero
    ;; elossa? : boolean
    (struct peli hahmo (namut haamut pisteet elossa?))


    Big-bang saisi tästä eteenpäin tämänlaisen structin sisäänsä, joten muutimme LÄHTÖ-tilan käyttämään uutta structia. Asetimme toistaiseksi haamut-listan tyhjäksi.

    (define LÄHTÖ
      (peli 1 1 OIKEALLE namu-lista '() 0 true))


    Koska emme olleet vielä  tehneet namujen piirtoon kunnon funktiota, teimme sen nyt muokkaamalla sen vanhasta TESTI:stä.

    ;; namunpiirtäjä : paikka-lista -> kuva
    (define (namunpiirtäjä namulista)
      (foldl piirrä-namupala SEINÄT namulista))


    ja otimme uuden namunpiirtäjä - funktion käyttöön piirrä-pacman - funktiossa:
    ;; piirrä-pacman : peli -> kuva
    (define (piirrä-pacman tila)
      (place-image (anna-pacman
                   (hahmo-suunta tila))
                   (skaalaa (paikka-x tila))
                   (skaalaa (paikka-y tila))
                   (namunpiirtäjä (peli-namut tila))))


    Nyt pääsimme korjaamaan hoida-näppäimet - funktiota, muutimme sitä niin että se palauttaa peli - structin.

    ;; hoida-näppäimet : peli -> peli
    (define (hoida-näppäimet tila n)
      (define p-suunta (hahmo-suunta tila))
      (cond [(key=? n "up")
             (peli (paikka-x tila)
                        (paikka-y tila)
                        YLÖS
                        (peli-namut tila)
                        (peli-haamut tila)
                        (peli-pisteet tila)
                        (peli-elossa? tila))] 

                        ... jne

    Koska nyt pelissä tapahtuisi paljon muutakin kuin vain pacmanin liikkuminen, teimme uuden funktion päivitä-peli, joka korvaisi liikuta-hahmo - funktion (huom! jätimme liikuta-hahmo:n kuitenkin apufunktioksi. Tämä funktio toimii niin, että ensin laskemme uuden sijainnin pacamanille vanhan liikuta-hahmo - funktion avulla, sitten testaamme että onko uudessa ruudussa seinäpala, ja jos on emme liiku siihen (else-haara), jos taas ei ole siirrymme siihen (not seinä?). Seinän testaaminen tehdään onko-seinä? - funktiolla, ja sen teemme ensikerralla. Ensi kerralla lisäämme logiikan myös namujen havaitsemiseen sekä niiden syömiseen.

    ;; päivitä-peli : peli -> peli
    (define (päivitä-peli tila)
      (define pacman (liikuta-hahmo tila))
      (define seinä? (onko-seinä? pacman))
      (cond [(not seinä?)
              (peli (paikka-x pacman)
                   (paikka-y pacman)
                   (hahmo-suunta tila)
                   (peli-namut tila)
                   (peli-haamut tila)
                   (peli-pisteet tila)
                   (peli-elossa? tila))]
                   ... jne


    Tässä vaiheessa kurssia on jo selvää, että emme saa koko pacman-peliä valmiiksi. Haamut jäävät auttamatta pois, mutta toisaalta jos saamme edes namuja syövän, labyrintissa kulkevan pacmanin on se jo aivan huimaa!!!

    perjantai 13. helmikuuta 2015

    Koodauksen ABC: 12. oppitunti

    Tällä tunnilla yhdistimme kevään kenttäkoodin ja syksyn pacman-koodin samaan tiedostoon.  Avasimme molemmat tiedostot auki DrRacket:iin ryhdyimme siirtämään koodia pala kerrallaan vanhasta uuteen. Aivan suoraan emme voineet koodia kopioida, koska emme olleet syksyllä ottaneet huomioon aivan kaikkea...

    Ensimmäinen korjattava asia oli tietorakenteet. Syksyllä olimme ottaneet käyttöön sijainti-structin,  jossa oli kentät x, y ja suunta. Keväällä olimme ottaneet käytöön paikka-structin, jossa myös oli käytössä x ja y. Jotta pääsisimme vertailemaan onko pacman (sijainti) samassa ruudussa seinän (paikka) tai namun kanssa (paikka) olisi parempi jos molemmat olisivat samaa tietotyyppiä. Poistimme siis sijainti-structin ja otimme tilalle hahmo-structin, joka on paikka-struct johon on lisätty suunta-kenttä. Huomaa, että tällaista "kenttien periyttämistä" ei voi määritellä "define-struct":lla. Myös haamut olisivat tyyppiä hahmo.

    (struct hahmo paikka (suunta))  
     
    Aloitimme yhdistämisen anna-pacman - funktiosta. Kopioimme ensin kaikki define:t mitä pacmanin piirtämiseen tarvittiin ja testasimme, että edelleen saimme ylös, alas, vasemmalle ja oikealle katsovia pacmanejä. Sitten korjasimme LÄHTÖ - structin, käyttämään uutta hahmo - structia.

    (define LÄHTÖ
      (hahmo 1 1 OIKEALLE))

      
    Koska syksyn pacman oli tehty liikkumaan vapaasti missä tahansa, jouduimme muuttamaan piirrä-pacman-funktion käyttämään skaalausta.

    ;; piirrä-pacman : hahmo -> kuva
    (define (piirrä-pacman tila)
      (place-image (anna-pacman
                   (hahmo-suunta tila))
                   (skaalaa (paikka-x tila))
                   (skaalaa (paikka-y tila))
                   SEINÄT))


    Nyt saatoimme jo testata em. funktiota REPL:ssä LÄHTÖ-tilanteella. Pacman oli monella oppilaalla hieman liian iso tai liian pieni, joten tässä vaiheessa sen kokoa säädettiin sopivaksi.

    > (piirrä-pacman LÄHTÖ)

    Pacman pääsi labyrinttiin
    Jotta pacman lähtisi liikkeelle, kopioimme vielä siirrä-pacman - funktion ja muokkasimme sen käyttämään uutta hahmo-structia. Vaihdoimme myös funktion nimeksi liikuta-hahmoa, koska haamuakin voitaisiin siirtää käyttämällä tätä funktiota. Vaihdoimme myös NOPEUS-vakion arvoksi 1, koska nyt pacman liikkuisi aina seuraavaan ruutuun. Lopuksi kopioimme vielä big-bang- kutsun. Nyt pacman liikkuikin jo niin nopeasti, että hidastimme animaatiota lisäämällä HIDASTUS-arvon.

    (define HIDASTUS 0.2)
    (big-bang LÄHTÖ
              (on-draw piirrä-pacman)
              (on-tick liikuta-hahmoa HIDASTUS))


    Seuraavaksi tarvitseekin enää tehdä em. muutokset hoida-näppäimet - funktiolle, ja sitten vanha pacman-koodi onkin käytetty hyödyksi. Pacman ei kuitenkaan vielä huomaa seiniä eikä namuja, ja namujen piirtämiseenkään ei ole vielä tehty kunnon funktiota. Näihin keskitymme seuraavalla tunnilla.

    Aluksi mietin, että tämä tunti on varmasti turhauttava, koska korjaamme jo ns. kerran toiminutta koodia, mutta itseasiassa vanhaan koodiin palaaminen toi hyvin esille määrittelyjen merkityksen, jos unohti kopioida yhden define:n, koodi ei kääntynytkään enää, tai jos liitti sen väärään kohtaan, sekään ei toiminut. Myös big-bang:in toiminta animaation aikaansaamisessa ehkä valkeni hieman paremmin kuten myöskin kirjaston merkitys (big-bang vaatii 2htdp/universe-kirjaston). Ja taas lopputunnista yksi oppilas yllätti osaamisellaan, hän omatoimisesti lisäsi näppäin-toiminnot pacmaniinsä :-)

    keskiviikko 4. helmikuuta 2015

    Koodauksen ABC: 11. oppitunti

    Tällä kerralla laitoimme pacman:ille namut valmiiksi labyrinttiin. Koska oppilailla oli jo valmiina seinien sijainnit, pohdimme yhdessä miten saisimme kaikkiin muihin ruutuihin namuja. Päädyimme tekemään namut kaikkiin mahdollisiin ruutuihin ja poistamaan ne namut, jotka osuivat seinän kanssa samaan ruutuun.
    Koska olimme tehneet jo pari rekursiivista funktiota, teimme tämänkin rekursiivisesti. Suunnitelma oli käydä pelikenttä läpi rivi kerrallaan ja tehdä kokonaisen rivin namut yhdellä iteraatiokierroksella ja samalla kerätä valmista listaa "paikat"-muuttujaan.

    ;; tee-namut : numero tyhjä-lista -> paikka-lista
    (define (tee-namut rivi paikat)
      (if (> rivi PELIN-KORKEUS)
          paikat
          (tee-namut (add1 rivi) 

                     (append (map (tee-paikka rivi) leveys-lista)  
                              paikat)))) 

    Tällaisia rekursiivisia funktioita olimme jo tehneet, mutta jotta saimme kätevästi koko rivillisen namuja, teimme sen suoraan käyttämällä map-funktiota (ks. koodin sininen osa). Map ottaa parametrina funktion tee-paikka ja ajaa sen jokaiselle leveys-listan alkiolle. Leveys-lista sisältää x-koordinaatit pelikentän leveydeltä, ja teimme sen näin:

    (define leveys-lista (map add1 (build-list PELIN-LEVEYS values)))   

    Tee-paikka - funktio oli myöskin uusi ilmestys, koska se ei ollutkaan ns. tavallinen funktio. Kun sitä kutsuu, se palauttaa funktion. Tämän selittäminen oppilaille olikin aika vaikeaa, mutta ilmeisesti ohjelmoinnissa on niin monta "outoa" asiaa, ettei tämä herättänyt sen kummempaa vastustusta.

    ;; tee-paikka : numero -> funktio
    (define (tee-paikka y)
      (lambda (x) (make-paikka x y)))


    Testasimme REPL:issä length:in avulla, että oikea määrä namuja syntyi (PELIN-LEVEYS * PELIN-KORKEUS):

    > (length (tee-namut 1 '()))

    Nyt poistimme tästä listasta ne, jotka osuivat seinän kanssa samaan ruutuun. Tähän käytimme remove*-funktiota.

    (define namu-lista
      (remove* seinä-lista (tee-namut 1 '()) paikka=?))


    Remove* - funktio tarvitsee parametrina apufunktion, joka kertoo milloin kaksi paikka ovat "samat". Jouduimme kirjoittamaan tätä varten paikka=? - funktion. Selitin, että tätä kutsutaan "predikaatiksi", mutta sillä ei ole äidinkielen predikaatin kanssa mitään tekemistä. Funktion toiminta oli helppo perustella: kaksi paikkaa ovat samat, jos niiden x-koordinaatit ovat samat JA niiden y-koordinaatit ovat samat. Tätä käyttäisimme myös jatkossa hyväksi kun testaamme onko pacman törmäämässä seinään tai syömässä namua.

    ;; paikka=? : paikka paikka -> boolean
    (define (paikka=? p1 p2)
      (and (equal? (paikka-x p1)
                   (paikka-x p2))
           (equal? (paikka-y p1)
                   (paikka-y p2))))

     
    Lopuksi teimme piirtofunktiot namujen piirtämiselle. Otimme mallia viime viikon vastaavista funktioista, joilla piirsimme seinäpalat pelipohjalle. 
      
    (define SEINÄT (foldl piirrä-seinäpala PELIPOHJA SEINÄ-LISTA))

    (define NAMU (circle (/ RUUTU 3) "solid" "red"))

    ;; piirrä-namupala : paikka kuva -> kuva
    (define (piirrä-namupala p k)
      (place-image NAMU (skaalaa (paikka-x p)) (skaalaa (paikka-y p)) k))

    (define TESTI (foldl piirrä-namupala SEINÄT namu-lista))  


     Ja tätä kun testasi niin voilà labyrintti täyttyi namuista!

    Keltaiset namut
    Punaiset namut
    Tämän oppitunnin asiat olivat kyllä jokseenkin vaikeita, enkä tiedä kunka moni oikeasti ymmärsi mistä oli kyse kun käytimme map, foldl ja remove* - funktioita saati sitten lambdaa. Kun joululomalla itse tein pacman-pelin tein sen aivan eri tavalla. Nyt yritin tehdä sen yksinkertaisemmin ja välttelin sisäkkäistä rekursiota viimeiseen asti (rekursiivista funktiota, joka kutsuu toista rekursiivista funktiota). En nyt lopultakaan tiedä, oliko tämä valinta sitten yhteään parempi, koska jouduin vetämään esiin map:in ja lambdan. Ensi viikolla pacman pääsee syömään herkkuja, eli yritämme integroida syksyllä tehdyn koodin kevään koodiin. Katsotaan miten siinä käy.

    keskiviikko 28. tammikuuta 2015

    Koodauksen ABC: 10. oppitunti

    Tällä viikolla palasimme takaisin pacman-pelin suunnitteluun. Aloitimme suunnittelemalla pelikentän labyrintin ruutupaperille. Jokainen teki omanlaisensa pelikentän:

    Suunnitelmat
    Jotta saisimme peliä hieman yksinkertaisemmaksi, siirryimme tässä vaiheessa ajattelemaan peliä ruudukkona, jossa jokainen ruutu on joko seinää tai käytävää. Käytäväruudussa voi olla, joko pacman, namu ja/tai haamu. Tästä eteenpäin koordinaatit eivät enää olisikaan suoraan pikseleitä ruudulla vaan loogisia sijainteja "grid":ssä ja skaalaisimme loogiset koordinaatit pikseleiksi skaalaa-apufunktion avulla. Tätä varten määritimme RUUTU-vakion, jota käytettiin skaalaa-funktiossa (grid-ruudun koko pikseleissä):

    (define RUUTU 40)

    (define (skaalaa x)
      (* RUUTU x))


    Sitten mietimme miten voisimme tallentaa seinien sijainnit. Koska tähän asti olemme opiskelleet vasta struct:in ja listan niin eipä ollut vaikea keksiä, että mitäpä jos seinäpalojen koordinaatit tallennettaisiin "paikka"-structiin ja näistä tehtäisiin lista. Aluksi teimme kokeeksi listan, jossa oli vain kolme paikkaa, jos tämä toimisi, paikkoja voisi kirjoittaa helposti lisää.

    (define-struct paikka (x y))

    (define SEINÄ-LISTA
      (list (make-paikka 2 3)
            (make-paikka 3 3)
            (make-paikka 4 3)))


    Sitten piti vain kirjoittaa koodi yhden seinäpalan piirtämiselle ruudulle eli määrittelimme piirrä-seinäpala-funktion:

    (define SEINÄPALA (rectangle RUUTU RUUTU "solid" "black"))

    ;; piirrä-seinäpala : paikka kuva -> kuva
    (define (piirrä-seinäpala p tausta)
      (place-image SEINÄPALA (paikka-x p) (paikka-y p)  tausta))


    Tässä vaiheessa piti kerrata syksyltä struct:in käyttö sekä funktion parametrien tyypit ja paluuarvo. Tämä asia näytti olevan edelleen vaikeaa ymmärtää.

    Ja viimeisenä teimme rivin joka piirsi kaikki seinäpalat pelikentälle. foldl-funktio ottaa parametrina piirtofunktion (piirrä-seinäpala), tyhjän pelikentän (PELIPOHJA) sekä listan seinäpalojen paikka-structeja (SEINÄ-LISTA). Se käsittelee listan alkioit yksi kerrallaan eli kutsuu piirrä-seinäpala-funktiota niin monta kertaa kuin annetussa listassa on alkioita. Annettu parametri PELIPOHJA on ns. lähtötilanne ja se annetaan niin ikään parametrina piirrä-seinäpala:lle. Syntyvä kuva siis ikäänkuin "laskostuu" alkutilanteen päälle, eli tämä on taas yksi tapa tehdä loop ja käyttää "akkumulaattoria". En tiedä ymmärsikö yksikään oppilas tätä mutta koodi oli lyhyt kirjoittaa ja se toimi hyvin :-)

    (foldl piirrä-seinäpala PELIPOHJA SEINÄ-LISTA)

    PELIPOHJA:aan teimme kiinteät reunat laittamalla empty-scene:llä tehdyn värillisen pohjan päälle valkoisen suorakulmion.

    (define PELIN-LEVEYS 18)
    (define PELIN-KORKEUS 9)

    (define POHJA
      (empty-scene (skaalaa (add1 PELIN-LEVEYS))
                   (skaalaa (add1 PELIN-KORKEUS))
                   "black"))

    (define PELIPOHJA
      (overlay (rectangle (skaalaa PELIN-LEVEYS) 

                          (skaalaa PELIN-KORKEUS) 
                          "solid" "white") 
               POHJA))

    PELIPOHJA valmiina (siniset ruudut on lisätty selkeyden vuoksi)

    Ja tässä kuvaa siitä miltä pelikentän koodausnäytti:

    Pelikentän koodausta DrRacketillä

    Teimme myös kuvat namuille ja latasimme haamut tästä linkistä, mutta emme ehtineet asettelemaan niitä vielä pelikentälle. Niitä ihmettelemme ensiviikolla. 

    tiistai 20. tammikuuta 2015

    Koodauksen ABC: 9. oppitunti

    Jatkoimme Racket-Turtlen kanssa opiskelua. Kerroin että tehdäksemme tämän squiral-kuvion, tarvitsimme rekursiota:
    Motivaatio rekursion käytölle...
    Oppilaiden oli helppo nähdä tästä, että kuvio syntyisi, jos Turtle liikkuisi jokaisella kierroksella hieman pidemmän matkan kuin edellisellä.

    Koodi tehtiin samanlaisella "akkumulaattori"-tekniikalla kuin edellisen tunnin merkkijonoja käyttänyt toista-hokema, eli keräsimme rekursiivisesti käskylistaa Turtlelle yhteen parametriin.Toteutimme tämän silmukka-kuvio-funktion käyttämällä sivu-ja-kulma-apufunktiota, joka teki ohjeet yhden sivun piirtämiseen.

    (define (sivu-ja-kulma s k)
      (list (forward s) (turn-left k)))

    (define (silmukka-kuvio s k kerrat ohjeet)
      (if (<= kerrat 0)
          ohjeet
          (silmukka-kuvio (+ s 5) k (sub1 kerrat) (append ohjeet (sivu-ja-kulma s k))))) 


    Ja tätä kutsuttiin REPL:stä näin:

    (piirrä (silmukka-kuvio 1 90 20 '()))

    Ja nyt vasta aukesikin ovi Racket-Turtle-leikille. Ryhmä piirteli innoissaan erilaisia Turtle-taideteoksia, joissa monimutkaisimmissa oli jopa 5000 viivanpätkää!

    Tässä ryhmäni aikaansaannoksia!











    P.S. Sateenkaaren värit saatiin aikaan käyttämällä random-funktiota eli arpomalla make-color:ille uudet RGB arvot:

    (change-color (make-color (random 255) (random 255) (random 255)))
     

    Koodauksen ABC: 8. oppitunti

    Pienen tauon jälkeen koodariryhmä palasi taas kurssille ja aloitimme valmistautumisen pacman-pelin uusiin vaiheisiin opiskelemalla listoja. Ajattelin, että jos listat ja rekursio olisivat jollakin tapaa ymmärretty (edes pinnallisesti), niiden lisääminen peliin olisi helpompaa.

    Harjoittelimme ensin listan luomista list-komennon avulla ja jokainen oppilas teki listan kavereidensa nimistä tähän tyyliin:

    (define KAVERIT (list "Antti" "Tomi" Timo" "Pasi" "Kalle"))

    Sitten kokeilimme palauttaa ensimmäisen kaverin nimen listasta first-komennolla, ja samalla huomasimme, että myös second ja third toimivat! Listan lopun (kaikki muut paitsi ensimmäisen) listasta sai otettua rest-komennolla, joten haasteeksi tuli palauttaa listan viimeinen nimi sisäkkäisillä kutsuilla:

    (first (rest (rest (rest (rest KAVERIT)))))

    Teimme toisenkin listan KOULUKAVERIT, ja yhdistimme nämä käyttämällä appendia ja lopuksi selitin vielä miten yksittäisiä kavereita voi lisäillä käyttämällä cons:ia (tässä yhteydessä olisi pitänyt tietysti käsitellä myös kavereiden poistaminen listasta remove:lla mutta eipä tullut mieleen siinä tunnilla).

    (append KAVERIT KOULUKAVERIT)
    (cons "Ville" KAVERIT)

    Toinen tavoite oli rekursion ymmärtäminen. Selitin ensin, että rekursio tarkoittaa sitä, kun funktio kutsuu itse itseään eli syntyy silmukka. Kerroin, että silmukan tekemisessä melko oleellista oli miettiä millä ehdolla se lopetta toimintansa. Teimme sitten tällaisen rekursiivisen funktion, joka kerää "tekstiä" yhteen parametriin jokaisella kierroksella:

    (define (toista-hokema hokema kerrat koko-teksti)
      (if (<= kerrat 0)
          (string-append koko-teksti " Kiitos ja loppu.")
          (toista-hokema hokema
                         (sub1 kerrat)
                         (string-append " " (number->string kerrat) ". kerta " hokema koko-teksti))))


    Tätä voitiin kutsua REPL:stä vaikka näin:

    (toista-hokema "Pehmeet touhuu." 10 "")

    Nämä kaksi harjoitusta johdattivat meidän Racket-Turtlen pariin ja toisen tunnin ajan oppilaat laativatkin käskylistoja Racket-Turtlelle. Ensimmäinen tehtävä oli piirtää Turtlella tasasivuinen kolmio. Kaikki saivat tämän tehtyä melko kivuttomasti, tutkimalla miten neliön koodi oli tehty. Tässä välissä keskustelimme hieman "The Total Turtle Trip Theorem":sta (Pappert, Mindstorms) eli siitä kuinka monta astetta Turtlen pitää yhteensä kääntyä, että se saa piirrettyä monikulmion. Ja pienen pohdinnan jälkeen löytyi vastauskin: täydet 360-astetta.

    Tämän jälkeen neuvoin, että repeat - käskyllä voisi tehdä monikulmioita hiemän älykkäämmin ilman saman koodin kopioimista. Tämäkin onnistui useimmilta ja yksi oppilas hoksasi samalla, että kun riittävästi pienentää askelta ja kulmaa niin monikulmio näyttääkin jo ympyrältä. Repeat avasi yllättäen myös mahdollisuudet tehdä Turtlella hyvinkin monimutkaisia kuvioita, joten tästä eteenpäin vain ihmettelin mitä kaikkea oppilaat saivatkaan piirrettyä. 

    lauantai 3. tammikuuta 2015

    Seymour Papert:in Turtlet

    Ostin itselleni joululahjaksi Seymour Papert:in klassikkokirjan "Mindstorms: Children, Computers, and Powerful Ideas". Vaikka kirja on ilmestynyt ensimmäisen kerran vuonna 1980, se on silti hämmästyttävän ajankohtainen ja ajatuksia herättelevä. Erityisesti Papertin kritiikki "koulumatematiikkaa" ja "koulufysiikkaa" kohtaan osui ja upposi. Ne todellakin ovat historian muovaamia konstruktioita, aihepiirejä joitka ovat valikoituneet mukaan sen mukaan mitä on perinteisesti voitu opettaa ja oppia niillä välineillä, joita kouluissa on ollut saatavilla: kynä, kumi, paperi, viivoitin ja harppi. Papert näki jo 1980, että tietokoneita käytettiin opetuksessa aivan väärin, niiden avulla yritettiin opettaa näitä "kuolleita kynä-plus-paperi-ajan taitoja". Hänen mielestään on jo lähtökohtaisesti väärin ohjelmoida drillaavia opetusohjelmia, jotka "ohjelmoivat" oppilaita toimimaan tietyllä tavalla. Hänen mielestään asian pitäisi olla täysin päinvastainen eli oppilaiden pitäisi päästä ratkomaan oikeita "aikuisten" ongelmia eli ohjelmoimaan tietokoneita. Tämä on Papertin mukaan ainoa tapa saada kuollut (paperille kirjoitettu) koulumatematiikka palautettua elävien kirjoihin.

    Papertin ratkaisu tähän oli LOGO-ohjelmointikieli ja turtle-geometria, joita en itse lapsena valitettavasti päässyt kokeilemaan. Omassa peruskoulussani/lukiossani ei 1980-luvulla ollut tarjolla kuin konekirjoitusta. Heti kirjan luettuani halusin päästä kokeilemaan Turtlen ohjaamista, joten koodasin itselleni sellaisen Racket:illä. Halusin seurata kirjan "design principles"-ohjeita, ja tehdä niiden pohjalta oman versioni klassikosta. Jos Lego Mindstorms:in, Scratch:in sekä Turtle-Roy:n kehittäjät ovat seuranneet Papertin jalanjälkiä, niin aion minäkin. Ja kivastihan siinä lomapäivä vierähtikin...

    Olin toki aikaisemminkin piirrellyt "turtle-geometrisia"-kuvioita Scratch:illä, joten periaate oli sinänsä tuttu. Miksi halusin tehdä vastaavan Racket:illä, johtui siitä että olemme jo käyttäneet oppilaideni kanssa Racket:iä, joten saman välineen kanssa jatkaminen tuntuisi paremmalta idealta kuin eri ympäristöjen välillä sinkoilu. 7. luokan koulugeometrian opettamisen lisäksi, mietin samalla miten voisin opettaa listojen ja rekursion käyttöä KoodauksenABC-kurssilaisilleni. Ja aina kannattaa käyttää tilaisuus hyväksi: koodaamista oppii vain itse koodaamalla :-)

    Racket-turtle:a ohjataan periaatteessa samalla tavalla kuin esikuvaansa LOGO-Turtlea, mutta komennot annetaan listana. Esim. tämä koodin pätkä määrittelee Turtle-komentolistan neliön piirtämiselle:

    (define neliö
      (list (forward 100)
            (turn-left 90)
            (forward 100)
            (turn-left 90)
            (forward 100)
            (turn-left 90)
            (forward 100)))


    Varsinainen piirtäminen tehdään kutsumalla "piirrä"-funktiota tai "piirrä-osissa"-funktiota, joka piirtää kuvan pala palalta, kun jotain näppäintä painetaan. Eli näin piirrettäisiin neliö:

    (piirrä neliö) TAI
    (piirrä-osissa neliö)
    Ensimmäinen Racket-turtlella piirretty kuvio
    Kynän väri on oletuksena sininen ja se on aina laskettuna, mutta kynää voi halutessaan ohjailla myös näillä komennoilla:

    (pen-up)
    (pen-down)
    (change-color "red")
    (change-color (make-color 255 0 0))

    Jos haluaa tehdä neliönsä älykkäämmin käyttäen toistorakennetta, sekin onnistuu repeat:in avulla:

    (define sivu
      (list (forward 100)
            (turn-left 90)))

    (define toisto-neliö
      (repeat 4 sivu))


    (piirrä toisto-neliö)

    Jos haluaa tehdä samaan kuvaan kaksi kuviota, komentolistat voi yhdistään käyttämällä append:a:

    (define siirry
      (list (pen-up)
            (forward 100)
            (pen-down)
            (change-color "red")))

    (define kaksi-neliötä
      (append neliö
              siirry
              toisto-neliö))

    Yhdistelmäkuvio

    Koska Racket on oikea ohjelmointikieli, sillä voi koodata myös funktioita, jotka tekevät komentolistoja eri kokoisille tai värisille kuvioille. Nämä funktiot yhdessä piirtävät halutun kokoisen ja värisen neliön:

    (define (sivu2 pituus kulma)
      (list (forward pituus)
            (turn-left kulma)))

    (define (neliö2 pituus väri)
      (cons (change-color väri)
            (repeat 4 (sivu2 pituus 90))))


    (piirrä (neliö2 50 "red"))
    Neliö muuttuvalla sivun pituudella sekä värillä
    Yllä olevan sivu2-koodin avulla voi rakentaa funktioita myös muille säännöllisille monikulmioille. Huomaa, että neliö2:ssa on käytetty cons:ia list:in tilalla, koska repeat palauttaa kokonaisen listan ja (change-color väri) on pelkkä lista-alkio (list vaatisi kaksi lista-alkiota ja append puolestaan kaksi listaa). Racket-turtlen yksi tarkoitus on opettaa listojen käsittelyä, ja listojen käsittelyssä on pidettävä mielessä listan ja alkion ero. Seuraava dia selventänee list, cons ja append - funktioiden käyttöä:

    Mitä eroa on list, cons ja append - funktioilla

    Kun sain Racket-turtleni toimimaan, mopo karkasi välittömästi lapasista ja huomasin leikkiväni sillä  kuin pieni lapsi. Illan aikana tuli piirrettyä mm. nämä Papertin kirjan innoittamat kuviot:
    Ympyrä-kukka


    Random-kukat (random koko, random väri)
    Vino "squiral"
    Ja tässä vielä video Racket-turtlen toiminnasta (melko hypnoottista):


    Jos haluat itse kokeilla Racket-turtlea niin lataa tästä tiedosto ja avaa se DrRacket:iin. Kirjoita tiedoston loppussa olevien esimerkkien tilalle omat komentosi. Pidä hauskaa!

    Videolla näkyvä "squiral" on toteutettu rekursion avulla. Jos haluat kokeilla sitä itse niin tässä on koodi, joka generoi tarvittavan komentolistan Racket-turtlelle:

    (define (squiral pituus kerrokset)
      (if (<= kerrokset 0)
          '()
          (append (list (forward (* 5 pituus)) 

                        (turn-left 90)) 
                  (squiral (add1 pituus) (sub1 kerrokset)))))

    sunnuntai 28. joulukuuta 2014

    Koodauksen ABC: 7. oppitunti

    Tämä oli viimeinen oppituntimme ennen jakson vaihtumista, joten mitään uutta ja isoa emme olisi ehtineet aloittaa. Ajattelinkin että tässä olisi hyvä tilaisuus antaa oppilaiden tuunata omia pacman-pelejään haluamaansa suuntaan. Ilmeisesti olimme kuitenkin tehneet töitä liian kauan opettajajohtoisesti, että suurin osa oppilaista ei oikein osannut tai edes keksinyt mitä haluaisi pacmaniinsä lisätä. Yritin ehdotella uusia näppäinkomentoja, "Game over" - tekstiä yms. Lopulta yksi oppilaista tarttui "Game over" - tekstiin. Teksti lisätään kirjoittamalla uusi, viimeisen näkymän piirtävän funktio ja antamalla tämä parametrina stop-when:lle.

    (define (piirrä-loppu tila)
      (overlay (text "Game Over" TEKSTI-KOKO "black") (piirrä-pacman tila)))


    Teksti muutetaan ensin kuvaksi

    Pienen mietinnän jälkeen yksi oppilas halusi lisätä pacmanille "syömisliikkeen" mutta hän ei osannut lähteä sitä itse koodaamaan joten ryhdyimme pohtimaan sitä yhdessä. Päädyimme siihen, että pelkät muuttuvat sektorit eivät anna oikeaa "syömisvaikutelmaa" vaan syövä pacman täytyy muodostaa kahdesta liikkuvasta puoliympyrän muotoisesta leuasta. Leukojen muotoiluun (crop, flip-horizontal, overlay/xy) ja niiden liikuttelun suunnittelemiseen hurahtikin sitten loppuaika, emmekä lopulta saaneet syövää pacmania edes valmiiksi. Ideana oli kuitenkin se, että leuat avautuvat vastakkaisiin suuntiin yhtä paljon (rotate). Uudet pacmanit näyttäisivät siis jotakuinkin tältä:

    Syövät pacmanit

    Keväällä jatkamme tätä kurssia, ja kysyinkin oppilailta että haluavatko he jatkaa pacman-pelin kimpussa vai koodaammeko jotain muuta. Kaikki halusivat jatkaa pacmanin kanssa, joten ilmeisesti keväällä teemme labyrinttejä, syömme namuja ja väistelemme haamuja. Joululomalla harjoittelin pacman-pelin koodaamista ja totesin, että siinä on edessä melkoinen määrä opittavaa sekä opettajalla että oppilailla! Saapa nähdä kuinka hienot pacman pelit saamme kevään aikana kokoon.

    Tässä harjoitteluni tulos:






    Koodauksen ABC: 6. oppitunti

    Tämä oppitunti aloitettiin siirrä-pacman-funktion loppuun kirjoittamisella ja debuggauksella. Tässä vaiheessa monen oppilaan koodissa ilmeni ihmeellisiä asioita mm. yksi pacman lähti yllättäen liikkumaan viistosti ruudun poikki ja pääsimme miettimään sitä miksi päivitämme koodissamme kerrallaan vain x:ää tai y:tä.

    Lopulta saimme jokaisen pacmanin liikkumaan ylös, alas, vasemmalle tai oikealle sen mukaan mitä LÄHTÖ-tilassa määrättiin. Tietenkään tämä ei ollut kovin mielenkiintoista, joten lisäsimme vielä näppäinohjauksen pacmanille. Teimme uuden funktion hoida-näppäimet, jossa käytimme taas vanhaa tuttua COND:ia. hoida-näppäimet - funktio lisättiin luonnollisesti myös big-bang:in kohtaan "on-key". Kulloinkin panettu näppäin saadaan big-bang:ltä tilan lisäksi.

    ;; hoida-näppäimet : sijainti key -> tila
    (define (hoida-näppäimet tila n)
        ( .. ))

    Painettu näppäin (n) saadaan big-bang:ltä
    Tämän funktion implementoinnin ja testaamisen jälkeen pacman olikin jo valmis liikkumaan kaikkiin suuntiin ja "pelaaja" pystyi ohjailemaan pacmania nuolinäppäimillä. Viimeinen silaus pacmanille tehtiin korjaamalla lopetusehto sellaiseksi, että mihin tahansa neljästä reunasta ajautuminen lopettaa pelin (edellisten muutosten ajaksi olimme kommentoineet stop-when-koodin pois käytöstä).

    Jotta lopetusehto olisi helpompi kirjoittaa ja lukea, otimme käyttöön lokaalit muuttujat. Tallensimme ensin pacmanin sijainnin paikallisiin muuttujiin x ja y. Sitten kokosimme ehdot pelikentän reunan ylittämiselle liudaksi ehtolauseita, jotka yhdistettiin loogisella operaattorilla or. Samalla muutimme lopetusehto-funktion nimen muotoon "peli-loppui?".
    Otamme käyttöön lokaalit muuttujat sekä loogisen operaattorin OR
    Lähes kaikki oppilaat saivat koodinsa toimimaan ja tältä se näyttää:

    sunnuntai 21. syyskuuta 2014

    Koodauksen ABC: 5. oppitunti

    Nyt kun kaikilla oli yhteen suuntaan liikkuva pacman käsissään, oli helppo lähteä kokeilemaan pacmanin liikuttelua eri suuntiin. Annoin oppilaille tehtäväksi etsiä koodista ne kohdat, jotka pitäisi muuttaa että pacman saataisiin kulkemaan oikealta vasemmalle, ylhäältä alas ja alhaalta ylös. Tämä harjoitus lämmitti matematiikan opettajan sydäntäni, koska tässä tuli niin selväksi käsitteet muuttuja ja vakio. Jos mennään vasemmalta oikealle tai oikealta vasemmalle, x on muuttuja. Jos mennään ylhäältä alas tai alhaalta ylös, y on muuttuja. Nyt piti muuttaa myös big-bang:ille annettavaa lähtötilaa (tähän asti ollut 0). Piti muuttaa myös yhteenlaskuja vähennyslaskuiksi siirrä-pacman - funktiossa. Ja tietysti lopetusehtoakin piti hieman rukata (moni pacman jumiutui vanhalla lopetusehdolla paikoilleen).

    Kun pacmanin liikuttelu oli saatu hanskaan, ryhdyimme yhdessä miettimään miten olisi mahdollista tehdä pacman, joka voisi liikkua vapaasti sekä ylös, alas, vasemmalle että oikealle. Nythän se oli mahdollista vain yhteen suuntaan kerrallaan. Mitä tietoja tilaan pitäisi tallentaa x-koordinaatin lisäksi?
    Mitä tietoa pitää tallentaa?

    Oppilaat löysivät helposti y-koordinaatin, mutta vaikeampaa oli jo tajuta, että myös suunta, johon pacman on liikkumassa pitäisi olla tallessa tilamuuttujana.

    Tästä pääsimmekin hyvin siihen, että nämä kolme tietoa pitäisi saada tallennettua yhteen "säiliöön"  joten esittelin heille tietorakenteen nimeltä struct.
    Tallensimme tiedot sijainti-nimiseen structiin

    Jotta struct:in käsite tulisi oppilaille selväksi, demonstroin sitä luokassa olleella muovilaatikolla ja paperin paloilla. Muovilaatikko oli struct, paperit laatikossa olivat kenttiä. Yksi paperi oli nimeltään x ja siihen kirjoitin (= tallensin) 0 jne.

    Aloitimme helpoimmaista päästä. Teimme definellä ensin LÄHTÖ-nimisen structin, johon tallensimme pacmanin lähtötilan.

    (define LÄHTÖ (make-sijainti 0 0 OIKEALLE))

    Myöskään piirrä-pacman - funktiota ei ollut vaikea muuttaa, muutimme sen ottamaan suunnan, x- ja y-koordinaatin ulos tila - muuttujasta

    ;; piirrä-pacman : sijainti -> kuva
    (define (piirrä-pacman tila)
            (place-image 
                  (anna-pacman (sijainti-suunta tila))
                  (sijainti-x tila) 
                  (sijainti-y tila)
                   KENTTÄ))

    Suurin työmaa olikin sitten siirrä-pacman - funktion muutoksissa. Vaikka siinä käytettiin "vanhaa tuttua" cond:ia, ehdimme tehdä vain ensimmäisen ehdon kuntoon, ja osalla jäi vielä bugeja koodiin korjattavaksi seuraavalla kerralla.

    Pacmanin toimintalogiikka

    Onneksi muutama oppilas sai pacmanin kaikesta huolimatta taas liikkeelle. Tämä oppitunti kului siis muutoksiin, jotka eivät sinällään lisänneet mitään näkyvää toiminnallisuutta mutta tekivät jatkon mahdolliseksi. Tätä se usein on koodarin elämä...

    Seuraavalla kerralla pääsemme ehkäpä liikuttelemaan pacmania jo nuolinäppäimillä?

    Koodauksen ABC: 4. oppitunti

    Koska edellisellä oppitunnilla tuli niin paljon uutta asiaa, olin päättänyt että tällä tunnilla vain kertaisimme vanhoja asioita koodaushaasteen avulla. Haaste olisi saada pacman liikkumaan vasemmalta oikealle eli soveltaa edellisen tunnin oppeja ufon laskeutumisesta.

    Ja vaikka olin päättänyt, että mitään uutta tällä tunnilla ei tule niin otin kaikesta huolimatta cond - lauseen esiin. cond:ia ei sinänsä olisi tarvittu pacmanin liikuttamiseen mutta ajattelin, että voisi olla hyvä harjoitella vielä funktion kirjoittamista yhden apufunktion avulla. Eli teimme anna-pacman - funktion, joka palauttaa oikeaan suuntaan katsovan pacman-hahmon (kuvan). Parametrina oli tietysti suunta (YLÖS, ALAS, OIKEALLE, VASEMMALLE).

    cond - lauseen avulla saadaan tehtyä koodihaaroja
    Ehtolauseet olivat yksinkertaisesti (equal? suunta OIKEALLE) ja jos näin oli palautettiin oikealle katsova pacman jne. else - haaraan laitoimme pelkän PÄÄ:n ilman suuta. Tätäkin funktiota testattiin heti eri suunnan arvoilla ennen kuin jatkoimme eteenpäin.

    Vaikka tarkoituksena oli tehdä aivan samaa kuin viime kerrallakin, oppilaiden oli vaikea ryhtyä työhön. Selvästikään funktion ja sen paluuarvon käsittettä ei oltu vielä ymmärretty riittävän hyvin. Siksi kysyin oppilailta kysymyksiä heidän koodistaan, missä oppilaat toimivat "funktioina" ja "kutsuin" heitä eri parametreillä. Tällaisia kyselin:
                 - mitä annat minulle kun sanon "(piirrä-ufo 100)"?  - kuvan
                 - mitä annat minulle kun sanon "(siirrä-ufo 150)"?  - 160 (ASKEL oli 10)
                 - mitä annat minulle kun sanon "(maassa? 350)"?   - true (MAANPINTA oli 300)

    Tämän kyselykierroksen jälkeen suurin osa tuntui ymmärtävän funktion käsitteen niin hyvin, että funktioiden kirjoittaminen lähti liikkeelle. Teimme siis uudelleen lähes samat funktiot kuin viime kerrallakin nyt vain eri nimisinä: piirrä-pacman, siirrä-pacman, reunalla? Ainoa ero  oli se, että piirrä-pacman kutsui anna-pacman - apufunktiota saadakseen oikeanlaisen pacmanin. big-bang oli myös aivan samanlainen. Lopputunnista kaikilla oli liikkuva pacman - koodi valmiina.

    tiistai 2. syyskuuta 2014

    Koodauksen ABC: 3. oppitunti

    Kolmannella oppitunnilla opettelimme miten ufon saa laskeutumaan ruudun yläreunasta alas ja  pysähtymään ruudun alareunaan. Teimme koodin yhdessä pala palalta ja vaikka olin tehnyt aikaisemmin ohjeet ufon laskeutumiselle, huomasin meneväni asioita aivan erilailla kuin ohjeessa (Lataa ufo-animaation ohje).

    Lähdin liikkeelle siitä, että selitin miten Racketin 2htdp/universe - kirjaston big-bang toimii. Tähänastihan olimme tehneet vain ohjelmanpätkiä, jotka tekevät jotain ja lopettavat toimintansa heti eli niissä ei ole ollut ns. message loop:ia, joka jäisi kuuntelemaan käyttäjän näppäinten painalluksia tai tietokoneen kellon "tikitystä". big-bang - funktio käynnistää tällaisen loopin. 

    big-bang:in avulla voi tehdä interaktiivisia ohjelmia
    Jotta big-bang tietäisi mitä milloinkin pitää tehdä, ohjelmoijan on kirjoitettava muutama funktio. Aloitimme kirjoittamalla ainoan pakollisen funktion, eli sen miten näyttöä päivitetään, kun vuorossa on toiminto to-draw. Koska tehtävänä oli saada ufo laskeutumaan ruudun yläreunasta alaspäin, kirjoitimme piirrä-ufo - funktion. 
    Tämä oli ensimmäinen oppilaiden itsensä kirjoittama funktio, joten käytimme ensin aikaa muuttuja - käsitteen sisäistämiseen. Harjoittelimme ufon laskeuttamista ns. "manuaaliohjauksella" eli määrittelimme ensin define:llä muutaman vakion (KORKEUS, LEVEYS, KENTTÄ) sekä ufon kuvan (UFO) ja sitten kirjoitimme DrRacketin alempaan ikkunaan rivin koodia, joka piirsi UFO:n kentälle. Tehtävänä oli ajaa koodirivi uusilla parametreilla niin kauan kunnes ufo pääsi maanpinnalle. 




    Nyt olikin jo paljon helpompaa miettiä, mikä voisi olla piirrä-ufo - funktion muuttuja. Valitsimme muuttujan nimeksi tila, ja kirjoitimme ensimmäisen funktion. Sitä myös testattiin heti kutsumalla sitä eri muuttujan arvoilla DrRacketin alemmasta ikkunasta. Ja sehän toimi! Tämän jälkeen kirjoitimme funktiolle myös kuvauksen kommenttiriville. Funktiota on jatkossa helpompi käyttää, kun merkitsee tällä tavalla funktion nimen (piirrä-ufo), parametrien tyypit (numero) ja paluuarvon tyypin (kuva). 

    ;; piirrä-ufo : numero -> kuva
    (define (piirrä-ufo tila)
            (place-image UFO 250 tila KENTTÄ))

    Kokeilimme tätä funktiota big-bang:in kanssa. Ja tietenkään ufo ei liikahtanutkaan.

    (big-bang 0 
             (to-draw piirrä-ufo))

    Pienen mietinnän jälkeen oli selvää, että kukaan tai mikään ei koskaan muuta tilaa, ja näin pääsimme tekemään siirrä-ufo - funktiota. Tämä on se funktio, jota big-bang kutsuu on-tick:llä, eli se toteutaa asioita, jotka muuttuvat ajan funktiona. Tässä vaiheessa piti miettiä miten tila ufon laskeutuessa muuttuikaan, ja päädyimme yhteenlaskuun (y-koordinaatithan kasvavat alaspäin kuljettaessa Racket:issa):

    ;; siirrä-ufo : numero -> numero
    (define (siirrä-ufo tila)
            (+ tila ASKEL))

    Lisäsimme uuden rivin big-bangiin ja nyt ufo lähti tosiaan liikkeelle mutta katosi maanpinnan alle!

    (big-bang 0 
             (to-draw piirrä-ufo)
             (on-tick siirrä-ufo))

    Viimeinen uusi asia tällä tunnilla oli big-bang:in lopetusehdon kirjoittaminen eli stop-when. Funktion nimeksi valittiin  maassa?. Nimessä oleva kysymysmerkki kertoo siitä, että se palauttaa ns. boolean arvon. Vaikka boolean - arvot eivät olleet entuudestaan tuttuja, oppilaiden tuntui olevan melko helppo ymmärtää, että jokin matemaattinen lauseke voi olla true tai false. Tällainen lopetusehdosta tuli:

    ;; maassa? : numero -> boolean
    (define (maassa? tila)
                (>= tila MAANPINTA))

    Ja big-bangiin lisättiin taas yksi uusi rivi:

    (big-bang 0 
             (to-draw piirrä-ufo)
             (on-tick siirrä-ufo)
             (stop-when maassa?))

    Nyt ufo saatiin laskeutumaan maanpinnalle. Jokaisella oppilaalla oli hieman eri kokoinen KENTTÄ, joten he saivat itse etsiä sopivan arvon MAANPINTA-vakiolleen.

    Tässä kaksoistunnissa oli hirveästi asiaa, ja tunnin lopuksi oppilaat olivat hieman pyörällään päästä. Ehkäpä jatkossa pitäisi yrittää tehdä pienempiä kokonaisuuksia :-)