Tetris game using the 8th programming language

Here is a 8th programming language version of my original version for Fortran. Uses grid layout for drawing.

\
\ Simple Tetris game for 8th programming language
\
needs[ nk/gui nk/keyboard nk/render-timed ]

18 font:system font:new "font1" font:atlas! drop
42 font:system font:new "font2" font:atlas! drop

\ Tetrominos, coords stored using [row,col] order.
[
  { 
    coords: [
              [[1,0],[1,1],[1,2],[1,3]],
              [[0,1],[1,1],[2,1],[3,1]]
            ],
    color: 1,
    score: [5,8]
  },
  {
    coords: [
              [[0,0],[1,0],[0,1],[1,1]]
            ],
    color: 2,
    score: [6]
  },
  { coords: [
              [[0,1],[1,0],[1,1],[1,2]],
              [[0,1],[1,1],[1,2],[2,1]],
              [[1,0],[1,1],[1,2],[2,1]],
              [[0,1],[1,0],[1,1],[2,1]]
            ],
    color: 3,
    score: [5,5,6,5]
  },
  {
    coords: [
              [[0,0],[0,1],[1,1],[1,2]],
              [[0,1],[1,0],[1,1],[2,0]]
            ],
    color: 4,
    score: [6,7]
  },
  {
    coords: [
              [[0,1],[0,2],[1,0],[1,1]],
              [[0,0],[1,0],[1,1],[2,1]]
            ],
    color: 5,
    score: [6,7]
  },
  {
    coords: [
              [[0,2],[1,0],[1,1],[1,2]],
              [[0,0],[1,0],[2,0],[2,1]],
              [[0,0],[0,1],[0,2],[1,0]],
              [[0,0],[0,1],[1,1],[2,1]]
            ],
    color: 6,
    score: [6,7,6,7]
  },
  {
    coords: [
              [[0,0],[0,1],[0,2],[1,2]],
              [[0,1],[1,1],[2,0],[2,1]],
              [[0,0],[1,0],[1,1],[1,2]],
              [[0,0],[0,1],[1,0],[2,0]]
            ],
    color: 7,
    score: [6,7,6,7]
  }
] constant tetrominos

[ [255,255,255,255], [0,255,255,255], [255,255,0,255],
  [255,0,255,255], [255,0,0,255], [0,255,0,255],
  [255,127,0,255], [0,0,255,255] ] constant colors

[300, 250, 200, 150, 100, 80] constant delays

20 constant blocksize

3 constant startx
-4 constant starty

var grid

var score
var rows
var level
var delay
var gameover

var tetromino
var rotation
var next-tetromino
var next-rotation
var old-rotation
var drop-tetromino
var x
var y

: new-row
  [0,0,0,0,0,0,0,0,0,0] const ;

: init-grid
  null grid !
  a:new ( new-row a:push ) 22 times grid ! ;

: random-tetromino
  rand-pcg 7 n:mod ;

: random-rotation \ tetromino -- n
  tetrominos swap a:_@ "coords" m:_@ a:len nip
  rand-pcg swap n:mod ;

: init-game
  false gameover !
  init-grid
  startx x !
  starty y !
  random-tetromino next-tetromino !
  next-tetromino @ random-rotation next-rotation !
  random-tetromino tetromino !
  tetromino @ random-rotation rotation !
  0 level !
  0 score !
  0 rows !
  delays 0 a:_@ nk:timer-delay ;

: delete-rows
  grid @
  ( dup>r a:@
    0 ' n:= a:indexof nip
    null? if
      drop r@ a:-
      new-row a:slide
      1 rows n:+!
      rows @ 10 n:/ n:int level !
      level @ delays a:len nip n:< not if
        delays a:len nip n:1- level !
      then
      delay @ delays level @ a:_@ dup delay !
      n:= !if
        delay @ nk:timer-delay
      then
      tetrominos tetromino @ a:_@ "score" m:_@ rotation @ a:_@ score n:+!
    else
      drop
    then
    rdrop ) 0 21 loop drop ;

