Welcome to ZX85, a programmable general-purpose computer for introducing children between the ages of 6 and 106 to the centuries-old tradition of wizardry. One could also say "regular symbolic manipulations under a Turing-complete set of rules" instead of "wizardry" and that would be certainly correct, but the latter seems a more fitting expression for the art of creating functioning mechanisms of unlimited complexity by merely describing them in a special language. Wizardry has been around for much longer than computers, so while computer programming is certainly wizardry, there is more to it than programming computers. A large part of mathematics and science is also wizardry and so is genetic engineering and many other important human endeavors. While capable wizards are today among the most sought-after specialists, mastering this art is its own reward, being one of the most stimulating intellectual exercises.
In this book, the first uses of important terms are set in bold italic. 
Usually, you will find the explanation of these terms somewhere around their first use. If not, you 
can look them up elsewhere or ask someone who knows what they mean. Framed texts are either 
notes, providing additional information typically relating the discussed topic to the 
broader context of the world beyond the ZX85 or trivia, interesting facts related to 
the discussed topic, often providing a historical perspective or challenges. The 
first word in the frame, set in bold regular tells you which one that particular 
frame is. Texts that you should see on the screen, such as keywords, program line numbers, listings, 
reports and so on, are set in the native font of ZX85.
To be continued...
The console is how wizards talk to computers, in their own language. Unlike the desktop, which is what muggles use to get the computer do what they need, the console allows you, in principle, to make the computer do anything that the computer is capable of doing at all. It might seem less fancy, but it is far more powerful and expressive than the desktop. Different computers might speak different languages on their console, but they are, in many ways, similar.
The language your ZX85 understands is called BASIC, 
which stands for
Beginner's 
All-purpose
Symbolic 
Instruction
Code.
There are many dialects of BASIC; ours is an evolution of ZX 
BASIC, developed for Sinclair's ZX line of computers in the early 
1980's. In particular, it is to a large extent backwards compatible with 
that of the ZX Spectrum, upon which ZX85 is 
based.
When you turn on your ZX85, it performs a quick self-test and greets you with the following message near the bottom of the screen:
© 2019 ePoint Systems Ltd © 1982 Sinclair Research Ltd
You are using the console now. It is, essentially a dialog with the computer, similar to instant messaging. You type something, hit the ENTER key and the computer responds. ENTER is just about the most important key on the keyboard. Just pressing it means, "Okay computer, I've typed in your orders. Now go and obey them."
As you start typing, you can immediately notice that the screen is divided into two parts by a black stripe with a rainbow:
 
In fact, the part below the stripe is the console, the part 
above it is the canvas. Inside the 
stripe, you find the following information: BASIC stands 
for the kind of input the console expects from you. In this case, it 
expects a sequence of statements in BASIC. Then you have 
:1, which just indicates that you are about to type the 
first statement. Near the middle of the stripe, you see an 
L character, which is the current input 
mode. It tells you that if you press a letter key, it will 
produce a lower-case letter (try it!). The 
rest is just decoration.
The blinking square is the so-called cursor which shows you where what you type is going to appear. If you have already typed in anything, you can move the cursor using the arrow keys. You can also delete what is before the cursor (which is normally what you have typed last) by hitting the DELETE key.
If you press CAPS LOCK, the mode changes to C which 
stands for capital-case letters (try!). You 
can change back to L mode by hitting CAPS LOCK again. You 
can also type capital-case letters in L mode by pressing 
and holding the SHIFT key before hitting the letter key. The 
SHIFT key needs to be pressed while you are hitting the letter key in 
order for it to produce a capital letter. If you keep any key pressed 
long enough, it will repeat its function. To type the symbols on either 
number or letter keys, you should press the SYM key (also called 
Symbol Shift) similarly to how you type 
capital-case letters with SHIFT.
Note: On many other computers, the symbols on number keys are typed with the SHIFT key, while the symbols on letter keys are typed with some other shift-like key (typically ALT GR). Not on a ZX! Here, all symbols are consistently typed with Symbol Shift, while SHIFT'ed numbers are performing the functions of control keys (for example, SHIFT+1 is the same as EDIT, which will undo everything you have typed so far). Be careful with those.
Now you know enough to start chatting with the ZX85.
If you try to greet the computer by typing Hello ZX! and 
hitting ENTER, the cursor will jump to after the first letter. This is 
the computer's tactful way of telling you it does not understand a word 
you're saying. In fact, the cursor is moved to after the first character 
that the computer did not understand; the very first one, in this case.
Any valid statement in ZX BASIC must begin with a so-called instruction keyword. They are, in alphabetical order, the following:
| AND BEEP BORDER BRIGHT CAT CIRCLE CLEAR CLIP CLOSE # CLS CONTINUE COPY DATA DEF FN DELETE DIM | DISPLAY DRAW ELSE END IF END PROC END WHILE ERASE EXIT FLASH FOR FORMAT GO SUB GO TO IF INK INPUT | INVERSE LET LIST LLIST LOAD LOCAL LPRINT MERGE MOVE NEW NEXT ON ERROR OPEN # OUT OVER PALETTE | PAPER PAUSE PLAY PLOT POKE POP PRINT PROC RANDOMIZE READ REM RENUM REPEAT RESTORE RETURN RUN | SAVE SCALE STACK STEP STOP UNTIL USR VERIFY WHILE WRITE # YIELD | 
If you type Draw. and hit ENTER, you will notice that as 
soon as you type the period, all the letters of the word 
DRAW are turned to capitals, an extra space is inserted 
before the period and the cursor is flashing after the period. The 
computer does still not understand what this is supposed to mean, though 
it understands a bit more than last time.
Note: Even though the keywords of BASIC are mostly English words, BASIC is not English. For the computer to understand what you type, it needs to be correct BASIC, not correct English. For instance, in BASIC, you do not end statements with a period.
If you move the cursor around, the computer won't let you move it inside the (now capitalized) keyword anymore; it jumps from one side, to the other. That is because it has recognized the keyword and now treats it as a single unit, a so-called token.
Trivia: Statements instructing the 
computer to answer us in writing begin with the PRINT 
keyword. The reason for this is historical: computers got consoles 
before they became able to display anything on screens. In those days, 
the console's output was a roll of paper on which things were literally 
PRINTed. While this has no longer been the case for many decades now, 
the keyword stuck.
 
