8th on moderni Forth tyylinen ohjelmointikieli, joka palauttaa käyttäjän takaisin aikakauteen jolloin ohjelmointi vielä oli addiktiivista ja kivaa.
Alla äänitukea ja pause toiminnallisuutta lukuunottamatta täysi Tetris toteutus. Nämä puutteet korjaan myöhemmin. Äänituki ei ole mukana 8th:n ilmaisessa versiossa, joten tämä versio sopinee muutenkin paremmin tänne.
\ \ Simple Tetris game written in 8th. \ \ GUI needs work. Highscore list would be nice. \ true app:isgui ! : BOARDWIDTH 10 ; : BOARDHEIGHT 22 ; : BLOCKWIDTH 20 ; : BOARDX 20 ; \ board x-position : BOARDY 40 ; \ board y-position : NEXTSHAPEX 270 ; : NEXTSHAPEY 50 ; : STARTY -4 ; \ possible game states : TITLE 0 ; : RUNGAME 1 ; : PAUSED 2 ; : GAMEOVER 3 ; var gamestate var delay [30, 25, 20, 15, 10, 8] var, delays { "cursor up" : false, "cursor down" : false, "cursor left" : false, "cursor right" : false, "spacebar" : false } var, keys : reset-keys keys @ "cursor up" false m:! "cursor down" false m:! "cursor left" false m:! "cursor right" false m:! "spacebar" false m:! drop ; : onkey drop g:keyinfo nip "desc" m:@ nip dup keys @ swap m:exists? if swap true m:! drop else 2drop then true ; ["black","red","orange","yellow","green","blue","cyan","violet"] var, colors [ \ #### { "coords" : [ [ { "x" : 0, "y" : 1 }, { "x" : 1, "y" : 1 }, { "x" : 2, "y" : 1 }, { "x" : 3, "y" : 1 } ], [ { "x" : 1, "y" : 0 }, { "x" : 1, "y" : 1 }, { "x" : 1, "y" : 2 }, { "x" : 1, "y" : 3 } ] ], "points" : [ 5, 2 ], "color" : "red" }, \ ## \ ## { "coords" : [ [ { "x" : 0, "y" : 0 }, { "x" : 0, "y" : 1 }, { "x" : 1, "y" : 0 }, { "x" : 1, "y" : 1 } ] ], "points" : [ 6 ], "color" : "orange" }, \ # \ ## \ # { "coords" : [ [ { "x" : 1, "y" : 0 }, { "x" : 0, "y" : 1 }, { "x" : 1, "y" : 1 }, { "x" : 2, "y" : 1 } ], [ { "x" : 1, "y" : 0 }, { "x" : 1, "y" : 1 }, { "x" : 2, "y" : 1 }, { "x" : 1, "y" : 2 } ], [ { "x" : 0, "y" : 1 }, { "x" : 1, "y" : 1 }, { "x" : 2, "y" : 1 }, { "x" : 1, "y" : 2 } ], [ { "x" : 1, "y" : 0 }, { "x" : 0, "y" : 1 }, { "x" : 1, "y" : 1 }, { "x" : 1, "y" : 2 } ] ], "points" : [ 5, 5, 6, 5 ], "color" : "yellow" }, \ ## \ ## { "coords" : [ [ { "x" : 0, "y" : 0 }, { "x" : 1, "y" : 0 }, { "x" : 1, "y" : 1 }, { "x" : 2, "y" : 1 } ], [ { "x" : 1, "y" : 0 }, { "x" : 0, "y" : 1 }, { "x" : 1, "y" : 1 }, { "x" : 0, "y" : 2 } ] ], "points" : [ 6, 7 ], "color" : "green" }, \ ## \ ## { "coords" : [ [ { "x" : 1, "y" : 0 }, { "x" : 2, "y" : 0 }, { "x" : 0, "y" : 1 }, { "x" : 1, "y" : 1 } ], [ { "x" : 0, "y" : 0 }, { "x" : 0, "y" : 1 }, { "x" : 1, "y" : 1 }, { "x" : 1, "y" : 2 } ] ], "points" : [ 6, 7 ], "color" : "blue" }, \ ### \ # { "coords" : [ [ { "x" : 2, "y" : 0 }, { "x" : 0, "y" : 1 }, { "x" : 1, "y" : 1 }, { "x" : 2, "y" : 1 } ], [ { "x" : 0, "y" : 0 }, { "x" : 0, "y" : 1 }, { "x" : 0, "y" : 2 }, { "x" : 1, "y" : 2 } ], [ { "x" : 0, "y" : 0 }, { "x" : 1, "y" : 0 }, { "x" : 2, "y" : 0 }, { "x" : 0, "y" : 1 } ], [ { "x" : 0, "y" : 0 }, { "x" : 1, "y" : 0 }, { "x" : 1, "y" : 1 }, { "x" : 1, "y" : 2 } ] ], "points" : [ 6, 7, 6, 7 ], "color" : "cyan" }, \ # \ ### { "coords" : [ [ { "x" : 0, "y" : 0 }, { "x" : 1, "y" : 0 }, { "x" : 2, "y" : 0 }, { "x" : 2, "y" : 1 } ], [ { "x" : 1, "y" : 0 }, { "x" : 1, "y" : 1 }, { "x" : 0, "y" : 2 }, { "x" : 1, "y" : 2 } ], [ { "x" : 0, "y" : 0 }, { "x" : 0, "y" : 1 }, { "x" : 1, "y" : 1 }, { "x" : 2, "y" : 1 } ], [ { "x" : 0, "y" : 0 }, { "x" : 1, "y" : 0 }, { "x" : 0, "y" : 1 }, { "x" : 0, "y" : 2 } ] ], "points" : [ 6, 7, 6, 7 ], "color" : "violet" } ] var, shapes var board var shape var x var y var rotation var oldx \ old x var oldy \ old y var oldrotation \ old rotation var nextshape var nextx var nextrotation var score var rows var level : init-board [ [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0] ] const board ! ; : new-row [0,0,0,0,0,0,0,0,0,0] const ; : delete-rows ( >r board @ r@ a:@ 0 ' n:= a:indexof nip null? if drop r@ a:- new-row a:slide drop rows @ n:1+ rows ! else 2drop then rdrop ) 0 BOARDHEIGHT n:1- loop ; : canmove true >r shape @ "coords" m:@ nip rotation @ a:@ nip ( nip "x" m:@ x @ n:+ dup 0 n:< swap 10 n:< not or swap "y" m:@ y @ n:+ 22 n:< not rot or if drop break else board @ swap "y" m:@ y @ n:+ dup 0 n:< if 2drop drop else swap >r a:@ nip r> "x" m:@ nip x @ n:+ a:@ nip 0 n:= not if break then then then break? if false rdrop >r then ) a:each drop r> ; : rand7 rand-pcg n:abs 7 n:mod ; : next-shape shapes @ rand7 a:@ nip dup nextshape ! "coords" m:@ nip a:len nip rand-pcg n:abs swap n:mod nextrotation ! rand7 nextx ! ; : draw-board 2 g:line-width "black" g:scolor board @ ( swap BLOCKWIDTH n:* BOARDY n:+ >r ( >r rswap BLOCKWIDTH n:* BOARDX n:+ r@ rswap BLOCKWIDTH BLOCKWIDTH g:rect colors @ r> caseof g:fcolor g:stroke-fill ) a:each drop rdrop ) a:each drop ; : draw-shape 2 g:line-width "black" g:scolor >r shape @ "color" m:@ r> swap g:fcolor swap "coords" m:@ nip rotation @ a:@ nip ( nip "y" m:@ y @ n:+ BLOCKWIDTH n:* BOARDY n:+ dup BOARDY n:< not if swap "x" m:@ nip x @ n:+ BLOCKWIDTH n:* BOARDX n:+ swap BLOCKWIDTH BLOCKWIDTH g:rect else 2drop then ) a:each drop g:stroke-fill ; locals: : draw-nextshape 4 "min-x" w:! 4 "min-y" w:! 0 "max-x" w:! 0 "max-y" w:! nextshape @ "coords" m:@ nip nextrotation @ a:@ nip ( nip "x" m:@ dup "max-x" w:@ n:max "max-x" w:! "min-x" w:@ n:min "min-x" w:! "y" m:@ dup "min-y" w:@ n:max "min-y" w:! "min-y" w:@ n:min "min-y" w:! drop ) a:each drop 4 "max-x" w:@ "min-x" w:@ n:- n:1+ n:- BLOCKWIDTH n:* 2 n:/ "min-x" w:@ BLOCKWIDTH n:* n:- "o-x" w:! 4 "max-y" w:@ "min-y" w:@ n:- n:1+ n:- BLOCKWIDTH n:* 2 n:/ "min-y" w:@ BLOCKWIDTH n:* n:- "o-y" w:! 2 g:line-width "black" g:scolor "darkgray" g:fcolor NEXTSHAPEX 5 n:- NEXTSHAPEY 5 n:- BLOCKWIDTH 4 n:* 10 n:+ dup g:rect g:stroke-fill >r nextshape @ "color" m:@ r> swap g:fcolor swap "coords" m:@ nip nextrotation @ a:@ nip ( nip "x" m:@ BLOCKWIDTH n:* NEXTSHAPEX n:+ "o-x" w:@ n:+ swap "y" m:@ nip BLOCKWIDTH n:* NEXTSHAPEY n:+ "o-y" w:@ n:+ BLOCKWIDTH BLOCKWIDTH g:rect ) a:each drop g:stroke-fill ; : draw-title draw-board 2 g:line-width "black" g:scolor "darkgray" g:fcolor NEXTSHAPEX 5 n:- NEXTSHAPEY 5 n:- BLOCKWIDTH 4 n:* 10 n:+ dup g:rect g:stroke-fill "20" g:setfont g:l-text NEXTSHAPEX 8 n:- 160 "NEXT PIECE" g:draw-text-at NEXTSHAPEX 30 n:- 200 score @ "score: %d" s:strfmt g:draw-text-at NEXTSHAPEX 30 n:- 220 level @ "level: %d" s:strfmt g:draw-text-at ; : draw-rungame draw-board draw-shape draw-nextshape "20" g:setfont g:l-text NEXTSHAPEX 8 n:- 160 "NEXT PIECE" g:draw-text-at NEXTSHAPEX 30 n:- 200 score @ "score: %d" s:strfmt g:draw-text-at NEXTSHAPEX 30 n:- 220 level @ "level: %d" s:strfmt g:draw-text-at ; : draw-gameover draw-board draw-shape draw-nextshape "20" g:setfont g:l-text NEXTSHAPEX 8 n:- 160 "NEXT PIECE" g:draw-text-at NEXTSHAPEX 30 n:- 200 score @ "score: %d" s:strfmt g:draw-text-at NEXTSHAPEX 30 n:- 220 level @ "level: %d" s:strfmt g:draw-text-at "50" g:setfont "white" g:scolor g:c-text 200 250 "GAME OVER!" g:draw-text-at ; : ondraw [ ' draw-title , ' draw-rungame , ' draw-rungame , ' draw-gameover ] gamestate @ caseof ; : init-game 0 score ! 1 level ! 0 rows ! next-shape nextshape @ shape ! nextrotation @ rotation ! nextx @ x ! STARTY y ! next-shape delays @ 0 a:@ nip delay ! init-board RUNGAME gamestate ! reset-keys ; : points? shape @ "points" m:@ nip rotation @ a:@ nip ; : shape-color? shape @ "color" m:@ nip colors @ swap ' s:= a:indexof nip ; : store-block \ pointmap "y" m:@ y @ n:+ dup 0 n:< if 2drop else board @ swap a:@ nip swap "x" m:@ nip x @ n:+ shape-color? a:! drop then ; : gameloop keys @ "cursor up" m:@ if "cursor up" false m:! rotation @ dup oldrotation ! n:1+ dup rotation ! shape @ "coords" m:@ nip a:len n:1- nip n:> if 0 rotation ! then canmove not if oldrotation @ rotation ! then then "cursor left" m:@ if "cursor left" false m:! x @ dup oldx ! n:1- x ! canmove not if oldx @ x ! then then "cursor right" m:@ if "cursor right" false m:! x @ dup oldx ! n:1+ x ! canmove not if oldx @ x ! then then "spacebar" \ block is dropped m:@ if "spacebar" false m:! repeat y @ n:1+ y ! canmove not if break then again y @ n:1- y ! shape @ "coords" m:@ nip rotation @ a:@ nip ( nip store-block ) a:each drop score @ points? n:+ score ! delete-rows y @ 0 n:< if GAMEOVER gamestate ! else nextshape @ shape ! nextrotation @ rotation ! nextx @ x ! STARTY y ! next-shape then else \ block falls or down cursor pressed "cursor down" m:@ delay @ n:1- dup delay ! 0 n:> not or if "cursor down" false m:! y @ dup oldy ! n:1+ y ! canmove not if oldy @ y ! shape @ "coords" m:@ nip rotation @ a:@ nip ( nip store-block ) a:each drop score @ points? n:+ score ! delete-rows y @ 0 n:< if GAMEOVER gamestate ! else nextshape @ shape ! nextrotation @ rotation ! nextx @ x ! STARTY y ! next-shape then else rows @ 10 n:/ int n:1+ dup level ! 0 5 n:clamp n:1- delays @ swap a:@ nip delay ! then then then drop ; : ontimer [ ' noop , ' gameloop , ' gameloop , ' noop ] gamestate @ caseof g:invalidate ; : onMenuSelected n:1- [ ' init-game , ' g:quit ] case ; var gui { "kind" : "win", "buttons" : 5, "title" : "Tetris v. 0.1", "wide" : 400, "high" : 500, "center" : true, "bg" : "gray", "font" : "Arial 10", "draw" : "ondraw", "timer" : "ontimer", "key-pressed" : "onkey", "timer-period" : 20, "children" : [ { "kind" : "menubar", "name" : "menu", "bounds" : "0,0,parent.width, parent.height/20", "menu-selected" : "onMenuSelected", "menu" : [ [ "Game", 0, "New game", 1, "Quit", 2 ] ] } ] } var, gui-desc : app:main init-game TITLE gamestate ! gui-desc @ g:new gui ! ;
Koodi on kaikkea muuta kuin selvää, joten suosittelen kommenttien lisäämistä.
Lisäsin alkuruutuun kuvan ja peliin pause toiminnallisuuden.
Päivitetty koodi ja ajettavat binäärit Windows, Linux ja macOS käyttöjärjestelmille löytyy täältä.
Lisäsin alustavan äänituen peliin. Omalla koneellani en kuitenkaan jostain syystä saa ääntä kuuluviin, vaikka mielestäni tulkitsen 8th:n manuaalia oikein.
Alla olevan linkin takana on nykyinen kehitysversio ajettavien binäärien kera Windows ja Linux käyttöjärjestelmille. Voisiko joku kokeilla johtuuko äänettömyys omasta koneestani, bugista Windows tuessa tai sitten vaan siitä, että en vaan osaa.
Eritoten kiinnostaisi Linux version toimivuus.
Löytyypi täältä.
Ei kuulu ääniä Windows versiossa.
Pessi kirjoitti:
Ei kuulu ääniä Windows versiossa.
Kiitos! Alan kallistua sille kannalle, että windows version äänituessa on bugi. Itselläni "snd:volume?" palauttaa aina nolla, vaikka pitäisi palauttaa järjestelmän äänitaso. Lisäksi saan ääntä kuuluviin, jos laitan sen soimaan loopissa ja katkaisen soiton äänitiedoston keston jälkeen manuaalisesti. Tällöin tosin soitto lähtee vasta pienellä viiveellä ja tämä tuntuu muutenkin "tempulta", jota ei koodiin haluaisi kirjoittaa.
Linux käyttäjiä?
Linux-versiossa ääni kuuluu vain vasemmasta kanavasta (vaikka yleensä mono-ääni pitäisi kopioida molempiin kanaviin).
Metabolix kirjoitti:
Linux-versiossa ääni kuuluu vain vasemmasta kanavasta (vaikka yleensä mono-ääni pitäisi kopioida molempiin kanaviin).
Kiitos! Lisäsin 8th bug trackeriin huomautuksen asiasta.
Aihe on jo aika vanha, joten et voi enää vastata siihen.