C64 Tetris
Here is the original Tetris program I wrote back in the early 90s. Everything is as it was. Nothing is changed. That's quite generous on my part, owing to the very weird spacing conventions I used. Did someone in COMPUTE! tell me to do that to save valuable bytes? Very weird. I would have to pay a memory inducing quack thousands of dollars to pluck that little shard of memory from my head. Apart from the "out-there" spacing conventions, there are numerous other errors too embarrassing to bring to everyone's attention in bullet point form.
Of particular note is the fact that this isn't ordinary Tetris -- this is Tetris with pieces that are made with 5 bricks instead of the usual humdrum 4 brick pieces. I guess this shouldn't really be called Tetris. Pentris would have been better, but then people would have vandalized the arcade machine cases and made crude remarks with the game's name... so Tetris II is the official name for this one. For a straw-man attack to support this mediocre naming scheme, see the Intel naming scheme for the Pentium chips.
I was able to pluck it from the original disk and into ASCII land by using the XE1451 Extended Cable, the Star Commander Program, and a paper clip. Many thanks to the creators of these products... I don't believe the original disks would have withstood another winter outside in an unheated shed on my parent's farm. The screen shots were obtained from the VICE emulator.
2 poke53281,0:poke53280,0:printchr$(8)
Everything in black. How psychologically interesting. My mother said that even as toddlers, a personality is quite well formed. And look no further than right here -- we've got the POKEs and the Cascading Style Sheets to prove this idiom right.
3 printchr$(158):poke650,0
5 dims(17,4,4)
6 b=1053:c=55325:fora=1to7:pokeb,93:b=b+40:pokec,12:c=c+40:nexta:pokeb+40,74
8 pokeb+1,75:pokec+1,12:c=c-41:b=b-41:fora=1to7:pokeb,66:b=b-40:pokec,12:c=c-40
All these FOR loops created the border for the "next shape" box -- we're POKE'ing in the border lines with FOR loops. The screen display codes give this one away, if you translate them using the proper Commodore cypher -- 12 is space, 74 is a round character in the upper right corner, 75 is the same in the left corner, and 66 is "|".
12 fora=1to30:readz9:readx9$:nexta
15 fora=1to17:forb=1to4:forc=1to4:reads(a,b,c):nextc:nextb:nexta
All of the shapes are loaded into the "s" array. Each indice represents one block of a total shape. The a indice delineates each shape, the b indice determines the shape it becomes for each of the three rotations, and the c indice describes the each individual block of the pieces. There are only 4 because the middle block is always known to exist, so there is no need to store its indice.
20 print"{clear}":goto25
21 b=1052:c=55324:fora=1to7:pokeb,93:b=b+40:pokec,12:c=c+40:nexta:pokeb,74
22 pokec,12:c=c+1:b=b+1:fora=1to10:pokeb,67:b=b+1:pokec,12:c=c+1:nexta
23 pokeb,75:pokec,12:c=c-40:b=b-40:fora=1to7:pokeb,66:b=b-40:pokec,12:c=c-40
24 nexta:return
25 gosub21
GOTOs considered harmful -- what about a GOTO (line 20) into a GOSUB (line 25) to prime three back-to-back FOR loops. Why did my parents never buy me academic journals for Christmas?
30 fora=1to17
60 fora=1034to1760step40:pokea,66:nexta:gosub10000
70 poke1794,109:fora=1795to1810:pokea,67:nexta
80 poke1811,125:fora=1771to1049step-40:pokea,93:nexta
82 b=55306:fora=1to19:pokeb,12:b=b+40:nexta:fora=1to17:pokeb,12:b=b+1:nexta
83 fora=1to20:pokeb,12:b=b-40:nexta
90 c=1924:wg=8:gosub5000

