Lemonade 2: loppuraportti

Lemonade 2: taustaa

Ajatus Lemonade 2:een lähti kaukaa lapsuudestani, arvaukselta vuosilta 89-90. Tällöin kotiimme tuotiin uusi ja ihmeellinen vempain, Applen pieni tietokone. Kyseinen laite hankittiin kotiimme ilmeisesti lähinnä heikohkoksi statussymboliksi, mutta yhden käyttökelpoisen ohjelman se sisälsi; vapaata markkinataloutta mitä hienoimmalla tavalla ilmentävän pelin nimeltä Lemonade. Pelin perusidea oli kutkuttavan yksinkertainen. Kuuntele ennusteet. Tilaa pulloja. Säädä niille hinta.

Rikastu.

Peli oli tekstipohjainen ja toisti itseään loputtomiin. Sitä ei voinut voittaa, sen saattoi vain hävitä. Mutta pieni kapitalisti sisälläni puski minua istumaan sen ääressä useita tunteja kiroten välillä sääennusteiden paikkansapitämättömyyttä tai markkinoiden hiljaisuutta. Onneksi peli hajosi ennen kuin olin peruuttamattoman rahanhimon vallassa. Aikaa myöten se unohtui. Tuli uusia ja hienompia pelejä. Cat, Slicks, Super Mario ja ennennäkemättömät kolmiulotteiset pelit. En koskaan muistellut Lemonadea ennen kuin se eräänä päivänä matkalla Otaniemeen muistui mieleeni kun pohdin sopivaa aihetta ohjelmointiprojektiini. Tiesin, että vastaus oli löytynyt. Aika oli viimein kypsä.

Lemonaden oli aika tehdä paluu.

Yleiskuvaus:

Lemonade 2 on perusajatukseltaan uskollinen legendaariselle esikuvalleen. Uuteen versioon on kuitenkin lisätty hieman mausteita, jotta pelielämys olisi entistäkin jännittävämpi ja nautinollisempi. Peli lähtee liikkeelle sääennusteesta. Uudistuksena vanhaan lemonadeen mukana ovat nyt myös tapahtumat. Pelaaja saa ruudulle seuraavan päivän sääennusteen ja tiedot mahdollisesti seudulla olevista tapahtumista. Tämän perusteella hän tekee limsatilaukset. Pelaajalla on valtaa paitsi tilattavien pullojen määrään, myös niille asetettavaan hintaan sekä uutena ominaisutena myös työntekijän palkkaan. Seuraavaksi pelaaja saa ruudulle päiväraportin, jossa kerrotaan, pitikö sääennuste paikkansa ja montako pulloa limsaa myytiin.

Merkittävin lisäys vanhaan Lemonadeen lienee tuotteiden määrä. Kun lapsena pääsin valloittamaan virvoitusjuomamarkkinoita vain yhden tuotteen voimin, tuo Lemonade 2 tähän merkittävän lisähaasteen tarjoamalla asiakaskunnalleen jopa neljää eri tuotetta. Pelaajan on kuitenkin osoitettava hallitsevansa myynnin salat, ennen kuin hän pääsee käsiksi useampaan tuotteeseen, joista jokainen on tietyn asiakassegmentin erityissuosiossa. Tällöin tapahtumien merkitys nousee jopa kohtalokkaaksi. Pelaajan on osatava arvioida, millainen tapahtuma houkuttelee juuri tiettyä asiakasryhmäää kioskin läheisyyteen samalla kun hän tasapainoilee työtyytyväisyyden ja epämääräisten sääennusteiden puristuksessa.

Haastetta pelaamisessa? Kyllä. Haastetta tekemisessä? Voi pojat.

Käyttöliittymän kuvaus:

