Tuesday 31 December 2013

Eräänlainen vuosikertomus

Eräänlainen vuosikertomus

On taas tullut tämä vuoden hienoin aika, tässä vaiheessa vielä joulu, mutta kun tämä on luettavissa, uusivuosi. Kuten olemme tottuneet, pitää tällaisina aikoina kertailla kuluneita tapahtumia. Viimeksi hehkutin kuinka kulunutta vuotta pystyi kuvaamaan sanalla "loistava". Tällä kertaa tämä sana olisi enemmänkin "hämmentävä". Kertaillessani tuota edellisen vuoden postia hymisen hiljaa. Hehkutan tekstissä lispejä, emacsia, C#:tä ja kaikkea tavoin, joiden vuoksi olettaisin ihmisten miettivän että mikähän noobi tämänkin tekstin on kirjoittanut. Todennäköisesti katson myös tätä tekstiä vuoden päästä samoin. Ilmiö näkyy myös ohjelmoinnissa: ohjelmoijaystävistäni jokainen tietänee sen ihmetyksen, jota kokee lukiessaan yli vuoden vanhaa koodiaan. Tämä ihmetys kertoo jotain siitä, että on kasvanut ja oppinut jotain kirjoittamisen jälkeen.

Onneksi en ole sama hölmö kuin 5 vuotta sitten

Mikä on muttunut? Miten olen kasvanut?

Emacs on selkein esimerkki. Clojure-juttujen, blogin kirjoittamisen, sekä yleisluontoisten konffausharjoitusten avulla olen monta kertaa tehokkaampi editorin kanssa tuohon verrattuna. Viimevuotisessa tekstissä kirjoitan kuinka "rivit ovat Lisp-puussa, jonka Emacs muuttaa edellämainitulla html:ksi ennen bloggeriin siirtoa.". Tämä lausahdus edusti vaihetta, jota ymmärtääkseni tavataan uusilla lispereillä usein. Heidän mielestään kaiken voi tiivistää s-expreihin. Blogitekstin kirjoittaminen lispinä tuntui todella hyvältä verrattuna kahteen aiemmin oppimaani tapaan: bloggerin graafisen postihärpäekkeen käyttäminen, ja HTML:n käsinkokoaminen Notepad++lla. Kirjoita posti, hallitse dokumentin omituisuuksia hiireen koskematta, ja lopulta C-x C-e:llä generoi HTML, jota blogger syö. Valitettavasti jokaisesta ohjelmointikielestä tutut escape-ongelmat, sekä yleisluontoinen tönkköys pyörivät tämänkin metodin ympärillä. Lopulta löysin kuin löysinkin ideaalin kirjoitustavan, millä tietysti yritän sanoa että opettelin käyttämään Emacsin html-modea, joka hoitaa xml-tason escapet, tagien automaagisen sulkemisen, ja omaa pikanapit kaikkiin tavallisimpiin muotoiluihin, ja mikä tärkeintä, toisin kuin Wordissä, nämä napit toimivat deterministisesti.

Huhtikuussa aloin projektin, joka tulisi muuttamaan vielä kaiken. Maaliskuun loppupuolella olin hakenut Kotkaan inssikouluun, jossa oli suunnitteilla opiskella pelien tekemistä ja muita ohjelmistoinssijuttuja, ja huhtikuussa aloitettu projekti muutti, Applen hokemaa lainatakseni, taas kaiken. Kyseessähän oli MERPG, joka teknisesti ottaen on ollut meneillään yli puolena tämän blogin näkemistä vuosista, mutta nyt se otti aivan uuden suunnan. Kun olin edellisenä jouluna rakentanut Clojure-ympäristön parillekin koneelle, sekä lukenut muutaman kirjan aiheesta, oli nyt, kun löysin lopultakin ylimääräistä aikaa, aika tutustua ympäristöön käytännössä, ja ratkoa sillä käytännön ongelmia, ja millainen matka siitä tulikaan. Sanotaan, että Lisp-valaistuminen muuttaa tapaa, jolla ohjelmoit muita kieliä. Vaikka edellisessä vuosipostissa hehkutinkin että PHP-koodini (jolle minulla oli suuret suunnitelmat, mutta joka ei ole saanut rakkautta sitten toissalokakuun, ja joka pitäisi pelastaa kuolevan Viuhka.fi:n alta...) luonnetta, se sai minut lähinnä tajuamaan kuinka suurin osa maailmasta elää jotain ohjelmoinnin pronssikautta. Muutama harva on jo antiikissa ML:ien ja Lispiensä kanssa. Itse siis uskon ja toivon että imperatiivinen (olio-)malli katoaa suleutettujen järjestelmien ja systeemiohjelmoinnin ulkopuolelta lähivuosina.

Tämän ajatuksen ongelma on, että piireissäni olen aika pitkälti ainoa, joka siihen uskoo, lukuunottamatta ehkä Tässiä, jolta kyllä löytyy tälle alalle tarvittavaa hulluutta, mutta joka suuntaa sen aivan muihin juttuihin, ja Alpia, joka palloilee (netti-)ohjelmoinnin parissa välillä, ja jolta löytyy ehdottomasti edellämainittua hulluutta, mutta joka keskittyy muihinkin juttuihin. Hän mainitsi jotain jonkinlaisesta vaalikoneprojektista, ja udeltuani vähän tarkemmin, hän yllätti minut kertomalla kuinka oli miettinyt jonkin algoritmin vektoriaritmetiikan ympärille. Aluksi se kuulosti nimenomaan edellämainitulta hulluudelta, tarkemmin ajateltuna se kuulosti esimerkiltä mitä matemaagisesti orientoitunut persoona voi tuoda ohjelmointiin, kolmannella kerralla se kuulosti esimerkiltä siitä miten erilaisin tavoin java-ohjelmoija, lisp-ohjelmoija, ja matemaagisissa piireissä aloittanut ohjelmoija lähestyvät samaa ongelmaa.

Olen siis ainoa siihen uskova, täysipäiväinen ojelmoija. Edellämainitut saattaisivat uskoa, jos keskittyisivät ohjelmointiin, eivätkä herranjumalasentään, siihen mihin itse haluavat. Suurin osa ihmisistä on tyytyväisiä kieliin, jotka oppivat 10-20 vuotta sitten, tai joita opettaja ja internetin kaikkitietävät1 tunkevat kurkusta alas. Minä en, ja tämä on tuottanut minulle tänä syksynä paljon päänvaivaa. Aikoinaan, kun sain työpaikan, oli hyvän tuurin (ja sen kuuluisan kutinan aivojen pohjalla) syytä että lähdin pääasiassa C#:tä tekevään taloon, sillä C# on imperatiivisen maailman tuotantokielistä vähiten huono. Se kuitenkin alkaa... noh, tuntua ympäristöltä, joka ei ole luotu ihan minua varten, jos näin voi sanoa :) Tämä tunne on niin voimakas, että tuskin maltan odottaa koulun C#-tunneille paluuta tämän joululoman jälkeen. Tämä tunne myös on saanut minut mietiskelemään että nyt, kun olen MERPG-clj:n kanssa löytänyt suurimmat yllätykset Clojuresta2, mitä jos aloittaisin startupin tämän ympäristön ympärille? Mitä jos rakentaisin oman Viawebbini, pelien, mikä antaisi kiksejä sekä kouluni peliopettajille, että suomen pelihuumasta humaltuneille, tai yleissoftan ympärille? Mitä jos lakkaisin hokemasta "Mie oon nero!"a, ja sen sijaan todistaisin tämän faktan teoillani?

Lisp on tyhmää. Etkö muusta osaa puhua?

En. Voin silti yrittää. Maaliskuussa sijoitin muutaman euron Macbook Prohon. Yksilöhän oli retinallinen yksilö, joten kauneimmillaan tämän koneen DPI on naurettavan suuri (ja teksti täten lukukelvotonta <3), ja SSD:n ja puuttuvan optisen aseman vuoksi koneen ohuus on lähes naurettava, akkukesto kiva windowskoneisiin tottuneelle, ja mikä tärkeintä, OS X, toisinkuin eräs toinen kaupallinen järjestelmä, ei käyttäydy huonosti. Eikä myöskään yritä toteuttaa nelituumaisilla näytöillä hyväksitodettuja ideoita 24-tuumaisilla näytöillä huonosti. Sen sijaan OS X:ään on tuotu parhaimpia omituisuuksia vapaista ympäristöistä, ja kauneimmillaan jopa paranneltu niitä. Hyvänä esimerkkinä tästä Mission Controllin (Exposen?) virtuaalityöpöydät, ja se, miten näiden välillä voi hyppiä touchpadin avulla. Näitä on vaikea tuoda Gnomeen, koska PC:n touchpadit ovat hirveää roskaa, rottien kanssa vaihtosysteemi ei vain toimi, ja PC:n näppikset ovat täysin epäyhteensopivia Macin layoutiltaan kauniin näppiksen kanssa.

Käyttis ei kuitenkaan ole yhtä juhlaa: CMD-Tab sallii vaihtaa vain sovellusten välillä, sovellusten ikkunoihin ei pääse käsiksi niinkuin Gnomen Ctrl+Tabilla. Finder ei kaadu ja hidastele hölmöistä syistä, kuten muuan Explorer, mutta muuten on täysi susi: tiedostojen hallinta on jopa bashin komennoilla helpompaa kuin Finderilla. Explorerista soisin Applen ottavan mallia terveellisissä määrin, muttei liikaa. Alt-F4:n kaltaista "tapa ikkuna, mutta jätä prosessi" - pikanappia itse en ainakaan ole löytänyt, ja ajatus siitä että CMD-Q:n tulkinta prosessien totaalitaposta on parempi, on absurdi. Kokeilkaa suunnitella Clojurella ikkunoita, ja kärsikää kun tapatte vanhaa ikkunaa alta CMD-Q:lla, aikeena avata uusi ikkuna erikseen koska Swing ei ymmärrä dynaamista REPL-työskentelyä, ja huomaatte REPLin kaatuvan koska CMD-Q ei tuhonnutkaan vain ikkunaa, vaan koko java-prosessin taustalta.

Koneen suurin rautaongelma on näppäimistö. Älkää ymmärtäkö väärin, tämän koneen näppis on paljon parempi kuin yhdenkään alle kymmenvuotiaan PC-läppärinäppäimistön, mutta Ctrl ja Fn - napit ovat aivan väärinpäin, fn tuntuu olevan aivan hajalla, ja en ole vieläkään varma tykkäänkö napeista, joiden ympärillä on näin paljon tilaa. Miltei täydellisen näppäimistön olen asentanut pöytäkoneeseeni, ja tuohon verrattuna ainoat hyvät mitä Macin näppiksestä voi sanoa, on paljon parmpi merkkikartta, paremmat pikanapit poppiohjelman hallintaan, ja taustavalaistus.

Silti, kun ohjelmoin Emacsilla JVM:n päälle, ei alla olevalla käyttöjärjestelmällä ja raudalla ole niin paljoa väliä.

Jos seuraavaksi yrittäisin päästä tietokoneiden ulkopuoliseen maailmaan?

