Lesson 10: Math Functions

This lesson is going to assume that you understand the difference between the Decimal (BASE10), Binary (BASE2), and Hexadecimal (BASE16) numbering systems. If you do not or feel you need a refresher I highly suggest you view the side-lesson on computer numbering systems before continuing with this lesson.

View the side-lesson on Computer Numbering Systems

Bitwise Math and Boolean Logic

George Boole was a 19th century mathematician that devised a new form of mathematics based on logic and is often considered the founder of the field of computer science. Today we use his insights into logic, called Boolean logic, to describe the interactions between binary numbers and electronic states. Boolean logic is a perfect fit in computing because it can have only two states, true and false, and therefore only two outcomes, true or false. By equating the two digits of binary to the these states, 0 (zero) for false and 1 (one) for true, we can use George Boole's logic to perform what is known as bitwise math.

Bitwise math consists of operations that act on numbers at the bit level. Bitwise operations are often used for storing useful information and manipulating numbers at the bit level through the use of logic gates. Logic gates are used to sample bits, or inputs, and derive a single output in bit form. There are a number of logic gate operators available in QB64 that allow the programmer to manipulate numbers at the bit, or binary, level.

The AND Statement

The AND statement is a logical numerical operator that compares two values at the bit level. If two corresponding bits are set to 1, or true, the result of the operation becomes true, anything else will result in false. Logical operators can be described using truth tables. The truth table for the AND logical operator is shown in Figure 3 below.

Figure 3: AND truth table

The only time the AND logical operator will return a value of 1, or true, is if both inputs being tested are 1 or true. The truth table equates to:

0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1

Logical operators also work with zero (false) and non-zero (true) values. A logical operator can be used in the following manner:

IF Number% = 10 AND User$ = "Admin" THEN

The tests on either side of AND will result in either true or false. If both tests result in true then the IF...THEN statement will proceed. Logical operators also work at the bit level on numeric values. Type the following three lines of code in then execute it.

b1% = 255
b2% = 64
PRINT b1% AND b2%

The value of 64 should have been printed, but why? Logical operators look at numeric values in binary. The decimal number 255 when converted to binary is 11111111. The decimal number 64 in binary is 01000000. The bits in each one of these binary numbers is what AND is actually looking at. Figure 4 below shows what this looks like.

Figure 4: ANDing 255 and 64

The only column with a 1 in both cells is in the 64's place making the answer to ANDing 255 and 64 the binary number of 01000000 or 64 in decimal. Let's AND 237 and 212 together to see the result as seen in Figure 5 below.

Figure 5: ANDing 237 and 212

In this example the 128's, 64's, and 4's place columns all contain the value of 1 resulting in the binary number 11000100 or 196 in decimal. Many times in source code you'll come across something like this:

Variable% = 255
IF Variable% AND 64 THEN
    ' do some code here
END IF

We already know that the result of ANDing 255 and 64 results in 64. So basically the IF...THEN statement equates to this:

IF 64 THEN

and since 64 is non-zero it will be seen as true and the block of code will be entered. How can code like this be useful? Well get to that in a bit but first there are a few more logical operators to cover.

The OR Statement

The OR logical operator requires two input bits to test as well resulting in the following truth table.

Figure 6: OR truth table

The only time the OR logical operator will return a value 0, or false, is if both inputs being tested are 0 or false. The truth table equates to:

0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1

Just like with AND the OR logical operator can be used for IF...THEN testing:

IF Number% = 10 OR User$ = "Admin" THEN

If either side of OR results in true then the entire statement becomes true.

Let's OR the numbers 237 and 212 together to see the result.

Figure 7: ORing 237 and 212

The XOR Statement

The XOR, or eXclusive OR, logical operator requires two input bits to test as well resulting in the following truth table.

Figure 8: XOR truth table

The only time the XOR logical operator will return a value of 1, or true, is when only one input is true, not both. The truth table equates to:

0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0

Just like with AND and OR, the XOR logical operator can be used for IF...THEN testing:

IF Number% = 10 XOR User$ = "Admin" THEN

If only one side of XOR results in true the entire statement becomes true.

Let's XOR the numbers 237 and 212 together to see the result.