Edeltäästään poiketen Lemonade 2:n käyttöliittymä on graafinen. Käyttöliittymä on jaettu kahteen osaan, joista oikea puoli käsittää kaiken toiminnalisuuden ja vasen tarjoaa pelaajalle visuaalista nautintoa Kaisa Simolan vaihtuvien funkistaideteosten muodossa. Oikeaa puolta suunniteltaessa tavoitteena oli saada kaikki tarvittavat komponentit sekä mahtumaan niile varattuun tilaan että esittää ne vieläpä mahdollisimman selkeässä järjestyksessä. Tämän osoittauduttua GridBagLayout-taidoillani mahdottomaksi ymmärsin lopulta jakaa koko oikean puolen vielä kolmeen eri JPaneliin, joista jokaisella oli oma layoutinsa. Tämän jälkeen komponentit käyttäytyivät jo paljon suotuisammin ja lopulta asettuivat allaolevan kaltaiseen järjestykseen. Pelin aluksi kolme viimeistä tuoteriviä ovat aluksi piilotettuina, ja ne asetetaan näkyviin, mikäli Pelipaneeli tarkistettuaan kassan joka vuoron jälkeen niin päättää.

Peli alkaa JTextArean läväyttäessä esiin ensimmäisen sääennusteen ja tiedon mahdollisesta seuravan päivän tapahtumista. Koska peli alkaa aina tammikuusta, minkä voi huomata oikean ylänurkan kalenterista, ennuste lupaa suurella todennäköisyydellä kylmää säätä. Vuodenaikoja voi varauksella seurata myös vasemman puolen kuvista. Pelaaja asettaa työntekijälleen palkan, joka oletuksena on 50 dollaria, ja siirtyy itse asiaan eli tilauksiin.

Pelin alussa esillä on vain yksi tuoterivi, jonka kahteen JSpinneriin pelaaja asettaa tilattavien pullojen määrän sekä niille asetettavan hinnan. Arvot voi valita joko spinnereiden nuolilla tai kirjoittamalla ne itse. Tämän jälkeen seuraa pelin jännittävin hetki, kun pelaajan tulee painaa alhaalla olevaa "Tilaa" -JButtonia. Tämän jälkeen JTextArea näyttää tiedot menneen päivän säästä ja myynneistä. "Valmis" -nappi päättää päivän. Uusi päivä koittaa uusine ennustuksineen ja tapahtumineen, kassa on päivittynyt ja tuotevalikoima mahdollisesti lisääntynyt. On jälleen aika tilata juomaa.

Kuva käyttöliittymästä

Mikäli pelaaja kokee tarvitsevansa peliohjeita, ne löytyvät ylävasemmalta "peli"- valikosta, mistä voi myös halutessaan aloittaa uuden pelin tai ääritapauksessa lopettaa sen ja siirtyä tekemään jotain järkevää.

Tekninen toteutus:

Toteutuksen yleisperiaatteet:

Kunnon suunnitelma oli ylellisyys, josta en missään vaiheessa projektia saanut nauttia. Projektisuunnitelmaan äkkiä kyhäämäni ajatus lähinnä satunnaislukuihin pohjautuvasta teknisestä perusideasta heitti kuperkeikkaa lukemattomia kertoja, kunnes se assarien ehdotuksesta lopulta asettui lähemmäs simulaatiota kuin alkuperäistä satunnaisvuoristorataa.

Luokkajako muuttui lähes päivittäin kunnes se lopulta hahmottui mielestäni järkeväksi 8:n luokan kokonaisuudeksi, vain päivää deadlinen jälkeen.

Lopullinen peli kuitenkin perustuu siihen, että pelaajan säädettyä kenttiin valitsemansa arvot ohjelma laskee kolme erillistä säästä, tapahtumista ja työtyytyväisyydestä riippuvaa kerrointa, joiden perusteella simulaattori laittaa 1000:n asiakkaan asiakaskunnan joko tekemään limuostoksia tai jäämään kotiin. Hinnan merkitys tulee esiin vasta tämän jälkeen, kun asiakkaat "ovat jo kioskilla". Lempituotteistaan asiakkaat ovat valmiit maksamaan hieman korkeampaa hintaa kuin muista. Samoin pirtusta he ovat valmiit maksamaan jopa hunajaa. Lopuksi ohjelma laskee myydyt limsat, tulostaa tiedon niistä ja päivittää kassan.

