See also: part 1, part 2, part 3 and part 4.
Previously, I presented this Color BASIC program:
0 ' BIGMAZE.BAS 10 C=2 20 B$=CHR$(128) 30 L$=CHR$(128+16*C+9) 40 R$=CHR$(128+16*C+6) 50 M$(0,0)=B$+R$:M$(0,1)=R$+B$ 60 M$(1,0)=L$+B$:M$(1,1)=B$+L$ 70 P=512-32*2 80 M=RND(2)-1 90 PRINT@P,M$(M,0);:PRINT@P+32,M$(M,1); 100 P=P+2:IF P>479 THEN PRINT:GOTO 70 110 GOTO 80
Running it produces this:
And I ended with “Make it smaller. Make it faster.”
William Astle commented:
Welp, everything up to line 60 can be mashed into a single line. Since it’s all setup and none of it is performance critical, you can dispense with the variables and just set the M$ array directly. More typing, but it keeps the variable table smaller. Or define P right at the start so it’s the first variable in the table which would give you a speedup all on its own since the lookups to find P will be faster.
I suspect that if you put the PRINT statement from line 100 at, say, the start of line 70 and have “THEN 70” instead of “THEN PRINT:GOTO 70”, you might get a bit of a performance gain there, especially in the false case where that gives a handful fewer bytes to skip over.
There might be some sort of trick involving FOR/NEXT that can be used to improve the main loop but I think the overhead of setting up a FOR loop will be more than the saving in this case, especially if the setup lines are combined into a single program line.
On a side note, and this won’t improve the speed any, you could put a DIM M(1,1) at the start to avoid the implied DIM M(10,10). That saves a bit of memory, though I don’t think that’s even an issue for this program even on a 4K machine. But it is 585 bytes nevertheless.– William Astle
Let’s start with the “everything up to line 60” part, which gives us this:
If I compare that to the original, it’s about a few less characters to type:
It loses the ability to change the color of the maze (easily), but it saves three string variables (B$ for blank block, R$ for right block, and L$ for left block) and one numeric variable (C for color). Definitely lower RAM use, and I am sure it is code-space too since you can’t tokenize “128+16*C+6” (10 bytes) which is replaced by “166”.
Combining the rest of the lines, where possible, and moving the PRINT (so line 100 has a bit less to parse through to get to the end of that line when P>479 is not true) results in:
0 ' BIGMAZE2.BAS - William Astle 10 M$(0,0)=CHR$(128)+CHR$(166):M$(0,1)=CHR$(166)+CHR$(128):M$(1,0)=CHR$(169)+CHR$(128):M$(1,1)=CHR$(128)+CHR$(169) 70 PRINT:P=448 80 M=RND(2)-1:PRINT@P,M$(M,0);:PRINT@P+32,M$(M,1);:P=P+2:IFP>479THEN70 110 GOTO80
On my simulated CoCo, removing the REM statements, then loading the original version and doing “? MEM” showed 8256. Doing the same to the second version shows 8307 — saving 51 bytes of program space. I did not measure what the saving in string and variable memory would be, but that would be even more. Great win.
Since the difference was mostly in the setup of the variables, they should run at the same speed — or will they? Let’s quickly test William’s suggestion of moving the PRINT so the IF statement doesn’t have to parse the end of the line:
0 'bigmazebench.bas 100 P=0:TIMER=0:A=0 110 P=0 120 P=P+2:IF P>479 THEN PRINT:GOTO 110 120 A=A+1:IF A >1000 THEN 150 140 GOTO 120 150 PRINT TIMER 200 P=0:TIMER=0:A=0 210 PRINT:P=0 220 P=P+2:IF P>479 THEN 210 230 A=A+1:IF A>1000 THEN 250 240 GOTO 220 250 PRINT TIMER
Not very elegant, but it should do the job. Since I could not easily use a FOR/NEXT loop for the counter, I used A and a check in line 130 or 230 to exit the test.
This prints 771 for the first one, and 1414 for the second one.
This is not what I would have expected. I must be doing something wrong, because I agree with William that…
IF P>479 THEN PRINT:GOTO 210
…should be slower every time P is NOT greater than 479, compared to:
IF P>479 THEN 210
In the first example, each time P is not greater than 479, BASIC should still have to skip everything past then THEN looking for either ELSE or the end of the line. It should be scanning past a PRINT and GOTO token then the number 220.
In the second example, it should only have to skip the number 210.
I think I did something wrong.
What am I missing?
To be continued…
If you have both versions in the same program, the “backwards” jumps will be slower the later in the program they are because they have to do a sequential scan of the program from the beginning to find the correct line number. If you have been running them in the same program, try separating them and running them independently.
Good catch. Normally I use the FOR/NEXT which wouldn’t be impacted by location.
I might be missing something, but I struggle to see why a for next loop wouldn’t work?
My brain may just not be working, but I was not sure how to handle it with two different GOTO points (one to continue adding, and another to reset).
What is your idea?
My idea is to use for-next for the most common result of the if then, and just a goto for the other case. Something like this
70 PRINT:FOR P=448 TO 480 STEP 2
Btw I’m not sure if the FOR NEXT tests if the variable has reached exactly the end value, or if it checks if it has reached the end value or anything more (i.e larger for a positive step or lower for a negative step).
Bonus: For a program where a certain key press ends an otherwise endless loop you could use the same variable as the “fake” loop counter and the A=ASC(INKEY$) statement, have an end of the FOR…NEXT above 255, and the IF statement that checks for the exit key can set A to 256 or so. Or if you end it with whichever keypress that generates the largest ascii code you could just set that ascii value as the TO in the FOR loop (or rather one below if STEP is implied to be 1).
Pingback: 10 PRINT big maze in Color BASIC – part 3 | Sub-Etha Software