PROGRAMMING IN QuickBasic 4.5 for Complete Beginners. ***************************************************** By Raze 1st March 2000, Updated 3rd April 2000 LESSON II Skill Level - Beginner IN THIS LESSON: =============== Operators: % & ! # Keywords: ASC CHR$ COLOR DATA DIM LCASE$ LEFT$ LEN LOCATE MID$ PRINT USING RESTORE RIGHT$ SHARED UCASE$ CONTENTS: ========= PART 1: The Journey So Far... PART 2: Practical Stuff - LOCATE statement - COLOR statement - PRINT USING statement - Variable Types - Manipulating String Data - Changing Letter Caps - Combining Strings - More String Handling - ASCII Values - DATA statement - Introducing Arrays PART 3: Putting It All To Good Use PART 1 - THE JOURNEY SO FAR... ============================== Be proud of yourself. Just by reading lesson 1, you already have gained the know-how to make simple text-adventures. What's that... You don't want to make text adventures for fun, you want graphics, sound, action, thrills, chills, spills? All in good time. In the meantime think of it this way: You're already as good as a commercial programmer (of 20 years ago) ... If it makes you feel any better. Hopefully you haven't just sorta skim-read the thing and gone 'Oh yeah, I get that!' and move on. What you really need is practice. Don't just copy the examples, extend them. Modify them. Experiment to find it's various uses, (and limits). Make your own programs from scratch. You really need to learn these well just like you must learn to speak a foreign language fluently if you travel to that country. If you practice enough, it should come as a second nature. You don't want to have to keep referring back to these tutorials every time you want to do a SELECT CASE. PART 2 - THE PRACTICAL STUFF ============================ Right. Let's do some more text stuff. Nooo, not text again! I hear you say. Don't worry, while text is currently the main way of outputting, the time will come when you will barely use the PRINT command at ALL, except for perhaps a data initialization screen before the graphics kick in. PRINT also comes in handy as a debugger in programming. I mean, if something chokes you can print a value of the suspect variable/s to find out exactly what data it's trying to crunch when it stops. You'll see one day. LOCATE STATEMENT ================ The screen in any graphics mode has a set amount of rows/columns for text characters. I think for mode 0 (text) which we currently still use, it's something like 80 characters across the screen and 25-30 rows down the screen. If you really want an exact amount, all the info on screen modes are in the QB help. More on screen modes later on, that's when we get to the good stuff... Graphics! Well, there is a statement called LOCATE which allows you to set the point on the screen to place the text cursor, this applies for the PRINT and INPUT statements. The basic syntax is LOCATE x, y where x is the rows and y is the columns from the top-left of the screen. So, if you want to print a message 5 rows down and 20 characters across: LOCATE 5, 20 PRINT "It's lonely out here" You'll find that even with LOCATE, if the text is long enough to reach the right end of the screen it goes to the next row down but still from the very left. If you want the text on the row below to be inline, you have to use LOCATE again with one more to the row value and leave the column position the same. You will figure out for yourself the handiness of this for text- based programs. COLOR STATEMENT ================ Before I get e-mails saying I can't make up my mind how to spell colour/color it's because I'm an aussie: We spell the 'c' word properly here, with a 'u'! (No, not THAT 'c' word ;) ) But although I spell colour like this, I still obviously have to spell it like COLOR when I'm referring to the QB statement. So to make text just that LITTLE bit more interesting, try this: COLOR foreground, background This has a similar syntax to the LOCATE statement. Replace foreground with the number/variable etc of what colour you want the text to be, and background obviously changes the background colour. By default this is 7, 0. Try this: CLS FOR x = 0 to 15 COLOR x PRINT x NEXT x Can you follow that? This repeats the COLOR and PRINT lines 16 times. Each time it uses the value of x which is increasing by 1 each time. So COLOR x is saying "Use the colour number of whatever value x is." PRINT x is saying "Display the value of x". Because the colour of the text changes each time, each new number will be a different colour. If you're wondering where 0 went off to, you can't see it because colour number 0 is black. Notice that for COLOR you can get away with only including a foreground colour if that's all you need to change. Now try this: CLS FOR x = 0 TO 15 COLOR 7, x PRINT x NEXT x This keeps the text colour the same, usual light grey. But Each number is surrounded by a differently coloured box! This 'box' is the background of those characters. You can see no box around 0 simply because it is black like the rest of the screen. 7 is just a box with no text because the background and foreground colours match. By the way, text mode is only capable of 16 colors. The next 16 above are only the same all over again except they flash on and off. Good if you want to emphasize some text or if an error occurs. And if you want to change the background colour of the whole screen?... COLOR 1, 2 CLS This example gives the text color number 1 (Dark Blue) and the background color 2 (dark green). CLS resets the whole screen blank as those color values, so the whole screen will become dark green, and any text you type after will be dark blue. PRINT USING STATEMENT ===================== Especially designed for printing out set length variables, so it all stays neat and in line. Look at this program: ' Multiplication Table CLS INPUT "Enter number to find tables of: ", number CLS PRINT number; " Times Table" PRINT "" FOR x = 0 to 12 result = x * number PRINT x; " x "; number ; " = "; result NEXT x END Just in case you cannot follow this, the program first gets the user to input a number to get the times table of. The program then uses FOR/NEXT to find the result of 0 times the number through to 12 times, and displays each result. Because the numbers involved can vary in length from 1 to 2 or more digits, this can make each line un-aligned with its neighbours, which may be less easy to read. PRINT USING allows you to create a line format that combines printed in-quote characters as well as variables etc. This is how you can modify the PRINT line in the program above: PRINT USING "### x ### = ###"; x; number ; result The information inside the quotes is the line format that will be USED for the variables, hence PRINT USING. The # symbols are the spots that are replaced by the variable info. Each one has 3 spaces, so numbers with a maximum 3 digits can fit. The 'x' and the '=' between are for show, they are outputted to the screen between the values to make it appear as a times table. The 'x' in the quotes has NOTHING to do with the variable x, it's purpose is to represent the multiply sign instead of using the asterisk(*), so it resembles real math symbols better. The variables to replace the hash symbols are outside of the quotes, between semi-colons at the end of the line. You can apply this in many ways, say for representing a number as money: cash = 250 PRINT USING "You have $####.## in your bank."; cash This format allows a number with up to 4 digits and 2 decimal places (cents) to be displayed. It also adds the dollar sign in front of it. There are other ways of using PRINT USING but we donot need to cover this any further for now. VARIABLE TYPES ============== Before we go any further you must grasp the concept that there are different kinds of variable data. You already know about strings($) which hold text, but there are numerous kinds of numerical variables: NOTE: The chart below comes from the QuickBasic help section. DATA TYPES EXPRESSIONS MAX VALUE MIN VALUE Strings $ 32,767 chars. 0 chars. Integers % 32,767 -32,768 Long Integers & 2,147,483,647 -2,147,483,648 Single (7-digit) precision ! ñ3.402823 E+38 ñ1.401298 E-45 Double (15-digit) precision # ñ1.7976931 D+308 ñ4.940656 D-324 Depending on what the variable is needed for, you will need different kinds of data types. Strings($) as you know can hold text. Integers(%) can have no decimal places, but are good for holding smaller numbers within the range shown. Use this when data type is not large, it saves memory. Long Integers(&) have a much higher ñrange. Again no decimal places. The disadvantage is it takes more memory. Use only when necessary. Single Precision(!) has the ability to keep track of many decimal places, but the ñrange overall is not so high. Double Precision(#) Can hold even more decimal spaces, but takes up more memory space again. Ideal for where accuracy is required (timers etc) From now on, you should always give the variables you use the appropriate expression e.g name$, musicCDAmount%, distanceToSun&, milliseconds# This will avoid any possible confusion. More on variables and defining data types later. MANIPULATING STRING DATA ======================== So you can process the data inside numerical variables. Can you also process data inside string variables? You sure can, of course not in a mathematical way. You can do all sorts of things from changing the caps of the letters from upper to lower, you can pick out a section of the array only, you can add strings to other strings. CHANGING LETTER CAPS ==================== There's a couple of functions called LCASE$ and UCASE$. The dollar signs indicate their primary job is to handle string data only. Here's how you use them: text$ = "AbCdEfG123#%..." CLS PRINT "Normal: "; text$ PRINT "All letters upper case: "; UCASE$(text$) PRINT "All letters lower case: "; LCASE$(text$) Notice that only the letters are altered, not just any keyboard character that has a SHIFT alternate. See how the numbers and other characters remain unchanged. COMBINING STRINGS ================= You can quite literally add strings together as you can see below: t1$ = "s" t2$ = "hit" t3$ = UCASE$(t1$) + t2$ + "..." PRINT t3$ Basically, the third line takes t1$ (s) and makes it a capital. Then, t2$ is added immediately on, and 3 full stops are added to the whole thing. The result is a word I can't repeat if little kids happen to read this ;). Of course in this context the '+' sign has a different usage than summing two values, it sticks together string data one after the other. MORE STRING HANDLING ==================== LEFT$(string$, length%) - a$ = "This string is being dissected." PRINT LEFT$(a$, 6) PRINT RIGHT$(a$, 6) PRINT MID$(a$, 6, 14) PRINT LEN(a$) When a program requires intensive user input analysis like an adventure game for example - Where you must enter commands like "get key" or "talk to ghoul" These functions are ideal for identifying individual words. LEFT$ returns the first characters of a string, in this example the first 6 of a$ which will be "This s". RIGHT$ is similar to LEFT$ except it returns the end characters. The example returns the last 6 of a$ which is "ected." MID$ can return part of a string from any position and of any length. The example returns 14 characters including and after position 6 - The result will be "string is bein". LEN simply returns the total length of a string, that is the amount of characters. In the example the output will be 31 . Using the above QB commands we can create a simple program that counts the amount of spaces in a string of characters: countSpace% = 0 text$ = "There are spaces between every word, and between my ears." CLS FOR x% = 1 to LEN(text$) 'Check text from position 1 not 0 IF MID$(text$, x%, 1) = " " THEN 'Is only checking one letter at a time. countSpace% = countSpace% + 1 END IF NEXT x% PRINT USING "Counted ### spaces in the string."; countSpace% The program only needs to loop the length of characters that exist so we use the length of text$ as the position of where to stop. Notice FOR/NEXT starts from 1 not 0, as it is impossible to have position 0 in a string. Remember not all data kinds begin from 0. Every time MID$ finds a space, the counter is incremented. It's as simple as that! There are more statements and functions for text data handling but the fact is they are really inessential to learn, you can easily create a good application or game without them. ASCII VALUES ============ No, before you fret we're not going to jump into machine language! But what we can do in high-level programming is refer to an ASCII value of a single character. Remember how in lesson 1 it was explained how there are 256 ASCII characters (0 to 255) because 8 bits(1 byte) can only be combined in 256 different ways? Well normal numbers, alphabetical characters and punctuation are some of the many ASCII characters. There is a function to display the character of an ASCII value, that is CHR$. Try the program below: CLS FOR x% = 0 TO 255 'ASCII characters range from 0 to 255 PRINT CHR$(x%); " "; 'Simply some space between the characters NEXT x% Upon running, all 256 ASCII characters should fill the first few lines of your screen... All those smiley faces and wierd shapes, including the standard keyboard characters. Notice upper-case and lower-case letters have different ASCII codes, this is important to remember. To avoid confusion, before referring to ASCII values of a string use either UCASE$ or LCASE$ to make them all the same case. Remember, there is an ASCII table in QB's help that is always good to refer to. The opposite of CHR$ is ASC. This takes a character and returns its ASCII value. For example: text$ = "AbCdefgHIJ" text$ = UCASE$(text$) 'We want all the case to match. FOR x% = 1 to LEN(text$) PRINT ASC(MID$(text$, x%, 1) NEXT x% This will simply return the ASCII number values of upper-case characters A-J, as those characters are in the string in order. DATA STATEMENT ============== There is a way to place data used by a program in an easy to refer to way by the programmer. Here's an example: CLS RESTORE dataexample READ a$: READ b% PRINT a$ PRINT "The number read from the data is: "; b% END dataexample: DATA "This string is read into a variable.", 10 The DATA statement can be followed by multiple numerical data or string data (in quotes). If there is multiple data on one line, they must be separated by commas. In this example 2 values, string and numerical, are read into two variables and printed out. See the resemblance of RESTORE to GOSUB, in that it goes to a label? In this case, RESTORE is exclusive to reading data as it does not need a RETURN line, the program automatically returns when there is no more data to read under the label. There is a string and a numerical value after the one DATA statement therefore they must be separated by commas. The DATA statement has little use or relevance at this stage, but in the next part below we can begin to make some practical use out of it. INTRODUCING ARRAYS ================== Here's a new ground-shaking concept for you to learn, which is MORE than important to learn. In a nutshell, arrays are variables that can store more than one value or string under one name. A variable is like an egg-cup, but an array is like an egg-carton, it can hold multiple values (but of course more or less than a dozen unlike an egg carton!). Let's take a variable called 'a%'. As you learned in lesson 1, variables are pockets of memory allocated by QB under the name you have chosen for it, and in it you can hold a numerical value or string of characters. But that's it: That's all you can do within a single variable. Once a variable a% has a value of 82 for example, no other value can be held simultaneously. Say you want to store the score for 10 different levels in a game for example. Instead of using 10 different variables, you can create ONE array for it: DIM score%(9) DIM is the statement that you prefix to define any value or array. For all programs, you must always DIM every array you are going to use. At this stage we donot need to DIM variables. In this case score% is the name of the array we are defining and its of an integer kind, and the value in the brackets defines the amount of values we can store. In this case it's 10 (this includes zero as well). So now, we can hold 10 different numbers in one name. Assuming your first level is level 0 not 1, we can afterwards record your score for a level like this: points% = 10 score%(6) = points% 'Stores your score for level 6 as 10 points. But if you try to do this for example: score%(22) = points% The program will crash, with a 'Subscript out of Range' error. This is because your array isn't allocated to hold 0 to 22 values, only 0 to 9. Of course the same will occur if you try anything less than zero, because by default the minimum range is zero. Just like you can't pour all the beer from a full bottle into a glass at once. My solution? Just drink straight outa the bottle, it's less effort. Okay, that bit had absolutely nothing to do with QB... Arrays don't just hold numerical data, like normal variables strings can be stored, except this time multiple strings. Copy and run this program: DIM words$(14) 'Define string array for 15 strings 'Reading data into array RESTORE sentenceData FOR x% = 0 to 14 READ words$(x%) NEXT x% 'Now let's print it out as a sentence CLS FOR x% = 0 to 14 PRINT words$(x%) + CHR$(32); 'Adds a space (ASCII code 32) between words NEXT x% END sentenceData: DATA "This", "program", "illustrates", "an", "array's", "ability", "to" DATA "read", "from", "a", "DATA", "statement", "into", "an", "array." Something to notice is that once the end of one DATA line is reached, it continues on the next. This will not happen in this example but if you are reading with FOR/NEXT and you exceed the amount of DATA this will cause an 'Out of data' error. BIVARIATE DATA ARRAYS ===================== One of the most common uses for arrays in games is for storing map data. Say you have an overhead level map with different features like pathways, walls, doors etc, you might allocate a value for each kind like 0 = pathways, 1 = walls etc. If you want for example a 100x100 tile map, there will be 10,000 different spaces on the map therefore to define an array as map%(10000) should be enough. But when you refer to a location on the map you use two variables, one for it's x location(left/right) and one for its y location(up-down), just like the grid-system in your street directory. But arrays as we've shown so far are not represented in two variables, like x/y. A map's data in the above example would store the first row of the map (from x = 0 to 99, y = 0) in the first 100 spaces (0 to 99). The second row of the map (from x = 0 to 99, y = 1) will be stored in the second 100 spaces of the array (100 to 199)... and so forth. Therefore to easily refer to the right locations we need a simple algorithm (set of repeatedly used instructions in a sub-procedure)) to do this for us , something like: DIM map%(10000) mapwidth% = 100 tileput% = 1 mapx% = 10 mapy% = 35 GOSUB maparray END maparray: map%((mapy% * mapwidth%) + mapx%) = tileput% RETURN The above example puts the value of 1 to the location 10 across, 35 down. The formula (mapy% * mapwidth%) + mapx% seeks the right location in the array by multiplying the row positions by the map's maximum width then adding the across positions. The location in this case will be (35 * 100) + 10 ... hence 3510. Not quite as neat as we'd like it to be, eh - And it's possibly very confusing if you are a beginner programmer. Well not to worry, QB lets us define arrays as bivariate which is ideal for map data. Here's how we'd define it: DIM map%(100, 100) 'defines array with maximum 100 and maximum 100 values. In the first part we can store the x value and in the second the y value. Here is how we can re-write the old program: DIM map%(100, 100) tileput% = 1 mapx% = 10 mapy% = 35 GOSUB maparray END maparray: map%(mapx%, mapy%) = tileput% RETURN This once again puts the value of tileput% to 10 across, 35 down but we no- longer need an equation to work out where in the array we need to store this data as the array is split into an x/y sort already! ARRAY RANGES ============ What if we want the location of our map to start from 1x1, not 0x0 ? That is simple, by specifying the lower range as below: DIM map%(1 to 100, 1 to 100) This simply defines each part with a lowest position of 1 and a highest of 100. This applies for normal, non-bivariate data as well when it comes to defining a lowest range. PART 3 - PUTTING IT ALL TO GOOD USE =================================== We've made progress since that text-adventure example at the end of lesson 1, but nothing a far-cry to making a more advanced game. Next lesson we will look into proper SUBs modules, using DO/LOOP to make some realtime programs, and we'll do some theory on realtime program structure using modules and as effecient non-redundant code as possible. Until then, work on the stuff in this lesson more, perhaps you can advance on that text adventure, use a bit of colour and maybe see if you can use an array to incorporate a 'map'! That is, the description of where you are, what you can do and what happens depends on your x/y location. Like if you choose 'Enter east door' your x value increments by one... Well, c-ya next time.