Pelin kauneus piilee tälläkin vuosikymmenellä siinä, että siinä ei edelleenkään ole voittajia. On vain kahdenlaisia häviäjiä. Ensimmäiset ovat yksinkertaisesti surkeita myyjiä. Mikäli kassa on miinuksella kolme perättäistä vuoroa, on kohtalona konkurssi. Toinen tapa hävitä esitellään alempana.

Toteutusympäristö:

Peli on toteutettu Eclipse- ohjelmalla. Vasemmalla esiintyvät uniikit kuvat on tehty Paintilla. Apuna tiedon haussa on käytetty Java-APIa, Westerholmin ja Kypön kirjoittamaa "Java-ohjelmointi" -kirjaa sekä kurssiassistenttejen loputonta tietämystä.

Toteutuksen pääpiirteet:

Peli käynnistyy Limupeli-luokasta, joka pääohjelmametodillaan avaa ruudulle ikkunan. Oikealla puolella näkyvä Pelipaneeli arpoo sääennusteen ja mahdollisen tapahtuman, hakee nämä Olosuhteet -luokasta ja esittää ne tekstikentässä. Pelaajan painaessa "Tilaa" -nappia Pelipaneeli ottaa talteen spinnereissä olevat, pelaajan säätämät arvot, laskee säähän pohjautuvan sääkertoimen, työntekijän palkkaan pohjautuvan asiakaspalvelukertoimen sekä tapahtumiin pohjautuvat nais- lapsi- ja mieskertoimet. Samalla se kutsuu Ihmissimulaattorin teeOstot-metodia, joka ottaa paneelilta kyseiset kertoimet ja näiden perusteella luo 350 Naista, 350 Miestä ja 300 Lasta ja kertoimien perusteella joko kutsuu tai ei kutsu näiden osta- metodeja. Varsinaiset ostopäätökset tekevät kuitenkin Ihmiset itse ja kertovat näistä Ihmissimulaattorille. Simulaattori taas lähettää nämä tiedot Pelipaneelille joka tulostaa tiedot päivän tapahtumista ja tekee tarvittavat muutokset paneeliin. Tällöin myös kahden JButtonin aktiivisuudet vaihtuvat, päivän päättyessä aktiivisena on ainoastaan "Valmis" -nappi. Tätä painettaessa kierros lähtee jälleen alusta.

Tärkeimmät algoritmit:

Lemonade 2:n monimutkaisin algoritmi on ehdottomasti asiakkaiden ostokäyttäytyminen. Tämä toteutetaan kolmen eri luokan yhteistyönä. Pelaajan painaessa tilaa-nappulaa Simulaattori käynnistää teeOstot- metodin ja kysyy Pelipaneelilta kolme eri kerrointa. Pelipaneeli hakee päivän säähän perustuvan sääkertoimen, työntekijän palkkaan perustuvan asiakaspalvelukertoimen sekä tapahtumiin perustuvat mies- lapsi- ja naiskertoimet ja kertoo nämä simulaattorille, joka luo kolmella for-loopilla asiakaskunnan ja laittaa nämä tekemään ostoksia kertoimien ja satunnaislukujen suotuisuuden mukaan. Asiakkaat tekevät nämä ostokset omissa luokissaan. Mikäli asiakassegmentin oma lempituote on myynnissä, he ostavat ensisijaisesti sitä. Tässä vaiheessa tarkistetaan myös hinta. Lempituotteestaan asiakas on valmis maksamaan korkeampaa hintaa kuin muista. Käytännössä tämä tapahtuu pitkällä if-lauseiden sikermällä, jossa siirrytään vaihtoehdosta toiseen sen mukaan, ovatko kaikki tuotteet myynnissä ja onko niiden hinta säädetty liian korkeaksi. Mikäli minkäänlaista ostopäätöstä ei synny, metodin suoritus päättyy kyseisen asiakkaan osalta. Mikäli taas asiakas päättää ostaa juomaa, tämä lähettää tiedon ostopäätöksestään Ihmissimulaattorille ja parametrina ostamansa tuotteet. Simulaattori kerää tiedot ostoista laskureihinsa ja lopulta lähettää ne Pelipaneelille, joka tekee kassaan muutokset.

