<?xml version="1.0"?>

<Module name="Tetris">
<Comment> 
And here is a real application. 
</Comment>
<Group name="Introduction">
<Comment/>
<Word name="TheTetrisGame">
<Comment/>
<Definition> 
Text: TheTetrisGame
  This program is a port of the &quot;tt.pfe&quot; Tetris, written by Dirk Zoller
for PFE. It is ANSI compliant in the original, and more or less so in
the present version. 
  Tetris has been ported to Holon by Thomas Beierlein and reworked by
Wolf Wejgaard. Tetris is included with Holon as a working demonstration
program. 
</Definition>
</Word>
<Word name="TheRules">
<Comment/>
<Definition> 
Text: TheRules
   Bricks of different shapes appear at random and drift down into a
pit. You can move the current brick left or right with the 'left' or
'right' cursor keys, or rotate the brick in 90 degree increments with
the 'up' key. The aim is to build complete lines with no gaps. A
completed line is removed, providing space for more bricks. Bricks and
lines contribute to the score.
   The 'down' key lets the brick fall straight down, the space bar
halts the game until you press space again, and 'Q' ends the current
game. 
</Definition>
</Word>
</Group>
<Group name="Pit">
<Comment/>
<Word name="PitWidth">
<Comment/>
<Definition> 
12 constant PitWidth 
</Definition>
</Word>
<Word name="PitDepth">
<Comment/>
<Definition> 
16 constant PitDepth 
</Definition>
</Word>
<Word name="DefinePit">
<Comment> 
Defining word for a pit. Creates the data array and defines the action
of the pit. The pit delivers the address of the element i,j. 
</Comment>
<Definition> 
: DefinePit
     create  PitWidth PitDepth * allot
     does&gt;   rot PitWidth * rot + +  ;   ( i j -- adr ) 
</Definition>
</Word>
<Word name="Pit">
<Comment> 
Defines the pit. 
</Comment>
<Definition> 
DefinePit Pit 
</Definition>
</Word>
<Word name="PitX0">
<Comment> 
Upper left corner of the pit. 
</Comment>
<Definition> 
5 constant PitX0 
</Definition>
</Word>
<Word name="PitY0">
<Comment/>
<Definition> 
4 constant PitY0 
</Definition>
</Word>
<Word name="Position">
<Comment> 
Sets cursor to position row col in the pit. 
</Comment>
<Definition> 
: Position  ( row col -- )
     2* PitX0 + swap PitY0 + xy ; 
</Definition>
</Word>
<Word name="Black">
<Comment> 
Background colour. 
</Comment>
<Definition> 
0 constant Black 
</Definition>
</Word>
<Word name="Draw?">
<Comment/>
<Definition> 
Flag Draw? 
</Definition>
</Word>
<Word name="2emit">
<Comment/>
<Definition> 
: 2emit   ( c -- )
     dup emit emit ; 
</Definition>
</Word>
<Word name="Stone">
<Comment> 
Draws or undraws two characters with attribute b (=0, if blank) 
</Comment>
<Definition> 
: Stone   ( b -- )
     Attribute swap
     Draw? over and 
     if   8 + Black + is Attribute  178 2emit  
     else drop Black is Attribute 2 spaces
     then is Attribute  ; 
</Definition>
</Word>
<Word name="EmptyPit">
<Comment> 
Fills pit array with zeroes. 
</Comment>
<Definition> 
: EmptyPit
     0 0 Pit PitWidth PitDepth * erase  ; 
</Definition>
</Word>
</Group>
<Group name="Drawing">
<Comment/>
<Word name="Wall">
<Comment/>
<Definition> 
: Wall
     177 2emit  ; 
</Definition>
</Word>
<Word name="DrawBottom">
<Comment> 
Draws the bottom of the pit. 
</Comment>
<Definition> 
: DrawBottom
     PitDepth -1 Position  
     PitWidth 2+ 0 do Wall loop  ; 
</Definition>
</Word>
<Word name="DrawFrame">
<Comment> 
Draws the border of the pit. 
</Comment>
<Definition> 
: DrawFrame
     PitDepth 0 
     do i -1 Position Wall  i PitWidth Position Wall loop 
     DrawBottom ; 
