www.jupiter-ace.co.uk

Previous Page > Listings Index > SokoACE listing


SokoAce
by
Ricardo Fernandes Lopes
( SokoACE )
( Sokoban game for the Jupiter ACE )

( (c) 2006  by Ricardo Fernandes Lopes )
( under the GNU General Public License )

( Load and play the game with: )
( LOAD SOKOACE PLAY            )

( Graphic characters )
CREATE T 56 ALLOT
: GR 8 * T + DUP 8 + SWAP DO I C! LOOP ;
: SETGR 56 0 DO T I + C@ 10240 I + C! LOOP ;
16 BASE C!
00 00 00 00 00 00 00 00 00 GR ( Not used      )
00 00 00 18 18 00 00 00 01 GR ( Target        )
3C 42 81 FF 99 99 7E 3C 02 GR ( Soko          )
3C 42 BD FF BD 99 7E 3C 03 GR ( Soko + Target )
00 7E 42 42 42 42 7E 00 04 GR ( Box           )
00 7E 42 5A 5A 42 7E 00 05 GR ( Box + Target  )
FF AB D5 AB D5 AB D5 FF 06 GR ( Wall          )
DECIMAL

( Screen elements)
01 CONSTANT TARGET  ( 00 0001b )
02 CONSTANT SOKO    ( 00 0010b )
04 CONSTANT BOX     ( 00 0100b )
06 CONSTANT WALL    ( 00 0110b )
32 CONSTANT BL      ( 10 0000b )

: KEY ( -- c ,wait for keypress)
  BEGIN INKEY 0= UNTIL
  BEGIN INKEY ?DUP UNTIL ;

: >UPPER ( c1 -- c2 ,convert to uppercase)
  DUP 96 > IF 223 AND THEN ;

: IN? ( -- n , get number from user)
  QUERY NUMBER DUP
  IF 4181 =         ( avoid float numbers)
    IF DROP 0 THEN
  THEN ;

: +! ( n adr -- )  SWAP OVER @ + SWAP ! ;
: INC ( a -- )   1 SWAP +! ;
: DEC ( a -- )  -1 SWAP +! ;

0 VARIABLE SOKO>   ( holds SOKO screen address )
0 VARIABLE #BOX    ( number of boxes out of target )
0 VARIABLE #STEP   ( number of steps )
0 VARIABLE #PUSH   ( number of pushes )

( Movement directions)
-32 CONSTANT UP
 32 CONSTANT DOWN
 -1 CONSTANT LEFT
  1 CONSTANT RIGHT

: STEP ( a1 -- , move SOKO one step)
  DUP C@ TARGET AND SOKO OR OVER C! ( place SOKO in new position )
  SOKO> @                           ( previous SOKO position )
  DUP C@ TARGET AND                 ( check previous contents )
  IF TARGET ELSE BL THEN            ( Target or Blank )
  SWAP C!                           ( remove SOKO from old position )
  SOKO> !
  #STEP INC ;

: PUSH ( a1 a2 -- , push a BOX)
  OVER C@ TARGET AND IF #BOX INC THEN     ( Box entered a target region)
  DUP  C@ DUP TARGET AND IF #BOX DEC THEN ( Box exited a target area)
  TARGET AND BOX OR SWAP C!               ( Move Box)
  STEP                                    ( Move Soko)
  #STEP DEC                               ( Inc Pushes but not Steps)
  #PUSH INC ;

: GO ( dir -- , try to move Soko in the specified direction)
  SOKO> @ OVER +         ( next position )
  DUP C@ WALL =
  IF DROP DROP           ( if WALL, do nothing )
  ELSE
    DUP C@ DUP BL = SWAP TARGET = OR 
    IF STEP DROP         ( if Blank or Target, do Step)
    ELSE
      SWAP OVER +        ( over next position )
      DUP C@ DUP BL = SWAP TARGET = OR 
      IF   PUSH          ( if Blank or Target, do Push )
      ELSE DROP DROP     ( else, do nothing )
      THEN
    THEN
  THEN ;

: WALK ( c -- c, interpret key and move Soko )
  DUP ASCII I = IF UP    GO ELSE
  DUP ASCII K = IF DOWN  GO ELSE
  DUP ASCII J = IF LEFT  GO ELSE
  DUP ASCII L = IF RIGHT GO
  THEN THEN THEN THEN ;

: .#### ( a -- , formatted score type)
  @ 0 <# # # # # #> TYPE ;

: .SCORE ( update score )
  3 28 AT #BOX  @ . 
  5 28 AT #STEP .####
  7 28 AT #PUSH .#### ;

: .FRAME ( Draw screen )
  CLS ." ______SokoACE_______ version 1.0"
   3 22 AT ." BOXES ?"
   5 22 AT ." STEPS"
   7 21 AT ." PUSHES"
  10 21 AT ." I Up"
  11 21 AT ." K Down"
  12 21 AT ." J Left"
  13 21 AT ." L Right"
  15 21 AT ." N Level + 1"
  16 21 AT ." P Level - 1"
  17 21 AT ." M Level ?"
  18 21 AT ." R Restart"
  20 21 AT ." Q Quit"
  22  0 AT ." _by Ricardo F Lopes_   c 2006" ;

: SCAN ( Scan screen map for Soko position and count boxes out of target)
  0 #BOX ! ( reset number of boxes)
  9920 9280
  DO
    I C@
    DUP SOKO = IF I SOKO> ! ELSE ( Search for SOKO start position)
    DUP BOX  = IF #BOX INC       ( Count Boxes out of target)
    THEN THEN DROP
  LOOP ;

20 21 * CONSTANT MAPSIZE ( Map size = 20x20 chars + 20 chars for map label line )
CREATE MAPS MAPSIZE 30 * ALLOT ( Room for 30 maps )
: MAP ( level -- a , return a map address)
  MAPSIZE * MAPS + ;

: MAP>SCR ( level -- , copy level map to screen)
  MAP 22 1
  DO
    I 0 AT DUP 20 TYPE CR 20 +
  LOOP DROP ;

0 VARIABLE LEVEL

: INIT ( level -- , Initialize Level)
  0 #STEP ! ( reset steps count )
  0 #PUSH ! ( reset pushes count )
  .FRAME
  0 MAX 29 MIN DUP LEVEL !
  MAP>SCR
  SCAN ;

: MAP? ( c -- c , Change level)
  DUP ASCII N = IF LEVEL @ 1+ INIT ELSE ( Next level)
  DUP ASCII P = IF LEVEL @ 1- INIT ELSE ( Previous level)
  DUP ASCII M = IF   IN?      INIT ELSE ( Entered level number)
  DUP ASCII R = IF LEVEL @    INIT      ( Re-start same level)
  THEN THEN THEN THEN ;

: PLAY ( Main code, run this to play SokoACE)
  SETGR     ( initialize graphics )
  0 INIT    ( start the first level )
  BEGIN
    .SCORE       ( Update Score)
    KEY >UPPER   ( Get key pressed )
    WALK         ( Move SOKO )
    MAP?         ( Check for level request)
    1 9 AT
    #BOX @ 0=    ( No boxes left?)
    IF
      ." Done !" 100 50 BEEP 75 25 BEEP ( Level completed !)
    THEN 
    ASCII Q =    ( Quit?)
  UNTIL
  ." Quit." ;

( Before saving this program, populate the  )
( level slots with maps created with SokoED.)
( To do that, use:   )

( 0 MAP 0 BLOAD MAP1 )
( 1 MAP 0 BLOAD MAP2 )
( 2 MAP 0 BLOAD MAP3 )
( etc..)

( Where 0, 1, 2,.. 29 are the available level numbers)
( and MAP1, MAP2, etc. are map files created with SokoED)