-=-=-=-=-=-=-=-=-=-=-=-=-=- = Qbasic = - Developers - = Forum = -=-=-=-=-=-=-=-=-=-=-=-=-=- Issue II Qbasic Developers Forum is a FREE newsletter made for Advanced Basic Programming. Qbasic (compiled and interpreted) is mainly discussed. If you like what you read. Please Email me if you want to be on the mailing list. LordAcidus@aol.com Other places to get this: HTTP://www.geocities.com/TimesSquare/arena/5451/Qbasic HTTP://www.programmersheaven.com ************************************ *The FAT (Feature Allocation Table)* ************************************ (editorials) -From the Acid Pool... (features) -Free Software (ad its LEGAL!) -"What Are You Doing Dave?" Simple AI programming -The Printer Is your Slave! How to maximize the use of the printer in Qbasic -The Boolean Hell What Boolean operators are and how to use them (every issue) -Face Lift How to milk QBasic, GW-BASIC, and BASICA for every bit of speed -Quick Tips Fast solutions to little annoying programming problems -ABORT Final Thoughts ----------------- >From the Acidpool ----------------- Well, this is the 2nd QDF. I received alot of Mail from the posting on Web pages. I have uploaded this to AOL as well. First off, most of the WWW sites I mailed never wrote me back. I would like to thanks Alain Martineau, who was very positive about this, read my file, and helped me out. His web page is at "http://www.alphalink.com.au/~alain/qbas/" and please visit it. Again Alain, thank you for helping start this. Response on the ol' web has been poor. Not from you guys, but all the pricks that run these Qbasic Web pages. No one helps me with this shit, I do it myself. I sent off 50-60 copies of this to Web pages, and only a few have responded. So, as for right now, yall (my southern background showing through) are the means of transporting this around. There are 3 places you even find a trace of this on the internet. 2 sites contain the file itself, and the other just have a link to my Email addess. Most of you proably used the last one. AOL IS A FLAMING BALL OF CRAP! I have uploaded this to them 7 times, and I've never gotten a reply. FUCK YOU AOL. So, since I don't have to please my sponsers, I will cuss a HELL of alot more than before. I don't go overboard, I just changed them to things like #$%! and @$$. Some of you how were on the original list got the un-cut version of QDF1. Basiclly, if you know of friends who like Qbasic, send 'em my way (just like Lenny Kravitz), and I will hook them up. If you have a web page, I would just about fly to your town and be your slave for a week if you post either the file or my Email address on it. I would like some feedback people! I have had some members mail me. Help me out. It takes 2 minutes, just Email me at LordAcidus@Aol.com and tell me what you think. If you have new ideas for topics, or would like some changes, Email me, and I will be happy to help you out. This is YOUR newsletter, so tell me what you want to see more of. I know all you guys do the Web, but I am working on setting up a BBS to call up with all my stuff on it. It will be up proably in March '99. I am writing it in Qbasic, so I will also post the source code. While on the topic of source code, some people mailed me and asked if I would include separate files of the code with this newsletter and ZIP them up. The reason I don't want to do this is because if you lose those files, you will have no idea what I am talking about. I will continue to put code into the newsletter, and include no other files. This way you can't lose the code. I might make separate files and put them on my BBS. ------------- Free Software ------------- Software is SO expensive these days. Windows 98, Office 97, Turbo C++, it all cost a ton. So, how do you get free software, without Downloading it. And for that matter, how do you do it LEGALLY? Well, you can't have a friend give you the disks, becuase that is illegal. You can download it, thats illegal. You can't crack the Copyright protect, because that is editing their program without consent. There really is only one way to get software without paying for it. Buy it indirectly. What I mean is go to your local used computer store. Blam, used harddrives. Buy one over 400 megs. Jackpot. Everyone is in a rush to get new harddrives, now that a gig is cheap and 2-3 gigs is standard on a store bought system. People yack their harddrives out, and very few erase them, let alone format them. The store just plugs them in, sees if BIOS will recognize it, and runs scandisk. Then its on the shelf. You have a wealth of knowledge. ALWAYS RUN UNDELETE. There is no telling what is left on that drive. The best thing of all. Its prefectly LEGAL. The owner didn't erase it. He just pirated software, and the store sold it to you. You didn't know what would be on it, if anything. There you go, free, high-end software that is prefectly LEGAL. -------------------------- "What Are You Doing Dave?" -------------------------- Artifical Intellegence or AI is a MUST for games. 2 player games are cool and easy to program, as shown in the article "Player too" in the last issue, but 2 players are not always available to play a game. Thus, games should be made with the ability to play against AI or anohter player. AI can do alot of things. I'm currently working on a Computer butler name JARVIS, who reacts to incoming data with preset limitations. More inforamtion about the developement of JARVIS and other aspectsof AI will be discussed in future issues. This article will focus on creating AI for games that reacts similar to the way humans do. Making a games AI is easy. Giving it strategy, and planning is the hard part. Also, because of the speed at which a computer runs, AI's can sometimes be impossible to beat. The following is an example from an old PACMAN(tm) game I wrote: MainGameLoop: 'Get input from player 'Compute it 'blah blah blah 'Computers move IF PacmansX < GhostsX THEN GhostsX = GhostsX - 1 IF PacmansX > GhostsX THEN GhostsX = GhostsX + 1 IF PacmansY < GhostsY THEN GhostsY = GhostsY - 1 IF PacmansY > GhostsY THEN GhostsY = GhostsY + 1 'Do other stuff GOTO MainGameLoop Ok, the above code just basicly says if PACMAN is above the ghost, then the ghost moves up, and if Pacamn is below the ghost move down, and if he is to the right or left, the move right or left respectfully. These 4 statements make up the AI of PACMAN. But there is a problem with this. The computer gets to evaluate its position in realtion to PACMANs position ever time through the main game loop. But a human player has to see the ghost on the screen, and move accordingly. All that takes only a faction of a second, but in that time, the computer has looped at least 4 times, and the ghost has moved 4 more times, while poor PACMAN is waiting to move just once. PACMAN can never win against those odds! This problem is there because all computers, even OLD computers, don't run in real-time, that is human time. The US government had this problem in the mid 70's when it was programming software that connected, at that time, our nations nuclaer missle silos with radar stations. What they had to do to stop nuclear holocaust is basicly what you have to do to program PACMAN. (as amusing as it sounds, its true see "COMPUTER LANGUAGES" by Namomi Baron, Anchor Books/press, NY NY. 1986) You have to retard the computer so runs at the speed of a human. Here is an improved version of PACMANs AI Waitfor = 5 MainGameLoop: 'Get input from player 'Compute it 'blah blah blah 'Computers move Retard = Retard + 1 IF Retard = Waitfor THEN IF PacmansX < GhostsX THEN GhostsX = GhostsX - 1 IF PacmansX > GhostsX THEN GhostsX = GhostsX + 1 IF PacmansY < GhostsY THEN GhostsY = GhostsY - 1 IF PacmansY > GhostsY THEN GhostsY = GhostsY + 1 END IF 'Do other stuff GOTO MainGameLoop This is a great method to use because as the player gets father into the game, the program can always be allowed to move more often by lowering the value of Waitfor. On the Acidus version of Pacman, we have: NextLevel: Cen40 "Level Clear", 13 level = level + 1 delayit = delayit +1 if delayit = 3 then Waitfor = Waitfor - 1 :dealyit = 0 Goto reset The "delayit" thingy menas that every 3 levels, make the ghosts faster. This is basicly how PACMAN for the Atari worked, except that was an arcade game, and was all CMOS chips, and had no software. -------------------------- The Printer Is Your Slave! -------------------------- The printer is a DEVICE, yet so many people don't know how to use it. This article will discuss the fun stuff that you can do by using, (and abusing) keywords and bugs in Basic to master the printer. There are alot of nifty things that a printer can do, that no one seems to know about. Tis first trick I will tell you about came around back in the dawn of the computer age, when Computers didn't have screens. They displayed everything my printing it. These printers were so big, they were in separate rooms from the computer. Early hackers at MIT, because computers weren't complex enough to warrent the creation of viruses, made programs that looped, spitting out pages of paper. They did this with little known ASCII character 12. 12, when sent to the printer, tells the printer to spit out a page of paper. The thing is ASCII code 12 is a STANDARD. This means EVERY printer EVER made will eject a page when it gets it. Form a 1979 Epson dot matrix to a 1998 HP LaserJet pro. This is a good thing to know, because with modern laser jets, they don't eject the page they are printing on until that whole page is filled up, or you manually tell the printer to eject the page (you have to push a button). Ok, you think, what can I do to use this in my programs? Simple, if you have any printing in your program, finish it with a "LPRINT CHR$(12)" command. In fact, as you will read in the article "Your OS on Steriods" next issue, I made and compiled a little program, now called "Eject.exe", that spews a page from your printer. Another cool thing is called IBM/Epson Control Codes. These bad boys let you do cool things to stuff being sent to the printer. Almost all printers use these codes, unless they are very old (late 60's to mid 70's), these codes should work. These codes allow for weird stuff like underlining, bold text, and changing the number of characters per inch. You can really mess up your printer with these, and it stays that way until you turn off your printer. Most IBM/Epson codes begin with ESC (ASCII character 27), followed by other ASCII characters. Here are some: Function BASIC CODE: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Begin Italics LPRINT CHR$(27);CHR$(52); End Italics LPRINT CHR$(27);CHR$(53); Begin Underlining LPRINT CHR$(27);CHR$(45);CHR$(49) End Underlining LPRINT CHR$(27);CHR$(45);CHR$(48) Begin Bold LPRINT CHR$(27);CHR$(69); End Bold LPRINT CHR$(27);CHR$(70); Clear Printer Buffer LPRINT CHR$(24); ***This printer buffer is the one inside your printer, and in NO way is realted to the MS-DOS "PRINT" command GRAPHICS. Everyone thinks that the printer can only print text. Wrong! All printers can print graphics. These is some way you can send "graphics data" to your printer after use some IBM/Epson Control Code, but my old printer manual (1987) doesn't say what format it is in. So I tried a lot of stuff. I tried GETting a picture into an array "gfx" and then used "LPRINT gfx ", but it didn't work. I tried sending one byte of data to the printer using a FOR-NEXT loop. I tried then BSAVing the graphic and then opening the file in binary format, and sending it that way. Nothing worked. So the only way I could print graphics was to display them on the screen with PUT or using LINEs, CIRCLEs, etc, and then printing the screen. Please know that for this to work, you MUST run "graphics.com" in DOS. I have it in my Config.sys to load up. It is a TSR, and only takes a small about of memory. The final thing I want to tell you about is about printing the screen. There are 3 ways I know of to do this. The first and easiest is to simple press [SHIFT] + [PRT SC] (yours my not be "prn sc", but you should be able to figure out which key I mean). This prints the screen to LPT1, and if it doesn't exist, well, nothing happens, DOS doesn't return an error message. The other two methods seem pointless when you have a key on the keyboard for printing the screen. Well, the only reason I include this next example is because you can can test to see if the printer is working or not. The following is a Qbasic code bit I got from Qbasic's help file. It is a simple (their words, not mine, I know jack about CALL absolutes) Call absolute to print the screen. I am not going to say how it works, Assembly confuses the HELL out of me, and this is Machine code, a step LOWER than Assembly. Basiclly, this puts the numbers 205, 5, 203 into memory in a% space in memory, an then uses Call absolute to run the program. 205, 5, 203 are the machine code numbers of an Assembly program. You really don't need to learn ASM, it is antoher language, and is qutie tough. I will talk About it in an upcoming issue. (from Microsoft's Qbasic 1.1 HLP file with DOS 6.22) DIM a%(2) DEF SEG = VARSEG(a%(0)) FOR i% = 0 TO 2 READ d% POKE VARPTR(a%(0)) + i%, d% NEXT i% DATA 205, 5, 203 : ' int 5 retf 'Machine-language code 'for printing screen. CALL ABSOLUTE(VARPTR(a%(0))) DEF SEG Ok, whatever. Now, Why do I do this instead of using the print screen key. Simple, error trapping. If you are printing something already (you used the DOS command PRINT, then entered Qbasic), you can tell the user that the print is being used by something else, and to wait. The best part of this is you can find out if the printer is even on. Better still, You can use the KEY statements and functions, and make it so that pressing [F12] prints the screen. The final method I mention only so I can say this is the COMPLETE record of ALL ways to print the screen. This only works on text, and I guess is usefulk if you want to print the text of a screen that has graphics. FOR row = 1 to 25 a$ = "" FOR Cl = 1 to 80 a$ = a$ + CHR$(SCREEN(row, cl)) NEXT LPRINT a$ NEXT LPRINT CHR$(12) 'eject the page ---------------- The Boolean Hell ---------------- 6 XOR 4 = 2. Yes my friend, it seems the madness of boolean "logic" is at work again. What is that you ask, well, that is a histroy lesson. But I can sum it up in one word "sex", and Mr. Boolean's lack of getting. You see, Boolean was this math dude from long ago. And he saw all these other math dudes, making "cool" math discoveries, which begot fame, which begot, glory, which begot sex. So Boolean thought, "I want sex, I better think of something cool." So young Boolean studied and searched, but alas, was as dumb as a post, and couldn't come up with any new formula or theorm that other math dudes thought was cool. So, he says, "Well hell, I'll just make up my own math damn it!" So Boolean came but with TRUTH TABLES. OH NO, I SLEPT IN PRE-CAL, I DIDN'T LEARN THEM! (well it sucks to be me!) Basicly, Truth tables compared 2 things. here is an example: A= TRUE B= FALSE A and B = B, because both A and B aren't true, the result is false A or B = A, because Either A is True or B is True, the result is true B or B =B, because neither is true, the result is false Get it. Well, Everyone thought it was cutting edge and neato, but it had ABSOLUTLY NO use in his time. "oh cool, A and B = A, wow." That was all you could do with it, break the ice at parties. Well, Boolean didn't care because he was famous and was getting some every night until he died a very happy man. When you get down to it, all your computer can do is compare stuff, and return the result of that compare. Adding is just complicated comparing (which I will show later), and all other math functions can be broken down into alot of adding. So, what does all this do with Boolean logic. Well, thats easy. Computers are electrical do-hickies, so they can only have 2 ways of expressing stuff. High current, Low current. Why not medium current? I don't know, but I'm sure Intel could tell me. Any way. High and Low and like true and false. Which is Boolean! Hundreds of years after he's dead, his invention is useful. OK, so how does this work? Well, you have to make a system of numbers that can only have trues or falses. Thus binary was created. It goes like this: 13 in binary = 1101 because: (2^3)(1) + (2^2)(1) + (2^1)(0) + (2^0)(1) = 13 (8)(1) + (4)(1) + (2)(0) + (1)(1) = 13 8 + 4 + 0 + 1 = 13 See, and 37 in binary = 100101 because: (2^5)(1) + (2^4)(0) + (2^3)(0) + (2^2)(1) + (2^1)(0) + (2^0)(1) =37 (32)(1) + (16)(0) + (8)(0) + (4)(1) + (2)(0) + (1)(1) =37 32 + 0 + 0 + 4 + 0 +1 =37 So, In Qbasic, AND, OR, NOT, XOR, IMP, and EQV are your Boolean tools Don't worry about NOT, IMP, and EQV, there aren't used alot. What Qbasic does when using these tools is convert the 2 numbers being compare into binary. For example lets compare 21 with 9. First, we convert them to binary. 21=10101 9 =01001 Qbasic adds 0's to the front of the numbers until they are both the same amount of digits. In this case a single 0 was added to the front of 9's binary form so that both numbers in binary have 5 digits Next, Qbasic compares Bit by Bit (On/off by On/off) the numbers, depending on which operator you use. I'm using OR in this example, which returns a TRUE (1) if either bit is equal to 1, and a 0 if both bits equal 0. 21 OR 9 = 29 ------- 1 or 0 = 1 one of the them =1 so it is true 0 or 1 = 1 one of the them =1 so it is true 1 or 0 = 1 one of the them =1 so it is true 0 or 0 = 0 neither are =1 so it is false 1 or 1 = 1 one of the them =1 so it is true 11101 is binary for 29. (2^4)(1) + (2^3)(1) + (2^2)(1) + (2^1)(0) + (2^0)(1) =29 (16)(1) + (8)(1) + (4)(1) + (2)(0) + (1)(1) =29 16 + 8 + 4 + 0 +1 =29 x AND y : Both x and y must be true for the result to be true x OR y : Either x or y must be true for the result to be true x XOR y : Either x or y must be true for the result to be true, but if both are true, then the result is false Those are the only ones you really need to learn, but the truth tables of the other tools are in Qbasic's help file. Here is an example of XOR. 108 = 1101100 14 = 0001110 108 XOR 14 = 98 --------------- 1 0 = 1 1 0 = 1 0 0 = 0 1 1 = 0 1 1 = 0 0 1 = 1 0 0 = 0 1100010 is binary for 98 --------- Face Lift --------- Qbasic is a high level langauge. This is good because it is a Hell of a lot easier to learn then C++ or Java. This is bad because it compiles it into large EXE's and the programs are slower than lower level computer languages. So, you have to learn to milk every bit of speed out of BASIC. One way to do so is with Signed numbers. This means you added one of thoughs symbol thingys(%, $, !, etc) to a variable so Qbasic know what it is (an integer, String, long, etc). As you know, looping is one of the slowest things you can do in Qbasic. But if you use x% instead of x, Qbasic doesn't have to spend as much time figuring out what type of variable its dealing with, and instead can devote all its power to looping as fast as possible. I did the following on an old IBM XT s = TIMER FOR x% = 1 to 100 FOR y% = 1 to 100 NEXT y% NEXT x% PRINT TIMER - s This took the computer 1.701 seconds to complete.I did the following on the same computer, using unsigned( no little symbol thingys) varibles: s = TIMER FOR x = 1 to 100 FOR y = 1 to 100 NEXT y NEXT x PRINT TIMER - s This took the computer 16.91 seconds to complete. I did the test several other times on the XT, than 3 times on a 386sx, and then 3 times on a 486. Each time the first method was between 91.5% and 89.5% faster than the second. to get these numbers I used the folling formula: (SPEED OF SECOND METHOD - SPEED OF FIRST METHOD) / (SPEED OF SECOND METHOD) You multiply this number by 100 to get a percentage. Go ahead try them on your computer and compare the time on each. I used this method to rapidly speed up the movement algorithm in "VOICES OF LIBERTY" (tm), by Acidus Software. Another way Qbasic programs are slowed down alot is through the use of subs/functions. Many programs use the "DIM SHARED (variable)" to create variables that can be used by all subs/functions. This is ok, but everytime Qbasic calls that sub, it must not only pass all variables need for the sub/function, but it also passes ALL the DIM SHARED variables. Lets say you had code that looked like this: DECLARE SUB somecoolsub (text$,value1,value2) DIM SHARED Globalvar1 DIM SHARED Globalvar2 DIM SHARED Globalvar3 somecoolsub "neato", 99, 10 END When you compile this, it is turned into: DECLARE SUB somecoolsub (text$,value1,value2,Globalvar1,Globalvar2,Globalvar3) DIM SHARED Globalvar1 DIM SHARED Globalvar2 DIM SHARED Globalvar3 somecoolsub "neato", 99, 10, Globalvar1, Globalvar2, Globalvar3 END If you repeatedly call this sub/function, it makes the EXE bigger, and the sub/function takes longer to load. Also, as mentioned before, ALWAYS use signed variables, even if you are DECLAREing a function. For Example DECLARE FUNCTION areaoftri% ( length%, width%) ... FUNCTION areaoftri% ( length%, width%) areaoftri% = length% * width% * .5 END FUNCTION Executes at the same "91.5% and 89.5% faster" ratio than if you used: DECLARE FUNCTION areaoftri ( length, width) ... FUNCTION areaoftri ( length, width) areaoftri = length * width * .5 END FUNCTION If you have read the article "Boolean Hell," then you know that 0 is false and 1 is true. Well, that is only have right. In Qbasic, 0 is false, and everyting else is true. The 2 following lines do the same thing, assuming that IHaveTheKey% does equal 1. IF IHaveTheKey% = 1 THEN UnlockDoor IF IHaveTheKey% THEN UnlockDoor The second one works just like the first one, reduces program size by 3 bytes, and runs about 50% faster because Qbasic doesn't have to compare it with anything. Another example is: INPUT "Name:",TheirName$ IF LEN(TheirName$) >=1 then goto PassWordCheck INPUT "Name:",TheirName$ IF LEN(TheirName$) then goto PassWordCheck Also along the lines of math, Qbasic is STUPID. If you give it a line like: sum= y1*x+y2*x+y3*x You can reduce time by FACTORING IT DOWN. Yes, and you thought sleeping through Algebra II wouldn't cause you to miss anything good! (Boy was I screwed this year in Calculus!). Just like me struggling to do this problem with 1 minute left for a test, it takes BASIC a long time. If I wasn't so stressed, I would have seen that "sum= y1*x+y2*x+y3*x" and "sum = x(y1+y2+y3)" were then same thing. Well Hell, the second was is much easier. Qbasic's child like mind thinks the same way, and will solve "sum = x(y1+y2+y3)" a hell of alot faster than "sum= y1*x+y2*x+y3*x" The Lesson: make the math as simple as possible for Qbasic. Oh here is something I ran across in a TXT file once, but the programmers name wasn't one it, so I can't give credit, sorry, but I DIDN'T figure this one out on my own. For some god awful reason if you use "\" to divide integers instead of "/", it goes WAY faster. The only thing is if you get a decimal as an answer, if rounds, as if the INT function was used. Weird Huh!?! ALWAYS USE SIGNED VARIBLES! It cuts down on the speed, and the size of the .EXE files you make. While on the topic of EXEs, there are a few ways to make better compiled programs. First of all, NEVER EVER EVER compile a program so the program needs BRUN41.LIB (or VBRUN300, etc.) to run. If you widely distribute your program, you have no way of knowing if whoever downloads your program has that file. Plus it looks SO unprofessional. The ONLY reason you would want to do this is you have alot of programs in the same directory that need this file. Compiling in this way makes the .EXE smaller, but with how cheap gigabytes are, it is pointless to use this method to save 20 kilobytes. Also, .COM files load and run much faster than .EXE files. Look around on the internet, and you can find a shareware EXE2COM file. This makes the file smaller and faster, and more professional. Well, thoughs are all the ways to squeeze every bit of speed out of Qbasic programs I've found on my own (except that one :-0). If you find anymore, please contact me. ---------- Quick Tips ---------- How do I copy files through Qbasic without using "SHELL"? This is a tricky one, but done very quickly and without SHELLing to DOS. Basiclly what happen is you open the the file you what to copy in Binart format. You then read from this file in 4K (4094 bytes) chucks, and spit them into another file opened in binary format. It does this until you have < 4096 bytes left, in which it takes the remaining bytes and copies them over. Here you go: FUNCTION Copy (from$, to$) Num1 = FREEFILE OPEN from$ FOR BINARY AS #Num1 Num2 = FREEFILE OPEN to$ FOR BINARY AS #Num2 ' BytesLeft = LOF(Num1) 'GEt the length in bytes of file WHILE BytesLeft > 0 'While there are still bytes left to copy... IF BytesLeft > 4096 THEN CopyThisMany = 4096 ELSE CopyThisMany = BytesLeft 'Then copy all reamaining bytes END IF Space1$ = SPACE$(CopyThisMany) 'Make space to copy CopyThisMany bytes GET #Num1, , Space1$ 'read CopyThisMany bytes from file from$ PUT #Num2, , Space1$ '& put them into file to$ BytesLeft = BytesLeft - CopyThisMany 'Update amount left to Copy WEND END FUNCTION ----- Abort ----- Ok, Ok, I know that has a fluff issue. I pieced it together in about 5 days from half-completed articles and little frustrated marks on scrap paper, but I hope that the info was helpful. I've spent most of my time on that BBS, so stay tuned. Next issue will be tricky. I have a big section on Operating systems, and making "Windows" in Qbasic. It isn't as bad as it sounds. I will also talk about some nifty things you can do to modify your computer. Rock On, Acidus