Copyright: This helpfile is copyright 2000 Shoptalk Systems


Contents
Liberty BASIC Course

A six week course in Liberty BASIC!  This is the same course we used to teach hundreds of people on-line at ZDU (now Smart Planet)!

Week One - An introduction to Liberty BASIC

Homework

Homework Solution

Week Two - Using Arrays

Homework

Homework Solution

Week Three - Reading and Writing Sequential Files

Homework

Homework Solution

Week Four - Programming the Windows Graphical User Interface

Homework

Homework Solution

Week 5 - Introduction to Programming Graphics

Homework

Homework Solution

Week 6 - Building Our Application


Liberty BASIC Tutorial
Week One - An introduction to Liberty BASIC

It may sound incredible, but anyone can program for Microsoft Windows.  Creating software for Microsoft Windows was once the domain of the elite C and C++ programmer.  But finally, after years of waiting, tools have arrived for the non-guru computer user.  This tutorial is written about one of these tools, Liberty BASIC, and about how to make it work for you.

What is Liberty BASIC?

Overview of this Tutorial

Programming: What is it?

An Introduction to BASIC

Goto - Doing something more than once

IF . . . THEN - Adding Smarts to Our Tax Program

String Variables

Some things to do with Strings

Functions

Documenting BASIC Code

Let's Write a Program  -  HILO.BAS

Complete listing for HILO.BAS


What is Liberty BASIC?

Liberty BASIC is a Windows programming tool that brings BASIC's ease of use to Microsoft Windows.

Liberty BASIC includes:

A powerful BASIC language for Windows;

A Visual Development Tool, Freeform;

An editor for writing BASIC programs;

An easy to use tracing debugger;

Easy calling of DLLs and APIs;

A programmable spreadsheet;

Color graphics capability

An OS/2 version is also available!!!

Next Section: Overview of this Tutorial


Overview of this Tutorial

This tutorial  covers:

Programming: What is it? ;

An Introduction to BASIC ;

GOTO - Doing something more than once ;

IF . . . THEN - Adding smarts to our tax program ;

String Variables ;

Some things to do with Strings ;

Functions ;

Documenting BASIC Code ;

Let's write a program  -  HILO.BAS

Next Section: Programming: What is it?


Programming: What is it?

There's nothing mystical about programming computers.  Although the newest software on the market today begins to look like magic, all software is built from the ground up out of combinations of the simplest software parts.  Once you learn what these software parts are and how they're used, hard work and imagination can take you almost anywhere.

Programming is (simply put) the laying out of simple steps to solve a problem, and in a way that a computer can understand.  This is a little bit like teaching a person.  These steps must be arranged in the correct order.

For example:

How to drive a car with automatic transmission:

Get into drivers seat ;

Fasten safety belt ;

Insert ignition key and turn it to start engine ;

Press brake with foot ;

Move transmission selection to D ;

Look around to see if you're safe ;

Remove foot from brake ;

Press accelerator pedal with foot ;

Manuever into traffic ;

Don't crash

Obviously if the above steps are scrambled up (and maybe even if they aren't) you're in for a pretty big insurance claim.  Not only that, but if the instructions are given to someone who speaks only, say, Chinese, we will have a similarly spectacular crash!  In the same way, computers are particular about both the order and content of the instructions we give them.

A program in its simplest form usually contains three kinds of activity:

   INPUT	The program asks the user for some kind of information ;

   CALCULATION 	The program transforms or manipulates the information ;

   OUTPUT 	The program displays the final result of CALCULATION

It is the programmer's job to determine exactly how to accomplish these steps.

Next Section: An Introduction to BASIC


An Introduction to BASIC

BASIC (Beginners All purpose Symbolic  Instruction Code) was created in the 1960's as an easy to learn programming language for computers.  Because of BASIC's simple form and because it was an interpreted language and gave the programmer instant  feedback, it became the most popular programming language when microcomputers made their debut, and it has a large following even today.

This tutorial introduces the first principles of Liberty BASIC, but doesn't provide a thorough description of all language features.  For more on the full language and command set, refer to the documentation included with your copy of Liberty BASIC.

Salestax.bas, a simple BASIC program.

Now let's create a very simple program to introduce you to the simplest of BASIC's features.  We want a BASIC program that:

1 - Asks for a dollar and cent amount for goods ;

2 - Calculates a 5% sales tax amount for the dollar and cent figure ;

3 - Displays the tax amount the total amount

INPUT - First we need an instruction for the computer that gets information from the user.  In BASIC there are several ways to do this but we will choose the input command for our program.  In this case input would be used like so:

  input "Type a dollar and cent amount ?"; amount

                                           ^------this is a variable

This line of BASIC code will display the words, "Type a dollar and cent amount ?",  and the computer will stop and wait for the user to type something in.  When the [Enter] key is pressed, the typed information will then be stored in the variable* amount.

*variable  -  In programming, you must assign each bit of data (or information) a unique name.  This combination of a name and its data is called a variable because the data part can vary each time the program is used.  When you edit a program, you choose a name for each variable.  You pick the name for each variable to best fit the kind of data it represents.  BASIC doesn't care what names you assign to your variables, except you can't use the names of BASIC commands or functions (called reserved words). Choose names that make it easy for anyone to understand what the BASIC program code means and does.  When running a program, BASIC uses the data part of the variable in its calculations. BASIC uses the name of the variable to fetch its data part or to store new data in that variable.  Variable data can change many times during the execution of a BASIC program.

CALCULATION - Now we need to calculate the tax for the data in our amount variable:

let tax = amount * 0.05

This line of code creates a new variable called "tax" to hold our computed tax data.  The BASIC command, "let", tells BASIC to calculate the arithmetic on the right side of the = and set the data of the variable "tax" equal the results of the equation.  The "let" word is optional (and most programmers leave it out) but I use it here as an example of BASIC syntax. It could have been coded as follows:

tax = amount * 0.05

Now you may be wondering what is that funny little '*' (called asterisk).  Since there are no formal arithmatic symbols on a typewriter keyboard, most programming languages use * to denote multiplication, / for division, and the addition and subtraction symbols get lucky and are + and - (what else?).

OUTPUT - Now that we have calculated our tax amount, we will display it with:

print "Tax is: "; tax; ". Total is: "; tax + amount

The print command displays the information to the screen.  The line of code above shows how print is used to display several items of data, each separated by a ';' (semicolon).

The items are:

   "Tax is: "	- This displays on screen as is, but without the quotation marks ;

   tax	- This displays the value of the variable tax ;

   ". Total is: "	- This also displays on screen as is, but without quotation marks ;

   tax + amount	- This displays the sum of the two variables, tax and amount

These will all be displayed on the same line.  The semicolons are not displayed.  Each print command is followed by a carriage return.  The result might look like this:

Tax is: 0.05. Total is: 1.05

Now let's run the program.  Type or cut/paste the following program into the Liberty BASIC editor so that it looks as shown.

    input "Type a dollar and cent amount?"; amount

    let tax = amount * 0.05

    print "Tax is: "; tax; ". Total is: "; tax+amount

Now run the program by clicking on the Run menu and clicking on the Run item (or press Shift+F5).

And here is a sample run:

    Type a dollar and cent amount?1.20

    Tax is: 0.06. Total is: 1.26

Now let's save our program.  Select the File menu and choose Save As.  Now type the name salestax.bas and click on OK.

Next Section: Goto - Doing something more than once


Liberty BASIC Course

A six week course in Liberty BASIC!  This is the same course taught on-line on the Internet (visit www.zdu.com)!

Notice:  Look in the tutorial subdirectory of your Liberty BASIC installation for the files mentioned throughout this course lessons below!

Week One - An introduction to Liberty BASIC

Homework

Homework Solution

Week Two - Using Arrays

Homework

Homework Solution

Week Three - Reading and Writing Sequential Files

Homework

Homework Solution

Week Four - Programming the Windows Graphical User Interface

Homework

Homework Solution

Week 5 - Introduction to Programming Graphics

Homework

Homework Solution

Week 6 - Building Our Application


Week One Homework

Week 1, homework assignment 1

Liberty BASIC programming course

Copyright 1996 Shoptalk Systems

All Rights Reserved

Refining SALESTAX.BAS

Our SALESTAX.BAS example program provides a good base for us to build on.

Here it is:

[start]

    print "Type a dollar and cent amount."

    input "(Press 'Enter' alone for help) ?"; amount

    if amount = 0 then [help]

    let tax = amount * 0.05

    print "Tax is: "; tax; ". Total is: "; tax + amount

    goto [start]

[help]

    cls

    print "SALESTAX.BAS Help"

    print

    print "This tax program determines how much tax is"

    print "due on an amount entered and also computes"

    print "the total amount.  The tax rate is 5%."

    print

    input "Press [Enter] to continue."; dummyVariable

    cls

    goto [start]

This program calculates a 5% sales tax.  It leaves any rounding up to the computer user.  We want to extend our SALESTAX program to round up properly and display our amount paid, tax, and total amounts in a way suitable for currency.

Rounding the tax up

Let's define how we will round up tax amounts for our example.  Where I live, I pay 5 cents for every whole dollar.  The remaining non-whole dollar amount is rounded up or down to the nearest 20 cent amount, and I pay 1 cent for each 20 cents of this rounded result.  Let's look at it step by step.

Given a purchase price of $5.45

(a) Strip away the cents from dollars and cents, that gives us $5.

(b) Pay 5 cents for each dollar, or 25 cents ($5 * 5 cents).

(c) Round the 45 cent portion of our purchase price to the nearest multiple of 20, that gives us 40 cents.

(d) Now we will pay 1 cent for every 20 cents in that 40 cent amount for a total of 2 cents (40 / 20 = 2).

(e) Add the 25 cents from (b) to the 2 cents from (d), and we have our total tax amount, 27 cents.

We can compute the value for step (b) using the INT() function.

Let's see how this works:

    'demonstration of INT() function

    input "Enter a non integer value (ie. 3.14) ?"; valueA

    let valueB = int(valueA)

    print "The integer part of "; valueA; " is "; valueB

To get the cent amount for the above calculations, we subtract the integer part from the entered amount, or valueA - valueB.  Let's see how this works:

    'getting the non-integer part of a value

    input "Enter a non integer value (ie. 3.14) ?"; valueA

    let valueB = int(valueA)

    print "The non-integer part of "; valueA; " is "; valueA - valueB

Now that we know how to get the cents out of our dollars and cents amount, we need to round it to the nearest 20 cent multiple.  Here is a simple way to round any number to a closest chosen multiple (this works for values greater than 0):

(a) Divide your chosen multiple by two.  We will use this as an adjustment value.  If we round 0.14 to the nearest multiple of 0.20, then our adjustment is 0.10 (0.20 / 2 = 0.10).

(b) Add the adjustment value to the number we want to round.  If our cent amount is 0.45 then our adjusted amount is 0.55.

(c) Now divide the adjusted amount by the multiple we want to round to.  This will give us 2.75 (0.55 / 0.20 = 2.75).

(d) Compute the integer portion of 2.75 and we have 2.  Take this value and multiply it by 0.20 (our chosen multiple), and we get 0.40 (2 * 0.20 = 0.40).

So we see that 0.45 rounded to the nearest multiple of 0.20 is 0.40.  Here is an expanded version of the program above that shows how to round a number in the way outline above:

    'getting the non-integer part of a value

    input "Enter a non integer value (ie. 3.14) ?"; valueA

    let valueB = int(valueA)

    valueC = valueA - valueB

    print "The non-integer part of "; valueA; " is "; valueC

    'now round valueC to the nearest multiple of 0.20

    adjustment = 0.20 / 2

    adjustedC = valueC + adjustment

    multiples = int(adjustedC / 0.20)

    roundedC = multiples * 0.20

    print valueC; " rounded to the closest 0.20 is "; roundedC

Displaying Monetary Values

When you buy something at the store, the cashier will usually give you a receipt for your purchase.  The product price, sales tax, and total amount paid are formatted something like this:

Product Price     4.95

Sales Tax         0.25

----------------------

Total             5.20

We want our SALESTAX program to produce information in a similar fashion.  Notice that the numbers are all aligned so that their decimal points are one under another.  This is called justification.  Liberty BASIC provides a function for justifying numeric values called USING().  Here is a quick little program that demonstrates the USING() function:

    'display 3 values justified

    valueA = 0.9

    valueB = 120

    print using("#####.##", valueA)

    print using("#####.##", valueB)

    print using("#####.##", valueA + valueB)

Running this program produces:

    0.90

  120.00

  120.90

Notice the string literal "#####.##".  The USING() function uses this as a template for formatting a value.

The 5 # characters before the period in "#####.##" tell USING() to place extra spaces in front of the number to ensure that there will be 5 places before the decimal point.

The 2 # characters after the decimal point in "#####.##" tell USING() to add zeros after the decimal point if there are none, or to drop all digits after the second digit following the decimal point.

Loss of Precision

In the realm of computers, real numbers are usually represented in what's called single precision floating point format.  This format is not an absolutely accurate way to represent the value of real numbers.  Sometimes in the process of executing BASIC code to arrive at a result, some loss of precision will become apparent.  This will usually manifest itself as a loss of the minutest amount of a value.  For example, a result that should be 1.5 becomes 1.499999.

For many applications, this is not a very significant problem.  In the realm of money though, it is a very real problem indeed.  If in the course of calculating our sales tax the customer owes $0.24 but the computed result is 0.2399999, it slip through unnoticed.  The USING() function would remove the 9's after 0.23, and we would never see it.

The solution is to add 0.001 to monetary values whenever the USING() function is employed.  This is important when using PRINT to display results, and also saving information to a disk file (this will be covered later in our course).  Here is how that BASIC code would look:

    'display 3 values justified

    valueA = 0.9

    valueB = 120

    print using("#####.##", valueA + 0.001)

    print using("#####.##", valueB + 0.001)

    print using("#####.##", valueA + valueB + 0.001)

Here's the assignment

Using the techniques we've just covered, extend the SALESTAX.BAS program listed at the start of this text and expand it so that...

  - It uses the method of tax calculation described above

  - It displays a result in sales receipt fashion like so:

    Product Price     4.95

    Sales Tax         0.25

    ----------------------

    Total             5.20

  - It corrects its calculations for loss of precision

Week One Homework Solution


Week Two
Week Two - Using Arrays

Week 2 course material

Liberty BASIC programming course

Copyright 1996 Shoptalk Systems

All Rights Reserved

Using Arrays

Computers are more often than not used to keep track of lists of things. These could be lists of customers, lists of stock #'s, or lists of fonts.  A list of things doesn't even have to look like a list.  For an example of this, take a look at your favorite video games.  If you've ever played PacMan or Doom, the monsters are managed in one kind of list or another (but you never see the list).  Arrays are what BASIC (and most other programming languages) uses to keep lists.

To see why we need arrays, let's look at what programming for lists looks like without arrays.  Suppose we want to keep track of a list of 10 names.  Using only the techniques we've covered so far it would look like this:

    'NOARRAY.BAS

    'List handling without arrays

[askForName]  'ask for a name

    input "Please give me your name ?"; yourName$

    if yourName$ = "" then print "No name entered." : goto [quit]

    if name1$ = "" then name1$ = yourName$ : goto [nameAdded]

    if name2$ = "" then name2$ = yourName$ : goto [nameAdded]

    if name3$ = "" then name3$ = yourName$ : goto [nameAdded]

    if name4$ = "" then name4$ = yourName$ : goto [nameAdded]

    if name5$ = "" then name5$ = yourName$ : goto [nameAdded]

    if name6$ = "" then name6$ = yourName$ : goto [nameAdded]

    if name7$ = "" then name7$ = yourName$ : goto [nameAdded]

    if name8$ = "" then name8$ = yourName$ : goto [nameAdded]

    if name9$ = "" then name9$ = yourName$ : goto [nameAdded]

    if name10$ = "" then name10$ = yourName$ : goto [nameAdded]

    'There weren't any available slots, inform user

    print "All ten name slots are already used!"

    goto [quit]

[nameAdded]   'Notify the name add was successful

    print yourName$; " has been added to the list."

    goto [askForName]

[quit]

    end

In this example when we enter a name, the computer will look at up to ten variables in turn.  If it finds a variable that is an empty string, it will set the value of that variable to be equal to yourName$.  Then it will GOTO [nameAdded] and display a notice that the name has been added.  If this were part of a larger program, we would execute this code whenever we wanted to add a name, and the name would be inserted into the next available slot.

This technique is manageable only for very small lists.  Imagine how many lines of code we would need to track hundreds or thousands of names in this way!  Arrays provide a special way to make a single line of BASIC code refer to any one variable out of a list of thousands.

Here is our name list example rewritten using a BASIC array.  If it seems unclear to you, try running the debugger on it.

    'ARRAYS.BAS

    'List handling with arrays

    dim names$(10)  'set up our array to contain 10 items

[askForName]  'ask for a name

    input "Please give me your name ?"; yourName$

    if yourName$ = "" then print "No name entered." : goto [quit]

    index = 0

[insertLoop]

    'check to see if index points to an unused item in the array

    if names$(index) = "" then names$(index) = yourName$ : goto [nameAdded]

    index = index + 1 'add 1 to index

    if index < 10 then [insertLoop] 'loop back until we have counted to 10

    'There weren't any available slots, inform user

    print "All ten name slots already used!"

    goto [quit]

[nameAdded]  'Notify the name add was successful

    print yourName$; " has been added to the list."

    goto [askForName]

[quit]

    end

You'll immediately notice the dim names$(10) statement at the start of this example.  This tells BASIC to create an array called names$ with space for 10 items.  Because the name of the array ends with a $ character, this is an array of strings.  An array named without a $ character would contain numeric values.  The first item in an array is referred to by the value 0, and so an array of 10 items starts with item 0 and ends with item 9 (most BASICs work like this, but Liberty BASIC will actually number the array from 0 to 10, giving you eleven items).

The real work is done in this part of the code:

    index = 0

[insertLoop]

    'check to see if index points to an unused item in the array

    if names$(index) = "" then names$(index) = yourName$ : goto [nameAdded]

    index = index + 1 'add 1 to index

    if index < 10 then [insertLoop] 'loop back until we have counted to 10

Here we are using a simple loop to control a search for an empty slot in our array names$.  Take a look at this line:

    if names$(index) = "" then names$(index) = yourName$ : goto [nameAdded]

The  if names$(index) = ""  part of the line uses the value of index to point to a selected string in the array.  When index is 0, we are looking at the first item in the array.  When the code loops back and index is 1, it will be looking at the second item in the array, and so on.

In the same line of code, the  names$(index) = yourName$  part is used to set the item selected by index.  The total effect of the line is to set the selected item in the array to to be yourName$ if that selected array item is an empty string.  When this happens it also exits the loop with GOTO [nameAdded].

This is more involved than the technique used in NOARRAY.BAS, but it has the advantage of size.  Even if we need to manage 1000 items, we only need to change our code like this (most of the code is left out):

    dim names$(1000)

    .

    .

[insertLoop]

    .

    .

    if index < 1000 then [insertLoop] 'loop back until we have counted to 1000

If we extend NOARRAY.BAS to manage 1000 names, the resulting program would have one line for each name slot we needed to keep.  Our program would be more than 1000 lines long!

Index Out of Bounds

Try running the following mini program.

    'done wrong

    dim names$(10)

    print names$(15)

You will get an error message saying 'index out of bounds'.  This means that you tried to print the contents of an array item that doesn't exist.  If you dimension the array to make it large enough, it will work.

    'done right

    dim names$(20)

    print names$(15)

A similar error will occur if you try to set the item of an array using an index that is too large.

    'done wrong again

    dim names$(10)

    names$(15) = "John Doe"

Playing the numbers

Numeric arrays work very much like their relative the string array (featured above in ARRAYS.BAS).  To demonstrate how numeric arrays work, examine this program that asks for some numbers, adds them up, and calculates the average value.

    'AVERAGE.BAS

    'Accept some numbers from the user, then total and average them

    dim numbers(20)

    print "AVERAGE.BAS"

    print

    'loop up to 20 times, getting numbers

    print "Enter up to 20 non-zero values."

    print "A zero or blank entry ends the series."

[entryLoop]  'loop around until a zero entry or until index = 20

    'get the user's entry

    print "Entry "; index + 1;

    input entry

    if entry = 0 then [endSeries]  'quit if entry is zero or blank

    index = index + 1       'add one to index

    numbers(index) = entry  'set the specified array item to be entry

    total = total + entry   'add entry to the total

    if index = 20 then [endSeries]  'if 20 values were entered, exit loop

    goto [entryLoop]  'go back and get another entry

[endSeries]  'entries are finished

    'Set entryCount to index

    entryCount = index

    if entryCount = 0 then print "No Entries." : goto [quit]

    print "Entries completed."

    print

    print "Here are the "; entryCount; " entries:"

    print "-----------------------------"

    'This loop displays each entered value in turn.

    'Notice that we re-use the index variable.  It

    'can be confusing to use a new variable for each

    'new loop.

    index = 0

[displayLoop]

    index = index + 1

    print "Entry "; index; " is "; numbers(index)

    if index < entryCount then [displayLoop]

    'Now display the total and average value

    print

    print "The total is "; total

    print "The average is "; total / entryCount

[quit]

    end

Try stepping through this program with the debugger.

******************************

    Challenge exercises

******************************

1) Add code to ARRAYS.BAS after [quit] to display all the names entered.