Päivän olosuhteiden arpominen puolestaan on toteutettu niinsanotusti puolisatunnaisesti. Pelissä on 20 mahdollista säätä ja 10 mahdollista tapahtumaa. Puolisatunnaisuus tässä yhteydessä tarkoittaa sitä, että tietyt säät on asetettu mahdolliseksi vain tiettyihin vuodenaikoihin. Vuodenaikoja seurataan vuorolaskurilla, joka nollataan jokaisen vuoden lopussa. (Mainittakon tässä että Lemonade 2:ssa kuukausi kestää neljä vuoroa.) Näin kesäisillä chilifestivaaleilla ei sada lunta eikä joulukuussa kärsitä helteestä. Olosuhteet arvotaan teeToimenpiteet -metodilla, joka kalenterin mukaan arpoo päivän sään ja mahdollisen tapahtuman. Seuraavaksi se hakee näiden kuvaukset Olosuhteet -luokasta. Sääennusteiden epävarmuus luodaan tässä kohti. Arvottu sää pitää aina paikkansa, mutta tietyllä todennäköisyydellä haetaan Olosuhteista väärä kuvaus. Pelaajasta näyttää siltä, ettei sääennuste toteutunut, vaikka kooditasolla kyse on vain siitä, että pelaajaa kusetettiin alun perinkin.

Lyhyet kuvaukset luokista :

Luokkien välistä kommunikaatiota havainnollistava kuva

Limupeli:
Periytyy JFramesta ja luo pääohjelmametodillaan peli-ikkunan, jossa kaksi JPanelia. Vasen paneeli sisältää JLabelin, jonka ikoni vaihtuu kalenterin mukaan. Oikea puoli sisältää Pelipaneelin. Aloituksen jälkeen Limupelillä ei ole pelin aikana muta toiminnallisuutta kuin vaihtaa oikean puolen ikonia Pelipaneelin niin vaatiessa.
Pelipaneeli:
Toiminnallisuudeltaan laajin ja tärkein luokka. Toimii lähes kaikkien luokkien välisenä tiedon kuljettajana ja säilöjänä. Hakee olosuhteet, laskee kertoimet, laittaa simulaattorin käyntiin ja vieläpä tulostaa kaiken tiedon pelaajan käytettäväksi. Jokainen kierros alkaa pelipaneelista ja myös päättyy sinne.
Olosuhteet:
Toimii ainoastaan kuvausten säilyttäjänä, josta pelipaneeli sopivalla parametrillä hakee ne arvottuaan ensin sään ja tapahtumat.
Ihminen:
abstrakti luokka, jolla onvain yksi abstrakti metodi, eli osta(). Aliluokkinaan Ihmisellä ovat luokat Mies, Nainen ja Lapsi.
Ihmissimulaattori:
Hoitaa ostamisen kolmella for-loopilla, tallettaa myös tiedot ostoista ja lähettää ne Pelipaneelille. Lähettää myös tiedot mahdollisista asiakasvalituksista, jotka kertovat työtyytyväisyyden alhaisuudesta ja siitä, onko pirtua myyty alaikäisille.
Nainen:
Ainoastaan yksi ostometodi. Nainen ostaa ensisijaisesti light-juomaa.
Mies:
Omaa ainoastaan vastaavan ostometodin kuin nainenkin, erotuksena ainoastaan lempituote, joka miehellä on Pirtu.
Lapsi:
Samoin lapsella on ainoastaan ostometodi ja heidän lempituotteensa on energialimsa. Lapset eivät tee asiakasvalituksia, mutta pirtun ollessa myynnissä lasten kanssa pitää noudattaa erityistä varovaisuutta. He nimittäin ostavat tässä tapauksessa ensisijaisesti pirtua, ja mikäli työntekijän palkka ei ole tarpeeksi korkea, hän moraalittomasti saattaa myydä pirtua myös lapsille. Mikäli näin tapahtuu, tästä tulostetaan tieto päivän raporttien mukana ja sakotetaan. Kolmannella kerralla kioski suljetaan ja peli päättyy. Tässä ylempänä lupaamani toinen häviämistapa.
Ohjeet:
Säilyttää peliohjeet, joille avataan oma ikkuna.

