' -------------------- tetris for Armmite F4 ----------------------
'
' the game has an array playfield of 20 deep and 10 wide
' the game plays on this field, and is pasted onto suitable graphics per
' platform. For F4 this is the ILI9341 320x240 display configuration
'   OPTION LCDPANEL ILI9341_16, PORTRAIT
'   OPTION TOUCH PB12, PC5
'
' the game is developped using a terminal for game control
' keyboard controls: a/s for left-right, h for rotate, d for down,
'                    space for fast down
'
' empty playfield cells have value 0, moving pieces 1-7 have color 1-7.
' locked pieces color -1..-7
'
' to do:
'   - implement hardware buttons
'
'
'
' -- 25-2-2021 ------------------------------------- Volhout -----



'------------------------ declare variables -----------------------

  Dim field(10,20)  'playing field 10x20,each cell is 0(empty) or a color
  Dim pieces(7,8,4) '7 different pieces, each 4 coordinates, 4 orientations
  Dim hiscore(8)    'the hiscore list 8 deep (no player names)
  Dim colors(7)     'the 7 colors for the 7 different pieces

   'read hiscore list
  On error skip 'if file does not exist:create it
  Open "hiscore" For input As #1
  If MM.Errno <>0 Then
    For i=1 To 8:hiscore(i)=0:Next i: GoSub writescores
    Open "hiscore" For input As #1
  EndIf
  For i=1 To 8:Input #1,hiscore(i):Next i
  Close #1

  'start endless music now
  Play volume 30    'adjust for best gameplay (0-100)
  restartplay       'start music (endless loop)


'---------------------- init varaibles ------------------------
startgame:
'init all the variables for a new game
  'clear field
  For i=1 To 10:For j=1 To 20: field(i,j)=0: Next j: Next i
  'load pieces in 4 orientations
  Restore
  For o=1 To 2: For i=1 To 7:For j=1 To 8:Read pieces(i,j,o):Next j:Next i:Next o
  Restore
  For o=3 To 4: For i=1 To 7:For j=1 To 8
    Read u
    pieces(i,j,o)=-u
  Next j:Next i:Next o
  'init the colors
  colors(1)=RGB(yellow):colors(2)=RGB(red):colors(3)=RGB(blue)
  colors(4)=RGB(green):colors(5)=RGB(cyan):colors(6)=RGB(magenta)
  colors(0)=RGB(black):colors(7)=RGB(140,140,140)

  'setup game start
  score=0 : gameend=0
  startx=5 : starty=1 : startorientation=1
  o=startorientation
  c=Int(1+7*Rnd(Pi)) : cc = c 'make the first piece a random
  GoSub size

'------------------------ GAME START --------------------

  'game intro
  CLS
  Load image "tet_strt.bmp",0,40
  Text 120,10,"ARMmite TETRIS F4","C",2,,RGB(white)
  Text 120,250,"use arrow keys","C",2,,RGB(white)
  Text 120,280,"up=rotate <spc>=down","C",2,,RGB(white)
  Pause 3000

  'game init
  CLS 'clear lcd screen
  GoSub lcdborder
  GoSub newpiece

  'start game
  SetTick 1000,godown                'this is the default game speed



'------------------------ GAME LOOP --------------------------

mainloop:
  'keyboard input
  a$ = Inkey$
  Pause 10 ' during this pause the "d" key can be enforced by the timer
  If a$="" GoTo mainloop
  'skip <esc> to speed up reponse with arrow keys (27 91 6x)
  acs=Asc(a$)
  If Acs=27 GoTo mainloop
  If Acs=91 GoTo mainloop

  'first erase the piece at the old location
  GoSub clearpiece

  'check for the change, and execute it
  'left (left arrow) and right (right arrow) movement
  If Asc(a$)=68 AND x+xl=>2 Then
      oke=1
      For i=1 To 7 Step 2 'check 4 coordinates per piece
        If field(x-1+pieces(c,i,o),y+pieces(c,i+1,o)) < 0 Then oke=0
      Next i
      If oke Then x=x-1
    EndIf
  If Asc(a$)=67 AND x+xr=<9 Then
      oke=1
      For i=1 To 7 Step 2 'check 4 coordinates per piece
        If field(x+1+pieces(c,i,o),y+pieces(c,i+1,o)) < 0 Then oke=0
      Next i
      If oke Then x=x+1
    EndIf

  'down will go automatic, but can also be manual
  '<space> will move the piece all the way down
  If a$="d" OR a$=" " Then