</Definition>
</Word>
<Word name="DrawLine">
<Comment/>
<Definition> 
: DrawLine  ( line -- )
     dup 0 Position  
     PitWidth 0 do dup i Pit c@ stone loop  drop ; 
</Definition>
</Word>
<Word name="DrawPit">
<Comment> 
Draws the contents of the pit. 
</Comment>
<Definition> 
: DrawPit
     PitDepth 0 do i DrawLine loop ; 
</Definition>
</Word>
<Word name="ShowHelp">
<Comment/>
<Definition> 
: ShowHelp
     50  1 xy  .&quot; ***** T E T R I S *****&quot;
     50  2 xy  .&quot; =======================&quot;
     50  5 xy  .&quot; left :   Move left&quot;
     50  6 xy  .&quot; right:   Move right&quot;
     50  7 xy  .&quot; up   :   Rotate&quot;
     50  8 xy  .&quot; down :   Drop&quot;
     50  9 xy  .&quot; space:   Pause&quot;
     50 11 xy  .&quot; 'Q'  :   Quit&quot;
       3 1 xy  .&quot; Score:&quot;
      16 1 xy  .&quot; Pieces:&quot;
      31 1 xy  .&quot; Levels:&quot;
     ; 
</Definition>
</Word>
<Word name="Score">
<Comment/>
<Definition> 
0 integer Score 
</Definition>
</Word>
<Word name="Pieces">
<Comment/>
<Definition> 
0 integer Pieces 
</Definition>
</Word>
<Word name="Levels">
<Comment/>
<Definition> 
0 integer Levels 
</Definition>
</Word>
<Word name="Delay">
<Comment/>
<Definition> 
0 integer Delay 
</Definition>
</Word>
<Word name="UpdateScore">
<Comment> 
display current score 
</Comment>
<Definition> 
: UpdateScore
     10 1 xy  bold Score   3 .r normal
     24 1 xy  Pieces  3 .r
     39 1 xy  Levels  3 .r  ; 
</Definition>
</Word>
<Word name="Refresh">
<Comment> 
Redraw everything on the screen. 
</Comment>
<Definition> 
: Refresh
     page  DrawFrame  DrawPit  ShowHelp  UpdateScore  ; 
</Definition>
</Word>
</Group>
<Group name="Bricks">
<Comment/>
<Word name="Brick:">
<Comment> 
Defining word for bricks. 
</Comment>
<Definition> 
: Brick:
     create  8 0 do c, loop   ( 8bytes -- )
     does&gt;  ; 
</Definition>
</Word>
<Word name="Brick1">
<Comment> 
The shape and the colour of the brick are defined by the numbers &lt;&gt; 0. 
</Comment>
<Definition> 
1 1 1 0 
0 1 0 0   Brick: Brick1 
</Definition>
</Word>
<Word name="Brick2">
<Comment/>
<Definition> 
2 2 2 2
0 0 0 0   Brick: Brick2 
</Definition>
</Word>
<Word name="Brick3">
<Comment/>
<Definition> 
0 3 3 3 
0 3 0 0   Brick: Brick3 
</Definition>
</Word>
<Word name="Brick4">
<Comment/>
<Definition> 
4 4 4 0
0 0 4 0   Brick: Brick4 
</Definition>
</Word>
<Word name="Brick5">
<Comment/>
<Definition> 
0 5 5 0
0 5 5 0   Brick: Brick5 
</Definition>
</Word>
<Word name="Brick6">
<Comment/>
<Definition> 
6 6 0 0 
0 6 6 0   Brick: Brick6 
</Definition>
</Word>
<Word name="Brick7">
<Comment/>
<Definition> 
0 7 7 0
7 7 0 0   Brick: Brick7 
</Definition>
</Word>
<Word name="Bricks">
<Comment> 
Table of brick execution tokens. Note: ',' is a compiling word of the
host. 
</Comment>
<Definition> 
create Bricks
     ' Brick1 ,  ' Brick2 ,  ' Brick3 ,  ' Brick4 ,
     ' Brick5 ,  ' Brick6 ,  ' Brick7 , 
</Definition>
</Word>
<Word name="Scratch">
<Comment/>
<Definition> 
create Scratch   16 allot 
</Definition>
</Word>
<Word name="scratch!">
<Comment> 
Stores byte b in element i,j of the scratch array. Sometimes it is
helpful to note the stack action in stack diagrams. 
</Comment>
<Definition> 
: scratch!   ( b i j -- )
     scratch                       \ -- b i j adr
     rot                           \ -- b j adr i
     4 *                           \ -- b j adr n
     rot                           \ -- b adr n j
     +                             \ -- b adr n2
     +                             \ -- b adr2
     c!  ; 