2) There's no reason we can't have more than one array in a BASIC program.  For an example of this, look at the program file FF12.BAS.  Altogether, it uses no fewer than 18 arrays.  Some of them are string arrays and some are of the numeric type.  Modify the AVERAGE.BAS program so it asks for a name and age for up to 20 people.  The names should be managed in a string array (remember ARRAYS.BAS?) that you'll add to the program.  When the list of entries is displayed, each name is to be displayed with its age.  Then the total and average age will be displayed after this.  Call the program AGES.BAS.


Goto - Doing something more than once

Assuming that salestax.bas does what we want (see previous section), it still only does it once.  Each time you want to use this handy little program you have to run it again.  This can get to be tedious and even error prone (say rubber baby buggy bumpers  ten times fast).  What we need is a way for our program to go to the beginning and do it over.  In BASIC (and in some other languages) the command for doing this is called goto (surprise!).

Knowing that we have to goto some place is not enough.  We also need to know where to go. When you hop into your car in a foreign country looking for a food market, you at least know what you are looking for.  Liberty BASIC can't ask for directions, so you need to be very precise.

The mechanism that Liberty BASIC uses to mark places that we can goto is called a branch label.  This is a lot like a mailing address.  When you send a letter or package, you mark it with a known mailing address (hopefully).  There is a house or building somewhere marked with that address, and that is where your parcel goes to.  So in the same way, you mark the place in your BASIC program where you want it to continue running with a branch label (a mailing address of sorts).

There are two ways to define a branch label in Liberty BASIC.  You can use any valid integer number as a branch label, or you can use an easier to remember type which uses letters.

Examples of integer branch labels:

10	150	75	900	5400	etc...

Examples of alphanumeric (using letters and numbers) branch labels:

[start]  [loopBack]  [getResponse]  [point1]  etc...

Examples of unacceptable branch labels:

[loop back]	no spaces allowed

start	must use brackets

(point1)	only use square brackets

Since no spaces are allowed in the alphanumeric branch labels, it works well to capitalize the first letter in each word when multiple words are used in a branch label.  For example [gettimedresponse] is valid, but [getTimedResponse] is much more readable.

So let's pick a branch label for our salestax.bas program.  Since we are going to do it over again from the start, we could pick from several reasonable branch label names like perhaps [start], [begin], or [go].  We will use [start] for our program.

Let's add the branch label as shown:

[start]

    input "Type a dollar and cent amount "; amount

    let tax = amount * 0.05

    print "Tax is: "; tax; ". Total is: "; tax + amount

Now we need our goto line.  Now that we have our branch label, the correct format for goto is goto [start]. And here's what our program looks like when both a branch label and a goto:

[start]

    input "Type a dollar and cent amount "; amount

    let tax = amount * 0.05

    print "Tax is: "; tax; ". Total is: "; tax + amount

    goto [start]

Now let's try running this program.  It runs over and over and over, right?  This programming format is called an unconditional loop because it always loops back to repeat the same code no matter what.  When we are finished with it, we can close it like any other Windows program by double-clicking on the system menu box.

Next Section: IF . . . THEN - Adding Smarts to Our Tax Program


IF . . . THEN - Adding Smarts to Our Tax Program

The program we designed above will only do one thing for us, no frills.  Let's learn how to add some smarts to our program.  One way that this can be done is with the if . . . then statement.

The if . . . then statement is a direct descendant of those do-it-yourself style instruction manual texts.  For example:

    Problem: Your car's engine won't turn over

      1) Check your battery for correct voltage.

      2) If voltage is less then 11 volts then goto to step 13

      3) Clean and tighten ground connection.

      4) If this doesn't solve the trouble, continue to step 5.

      5) Remove the starter.

      6) Connect starter directly to battery

      7) If starter does not spin then goto step 18

      8) Check starter relay.

      . . .

    13) Charge battery

      . . .

    18) See chapter 4 on rebuilding the starter unit

Notice how in the above example how you are led smartly through the troubleshooting procedure.  The steps containing the words if and then make it possible to intelligently work through a complex procedure.  In the same way, the if . . . then statement in BASIC makes it possible to add a kind of  intelligence to your programs.

Let's see how we can apply this.  Suppose we want the computer to give us the option to display instructions about how to use our tax program.  An easy way to add this ability would be to display instructions whenever a zero value is entered as our dollar amount.

Now, whenever input is used to get a number from the user, if the user doesn't type a number but only presses the [Enter] key, then Liberty BASIC substitutes zero for the variable.  We can exploit this feature in our salestax.bas program.  By checking to see if the variable equals zero after the input statement, we can decide whether or not to display instructions.

Here's what our new program looks like:

[start]

    print "Type a dollar and cent amount."

    input "(Press 'Enter' alone for help) ?"; amount

    if amount = 0 then goto [help]

    let tax = amount * 0.05

    print "Tax is: "; tax; ". Total is: "; tax + amount

    goto [start]

[help]

    print "This tax program determines how much tax is"

    print "due on an amount entered and also computes"

    print "the total amount.  The tax rate is 5%"

    goto [start]

Notice the line "if amount = 0 then goto [help]" in the program above.  When Liberty BASIC execute this line, it checks to see if the variable is equal to 0.  If the variable equals zero, then the goto[help] statement in the line is executed.  Liberty BASIC then begins executing the instructions after the [help] branch label.  The if...then statement reads exactly like it executes.

Actually, the goto part of the if . . . then statement is optional.  Either of these two forms is acceptable:

if amount = 0 then goto [help]

    - or -

if amount = 0 then [help]

Comparing numbers  -  The = (equality) operator is only one of several that can be used to make decisions in an if . . . then statement.  We can use the if . . . then statement and the ( =, <>, <, >, <=, >= ) operators to determine whether:

    a = b     	a is equal to b

    a <> b     	a is unequal to b

    a < b   	a is less than b

    a > b     	a is greater than b

    a <= b     	a is less than or equal to b

    a >= b     	a is greater than or equal to b

For example, instead of checking to see if amount was equal to 0 in the above program, we could have checked to see whether it was less than 0.01 (or one cent).  For example:

    if amount < 0.01 then goto [help]

When you run the program above you will probably notice that the things displayed sort of run together. There are things that we can do to neaten up the appearance of a BASIC program.  We can add extra blank lines between our printed output to break things up.  This is done by using an empty print statement, one for each blank line.  We can also clear the window at an appropriate time with the cls statement.  Both of these techniques are applied to our tax program in the listing below:

[start]

    print "Type a dollar and cent amount."

    input "(Press 'Enter' alone for help) ?"; amount

    if amount = 0 then [help]

    let tax = amount * 0.05

    print "Tax is: "; tax; ". Total is: "; tax + amount

    goto [start]

[help]

    cls

    print "SALESTAX.BAS Help"

    print

    print "This tax program determines how much tax is"

    print "due on an amount entered and also computes"

    print "the total amount.  The tax rate is 5%."

    print

    input "Press [Enter] to continue."; dummyVariable

    print

    goto [start]

Notice the line 'input "Press [Enter] to continue."; dummyVariable' in the listing.  In this example, we are using an input statement to halt  the program, so the instructions can be read.  When [Enter] is pressed as instructed, dummyVariable receives the value of what is entered.  In this case, only [Enter] is pressed, so dummyVariable gets a value of zero for its data.  It really doesn't matter what dummyVariable's data is since we don't use the variable in any calculations elsewhere (hence the name dummyVariable).

Next Section: String Variables


String Variables

So far, the only kind of variables we have used are for holding number values.  There are special variables for holding words and other non-numeric character combinations.  These variables are called string variables (they hold strings of characters*).

    *Characters are:

      Letters of the alphabet ;

      Digits 0123456789 ;

      Any other special symbols like: , . < > / ? ; : ' " [ ] { } ` ~ ! @ # $ % ^ & * ( ) + - \ |    etc . . .

Let's look at a very simple program using strings:

    input "Please type your name ?"; name$

    print "It's nice to meet you, "; name$

This two-line program asks you for your name.  Once you've typed it and pressed [Enter], it responds with:

    It's nice to meet you, your-name-here

Notice one special thing about our string variable name.  It ends with a $ (dollar sign).  In BASIC, when you want to store characters in a variable, you end the variable name with a $.  This makes it a string variable.  As you can see from our program example, you can both input and print with string variables, as we did earlier with our non-string or numeric variables.

We've actually been using strings all along, even before this section about string variables.  Whenever you saw a BASIC program line with words in quotes (for example:  print "It's nice to meet you, ") you were looking at what is called a string literal.  This is a way to directly express a string in a BASIC program, exactly the way we type numbers directly in, only with characters instead.  A string literal always starts with a quotation mark and always ends with a quotation mark.  No quotation marks are allowed in between the starting and ending quotation marks (point: string literals cannot contain quotation marks).

NOTE  -  A string can have zero characters.  Such a string is often called an empty string.  In BASIC, an empty string can be expressed in a string literal as two quotation marks without any characters between them.  For example (noCharactersHere$ is the name of our string variable):

    let noCharactersHere$ = ""