So let's try this: type PRINT 2+2 and hit ENTER. The 
computer dutifully writes 4 on the canvas and reports
0 OK, 0:1on the console. Note, that we get the same result no matter which letters are capitalized in the
PRINT keyword; pRiNt 2+2 
would work just the same.
What happened here? The 4 is, as you might have guessed, 
the evaluation of 2+2. OK means what 
you'd think it means: that there was no error in executing this 
statement. The 0:1 after the comma tells us that this was 
the first statement (:1, same as in the black stripe) of a 
sequence entered from the console. The very first 0 in the 
report is the report code identifiying the report. 
0 stands for OK. While we need to talk to the 
ZX85 in BASIC, it reports to us in English. However, it can be changed 
to report in other human languages as well, in which case the text of 
the report might change to the point of unintelligibility for those who 
do not know that particular language, but the report code remains the 
same, no matter what the langauge of the report is. Thus, one can 
understand what the report says even without understanding the language 
in which the computer is reporting, given a full list of error codes. If 
the computer needs to read its own or another computer's reports, it 
can just look at the report code, ignoring the text in a language which
it does not understand (such as English).
So, now you can use the computer as a calculator. But if you type 
PRINT Hello and hit ENTER, instead of a greeting on the 
canvas, we get an error report:
2 Variable not found, 0:1
This is because Hello is treated as a 
name of a variable 
(more on this a bit later), not as a text to print. If we mean a 
specific text, in BASIC, we must put it inside double quotes.
PRINT "Hello" actually does write 
Hello on the canvas.
Note: Computers are very fussy that you should 
distinguish between the digit zero and the letter O. To make it 
absolutely clear, zero appears on the screen as 0, with a 
slash through it. You also need to distinguish between the digit one 
(1), the capital letter i (I) and the small 
letter L (l). All ten digits are on the top row of the 
keyboard. Furthermore, you must use the star (*) for 
multiplication, not the letter x.
Instructions given on the console are called commands. However, one can also assemble sequences of instructions, which are called programs and execute them together. Type
1 PRINT "Hello World!"in the console and hit ENTER. Instead of just writing
Hello 
World! on the canvas, the entire command gets "moved" up 
to the canvas. Something very important has happened: you have just written 
your first computer program. Congratulations!
Trivia: ZX BASIC is a programming language in which the instructions for programs and the instructions for commands are exactly the same. Such languages are called scripting languages, because programs are like scripted commands.
To run the program, just type the 
RUN command. It does write Hello World! on the 
canvas, just like in the previous example, but the report is slightly 
different:
0 OK, 1:1
The difference is the statement identifier. It says 1:1 
now instead of 0:1. This is because the last statement 
executed was not directly from the console, but from program 
line 1. You can enter another program line, too:
2 PRINT "My name is ZX85."
When you ENTER this program line, it also moves up and you see this in the canvas area:
   1 PRINT "Hello World!"
   2>PRINT "My name is ZX85."
