ALERT! ALERT! We are doing this wrong. It has been pointed out in a comment to an earlier installment that we missed an important part about what this contest was supposed to produce!
More on that in a moment… But first, let’s look at a faster version of the challenge, created by Dillon Teagan:
10 TIMER=0
20 PMODE4,1:PCLS1:SCREEN1,1
30 L=&HFF:I=-3:DIMR,L,U,D
40 D$="D=D;R=R;U=U;L=L;
50 DRAW"BM0,191C0R=L;U191L=L;
60 FORD=188TO3STEP-6:R=L+I:U=D+I:L=R+I:DRAWD$:NEXT
70 PRINTTIMER/60
This one clocks in at 2.7 seconds, and does some wonderful optimizations!
First, Dillon clearly understands how the BASIC interpreter works. He is doing things like using hex &HFF instead of decimal 255 which makes that bit parse a tad faster. Next, you see him declare a variable, followed by a DIM which pre-declared R, L, U and D. In this example, that DIM probably does not help, but in a real program, you can declare your variables up front and do them in the order of “most accessed” to least. This sets their order in the variable table, so when you try to access one, the one you access the most can be at the top of the list. I’ve posted about this before, but consider this:
10 DIM A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z
20 PRINT Z
30 Z=Z+1
40 IF Z<100 THEN 20
If we truly did have 26 variables in use, and declared them like this, Z would be at the end of the variable table. EVERY time Z is needed, BASIC has to scan through all 26 variables trying to match Z so it can be used. This would be MUCH slower than, if you knew Z was being used the most often, you did this:
10 DIM Z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y
20 PRINT Z
30 Z=Z+1
40 IF Z<100 THEN 20
With that one minor change (declaring Z first), Z is now the first variable in the table and will be found much quicker. Try it sometime.
But I digress…
The next cool optimization Dillon does is by drawing the initial bottom line (bottom left to bottom right, 256 pixels wide) and right side (bottom right to top right, 192 pixels tall) in line 50, along with the first left (top right to top left, 256 pixels wide) before entering the loop.
The loop itself is using a FOR/NEXT loop which is faster than using GOTO since no line scanning has to be done. BASIC stores where the FOR is, and when NEXT is encountered it pops back to that location rather than scanning forward to find the target line, or starting at the first line and scanning forward from there. Nice.
With the first three “full width/height” lines out of the way, the loop is just doing the “minus 3” size of all four lines. That’s clever. The entire draw string is in a string (D$) and I am unsure if this speeds things up versus having it directly in the line itself (line 60).
Impressive. I wish I’d thought of that!
However… we have been doing it wrong, so far, it seems.
We’ve been doing it wrong so far, it seems.
In a comment left on part 1 of this series, Paul Fiscarelli pointed out something that I completely got wrong:
Hello Allen – I sent you a DM on FB, but I don’t think you’ve seen it yet. What you have posted above is not exactly an accurate reproduction of what is being asked in the challenge question. You are using an offset decrease of 3 in each of your iterations, which produces a gap of only 2-pixels in both height and width. The challenge is indicating a gap of 3-pixels between lines, which requires an offset decrease of 4 in each iteration. This is further evident in the challenge’s diagram of the spiral, which indicates a line-length of 188-pixels for the line on the far left-hand side of the screen. If you perform a screen grab in XRoar (pixel perfect geometry of 640×480 window and 512×384 screen resolution), you will find your code generates a line length of 189 pixels (scale the 512×384 in half).
If you change the offset decrease in your code to 4 instead of 3, you will achieve a render time of roughly 2.43 seconds. This is due to the fact that you are rendering about 23% fewer lines with the DRAW statement.
You can reduce this time even further if you were to use only a single offset variable for drawing both the horizontal and vertical lines, and by adding a separate width to the horizontal lines with a value of 64 = (256w – 192v). This method will shave roughly 0.10 seconds off your render time, down to approximately 2.33 seconds.
10 TIMER=0
20 PMODE4,1:PCLS:SCREEN1,1
30 OF=191:DRAW”BM0,191″
40 DRAW”R=OF;R64″
50 DRAW”U=OF;L=OF;L64;”
60 OF=OF-4
70 DRAW”D=OF;R=OF;R64;”
80 OF=OF-4
90 IF OF>0 THEN 50
100 TM=TIMER
110 IF INKEY$=”” THEN 110
120 PRINT TM/60As an additional optimization step, you can replace the IF/THEN boundary check with a FOR/NEXT loop, to shave another 0.05 seconds from your render time – getting it down to roughly 2.28 seconds.
10 TIMER=0
20 PMODE4,1:PCLS:SCREEN1,1
30 OF=191:DRAW”BM0,191″
40 DRAW”R=OF;R64″
50 FOR N=0 TO 23
60 DRAW”U=OF;L=OF;L64;”
70 OF=OF-4
80 DRAW”D=OF;R=OF;R64;”
90 OF=OF-4
100 NEXT
110 TM=TIMER
120 IF INKEY$=”” THEN 120
130 PRINT TM/60There are probably some other optimizations that can be done here – but it’s a start. Oh, I also tested these examples with VCC 2.1.9.2-pre2. I’m sure there will be slight variations in timing with the different emulators, and probably even with different versions of VCC.
– Paul Fiscarelli
So, uh … oops. It seems obvious now:

Let’s regroup, and take a look at Paul’s versions in the next installment.
Until then… RTFM!