|
3DMaze
by Ricardo Fernandes Lopes
|
3D Maze download as a Tap file and instruction can be found here
( 3D MAZE version 1.0 for Jupiter ACE )
( Copyright (c) 2007 by Ricardo F. Lopes )
( under the GNU General Public License version 2 )
( Start the game with: )
( 3DMAZE )
( Game control keys: )
( I = Step forward )
( J = Turn left )
( L = Turn right )
( K = Step backward )
( H = 2D map look-up )
( Q = Quit game )
( ============= )
( MISC. WORDS )
( ============= )
16 BASE C! ( Hex )
CREATE CMOVE ( src dst n -- : Move n bytes from src to dst )
DF C, ( RST 18 ; get 'n' )
42 C, ( LD B,D ; BC = 'n' )
4B C, ( LD C,E )
DF C, ( RST 18 ; get 'dst' )
D5 C, ( PUSH DE ; save it )
DF C, ( RST 18 ; get 'src' )
E1 C, ( POP HL ; HL = 'dst' )
EB C, ( EX DE,HL ; DE = 'dst' , HL='src' )
ED C, B0 C, ( LDIR ; move 'n' bytes )
FD C, E9 C, ( JP (IY) ; end )
CMOVE DUP 2- ! ( Make CMOVE an executable word )
CREATE TOUPPER ( c -- C : convert a character to uppercase )
DF C, ( RST 18h ; E = char to convert )
7B C, ( LD A,E ; A = char to convert )
CD C, 07 C, 08 C, ( CALL 0807h ; to-upper ROM routine )
5F C, ( LD E,A ; E = converted char )
D7 C, ( RST 10h ; Push char to Data Stack )
FD C, E9 C, ( JP [IY] ; end )
TOUPPER DUP 2- ! ( Make TOUPPER an executable word )
DECIMAL
: 2DUP ( x1 x2 -- x1 x2 x1 x2 ) OVER OVER ;
: 1+! ( adr -- ) DUP @ 1+ SWAP ! ;
: KEY ( -- c : wait for a keypress )
BEGIN INKEY 0= UNTIL
BEGIN INKEY ?DUP UNTIL
;
( Random Number Generator )
0 VARIABLE RND
: RANDOMIZE 15403 @ RND ! ;
: RANDOM RND @ 31421 * 6927 + DUP RND ! ;
: RAND RANDOM U* SWAP DROP ; ( n1 -- n2 : n2 = random from 0 to n1-1)
( ==================== )
( GRAPHIC CHARACTERS )
( ==================== )
CREATE GRAPH 272 ALLOT ( Graphic characters )
( Load graphic chars from files created with FontEd )
GRAPH 256 BLOAD GRAPH ( ASCII 0 to 31 = graph )
GRAPH 256 + 8 BLOAD SLASH ( ASCII 47 = slash / )
GRAPH 264 + 8 BLOAD BACKSLASH ( ASCII 92 = backslash \ )
: GR ( Set graphic characters to char generator mem )
GRAPH 11264 256 CMOVE ( ASCII 0-31 )
GRAPH 256 + 11640 8 CMOVE ( ASCII 47, slash )
GRAPH 264 + 12000 8 CMOVE ( ASCII 92, backslash )
;
( ====== )
( MAZE )
( ====== )
( Maximum Maze dimensions + margins )
32 CONSTANT MAXX ( must be 32, the screen width )
22 CONSTANT MAXY
( Current maze dimensions )
10 VARIABLE XSIZE
7 VARIABLE YSIZE
( Add to maze size )
: X+ ( n -- ) XSIZE @ + 3 MAX 30 MIN XSIZE ! ;
: Y+ ( n -- ) YSIZE @ + 3 MAX 20 MIN YSIZE ! ;
CREATE MAZE MAXX MAXY * ALLOT ( Maze mem )
( Convert room xy coordinates to maze mem address )
: XY>ROOM ( x y -- room ) MAXX * + MAZE + ;
( Room walls as bit masks )
1 CONSTANT WEST ( 0001 West wall )
2 CONSTANT SOUTH ( 0010 South wall )
4 CONSTANT EAST ( 0100 East wall )
8 CONSTANT NORTH ( 1000 North wall )
15 CONSTANT ALL ( 1111 All walls )
2 CONSTANT RIGHT
4 CONSTANT BACK
8 CONSTANT LEFT
: TURN ( facing1 dir -- facing2 : Change Direction )
OVER 16 * ROT OR ( NESW -> NESWNESW )
SWAP / ( Turn )
15 AND ( NESWNESW -> NESW )
;
( ============= )
( DRAW 2D MAP )
( ============= )
: _MAP ( Draw 2D maze map )
YSIZE @ 2+ 0 ( line count including margins )
DO
0 I XY>ROOM ( source = maze )
9216 I 32 * + ( destination = screen )
XSIZE @ 2+ ( column count, including margins )
CMOVE ( copy maze to screen )
LOOP
;
: _ROOM ( room -- : Draw one maze room in 2D )
DUP C@ SWAP ( Stack: walls room )
MAZE - 9216 + ( Stack: walls screen )
C! ( Set screen char )
;
: _POS ( room -- : Show player position in 2D map )
DUP C@ 16 OR SWAP
MAZE - 9216 + C!
;
( ================= )
( MAZE GENERATION )
( ================= )
: INIT ( Initialize maze before generate)
YSIZE @ 2+ 0
DO
XSIZE @ 2+ 0
DO
ALL I J XY>ROOM C! ( Close rooms )
LOOP
0 I XY>ROOM DUP C@ EAST AND SWAP C! ( West border )
XSIZE @ 1+ I XY>ROOM DUP C@ WEST AND SWAP C! ( East border )
LOOP
XSIZE @ 2+ 0
DO
I 0 XY>ROOM DUP C@ SOUTH AND SWAP C! ( North border )
I YSIZE @ 1+ XY>ROOM DUP C@ NORTH AND SWAP C! ( South border )
LOOP
;
( Walking to next room )
CREATE WLK ( const table for WALK word )
0 , -1 , MAXX , 0 , 1 , 0 , 0 , 0 , MAXX NEGATE ,
: WALK ( room1 facing -- room2 facing : Walk to facing room )
DUP 2 * WLK + @ ( displacement )
ROT + SWAP
;
: REMOVE ( room facing -- room facing : Remove facing wall )
2DUP -1 XOR ( facing bit mask )
OVER C@ AND ( clear facing bit )
SWAP C! ( store it back )
OVER _ROOM ( update screen )
;
: CUT ( room1 facing -- room2 : Open walls to next room )
REMOVE ( remove facing wall )
WALK ( go to next room )
BACK TURN ( look back )
REMOVE ( remove back wall )
DROP ( discard facing )
;
CREATE FACES 4 ALLOT ( direction buffer for neighbor check )
: CHECK ( n room facing -- n : Check & count if next room is closed )
WALK ( walk to next room )
SWAP C@ ALL = ( is it a closed room? )
IF
OVER FACES + C! ( save facing )
1+ ( count as closed )
ELSE
DROP ( discard facing )
THEN
;
: CHECKAROUND ( room -- room n : Check around for closed rooms )
0 ( start counting closed neighbors )
OVER WEST CHECK
OVER NORTH CHECK
OVER EAST CHECK
OVER SOUTH CHECK
;
( Select a random direction toward a closed room )
: RNDFACE ( n -- facing ) RAND FACES + C@ ;
0 VARIABLE REMAINING ( remaining rooms to visit )
: DFS ( room #rooms -- : Depth-First Search maze generation )
1- REMAINING ! ( rooms to visit )
0 SWAP ( stack a marker )
BEGIN
REMAINING @ ( stop when all rooms visited )
WHILE
CHECKAROUND ?DUP ( any closed neighbor? )
IF
OVER SWAP ( stack current room )
RNDFACE CUT ( cut a passage to a closed room )
REMAINING @ 1- ( count visited rooms )
REMAINING !
ELSE
DROP ( backtrack last visited room )
THEN
REPEAT
BEGIN 0= UNTIL ( clear stack to marker )
;
: GENERATE ( Start maze generation )
CLS INIT _MAP
XSIZE @ 2 / YSIZE @ 2 / ( Start from maze center )
XY>ROOM XSIZE @ YSIZE @ * DFS
;
: SETEXIT ( Set a random maze exit at North border )
XSIZE @ RAND 1+ 0
XY>ROOM SOUTH CUT DROP
;
: START ( -- room facing : Generate a random start position at South )
XSIZE @ RAND 1+ YSIZE @
XY>ROOM NORTH
;
( ============== )
( DRAW 3D MAZE )
( ============== )
0 VARIABLE LOOKUPS ( Number of 2D map invoking )
0 VARIABLE STEPS ( Number of steps )
: .SCORE ( Display score and controls )
0 24 AT ." 3D MAZE"
2 25 AT ." Steps"
3 26 AT STEPS @ .
5 25 AT ." Helps"
6 26 AT LOOKUPS @ .
15 25 AT ." I"
16 24 AT ." JKL Move"
18 25 AT ." H Help"
20 25 AT ." Q Quit"
;
CREATE MRKS ( 3D drawing key screen coordinates )
( A B )
0 C, 22 C,
1 C, 21 C,
5 C, 17 C,
8 C, 14 C,
10 C, 12 C,
11 C, 11 C,
: MARKS ( n -- A[n] B[n] ) 2 * MRKS + DUP C@ SWAP 1+ C@ ;
( Draw Vertical edges: )
( Left edge column = A[n+1]-1 )
( Right edge column = B[n+1]+1 )
( 1 past Last line = B[n+1]+1 )
( First line = A[n+1] )
: _EDGES ( n -- : Draw Vertical Edges )
1+ MARKS 1+ ( A[n+1] B[n+1]+1 )
OVER 1- ( A[n+1] B[n+1]+1 A[n+1]-1 )
SWAP ROT ( A[n+1]-1 B[n+1]+1 A[n+1] )
OVER SWAP ( A[n+1]-1 B[n+1]+1 B[n+1]+1 A[n+1] )
DO ( A[n+1]-1 B[n+1]+1 )
OVER I SWAP AT 4 EMIT ( Draw left edge )
I OVER AT 1 EMIT ( Draw right edge )
LOOP
DROP DROP ( discard left & right column coord )
;
( Draw Back Wall: )
( Top line = A[n+1]-1 )
( Bottom line = B[n+1]+1 )
( 1 past last column = B[n+1]+1 )
( First column = A[n+1] )
: _BACK ( n -- : Draw Back Wall )
1+ MARKS 1+ ( A[n+1] B[n+1]+1 )
OVER 1- ( A[n+1] B[n+1]+1 A[n+1]-1 )
ROT ROT ( A[n+1]-1 A[n+1] B[n+1]+1 )
DUP ROT ( A[n+1]-1 B[n+1]+1 B[n+1]+1 A[n+1] )
DO ( A[n+1]-1 B[n+1]+1 )
OVER I AT 2 EMIT ( Draw top line )
DUP I AT 8 EMIT ( Draw bottom line )
LOOP
DROP DROP ( discard top & bottom line coord )
;
( Draw Left Wall: )
( Bottom line = B[n] )
( Top line = A[n] )
( 1 past last column = A[n+1] )
( First column = A[n] )
: _LWALL ( n -- : Draw Left wall )
DUP MARKS SWAP ( n B[n] A[n] )
ROT 1+ MARKS DROP ( B[n] A[n] A[n+1] )
SWAP ( B[n] A[n+1] A[n] )
DO ( B[n] )
I I AT 92 EMIT ( Draw top line \ )
DUP I AT 47 EMIT ( Draw bottom line / )
1- ( update bottom line coord )
LOOP
DROP ( discard bottom line coord )
;
( Draw Right Wall: )
( Bottom line = B[n+1]+1 )
( Top line = A[n+1]-1 )
( 1 past last column = B[n]+1 )
( First column = B[n+1]+1 )
: _RWALL ( n -- : Draw Right Wall )
DUP 1+ MARKS 1+ SWAP 1- ( n B[n+1]+1 A[n+1]-1 )
ROT MARKS SWAP DROP 1+ ( B[n+1]+1 A[n+1]-1 B[n]+1 )
ROT ( A[n+1]-1 B[n]+1 B[n+1]+1 )
DO ( A[n+1]-1 )
DUP I AT 47 EMIT ( Draw top line / )
I I AT 92 EMIT ( Draw bottom line \ )
1- ( update top line coord )
LOOP
DROP ( discard top line coord )
;
( Draw Left Back Wall: )
( Bottom line = B[n+1]+1 )
( Top line = A[n+1]-1 )
( 1 past last column = A[n+1] )
( First column = A[n] )
: _LBACK ( n -- : Draw Left Back Wall )
DUP 1+ MARKS 1+ ( n A[n+1] B[n+1]+1 )
ROT MARKS DROP ( A[n+1] B[n+1]+1 A[n] )
ROT DUP 1- ( B[n+1]+1 A[n] A[n+1] A[n+1]-1 )
SWAP ROT ( B[n+1]+1 A[n+1]-1 A[n+1] A[n] )
DO ( B[n+a]+1 A[n+1]-1 )
DUP I AT 2 EMIT ( Draw top line )
OVER I AT 8 EMIT ( Draw bottom line )
LOOP
DROP DROP ( discard top & bottom line coord )
;
( Draw Right Back Wall: )
( Bottom line = B[n+1]+1 )
( Top line = A[n+1]-1 )
( 1 past last column = B[n]+1 )
( First column = B[n+1]+1 )
: _RBACK ( n -- : Draw Right Back Wall )
DUP 1+ MARKS 1+ SWAP 1- ( n B[n+1]+1 A[n+1]-1 )
SWAP ROT MARKS SWAP DROP 1+ ( A[n+1]-1 B[n+1]+1 B[n]+1 )
OVER ( A[n+1]-1 B[n+1]+1 B[n]+1 B[n+1]+1 )
DO ( A[n+1]-1 B[n+1]+1 )
OVER I AT 2 EMIT ( Draw top line )
DUP I AT 8 EMIT ( Draw bottom line )
LOOP
DROP DROP ( discard top & bottom line coord )
;
( Check if facing wall exist )
: WALL? ( room facing -- flag ) SWAP C@ AND ;
: _3D ( room facing -- )
CLS
.SCORE
5 0 ( max View depth = 5 rooms )
DO
I _EDGES ( Draw vertical edges )
2DUP LEFT TURN WALL? ( Is there a wall at left? )
IF
I _LWALL ( draw left wall )
ELSE
2DUP LEFT TURN WALK ( Go to left room )
RIGHT TURN WALL? ( Is there a left back wall?)
IF
I _LBACK ( draw left back wall )
THEN
THEN
2DUP RIGHT TURN WALL? ( Is there a wall at right? )
IF
I _RWALL ( draw right wall )
ELSE
2DUP RIGHT TURN WALK ( Go to right room )
LEFT TURN WALL? ( Is there a right back wall? )
IF
I _RBACK ( draw right back wall )
THEN
THEN
2DUP WALL? ( Facing a wall? )
IF
I _BACK LEAVE ( Draw back wall and leave loop )
ESLE
WALK ( Move forward )
OVER MAZE - 32 < ( Maze exit? )
IF
LEAVE ( Leave loop )
THEN
THEN
LOOP
DROP DROP ( Discard room & facing )
;
( ============== )
( GAME CONTROL )
( ============== )
( Check if the exit was found )
: EXIT? ( room -- flag ) MAZE - 32 < ;
: .FACE ( facing -- : Print facing direction )
DUP WEST = IF ." West" THEN
DUP SOUTH = IF ." South" THEN
DUP EAST = IF ." East" THEN
NORTH = IF ." North" THEN
;
: .INFO ( room facing -- : Print info bar at screen bottom )
100 DUP BEEP
22 0 AT ." Facing " .FACE
." , S:" STEPS @ .
." H:" LOOKUPS @ .
EXIT?
IF
50 DUP BEEP 30 DUP BEEP
." EXIT"
THEN
;
: _2D ( room facing -- : Draw 2D map and wait for a keypress )
CLS _MAP OVER _POS .INFO
KEY DROP ( Wait for a keypress )
;
: FORWARD ( room1 facing -- room2 facing : Step forward )
2DUP WALL? 0=
IF
WALK STEPS 1+!
THEN
;
: BACKWARD ( room1 facing -- room2 facing : Step backward )
BACK TURN FORWARD BACK TURN
;
: PLAY
0 STEPS !
0 LOOKUPS !
GENERATE SETEXIT START
2DUP _2D
BEGIN
2DUP _3D
KEY TOUPPER ROT ROT
3 PICK ASCII I = IF FORWARD THEN
3 PICK ASCII K = IF BACKWARD THEN
3 PICK ASCII J = IF LEFT TURN THEN
3 PICK ASCII L = IF RIGHT TURN THEN
3 PICK ASCII H = IF 2DUP _2D LOOKUPS 1+! THEN
ROT ASCII Q = 3 PICK EXIT? OR
UNTIL
_2D
;
: .MENU
CLS
." ____________3D_MAZE_______v 1.0_"
4 11 AT ." Width:"
5 10 AT ." Height:"
7 11 AT ." I"
8 10 AT ." JKL Set Maze Size"
10 11 AT ." P Play"
12 11 AT ." Q Quit"
20 3 AT ." c 2007 by Ricardo F. Lopes"
21 3 AT ." General Public License v.2"
;
: .SIZE
4 18 AT XSIZE @ .
5 18 AT YSIZE @ .
;
: 3DMAZE ( Start the 3D game )
GR RANDOMIZE .MENU
BEGIN
.SIZE
KEY TOUPPER
DUP ASCII I = IF 1 X+ THEN
DUP ASCII K = IF -1 X+ THEN
DUP ASCII J = IF -1 Y+ THEN
DUP ASCII L = IF 1 Y+ THEN
DUP ASCII P = IF PLAY .MENU THEN
ASCII Q =
UNTIL
CLS
;
|
|