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 SUBMinä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.