Onhan nuita matopelejä jo täällä nähty, mutta kerron silti oman versioni jossa on kätevällinen ja freshi idea. Tein tämän alun perin Casio FX 1.0 -laskimeeni, jonka hitaan tulkkikielen vuoksi jouduin tekemään koodista mahdollisimman nopean suorittaa.
Miten matopeli toimii?
Matopelin teh idea on, että madon pään sijainti (x, y) tallennetaan taulukkoon, ja samalla madon pää piirretään ruudulle. Madon hännän kohdalla taas tyhjennetään ruudulla oleva madonpala. Helpointa tämä on toteuttaa niin, että jokaisessa framessa taulukon jokaista alkiota aina häntään asti muutetaan (nykyinen sijainti edelliseen sijaintiin), mutta tällöin peli hidastuu madon pitenemisen myötä. Toinen tapa on tallettaa kaksiulotteiseen matriisiin kentän jokainen ruutu, johon sitten tallennetaan esimerkiksi tieto siitä, onko ruudussa mato, mihin suuntaan se siitä kääntyy jne. Laskimissa näin suurien matriisien kutsuminen on usein hidasta, ja kääntymiseen tarvittavat iffit hidastaisivat myös tulkkausta. Lisäksi tätä ei voi soveltaa matopeleissä, jossa mato kääntyy portaattomasti.
Optimointi tässä koodivinkissä
Tässä versiossa muutetaan vain madon pään ja hännän kohdalla olevien taulukon alkioiden indeksinumeron sisältäviä muuttujia (kasvatetaan joka framessa yhdellä), jolloin taulukkoon ei tarvitse koskea kuin kerran madon pään kohdalla. Nyt joudutaan kuitenkin tarkistamaan, etteivät muuttujat ylitä taulukon rajoja, mutta se on koneelle huomattavasti pienempi tehtävä kuin jokaisen alkion läpikäynti joka framessa.
Periaatteessa suoritus ei siis hidastu madon kasvaessa. Toisin kävi laskimessa, Casio kun lukee alkion 1 noin kolme kertaa nopeammin kuin alkion 255. Mutta joo, nopeampi tämä on kuin kavereiden laskinmatopelit. ;)
Koodi
' - Optimoitu matopeli - ' ' Niin ylpeä olen ideastani, että annan sen ' vapaaseen käyttöösi omissa matopeleissäsi ;) ' ' Ideana on, että madon pään koordinaatit tallennetaan taulukkoon ja ' matopalikka piirretään niiden kohdalle. Sen jälkeen pään taulukkoindek- ' sin kertovaa muuttujaa kasvatetaan yhdellä. Jalan taulukkoindeksi myös ' kasvaa yhdellä. Jalan kohdalle piirretään tyhjä palikka. Koko taulukkoa ' ei tarvitse käsitellä, josta varsinainen etu saavutetaan. ' ' (!)LISŽOPTIMOINTI: ' poista sisennykset, kommentit ja turhat rivinvaihdot jos ajat tulkissa xd ' ' ' 16.9.2005 - Phvli DECLARE SUB teeOmena () 'sijoittaa uuden omenan näytölle CONST tauko = .1 'framen hidastus sekunneissa CONST matoja = 3, maxPituus = 500, alkuPituus = 5 DIM pelaajia AS INTEGER DIM matoX(matoja, maxPituus) AS INTEGER, matoY(matoja, maxPituus) AS INTEGER DIM pisteet(matoja) AS INTEGER DIM iPaa(matoja) AS INTEGER, iHanta(matoja) AS INTEGER DIM liikeVaaka(matoja) AS INTEGER, liikePysty(matoja) AS INTEGER DIM SHARED omenaX, omenaY DIM mato AS INTEGER 'käsiteltävä mato DIM Btn AS STRING 'viimeisin INKEY$ DIM alkuTIMER AS DOUBLE 'delay (TIMER:n arvo ennen delayta) DIM i AS INTEGER 'nykyinen taulukkoindeksi DIM X AS INTEGER, Y AS INTEGER 'väliaikaiset koordinaatit vähän kaikkeen DIM nimi AS STRING 'madon nimi (yläpalkissa) WIDTH 80, 50 CLS PRINT "''' OPTIMOITU MATOPELI (QBasic-koodivinkki) '''''''''''''''''''''''''''''''" PRINT "' '" PRINT "' Ohjelma esittelee matopelin optimointi-ideaa. Käytännön hyötyä '" PRINT "' siitä ei nykytietokoneilla ole, mutta esimerkiksi laskimiin portattuna '" PRINT "' nopeusetu on huomattava (tein tämän alun perin Casio FX 1.0:lle). '" PRINT "' '" PRINT "' Ennätyksiä ei tallenneta, koska perusideana oli vain itse pelin '" PRINT "' ja sorsan kikkailun esittäminen. '" PRINT "' '" PRINT "' '" PRINT "' Phvli (joe_eronen@suomi24.fi) 16.9.2005 '" PRINT "' '" PRINT "'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''" PRINT : PRINT COLOR 14 PRINT " Matojen ohjaus:" PRINT "" COLOR 15 PRINT " Sininen: nuolet Vihreä: A, D, W ja S" PRINT " Syaani: J, L, I, K Punainen: NumPad (Scroll Lock OFF)" 'luetaan pelaajien määrä käyttäjän syötteestä LOCATE 26, 5: PRINT "Pelaajia (1 - " + LTRIM$(STR$(matoja + 1)) + "): "; INPUT "", pelaajia 'koska taulukot yleensä alkavat nollasta, vähennetään muuttujaa yhdellä pelaajia = pelaajia - 1 'tarkistetaan vielä, että pelaajat mahtuvat taulukkoihin IF pelaajia < 0 THEN pelaajia = 0 IF pelaajia > matoja THEN pelaajia = matoja COLOR , 0: CLS 'ALUSTUS (käsitellään kaikkien pelaajien madot) FOR mato = 0 TO pelaajia 'Madon pää ja häntä iPaa(mato) = alkuPituus: iHanta(mato) = 0 pisteet(matoja) = 0 SELECT CASE mato CASE 0 nimi = "Sininen" X = 77 Y = 45 liikeVaaka(mato) = -1 liikePysty(mato) = 0 CASE 1 nimi = "Vihreä" X = 4 Y = 7 liikeVaaka(mato) = 1 liikePysty(mato) = 0 CASE 2 nimi = "Syaani" X = 4 Y = 45 liikeVaaka(mato) = 0 liikePysty(mato) = -1 CASE 3 nimi = "Punainen" X = 77 Y = 7 liikeVaaka(mato) = 0 liikePysty(mato) = 1 END SELECT 'mato kartalle FOR i = 0 TO maxPituus matoX(mato, i) = X matoY(mato, i) = Y NEXT 'madon nimi ylös COLOR 15, mato + 1 LOCATE 1, mato * 22 + 3: PRINT " " + nimi + " " COLOR 14, 0 LOCATE 2, mato * 22 + 5: PRINT pisteet(mato) NEXT 'tehdään kenttä COLOR 0, 4 LOCATE 4: PRINT STRING$(80, 254) FOR Y = 5 TO 47 PRINT "þ"; COLOR , 1: PRINT SPACE$(78); COLOR , 4: PRINT "þ" NEXT LOCATE Y: PRINT STRING$(80, 254) LOCATE 16, 24: PRINT STRING$(34, 254) LOCATE 36, 24: PRINT STRING$(34, 254) omenat = 0 teeOmena 'päägotosilmukka DO 'varsinainen pelisilmukka, murretaan nappia painettaessa DO 'käsitellään kaikkien pelaajien madot FOR mato = 0 TO pelaajia X = matoX(mato, iPaa(mato)) Y = matoY(mato, iPaa(mato)) 'pyyhitään madon jalka LOCATE matoY(mato, iHanta(mato)), matoX(mato, iHanta(mato)) COLOR , 1: PRINT " " 'kasvatetaan pään ja jalan taulukkoindeksiä, eli siiheen 'viittaavaa muuttujaa, yhdellä iPaa(mato) = iPaa(mato) + 1 iHanta(mato) = iHanta(mato) + 1 'tarkistetaan, etteivät taulukkoviittaajat ylitä rangea IF iPaa(mato) > maxPituus THEN iPaa(mato) = 0 IF iHanta(mato) > maxPituus THEN iHanta(mato) = 0 'liikutetaan matoa matoX(mato, iPaa(mato)) = X + liikeVaaka(mato) matoY(mato, iPaa(mato)) = Y + liikePysty(mato) 'asetetaan väri madon piirtämistä varten COLOR mato + 9, 0 'omenan keräys IF ABS(X - omenaX) < 1 AND ABS(Y - omenaY) < 1 THEN GOSUB syoOmena 'törmäyksen tarkistus IF NOT SCREEN(Y, X) = 32 THEN 'ihkuinen ääniteaseri SOUND 400, 1: SOUND 300, 3 'pelin lopetus SLEEP END END IF 'piirretään madolle pää (vasta törmäystarkistuksen jälkeen) LOCATE Y, X: PRINT "þ" NEXT 'odotetaan tomaattien kypsymistä alkuTIMER = TIMER DO: LOOP WHILE TIMER < alkuTIMER + tauko Btn = INKEY$ LOOP WHILE Btn = ""'Ei hidasteta pääsilmukkaa näppäinten tarkistuksella SELECT CASE UCASE$(Btn) 'sinisen madon liikutus (nuolet) CASE CHR$(0) + "K": IF NOT liikeVaaka(0) = 1 THEN liikeVaaka(0) = -1: liikePysty(0) = 0 CASE CHR$(0) + "M": IF NOT liikeVaaka(0) = -1 THEN liikeVaaka(0) = 1: liikePysty(0) = 0 CASE CHR$(0) + "H": IF NOT liikePysty(0) = 1 THEN liikeVaaka(0) = 0: liikePysty(0) = -1 CASE CHR$(0) + "P": IF NOT liikePysty(0) = -1 THEN liikeVaaka(0) = 0: liikePysty(0) = 1 'vihreän madon liikutus (ASWD) CASE "A": IF NOT liikeVaaka(1) = 1 THEN liikeVaaka(1) = -1: liikePysty(1) = 0 CASE "D": IF NOT liikeVaaka(1) = -1 THEN liikeVaaka(1) = 1: liikePysty(1) = 0 CASE "W": IF NOT liikePysty(1) = 1 THEN liikeVaaka(1) = 0: liikePysty(1) = -1 CASE "S": IF NOT liikePysty(1) = -1 THEN liikeVaaka(1) = 0: liikePysty(1) = 1 'syaanin madon liikutus (JLIK) CASE "J": IF NOT liikeVaaka(2) = 1 THEN liikeVaaka(2) = -1: liikePysty(2) = 0 CASE "K": IF NOT liikeVaaka(2) = -1 THEN liikeVaaka(2) = 1: liikePysty(2) = 0 CASE "I": IF NOT liikePysty(2) = 1 THEN liikeVaaka(2) = 0: liikePysty(2) = -1 CASE "L": IF NOT liikePysty(2) = -1 THEN liikeVaaka(2) = 0: liikePysty(2) = 1 'punaisen madon liikutus (NP) CASE "4": IF NOT liikeVaaka(3) = 1 THEN liikeVaaka(3) = -1: liikePysty(3) = 0 CASE "6": IF NOT liikeVaaka(3) = -1 THEN liikeVaaka(3) = 1: liikePysty(3) = 0 CASE "8": IF NOT liikePysty(3) = 1 THEN liikeVaaka(3) = 0: liikePysty(3) = -1 CASE "5", "2": IF NOT liikePysty(3) = -1 THEN liikeVaaka(3) = 0: liikePysty(3) = 1 CASE CHR$(27) 'Esc murtaa silmukan -> peli loppuu EXIT DO END SELECT LOOP CLS COLOR 7 END 'omenan syödään optimoinnin vuoksi muualla kuin pääsilmukassa. 'Gotoleimatoteutus siksi, etteivät kaikki laskimet tue funktioita 'tai toisten ohjelmien kutsua syoOmena: 'pyyhi wanha omena ja tee uusi tilalle COLOR , 1: LOCATE omenaY, omenaX: PRINT " " teeOmena 'Pidennetään matoa (pienennetään hännän taulukkoindeksiä ' -> häntänä toimii silloin vanhempi sijainti), 'ja tarkistetaan, ettei uusi indeksi ylitä rajoja. i = iHanta(mato) iUusi = iHanta(mato) - 5 IF iUusi < 0 THEN iUusi = iUusi + maxPituus 'Siirretään kaikki hännän vahan ja uuden sijainnin väliin jäävät 'palat samaan kohtaan (muuten jo pyyhitty osa pyyhittäisiin uudestaan, 'ja esimerkiksi paikalle ehtinyt toinen mato katkeaisi). DO matoX(mato, i) = matoX(mato, iHanta(mato)) matoY(mato, i) = matoY(mato, iHanta(mato)) i = i - 1 IF i < 0 THEN i = i + maxPituus LOOP UNTIL i = iUusi iHanta(mato) = i IF iHanta(mato) < 0 THEN iHanta(mato) = iHanta(mato) + maxPituus END IF 'kasvatetaan pisteitä pisteet(mato) = pisteet(mato) + 1 COLOR 14, 0 LOCATE 2, mato * 22 + 5: PRINT pisteet(mato) 'jätetään madolle kirkas hömöhaha COLOR 15, 7 RETURN SUB teeOmena 'etsitään omenalle vapaa paikka DO omenaX = CINT(RND * 77) + 2 omenaY = CINT(RND * 43) + 5 LOOP UNTIL SCREEN(omenaY, omenaX) = 32 'piirrä omena kartalle LOCATE omenaY, omenaX COLOR 14, 6: PRINT "þ" END SUB
Minäkin olen tehnyt matopelin Casio FX 1.0 -laskimella ja päätynyt samanlaiseen ratkaisuun. :)
No jopas, näyttää aika hauskalta. Olen koulussa joten en voi nyt mitenkään testata sitä, mutta jahka pääsen kotiin niin sitten.
Näinhän ne BASIC-matopelit tehtiin 80-luvulla. Periaate on aika lailla se hyvä ja optimaaliseksi todettu, joskin luulen että saisit vielä nopeammaksi jos muuttaisit matoX- ja matoY-taulukot yksiulotteisiksi. Taulukon wrappailussa voit kokeilla ylärajavertailun sijaan myös AND-operaattoria :)
Niin, ja näyttöindeksitkin mielellään yksiulotteisiksi mikäli vain suinkin mahdollista niin häipyy monesta kohti tarve kahdelle erilliselle taulukolle. Lisäksi voit tutkia omenan läsnäolon suoraan näytöltä, jolloin luultavasti saat pienen nopeusedun ja siinä sivussa "rajattoman" omenamäärän :)
viznut kirjoitti:
voit tutkia omenan läsnäolon suoraan näytöltä, jolloin luultavasti saat pienen nopeusedun ja siinä sivussa "rajattoman" omenamäärän
Tässä tapauksessa tuo kyllä toimisi, mutta 'grafiikkatiloissa' se vain hidastaisi ohjelman ajoa. Totta on myös, että yksiulotteinen taulukko on nopeampi käsitellä, mutta silloin on paha simuloida useita matoja helposti. Muuten kiitos vinkeistä, pitää nuiden boolean-operaattoreiden käyttöön vielä perehtyä.
Aihe on jo aika vanha, joten et voi enää vastata siihen.