That little > sign after line number 
2 is the so-called program 
cursor. You can move it up and down with the arrow keys 
and bring the pointed program line back down to the console by pressing 
the EDIT key for further EDITing. Now try giving your ZX85 a personal 
name by EDITing line 2. If you wish to delete a program line, just 
enter its number without any instructions. Thus, for example, entering 
1 would remove the greeting from before the 
self-introduction.
In order to make it easier to insert new program lines in between 
existing ones, there is a convention to number program lines by the 
multiples of 10. The command RENUM re-numbers the program 
to follow this convention. After RENUM. the two-line 
program above becomes this:
10 PRINT "Hello World!" 20 PRINT "My name is ZX85."
Re-numbering does not change what the program does, at least for well-written programs, it just makes it easier to work on it.
Now you know enough to start programming your ZX85. In each of the following chapters, you find programs of increasingly complex computer games and explanations of how and why they work. You are encouraged to modify them, make them better and, eventually, write your own games
The following ten-line program is a simple number guessing game. The 
computer picks a random number between 1 and 1000 and you need to guess 
it. After each guess, the computer would tell you whether your guess was 
correct, too high or too low. Based on this information, you can close 
in on the number picked by the computer. Type in the following program 
and then enter the RUN command to play a game.
10 LET number=1+INT (1000*RND) 20 LET guesses=0 30 REPEAT 40 INPUT "Your guess?",guess 50 IF guess<number THEN PRINT guess;" is too low." 60 IF guess>number THEN PRINT guess;" is too high." 70 LET guesses+=1 80 UNTIL guess=number 90 PRINT "Congratulations, you guessed ";number;"." 100 PRINT "It took you ";guesses;" guesses."
When the game ends, the console shows the following report:
0 OK, 100:1By now, you know what that means. You can always play another round by entering
RUN again.
Note: When the game asks you to guess a number, the 
separator stripe between the canvas and the console says 
NUMERIC instead of BASIC which means that it 
expects a number or a numeric expression; a 
formula that, when evaluated, results in a number. So, if you type 
100+200, it qualifies as a guess of 300. Try 
it!
Challenge: This behavior actually allows the player to "cheat" and always guess the number picked by the computer correctly upon first attempt. Wizards that are good at noticing and exploiting such opportunities are called hackers. Can you think like a hacker and win the game in one guess every time?
Now, let's see what it actually does. In the first two lines 
(numbered 10 and 20, respectively), the 
instruction keyword is LET. It 
assigns values to 
variable names. That is, it LETs the name mean 
a particular number, until further assignments. For example, in line 
20, the variable name guesses is made to stand 
for zero. You can try it separately. If you enter that line without the 
line number and then type PRINT guesses it will output 
0. If you enter LET five=2+2 and then 
PRINT five, it will output 4. This is 
perfectly OK, because five is just a name, and 
when a wizard uses a name, it means just what the wizard chooses it to 
mean — neither more nor less.
Note: In ZX BASIC, anywhere where you can enter a 
number, you can also enter numeric expressions. There is only one 
exception from this rule: the line number before statements. Thus 
2+2 PRINT is not correct BASIC, but anywhere else where 
4 is accepted, 2+2 or 2*(1+1) is 
also accepted and means the same.
Line 10 has two keywords that are not even on the list 
of the previous chapter: INT and RND. This is 
because they are not instruction keywords. BASIC statements cannot begin 
with those. Let's explore them in more detail!
RND is similar to variables, except that it does 
not need to be assigned and changes its value all by itself. It means a
random number that is at least zero and always less than 1. If you enter
PRINT RND, it will output a different fractional number
every time. Once in a while, it will output 0, though the 
chances of that happening are pretty slim: one to 65536. It will never
output 1, though it can get pretty close.
Thus, 1000*RND is a number that is at least zero and 
always less than 1000. Try PRINT 1000*RND a few times. Most 
of these numbers have a fractional part after a decimal point. This is 
what INT removes. INT is a 
function that does something to the number 
that follows it. In particular, it turns it into an 
integer (which is just wizard-speak for whole 
numbers), removing the fractional part of numbers greater than zero.
However, PRINT INT 1000*RND does exactly the same as 
PRINT 1000*RND (try!). This is because the number that 
follows INT is 1000 so it takes the whole part 
of 1000, which is still 1000. Only then is it multiplied by 
RND. Similarly, INT RND * 1000 is still not 
what we want (try it!), because the number following INT in 
this case is RND. Since RND has only a 
fractional part, INT RND is always zero. If you multiply 
that by 1000, it is still zero.
To exactly determine the order of operations, you need to use 
( and ), the so-called 
parentheses. What is between these is 
evaluated before what is outside of them. Hence, INT 
(1000*RND). This results in a random whole number that is at 
least zero and less than 1000, that is at most 999. But since we want a 
random number between 1 and 1000, 1 needs to be added to it. Actually, 
INT (RND*1000)+1 would work just the same. This is because 
the results of multiplication and addition do not change if we switch 
the order of the numbers to multiply or to add. Wizards call such 
operations commutative.
Line 30 contains a single keyword REPEAT. 
It means that what follows until the keyword UNTIL (see 
line 80) must be repeated. It must be repeated 
UNTIL the condition following that keyword becomes true. 
In our case, until the player's guess and the number 
picked by the computer (in line 10) become equal.
Now let's look to the four lines between REPEAT and 
UNTIL, which is what needs to be REPEATed.
Line 40 begins with the keyword INPUT. Its 
purpose is somewhat similar to that of LET in that it 
assigns values to variables, but, unlike LET, it reads the 
value from the console. The keyword INPUT is followed by a 
list consisting of things to write to the console and variable names 
which are to be assigned the values read from the console. These can be 
separated by comma (,, SYM+N), semicolon (;, 
SYM+O), or apostrophe (', SYM+7). The difference is where 
the next item is going to appear on the console: in case of a comma, it 
is going to be neatly tabulated to either the middle of the line, or, if 
there is no space for that, to the beginning of the next line, in case 
of a semicolon, it is going to appear right after the end of the 
previous item, while in case of an apostrophe, it is going to appear at 
the beginning of the next line. Try changing the separator between 
"Your guess?" and guess in line 
40 to see all this in action.
Lines 50 and 60 begin with an 
IF keyword followed by a condition, a THEN 
keyword and a PRINT statement. Such program lines execute 
the part after THEN only IF the condition following 
IF holds true.
Line 70 contains an unusual LET statement, 
a so-called update. It is, essentially, a 
shorthand for LET guesses=guesses+1. It requires less 
typing on the wizard's part and less interpreting on the computer's 
part.
Line 80 closes the loop 
started in line 30. It is called a loop, because if you 
draw little arrows from each statement to possible next statements, your 
arrows are going to form a loop; a sequence of statements to be 
(possibly) repeated.
Trivia: Loops that read the console and depending on what has been entered print something are called REPL by wizards, which stands for Read - Evaluate - Print - Loop. If you think about it, ZX BASIC itself is a REPL.
Lines 90 and 100 are just regular 
PRINT statements. Just like in INPUT the 
separators between things to be printed determine how they are going to be
positioned relative to one another on the canvas.
How many attempts do you need to guess the number without cheating? Let's switch sides with the ZX85 now and write a program that plays this same game as the guesser: the player thinks of a number and the computer tries to guess it.
10 PRINT "Think of a whole number between 1 and 1000."'' 20 LET low=0: LET high=1024 30 REPEAT 40 LET guess=(low+high)/2 50 PRINT "Is it ";guess;"?", 60 INPUT "0: Too high."'"1: Too low."'"2: Correct."'answer 70 IF answer=0 80 LET high=guess 90 PRINT "0: Too high." 100 ELSE IF answer=1 110 LET low=guess 120 PRINT "1: Too high." 130 ELSE IF answer=2 140 PRINT "2: It is ";guess;"." 150 STOP 160 ELSE 170 PRINT "I do not understand ";answer;"." 180 END IF 190 UNTIL high-low<2 200 PRINT "This cannot be."'low;" is too low, but ";high;" is too high."
You may have noted that the initial high number is 1024, rather than 1001.
The reason for this is to make sure that the division in line 40
always results in a whole number, since
1024 = 2 × 2 × 2 × 2 × 2 × 2 × 2 × 2 × 2 × 2
that is two multiplied by itself ten times or, as wizards say:
"two to the power of ten". It is written like 210.
Thus, 1024 can be divided by 2 ten times, which is, not entirely by lucky 
coincidence, the maximum number of guesses the computer needs to guess a 
whole number between 1 and 1000.
The operation of multiplying a number by itself (or taking 
powers in wizard-speak) is denoted by the upwards-pointing arrow 
(^, SYM+H) in ZX BASIC. Try PRINT 2^10.
Play this game a few times and note the sequences of answers you give to ZX85. It will always guess the number in no more than 10 questions and for any given number, the sequence of questions and answers will be the same. In fact, if you know that the number is somewhere between 1 and 1000, the answers you give already uniquely identify the number. Now let us modify the game a little bit. Change the following lines:
50 PRINT "Is it at least ";guess;"?";TAB 22; 60 INPUT "0: No, it is not."'"1: Yes, it is."'answer 90 PRINT "0: No." 120 PRINT "1: Yes." 200 PRINT "The number is ";low;"."
Then delete lines 130 through 150. You can
accomplish this by the command DELETE 130 TO 150. The new
keyword TAB in line 50 is similar to the 
comma, but it can tabulate to any position (22, in our case), not just
the middle of the line, which would be TAB 16.
Now you can RUN this "new game". It is now more 
similar to Twenty Questions in that every question is answered 
by either "yes" or "no". However, the computer will
always guess the number in exactly 10 questions. For example, 500 would be
guessed like this:
Is it at least 512? 0: No. Is it at least 256? 1: Yes. Is it at least 384? 1: Yes. Is it at least 448? 1: Yes. Is it at least 480? 1: Yes. Is it at least 496? 1: Yes. Is it at least 504? 0: No. Is it at least 500? 1: Yes. Is it at least 502? 0: No. Is it at least 501? 0: No. The number is 500.If you change the
1000 in line 10 to 1000000
(one million) and also high in line 20
from 1024 to 2^20 it will ask 20 questions before 
guessing the number. Any integer up to a million in just 20 yes/no
questions!
Trivia: The strategy of picking the middle element in some ordering, comparing it to what you are looking for and based on whether or not it is greater, continuing either with the lower or the upper half of the ordering is called binary search and it is the wizard's way of quickly finding needles in haystacks. It is a simple, yet very powerful strategy (or algorithm in wizard-speak) from a broader class of divide and conquer algorithms. You are going to learn many of those and maybe invent some of your own.
Our word "algorithm" is honoring an outstanding medieval wizard, Muhammad al-Khwarizmi, who invented or rigorously described several algorithms that we regulary use to this day.
 
The answers in this game do uniquely identify the 
integer and you do not even need to know how large it can be, as that 
can be learned from the number of answers. It is called 
binary representation and this is how 
computers represent numbers internally. In ZX Basic, you can use this 
representation using the BIN keyword. For example, you can 
write BIN 0111110100 instead of 500. Moreover, 
you can omit the leading zeroes, so BIN 111110100 also 
means 500.
In general, everything is a sequence of zeroes 
and ones for the computer. Just like in the game above, it is common 
practice to represent "yes" by 1 and 
"no" by 0. ZX85 actually does it all the time. 
For example, ask it whether two is less than one by typing PRINT 
2<1. It will answer 0 by which it means 
"No.". But to PRINT 2>1 or to 
PRINT 2+2=4 it will answer 1 by which it means 
"Yes.". Anywhere where you can write answers to yes/no questions,
you can also write numeric expressions, where those evaluating to zero
will be taken as a "no", everything else as a "yes".
For example, IF 0 THEN PRINT "zero" will not output anything,
but IF 1 THEN PRINT "one" will output one to
the canvas, and so will IF 5 THEN PRINT "one".
Binary numbers are very important in wizardry, or as wizards call it, 
computer science. Those wishing to become 
wizards better learn to use them as second nature. For example, you 
should get into the habit of counting on your fingers in binary. Thus, 
instead of just 5, you will be able to count up to 31 on the fingers of 
one hand (be careful with showing number 4 to other people; muggles 
might misunderstand you). Each digit (which is 
just Latin for "finger") in a binary number corresponds to a 
power of 2. Your thumb corresponds to 1 (which is 20), your 
index finger corresponds to 2 (21), your middle finger to 4 
(22), your ring finger to 8 (23) and your pinky to 
16 (24). For example, if you want to show 7, you only extend 
your thumb, your index finger and your middle finger, becase 1 + 2 + 4 = 
7 (try PRINT BIN 00111). An extended thumb, index finger 
and pinky means 19 (which is 1 + 2 + 16, BIN 10011). In 
general, if you want to find out the binary representation of a number, 
you can either play the game above, or use another keyword, 
STR$. For example, PRINT STR$ (30,2) will be 
answered with 11110 (try it!), meaning all except your 
thumb extended. STR$ is used to obtain various 
representations of numbers, with 2 meaning binary. The 
parentheses are needed, because otherwise STR$ will only 
concern itself with 30, the comma would mean tabulation to 
the middle of the line and 2 would mean 2. Try!
Trivia: While all actual computers used today by humans are binary, it is not the only reasonable choice for digital computers. The reason humanity ended up using binary computers exclusively is that there are tremendous benefits in all computers being based on the same logic so that many things only need to be done once and used everywhere. The other reasonable choice for digital computers is ternary, which is based around the powers of 3 rather than two. The three kinds of digits there are -1, 0 and +1, meaning "no", "unknown" and "yes". Alien computers may very well be ternary. In fact, humanity has also explored ternary computing, an effort culminating in the serial production of a ternary computer named Setun' after a creek flowing through the campus of Lomonosov University in Moscow, Russia. But by the time the details were worked out to the point of practicality, binary computers were so widespread that it did not make much sense to continue.
However, this choice does not matter all that much. All digital computers can "pretend to be" (wizards say emulate) any other digital computer, so the (amazingly broad and to this day largely unexplored) set of problems that can be solved by digital computers does not depend on this choice. This universality (called Turing-completeness in his honor) was discovered and proven by an outstanding British wizard in the middle of the twentieth century, a pioneer of electronic computing, Alan Turing.
 
Now is the time to write the first game that is actually a lot of fun to play, to the point of being mildly addictive. The author readily admits to spending most of a twelve-hour trans-pacific flight playing this game on the onboard entertainment system. Playing this game will also help you memorize the powers of two. This game called 2048 was designed by an Italian wizard, Gabriele Cirulli in 2014.
The game is played on a 4×4 grid, starting out empty. Every turn, a new tile with a value 2 or 4 will appear in one of the empty spots. The player can slide the tiles with the arrow keys. Each tile slides until it hits another tile or the edge of the grid. When two tiles with the same number collide, they will merge into a tile carrying their total value. The resulting tile will not merge in the same move. The game is won when a tile with a value of 2048 appears. We can clear the board and restart the game any time pressing the DELETE key.
We will add score- and time-keeping, colorful graphics and music to our game later, first we write the most important parts of the game so that it can be played. Let's begin with drawing the grid:
  10 LET b$="+----"*4+"+"+CHR$ 13
  20 PRINT (b$+("|    "*4+"|"+CHR$ 13)*4)*4+b$
There are several new things here. The variable name b$ 
ends with $ pronounced as string, 
making b$ a string variable. The 
value it holds is a string, rather than a number. Strings are sequences 
of characters, which can be letters, digits, 
symbols, tokens or control 
characters. In fact, the quoted texts in previous examples 
are all strings. In the assignment of b$ in line 
10, there is a string expression. The 
+ between two strings results in a new string, consisting 
of the lefthand string immediately followed by the righthand string. For 
example "Hello"+"World!" would result in 
HelloWorld!. Try it! If there is a * between a 
string and a number (or a string expression and a numeric expression), 
it repeats the lefthand string the righthand number of times. For 
example, "!"*3 results in !!!.
There is also a new keyword, CHR$. Keywords ending with 
$ are string valued functions 
that turn their arguments into strings. In particular CHR$ 
turns the number following it into one character with that particular 
character code, if that number is between 0 and 255. Character 
codes between 0 and 23 are control characters. 
Among them, CHR$ 13 is known under a number of names: 
Carriage Return, CR, Enter, New Line, Return, but they all mean 
the same thing. It indicates the end of a line and a beginnig of a new 
line.
Thus, line 10 assings the following string to b$:
+----+----+----+----+You can verify this by entering
PRINT b$.
Note: The program would work exactly the same, if we 
changed line 10 to this:
10 LET b$="+----+----+----+----+"+CHR$ 13However, that would take more time to type and would take up more memory of the computer. By typing a long and repetitive line like that the wizard does something that a computer can do better and faster. Why not let the computer do all this tedious work?
Line 20 uses b$ to draw the grid using similar 
techniques. By now, you know enough to understand every detail of it. Of 
course, instead of those two lines, one could naively write a sequence of 
PRINT statements:
1 PRINT "+----+----+----+----+" 2 PRINT "| | | | |" 3 PRINT "| | | | |" 4 PRINT "| | | | |" 5 PRINT "| | | | |" 6 PRINT "+----+----+----+----+" 7 PRINT "| | | | |" 8 PRINT "| | | | |" 9 PRINT "| | | | |" 10 PRINT "| | | | |" 11 PRINT "+----+----+----+----+" 12 PRINT "| | | | |" 13 PRINT "| | | | |" 14 PRINT "| | | | |" 15 PRINT "| | | | |" 16 PRINT "+----+----+----+----+" 17 PRINT "| | | | |" 18 PRINT "| | | | |" 19 PRINT "| | | | |" 20 PRINT "| | | | |" 21 PRINT "+----+----+----+----+"But typing all that would be a huge waste of the wizard's time and the computer's memory. There are computer programs having such repetitive patterns in them and there are legitimate justifications for writing software like this, but these are typically written by another computer program. In a later chapter, we shall see how to write computer programs writing computer programs.
30 DIM b(4,4)
The DIM keyword allocates an 
array. In this case, b is a 
numeric array, a collection of numeric 
variables. The numbers between parentheses following the array's name 
are called dimensions. Each element in the 
array is a variable that can be assigned a value. Initially, all of them 
are zero. One way to think about DIM is that it creates as 
many variables as the product (which is just 
wizard-speak for multiplication) of all its dimensions, in our case 16. 
Each variable's name is the name of the array, followed by a list of 
subscripts in parentheses, separated by 
commas. Each subscript is a number between 1 and the corresponding 
dimension. So, for example, b(3,2) is one of these 
variables, but b(1,1,1) or b(5,2) are not, if 
b was allocated according to line 30. Try it! 
If you use wrong subscripts, ZX85 will report
3 Subscript wrong, an error.
The benefit of arrays compared to just naming variables, say, 
b11 or b32 is that subscripts can be numeric
expressions. That is the choice of which one to access or assign can
depend on other variables. A typical example when arrays are useful is when
the same operation needs to be performed with many variables. Without arrays,
one needs to write out the same operation for every variable. This is tedious
work not worth a wizard's time. Instead, you can (and should) use an array
and write a loop and make the computer perform the same operation on various
elements in the array. The wizard only needs to spell it out once. You will
see many examples of this in the game.
After drawing the board on the screen and allocating it in the 
computer's memory (as array b will hold the 
state of the board). we are ready to write the 
main loop of the game. Just like in the 
previous number guessing games, the main loop describes what happens in 
each turn of the game. Unlike the previous games, the main loop here is 
not a REPL, as the game only uses the canvas, not the console.
40 REPEAT 50 PROC drop() 60 REPEAT 70 LET action=0 80 REPEAT 90 LET k=CODE INKEY$ 100 UNTIL k>=8 AND k<=12 110 IF k=8 THEN PROC tilt(1,1,0,1) 120 ELSE IF k=9 THEN PROC tilt(4,4,0,-1) 130 ELSE IF k=10 THEN PROC tilt(4,1,-1,0) 140 ELSE IF k=11 THEN PROC tilt(1,4,1,0) 150 UNTIL action OR k=12 160 PROC board() 170 UNTIL k=12 180 RUN
In line 50, there is a new and very important keyword: 
PROC. It calls (or invokes) a 
procedure. A procedure is a program that has a name and can be
executed from another program (or itself). In this case, the name is
drop and the empty parentheses after the name indicate 
that it has no parameters. As its name might suggest, it drops a
tile with 2 or 4 on it onto a random empty cell of the grid. How
it does it is described later in the program.
The most important role of procedures is that it allows the wizard not to write the same sequence of statements in multiple places in the program. As you might have noticed, this kind of laziness is a cherished trait of wizards.
But drop is not called from anywhere else in our game. 
In line 50 we use a procedure for a different purpose. It 
has to do with making the code more understandable for other wizards, 
which very much includes ourselves some time later. A wise wizard 
acknowledges and works around the limitations of the human mind, 
including ones own. One such limitation is that we have a difficulty 
thinking on different levels of detail at the same time and even more 
difficulty following someone else's thoughts if they are doing that. It 
is important to keep in mind that even a mere two weeks from now you 
will be a different person. A very typical source of frustration with 
poor wizardry is trying to understand our own code written a long time 
ago. In the main loop of our game, we spell out the rules of the game 
and should not get into the minute details of placing a tile at a random 
empty location. There are no strict rules about what parts of code to 
place in separate procedures; it is an art, mastered with practice. 
However, if you find that it took you too much effort understading part 
of a program because it worked on a lower level of detail than 
its surrounding, do not hesitate to move it out into a procedure and 
give it a name that describes what it does, not getting into 
the details of how it is done. Such re-arranging of the code is 
called refactoring and even though it takes 
some effort, it saves a lot of time and nerves down the road. Avoiding 
it is the wrong kind of laziness.
The loop between lines 60 and 150 keeps 
running until something actually happens on the board. This is captured 
in the variable action assigned in line 70 
meaning no action and perhaps changed in one of the 
moves in lines 110 through 140. 
This is a loop within a loop, or as wizards call it, a 
nested loop.
In turn, it has another loop nested in it between lines 
80 and 100. In its 
body, it has a single LET 
statement with two new keywords. CODE is the reverse of 
CHR$. It takes a string and, if it is a single character, 
turns it into its character code, which is a number. The 
CODE of an empty string is zero. 
Try PRINT CODE "". The keyword 
INKEY$ means the key pressed, or an empty string, if no key 
is pressed. As the $ at its end suggests, it is a string. 
Together, CODE INKEY$ means the character code of the key 
that is pressed, or zero if there is none. You can try REPEAT: 
PRINT AT 0,0;CODE INKEY$,:UNTIL 0 to see what code corresponds to 
which key on the keyboard. Character codes 8 through 11 correspond to 
the arrow keys, code 12 corresponds to DELETE. Hence, line 
100 repeats the loop UNTIL one of them is 
pressed. If it is DELETE, the outer loops also quit.
Lines 110 through 140 are there to 
move the tiles in the direction of the arrow key pressed.
Note: If you felt a bit of tedium while typing them in, 
it is a good sign, as this is somewhat unwizardly code, worthy of 
refactoring. It works correctly, but the parameters of move 
have to do with how to move the tiles in different directions, 
even though at this level we are supposed to be thinking about 
what needs to be done, not preoccupied with the minute details 
of how. So let's refactor it! These four lines express that the 
tiles need to be moved in the direction determined by the key pressed. 
We can replace it with a statement that expresses it more clearly:
DELETE 110 TO 140 110 IF k<>12 THEN PROC tilt(k-8)
The <> sign means not 
equal. This change will require further changes in the 
definition of PROC move beginning 
with line 310, which are discussed below it.
Line 160 redraws the board with all the tiles moved. The 
reason why redrawing is not part of PROC move is that we 
only want to redraw the board if some tiles have actually moved. Since the 
loop above this line keeps repeating until it happens, at this point we 
can be certain that at least one tile moved. The main loop continues until
the DELETE key is pressed, at which point the entire program is 
RUN again.
Now it is time to flesh out the procedures called from the main loop. The 
definitions of procedures begin with the @ label marker,
followed by the name of the procedure and its parameters. The definition
ends with the END PROC keyword.
190 @drop() 200 LOCAL x,y,k 210 FOR i=1 TO 4: FOR j=1 TO 4 220 IF NOT b(i,j) 230 LET k+=1 240 IF RND<1/k THEN LET x=i: LET y=j 250 END IF 260 NEXT : NEXT 270 LET k=1+(RND<.5) 280 LET b(x,y)=k 290 PROC tile(x,y,k) 300 END PROC
The drop procedure has no parameters. In line 
200 we declare local variables. 
These variable names are used by the procedure but we do not want the 
procedure to interfere with the rest of the program, if it happens to 
use the same variable names for different purposes. For example, in the 
main loop we use k for the code of the key pressed, but 
inside drop we use it as a counter of empty cells in the 
grid and as the random value of the tile dropped. It is always a good 
idea to declare every variable that we intend 
to use inside a procedure as LOCAL, as it will reduce the 
possibility of unintended side effects of
our procedure. Also, since local variables are considered first when 
looking up a variable by name, this practice actually speeds up
the execution of our programs. Unless stated otherwise in the declaration,
local numeric variables are initialized to zero and local string variables
to the empty string.
Lines 210 through 260 describe two
nested FOR loops. These are loops that need to be executed
a given number of times (4, in our case). The FOR keyword 
is followed by the name of the loop variable,
that is going to take a different value during each execution (or
iteration) of the loop. These values are
determined by what follows the = sign: 1 TO 4
means that it will be first assigned 1, then 2 and so on until the last
iteration in which it will be assigned 4. Thus, variables i
and j will be assigned all 16 possible combinations.
Try FOR i=1 TO 4:FOR j=1 TO 4:PRINT "i=";i,"j=";j:NEXT:NEXT
to get a better understanding of what goes on here.
Note: The loop variable is always local to the loop. The following command prints numbers from 1 to 5:
FOR i=1 TO 5: PRINT i: NEXT
Afterwards, however, PRINT i results in a
2 Variable not found, 0:1report, because
i is accessible only inside the loop.
There are no side effects even when nesting two loops with the same loop variable name. Try this:
FOR i=1 TO 10: FOR i=1 TO 10: PRINT "*";: NEXT: PRINT: NEXT
It will output a grid of 100 stars to the console:
********** ********** ********** ********** ********** ********** ********** ********** ********** **********
Lines 220 and 250 mean that the entire body 
of these two nested loops is conditioned on b(i,j) being 
empty. An IF statement without a THEN keyword 
after the condition continues with the next statement, if the condition 
is true. If it is false, absent of an ELSE keyword,
it continues after the matching END IF keyword. In this case,
lines 230 and 240 are only executed if
NOT b(i,j) in line 220 is true, that is
if b(i,j) equals zero. This also shows the power of arrays,
as we can test 16 variables in a single loop with a handful of statements.
Line 230 counts empty cells in the grid in variable 
k. Line 240 replaces the values of 
x and y with the current values of 
i and j if with a probability 
1/k. In other words, at random, on average 1 out of 
k times, those values are replaced. At the first empty 
cell, when k=1, they are replaced with certainty, as 
RND is always less than one. When the loops have run their 
course, x and y contain the subscripts of one 
of the empty cells chosen randomly and uniformly from among all the empty
cells.
At this point, k contains the number of empty cells, but 
we do not want to do anything with that number. So, in line 
270 we reuse k for a different purpose: it 
becomes either 1 or 2 with equal probability. It means which power of 
two the new tile will be: 21 (2) or 22 (4). In 
lines 280 and 290 this value is used to assign 
the corresponding element of array 
b and display the corresponding tile on the canvas. 
PROC tile is defined later, beginning with line 
580, as the details of drawing clearly do not belong to 
the procedure of random placement of a new tile.
310 @tilt(i,j,x,y) 320 FOR n=1 TO 4 330 PROC slide(i,j,x,y) 340 LET i+=y: LET j-=x 350 NEXT 360 END PROC
The procedure move takes four parameters: the subscripts 
of one corner of the board and the numbers by which they are changed in 
each of the four iterations, as tile sliding and merging is calculated 
for all four rows or columns. The local 
variables to which parameter values are assigned are listed in the 
parentheses after the procedure name in the DEF PROC 
statement. How tile sliding is done is defined in procedure 
slide, which is called in line 330. Since 
parameter variables are local to the procedure, there are no side 
effects from updating i and j in line 
340.
Note: As noted in the discussion of the main loop,
it is better if the job of setting parameters i, j,
x and y depending on the direction of movement
would be the job of procedure move rather than the main loop.
The corresponding refactoring of procedure move would be
as follows:
310 @tilt(k) 315 LOCAL i=k?(1,4,4,1),j=k?(1,4,1,4),x=k?(0,0,-1,1),y=k?(1,-1,0,0)
Thus, PROC move takes a single parameter, the direction of
the movement, a number between 0 and 3. In line
315 the former parameters are declared as local variables 
and initialized with the appropriate values. The question mark ?
operator is called selector and it picks 
the corresponding value from the list in the following parentheses. In order
to be able to use conditions before the question mark, the first value is 0.
For any value greater than the number of expressions in parentheses, the 
last one is used, though this can never happen in this program.
370 @slide(i,j,x,y) 380 LOCAL k=i,l=j 390 FOR n=1 TO 3 400 LET i+=x: LET j+=y 410 IF b(i,j) 420 WHILE b(k,l) AND b(k,l)<>b(i,j) 430 LET k+=x: LET l+=y 440 END WHILE 450 IF NOT b(k,l) 460 LET b(k,l)=b(i,j) 470 LET b(i,j)=0 480 LET action=1 490 ELSE IF b(k,l)=b(i,j) AND (i<>k OR j<>l) 500 LET b(k,l)+=1 510 LET k+=x: LET l+=y 520 LET b(i,j)=0 530 LET action=1 540 END IF 550 END IF 560 NEXT 570 END PROC
The parameters of procedure slide are subscripts 
i and j of the first cell of the row or column, 
the one toward which tiles are going to slide. Parameters
x and y are the values that need to be added to
i and j, respectively, to point to the next
cell of the grid (array b).
The procedure also has a new type of loop in it, the WHILE 
loop between lines 420 and 440. The condition 
following WHILE is evaluated before each iteration 
of the loop. If it is false (that is zero), then execution 
continues after the END WHILE statement. 
Otherwise, the body of the loop is executed and the condition checked 
again. 
Note: Both WHILE/END WHILE 
and REPEAT/UNTIL loops depend on a condition. 
The main difference is that REPEAT/UNTIL loops 
execute at least once. WHILE loops are not 
executed at all, if their condition is false in the beginning. 
Another difference is that the condition after WHILE must 
be non-zero but the condition after UNTIL must be zero for
the loop's body to be executed one more time.
But there is also a subtler difference. The condition after 
WHILE is evaluated in the enclosing 
context, meaning that variables local to the loop are not 
accessible there. By contrast, the condition after UNTIL is 
evaluated in the local context of the loop 
itself, allowing for using variables local to the loop.
580 @board() 590 FOR i=1 TO 4: FOR j=1 TO 4 600 PROC tile(i,j,b(i,j)) 610 NEXT : NEXT 620 END PROC
The board procedure simply iterates through all the cells of
the board and calls PROC tile to draw the tiles and empty
cells, as the case might be.
 630 @tile(x,y,k)
 640  LOCAL k$=k?("",STR$ (2^k))
 650  LET k$=" "*(4-LEN k$)+k$
 660  PRINT AT x*5-2,y*5-4;k$
 670 END PROC 
This last procedure is used to draw one empty cell or a tile corresponding
to subscripts x and y. The keyword LEN
means the length of the string after it. Line 650 makes sure
that k$ is always exactly 4 characters, with the number on the 
tile at the end of the string.
When we are going to change the graphics of the game to look prettier, have colors and so on, most changes go into this procedure. This first version is very rudimentary, but it is (barely) enough to make the game playable.