Figure 9: XORing 237 and 212

The NOT Statement

The NOT logical operator takes one input and inverts it.

Figure 10: NOT truth table

Many times NOT is used to make a line of code easier to read like so:

CONST FALSE = 0
CONST TRUE = NOT FALSE

PlayerDead% = FALSE '     player is alive
IF NOT PlayerDead% THEN ' is player dead?
    '                     no, do this code because player is still alive
END IF

The NOT logical operator in the line above inverts the value of the variable turning what was a FALSE into a TRUE and therefore making the IF...THEN statement true.

BitWise Example: A BitMapped Maze

There are many, many variables and conditions to track when writing computer games; is the player alive or dead, did the player grab the key needed to open this door, can the player move in a certain direction, etc. Many of these conditions can be set as true or false flags. Instead of creating a separate variable for each condition the information can be stored in the individual bits of a single variable. This was a highly preferred method of storing information back in the early days when home computers had 4KB to 64KB of RAM. There simply was not enough room to store hundreds of individual variables and their values.

A map can easily be created using bitwise operations to indicate which walls are closed and which are open to move through. Each cell of the map is given the following attributes:

Figure 11: Setting up a map using 4-bit binary numbers

By adding each binary placeholder with a corresponding wall a number can be created that indicates which walls are turned on. For example, the top left square contains the value of 11 because the North wall has a value of 1 (2^0), the East wall has a value of 2 (2^1) and the West wall has a value of 8 (2^3) that when added together equals 11. Bitwise operations can now be used to test for the existence of a wall in any given cell.

CONST NORTH = 1 ' (2^0)
CONST EAST = 2 '  (2^1)
CONST SOUTH = 4 ' (2^2)
CONST WEST = 8 '  (2^3)

CellWalls% = 11 ' the walls in the current room

IF CellWalls% AND NORTH THEN PRINT "North wall is present" ' test the first bit
IF CellWalls% AND EAST THEN PRINT "East wall is present" '   test the second bit
IF CellWalls% AND SOUTH THEN PRINT "South wall is present" ' test the third bit
IF CellWalls% AND WEST THEN PRINT "West wall is present" '   test the fourth bit

The first four bits in CellWalls% are being used as flags to determine if a wall is present or not. The number 11 equates to 1011 in binary. The right-most bit is 1 so there is a wall to the North. The second bit from the right is 1 so there is a wall to the East. The third bit from the right is a 0 so there in no wall to the South. Finally, the fourth bit from the right is 1 so there is a wall to the West. This one variable effectively holds 4 pieces of information for us encoded in its bits.

Here is a demonstration program showing this concept in action. The player can use the arrow keys to move the red circle within the map. The player will not be able to go through walls if they are present. The ESC key allows the player to leave the demonstration.

( This code can be found at .\tutorial\Lesson10\BitMaze.bas )

'*
'* Bitwise math demonstration
'*
'* Draws a simple map and allows player to move circle within map
'*

'--------------------------------
'- Variable Declaration Section -
'--------------------------------

TYPE MAP '             set up map cell structure
    x AS INTEGER '     upper left x coordinate of cell
    y AS INTEGER '     upper left y coordinate of cell
    walls AS INTEGER ' identifies cell walls
END TYPE

DIM MAP(4, 4) AS MAP ' create the map array
DIM cx% '              current x cell coordinate of player
DIM cy% '              current y cell coordinate of player
DIM KeyPress$ '        player key presses

'----------------------------
'- Main Program Begins Here -
'----------------------------