Next Section: Some things to do with Strings


Functions

Now that we've covered bringing data into your programs with input, displaying data with print, keeping data in string and numeric variables, and controlling program flow with if . . . then, we will bring one more way to light to flesh out your programs.  Functions provide a means for manipulating program data in meaningful ways.

    Look this short program:

    input "Please type your name ?"; name$

    print "Your name is "; len(name$); " characters long."

The second line demonstrates the use of the len( ) function.  The len( ) function returns the number of characters in a string.  The expression inside of the parenthesis must either be a string literal, a string variable, or an expression that evaluates to be a string.  This identifies len( ) as a string function.  There are other string functions (for example: val( ), trim$( ) ).  The result returns is a number and can be used in any mathematical expression.

There are numeric functions as well.  For example:

[start]

    let count = count + 1

    print "The sine of  "; count; " is "; sin(count)

    if count < 45 then goto [start]

This simple program lists the sines for the values from 1 to 45.  The sin( ) function takes the value of count enclosed in parenthesis and returns the sine (a function in trigonometry, a branch of mathematics) for that value.  Just like the len( ) function above, cos( ) and other numeric functions can be used as parts of bigger expressions.  We will see how this works just a little further along.

Notice also the way the program counts from 1 to 45.  On the first pass, count is equal to zero until it gets to the line  let count = count + 1  which makes sets the data for variable count to be one more than its value at that point.  Then the program prints the sine of count (the sine of one, in other words).  After this, the line  if count < 45 goto [start]  checks to see if the data for count is less than 45.  If it is, then BASIC goes back to the branch label [start] to do it again.  This happens over and over until count reaches a value of 45, and then it doesn't go back to [start] again, but instead having no more lines of code to run, the program stops.

Going back to execute code over again is called looping.  We saw this earlier when we first used the goto statement.  In our first use of goto, the program always looped back.  In this newest example program we see going back to execute code over again, but based on a condition (in this case whether count is less than 45).  This is called conditional looping (you guessed it, the looping that always happens is called unconditional looping, or infinite looping).

Next Section: Documenting BASIC Code


Documenting BASIC Code

When writing very short and simple BASIC programs, it isn't usually difficult to grasp how they work when reading them days or even weeks later.  When a program starts to get large then it can be much harder.  There are things that the programmer (yes, you) can do to make BASIC programs more understandable.

VARIABLES  -  Liberty BASIC makes it easy to give your variables very meaningful names.  Since a variable name can be as long as you like and because Liberty BASIC lets you use upper and lower case letters, variable names can be very meaningful.

    For example:

        let c = (a^2 + b^2) ^ 0.5

    could better be expressed:

        let lengthOfCable = (distanceFromPole ^ 2 + heightOfPole ^ 2) ^ 0.5

Both are valid Liberty BASIC code, but the second is easier to read and maintain.

BRANCH LABELS  -  Make sure that when you use goto that your branch labels describe the kind of activity your BASIC program performs after the label.  For example if you are branching to a routine that displays help then use [help] as your branch label.  Or if you are branching to the end of your program you might use [endProgram] or [quit] as branch labels.

COMMENTING CODE  -  BASIC also has a built in documentation feature that lets you add as much commentary as you like in the language of your choice.  The rem (short for remark) statement lets you type whatever you like after it (you can even misspell or type gobbledy-gook, it doesn't care!).  For example:

[askForName]

    rem  Ask for the user's name

    input "What is your name ?" ; yourName$

    rem  If the user didn't type anything, then ask again

    if yourName$ = "" then goto [askForName]

Notice how a rem statement was added before the input statement and before the if . . . then statement to describe what they should do.  Liberty BASIC just skips over these lines, but a human reader finds this kind of documentation very helpful.

Also see the way that blank lines were added between the different parts of the program?  These help to group things together, making the program easier to read.

A more elegant form of the rem statement uses the ' (apostrophe, the key just to the left of the Enter key). Instead of typing rem, substitute the ' like so:

[askForName]

    '  Ask for the user's name

    input "What is your name ?" ; yourName$

    '  If the user didn't type anything, then ask again

    if yourName$ = "" then goto [askForName]

Most people consider this to be more readable than using rem, and it works the same.  One extra thing that you can do only with the apostrophe version of rem is to hang it off the end of whatever line you are commenting.  For example:

[askForName]

    input "What is your name ?" ; yourName$     '  Ask for the user's name

    if yourName$ = "" then goto [askForName]  '  The user didn't type anything. Ask again

This optional, but it saves screen space and many prefer it.

Learning to document the programs you write takes practice.  Try to develop a consistent style.  Everyone does it differently and there isn't a right or wrong way to do it.  Smaller programs may not need any documentation at all.  Programs that you intend to share with others should probably be thoroughly documented.

Next Section: Let's Write a Program  -  HILO.BAS


Let's Write a Program  -  HILO.BAS

Now we will write a simple game using all of the concepts described in this tutorial.  These include:

    input statement

    print statement

    let statement

    variables

    goto statement

    conditional branching with if . . . then

    functions

    documenting

THE GAME  -  HILO.BAS

Hi-Lo is a simple guessing game.  The computer will pick a number between 1 and 100.  Our job is to guess the number in as few guesses as we can.  When we guess, the computer will tell us to guess higher or to guess lower depending on whether we guessed too high or too low.  When we finally get it, the computer will tell us how many guesses it took.

Let's outline how our program will work before we begin to write code:

(1) Pick a number between 1 and 100

(2) Print program title and give some instructions

(3) Ask for the user to guess number

(4) Tally the guess

(5) If the guess is right go to step (9)

(6) If the guess is too low tell the user to guess higher

(7) If the guess is too high tell the user to guess lower

(8) Go back to step (3)

(9) Beep and tell the user how many guess it took to win

(10) Ask the user whether to play again

(11) If the user answers yes then clear the guess tally and goto step (1)

(12) Give the user instructions on how to close the game window

(13) End the program

When we write an outline for a computer program like we did here, the resulting outline is often called pseudocode, which is a fancy name for false code.  This can be a useful tool for planning out software before it is written, and it can be very helpful in developing ideas before code is actually written.

Now we are going to take each of the steps above and write BASIC code for each step.  We will document the code to explain its purpose:

(1) Pick a number between 1 and 100

    ' Here is an interactive HI-LO

    ' Program

[start]

    guessMe = int(rnd(1)*100) + 1

The first couple of lines are just ' remark statements to give a brief desciption for the program.  The [start] branch label is equivalent to calling this part of the program step

Now we have the code that picks the number.  We use two functions here to accomplish this task:

The rnd( ) function is the key to this line of code.  It picks a random (or nearly random) number greater than 0 and less than 1 (for example 0.3256).  Then we multiply this by 100 with the * operator to get a number between greater than 0 but less then 100 (0.3256 times 100 would be 32.56) ;

The int( ) function removes the fractional part to leave only the integer part of the number (the 0.56 part of 32.56 would be removed to leave only 32).

Then we add 1 to this.  This is necessary because we want to pick a number as small as 1 and as large as 100.  The rnd( ) function only gives us a number as large as 0.9999999 and not as large as 1.  If you multiply 0.9999999 by 100, the biggest number you can get is 99.99999, and this is not big enough so we add one.

When we have picked the number, we assign its value to the variable guessMe.

2) Print program title and give some instructions

    ' Clear the screen and print the title and instructions

    cls

    print "HI-LO"

    print

    print "I have decided on a number between one"

    print "and a hundred, and I want you to guess"

    print "what it is.  I will tell you to guess"

    print "higher or lower, and we'll count up"

    print "the number of guesses you use."

    print

This very simple part of the program wipes the window clean and prints the title HI-LO and some instructions.  Notice the use of blank print statements to add space between the title and the instructions and after the instructions also.

3) Ask for the user to guess number

[ask]

    ' Ask the user to guess the number and tally the guess

    input "OK.  What is your guess"; guess

Here the branch label [ask] will let us go back here later if the user needs to be asked to guess again.  Then we use the input statement to ask the user for a guess.  The user's guess is then placed in the guess (what else?) variable.

4) Tally the guess

    ' Now add one to the count variable to count the guesses

    let count = count + 1

Now we take the value of count and add one to it.  Each time this code is performed, count's value will increase by one.

5) If the guess is right go to step (9)

    ' check to see if the guess is right

    if guess = guessMe then goto [win]

This line compares the variable guess with the variable guessMe.  If they are equal, then we goto [win], which is equivalent to . . . go to step (9) in our outline.

6) If the guess is too low tell the user to guess higher

    ' check to see if the guess is too low

    if guess < guessMe then print "Guess higher."

This line compares the variable guess with the variable guessMe.  If guess is less than guessMe, then display the text "Guess higher."

7) If the guess is too high tell the user to guess lower

    ' check to see if the guess is too high

    if guess > guessMe then print "Guess lower."

This line compares the variable guess with the variable guessMe.  If guess is greater than guessMe, then display the text "Guess lower."

8) Go back to step 3

    ' go back and ask again

    goto [ask]

9) Beep and tell the user how many guess it took to win

[win]

    ' beep once and tell how many guesses it took to win

    beep

    print "You win!  It took"; count; "guesses."

    ' reset the count variable to zero for the next game

    let count = 0

This is the code our game executes when the player wins.  The beep statement rings the terminal bell once. Then the print statement says that the game is won and how many guesses it took.  Finally, the let statement resets the count variable to zero for the next game.

10) Ask the user whether to play again

    ' ask to play again

    input "Play again (Y/N)"; play$

This input statement asks whether or not to play again.  The resulting string is stored in the string variable called play$.

11) If the user answers yes then goto step 1

    if instr("YESyes", play$) > 0 then goto [start]

This if . . . then statement uses the instr( ) function to determine whether the player answered "Y", "y", "YES",  or "yes".  The instr( ) function checks to see if the string in play$ is found anywhere in the string literal "YESyes".  If it is, then instr( ) returns the position, which is then compared with 0 using the > operator.  If the contents of play$ are found in "YESyes", then the value returned from instr( ) will be greater than zero, so that goto [start] will be executed, and the game will be restarted.

12) Give the user instructions on how to close the game window

    print "Press ALT-F4 to close this window."

Since the player did not wish to play again, we will display instructions on how to close the game window.

13) End the program

    end

It is good practice place the end statement as the end of your BASIC programs.  It can also be used at any place where you want the program to stop running.

Next Section: Complete listing for HILO.BAS


Complete listing for HILO.BAS

Here is the complete listing for HILO.BAS so that you can just copy and paste it into Liberty BASIC to run it.

    ' Here is an interactive HI-LO

    ' Program

[start]

    guessMe = int(rnd(1)*100) + 1

    ' Clear the screen and print the title and instructions

    cls

    print "HI-LO"

    print

    print "I have decided on a number between one"

    print "and a hundred, and I want you to guess"

    print "what it is.  I will tell you to guess"

    print "higher or lower, and we'll count up"

    print "the number of guesses you use."

    print

[ask]

    ' Ask the user to guess the number and tally the guess

    input "OK.  What is your guess?"; guess

    ' Now add one to the count variable to count the guesses

    let count = count + 1

    ' check to see if the guess is right

    if guess = guessMe then goto [win]

    ' check to see if the guess is too low

    if guess < guessMe then print "Guess higher."

    ' check to see if the guess is too high

    if guess > guessMe then print "Guess lower."

    ' go back and ask again

    goto [ask]

[win]

    ' beep once and tell how many guesses it took to win

    beep

    print "You win!  It took"; count; "guesses."

    ' reset the count variable to zero for the next game

    let count = 0

    ' ask to play again

    input "Play again (Y/N)"; play$

    if instr("YESyes", play$) > 0 then goto [start]

    print "Press ALT-F4 to close this window."

    end


Some things to do with Strings

Just as you can manipulate numbers in a computer programming language by adding, subtracting, multiplying, and dividing, (and more!), we can also manipulate strings

Adding strings  -  We can add (or concatenate) two or more strings together in BASIC like so:

    input "What is your first name ?"; firstName$

    input "What is your last name ?"; lastName$

    let fullName$ = firstName$ + " " + lastName$

    print "Your full name is: "; fullName$

In this short program, we input two strings, your first and last name.  Then we concatenate the string in firstName$ with the string literal  " "  (a single space between two quotes) and with lastName$.  The result is made the data for the string variable fullName$, which we then print out.

Comparing strings  -  We can compare strings with each other just as we can compare numbers.  This means that we can use the if . . . then statement and the ( =, <>, <, >, <=, >= ) operators to determine whether:

    a$ = b$	a$ is equal to b$

    a$ <> b$	a$ is unequal to b$

    a$ < b$   	a$ is less than b$

    a$ > b$	a$ is greater than b$

    a$ <= b$	a$ is less than or equal to b$

    a$ >= b$	a$ is greater than or equal to b$

When comparing strings, a string is considered to be equal to another string when all the characters in one string are exactly  the same in both strings.  This means that even if they both print the same onto the screen, they can still be unequal if one has an invisible space on the end, and the other doesn't.  For example:

    a$ = "Hello"

    b$ = "Hello "

    print "a$ is "; a$

    print "b$ is "; b$

    if a$ = b$ then goto [areTheSame]

    print "a$ and b$ are not the same"

    goto [end]

[areTheSame]

    print "a$ and b$ are the same"

[end]

When the line  if a$ = b$ then goto [same]  is performed, the result is not to goto [same], because even though if a$ and b$ were printed they would look  the same, they are not actually the same.

Next Section: Functions


Week One Homework Solution

    'Week 1, homework solution 1

    'Liberty BASIC programming course

    'Copyright 1996 Shoptalk Systems

    'All Rights Reserved

[start]

    'Ask for a dollar and cent amount

    print "Type a dollar and cent amount."

    input "(Press 'Enter' alone for help) ?"; amount

    'Display help if user pressed [Enter]

    if amount = 0 then [help]

    'Calculate tax for the nearest 20 cent multiple

    dollars = int(amount)

    dollarsTax = dollars * 0.05

    cents = amount - dollars

    adjustment = 0.10

    centsAdjusted = cents + adjustment

    centsRounded = int(centsAdjusted / 0.20) * 0.20

    centsTax = (centsRounded / 0.20) / 100

    tax = dollarsTax + centsTax

    'Display the amount, tax, and total

    'Add 0.001 to compensate for any loss of precision because

    'we are dealing with monetary values.

    print "Product Price $"; using("#####.##", amount + 0.001)

    print "Sales Tax      "; using("#####.##", tax + 0.001)

    print "------------------------"

    print "Total          "; using("#####.##", tax + amount + 0.001)

    print

    goto [start]

[help]

    cls

    print "SALESTAX.BAS Help"

    print

    print "This tax program determines how much tax is"

    print "due on an amount entered and also computes"

    print "the total amount.  The tax rate is 5%."

    print

    input "Press [Enter] to continue."; dummyVariable

    cls

    goto [start]


Week Two Homework

Week 2 Homework

Liberty BASIC programming course

Copyright 1996 Shoptalk Systems

All Rights Reserved

FOR/NEXT Loops

Up to this point most of the loops that we've used in our code have had this form:

    variable = 0

[loopPoint]

    'put some code in here

    variable = variable + 1

    if variable < 10 then [loopPoint]

