Not Only 30 Programs for the Sinclair ZX81 1K Not only Battleships, Roulette, Blackjack, Star Wars, Breakout, Memory Left, Mini Adventure, 1K Draughts...and more all for the 1K ZX81 But also Each program explained Programming hints Space saving techniques PEEK and POKE explained and much, much more! X. PUBLISHER'S NOTE A. INTRODUCING THE ZX81 1K 1. [Random] Patterns 2. Leapfrogs 3. Noughts and Crosses 4. Pinch 5. Battleships B. GAMBLING GAMES 6. Craps 7. Fruit Machine 8. Roulette 9. Horse Races 10. Blackjack C. ZX81 SHOWS THE WAY 11. Day of the Week 12. Simple Simon 13. Kings and Queens 14. Hangman 15. Quadratic Equations 16. Simultaneous Equations D. ARCADE GAMES 17. Star Wars 18. Lunar Lander 19. Asteroids in Space 20. Bombs Away 21. UFO 22. Breakout 23. Space Taxi E. ZX81 UTILITY PROGRAMS 24. Bubble Sort 25. Line Renumbering 26. Machine Code Editor F. CHALLENGING THE ZX81 1K 27. Mastermind 28. Doctor ZX81 29. Caves and Pitfalls 30. Draughts X. MACHINE LANGUAGE MADE SIMPLE X. UNDERSTANDING YOUR ZX81 ROM X. ORDERING DETAILS X. PUBLISHER'S NOTE ------------------- We at Melbourne House are very excited to be involved with the publication of this book, making available as it dos not only 30 interesting and varied programs for the Sinclair ZX81 - undoubtedly the most affordable and most popular small computer in the world - but also giving the user an insight into the way the Sinclair ZX81 can be used and filled with programming tips. We are also publishers of other titles for the sinclair computer, as a glance at the back pages of this book will show, ranging from titles for the complete beginner to titles of interest to more experienced users. We have a commitment to providing literature and software for the Sinclair ZX81, and as you will note by leafing through this book, many of the programs we publish are the result of programs that were submitted to us by ZX81 owners. So if you have a program or article you think would be of interest to other ZX81 users, please write to us. We will give you a prompt assessment and reply whether the material is something we could use. In the meantime, happy computing. Alfred Milgrom Publisher A. INTRODUCING THE ZX81 1K -------------------------- 1. Random Patterns ------------------ This program will generate thousands of interesting random patterns, stopping only when you type (BREAK). Program Structure The program works by randomly selecting graphics characters, and putting them in the array A$. The codes for these characters are 128 to 138. This program as well as being very pretty and quite relaxing to watch is also a good demonstration of the possible uses of the RANDOM function. There are more graphics characters with codes 1 to 10, so by changing line 120, you can vary the patterns formed. 120 LET A$(X)=CHR$(INT(RND*9+1)) The characters are printed repeatedly to form the patterns. To give you time to see the pattern a PAUSE statement has been included after printing the pattern. The effect of this is to stop any computations and hold the display for the number of frames specified. Line 190 is only needed if you are running the program in fast mode. Random Patterns 100 DIM A$(7) 110 FOR X=1 TO 7 120 LET A$(X)=CHR$(INT(RND*10+128)) 130 NEXT X 140 FOR X=1 TO 77 150 PRINT A$; 160 NEXT X 170 PRINT A$( TO 5) 180 PAUSE 260 190 POKE 16437,255 200 CLS 210 GOTO 100 2. Leapfrogs ------------ The game of leapfrogs is a nice simple one: You start off with two opposing sets of frogs, and each frog can only move to an adjacent space or leap over one frog. X X X X - O O O O 1 2 3 4 5 6 7 8 9 So, as a first move, for example, the frog at position 4 can move to 5, or the frog at 6 can move to 5, or the frog at 3 can leap over frog 4 to land at 5, or the frog at 7 can leap over the frog at 6 to land at 5. The object of the game is to try to get all the frogs on the left to the right, and vice versa, in the least possible amount of moves. It's great fun! Structure of the Program The first part of any program is to initialize whatever variables may be needed. In this case, we want to define the initial position of the frogs and set the number of moves taken so far to zero. An overview of the program reveals the following structure: INITIALISE VARIABLES PRINT PRINT POSITION OF FROGS CHECK IF FINISHED IF YES, THEN GO TO FINISH INPUT ENTER PLAYER'S MOVE CHECK IF MOVE IS ALLOWED IF NOT, GO TO INPUT AGAIN ADD ONE TO NUMBER OF MOVES MAKE THE MOVE AND GO TO PRINT FINISH CONGRATULATE PLAYER ASK IF PLAYER WANTS TO PLAY AGAIN IF YES, THEN RUN AGAIN This simple "top-down" approach gives us an overview of the program and lets us understand the program should we wish to make any changes at a later date. Structure of the Variables For this program we shall be using "string variables" to define the position of the frogs. A string variable is easy to manipulate in this context and makes printing very fast. We define "O$" as the original position of the frogs, and "P$" as the present position of the frogs. We can use the same variables to check if we are finished and this is done in line 150. We use the variables "T" and "F" to represent the position "to" and "from" that the frog is moving. Because a frog can only move into an empty position we can check this easily, as in line 200. The rest of the program is very straightforward with "C" the count of moves taken. Leapfrogs 100 LET O$=" ~ ~ ~ ~ p p p p" (These characters are obtained by using uppercase "SPACE" and "P" while in GRAPHICS mode.) 110 LET P$=O$ 120 LET C=0 130 CLS 140 PRINT P$,,,"1 2 3 4 5 6 7 8 9",,,"ENTER MOVE" 150 IF P$(1 TO 7)=O$(11 TO 17) AND P$(11 TO 17)=O$(1 TO 7) THEN GOTO 250 160 INPUT A$ 170 IF A$="" THEN STOP 180 LET F=2*(CODE A$(1)-28)-1 190 LET T=2*(CODE A$(2)-28)-1 200 IF P$(T)<>" " OR ABS(T-F)>4 THEN GOTO 160 210 LET C=C+1 220 LET P$(T)=P$(F) 230 LET P$(F)=" " 240 GOTO 130 250 PRINT "YOU DID IT IN ";C;" MOVES",,,"ANOTHER GO?" 260 INPUT A$ 270 IF CODE A$-62 THEN RUN Running the Program The program expects a 2-digit input to define the move "from" and the move "to". It will therefore only accept as a valid first entry one of the following moves: 35 45 65 75 Happy leapfrogging! 3. Noughts and Crosses ---------------------- We have all played Noughts and Crosses in our time, and this time the challenge is to beat the computer. The computer in this program is a little greedy in that it refuses to let you start, but then again, it's not like your next-door neighbour. It is always available for a game! The screen display is as follows: 1 | 2 | 3 --+---+-- 8 | X | 4 --+---+-- 7 | 6 | 5 You enter your move by choosing the number where you wish to move to. The programmer is so confident of his program that you will notice there is only provision for the computer to win or to concede a draw! Noughts and Crosses 50 PAUSE S*Z 60 CLS 70 LET A$="1~ 2~ 38~ X~ 47~ 6~ 5" (graphics 8) 80 FOR I=X TO X+Y 90 PRINT A$(TO B-X) 100 IF I<=Y THEN PRINT "~q~~q~~" (graphics 6, [graphics] Q, [graphics] 6, [graphics] 6, [graphics] Q, [graphics] 6[, graphics] 6) 110 LET A$=A$(B TO ) 120 NEXT I 130 FOR T=X TO Z 140 PRINT AT B,X;"YOUR TURN" 150 LET V=52 160 INPUT R 170 GOSUB 420 180 PRINT AT B,X;"MY TURN " 190 IF T>X THEN GOTO 230 200 LET E=R=Y*INT(R/Y) 210 LET P=R 220 LET A=P-P 230 LET V=61 240 LET A=A+X 250 IF T=X OR P=R+Z OR P=R-Z THEN GOTO 300 260 LET P=P+Z 270 GOSUB S*Z 280 PRINT "I WON" 290 GOTO X 300 IF A=X+Y AND E THEN LET A=B-X 310 IF A=Z THEN LET A=Z+Y 320 LET P=P+A 330 GOSUB S*Z 340 IF A=B-X THEN GOTO 280 350 NEXT I 360 PRINT "DRAW" 370 GOTO X 400 IF P>B THEN LET P=P-B 410 LET R=P 420 LET D=PEEK 16396+256*PEEK 16397 430 FOR I=X TO Z*B+B 440 IF PEEK(D+I)=R+28 THEN POKE D+I,V 450 NEXT I 460 RETURN It should be obvious to you looking at line 50 that the program would crash if you were to press (RUN) (NEW LINE). This is because we have some undefined variables, such as S and Z. The reason these variables are used in the program is to save space, and the variables need to be defined before we can play Noughts and Crosses. One way to do this is used in many other programs in this book- use 2 programs, the first program to define the variables, (RUN) it, and then enter the second program with the variables stored in memory. We could use this same method in this program, but instead we will use the second method of defining the variables after the main program has been entered. We can do this by entering BASIC lines without line numbers, and pressing (NEW LINE). This tells the operating system we wish to execute that instruction immediately. In this program, after you have entered the listing above, enter the following lines without line numbers, and press (NEW LINE) after each line has been entered: LET X=1 LET Y=2 LET Z=4 LET B=8 LET S=100 All the variables have been defined. We still cannot press (RUN) as this will clear the variables from memory. Enter (GOTO 1) (NEW LINE). Structure of the Program You may find it difficult to follow the structure of this program because its logic is fairly well hidden and the use of variables in the listing makes it difficult to know what is happening. Lines 70 to 120 draw the Noughts and Crosses board. Note that this is the only time in the program that the board is drawn up. All changes to the board which occur later involve changing only the piece that is to be moved. The variable T in line 130 indicates TURN and after each player has had 4 turns, the game must be a draw (see line 360). This is obvious as after 4 turns 8 spaces will be filled up and the computer's first turn has already been made = all 9 possible positions have been filled. When it is the player's turn, we obtain R (REPLY) and go to [the] subroutine [at line] 420. This subroutine looks at the first 40 squares of the screen display and replaces the number entered by the letter 'O'. This effectively finishes the player's turn. If it is the computer's first turn (T=1) there are some preliminaries we have to do: remember the PREVIOUS move (variable P), set the ADD factor (variable A) to zero, and determine if the player started on an EVEN (variable E) number or odd. Note that the board is defined in such a way that all corner moves are represented by an odd number and all side moves are even numbers! The other feature of interest is that a blocking move is either +4 or -4 the original move. In other words if the computer moves to square 1, you will need to move to square 5 to stop it making 3 in a row. If it is the computer's first move or if the player has blocked successfully (line 250) then the computer cannot yet claim victory (GOTO 300). On the other hand, if it is a win the computer fills in the appropriate square, claims victory and jumps back to the beginning. The computer's sequence of moves is determined by PREVIOUS and ADD. The computer's first move will always be to the right clockwise of the player's first move (PREVIOUS+1). Its next move will be 2 to the right clockwise from that (PREVIOUS+2), and so on. Lines 300 and 310 take care of the exception where ADD is not automatically incremented by 1. That's it. Good luck. 4. Pinch -------- This is a 1-dimensional version of the Japanese game Go. This is one of the few games in this book where it is not possible for a player to play alone or against the computer. Nonetheless we have included it in this book because it is such an interesting and challenging game. The rules of the game of Pinch first appeared in Scientic American in 1980. (As mentioned above it is a 1-dimensional version of Go, and was included in a discussion of possible 1-dimensional games, including 1- dimensional chess!) Two polayers take turns to place stones on a 9-position board. You can capture a connected group of your opponent's stones by surrounding them on both sides. The board appears like this: - - X O - - - - O 1 2 3 4 5 6 7 8 9 If it is X to move, placing a stone at 5 would capture the stone at 4. Placing a stone at 8 would capture the stone at 9. On the other hand placing a stone at 6 would have no effect. It is illegal to make a suicidal move, but you can go into what would normally be a suicidal position if that results in the capture of opposing stones. There is only one other rule, and that is that it is illegal to make a move which would make the positions exactly the same as they had been after your last turn. This is a rule which this program does not check for. Program Structure The structure of the main program is: Initialize variables MOVE Print the board Print the player's move If possible, kill opponent's groups If possible, kill player's groups If the move was lega, goto MOVE Give the player another turn B is the current player. The subroutine at 500 checks which groups not belonging to B are dead and removes them. The ends are regarded as belonging to B. After the subroutine has been run once, B is changed, and the subroutine checks for a suicidal move. Initialize variables Go to NEXT B FIND Find a stone of B's Set equal to S NEXT B Increment P until a blank or another of B's stones is found If a blank go to FIND Kill stones between S and F Pinch 100 LET A$="- - - - - - - - - -" (10 -s) 110 LET B$="OX" 120 LET B=1 130 CLS 140 PRINT A$( TO 17) 150 PRINT "1 2 3 4 5 6 7 8 9" 160 PRINT AT 5,B-B;"PLAYER ";B;"?" 170 INPUT S 180 LET A$(2*S-B/B)=B$(B) 190 PRINT AT B-B,B-B;A$( TO 17) 200 LET D=B-B 210 GOSUB 500 220 LET B=INT((B+1)/B) 230 LET D=-D 240 GOSUB 500 250 IF D<=B-B THEN GOTO 130 260 GOTO 220 500 LET A$(19)=B$(B) 510 LET P=-1 520 GOTO 560 530 LET P=P+2 540 IF P>=19 THEN RETURN 550 IF A$(P)<>B$(B) OR A$(P+2)=B$(B) THEN GOTO 530 560 LET S=P 570 LET P=P+2 580 IF A$(P)="-" THEN GOTO 530 590 IF A$(P)<>B$(B) AND P<19 THEN GOTO 570 600 IF P>B/B THEN LET D=D+B/B 610 FOR K=S+2 TO P-2 STEP 2 620 LET A$(K)="-" 630 NEXT K 640 GOTO 540 Notes You may realise from looking at this program that it only just fits into the 1K machine. There are therefore attempts to minimise any waste of memory. One simple way to do this is to eliminate all references to the number 1 or 0 in the listing as these both take up 6 bytes. Instead we use B/B for 1, and B-B for 0 - both of these only require 3 bytes. Line 220 gives a simple way of switching between 1 and 2. If B was 1, the result will be B=2, while if B was 2 the result will be B=1! Running the Program It is illegal to move to a position where you will immediately be captured unless you can capture some of your opponent's stones by the move. If you do make an illegal move, the group of stones will be removed, and it will still be your move. The computer does not play this game, but provides for two players to compete, removing dead stones for them. This is an interesting, and constantly changing game. The strategies involved should keep you thinking for a while. Improving the Program The basic strategy of the game is to ensure that you can capture whenever possible and not walk into a trap! The subroutine located at line 500 at present kills any group located between enemies. It is quite easy to adapt this subroutine to merely check if a group would be killed and if so how many men removed. The strategy for a computer to play would therefore be something along the lines: For all possible squares Check how many enemies would be killed by this move Check how many of one''s own men would be killed by this move. As there are only 9 possible squares to go to, this should not take too long to compute. Then choose the move which would kill most enemies. If no such move exists, choose a move which would not result in suicide. Once this is working, a further improvement would be to check whether the move just made would make it easy for the enemy to capture on the next move (a simple version of look ahead). You are going to need all your computer concentration skills for this one. 5. Battleships -------------- World War ][ has been declared. As you sit at the console of your submarine, the captain alerts that Intelligence reports 4 battleships in the general area. Radar is out! You must bomb the seas and hope to find the battleships before they find you! Each ship occupies to adjacent spaces on your 9*9 grid: enter the coordinates and the boys in the missile room will send a torpedo hurtling to that point. If you've hit a ship, an 'X' will appear on the console - otherwise only 'O'. When you've hit all ships in the area, the captain orders a move to a new part of the Atlantic. Program Structure Randomly choose the positions of the 4 battleships Print the grid BOMB Get the position to be bombed If a ship is there, then print an 'X' there and if there have been 8 hits then run the program again Otherwise, print a 'O'. Go to BOMB again. The array X cotnains the position of the 4 battleships (2 battleships on the grid for each ship). The player's input is checked against each of these. Grid position 4,1 is stored as 41 and printed at 8,2 on the screen (the extra spaces between lines aids readability). The number of hits is stored in H, and when 8 hits have been recorded the game is started afresh. Battleships 100 LET H=0 110 DIM X(8) 120 FOR R=1 TO 8 STEP 2 130 LET X(R)=INT(RND*8+1)*10+INT(RND*9+1) 140 LET X(R+1)=X(R)+10 150 NEXT R 160 CLS 170 PRINT " 1 2 3 4 5 6 7 8 9" 180 FOR R=1 TO 9 190 PRINT AT 2*R,R-R;R 200 NEXT R 210 INPUT M 220 FOR R=1 TO 8 230 IF M=X(R) THEN GOTO 300 240 NEXT R 250 PRINT AT INT(M/10)*2,(M-INT(M/10)*10)*2;"O" 260 GOTO 210 300 PRINT AT INT(M/10)*2,(M-INT(M/10)*10)*2;"X" 310 LET H=H+1 320 IF H=8 THEN RUN 330 GOTO 210 Running the Program When you press (RUN) (NEW LINE), the screen will be blank and two axes will show the numbers 1 to 9. This is your bombing grid. The ZX81 will be waiting for a number [sic] input from you to bomb. A 2- digit number is required, with the vertical axis the first number - eg. enter 52 if you want to bomb the position 5 rows down and 2 columns across. You should be able to locate all the ships in about 40 turns. When all have been bombed, a new set of axes will be drawn, with the ships now hidden at different locations. B. GAMBLING GAMES ----------------- 6. Craps -------- © by Neil Streeter This program simulates the dice game 'craps'. If you throw a 7 or 11 on your first throw, you win immediately. A score of 2, 3 or 12 on your first throw means you lose. If you throw anything else you get another throw. On subsequent throws, you win by matching your first throw, or lose by throwing a 7. This is an easy and fun game to play: just type 'Y' to continue playing and the computer does the rest. It even throws the dice for you. Program Structure The throwing of the dice is simulated by using the RND function. Throwing a single die can be simulated by: LET C=INT(RND*6)+1 and throwing two dice by: LET C=INT(RND*6)+INT(RND*6)+2 Note that this is not the same as: LET C=INT(RND*11)+2 although both give a random number between 2 and 12. Craps 100 CLS 110 PRINT "CRAPS" 130 LET J=0 140 LET C=INT(RND*6)+INT(RND*6)+2 150 LET J=J+1 160 IF J=1 THEN GOTO 210 170 IF C=D THEN GOTO 340 180 IF C=7 THEN GOTO 360 190 PRINT "YOU SCORE ";C 200 GOTO 140 210 IF C=7 OR C=11 THEN GOTO 250 220 IF C=2 OR C=3 OR C=12 THEN GOTO 270 230 LET D=C 240 GOTO 190 250 PRINT "SCORE ";C;" AND WIN" 260 GOTO 280 270 PRINT "SCORE ";C;" AND LOSE" 280 PRINT 290 PRINT "TYPE ""Y"" TO CONTINUE" 300 INPUT L$ 310 IF L$<>"Y" THEN STOP 320 PRINT 330 GOTO 100 340 PRINT "SCORE ";C;" AND WIN BY MATCHING" 350 GOTO 280 360 PRINT "SCORE ";C;" AND LOSE" 370 GOTO 280 Happy gambling! 7. Fruit Machine ---------------- © by Neil Streeter This program simulates a fruit machine, just like those in Los Vegas. You start with $10, and it costs 50c to play a game. The names of three objects will appear on the screen. If they are all the same, you win, and the computer will credit you with your winnings. Otherwise it will subtract the cost of the game. You can play again by entering (NEW LINE), until you run out of money: unlike your favourite casino the computer does not give credit. Structure of the Program The randomizing function (RND) is used to generate either a bar, an orange or a cherry. Line 20 generates a number 1<=N<10. If N<2, a bar is generated, if 22 THEN GOTO 130 100 PRINT "BAR "; 110 LET A=A+1 120 GOTO 190 130 IF N>5 THEN GOTO 170 140 PRINT "ORANGE "; 150 LET B=B+1 160 GOTO 190 170 PRINT "CHERRY "; 180 LET C=C+1 190 NEXT I 200 IF A=3 OR B=3 OR C=3 THEN GOTO 250 210 PRINT AT 4,4;"TOO BAD...YOU LOSE" 220 LET M=M-.5 230 IF M<=.1 THEN GOTO 340 240 GOTO 280 250 PRINT AT 4,10;"YOU WIN" 260 LET M=M+A*2/3+B/2+C/3 280 PRINT 290 PRINT "YOU NOW HAVE $";M 310 PRINT "TO PLAY AGAIN PRESS NEWLINE." 315 INPUT A$ 320 CLS 330 GOTO 40 340 PRINT "YOU HAVE LOST ALL YOUR MONEY" 8. Roulette ----------- © by Alistair Ogilvy A gambler's delight - you start with $100 to bet on the roulette wheel. You can place your money on a range of values (between 1 and 36) or odds and evens. Then the wheel begins to spin. The numbers flash onto the computer until the wheel stops. The computer then calculates how much you have won or lost, and adjusts the total of your money. You'd better be careful with this one - you could lose everything! Program Structure The numbers are generated by RND. To simulate the spinning of the wheel, 15 numbers are generated and successively printed at the same position. In line 370, the expression: IF (K AND C) is used. This is true if K and C are both non-zero. Roulette 100 LET M=100 105 PAUSE 100 108 CLS 110 LET C=2 115 PRINT "HIGH?" 120 INPUT H 130 PRINT H,,"LOW?"; 140 INPUT L 150 PRINT L 160 IF H<=L OR L<1 THEN GOTO 105 170 IF H<=36 THEN GOTO 206 190 PRINT "0=ODDS OR 1=EVENS" 200 INPUT C 205 LET H=17+L 206 LET O=INT(36/(H-L)) 208 PRINT "ODDS=1 IN ";O 210 PRINT "$";M," BET$" 220 INPUT B 230 PRINT B 250 FOR I=1 TO 15 260 LET A=INT(37*RND) 265 PRINT AT 9,0;A;" " 267 NEXT I 270 IF C<>2 THEN GOTO 360 280 IF A<=H AND A>=L THEN GOTO 330 290 PRINT "LOST" 300 LET M=M-B 310 IF M<=0 THEN GOTO 500 320 GOTO 105 330 LET P=O*B-B 340 PRINT "WON $";P 345 LET M=M+P 350 GOTO 105 360 LET K=INT(A/2=INT(A/2)) 370 IF (K AND C) OR (K=C AND C=O) THEN GOTO 330 390 GOTO 290 500 PRINT "LOST ALL" Running the Program When the program commences, you are going to be asked for the number range you wish to bet on: Choose any high limit and any low limit. For example, you could bet on all numbers from 1 to 12 or only from 30 to 34. If you wish instead to bet on odds or evens, simply enter a number greater than 37 in answer to the query for the high number, and any number smaller than that for low. The computer will then ask whether you want odds or evens. The odds will be calculated (to a round number) and displayed so that you can place you bet. The croupier will obligingly wait until you have placed your bet before spinning the wheel. 9. Horse Races -------------- © by Neil Streeter This program simulates a three horse race. You have $500 to begin with and you can bet on any horse. All horses have an equal chance of winning. The game ends when you have lost all your money. Program Structure The path of each horse is shown by a black bar. The horse with the longest bar is winning. This is done by using the PLOT function to fill in his present position. The race ends as soon as once horse reaches position 31. The RND function generates a number between 0 and 1. This is multiplied by 3 and added to 1. The integer part of this is then 1, 2 or 3. The horse thus chosen is allowed to advance one space. Note the use of RAND. The function RND always uses the same sequence of numbers. That is, it doesn't really generate random numbers, but returns a pseudo-random number, a number which appears to be random. For many purposes this is sufficient, but in this program, that would mean that it would become predictable which horse would win. Horse Race[s] 105 LET E=500 110 PRINT "BET?" 115 INPUT B 120 PRINT B 122 PRINT "HORSE (1-3)?" 125 INPUT H 130 CLS 135 PRINT "1"," " 136 PRINT "2"," FINISH" 138 PRINT "3," " 140 LET W=2 145 LET X=2 150 LET Y=2 160 RAND 200 LET R=INT(RND*3+1) 210 IF R=1 THEN LET W=W+1 220 IF R=2 THEN LET X=X+1 230 IF R=3 THEN LET Y=Y+1 250 PLOT W,43 260 PLOT X,41 270 PLOT Y,39 290 IF X=31 OR Y=31 OR W=31 THEN GOTO 350 300 GOTO 200 350 IF W=31 AND H=1 THEN GOTO 450 360 IF X=31 AND H=2 THEN GOTO 450 370 IF Y=31 AND H=3 THEN GOTO 450 390 LET E=E-B 400 PRINT AT 5,0;"MONEY=";E 410 IF E<=0 THEN STOP 420 GOTO 110 450 LET E=E+B*2 460 GOTO 400 10. Blackjack ------------- © by Alistair Ogilvy This is the traditional card game, and it all fits into the unexpanded 1K ZX81. It even keeps track of the card totals, how much money you have bet and how much you have left. And naturally, it won't let you bet more than you have. The ZX81 dealer asks you how much you want to bet, then deals himself a card, and then deals you a card. You are then asked if you want a HIT? (ie. do you want another card?). Any answer such as Y or YES or even pressing (NEW LINE) will be yes, while N or NO or NEVER! will be taken to mean you don't want another card. If your card total is over 21, you've lost. If you stop before you BUST then the dealer will deal himself more cards. The dealer always draws below 16 and sits on 17 and above. The amount of money you have left will be shown and you will be invited to bet again. Step right up, ladies and gentlemen! Structure of the Program As you can imagine, it's not easy to fit such a complex program into the unexpanded ZX81. We have to resort to two space saving techniques: 1. Defining variables in another program first. 2. Replacing as many numbers as possible in the program listing by variables. The second method can chop as much as 100 bytes off a program like this because each number in a program line takes up 6 bytes as opposed to one byte for a variable. Admittedly the variable takes 7 bytes in memory in the first place, but if you use the same numbers a lot there are considerable savings possible. Program 1 This program defines the variables we need - mainly predefining 'A' as a variable and the string variable B$ which defines the cards in the pack. The other variables are the numbers most commonly used in Program 2: X=1, Y=2, Z=10, and T=21. 100 LET X=1 110 LET Y=2 120 LET Z=10 130 LET T=21 140 LET B$="N23456789TAJQK" 150 LET A=0 Note that in this definition of cards we used the letter 'T' to denote the '10' - this enables us to display all cards as a single letter. Once this program is entered, press (RUN) and (NEW LINE). This will save the variables in memory and the listing of this program is therefore no longer required. Program 2 This is the program that does all the work - indeed if you have additional memory you would not need Program 1. (All you would need to do is to replace all references to X by 1, etc., and include the lines defining A and B$ in the main program.) The structure of this program is as follows: NEW BET INPUT PLAYER'S BET IF NO MONEY LEFT, STOP DEAL FIRST CARD TO DEALER AND PLAYER FOR PLAYER AND FOR DEALER: IF PLAYER ASK IF CARD WANTED IF SWITCH TO DEALER DEAL THE CARD AND PRINT IT CALCULATE VALUE OF HAND AND PRINT IT IF OVER 21 GO TO PAYOUT IF DEALER AND OVER 16 GO TO PAYOUT PAYOUT IF PLAYER BUST, MONEY WON=0 IF DEALER<21 AND DEALER TOTAL <= PLAYER TOTAL THEN MONEY WON=0 MONEY=MONEY+MONEY WON GO TO NEW BET AGAIN As you can see from the above structure there was insufficient room in the 1K version to allow for greater payout if the player makes Blackjack, or to allow the player to win if he gets '5 and under'. If you have additional memory you can easily write those provisions in. The numbering in this program is slightly non-standard: this has been done deliberately to allow the use of variabales in GOTO and GOSUB statements. We therefore start at line 90 and have the unusual line 125. 90 LET M=Z*Z 100 DIM P(Y+Y) 110 PRINT AT Z,X;"$;M;" BET?" 120 INPUT B 125 CLS 130 IF B>M THEN STOP 140 LET M=M-B 150 PRINT " YOU";TAB Z;"ZX81" 160 FOR I=Y TO X STEP -X 170 GOSUB T*T 180 NEXT I 190 FOR I=X TO Y 200 LET A=P(I)=X+Z 210 IF I=X THEN PRINT AT Z,X;"HIT?" 220 IF I=X THEN INPUT A$ 230 PRINT AT Z,X;" " 240 IF I=X AND CODE A$=CODE B$ THEN GOTO 280 250 GOSUB T*T 260 IF P(I)>T OR (I=Y AND P(I)>16) THEN GOTO 290 270 GOTO T*Z 280 NEXT I 290 IF (P(Y)<=T AND P(Y)>=P(X)) OR P(X)>T THEN LET B=B-B 300 LET M=M+B+B 310 GOTO Z*Z 500 LET P(I+Y)=P(I+Y)+X 510 LET C=INT(13*RND)+Y 520 IF C=Z+X THEN LET A=A+X 530 LET P(I)=P(I)+C*(CZ+X) 540 IF P(I)31 THEN GOTO 160 180 PRINT D,"MONTH", 190 INPUT M 200 PRINT M,"YEAR" 210 INPUT Y 220 IF Y<1700 THEN GOTO 450 230 CLS 300 LET K=0 310 IF M<3 THEN LET K=1 320 LET L=Y-K 330 LET O=M+12*K 340 LET P=INT(L/100) 350 LET Z=INT(13*(O+1)/5)+INT((5*L)/4)-P+INT(P/4)+D-1 360 LET Z=(Z-7*INT(Z/7))*3+1 400 PRINT A$;" WAS BORN ON ";D$(Z TO Z+2) 410 STOP 450 PRINT A$;" IS TOO OLD" 12. Simple Simon ---------------- "Simple Simon" is the computer successor to the age-old game of "Simon Says". In the computer version, a letter or number is displayed one at a time on the screen and the player has to correctly type in the sequence of letters and numbers as they appeared. As the number of letters that has appeared on the screen increases, the letters (or numbers) flash onto the screen more and more quickly. This program will also make use of the fact that in the Sinclair ZX81 the character generator is located in software and use this information to display each letter in large (8 lines deep) size. On many other computers the character generator (that is, whatever it is in the computer that defines what each letter of the alphabet will look like on the screen) is handled by a special chip. In the Sinclair ZX81 all that information is stored in the ROM (Read Only Memory), which also contains the operating system. If you look at your screen closely enough, you will be able to see that each letter on the screen (and even the graphics characters) are made up of little dots closely joined up. In fact each character space on the screen has room for 64 dots (an array of 8 lines of 8 dots each), and the information about which dots are to be on and which are to be off is stored at memory location 7680 onwards. The information about each letter of the alphabet (and each graphic character and number) can be stored in 8 bytes of memory. This is because each byte of memory has 8 bits and the ZX81 uses each bit to indicate a different position on the screen. We will first of all display this ability to have larger than life characters in the following short program. Displaying Large Characters 100 LET A$="~" (graphics space) 110 LET B$=" " (space) 120 FOR X=28 TO 63 130 FOR L=1 TO 8 140 LET L=PEEK(7679+L+8*X) 150 LET P$="" (empty string) 160 LET D=256 170 FOR K=8 TO 1 STEP -1 180 LET D=D/2 190 LET C$=B$ 200 IF VZ$ THEN GOTO 370 360 NEXT G 370 PRINT "YOU SAID ";Z$ 380 PRINT Y$;" IS RIGHT" The structure of this program is very simple to follow, once you have seen the [Displaying] Large [Characters] program. Note that the amount of time the character is displayed is reduced as you go along. Remember the sequence of 20 letters and numbers correctly, and you've won! 13. Kings and Queens of England ------------------------------- This program uses a 'multiple choice' format to test your knowledge of history. You will be asked who was reigning in a particular year, and given three answers from which to select. You then type the number of the answer, and the computer will tell you if you are right or not. Program Structure Initialize variables FOR J=1 TO 3 Select a date Find the king who reigned then NEXT J Select one of the 3 dates Print question and choices Check the answer Run the program again The kings and years of their death are stored in the string Z$. The entry for each king is 7 characters long - padded with blanks if necessary. When a date has been chosen, each entry is checked until one is found where the king died after the chosen year. The next entry is obtained by adding 7 to the position of the character that was previously being considered. B$ is a two-dimensional array used to store the names of the 3 kings offered as choices. B$(1,1) refers to the first character in the first name. B$(1) refers to the whole of that name. Note that the program is run again at the end. RUN does not clear the screen, so the question remains on the screen while the computer selects the next question. This gives you time to see the answer given by the computer. Kings and Queens of England 100 LET Z$="1087W1 1100W2 1135H1 1154ST 1189H2 1199R1 1216J 1272H3 1307ED11327ED21377ED31399R2 1413H4 1422H5 1461H6 " 110 DIM P(3) 120 DIM B$(3,3) 130 RAND 140 FOR J=1 TO 3 150 LET P(J)=INT(RND*396+1065) 160 LET X$="0000" 170 LET I=J/J 180 LET X$=Z$(I TO I+3) 190 LET I=I+7 200 IF VAL(X$)<=P(J) THEN GOTO 180 210 LET B$(J)=Z$(I-3 TO I-1) 220 NEXT J 230 LET N=INT(RND*3+J/J) 240 CLS 250 PRINT "WHO REIGNED IN ";P(N),"1 ";B$(J/J);" 2 ";B$(2);" 3 ";B$(3) 260 INPUT X 270 IF B$(X)=B$(N) THEN PRINT "YES" 280 IF B$(X)<>B$(N) THEN PRINT "NO ";B$(N) 290 RUN Take especial care in entering line 100! Remember the format we are using: for each monarch, we enter the date of his death, then a three letter code for the monarch's name. Each entry therefore has 7 characters. (Where the abbreviation is less than 3 characters, we make it up to three by adding spaces.) Running the Program This program will keep on running forever unless you [press] (BREAK) or input an illegal value. You will be asked who reigned in a particular year, and three possible answers will be given. It is possible that not all of these answers will be different. You will still be marked correctly, no matter which of the correct answers you choose. If the king died in the given year (so that there were 2 kings in that year) choose the new king. If you select the wrong answer, the correct answer will be printed on the screen. W1 1066-1087 Norman W2 1087-1100 H1 1100-1135 St 1135-1154 H2 1154-1189 Plantaganet R1 1189-1199 J 1199-1216 H3 1216-1272 Ed1 1272-1307 Ed2 1307-1327 Ed3 1327-1377 R2 1377-1399 H4 1399-1413 Lancaster H5 1413-1422 H6 1422-1461 14. Hangman ----------- This is a game for two players. One types in a secret word while the other is not watching. Then the other player tries to guess the word. After guessing 10 wrong letters, the game is over and the man is hung. Program Structure Get the secret word GUESS Print the number of letters in the word Get the letter guessed If the letter is in the word Put every occurrence into the right place in A$ Otherwise, G=G+1 Print G parts of the man being hung Print A$ If the man is hung, or the word guessed Start a new game Otherwise, goto GUESS The variable G contains the number of wrong guesses so far. To begin with, only [the] subroutine [at line] 500, which does nothing, is executed. After that, for every wrong guess, one more of the subroutines is executed. Each prints another part of the man being hung. A$ contains the word guessed so far. G$ is the ltter guessed this time. Hangman 100 PRINT "NEW SECRET WORD?" 110 INPUT X$ 120 LET W=LEN X$ 130 DIM A$(W) 140 LET G=0 150 PRINT W;" LETTERS" 160 PRINT "GUESS?" 170 INPUT G$ 180 CLS 190 LET C=0 200 FOR L=1 TO W 210 IF X$(L)=G$ THEN LET A$(L)=G$ 220 IF A$(L)=G$ THEN LET C=1 230 NEXT L 240 IF NOT C THEN LET G=G+1 250 IF A$=X$ THEN PRINT "**YES**" 260 PRINT A$ 270 PRINT 280 FOR L=500 TO 500+20*G STEP 20 290 GOSUB L 300 NEXT L 310 IF A$=X$ OR G=10 THEN RUN 320 PRINT 330 GOTO 150 500 RETURN 520 PRINT "aaa"; (graphics A, [graphics] A, [graphics] A) 530 RETURN 540 PRINT "aaa" (graphics A, [graphics] A, [graphics] A) 550 RETURN 560 PRINT " 5" (space, space, graphics 5) 570 RETURN 580 PRINT " 5" (space, space, graphics 5) 590 RETURN 600 PRINT " ee5" (space, graphics E, [graphics] E, [graphics] 5) 610 RETURN 620 PRINT " r"; (space, graphics R) 630 RETURN 640 PRINT "r1" (graphics R, [graphics] 1) 650 RETURN 660 PRINT " 8~" (space, graphics 8, [graphics] space) 670 RETURN 680 PRINT " q"; (space, graphics Q) 690 RETURN 700 PRINT "84" (graphics 8, [graphics] 4) 710 RETURN Running the Program The word appears on the screen as you type it in, so make sure your opponent doesn't sneak a look. The computer does not check that the word is legal or contains only legal characters, but I don't think your opponent will be too pleased if it [is] illegal. When guessing you can only guess one letter at a time. As you guess wrong letters, a man being hung is drawn. The aim is to guess the word before the man is hung. The letters you have guessed correctly are displayed in their correct positions at the top of the screen. You will be told how many letters are in the word. 15. Quadratic Equations ----------------------- © by Neil Streeter This program will solve equations of the form: A*(X**2)+B*X+C=0 for you. You enter the values of A, B and C. The program will solve for real or imaginary roots. Program Structure The program uses the formulae: X=(-B+SQR(B**2-4*A*C))/2*A and X=(-B-SQR(B**2-4*A*C))/2*A to calculate the roots of the equation. If B**2-4*A*C is negative, then the roots are imaginary. Quadratic Equations 110 PRINT "A QUADRATIC EQUATION HAS","THE FORM" 120 PRINT 130 PRINT "A (X)SQUARED + B (X) + C = 0" 140 PRINT 150 PRINT "INPUT A", 160 INPUT A 170 PRINT "A=";A 180 PRINT "INPUT B", 190 INPUT B 200 PRINT "B=";B 210 PRINT "INPUT C", 220 INPUT C 230 PRINT "C=";C 235 LET J=(ABS B)**2-(4*A*C) 240 IF J<0 THEN GOTO 500 250 LET P=SQR J 260 PRINT "THE ROOTS ARE REAL" 270 PRINT 280 PRINT "X=";(-B+P)/(2*A);" OR ";(-B-P)/(2*A) 290 STOP 500 PRINT "THE ROOTS ARE IMAGINARY" 510 PRINT 520 LET P=ABS J 530 PRINT "X=";-B/(2*A);" +/- ";(SQR P)/(2*A);"I" 540 PRINT 550 PRINT "WHERE I = THE SQUARE ROOT","OF -1" 16. Simultaneous Equations -------------------------- This program solves two simultaneous equations of the type: A*X+B*Y+C=0 You enter, in order, A, B and C for the first equation, then for the second. If there is no solution, or there are an infinite number of solutions, you will be told that the problem is degenerate. Program Structure The input variables are stored in the array X. A and B are the solutions. D is the common denominator of the solutions. Line 140 generates the A, B and C that appear down the screen as prompts as you input the data. 37 is one less than the code for A. So for I=1 or 4, it prints A; for I=2 or 5, B; and for I=3 or 6, C. [Simultaneous Equations] 100 DIM X(6) 110 PRINT "SOLUTIONS TO 2 EQUATIONS:" 120 PRINT "A*X + B*Y + C =0",,,"ENTER DATA" 130 FOR I=1 TO 6 140 PRINT CHR$(37+I-INT((I-1)/3)*3), 150 INPUT X(I) 160 PRINT X(I) 170 NEXT I 300 LET D=X(2)*X(4)-X(1)*X(5) 310 IF D=0 THEN GOTO 360 320 LET A=(X(3)*X(5)-X(2)*X(6))/D 330 LET B=(X(1)*X(6)-X(3)*X(4))/D 340 PRINT "SOLUTIONS ARE:",,"X=";A,,"Y=";B 350 STOP 360 PRINT "DEGENERATE: NO SOLUTIONS" D. ARCADE GAMES --------------- 17. Star Wars ------------- © by Neil Streeter You are the rear gunner in a spacecraft defending the mothership. Through your spaceport you can see the enemy trying to get through the defenses of the Imperial forces. You quickly shift your gun sight to get the enemy within firing range, and blast away. GOT HIM! But what's that? Another one? YES - have to get him as well. If you can ward off the repeated attacks for 3 minutes you know that the Imperial forces will have been able to smash the enemy's power supply. How many of the invaders can you destroy? Program Structure Lines 140 to 170 both print the spaceship at its present location and blank out its previous position. Since it can only move one space at a time, by writing blanks to all adjacent positions, the previous ship is overwritten. 100 LET A=600 110 LET B=0 120 LET H=INT(RND*18) 130 LET V=INT(RND*26) 140 PRINT AT H-1,V+1;" " (2 blanks) 150 PRINT AT H,V;" 2t " (blank, graphics 2, graphics T, blank) 160 PRINT AT H+1,V;" 22 " (blank, 2*graphics 2, blank) 170 PRINT AT H+2,V+1;" " (2 blanks) 180 PRINT AT 11,15;"+" 190 LET A=A-1 200 IF A=0 THEN GOTO 300 210 IF INKEY$="5" THEN LET V=V-1 220 IF INKEY$="6" THEN LET H=H+1 230 IF INKEY$="7" THEN LET H=H-1 240 IF INKEY$="8" THEN LET V=V+1 250 IF INKEY$="9" THEN GOTO 270 260 GOTO 140 270 IF H>=10 AND H<=11 AND V>=13 AND V<=14 THEN LET B=B+1 280 CLS 290 GOTO 120 300 PRINT AT 0,0;"SCORE=";B Running the Program The aim of this game is to shoot as many alien spaceships as you can in the allotted time. You can move the gunsight of your spaceship with the buttons 5 to 8. They move the laser gun in the direction of the arrows. When the alien spaceship is in the crosslines of your gun (the '+' in the centre of the screen) you can fire by pressing 9. At the end of your allotted time at the guns, your score will be displayed. Can you shoot more invaders the next time they attack? 18. Lunar Lander ---------------- © by Neil Streeter and Beam Software In this ZX81 version of the classic arcade game, you have to land your spaceship safely. The control panel of your spacecraft is able to display the velocity relative to the ground, the height above the ground and the amount of fuel left. An inset displays your position above ground graphically. Unfortunately your control panel is not able to show your acceleration, so you have to use some intuition to get a good landing. Your initial velocity is upwards, and you are at a distance of 2000' above the ground. (A positive velocity corresponds to upwards travel and a negative velocity to downwards travel.) If your velocity is more than 100'/sec when you reach the ground, your craft will not be able to withstand the impact and you will ** CRASH **. You can alter your velocity by applying thrust, and deciding the duration of the thrust. If you run out of fuel your attempts to apply upwards thrust will be futile and you will no doubt crash as your craft accelerates to the ground. Program Variables V is the velocity of the ship, and initially it is a random number between 0 and 500. H = the height of the ship above the ground. R = the fuel reserve. F = the force (or thrust) upon the ship. A = the acceleration. T = the time for which the force will act. Program Structure The new velocity and height are calculated using the formulae: V = 2*A*T+V and H = H+A*T**2+V*T You may note from the formula A=F-32 that the acceleration is that of the earth. This is a lunar lander that is coming back to earth! Line 200 prints the lander. If its distance from the ground is more than 2000' it is printed at 0,2, since (H(2000) is false and therefore has value 0. So the value of the whole expression is 0. If (H(2000) is true (and has value 1) then the value of the expression is 20-H/100. Lunar Lander 110 LET V=INT(RND*500) 120 LET H=2000 130 LET R=6000 140 GOTO 260 150 PRINT AT 1,0;"THRUST (0-99)" 160 INPUT F 180 PRINT AT 1,0;"TIME (1-6) " 190 INPUT T 200 CLS 210 IF F*T>R/10 THEN LET F=R/(10*T) 220 LET R=R-F*T*10 230 LET A=F-32 240 LET H=A*T**2+V*T+H 250 LET V=2*A*T+V 260 PRINT,"MOON LANDER" 270 IF H<=0 THEN LET H=0 280 PRINT,"SPEED ";V 290 PRINT,"DIST ";INT H 295 PRINT,"FUEL ";R 300 PRINT AT (H<2000)*(20-H/100),2;"ty" (graphics T[, graphics] Y) 305 PRINT AT 21,1;"6666" (4*graphics 6666) 310 IF H>0 THEN GOTO 150 320 PRINT AT 5,0;"SCORE=";100+V 330 IF V<-100 THEN PRINT "CRASH" 360 IF 100+V>0 THEN PRINT "LANDED" Running the Program This 1K version does not feature "real time" action of the lander. Rather you have to decide what thrust you are going to apply and for how long. The program then calculates your position as a result of that decision and you start again. Note that the height and velocity of the lander are determined by the laws of physics: the effect of acceleration varies as the square of the time. If you apply a thrust of 80 for 2 seconds the effect is going to be quite different to applying that thrust for 4 seconds! Hints for Happy Landing Yoru craft is initially travelling with an upwards velocity, just like a tennis ball as it leaves the tennis racket - if no further force is applied, the ship is going to continue travelling upwards for a little time and then fall down to earth gently again. Until you gain familiarity with the controls apply thrust for only short periods of time: this will mean you will probably run out of fuel in your first few games, but you can use this expertise to make perfect landings later. If you find the program to easy, you can alter the initial amount of fuel in your lunar lander by altering line 130. Best of luck, lunar man. 19. Asteroids in Space ---------------------- © by Neil Streeter You are travelling through space in your spaceship, when suddenly you encounter a meteor storm. You can steer your ship past these using your rudder controls only (key '5' to go left and '8' to go right) - hyperdrive has been disabled by one of the meteors. As if this was not bad enough, if you do survive the meteor storm, you will find that you have become so disoriented that you are travelling the wrong way on the space lanes. All the other spaceships are coming at you. You must steer past these in the same way as before, but the ships are bigger than the meteors, so it is more difficult. Eventually you will CRASH! When you do, you will find your survival rating on the screen of your spaceship console. Program Structure The program uses the SCROLL function to move the meteors and spaceships (other than yours). When the display is SCROLLed, your ship will move with everything else, so it is overwritten with blanks, then printed again in the correct location. In this way, your ship stays on the same line in the screen while everything else moves. The positions of the meteors coming towards you are kept in A, B, C, D and E; with E being the closest. By comparing E with the position of your ship, the program determines whether or not you are about to crash. Asteroids [in Space] 100 LET A$="a" (graphics A) 105 LET N=0 110 LET A=0 115 LET B=0 120 LET C=0 125 LET D=0 130 LET T=1 135 LET X=12 160 LET R=INT(RND*27) 170 PRINT AT 21,R;A$ 180 SCROLL 190 SCROLL 200 LET N=N+1 205 IF N=100 THEN LET A$="t4" (graphics T[, graphics] 4) 210 IF N=104 THEN LET T=2 215 LET E=D 220 LET D=C 230 LET C=B 240 LET B=A 250 LET A=R 255 PRINT AT 9,X-2;" " (6 spaces) 260 PRINT AT 11,X;"yt" (graphics Y[, graphics] T) 270 IF X>=E-2 AND X<=E+T THEN GOTO 500 280 IF INKEY$="5" THEN LET X=X-T 290 IF INKEY$="8" THEN LET X=X+T 300 GOTO 160 500 PRINT AT 11,X-1;"CRASH" 510 PRINT AT 0,0;"SCORE=";N 20. Bombs Away -------------- © by Clifford Ramshaw The aim of this game is to land your plane; but the runway is covered with rubble which you must clear away first. Your plane moves right and descends slowly automatically, and you can drop bombs to clear away the rubble by typing 'F'. If there is any rubble left by the time your plane tries to land, you will CRASH, otherwise you will glide to a halt. (Ed: We think so, but noone here has been able to get a perfect score yet!!) This is quite a difficult task, so good luck. Program Structure The only record of where there is rubble is in the display. To find out if the plane is about to crash, the program PEEKs at addresses 16398 and 16399 which contain the address of the next location to be printed. When we PEEK in memory we look to see what is in that particular memory location. We know from the ZX81 manual that memory locations 16398 and 16399 have been set aside by the operating system to hold the position in memory of the next position to be printed. This is a little like the ability to specify where we want to print by using PRINT AT. In fact we can have the same effect as PRINT AT by changing (ie. POKing) the contents of 16398 and 16399. Of course it's much harder to do it that way so noone does that, but there is no BASIC command which lets us say something like: IF PRINT AT=137 THEN ... Instead if we calculate: PEEK 16398+256*PEEK 16399 we will get the address in memory of the next position to be printed. This is not quite the same as knowing which line and position but it's enough for us to work out what is there. If we look at the contents of that position (ie. PEEK) - hence that horrible: PEEK(PEEK etc.) in line 270 - we will know what is just ahead of the aeroplane. There are three possibilities for what we will find just ahead of the plane: an empty space, an end-of-line character, or rubble. This is what we check for in line 270. If it's rubble, STOP!!! The rest of the program is simple to follow - extremely ingenious and a great game. Bombs Away 110 LET A=1 120 LET B=0 130 LET S=B 140 FOR I=B TO 19 150 PRINT AT 9,I;"g" (graphics G) 160 PRINT AT 10,I;"a" (graphics A) 170 NEXT I 180 PRINT AT A,B;" " (space) 190 LET B=B+1 200 IF B<22 THEN GOTO 240 210 LET A=A+1 220 PRINT AT S,B-1;" " (space) 230 LET B=0 240 PRINT AT A,B;">" 250 IF A=9 AND B=19 THEN STOP 260 PRINT AT A,B+1; 270 IF PEEK(PEEK 16398+256*PEEK 16399)=137 THEN STOP 280 IF S=0 THEN GOTO 400 290 PRINT AT S,B-1;" " (space) 300 IF S=9 THEN GOTO 340 310 LET S=S+1 320 PRINT AT S,B;"3" (graphics 3) 330 GOTO 180 340 LET S=0 400 IF INKEY$="F" THEN LET S=A 405 GOTO 180 Running the Program It will only take you a few games to realise that you need good aim to clear all the rubble! The "Bombs Away" is controlled by the firebutton: the use of the INKEY$ routine in line 400 means that by pressing 'F' continuously you will keep on dropping bombs - but that's not good enough to clear the runway. Like any good aircraft pilot, you need to exercise split second timing and sound judgement as to when the drop the bomb. 21. UFO ------- © by J. M. Revis The aim of this game is to strike the fast-moving UFO as it flies overhead. It is essential to be accurate - those UFOs will keep trying to invade the earth until they are eventually destroyed. Program Structure The UFO is printed by two FOR-NEXT loops. One prints it going from left to right; then the other prints it from right to left. The UFO is printed, then immediately overwritten with blanks. It is by using the shortest possible number of instructions in the FOR- NEXT loops that the UFO is able to move so fast. Unfortunately this also causes the image of the UFO to flicker. The function INKEY$ is used to input the 'fire' command. Unlike the INPUT statement, INKEY$ doesn't wait for input from the keyboard, so if you don't type 'F' the program keeps going. UFO 105 LET B=2 110 FOR N=30 TO 1 STEP -1 120 PRINT AT B,N;"ty" (graphics T, [graphics] Y) 140 PRINT AT B,N;" " (2 spaces) 145 IF INKEY$="F" THEN GOTO 205 150 NEXT N 160 FOR N=1 TO 30 170 PRINT AT B,N;"ty" (graphics T[, graphics] Y) 180 PRINT AT B,N;" " (2 spaces) 185 IF INKEY$="F" THEN GOTO 205 190 NEXT N 200 GOTO 110 205 PRINT AT B,N;"ty" (graphics T[, graphics] Y) 210 FOR X=20 TO 1 STEP -1 212 LET Y=15 215 PRINT AT X,Y;"t4" (graphics T[, graphics] 4) 220 PRINT AT X,Y;" " (2 spaces) 225 NEXT X 227 GOTO 40 230 PRINT AT X,Y;"BOOOOM" 22. Breakout ------------ © by Clifford Ramshaw This program is a BASIC version of the TV game variously known as BREAKOUT or BRICK WALL. Although a machine language version of this game is quite easily done in 1K it is a remarkable achievement by Clifford Ramshaw to have managed to give us a BASIC version which fits into 1K. The aim of this game is to knock out as much of the wall as possible. You keep the ball in the court by moving your paddle left (with the 'Z' key) or right (with the 'M' key) to hit the ball. To make the game more interesting, the angle at which the ball bounces back is random. Program Structure X and Y are the coordiantes of the ball. DX and DY give the gradient of the path of the ball. The ball is moved by adding DX to X, and DY to Y then plotting with the new position. The old position is cleared with the UNPLOT function. If this position is part of the wall, then there will be a hole in the wall. Breakout 105 GOSUB 1000 110 LET P=10 115 LET X=P 120 LET Y=21 125 LET DX=1 130 LET DY=DX 135 UNPLOT X,Y 140 LET X=X+DX 145 LET Y=Y+DY 150 PLOT X,Y 152 IF Y>42 THEN LET DX=INT(RND*3-1) 155 IF Y>42 THEN LET DY=-DY 160 IF X<3 OR X>22 THEN LET DX=-DX 165 IF Y>19 THEN GOTO 180 170 IF X<>P AND X<>P+1 THEN STOP 175 LET DY=-DY 180 UNPLOT P,19 185 UNPLOT P+1,19 190 IF INKEY$="X" THEN LET P=P-1 195 IF INKEY$="M" THEN LET P=P+1 200 PLOT P,19 205 PLOT P+1,19 210 GOTO 135 1000 FOR I=1 TO 13 1005 PRINT "a"; (graphics A) 1010 NEXT I 1015 FOR I=0 TO 11 1020 PRINT AT I,0;"~" (graphics space) 1025 PRINT AT I,13;"~" (graphics space) 1030 NEXT I 1035 RETURN Running the Program After you press (RUN) (NEW LINE), you will see the two boundary walls of the court being drawn up, and then the row of bricks across the back wall. Your paddle is 2 characters wide across the bottom of the court area, and you must position it correctly to catch the ball as it comes towards the baseline. If you should miss the prorgam will STOP. Press (RUN) (NEW LINE) for a new game. This one is a real challenge. If you want to make it much easier and allow yourself an unlimited number of balls to knock the wall down, you can change line 300 to read: 300 IF X<>P AND X<>P+1 THEN RUN 40 This will have the effect of leaving the old ball and paddle on the baseline, but as you play on they will be erased. 23. Space Taxi -------------- © by Clifford Ramshaw Space Taxi is a game in which you are in a spaceship and you must fly over the white mountains, dodging the obstacles for as long as possible. You move the spaceship up (using the 'Z' key) or down (using the 'M' key), but the movement to the right is automatic. If your ship collides with the mountains or an obstacle, your score is shown and a new game started. If the ship reaches the right hand side safely, it will reappear on the left with a new display. Program Structure The subroutine at [line] 1000 sets up the mountains and the obstacles. The height of the mountains, and the position of the obstacles, is random. They are shown on the screen, and this is the only record of where they are. Line 170 looks at the next position to be printed, and checks whether or not it is blank. The position of the spaceship is recorded by coordinates X and Y. Lines 80 and 90 control the vertical movement of the spaceship, and the X component is automatically incremented. Space Taxi 10 LET S=0 20 LET Y=1 30 LET X=0 40 CLS 50 GOSUB 1000 60 PRINT AT Y,X;"~" (graphics space) 70 LET D=0 80 IF INKEY$="M" THEN LET D=-1 90 IF INKEY$="Z" THEN LET D=1 100 LET S=S+1 110 LET X=X+1 120 IF X=31 THEN GOTO 30 130 LET Y=Y+D 140 IF Y<0 THEN LET Y=0 150 PRINT AT Y,X;">" (graphics greater than) 160 PRINT AT Y,X+1; 170 IF PEEK(PEEK 16398+256*PEEK 16399)<>0 THEN GOTO 60 180 PRINT S 190 PAUSE 100 200 RUN 1000 FOR J=0 TO 31 1005 FOR I=0 TO 2+INT(RND*3) 1010 PRINT AT I,J;"~" (graphics space) 1015 NEXT I 1020 NEXT J 1025 FOR I=1 TO 15 1030 PRINT AT INT(RND*5),INT(RND*27)+5;" " (space) 1035 NEXT I 1040 RETURN Running the Program As you can see, this is only a short program but exceptionally well designed and great fun to play. Moreover it has a great scoring routine which gives you a score as soon as you crash. If you manage to get through all the obstacles on your first pass through the mountains, another mountain range appears and the score is continually updated. You will note that line 200 contains the instruction (RUN) which you would not normally expect to find in a program listing. This is a device to enable continuous running of the program: if you should happen to CRASH, then the score will be displayed for some seconds, and then the game starts over again. In effect this saves you the need from [sic] having to type (RUN) (NEW LINE) at the end of each pass through the mountains. Watch out: Space Taxi is addictive! E. ZX81 UTILITY PROGRAMS ------------------------ 24. Bubble Sort --------------- This program sorts 15 numbers. You can watch the smallest numbers 'bubble' to the top on the screen. If you don't want to input your own numbers, the computer will generate them for you. Program Structure This program would probably be more useful as a subroutine than by itself. You could use it, for example, to order the cards in a hand in a bridge program, for example [sic]. Nonetheless it is an interesting program in its own right. If you want to use it in a subroutine, note: The variable N contains the number of items to be sorted. If this is not 15, then change line 100. The numbers to be sorted are in array P. If your program sets up the array, then all you need to sort the array are lines 270 to 380. Lines 350 and 360 just print the array so that you can watch the sort. You may want to delete them if you are using the sort in another program. 100-110 INITIALIZATION 120-190 ENTER NUMBERS FROM KEYBOARD 200-220 COMPUTER GENERATED NUMBERS 230-260 PRINT NUMBERS 270-380 SORT The program works by comparing two elements in the array (in line 310) and changing their order if necessary. If it called a Bubble Sort because the "lighter" numbers seem to "bubble" to the top. When there are no more bubbles, the program stops. Bubble Sort Demonstration 100 LET N=15 110 DIM P(N) 120 PRINT "OWN NUMBERS?" 130 INPUT B$ 140 IF B$="N" THEN GOTO 200 150 PRINT "ENTER 15 NUMBERS" 160 FOR Y=1 TO N 170 INPUT P(Y) 180 NEXT Y 190 GOTO 230 200 FOR X=1 TO N 210 LET P(X)=15*RND+1 220 NEXT X 230 CLS 240 FOR X=1 TO N 250 PRINT AT X,0;P(X) 260 NEXT X 270 FOR J=1 TO N-1 280 LET K=J+1 290 FOR I=K TO N 300 LET L=N+K-I 310 IF P(L)>P(J) THEN GOTO 370 320 LET T=P(L) 330 LET P(L)=P(J) 340 LET P(J)=T 350 PRINT AT J,0;P(J);" " 360 PRINT AT L,0;P(L);" " 370 NEXT I 380 NEXT J Running the Program This program gives a very nice visual demonstration of the sorting process, so it is much more interesting if run in the SLOW mode. If you are going to be loading this program from cassette, be sure to change back to SLOW before pressing RUN. The program will ask you if you would like to submit your own disordered numbeers or let the computer choose its own (line 140). Then just sit back and watch the program at work. 25. Line Renumbering -------------------- This is a program that you may find useful if you are in the habit of developing program and find yourself at the end of the day with oddly numbered lines and no room to fit in that last brilliantly conceived routine in the middle. Enter this short program, type (RUN 9990), (NEW LINE), and next time you look at the listing, just like magic it has all been cleaned up. An added bonus is that at the cost of only one more line you can have a screen display of all lines which contain a GOTO or GOSUB statement. (Because the ZX81 allows computed GOTOs and GOSUBs as in GOTO 100 + A*10 it is not possible for a program to renumber the GOTOs and GOSUBs - but at least youll know where they are and won't miss any. Test Program The following program is a 'test' program in that it won't change any of the line numbers but will illustrate the operation of the Line Renumbering program. The program assumes that the first line in your program is numbered 100 and that you want the program lines to increase in steps of 10. To change this, alter line 9990 to change the starting line number and line 9998 to change the size of the increment. You cannot LOAD a program from tape once you already have a program in memory, so either load this program first whenever you are going to develop programs or key it is in as required - it's only short, anyway. 9990 LET L=110 9991 FOR N=16515 TO 17400 (assumes 1K. Change to '16515 TO 32700' for 16K) 9993 IF PEEK N<>118 THEN GOTO 9999 9994 IF PEEK(N+1)>38 THEN STOP 9995 PRINT 256*PEEK(N+1)+PEEK(N+2);" ->"; (space, minus, greater than) 9996 PRINT L 9997 LET N=N+3+PEEK(N+3)+256*PEEK(N+4) 9998 LET L=L+10 9999 NEXT N If you add a short program of oddly numbered lines you will be able to test this test program. Enter (RUN 9990) (NEW LINE) and the screen display will show you the old line numbers and what they would be converted to if they were renumbered. Line Renumbering Model 1 The Line Renumbering program is identical to the test program above, but we replace lined 9995 and 9996 as follows: 9995 POKE N+1,INT(L/256) 9996 POKE N+2,L-256*INT(L/256) The program will now do the renumbering that was only indicated in the test program. Note that the screen display will now not be shown. This program occupies about 260 bytes of memory. If you are concerned about memory usage, see "Machine Code Editor" where a 38-byte version of this program can be found. Line Renumbering Model 2 The super deluxe Line Renumbering program is the same as Model 1 with the addition of the following line: 9992 IF PEEK N=236 OR PEEK N=237 THEN PRINT L-10;"=";CHR$ PEEK N Your screen will now show the line number of all GOTOs and GOSUBs. If you are using a line increment different from 10 then don't forget to make the appropriate change in this line. The Model 2 Line Renumbering program requires about 310 bytes. Note to Shorten the Programs You will no doubt notice that line 9997 is quite cumbersome. That line actually serves two purposes: 1. To determine where the start of the next BASIC is, and 2. To eliminate the possible of finding an end-of-line character in the middle of [a] line. The latter is possible because without your even knowing about it because of the way the ZX81 treats numbers in a BASIC line - it converts them to 6-byte code for numbers. This means that an innocuous number like 123 actually ends up containing the dreaded end-of-line character in it! You can shorten line 997 to read: 9997 LET N=N+4 at the risk of a slower program and of finding an end-of-line character in the text. Should this happen, most of the time no problem will arise except lines after the offending one will not be renumbered and occasionally your number will be corrupted. The "show only" version of Line Renumbering will show you when that happens. Just a tip if you are tight on memory and forgot to enter the machine code version first! 26. Machine Code Editor ----------------------- The aim of this short BASIC program is to enable you to enter a machine code program into a REMark statement at the beginning of the program. Incidentally, this program will also illustrate the uses of PEEKs and POKES. The first line of the program is numbered line 100 and is a REMark statement containing 32 number '1's: this is where we will place the machine code, and as far as we are concerned, it could be filled with the letters of the alphabet or any 32 graphics characters. All that this line does is to reserve 32 bytes of memory at an address we know. (The ZX81 manual tells us that the first byte of the REM statement will be at memory location 16514: you could check this out [by] writing a short program to PEEK around memory!) The subroutine located at lines 500 onwards PEEKs at what is in the Ith location (I=0 TO 31 in this case, but if you want to enter longer machine code programs, you could alter this maximum value of I in line 120. Be sure to make the REM statement long enough.) To PEEK at a memory location is merely to look at what is in there. The subroutine on its return contains the contents of that memory location in A$. You then have a choice of leaving that memory location as it is (press (ENTER) which makes A$="" - see line 170) or of entering what you would like the memory location to contain. Your input has to be in 2-digit hex code. Line 190 POKes what you decided you wanted in to that memory location. POKE is computer for PUT - we are therefore only putting a selected value into that space. Machine Code Editor 100 REM 11111111111111111111111111111111 (32 times '1') 110 LET S=16514 120 FOR I=0 TO 31 130 SCROLL 140 GOSUB 500 150 PRINT I;"=";A$;". -) ."; 160 INPUT A$ 170 IF A$="" THEN GOTO 200 180 LET V=16*CODE A$+CODE A$(2)-476 190 POKE S+I,V 200 GOSUB 500 210 PRINT A$ 220 NEXT I 230 STOP 500 LET V=PEEK(S+I) 510 LET H=INT(V/16) 520 LET L=V-16*H 530 LET A$=CHR$(H+28)+CHR$(L+28) 540 RETURN Running the Program Part 1 When you have entered (RUN) (NEW LINE) the screen will display the following at the bottom: 0 = 1D -) and the cursor will indicate it is waiting for a string input. What the display is showing you is that the contents of the 0th location is 1D (in hex: you cn confirm this is the number '1' by checking the table at the back of the ZX81 manual). If you now enter 1E (NEW LINE), you will see: 0 = 1D -) 1E 1 = 1D -) This means that the contents of the 0th location were changed from 1D to 1E. Enter the values 1E, 1F, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 2A, 2B, 2C, 2D, 2E, 2F, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 3A, 3B, 3C, 3D. If you now look at the listing of the program you will see that we changed the contents of the REM line! It now reads: 100 REM 23456789ABCDEFGHIJKLMNOPQRSTUVWX This should be a pretty good demonstration of what PEEK and POKE is all about. Obviously we can also PEEK or POKE into the screen area, the variables held in memory, and so on. Running the Program Part 2 We will now use the same program to enter a 32-byte machine language routine which can renumber the lines for us. This is similar to our program "Line Renumbering" except that it is not able to indicate which lines contain GOTOs and GOSUBs. The benefits of this machine language routine however are that it is much faster in execution and that it occupies less memory than the BASIC listing of our other program. Enter the following values: 11, A2, 40, 21, 64, 00, 1A, 3D, FE, 75, C0, 13, 1A, FE, 27, D0, 01, 0A, 00, 09, EB, 72, 23, 73, 23, 4E, 23, 46, 09, EB, 18, E6. You can now test this machine language program by entering the line (without line number): PRINT USR 16514 (NEW LINE) If you now look at the listing you will see that the lines have all been renumbered! (If you want the BASIC program to still work, alter lines 140 and 200 to now read GOSUB 240.) It is beyond the scope of this book to explain how the machine code works, but for the benefit of readers who understand machine language we have included the assembly listing with comments. If you do not understand machine language, we recommend our book 'Machine Language Programming Made Simple' - see details at the end of the book for price and ordering details. The routine you now have stored in line 100 will work for any program. Just delete line 110 onwards, and press (CLEAR) (NEW LINE). You now have a short program (38 bytes) which will renumber lines for you just by entering: PRINT USR 16514 (NEW LINE) Save this program on cassette and load it every time you want to develop your own program. When you have finished developing your program, you can delete line 100 and replace it by a REM statement with the name of your program, such as: 100 REM THE GREATEST PROGRAM EVER or some other suitable title. Running the Program Part 3 We can use the same program to also create any other machine language program we may find useful. For example, it is possible to write a 13-byte machine language program which will tell you to the byte how much memory you have left. This "Memory Left" utility can even be used during the running of a program! Because we have a shorter machine language program, set up line 100 to have only 13 number '1's, and amend line 120 to read: 120 FOR I=0 TO 12 Enter the following values into the program when requested to do so: B7, ED, 5B, 1C, 40, ED, 62, 39, ED, 52, E5, C1, C9. This short machine code program will return the memory left in answer to entering PRINT USR 16514. Try this now. Now delete all lines excep for line 100 and press (CLEAR) (NEW LINE). (This will remove all the variables created in running the machine code editor program). See what the result of PRINT USR 16514 is now. As mentioned above, this utility can even be used during the running of a program, as in the following line: IF USR 16514 < 50 THEN STOP This can be very useful in developing programs, and so on. The assembler codes for this short machine language program are: B7 OR A ED 5B 1C 40 LD DE,(16412) ED 62 SBC HL,HL 39 ADD HL,SP ED 52 SBC HL,DE E5 PUSH HL C1 POP BC C9 RET It is beyond the scope of this book to talk about machine language programming, but it should be obvious from these few examples that machine language programming is able to provide many benefits for users of the Sinclair ZX81. The line renumbering routine, for example, is able to be reduced from some 260 bytes to only 38 bytes and will even execute much more quickly. The "Memory Left" utility, for example, cannot be properly duplicated in a BASIC program because it is impossible to determine how far down the stack is without recourse to machine language. If you are interested in learning machine language, we recommend our book, 'Machine Language Programming Made Simple'. Price and ordering details are available at the back of this book. You may be interested to know that both the Line Renumbering and Memory Left utilities can be combined into one REM statement if you so desire. Set line 100 to have 45 number '1's in it, and amend line 120 to read FOR I=0 TO 44. Enter the Memory Left code first, then the Line Renumbering code. (The code is exactly the same EXCEPT for the second entry in the Line Renumbering Program: change [it] from A2 to AF). To obtain Memory Left, use PRINT USR 16514, while to obtain Line Renumbering, use PRINT USR 16527. Running the Program Part 4 It is not necessary to only store machine code routines in the REM statement. You can use this program to store any numbers as long as they are in the range 0 to 255. There may be times when you need to store a lot of variables for one of your own programs, but don't want to store them in a variable because this would use up 6 bytes per number. This program is ideal for that kind of situation. We use this principle in the program "Doctor ZX81". 00100 ; ZX81 LINE RENUMBERING ROUTINE 00110 ; 00120 ; CODED TO BE STORED IN A REM STATEMENT 00130 ; NUMBERED AS LINE 100 00140 ; NOTE: THIS MUST BE FIRST LINE OF PROGRAM 00150 ; 00160 ; 4082 00170 ORG 16514 00180 ; 00190 ;DEFINE STARTING POSITION AS END OF REM LINE 100 4082 11A240 00200 START LD DE,16546 ;END OF REM LINE 100 4085 216400 00210 LD HL,100 ;LINE # OF FIRST LINE 00220 ; 00230 ;CHECK WE HAVE AN END-OF-LINE CHARACTER 4088 1A 00240 ENDLIN LD A,(DE) ;CHECK LOCATION CONTAINS 4089 3D 00250 DEC A ; END-OF-LINE CHARACTER 408A FE75 00260 CP 75H ;IF A=75H, ALL OK 408C C0 00270 RET NZ ;OTHERWISE ABORT 00280 ; 00290 ;DE POINTS TO NEW LINE START 00300 ;HL CONTAINS LINE # OF PREVIOUS LINE 408D 13 00310 NXTLIN INC DE ;DE POINTS TO NUMBER OF LINE 408E 1A 00320 LD A,(DE) ;CHECK IF LINE # > 9999 408F FE27 00330 CP 27H ;IF YES, ALL LINES RENUMBERED 4091 D0 00340 RET NC ; SO ALL FINISHED 00350 ; 4092 010A00 00360 LD BC,10 ;THIS IS LINE INCREMENT 4095 09 00370 ADD HL,BC ;CALCULATE NEW LINE # 00380 ; 00390 ;INSERT NEW LINE # IN MEMORY 4096 EB 00410 EX DE,HL ;SWAP HL,DE 4097 72 00420 LD (HL),D ; SO THE DE HAS LINE # 4098 23 00430 INC HL ;POKE MEMORY WITH 4099 73 00440 LD (HL),E ; NEW LINE NUMBER 00450 ; 00460 ;GET LINE LENGTH AND SKIP TO END OF LINE 409A 23 00480 INC HL ;HL NOW POINTS TO 409B 4E 00490 LD C,(HL) ; LENGTH OF LINE 409C 23 00500 INC HL ;LOAD BC WITH LINE LENGTH 409D 46 00510 LD B,(HL) 409E 09 00520 ADD HL,BC ;HL NOW POINTS TO END OF LINE 00530 ; 409F EB 00540 EX DE,HL ;SWAP HL,DE TO RESTORE 00550 ; ORIGINAL CONDITIONS 40A0 18E6 00560 JR ENDLIN ;RENUMBER NEXT LINE F. CHALLENGING THE ZX81 1K -------------------------- 27. Mastermind -------------- In this game, the computer generates a number of as many digits as you specify (between 3 and 7). You then have to try to guess the number. After each guess, the computer will tell you how many of the digits you entered were in its number, and how many were in the correct position. There are no repeated digits in the number chosen by the computer. This means that it will never come up with a number such as 1231. When you work out what the number is, your score will be displayed. If you get frustrated, and want to give up, press (NEW LINE) when you are asked for a number. Of course, in that case you'll never find out what the number was. Program Structure The computer's number is stored as an array of digits in A. Your number is stored as a string of characters in B$. The function CODE used in line 380 returns the numerical code for the first character in the string. '28' is the code for '0', so CODE(B$)-28 gives the numerical value of the first character in B$. C contains the number of digits in the correct position. R contains the number of digits that are in the computer's number. Mastermind 100 PRINT "WELCOME TO MASTERMIND" 110 PRINT "ENTER NO OF DIGITS IN NUMBER (3-7)" 120 INPUT N 130 DIM A(N) 200 FOR I=1 TO N 210 LET X=INT(RND*10) 220 FOR J=1 TO I 230 IF X=A(J) THEN GOTO 210 240 NEXT J 250 LET A(I)=X 260 NEXT I 270 LET G=0 280 GOTO 530 300 INPUT B$ 310 IF B$="" THEN STOP 320 CLS 330 PRINT B$ 340 LET R=0 350 LET C=R 360 LET G=G+1 370 FOR I=1 TO N 380 LET X=CODE(B$)-28 390 IF X=A(I) THEN LET C=C+1 400 FOR J=1 TO N 410 IF X=A(J) THEN LET R=R+1 420 NEXT J 430 LET B$=B$(2 TO ) 440 NEXT I 450 IF C=N THEN GOTO 600 500 PRINT "NUMBERS RIGHT=";R 510 PRINT "CORRECT POSITIONS=";C 520 PRINT 530 PRINT "ENTER ";N;" FIGURE GUESS" 540 GOTO 300 600 PRINT "YOU DID IT IN ";G;" GUESSES" 28. Doctor ZX81 --------------- In this program we will try to bring within the limitations of a 1K machine a conversational program. This is the beginning of a program which shows the possibilities of Artificial Intelligence in the Sinclair ZX81. Unfortunately because of the restrictions of memory, Doctor ZX81 is not a very stimulating conversationalist. In fact, some people say that like most professionals, he doesn't seem to listen to what ordinary people like you and me have to say! Structure of the Program In this program we will have a conversational exchange between the computer and a 'patient'. We will have to define the vocabulary of the good Doctor and the sentences he will speak. In order to fit all this into 1K we have divided up the program into three smaller programs: one to define the vocabulary, one to define the sentences, and the last one to make the Doctor talk to us. Vocabulary We will define the vocabulary into a string variable called A$. This is what Program 2 does. The point to remember is that it is not essential to keep the string variable in the program listing as well as in memory. Thus after we have (RUN) Program 2, we can delete the string variable A$ from the program listing and still have it available for Program 3. The string variable consists of 38 words which we define as the Doctor's vocabulary. By defining the vocabulary in this way, we can then refer to any word by its position in A$ and not have to waste memory by using the full word many times. We put a space after each word and that is the way we will be able to tell where each word ends. Sentences for the Doctor After we defined the vocabulary for the Doctor we noticed that the length of the vocabulary was less than 256 characters (in fact only 203). You can immediatly see that if 38 words require 203 bytes we need to conserve memory wherever we can. Since each word can thus be defined by a single number between 1 and 203 (and thus would need only 1 byte in memory in an ideal system), it seems a waste of memory to use a normal variable for each word which takes up 6 bytes! We want to define 11 sentences with a total of 75 words: using normal variables would be counterproductive as we would run out of memory! Instead we will use a REM statement at the beginning of the program to store the words we want to use in our sentences. We will use a simple BASIC program to POKE the numbers we want into the REM statement. If you have problems understanding what POKE and PEEK is all about, have a look at the program "Machine Code Editor" which may make it clearer to you. As no word can start at location 0, we will use '0' to define the end of a sentence in much the same way that we used a space to define the end of a word. Conversing with the Doctor This is Program 3. By the time we come to run this program we have the vocabulary stored in A$ in memory (and so cannot use (RUN) or (CLEAR)) and the sentences in the REM statement in line 100. The program consists of taking each word in turn (variable I) and displaying each character of that word (variable J). If the character about to be displayed is a space, we then go to the next word (lines 170 and 180). If the word about to be displayed is word #0, then we know the sentence is finished and we display a question and wait for the user's response. Program 1 This program defines the sentences we will be showing on the screen and stores the word numbers in the REM statement defined in line 100. 100 REM 111111111111111111111111111111 222222222222222222222222222222 333333333333333333333333333333 (at least 90 chars) 110 FOR I=1 TO 89 120 SCROLL 130 PRINT AT 1,1;I;" "; 140 INPUT V$ 150 IF V$="" THEN GOTO 180 160 PRINT V$ 170 POKE 16513+I,VAL V$ 180 NEXT I Press (RUN) (NEW LINE). Enter the following numbers followed by (NEW LINE) when each value of the word number is shown: 1 68 (NEW LINE) 2 141 etc... 1= 68 21= 36 41= 0 61= 85 81=158 2=141 22=195 42= 36 62=102 82= 27 3= 88 23= 46 43=199 63= 0 83= 74 4=199 24=161 44= 60 64= 36 84= 14 5=131 25= 0 45= 51 65=199 85= 0 6=175 26= 36 46=153 66= 60 86=124 7= 85 27=195 47= 3 67= 51 87=109 8=199 28=147 48= 0 68=153 88= 20 9= 97 29=153 49= 36 69= 3 89= 0 10= 0 30= 85 50=195 70= 0 11=175 31=109 51= 27 71= 83 12= 32 32= 0 52= 74 72=147 13=195 33=185 53=158 73=124 14=120 34= 36 54= 51 74= 39 15=199 35=195 55= 60 75= 56 16= 97 36=147 56= 0 76=161 17=167 37=195 57= 36 77=180 18= 14 38= 46 58=195 78=189 19= 0 39=153 59=147 79=195 20= 79 40=171 60=153 80= 92 After you have (RUN) this program, you will get a surprise if you look at the listing of the program: line 100 will now be full of strange letters, commands and graphics characters. This is perfectly normal. Delete all lines of Program 1 except for line 100. You should now have a program listing that looks like: 100 REM $ ? SIN = J ? SIN ? etc. You can now also press (CLEAR) (NEW LINE) to delete the variables held in memory to make more room for the string variable you are about to enter in Program 2. Program 2 Add the following line 110 to the line 100 you already have: 110 LET A$="A ACCEPTABLE AGAIN BYEBYE COME DID DO ENOUGH FEEL FIND FOR FRIENDS HELLO HERE HOW I IS I/M LIKE NAME NORMAL REASONABLE SAY THAT/S THERAPIST THERE THINK THIS TO TODAY WAS WAY WHAT WHEN WHY WOULD YOU YOUR " Be sure that you leave a single space only after each word (including the last word). It is important that each word should be spelt properly because if you have any errors, the words after that may not be printed out correctly. Press (RUN) (NEW LINE). The variable A$ is now stored in memory, so let's check that there are no errors. Enter the following lines without line numbers: PRINT A$(1) (NEW LINE) - you should see A PRINT A$(199 TO 202) (NEW LINE) - you should see YOUR If you have made any errors, check A$ again. Once you have everything in correctly, and A$ in memory, delete line 110. Be sure not to press (CLEAR) or (RUN) from now on. Program 3 110 FOR I=1 TO 89 120 LET J=-1 130 LET J=J+1 140 LET K=PEEK(16513+I) 150 IF K=0 THEN GOTO 190 160 PRINT A$(K+J); 170 IF A$(K+J)<>" " THEN GOTO 130 180 IF K>0 THEN GOTO 220 190 PRINT "?" 200 INPUT B$ 210 CLS 220 NEXT I In order to run this program enter (GOTO 1) (NEW LINE). Improving the Program It should be fairly obvious to you by now how to change the conversational patter of the good Doctor: merely redefine new words for the sentences in Program 1. The numbers refer to the position in A$ where the word is stored. Obviously a great improvement could be made by making the doctor responsive to the player's replies. This could be achieved in a system with more memory by adding lines such as the following: 105 LET C$="" 205 IF I=19 THEN LET C$=B$ 215 IF C$<>"" THEN PRINT C$;" "; This will add the name of the player in front of the questions, for example. 29. Caves and Pitfalls ---------------------- © by Clifford Ramshaw "Caves and Pitfalls" is an attempt to bring to the user of the Sinclair ZX81 a mini-adventure within the confines of a 1K machine. Naturally this means a loss of many of the nicer things in traitional adventure games, such as predefined pathways and creatures. We have to rely entirely on the use of the RND function in this game, and even then it is a very tight squeeze indeed. The program is so difficult to fit into 1K that it has been found necessary to break the program up into two: 1. The first program defines all the variables, and after we (RUN) this program we can delete the listing. All the variables will still be remembered in the memory. 2. The second program is the one that actually controls the game. Because we need the variables we saved from the first program, we cannot (RUN) it ((RUN) destroys all variables in memory), but must use (GOTO 1) instead. The other major space saving used in this program is to try to eliminate numbers from the listing of the program. Any number in a program listing, whether something like "FOR X=1 TO 3" or something like "LET S=16384", will use up 6 bytes of memory. This is obviously very wasteful for single and double digit numbers and we have used variables defined in the first program to get over this. Variables Used in the Program We define all the fixed variables in the first program. These are: Monsters we may meet: These are stored in "A$" which is dimensioned as 4 strings of up to 6 characters each. Treasure we can find: These are stored in "B$" which is dimensioned as 2 strings of 6 characters each. Factors which define the score: These are "T" which is the value of treasure found, and "K" which is the number of monsters killed. The score is obtained by multiplying these two. Numbers used in the second program: 1, 2, 3, 5, 10. Program 1 100 DIM A$(4,6) 110 LET A$(1)="DRAGON" 120 LET A$(2)="ZOMBIE" 130 LET A$(3)="WRAITH" 140 LET A$(4)="HYDRA" 150 DIM B$(2,6) 160 LET B$(1)="GOLD" 170 LET B$(2)="SILVER" 180 LET T=0 190 LET K=0 200 LET X=1 210 LET R=2 220 LET Y=3 230 LET Z=5 240 LET W=10 You must (RUN) this program before you can delete this listing and continue with entering Program 2. After you have (RUN) this program, you may be interested to see how much space it is possible to save by this method of predefining variables. Enter the following line without a line number: PRINT PEEK 16404+256*PEEK 16405 This will give you the position of the end of the variable [area], and should be about 16900. Now delete all this listing by entering the line numbeers followed by (NEW LINE). You will end up with a blank screen. To check that all the variables are still remembered you can enter: PRINT A$(1) When you press (NEW LINE) you will see DRAGON on the screen! By entering the line as above to check on memory usage, you will see that we have saved about 300 bytes of program listing, without losing any of the variables! Program 2 Important note You cannot enter Program 2 without having entered Program 1, (RUN) Program 1, and deleted its listing above. Also do not press (CLEAR) or (RUN) at any stage while entering Program 2 as this will destroy the variables saved. 100 GOSUB 800 110 PRINT "LEFT,RIGHT?" 120 INPUT A 130 CLS 140 GOTO W*W*INT(R*R*RND+X) 200 PRINT "A DOOR",,"LEAVE OR IN?" 210 INPUT A 220 IF A=X THEN GOTO W*W 300 PRINT "YOU SEE A ";A$(INT(R*R*RND)+X),"IT ADVANCES" 310 GOTO 500 400 PRINT "WHOOPS...A PIT" 410 LET A=INT(W*RND+X) 420 IF A=X THEN GOTO W*W 430 PRINT "AT THE BOTTOM "; 440 IF AX THEN GOTO W*W 540 LET A=W*RND 550 IF A>=Y THEN GOTO 600 560 PRINT "RIP" 570 GOTO 800 600 IF A>Z THEN GOTO 700 610 PRINT "LOST" 620 LET T=INT(T/R) 630 GOTO W*W 700 PRINT "MONSTER IS DEAD" 710 LET K=K+X 720 LET A=R*RND+X 730 PRINT "FOUND ";B$(INT A) 740 LET T=T+INT(A*W*RND+X) 750 GOTO W*W 800 PRINT "SCORE ";T*K 810 RETURN Note: You may have noticed that the line numbers do not follow an orderly sequence. This is deliberately so, so that the GOTO statement in line 140 can be used. Running the Program As mentioned above, we cannot use (RUN) with this program as doing so will destroy all the variables which were so carefully defined. We must therefore use (GOTO 1) instead. At each step of the mini-adventure, you will be given a choice of two: TURN LEFT OR TURN RIGHT? GO THROUGH THE DOOR OR NOT? FIGHT THE MONSTER OR FLEE? The program is looking for an input of "1" or "2", signifying that you have picked choice 1 or 2. The game terminates when you have been killed by one of the monsters. Should you wish to fight another round of "Caves and Pitfalls", you will need to reinitialize the treasure and kill count. Enter the following lines (without line numbers) to start again: LET T=0 (NEW LINE) LET K=0 (NEW LINE) GOTO 1 (NEW LINE) Best of luck against the demons of the caverns! 30. Draughts ------------ It is possible for the standard 1K Sinclair ZX81 to play draughts! Of course, this is only possible by using every trick in the book, including resorting to machine language programming for the part of the program that plays against the human player. The board is shown on the screen as follows: 1 1B B B B 2 B B B B 3 3B B B B 4 a a a a 5 5a a a a 6 W W W W 7 7W W W W 8 W W W W ABCDEFGH (The outer numbers 1-8 and the designation of columns A-H are not shown on the screen - the only numbering included are the numbers 1, 3, 5 and 7 as shown within the board.) The rules of the game the ZX81 plays follow the standard rules except that multiple jumps are not allowed and capture is not compulsory. Reaching the end line results in the creation of a king (shown on the screen as [an] inverse letter), which can only move one square at a time but is allowed to jump backwards. Structure of the Program As mentioned above, the board is displayed on the screen - this is the only record the ZX81 has of the state of play! No other array is required to keep track of the game. This therefore requires the use of PEEKs to find out where the pieces are and POKEs to move them. In the standard 1K version there is also insufficient room to retain the string variable that initially sets up the board in the program listing. We must therefore resort to putting that string variable in memory and using (GOTO 1) to start the program. The last space saving "trick" involves replacing all 1- and 2-digit numbers in the listing: each number in a ZX81 listing takes up 6 bytes! This is true even if your number is only a single digit. We therefore make liberal use of constructs such as VAL"2" or CODE"W" which only require 4 bytes. The program is also broken down into 3 different programs, the last two of which overlay the previous ones. This is the only way to enter so much information into the Sinclair ZX81 1K. The structure is as follows: Program 1 PUT MACHINE CODE ROUTINE INTO REM STATEMENT Program 2 DEFINE THE BOARD FOR PLAY Program 3 DRAW THE BOARD INPUT PLAYER'S MOVE CHECK PLAYER MOVE VALIDITY MAKE COMPUTER'S MOVE (CALL TO USR ROUTINE) GO TO NEXT PLAYER INPUT Program 1 100 REM 11111111111111111111111111111111 (The REM statement must have 175 22222222222222222222222222222222 chars in it.) 33333333333333333333333333333333 44444444444444444444444444444444 55555555555555555555555555555555 66666666666666666 150 LET S=16514 160 FOR I=0 TO 174 170 SCROLL 180 PRINT I; 190 INPUT A$ 200 IF A$="" THEN STOP 210 LET V=16*CODE A$+CODE A$(2)-476 220 POKE S+I,V 230 PRINT TAB 5;A$ 240 NEXT I This program will input the machine language code into the REM statement. When you (RUN) this program, the screen will show the number, and waits for you to input the code. If you make an error, just enter (NEW LINE) to stop the program, and start again. The code is on one page at the end of this program. Note that the code to be entered can only contain the letters A-F and the numbers 0 to 9. (It should take you less than 15 minutes to enter this code into the array.) Once you have (RUN) Program 1, the machine language code will be stored in line 100. The listing of Program 1 is no longer required. Save the program you have made so far on to tape [after you] delete lines 150-240. Program 2 120 LET A$="1B B B BB B B B 3B B B Ba a a a 5a a a aW W W W 7W W W WW W W W " (The graphics character is obtained by using GRAPHICS and (SHIFT) (A).) 150 FOR L=VAL "1" TO VAL "8" 160 PRINT A$(TO VAL "8") 170 LET A$=A$(VAL "9" TO ) 180 NEXT L 200 INPUT A$ 310 IF USR 16514>VAL "0" THEN GOTO 200 This program will display the board on to the screen and test the machine language routine entered in Program 1. The screen is saved in the string variable A$, and is printed in 8 lines. It is essential that a minimum configuration screen is set up, so that the structure in memory of the board is as follows: 1B B B B&B B B B &3... (where the symbol & is used to represent end-of-line). If you check this out this means that all legal moves are limited to increases and decreases of 8 or bytes in memory. Users with additional memory connected to the ZX81 should add the following lines: 130 POKE 16389,76 140 CLS This will ensure that a minimum configuration screen is set up. Running Program 2 After SAVing the program, press (RUN). You can choose to be in either SLOW or FAST mode. The screen will be displayed as shown at the beginning, and the ZX81 will be waiting for a string input. Press (NEW LINE) to see the computer's first move. You should see the computer move its first piece from G3 to H4. You can continue to press (NEW LINE) to see what the computer would do next if that was its position. If you so desire you can change the string variable in [line] 120 to set up any starting position. Preparing for Program 3 Enter the following line into your listing: 130 STOP and then press (RUN). Delete lines 120 and 130 and SAVE your program so far. This has the effect of storing the string variable in memory without the need to keep it in the program listing. (Users with more than 1K memory do not need to do this - retain your original lines 120-140.) Program 3 At this stage you should have lines 100, lines 150-180, and lines 200 and 310. The string variable A$ is stored in memory, so do not press (RUN) or (CLEAR) as this will destroy the contents of A$. Add the following lines to your program: 210 LET S=PEEK 16396+VAL "256"*PEEK 16397 220 LET F=S+CODE A$+VAL "9"*CODE A$(VAL "2")-VAL "298" 230 LET T=S+CODE A$(VAL "3")+VAL "9"*CODE A$(VAL"4")-VAL "298" 240 LET M=(T+F)/VAL "2" 250 IF (PEEK F<>CODE "W" AND PEEK F<>CODE "w") OR (ABS(F-T)>VAL "10" AND PEEK M<>CODE "B" AND PEEK M<>CODE "b") OR (PEEK FCODE "a" THEN GOTO 200 (Letters in lowercase are inverse characters obtained using GRAPHICS mode.) 270 POKE T,CODE "W"+CODE "~"*(PEEK F>CODE "W" OR (T-S)VAL "10" THEN POKE M,CODE "a" 300 PAUSE CODE "W" You may find some difficulty in entering the long lines, such as line 250, if you only have 1K of memory. A good hint is to enter (CLS) and (NEW LINE). This does not affect the variables but gives you more room on the screen to enter your line. SAVE this prorgam before trying to play draughts. Playing Draughts As we mentioned at the beginning, if you only having 1K you cannot use (RUN) as this will clear the variable so carefully saved. Use (GOTO 1) instead. (Users with additional memory have the string variable in the listing so can use (RUN).) You should already have tested Program 2 by the time you come to this point, so you already know the display routine works and the computer playing routine works. The additions in Program 3 are the player's moves and checking if these moves are allowed. (This is all in line 250: "F" is "from" and "T" is "to". We check to see if the move from contains a white piece, the move to is empty, any captured sequares do in fact contain a black piece, and so on). The input the computer is waiting for is a 4-character string, such as "A6B5". This means that you mean to move from square A6 to square B5. As only the most rudimentary numbering has been included in the screen, you may find it useful to keep a properly numbered board by the computer when playing. The ZX81's response is extremely fast - almost instantaneous as it is written in machine language - so a short delay (line 300) has been introduced to allow you to see the computer's move being made. You can choose to play in either FAST or SLOW mode. If you should wish to play a second game, you cannot simply use (GOTO 1) again, as the string variable A$ has been deleted from memory. You will need to either reload the program from cassette or reenter on the edit line the string variable A$ as in line 120 (but without the line number). You can use this to also enter different set positions you wish to examine - eg. giving the computer a head start, etc. Machine Language As mentioned in the beginning, the part of the program which determines the computer's next move is written in machine language. We have already seen in the "Line Renumbering" program and in [the] "Machine Code Editor" program that using machine language can result in an enormous saving in memory. This is the reason this part of the program was written in machine language - the validation of the player's move in the above program takes as much room as the entire section dealing with the computer's move: searching for best moves, watching out for traps, making the moves, converting to kings if required, and so on. Unfortunately it is beyond the scope of this book to give an explanation of how this particular machine language program works, or how to write improvements for it. All this information can be found in our book "Machine Language Programming Made Simple". That book also includes a full description of this Draughts program. Price and ordering details are at the back of this book. 0=AF 50=40 100=EB 150=42 21 23 E5 40 3C 10 ED E1 40 DA 52 22 06 11 E1 44 0A 3C 38 40 77 40 02 E1 23 21 F6 22 10 43 80 40 FC 40 77 40 10=06 60=34 110=C9 160=C9 48 35 E5 3A 2A 01 19 3F 0C 04 7E 40 40 00 FE A7 E5 20 08 28 7E 02 20 D8 FE 0E 12 E1 A7 00 E5 E1 28 D5 19 C9 20=12 70=D5 120=7E 170=5E FE E1 E6 23 27 09 7F 56 20 0E FE EB 1A 04 3C 174=C9 11 ED 28 08 B0 22 00 CD E1 CD 2C 22 F1 41 3E 30=40 80=36 130=40 11 08 E1 0A E1 22 00 CD 3C CD 2C 40 F1 41 C9 40 7E E6 18 36 7F 0C 08 FE 11 13 3C 40=F6 90=EB 140=20 FF CD 1A CD 2C E5 F1 41 19 40 D1 7E 11 01 FE F8 3F 08 FF 00 20 CD EB 12 F1 09 22 X. MACHINE LANGUAGE MADE SIMPLE ------------------------------- This is the book you have been waiting for - the one which will explain in simple and easy terms exactly how to get more power out of the computer. Go beyond BASIC into the world of machine language programming and open computer horizons you never thought possible! More computing power Now at last you can use the full potential of the heart of the Sinclair ZX81 - the powerful Z80 microprocessor - by creating your own fast machine language programs or subroutines. Machine Language Made Simple is easy to use and designed for the complete beginner: no other knowledge of programming is required. * Includes the machine code listing of the Draughts game found in this book, commented and explained, as well as other programs and examples. Faster running programs! X. UNDERSTANDING YOUR ZX81 ROM ------------------------------ INCLUDES SPECIAL SECTION: How to use machine code routines in your BASIC programs. Dr Ian Logan was the first person to disassemble the ZX80 monitor and now he is the first to explain exactly how the Sinclair ZX81 ROM does what it does. Full details on the structure of the ROM are given, including the way the Sinclair ZX81 handles the display, the cassette, and the keyboard! ROM addresses for all major ROM routines are given. This makes it easy to write your own specific programs using the ROM's routines. This is an essential book for those who really want to understand the full working of the Sinclair ZX81. X. ORDERING DETAILS ------------------- ORDERS FOR PUBLICATIONS OF MELBOURNE HOUSE SHOULD BE SENT TO: MELBOURNE HOUSE PUBLISHERS 131 TRAFALGAR ROAD GREENWICH LONDON SE10 ENCLOSING A CHEQUE OR MONEY ORDER. A POST AND PACKING CHARGE OF 80 PENCE SHOULD BE ADDED TO THE COSTS OF THE BOOKS. Any correspondance regarding any of these titles should be addressed to: Melbourne House Publishers Glebe Cottage Station Road Cheddington Leighton Buzzard Beds. LU7 7NA Titles available: THE COMPLETE ZX81 BASIC COURSE £17.50 BASIC Course Programs on Cassette £ 2.50 NOT ONLY 30 PROGRAMS FOR ZX81 1K £ 6.95 MACHINE LANGUAGE MADE SIMPLE (ZX81) £ 8.95 UNDERSTANDING YOUR ZX81 ROM £ 8.95