loop_d:
    score = score+1
    If score = 500 Then SetTick 800,godown  'speed up
    If y+yd=20 Then     'we are at the bottom
      GoSub lockpiece
      GoSub newpiece
    Else                'check if we can go down further
      oke=1
      For i=1 To 7 Step 2 'check 4 coordinates per piece
        If field(x+pieces(c,i,o),1+y+pieces(c,i+1,o)) < 0 Then oke=0
      Next i
      If oke Then       'if OK then we go down
        y=y+1
      Else              'if not, we lock the piece into place by changin color
        GoSub lockpiece
        If y=1 Then     'if this happens top line of field, it is game end
          gameend=1
        Else            'not game end: generate new piece
          GoSub newpiece
        EndIf
      EndIf
    EndIf
    If oke AND a$=" " Then GoTo loop_d    'if space was hit we cycle once more
  EndIf

  'rotate piece to 90 degrees orientation with arrow up
  If Asc(a$)=65 Then
    o=(o MOD 4)+1
    GoSub adjust
  EndIf

  'show the changes on the board
  GoSub showpiece       'update field with the new piece locatio
  'GoSub showfield      'prints the field on the serial terminal
  GoSub lcdfield        'shows the field in graphics

  If gameend=1 Then     'at game end process high score list
    hi=score
    GoSub sortscores
    GoSub highscoreshow
    'End
    'GoSub endgame      'nice graphics to show the end
                        'with text "press key for next game"
waitforkey:
    a$=Inkey$:If a$="" Then GoTo waitforkey
    GoTo startgame      'start new game
  EndIf

  GoTo mainloop

'---------------------- GAME SUBS ---------------------

'sort the high score list by shifting entries down to make room

'the highscore list is in reverse order, the lowest at (1) the highest
'at (8). A new score is added at (0) and the list is sorted.
sortscores:
  hiscore(0)=hi
  Sort hiscore()

writescores:  'overwrite highscore list
  Open "hiscore" For output As #2
  For i=1 To 8: Print #2, hiscore(i): Next i
  Close #2

  Return  'for both sortscores as writescores


'generate a random new piece after checking if there is a full line
newpiece:
  GoSub checkfullline
  x=startx:y=starty:o=startorientation
  c=cc
  GoSub size
  GoSub previewnext
  Return

'show the next piece (cc) in the preview window on lcd only
previewnext:
  Box 3,3,68,68,4,RGB(white),RGB(black)
  cc=Int(7*Rnd(6)+1)
  xc=3+34:yc=3+34
  For i=1 To 7 Step 2
    Box pieces(cc,i,4)*15+xc,pieces(cc,i+1,4)*15+yc,15,15,1,0,colors(cc)
  Next
  Return

'after rotation, the geometry has changed. make it fit on the field.
adjust:
  GoSub size
  If x+xl<1 Then x=1-xl
  If x+xr>10 Then x=10-xr
  If y+yu<1 Then y=1-yu
  Return

'calculate the max piece size around it's centre, after rotation
size:
  xl=Min(pieces(c,1,o),pieces(c,3,o),pieces(c,5,o),pieces(c,7,o))
  xr=Max(pieces(c,1,o),pieces(c,3,o),pieces(c,5,o),pieces(c,7,o))
  yu=Min(pieces(c,2,o),pieces(c,4,o),pieces(c,6,o),pieces(c,8,o))
  yd=Max(pieces(c,2,o),pieces(c,4,o),pieces(c,6,o),pieces(c,8,o))
  Return