SCREEN _NEWIMAGE(250, 250, 32) '                                 create 250x250 32bit screen
_TITLE "Simple Map" '                                            give window a title
CLS '                                                            clear the screen
DRAWMAP '                                                        draw the map
DO '                                                             MAIN LOOP begins here
   PCOPY 1, 0 '                                                  copy page 1 to current screen
   CIRCLE (MAP(cx%, cy%).x + 24, MAP(cx%, cy%).y + 24), 20, _RGB32(255, 0, 0) ' draw player
   PAINT (MAP(cx%, cy%).x + 24, MAP(cx%, cy%).y + 24), _RGB32(128, 0, 0), _RGB32(255, 0, 0)
   _DISPLAY '                                                    update the screen without flicker
   DO '                                                          KEY INPUT LOOP begins here
        KeyPress$ = INKEY$ '                                     get a key (if any) that player pressed
       _LIMIT 120 '                                              limit loop to 120 times per second
   LOOP UNTIL KeyPress$ <> "" '                                  KEY INPUT LOOP back if no key
   SELECT CASE KeyPress$ '                                       which key was pressed?
       CASE CHR$(27) '                                           the ESC key
           SYSTEM '                                              return to Windows
       CASE CHR$(0) + CHR$(72) '                                 the UP ARROW key
           IF NOT MAP(cx%, cy%).walls AND 1 THEN cy% = cy% - 1 ' move player up if no wall present
       CASE CHR$(0) + CHR$(77) '                                 the RIGHT ARROW key
           IF NOT MAP(cx%, cy%).walls AND 2 THEN cx% = cx% + 1 ' move player right if no wall present
       CASE CHR$(0) + CHR$(80) '                                 the DOWN ARROW key
           IF NOT MAP(cx%, cy%).walls AND 4 THEN cy% = cy% + 1 ' move player down if no wall present
       CASE CHR$(0) + CHR$(75) '                                 the LEFT ARROW key
           IF NOT MAP(cx%, cy%).walls AND 8 THEN cx% = cx% - 1 ' move player left if no wall present
   END SELECT
LOOP '                                                           MAIN LOOP back

'-----------------------------------
'- Function and Subroutine section -
'-----------------------------------

SUB DRAWMAP ()

    '*
    '* draws a map based on the value of each map cell
    '*

   SHARED MAP() AS MAP ' need access to map array

   DIM x%, y% '          x,y map coordinates

   FOR y% = 0 TO 4 '                                                 cycle through map rows
       FOR x% = 0 TO 4 '                                             cycle through map columns
           READ MAP(x%, y%).walls '                                  read wall DATA
            MAP(x%, y%).x = x% * 50 '                                compute upper left x coordinate of cell
            MAP(x%, y%).y = y% * 50 '                                compute upper left y coordinate of cell
           IF MAP(x%, y%).walls AND 1 THEN '                         is NORTH wall present?
               LINE (MAP(x%, y%).x, MAP(x%, y%).y)-_
                    (MAP(x%, y%).x + 49, MAP(x%, y%).y), _RGB32(255, 255, 255) '      yes, draw it
           END IF
           IF MAP(x%, y%).walls AND 2 THEN '                         is EAST wall present?
               LINE (MAP(x%, y%).x + 49, MAP(x%, y%).y)-_
                    (MAP(x%, y%).x + 49, MAP(x%, y%).y + 49), _RGB32(255, 255, 255) ' yes, draw it
           END IF
           IF MAP(x%, y%).walls AND 4 THEN '                         is SOUTH wall present?
               LINE (MAP(x%, y%).x, MAP(x%, y%).y + 49)-_
                    (MAP(x%, y%).x + 49, MAP(x%, y%).y + 49), _RGB32(255, 255, 255) ' yes, draw it
           END IF
           IF MAP(x%, y%).walls AND 8 THEN '                         is WEST wall present?
               LINE (MAP(x%, y%).x, MAP(x%, y%).y)-_
                (MAP(x%, y%).x, MAP(x%, y%).y + 49), _RGB32(255, 255, 255) '          yes, draw it
           END IF
       NEXT x%
   NEXT y%
   PCOPY 0, 1 '                                                      save a copy of the map

END SUB

'------------------------
'- Program DATA section -
'------------------------

'*
'* Map cell values
'*

DATA 11,15,15,11,15,12,5,5,4,3,15,15,15,15,10,11,15,9,5,6,12,5,6,15,15

Figure 12: A Bitwise maze

A few things are going on in this example that need explained. First, in lines 73, 77, 81, and 85 the LINE statement is abruptly ended with an underscore ( _ ) character and then the statement resumes on the next line. When a line of code ends in an underscore ( _ ) character that means the rest of the command is continued on the next line. This is used to keep lines of code from scrolling off the right side of the screen. The use of this IDE feature is completely optional but it has been used here so the entire lines of code can be seen in the example screen shot above. You'll see more of this use in later lessons as code starts getting more complex.