This will loop 10 times.  The first time through the loop the value of variable will be 0, and the tenth time through the loop it will be 9.  This kind of coding does work, but it can look complicated and it takes some care to make sure it is coded properly (it's easy to make a mistake).  Now let's take a look at an alternative:

    for variable = 0 to 9

        'put some code here

    next variable

This is called a FOR/NEXT loop.  The code that is inserted between the FOR and NEXT statement will be executed 10 times.  The value of variable will be 0 the first time through and 9 the last time.

We can use this kind of code to do something a specifed number of times or to do something using a range of values (for example 0 to 9).

FOR/NEXT loops require fewer lines of code.  When programming, it is usually best to use the simplest possible way to code any solution.  This kind of simplicity doesn't come without a price.  There are things you can do with the first kind of looping that you cannot do with a FOR/NEXT loop.

Look at this example:

    variable = 0

[loopPoint]

    'put your code here

    if someCondition = 1 then [specialException]

    variable = variable + 1

    if variable < 10 then [loopPoint]

Notice the line of code that branches to [specialException] if someCondition is equal to 1.  This is perfectly acceptable coding practice.  Now look at this example:

    for variable = 0 to 9

        'put your code here

        if someCondition = 1 then [specialException]

    next variable

This is not acceptable.  Liberty BASIC expects the loop to finish properly.  It is OK to use GOSUB to call a subroutine from inside a FOR/NEXT loop because when the subroutine returns, execution will resume inside the loop and it will be allowed to finish properly.

A good question to ask when deciding whether to use a FOR/NEXT loop is "Do I know how many times this loop will be executed?"  In our example above we don't know because at any time someCondition could suddenly be equal to

1, and then our loop is finished.

You can cheat.  If you set the value of variable to its finishing value (or limit), the loop will think it's all done and it will quit.  For example the following code will only count to 5.

    for x = 0 to 9

        print x

        if x = 5 then x = 9

    next x

It's also worth noting that FOR/NEXT loops don't need to use integer values.  Here is an example that counts from 0 to 1 in steps of less than 1.

    for i = 0 to 1 step 0.04

        print i

    next i

By using the word STEP and a value we specified what value to add to our variable (i in this case).  We can count backwards using a similar technique.

    for j = 10 to 1 step -1

        print j

    next j

Finally, if a starting value for the variable in our FOR/NEXT loop is greater than its limit, the code between the FOR and NEXT statements will be skipped over.  The following code will not display any numbers at all.

    for x = 1 to 0

        print x  'this line will not be executed

    next x

Similarly...

    for x = 0 to 1 step -1

        print x  'this line will not be executed

    next x

Here is our ARRAYS.BAS program taken from our Week 2 Solutions to Challenge Exercises.  We've modified it by replacing one of the loops with a FOR/NEXT loop.  Study it carefully.

    'ARRAYS.BAS

    'List handling with arrays

    'Here is a version that displays the names entered

    'after [quit]

    dim names$(10)  'set up our array to contain 10 items

[askForName]  'ask for a name

    input "Please give me your name ?"; yourName$

    if yourName$ = "" then print "No name entered." : goto [quit]

    index = 0

[insertLoop]

    'check to see if index points to an unused item in the array

    if names$(index) = "" then names$(index) = yourName$ : goto [nameAdded]

    index = index + 1 'add 1 to index

    if index < 10 then [insertLoop] 'loop back until we have counted to 10

    'There weren't any available slots, inform user

    print "All ten name slots already used!"

    goto [quit]

[nameAdded]  'Notify the name add was successful

    print yourName$; " has been added to the list."

    highestSlot = index  'this is a new line of code

    goto [askForName]

[quit]

    'display all the entered names

    print

    print "Here is a list of the names entered:"

    print "------------------------------------"

    for index = 0 to highestSlot

         if names$(index) <> "" then print names$(index)

    next index

    end

Here's the assignment

1) In similar fashion to our modification of ARRAYS.BAS (above), modify the AGES.BAS program below, replacing appropriate looping code with FOR/NEXT loops.

    'AGES.BAS

    'Accept some names and ages from the user, then total and average them

    dim numbers(20)

    dim names$(20)

    print "AGES.BAS"

    print

    'loop up to 20 times, getting numbers

    print "Enter up to 20 non-zero values."

    print "A zero or blank entry ends the series."

[entryLoop]  'loop around until a zero entry or until index = 20

    'get the user's name and age

    print "Entry "; index + 1;

    input name$

    if name$ = "" then [endSeries]  'quit if name$ is blank

    print "Age   ";

    input age

    index = index + 1       'add one to index

    names$(index) = name$   'set the specified array item to be name$

    numbers(index) = age    'set the specified array item to be age

    total = total + age     'add entry to the total

    if index = 20 then [endSeries]  'if 20 values were entered, exit loop

    goto [entryLoop]  'go back and get another entry

[endSeries]  'entries are finished

    'Set entryCount to index

    entryCount = index

    if entryCount = 0 then print "No Entries." : goto [quit]

    print "Entries completed."

    print

    print "Here are the "; entryCount; " entries:"

    print "-----------------------------"

    'This loop displays each entered value in turn.

    'Notice that we re-use the index variable.  It

    'can be confusing to use a new variable for each

    'new loop.

    index = 0

[displayLoop]

    index = index + 1

    print "Entry "; index; " is "; names$(index); ", age "; numbers(index)

    if index < entryCount then [displayLoop]

    'Now display the total and average value

    print

    print "The total age is "; total

    print "The average age is "; total / entryCount

[quit]

    end

2) Add a routine onto the end of AGES.BAS that asks the user for a name.  Then using a FOR/NEXT loop searches for that name in the names$ array and if the name is found displays the age for that name.


Homework Solution

Week 2 course material: Solutions to challenge exercises

Liberty BASIC programming course

Copyright 1996 Shoptalk Systems

All Rights Reserved

1) Add code to ARRAYS.BAS after [quit] to display all the names entered.

    'ARRAYS.BAS

    'List handling with arrays

    'Here is a version that displays the names entered

    'after [quit]

    dim names$(10)  'set up our array to contain 10 items

[askForName]  'ask for a name

    input "Please give me your name ?"; yourName$

    if yourName$ = "" then print "No name entered." : goto [quit]

    index = 0

[insertLoop]

    'check to see if index points to an unused item in the array

    if names$(index) = "" then names$(index) = yourName$ : goto [nameAdded]

    index = index + 1 'add 1 to index

    if index < 10 then [insertLoop] 'loop back until we have counted to 10

    'There weren't any available slots, inform user

    print "All ten name slots already used!"

    goto [quit]

[nameAdded]  'Notify the name add was successful

    print yourName$; " has been added to the list."

    goto [askForName]

[quit]

    'display all the entered names

    print

    print "Here is a list of the names entered:"

    print "------------------------------------"

    index = 0

[displayLoop]

    if names$(index) <> "" then print names$(index)

    index = index + 1

    if index < 10 then [displayLoop]

    end

2) Modify the AVERAGE.BAS program so it asks for a name and age for up to 20 people.  The names should be managed in a string array (remember ARRAYS.BAS?) that you'll add to the program.  When the list of entries is displayed, each name is to be displayed with its age.  Then the total and average age will be displayed after this.  Call the program AGES.BAS.

    'AGES.BAS

    'Accept some names and ages from the user, then total and average them

    dim numbers(20)

    dim names$(20)

    print "AGES.BAS"

    print

    'loop up to 20 times, getting names and ages

    print "Enter up to 20 names with ages."

    print "A blank name entry ends the series."

[entryLoop]  'loop around until a zero entry or until index = 20

    'get a name and age

    print "Name "; index + 1;

    input name$

    if name$ = "" then [endSeries]  'quit if name$ is blank

    print "Age   ";

    input age

    index = index + 1       'add one to index

    names$(index) = name$   'set the specified array item to be name$

    numbers(index) = age    'set the specified array item to be age

    total = total + age     'add entry to the total

    if index = 20 then [endSeries]  'if 20 values were entered, exit loop

    goto [entryLoop]  'go back and get another entry

[endSeries]  'entries are finished

    'Set entryCount to index

    entryCount = index

    if entryCount = 0 then print "No Entries." : goto [quit]

    print "Entries completed."

    print

    print "Here are the "; entryCount; " entries:"

    print "-----------------------------"

    'This loop displays each entered value in turn.

    'Notice that we re-use the index variable.  It

    'can be confusing to use a new variable for each

    'new loop.

    index = 0

[displayLoop]

    index = index + 1

    print "Entry "; index; " is "; names$(index); ", age "; numbers(index)

    if index < entryCount then [displayLoop]

    'Now display the total and average value

    print

    print "The total age is "; total

    print "The average age is "; total / entryCount

[quit]

    end


Week Three - Reading and Writing Sequential Files

Week 3 course material

Liberty BASIC programming course

Copyright 1996 Shoptalk Systems

All Rights Reserved

Reading and Writing Sequential Files

Now we will learn about how to work with disk files.  With few exceptions, all personal computers have at least one floppy disk drive and one hard disk drive.  Liberty BASIC provides ways to write information to disk files on these devices, and we can take advantage of this when we write our programs.

There are two ways to read and write files in Liberty BASIC.  One is called sequential and the other is called random access.  We will use the sequential method for our examples here.  The reason it is called sequential is that

when reading or writing we start at the beginning of the file and work one item at a time to the end.  An item can be words or characters separated by commas, or an item can be a complete line of data.

Let's familiarize ourselves with a few Liberty BASIC statements that help us work with files.

OPEN

The OPEN statement causes Liberty BASIC to open a file.  A file must be opened if we want to write into it or read from it.  There are several ways to open any file for sequential access.  These 'ways' are called modes.

The OUTPUT mode:  The OUTPUT mode is for writing to a file.  This is what an OPEN statement for OUTPUT looks like:

    open "myfile.txt" for output as #myHandle

You can see the OUTPUT mode is specified.  The last item on the line is #myHandle.  It is a name (called a file handle) given to Liberty BASIC to use for the open file.  Any code that writes to this file must include a reference to #myHandle.  This is so that Liberty BASIC knows which file to write to.

A file handle starts with a # character followed by any word or sequence of characters (using letters and digits).  It is best to choose handles that make it easy for you to remember which file you are working with while writing your program.  Some examples of valid file handles are:

    #1

    #abc

    #dataFile

    #customers

You cannot have more than one file open at a time that uses the same file handle or your program will terminate with an error.

The INPUT mode:  There is also a mode for reading sequentially from a file. This mode is called INPUT.  Here is an example of an OPEN statement for INPUT:

    open "myfile.txt" for input as #myHandle

CLOSE

The CLOSE statement is used for closing open files when we are done reading or writing them.  This is a required operation when working with files and it is a very important thing to remember when writing programs.

Here is how OPEN and CLOSE work together:

    open "myfile.txt" for output as #myHandle

    'put some code in here that writes to #myHandle

    close #myHandle

Another thing to remember is that a file opened for one mode must first be closed before it can be opened for a different mode.  If you are going to read from a file you've just written to, you must close the file and reopen

it, like so:

    open "myfile.txt" for output as #myHandle

    'put some code in here that writes to #myHandle

    close #myHandle

    open "myfile.txt" for input as #myHandle

    'put some code in here that reads from #myHandle

    close #myHandle

PRINT

We've already seen how the PRINT statement can display text into a window on the screen.  PRINT can also be used to write into a file opened for sequential OUTPUT.  Here is an example:

    open "myfile.txt" for output as #myHandle

    print #myHandle, "Hello"

    print #myHandle, "World!"

    close #myHandle

This little program produces a file containing two lines of text (each could be considered an item, see above).  Type the code in and run it.  When the program finishes executing, open Windows Notepad on MYFILE.TXT to see the

result!

Let's Take It For A Spin

Now we'll modify the AGES.BAS program from out WK2SOL.TXT file so that it saves the names and ages that we enter into a file.  Take a look at this modified program:

    'AGES.BAS

    'Accept some names and ages from the user, then total and average them

    dim numbers(20)

    dim names$(20)

    print "AGES.BAS"

    print

    'loop up to 20 times, getting numbers

    print "Enter up to 20 non-zero values."

    print "A zero or blank entry ends the series."

[entryLoop]  'loop around until a zero entry or until index = 20

    'get the user's name and age

    print "Entry "; index + 1;

    input name$

    if name$ = "" then [endSeries]  'quit if name$ is blank

    print "Age   ";

    input age

    index = index + 1       'add one to index

    names$(index) = name$   'set the specified array item to be name$

    numbers(index) = age    'set the specified array item to be age

    total = total + age     'add entry to the total

    if index = 20 then [endSeries]  'if 20 values were entered, exit loop

    goto [entryLoop]  'go back and get another entry

[endSeries]  'entries are finished

    'Set entryCount to index

    entryCount = index

    if entryCount = 0 then print "No Entries." : goto [quit]

    print "Entries completed."

    print

    print "Here are the "; entryCount; " entries:"

    print "-----------------------------"

    'This loop displays each entered value in turn.

    'Notice that we re-use the index variable.  It

    'can be confusing to use a new variable for each

    'new loop.

    for index = 1 to entryCount

      print "Entry "; index; " is "; names$(index); ", age "; numbers(index)

    next index

    '*** New code starts here ***

    'Write the data into ages.dat

    open "ages.dat" for output as #ages

    for index = 1 to entryCount

      print #ages, names$(index)

      print #ages, numbers(index)

    next index

    close #ages

    '*** New code ends here ***

    'Now display the total and average value

    print

    print "The total age is "; total

    print "The average age is "; total / entryCount

[quit]

    end

Type the new program lines in.  Run the program and enter a few names and ages.  When the program finishes executing, open the file with Notepad and you'll the data you entered.  It should look something like:

Tom Jones

52

Victor Krueger

39

Sue White

64

Let's see how our newly added code works.

1) First we open the file AGES.DAT with the OPEN statement.  It is opened for OUTPUT (writing) and its file handle is #ages.

    open "ages.dat" for output as #ages

2) This sets up a FOR/NEXT loop.

    for index = 1 to entryCount

3) Now we print a name and age, each on a separate line.

    print #ages, names$(index)

    print #ages, numbers(index)

4) Here's the back end of our FOR/NEXT loop.  Loop back until index equals entryCount.

5) Now we will close AGES.DAT.

    close #ages

Reading from a file

Now that we've written information to a disk file, we are going to read that information back into our program.  This is done using the INPUT statement. Just as we saw PRINT used to display information in a window and to write information to a disk file, INPUT can be used to get keyboard input, or to get information from a disk file.

To read from a file, it must be opened using the INPUT mode.  The OPEN statement is used like so:

    open "ages.dat" for input as #ages

Our INPUT statement for reading from a file looks a lot like the PRINT statement above:

    input #ages, var$

Notice we use the file handle, and then we specify a variable name to read into.  In a program that reads a list of items, an INPUT statement like the one above would be placed inside of a loop.  In this way each item in the file can be read in turn and stored in an array.

An important point is that that we don't always know how many items have been written to AGES.DAT.  This means we don't know when to stop looping around and reading items from the file.  One solution is to add a PRINT statement to the program that creates the file.  This PRINT statement would write the number of items at the start of the file, like so:

    'Write the data into ages.dat

    open "ages.dat" for output as #ages

    print #ages, entryCount

    for index = 1 to entryCount

      print #ages, names$(index)

      print #ages, numbers(index)

    next index

    close #ages