Viime tammikuussa kävin etelässä, muistaakseni Egyptissä. Se oli loistava reissu, Lomaproosa (alkuperäisten MERPG-suunnitelmien pohjalta alkanut proosaprojekti, joka vaikuttaa pahasti määrittelevän suunnitelmien kanssa pahasti ristiriidassa olevan kaanonin. Noh, jos Doctor Wholle se sallitaan, sallittaneen se minullekin) edistyi siellä melkoisesti, ja kun kotimatkalla keskustelin tuttavaperheen kanssa siitä kuinka "olin kirjoittanut kymmenisen tuhatta sanaa matkan aikana", minulta totuttuun tapaan kysyttiin että enkö ollut etelässä lomailemassa. Tietysti olin lomalla, ikäänkuin minulla olisi arkena aikaa tehdä mitään noin tuottavaa.

Tammi- ja helmikuu menivät matikkaa opiskellen. Syksyllä 2012 aloin siis lukea pitkää matikkaa aikuislukiossa, ja viime maaliskuussa kirjoitin sen kuuden hyväksytysti suoritetun kurssin voimin. Kuten odottaa saattoi, lopputulos oli että pääsin kokeesta läpi pisteellä \o/. En osaa (vai kehtaa?) sanoa mikä minut ajoi hosumaan tuon kirjoituksen kanssa, mutta sain siitä taas yhden tarinan kerrottavaksi.

Maaliskuussa kävin myös tutustumassa Porin rataan välillä Tampere-Vammala-Tampere. Valitettavasti siitä ei ole niin paljoa sanottavaa, joten vain näyttelen siitä kuvia

Huhtikuu oli MERPGin aikaa, siitä kerroin jo. Toukokuu meni elämästä nauttien, ja muistaakseni päätin toukokuussa saattaa ensimmäisen Lomaproosan loppuun. Kuukausi myöhemmin se oli lopussa, editoin sen .txt:stä takaisin .docx:ksi fiksuja muotiluja varten, ja tulostin Tässille kappaleen. Hän tahri kyseisen kappaleen punakynällä, palautti sen minulle, ja siitä asti on ollut työn alla korjailla kyseistä tekstiä tahrituista kohdista. Tuosta tapauksesta en oppinut paljoakaan kielestä tai kirjoittamisesta, mutta opin että minun on kirjoitettava Lomaproosa2 org-mode:n syntaksilla tai opeteltava käyttämään TeXiä. Word ja Libreofficen kirjoitussofta ovat aivan hirveitä työkaluja kirjoittamiseen.

Kesäkuussa, kesälomani aikaan, vasta innostuinkin tekemään MERPGiä. Huhtikuulta Google Coden repo näyttää 4 committia, toukokuulta ei yhtään, ja kesäkuulta sitten 15. Kesäkuuhun osui myös parhaat yökättelyajat Tässin saunalla, sekä retki Hankoon. Heinäkuun ensi viikolla kävin pohjoisessa, tutustumassa Rovaniemen kauniiseen kaupunkiin. Ainoa pärstäkirjasta löytynyt kuva on tässä. Näiden kahden tapahtuman väliin sijoittui Tuska + Rock the Beach, joissa todistin Wintersunin, Kotiteollisuuden, Rammsteinin, Amaranthen ja Nightwishin loistavia livevetoja. Tuskassa järjestettiin Nightwishiltä nimmarisessio, jonne keskivertokansa vei levykansia ja sellaisia signeerattavaksi. Minä vein Don Rosan Sammon Salaisuus - opuksen, ja sain palkkioksi niin hassuja hymähdyksiä, että sisäinen faniteinini on tyydytetty taas vuosiksi eteenpäin.

Elokuussa oli aika muuttaa Kotkaan. Ostin sinne uuden polkupyörän, jolla suunnittelin hiihtävänipolkevani kouluun kesät talvet. Suunnitelmassani olen myös pysynyt, lukuunottamatta yhteensä kolmea viikkoa edeltävältä syksyltä, joiden aikana joko ei kroppa kestänyt, ei pyörä kestänyt, tai oli typerä sää. Koulumatkani ei ole mikään kovin valtava, reilu 5km, ja maasto on todella helppoa asfalttia, joten jos tämä lumeton talvi on lumeton toukokuuhun asti, alan todella miettiä että oliko maastopyöräni vähän liioiteltu hankinta kaupunkimaastoon. Haaveilen tosin ensi kesänä kurittavani kyseistä fillaria Nuuksion metsissä, tai ehkä matkustavani sen kanssa pohjoiseen.

Vaikka yksiöni ikkunasta katsoen Kotka vaikuttaa rumalta betonihelvetiltä, polkupyörää luovasti soveltaen olen todistanut tämän käsityksen vääräksi. Junaharrastukseni on tosin näivettynyt, kun paikallisen henkilöliikenteen diversiteettiä ei voi verrata Pasilan ja Helsingin vastaaviin, mutta kantasataman ja rautatieaseman ratapihoilla pörräävät seeprat ja rantarataan verrattuna ääretönkertainen rahtiliikenne ovat pakottaneet kameran, joskin lumian eikä pokkarini, laulamaan. Pokkarikin on yhä olemassa, mutta se tuntuu olevan varattu suuria tapahtumia, kuten viimeisintä Turku-Uusikaupunki-Turku - lättäajelua, varten, ja tällaisiä ei kauhesti ole kiikarissa.

Lokakuussa oli ensimmäiset suuremmat tapahtumat koulupiireissä: NGS2013 ja GSGDC. Global Student Game Developer Competitionin idea oli hakkeroida yksinkertainen peli erään kuvan pohjalta. Tästä ei jäänyt käteen valmista peliä; muutama ajatus sen sijaan jäi:

  • Java (ja sen sisaret) on hirveä kieli jos projektin pitää valmistua seuraavan 365 päivän sisään, puhumattakaan seuraavasta 24 tunnista
  • Mergeen kykenevät hajautetut versionhallinnat ovat ehdottoman kriittisiä, eikä niitä voi korvata millään Dropboxin kaltaisella roskalla (vaikka Db onkin aika oleellinen jos yksi koodari työstää projektia useammalla koneella...)
  • Usean kokinkoodarin ympäristöt epäilyttävät minua. Pariohjelmoinnilla voi olla teoreettisia pohjia toimia, mutta liian monta samanaikaista koodaria saman projektin kimpussa kuulostaa vaikeammalta kuin monisäikeistys C++ssa.
  • Minun pitäisi tutustua Unityyn, vaikka se tarkoittaisikin C#:tä ja hiiren käyttöä.

Northern Game Summit taas... siellä oli esityksiä pelikehityksestä, pelistartuppien pitsauskisa (hirttäkää minut, finglishkriitikot \o/), ja verkostoitumista. Minä tykkäsin esityksistä, näin syyn startuppikisoille, ja vihasin verkostoitumista. Verkostoitumisella tarkoitan niitä juhlia siellä baarissa keskellä kajaania. Vaikka olisin edellisenä yönä herännytkin ihmisten aikaan, enkä jo viideltä, on meluisa baari typerä ympäristö. Lisäksi, ymmärsin että suurin osa joi viinaa/olutta, jotka maistuvat pahalta, jakelivat käyntikorttejaan, mille voin ehkä pitkin hampain myöntää olevan perusteita, ja harrastivat smalltalkkia, toisinsanoen puhuivat merkityksettömiä, mikä on typerää, koska se on merkityksetöntä. Lähtöni kyseiseen baariin oli ehkä hieman väärin laskelmoitu ajatus kokeilla tätä verkostoitumista, josta kaikki niin meuhkaavat, mutta pohjimmiltaan... se oli oikea valinta. Sillä jos olisin jäänyt hotellille koodaamaan, voisinko nyt kirjoittaa tätä kappaletta? :)

Suurin, mitä matkalta jäi käteen, oli vahvistus siitä että olen oikealla tiellä. Hakkeroituani MERPGiä ensin 7 tuntia bussissa Kotkasta Kajaaniin, ja seuraavana päivänä 7 tuntia Kajaanista Kotkaan, sain krapulaa kärsivältä toveriltani kommentin kuinka mie oon ihan hullu, kun ensinnäkin pystyn keskittymään siinä metelissä koodaukseen, toisennakin en käytä C++aa (joka kuulemma on tosi makee kieli) enkä Javaa, kolmantenakin koska ruudulla ollut testitilesetti oli niin hassun värinen.

Sitten tuli marraskuu. Tapahtuiko silloin muuta kuin Doctor Whon 50-vuotisjakso?

Joulukuu oli kiva. Alkukuusta stressi/epätoivo paistoi läpi, ja kirjoitin muutamankin tekstin purkaakseni pahaa oloani, mutta puolivälissä, kun koulu alkoi rauhoittua, ja ne vähät koulutuntini alkoivat puolenpäivän pintaan, ei minulla ollut mitään syytä mennä yhdeksältä nukkumaan, vaan sain työskenneltyä aamuyhteen kauneimmillaan. Seuraava tällainen jakso tulee uskoakseni aikaisintaan toukokuussa, mutta en usko tämän haittaavan, sillä luulen koulusta tulevan tänä keväänä mielenkiintoisia projekteja. Mielenkiintoiset projektit ovat kivoja syistä, joiden luulisi olevan ilmiselviä, mutta joista minun pitää varmaan jossain välissä kirjoittaa jotain.

Nyt, kun kaikki on sanottu, on aika nauraa, sillä mitään ei ole sanottu. Sanoa ei voi ellei koe, ja sen vuoksi odotan ensi vuotta innolla. MERPG näyttää ja tuntuu nyt paremmalta kuin vuonna 2011, jolloin viimeksi se on ollut tällä tasolla, ja minulla on taas jonkinlainen ajatus siitä mitä hittoa olen tekemässä. Pitäisi kerätä koulusta pieni tiimi tekemään grafiikkaa, kommentoimaan suunnitelmia, ja yleisesti olemaan tiimi, jonka kanssa pallotella näitä konsepteja. Tässi on loistava tiimiläinen, mutta kahden miehen, joista kummallakin on vain hämyisä käsitys mitä ollaan tekemässä, tiimi on melko pieni, vaikka jätettäisiin huomiotta Espoo - Kotka välimatkan suuruuden tuomat ongelmat.

Mitä odotan ensi vuodelta? En tiedä. Tähän mennessä minulla on ollut jokaiselta vuodelta jonkinlainen käsitys tulevasta, mutta nyt en todellakaan osaa sanoa, ja tämän voin tunnustaa hieman ahdistavan. Jos alkuvuonna saan MERPGin kartat viimeisteltyä (enää pitäisi optimoida piirtorutiineja niin ettei jokaista kartan layeriä piirretä minuuttia), sekä IO-rutiinit luotua, sekä räkäisen karttaprimitiiveihin siirtymävektorit kiinni, on minulla maaliskuuhun mennessä vanhan javakoodin kanssa feature-compatible versio, paljon kauniimmalla pelimoottorilla, joten jos aikataulu sallii, voisi minulla olla toukokuussa valmis pelinpuolikas placeholder-graffoilla. Tämän voisi sitten antaa graafisille tiimiläisille paranneltavaksi, ja alkaa itse kirjoittaa edes jollain tasolla lomaproosan kanssa yhteensopivaa toista puolikasta. Tällä demolla voisin sitten lähteä tavoittelemaan startuppikonseptia, josta kirjoitin aiemmin. Puhunko järkeä vai hulluja? Harva osaa sijoittaa puheitani näihin kasteihin, mutta normaalisti ainakin minä kykenen siihen. Nyt en, ja tämä hämmentää.

Joka tapauksessa, Feuer kiittää tästä vuodesta, ja toivoo seuraavalle vuodelle hämmennystä ja loistavuutta riittävästi jokaiselle. Kiitos, maailma.

Ducere me in lucem
Liberare me a tenebris, salva me