The image shown displays the screen as it is being constructed. The circles (character number 81 in Commodore-speak) were actually put into the screen buffer, and then turned black, and therefore invisible. The pieces were made visible by changing the color of the individual circles black.
Line 5000 seems to contain the "a-mazin' subrutine", which 'unfolds' a message on the screen. See the subroutine for more details. The message displayed is identified by the wg variable, which in turn references a particular element of the data array of messages located later on. The c variable holds the screen location of where the message starts.
100 sh=int(rnd(0)*17+1):sp=1:s3=int(rnd(0)*17+1)
101 ifw<1030orw>1212thenl3=5
102 ifw<1229orw>1452thenl3=10
103 ifw<1469orw>1692thenl3=15
104 ifw<1709orw>1802thenl3=19
120 remshape-down!
130 w=55316:ti$="000000"
140 pokew,1:fora=1to4:pokew+s(sh,sp,a),1:nexta:geta$:iffd=1then4000
In line 140, we do a couple of things. One, we draw the shape. Secondly, if fd is 1, then the space bar has been pressed (see line 149), and we can go into the proverbial free-fall mode.
145 poke1138,81:fora=1to4:poke1138+s(s3,1,a),81:nexta
146 poke1053,14:poke1054,5:poke1055,24:poke1056,20:poke1058,19:poke1059,8
147 poke1060,1:poke1061,16:poke1062,5
This is particularly stupid. In Commodore-speak, 14 = N, 5 = e, 24 = x, etc. This puts down the words "next shape" during EVERY TURN.
149 ifa$=" "thenfd=1
150 ifa$="z"then1000
160 ifa$="x"then1000
170 ifa$=","then1000
180 ifa$="."then1000
190 ifa$=chr$(13)then3000
200 ifti$>"000000"then4000
210 goto140:print210
1000 remchehx
1001 ifti$>"000000"then4000
A string comparison... you know, this could be the root cause of the performance issues.
1002 pokew,0:fora=1to4:pokew+s(sh,sp,a),0:nexta
1003 w2=w:s7=sp:tr=0
1004 ifa$="z"thensp=sp+1
1005 ifsp=5thensp=1
Ignore this. Very sloppy work here. This is already taken care of in line 1015.
1010 ifa$="x"thensp=sp-1
1011 ifa$="."thenw=w+1
1012 ifa$=","thenw=w-1
1015 ifsp=0thensp=4
Looks like the sp variable is the rotation variable, and the w variable is the width variable.
1020 fora=1to4
1021 c=peek(s(sh,sp,a)+w)
1031 ifc=224thentr=tr+1
1032 ifc=160thentr=tr+1
1033 ifc=128thentr=tr+1
1034 ifc=176thentr=tr+1
1035 ifc=32thentr=tr+1
1037 ifc=144thentr=tr+1
1039 ifc=16thentr=tr+1
1040 ifc=0thentr=tr+1
1041 ifc=48thentr=tr+1
1042 ifc=112thentr=tr+1
1043 ifc=240thentr=tr+1
1044 ifc=96thentr=tr+1
We're checking to see if we hit any of the shapes currently placed on the board. We're PEEKing to see what's in that location. And then the tr variable... well, I'll get back to you. When it is 4, that means there are no spaces being taken up by the HYPOTHETICAL next position of the shape, and we can move the shape down one more line.
1045 nexta
1047 iftr=4then140
...and if we do see that we've got something in the way, we leave the shape right there.
1171 w=w2:sp=s7:goto140
3000 rempause
This is free fall mode. Straight down!
4000 w2=w:s7=sp:tr=0:
4020 pokew,0:fora=1to4:pokew+s(sh,sp,a),0:nexta
Erase the shape in the previous space.
4025 w=w+40
This moves the shape down a notch. This is the HYPOTHETICAL position that the shape will take on the next turn. We now check to make sure that it can fit there (i.e. no other shape's in the way).
4030 fora=1to4
4040 s9=w+s(sh,sp,a)
4050 c=peek(s9)
4060 ifc=0thentr=tr+1
4061 ifc=144thentr=tr+1
4062 ifc=16thentr=tr+1
4065 ifc=48thentr=tr+1
4067 ifc=96thentr=tr+1
4068 ifc=176thentr=tr+1
4070 ifc=112thentr=tr+1
4075 ifc=128thentr=tr+1
4080 ifc=240thentr=tr+1
4082 ifc=32thentr=tr+1
4083 ifc=160thentr=tr+1
4084 ifc=224thentr=tr+1
Tsk tsk. Copied code. Do you think I used the old print-the-lines-out-then-rewrite-the-new-line-numbers-over-top trick? I miss doing that -- vi-like power in a Microsoft product.
4085 nexta
4090 iftr=4then4500
4100 w=w2:sp=s7:gosub21
4110 pokew,1:fora=1to4:pokew+s(sh,sp,a),1:nexta
4115 poke1138,96:fora=1to4:poke1138+s(s3,1,a),96:nexta
4120 sh=s3:s3=int(rnd(0)*17+1)
4130 w=55316:gosub6000
If we've hit bottom, then we can check to see if any lines have been made.
4140 fd=0:ti$="000000":sp=1:goto140
4500 remo.k.
The shape is OK, nothing's in the way... let's drop the shape another level.
4510 pokew,1:fora=1to4:pokew+s(sh,sp,a),1:nexta
4520 ti$="000000"
4540 goto140
5000 rem*a-mazin' subrutinÁ
5005 ifwg>29then5035
5010 restore
5020 fora=1towg:forb=1to2:reada1$:nextb:nexta
5030 readb1:reada1$
5035 c1=b1
5037 c2=c
Wouldn't want any side effects, would we? Can't be wrecking that valuable c variable. I am sure that EJD would have smiled at this childhood adherence to acceptable software practices from his place in the sky (well, actually, back then, I guess it would have been Texas).
5040 fora=0tob1
5050 d1=asc(mid$(a1$,b1,1))
5051 ifd1<28ord1>62then5070
5069 pokec,asc(mid$(a1$,b1,1)):goto5075
5070 pokec,asc(mid$(a1$,b1,1))-64
POKE the text in there, starting from the MIDdle.
5075 pokec+40,64:pokec-40,64
5076 pokec+1,66:pokec+41,75:pokec-39,73
5080 b1=b1+1:c=c+1:nexta
5081 b1=c1:pokec,66:pokec+40,75:pokec-40,73
The lines are placed above and below the text (one column is 40 characters wide in Commodore-speak).
5085 c=c2
5090 fora=1tob1
5095 d1=asc(mid$(a1$,b1,1))
5096 ifd1<28ord1>62then5100
5097 pokec,asc(mid$(a1$,b1,1)):goto5105
5100 pokec,asc(mid$(a1$,b1,1))-64
This code is never entered... Looks like I was testing to see if I could put everything into lowercase.
5105 pokec+40,64:pokec-40,64:
5107 pokec-1,66:pokec-41,85:pokec+39,74
5110 b1=b1-1:c=c-1:nexta
5115 pokec,66:pokec-40,85:pokec+40,74
5120 return
6000 rem check for 'da lines
Well, now that we're all the way here, I should tell you a little secret. This program never worked. I was so close... but it just wouldn't properly check for any lines. Perhaps the... OH MY GOD! I found the problem. The xx variable is incremented by one for every line we hit. If it hits 16, then we've got a line. BUT, if there was no line it would have been less than 16. And in the incriminating line 6060, we subtract by 16 every time. How heartbreaking. I was THIS close to child freakin' genius of the decade. UPDATE: Nope, that's not it. Not getting any smarter in my old age, I guess. Fairly embarrassing to leave those previous comments there. That said, in the spirit of this code review, I'll not discriminate against stupidity of a more recent vintage. The xx variable simply keeps track of the current screen location that we are checking for the presence of present blocks. Perhaps the wrong value from the peek is being returned in line 6025?
6010 xx=55307
6020 fornn=1to19
6023 fornj=1to16
6025 c=peek(xx)
6030 ff=c/16
6031 fg=int(ff)
6032 iffg<>ffthentr=tr+1
6040 xx=xx+1:nextnj
6050 iftr=16then9000
6060 xx=xx-16:xx=xx+40:nextnn
6070 return
9000 rem gnarly! a line!
Here, we move all the lines down. Laboriously going through all the lines that are above the subtracted lines, we look at what's above, and move it down. Is there any other way?
9010 xx=xx-nj:xx=xx-1
9020 fornn=1to16:xx=xx+1
9030 pokexx,15:forx=1to50:nextx:pokexx,12:forx=1to50:nextx:pokexx,11
9040 forx=1to50:nextx:pokexx,0
9050 dx=xx
9060 dx=dx-40
9065 c=peek(dx):ff=c/16:fg=int(ff)
9070 ifff<>fgthenpokedx-40,1:pokedx,0
9080 dx=dx-40:ifdx<55296thennextnn
9090 x=int(random(0)*13+1)
If ya got a line, I've got a message for ya...
9091 ifx=1thenwg=0
9092 ifx=2thenwg=1
9093 ifx=3thenwg=3
9094 ifx=4thenwg=12
9095 ifx=5thenwg=14
9096 ifx=6thenwg=15
9097 ifx=7thenwg=17
9098 ifx=8thenwg=19
9099 ifx=9thenwg=23
9100 ifx=10thenwg=24
9101 ifx=11thenwg=25
9102 ifx=12thenwg=26
9103 ifx=13thenwg=27
9110 c=1924:gosub5000
9120 return
10000 xx=1035:x1=55307:forb=1to19
10001 fora=xxtoxx+15:pokea,81:nexta:xx=xx+40:fora=x1tox1+15:pokea,0:nexta:x1=x1+40
10002 nextb:return
50000 data10,your're a tetris dude,12,keep those lines coming!
Here's the fun part. Is there any better word than gnarly? And dude. And superdude! Of course, I knew when to quit -- I cooled things down with that "regular tetris person" line.
50010 data8,gnarly! a tetris,14,excellent placing of tetrads
50020 data5,game over!,5,pause mode,19,select song by pressing number(1 to 6)
50030 data12,start song again (y-n)?,8,start your game!
50040 data18,maybe candyland would be more your game!
Ooooh. Ya, Candyland... just like my little SISTER plays. Loser.
50050 data17,press (space) or m to choose music
50060 data16,1000 points till your high score,5,high score
50070 data11,regular tetris person,4,oh! oh!,11,superdude! great going
These are interesting... the previously mentioned "regular tetris person" quotation... the orgasmic "oh! oh!"... and the saccharine yet jarring "superdude! great going". All in one line, folks.
50080 data2,wow!,6,high scores!,10,major gnarly player!,5,try again.
50090 data5,excellent!,14,ha! ha! that score is a joke
50100 data14,disgrace to the tetris race!,3,gross!
"Disgrace to the Tetris Race!" I rather like this one. Nice cadence.
50110 data14,tetrisdude. or tetrisdudette,9,been playing alot?,3,cool!!
Even back then, I knew that SEXISM WAS WRONG.
50120 data16,condider yourselve a mentalblock,7,you are a hero,4,you suck
55560 data40,80,1,41,40,41,42,1,40,80,41,81,1,2,41,42
55565 data40,80,39,41,40,80,39,38,1,2,41,81,40,41,42,80
55570 data40,1,41,81,1,2,40,41,40,80,41,81,1,40,41,39
55575 data1,2,42,43,40,39,79,119,1,41,42,43,40,80,79,119
55580 data40,80,120,160,1,2,3,4,40,80,120,160,1,2,3,4
55585 data1,41,81,121,40,1,2,3,40,80,120,121,40,39,38,37
55590 data1,40,80,120,40,41,42,43,40,80,120,119,1,2,3,43
55595 data40,41,81,121,40,39,1,2,40,80,81,121,1,40,39,38
55600 data40,41,42,2,1,41,81,80,1,2,40,42,40,80,81,1
55605 data40,80,120,79,1,2,3,42,40,80,120,41,39,40,41,42
55610 data40,80,120,39,1,2,3,41,40,80,120,81,38,39,40,41
55615 data40,80,39,41,40,80,41,39,40,80,41,39,40,80,41,39
55620 data40,80,81,82,40,80,79,78,1,2,42,82,1,2,40,80
55625 data1,40,39,79,40,41,81,82,40,39,79,78,1,41,42,82
55630 data40,39,41,81,1,40,80,39,40,41,42,81,40,80,41,79
55635 data40,80,81,39,40,39,38,79,1,41,81,42,40,41,39,79
55640 data40,39,38,78,1,41,81,82,40,41,42,82,1,41,81,82
55650 data0,31,254,0,31,254,0,95,254,0,199,112,3,135,112,6,63,254,12,0,0,13,187
55660 data46,29,18,168,53,147,46,69,18,162,133,146,174,4,0,0,5,255,254,4,7,112,4
55670 data7,112,8,31,254,0,31,254,0,31,254,0,0,0,0,0,0