'paste the piece into the playing field at location x,y
showpiece:
  For i=1 To 7 Step 2 '4 coordinates per piece
    field(x+pieces(c,i,o),y+pieces(c,i+1,o))=c
  Next i
  Return

'erase the (former) position of the piece from the playing field
'by setting the field positions back to 0
clearpiece:
  For i=1 To 7 Step 2 '4 coordinates per piece
    field(x+pieces(c,i,o),y+pieces(c,i+1,o))=0
  Next i
  Return

'lock a piece in position x,y by inverting the color
lockpiece:
  For i=1 To 7 Step 2 '4 coordinates per piece
    field(x+pieces(c,i,o),y+pieces(c,i+1,o))=-c
  Next i
  Return

'show the field array on the serial terminal. 0=empty
'1..7 = active piece,-7...-1 = locked piece
showfield:
  For j = 1 To 20
    For i = 1 To 10
       Print field(i,j);
    Next
    Print
  Next
  Return

'draw the playing field borders and fixed texts on the LCD
lcdborder:
  Box 80,3,160,310,4,RGB(white),RGB(black)
  Box 3,3,68,68,4,RGB(white),RGB(black)
  Text 65,80,"score","R",2,,RGB(cyan)
  Text 65,130,"high","R",2,,RGB(cyan)
  GoSub highscoreshow
  Return

'interrupt timer pushes a "d" into the main loop to force a piece to move down
Sub godown
  a$="d"
End Sub

'timed interrupt to restart the gameboy tetris tune when ended (1:16 duration)
Sub restartplay
  Play wav "tet16cut.wav",restartplay 'start music, and restart when end
End Sub


'draw the active field into the playing field border and update the score
lcdfield:
  For i=1 To 10
    For j=1 To 20
      lcdx=78+(i-0.5)*15
      lcdy=(j-0.5)*15
      Box lcdx,lcdy,15,15,1,0,colors(Abs(field(i,j)))
    Next j
  Next i
  'score
  Text 65,100,Str$(score),"R",2,,RGB(yellow)
Return

'check if we have a full line, and remove it by copying the upper
'part of the field 1 row down. Up:y=1, down:y=20
checkfullline:
  For j=2 To 20
    oke=1
    For i=1 To 10
      If field(i,j)=0 Then oke=0
    Next i
    If oke Then
      For jj=j-1 To 1 Step -1
        For i=1 To 10
          field(i,jj+1)=field(i,jj)
        Next i
      Next jj
    EndIf
  Next j
  Return

'show the highscore values on the LCD, hiscore(8)on top
highscoreshow:
  For i=1 To 8
    Text 65,310-20*i,Str$(hiscore(i)),"R",2,,RGB(yellow)
  Next
  Return



'------------------------ PIECES --------------------------

'pieces horizontal view
Data 0,0,-1,0,0,1,1,1   'S
Data 0,0,1,0,1,1,0,1    'cube
Data 0,0,-1,0,1,0,2,0   'I
Data 0,0,1,0,0,1,-1,1   'Z
Data 0,0,1,0,-1,0,-1,1  'L
Data 0,0,-1,0,1,0,1,1   'J
Data 0,0,-1,0,1,0,0,1   'T

'pieces vertical view
Data 0,0,0,1,1,0,1,-1   'S
Data 0,0,1,0,1,1,0,1    'cube (no difference)
Data 0,0,0,-1,0,1,0,2   'I
Data 0,0,0,-1,1,0,1,1   'Z
Data 0,0,0,-1,0,1,1,1   'L
Data 0,0,0,-1,0,1,-1,1  'J
Data 0,0,0,-1,0,1,1,0   'T
