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ä :-)

perjantai 6. helmikuuta 2015

Tuunaa tuntisi 2015

Pidin Tampereen Tuunaa tuntisi - messuilla kaksi erilaista Racket-ohjelmointipajaa. Aamupäivän pajassa tutustuimme Racket-ohjelmointiin piirtämällä peruskuvioita sekä Turtle-grafiikkaa. Tässä käyttämäni materiaali sekä Racket-turtle-koodi:  Iltapäivän pajassa teimme yksinkertaisen pelin, jossa kerätään palkintoja ja väistellään vaaraa. Pelipohjan ja kirjastot on tehnyt Bootstrap - projekti.
Saadaanko tämä pelihahmo liikkeelle?

Racket-turtle 7.luokan matikan tunneilla

Tällä viikolla olemme koodanneet 7. luokkalaisten kanssa geometrisia kuvioita Racket-turtlella. Olimme juuri opiskelleet erilaisten monikulmioiden nimiä, joten nyt pääsimme hyödyntämään tietojamme. Näytin oppilaille ensin miten Turtlelle annetaan ohjeet neliön piirtämiseksi. Tämän jälkeen he saivat tehtäväksi piirtää tasasivuisen kolmion, säännöllisen kuusikulmion, suunnikkaan tai neljäkkään, ympyrän sekä itse keksimänsä kuvion. Lopuksi kuviot piti saada piirtymään samaan kuvaan eri väreillä.

Osa oppilaista keksi nopeasti miten Turtlea ohjataan, osalla kesti hieman kauemmin, mutta jokainen sai ensimmäisen tunnin jälkeen vähintäänkin kolmion valmiiksi (myös yksi ympyrä ilmestyi jo ensimmäisellä tunnilla). Toisella tunnilla nopeimmat pääsivät jo yhdistämään kuvioitansa. 
Ohjelmointi vaikutti innostavalta, koska jälleen kerran kun kello soi, useampi oppilas jäi vielä koodaamaan kuvioitansa ja lopuksi minulta kysyttiin: kai me jatketaan näitä vielä?

Tunnin jälkeen parantelin Racket-Turtle koodiani, koska tulin siihen tulokseen, että yksinkertaisuuden vuoksi on kuitenkin helpompaa, että ohjeissa sallitaan sittenkin sisäkkäiset listat (eli ohjeiden antajan ei tarvitse osata käyttää "append:ia" tai "cons":ia, pelkkä "list" toimii). Rajoitin piirtämistä myös niin, että vain piirtoalueelle osuvat viivat piirretään (jotta animaatio ei sekoaisi liian isojen kuvien kanssa). Oppilaiden oli myös vaikea hahmottaa mihin kohtaan tiedostoa omat ohjeet kirjoitettiin, joten laitoin Racket-turtle-koodin omaan tiedostoonsa (teachpacks/racket_turtle.rkt), esimerkit omaansa (turtle_esimerkit.rkt) ja tehtävät kolmanteen (turtle_harjoitukset_1.rkt).

Jos haluat kokeilla oppilaittesi kanssa Racket-turtle: lataa racket_turtle.zip. Pura paketti esim. kotihakemistoosi ja tutustu turtle_esimerkit.rkt - tiedostoon. Avaa sitten turtle_harjoitukset_1.rkt ja koodaa ohjeen mukaiset kuviot (kuva alla). Zip-paketti sisältää kaiken tarvittavan (paitsi itse DrRacket:iä).
Racket-turtle:lla piirretty harjoitus 1 (yksi mahdollinen ratkaisu)

Näillä matematiikan tunneilla Racket-turtlen käytössä pääpaino oli kuvioiden piirtämisessä. Pohdimme kuvioiden geometrisia ominaisuuksien kuten vaatimuksia sivujen pituuksille sekä kulmien suuruuksille. En edes yrittänyt ottaa esille ns. ohjelmointiteknisiä juttuja, kuten sitä että turhaa toistoa pitäisi välttää (en puuttunut siihen, että kuusikulmiossa oli kuusi kertaa sama koodin pätkä). Toki otimme esille repeat:in käytön, mutta vasta ympyrän kohdalla, koska ympyrän piirtämisessä tarvittava toistojen määrä olisi ollut järjetöntä koodata käsin. Myös siirtymät kirjoitettiin jokainen erikseen: siirtymä1, siitymä2 jne. vaikka siirtymäkoodi olisi toki järkevämpää kirjoittaa funktion muodossa, jossa annetaan käännöksen suuruus, siirtymän pituus ja uuden kuvion väri. Ehkä seuraava askel voisi olla oman funktion kirjoittaminen?

keskiviikko 4. helmikuuta 2015

Koodauskerho robottien kimpussa

Kevät on lähtenyt liikkeelle vauhdikkaasti koodauskerhossa. Blender 3D-mallinnuksen ja Racket-koodauksen lisäksi kerhossa on rakennettu ja ohjelmoitu Lego NXT ja EV3 robotteja. Ensimmäisenä kerhossa valmistui perusrobotti, joka ohjelmoitiin ajamaan kuusikulmion muotoista reittiä. Keksimme laittaa sille kynän kiinni "takapuoleen" ja saimme hienon "robottitaideteoksen" luokan seinälle.
Kerhomme ensimmäinen robotti
Toinen robotti syntyi EV3:n ohjeilla ja sille ohjelmoitiin "tutka"-toiminto, ettei se törmäilisi esteisiin.

EV3 - robotti
Tässä kuvaa "tutka-ajosta":


Tällä viikolla saimme käyttöömme myös sumo-pelilaudan ja tottakai sitä piti päästä heti testaamaan. Tässä kerhomme ensimmäinen sumorobotti kokeili pelilaudalla pysymistä, ja hyvinhän se siinä pysyi!


Kolmantena robottina valmistui Joulupukki ja poro näillä ohjeilla.

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.