[ @scan:LEFT, @scan:RIGHT, @scan:UP, @scan:DOWN, @scan:SPACE ] constant keys

: key-state-changed?  \  s a -- a
  nk:scancode?
  ' >n a:map over nk:get over ?:
  rot third nk:set
  ' n:cmp a:2map ;

: key?  \ -- n | null
  null "keystates" keys key-state-changed?
  (
    swap a:pop -1 n:= if
      rot drop break
    else
      nip
    then
  ) 0 third a:len nip n:1- loop- drop ;

: new-win
  {
    name: "main",
    wide: 320,
    high: 448,
    resizable: false,
    bg: "white",
    title: "Tetris"
  } nk:win ;

: draw-grid
  grid @
  ( swap dup rot ( dup>r 0 n:= !if
      1 tuck nk:grid dup 0 colors r> a:_@ nk:fill-rect
      0 2 "black" nk:stroke-rect 
    else
      2drop rdrop  
    then dup ) a:each 3drop ) a:each drop ;

: store-tetromino
  tetrominos tetromino @ a:_@ "coords" m:@ rotation @ a:_@
  ( 0 a:@ y @ n:+ dup 0 n:< !if
      grid @ swap a:_@ swap 1 a:_@ x @ n:+
      third "color" m:_@ a:! drop
    else
      2drop
    then 
  ) a:each! 2drop ;

: draw-tetromino
  tetrominos tetromino @ a:_@ "coords" m:@ rotation @ a:_@
  ( y @ x @ 2 a:close ' n:+ a:2map
    1 a:@ 0 9 n:between swap 0 a:@ 0 21 n:between rot and if
      a:open 1 tuck nk:grid
      dup>r 0 third "color" m:_@ colors swap a:_@ nk:fill-rect
      r> 0 2 "black" nk:stroke-rect
    else
      drop
    then
   ) a:each! 2drop ;

: can-move?  \  [row,col] rot -- T
  tetrominos tetromino @ a:_@ "coords" m:_@ swap a:_@
  true swap
  ( third ' n:+ a:2map dup>r
    1 a:@ 0 9 n:between swap 0 a:_@ -4 21 n:between and and dup !if
      break
    else
      r@ 0 a:@ 0 n:< !if
        grid @ swap a:open >r a:_@ r> a:_@ 0 n:= and dup !if
          break
        then
      else
        drop
      then
    then rdrop ) a:each! drop nip ;

: left-key
  y @ x @ n:1- 2 a:close rotation @ can-move? if
    -1 x n:+! 
  then ;

: right-key
  y @ x @ n:1+ 2 a:close rotation @ can-move? if
    1 x n:+! 
  then ;

: up-key
  rotation @ dup old-rotation !
  n:1+ tetrominos tetromino @ a:_@ "coords" m:_@ a:len nip n:mod rotation !
  y @ x @ 2 a:close rotation @ can-move? !if
    old-rotation @ rotation !
 then ;

: down-key
  repeat
    y @ n:1+ x @ 2 a:close rotation @ can-move? if
      1 y n:+!
    else
      break
    then
  again

  store-tetromino
  delete-rows
  y @ 0 n:< if
    true gameover !
  else
    startx x !
    starty y !
    next-tetromino @ tetromino !
    next-rotation @ rotation !
    random-tetromino next-tetromino !
    next-tetromino @ random-rotation next-rotation ! 
  then ;