Then all we would need to do is read that number first, and then loop that many times to read each name and age.  I will tackle it from a different direction though, because  I want to introduce the EOF() function.

The EOF() function stands for End Of File.  For a given file handle, it will return 0 if we are not at the end of file, and -1 if we are at the end of file.  For example:

    open "ages.dat" for input as #ages

    if eof(#ages) = 0 then print "NOT AT END OF FILE"

    close #ages

The above code would print NOT AT END OF FILE because we haven't read all the way to the end of the file.

Here is a version of AGES.BAS that reads it's information from the file we created above and uses EOF() to check for the end of file.

    'AGES_IN.BAS

    'Read names and ages from AGES.DAT, then total and average them.

    'This version doesn't write the data back out to the file.

    dim numbers(20)

    dim names$(20)

    print "AGES_IN.BAS"

    print

    print "Reading AGES.DAT..."

    'open ages.dat

    open "ages.dat" for input as #ages

[entryLoop]  'loop around until end of file or until index = 20

    'test for the end of file

    if eof(#ages) = -1 then [endSeries]

    'get the user's name and age

    input #ages, name$

    input #ages, age

    index = index + 1       'add one to index

    names$(index) = name$   'set the specified array item to be name$

    numbers(index) = age    'set the specified array item to be age

    total = total + age     'add entry to the total

    if index = 20 then [endSeries]  'if 20 values were entered, exit loop

    goto [entryLoop]  'go back and get another entry

[endSeries]  'entries are finished

    'close ages.dat

    close #ages

    'Set entryCount to index

    entryCount = index

    if entryCount = 0 then print "No Entries." : goto [quit]

    print "Entries completed."

    print

    print "Here are the "; entryCount; " entries:"

    print "-----------------------------"

    'This loop displays each entered value in turn.

    'Notice that we re-use the index variable.  It

    'can be confusing to use a new variable for each

    'new loop.

    for index = 1 to entryCount

      print "Entry "; index; " is "; names$(index); ", age "; numbers(index)

    next index

    'Now display the total and average value

    print

    print "The total age is "; total

    print "The average age is "; total / entryCount

[quit]

    end

Challenge Exercise

Create a modified ARRAYS.BAS using the code below so that it keeps its list in a disk file named ARRAYS.DAT.  The program must read its list from ARRAYS.DAT before asking for additional names.  When the user is done

entering names, or if all the slots are filled, the list will be written to ARRAYS.DAT before the program ends.  See the source code below for some clues.

    'ARRAYS2.BAS

    'List handling with arrays and file input/output.

    'This version stores more than 10 names.

    dim names$(50)  'set up our array to contain 50 items

    'Insert code that reads ARRAYS.DAT into the array names$.

[askForName]  'ask for a name

    input "Please give me your name ?"; yourName$

    if yourName$ = "" then print "No name entered." : goto [quit]

    index = 0

[insertLoop]

    'check to see if index points to an unused item in the array

    if names$(index) = "" then names$(index) = yourName$ : goto [nameAdded]

    index = index + 1 'add 1 to index

    if index < 50 then [insertLoop] 'loop back until we have counted to 50

    'There weren't any available slots, inform user

    print "All ten name slots already used!"

    goto [quit]

[nameAdded]  'Notify the name add was successful

    print yourName$; " has been added to the list."

    goto [askForName]

[quit]

    'display all the entered names

    print

    print "Here is a list of the names entered:"

    print "------------------------------------"

    for index = 0 to 49

        if names$(index) <> "" then print names$(index)

    next index

    'Insert code that saves the names$ array to ARRAYS.DAT.

    end


Week Three Homework

Week 3 Homework 1

Liberty BASIC programming course

Copyright 1996 Shoptalk Systems

All Rights Reserved

In our first Week 3 installment (WK3CM1.TXT) we covered the OUTPUT and INPUT file modes.  These are for writing and reading files, respectively.  This part of the lesson covers a new file mode called APPEND.  We will also learn how to use the LINE INPUT statement, and we will also use a few other new Liberty BASIC statements in passing.

The APPEND file mode

There is a limitation in the OUTPUT file mode.  The OUTPUT mode is only good for creating new files because it always deletes the contents of any file opened when it is used.  For example:

    'open a file and print some information into it

    open "numbers.txt" for output as #numbers

    for x = 1 to 10

        print #numbers, x

    next x

    close #numbers

    'now rewrite the file, replacing its original contents

    open "numbers.txt" for output as #numbers

    for x = 11 to 20

        print #numbers, x

    next x

    close #numbers

    end

Run the above program and open Notepad on NUMBERS.TXT.  You'll see that only the numbers 11 to 20 will be in the file.  The only way to retain the original information in the file and add new information using the OUTPUT mode would be to read the original information and insert it into a new file.  Then the new information is inserted into the still open file. Here is what that code looks like:

    'open a file, and print some information into it, then close it

    open "numbers.txt" for output as #numbers

    for x = 1 to 10

        print #numbers, x

    next x

    close #numbers

    'open the original file for input

    open "numbers.txt" for input as #in

    'open the new file for output

    open "numbers2.txt" for output as #numbers

[copyLoop]  'copy the contents of numbers.txt to numbers2.txt

    if eof(#in) then [doneCopying]  'we reached the end of the file

    input #in, value

    print #numbers, value

    goto [copyLoop]

[doneCopying]  'close numbers.txt & print new numbers into numbers2.txt

    close #in

    for x = 11 to 20

        print #numbers, x

    next x

    close #numbers

    'delete the original numbers.txt file

    kill "numbers.txt"

    'rename numbers2.txt to numbers.txt

    name "numbers2.txt" as "numbers.txt"

    end

Once the new file has been created containing the contents of the original file, plus our new numbers 11 to 20, we delete the old file using the KILL statement and rename the new file using the NAME statement.

There is a better way.  Using the APPEND file mode, we can eliminate a lot of program code.  The APPEND mode opens a file for writing, just like the OUTPUT mode does.  Instead of erasing the contents of the file opened and starting at the beginning, the APPEND mode opens a file and retains everything.  All we need to do then is write to the file, and everything we write will be added to the end of the file.  Here is the example above rewritten using the APPEND mode:

    'open a file, and print some information into it, then close it

    open "numbers.txt" for output as #numbers

    for x = 1 to 10

        print #numbers, x

    next x

    close #numbers

    'open the original file for append

    open "numbers.txt" for append as #numbers

    for x = 11 to 20

        print #numbers, x

    next x

    close #numbers

    end

This is much shorter and simpler than the last example.  When we are done writing the numbers 11 to 20 there is no need to delete or rename files.

Comma delimited files

By default, the standard way of reading from a sequential file using the INPUT statement breaks up the data we read at each end of line, and at each comma.  Look at these equivalent file contents:

File 1 contents:

1,2,3,4,5,6,7,8,9,10

File 2 contents:

1,2,3,4,5

6,7,8,9,10

File 3 contents:

1

2

3

4

5

6

7

8

9

10

All three of the above possible file contents would produce the same displayed output using a loop like the following:

[loop]  'display each item on its own line

    if eof(#in) then [stopLooping]

    input #in, item$  'read the next item

    print item$

    goto [loop]

Since the comma is used to separate items (in addition to the end of line), this kind of file format is often called 'comma delimited'.  This is a preferred generic format often used by commercial application software (spreadsheets are a good example) as a data export option.  This is so that people like you and me can write our own customized software to use that information.

What if we want to use commas in our information?  As an example, we decide that we will build an application that logs responses to a direct mail campaign, and we want the name to be entered in a single field.  The name will be entered last name first, with a comma separating the last from the first name (example: Doe, John).

Take a look at this hypothetical file entry:

Doe, John

123 Main Street

Scottsdale

AZ

01234

Now look at this code fragment:

    input #in, name$

    input #in, street$

    input #in, city$

    input #in, state$

    input #in, zip$

If we read the file entry with the above code, name$ would only contain up to the first comma.  So name$ would contain the string "Doe", street$ would contain "John", city$ would contain "123 Main Street", and so on.  This is

not what we want.

There is a special form of the INPUT statement that will read a whole line from a file, including commas.  It is the LINE INPUT statement.  Here is the code fragment above rewritten using LINE INPUT:

    line input #in, name$

    line input #in, street$

    line input #in, city$

    line input #in, state$

    line input #in, zip$

Another example of where LINE INPUT is useful is reading of a text document, like this one.  There are many commas in this document.  Here is a short program that uses the FILEDIALOG statement to ask for a *.txt file to read. Then it opens the file and reads and displays it.

    'ask for a filename

    filedialog "Select a text file", "*.TXT", filename$

    'if filename$ = "" then the user canceled the file selection

    if filename$ = "" then end

    'open the file

    open filename$ for input as #in

[readLoop]  'read and display each line in the text file

    if eof(#in) then [stopLooping]

    line input #in, lineOfText$

    print lineOfText$

    goto [readLoop]

[stopLooping]  'close the file

    close #in

    end

Run this small program and try it with a file that you know has some commas in it.  Then change the line containing the LINE INPUT statement to use a regular INPUT statement and run the program again on the same file so you can see how they function differently.

Here's the assignment

Create a program called CALLER.BAS.  This program will give the user the following options:

  1) Enter a phone call

  2) Search by caller's name

  3) Search by person called

  3) Quit

Menu item 1 will ask the user for phone call information and append it as a new record to the end of a file named PHONELOG.TXT.  The added record must include:

  1) Caller's name

  2) Name of person called

  3) Date of the call

  4) Time of the call

  5) A one line description of the call's purpose

  6) A phone # where the caller can be reached

When searching, the program should stop and display the six data items for each record that matches and ask if the user wants to keep searching or quit the search.  The program will display a notice if no matches are found.

Possible enhancement to CALLER.BAS

It is possible to search for a partial match.  Look at the following code example:

    text$ = "The quick brown fox jumped over the lazy dog."

    searchFor$ = "FOX"  'change FOX to different values and re-run

    print "Searching for:"

    print "  "; searchFor$

    print "In the following:"

    print "  "; text$

    a$ = upper$(text$)

    b$ = upper$(searchFor$)

    if instr(a$, b$) > 0 then print "Found!" else print "Not Found!"

The UPPER$() function eliminates any letter case differences.  The INSTR() function returns a 0 value if our uppercased searchFor$ is not found in our uppercased text, or it returns the position of searchFor$ if it is found.


Week 3 Homework Solution

    'CALLER.LOG

    'Here is the solution to our homework assignment for

    'week 3 of our Liberty BASIC course

    'Copyright 1996 Shoptalk Systems

    'All rights reserved

[menu]  'display menu options

    cls

    print "**Caller Log Program**"

    print

    print " 1) Enter a phone call"

    print " 2) Search by caller's name"

    print " 3) Search by person called"

    print " 4) Quit"

    print

    print "Choose an option from 1 to 4."

    input ">"; option

    if option < 1 or option > 4 then gosub [badOption]

    if option = 1 then gosub [enterAPhoneCall]

    if option = 2 then gosub [searchByCallersName]

    if option = 3 then gosub [searchByPersonCalled]

    if option = 4 then [quit]

    goto [menu]

[badOption]  'display a notice that a bad selection was made

    print

    beep

    print "Option "; option; " is unsupported."

    print "Press any key."

    dummyVar$ = input$(1)

    return

[enterAPhoneCall]  'accept a phone log entry from the user

    cls

    print "**Enter a Phone Call**"

    print

    input "Caller's name ?"; callersName$

    input "Name of person called ?"; personCalled$

    input "Date of call (press 'Enter' for "+date$()+") ?"; dateOfCall$

    if dateOfCall$ = "" then dateOfCall$ = date$()

    input "Time of call (press 'Enter' for "+time$()+") ?"; timeOfCall$

    if timeOfCall$ = "" then timeOfCall$ = time$()

    input "Purpose of call ?"; purposeOfCall$

    input "Phone # where the caller can be reached ?"; callersPhone$

[saveEditCancelLoop]  'give the user the option to save, edit or abort

    cls

    gosub [displayEntryInfo]

    print "Save, Edit, Cancel Entry (S/E/C)?";

    answer$ = input$(1)

    if answer$ = "S" or answer$ = "s" then gosub [saveEntry] : goto [menu]

    if answer$ = "E" or answer$ = "e" then gosub [editEntry]

    if answer$ = "C" or answer$ = "c" then [menu]

    goto [saveEditCancelLoop]

[displayEntryInfo]  'display call information

    print "           Caller's name : "; callersName$

    print "   Name of person called : "; personCalled$

    print "            Date of call : "; dateOfCall$

    print "            Time of call : "; timeOfCall$

    print "         Purpose of call : "; purposeOfCall$

    print "Caller can be reached at : "; callersPhone$

    return

[editEntry]  'edit call information

    cls

    print "**Edit Caller Entry**"

    print

    print "           Caller's name : "; callersName$

    input "  Press Enter, or retype > "; newEntry$

    if newEntry$ <> "" then callersName$ = newEntry$

    print "   Name of person called : "; personCalled$

    input "  Press Enter, or retype > "; newEntry$

    if newEntry$ <> "" then personCalled$ = newEntry$

    print "            Date of call : "; dateOfCall$

    input "  Press Enter, or retype > "; newEntry$

    if newEntry$ <> "" then dateOfCall$ = newEntry$

    print "            Time of call : "; timeOfCall$

    input "  Press Enter, or retype > "; newEntry$

    if newEntry$ <> "" then timeOfCall$ = newEntry$

    print "         Purpose of call : "; purposeOfCall$

    input "  Press Enter, or retype > "; newEntry$

    if newEntry$ <> "" then purposeOfCall$ = newEntry$

    print "Caller can be reached at : "; callersPhone$

    input "  Press Enter, or retype > "; newEntry$

    if newEntry$ <> "" then callersPhone$ = newEntry$

    return

[saveEntry]  'write the entry info to PHONELOG.TXT

    open "PHONELOG.TXT" for append as #phones

    print #phones, callersName$

    print #phones, personCalled$

    print #phones, dateOfCall$

    print #phones, timeOfCall$

    print #phones, purposeOfCall$

    print #phones, callersPhone$

    close #phones

    return