</Definition>
</Word>
<Word name="Brick">
<Comment> 
Contains the pattern of the current brick. 
</Comment>
<Definition> 
create Brick  16 allot 
</Definition>
</Word>
<Word name="brick@">
<Comment/>
<Definition> 
: brick@   ( i j -- b )
     brick rot 4 * rot + + c@  ; 
</Definition>
</Word>
<Word name="BrickValue">
<Comment> 
The value in &quot;score&quot; for each brick's difficulty. 
</Comment>
<Definition> 
create BrickValue
     1 c,  2 c,  3 c,  3 c,  4 c,  5 c,  5 c, 
</Definition>
</Word>
<Word name="isBrick">
<Comment> 
Inserts the current brick's shape into &quot;brick&quot;. xt is the execution
token of a brick word, it is executed and delivers the address of the
brick's data. 
</Comment>
<Definition> 
: isBrick  ( xt -- )
     Brick 16 erase  
     execute        ( -- adr )
     8 times dup i + c@  Brick 4 + i + c!  loop  
     drop  ; 
</Definition>
</Word>
</Group>
<Group name="ShowBrick">
<Comment/>
<Word name="bRow">
<Comment> 
Brick position. 
</Comment>
<Definition> 
0 integer bRow 
</Definition>
</Word>
<Word name="bCol">
<Comment/>
<Definition> 
0 integer bCol 
</Definition>
</Word>
<Word name="Random">
<Comment> 
Leaves a random number smaller than max. 
</Comment>
<Definition> 
: Random   ( max -- u )  
     time@ + + +  swap mod  ; 
</Definition>
</Word>
<Word name="NewBrick">
<Comment> 
Choose a new brick randomly and add to score. 
</Comment>
<Definition> 
: NewBrick
     1 add Pieces  7 Random
     Bricks over cells + @ isBrick
     BrickValue swap chars + c@ add Score  ; 
</Definition>
</Word>
<Word name="RotateLeft">
<Comment> 
Rotates current brick left. 
</Comment>
<Definition> 
: RotateLeft
     4 0 do 4 0 do j i brick@  3 i - j scratch! loop loop
     Scratch Brick 16 move  ; 
</Definition>
</Word>
<Word name="RotateRight">
<Comment> 
Rotates current brick right. 
</Comment>
<Definition> 
: RotateRight
     4 0 do 4 0 do j i brick@  i 3 j - scratch! loop loop
     Scratch Brick 16 move  ; 
</Definition>
</Word>
<Word name="Rotate">
<Comment> 
Rotates brick according to flag f. 
</Comment>
<Definition> 
: Rotate   ( f -- )
     if RotateRight else RotateLeft then  ; 
</Definition>
</Word>
<Word name="DrawBrick">
<Comment/>
<Definition> 
: DrawBrick  
     4 0 do 4 0 do
          j i brick@ 0&lt;&gt;
          if brow j + bcol i + Position  j i brick@ Stone then
     loop loop  ; 
</Definition>
</Word>
<Word name="ShowBrick">
<Comment/>
<Definition> 
: ShowBrick   
     set Draw?  DrawBrick  ; 
</Definition>
</Word>
<Word name="HideBrick">
<Comment/>
<Definition> 
: HideBrick   
     clear Draw?  DrawBrick  ; 
</Definition>
</Word>
</Group>
<Group name="MoveBrick">
<Comment/>
<Word name="PutBrick">
<Comment> 
Put the brick into the pit. 
</Comment>
<Definition> 
: PutBrick  
     4 0 do 4 0 do
          j i brick@  0&lt;&gt;
          if brow j + bcol i + Pit  j i brick@ swap c! then
     loop loop  ; 
</Definition>
</Word>
<Word name="Brick?">
<Comment> 
Could the brick be there? 
</Comment>
<Definition> 
: Brick?  ( row col -- f )
     4 0 do  4 0 do
          j i brick@ 0&lt;&gt;
          if   over j +  over i +
               over dup 0&lt;  swap PitDepth &gt;= or
               over dup 0&lt;  swap PitWidth &gt;= or
               2swap Pit c@  0&lt;&gt;
               or or if unloop unloop 2drop false exit then
          then
     loop loop  2drop true ; 