Secondly, in line 102 you see the DATA statement. The DATA statement is used to store values internally inside of source code. In line 102 each cell of the maze wall values are stored here. In line 69 of the code a READ statement is used to read the DATA stored in line 102. DATA statements are not all that common any longer because it's just as easy to store data in files and read it from there. For the interest of keeping everything within one piece of code for this example a DATA statement was used. DATA statements were popular when home computers used cassette tape drives or very slow floppy diskette drives. Loading values from them took time and would pause a game. Visit the QB64 Wiki for more information on the READ and DATA statements.

Integer Division ( \ )

Integer division uses the backslash ( \ ) character instead of the foreslash character ( / ) used by standard division. Integer division will only return the whole number, or integer, portion of a result. Integer division is typically used when only integer results are needed such as working with screen coordinates. The following two lines of code highlight the difference between standard division and integer division.

PRINT 10 / 6 ' results in 1.666666 repeating
PRINT 10 \ 6 ' results in 1

Integer division will not round up but instead simply cuts off the fractional portion of a number resulting in the return of the whole number. It's important to remember that integer division will always round the divisor, or second number, up or down accordingly. If you try this:

v% = 10 \ .3 ' division by zero error!

you will get a division by zero error because .3 was rounded down to zero. The divisor in integer division must always be .5 or greater to avoid this error.

The MOD Statement (Modulus Division)

In Modulus division (sometimes referred to as remainder division) only the remainder of a division result is returned as an integer. The MOD statement is used to perform modulus division. The following lines of code (copied from the QB64 Wiki) highlight the differences between standard division, integer division, and modulus division.

D! = 100 / 9
I% = 100 \ 9
R% = 100 MOD 9
PRINT "Normal Division:"; D! '    results in 11.11111
PRINT "Integer Division:"; I% '   results in 11
PRINT "Remainder Division:"; R% ' results in 1

The reason modulus division resulted in the value of 1 is because 100 \ 9 = 11 and since 11 * 9 = 99 that leaves a remainder of 1. The MOD statement treats the divisor the same as integer division rounding the divisor up only if it's .5 or greater.

Modulus division is actually used quite often in programming especially in respect to timing in games. Say for instance you have a counter within a loop and you want some code to execute every 30 frames. The MOD statement can help with this.

DO
    Count% = Count% + 1 '      increment counter
   IF Count% MOD 30 = 0 THEN ' is counter evenly divisible by 30?
        ' execute the code here
   END IF
LOOP

Only when Count% is evenly divisible by 30 will there be no remainder:

30 \ 30 = 1 with no remainder
60 \ 30 = 2 with no remainder
90 \ 30 = 3 with no remainder
..
..
etc..

Exponents ( ^ )

Exponent ( ^ ) is used to raise a numeric value to an exponential value, "to the power of". For example:

PRINT 2 ^ 3 '         results in 8 (2 * 2 * 2)

Exponents can also be used to calculate square and cubed roots of numbers like so:

PRINT 144 ^ (1 / 2) ' results in 12 as the square root (12 * 12)
PRINT 27 ^ (1 / 3) '  results in 3 as the cubed root (3 * 3 * 3)

The INT Statement

The INT statement converts a numeric value to an integer by rounding down the value to the next whole number. INT rounds numbers down for both positive and negative values. Therefore:

PRINT INT(2.5) '  result is rounded down to 2

and

PRINT INT(-2.5) ' result is rounded down to -3

are both rounded down. The INT statement comes in handy when you need to convert between data types.

n! = 12.789 '  stored in a single variable type
c% = INT(n!) ' converted to an integer and stored in an integer variable type

The CINT and CLNG Statements

The CINT statement is specifically used to convert decimal numbers to an integer using Banker's Rounding. This means that numbers with decimal points less than .5 are rounded down, numbers with decimal points higher than .5 are rounded up, and numbers with .5 as the decimal value are rounded to the nearest even integer value which may be up or down depending on the number. CINT converts numbers to true integers meaning that you must remember to use numbers in the range of 32,767 to -32,768.