Kokemuksia projektista:

Alkuperäinen suunnitelmani toteutui lähes sellaisenaan. Yksityiskohtaisimmillaan suunnitelmani oli tehdä uusi versio Lemonadesta tietyin lisäominaisuuksin. Lopputulos oli uusi versio Lemonadesta tietyin lisäominaisuuksin. Tämän eritellympää, oikeasti mietittyä suunnitelmaa en lukuisista kauniista ajatuksista huolimatta koskaan saanut aikaiseksi.

Aihe-ehdotus sen paremmin kuin palauttamani projektisuunnitelma olivat lähinnä kiireessä kasattua hötöä, ja jälkimmäistä on lähes harhaanjohtavaa kutsua suunnitelmaksi. Sen tärkein ja parhaiten suunniteltu sisältö oli aikataulu, ja teknisesti ottaen aikataulua voisi siis kutsua projektin ainoaksi osaksi joka ei toteutunut suunnitellusti.

Hyppäsin siis side silmillä avantoon ja lähdin koodaamaan "suunnitellen vähän siinä samalla". Tästä johtuen pelkän käyttöliittymän kyhäämiseen meni kolme päivää. Hammasta purren koodasin, pyyhin, koodasin, pyyhin, koodasin ja pyyhin taas. Vasta neljä päivää ennen deadlinea ylpeyteni antoi periksi ja tartuin kynään ja paperiin. Sen jälkeen koodi syntyi paljon vaivattomammin. Projektini huipentui palautusviikonloppuun ja valvottuun lauantain ja sunnuntain väliseen yöhön. Sunnuntaiaamuna koodi oli valmis. Ainoa ongelma oli, ettei se toiminut enkä ymmärtänyt iltapäivällä enää riviäkään yöllisistä tuotoksistani. Toki ympärilläni oli monia häiriötekijöitä, istuinhan koko sunnuntain Länsiauto-areenalla seuraamassa muodostelmaluistelukilpailuja kannettava sylissäni ja Geir Rönningin laulaessa Maamme-laulua yritin epätoivoisesti miettiä, miksi hitossa kukaan ei osta mitään. Kisoista mainittakoon sen verran, että Marigold IceUnity otti suvereenin voiton edellisten kisojen kirvelevän hopeamitalin jälkeen.

Lienee sanomattakin selvää, etten saanut projektia haluamaani kuntoon deadlineen mennessä. Palautin kuitenkin yhden version ja jatkoin koodaamista maanantaina, tehden pieniä muutoksia kuten luokkarakenteen täydellisen vaihdoksen ja muuta pientä. En saanut projektia haluamaani kuntoon maanantai-iltaan mennessä. Jätin kuitenkin myöhemmät muutokset hamaan tulevaisuuteen, ja tässä esittelen projektiani sellaisenaan kuin se tuona maanantaina, 9. Tammikuuta 2006 klo 23.59 oli.

Virheistä ja vaikeuksista:

Lyhyesti: niitä riitti. Suurimman osan olisi voinut välttää suunnittelemalla koko projektin paremmin. Alun vaikeudet tosin johtuivat suurimmaksi osaksi kokemattomuudestani GridBagLayoutin käytössä. Komponentit asettuivat sinne tänne tai eivät ollenkaan. Joskus ne olivat päällekkäin. Joskus ne olivat väärin päin. Joskus ne olivat näkymättömissä, vaikka kuitenkin läsnä. Joskus olin valmis vetämään kannettavani pöntöstä alas. Ymmärrettyäni jakaa Pelipaneelin kolmeen helpommin kontrolloitavaan osaan tunsin kuitenkin itseni varsinaiseksi GridBagVelhoksi. Komponentithan asettuivat paikoilleen!