Alaviitteet

  1. Tämä blogi on internetissä, ja repertuaariini kuuluu keskimääräisen neron rooli. Laskenko siis itseni tähän netin kaikkitietävien joukkoon? Todellakin! Ainoa, mitä varmasti ja ehdottomasti tiedän, on etten tiedä mistään mitään, ja tämän vuoksi opin päivittäin paljon mielenkiintoisia asioita. Välillä huomaan myös että vaikken tiedä mistään mitään, moni muu tietää vielä vähemmän, ja moni muu vaikuttaa tietävän paljon enemmän, ja ihmettelen kumpi suunta olisi parempi, ja miten sinne pääsee.
  2. Typerät nimiavaruudet, työkalujen newbie-vihamielisyys, ja katkeransuloiset dynaaminen tyypitys, java-interop, sekä tilanhallinta, jos muutaman voin nimetä.

Sunday 29 December 2013

Ihmisen lento - tajunnanvirtaa viime toukokuulta

Löysinpä ~/Dropbox/blog/ - kansiosta toukokuussa kirjoitetun tajunnanvirran, joka jäi jostain syystä julkaisematta aikoinaan, mutta joka näytti niin loistavalta että julkaisen sen nyt iloksenna tässä:

Liian nopeasti aika voi viedä, siis lennä ja riennä. Mitä huomenna näet, vai näetkö mitään? Oletko huomenna keskellä ihmisiä, vai ylhäisessä yksinäisyydessäsi keskellä korpea? Ennen kuin huomaammekaan, olet kadonnut. Oletko mennyttä vai tulevaa? Vain hetki on jäljellä; kerro siis, mitä haluat, vai haluatko mitään? Poskesi ovat punaiset kuin taivas sininen; taivas, jonka pilville maalaat enkelin kasvoja. Joudunko yksin lähtemään? Kuka pelastaa, vai onko mustaa vaan? Kaadunko, jos en kirjoita kirjettä kotiin? Löydänkö koskaan kotiin, jos etsin tämän polun toisen pään? Odottaako polun tuolla puolen lapsuuden linna, vai merkityksettömyyden maja? Minne ovat mehiläiset kadonneet? Käteni on turvonnut mehiläisen pistosta, mutta en ole nähnyt hyönteisiä vuosiin. Olenko boheemi mohikaani, vai Hölmö toinen jalka suonsilmässä? Laukka, laukka, laukkaa ratsuni, jätä taaksemme mansikkamaat ja ruostuneet haat! Olen nuori mies, taskussani vain kultahampaat ja nauris, etsimässä onnea. Päädynkö australian aavikolle liikenneruuhkaan, vai hukunko Titanicin kanssa? Nuori nainen, selässänne on jotain! Hän palaa! Mistä hän tulee? Maailmankaikkeuden tuolta puolen, mutta minne hän on matkalla? Minne kukaan on matkalla? Utahiin, ylämaille. Alavilla mailla hallan vaara, vanha kansa lausui heinäkuun helteissä ruohomeressä sukellellessaan. Toisen niityille päivä ei paista, toisen rannalta ei järven tuolle puolen näy. Missä olen? Näenkö mitään? Mikä päivä on? Ketä nämä kaikki ihmiset ovat?

Tuesday 24 December 2013

HYVÄÄ JOULUA

Nyt lie sopiva hetki vuotuisalle postcountinkasvatuspostille? Kansalaiset, med börgare, todettakoon seuraavaa:  

LOISTAVAA JOULUA JOKAISELLE, JOKA TÄMÄN NÄKEE LUKEA

Joulutervehdyksiin varatut rahat, kuten edellisinäkin vuosina, sijoitan täysimääräisesti itseeni, ja MERPGin kehitykseen. Kohotettakoon jokaisella joulupäivällisellä malja (vaikka kokista) projektin terveydelle.

Sunday 15 December 2013

MERPG 0.3 readme.org

Since I forgot to put this file into the .zip I already threw to internet, and I can't bother to go through that experience again today, I'll dump an immutable version of this file here:

* Change-log
- Version 0.3
  + Added the drawing-dsl with basic keyboard polling functionality (http://yearofourlord.blogspot.fi/2013/11/planning-for-merpg-2d-library.html)
  + Added a basic demo to show what it can do
    Please note that the demo isn't mind-blowing. The code running that demo is.

- Version 0.2
  + Removed dependencies to old java-classes
  + Almost feature-compatible with the old memapper
  + First actually useable release in this branch
  + Old map-file - code discarded completely. I'll blog more about the planned map-file structure in somewhat near future.

- Version 0.1
  + A basic demo which experimented with writing the memapper in Clojure

* KNOWN ISSUES
- Rendering layers with opacity not either 255 or 0 in the editor is slow. Not tested if Processing/Quil is faster in this...
- Remove SELECTED tileset - button is broken, and pressing it probably kills the session irrecoverably
- Tilesets higher than 4*50 pixels probably won't fit in the screen. This happens with my Mac in resolution 1680 * 1050, and I haven't tested how the layout acts elsewhere and with other resolutions.
- You cant actually do anything useful with this release yet - since you can't save and load maps. In a next release I plan to write a system to (de-/)serialize the merpg.State - namespace (from/)to disk..
- The multiple-languageness of this project is ridiculous. That'll need some actions in a near future...

Friday 15 November 2013

Planning for Merpg-2D - library

Let me start by announcing stuff: I have moved my coding-projects and myself to a beautiful maritime city of Kotka (in August :P Better to announce late than never, 'eh?), and plan to graduate as a software engineer in a near future. To fight the oppressive blog-silence I shall publish a few words about MERPG

MERPG-context

A few days ago I released a second version of the Clojure-based MEMAPPER and had an idea to return to work with the engine (which, by the way, resides in the same google code - repository). Yeah, as if it would be that easy: I told a few months ago in this very blog that I had written an experimental engine on top of the Quil - 2D-graphics library. In tuesday I opened that experimentation again, only to find some ugly experimentation, most of which was removed immediately. Then I made sure I still knew how the library worked... and now I hope I wouldn't have :)

I don't have the willpower to deconstruct why I don't like Quil anymore. It just didn't flex to a mental model I had, and it has had some troubles with the REPL-based development. For example, the load-image - function returns nil always when called in REPL (something to do with single-threadedness - policy of most of the GUI toolkits?), and using it had some other nil-troubles. But then, I promised to not deconstruct this.

What I have willpower to do is design a graphics API I can live with, and wrap either Quil or Swing inside it.

How to make 2D-drawings?

Are there any requirements for this API? Oh yes. With Quil drawing to not-screen surfaces required hacking with the Processing - classes underneath (I think). The Quil API didn't provide (last summer, when I last looked) any way to achieve this. So, first requirement: we need to have two versions of all the functions. Other version takes the target surface as a parameter, other uses the default surface. Or... no, this seems rather nice space to apply dynamics scope. If you want to draw stuff into the default surface, you just call (draw-stuff x y & params), but if you want to draw into other surfaces, for example into an image, you bind *default-surface* into that surface and call the previous function.

(binding [*default-surface* in-memory-surface]
  (draw-stuff 300 400 32 11 12))
  

Binding - forms aren't beautiful in the not-library code. Luckily hiding them is easy. With a little macro-magic we can make the previous code look like this:

(draw-to-surface in-memory-surface
  (draw-stuff 300 400 32 11 12))

What are the stuff we should be able to draw with this library? Strings, primitives and images - for now. All of these could be abstracted behind a single Draw-protocol, so that one could write a rendering-procedure for an immutable map1

(let [tileset (image "/Users/Feuer/Dropbox/memapper/tileset.png")
      W 10
      H 10
      tileW 50
      map-data (merpg.State/get-current-map)
      map-surface (image (* W tileW) (* H tileW))]
  (draw-to-surface map-surface
                   (dotimes [layer (layer-count map-data)]
                     (dotimes [x (map-width layer)] ;;map-x - fns return count of tiles, not of pixels
                       (dotimes [y (map-height layer)]
                         (let [tile (tile-at map-data layer x y)]
                           (draw (subimage tileset (:x tile) (:y tile) tileW tileW)
                                 (* tileW x)
                                 (* tileW y))))))))

Or... a HUD-screen? 2

  (let [hud-data {:health 100
                :max-health 130
                :character-name "Varsieizan"
                :character-face-img "I'm an image, and I don't break if this string really is changed to an image \o/"}
      screen-width (width)] ;Width checks the width of the *default-surface*, but has overloads that take the surface as parameter for cases when draw-to-surface-macro would look stupid
  (draw (str (:health hud-data) "/" (:max-health hud-data)) 10 0) ;Draw should provide dimensionless overloads for when they are easily deduced
  (let [to-render (str "Playing: " (:character-name hud-data))
        text-width (width to-render)
        text-x (-> (/ screen-width 2)
                   (- (/ text-width 2)))]
    (draw to-render text-x 0))
  (draw (:character-face-img hud-data) (- screen-width (width (:character-face-img hud-data))) 0))
  

The beauty of the Clojure's dispatching system is this: draw-function can be written with Java's dispatching system (in other words, type of the second parameter (which represents the same entity as java's this-pointer) determines what function will be called), and with Clojure's extensions (namely extend-type) one can trick the type system to think that String - class has the draw-method. And when I've written the draw-method for for example the BufferedImage - class, and I've changed the :character-face-img - field to an actual image, the previous code still works.

What else?

In two previous code snippets I presented the (width) and (height) - functions, which are supposed to return (fun *default-surface*)3 if called without params, and the dimension of their parameter if such is provided. Then there is the image-function, which loads images if provided a string, and creates them if provided with 2 number params. Of course under all these small abstractions lies an important one too: the drawing queue which, I think, should be created per-frame. Sadly this'll mean either a one-frame policy or some peculiar way to handle in what frame a media should reside. Or maybe raw images reside outside the queues, and I'll write a distinction between these images (which have to be drawn separately in the game loop) and Objects (who live in the frame's drawing queue, have a location and support metadata).

So, functions to implement:

  • (image [path])
  • (image [w h])
  • (object [path/img x y angle])
  • (set-/get-angle [object])
  • (get-x/-y [object])
  • (set-position [object x y])
  • (move [object length])
  • (visible? [obj])
  • (set-visible [obj bool])

And what else? I'd say we need more graphics primitives than simple strings. We need also some kind of color-management. Maybe same sort of a per-frame *drawing-color*, and a with-color - macro to edit it? The primitives could be implemented as records, so that one could do this:

  (draw-to-surface img
                 (with-color "#FF0000" ;; With color delegates these values to... something in the java.awt - ns
                   (draw (Circle. :r 40) 40 40))
                 (with-color (java.awt.Color/BLUE)
                   (draw "Hello-world" 40 50))
                 (with-color {:r 244 :g 50 :b 177 :a 44}
                   (draw (Rect. :w 60 :h 10) 100 20)))
  

Footnotes

  1. This implementation is somewhat broken with the real code-base...
  2. In real life these would be functions, but the types of these values in these demos are somewhat important, and they wouldn't be visible in a defn-form.
  3. fun ∈ {width, height}

Saturday 10 August 2013

Duck-State management - or how I am unable to deal with state

People are afraid of typeless programming environments, and I might have become an example of why. The merpg.State - namespace of the MERPG-engine, written in Clojure (and available here, although the last commit pushed out of this Mac is at least a month old...), has become a vast, typeless trap for the unwary adventurer, if I may quote this excellent expression. How has this happened? How am I going to advance from here, now that I've accepted the situation? Well, let's find out!

I know you all know this, but let me revise the Clojure state-system here, since it's a prerequisite to understanding any of my blabbering in this text. The baseline is this: there are no typed vars, only (JVM-)typed values. Functions are allowed to take any kind of params, and return whatever they want. It seems rather identical to the type system of the Ruby, for example, which is an absolutely intriguing language, one which I hope to be able to make something with in the future. Anyway, this type(less)-system you're accustomed to in other Lisps or Ruby has one major difference compared to the just-mentioned languages: there are no destructive operations. You can't re-set the the variable (without resorting to functional hacks), and if you have a vector [2 3 4 5], you can be certain no one is going to cons stuff into it, since, you know, it is impossible to do so. (assoc that-vector 0 10) returns previously mentioned vector with the first element set to 10 instead of 2, and leaves the original untouched. In some hypothetical language following Ruby's syntax and Clojure's naming semantics, one would use .add!(elem) - method to add stuff to a list, exclamation marking the fact that this method probably returns void or the object upon which it is called, and instead edits the list in place. However, there are no exclamation-point-functions in Clojure (well, there are, but you don't need to know it :), so there are no in-place-edits on data structures, so data-structures are really immutable, and as a side effect we have a thread-safe system, and the coder has to write some little elegance to work around the limits immutable values present.

Remember when I told you there's no way to assign new values to variables? Well, I lied! There are a couple of wrappers that enable state management in a functional, thread-safe way. There are refs, which have to be used inside transaction (and in case system blows up inside the transaction, every ref already set inside that transaction is returned to the original value), with functions whose use is explained lousily in every doc I've been able to find. Then there are agents, which I have also not used in my codes, so I may be talking rubbish here, but if I recall, they present same interface for setting their value as atoms, and their new values are calculated in new threads. And then, my favourite, atoms: swapping their value is done with (swap!) - function. It takes the atom-to-be-set and a function taking the old value as a param and returning the new value as the parameters. Not counting that this is an atomic operation, rest of the system won't see "intermediate" values, the elegance of this systems stems from the fact you can... well... calculate the new value with any function. Newbies could increment an integer-atom with (swap! atom #(+ 1 %)), when in fact the built-in (inc) is basically the same as #(+ 1 %).

(At least) refs and atoms have a couple of cool features: validators, which are what java's type system wants to be once it has grown up. If you swap! something irresistibly stupid into an atom that has a validator designed to catch that kind of values, the validator throws an exception. Another cool feature is: watches, which can be used to raise warnings in case where validators raise errors, but there are other use cases. Watches are really what WPF's INotifyPropertyChanged-databindingsystem wants to be once it grows up. Once someone swap!s new values into the state, watches get called, and inside these watches we rebuild the GUI (a hairy area we're not going to talk about, although Seesaw makes GUI-stuff rather bearable compared to Swing, Winforms and WPF).

Then there's the really hairy stuff known as the java-interop, but we don't want to go there, do we?

Hopefully I have built up enough base on which to tell you about my lousy state-management

The problems

Because of the Clojure's duck-typing, there's no need to bother what gets sent to the map-editing functions. You need to have a vague idea of what is a sequence, what is a sequence of sequences and what is a basic tile. It is the same syntax for every case. But if we remember we're analysing the State - namespace, we know there has to be some atoms, and atoms require special syntax. Special syntax is baaad :) Well, no, since having to (deref) stuff would be worse. Thus user-definable reader macros would be nice, but that's stuff for another rant.

Although special syntax of atoms isn't necessarily bad, leaking it outside State-ns is. So first tenet of the refactored State-ns is: no leaked atoms. If the coder requires access to atoms, tough luck: use either setters for watches or getters, which return the derefed value.

And of what will these atoms consist of? Well, tiles, which are easy. They are just simple maps: {:x :y :tileset :rotation}. Then there are the layer-generators, from which you may take layers by (take Width (layerrow W H Name))ing. Thusly layers look like this: {:title name :transparency transparency :layer [[tile tile...][tile tile...][tile tile...]...]}. In case one has to change tiles in this structure, which would seem hard considering this map is immutable, one could use code like this:

 (let [layer (:layer %)]
 (assoc layer x
        (assoc (nth layer x) y new-tile)))
      
although one should be wary of layer being a lazy-seq and thus M-% layer RET (vec layer) RET. This code first destructures the layer for us, then updates xth row to be thing, that's returned after updating yth tile in the xth row to new tile. The syntax may seem weird to a C-world dweller, but how would one define such a construct in C? Well, layer[x][y] = newtile; is easier to write, but the cases where C-syntax is better than Lispy lack-of-such are rare, and even this lacks in A) the thread-safety B) Watch-department

Layer defines apis for setting title (which is going to be awesome if I'm forbidding the leaking of the atoms \o/. But as Paul Graham said, in dynamic world one can write code as if sketching with a pencil, instead of sculpting granite), transparency, tiles (according to the previous code snippet). We also need getters for all the previous stuff to prevent leaking the atoms, but they sure-as-hell are not going to be named javaesquely getX. I think we need add-watches for the :layer - atoms too.

The root-level atoms are the maps. Maps are basic, ordered vectors (pun very-much intended :) of layers. Structure looks something like this: [{:title name :transparency transparency :layer [layer]} {:title name :transparency transparency :layer [layer]}]. This structure defines api for adding new layer, adding watch for new layers, getting layer per index and moving layers up or down.

Then there is the great root: map-set. Although the name suggests otherwise, I'll probably implement this as a vector of maps. One can add- and remove maps, and although this is logically unordered set, I'll write ordering semantics for the editor. The problem in this implementation: how am I going to represent the relationships of maps (also known as "displacement vectors" in the old code, meaning the coordinates ({:map :x :y}) where players will be transferred to other map ({:index-of-another-map :x :y}). Well... maybe the mapset looks like this: {:displacement-coordinates [[{:map :x :y}{:map :x :y}][another-two-tuple]] :maps [map another-map third-map etc.]}.

And as already mentioned twice (or thrice? I'll repeat it once more, just to remind myself of it): Atoms are not to be leaked outside the State-ns! A (deref)-call, @-char and (add-watch)-call all equal pure fail. Not a procedural fail, not an OO-fail, but a pure, functional fail.

And with these words, I hereby conclude this text, publish it without any cleanup, and C-x b myself back to the State.clj

Sunday 23 June 2013

ME-non-MAPPER

Johdanto

Tjaha, kirjoitinkin tämän englanniksi huomaamattani. No, ei voi mitään, ˜1000 sanaa MERPGistä, toivottavasti joku tykkää :P

Idle but integrated thoughts

As I have written earlier (have I?), I am integrating MEMAPPER and MERPG-engine more tightly than probably is good for anyone's health. While running the engine, which has loaded the map set of test environment, one could press a well-documented key-shortcut, which fires up the map editor with the current map-set and current map. After doing whatever you were doing for the map, you press a button (or another shortcut) that sends the map back to the engine, which shows the thing "in the real world" and serializes it unto the disk.

Sooner or later I'll have to write a REPL-interface for actually creating the new maps, but since currently there's no real engine (only a Quil-based experimentation) the editor is a standalone program, with which you create new maps by restarting the thing. The unresetable nature of the program is an unbelievable pain in the arse when trying to do incremental development in the REPL, but tough luck, the alternative is to rewrite the old-as-hell state management in Clojure instead of Java... Which reminds me, if someone knows who to cheat leiningen to compile first (gen-class)'d clojure classes, then :java-sources, then the rest, feel free to answer my question in Javaranch.

Today while cycling to our local beach I kept thinking this mapeditor-engine - project which I have seriously developed for the last week (see the changelog's timestamps, Pröng's development seems to be postponed to the indefinite future...). Editor and engine sharing a map-set atom is a cool concept, but can this concept be extended? Could the user press one keyboard shortcut and enter the animation editor (Tässi? What kind of animations are we using? Some colourless gif-monster or png-series of images?), one to enter the NPC-editor, one to enter the Quest editor, one to enter the character-editor, one to enter the... whatever-editor?

Of course most of these are natural extensions of the map-editor. For example the NPC-editor would present an UI (for those uneducated pagans not fond of Emacs) for controlling enemy-spawning and scripting the artificial intelligence of the enemies and allies on the current map. I have no idea how the NPC:s are supposed to be scripted, but I think when the time comes, I'll have some sort of Clojure-based DSL ready. Character-editor will present tools to script your allies (remember: you have all the time two NPC-friends playing alongside you, and you can play any of them) on either global and per-map scale. One can also edit both the starting values and the values present in the engine's copy of the ally-atoms. Character-editor will also be able to assign animations to the all of the game's character classes in the game, and it'll maybe allow creating the whole new classes. We'll see, this system is meant to be open for poking, and I have grown fond of the Lispy idea of having unbelievably small kernel to build the things around, so probably all the game's character classes will be implemented through this system instead of hardcoding them.

Before I forget, a very important property of all the character classes is: singleton. This property ensures, as all those who have read their GoFs know, that there is only one concrete character of this class ever present. All the allies (err, main characters) will have this flag set to true, although this could present problems with some characters if taken to extremes :P

Quest-editor sounds also nice. Quest-data structure should probably have a :completed? - function, which is called every time player does something that could end a quest... or no. It is maybe nicer to make possible for adding a "Quest ready" - zone into maps. When the player wanders into the zones, all active quests are queried for their :completed? - function, and in case there's any, they are marked as such and archived. Unfortunately this approach will have some issues down the road, for example: who is going to host all the resources needed for specific quests. (@fatima-fought :completed?) - function will need to know the current HP of the Tutima-Fatima - singleton-character, but if one is nowhere near the fatima-fight (for example where? I have no idea! Let's just declare this situation hypothetical, so I don't have to open up the spec and begin searching for a needle in a haystack), it can't go loading the character from the filesystem every time player takes a step that ends up to the Zone.

Because RAM is cheap nowadays (and the JVM doesn't hoard it anyway, if you didn't catch the joke), I'll probably begin with an implementation that has instance of every singleton-character in the memory all the time. Their AI is just killed and they are not drawn when they are not needed, so there is no slow io required accessing them. They are maybe serialized to the same object as the set-of-maps. Or maybe they deserve their own slot in the global space, which unfortunately would require a whole new filetype next to the .MEMAP.

And this whole thing has to be documented somehow. For the relevant parts I'll follow the Quil's way of having a couple of functions listing all the relevant stuff, and telling everyone to (doc 'them-symbols). Opening up the doc-problem however would need twice this long text, so we shall not go there today.

tl:dr

The memapper (downloadable here) will be integrated to the engine, and will get a whole lot of functionality, which maybe disqualifies it to be a map editor, but it will still be cool. Let's hope I'll be able to write this "coolness" I feel into this software :P

Saturday 1 June 2013

Mallintamista ja PHP:tä

Tämä teksti alkoi mallinnuksen pohdintana, mutta päätyi abstraktiksi suunnitelmaksi Pröngin tulevaisuudesta. Nauttikaa kohtuudella, ja muistakaa olla olettamatta että tämän tekstin olevan faktaa. Tämä on hakemista; minulla on abstrakti aavistus tekstistäni, jonka muotoa yritän hakea paperille, ja joskus onnistun siinä hyvin, joskus huonosti. Tämä vaikuttaa, ainakin vielä Emacsista katsottuna, ensimmäiseen kategoriaan kuuluvalta.

Mallintaminen on uskomattoman vaikeaa. Harjaantumattomalle silmälle se voi näyttää helpolta, mutta ei. Turha toivo. Osa mallintamisen haasteista perustuu siihen, että kaikki ohjelmoidessa kohdattavat ongelmat voidaan jalostaa pienemmiksi ja pienemmiksi, kunnes omataan enää mallinnusongelmia. Yritätkö kirjoittaa N-ulotteista peliä? Noh, algoritmisi riippuvat siitä miten mallinnat pelihahmot, niiden liikkeet, niiden piirtämiset ja sellaiset. Yritätkö kirjoittaa nettisivua, johon tallennat blogitekstejä, kuvia, tai, noh, luonnoksia? Silkkaa mallintamista! En ole kohdannut Pröngin kanssa ongelmaa, joka ei olisi ratkennut muuttamalla mallinnuksessa käytettyä lähestymistapaa.

Tämän tekstin lukijoille suositellaan Universaaliin Designpatterniin tutustumista, ja Helloworldiä jollain Lispillä. Käytännössä kuitenkin riittää että ymmärtää Javan ja PHP:n oliomallit riittävän syvällä tasolla.

Miltä näyttää mallinnusongelma?

Tällaisen tunnistaa jokainen, joka joskus on luokkapohjaisella kielellä ohjelmoinut. Itse asiassa olen ilmeisesti lähestynyt aihetta aiemminkin. Teksti on luonnollisesti roskaa, mutta kun oikein siristelee silmiään, teksti puhuu ihan asiaa. Toteuttaakko (suorituskyvyltään ja laajennettavuudeltaan naurettavan \o/) skriptimoottorin suorittava osa iteroimalla rivien ensimmäisiä avainsanoja tuhannen rivin switch-hirviötä vasten, vai rakentaakko jokaista avainsanaa varten oma luokkansa, ja hakeakko vaikka reflektiolla avainsanan perusteella oikean luokan instanssi, jota vasten kutsutaan polymorfista execute() - metodia? Vai käyttääkkö kieltä, joka

  1. Tuntee Closure-käsitteen
  2. Omaa siistin syntaksin closureille
, ja laittaakko näitä hashmappiin keyword-string => closure - järjestykseen, jolloin "polymorfisen execute() - metodin" haku on vakio-mittainen operaatio (en kyllä tiedä mitään Javan ja C#:n reflektioiden big-O - laadusta, joten en tiedä onko tämä suorituskykyisempää kuin luokkien hakeminen niiden kautta...), ja saadaan yhä kaikki luokkien edut (toisinsanoen saadaan funktion ympärille tila) ilman kankeaa syntaksia.

Tältä, hyvät ystävät, näyttää mallinnusongelma. Kerroin kolma tapaa mallintaa skriptimoottorin runtimeä: proseduaalinen switch-helvetti (enpä tiedä ratkaisisivatko edes C:n ja Pascalin kaltaisiin juuttuneet tätä ongelmaa näin), luokkaperäinen toteutus (jota voisi odottaa mm. Java-, C#-, ja ehkä PHP-ohjelmoijiltakin?), ja funktionaalisuutta havitteleva closure-ratkaisu, jota Lisp-väki soveltaisi oikopäätä.

Aivan yksinkertaista

Onko? Otetaan esimerkki: Pröng. Pröngissä on luonnoksia, joilla on paljon erilaisia ominaisuuksia:

  • Teksti
  • Lähettäjä
  • Lähetysaika
  • Kommentit
  • Tagit
Tämä on jo monimutkainen rakenne. Oletetaan ettei kukaan tekisi omaa taulukkoaan jokaiselle näistä ominaisuuksista, ja määrittäisi yksittäistä oliota indeksiksi näihin viiteen taulukkoon, vaikka se voisikin tehdä relaatimalliin mappauksesta ehkä jopa helppoa. Ei, nykyaikainen koodari, joka on opiskellut ketteriä OO-metodologioita koko pienen ikänsä, mallintaa tämän hetkessä java-luokkana:

public class Luonnos
{
    private String Teksti;
    public String getTeksti()
    {
 return Teksti;
    }

    public void setTeksti(String value)
    {
 Teksti = value;
    }

    private String Lähettäjä;
    public String getLähettäjä()
    {
 return Lähettäjä;
    }

    public void setLähettäjä(String value)
    {
 Lähettäjä = value;
    }

    private String Lähetysaika;
    public String getLähetysaika()
    {
 return Lähetysaika;
    }

    public void setLähetysaika(String value)
    {
 Lähetysaika = value;
    }

    private List<String> Kommentit;
    public List<String> getKommentit()
    {
 return Kommentit;
    }

    private List<String> Tagit;
    public List<String> getTagit()
    {
 return Tagit;
    }
}

Jätän vielä huomiotta tietokannan. Oikeassa elämässä tämä perisi jonkin DAO-luokan, joka generoi SQL:n tallentamiseen, päivittämiseen ja hakemiseen. Tämä on perinteinen, enkapsulointia suojeleva tapa mallintaa dataolioita. C#ssä get- ja set-metodit korvattaisiin {get;set;} - loitsuilla julkisten kenttien perässä. PHPllä luokka mallinnettaisiin täysin samoin kuin Javalla. Ilmeisesti PHP 5.4n myötä PHP:n setterit ja getterit voisi korvata C#n kaltaisilla {get;set;} - kaksikoille.

Miten tämä mallinnettaisiin Clojurella? Hyvä että kysyitte!

  (defn new-luonnos [Teksti Lähettäjä Lähetysaika Kommentit Tagit]
     {:teksti Teksti :lähettäjä Lähettäjä :lähetysaika :kommentit Kommentit :tagit Tagit})

Sanokaa minua höpsöksi, mutta Clojuren tulkinta, josta näytin varsin verboosin esimerkin, on paljon mukavampi (toisinsanoen lyhyempi) kirjoitettava. Toki se rikkoo enkapsuloinnin, mutta Clojuren kanssa sillä ei ole väliä, koska mapin arvoja ei voi lisätä eikä muuttaa. Vaikka Clojuren java.util.Map - toteutuksen .put() käyttäytyisikin samoin kuin java.util.HashMap.put(), ei enkapsuloinnin puuttumisella välttämättä olisi väliä. Dokumentaatio on tärkeää.

Pröng

Otetaan tuo rakkain sivustoni lähempään tarkasteluun. Mainitsin aiemmin että PHPllä dataluokat voidaan mallintaa Javan tavoin, mutta oikeasti, miksi ihmeessä kukaan haluaisi tehdä näin? Yritä tehdä SQL:ää generoiva kerros javamaisille luokille, joissa luokkaskeemoissa on listattu jokainen property, ja niille erikseen setterit ja getterit. Joku reflektio-apina löytyy myös PHP:stä, mutta Java jätti ainakin minulle niin kovat traumat, etten edes tutkinut tätä mahdollisuutta. Sen sijaan, PHP tarjoaa joukon taikametodeja, joista tämän keskustelun kontekstissa kiintoisat ovat __get ja __set.

Mutta eivätkö nämä metodit hajota tyyppiturvallisuuden? Totta kai! Ne myös tarjoavat todella yksinkertaisen do-it-yourself - reflektion, jos __set()in asettaa asettamaan arvonsa mappiin, ja __getin asettaisi palauttamaan arvonsa tästä mapista. Tästä voi sitten generoida helposti SQL:t kaikkiin tarpeisiin... Jos niin haluat. Persistenssin datan mallinnus nimittäin monimutkaistaa yleisiä mallinnusongelmia todella pahasti. Yleensä persistenssillä datalla tarkoitetaan relaatiokantaan tallennettavaa dataa, mutta on olemassa myös ratkaisuja, jotka ottavat sisäänsä JSONää, XMLää ja jotain täysin omia ratkaisuja. Koska esimerkkinämme on Pröng, jonka persistenssimetodit ovat MySQL ja tiedostojärjestelmä, oletettakoon tämän teksin ajan ettei NoSQLää ole olemassa, ja etten ole siirtymässä viuhkasta pois lähiaikoina.

Tärkeä fakta, jonka jokainen harrastelija ja ammattilainen oppii, yleensä vaikeimman kautta: O/R-mappaus on vaikeaa. Alkuun kokeileva koodari kirjoittaa haku- ja syöttöfunktiot käsin (hauskaa! ensimmäisen viuhka-pröngin käsinkirjoitettu datakerros vei suhteelliselta rivimäärältään eniten tilaa koodipohjasta, ja oli täynnä SQL-reikiä, koska kukaan ei ollut kertonut hra koodarille PDOsta...), myöhemmin hän kuulee ORM-kirjastoista (Hibernate, EF, "ne mitä PHP:lle onkaan kirjoitettu"), ja kokeiltuaan niitä, hän toteaa näiden olevan vaikeita käyttää: kirjoitettakoon oma! Ei liene vaikea päätellä tuliko tästä omasta ORMista helppokäyttöinen, tai edes ongelmaton.

Miksi olioiden ja relaatiomallin välinen mappaus on niin vaikeaa? Ei se olekaan, jos luokka sisältää vain primitiivityyppisiä propertyjä, mutta jo listan mallinnus vaatii relaatioympäristössä säätämistä. Listalle pitää luoda oma taulunsa, johon asetetaan vierasavaimena tieto siitä mikä alkuperäisen taulun rivi nämä rivit omistaakaan. Muinaiset hakkerijumalat sinua auttakoon, jos haluat tallentaa oliopropertyjä kantaan.

Sivuhuomiona haluan sanoa, että uskoisin näiden ongelmien olevan paljon helpompia ratkottavia Clojurella, koska SQL on siellä ensiluokkainen kansalainen, joten SQL-stringien ja geneeristen prepared statementtien täyttäjien sijaan tietokantakoodin voi generoida ihan lispinä, niinkuin kaiken muunkin. En kuitenkaan ole vielä tehnyt tietokantakoodia, saati nettisivuja, Clojurella, joten jätän nämä uskonasioiksi vielä.

Mikä ratkaisuksi persistenssiin?

En minä tiedä! Helpointa olisi sijoittaa muutama lantti ympäristöön, joka oikeasti tukee Clojurea, ja johon voi asentaa minkä tietokannan haluaa, mutta sellaiset ympäristöt eivät ole halpoja, enkä tiedä onko tuotoiltaan olematonta sivustoa järkeä siirtää sellaiseen ympäristöön. Jos viuhkan tietokanta olisi SQL Server, voisi luonnoksia ja muita olioita tallentaa XML:nä sinne. MySQL ei ymmärtääkseni anna upottaa XPathia/XQueryä kyselyihin, toisinkuin SQL Server, ja tämä helpottaisi kyselyiden tekoa relaatiokantaan upotettuja XML-sarakkeita vasten.

Vai helpottaisiko?

Emmekö juuri todenneet O/R - mappauksen olevan vaikeaa? Miksi siis O <-> R <-> X - mappaus olisi yhtään helpompaa!?

Voisin kuitenkin väittää O/X-mappauksen olevan suhteellisen helppoa, niin kauan kuin XML:n ulkoasulle ei ole keinotekoisia rajoitteita. Yllättäen en ole tehnyt tätä PHPlla laisinkaan (okei, Pröngin XML-toteutukset ovat teoriassa olleet OX-mappausta, koska ne ovat olleet stringien katenointiin perustuvaa purkkaa, niitä ei lasketa), mutta Javalla olioista saa aika helposti XML:ää, ja C#llä vaaditaan 10 riviä jotta jokaiselle oliolle saadaan täysin geneerinen XMLSerialize() ja Stringeille XMLDeserialize<T>(). Jos Pröngiin tekisi tiedostopohjaisen "säilön" luonnoksille, ja tarpeen tullen laajentaisi tätä keskustelun pohjalle? Tai jos ensin saisi edes tagien mukaisen ryhmittelyn toimimaan kunnolla...

Mallinnuksesta kun tuli puhe, haluatteko kuulla miten mbnetin, jossa ei tosiaan ole tietokantaa saatavilla, aikaisessa Pröngissä luonnokset tallennettiin? XML:nä tiedostoon? Ha! Luonnokset löytyivät tiedostosta ./Luonnokset/$lid.php, jonne ne tallennettiin formaattiin:

    <?php
      $luonnos = "Luonnos";
      $lähettäjä = "Feuer";
      $lähetysaika = "01-06-2013";
    ?>
    
Näitä tiedostoja sitten include()tettiin luupin sisällä, ja työstettiin selaimelle, tai johonkin.

Joten mitä Pröngille tapahtuu

Pröngin persistenssiongelmat pitäisi vähitellen ratkaista. Marraskuusta käytössä ollut DAO oli hauska rakentaa, mutta suorituskyvyltään se oli niin urpo, että siihen piti rakentaa purkasta kakku ympärille, jottei tarvitsisi käydä tietokannassa kuin kerran per sivulataus. Pystyttänen macille jonkinlaisen kehitysympäristön, jossa testata tämän teksin konsepteja, ja päivittynyt koodi valuu viuhkaan kun ehtii.

Tuesday 21 May 2013

Tylsä olen-elossa – päivitys

Tämä teksti on roskaa, mutta koska siinä on tärkeä ajatus MERPGistä, en hukuta tätä pienessä hiprakassa muodostettua tekstinketaletta. Lisäksi tein joskus puoli vuotta sitten tyhmän lupauksen yrittää kirjoitella jotain säännöllisen välillä, ja tällä tahdilla minulla ei ole varaa hylätä yhtään tekstiä :P

Mistä puhutaan? Keskustellaanko binäärioperaattorien käänteisfunktioiden partiaalisesti sovelletuista versioista? Ai ei? Hyvä, sillä en minäkään osaa F#ää/OCamlia. Clojuressa toisaalta näitä voi soveltaa, mutta enpä tiedä onko siitä mitään hyötyä. Clojure on kyllä mielenkiintoinen kieli, jonka ominaisuuksia kaipaa pieni ihminen ohjelmoidessaan C#:llä/Javalla. Let on uskomattoman kiva sana. Sillä saa kivasti järjesteltyä muuttujamäärittelyt lohkon ylälaitaan, ja oikeassa elämässä ihmisen ei edes tarvitse määritellä muuttujia keskellä lohkoa. Tarvitseeko ihmisen edes määritellä muuttujia? Riittäisivätkö vakiot? UI-paradigma lie todella haastava toteuttaa muuttumattomilla datastruktuureilla, mutta toisaalta GUI on roskaa. Graafisin UI, jonka vakavaan työntekoon tarvitsee, on Emacs, joka ei toteuta päällekkäistä ikkunointia, mikä on oikeastaan loistava. Jos ikkunat eivät voi mennä päällekäin, ei voi tulla tilannetta, jossa joku itsekäs ikkuna, joka luulee olevansa tärkeä, peittää jonkin oikeasti tärkeän akkunan.

Toisaalta jonkin pelimaailman toteutus muuttumattomilla voisi olla jännää. Toisaalta, tuhannen pikselin matkan siirtyminen generoi tuhat (decimal * decimal * decimal) - mappia, mikä saattaa performanssifriikkiä ottaa sydämestä, mutta toisaalta sitä varten meillä on GC. Jotain tuonkaltaista olen toteuttamassa MERPGiin, jonka toteutusalustaksi on määritelty Clojure. Clojuren dynaaminen maailma mahdollistaa maailmojen kehittämisen lennossa (voi hitto! Täytyykö minun toteuttaa interaktiivinen karttaeditori? (Toisaalta, miksei?)), yksinkertaisen laajentamisen (toisaalta, mikä vaan mikä omistaa REPLin ja/tai evalin, on helposti laajennettava) kolmansille osapuolille, ja jonkinlaista fiksuutta kehittämiseen. Common Lispin piirtokirjastoista en (olemattomalla) etsinnällä löytänyt mitään kiehtovaa, C/C++/C#/Java - kvartetin muodostama staattisten kielten joukko on... noh, staattinen, mikä on MERPGIN kontekstissa synonyymi roskalle. Staattisuus pysäyttää maailman.

Makrot ovat kivoja. Niillä saa kieltä taivuteltua kohti ongelmaa (sen sijaan että taivuttaa ongelmaa kohti kieltä, niinkuin on tehtävä edellämainitun kvartetin kanssa). Idyllinen tapa toteuttaa MERPGin koneohjatut pelaajat olisi defcharacter-makro, jolle määriteltäisiin reaktiot ja initial statsit kivalla, lispihtävällä kielellä, jota ei kuitenkaan voi suorittaa suorittamatta defcharacterin määritystä. Otetaanpa esimerkki:

(defcharacter Varsieizan
  :type :flying
  :attack 500 ;;He's the the master bird
  :defense 600 ;;And has the heaviest armor in the whole game
  :speed 700 ;;But damn he is fast
  :reactions
  [
   (ground
    (use-moves-type :flying))
   (thunder ;;Does the game have thundery opponents, or am I thinking pokemon?
    (do-defense)
    (if-opponent-does-stupid
     (use-moves-type :flying)))
   (rest
    (attack (move-with-greatest :attack)))])
      

Tuon kaltaisilla makroilla voisi olla kiva ylläpitää globaalia tilaa... tai sitten ei. Koska Clojure, tuon voisi siististi myös muuttaa funktioksi, joka palauttaa characterin. Common Lisp vaatisi [vector syntaksin] muuttamisen makroversiossa (lista syntaksiksi), ja funktioversiossa '(suorittamattomaksi lista syntaksiksi), ainakin tietääkseni. Joka tapauksessa globaalista tilasta voi tulla jännä toteuttaa. Kartan hahmot piilotetaan karttaan, ja aidosti globaalit hahmot (ie. Tässi & co) pidetään jossain ref'ssä (vai atomissa? Clojuren tilanhallinta on niin perverssiä, että sen unohtaa kun ei käytä kieltä viikkoon...).

Mitäs muuta? Animaatoori on yhä harkinnan alla, vaikka Tässi on ilmeisesti harkitsemassa GIF-animaattoria, jolla voisi piirrellä iPadillaan. Minä olen valmis kirjoittamaan MERPG-moottorin grafiikan Quil-kirjastolla, joka saattaa taipua myös MERPG2:n kolmiulotteiseksi speksattuun maailmaan. Mahdollisesti, sillä yritin yksi ilta leikkiä Quilin kolmiulotteisilla omituisuuksilla, jolloin kävi ilmi että Macistä puuttui jokin OpenGL-riippuvuus. Jos Quilin 3D:n säätämisestä tulee haastavaa, hylännen sen kuitenkin herkästi, koska samalla vaivalla säätää Windows-koneelle gcc:n, ja siihen DirectX SDK:n, tai säätää gcc:n (vai clangin?) Maciin ja opiskelee käyttämään OGLää.

Hyötä :P

Monday 25 February 2013

Testi

public class testi
{
  public static void main(String[] argh)
  {
    System.out.println("Toimiiko koodinvärjäys?");
  }
}
(defun hello-world ()
   (insert "Entäs tämä?"))

Wednesday 20 February 2013

BB: Metaa!

Blogibloggi

Minua ärsyttää ja harmittaa tämä blogi. Tämän postausvauhti on moninkertaistunut negatiivisesti vuosi vuodelta, ja mitä minä olen tehnyt? En ainakaan korvannut määrää riittävällä laadulla, vaikka tästä tasosta olenkin tullut roimasti ylöspäin. Etenkin 2010 aloittamani uudenvuoden kertomukset ovat edustaneet tekstieni yläpäätä. Niissä on sisältöä (ainakin sanamäärällä tarkasteltuna, vaikka sanamäärä onkin sisällöstä yhtä hyvä mittari kuin rivimäärä ohjelmakoodin laadusta), niissä on yritetty välttää juttuja, joita en edes itse tajua vuotta myöhemmin, ja niissä on yritetty olla koherentteja alusta loppuun. Haastavan kuuloista, eikö :)

Blogin pölyjä pitää siis pyyhkiä, ennenkuin raukka aivan tukehtuu. Miten tämä prosessi oli suotavaa toteuttaa? Blogin kirjoitetun formaatin huomioonottaen voisin ehdottaa kirjoittamista. Silmitöntä kirjoittamista. Nyt olisi hyvä hetki sulkea suodatin, joka näyttää minulle jokaisen tekstin huonot puolet, ja on näin jo tappanut ensimmäisen bb-tekstini, jonka toinen versio tämä on. No, mistä kirjoittaisin? Kirjoittamisaiheita ei roiku ihan joka oksalla, jos ymmärrät mitä tarkoitan, kuulisin ihmisten sanovan jos en soittaisi Epicaa rauhallisena taustamusiikkina. Vaikka aiheita roikkuisikin puiden oksilla, autolla- ja junalla taitettavilla työmatkoilla keskinopeus nousee niin suureksi ettei niitä ehdi tarkastella lähemmin, puhumattakaan siitä että ottaisi kotiin mukaan.

Niin, pölynpyyhintätekstit, joita ei kannata ottaa kovin tunteella, ja joiden takana en välttämättä enää julkaisun jälkeen seiso, merkitsen nimellä blogibloggi. Luovaa. Arvatkaa keneltä sain idean tähän.

Kirjoittaminen ei ole helppoa. Jotta se helpoittuisi, siinä on tultava taitavammaksi, mikä vaatii että kirjoittaa, mutta kuten äsken yritin selittää, tämä on loputon rekursio: ongelmana kirjoittamisen haastavuus, ratkaisuna on kirjoittamisen harjoittelu kirjoittamalla, mutta sen ongelmana on kirjoittamisen haastavuus, mikä ratkeaisi harjoittelemalla kirjoittelua kirjoittamalla, mutta se palauttaisi meidät lähtötilanteeseen niin monta kertaa, että kohta kutsupinostamme loppuu tila. Ongelma on siis vaikea, koska rekursion pohjaehto ei tule koskaan todeksi, ellei tilannetta muuta tavalla, joka on reaalimaailmassa mahdotonta, ohjelmoijalle haastavaa, mutta kirjoittajalle todella helppoa: kierretään ongelman ympäri, ja ratkaistaan se välttelemällä sen katsetta niin kauan, että se kuolee huomionpuutteeseen. Vähemmän abstraktilla tasolla selitettynä, kirjoitetaan jostain niin helposta aiheesta, että jopa ei-ohjelmoija osaa siitä kirjoittaa: METAA!

Metallahan tarkoitan tietysti metabloggausta, ja tässä tekstissä yleisestä kirjoittelusta kirjoittelua. Muuan Atwood1 kerran kirjoitti metablogailusta (Puhun listan 10. (ja tämän tekstin tapauksessa myös 9.) jäsenestä) melko hyvin:

Meta-blogging is like masturbating. Everyone does it, and there's nothing wrong with it. But writers who regularly get out a little to explore other topics will be healthier, happier, and ultimately more interesting to be around – regardless of audience.
Olkoon tämä teksti siis hetkeni metaforiseen itsetyydytykseen blogini parrasvalossa.

Kirjoittelua ja blogaamista

Kerran lausuin hyvälle ystävälleni Tässille että "Se on mun blogi, ja mä voin tehdä sillä mitä haluan", minkä lopputuloksena oli muuan otsikko yool-blogiin. Aivan, otsikko, se ei ollut teksti. Sen leipätekstin sanamäärä oli pienuudessaan henkilökohtainen ennätykseni. Leipätekstissä merkkejä kaksi, aitoja sanoja ei yhtään. Katsokaa vaikka.. Nyt, kun neljä vuotta myöhemmin tarkastelen tilannetta, huomaan olleeni oikeassa, ja silti rikkoneeni etikettiä. Blogille voi tehdä mitä haluaa, mutta se yleensä (toivottavasti) vain tappaa blogin. Teksteissä taas, tiukan asiatekstin ulkopuolella, avaruudessa jossa suurin osa blogiteksteistä elää, sen sijaan lähes mikä vain on sallittua. Ero on hiuksenhieno, sekä vaikea selittää, mutta kun sen ymmärtää, on tajunnut sekä ohjelmoinnista että proosan (ja ehkä runojenkin? En osaa sanoa; en ole tehnyt niitä aikoihin) kirjoittamisesta jotain syvällistä. Aikoinaan minulla oli proosaprojekti, jossa olin yrittänyt pysyä realistisessa romantiikassa, mutta tajuttuani että omassa universumissani kaikki oli sallittua, tein hahmoista tietoisia siitä että he ovat vain mielikuvituksen tuotetta. Se toimi hyvin, mutta teksti oli muuten sen verran kamala etten laita sitä nettiin referenssiksi :)

Pisteet sille, joka keksii mistä keksin tehdä fiktiivisistä hahmoista tietoisia fiktiivisyydestään

Oma ääni on tärkeä. Tiedän kuulostavani tämän sanottuani yläasteen äidinkielenkirjalta, mutta toisinkuin silloin tunsin, tämä on todella tärkeää. Objektiiviset, ei kenellekään ja silti kaikille suunnatut tekstit, joita tapaa uutislehdissä ja äidinkielen yo-kirjoituksissa, ovat toki ehkä asiasta tietoa etsivälle mielenkiintoisia, mutta ei niistä saa näitä harvoja erikoistapauksia lukuunottamatta mielenkiintoisia millään. Näin ollen minun on rikottava tekstini objektiivisuus, ja todettava myös, että ellei teksti käsittele ohjelmointia (tai sen oheisprosesseja), tai kirjoittamista, laskee sen kiinnostavuus runsaasti. Voitte olla eri mieltä, mutta <sarcasm>minä olen oikeassa :)</sarcasm>.

Tästä tulikin mieleeni, Yegge kerran otti puheeksi sen, että blogaajat ovat narsisteja. Tunnustan tämän piirteen itsessäni, oikeassa elämässä olen ehkä hieman introvertimpi, paitsi harvoille ja valituille, mutta blogeissa aiheina on minä, minä, minä ja minä, ja suu käy mahdollisimman jatkuvasti. Se on lähes-tarkoituksellista, koska jos välttelisin minusta itsestäni kirjoittelua, olisi näiden kirjoittelu todella tylsää, koska koulutukseni on ohjelmoijan eikä toimittajan. Minua ihmetyttää myös argumentit siitä, miten osa ihmisistä ei uskalla laittaa ajatuksiaan nettiin. Toki niiden poistaminen netistä voi olla haasteellista, mutta sen takia kirjoitellessa pitääkin olla varovainen siitä ettei lausu mitään ajattelematonta, ja muistaa mainita milloin teksti muuttuu asiallisesta vitsin väännöksi, niinkuin minä tein yllä <sarcasm>-tageilla.

Blogikirjoituksen perimmäinen tarkoitus on olla loki siitä, miten kirjoittaja on päässyt tilanteesta A johtopäätökseen D. Ajatellessaan, kirjoitaessaan ja keskustellessaan ihminen muodostaa päänsä sisällä puumaisen rakenteen, jossa jokaisesta pointista voi liikkua eteenpäin monella eri tavalla, ja läpikäytyään tämän puun optimaalisinta reittiä pitkin, on tuloksena blogitekstin/esseenkaltainen, lineaariseksi litistynyt joukko pointteja, joiden välillä kirjoittaja yrittää liikkua jouhevasti, välillä onnistuen paremmin, useimmiten huonommin. Jos oikein tarkasti kuuntelette, harjoittelemattomissa puheissa ja sponttaaneissa keskusteluissakin on tällainen tietorakenne taustalla, läpikäymisprosessi on vain paljon selvemmin esillä, kun äänessäolijalla ei ole aikaa viimeistellä sitä.

Mutta ajattelu, siinä vasta mielenkiintoinen touhu. Väittäisin että blogin kirjoittaminen on ajattelutöistä jalointa ja tehokkainta, minkä vuoksi yool-blogi on yhä olemassa, ja mikä tärkeintä, paljon toimivampaa kuin ajattelu ääneen tai pään sisällä, ilman persistenttiä mediaa2 ajatteleminen. Yeggeä lainatakseni, blogi tarjoaa helpon laajennuksen pään sisäiselle keskusmuistille, jonne on todella vaikeaa säilöä mitään pitkäkestoista. Kun saan idean, on helpompi avata läppäri ja Emacs, ja kirjoittaa ideasta luonnosteksti ~/Dropbox/bloggailu:un, josta se on käytettävissä kunhan pääsen kotikoneelle, jossa se on helposti saatavissa Dropboxista, kuin jos yrittäisin säilöä sitä omaan muistiini, jolloin kotikoneelle päästyäni en pohtisi "Dropboxissa oli jotain inspiroivaa", vaan pikemminkin "Pelaisko pleikkarilla vai tietskarilla illan ratoksi?".

Fiktio/Proosa

Fiktiivinen teksti, ah siinä vasta hieno taiteenlaji. Asiatekstejä harrastan koska niitä kirjoittamalla otetaan paljon vakavammin3, ja ne ovat omalla oudolla tavallaan kiehtovia, vähän niinkuin Javalla XML:n jauhaminen on mielenkiintoista, kun on ensin perehtynyt miten asiat tehtäisiin Lispillä jos oltaisiin fiksuja. Fiktiivisiä tekstejä harrastan samasta syystä kuin harrastan peliohjelmointia4: koska se on uskomattoman mielenkiintoista, ja omalla alallaan parasta. Omalla tavallaan fiktiiviseen kirjoitusharrastukseeni vaikutti se, että olin nuorempana (kuulemma :) kamalan nirso kirjojen suhteen: kun suurin osa kirjoista oli pöljiä, mikä oli paras tapa saada epäpöljä kirja? No kirjoittaa itse sellainen, tietysti!

Olen vuosien saatossa kirjoittanut varmaan parin sadan tuhannen sanan edestä proosaa, 10 000-60 000 sanan tekstejä, joiden määrää en pysty varmistamaan kun tikkuni, jolla Dropboxia vanhemmat tarinat elävät, on hukassa, ja olen omaani ja muiden työtä tutkimalla ja tarkkailemalla oppinut jutun jos toisenkin. Tärkein asia, joka pätee sekä asiatekstissä että proosassa, on sammuttaa itsekritiikki, ja antaa sielun vuodattaa sanoja paperille/Emacsiin. Toki, jos aikoo julkaista tekstin, kannattaa siitä aivopierut siivota pois, mutta jos se jää pöytälaatikkoon, sottaisempi on mielestäni parempi. Sottaisia tekstejä voi sitten vuosien päästä käyttää esimerkkeinä siitä mikä toimii ja mikä ei.

Tai, kuten Yegge Stephen Kingiä tulkitsee:

Stephen King also offers good advice: be honest. His definition of honesty is subtle, and in a sense it takes him his entire book On Writing to define it. It's a funny and thoughtful book, and I recommend it, even if you're not one of his fans. His "honesty" is the closest word to something we have no word for, so it's hard to capture as a line or two of advice. If I were forced to paraphrase, it'd be that you need to un-learn the stuff they taught you about writing in school, and just write directly from your soul.

Then, of course, you need to go back and edit your work before publishing it. Your soul might occasionally emit statements that are legally actionable, and your soul doesn't always have good judgement or common sense. You should follow the official company guidelines for writing blogs. They're of course the bare minimum, and you should follow them.
Minun pitäisi luultavasti perehtyä Kingiinkin jonain kauniina päivänä, tai ainakin sitatoituun On Writing - teokseen.

Kliseet ovat kivoja. MERPGin maailmaan perustuva, vuoden työn alla, koodinimellä Lomaproosa kulkeva teokseni pyörii kliseiden, sekä uusien ja vanhojen vitsien ympärillä. Tämä voi toimia kerran, mutta lainatakseni Atwoodia, jota jo yllä lainasin, tällaisesta kirjoittamisesta tulee äkkiä samanlaista kulunutta roskaa kuin metailusta blogissa. Tämän vuoksi suunnitelmanani on kirjoittaa tarinan alkupuoli (ensimmäiset mitä, 25 000 sanaa?) lähes uudelleen löydettyäni loppuratkaisun. Tammikuussa, Egyptissä ollessani, löysin lopultakin kyseiseen tarinaan punaisen langan, jonka puuttuminen oli haitannut tarinan kulkua jo ainakin puoli vuotta, ja nyt en pysty selailemaan ensimmäisiä neljääkymmentä sivua, koska niissä Rajolilla (sama Rajol kuin Pröngissä ;) ei ole mitään suuntaa elämällään, joten hän keskittyy lähinnä lainailemaan Assassin's Creediä ja Dragon Ageja.

Ennen tämän tekstin johtopäätöksiä haluan tuoda esiin erään seikan, jonka tajuaminen nauratti ainakin minua. Tekstit, joille on määritelty tiukka aihe, ovat määritelty tiukasti epäonnistumaan. Sen takia äidinkielen yo-kokeestani tuli muistaakseni vain C, joka aiheutti hieman "Etkös sä kuitenki aika hyvin äikkää osaa?" - reaktioita ympärilläni. Sekä Yegge että Graham argumentoivat siitä kuinka oikeassa esseekirjoituksessa on unohdettava mitä koulussa on siitä kerrottu, ja kuinka kumpikin yllättyy parhaimpien esseidensä lopputuloksista, olkoon lopputulos se kuinka Graham tajuaa jonkin uuden asian, joka on hänellä alitajunnassaan kolkutellut jo jonkin aikaa, tai se, kuinka Yegge kuulee kuinka jokin hänen yleistietona pitämänsä fakta oli täysin uutta jollekulle. Jokainen ihminen on oppimisessaan eri vaiheessa: minä pidän itsestäänselvyytenä sitä miten helppoa Emacsissa on liikkua dokumentin eri osien välillä, mutta Tässi ja useimmat töissä ovat täysin tyytyväisiä Wordin tai erilaisten IDE-ohjelmien TODELLA kömpelöihin tekstinkäsittelyominaisuuksiin5. Vastaavasti Tässi pitää (valistuneesti arvaten :P) kompleksilukuja itsestäänselvyytenä, ja vaikka ottaakin ylpeydelle sanoa, töissä IDE-ihmiset tietävän C#stä ja Javasta juttuja, joita pitävät itsestäänselvyytenä, ja joista minulla ei ole hajuakaan.

Yllättyneisyys tekstin lopputuloksesta pätee myös proosassa. Minä ainakin suunnittelen proosiani todella huonosti (mikä näkyy :), Lomaproosankin suunnitelma löytyy... täältä. Tämän vuoksi kirjoittaminen on kuin lukisi interaktiivista kirjaa. Aamulla kun aloitan kirjoittamaan, en tiedä missä päähenkilö on illalla. Tällöin tyhmistä ratkaisuista voi myös syyttää vain itseään, ja niistä voi peruuttaa takaisin, niinkuin aion Lomaproosassa tehdä (En tunnusta kirjoittaneeni tarinan pahiksen kuolemasta niin pöljää kuin se nyt on!).

Johtopäätöksiä? Mitä opimme?

The topic sentence is your thesis, chosen in advance, the supporting paragraphs the blows you strike in the conflict, and the conclusion-- uh, what is the conclusion? I was never sure about that in high school. It seemed as if we were just supposed to restate what we said in the first paragraph, but in different enough words that no one could tell. Why bother?

Puolikkaiden kappaleiden lainaaminen kontekstin ulkopuolelle on kivaa :) En itsekään ole enää aivan varma mitä johtopäätösten alle itse asiassa kuuluu, tiivistelmä asiakappaleista? Se on helppoa:

  1. Tämän blogin turmioksi tulee koitumaan alle kymmenen postin vuositahti, etenkin kun sovellamme 90% on roskaa - sääntöä (jonka lähteen pastean kommentteihin sen löydettyäni). Tätä vastaan taistellakseni aloin blogibloggi-tekstit, joihin kirjoitan mitä mieleen tulee, sen kummempia suodattamatta.
  2. Metabloggaus jää tähän postiin
  3. Kirjoitteluoppeja
  4. Ajatteluoppeja
  5. Fiktio-oppeja
  6. Paljon blogilinkkejä, sekä semikoherenttiä, perustelematonta lukioäikän haukkumista :P
  7. Yllätys on tärkeää - onko tässä tekstissäni siis mitään tärkeää?

Ei minulla varmaan muuta tälle illalle. Toivottavasti saan seuraavan tekstin aikaiseksi ennen huhtikuuta :P

[1] Mitä varten ihmiset eivät aseta fiksuja id-attribuutteja dokumentteihinsa? Helpottaisi linkkausta todella paljon.

[2] Mitä persistentti media on suomeksi? Puhun siis paperin, blogin tai Emacsin kaltaisista medioista, joihin ajatusten vuodattaminen takaa niiden säilyvyyden, toisinkuin niiden ääneenajattelu tai pään sisälle jättäminen.

[3] Ainakin ohjelmointipiireissä, voisin yrittää tehdä seuraavan blogibloggitekstin siitä...

[4] Oikeasti! MERPG edistyy, hitaasti mutta varmasti, ja jos tänä keväänä saisi kartat ja grafiikat lopultakin kuntoon, pääsisi moottorissa eteenpäin!

[5] Pitäisikö kirjoittaa bloggiblogitekstinä myös ylistyslaulu Emacsille? Olisiko kiinnostuneita?

Monday 7 January 2013

Uuden vuoden projekteja

Hyvää uutta vuotta 2013!

D&W:n puolella ehdinkin jo analysoida mennyttä vuotta; nyt on aika analysoida tulevaa. Kirjoitan tätä Egyptistä, ilman internet-yhteyttä, joten en voi ottaa kantaa vuoden ensimmäisen viikon mahdollisiin tapahtumiin MERPGin ja muiden ympärillä, vaan sen sijaan kirjoittelen altaalla ja lämmöstä nauttiessa syntyneistä ideoista. Näitä on monia, ainakin kolme, ja jokainen näistä ideoista tuo lisäarvoa MERPGiin tai tiimille.

mAnimaattori



Tarve tälle projektille on ollut olemassa yhtä kauan kuin MERPGistä on puhuttu, mutta nyt vasta osaan antaa sille nimen. Aikoinaan oletin että Coolbasic-foorumin Latexi95:n Animaattori valmistuisi sopivasti MERPGin animointivaiheeseen, mutta nyt näyttää siltä kuin Animaattori 3:n toinen inkarnaatio on jäätynyt ja haudattu. Etelänlomani aikana "tulin lukeneeksi" viimeiset 300 sivua Clojure-raamatustani (ja Pitkä Sigma 4:n, ja 200 sivua Poea, hienoja kirjoja), ja nyt minulla on jonkinlainen käsitys siitä miltä oman animaattorin tulisi näyttää.

MERPGin animaatiot tunnetusti ovat framecount*frameW pikselin levyisiä ja frameH korkuisia animaatioita. Javan kaksiulotteiset grafiikkaluokat muistaakseni sallivat kuvien helpon jakamisen ja yhdistelyn, joten animaattorin ainoa "iso" ongelma on freimien käsittelyyn käytettävän käyttöliittymäkomponentin löytäminen/rakentaminen. Olisi kiva jos varsinaisen kuvankäsittelyn voisi ulkoistaa Paint.NETille upottamalla sen Swing-layouttiin, mutta se on vaikeampi toteuttaa kuin Emacsin tekstieditorin integrointi Visual Studioon, toisinsanoen siis mahdotonta. Onneksi javakäyttöliittymien rakentaminen ei vuodenvaihdetta edeltäneiden kokeilujeni pohjalta ole Clojurella ja Seesaw'lla lainkaan samanlainen PITA kuin raa'alla Swingillä.

Käytännössä tämän ohjelman malliksi riittäisi lista/vektori java.awt.Image'ista, jotka tallennusmetodi yhdistäisi yhdeksi Imageksi, joka kirjoitetaan ImageIO:lla levylle. Luonnollisesti latausmetodi lataisi kuvan, hajottaisi sen useaksi imageksi, ja muodostaisi näistä listan. Freimien määrä pitäisi myös jostain päätellä, minä en ainakaan keksi miten sen voisi päätellä pelkästä png-kuvasta rajoittamatta joko freimien leveyttä tai jotain muuta suuretta. Pöljempi voisi luulla että koska MEMAPPERin tilet ovat 50x50, animaatioidenkaan ei ole mitään syytä olla eri kokoisia, mutta se on väärin, koska viime keväänä päätin että hahmojen liike on jatkuvaa, eivätkä ne ole muutenkaan 50x50 - tilejen rajoittamia

Yksi tapa ratkaista ongelma olisi rakentaa Clojuren record, johon tallennetaan Image-lista ja tilejen määrä. Tämä sitten sarjallistettaisiin java.io.Writeobjectilla (tai jollain sellaisella, en omaa localhostissa javadoceja, joista luntata nimiä), jolloin ei IO:n yhteydessä tarvitsisi leikkiä ImageIOlla. Tässä kohtaa todellakin toivon java.awt.Image - luokan olevan sarjallistuskelpoinen.

Näkymään koostuisi yhdestä ikkunasta. Tässä ikkunassa olisi kolme osaa: piirtopinta, AKA "PDN-klooni", työkalualue, ja aikajana. Aikajanalla näytetään jokaisesta freimistä thumnail, ja siltä pystyy ottamaan jokaisen freimin lähempään tarkasteluun piirtopinnalle. Kun freimejä on enemmän kuin ikkunaan mahtuu, näytetään joko nuolinapit, tai sitten toteutetaan jonkinlainen hiireen perustuva animaationsisälläliikkumismenetelmä. Aikajana tarjoaa myös taikanapin freimien lisäämiselle, sekä napin, joka ensin varmistaa että olethan nyt varma, ja sen jälkeen poistaa jos käyttäjä oli varma.

Sitten on työkalualue. Alussa siellä on todennäköisesti tasan pikselintarkka kynä, ja ehkä jotain muototyökaluja. Muut työkalut yritän kirjoittaa pyynnöstä, tai annan ohjelmointitukea kaikille, joilla on abstrakti algoritmi, jonka haluavat muuttaa Clojureksi, mAnimaattorin ladattavaan muotoon. Lisäksi työkalualueella on väridialogi ja "Outline - Filled - Outline & Filled" - valinnat, jotka toimivat kuten PDN:ssä. Työkalujen kirjoituksen tueksi kirjoittanen deftool-makron, joka antaa värin ja Outline-valinnan arvon ohjelmoijalle. Tietysti jos hyvä ohjelmoija olisi, tekisi näille arvoille myös funktiorajapinnat, jotta HOFfien käytöstä ei tulisi ihan kamalaa.

Tätä ohjelmaa helpottaisi kuitenkin jos löytyy  Swing-yhteensopiva, upotettava kuvienmuokkas-UI-elementti.

MEsE



Clojureraamattua lukiessani huomasin Leiningenin, Clojuren Mavenin, olevan aika tajuttoman kiva kapistus. Viuhkaan, jonne en jumalauta yhtään .waria laita enää ikinä, laittaessani MEsEn palvelinpuolta, ensin piti shellillä tappaa tomcat, sitten netbeansissa clean&buildata uusi koodi, ftp:llä siirtää 25-megainen .war (...ehti hyvin keittää teetä yhden siirron aikana, ja huh hellettä jos huomasit paketissa bugeja...), riidellä tomcat päälle, ja toivoa että se oli päällä vielä huomennakin. Tämä on ihan yhtä pöljä tapa ohjelmoida kuin miltä kuulostaakin. Sen sijaan, edellämainitusta Clojureraamatusta luin leinin kykenevän tekemään tämän saman Amazonin clj-ympäristöön seuraavan proseduurin mukaan: kirjoitetaan % lein deploy shelliin. Okei, myönnetään, project.clj vaatii hieman määrityksiä ennen kuin deploy - komento on olemassa, mutta silti erittäin paljon pienempi PITA (kiva akronyymi <3 ) rakentaa asianmukainen project.clj kuin tehdä ensimmäinen prosessi neljä kertaa päivässä yhden nullpointerin etsimisen vuoksi, ja huomata sen jälkeen aamun muuttuneen aamuyöksi.

En takaa että korrekti komento oli lein deploy. Kirjoitan tätä tekstiä ulkomuistista, tarkistamatta mitään mistään, joten mikään ei välttämättä pidä paikkaansa, mutta joka tapauksessa kyseessä oli yksinkertainen, lyhyt lein-komento.

Näinollen, kunhan kotiin pääsen, on minun tarkistettava mitä Amazonin ympäristö maksaa, ja tarjoaisiko Leiningen yhtä kivan tuen ilmaiselle Google App Enginelle. Jos (käy ihme ja ) Amazonia voi yksityisenä käyttää ilmaiseksi, tai GAElle tarjotaan samaa tukea, tiputan vanhan MEsErver-koodipohjan ja aloitan sen suosiolla alusta Clojurella. Tämä ratkaisisi monia ongelmia, joita vanhassa koodissa on, deployingin (mitä hittoa se on suomeksi?) lisäksi. Javan Collections - API on olevinaan kiva, mutta MEsErverissä se oli enemmän kuutamolla kuin normaalisti. Jostain syystä olioita, jotka olin tasan tarkkaan laittanut kokoelmiien, ei enää hetken päästä löytynyt niistä, eivätkä oliot poistuneet pyydettäessä. Lisäksi yhtään monimutkaisemmat datankäsittelyoperaatiot javakokoelmilla ovat juuri niitä: monimutkaisia. Miten kukaan selviää ilman mappia/selectiä, filtteriä/whereä ja muita!?

Joka tapauksessa, tutkin siis kotiinpäästyäni MEsEn toteutusmahdollisuuksia Clojure-palvelimella.

Salaperäinen kolmas projekti

Ehdin jo unohtaa mikä tämä oli. Ehkä Javaan pohjautuva MEserver GAElle, jos lein ei tue ympäristöä? En tiedä.