PRINT CINT(2.5) '  results in 2 as 2 is the closest even number (round down)
PRINT CINT(3.5) '  results in 4 as 4 is the closest even number (round up)
PRINT CINT(10.6) ' results in 11 (round up)
PRINT CINT(10.4) ' results in 10 (round down)
N! = 1034.5 '      single value in integer range
I% = CINT(N!) '    results in 1034 as it is the closest even number (round down)

The CLNG function is specifically used to convert a decimal number to a long integer. It uses the same Banker's Rounding scheme as CINT. Since CLNG converts to long integers you must remember to use numbers in the range of 2,147,483,647 to -2,147,483,648.

The CSNG and CDBL Statements

The CSNG statement is used to convert a numeric value to the closest single precision value. This function is useful for defining a numeric value as single precision as well.

N# = 895.18741378374
S! = CSNG(N#)
PRINT S! ' results in the single precision value 895.1874

The CDBL statement is used to convert a numeric value to the closest double precision value. This function is mainly used to define any numeric value as double precision.

The _ROUND Statement

The _ROUND statement is used to round a numeric value to the closest even integer, long integer, or _integer64 value. This statement can accept any numeric value to convert and performs the same functions as CINT and CDBL while offering conversion for numbers that exceed long integer in size. Since _ROUND uses the same Banker's Rounding scheme as CINT and CDBL it makes a suitable substitute for both commands.

The FIX Statement

The FIX statement rounds a numerical value to the next whole number closest to zero, in effect truncating, or removing, the fractional portion of a number returning just the integer. This means that the FIX statement rounds down for positive numbers and up for negative numbers.

PRINT FIX(2.5) '  result is 2 printed to the screen
PRINT FIX(-2.5) ' result is -2 printed to the screen

The SQR Statement

The SQR statement returns the square root of any positive numeric value.

N% = 256
PRINT SQR(N%) ' returns 16

Calculating the hypotenuse of a right triangle:

A% = 3 '                        side A
B% = 4 '                        side B
H! = SQR((A% ^ 2) + (B% ^ 2)) ' side C
PRINT "Hypotenuse:"; H! '       value of 5 is printed

Remember that the SQR statement will only work with positive numbers and the accuracy of SQR is determined by the variable type being used to store the result.

The ABS Statement

The ABS statement returns the absolute value of any numeric value in effect turning negative numbers into positive numbers.

N% = -100
PRINT ABS(N%) ' results in 100 printed

The SGN Statement

The SGN statement returns the sign of a numeric value. The value of -1 is returned for values less than zero. The value of 1 is returned for values greater than zero and 0 if the numeric value is zero.

N1% = -10
N2% = 0
N3% = 10
PRINT SGN(N1%), SGN(N2%), SGN(N3%) ' -1  0  1  printed to screen

The SIN and COS Statements

The SIN statement returns the sine of an angle measured in radians. The following is an example that displays the seconds for an analog clock. (Adapted from code by Ted Weissgerber.)

( This code can be found at .\tutorial\Lesson10\Second_Hand.bas )

SCREEN 12
Pi2! = 8 * ATN(1) '                            2 * Pi
sec! = Pi2! / 60 '                             (2 * pi) / 60 movements per rotation
DO
   LOCATE 1, 1
   PRINT TIME$
    Seconds% = VAL(RIGHT$(TIME$, 2)) - 15 '    update seconds
    S! = Seconds% * sec! '                     radian from the TIME$ value
    Sx% = CINT(COS(S!) * 60) '                 pixel columns (60 = circular radius)
    Sy% = CINT(SIN(S!) * 60) '                 pixel rows
   LINE (320, 240)-(Sx% + 320, Sy% + 240), 12
   DO
        Check% = VAL(RIGHT$(TIME$, 2)) - 15
   LOOP UNTIL Check% <> Seconds% '             wait loop
   LINE (320, 240)-(Sx% + 320, Sy% + 240), 0 ' erase previous line
LOOP UNTIL INKEY$ = CHR$(27) '                 escape keypress exits

The COS statement returns the cosine of an angle measured in radians. The following code creates 12 analog hour points that can be used with the clock example above. (Adapted from code by Ted Weissgerber.)

The TAN and ATN Statements

The TAN statement returns the ratio of sine to cosine, or the tangent value of an angle measured in radians. Here is an example of SIN and TAN working together to create spiraling text. (Adapted from code by John Onyon.)

( This code can be found at .\tutorial\Lesson10\TAN_Demo.bas )

DIM SHARED text AS STRING
text$ = "S P I R A L"
DIM SHARED word(1 TO LEN(text$) * 8, 1 TO 16)
CALL analyse
CLS
CALL redraw

SUB analyse

   CLS
   SCREEN 12
   COLOR 2: LOCATE 1, 1: PRINT text$
   DIM px AS INTEGER, py AS INTEGER, cnt AS INTEGER, ltrcnt AS INTEGER
    px = 1: py = 1
   DO
        word(px, py) = POINT(px, py)
       PSET (px, py), 1
        px = px + 1
       IF px = LEN(text$) * 8 THEN
            px = 1
            py = py + 1
       END IF
   LOOP UNTIL py = 16

END SUB

SUB redraw

   CLS
   DIM row AS INTEGER, cnt AS INTEGER, cstart AS SINGLE, cend AS SINGLE
   DIM xrot AS INTEGER, yrot AS INTEGER, SCALE AS INTEGER, pan AS INTEGER
    cstart = 0: cend = 6.2
    xrot = 6: yrot = 6: SCALE = 3: pan = 30
   OUT &H3C8, 1: OUT &H3C9, 10: OUT &H3C9, 10: OUT &H3C9, 60
   DO
        row = 2
       DO
           DO
               FOR i = cend TO cstart STEP -.03
                    x = (SCALE * 60 - (row * xrot / 4)) * TAN(COS(i))
                    y = SIN(SCALE * 60 - (row * yrot)) * TAN(SIN(i)) * pan
                    cnt = cnt + 1
                   IF word(cnt, row) > 0 THEN
                       CIRCLE (x + 320, y + 220), SCALE + 1, 1 'circled letters
                        'LINE (x + 320, y + 220)-STEP(12, 12), 1, BF 'boxed letters
                   END IF
                   IF cnt = LEN(text$) * 8 THEN cnt = 0: EXIT DO
               NEXT
           LOOP
            row = row + 1
       LOOP UNTIL row = 16
        cend = cend + .1
        cstart = cstart + .1
        now! = TIMER
       DO
            newnow! = TIMER
       LOOP UNTIL newnow! - now! >= .15
       LINE (1, 100)-(639, 280), 0, BF
   LOOP UNTIL INKEY$ = CHR$(27)

END SUB

The ATN statement returns the arctangent of an angle measured in radians. Here we use ATN to find the angle from the mouse pointer to the center point of the screen. (Adapted from code by Rob aka Galleon.)

( This code can be found at .\tutorial\Lesson10\ATN_Demo.bas )

SCREEN _NEWIMAGE(640, 480, 32)
x1! = 320
y1! = 240
DO
   PRESET (x1!, y1!), _RGB(255, 255, 255)
    dummy% = _MOUSEINPUT
    x2! = _MOUSEX
    y2! = _MOUSEY
   LINE (x1, y1)-(x2, y2), _RGB(255, 0, 0)
   LOCATE 1, 1: PRINT getangle(x1!, y1!, x2!, y2!)
   _DISPLAY
   _LIMIT 200
   CLS
LOOP UNTIL INKEY$ <> ""
END

FUNCTION getangle# (x1#, y1#, x2#, y2#) 'returns 0-359.99...
   IF y2# = y1# THEN
       IF x1# = x2# THEN EXIT FUNCTION
       IF x2# > x1# THEN getangle# = 90 ELSE getangle# = 270
       EXIT FUNCTION
   END IF
   IF x2# = x1# THEN
       IF y2# > y1# THEN getangle# = 180
       EXIT FUNCTION
   END IF
   IF y2# < y1# THEN
       IF x2# > x1# THEN
            getangle# = ATN((x2# - x1#) / (y2# - y1#)) * -57.2957795131
       ELSE
            getangle# = ATN((x2# - x1#) / (y2# - y1#)) * -57.2957795131 + 360
       END IF
   ELSE
        getangle# = ATN((x2# - x1#) / (y2# - y1#)) * -57.2957795131 + 180
   END IF

END FUNCTION

The RND Statement

The RND statement is used to return a random number, well, sort of. This may come as a surprise but no computer in the world to this date has been able to generate truly random numbers. It's one of those things on computer scientist's to-do lists. Computers can generate pseudo-random numbers that appear to be random but in reality are not (makes you think about the "randomness" of those video poker machines in Vegas or the auto lotto numbers chosen for you doesn't it?) To see this in action type in the following code:

FOR i% = 1 TO 5
   PRINT RND
NEXT i%

Run the code a few times and you'll see the same five "random" numbers appearing over and over again. Computers can generate what appear to be random numbers but in actuality are not. There is a work around to this however. Change the code as seen below:

RANDOMIZE TIMER
FOR i% = 1 TO 5
   PRINT RND
NEXT i%

Run the code a few times again and you'll get what appears to be truly random numbers even though they are not.

The RND statement generates random numbers between 0 and .9999999 but in a predictable manner each time a program is executed.

The RANDOMIZE statement is used to seed the random number generator with a supplied numeric value. This basically tells the random number generator to start somewhere else in the list.

The TIMER statement returns the number of seconds that have elapsed since midnight which ranges from 0 (midnight) to 86399 (11:59:59PM). TIMER resets back to 0 every day at midnight. TIMER is often used to seed the random number generator but even this is not fool-proof. If you execute a program at exactly noon on two or more separate days you'll get the same random numbers.

Since RND only produces random numbers between 0 and .9999999 you'll need to multiply RND's value with the largest random number you are looking for plus 1. Modify the code once again to see this.

RANDOMIZE TIMER
FOR i% = 1 TO 5
   PRINT INT(RND * 10) + 1
NEXT i%

The code above produces random numbers between 1 and 10. RND * 10 will produce a random value between 0 and 9.999999, the INT function converts that to an integer, and finally + 1 is added so the resulting number will between 1 and 10. If you want to produce random numbers between 50 and 100 you would need to add a start value like so.

RANDOMIZE TIMER
FOR i% = 1 TO 5
   PRINT 50 + INT(RND * 51)
NEXT i%

RND * 51 will produce a value between 0 and 50.99999, the INT function converts that to an integer, and finally 50 is added to that value with the result being between 50 and 100.

You can get creative with seeding the random number generator to make the values seem even more random. If your game is mouse driven you could do this every so often in your code:

RANDOMIZE TIMER \ (_MOUSEX + 1)

One is added to the value of _MOUSEX to ensure if the mouse pointer is sitting at coordinate zero (or off the game screen) TIMER will always be integer divided by at least the value of one to avoid division by zero errors. As the player is moving the mouse around _MOUSEX will be truly random since the player is giving it analog input through the mouse and the player's movements can't be predicted.

Your Turn

This lesson has been more of a general knowledge section on some of the math functions available in QB64. Don't worry if some of the concepts here have flown over your head so to speak. Many of the functions introduced here will be used in later advanced lessons and their usage will become more clear.

It's time to write your first game. Lessons 1 through 10 have given you enough of a working background with QB64 to write a simple slot machine as seen in the figures below.

Figure 13: Program first run

Figure 14: Chicken dinner!

Figure 15: Not even close

Figure 16: More chicken!

Figure 17: One armed bandit!

Figure 18: Oh so close!

The program consists of:

Program operation:

Many times while writing a program you'll need to do something you have not done before and a command you have not encountered may help. There are a few commands used in the program that have not been covered yet but will aid in the game's creation. These statements will be covered in later lessons:

These lessons are going to cover MANY of QB64's commands but it won't even be half of them available to you. When you have finished these lessons make sure to check out all the commands in the QB64 Wiki.

Program Theory:

In this example program I chose to envision the symbols on a rotating wheel just like the real old time slot machines had. Each wheel consists of 16 places with the symbols arranged as such:

Of course there are many different ways to achieve the same outcome. That's the great thing about programming. Your code may be completely different from the example code and still achieve the same outcome. Make sure to share your code on the QB64 forum when you are finished.

Click here to see the solution.

Commands and Concepts Learned