[searchByCallersName]  'look for a phone log entry by caller's name

    cls

    print "**Search by Caller's Name**"

    print

    print "Please enter a partial name to search by."

    input ">"; searchCaller$

    if searchCaller$ = "" then [menu]  'nothing entered, abort search

    searchCaller$ = upper$(searchCaller$) 'convert to uppercase for search

    open "PHONELOG.TXT" for input as #in

    foundFlag = 0

    quitFlag = 0

    if eof(#in) = -1 then [endOfCallerSearch]

[searchByCallerLoop]

    gosub [readEntry]  'get next entry from PHONELOG.TXT

    if instr(upper$(callersName$), searchCaller$) > 0 then gosub [matched]

    if eof(#in) = 0 and quitFlag = 0 then [searchByCallerLoop]

[endOfCallerSearch]

    close #in

    if foundFlag = 0 then print "No matches."

    print "Press any key."

    dummyVar$ = input$(1)

    goto [menu]

[searchByPersonCalled]  'look for a phone log entry by person called

    cls

    print "**Search by Person Called**"

    print

    print "Please enter a partial name to search by."

    input ">"; searchCalled$

    if searchCalled$ = "" then [menu]  'nothing entered, abort search

    searchCalled$ = upper$(searchCalled$) 'convert to uppercase for search

    open "PHONELOG.TXT" for input as #in

    foundFlag = 0

    quitFlag = 0

    if eof(#in) = -1 then [endOfCalledSearch]

[searchByCalledLoop]

    gosub [readEntry]  'get next entry from PHONELOG.TXT

    if instr(upper$(personCalled$), searchCalled$) > 0 then gosub [matched]

    if eof(#in) = 0 and quitFlag = 0 then [searchByCalledLoop]

[endOfCalledSearch]

    close #in

    if foundFlag = 0 then print "No matches."

    print "Press any key."

    dummyVar$ = input$(1)

    goto [menu]

[readEntry]  'read the next entry from PHONELOG.TXT

    line input #in, callersName$

    line input #in, personCalled$

    line input #in, dateOfCall$

    line input #in, timeOfCall$

    line input #in, purposeOfCall$

    line input #in, callersPhone$

    return

[matched]  'stop and show a match & ask what to do next

    foundFlag = 1

    print "---------Match---------"

    gosub [displayEntryInfo]

    print

    print "Next Entry, Quit Searching (N/Q)?"

    answer$ = input$(1)

    'only check for quit response

    if answer$ = "Q" or answer$ = "q" then quitFlag = 1

    return

[quit]  'end CALLER.BAS here

    end


Week Four
Week Four - Programming the Windows Graphical User Interface

Week 4 course material

Liberty BASIC programming course

Copyright 1996 Shoptalk Systems

All Rights Reserved

Programming the Windows Graphical User Interface

What is a GUI (graphical user interface) anyway?  This probably means different things to different people but to most it means a way of controlling a computer that involves manipulating windows (rectangular areas displayed on a computer screen) using a mouse or other pointing device.

If you are using Microsoft WINDOWS then you are using a software system with a GUI (we will use all uppercase when referring to Microsoft's product and not to any kind of window in general).  WINDOWS isn't just a GUI, but it does include one.

You have been using the WINDOWS GUI all along while working through this Liberty BASIC course.  The programs that you've written each run in a window that is managed by the WINDOWS GUI.  This simple text-only window is called a 'main window' in Liberty BASIC lingo.  This part of the course is concerned with teaching how to do fancier things in the way of designing windows for your programs.

In this section we will learn:

1) How to use Liberty BASIC to open windows in the WINDOWS GUI;

2) How to set the size, position, and type of our windows;

3) How to add buttons and other controls to our windows;

4) How to program the buttons and other controls to do what we want

Note: All of the program examples in this section are also provided as individual program files.

Your First Window

Type in the following short program and run it.  Insert your name in the place where it says myname.

    'WK4PROG1.BAS

    'open a window

    open "myname's first window!" for window as #myFirst

    'now stop and wait

    wait

    end

A window will appear with your name in the title!  That was easy, wasn't it?  Now click on the program's initial window (called its 'main window'). Now close the window and the program will end.

Liberty BASIC uses file statements like OPEN, CLOSE and PRINT to manage and control windows.  Once a window is opened, we can send it commands by PRINTing to it.

Note: There is nothing to prevent a Liberty BASIC program from opening more than one window as demonstrated.  All you need is multiple OPEN statements, and each window must have a unique handle (the window in the example above has the handle #myFirst).

Adjusting Window Size and Position

Let's change the size of our Window.  This is done by changing the value of two special variables WindowWidth and WindowHeight before opening the window.  We can also determine the opening position of the window by adjusting two other special variables UpperLeftX and UpperLeftY.  This positioning is relative to the upper-left corner of the display screen.  Take a look at this example:

    'WK4PROG2.BAS

    'open a window sized at 300 by 100 at position 200, 150

    WindowWidth = 300

    WindowHeight = 100

    UpperLeftX = 200

    UpperLeftY = 150

    open "myname's first window!" for window as #myFirst

    'now stop and wait

    wait

    end

Adding a button

Now let's add a button to our window.  Look at the following short program:

    'WK4PROG3.BAS

    'open a window with a button

    'the window is sized at 300 by 100 at position 200, 150

    WindowWidth = 300

    WindowHeight = 100

    UpperLeftX = 200

    UpperLeftY = 150

    button #myFirst.ok, "OK!", [okClicked], UL, 15, 15

    open "myname's first window!" for window as #myFirst

    'now stop and wait

    wait

[okClicked]  'the OK! button was clicked

    notice "You clicked on OK!"

[quit]

    'now close the window

    close #myFirst

    end

Notice the BUTTON statement we placed before the OPEN statement.  This must be inserted BEFORE the OPEN statement.  Let's break that line of code down to explain how it works.

    button #myFirst.ok, "OK!", [okClicked], UL, 15, 15

The first item in the BUTTON statement is a handle for the button.  In this case our handle is #myFirst.ok.  We are adding this button to a window that will have a handle of #myFirst, and we add .ok to extend the handle so that the button will have its own handle different from the handle of the window.

The second item is the [okClicked] branch label.  This tells Liberty BASIC to perform an implicit GOTO [okClicked] when the button is clicked on.  In this case the NOTICE statement causes a dialog box to appear that displays "You clicked on OK!".  Once you clear the dialog box the window will be closed and the program will end.

The third item (UL) tells Liberty BASIC that the button will be positioned relative to the upper-left corner of the window.  There are also UR, LL, and LR (for placing buttons relative to the upper-right, lower-left and lower-right corners).

The fourth and fifth items specify how many pixels from the (in this case upper-left) corner to place the button.

One option is to specify the exact width and height of the button.  Liberty BASIC automatically chooses the size of the button if you don't specify it.  Here's the same button, but we'll change the size to 75 pixels wide and 50 pixels high:

    button #myFirst.ok, "OK!", [okClicked], UL, 15, 15, 75, 50

The WAIT statement in our program is very important.  Liberty BASIC will react to  user actions performed on buttons or other controls when a running program is stopped at a WAIT statement (also SCAN and INPUT statements, but we won't cover those here).

Adding an Entry Field with TEXTBOX

Now let's add a textbox control to our window that let's us display and edit a line of text.  We will use the TEXTBOX statement to accomplish this.  Look at this code:

    'WK4PROG4.BAS

    'open a window with a button and a textbox

    'the window is sized at 300 by 100 at position 200, 150

    WindowWidth = 300

    WindowHeight = 100

    UpperLeftX = 200

    UpperLeftY = 150

    button #myFirst.ok, "OK!", [okClicked], UL, 15, 15

    textbox #myFirst.field, 60, 15, 200, 25

    open "myname's first window!" for window as #myFirst

    'now print some text into the textbox

    print #myFirst.field, "Type some text here."

    'now stop and wait

    wait

[okClicked]  'OK! was clicked.  Get the contents of the entry field

    'now get the contents from the textbox

    print #myFirst.field, "!contents? aString$"

    'now pop up a notice saying what was in the textbox

    notice aString$

[quit]

    'now close the window

    close #myFirst

    end

Run the example and see how it works!  Notice the TEXTBOX statement after the BUTTON statement.  Its format is very simple.

    textbox #myFirst.field, 60, 15, 200, 25

The first item #myFirst.field is a handle for the textbox.  This is just like the handle we gave to our button.

The next two items 60 and 15 are the placement of the control relative to the upper-left corner of the window.

The last two items 200 and 25 are the width and height of the control.  If it isn't wide enough, the end of the line will not appear.  If it isn't tall enough, you won't see any text in the control at all.

After our OPEN statement, see how we print some text "Type some text here." into our textbox control.  This is the text that you see in the control when the program is run.

Now look at how we changed the code after our [okClicked] branch label.  Instead of just displaying the same message every time it displays the text in our textbox control.  We accomplish this by printing a command to the textbox with the line:

    print #myFirst.field, "!contents? aString$"

This tells Liberty BASIC that we want to put the contents of that textbox control into the variable named aString$.  We could also have written this as two separate PRINT and INPUT statements as follows, and you may see other Liberty BASIC programs which do it this way:

    print #myFirst.field, "!contents?"

    input #myFirst.field, aString$

When printing a command to a textbox control, the command must start with an exclamation point.  This is how Liberty BASIC knows that we want to send a command to the textbox.  The contents of our textbox would have been replaced with "contents? aString$" if we had left out the exclamation point like so:

    'this would not work

    print #myFirst.field, "contents? aString$"

Labeling with Statictext Controls

So far our window designs have been simple enough that we haven't needed to mark any part of our window with a descriptive label or annotation.  This is an important matter in many applications where more than one textbox (or other kind of control) is used.  Here is a version of our program that uses a statictext control as a label:

    'WK4PROG5.BAS

    'open a window with a button, a textbox, and a statictext

    'the window is sized at 300 by 100 at position 200, 150

    WindowWidth = 300

    WindowHeight = 100

    UpperLeftX = 200

    UpperLeftY = 150

    button #myFirst.ok, "OK!", [okClicked], UL, 220, 35

    textbox #myFirst.field, 10, 35, 200, 25

    statictext #myFirst.label, "Type some text here.", 10, 10, 150, 25

    open "myname's first window!" for window as #myFirst

    'now stop and wait

    wait

[okClicked]  'OK! was clicked.  Get the contents of the entry field

     'now get the contents from the textbox

    print #myFirst.field, "!contents? aString$"

    'now pop up a notice saying what was in the textbox

    notice aString$

[quit]

    'now close the window

    close #myFirst

    end

See how we move the button and the textbox around in the window, and we add a text description with instructions to the user.  The format for the STATICTEXT statement is almost the same as for the TEXTBOX statement.

    statictext #myFirst.label, "Type some text here.", 10, 10, 150, 25

The first item is a string used for the text in our statictext control.

The second item #myFirst.field is a handle for the statictext.  This is just like the handle we gave to our button.

The next two items 10 and 10 are the placement of the control relative to the upper-left corner of the window.

The last two items 150 and 25 are the width and height of the control.  The size must be large enough to hold the text.  If it isn't wide enough, the end of the line will not appear.  If it isn't tall enough, you won't see any text in the control at all.

Trapping a Window Close Event

When running any of our examples above we can close our custom made window at any time by double-clicking on its menu box, by pressing Alt-F4, etc.  Then WINDOWS will go ahead and close the window, but Liberty BASIC will still think it's open.  This will cause some trouble if our code tries to perform some work with a window that just isn't there anymore.

The way around this problem is by sending the trapclose command to our window in question.  The trapclose command tells Liberty BASIC to hook into the WINDOWS event that closes our window.  Here is our program modified to use trapclose.

    'WK4PROG6.BAS

    'open a window with a button, a textbox, and a statictext

    'show how trapclose works

    'the window is sized at 300 by 100 at position 200, 150

    WindowWidth = 300

    WindowHeight = 100

    UpperLeftX = 200

    UpperLeftY = 150

    button #myFirst.ok, "OK!", [okClicked], UL, 220, 35

    textbox #myFirst.field, 10, 35, 200, 25

    statictext #myFirst.label, "Type some text here.", 10, 10, 150, 25

    open "myname's first window!" for window as #myFirst

    'send the trapclose command

    print #myFirst, "trapclose [quit]"

    'now stop and wait

    wait

[okClicked]  'OK! was clicked.  Get the contents of the entry field

    'now get the contents from the textbox

    print #myFirst.field, "!contents? aString$"

    'now pop up a notice saying what was in the textbox

    notice aString$

[quit]

    'ask if the user wants to quit

    confirm "Really Quit?"; answer$

    if answer$ <> "yes" then wait 'abort quitting

    'now close the window

    close #myFirst

    end

See how the trapclose command that we print to our window also specifies the [quit] branch label.  This means that when we try to close the window using a mouse or keyboard action (using the CLOSE statement doesn't trigger trapclose), then WINDOWS will not close the window.  Instead Liberty BASIC will execute an implicit GOTO [quit].

Notice also how we added code following the [quit] branch label.  Using a CONFIRM statement we bring up a dialog box to confirm that we really do want to quit.  If the user answers no, we execute a WAIT statement.  Once we do this, we are ready for the user to do something else.

Challenge Exercise

We can add many controls to a window in Liberty BASIC.  If we need a dozen textboxes to hold different data items and if we need a statictext to label each textbox we can do it.  Using WK4PROG6.BAS as a base, create a program that opens a window titled "Caller Record Data".  This window contains several textbox controls, each labeled by a statictext control.

The labels for our textbox controls are from our Week 3 homework:

  1) Caller's name

  2) Name of person called

  3) Date

  4) Time of call

  5) Purpose of call

  6) Caller can be reached at

When the user clicks on OK!, the program should then extract all the information entered into the textbox controls and then close the window. The information should then be displayed in the program's main window.


Week 4 Homework

Week 4 Homework

Liberty BASIC programming course

Copyright 1996 Shoptalk Systems

All Rights Reserved

Using NOMAINWIN

The default text window that opens with each Liberty BASIC program we create can be optionally turned off.  This can be desirable when we don't want our application to be operated out of a text window.  The statement that accomplishes this effect is NOMAINWIN.  Simply include NOMAINWIN in your program's source code anywhere at all.  This accomplishes two things:

1) It hides the program's main window;

2) It prevents the 'Execution Complete' notice from appearing at the end of a program run.

This comes at a cost.  Your program must close itself down properly when all the windows have been closed.  Below is a modified version of our WK4PROG6.BAS program.  In this case all we needed to do was add the NOMAINWIN statement.  No other changes are needed because we have an END statement after our example window and because we don't use the program's main window for anything.

    'WK4PROG7.BAS - (WK4PROG6.BAS modified)

    'open a window with a button, a textbox, and a statictext

    'show how trapclose works

    'the window is sized at 300 by 100 at position 200, 150

    nomainwin  'do not open a main window

    WindowWidth = 300

    WindowHeight = 100

    UpperLeftX = 200

    UpperLeftY = 150

    button #myFirst.ok, "OK!", [okClicked], UL, 220, 35

    textbox #myFirst.field, 10, 35, 200, 25

    statictext #myFirst.label, "Type some text here.", 10, 10, 150, 25

    open "myname's first window!" for window as #myFirst

    'send the trapclose command

    print #myFirst, "trapclose [quit]"

    'now stop and wait

    wait

[okClicked]  'OK! was clicked.  Get the contents of the entry field

    'now get the contents from the textbox

    print #myFirst.field, "!contents? aString$"

    'now pop up a notice saying what was in the textbox

    notice aString$

[quit]

    'ask if the user wants to quit

    confirm "Really Quit?"; answer$

    if answer$ <> "yes" then wait 'abort quitting

    'now close the window

    close #myFirst

    end

After the CLOSE #myFirst statement in our program above, we have an END statement.   If we didn't, then our program would not terminate properly, and there would be no error message to clue us in.  Always use END in your programs because just closing all the windows doesn't end the program.  There are kinds of programs which don't have windows open all the time, and even some which don't have any windows at all.

The Structure of a Liberty BASIC GUI Program