Luojalle kiitos Eclipsestä ja Eclipselle kiitos koodin jatkuvasta kääntämisestä. Näin vältettiin lähes kaikki pikkuvirheet. Sääli ettei isoja. Lähes kaikki loput virheet olivat tulosta huonosti suunnitellusta luokkien välisestä kommunikaatiosta. Suurin ongelmani oli ymmärtää, miksi kukaan ei todellakaan ostanut mitään. Vaikka olisin säätänyt niiden hinnaksi nollan, tekstikenttä kertoi päivästä toiseen karua faktaa: Sitruunalimua myyty 0 kpl. Tämä johtui ainoastaan Ihmisten ja myöhemmin Ihmissimulaattorin korvaaman Asiakas-luokan puutteellisesta kommunikaatiosta. Tämän tajuamiseen meni useita tunteja, ja ongelma poistui heti kun olin muuttanut luokkarakennetta järkevämmäksi.

Omasta työskentelystä:

Oma työskentelyni projektin parissa oli odotetun kaltaista. Alkuinnostuksen jälkeinen taantuma ja lopun 30 tunnin pakotettu koodausputki olivat kaikki odotettavissa, ei tosin toivottavissa. Iloitsin kuitenkin siitä, että kyse ei ollut motivaation puutteesta. Koodasin varsin mielelläni innostuneena siitä että olin tosiaankin syksyn kuluessa oppinut jotain. Alkusyksyn "Enhän nyt MÄ voi tollasta koskaan osata"- pelot vaihtuivat suureen riemuun spinnerien, if- rimpsujen, looppien ja kertoimien lopultakin alkaessa pelittää keskenään.

Kokonaisuudessaan käytin projektiin aikaa noin 60 tuntia. Tästä suuri osa kului rakenteiden ja luokkayhteistyöratkaisujen pohtimiseen. Kunnon suunnittelulla itse koodaamiseen kulunut aika olisi varmasti pudonnut ainakin kymmenellä tunnilla. Toisaalta tässä tapauksessa olisin luultavimmin lisännyt peliini ominaisuuksia, jolloin tuntimäärä olisi saattanut kavuta takaisin entiseen. Pelistä olisi vain tullut hieman parempi.

Oppimiskokemuksena projekti oli itselleni täysin vertaansa vailla. Monen virheen kautta koen todella oppineeni jotain etenkin juuri luokkien välisestä kommunikaatiosta ja luokkarakenteesta yleensä. Itse koodini ei sisällä paljoa asiaa, jonka olisin joutunut opettelemaan vasta projektivaiheessa, mutta ohjelmani vaikeus piileekin ehdottomasti koodin rakenteessa, ei sen varsinaisessa sisällössä. Tästä koen todella oppineeni paljon, vaikka höyryjen pitikin laskeutua parin päivän ajan ennen kuin ymmärsin asian.

yhteenveto:

Nyt viikon ajan rentouduttuani ja peliä pari kolme kertaa pelattuani voin todeta olevani suhteellisen tyytyväinen lopputulokseen. Pitäisi riemuita jo siitä, että sain jotain näkyvää aikaiseksi. Lisäksi se vielä toimii lähes toivotulla tavalla. Olen siis oikeasti oppinut jotain syksyn tuskien taipaleella.

Tärkein opetus koko tarinassa on se, että suunnittelu on tärkeää, tärkeämpää, tärkeintä. Siis oikeasti tärkeintä. Nyt valaistuksen kokeneena lupaan pyhästi, etten koskaan enää lähde mihinkään ilman suunnitelmaa. En mene metsään ilman karttaa. En lähde veneilemään ilman airoja. Koskaan, en koskaan enää lähde bussipysäkille katsomatta ensin reittiopasta.

Ennen kaikkea en enää koskaan lähde koodaamaan ilman suunnitelmaa.

Enkä aio ohjelmoida riviäkään ainakaan kuukauteen.