ASCII Maze Game for Apple IIe
A downloadable game
A simple maze game. Find your way through the randomly generated maze to the X to escape!
Arrow keys to move the little @ symbol (you). Game ends when you reach the X on the map.
This was a really fun challenge. The restriction for the jam was no more than 10 lines of BASIC for an 8-bit system of your choice, and no more than 72 characters per line. I had to come up with so many non-intuitive ways of doing things to squash everything in to the size restrictions. Set the same variable to the same value 200 times? Sure, why not! Create a one time IF statement so your GOTO works? You betcha! Get lazy at the end and use multiple print statements for formatting? I just want the madness to stop....
Definitely more like a 4 dimensional logic puzzle than regular programming, but it's been fun.
I used AppleWin for running, testing. I couldn't find a web based emulator that lets you copy and paste code, but it's easy with AppleWin. If I find an easy way for people to load the program and play it, I'll post it here. Word of warning, this program is optimized heavily for code line length and line count. NOT for efficiency. If you're going to try it out set yourself a timer - it takes about 2 minutes after you type RUN before the maze is displayed. There is however a nice spinning indicator to watch, so at least you'll know the program is doing something. Once the maze loads, everything runs smoothly and you can zip around the maze crashing into walls as people do.
The maze is created using an unholy mockery of Prim's Minimum Spanning Tree algorithm. Per the jam instructions, I will be posting an explanation of the code later. I need a break from this at the moment..
Here is the full code for your perusal.
0HOME:DIMG(40,24):DIMD(3):FORN=0TO3:D(N)=N:NEXTN:I=1:DIML(210,1):H=2:V=2 1J=RND(1)*4:K=RND(1)*4:T=D(J):D(J)=D(K):IFS=0THENDIMQ$(2):Q$(0)="#":S=1 2D(K)=T:FORT=0TO3:N=D(T):O=(N=0)-(N=1):P=(N=2)-(N=3):L(1,0)=H:L(1,1)=V 3X=L(I,0)+O*2:Y=L(I,1)+P*2:E=1:IFX>39ORX<1ORY>23ORY<1ORG(X,Y)THENE=0 4IFETHENT=3:G(X,Y)=1:G(X-O,Y-P)=1:I=I+1:L(I,0)=X:L(I,1)=Y:NEXTT:GOTO1 5HTABH:VTABV:?"DIGGING"MID$("-\|/",T+1,1):NEXTT:I=I-1:Q$(1)=" ":IFIGOTO1 6G(38,22)=2:Q$(2)="X":FORY=1TO23:FORX=1TO40:?Q$(G(X,Y));:NEXTX,Y 7HTABH-A:VTABV-B:?" ":HTABH:VTABV:?"@":WAIT49152,128:K=PEEK(49152)-128 8A=(K=21)-(K=8):B=(K=10)-(K=11):T=G(H+A,V+B):IFT=0THENA=0:B=0:?CHR$(7) 9H=H+A:V=V+B:POKE49168,0:ONT<2GOTO7:?"":?"":?"YOU'RE FREE!":END
[Update] Added a disk image! Now you can download the disk image, then go to https://www.scullinsteel.com/apple2/ and drag and drop the disk image onto the Apple II emulator to play.
[Update] Okay, per the jam instructions, here is an explanation of the code:
==[ LINE 0 ]========================================== 0HOME:DIMG(40,24):DIMD(3):FORN=0TO3:D(N)=N:NEXTN:I=1:DIML(210,1):H=2:V=2 ------------------------------------------------------ HOME // Clears the screen. DIM G(40, 24) // Creates an array to hold the maze grid. Apple IIe text mode resolution is 40x24 DIM D(3) // Array for holding possible directions. This will be shuffled for randomized direction checking. FOR N = 0 TO 3 // Need to set all four directions to a number D(N) = N // Use the numbers from the FOR loop to set the initial state of D() to 0,1,2,3 NEXT N // Loop until N = 3 I=1 // Index for the list of spaces that still need to be checked as a possible maze path. DIM L(210,1) // An array to hold the List of spaces that need to be checked. The max possible number of spaces is only 209 (38/2)*(22/2). I don't know why I made it 210. H=2 // Initial starting X location for maze generator. V=2 // Initial starting Y location for maze generator. ====================================================== ==[ LINE 1 ]========================================== 1J=RND(1)*4:K=RND(1)*4:T=D(J):D(J)=D(K):IFS=0THENDIMQ$(2):Q$(0)="#":S=1 ------------------------------------------------------ J = RND(1) * 4 // Get a random number from 0 to 3 K = RND(1) * 4 // Get another random number from 0 to 3 T = D(J) // Using those two random numbers as locations, we do a swap of two numbers in our direction array. T for Temp? D(J) = D(K) // Swap isn't quite finished. That happens on the next line. IF S = 0 THEN DIM Q$(2) // Needed a place to create this array so used an IF statement that is designed to only run once. Uninitialized variables are set to 0 in Applesoft BASIC. Q$(0) = "#" // This array is to hold the "graphics" for drawing the maze. 0=# (walls) S = 1 // Set S to 1 so this IF statement will not run again. ====================================================== ==[ LINE 2 ]========================================== 2D(K)=T:FORT=0TO3:N=D(T):O=(N=0)-(N=1):P=(N=2)-(N=3):L(1,0)=H:L(1,1)=V ------------------------------------------------------ D(K) = T // Finish doing the direction shuffle FOR T = 0 TO 3 // Re-using T for this FOR loop N = D(T) // What direction do we check first? O = (N = 0) - (N = 1) // If N is 0, then this evaluates to 1. If not, it evaluates to 0. So if N is 0, this would read O = 1 - 0 P = (N = 2) - (N = 3) // If N was 3, then this would read as P = 0 - 1. This technique gives us four possible outcomes for (O,P). (1,0), (-1,0), (0,1), (0,-1) L(1, 0) = H // Set the first position of our list of positions to H,V. L(1, 1) = V // These two lines run over and over setting this position to the same X,Y but it doesn't matter because if the list ever gets to 0 then the maze generator stops. ====================================================== ==[ LINE 3 ]========================================== 3X=L(I,0)+O*2:Y=L(I,1)+P*2:E=1:IFX>39ORX<1ORY>23ORY<1ORG(X,Y)THENE=0 ------------------------------------------------------ X = L(I, 0) + O * 2 // O * 2 because we are checking two spaces away to see if we can carve a tunnel there. Y = L(I, 1) + P * 2 // We take the X,Y if the top location on our list, then add the offset O,P to get the X,Y to check E = 1 // The following IF was too long for line 4, so we are using E to teleport the result of the check to the next line IF X > 39 OR X < 1 OR Y > 23 OR Y < 1 OR G(X, Y) THEN E = 0 // Check X,Y to see if it's out of bounds, or if G(X,Y) already has a tunnel (0 = false) ====================================================== ==[ LINE 4 ]========================================== 4IFETHENT=3:G(X,Y)=1:G(X-O,Y-P)=1:I=I+1:L(I,0)=X:L(I,1)=Y:NEXTT:GOTO1 ------------------------------------------------------ IF E THEN T = 3 // By setting T to 3, the next NEXT T will exit the loop. IF E is false, Applesoft BASIC will skip the rest of the statements in this line G(X, Y) = 1 // If we are here, the X,Y location must be good. Set it to 1 (floor) G(X - O, Y - P) = 1 // Since the X,Y location we checked was two spaces away, back off one space and carve that too. I = I + 1 // We have a new valid space to add to our list. L(I, 0) = X // Add the X of space index I to the list. L(I, 1) = Y // Add the Y of space index I to the list. NEXT T // We found a valid space to carve in to, and since we set T to 3, we can now end the FOR loop. GOTO 1 // If we found a valid space, we need to check it's neighbors. Lets do it all again. ====================================================== ==[ LINE 5 ]========================================== 5HTABH:VTABV:?"DIGGING"MID$("-\|/",T+1,1):NEXTT:I=I-1:Q$(1)=" ":IFIGOTO1 ------------------------------------------------------ HTAB H // I had some extra room on this line, VTAB V // so I re-used H and V to set the screen location for the following ? "DIGGING" MID$("-\|/", T + 1, 1) // ? is the same as PRINT in Applesoft BASIC. You also don't need any special characters to concatenate. // MID$ uses the T variable to go through the string "-\|/" and prints one of those after "DIGGING" to do the random spinny animation. NEXT T // This whole line is what runs when the direction we checked doesn't reveal a valid new location to carve in to. This NEXT says to keep checking until we check all four directions. I = I - 1 // If we didn't find a new direction to carve, then step back one place on our list of locations we've visited. Q$(1) = " " // Just crammed this in here because I needed to put it somewhere. 1=" " (floor) IF I GOTO 1 // If our index is not 0 (false) then we still have spaces to check that we might be able to branch off of. ====================================================== ==[ LINE 6 ]========================================== 6G(38,22)=2:Q$(2)="X":FORY=1TO23:FORX=1TO40:?Q$(G(X,Y));:NEXTX,Y ------------------------------------------------------ G(38, 22) = 2 // Crammed this in here. This the location for X, our exit from the maze Q$(2) = "X" // Last tile to define. 2=X (exit) FOR Y = 1 TO 23 // Since we draw the maze wall-space-wall, the size will always be odd. So 24-1 max height. FOR X = 1 TO 40 // Except to save code space we are drawing an extra column of walls... ? Q$(G(X, Y)); // Checked the number in the grid G and PRINTs the correct tile to the screen. // The trailing semi-colon tells the PRINT (?) statement to not go to the next line after printing. NEXT X, Y // Loop through the rows and columns. ====================================================== ==[ LINE 7 ]========================================== 7HTABH-A:VTABV-B:?" ":HTABH:VTABV:?"@":WAIT49152,128:K=PEEK(49152)-128 ------------------------------------------------------ HTAB H - A // Need to erase the player from their old location, so we subtract the offset. VTAB V - B // If this is the first loop through, it's just 0. ? " " // Draw a space where the player was. HTAB H // Move the cursor to the players new location VTAB V // the players new location. ? "@" // Draw the player. WAIT 49152, 128 // Sekret Apple code to wait for a keypress. K = PEEK(49152) - 128 // Get the key that was pressed from the keyboard buffer. ====================================================== ==[ LINE 8 ]========================================== 8A=(K=21)-(K=8):B=(K=10)-(K=11):T=G(H+A,V+B):IFT=0THENA=0:B=0:?CHR$(7) ------------------------------------------------------ A = (K = 21) - (K = 8) // Same as what we did in line 2, only this time we're checking what key was pressed. B = (K = 10) - (K = 11) // 21,8,10,11 for right,left,down,up. You could also use 68,65,83,87 for W,A,S,D. T = G(H + A, V + B) // Find out what is in the location we want to move to. IF T = 0 THEN A = 0 // If it's a wall (0) B = 0 // then we won't move there. ? CHR$(7) // Printing character 7 makes a beep. That's what you get for running into a wall. ====================================================== ==[ LINE 9 ]========================================== 9H=H+A:V=V+B:POKE49168,0:ONT<2GOTO7:?"":?"":?"YOU'RE FREE!":END ------------------------------------------------------ H = H + A // If A and B were a good offset location, then this will set H and V to the new locations. V = V + B // If not, then they equal 0 so this does nothing. POKE 49168, 0 // clear the keyboard buffer and get ready for more player button mashing. ON T < 2 GOTO 7 // This was a fun one. I wanted an IF, but that would require an extra line of code that I didn't have. ? "" // So what ON GOTO does is takes a list of locations to go to (in this case only one), then it uses the number ? "" // specified after the ON to pick the location. In this case we do a compare to see if T less than 2. If it's not, then it means we have reached the exit. ? "YOU'RE FREE!" // So if T is 0 or 1, then T<2 will evaluate to 1 which will then GOTO the first location on the list. If it evaluates to 0 (false), END // then it skips the GOTO and just keeps executing the rest of the line. Which is just printing out message that you're free and then ending the program. ======================================================
Status | Released |
Author | mauszozo |
Tags | 8-Bit, ascii, asciibasic10liner, Minimalist, Retro |
Comments
Log in with itch.io to leave a comment.
Your maze generation routine is superb. Great work.