IF SIN(X)>0.499999 AND SIN(X)<0.500001
X<0 SGN (X)=-1 X=0 SGN (X)=0 X>0 SGN (X)=1
Thus SGN(X)* ABS(X)=X.
X>0 for LOG and LN
X<=88 for EXP
Outside these ranges, the explicit error messages 'Log range' and 'Exp range' occur.
13 DIV 3
gives
4
13 MOD 3
gives
1
Clearly B%*(A% DIV B%)+A% MOD B% = A%
PRINT 3*(13 DIV 3)+13 MOD 3
10 INPUT "Type any vaLue (1 to 13) for the seed: ",SEED 20 CONST=19 30 DVSR=13 40 FOR J=1 TO 15 50 SEED=(CONST*SEED) MOD DVSR 60 PRINT SEED 70 NEXT J
The choice of 13 for the divisor gives 12 possible numbers, which as you will see come fairly randomly. The sequence then starts to repeat. The 'seed' simply decides at which point you join the repeating sequence.
If you want numbers between 0 and 1, this can simply be achieved by dividing the seed by 13.
With much larger prime numbers than 19 and 13, large sequences of random numbers can be produced. Try for example 4637 in place of 19 and 2546887 in place of 13 and replace line 50 by
50 SEED=CONST*SEED-INT(CONST*SEED/DVSR)*DVSR
since MOD will no longer work for such large numbers.
There are two alternative possibilities that you might need when using a random number generator. On the one hand, when using a program repeatedly, or developing a new program, you might want to use the same sequence each time, so that you 'know' the answer or can check other factors in your program. On the other hand, you may want to be sure of getting a different sequence each time, as in the card game we shall look at later.
BBC BASIC has a random number generator called by the function RND(X). This function has a number of different modes, depending on the value of X supplied. (As so often, X should be an integer, and real numbers are truncated.) These are summarized as follows
X absent
Random integer number between +232 and -232
X>1
Random integer number between 1 and X
X=1
Random fractional number between 0 and 1
X=0
Repeats the last number generated by RND if it was RND(1)
X<0
Returns the value X and reseeds the generator with a seed based on X
This choice caters for almost all eventualities. An effectively infinite range of numbers can be produced by RND or RND(1), depending on whether you want integer or fractional numbers. (To alter the range of real numbers, say to 0 to 100, use 100*RND(1). RND(100) would give an integer between 1 and 100.)
A random value from a set of integers up to N can be produced by RND(N). Thus to simulate the throw of a dice, you could use RND(6).
To get a repeatable sequence of numbers, start your program with X=RND(-1) (or any negative value which takes your fancy). To get a guaranteed non-repeatable sequence needs just a little more effort, since the generator always starts with the same seed when first switched on. The easiest solution is to reseed with a random negative number, which can quite simply be produced from TIME, which is busily ticking away 100 times a second.
10 CLS 20 X=RND(-TIME): REM RANDOMIZE THE START POINT 30 INPUT "Number of throws: ",N 40 PRINT 50 FOR J=1 TO N 60 T1=RND(6): T2=RND(6) 70 PRINT "Thows are ";T1;" and ";T2;". Totat ";T1+T2 80 NEXT J
10 CLS 20 INPUT "Hw many numbers are to be tested? ",N 30 X=RND(--TIME) 40 SUM=0: SUMDEVSQ=0 50 FOR J=1 TO N 60 X=RND(1) 70 SUM=SUM+X 80 SUMDEVSQ=SUMDEVSQ+(0.5-X)*(0.5-X) 90 NEXT J 100 PRINT '"Mean is ";SUMIN;" (nominal 0.5)" 110 PRINT '"Standard deviation is ";SQR(SUMDEVSQ/ (N*(N-1)))' "(nominal ";SQR(0.08333/(N-1)); ")"
10 CLS 20 DATA ACE,TWO,THREE,FOUR,FIVE,SIX,SEVEN 30 DATA EIGHT,NINE,TEN,JACK,QUEEN,KING 40 X=RND(-TIME) 50 DIM CARD$(13),SCORE(2),PONTOON(2),ACE(2),X(10),Y(10) 60 FOR J=1 TO 13: READ CARD$(J): NEXT J 70 WINNINGS=0 80 PLAYER=1 90 SCORE(PLAYER)=0: PONTOON(PLAYER)=0: ACE(PLAYER)=0 100 WHO$="Your": WHICH$=" first" 110 N=1: PROC_deal(1): PROC_result(WHO$,WHICH$,CARD$(X(N)), PONTOON(PLAYER),SCORE(PLAYER),ACE(PLAYER)) 120 INPUT "Place your bet, in pence: ",BET 130 WHICH$=" second" 140 N=2: PROC_deal(2): PROC_result(WHO$,WHICH$,CARD$(X(N)), PONTOON(PLAYER),SCORE(PLAYER),ACE(PLAYER)) 150 IF PONTOON(PLAYER)=0 THEN PROC_twist 160 IF SCORE(PLAYER)>21 AND PONTOON(PLAYER)=0 THEN PRINT "BUST": WIN=-1: GOTO 290 170 IF ACE(PLAYER)>0 AND SCORE (PLAYER)<-11 THEN SCORE(PLAYER)=SCORE(PLAYER)+10 180 IF N>=5 THEN PRINT "FIVE CARDS": SCORE(PLAYER)=40 190 PLAYER=2: SCORE(PLAYER)=0: PONTOON(PLAYER)=0:ACE(PLAYER)=0 200 WHO$="My": WHICH$=" first" 210 N=1: PROC_deal(1): PROC_result(WHO$,WHICH$,CARD$(X(N)), PONTOON(PLAYER),SCORE(PLAYER),ACE(PLAYER)) 220 WHICH$=" second" 230 N=2: PROC_deal(2): PROC_result(WHO$,WHICH$,CARD$(X(N)), PONTOON(PLAYER),SCORE(PLAYER),ACE(PLAYER)) 240 IF PONTOON(PLAYER)=0 THEN PROC_dealer 250 IF SCORE(PLAYER)>21 AND PONTOON(PLAYER)=0 THEN PRINT "BUST": WIN=1: GOTO 290 260 IF ACE(PLAYER)>0 AND SCORE(PLAYER)<=11 THEN SCORE(PLAYER)=SCORE(PLAYER)+10 270 IF N>=5 THEN PRINT "FIVE CARDS": SCORE(PLAYER)=40 280 IF SCORE(2)>=SCORE(1) THEN WIN=-1: ELSE WIN=1 290 IF WIN=ASN1 THEN PRINT "YOU LOSE": ELSE PRINT "YOU WIN" 300 WINNINGS=WINNINGS+BET*WIN 310 IF WINNINGS<0 THEN PRINT "You owe me ";ELSE PRINT "I owe you "; 320 PRINT ABS(WINNINGS);" pence" 330 INPUT "Would you like to play again (Y/N)",A$ 340 IF A$="Y" THEN GOTO 80 350 IF WINNINGS<0 THEN PRINT "Please pass my winnings into the grill" 360 END 1000 DEF PROC_deal(N) 1010 X(N)=RND(13) 1020 IF X(N)=1 THEN ACE(PLAYER)=N 1030 Y(N)=X(N): IF Y(N)>10 THEN Y(N)=10 1040 SCORE(PLAYER)=SCORE(PLAYER)+Y(N) 1050 IF ACE(PLAYER)>0 AND N=2 AND SCORE(PLAYER)=11 THEN PONTOON(PLAYER)=1: SCORE(PLAYER)=30 1060 REM PONTOON 1070 ENDPROC 2000 DEF PROC_result(WHO$,WHICH$,CARD$,PONTOONFLAG,SCORE,ACEFLAG) 2010 PRINT 'WHO$;WHICH$;" card is: ";CARD$ 2020 IF PONTOONFLAG>0 THEN PRINT "PONTOON": ENDPROC 2030 IF N>1 THEN PROC_score(SCORE,ACEFLAG) 2040 ENDPROC 3000 DEF PROC_score(SCORE,ACEFLAG) 3010 PRINT "Total: ";SCORE; 3020 IF ACEFLAG>0 THEN PRINT " or ";SCORE+10; 3030 PRINT 3040 ENDPROC 4000 DEF PROC_twist 4010 INPUT "Twist or stick (T/S)",A$ 4020 IF A$="S" THEN ENDPROC 4030 N=N+1: PROC_deal(N) 4040 PRINT '"Your next card is: ",CARD$(X(N)) 4050 IF SCORE(PLAYER)>21 THEN ENDPROC 4060 PROC_score(SCORE(PLAYER),ACE(PLAYER)) 4070 PROC_twist 4080 ENDPROC 5000 DEF PROC_dealer 5010 IF SCORE(PLAYER)>17 OR N>=5 OR (ACE(PLAYER)>0 AND (SCORE(PLAYER)<12 AND SCORE(PLAYER)>9)) THEN ENDPROC 5020 PRINT "I will twist" 5030 N=N+1 5040 PROC_deal(N) 5050 PRINT '"My next card is: ",CARD$(X(N)) 5060 IF SCORE(PLAYER)>21 THEN ENDPROC 5070 PROC_score(SCORE(PLAYER),ACE(PLAYER)) 5080 PROC_dealer 5090 ENDPROC
Note the use of recursion in the procedures PROC_twist and PROC_dealer. Most of the procedures have parameters because this is good practice, but it does make the program slightly longer.
IF <Boolean expression> THEN...
On the BBC microcomputer, the Boolean values TRUE and FALSE actually exist as pseudo-variables (they can be read, but not altered). Thus it is quite legal (though rather pointless) to write
IF TRUE THEN...
In this case the statements following THEN would always be obeyed. A more useful application is with REPEAT...UNTIL, where again a Boolean is evaluated.
REPEAT...UNTIL FALSE
will create an endless loop, which can sometimes be useful. A loop that is to be repeated until a certain condition is fulfilled midway through the loop could have the structure
FOUND= FALSE REPEAT ... ... ... IF <condition> THEN FOUND=TRUE ... ... ... UNTIL FOUND
Just as with arithmetic, we can have Boolean expressions as well as simple Booleans. There is a complete algebra for Boolean expressions. We will study this in full after reviewing the types of conditional test that are possible.
> | greater than |
< | less than |
= | equals |
<> | not equals |
>= | greater than or equals |
<= | less than or equals |
LET <variable>=<expression>
The LET is almost invariably omitted, but it does serve to emphasize that this is an assignment.
The second use of the equals sign is in a relational test. The computer finds no ambiguity in this, since it will treat = as an assignment where it is logical to do so, and a relational test otherwise.
Actually, a test for equality is not always a good idea, as was pointed out earlier when discussing functions. Never test real variables for equality if either has been evaluated in an expression, rather than having a directly assigned value (as the control variable of a FOR loop, for example).
PRINT (1=2) PRINT 15*(7<>3) PRINT 7+(9<=10)
The fact that BASIC treats TRUE and FALSE as numbers means that it is also possible to write IF statements of the form
IF A% THEN...
A% will evaluate as TRUE if it has any value other than zero. Thus the above is a shorter equivalent to
IF A%<>0 THEN...
(Beware, however, that IF NOT A%... is not equivalent to IF A%=0...)
Check out these statements by lines such as
IF 10 THEN PRINT "TRUE" ELSE PRINT "FALSE"
The fact that a relational expression takes a value can be utilized to advantage to produce more compact programs and avoid IF...THEN statements.
For example, suppose we want to set a bus fare to 10p, 50p or 25p depending on whether the age of the traveller is under 15, adult or over 65. The obvious way to do this is
100 FARE=10 110 IF AGE>=15 THEN FARE=50 120 IF AGE>=65 THEN FARE=25
However, since a relational expression is -1 if TRUE and 0 if FALSE, this can be written much more compactly (though at the expense of clarity) as
100 FARE=10-40*(AGE>=15)+25*(AGE>=65)
Relational operators work with strings and string variables as well as numbers. The meaning of = and <> is fairly obvious, and useful in situations such as YES/NO tests. For example
100 INPUT "Do you want to continue? ",YN$ 110 IF YN$="YES" THEN PR0C_continue ELSE IF YN$<>"NO" THEN GOTO 100
Testing for inequalities is also possible, however, and carries out an alphabetical (or more strictly, lexicographical) test, ordering strings as they would appear in a dictionary. Thus
"FRED" is less than "JIM"
"BET" is less than "BETTER"
"10" is less than "20" (but "20" is less than "3")
Actually, what is done is a successive comparison of the ASCII values of the characters until a difference (if any) is found.
A AND B
is TRUE if A and B are both TRUE
A OR B
is TRUE if either A or B is TRUE
A EOR B
is TRUE if either A or B but not both is TRUE
NOT is rather different, being a unary operator which reverses the value of a relationship. (A unary operator is one which operates on a single quantity; the others are binary operators, and act on two quantities.) Thus
NOT TRUE is FALSE
NOT FALSE is TRUE
() NOT
(and + - as unary operators, as in 7*-6)
^ * / > < <> >= <= = AND OR EOR
As with simple conditional tests, the Boolean operators find their most obvious use in IF statements. A pair of tests might be combined with a Boolean operator, for instance
IF X<1 OR X>4 THEN PRINT "Error"
or
IF NOT(R$="Y" OR R$="N") THEN PRINT "Error"
NOT - 1 =0 NOT 0=-1 0 AND -1=-=0 -1 AND -1=-1 0 OR -1=-1 0 OR 0=0 0 EOR -1=-1 -1 EOR -1=0
1.
X=NOT((2+5)>7
PRINT X
gives -1 (TRUE)
2.
Z=NOT((3+2)>=4)
PRINT Z
gives 0 (FALSE)
3.
Y=6=3 AND (2<>3 OR 1<2)
PRINT Y
gives 0 (FALSE)
Try example 3 again without the brackets, and then with brackets around 6=3 AND 2<>3.
Note that Y=6=3 makes perfectly good sense to the BBC microcomputer, since 6=3 is evaluated first as a relational expression.
The most useful application of Boolean expressions is within the IF...THEN statement, which tests the truth of a Boolean expression.
For example, examine line 5010 of the pontoon program, Example 5.4, where the computer decides whether to twist. Note also the use of brackets to ensure the desired logic.
10 INPUT "Score ",SC 20 INPUT "Ace ",ACE 30 INPUT "N",N 40 IF SC>17 OR N>=5 OR (ACE>0 AND (SC<12 AND SC>9)) THEN PRINT "STICK" ELSE PRINT "TWIST"
Run this using different values of Score, Ace and N to check the logic of the relationship.
011 OR 110
gives 111, or decimal 7.
127 AND 159, or in binary
01111111 AND 10011111
gives 00011111, or decimal 31.
21 EOR 24, or in binary
10101 EOR 11000
gives 01101, or decimal 13.
NOT gives a full 32-bit complement of a number, so
NOT 111 gives 111...11000
Readers familiar with the two's complement representation of signed numbers will recognize this as -8 (that is, NOT 7=-8).
This also explains the apparently curious use of -1, rather than 1, for TRUE. TRUE is NOT FALSE, and to 32 bits,
NOT 0=111...11111
which in two's complement is -1.
PRINT 3 AND 6 PRINT NOT 7