AN EXAMPLE PROGRAM IS BELOW
'Simple RPG demo 'in Qbasic 'using ASCII graphics 'By Steve M. ' SCREEN 12: DIM MAZE$(17, 17), T$(150) FOR A = 1 TO 17: READ Char$ FOR B = 1 TO 15 T$ = MID$(Char$, B, 1): MAZE$(A, B) = T$ NEXT B: NEXT A LOCATE 3, 20: PRINT "Use the arrow keys to navigate the maze" LOCATE 4, 22: PRINT "Open the door (D) with the key ($)." X = 31: Y = 7: LOCATE Y, X: PRINT CHR$(1); ' 'Plot the maze ' COLOR 2: FOR A = 1 TO 17: FOR B = 1 TO 17 LOCATE 6 + (B - 1), 30 + (A - 1): PRINT MAZE$(B, A) NEXT B, A: GOSUB Display.man LOCATE 9, 43: COLOR 14: PRINT "$"; : LOCATE 22, 43: COLOR 9: PRINT "D"; ' Kyboard: I$ = INKEY$: IF I$ = "" THEN GOTO Kyboard OLDX = X: OLDY = Y IF I$ = CHR$(0) + "M" THEN GOSUB Clear.man: X = X + 1: IF I$ = CHR$(0) + "K" THEN GOSUB Clear.man: X = X - 1 IF I$ = CHR$(0) + "H" THEN GOSUB Clear.man: Y = Y - 1 IF I$ = CHR$(0) + "P" THEN GOSUB Clear.man: Y = Y + 1 IF I$ = CHR$(27) THEN END IF MAZE$(Y - 5, X - 29) = "#" THEN GOSUB Recall.old.position IF MAZE$(Y - 5, X - 29) = "$" THEN GOSUB Keyfound IF MAZE$(Y - 5, X - 29) = "D" THEN GOSUB Check.door GOSUB Display.man LOCATE 1, 25: PRINT "MAZE MATRIX POSITION:"; Y - 5; " "; X - 29; GOTO Kyboard Clear.man: LOCATE Y, X: PRINT CHR$(32); : RETURN Display.man: COLOR 12: LOCATE Y, X: PRINT CHR$(1); : RETURN Recall.old.position: X = OLDX: Y = OLDY: RETURN Keyfound: COLOR 15: LOCATE 24, 10: PRINT "You have found the key. "; PRINT "Now unlock the door and go home.": Unlocked = 1: RETURN Check.door: IF Unlocked THEN GOTO Escaped COLOR 15: LOCATE 24, 10: PRINT "The door is locked. Get the key ($). "; PRINT "to free yourself.": MAZE$(3, 14) = CHR$(32) LOCATE 8, 43: PRINT CHR$(32); : GOSUB Recall.old.position: RETURN Escaped: COLOR 15: LOCATE 24, 10: PRINT SPACE$(61); LOCATE 24, 20: PRINT "You have escaped from this maze for now." LOCATE 22, 43: PRINT CHR$(32): END DATA ############### DATA # # # DATA # # # ####### DATA # # # # $# DATA # # # ####### DATA # # # # DATA ####### # ### # DATA # # # # DATA # ########### # DATA # # DATA # ############# DATA # # DATA ############# # DATA # # DATA # ############# DATA # # DATA #############D#
A screenshot from
SIMPLE RPG DEMO
I realize that this picture from the demo is quite hard to see. It's the best bmp viewer I have to date. Anyway you must navigate through a small maze to find the key to the door. I have designed it so that you (dark blue blotch at top left) must first touch the door at the bottom (the red blotch) and then the wall guarding the key will disappear. Then you can get the key(bright green blotch) and unlock the door and win the simple demo. I will explain how the code works below. Important: You must have some experience programming with Qbasic or Basic in order to understand what the code does.
The game demo explained
First call the screen with SCREEN 12. Then dimension the strings Maze$(17,17) and T$(150). The Maze$ string will store the entire ASCII maze in it. The T$ string will be intialized to check for specific characters within the Maze$ string. After that the FOR A=1 TO 17:READ Char$ will loop 17 times and read each data statement into the Char$ string. For example, after read Char$ the first time it will contain "###############". Therefore it will have remembered the first line in the DATA statement at the end of the program. The next sequence is FOR B=1 TO 15. The B loop will cycle through 15 characters in the Maze$ string. I will explain that in a minute. Thereafter the statment T$=MID$(Char$, B, 1) will retain a single character upon each call to the T$. The MID$ means to search for the middle string. The loop will cycle through 15 generations of characters in the Char$ statement. So then the Maze$(A, B)= T$ will now be occupied with the first character in the DATA statment. That means Maze$(A, B)= "#" (T$). The program is scanning through and reading the DATA statements and storing each character (example #,(space),$) here. Next the statements NEXT B: NEXT A will complete the A and B loops. The NEXT A will only contain a 2 only after the B has looped through 15 times. This is what is known as a nested-loop. By the time this line completes (FOR A=1 TO 17:READ Char$:FOR B=1 TO 15:T$=MID$(Char$,B,1):MAZE$(A,B)=T$:NEXT B) the Maze$(1) string will contain Maze$(1,1)="#". Does that simplify things for you? The program is building a string to place the maze for you. Then the next statement LOCATE 3,20:PRINT "Use the arrow keys to navigate the maze" will position the cursor down 3 lines and over 20. The message 'Use the arrow keys to navigate the maze' will then be printed at that location on the screen. Next the LOCATE 4,22:PRINT "Open the door (D) with the key ($)." will park the cursor down 4 lines and over 22 across. Then the message in quotes will be printed. After that the X=31:Y=7 will create 2 variables, X and Y. The X variable will contain the number 31. The Y variable will hold a number 7. These variables are used to position our character (the smily face) on the screen in the next statement. So that brings us to the LOCATE Y,X:PRINT CHR$(1); which will position our smily face down Y positions and over X positions. Remember the paragraph we spoke about earlier. The smily face will be located at 7,31. So the statement will look like this: LOCATE 31,7:PRINT CHR$(1);. The CHR$(1); will draw a ASCII character face on the screen. The semicolon after it will retain that position for the next PRINT statement. This can be useful if you have text in 2 different PRINT statements, but still want the entire message on a line. Example LOCATE 10,15:PRINT "Qbasic is ";:PRINT "cool!"; Next the program will be ready to plot the maze. The statments COLOR 2:FOR A=1 TO 17:FOR B=1 TO 17:LOCATE 6+(B-1),30+(A-1);:PRINT Maze$(B,A):NEXT B,A will draw the maze on the screen. The program will loop through the string Maze$(B,A) to begin designing the walls, spaces, key and the door (#, $,D). The next statements LOCATE 6+(B-1),30+(A-1);:PRINT Maze$(B,A):NEXT B,A will position each character of the maze (starting down 6 spaces+(17 more upon each execution) and over 30+(17 more positions over). Here's a quick example of what is going on. After you run the above statements you will get this printed on the screen: ############### # # # # # # ####### # # # # $# # # # ####### # # # # ####### # ### # # # # # # ########### # # # # ############# # # ############# # # # # ############# # # #############D# The maze is now located down six spaces and over 30 (LOCATE 6,30). The next statment GOSUB Display.man will skip over the lines after it and search for the Display.man: label. What is a label? Labels are used to store routines that the program will jump to occasionally to fetch some data or draw something on the screen. The program will now be looking at the statments below: Display.man: COLOR 12:LOCATE Y,X:PRINT CHR$(1);:RETURN Explanation: The program will plot a bright red smily face at location y,x (7,31). The COLOR 12 is for the bright red color. The LOCATE and PRINT CHR$(1); I've already briefed you about. The last statement RETURN will return back to the location it was last at. Therefore the program will return back to the next statement after the GOSUB Display.man statement. Then the statements LOCATE 9,43:COLOR 14:PRINT "$"; will print a yellow $ character on the screen down 9 lines and over 43 positions. Next LOCATE 22,43:COLOR 9:PRINT "D" will print a bright blue D down 22 lines and over 43 positions. The next statement Kyboard: is a label representing the keyboard subroutine. First the I$=INKEY$ will create a string called I$ and store a key value that you press on the keyboard in it. If you press the Z key on the keyboard, then I$ will look like this: I$="Z" Next the IF INKEY$="" THEN GOTO Kyboard will check to see if the user (you) pressed any keys on the keyboard. If you did, then it will remain at the Kyboard: label. This is known as clearing the keyboard buffer. The next statements are kind of tricky. The variables OLDX=X:OLDY=Y will store the X value in OLDX and the Y value in OLDY. These variables will be used later in the program. Just keep in mind that OLDX and OLDY are remembering where our player is on the screen. The next line IF I$=CHR$(0)+"M" THEN GOSUB Clear.man: X=X+1 is checking to see if you pressed the right arrow key on the keyboard (I$=CHR$(0)+"M"). If you did the next statment GOSUB Clear.man will jump to the Clear.man label and erase the smily face from the screen. This is necessary so that upon each press of the right arrow key, we don't end up with a bunch of trailing smily faces across the screen. In other words, it gives the animation effect of movement. The next statement X=X+1 will increase the X value by 1 position each time you press the right arrow key. For example, the first time you press the right arrow key X will contain a 1. (X=1). The next time you press it, X will contain a 2. (X=2). This is used in conjunction with the LOCATE Y,X:PRINT CHR$(1); above to provide the fluid animation you will see. The next statement IF I$=CHR$(0)+"K" THEN GOSUB Clear.man:X=X-1, will do the opposite. This time if you press the left arrow key (I$=CHR$(0)+"K") the smily face will advance in a backward direction (move left). Then the face will be cleared (Clear.man) and eventually redrawn again with the LOCATE Y,X:PRINT CHR$(1); statement. The next statement IF I$=CHR$(0)+"H" THEN GOSUB Clear.man:Y=Y-1, will make the smily face move up when you press the up arrow key. Then once again the smily face will be erased and redrawn later. The erasing part is achieved with the Clear.man label: Clear.man: LOCATE Y,X:PRINT CHR$(32);:RETURN The PRINT CHR$(32); will print a space each time you press the up arrow key or any of the arrow keys. The next statement IF I$=CHR$(0)+"P" THEN GOSUB Clear.man:Y=Y+1, will make the smily face move down the screen (erasing as it does) each time you press the down arrow key. The next statement, IF I$=CHR$(27) THEN END, is a key option I added so that when the player presses the escape key (I$=CHR$(27) - CHR$(27) is the character code for the Esc ASCII character), the demo will end (END). Then the statement IF Maze$(Y-5,X-29)="#" THEN GOSUB Recall.old.position, will check to see if the smily face make contact with the # (wall) character. If this has happened, then the program will jump to the Recall.old.position which will remember where the player was before he moved (explained a little later). The Maze$(Y-5,X-29)="#" is searching the Maze$ string for a "#" character. If you can remember, earlier we stored the entire maze in the Maze$ string. Now when the IF statement searches through the Maze$ string it will check for initial contact of the "#" character. In other words, if the smily face makes contact with a # character, he will be halted in the same position (he will not be able to pass through the walls). Also note that the Maze$(Y-5,X-29) is subtracting a 5 from Y and a 29 from X. If you recall earlier, we set the X variable to 31 and the Y variable to 7. So the Maze$ string will be pointing at 2,2 because 7-5=2 (Y-5=2) and 31-29=2 (X-29=2). This will begin the scanning from 2,2, which will be where the player is at in the maze. He's down 2 locations from the top of the maze of over 2 positions from the left of the maze. That's why we need to subtract the correct values in Maze$ when scanning for contact with an object on the screen. So keep in mind if the player touches a # on the screen the program will jump to: Recall.old.position: X=OLDX:Y=OLDY:RETURN and then the X and Y values will be equal to the OLDX,OLDY variables, which means that the X and Y values will be equal to the position you were at before you moved. It's almost like you never even moved. This is what will prevent the smily face from proceeding beyond the wall (#) character. After that the statement IF Maze$(Y-5,X-29)="$" THEN GOSUB Keyfound, will see if the smily face made contact with the $ (key) character on the screen. If he did the label below will be called: Keyfound: COLOR 15:LOCATE 24,10:PRINT "You have found the key. "; PRINT "Now unlock the door and go home.":Unlocked=1:RETURN The screen will print white text (COLOR 15) saying "You have found the key. Now unlock the door and go home.". Then a special variable called Unlocked will be set equal to 1. This is also known as a flag. What that means that if the flag is up (=1) then an action will take place. If the flag is down (=0) then no action will take place. Hint: This is also how the computer reads everything instruction you type in. It called Binary. It will read either a 0 or a 1. Finally the RETURN will be encountered and the program will go back to where it came from prior to the last GOSUB Keyfound statement. That will then bring us to IF Maze$(Y-5,X-29)="D" THEN GOSUB Check.door. The program will check to see if the smily face touched the D (door) character at the bottom of the screen if he did this label will be called: Check.door: IF Unlocked THEN GOTO Escaped COLOR 15:LOCATE 24,10:PRINT "The door is locked. Get the key ($). "; PRINT "to free yourself.":Maze$(3,14)=CHR$(32) LOCATE 8,43:PRINT CHR$(32);:GOSUB Recall.old.position: RETURN If the user unlocked the door (IF Unlocked) (D) with the key ($), then the program will jump to this label: Escaped: COLOR 15:LOCATE 24,10:PRINT SPACE$(61); LOCATE 24,20:PRINT "You have escaped from this maze for now." LOCATE 22,43:PRINT CHR$(32);:END I'm going to now explain what each label is doing. Okay the first label the program jumped to (Check.door) will check to see if the smily face has already touched the D (door) character (remember earlier?). If this was successful (IF Unlocked THEN GOTO Escaped), then the program will print white text and let the player know that the game is over ("You have escaped from this maze for now." The statement PRINT SPACE$(61); will print 61 spaces at location 24,10 (Y,X) which erases an earlier message on the screen ("The door is locked. Get the key ($). "). Basically its used to erase an entire line on the screen. However, if the smily face didn't touch the $ (key) yet. Then the player will be informed that the door is locked and the statement Maze$(3,14)=CHR$(32) will be executed. This is replaces the character value of # (wall) with a space. What this does is erase the wall above the key, so that the smily face will be able to reach the key after touching (opening) the door (D) at the bottom of the screen. The LOCATE 8,43:PRINT CHR$(32); statement will physically erase the wall (#) above the key ($). Just remember that the Maze$(3,14)=CHR$(32) is used to erase a character in the string so that the smily face won't be barred by a wall (#) when trying to reach the key ($). This will allow the player to advance beyond the wall (because its gone now) to touch the key. After you have touched the key ($) and go to the bottom of the screen and touch the door (D) the game will end (END). Just recall that earlier when you tried to touch the door the label Check.door was called and a GOSUB Recall.old.position was encountered. This prevented the smily face from advancing beyond the door. Whew! I think I explained how everything works now. I hope I didn't lose anyone or put anyone to sleep with the logic lecture, but hopefully now you can create your own maze games (download my the Simple RPG demo below) out of this shell. That's a hard nut to crack, but I'm sure you'll figure it out. If not send me some Comments on my home page and I'll get back with you. Have fun and happy game programming. PS: The SIMPRPG.ZIP program below is what triggered the idea of "Mazes of Misery. Go to my Downloads page (see bottom of this page for a link) to view a screenshot of this upcoming graphics game. -SM
Filename/Size
Description of Game
SIMPRPG
2.21kb
ASCII Maze game