: play-area
  gameover @ !if 
    false drop-tetromino !
    key? null? !if
      [ ' left-key , ' right-key , ' up-key , ' down-key , ' down-key ] case
    else
      drop 
    then

    nk:timer? drop-tetromino @ not and if
      y @ n:1+ x @ 2 a:close rotation @ can-move? if
        1 y n:+!
      else
        store-tetromino
        delete-rows
        y @ 0 n:< if
          true gameover !
        else
          startx x !
          starty y !
          next-tetromino @ tetromino !
          next-rotation @ rotation !
          random-tetromino next-tetromino !
          next-tetromino @ random-rotation next-rotation !
        then
      then 
    then
  else
    key? null? !if
      init-game
    then drop
  then

  nk:widget if
    22 10 nk:layout-grid-begin
      0 22 0 10 nk:grid 0 "lightgray" nk:fill-rect
      0 22 0 10 nk:grid 0 1 "black" nk:stroke-rect
      draw-grid
      draw-tetromino
      gameover @ if
        0 22 0 10 nk:grid 
        "Game Over" "font2" nk:measure-font nk:pt>rect nk:center-rect
        "Game Over" "font2" [255,255,255,128] "black"
        nk:draw-text-centered
      then
    nk:layout-grid-end
  then ;

locals:
: next-area
  nk:widget if
    4 "minx" w:!
    0 "maxx" w:!
    4 "miny" w:!
    0 "maxy" w:!

    tetrominos next-tetromino @ a:_@ "coords" m:_@ next-rotation @ a:_@
    ( 1 a:@ "maxx" w:@ n:max "maxx" w:! 
      1 a:@ "minx" w:@ n:min "minx" w:!
      0 a:@ "maxy" w:@ n:max "maxy" w:!
      0 a:_@ "miny" w:@ n:min "miny" w:! 
    ) a:each! drop
     
    4 "maxx" w:@ "minx" w:@ n:- n:- blocksize n:* 2 n:/ "minx" w:@ blocksize n:* n:- "x" w:!
    4 "maxy" w:@ "miny" w:@ n:- n:- blocksize n:* 2 n:/ "miny" w:@ blocksize n:* n:- "y" w:!

    0 nk:rect@ "x" w:@ n:+ "x" w:!
    1 nk:rect@ "y" w:@ n:+ "y" w:!

    dup 0 "lightgray" nk:fill-rect 
    0 1 "black" nk:stroke-rect

    tetrominos next-tetromino @ a:_@ "coords" m:@ next-rotation @ a:_@
    ( a:open blocksize n:* "x" w:@ n:+ swap blocksize n:* "y" w:@ n:+
      blocksize dup 4 a:close dup>r 0 third "color" m:_@ colors swap a:_@ nk:fill-rect
      r> 0 2 "black" nk:stroke-rect ) a:each! 2drop
  then ;

: score-area
  nk:widget if
    { rows: 3, cols: 1, margin: 4 } nk:layout-grid-begin
      0 1 0 1 nk:grid nk:rect>local nk:grid-push
        score @ "Score: %d" s:strfmt nk:TEXT_LEFT "black" nk:label-colored
      1 1 0 1 nk:grid nk:rect>local nk:grid-push
        level @ "Level: %d" s:strfmt nk:TEXT_LEFT "black" nk:label-colored
      2 1 0 1 nk:grid nk:rect>local nk:grid-push
        rows @ "Rows: %d" s:strfmt nk:TEXT_LEFT "black" nk:label-colored
    nk:layout-grid-end 
  then ;

: info-area
  nk:widget if
    { rows: [100,80], cols:[100], rgap: 8 } nk:layout-grid-begin
      0 1 0 1 nk:grid nk:rect>local nk:grid-push next-area
      1 0 0 1 nk:grid nk:rect>local nk:grid-push score-area
    nk:layout-grid-end
  then ;

: main-render
  {
    bg: "darkgray",
    padding: [0,0],
    flags: [ @nk:WINDOW_NO_SCROLLBAR ]
  }
  nk:begin
    null { rows: 1, cols: [204, 116], margin: 4, cgap: 4 } nk:layout-grid-begin
      0 1 0 1 nk:grid nk:rect>local nk:grid-push play-area
      0 1 1 1 nk:grid nk:rect>local nk:grid-push info-area
    nk:layout-grid-end
  nk:end ;

: app:main
  null rand-pcg-seed
  ' init-game w:is nk:rendering
  new-win ' main-render -1 nk:render-timed ;

tetris

3 Likes

Hey Jalih, I had no idea that the original version was written in Fortan. That’s one blazingly fast programming language - one of only a handful of languages that’s a member of the elite petaflop club. I have enjoyed playing around with your Tetris 8th code - although I must admit I understand very little as I’m really just learning stack-based languages like GForth and 8th. :disguised_face: