File:  [gforth] / gforth / arch / misc / sokoban.fs
Revision 1.1: download - view: text, annotated - select for diffs
Thu May 29 19:43:11 1997 UTC (26 years, 10 months ago) by pazsan
Branches: MAIN
CVS tags: v0-6-2, v0-6-1, v0-6-0, v0-5-0, v0-4-0, HEAD
Added port of gforth to misc (original version, slow) to CVS archive

    1: \ sokoban - a maze game in FORTH
    2: 
    3: \ Contest from Rick VanNorman in comp.lang.forth
    4: 
    5: \ SOKOBAN
    6: 
    7: \ Sokoban is a visual game of pushing.  You (Soko) are represented by the
    8: \ at-sign "@"  You may move freely through the maze on unoccupied spaces.
    9: \ The dollar-signs "$" are the rocks you have to push. You can only push
   10: \ one rock at a time, and cannot push a rock through a wall "#" or over
   11: \ another rock. The object is to push the rocks to their goals which are
   12: \ indicated by the periods ".".  There are 50 levels, the first of which
   13: \ is shown below.
   14: 
   15: \ program is ANS FORTH with environmental dependency of case-insensitiv
   16: \ source. Tested with gforth, bigFORTH and pfe
   17: 
   18: \ bell (7) is replaced with "Wuff!" ;-)
   19: \ (this is a german joke)
   20: \ I don't like the keyboard interpreting CASE-statement either, but
   21: \ was to lazy to use a table.
   22: \ I could have used blocks as level tables, but as I don't have a good
   23: \ block editor for gforth now, I let it be.
   24: 
   25: Create pn-tab ," 000102030405060708091011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980"
   26: 
   27: : pn    ( n -- )  2* pn-tab 1+ + 2 type ;
   28: : ;pn   [char] ; emit pn ;
   29: : ESC[  &27 emit [char] [ emit ;
   30: : at-xy 1+ swap 1+ swap ESC[ pn ;pn [char] H emit ;
   31: : page  ESC[ ." 2J" 0 0 at-xy ;
   32: 
   33: 40 Constant /maze  \ maximal maze line
   34: 
   35: Create maze  1 cells allot /maze 25 * allot  \ current maze
   36: Variable mazes   0 mazes !  \ root pointer
   37: Variable soko    0 soko !   \ player position
   38: Variable >maze   0 >maze !  \ current compiled maze
   39: 
   40: \ score information
   41: 
   42: Variable rocks     0 rocks !  \ number of rocks left
   43: Variable level#    0 level# ! \ Current level
   44: Variable moves     0 moves !  \ number of moves
   45: Variable score     0 score !  \ total number of scores
   46: 
   47: UNLOCK
   48: >MINIMAL
   49: 
   50: : new-maze ( n -- addr ) \ add a new level
   51:     T here mazes rot 1 H ?DO T @ H LOOP T !
   52:     0 , 0 , here >maze ! 0 , H ;
   53: : count-$ ( addr u -- n )  0 rot rot
   54:     over + swap ?DO  I c@ [char] $ = -  LOOP ;
   55: : m: ( "string" -- )  \ add a level line (top first!)
   56:     -1 parse tuck 2dup count-$
   57:     T >maze @ 1 cells - dup @ rot + swap ! H
   58:     bounds ?DO  I c@ T c, H  LOOP
   59:     /maze swap - 0 ?DO  bl T c, H  LOOP
   60:     T >maze @ here over cell+ - swap ! H ;
   61: 
   62: LOCK
   63: 
   64: : maze-field ( -- addr n )
   65:     maze dup cell+ swap @ chars ;
   66: 
   67: : .score ( -- )
   68:     ." Level: " level# @ 2 .r ."  Score: " score @ 4 .r
   69:     ."  Moves: " moves @ 6 .r ."  Rocks: " rocks @ 2 .r ;
   70: 
   71: : .maze ( -- )  \ display maze
   72:     0 0 at-xy  .score
   73:     cr  maze-field over + swap
   74:     DO  I /maze type cr  /maze chars  +LOOP ;
   75: 
   76: : find-soko ( -- n )
   77:     maze-field 0
   78:     DO  dup I chars + c@ [char] @ =
   79: 	IF  drop I  UNLOOP  EXIT  THEN
   80:     LOOP  true abort" No player in field!" ;
   81: 
   82: : level ( n -- flag )  \ finds level n
   83:     dup level# !
   84:     mazes  swap 0
   85:     ?DO  @  dup 0= IF  drop false  UNLOOP  EXIT  THEN  LOOP
   86:     cell+ dup @ rocks !
   87:     cell+ dup @ cell+ maze swap chars move
   88:     find-soko soko ! true ;
   89: 
   90: \ now the playing rules as replacement strings
   91: 
   92: : 'soko ( -- addr ) \ gives player's address
   93:     maze cell+ soko @ chars + ;
   94: 
   95: : apply-rule? ( addr u offset -- flag )
   96:     'soko 2swap
   97:     \ offset soko-addr addr u
   98:     0 DO
   99: 	over c@ over c@ <>
  100: 	IF  drop 2drop false  UNLOOP  EXIT  THEN
  101: 	>r over chars + r> char+
  102:     LOOP  2drop drop  true ;
  103: 
  104: : apply-rule! ( addr u offset -- )
  105:     'soko
  106:     2swap
  107:     \ offset soko-addr addr u
  108:     0 DO
  109: 	count rot tuck c! rot tuck chars + rot
  110:     LOOP  2drop drop ;
  111: 
  112: : play-rule ( addr1 u1 addr2 u2 offset -- flag )
  113:     >r 2swap r@  apply-rule?
  114:     IF  r> apply-rule! true  ELSE  r> drop 2drop false  THEN ;
  115: 
  116: \ player may move up, down, left and right
  117: 
  118: : (move)  ( offset -- )
  119:     >r  1 moves +!
  120:     S" @ "  S"  @"  r@ play-rule  IF  r> soko +!  EXIT  THEN
  121:     S" @."  S"  &"  r@ play-rule  IF  r> soko +!  EXIT  THEN
  122:     S" @$ " S"  @$" r@ play-rule  IF  r> soko +!  EXIT  THEN
  123:     S" @*." S"  &*" r@ play-rule  IF  r> soko +!  EXIT  THEN
  124:     S" @* " S"  &$" r@ play-rule
  125:           IF  r> soko +!  1 rocks +! -1 score +!  EXIT  THEN
  126:     S" @$." S"  @*" r@ play-rule
  127:           IF  r> soko +! -1 rocks +!  1 score +!  EXIT  THEN
  128:     S" &*." S" .&*" r@ play-rule  IF  r> soko +!  EXIT  THEN
  129:     S" &$ " S" .@$" r@ play-rule  IF  r> soko +!  EXIT  THEN
  130:     S" & "  S" .@"  r@ play-rule  IF  r> soko +!  EXIT  THEN
  131:     S" &."  S" .&"  r@ play-rule  IF  r> soko +!  EXIT  THEN
  132:     S" &* " S" .&$" r@ play-rule
  133:     IF  r> soko +!  1 rocks +! -1 score +!  EXIT  THEN
  134:     -1 moves +!  r> drop  ;
  135: 
  136: : soko-right  1            (move) ;
  137: : soko-left   -1           (move) ;
  138: : soko-down   /maze        (move) ;
  139: : soko-up     /maze negate (move) ;
  140: 
  141: : print-help
  142:     ." Move soko '@' with h, j, k or l key (like vi)" cr
  143:     ." or with vt100 cursor keys." cr ;
  144: 
  145: Variable redraw
  146: 
  147: : play-loop ( -- )  redraw on
  148:     BEGIN
  149: 	rocks @ 0=
  150: 	IF
  151: 	    level# @ 1+ level  0= IF  EXIT  THEN
  152: 	    redraw on
  153: 	THEN
  154: 	key? 0= redraw @ and  IF  .maze redraw off  THEN
  155: 	key
  156: 	CASE
  157: 	    [char] ? OF  print-help false  ENDOF
  158: 	    
  159: 	    [char] h OF  soko-left  redraw on false  ENDOF
  160: 	    [char] j OF  soko-down  redraw on false  ENDOF
  161: 	    [char] k OF  soko-up    redraw on false  ENDOF
  162: 	    [char] l OF  soko-right redraw on false  ENDOF
  163: 
  164: 	    \ vt100 cursor keys should work too
  165: 	    27       OF  key [char] [ <>   ENDOF
  166: 	    [char] D OF  soko-left  redraw on false  ENDOF
  167: 	    [char] B OF  soko-down  redraw on false  ENDOF
  168: 	    [char] A OF  soko-up    redraw on false  ENDOF
  169: 	    [char] C OF  soko-right redraw on false  ENDOF
  170: 
  171: 	    [char] q OF  true              ENDOF
  172: 	false swap  ENDCASE
  173:     UNTIL ;
  174: 
  175: \ start game with "sokoban"
  176: 
  177: : sokoban ( -- )
  178:     page 1 level IF  play-loop ." Game finished!"  THEN ;
  179:     
  180: 001 new-maze
  181: m:     #####
  182: m:     #   #
  183: m:     #$  #
  184: m:   ###  $##
  185: m:   #  $ $ #
  186: m: ### # ## #   ######
  187: m: #   # ## #####  ..#
  188: m: # $  $          ..#
  189: m: ##### ### #@##  ..#
  190: m:     #     #########
  191: m:     #######
  192: 002 new-maze
  193: m: ############
  194: m: #..  #     ###
  195: m: #..  # $  $  #
  196: m: #..  #$####  #
  197: m: #..    @ ##  #
  198: m: #..  # #  $ ##
  199: m: ###### ##$ $ #
  200: m:   # $  $ $ $ #
  201: m:   #    #     #
  202: m:   ############
  203: 003 new-maze
  204: m:         ########
  205: m:         #     @#
  206: m:         # $#$ ##
  207: m:         # $  $#
  208: m:         ##$ $ #
  209: m: ######### $ # ###
  210: m: #....  ## $  $  #
  211: m: ##...    $  $   #
  212: m: #....  ##########
  213: m: ########
  214: 004 new-maze
  215: m:            ########
  216: m:            #  ....#
  217: m: ############  ....#
  218: m: #    #  $ $   ....#
  219: m: # $$$#$  $ #  ....#
  220: m: #  $     $ #  ....#
  221: m: # $$ #$ $ $########
  222: m: #  $ #     #
  223: m: ## #########
  224: m: #    #    ##
  225: m: #     $   ##
  226: m: #  $$#$$  @#
  227: m: #    #    ##
  228: m: ###########
  229: 005 new-maze
  230: m:         #####
  231: m:         #   #####
  232: m:         # #$##  #
  233: m:         #     $ #
  234: m: ######### ###   #
  235: m: #....  ## $  $###
  236: m: #....    $ $$ ##
  237: m: #....  ##$  $ @#
  238: m: #########  $  ##
  239: m:         # $ $  #
  240: m:         ### ## #
  241: m:           #    #
  242: m:           ######
  243: 006 new-maze
  244: m: ######  ###
  245: m: #..  # ##@##
  246: m: #..  ###   #
  247: m: #..     $$ #
  248: m: #..  # # $ #
  249: m: #..### # $ #
  250: m: #### $ #$  #
  251: m:    #  $# $ #
  252: m:    # $  $  #
  253: m:    #  ##   #
  254: m:    #########

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>