Racket Turtlella voidaan piirtää geometrisia kuvioita käyttäen Racket-kieltä ja DrRacket:iä. Koodin voi ladata nyt GitHub:ista. Päivitän koodia jatkossakin sinne, eli kannattaa latada koodi zip-pakettina suoraan sieltä.
Tässä lyhyet käyttöohjeet Racket Turtlelle:
1) Asenna DrRacket täältä : http://download.racket-lang.org/
2) Lataa Racket Turtle:n koodi täältä (Download ZIP): https://github.com/tyynetyyne/racket_turtle
3) Pura ZIP-paketti
4) Avaa racket_turtle_esimerkit.rkt tiedosto DrRacket:lla
5) Paina "run"
Huom! jos DrRacket ei tunnistanut tiedoston ohjelmointikieltä automaattisesti, valitse se valikosta
--> Language --> Choose Language --> Student Languages --> Beginning Student
Nyt voit testata Racket-Turtlen esimerkkikoodeja. Kirjoita DrRacket:in REPL:iin (alempaan ikkunaan): (piirrä neliö) ja paina <enter> ja Racket-Turtle piirtää sen.
Tämä toimii, koska racket_turtle_esimerkit.rkt tiedostossa on määritelty neliölle ohjeet näin:
Racket-Turtle osaa myös toistorakenteita eli repeat - käskyn avulla voit ensin määritellä yksinkertaisen kuvion (sivu), jota toistetaan haluamasi määrä:
(define sivu (list (forward 100) (turn-left 90)))
(define toisto-neliö (repeat 4 sivu))
Racket-Turtlen kynän voi myös nostaa ilmaan/laskea, sekä sen värin voi muuttaa haluamakseen:
Racket-Turtlen avulla voidaan opetella kirjoittamaan myös funktioita. Tämän esimerkkikoodin avulla voidaan piirtää minkä tahansa kokoinen neliö (x on sivun pituus): (define (muuttuva-sivu x) (list (forward x) (turn-left 90)))
GitHub:ssa on myös racket_turtle_harjoitukset_1.rkt tiedosto, josta löytyy 7.luokan matematiikan tunnille sopivia geometrian harjoituksia.
Omat oppilaani ovat tykänneet leikkiä Racket Turtlella joten uskon, että tästä on iloa myös teille muillekin! Tässä Lielahden koulun 7F-luokan oppilaiden Racket Turtle taidetta, jota he piirsivät matematiikan tunnilla:
Olin eilen Innokas - verkoston Kiinnostaako koodaus ja robotiikka - jatkokoulutuksessa opettamassa Racket - ohjelmointia. Aamupäivän pajassa kävimme läpi Racket - kielen perusteita (peruslaskuja sekä piirtämistä) sekä sitä miten Racket-Turtlea voi käyttää geometrian opetuksessa.
Emme meinanneet päästä lounastauolle, kun tämä kuvio piti saada koodattua Racket-Turtlella
Iltapäivällä työskentelimme Peruspeli 1:n kimpussa. Peruspelissä on kaksi eri pohjaa, joiden avulla voidaan opettaa ohjelmoinnin perusperiaatteet: ehtolauseet, haarautuva koodi sekä funktion kirjoittaminen.
Kevään aikana olen tutustunut amerikkalaisen Bootstrap-projektin ohjelmointimateriaaleihin yläkoulun matematiikan opiskelussa. Heidän lähestymistapansa on mielenkiintoinen, ei vähiten siksi, että hekin käyttävät Racket-kieltä vaan sen takia, että materiaalin keskiössä on matematiikka, ei ohjelmointi. Projektissa jokainen oppilas tekee itselleen oman "videopelin" melko valmiiseen pohjaan. Pelipohja on suunniteltu niin, että oppilaan pitää ratkaista jonkin verran matemaattisia ongelmia, mutta kirjoittaa mahdollisimman vähän ja mahdollisimman yksinkertaista koodia.
Vaikka periaatteessa Bootstrap I - pelipohja on aivan toimiva sellaisenaan (se on riisuttu niin yksinkertaiseksi kuin pelin koodaamisen lausekielellä nyt voi riisua), niin olen törmännyt sen kanssa muutamaan harmilliseen asiaan:
Pelipohja on toteutettu niin, että sillä voi tehdä yhden ja saman pelin variaatioita ja sitten tulee seinä vastaan. Esim. vaara (DANGER) ja kerättävä (TARGET) liikkuvat vain x:n suunnassa, eikä sitä voi muuttaa mitenkään. Myös koko big-bang on piilotettu, joten esim. hiiren tai kosketusnäytön eventtien lisääminen ei onnistu mitenkään helposti.
Pelipohjan käyttämä kirjasto kääntää koordinaatiston ns. "oikeinpäin" (Racket:issa (0, 0) on normaalisti vasemmassa ylänurkassa, Bootstrap:illä se on vasemmalla alhaalla). Äkkiseltään ajatus tuntuu loistavalta, mutta kun kaikki muut toiminnot kuten overlay/xy toimii Racketissä vasemman ylänurkan origon logiikalla niin tämä Boostrapin versio aiheuttaa vain turhaa hämmennystä.
Bootstrap:in pelipohjassa on mainio "kolmio"-trace, jolla nähdään miten hahmojen etäisyyden laskemiseen tarvittava kolmio muodostuu livenä. Vaikka kolmio auttaakin Pythagoraan lauseen hahmottamisessa niin näkisin, että paikallaan olevaa kolmiota olisi helpompi tutkia ja oikeita lukuja voisi pyöritellä helpommin kuin hahmojen koordinaatit näkyisivät kuvassa (nyt ne eivät näy, sen sijaan etäisyydet näytetään sitten kun ne on ohjelmoitu).
Pelipohjassa ärsytti myös se, että Bootstrap - kirjasto on taas yksi "black box", jonka toimintaa voi vain arvailla, eikä kaikkia pelin asetuksia pääse muuttamaan (pelin korkeus, leveys, hahmojen lähtösijainnit jne.)
Kerholaisteni kanssa törmäsin myös siihen, että 5.-7. luokkalaisille oli melkoisen vaikea opettaa Pythagoraan lausetta, kun potenssit sekä neliöjuurikin puuttuivat sanavarastosta. Törmäys pitäisi siis voida todeta myös ilman Pythagoraan lausetta.
Kokeilin pystyisikö vastaavan pelipohjan kirjoittamaan kokonaan ilman rajoittavaa Bootstrap - kirjastoa, ja tällaisen version sain aikaan: Peruspeli-pohja. Oppilaan koodattavaksi on jätetty vastaavat osuudet kuin Bootstrap I - pelissäkin, mutta valmis koodi on jätetty tiedoston loppuun niitä oppilaita ja opettajia varten, jotka haluavat nähdä mitä "kulissien takana" tapahtuu ja vaikka jatkaa pelin muokkaamista vielä pidemmälle. Tein pelipohjasta kaksi eri versiota: 1a versiossa törmäys havaitaan ilman Pythagoraan lausetta ja 1b, jossa Pythagoraan lause on käytössä.
Koodista ei tullut aivan niin selkeää kuin olisin toivonut: törmäystä havainnolistavien kolmioiden/suorakulmioiden ja koordinaattien tulostaminen teki siitä turhankin monimutkaista. Kumpi on sitten loppupeleissa tärkeämpää, tarjota apuja niille oppilaille, jotka eivät heti hahmota miten hahmojen törmäysehdon voi koodata vai antaa helppo ja selkeä pohja innostuneille pikkukoodareille jatkaa pelin kehittelyä eteenpäin?
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.
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:
Plussat
Miinukset
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.
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.
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.
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!!!