CONTINUED FROM "MC-QB-2.TXT" A Guide to QBASIC for Beginners. By Michael Calkins Floresville, Texas, United States E-mail: "mcalkins0@hotmail.com" Please contact me with any corrections, questions specific to this tutorial, or comments. Post general QBASIC questions on: Jack's QBASIC ("http://www.geocities.com/jacksqbasic/") Updates and revisions of this guide will be posted on Jack's QBASIC thanks to Jack Davies. I tried to make sure the examples in this guide perform as described and that the text is factually accurate. If you find errors, please let me know. I wrote most of it quite late at night. And I have been known to make wildly inaccurate assumtions any time of day. 06-07-2005 PART 3: CHAPTER 9 TO CHAPTER 13. (MC-QB-3.TXT) --- 9. Fancier screen output. I hope you haven't gotten bored yet. Quite a bit of the stuff you have been learning so far is essential, but dreaysome. This next bit should be a tad more entertaining. You were introduced to screen output in chapter 4. You know how to clear the screen (CLS) and display text on it (PRINT). Now you will expand on that knowledge. Instead of just displaying the text sequentially, one line after another, you can tell QBASIC where to move the cursor, that is, where you want your next screen output to go. The screen, as far as text output is concerned, is made up of horizontal lines and vertical columns. Usually, the screen is 25 lines tall (numbered 1 thru 25), and 80 columns wide (numbered 1 thru 80). The LOCATE statement is used for this and other cursor related things. See the Help. You can specify a new line, a new column, or both for the cursor. When specifying the new cursor location, the optional line's expression goes first, the optional cursor's expression goes second. Use commas as separators. LOCATE 3 'move cursor to 3rd line, same column LOCATE , 5 'move cursor to same line, 5th column LOCATE 25, 1 'move cursor to 25th line, 1st column The LOCATE statement can also be used to hide or display the cursor, as well as change cursor style. See the Help for details. LOCATE , , 0 'hides cursor without moving it. LOCATE 3, 5, 1 'moves cursor to 3rd line, 5th column and 'makes it visible. Suppose you wanted your program to ask the computer where the cursor is now. QBASIC provides two functions for obtaining that information. CSRLIN returns the current cursor line position. POS() returns the current cursor column position. For some stupid reason, POS() requires 1 parameter in the form of any expression. This is pointless because POS() doesn't even use it to find a value to return. Example: CLS LINE INPUT "(1 to 80) What column? "; t$ DesCol% = VAL(t$) IF (DesCol% < 1) OR (DesCol% > 80) THEN PRINT "Out of range." END END IF LOCATE , DesCol% 'keep current line, but go to new column PRINT "*"; 'the ; means that no line is skipped after the '* is displayed CurLin% = CSRLIN CurPos% = POS(0) PRINT 'skips the line that wasn't skipped earlier. PRINT "The space immediately after '*' is on line"; PRINT CurLin%; ", column"; CurPos%; "." Screen output: (1 to 80) What column? 13 * The space immediately after '*' is on line 2 , column 14 . Screen output: (1 to 80) What column? 80 * The space immediately after '*' is on line 3 , column 1 . VIEW PRINT is a statement that changes the text viewport (ie, area of screen used for sequential output). See the help. VIEW PRINT 1 TO 24 'makes the text viewport extend from lines 1 'to 24. This is normal. I rarely use VIEW PRINT, but it has a purpose. Remember how QBASIC scrolls the screen when output nears the bottom? Well where is that bottom? That is one of the things VIEW PRINT defines. Consider: CLS FOR i% = 1 TO 23 PRINT i% 'lines 1 to 23. NEXT i% PRINT 24; 'line 24, use semicolon to prevent scrolling. We have reached the bottom of the viewport. Line 25 is not in the viewport. How can it be accessed? One way is to do this: LOCATE 25, 40: PRINT "line 25"; CLS 2 LOCATE 1, 40: INPUT "Press ENTER"; junk$ 'just so it doesn't disappear But you are still out of the text viewport. CLS 2 erased the text viewport, but not line 25. VIEW PRINT 1 TO 25 CLS FOR i% = 1 TO 24 PRINT i% 'lines 1 to 24. NEXT i% PRINT 25; 'line 25, use semicolon to prevent scrolling. LOCATE 1, 40: INPUT "Press ENTER"; junk$ 'just so it doesn't disappear does it well. WIDTH is a statement that can change the text resolution of the screen (or another device). If you are curious, look it up. You will use it later, but it is not important now. An even more entertaining aspect of QBASIC is the use of color. So far, probably all of your programs have had white text on a black background. Both foreground (text) and background colors can be set using the COLOR statement. The COLOR statement actually has several syntax versions. Which you use depends on which screen mode you are in. We will discuss that later. Just know that for now, we are using only screen mode 0. Therefore, in this case, we use the COLOR [foreground%] [,[background%] [,border%]] syntax. (See the Help article "Syntax Conventions" if you aren't familiar with syntax conventions already.) As all those brackets indicate, this, like LOCATE, is one of those and/or deals. Foregrounds can be 0 to 31, backgrounds can be 0 to 15. Use commas as separators. COLOR 7 'White foreground. Background unchanged. COLOR , 1 'Foreground unchanged. Blue background. COLOR 15, 4 'Bright white foreground. Red background. COLOR 31, 4 'Blinking bright white foreground. Red background. I am not actually sure what the border thing does. I have tried using it and can't tell that it has done anything. Anyway, you see that colors are represented by numbers. Those numbers are called color attributes. Color attributes and color values can be two separate things. Think of color values as being absolute. (For particular screen modes and graphics hardware, certain color values are always certain actual colors.) Color attributes, on the other hand, can be changed and reassigned. Color values are assigned to color attributes, and it is the color attribute that you specify at the COLOR statement. Why the complexity? You will learn why later as well as how to reassign the attributes. Default color assignments: (Screen mode 0 (text only), with color monitor.) attribute default value color for that value 0 0 Black 1 1 Blue 2 2 Green 3 3 Cyan 4 4 Red 5 5 Magenta 6 6 Brown 7 7 White 8 8 Gray 9 9 Light blue 10 10 Light green 11 11 Light cyan 12 12 Light red 13 13 Light magenta 14 14 Yellow 15 15 Bright white 16 to 32 correspond to 0 to 15 but blink, and can be used for foreground only. Although you can use 0 to 15 for background, you will find that, as far as backgrounds are concerned, 8 to 15 will duplicate 0 to 7. You will learn about screen modes and color assignment later. You will even have different color possibilities to choose from. All in due time. Of course, if you feel you're ready, its all there in the Help for you. Example: CLS LINE INPUT "(0 to 31) Foreground? "; t$ f% = VAL(t$) LINE INPUT "(0 to 15) Background? "; t$ b% = VAL(t$) COLOR f%, b% PRINT "Howdy, folks!" COLOR 7, 0 'restores white, black before terminating. '(not actually necessary) To see the screen output, type or copy and paste the above code into a program and run it. As you experiment, you will soon notice that when you clear the screen, the background for the whole screen becomes the current background color. COLOR 7, 1 'white, blue CLS 'The whole screen becomes blue Review: 1. Describe the text layout of the screen. 2. How can you move the cursor? 3. What functions can you use to find the cursor's current position? 4. What is the text viewport? 5. What does VIEW PRINT do? How can you use it to make accessing the last line easier? 6. What statement changes the colors of text screen output? 7. What is the difference between a color value and a color attribute? Which is changeable? 8. Which colors do the color values 0 to 15 stand for (mode 0, color monitor)? --- 10. Text STRING manipulation part 1. We have spent several chapters together learning about how to work with math and manipulate numbers. Other than splicing text STRINGSs together, we haven't done much text manipulation. Here is where you learn how to do more. You already now how to splice together text STRINGs: t$ = "ABC" + "DEF" PRINT t$ 'Screen output: ABCDEF But there is more that you can do. Remember the text in STRINGs is made up of ASCII characters. There is an article in the Help that lists them. Upper- and lower-case letters are not the same. Suppose that you wanted to force all the letters in a STRING to either upper-case or lower-case. You can use the UCASE$() or LCASE$() functions respectively. See the Help. Both return STRINGs based on the input parameter STRING, except that the returned output has been forced to upper- or lower-case. Example: CLS LINE INPUT "Type a text STRING: "; t$ upper$ = UCASE$(t$) lower$ = LCASE$(t$) PRINT "In upper-case:" PRINT upper$ PRINT "In lower-case:" PRINT lower$ Screen output: Type a text STRING: My name is Michael. In upper-case: MY NAME IS MICHAEL. In lower-case: my name is michael. Screen output: Type a text STRING: Hello012 345 ABCdef. In upper-case: HELLO012 345 ABCDEF. In lower-case: hello012 345 abcdef. Remember the last example from chapter 8? Remember the line: IF (t$ = "Y") OR (t$ = "y") THEN PRINT "Bye": END We can change that to: IF UCASE$(t$) = "Y" THEN PRINT "Bye": END Example: CLS a% = -10 1 PRINT a% a% = a% + 1 LINE INPUT "Quit? "; t$ IF UCASE$(t$) = "Y" then PRINT "Bye": END GOTO 1 Screen output: -10 Quit? n -9 Quit? n -8 Quit? n -7 Quit? y Bye Before we advance, let me mention a neat little function called LEN(). LEN() doesn't actually manipulate any STRINGs, what it does is return a number that tells you how long a STRING is, that is, how many characters it has. Example: t$ = "Hello world!" PRINT LEN(t$) 'Screen output: 12 It is also possible to trim unwanted space characters from the beginning or end of text STRINGs. For this use the LTRIM$() and RTRIM$() functions. Both return STRINGs based on the input STRINGs. LTRIM$() trims unwanted spaces at the beginning, ie, left side, of the STRING while RTRIM$() does it to the end, ie, right side. Example: CLS LINE INPUT "Type a text STRING: "; t$ PRINT PRINT "Untrimmed:" PRINT "'"; t$; "' Length"; LEN(t$) lt$ = LTRIM$(t$) rt$ = RTRIM$(t$) lrt$ = LTRIM$(RTRIM$(t$)) PRINT PRINT "Trimmed on the left:" PRINT "'"; lt$; "' Length"; LEN(lt$) PRINT PRINT "Trimmed on the right:" PRINT "'"; rt$; "' Length"; LEN(rt$) PRINT PRINT "Trimmed on both sides:" PRINT "'"; lrt$; "' Length"; LEN(lrt$) Screen output: Type a text STRING: Hello 123 Untrimmed: ' Hello 123 ' Length 21 Trimmed on the left: 'Hello 123 ' Length 13 Trimmed on the right: ' Hello 123' Length 17 Trimmed on both sides: 'Hello 123' Length 9 Is there any function return a specified number of a certain character? Yes. Look up the STRING$ function in the Help. Example: CLS PRINT STRING$(10, "-") 'Screen output: ---------- A related function is SPACE$(), which returns a STRING of spaces. Example: CLS PRINT "'"; SPACE$(4); "'" 'Screen output: ' ' Related functions are SPC and TAB. TAB actually deals with cursor position. Look them up in Help if you are curious. Review. 1. What functions can be used to force STRINGs to either upper- or lower-case? 2. What function returns the length of a STRING? 3. What functions can be used to trim unwanted spaces from the left and right sides of a STRING? 4. What function can be used to generate a repetition of a specified character? 5. What function can be used to generate a specified number of spaces? --- 11. Text string manipulation part 2. Now we can get into even more advanced manipulation. What if we want just a certain specific part of a STRING? We can use the LEFT$(), RIGHT$(), and MID$() functions. Each returns a STRING that is a section of the input STRING. In another parameter, you specify the length of the segment you want. LEFT$() returns the leftmost character(s) in a STRING (you specify how many). RIGHT$ returns the rightmost character(s). See the Help for syntax. MID$ is the most versatile of the three functions. It can get its selection from anywhere, left, right, or somewhere in between. Because of that, it has three parameters, the extra one indicates where in the input STRING the selection starts. See the Help for syntax. Example: CLS alpha$ = "abcdefghijklmnopqrstuvwxyz" PRINT alpha$ PRINT "3 leftmost characters: "; LEFT$(alpha$, 3) PRINT "5 rightmost characters: "; RIGHT$(alpha$, 5) PRINT "2 characters starting with the 10th character: "; PRINT MID$(alpha$, 10, 2) Screen output: abcdefghijklmnopqrstuvwxyz 3 leftmost characters: abc 5 rightmost characters: vwxyz 2 characters starting with the 10th character: jk Now suppose you want to modify some text in a STRING. There are several ways. When you looked up the syntax for the MID$ function, you also saw a MID$ statement. MID$ is a very unique statement that replaces a section of a STRING variable with another STRING. See the Help for syntax and an example. Note that there are a MID$ statement and a MID$ function. Don't get them mixed up. However, I prefer a more versatile method. Since you know how to obtain sections of a STRING with the LEFT$, RIGHT$, and MID$ functions, and you know how to splice STRINGs together, this method should be self evident. Watch this: Example: CLS alpha$ = "abcdefghijklmnopqrstuvwxyz" PRINT "Source: '"; alpha$; "' Length"; LEN(alpha$) l$ = LEFT$(alpha$, 9) '9 chars (1 to 9) m$ = UCASE$(MID$(alpha$, 10, 2)) '2 chars (10 and 11) r$ = RIGHT$(alpha$, LEN(alpha$) - 11) '15 chars (12 to 26) result$ = l$ + UCASE$(m$) + r$ PRINT "Result: '"; result$; "' Length"; LEN(result$) Screen output: PRINT "Source: 'abcdefghijklmnopqrstuvwxyz' Length 26 PRINT "Result: 'abcdefghiJKlmnopqrstuvwxyz' Length 26 What was done in the above example actually could have been done easily with the MID$ statement, because the text that was replaced, "jk", and the replacement "JK" were the same length. My method is versatile because it can handle replacement of a length unequal to the length of the text it replaces. Example: CLS alpha$ = "abcdefghijklmnopqrstuvwxyz" PRINT "Source: '"; alpha$; "' Length"; LEN(alpha$) l$ = LEFT$(alpha$, 9) '9 chars (1 to 9) r$ = RIGHT$(alpha$, LEN(alpha$) - 11) '15 chars (12 to 26) result$ = l$ + " Jay Kay " + r$ PRINT "Result: '"; result$; "' Length"; LEN(result$) Screen output: PRINT "Source: 'abcdefghijklmnopqrstuvwxyz' Length 26 PRINT "Result: 'abcdefghi Jay Kay lmnopqrstuvwxyz' Length 33 See? You won't be able to do THAT with the MID$ statement alone! Here is an example program to play around with: CLS LINE INPUT "Type a text STRING: "; orig$ PRINT LEN(orig$); "characters." LINE INPUT "Position to insert new STRING? "; t$ start% = VAL(t$) IF (start% > (LEN(orig$) + 1)) OR (start% < 1) THEN PRINT "Invalid position" END END IF LINE INPUT "Type a new STRING to insert: "; new$ LINE INPUT "Replace how many characters? "; t$ rep% = VAL(t$) keep% = LEN(orig$) - (start% - 1) IF (rep% > keep%) OR (rep% < 0) THEN PRINT "Invalid" END END IF keep% = keep% - rep% result$ = LEFT$(orig$, start% - 1) + new$ + RIGHT$(orig$, keep%) PRINT LEN(result$); "character result:" PRINT "'"; result$; "'" To see the screen output, run it yourself. You will see how it is much more powerful than the MID$ statement by itself. One more quite useful function is INSTR(). It returns the position of a specified STRING within another STRING. You can specify a position in the STRING in which to start the search. See the Help for the syntax. Example: f$="c:\mydoc.txt" PRINT INSTR(f$, ".") 'finds the first period in f$ Screen output: 9 Example: LINE INPUT "STRING to search: "; source$ LINE INPUT "STRING to look for: "; find$ i% = 1 1 w% = INSTR(i%, source$, find$) 'start looking at position i% IF w% = 0 THEN END PRINT w% i% = w% + 1 IF i% > LEN(source$) THEN END GOTO 1 Screen output: STRING to search: I hope that you have a good evening. STRING to look for: ha 9 17 Review. 1. What three functions return sections of a STRING? How are they used? 2. What statement can be used to replace a section of a STRING contained in a variable? 3. Briefly describe a better way of inserting or substituting text into a STRING. Why is this method better? 4. What does INSTR do? How many practical uses can you think of for it? 5. Review CHR$() and look up ASC(). What do you think can you do with them? --- 12. WHILE and DO loops. In chapter 8, you saw how you can use the GOTO statement in combination with line numbers to produce a loop. That method is perfectly adequate, and perfectly powerful. But there are other, more advanced, and, in some ways, easier methods that you will now learn. The WHILE statement is an older method that is now less popular. See the Help. The WHILE statement has as a parameter a condition, that is, an expression that can be evaluated as true (non-zero) or false (zero). This is just like the condition for the IF statement. Following the WHILE statement is a block of code that is in the loop area. The end of the loop area is marked by WEND. When execution reaches the WEND marker, it will loop back to the beginning of the block if the condition in the WHILE statement is true. If not, execution falls through, that is, passes through to whatever follows the WEND marker. Example: CLS a% = 0 'a% = 0 not necessary, but I put it anyway. PRINT "Hi" WHILE a% < 5 'The block is executed if (a% < 5) is true. PRINT a% a% = a% + 1 WEND 'Loops back if (a% < 5) is still true. PRINT "Bye" 'if not, execution comes here, that is, the next line. Screen output: Hi 0 1 2 3 4 Bye If you want to jump out of a WHILE ... WEND loop, you can do so with a GOTO statement. Of course, with any loop, you want to make sure you have no endless loops. Always make a way out to prevent your program from locking up. It is possible to nest WHILE ... WEND loops one inside another. Just be careful to keep track of which loop is which. The WHILE ... WEND method is good enough, but it is not considered as good as a newer method. The DO statement is more powerful. See the Help. The DO statement can be similar to the WHILE statement in that a condition can be specified. However, in the syntax you will notice that you get two choices with your condition: WHILE or UNTIL. You can duplicate the WHILE ... WEND method by choosing the WHILE option, or you can specify that you want your loop to execute and loop UNTIL some condition is true by choosing UNTIL. The same way WEND marks the end of the looping WHILE block, LOOP marks the end of a looping DO block. These example duplicate the WHILE ... WEND example, but uses DO ... LOOP instead. Example: CLS a% = 0 PRINT "Hi" DO WHILE a% < 5 'The block is executed if (a% < 5) is true. PRINT a% a% = a% + 1 LOOP 'Loops back if (a% < 5) is still true. PRINT "Bye" 'if not, execution comes here, that is, the next line. Screen output: Hi 0 1 2 3 4 Bye Example with UNTIL: CLS a% = 0 PRINT "HI" DO UNTIL a% > 4 'The block executes if (a% > 4) is NOT true. PRINT a% a% = a% + 1 LOOP 'Loops back if (a% > 4) is still NOT true (ie, false). PRINT "Bye" Screen output is the same as in the previous examples. In fact, you get the added option of putting the {WHILE | UNTIL} Condition on either the DO or on the LOOP. In most cases, whether you put it on the DO or on the LOOP makes no difference. The only time it makes a difference is if the condition is not met to begin with. In that case, if the condition is on the DO, the block won't execute, period. But, if the condition is on the LOOP, then the condition isn't even processed until after the block has been executed once. Example (The condition is on the DO): CLS a% = 5 'notice that (a% < 5) is already false. PRINT "Hi" DO WHILE a% < 5 'The condition is false, the block doesn't execute. PRINT a% a% = a% + 1 LOOP PRINT "Bye" Screen output: Hi Bye Example (The condition is on the LOOP): CLS a% = 5 'notice that (a% < 5) is already false. PRINT "Hi" DO 'the block executes because there is no condition yet. PRINT a% a% = a% + 1 LOOP WHILE a% < 5 'but it doesn't repeat. PRINT "Bye" Screen output: Hi 5 Bye If you want to jump out of a DO ... LOOP loop, you can do so with a GOTO statement. One more advantage, though, to using DO ... LOOP instead of WHILE ... WEND is that there is a convenient way of immediately exiting the loop. EXIT DO will terminate the loop, execution going to the line after the LOOP marker. See the Help. You will find EXIT DO to be handy when combined with IF statements. Again, don't allow endless loops. It is possible to nest DO ... LOOP loops one inside another. You notice though that, unlike with WHILE ... WEND, DO ... LOOP doesn't even require a condition? In fact, I rarely put one. I prefer to control the termination of the loop with IF ... THEN EXIT DO. See here: Example: CLS a% = 0 PRINT "Hi" DO 'no condition PRINT a% a% = a% + 1 IF a% > 4 THEN EXIT DO 'We control when we exit with IF LOOP 'no condition. loops back always PRINT "Bye" 'destination of EXIT DO is here, the 1st line outside 'the loop Screen output: Hi 0 1 2 3 4 Bye Review: 1. What is the syntax for the WHILE ... WEND method, and how does it work? 2. What is the syntax for the DO ... LOOP method, and how does it work? 3. When using the DO ... LOOP method, what is the difference between the WHILE and UNTIL keywords? 4. What is the one situation where it makes a difference whether you put the condition on the DO or on the LOOP? What happens in each instance? Why? 5. When using DO, must you put a condition? 6. How can you exit a DO loop early? --- 13. FOR loops and more conditional execution. There is yet one more method of looping. The FOR ... NEXT method is one I really like and use all the time. See the Help. In a FOR loop, you give it a numeric variable which it will use as a counter. You also give it 2 expressions that constitute a range that will be used in determining when to leave. The FOR statement automatically assigns the first number in the range to the variable. The block will be executed if the variable is within the range. When the block is done and execution comes to the NEXT marker, the variable is indexed, and, if the variable is still within the range, the block is repeated. The default index interval is 1, but you can change that with the STEP keyword. Example: FOR i% = 0 TO 7 PRINT i%; NEXT i% PRINT FOR a% = 66 TO 55 STEP -1 PRINT a%; NEXT a% PRINT FOR fp# = 0# TO 1# STEP .125# PRINT fp#; NEXT fp# PRINT Screen output: 0 1 2 3 4 5 6 7 66 65 64 63 62 61 60 59 58 57 56 55 0 .125 .25 .375 .5 .625 .75 .875 1 The variable that is being used for a counter is just a normal variable. The fact that the loop is using it does not prevent you from modifying it, as in the next example. Example: FOR i% = 0 TO 7 i% = i% + 1 PRINT i%; NEXT i% PRINT Screen output: 1 3 5 7 Of course when you do that, be very careful not to make an endless loop. Example of an endless loop: FOR i% = 0 to 7 i% = 0 PRINT i%; NEXT i% Partial screen output: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 You get the picture. Just like the DO ... LOOP loop, you can jump out of a FOR ... NEXT loop with ease. Of course you can use GOTO. If you simply want to exit prematurely, you can use EXIT FOR. Execution goes to the line after the NEXT marker. See the Help. You will find EXIT FOR to be handy when combined with IF statements. Of course it is possible, and many times extremely convenient, to nest FOR ... NEXT loops one inside another. Allow me to show you by generating a 5 by 25 box on the screen. Example: FOR y% = 0 TO -4 STEP -1 FOR x% = 0 TO 24 PRINT "*"; NEXT x% PRINT NEXT y% Screen output: ************************* ************************* ************************* ************************* ************************* Let's do it again, this time with variables defining the box. Example: CLS minx% = 4: maxx% = 42: stepx% = 2 miny% = -1: maxy% = -4: stepy% = -1 FOR y% = miny% TO maxy% STEP stepy% FOR x% = minx% TO maxx% STEP stepx% LOCATE 1 - y%, x% + 1 'for this, LOCATE is used PRINT "*" NEXT x% NEXT y% Screen output: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * You will see many more applications for nested FOR loops later, especially when you learn about multi-dimensional arrays. Remember that I said that I didn't completely trust the floating point data types? Here is a serious demonstration why. Example: FOR i! = 0 TO 1.2 STEP .1 PRINT i!; NEXT i! PRINT Screen output: 0 .1 .2 .3 .4 .5 .6 .7 .8000001 .9000001 1 1.1 Now you know that .7 + .1 doesn't equal .8000001. In fact: Example: a! = .7 b! = .1 c! = a! + b! PRINT c! Screen output: .8 So what happened? This is my theory. Correct me if I am wrong. I think it can be attributed to the fact that these floating point numbers are at heart binary. The values are actually stored as binary, and miniscule rounding is required to make them fit. Such corruption can build up, accumulate, as more and more operations are performed on the variable. It is not immediately evident, because when the value is converted back to decimal for human eyes, it is again subject to rounding. You can see this in the 2nd example in that when we assigned .7 to a variable and added .1, we got the right answer. In the 1st example, i! equaled .7, not by fresh assignment, but as the result of previous calculations. If you were to actually look at the variable in memory (which you will learn how to do later), you would see that even though both display as .7, they are actually unequal. What does this all mean for us? Mainly, you need to be ever mindful of this problem when you work with floating point numbers, especially when performing numerous operations on them. It may be necessary to occasionally round them back to what they should be. Also, instead of testing for equality, you may have to test for proximity. IF ABS(i! - .8) < .0001 THEN instead of IF i! = .8 THEN See what I mean? Now, to further advance the topic of conditional execution. Remember what IF does? It tests a condition, and decides whether to execute a block. Suppose that you want to test an expression for numerous possible values. How would you do it? You could use multiple IF statements. You could heavily employ ELSEIF. But there is a better way. SELECT CASE is one of the things I really like about QBASIC. It is designed to test a single expression for multiple possible values. See the Help for syntax details. You start with SELECT CASE followed by the expression you wish to test. Then, on the following lines, you can put CASE keywords, each describing at least one possible value, or ELSE which would cover anything not explicitly described. You can put multiple possible values, separated by commas, on the same CASE line. You can also specify a range using TO, or an equality/inequality using IS. The code to be executed follows, and can be on the same line (separated by colons) and/or subsequent lines. The end of the block is marked by either another CASE or by END SELECT. END SELECT marks the end of the CASE list. Example: LINE INPUT "Color? "; col$ SELECT CASE LCASE$(col$) 'Chose base on value of LCASE$(col$) CASE "black": col% = 0 'like: IF value = "black" THEN col% = 0 CASE "blue": col% = 1 CASE "green": col% = 2 CASE "cyan": col% = 3 CASE "red": col% = 4 CASE "magenta": col% = 5 CASE "brown": col% = 6 CASE "white": col% = 7 CASE ELSE 'if not one of the values described. Having a 'CASE ELSE at all is optional. PRINT "Sorry, I don't know that color." END END SELECT 'marks end COLOR col% PRINT col% Example: FOR i% = -5 TO 30 IF i% < 16 THEN PRINT i%; "is "; SELECT CASE i% CASE IS < 0: PRINT "less than 0" CASE 0: PRINT "zero" CASE 1, 3, 5, 7, 9: PRINT "odd in range of 1 to 9" CASE 2, 4, 6, 8: PRINT "even in range of 2 to 8" CASE 10 TO 15: PRINT "in range of 10 to 15" END SELECT NEXT i% Note that there are no provisions for i% from 16 to 30, and no CASE ELSE. But that isn't a problem. In that instance, nothing happens. If a variable was used as the expression to test for SELECT CASE, would modifying that variable in the conditional code mess anything up? No. See here: a% = 5 SELECT CASE a% CASE 5: a% = 6 CASE 6: PRINT "You will not see this. This line will not execute." END SELECT Review. 1. What is the syntax for the FOR...NEXT method, and how does it work? 2. What does STEP do here? What is the default interval? 3. Can you modify the variable being used as a counter? 4. How do you exit a FOR...NEXT loop early? 5. Think of some practical uses for FOR loops. 6. Briefly explain why care should be taken with floating point numbers. 7. What is the syntax for SELECT CASE, and how does it work? 8. Think of some practical uses for SELECT CASE. I will release the rest as soon as I get around to it. Look for continuations in seperate files. Regards, Michael.