We've taken a look at some simple elements of Liberty BASIC GUI programming.  Now let's consider how to organize code in a logical and practical way when creating GUI programs in LB.

Organization

In the case of a project that uses more than one window, we should make sure that code for each window is grouped together.  Most applications are designed around a cental window that is always open and from where other windows are launched.  When the central window is closed, the application ends.  Here is a simple outline (not a working program) that demonstrates this idea:

    'MYPROG.BAS

    'This is an example outline

    'A description of the program should go here

    '-----------------------Let's define our main application window here

    button #main.1, ....

    button #main.2, ....

    textbox ....

    open "My Application" for window as #main

    print #main, "trapclose [closeMain]"

    'wait here for user action

    wait

[button1Clicked]  'here is a routine that handles when a button is clicked

    'do something here

    'now wait

    wait

[button2Clicked]  'here is a routine that handles when a button is clicked

    'do something here

    'now wait

    wait

[closeMain]  'perform some cleanup code and close the window

    close #main

    gosub [sub1]  'act on user action

    'end application

    end

[openWin2]  '--------------------------Let's define our second window here

    button #2.3, ....

    button #2.4, ....

    textbox ....

    open "Window 2" for window as #2

    print #2, "trapclose [close2]"

    'wait here for user action

    wait

[button3Clicked]  'here is a routine that handles when a button is clicked

    'do something here

    'now wait

    wait

[button4Clicked]  'here is a routine that handles when a button is clicked

    'do something here

    'now wait

    wait

[close2]  'perform some cleanup code and close the window

    close #2

    gosub [sub2]  'act on user action

    'now wait

    wait

'--------------------------------------------------------------------------

    'We might place code here containing general subroutines

See how we arrange the code for each window like so:

[openMainWindow]

[userAction1]

[userAction2]

[closeMainWindow]

    end

[openWindow1]

[userAction3]

[userAction4]

[closeWindow1]

[openWindow2]

[userAction5]

[userAction6]

[closeWindow2]

The names we give to our branch labels above are of course generic.  All the code for a given window and user actions performed on it should be grouped together.  By structuring things in this way it is relatively easy to develop and debug your programs.

NOTE: Be careful to avoid creating a program that closes all its windows but doesn't actually end.  If the program stops at an input statement after closing all of it's windows, it will not terminate properly and resources will not be freed.  If the program loops continuously after closing all its windows then our WINDOWS message handler (the one set up to Liberty BASIC) will be locked out.  In this case press CTRL-Break to halt the errant program.

Using Dialog Boxes

Dialog boxes are a special kind of window.  They are different in several respects from other windows:

1) Pressing the Tab key causes WINDOWS to cycle through the controls added to a dialog box.  Each control is made the active one in turn.

2) Dialog boxes can be made modal.  This means that if I have a first window open and then I open a modal dialog box, that I will be unable to select the first window until the modal dialog is closed.  Most 'About' boxes are modal dialogs, as are many 'Properties' type windows.  For an example, select Liberty BASIC's Help menu and click on 'About Liberty BASIC'.  You won't be able to do anything else with Liberty BASIC until you close the dialog box that appears.

3) NOTE:  The following is not true anymore starting in Liberty BASIC v2.0!  In Liberty BASIC, dialogs ignore the UpperLeftX and UpperLeftY variables, so a dialog box's initial screen position cannot be determined in this way.

4) Dialog boxes cannot be resized by the user.

Some applications are appropriately constructed completely of dialog boxes and have no other kind of window at all.  Take a look at the program FreeForm (FFORM201.BAS), which comes with Liberty BASIC.  This program is more typical.  The main application windows is a graphics type, but most of the other windows are dialog boxes.

'Canned' Dialog Boxes

There are some simple preconstructed dialog boxes in Liberty BASIC.  These are useful for displaying simple notices, asking for a yes and no response, prompting the user to type some string, or selecting a filename from a list of filenames.

Here are some examples:

    'display a notice

    notice "File Not Found!"

    'ask a yes or no question

    confirm "Would you like to skip the tutorial?"; answer$

    'prompt for input string

    prompt "Please enter your name:"; name$

    'get a filename from the user

    filedialog "Pick a text file", "*.txt", selection$

There are right and wrong times to use these kinds of dialogs.  Here is the HILO.BAS program written using only the above dialog boxes.  This illustrates a wrong way to write a WINDOWS program.

    ' WK4PROG8.BAS

    ' Here is an interactive HI-LO

    ' Program

    'don't use a main window

    nomainwin

[start]

    guessMe = int(rnd(1)*100) + 1

    ' Clear the screen and print the title and instructions

    notice "HI-LO" + chr$(13) + _

     "I have decided on a number between one " + _

     "and a hundred, and I want you to guess " + _

     "what it is.  I will tell you to guess " + _

     "higher or lower, and we'll count up " + _

     "the number of guesses you use."

[ask]

    ' Ask the user to guess the number and tally the guess

    prompt "OK.  What is your guess ?"; guess$

    guess = val(guess$)

    ' Now add one to the count variable to count the guesses

    let count = count + 1

    ' check to see if the guess is right

    if guess = guessMe then goto [win]

    ' check to see if the guess is too low

    if guess < guessMe then notice "Guess higher."

    ' check to see if the guess is too high

    if guess > guessMe then notice "Guess lower."

    ' go back and ask again

    goto [ask]

[win]

    ' beep once and tell how many guesses it took to win

    beep

    notice "You win!  It took" + str$(count) + "guesses."

    ' reset the count variable to zero for the next game

    let count = 0

    ' ask to play again

    confirm "Play again (Y/N)?"; play$

    if instr("YESyes", play$) > 0 then goto [start]

    end

What is wrong with the program above?  Technically there is nothing wrong with it because it works.  However one obvious thing wrong is that there's only one place where you can exit the program (at the end of each game).  It also looks sloppy.  It doesn't have one application window that is open all the time as most WINDOWS programs do, so it will seem strange to most users.

More About Statictext

We saw in our earlier installment of this Week 4 lesson how a statictext control is used to label other controls in a window.  Statictext controls do not have to always display the same label.  We can print to them in similar fashion to the way we print to a textbox control.  Try the example code below:

    'WK4PROG9.BAS

    'demonstrate printing to statictext control

    statictext #countdown.stext, "Counting down:", 10, 10, 200, 25

    open "Countdown" for window as #countdown

    for count = 10 to 1 step -1

        print #countdown.stext, "Counting down: "; count

        t$ = time$()

        while t$ = time$()

        wend

    next count

    print #countdown.stext, "Counting Down: Done."

    end

Homework Assignment

Create a version of the WK4PROG8.BAS program above (call it MYHILO.BAS).  The program should have its own application window using the dialog window type.  This window should have at least one statictext control.  One of these should indicate whether to guess higher or lower and to count the guesses.  There should be a textbox for entering guesses, and a button labeled guess to click on to register each guess.  An additional button should be labeled 'About'.  This button should cause an About box to be displayed for the game.  The About box can be a canned dialog box, or you can construct one yourself using a modal dialog box.


Week 4 Homework Solution

    'MYHILO.BAS  -  Week 4 homework solution, Liberty BASIC course

    'This is a minimalistic program but it demonstrates the basics.

    'You may recognize some of this code from our example program,

    'but most of it is new.  Because we are using a dialog box, we

    'can tab between controls, or we can use the mouse.

    'Don't use a main window

    nomainwin

    'Set the width and height of our dialog box

    WindowWidth = 312

    WindowHeight = 145

    'Set up our controls

    statictext #myhilo.instruct, "Enter your guess here:", 14, 16, 176, 20

    textbox #myhilo.guessField, 14, 41, 216, 25

    button #myhilo.guessNow, "Guess", [guessNow], UL, 238, 41, 50, 25

    statictext #myhilo.status, "Status Line:", 14, 81, 176, 20

    button #myhilo.guessNow, "About", [aboutMyhilo], UL, 238, 81, 50, 25

    'Open our program's dialog box

    open "Myhilo Game -  WK4SOL" for dialog as #myhilo

    'When the user want to close our window, goto [quit]

    print #myhilo, "trapclose [quit]"

    'Let's display our about-box

    gosub [aboutMyhiloSub]

[startGame] 'Start a new game of MYHILO

    guessMe = int(rnd(1)*100) + 1

    print #myhilo.guessField, "-Ready for your guess-"

    'Wait here for input event

    wait

[guessNow]   'Perform action for the button named 'guessNow'

    'Get the guess from our guessField

    print #myhilo.guessField, "!contents? guess"

    'Now add one to the count variable to count the guesses

    let count = count + 1

    'Check to see if the guess is right

    if guess = guessMe then [win]

    'Check to see if the guess is too low

    if guess < guessMe then status$ = "Guess higher."

    'Check to see if the guess is too high

    if guess > guessMe then status$ = "Guess lower."

    print #myhilo.status, status$; " "; count; " guesses."

    'wait for more input

    wait

[win]

    'Beep once and tell how many guesses it took to win

    beep

    print #myhilo.status, "You won in " ; count ; "! Play again."

    'Reset the count variable to zero for the next game

    count = 0

    goto [startGame]

[aboutMyhilo]  'call our about-box subroutine

    gosub [aboutMyhiloSub]

    wait

[aboutMyhiloSub]  'display information about our program

    'We could have used a NOTICE statement here, but I wanted to show

    'how to do this using a modal dialog window type.

    'Set the size of our about box

    WindowWidth = 368

    WindowHeight = 190

    'Create statictext controls and an OK button to close the about box

    statictext #about.stext1, "I have decided on a number between one", 22, 16, 312, 20

    statictext #about.stext2, "and a hundred, and I want you to guess", 22, 36, 320, 20

    statictext #about.stext3, "what it is.  I will tell you to guess", 22, 56, 312, 20

    statictext #about.stext4, "higher or lower, and we'll count up", 22, 76, 296, 20

    statictext #about.stext5, "the number of guesses you use.", 22, 96, 256, 20

    button #about.OK, "OK", [closeAboutBox], UL, 278, 111, 64, 35

    'Open our about dialog box as modal

    open "About Myhilo" for dialog_modal as #about

    'Use the same close code as the OK button

    print #about, "trapclose [closeAboutBox]"

    return

[closeAboutBox]   'Perform action for the button named 'OK'

    close #about

    wait

[quit] 'Quit our MYHILO program

    close #myhilo

    end


Week 5 - Introduction to Programming Graphics

Week 5 course material

Liberty BASIC programming course

Copyright 1996 Shoptalk Systems

All Rights Reserved

An Introduction to Programming Graphics

One of the most interesting and useful things that can computers can do is create graphics.  These can be useful diagrams, page layouts, animations, beautiful works of art, etc.

Kinds of Graphics Controls

Liberty BASIC programs can display graphics using two different types of graphics controls.  The first is a graphics window.  We create a graphics window in our programs using an OPEN statement in the same fashion we create other kinds of windows:

    open "My Drawing Area" for graphics as #graphWin

We can also add one or more graphicbox controls to other windows, like so:

    graphicbox #main.graph, 10, 10, 150, 200

    open "My Window" for window as #main

Both kinds of graphics controls understand the same set of graphics commands.  Using the second approach can provide more than one drawing area, or it can be used to create a graphics area with other controls in a window or dialog box.

Turtle Graphics

In many computer graphics systems there is a way to draw using a pen and paper metaphor.  Because some versions of this system used real paper, and also a pen attached to a small robot that looked like a turtle (a hemisphere), this way of drawing is often referred to as turtle graphics.

The idea is that you send the turtle commands like:

   -raise the pen up from the paper

   -lower the pen down to the paper

   -move forward a specified distance, drawing if the pen is down

   -turn a specific number of degrees

By stringing these kinds of drawing commands together, useful and meaningful things can be drawn.

