100-line maze by RoePipi
About
For Category "PUR-80"
Title: "100-line maze"
Platform: Commodore 16 / Plus/4
Language: Commodore BASIC V3.5
INTRODUCTION
============
Wander through a binary maze (editable through program line 0), optionally measure your time.
FEATURES
========
- Fast action
- Joystick control (port 2 only)
- Diagonal movements
- 100(+4) screen rows of maze (editable through program line 0)
- Measure your time
GAME INSTRUCTIONS
=================
After starting the program, a 33-second rendering phase takes place, featuring some border color cycling. Then, the upper portion of a maze appears with your character in the middle of the screen.
Using joystick in port 2, move your character in the possible 8 directions, avoiding the walls. (They're not lethal though.) You can only step on the dots. If you reach the edge of the maze, you can't move further that way. Your aim is to reach the very bottom of the maze, the 104th screen line. If you succeed, the game displays your playing time (in 1/60th seconds), which you can compare to other players' runs.
PROGRAM LINES OVERVIEW
======================
Use the LIST command to view the program lines. You can use the LIST from-to syntax to list certain intervals, eg. LIST 1-5.
0 set map string
1-5 init & calculations
6 display initial screen, basic binary wall patterns data, joystick directions data, reset timer
7 loop: determine player move
8 scroll screen up or down
9 draw a line of the map; loop end, finish check
VARIABLES
=========
A, B, C, D, I loop variables
const A$(0-3) basic binary wall patterns (2-bit)
const B$(0-255) binary wall patterns (8-bit)
const E$ ESC char
const H$ HOME char
const K ROM read switch poke address (1177)
M map line to draw (0-115)
const M$ map, 5*13=65 char
const M$(0-103) map rows gfx
const O chargen ROM base address (53248)
const P player poke code (88)
const R player poke address row start (3552)
const S space poke code (46)
X player poke address (R - R+39)
Y player Y coordinate (map row, 0-103)
const W screen window poke base address (2022)
const X,Y(0-8) joystick X,Y directions (-1/0/1)
DETAILED CODE EXPLANATIONS
==========================
Note: Interconnected codes span across several lines. I marked the start line of the interconnected statements. Complex loops are expanded.
Also note that I use decimal dots (".") everwhere instead of "0" to gain extra speed! (BASIC 3.5 processes them faster)
LINE 0
------
// set maze map data, each line is 5 characters, freely editable
M$="(5*13=65 chars of map data)"
// read basic binary wall patterns (2-bit: "..", ".W", "W.", "WW")
FOR A=. TO 3:READ A$(A):NEXT
LINE 1
------
// read movement data for the 8 possible joystick directions (combinations of -1;0;1)
FOR A=1 TO 8:READ X(A), Y(A):NEXT
// set space character (screen code)
// FUN TIP: if set to 32 (space) and you set basic binary wall patterns at line 6 accordingly (" ", " W", "W ", "WW"), you can move upwards infinitely :)
S=46
// set HOME char, for line 9, to spare 2 characters there
H$="{home}"
// set player screen address row start (column 0 on line 12)
R=3552
// player will start at center of screen
X=R+20
// binary wall pattern data will require 256 different values; map will render into 104 lines
DIM B$(255), M$(103)
LINE 2
------
// initially, player will start at line 11 of map
Y=11
// set player screen poke code
P=88
// set ROM read switch poke address, used to fetch character data
K=1177
// set ROM character data base address, also used to flag line draw done at line 9 (var M)
O=53248
// initial "don't draw" flag; at start, we will display a screenful of lines at once instead
M=O
// Esc code shorthand to spare some characters later
E$=CHR$(27)
// disable BASIC scroll to be able to fill last columns of screen, clear screen, display message to user and setting cursor position for displaying initial map correctly later
PRINT E$"M{clear}{down} RENDERING...{up}"
// fill binary wall pattern data based on basic patterns and loop variable I
FOR A=. TO 3
::::FOR B=. TO 3
::::::::FOR C=. TO 3
::::::::::::FOR D=. TO 3
::::::::::::B$(I)=A$(A)+A$(B)+A$(C)+A$(D)
::::::::::::I=I+1
NEXTD, C, B, A
LINE 3
------
// poke address to set screen window top row; used for setting bottom row as well (2021) later
W=2022
// loop through map columns
FOR B=. TO 4
::::// loop through map rows
::::FOR A=. TO 12
::::::::// fetch the CHR$ code of one character from M$ (map string) based on A and B
::::::::C=ASC(MID$(M$, A*5+B+1, 1))
::::::::// convert CHR$ code to screen code (expressions in brackets evaluate to -1 if true and 0 if false)
::::::::C=C+(C>63)*64+(C>191)*64
::::::::// loop through character shape lines
::::::::FOR D=. TO 7
::::::::::::// set reading from ROM (NOTE: running on Plus/4, you MUSTN'T use string variables or anything that uses upper (>$8000) memory addresses while this is active)
::::::::::::POKE K, 62
::::::::::::// fetch a line of a character shape
::::::::::::I=PEEK(O+C*8+D)
::::::::::::// set reading from RAM back
::::::::::::POKE K, 63
::::::::::::// add new character line strings to the matching rows of the map strings array
::::::::::::M$(A*8+D)=M$(A*8+D)+B$(I)
::::::::NEXT
::::::::// display some border color cycles which then stops at a nice blue-green color
::::::::COLOR 4, A+1, B+1
::::NEXT
NEXT
LINE 5
------
// display the first 24 rows of the map (starts from the second row of the screen, fills the rest)
FOR I=. TO 23:PRINT M$(I);:NEXT
LINE 6
------
// basic binary wall patterns and joystick directions data
DATA "..", ".W", "W.", "WW", , -1, 1, -1, 1, , 1, 1, , 1, -1, 1, -1, , -1, -1
// reset timer, used to display play time at the end
TI$="000000"
LINE 7
------
// loop start
DO
::::// read joystick state at port 2, ignoring fire button (value 128)
::::I=JOY(2) AND 15
::::// A holds joystick X direction except if it's at either side of the screen and points outwards
::::A=X(I)-(X=R AND I>5)+(X=R+39 AND I>1 AND I<5)
::::// B holds joystick Y direction
::::B=Y(I)
// continue only if target screen address holds a "space" code
LOOP UNTIL PEEK(X+A+B*40)=S
LINE 8
------
// place a "space" character at player's position
POKE X, S
// add horizontal movement to player's X position (even if it's zero)
X=X+A
// if there's vertical movement
IF B THEN
::::// add vertical movement to player's Y position
::::Y=Y+B
::::// print an Esc code to scroll screen contents up or down, based on Y direction
::::PRINT E$CHR$(86-(B<.))
::::// place player character
::::POKE X, P
::::// set a screen window either to the top or bottom row, based on Y direction
::::POKE W+(B<.), (B>.)*-24
::::// will display 12 rows above or below player, based on Y direction
::::M=Y+B*12
::::// clear window (necessary; otherwise the interpreter crashes)
::::SCNCLR
LINE 9
------
// not enough place to do an IF, so display player character anyway
POKE X, P
// "inline" if using a DO-LOOP: display a map row only if we are inside dimensions 0-103
DO WHILE M>=. AND M<104
::::PRINT M$(M);
::::// flag a "don't draw" (until M is set a valid value next time)
::::M=O
LOOP
// unset window (2x HOME key)
PRINT H$H$
// if player is inside the map, go back to line 7
IF Y<103 THEN 7
// else position just below the visible map and display elapsed time
ELSE
::::CHAR , ., 14, "TIME:"
::::PRINT TI
// end
JOYSTICK DIRECTIONS
===================
8 1 2
\|/
7-0-3
/|\
6 5 4
CONVERTING CHR$ TO SCREEN CODES
===============================
CHR$ -> screen
32-63 -> 32-63 (same)
64-95 -> 0-31 (-64)
96-127 -> 64-95 (-32)
160-191 -> 96-127 (-64)
192- -> 64- (-128)