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.
  • Ei kommentteja:

    Lähetä kommentti