Liberty BASIC includes support for this kind of drawing.  Here is a short list of the Liberty BASIC turtle graphics commands:

    up    - lift the pen up (don't draw)

    down  - lower the pen down (draw)

    home  - place the pen in the center of the graphics area

    go    - move foreward in the current direction

    goto  - go to a specified position

    place - go to a specified position but don't draw

    turn  - turn the turtle clockwise a specified number of degrees

    north - cause the turtle to point north (straight up)

    posxy - tell us what the location of the turtle is

Let's draw a box in the center of a graphics control:

    'draw a box

    open "Draw A Box" for graphics as #draw

    print #draw, "up"

    print #draw, "home"

    print #draw, "north"

    print #draw, "go 50"

    print #draw, "turn 90"

    print #draw, "go 50"

    print #draw, "down"

    print #draw, "turn 90"

    print #draw, "go 100"

    print #draw, "turn 90"

    print #draw, "go 100"

    print #draw, "turn 90"

    print #draw, "go 100"

    print #draw, "turn 90"

    print #draw, "go 100"

    input r$

Run the program and look at the box we've drawn.  Now maximize the window.  See how the box doesn't get redrawn?  This is because we haven't told Liberty BASIC that we're done drawing a sequence.  To make what we've drawn 'stick', we must send a FLUSH command.  Insert the following line before our INPUT statement:

    print #draw, "flush"

Now rerun the program and maximize the window.  You will see that the box is redrawn now whenever the window is resized or redrawn.

Doing more with less

For what our box drawing program does it is quite long.  There are a couple of things we can do to shorten the program.  One is to print more than one command on a line.  Look at this:

    print #draw, "up"

    print #draw, "home"

    print #draw, "north"

    print #draw, "go 50"

    print #draw, "turn 90"

    print #draw, "go 50"

    print #draw, "down"

An equivalent line is:

    print #draw, "up ; home ; north ; go 50 ; turn 90 ; go 50 ; down"

See how we can print more than one command per line by inserting a semicolon in between each command.

Now we turn the turtle 90 degrees and go 100 pixels.  This happens four times:

    print #draw, "turn 90"

    print #draw, "go 100"

    print #draw, "turn 90"

    print #draw, "go 100"

    print #draw, "turn 90"

    print #draw, "go 100"

    print #draw, "turn 90"

    print #draw, "go 100"

With this example we can combine the TURN and GO commands into one line with a semicolon, and we can use a loop to perform the action four times, like so:

    for x = 1 to 4

        print #draw, "turn 90 ; go 100"

    next x

Here is the complete program rewritten as above:

    'draw a box

    open "Draw A Box" for graphics as #draw

    print #draw, "up ; home ; north ; go 50 ; turn 90 ; go 50 ; down"

    for x = 1 to 4

        print #draw, "turn 90 ; go 100"

    next x

    print #draw, "flush"

    wait

For fun, here is a short program that draws a spiral:

    'draw a spiral

    open "Draw A Spiral" for graphics as #draw

    print #draw, " home ; down"

    for x = 1 to 100

        print #draw, "turn 91 ; go "; x * 2

    next x

    print #draw, "flush"

    wait

See how we take the value of x in our FOR/NEXT loop and use it in our GO command.  We do this to make each next line longer than the last, and this is what makes our spiral get bigger as we draw it.

IMPORTANT: Spaces are important in any command that we print to a graphics control.  If we remove the spaces, the command will not work, and it will not return an error.  For example, the following line will NOT work:

        print #draw, "turn 91 ; go"; x * 2

See how the space was removed after the GO command?  This small change is very important.

More about FLUSH

FLUSH doesn't only make drawn items 'stick' so that they redraw.  It gives all the items drawn before that FLUSH a common number.  This is useful so that a collection of drawing elements can be referred to together.

Here is an example of how we might use this feature:

    'demonstrate the usefulness of flush

    open "Draw A Circle" for graphics as #draw

    print #draw, " home ; north ; down"

    for x = 1 to 30

        print #draw, "turn 12 ; go 10"

        print #draw, "flush"

    next x

    for x = 1 to 29 step 2

        print #draw, "delsegment "; x

    next x

    print #draw, "redraw"

    wait

Notice how we flush after each TURN and GO command.  This give each line its own segment number.  Then we loop through the odd numbers 1, 3, 5, ... 29 so we can delete every other segment.  When we print the REDRAW command, it only redraws the segments we haven't deleted and we see a dashed effect.

This segmented graphics capability is used a lot in the FreeForm program (FF12.BAS) that is included with Liberty BASIC.

We can get the current segment number at any time while drawing by using the SEGMENT command, like so:

    'demonstrate the usefulness of flush

    open "Draw A Circle" for graphics as #draw

    print #draw, " home ; north ; down"

    for x = 1 to 30

        print #draw, "turn 12 ; go 10"

        print #draw, "flush"

        'get the current segment number and print it

        print #draw, "segment segId"

        print segId

    next x

    for x = 1 to 29 step 2

        print #draw, "delsegment "; x

    next x

    print #draw, "redraw"

    wait

The SEGMENT command does NOT get the number of the last segment flushed.  Subtract 1 to get that number.

Color Graphics

We can dress up our graphics by adding some color to them.  Let's look at two commands for doing this:

    'draw a spiral

    open "Draw A Spiral" for graphics as #draw

    print #draw, "fill blue"

    print #draw, " home ; down ; color white"

    for x = 1 to 100

        print #draw, "turn 91 ; go "; x * 2

    next x

    print #draw, "flush"

    wait

The FILL command (the line after our OPEN statement) causes our graphic control to be filled with the color blue.  Then our spiral is drawn as white by setting the turtle's pen to white in the next line.

Users running 65,000 or more color drivers on their machine can specify RGB values for colors.  Each color can be given a value from 0 to 255 with 0 being darkest and 255 being lightest.  Try this example:

    'draw a spiral

    open "Draw A Spiral" for graphics as #draw

    print #draw, "fill darkgray"

    print #draw, " home ; down"

    for x = 1 to 100

        print #draw, "color "; x * 2 + 55; " 0 " ; 200 - x

        print #draw, "turn 91 ; go "; x * 2

    next x

    print #draw, "flush"

    wait

Here is a list of valid colors:

    black, blue, brown, cyan, darkblue, darkcyan, darkgray,

    darkgreen, darkpink, darkred, green, lightgray, palegray,

    pink, red, white, yellow

Line Thickness

So far everything we've drawn has been razor-thin.  Drawing with a bold line is simple matter.  The SIZE command is all that's needed.  Try this:

    'draw a box

    open "Draw A Box" for graphics as #draw

    print #draw, "up ; home ; north ; go 50 ; turn 90 ; go 50 ; down"

    'use a big fat pen

    print #draw, "size 10"

    for x = 1 to 4

        print #draw, "turn 90 ; go 100"

    next x

    print #draw, "flush"

    wait

If no size is specified, the default is 1.

Drawing Text with Graphics

Drawing text in a graphics control is simple.  Position the pen as desired and print the text with an backslash in front.  Take a look:

    'display some text

    open "Text example" for graphics as #draw

    print #draw, "place 50 50"

    print #draw, "\Hello world!"

    print #draw, "flush"

    wait

Each backslash in printed text causes a new line to be printed.  So if you want to display text that has a backslash in it (like a file path), then begin your text with a vertical bar '|' like so:

    'display some text

    open "Text example" for graphics as #draw

    print #draw, "place 50 50"

    print #draw, "|c:\config.sys"

    print #draw, "flush"

    wait

You can use the COLOR command to change the color of printed text.  Watch what happens when you also fill the window with some other color.

    'display some text

    open "Text example" for graphics as #draw

    print #draw, "fill red ; color blue"

    print #draw, "place 50 50"

    print #draw, "|c:\config.sys"

    print #draw, "flush"

     wait

To change this white backdrop to the be the same color as our FILL of red, we can use the BACKCOLOR command, like this:

    'display some text

    open "Text example" for graphics as #draw

    print #draw, "fill red ; color blue ; backcolor red"

    print #draw, "place 50 50"

    print #draw, "|c:\config.sys"

    print #draw, "flush"

    wait

Challenge Exercise

Create a program named TIME.BAS that gets the current time from the TIME$( ) function and then draws a clock with an hour, minute and second hand.  The display should have a background color, and each hand should have its own color.  A button on the clock should cause the display to redraw with the current time when clicked on.  The program doesn't have to run continuously.


Week 5 Homework

Week 5 Homework Assignment

Liberty BASIC programming course

Copyright 1996 Shoptalk Systems

All Rights Reserved

More on Programming Graphics

In the first part of week 5 we covered turtle graphics, drawing segments, drawing with color, and displaying text.  Now we will examine the use of bitmaps in Liberty BASIC.

NOTICE: This week's homework includes four bitmap files named LETTER.BMP, CALL.BMP, BROCHURE.BMP, and SALE.BMP for use in your assignment.

Using Bitmaps

In WINDOWS, a bitmap is a rectangular area containing an image.  WINDOWS supports bitmaps made up of just two colors (black & white), bitmaps made up of millions of colors, and everything in between.  Anyone can create their own bitmaps using WINDOWS Paintbrush and save them as *.BMP files.  There are other ways to create bitmap files, including the use of scanning equipment to produce an image from paper.  Electronic cameras are also becoming very popular.

Here we will examine Liberty BASIC's commands for loading bitmaps from a disk file and for displaying bitmaps into a graphics window or graphicbox control.

Here is a short program that loads one of the bitmaps that comes with Liberty BASIC and displays it in a graphics window:

    'open a graphics window

    open "Display a bitmap" for graphics as #bitmap

    'load a bmp file, calling it "aBitmap"

    loadbmp "aBitmap", "bmp\titlettt.bmp"

    'draw the bitmap named aBitmap at position 50, 50 and flush it

    print #bitmap, "drawbmp aBitmap 50 50"

    print #bitmap, "flush"

    wait

The LOADBMP statement is used to load the bitmap "bmp\titlettt.bmp" into memory.  Liberty BASIC gives this bitmap the name "aBitmap" ("aBitmap" is NOT the filename of the bitmap file).  See how this is used when sending the DRAWBMP command to the graphics window.

We can combine drawn graphics and bitmaps.  Here is a small program that combines a drawn spiral and a bitmap file.

    'combine graphics

    open "Draw combined graphics" for graphics as #draw

    'draw a spiral

    print #draw, " home ; down"

    for x = 1 to 100

        print #draw, "turn 91 ; go "; x * 2

    next x

    'load a bmp file, calling it "aBitmap"

    loadbmp "aBitmap", "bmp\titlettt.bmp"

    'display the bitmap named aBitmap at position 100, 100

    print #draw, "drawbmp aBitmap 100 100"

    'flush the combined drawing

    print #draw, "flush"

    wait

Here's an example of this program that draws in a graphicbox instead of a graphics window type:

    'draw in a graphicbox

    graphicbox #main.draw, 10, 10, 150, 150

    open "Graphicbox example" for dialog as #main

    'draw a spiral

    print #main.draw, " home ; down"

    for x = 1 to 100

        print #main.draw, "turn 91 ; go "; x * 2

    next x

    'load a bmp file, calling it "aBitmap"

    loadbmp "aBitmap", "bmp\titlettt.bmp"

    'display the bitmap named aBitmap at position 100, 100

    print #main.draw, "drawbmp aBitmap 25 45"

    'flush the combined drawing

    print #main.draw, "flush"

    wait

Using the idea above, a drawn graphic doesn't have to dominate the entire window.

Homework assignment

Write a program called STATUS.BAS that displays a graphic for one of these four status items:

  - initial letter

  - phone call

  - mail information pack

  - sale made

A dialog box should contain a graphicbox control to display the graphic, a listbox control to contain a list of the status item, and a statictext control to instruct the user to select a status item.

Four bitmap files have been included with this week's homework assignment.  Their names are LETTER.BMP, CALL.BMP, BROCHURE.BMP, and SALE.BMP.  You may use these files to display the graphic for each status item.  Here is a short program that shows how to use a listbox control:

This let's take this idea and add some other controls to the dialog box:

    'setup some colors

    dim color$(5)

    color$(0) = "red"

    color$(1) = "green"

    color$(2) = "blue"

    statictext #main, "Pick a color:", 10, 10, 120, 20

    listbox #main.colors, color$(, [loop], 10, 35, 120, 60

    open "Listbox example" for dialog as #main

    'tell the listbox where to branch on a single-click

    print #main.colors, "singleclickselect [colorSelected]"

    'wait here for user input

    wait

[colorSelected]  'draw spiral in the selected color

    print #main.colors, "selection? selectedColor$"

    notice "You selected " + selectedColor$

    wait


Week 5 Homework Solution

    'WK5SOL.BAS

    'Week 5 Liberty BASIC Course

    'Copyright 1996 Shoptalk Systems

    'All Rights Reserved

    'Here is our solution for week 5 of our Liberty BASIC course

    '------------------------------------------------------------------

    'load our bitmaps

    loadbmp "letter", "letter.bmp"

    loadbmp "call", "call.bmp"

    loadbmp "brochure", "brochure.bmp"

    loadbmp "sale", "sale.bmp"

    'setup our listbox choices

    dim status$(4)

    status$(0) = "letter"

    status$(1) = "call"

    status$(2) = "brochure"

    status$(3) = "sale"

    'no main window

    nomainwin

    'size the window

    WindowWidth = 200

    WindowHeight = 150

    'setup our controls and open the window

    statictext #main, "Select Status:", 10, 10, 120, 20

    listbox #main.status, status$(, [loop], 10, 35, 120, 60

    graphicbox #main.draw, 140, 35, 34, 34

    open "Week 5 Homework" for dialog as #main

    print #main, "trapclose [quit]"

    'tell the listbox where to branch on a single-click

    print #main.status, "singleclickselect [statusSelected]"

    'wait here for user input

    wait

[statusSelected]  'draw the icon for the selected status

    print #main.status, "selection? selectedStatus$"

    print #main.draw, "cls ; drawbmp "; selectedStatus$; " 0 0"

    wait

[quit]  'exit the program

    close #main

    end


Week 6 - Building Our Application

Week 6 course material

Liberty BASIC programming course

Copyright 1996 Shoptalk Systems

All Rights Reserved

Files included with this lesson

    WK6CM1.TXT    - This file

    TRACKER.BAS   - The finished solution to our week 6 project

    LETTER.BMP    - These five files are used as icons in our application

    CALL.BMP

    BROCHURE.BMP

    SALE.BMP

    DEADEND.BMP

    LETTER.TXT    - An example letter of introduction for our application

    PACKSLIP.TXT  - An example packing slip for our application

Building Our Application

We now arrive at the final stage of our Liberty BASIC course.  We will build

a kind of personal information manager for managing sales calls.  Our

application will manage an address list.  Each sales lead in the list will

have a field containing an event status for our sales effort.  Here is a list

of events for each sales lead:

  - Letter of Introduction

  - Follow-Up Phone Call

  - Brochure

  - Sale

  - Dead End Lead

Our application will provide a means of printing the Letter of Introduction,

personalized with each name and also dated.  We will launch NOTEPAD.EXE to

edit our form letter, and we will used tags (like <nameTag> and <dateTag>)

to insert the name and date where desired.  See the included example file

LETTER.TXT.  When we have printed a Letter of Introduction for any record,

its event status will automatically advance to Follow-Up Phone Call.

When making our Follow-Up Phone Call, our application will let us specify

if there was:

  - no contact, try later

  - positive contact, send brochure

  - negative contact, dead end lead

When we have had positive contact, the event status of the record will

be advanced to brochure.

When the sale is made, we will print a packing slip using a technique

similar to the one we will use for printing our letter of introduction.

See the included example file PACKSLIP.TXT.

Here is a functional description of our application:

1) Main window - When the program is started, a window will open with the

following controls:

    a) A combobox with a list of events (letter, call, brochure, etc).

       When we select an event in this control, our application code

       will fill our listbox below c) with leads that have the selected

       event status.

    b) A graphicbox for displaying an icon that represents the selection

       in our combobox above a).  This is similar to our week 5 homework.

    c) A listbox for holding the names of sales leads.  We will want to

       select a name from this list to perform actions on.

    d) A statictext to describe what possible action we can take on a

       lead we select in our listbox above c).

    e) A button to invoke the action described in our statictext d).

So if we want to work on making phone calls, we select 'call' from the

combobox a).  The graphicbox b) displays our call.bmp file, and listbox c)

fills up with leads that have an event status of 'call'.  The statictext

d) displays 'Make Call'.  Then we select a lead from the listbox c) and

click on button e) and another window opens to present the information we

need to make the call.  Assuming the call is a success, we provide a way

to register this in our application, and the event status of this sales

lead record is advanced to 'brochure'.

Our main window will also need buttons for the following:

    a) New Lead - Open a window for entering a new record.  Each record

       has these fields:

        - First Name

        - Last Name

        - Address 1

        - Address 2

        - City

        - State

        - Zip

        - Phone #

        - Comment

        - Event Status         (not user editable)

        - Date of Last Action  (not user editable)

    b) Edit/View Lead - Open a window to view or edit a record.  The fields

       are the same as above.

    c) Edit Letter - Run NOTEPAD.EXE to edit a letter of introduction

    d) Edit Packslip -Run NOTEPAD.EXE to edit a packing slip

Try out TRACKER.BAS

To get a good feel for how the application should work, try the included

TRACKER.BAS program file.  Notice how we use dialog boxes, most of them

modal dialog boxes.  See how we run NOTEPAD.EXE to edit our templates for

our letter of introduction and our packing slip.

Because this is a large project, it might be helpful for you to take a

good look at the TRACKER.BAS file.  Running the debugger on it might also

be a good idea.  Then once you have a good idea how things work, start

writing your own code.

File Input/Output

The sales lead records are loaded when the program is started into an

array 500 by 12.  This will work with 500 records (an arbitrary limit).

The first line in our file is a count of the number of records in the file.

Each record's fields are stored one to a line, and LINE INPUT is used to read

them in so that commas can be included in a field's text.  When we quit the

program, the records are saved back to disk.

Listboxes

The listbox is similar to the combobox we used in our week 5 homework.  It

fills itself with the contents of an array named in its originating

statement, like so:

    listbox #main.list, names$(), 10, 10, 100, 250

In the statement above, the listbox will fill itself with the contents of

they array named names$(), which must be single dimensioned.  Whenever the

contents of the listbox need to change, the RELOAD command is used, like

so:

    print #main.list, "reload"

Printing the letter of introduction and packing slip

Text is sent to the printer using the LPRINT statement.  The following will

send Hello World to the printer:

    lprint "Hello World"

    dump  'force the end of page

The DUMP statement forces the end of page, which is what you will want to do

each time you print a letter of introduction or print a packing slip.