</Definition>
</Word>
<Word name="RemoveBrick">
<Comment> 
Removes the brick from that position. 
</Comment>
<Definition> 
: RemoveBrick  
     brow bcol
     4 0 do  4 0 do
          j i brick@  0&lt;&gt;
          if over j +  over i + Pit  0 swap c! then
     loop loop 
     2drop ; 
</Definition>
</Word>
<Word name="MoveBrick?">
<Comment> 
Tries to move the brick. 
</Comment>
<Definition> 
: MoveBrick?  ( rows cols -- f )
     RemoveBrick
     swap brow +  swap bcol +  
     2dup Brick?
     if   HideBrick  is bCol is bRow  
          ShowBrick  PutBrick  true
     else 2drop  PutBrick  false
     then ; 
</Definition>
</Word>
<Word name="RotateBrick">
<Comment> 
f = left/right. 
</Comment>
<Definition> 
: RotateBrick  ( f -- )
     brow 0&lt; if drop exit then          \ no space yet for rotating
     RemoveBrick dup Rotate
     brow bcol Brick?
     over 0= Rotate
     if   HideBrick Rotate PutBrick ShowBrick  
     else drop   
     then  ; 
</Definition>
</Word>
<Word name="InsertBrick?">
<Comment> 
Introduces a new brick. 
</Comment>
<Definition> 
: InsertBrick?  ( row col -- flag )
     2dup Brick?
     if   is bcol is brow  PutBrick  DrawBrick  true
     else 2drop false  
     then  ; 
</Definition>
</Word>
<Word name="DropBrick">
<Comment> 
Moves the brick down fast. 
</Comment>
<Definition> 
: DropBrick
     begin 1 0 MoveBrick? 0= until ; 
</Definition>
</Word>
</Group>
<Group name="Lines">
<Comment/>
<Word name="MoveLine">
<Comment/>
<Definition> 
: MoveLine  ( from to -- )
     over 0 Pit  over 0 Pit  PitWidth move  DrawLine
     dup 0 Pit PitWidth erase  DrawLine  ; 
</Definition>
</Word>
<Word name="LineFull?">
<Comment> 
Leaves true, if line is filled with bricks. 
</Comment>
<Definition> 
: LineFull?   ( line# - f )
     true 
     PitWidth 0 
     do  over i Pit c@ 0= if drop false leave then  loop 
     nip  ; 
</Definition>
</Word>
<Word name="RemoveLines">
<Comment/>
<Definition> 
: RemoveLines
     PitDepth dup
     begin swap
           begin  1- dup 0&lt; if 2drop exit then  
                  dup LineFull?
           while  1 add Levels  10 add Score 
           repeat
           swap 1- 2dup &lt;&gt; if  2dup MoveLine  then
     again ; 
</Definition>
</Word>
</Group>
<Group name="Playing">
<Comment/>
<Word name="End?">
<Comment/>
<Definition> 
Flag End? 
</Definition>
</Word>
<Word name="AskMessage">
<Comment> 
Shows the message &quot;adr and waits for a key, leaves key code. 
</Comment>
<Definition> 
: AskMessage   ( &quot;adr -- c )
     @xy rot SaveScreen  
     50 20 xy .&quot; --- &quot;  &quot;type .&quot;  ---&quot; key 
     RestoreScreen -rot xy  ; 
</Definition>
</Word>
<Word name="&gt;Upper">
<Comment> 
Converts character c1 to uppercase c2. 
</Comment>
<Definition> 
: &gt;Upper  ( c1 -- c2)
     dup [char] a &gt;= over [char] z &lt;= and if $20 - then ; 
</Definition>
</Word>
<Word name="CharCases">
<Comment/>
<Definition> 
: CharCases   ( c -- )
     case &gt;Upper 
           09 of Halt endof      \ Tab switches to host in Coroutine
          'bl of &quot;  paused &quot; AskMessage drop  endof
          [char] Q of set End?  endof
     drop endcase  ; 
</Definition>
</Word>
<Word name="FuncCases">
<Comment/>
<Definition> 
: FuncCases   ( c -- )
     case 
          75 ( left ) of  0 -1 MoveBrick? drop   endof
          77 ( right) of  0  1 MoveBrick? drop   endof
          72 ( up )   of  0 RotateBrick  endof
          80 ( down ) of  DropBrick  endof
     drop endcase  ; 
</Definition>
</Word>
<Word name="Interaction">
<Comment/>
<Definition> 
: Interaction  ( -- )
     key ?dup if CharCases else FuncCases then  ; 
</Definition>
</Word>
<Word name="Initialize">
<Comment> 
Prepares for playing. 
</Comment>
<Definition> 
: Initialize
     0 is Score  0 is Pieces  0 is Levels  100 is Delay 
     emptyPit  Refresh  clear End?  !sp  ; 
</Definition>
</Word>
<Word name="AdjustDelay">
<Comment> 
Makes it faster with increasing score. 
</Comment>
<Definition> 
: AdjustDelay
     Levels 
          dup  50 &lt; if  100 swap -  
     else dup 100 &lt; if  62 swap 4 / -  
     else dup 500 &lt; if  31 swap 16 / - 
     else drop 0
     then then then
     is Delay  ; 
</Definition>
</Word>
<Word name="?Interaction">
<Comment> 
User interaction? 
</Comment>
<Definition> 
: ?Interaction
     Delay msec 
     key? if Interaction then  ; 
</Definition>
</Word>
<Word name="StartBrick?">
<Comment> 
Tests: ... Change the insert method while Tetris runs... 
(put the backslash in front of the other line and reload the word). 
</Comment>
<Definition> 
: StartBrick?   ( -- f )
\     NewBrick  -1  8 InsertBrick?           \ insert in center
     NewBrick  -1 PitWidth 2/ Random InsertBrick?    \ insert randomly
     ; 
</Definition>
</Word>
<Word name="PlayGame">
<Comment> 
Play one Tetris game. 
</Comment>
<Definition> 
: PlayGame 
     begin  StartBrick?
     while  begin  4 0 do ?Interaction loop 
                   End? if exit then
                   1 0 moveBrick?  0=
            until  
            RemoveLines  UpdateScore  AdjustDelay  
     repeat  ; 
</Definition>
</Word>
</Group>
<Group name="Tetris">
<Comment> 
This is the final group in the turnkey application. 
</Comment>
<Word name="TetrisGame">
<Comment> 
The final runtime word... Plays the game. 
</Comment>
<Definition> 
: TetrisGame
     Initialize CurOff &quot; Press any key&quot; AskMessage drop  
     begin  PlayGame 
            &quot;  Again? (Y/N)&quot; AskMessage &gt;Upper [char] Y =
     while  Initialize
     repeat
     page  50 20 xy  .&quot; --- bye (see you later) --- &quot; ; 
</Definition>
</Word>
<Word name="Tetris">
<Comment> 
The Tetrisprogram running as the main task. If you start it from the
host, the host waits for the program to end. 
</Comment>
<Definition> 
: Tetris
     Console TetrisGame  ; 
</Definition>
</Word>
<Word name="TheTetris">
<Comment> 
Makes Tetris the startword of a turnkey version of the tetris program.
Press Ctl+F7=Turnkey to save the code as file TURNKEY.EXE. 
</Comment>
<Definition> 
( TheTetris )
     Program: Tetris 
</Definition>
</Word>
</Group>
<Group name="TetrisTask">
<Comment> 
Use this group during development. Let Tetris run as a task in the
target system, and work on the running program. 
</Comment>
<Word name="TetrisTask">
<Comment> 
Creates the Tetris task. 
</Comment>
<Definition> 
Task TetrisTask 
</Definition>
</Word>
<Word name="TaskProgram">
<Comment> 
The task program which is started with the task. 
</Comment>
<Definition> 
: TaskProgram
     Screen TaskKeys TetrisGame  ; 
</Definition>
</Word>
<Word name="StartTetris">
<Comment> 
Starts the Tetris task. 
</Comment>
<Definition> 
: StartTetris
     make TetrisTask TaskProgram  start TetrisTask  ; 
</Definition>
</Word>
<Word name="StopTetris">
<Comment> 
Stops the task. 
</Comment>
<Definition> 
: StopTetris
     stop TetrisTask  ; 
</Definition>
</Word>
</Group>
</Module>
