# QB Express

### Issue #23 ~ August 13, 2007

"A magazine by the QB community, for the QB community!"

### In This Issue

Editors

- Pete Berg
- Imortis Inglorian
- MystikShadows

Contributors

- Lachie Dazdarian
- Kevin R.
- Mentat
- Stoves
- stylin
- sir_mud
- Seb McClouth
- Stoves
- Brandon Cornell
- Dean Menezes
- E.K.Virtanen
- diffeecult
- James Kinney
- Mathias
- anarky
- Pritchard
- Moneo
- Cha0s

- Regular Columns
  - From The Editor's Desk
  - Letters
  - Express Poll
  - News Briefs
  - Gallery - Seven Different Games
  - Awards
  - Comics - GodObject, PSET, Waterboard, "QBN Rise and Fall"
  - Final Word
- Articles & Editorials
  - Qlympics Voting Round - Pete
  - A Response to Z!re's Article in QB Express #22 - Pete
  - Best GUIs of the Modern Era - Brandon Cornell
  - Confessions of a game designer - Lachie Dazdarian
  - Just What Does Pritchard Have Against QBE Again? - Pritchard
- Tutorials
  - Angles in 2D Environments and Artificial Smarts based on them - Lachie Dazdarian
  - 3D Game Programming for Beginners: A Space Trading Sim is Born - Kevin R.
  - The Difference Between MOD and REMAINDER in QB - Moneo
  - Simple AI with Value Points - Mentat
  - How To Write A Chess Program in QBASIC - Dean Menezes
  - Down With Some Lingo - stylin
  - Programming Simple Cellular Automata with Freebasic - Mathias
  - Speed Equalization in QB and FB - Stoves
  - Singletons - stylin
  - Using Procedure Pointers to Vary Behavior - stylin
  - Commercial & Professional Software Development - Part 7 - MystikShadows

## From The Editor's Desk

### Written by Pete

Hello there, folks! Pete Berg here again, with the 23rd issue of QB Express! I apologize for the slight delay (this issue coming out in August instead of July), but I decided to go on a 40-day cross country roadtrip with some of my friends from the end of June until just a few days ago. We traveled from the Atlantic coast near Boston to the Pacific Coast in California, and just about everywhere in between -- including a dozen national parks and a ton of other attractions. Aside from when I almost died of dehydration on a hike to the bottom of The Grand Canyon in 111° Fahrenheit heat, it was a great trip.

Anyway, Issue #22 came out in June, and frankly, it was a sub-par issue of QB Express. It was missing a lot of regular sections (like Gallery, Poll and Monthly Awards); I neglected the Qlympics; and we hardly even bothered reporting the news, and what was there was horribly out of date. The reason was because I had been sitting on that issue for nearly a year, and seeing as how I had some free time and the Qmunity had slowed down, I decided that it was time to resurrect QB Express.

While #22 was shoddy product, and was thrown together from stagnant content, this issue is a complete reversal. Everything that was missing from the last issue is here...and more! I spent a ridiculous amount of time compiling the news, gallery and getting the Qlympics back on track, and now I think I can officially say: QB Express is back, baby!

I hope you enjoy all the articles, news and tutorials, as well as our biggest comics section ever. Also, please vote in the Qlympics. We are now in the final round of voting, and every vote counts. Make sure the most deserving nominees get the prize.

So go to it! Read and enjoy!

### Submit To QB Express

This magazine can't exist without people SUBMITTING articles, editorials, tutorials, reviews, news and feedback. This is not just a solo effort by me... it's a group effort by people throughout the QB community. If you have anything to submit, or have time to write something, DO IT!

If you want to write about something, but can't think of a topic, or need inspiration, check out the "Official QB Express Article Requests" thread! There have been quite a few articles requested -- and even if none of them strikes your fancy, we can help you come up with something that you would like to write about. If you're interested in getting your own monthly column or just want to write an article or two, by all means, do it! Anything that is submitted will be included!

I also want feedback and letters to the editor regarding this magazine. I want suggestions and critiques. What do you like? What don't you like? What could be done better? Let me know!

All submissions and feedback can be sent to qbexpress@gmail.com. You can also PM me on the Pete's QB Site or QBasic News message forums. If QB Express is going to continue to be so good, YOU need to contribute!

-Pete

## Letters

### Letter From Stoves

Congratulations on another great issue of QB Express, Pete! I completely agree with you that QBE worth keeping alive. Thanks for all your hard work and commitment to the Qmunity! I’m sure the excellent idea of spreading the responsibility for the monthly magazine will only serve to strengthen the publication. Thanks to Imortis and MystikShadows for stepping up to help out. Just wanted to express gratitude from a frequent reader. Keep up the good work!

Stoves

> Thanks, it's great to be back! And judging by the overwhelming number of submissions this month (you guys really came through in the last few days before...and after...the deadline), this is one of our best issues ever.

> -Pete

### Leter From E.K.Virtanen

I am extremely glad to see QBE back. You guys are doing a awesome job. QB/FB communities with out QBE...well, something was just missing but with your work here, pieces are coming back in their places. Thanks for your efforts and may the life of QBE be long as possible.

Short brief about PCopy! emagazine: It's still alive but since stuff around it is way too busy, we have alternative ways to handle and maintain it. Im sure in month or two, something about PCopy! is posted at boards.

Good coding and piece for all. Remember, programming can be fun, even with limited skills :)

E.K.Virtanen
ASCII-World

> Thanks for the kind words. As for PCopy!, I hope you guys manage to continue that magazine as well. The way I see it, the more mags, the merrier (plus, who doesn't like some friendly competition?)

> -Pete

### Letter From Seb McClouth

Hey guys,

I made a promise and a promise is a promise.

It’s has been silent around Qbinux. And I think it also has to a bit with the suddenly disappearance of the QB Express.

Well anyways, it’s good to have it back and have you back Pete. It’s been to silent.

I think it’s really a good thing you trying to keep up the QB-spirit. FB-ers, don’t kill me on this one! We’re all Basic- programmers under the skin, right?

I’m currently trying to get some skills on 3d stuff. For a couple of reasons:

1. creating a 3d file manager for dos
2. doing up some experience for a UGL-thingy for the GUI of Qbinux
3. maybe making some games

Well guys (yeah, you’re not alone on this anymore Pete) keep up the good work and remember that you guys are keeping the BASIC-spirit alive!!

Grtz

Seb

> Well, I'm glad to hear from you again! I agree about the silence that a lot of the smaller programmers experienced after QB Express went on hiatus. There were a lot of people who used QB Express to spread the news and information about their projects, and without it, they were left without much of a voice -- or much motivation to continue. People were only finding out about the biggest projects (like Lynn's Legacy and Kart), because nobody was reporting the news. QB Express is all about reporting on everything that's going on in the Qmunity, from both the big guys and the little guys.

> -Pete

Have a letter for the editor? Send all your rants, raves, ideas, comments and questions to qbexpress@gmail.com.

## Express Poll

Every issue, QB Express holds a poll to see what QBers are thinking. The poll is located on the front page of Pete's QBasic Site, so that's where you go to vote. Make sure your voice is heard!

### Should QB Express have a poll this month?

Genre | Votes | Percent | Graph
Yes | 0 | 0% |
No | 1 | 100% |
1 Total Vote

> Whoops, someone forgot to post a new poll this issue...I guess we're still getting back into the swing of things.

> Be sure to vote this month in the final round of the Greatest QB Developer of All Time tournament, on the front page of Pete's QB Site.

## News Briefs

News from all around the QB community, about the latest games, site updates, program releases and more!

### QB Site News

Lachie Launches the FreeBasic Games Directory

  Lachie Dazdarian, the guy behind the QBasic Games Directory of last year, recently launched The FreeBasic Games Directory. This fantastic website features downloads, ratings, information, and screenshots for every substantial FreeBasic game that has been released (currently, 61). The website also allows users to rate and comment on all the games, though for its official ratings, the site relies on a small staff. Additionally, if there have been any reviews written about a game anywhere, the site links to them.

  The FreeBasic Games Directory is already an invaluable resource, and is extremely well-executed. We're all indebted to Lachie for launching this site.

  News Brief by Pete

Forums in Flux

  This past month, we've seen a lot of moving and shaking in the QB/FB forum communities -- and luckily, most of it has been for the better.

  QBasic News

  In the past year, QBasic News has seen a steady decline in users, and a steady increase in spambots and human spammers (who were generally overzealous FreeBasic activists). This drove users away in droves -- but unfortunately the moderators in charge were powerless to stop the abuse. Site owners Sumo Jo and Wildcard had long since abandoned the site, and left nobody with the authority to modify or upgrade the forum software. The moderators were stuck; they could only control the forum through the mediocre PHPBB admin page -- and it wasn't helping. So, in what many felt to be an overzealous move, moderator Zap first disabled all new registrations, and when the spam and flaming continued, he posted a message that the forum was to be shut down entirely.

  QBN Users were given a few days to say their good-byes, and then Zap disabled posting in every forum on the site. It looked like QBasic News, the former hub of the Qmunity, was finally dead. But, like a pheonix rising from the grave, former admin and site owner wildcard emerged and brought the forum back to life. He upgraded the forum software to SMF from PHPBB, gave the site a makeover, and has begun a process to improve the QBasic News design, so that its other content is more accessable. All of the old posts and user accounts are still accessable at the QBasic News forum.

  This is great news. QBasic News is an incredible resource, and with this much-needed maintenance, its forum community can begin to prosper again.

  Freebasic.net and c0de

  Several flamewars have erupted over the past few months at the Freebasic.net forums, and head moderator Cha0s has stepped in to prevent them, locking countless active topics, and ending several heated debates. Many FB.net visitors felt that this was unecessary censorship and an abuse of power, and some users spoke out. Among them was Z!re, the outspoken FB.net member, and last month, Z!re and cha0s found themselves in a personal feud on the Freebasic.net Off-Topic Forum.

  In order to stop these debates that he did not approve of once and for all, Cha0s decided to shut down the Off Topic forum entirely. His reasoning was: "The Off-Topic forum has been removed due to its abuse. We wish to keep the site as professional as possible, and there is no room in that specification for what the Off-topic forum had become."

  Meanwhile, a few weeks beforehand, Cha0s decided to launch a new forum with more lax rules called c0de. According to cha0s, "The main focus [of the forum] is totally off-topic discussion, in addition we will not censor swearing or tastefully nude pics if you choose to post them."

  c0de has already developed an active community of posters, and there is a lot of FreeBasic-related discussion going on there.

  One interesting thing about c0de is that new users must be personally approved by Cha0s before they may post, a rule presumably made to prevent known spammers and undesirable users from registering. Unapproved users may post only in the "spam" section. (For this reason, it seems to me that c0de may not be as free and uncensored as Cha0s bills it to be, but at least everyone seems to get along.)

  News Brief by Pete

News from the QBasicNetwork Administration:

  I am closing down the main site, now administered by Phalaris Entertainment. All sub sites including BlobWorld Comics, QBNetwork Stuff and the brand new FB-World will be moved to their own domains by the end of the September 2007.

  On a higher note, I wish to announce that FB-World.com will take it's place as my new home and (hopefully) a very friendly window for the world to see what FreeBasic is and why they NEED to have it.

  As for games, in issue #16 (I think) We announced that work on 'Legend of Aquarius' had commenced. At this stage, the project is halted due to real life issues involving family life and court proceedings. If all goes well, By the end of September 2007, work will commence once again on it.

  'Genesis' has been dropped. why make an RPG engine so complex if only you will use it? Nobody was interest in it, since they wanted to make their own engines.

  'Orbs of Infinity' will also be made, but not until 'LoA' is finished. It will no longer be as huge as originally planned, but should offer a good 20+ hours of solid gameplay. This means the scripting has to be stripped down by about 75% - yes, there was a lot of it. Perhaps in the distant future a 'Director's cut' will emerge...

  The project known as Brainfreeze is nearly finished, but I haven't had time to work on it as I mentioned before. I have been doing other stuff online also to hone skills needed to finish it.

  A new IRC network has been created, consisting of 5 or 6 servers, myself one of them. It's called SpazNet, headed by Jake Myers, aka spaz. irc://irc.spaznet.net/spaznet ... We welcome your custom.

  Also home to this network is RedBall, my new IRC bot - a bot designed for the masses. You will periodically see it in action on SpazNet. Just /msg $$$ .

  That about wraps me up for the last few months, and hopefully for the next few. I will also try to be an active contributer to QBExpress once again.

  Just for now, you can see what goes on in my life at http://www.qbasicnetwork.com/fbworld. I look forward to your custom.

  anarky - "Screwing with your reality since 1998."

  News Brief by anarky

ASCIIWorld News Wrapup

  You have probably noticed that ASCII-World moved to WikiDot wiki software and hosting. Now anyone is allowed to edit and add contents there, only simple registering is needed. Its only for to avoid spam, and im sure peoples understands this.

  I also started an Retro2FB site few months ago. I got nearly 20 old classics ported to FreeBASIC but then i moved over 200km and i was offline nearly two months.

  Now, Retro2FB is combined as part of ASCII-World, since idea and purpose of it, fit's at ASCII-World like a nose in a head.

  E.K.Virtanen
  ASCII-World

  News Brief by lurah

Pritchard's Publishings -- Is a new FB magazine on its way?

  Pritchard's dislike for QB Express is no secret. Upon QB Express #22's release, he posted such things as "fuck your QB magazine," and "I wish QBE would just die and get hit by a giant horse, amiright?"

  And to top things off, he wrote an editorial this month about how much he hates the magazine, and how it's bad for the Qmunity. (We published it, of course.) Pritchard's opinions are cool with us, and as an "open magazine," we're happy to help spread them. We've also encouraged him on several occasions to start his own, competing publication if he detests QB Express so much.

  Well, it looks like Pritchard might finally be taking our advice. A few weeks ago, he created this "Tutorial/Article Request Thread" at the FreeBasic.net forum, where he asked members to request for tutorials, reviews and articles to be written: "If you have a topic that you'd like someone to write a tutorial or article about, please request it here. If you're wanting to write about something, but don't know what, you can look here as well."

  Pritchard didn't say what he was going to do with the articles, but it does insinuate that Pritchard might have some plans up his sleeve for his own publication.

  He certainly has been active lately, making some magazine-article-like posts (these are all worth checking out):

  - Game Spotlight: Review of Zonaxtic
  - Editorial: What makes Game Fun?

  Pritchard is also the sole contributor to Syn9's hardly-updated FB newsportal / magazine The Freebasic Report. The only new articles on the site since March have been by Pritchard: "FB community news tidbits", "pristd - priVect2D: A simple 2D Vector Class" and "FBgfx - More Flexible Than you Thought".

  Pritchard has said in the past that he was working on some sort of FB news portal or magazine, and admitted that he was disheartened when QB Express returned. Well, judging by all the great articles he's written lately, maybe he is ready to create a zine. If that's the case, we wish him luck. Anything it takes to get news, articles or tutorials published in this community is a good thing, and we are hardly worried about competition. Can't we all just get along and work toward the greater good (supporting the community)?

  News Brief by Pete

A Few New Sites & Forums
  - QBasics Español
    QBasics Español is a new barebones QB site, available in both Spanish and English, hosted by PhatCode.
  - Eternal Journey Forum
    SSC, the head honcho of SmithCoSoft, recently announced that he has opened a new forum for his RPG, Eternal Journey. There hasn't been anything new about the game's progress since early this summer, but if you want to talk about it, visit: http://forums.smithcosoft.com/
  - The QuickBasic Forum
    George Williams and a few of his friends have launched a new QuickBasic forum, called simply, The QuickBasic Forum, at: http://quickbasicforum.eamped.com/, which has a few very active users, who have begun a process of reposting all of the tutorials at my site, Pete's QB Site, in their forum. But hey, if you want to talk about QB, you might as well check it out.

### Project News

Color Triple YAHTZEE!

  MystikShadows has released a FreeBasic textmode version of the popular dice game Yahtzee! You can download it from the ASCII-WORLD website (scroll to the bottom). If you're a fan of Yahtzee, it's definitely worth checking out. Here's some info from MystikShadows:

  > Color Triple YAHTZEE! is more than a new version of a now classic dice game, it's a portal into the twisted mind of it's creator (namely, me! ;-) ). So take a look, give it a try and let me know what you think. I don't believe there's any problems with it so if you notice something, let me know so I can correct it.

  > You just need to take a look our last games page (page 3) right here.

  > MAIN FEATURES:
  > * 1 to 4 players can play at a time.
  > * Dice Face value AND Color of the face come to play in this game hence new scoring possibly are available.
  > * Added to the first feature 3 columns (SINGLE score, DOUBLE Points and TRIPLE points) for higher scores.
  > * Any and all possible scoring options are automatically evaluated and shown to you on the scoreboard for selection.
  > * You can save your current game (should you need to) and come back later to finish it.
  > * It keeps track of the top 100 scores you made and allows you to view them at any time from the main menu.

  > The title page is 95% EK's creation, the rest is my two cents ;-) but I love how it looks hehe.

  > Let me know what you think when you get to try it.

  > MystikShadows

  > "Fighting For The Rights of Text Games and Application All Over the Net!"

  What else is there to say? Yahtzee!

  News Brief by Pete

Skippy! - A FreeBASIC Card Game

  Speaking of parlour games, subxero recently released a FreeBasic adaptation of the popular card game Scip-Bo, entitled Skippy!

  "Skippy is a complete PC version of the card game, Skip-bo. It is written to be portable between both Windows and Linux, and utilizes pleasing graphics and smooth animations to increase the positive feel of the program."

  Skippy hasn't yet been optimized to run on faster or slower computers, but according to subxero, an updated version should be coming soon.

  For more info, or to download the game, visit the official site.

  News Brief by Pete

DaBooda OldSchool Gaming Library

  DaBooda is working on a self-titled library for all your FreeBasic gaming needs. He recently posted a small demonstration of the lib's capabilities, and it is very impressive.

  "It's basically a library that gives the programmer a structure for programming video console type games, think super nintendo. The plus is it is object oriented so several addons can be programmed separately which will be a big bonus and allows the individual to customize it to their game."

  Check out the features list:

  > Features
  > - Resolution can be any supported
  > - Emulated Resolution 320x240max - 8x8min
  > - Full Screen or Windowed
  > - Custom Palette with 256Colors, up to 262,144 total colors
  > - Keyboard or Joystick support(Directional and 8 buttons)
  > - Four Character Maps(any size and dynamic)
  > - One Effect map, for cool scrolling effects and overlay
  > - Eight TileSets(255 tiles each)
  > - Up to 1024x8x8 Sprites on Screen
  > - Many features will be done in seperate libraries that you can add on to your project...

  More information about the DaBooda OldSchool Gaming Library can be found at the official site.

  News Brief by Pete

Dr_D Posts Movies of Robo-Raider and Kart

  Dr_D and Rattrapmax6 are collaborating on two awesome-looking 3D games, which by now I'm sure you've heard about, and maybe already tried out. Anyway, he recently released full-motion videos of each game, which you should definitely check out:

  Robo-Raider: http://www.bes.pbasichasnoballs.com/wiki/images/Movie.wmv

  Kart: http://www.bes.pbasichasnoballs.com/wiki/images/kart_movie.wmv

  News Brief by Pete

Qbinux Updates

  Ah well. I’ve gotten my hands on a program that translates C to VB. So, this means that Qbinux contains more Linux- code then ever before.

  The file-system is still a pain in the butt. I’m planning on using some Novix code for that matter. The main core, without the FileSystem, MeMory, etc stuff is working according to plan. As before it is able to show a somewhat boot up, and retrieving some hardware stuff. As I might have said before about some QFS (Qbinux From Scratch)-project, that’s what I’m working on. This gives also a lot of less stress.

  In past QBExpresses were some stuff about a multitasking core. I’m also working on that matter to implant that as well.

  I can’t give an exact date for a beta of a somewhat full version but I’ll post a beta of what I currently have.

  If you go and test the thing, please let me know what you think and if you think it needs adjustments, let me know.

  News Brief by Seb McClouth

Games! Games! Lots of Games!

  There have been a HUGE number of games released since QB Express last (adequately) covered the news, and I'm not going to try to go back over the last year and cover everything. However, I thought I'd voer a few of the best games released over the past two months or so. (Thanks to the FreeBasic Games Directory for most of the screenshots.)

  (For a look at unfinished / upcoming games, look at this month's Gallery, which features SEVEN different games...complete with screenshots, of course.)

  Lodestar by ShenZN

  "An arcade game to subjects of the cosmic fighting. She consists of five levels, which do not similar the friend on friend by its playing process. In them to be kept the elements of the different plays such as Asteroids, Lander and others. On plot you ordinary warrior to empires Lodestar, which there is get through many obstacles and rescue the world from extraterrestrial invader. All that occurs on screen during play is closely connected with plot."

  Lander by Redcrab - (A Cute Short Game Project)

  "A rather nice variation on the classic Lander game with really cool old-style two-colors graphics and nice rotation/zoom in/zoom out effects."

  Parachuters by Kristopher Windsor

  "ParaChuters is a fun mini-game where you control a cannon with a mouse and shoot at the passing helicopters that drop parachuters. Very good gameplay, below average graphics, save high scores table feature with challenging scores to beat, nice music and non-intrusive sound effects."

  Dark Spelunker by Kristopher Windsor

  "Dark Spelunker is a SFCave clone. A similar game entitled UrthWurm exists for PC (DOS version). It’s a simple game where you guide a worm with one key, changing its direction toward up by holding the action key, while gravity pulls the worm down when you don’t hold the action key. Interesting graphics (but very small amount of it), save high score feature, no sound or music, and not so good gameplay."

  BeGemmed by Kristopher Windsor

  Yet another game by Kristopher Windsor! This one is a "BeJeweled" clone.

### Competition News

ciw1973's Primitive Graphics Competition - With $200 cash prize!

  There's a very interesting and successful competition going on at FreeBasic.net at the moment, hosted by ciw1973. Here's the description from the forum topic:

  > I'm a big fan of minimalism in games (and small downloads) and I notice that recently KristopherWindsor has posted a couple of his creations in which all of the on-screen graphics are drawn using the primitives of fbgfx2.

  > So, I propose a competition where everyone has until 11.59pm GMT on the 31/07/2007 to write a game using no "pre-drawn" graphics, just what can be created in code. To make it more interesting I'll be offering £100 (roughly $200 / EUR150) as a prize for my favourite game.

  Competitions with actual prizes are few and far between in this community, especially ones with substantial cash prizes. The promise of money, along with the interesting premise, made this one of the most successful competitions of recent memory.

  Screenshots of two entries, by Lachie Dazdarian and ChangeV.

  Though the deadline has already past, as of August 13th, there was a very respectable seven entries:

  > ENTRIES:
  > - Laser Pipes - Mlok
  > - Mighty Line - Lachie Dazdarian
  > - Text Fighter Alpha Ex - sir_mud
  > - Crane - ChangeV
  > - Space Arena - Barok
  > - Zonaxtic - KristopherWindsor
  > - Path of Revolution
  > - CATLOAF 2600 - radicoon

  The results have not yet been revealed, but ciw1973 is actively judging the competition (which has gone a bit over schedule). The prize should be awarded in a few days. According to ciw1973, the results are very promising:

  > I'm not going to give anything away, but would just like to say how much I've enjoyed playing all of the games, and four of them in particular have held a good deal of replay value for me.

  > It's pretty close, and I've had to resort to a proper scoring system when I was drawing up my initial rankings to be sure I was being fair, and this is why I'm going to wait until I get feedback from the entrants before making a final decision to ensure that I've simply not misunderstood something about their game which may make a difference to the scores.

  No matter who wins, all of the entries are very cool mini-games. Be sure to check them out!

  News Brief by Pete

Cat Loaf Competion at c0de

  The guys at c0de have begun an interesting game programming competition starring their mascot character...Cat Loaf! Here's some more on the competition:

  > You must create a game, in which Cat Loaf Cat Loaf is the main character. There must be a story line, which depicts Cat Loafs history. This can be told at the introduction or revealed throughout the game. Any style of game may be created. Any language can be used, preferable FreeBASIC.

  > A due date can be decided by evr'body.

  > When you are done with the game, post a link to download the source (along with any extra data/sprites ect) and a compiled exe.

  > When the due date arrives, we can vote on a winner.

  > GO FOURTH! FORTH!

  Judging by the CATLOAF 2600 game entered in the previous competition, it looks like something will come of this one. Meow!

  News Brief by Pete

Have some news to report? Email it to qbexpress@gmail.com!

## Qlympics Voting Round

### Organized by Pete

You thought it was dead. Well, think again!

It's been well over a year since we started the Qlympics 2006, and we probably should rename our QB/FB awards ceremony Qlympics 2007 at this point....but we're not going to. We're going to finish up the 2006 awards as-is, so that we can give recognition where recognition is due. As a reminder, this competition was open to any QB or FB game released between January, 2003 and July, 2006. So don't go complaining that games released in the last year are not on the ballot! (That's what the Qlympics 2008 is for, heheh.)

#### Where we stand

We have completed two NOMINATIONS rounds, where members of the community were able to nominate and vote for any game or program that they felt were deserving of awards. I spent an excruciatingly long time tallying up all of those votes, and getting rid of fraud ballots (I really should have created some kind of automated system, heheh), and now we have our final nominees! The community has spoken.

Votes will be accepted starting NOW, until the time that QB Express #24 is released (sometime in September). The final results of the Qlympics will be awarded in QB Express #24.

So what are you waiting for? To see the nominations and to vote, all you have to do is fill out...

Qlympics 2006 Voting Ballot

May the best programs win!

Questions or comments? Email pberg1@gmail.com!

## Gallery

### Written by Pete

Every issue QB Express features a preview and exciting new screenshots from an upcoming QB game. If you would like your game featured, send in some screenshots!

This particular issue, we're featuring screenshots of EIGHT different games from all over the QB/FB scene, since it's been so long since I've written a Gallery. Enjoy the screens!

### RTS: The Fallen Lands of Old by inded005

"The Fallen Lands of Old: A Real Time Strategy game programmed in pure FreeBASIC. This game aims to employ completely new concepts and a great story for a fresh gaming experience, rather than relying on flashy graphics etc."

Forum Post: http://www.freebasic.net/forum/viewtopic.php?t=7490

Official Site: http://sourceforge.net/projects/fbtfloo/

### 3d Side Scroller - Kcproductionz

> "Well I started messing around with OpenGL a few days ago and here is what I've got so far. Ignore the mario picture, Im going to be changing that to a model of my own in the near future."

> "Touching the water kills you, the objective is to get the crystal (flashing box =P), and it should be fairly easy as I increased how high you can jump for testing purposes."

Forum Post: http://www.freebasic.net/forum/viewtopic.php?t=9017

Download: http://www.kcdan.com/uploads/archives/SideScroller.zip

### Ghosts of Galaxian & 3D Walkaround/Flyer by thesanman112

Ghosts of Galaxian - http://www.freebasic.net/forum/viewtopic.php?t=8977

3d walkaround & flyer - http://www.freebasic.net/forum/viewtopic.php?t=8976

### 1945 Shooter Game by dreamerman

A 1945-style vertical scrolling shooter.

Forum Post: http://www.freebasic.net/forum/viewtopic.php?t=8768

Download: http://www.dream27.webpark.pl/1945.zip

### Side Scrolling Beat'em'Up by Rokkuman

"hay guys! Haven't been here in a while, and it looks like quite a few others are sharing that in common with me. But ANYWAY, I'm in the middle of yet another game project, which is not a fighting game this time, but actually a side-scrolling beat'em'up with RPG elements and all that good stuff. The engine is programmed in front of SDL and OpenGL (while the promising Cobra re-make only used SDL). This game, like my past failing projects, is coming along fairly well, and I can honestly say that I see it reaching completion, along with the fact that I'm motivated towards such since the college I'm going to in January would love to see it."

Forum Post: http://forum.qbasicnews.com/index.php?topic=12994.0

### Option V by Deleter

"A retro style 2D scrolling shooter created for the SHMUP-DEV Competition 2k7 Round 2 contest. Classic shoot-em-up gameplay with a twist as your ship has the ability to break down some of its components into autonomous helping "options" which you must retrieve from the forces of chaos."

Forum Post: http://c0de.aknerd.com/index.php?topic=111.15

Have a game in the works that you think is worthy of The Gallery? Email us.

## A Response to Z!re's Article in QB Express #22:
A new QB Express Policy

### Written By Pete

> Last month, we published an article by Z!re entitled "A serious article debating the pros and cons of FreeBASIC". Despite its innocuous title, the article amounted to little more than a nonsensical and highly offensive rant that had little to nothing to do about Freebasic.

> Readers went into an uproar about the article, and many people expressed their disapproval and threatened that they'd swear off QB Express for good if we continue to publish this type of content. (I'd link to some of the forum topics where these discussions went on, but unfortunately, Freebasic.net recently deleted its Off-Topic forum, along with all the posts.)

> In the past, it has been our policy to publish EVERYTHING that is submitted to the magazine. Previously, this has never been an issue. All of the content that was submitted has been high quality...or at least relevant to QB or FB programming. However, Z!re's article was different, and it wasn't until after the issue was released that I took notice.

> Here is our response, which outlines a slight change to our "anything goes" policy. (This article is taken from a series of private emails between myself, the other QB Express editors, and Cha0s, who took special offense to the article as webmaster of Freebasic.net and as someone with a prior history with Z!re.)

> - Pete

#### A Letter from Cha0s

Hello,

This is cha0s. I'd like to talk to you about z!re's psychosis, e.g. long inflammatory rants, with a "false flag" of being an editorial on FB. I think this is quite underhanded and if you want to make FreeBASIC look bad, do some studying and find the flaws in the compiler itself; do it objectively.

If this was not meant as a direct attack on FreeBASIC, then I'll be encouraged to hear the reasoning from whomever made the executive decision to run that "gem".

Contact me, let's talk about it.

-cha0s

#### Response from Pete, and Our New Policy

Hey cha0s (and Imortis and Mystik),

Sorry it took me so long to get back to you all.

Honestly, I didn't even read Z!re's article before it was published, since Imortis compiled and edited the entire issue. (I just skimmed over it to make sure that the formatting looked right.) I am as much to blame for the article being published as anyone else is, since I am the chief editor and I didn't even read the majority of the magazine. Certainly, I was negligent in that regard.

When I did finally get around to reading it this evening, I can see why there's an outrage. I agree with you that the article is rubbish. Z!re is a pathetic human being with a very bad attitude, and her only goal is to provoke and anger people in order to get attention. I've felt this way about her for quite some time.

But the real question is whether or not we should have published the article. It has been QB Express's policy since issue #1 to publish everything that is submitted, without censorship, whether we agree with it or not. However, we've never had an article like Z!re's before. It is almost entirely off-topic and aside from the title and a few small references, it hardly relates to programming or FreeBasic. Up to this point, every article that was submitted in some way, shape, or form related to programming or the QB/FB community. And that, I feel, is the important detail. Z!re's article is just an insane rant, designed for the express purpose of offending people who are easily offended, and has nothing to do with FB or programming. And for that reason, I feel like it does not belong in QB Express.

Our new policy: QB Express will publish everything that is submitted, so long as it relates to programming or the online programming community.

This DOES NOT mean that we will restrict publication of articles based on LANGUAGE, CONTENT or INTENTION. If Z!re's article had been partly or mostly about FreeBasic, and still had her rant about "jewboys" and the huge "FUCK YOU" at the beginning, it would still qualify for publication. (Side Note: This doesn't mean we will fall for loopholes. We're not stupid. We're not going to let a ten page article that has one line about FB tacked on, get published. But if the article is mostly about programming, it deserves to get published.)

By this new policy, Z!re's article would NOT have been published in QB Express #22. However, now that Z!re's article has been published, we will also NOT remove / ban the article retroactively.

As long as I am in charge, QB Express will be a programming magazine that steadfastly respects everyone's right to free speech, whether or not it will offend or provoke the public. However, we also can't forget that our subject matter is "programming."

Thanks,

Pete

#### Denoument

Just in case anyone's wondering, this policy has had absolutely no effect on the magazine thus far. Everything that was submitted for issue #23 was included, all of it was relevant to BASIC programming, and not a bit of it is the least bit offensive. I wouldn't be surprised if Z!re's article was a one-time thing.

Have an opinion on this? Email us at qbexpress@gmail.com!

## Best GUIs of the Modern Era

### Written By Brandon Cornell

I decided to write this because me and my friend were talking about the best English GUIs of the Modern era (Post 2004) and I thought I would write something about all the GUIs and their influence and power. I wrote comments on all the GUIs and I contacted a few people to also comment on some. The rank is based on the opinions of me, Todd Suess, and Murray Parkinson and a recent poll at BrandonCornell.com.

1. Fun500 GUI-

   My GUI.It had humble beginnings, as I didn't know much graphics or fancy commands, it started as a spaghetti coded keyboard controlled desktop with a few crappy apps. Then evolved to have a mouse and sidebar to windows and objects to where it is know in FB with a Scripter. It was the first BASIC GUI to load BMP themes. In GUI Awards 2005 Fun500 GUI 1.0 took 2nd.

   "It rocks"-

   Murray Parkinson, Author of DC-OS

2. M-OS

   Has been around for some time. It was dropped by the original author and picked up by Todd Suess. It only can run one app at a time and is ridiculed for using to much of other peoples code. In GUI Awards 2005 M-OS 2005 took 1st.

   "It sucks"-

   Murray Parkinson, Author of DC-OS

   "Since 2003, I've been obsessed with GUIs. Like a painting or a work of art, GUIs have always fascinated me with their user interfaces, colors, widgets, and much more.

   It all began in 2003 when I first found QB 4.5. I already knew there was a QBasic, but I knew it couldn't do much since it was more of an interpreter than a compiler. So when I had QB 4.5, I wanted to look for websites that had code for it. And I came across GUIs such as QBF OS 99, M-OS, GIMI, and more. Some other GUIs were made in other programming languages such as C++, Pascal, and even Assembly. I wanted them all. Around the summer of 2003, I had almost every GUI I could find on the internet. There were some that once existed but were no longer available which kept driving me to find them even more.

   Around the beginning of 2004, I became interested with the "anatomy" of a GUI in QB. So I found M-OS, which was my favorite, and looked at the source code to see how it worked. I tried compiling it in QB, but I couldn't get it to work since I wasn't using any libraries when loading QB. So when I got it to work, it was just graphics and no interface. Since M-OS depended on a mouse to function, there was none implemented since I didn't know how to load the QB library in M-OS. After finding another GUI's source with instructions (i.e. loading QB with "/L") I finally got the GUIs to work. Some of them I had never found out what they looked like since I didn't know about the "/L" switch in QB, and so seeing them for the first time was like a surprise and excitement you might receive on your birthday or Christmas.

   When I finally got my own internet connection, I start surfing the web for GUIs and their websites. I found out that most were unchanged since 2001 or just ceased development. I was really disheartened since I thought more GUIs would've been developed. So I decided to use M-OS as my project and improve the desktop and interface. In the beginning, it wasn't much I started by adding little things like background loading BMPs and adding WAV-audio functionality. They improved the interface and the architecture of the GUI, and so I decided to stick with M-OS. Another reason why I chose M-OS was because on the M-OS website (no longer exists) the original creator, Jonathan Thorpe, along with a few other people had created the GUI to the point where it was a fully-functional, high-resolution and powerful GUI with 256 colors, themes, and many programs. I never found that copy of M-OS. So my goal was to make a GUI like it."

   -Todd Suess on GUIs and his GUI, M-OS

3. Costa

   There from the Start. It started good and just got better. But like most Modern era GUIs never got out of BETA stages. Its site http://jacobpalm.dk was the most popular hang-out.

   "a GUI developed by Jacob Christensen, and eventually he started JacobPalm.dk. His website was simple and attracted some users, but not many. Eventually, he added a community and a place to show your GUIs. And after being one of the first members of JacobPalm.dk, the site grew and became a GUI hub of the internet. This also drove me to open up my own GUI reviews site to share the GUIs I had found and keep them in one central place for people to download

   "-Todd Suess on Costa

4. Gopos-

   Nice GUI with a few good programs, that never really had its own web-site.

5. EnSpireMe

   A inspiring GUI it had a lot of new features such as dithering.

6. DC-OS-

   Fairly new GUI based on G-Control took a lot of ideas from other GUI and incorporated them, such as BMP themes like Fun500 GUI and using other peoples programs like M-OS. It was drop recently.

   "It rocks"-

   Murray Parkinson, Author of DC-OS

7. Linster OS

   One of those GUIs were the Author was new to GUI coding and wanted fun. It got better over time but never got out of Betas. Later versions were based on QB object rebuild. It was a lot like 9OS.

8. Edge

   Great windowing engine but nor much else drop as a BETA was picked up by Fun550 and they had no releases. Version 2.0 was started but never finished

9. 9OS

   Another GUI were the author was new to GUI coding and wanted fun. It got better over time but never got out of Betas. It had a dark red theme. It was a lot like Linster OS.

10. iOS

    A newer GUI that was not advertised much around the community but worked good despite having themes of only 2 colors.

11. Atlantis

    Was going to be a revolutionary GUI but never was ready for a public release

12. Teddy

    Was going to be a powerful scripting engine, but never had a release.

13. Pheonix

    The brainchild of me, it had a crazy colorful theme and had only 1 beta release that was coded entirely by Todd Suess.

14. OFOS

    A beginners GUI and looked a lot like Fun500 GUI 2005 or 2005 Bronze. It has some neat programs but no mouse.

15. Alien GUI

    The project after OFOS and had one release with objects and mouse control.

    "Over the years, GUIs such as Fun500 GUI, M-OS, Costa, and Edge all improved greatly. Although their development may be slow or near the edge of becoming extinct, they will live on and inspire another person to find QB and start coding."- Todd Suess, author of M-OS

If you think this was rigged because my own GUI was at the top you are wrong. Todd Suess gave Fun500 GUI 2nd and M-OS 1st in this vote. Murray gave Fun500 GUI 1st and M-OS 6th in this vote. I gave Fun500 GUI 2nd and M-OS 1st in my vote.

Comments? E-Mail me at admin@brandoncornell.com

## Confessions of a game designer

### Written By Lachie Dazdarian

Hello. Most of you should know me as the author of quite few painfully mediocre games, if we are going to speak frankly, released during last 4 years. Earlier in QBasic, but last few in FreeBASIC. This is my pity-party. Stop reading now if not feeling empathic.

I cannot promise that this article will contain any educational value, but I do hope someone will recognize my problems as the ones he or she had sometime in the past, and perhaps find comfort in that. I don't know. I just don't feel very good lately and want to share my sentiments with others. Pointless thoughts or not, I don't care.

My current state of mind can be best described with one word - impotence. Those less mature among you will now start to giggle thinking I'm talking about my phallus-thing, but I'm sure most already understand that I'm not referring to a physical state. Usually when people talk or write about generic game design problems, the issue of apathy pops up, but I rarely saw people mentioning in such opportunities the inability to execute an idea. Game design apathy is a common and annoying problem, but it's very often a result of lack of energy and will to execute some goals which are clear and doable, but in the same time require too much of our time, effort and patience. These things usually don't exist in necessary amounts which leads to sense of frustration or sense of being burnout. Such problems CAN be overcame with a simple break and better planning of a game development, like, for example, using a map editor instead of creating all maps manually with Notepad (this almost killed Ball Blazing Fantasy).

My problem is more serious, as I already said. Inability to turn ideas into creative work. Strangely enough, I don't remember experiencing that during my QBasic years. I guess I had lower standards back then and creating was much easier. The first time I started feeling this was the last summer (sometime after I released Vector X 2006), which was completely obligations free for me since I managed to finish all my exams before that. Once more I put it into my head that I have to create something during that time. I had 2 months tops, which is plenty of time for making something nice looking, one thinks. Well, anyone who tried to finish a game knows how time isn't the only variable. Sadly, I always try to ignore the truth in such moments. If a clear objective is not present, enthusiasm soon turns into despair. That's how I felt then. It's not that I didn't have or don't have ideas. I do have ideas. Many. I dare to say some of them feature excellent concepts. But these ideas are rather demanding to execute and can't be done in 2 months. Or at least, shouldn't be done. Also, for a great deal of them I don't feel competent yet. So I was trying to think of a concept that would result in great game play, with high replay value, but which could also be executed excellently in short amount of time. Something. Some magical concept. Stupid, I know. The worst thing is, I did come up with few "mini-game" ideas before Star Cage, but I would always give up when realizing I can't execute the most fundamental part of the design, like animate a character (any character) for a platform game. Star Cage, which was created soon after this, was more a happy accident that a result of careful preparation and planning and/or accumulated skill. And even such a cool idea about a "full screen rotating engine" cannot result in anything memorable, as the game was developed less than month and a half. The very engine is highly potential, and I'm a bit surprised for the lack interest in it (perhaps the interested developers were not willing to verbalize it), but the game is far from top quality standards. Still, Star Cage is probably my least "defected" game.

One would think that I learned something from this, but obviously I needed two creative blockages to accept the truth. Again at the end of this year (2006) I ended up with some free time in front of me after graduating. Not plenty, but just enough for a small project. Again I started contemplating about a project that will bring me peace of mind. A project easy to execute in small amount of time, but which still results in a rewarding game (to release and play). Something I will be entirely happy with. But why, I should ask myself. Why I need this to have peace of mind?

After some contemplation I concluded that this tendency of mine, to release rushed projects in small pockets of free time, is a result of several things. First, I'm obviously highly disappointed with my work done so far. I came to sad realization that I didn't achieve my main game design goal - to release at least one overall excellent game. A game I can be proud of in front of any audience. A game without defects or flaws. My Ultimate Super Stack, if you like. A game that can only be disliked because it doesn't suits someone's taste or genre preference. I failed to accomplish that. So in every possible opportunity I try to fix this, foolishly with another rushed project, subconsciously aware that I will fail. That I will release yet another under-achieved product, with more or less flaws/defects. Unimpressive and unexciting above all. Mundane, how one person, who doesn't like me much, described my games. I guess if I was surer in my abilities to succeed in this world, I wouldn't feel like my time is running out every now and then. I would be more motivated to dedicate myself to an ambitious project and slowly work toward it. Learn in the process. This reminds me that the another result of so many rushed project, of this sick desire to release and not to grow, is my failure to become a better graphics artist, designer and coder. I made ignorable improvements in all these fields and it sickens me. Over 10 completed games and what I have to show beside the very (mundane) games? I'm not saying I regret for designing games. It was that or watching more TV. I regret the way I designed games.

Something that made me even more depressed was a large selection of current freeware games I played during last 6 months. For some reason I started playing a lot of games lately, including many old Amiga games ran through WinUAE, but these games are not the issue here. Anyway, the realization that there are so many excellent freeware games out there, flawlessly designed, made me question my abilities even more. A somewhat ironic situation for me since sometime ago I talked about this with one member of our community. I was saying something in the line of that there is no point it modeling your goals according to other people's games, because there will always be game designers doing excellent freeware stuff and trying to do better than them or equally good can only be unconstructive. I was saying that the best approach to game design is to set your own goals, goals you are happy with, and then work toward them. But one just can't help himself seeing all this excellence. Especially if that someone released so many inferior games before. Perhaps you'll understand my disappointment more if you see which games are those that impressed me so much. I should mention Nifflas first (http://www.ni2.se), a game design wizard (if not a genius). His Knytt, and especially Within a Deep Forest, are masterpieces of design, from the great atmosphere they feature to excellent coding. Then there was Spuds Quest, a marvelous Dizzy-like game I played recently. I shouldn't forget Bloody Monkey's (http://www.bloodymonkey.com) Naac and less-ambitious but still excellent Turbopac. I'm OK (http://www.slutbear.com/thompsonsoft/) is something you should also take a look at. Not to mention Joakim Sandberg's Noitu Love. Few days ago I finished Bernie's (www.origamihero.com) Reactor 9, which looks like a Sierra's game from 1994, though of a smaller scale. Impressive graphics and writing. And these are just some of the best. Dozens of other that raise high above my work come out weekly (check www.create-games.com - GOTW section especially). Inability to come close to these heights, or even to starting coming close, frustrates and disappoints.

Perhaps I'm realizing I have no talent, and it hurts. Perhaps I was only meant to admire other people's talent. I'm only sure that I love computer games. I honestly enjoy playing them and admire the quality of the excellent ones. At least I have that. At least I didn't turn into a hateful creature.

I'm not saying I'm giving up. That would be stupid and probably pointless. Who knows how I will feel in few months? On the end, I'm not an appalling game designer. And in no way the things I did in the past prevent me to do better in the future. They don't make me very hopeful, that's for sure. But maybe I just need to try harder, more than some others. And maybe I need to become a better person first in order to become a better game designer.

Few days ago I heard this great Croatian saying that describes my situation very well. In rough translation it would go something like this: "Help yourself so God could help you."

All the best, Lachie Dazdarian.

P.S.

Thanks to all those who honestly enjoyed playing my games (I know that there were few) despite their flaws.

## Just What Does Pritchard Have Against QBE Again?

### Written By Pritchard

NOTE: This was all written in one sitting. Please excuse it, or worship it. It is almighty text, brought to you by a Cola invented by Catloaf, which I am now drinking at a highly dangerous rate.

It's not the name, QBE that bothers me. That would be stupid. If we changed the name it would be the same magazine, but with different letters to celebrate its glory in. If that's the case, then just what does bother me about that magazine? A magazine I've been reading for many months. A magazine that has always been there for the community even when we're not doing anything. A magazine that managed to pull its own self out of the grave, just to prove how strong and capable it really is. Damn. Those are a lot of cool things about it. Guess I have no reason not to like it, right?

Well, it's not that I don't like the magazine in particular. I just didn't like the idea of the magazine coming back. I feel that QBE has always been the place that the community turns to when it's in a dump. When it hasn't been able to pull itself out of a deep, dark hole, Pete (and recently Imortis) would come along and do it for us. This wasn't a problem before. It wasn't ever, until now. QBE had a hiatus for a while, and it was gloom and doom at first, but in the right perspective it turned out to be pretty awesome.

The FreeBASIC Community, or QB 2.0 as some may wish to call it (they'd love to, but not until it supports literally impossible features), actually managed to survive on its own after QBE was gone for a bit. As the articles began to come out later and later, a lot of us were wondering just what to do without it. We didn't do much for some time, and as we saw the magazine dying, well . . .

The QBasic Community's dead - Well, what remains of it. Anyone left is just a bunch of zombies who crave nothing but GOTOOOOOS (qbasicstation, anyone?). What was left of it just as QBE began its hiatus was beginning to see its end in site, and had began to run off to other sites to avoid being poisoned with the X-Virus. We didn't have much else to go for a "Community" site or magazine, but we did have the FreeBASIC forums and the hellish QBNews. After a while, we settled in to the FreeBASIC Forums. There, we gathered in a different, but still homely way. Still no magazine, but we were beginning to think independently. We knew that if we didn't do something, no one was. It was time for someone to act.

The FreeBASIC Report was born. It was our first (failed) attempt at a replacement for QBE. While the report allows us to submit content in a really nifty manner (what I wish QBE had), it still lacked something major to all magazines - An editor. The FreeBASIC Report boasted that it was a community-driven site, so no editor was needed! Hahahah. Maybe about 5-6 years ago when everyone had their own forums, this might have worked. Not today. Pete's QBE focused on user-submitted content, but Pete went out of his way and every month would take several long journeys across the internet to make sure people knew to submit content. Even better, he offered an editor's note, and letters to the editor! This was not only a user magazine, but we had an action figure that would interact with us. Here comes Pete - QB Extraordinaire to the Rescue! Oh . . . Wait . . . He's still too busy :P

With an abandoned FreeBASIC Report, the community was once again forced to think of a solution. Several had come about. All of these without the help of Pete, who as much as some of the community might deny it, was our coach, or mother, a police officer, whatever gets you off, to help us get back on our feet and keep the community connected. As for the content that came out without Pete's help?:

- The FreeBASIC Code Library - I manage it at the moment, but is free to everyone to submit to. Located here. I look around the forums every now and then, find code which looks useful, and put it in its proper category. It's already growing big enough for me to start thinking of how to further divide the sections to make things easier to find. Of course, it's also become harder to manage (even if rarely anyone else adds a link to it), mainly for the fact that some code is realllly amazing, but REALLY old, so I'm having trouble porting it over and feeling guilty about linking to code that I haven't already.
- The FreeBASIC Games Directory - Managed by Lachie Dazdarian. Can be found here. He collects FreeBASIC Games, hosts them, rates them, and describes them. He does this with the help of his staff, and allows community input right there on the site. I think he manages to hit most of the important areas of a community site with this one.. Most of you should know (except maybe Lachie) that I don't like Lachie very much as a person, but I adore him as a coder to all hell. It's nicer to see him a bit more relaxed, when he's in a good mood. Who better than the game legend Lachie to start a site that collects games? This is probably the greatest example of a magazine without the help of Pete thus far :P
- c0de - Managed by cha0s. Can be found here. I would mention just about every other forum managed by FreeBASIC Members, but I won't. c0de was specifically built for a bunch of ambiguous (hahah!) reasons. Mainly, to have a place for us FreeBASIC Members to hang out. Made around the time QBNews decided to die (thank god), I always wished for it to be named FreeBASIC News, because it can easily be made to be managed as such, but it's actually been good having a more independent crowd hang around there than that from QBN. We *do* need a replacement for QBN. I think c0de's a great start, if only for the original cause rather than what's come of it so far. It has that QBN familiarity (more modern dark QBN theme by default), with no QB sub forums! Awesome :D Along with its currently empty challenge forum, and its light atmosphere, I think this will eventually be *the* hangout site if cha0s plays his cards right.

Wow! Look at those three small things. They're pretty darn good for what they are, imo. Oh yeah, and The FreeBASIC Report was revamped too, although still lacking an editor. Of course, QBE's back. A free market promotes competition, but who's going to compete when there's no money involved? QBE has monopoly on the magazine scene, IMO. This brings me to one of my next points.

After an amount of time of prosperity, and my hopes that someone with web experience who's well-known in the community would announce a more modern QBE, or a more modern FBN, I see this post on the forums: "OMG TEH QBE IS BACKS I R TEH IMORTIS!", and all my dreams turn into huge piles of shit and fall on cows. Instincts. The Community must survive, thus without QBE, we began creating our own content. Instincts. The Community is lazy. QBE is back. Horray. Now I can sit back and not do shit, and make articles, tutorials, and whatever content, only when Pete or Imortis come by begging everyone to submit. I don't care if I sound mean to them in particular. I really don't give a shit. I meant what I said when I explained how much I plain wanted QBE to die and rot, and be forgotten.

There is nothing wrong with QBE, except for that it's old and could use some modernization, as well as excluding QB articles (but this would piss off whoever still reads it). That's the problem. It's so good, and it's so legendary, that our community (not to hate on you guys but it's true) has began to depend on it. It's like having this really rich buddy. Not only is he rich, but when you spend all of the money he lent you, he'll actually rush down there in his ferrari, hand you some more cash, bail you out of jail, then give you the keys to his car. Fuck living, your friend's rich.

Of course, I think we still have a lot of potential. I may be kind of mean to QBE, and I do want to emphasize, I mean it. However, I don't mind community prosperity. If you really care about the community, you're going to take some time to sit down and think about whether you want to wait for QBE every month to start submitting content, or if you want to submit it every week.

You want to make an imaginary magazine that helps keep you going? Right on. Don't buy the community reliance on QBE? I'm positive nearly every article submitted over the past few issues of QBE were supposed to be written for the previous month the were submitted in. It began to show on the forums, and it began to show in the quality and quantity of content. Remove yourself from depending on the rest of the community. You're a part of it. You are the community. We are the community. We have to work together to be rich and prosperous, so we can all have each other's ferraris. Don't you like ferraris?

Thanks for your time,

Fuck you QBE,
Prichard.

You can reach Pritchard at theadventmaster@gmail.com.

## Monthly Awards

### Written by Pete

> c0de
> http://c0de.aknerd.com/
> Webmaster: cha0s

> c0de has only been around for about a month, but it's already one of the most active forums in the Freebasic community. It's got a much more relaxed, informal feeling than the Freebasic.net forums -- which, frankly, feel "all about business."

> c0de is a much more fun environment. Even though its members are the same people that populate every other forum in the FB scene, c0de is where people go to "hang out" (similar in a lot of ways to the now-defunct FBTK, or Nekrophidius' Basic Network, another popular hang out forum a few years ago). I mean, as soon as you load up the site, you can tell it's a fun place. For god's sake, it's got a friggin Cat Loaf on the front page!

> For being one of only new forums to develop into an active community, and for having an all-together good vibe, I hereby declare c0de to the QB Express Site of the Month!

### Programmer of the Month

> Kristopher Windsor

> Kristopher Windsor has been extremely busy the last two months, releasing no fewer than FOUR fun FreeBasic minigames: BeGemmed, Dark Spelunker, Parachuters and Zonaxtic. All of these games feature fun, old-school gameplay, and don't bog themselves down with too many play modes, a pretentious story, or unnecessary flair. Krisopher takes on reasonable projects that he knows he can finish within a day (or just a few hours), so he's remarkably productive. Many other coders in the FB scene set their sites too high, on making a huge epic RPG or a console-quality action game, and never come close to finishing their projects. If you want to finish products and share them with others, short, simple games are where it's at.

> A screenshot from Dark Spelunker.

> Another reason Kristopher is the winner this month is because he inspired a very successful competition at Freebasic.net: ciw1973's Primitive Graphics contest. According to ciw1973, he appreciated minimalism in games, and the idea for the competition came when he noticed "that recently KristopherWindsor has posted a couple of his creations in which all of the on-screen graphics are drawn using the primitives of fbgfx2." Other members of the FB community are taking notice not only of Kristopher's games, but of his programming style, and it's making an impression.

> For his countless game releases and simple but effective coding style, Kristopher Windsor is the QB Express Programmer of the Month.

Email us at qbexpress@gmail.com with your nominations for the monthly awards.

## Comics

### By James Kinney, sir_mud, Stoves, and diffeecult

This issue, we have godObject Comics from sir_mud, PSET comics from Stoves, "QBN Rise and Fall" by James Kinney and a comic from diffeecult.

Click comics to view them full-size (will open in a new window).

### "Rise and Fall of QBasic News" by James Kinney

### PSET by Stoves

### GodObject Comics by sir_mud

### Comic by diffeecult

If you'd like to submit a programming-related comic, send it to: qbexpress@gmail.com!

## Angles in 2D environment and artificial smarts based on them

### Written by Lachie Dazdarian (July, 2007)

### Introduction

I'm this dummy level tutorial I'll try to teach you how to retrieve an angle between two objects in a 2D environment, and then use this angle to create fun artificial smarts which can, for example, follow around the screen a circle you control. All this can on another level be used in various top-down games that feature 360 degrees rotation to tell enemy CPU controlled objects to react on the player on various ways (attack, run away, hit & run, etc.).

You could describe this as a more smart way of telling one object to follow another than using x and y pixel distances. The method with x and y distances can be sufficient to some extent, but you'll most likely find it crude and limited.

To extend the scope of the tutorial, in the second part of it I'll add to player the ability to shoot projectiles and destroy the circles that follow him. This will illustrate how projectiles shot with an angle should be managed, and we'll repeat some stuff from my "How To Program A Game With FreeBASIC - Lesson #2" tutorial.

The code was written in FreeBASIC ver.0.18 and compiles with –lang fb (should be fully compatible with FreeBASIC ver.0.17).

### Part One

We’ll work in the Cartesian coordinate system, system used to determine each point uniquely in a plane through two numbers, usually called the x-coordinate and the y-coordinate of the point. This is a given coordinate system with most programming languages as it results from the nature of computer monitors (stop laughing). For any other coordinate system, like polar, we would have to create it first from the Cartesian coordinate system. I still didn’t work in any other coordinate system so can’t tell you in what kind of 2D games they can be useful.

Some basic trigonometry knowledge will help a lot in understanding this tutorial.

Let's first start by declaring variables we'll need in our example program.

>     ' We include FreeBASIC's built-in library and allow
>     ' the usage of its additional constants and functions
>     ' with Using FB.
>     #include "fbgfx.bi"
>     Using FB

>     ' We set some needed constants.
>     const fpslimit = 60
>     const FALSE = 0
>     const TRUE = 1
>     const PI = 3.141593

>     ' We declare the needed variables.
>     DIM SHARED workpage AS INTEGER

>     ' The following 3 variables are used for
>     ' frames per second control.
>     DIM SHARED st AS DOUBLE
>     DIM SHARED frameintvl As Double = 1.0/fpslimit
>     DIM SHARED sleepintvl As Integer

>     DIM SHARED angledeg AS INTEGER  ' Main object's angle in degrees
>     DIM SHARED anglerad AS SINGLE   ' Main object's angle in radians
>     DIM SHARED mainx AS SINGLE      ' Main object's x pos
>     DIM SHARED mainy AS SINGLE      ' Main object's y pos
>     DIM SHARED mainspeed AS SINGLE      ' Main object's speed
>     DIM SHARED main_rotationspeed AS SINGLE ' Main object's rotation speed
>     DIM SHARED resultangledeg AS INTEGER ' Angle between the cpu cont. object and main object in degrees
>     DIM SHARED resultanglerad AS SINGLE  ' Angle between the cpu cont. object and main object in radians
>     DIM SHARED CPUobjectx AS SINGLE    ' CPU controlled object's x pos
>     DIM SHARED CPUobjecty AS SINGLE    ' CPU controlled object's y pos
>     DIM SHARED CPUobj_angledeg AS INTEGER ' CPU controlled object's angle in degrees
>     DIM SHARED CPUobj_anglerad AS SINGLE  ' CPU controlled object's angle in radians
>     DIM SHARED CPUobject_rotationspeed AS SINGLE ' Rotation speed of the CPU controlled object
>     DIM SHARED CPUobjectspeed AS SINGLE ' CPU controlled object's speed
>     DIM SHARED ASmode AS INTEGER ' artificial smarts control (on/off)

I think the comments explain most of it, with FPS control variables and the FB’s built-in graphics library initiation not belonging to the topic of this tutorial, but are something you should take a note of. It's quite obvious we'll need variables to store x and y positions of our two objects, their current angles in degrees (0 to 360) and radians (0 to 2*PI), their speeds, variables for the result angle (angle between the two objects), and a help variable that allows us to turn on/off the artificial smarts. Objects' positions and angles in radians should be declared with SINGLE or DOUBLE precision.

Constants for FALSE and TRUE you should declare always, and the PI constant too when using angles in your project.

After declaring all the variables we need, we should initiate our screen and variables' initial values.

>     ' We set our screen to 640 width, 480 height, 32 color bit-depth,
>     ' 2 work pages and full screen.
>     SCREENRES 640, 480, 32, 2, GFX_FULLSCREEN

>     ' We set the initial values for the main object's
>     ' and CPU controlled object's positions/angles/speeds.
>     mainx = 320
>     mainy = 220
>     mainspeed = 4
>     main_rotationspeed = 3
>     angledeg = 0
>     CPUobjectx = 200
>     CPUobjecty = 200
>     CPUobj_angledeg = 20
>     CPUobj_anglerad = (CPUobj_angledeg*PI)/180
>     CPUobject_rotationspeed = 3
>     CPUobjectspeed = 2
>     ASMode = TRUE

Everything clear here. Note how computer object's angle is converted from degrees to radians. We need this value in radians because radians are used with the FreeBASIC's SIN and COS functions (later in the tutorial).

From Wikipedia:

> One radian is the angle subtended at the center of a circle by an arc of circumference that is equal in length to the radius of the circle. It equals to 180/PI degrees, or about 57.2958 degrees. In calculus and most other branches of mathematics beyond practical geometry, angles are universally measured in radians. One important reason is that results involving trigonometric functions are simple and "natural" when the function's argument is expressed in radians.
> In most mathematical work beyond practical geometry, angles are typically measured in radians rather than degrees. This is for a variety of reasons; for example, the trigonometric functions have simpler and more "natural" properties when their arguments are expressed in radians. These considerations outweigh the convenient divisibility of the number 360. One complete circle (360°) is equal to 2*PI radians, so 180° is equal to PI radians, or equivalently, the degree is a mathematical constant ° = PI/180.

Now let's create a standard DO...LOOP where our program will be happening.

>     ' Your average do loop.
>     DO

>     st = Timer ' Record the current time into st variable
>                ' (used to limit FPS; no related to the
>                ' topic of the example program).

>     screenlock ' Lock our screen (nothing will be
>                ' displayed until we unlock the screen).
>     screenset workpage, workpage xor 1 ' Swap work pages.

>     CLS ' Clear the screen

>     workpage xor = 1 ' Swap work pages.
>     screenunlock ' Unlock the page to display what has been drawn.

>     ' Keep the FPS value within fpslimit (set above).
>     sleepintvl = Cint((st + frameintvl - Timer)*1000.0)
>     If sleepintvl>1 Then
>       Sleep sleepintvl
>     end if

>     LOOP UNTIL MULTIKEY(SC_ESCAPE) ' Do loop until ESC is pressed.

Let's now draw few circles that will represent our objects, and lines that will represent their direction. Put this above workpage xor 1.

>     ' We draw our objects (represented with small circles) and
>     ' lines that emulate these objects' directions.
>     LINE (CPUobjectx, CPUobjecty)-(CPUobjectx+sin(CPUobj_anglerad)*20,CPUobjecty-cos(CPUobj_anglerad)*20), RGB(2,117, 250)
>     CIRCLE (CPUobjectx, CPUobjecty), 3, RGB(2,117, 250)
>     LINE (mainx, mainy)-(mainx+sin(anglerad)*20,mainy-cos(anglerad)*20), RGB(200, 0, 0)
>     CIRCLE (mainx, mainy), 3, RGB(200, 0, 0)

Why adding SIN * object's_angle * 20 to X coordinate?

Allow me to post few pictures now that will help us to clear few things up.

Picture 1
Picture 2

If you refer to picture 1 and 2 above, you'll note that a in our system (system where 0 degrees is a vertical line going from bottom to top) is delta x (x2 - x1), and a is c * sin alfa, c representing the length of our line (or shortest distance from the point of origin - center of the object in our case). With y the situation is the same, only we use COS and we need to deduct it because in FreeBASIC the positive direction of the y axis goes from top to bottom. So COS * object's_angle in our system equals -(COS * object's_angle) in the default BASIC coordinate system. You can choose to work in the default BASIC coordinate system, but then your 0 degrees will be rotated by +PI (where 180 degrees is on the picture above). Anyway, I chose to work with the standard axis directions because they are burned into my brain from school. I find it less frustrating to make these corrections than accepting the inverted direction of the y axis.

Before all, we need to find a way how to retrieve the angle between our two objects. We have their coordinates, x and y, and from these coordinates we can calculate the distances between the two objects vertically and horizontally. To get the angle we'll use the arctangent function (ATAN2 (delta y, delta x) in FreeBASIC) which retrieves an angle from objects' distances vertically over objects' distances horizontally (delta y / delta x). Use the following lines to get alfa 2 (picture 1). Put this in the DO...LOOP before CLS:

>     resultanglerad = ATAN2((-1)*(mainy-CPUobjecty),(mainx-CPUobjectx))
>     resultanglerad = PI/2 - resultanglerad
>     IF resultanglerad < 0 THEN resultanglerad = resultanglerad + 2*PI

Think of CPUobjectx, CPUObjecty as of x1, y1 and mainx, mainy as of x2, y2. We need to multiply delta y with -1 because of the mentioned difference in the direction of the y axis, and since we need alfa 1 and not alfa 2 we deduct alfa 2 from PI/2 (90 degrees) to get alfa 1. Check picture 1 again. The third line in the code keeps our angle within 0 to 2*PI scope (full circle), to keep things clear (negative angles confuse)

That's one way of doing it. To get alfa 1 directly, we can to this:

>     resultanglerad = ATAN2((mainx-CPUobjectx), (CPUobjecty-mainy))
>     IF resultanglerad < 0 THEN resultanglerad = resultanglerad + 2*PI

This code does the compensation for the difference in the direction of the y axis, and for the fact our angle starts vertically from bottom to top (we replaced the positions of delta x and delta y in the function).

What ever you chose, it will work. Using the ATN function is more cumbersome and requires a more complex formula to retrieve the angle properly. Thus, I'm using ATAN2.

We also need to convert the calculated angle from radians to degrees so we can use its value in degrees with the artificial smart algorithm (it simplifies the code):

>     resultangledeg = (resultanglerad*180)/PI

After we found a way to retrieve the angle between our two objects, let's add some control for our player. We'll use left/right arrow key for angle change (rotation), and up arrow key for thrust.

>     ' The control keys for the main (player's) object.
>     IF MULTIKEY(SC_LEFT) THEN angledeg = angledeg - main_rotationspeed
>     IF MULTIKEY(SC_RIGHT) THEN angledeg = angledeg + main_rotationspeed
>     ' The following lines keep the angle between
>     ' 0 and 360.
>     IF angledeg<0 THEN angledeg=angledeg+360
>     IF angledeg>359 THEN angledeg=angledeg-360
>     anglerad = (angledeg*PI)/180 ' Convert the angle from degrees to radians
>     IF MULTIKEY(SC_UP) THEN
>     mainx = mainx + sin(anglerad)*mainspeed
>     mainy = mainy - cos(anglerad)*mainspeed
>     END IF

>     ' For turning AS on/off.
>     IF MULTIKEY(SC_1) THEN ASMode = TRUE
>     IF MULTIKEY(SC_2) THEN ASMode = FALSE

Note how the position of the main object is changed when the user presses the up arrow key. In each loop the object's x position is changed by the SIN of its current angle times its speed (picture 1 again), while the y position is changed by the COS of its current angle times its speed (deduced for the reasons explained above).

The last two statements are for turning on/off the artifical smart.

The entire source so far: codever1.txt

If you compile this you'll be able to move your object (red color), but the sky blue computer controlled object will just sit there since we didn't implemented any artificial smart algorithm.

So we need to tell our computer controlled object to rotate toward the player and move constantly. No problem! First, let's solve the rotation. Rotation will be handled like this. First, we have the angle between the computer controlled object and the player, and the current angle of the computer controlled object. By comparing these two angles and checking which rotation (clockwise or counter-clockwise) is of shorter distance for computer controlled object's angle to equalize with the result angle (angle between the two objects), we'll say to our computer controlled object to rotate in one or the other direction. Just observe the following code:

>     ' If ASMode is true apply the artificial smart code on the CPU controlled object
>     IF ASMode = TRUE THEN
>         ' If the CPU controled object's angle is larger than the
>         ' result angle (angle between the CPU object and player's object)...
>         IF CPUobj_angledeg > resultangledeg THEN
>             ' If the difference between the current angle of the
>             ' CPU controlled object and the result angle going
>             ' counter-clockwise is less than this difference
>             ' clockwise, rotate the CPU object counter-clockwise
>             IF (360-CPUobj_angledeg+resultangledeg) >= (CPUobj_angledeg-resultangledeg) THEN CPUobj_angledeg = CPUobj_angledeg - CPUobject_rotationspeed
>             ' If the difference between the current angle of the
>             ' CPU controlled object and the result angle going
>             ' clockwise is less that this difference counter-clockwise,
>             ' rotate the CPU object clockwise
>             IF (360-CPUobj_angledeg+resultangledeg) < (CPUobj_angledeg-resultangledeg) THEN CPUobj_angledeg = CPUobj_angledeg + CPUobject_rotationspeed
>         END IF
>         ' Same as above but for situation when CPU object's angle is
>         ' less that the result angle.
>         IF CPUobj_angledeg < resultangledeg THEN
>             IF (360-resultangledeg+CPUobj_angledeg) >= (resultangledeg-CPUobj_angledeg) THEN CPUobj_angledeg = CPUobj_angledeg + CPUobject_rotationspeed
>             IF (360-resultangledeg+CPUobj_angledeg) < (resultangledeg-CPUobj_angledeg) THEN CPUobj_angledeg = CPUobj_angledeg - CPUobject_rotationspeed
>         END IF
>         ' The following lines keep the CPU object's angle within
>         ' 0 to 360 degrees area.
>         IF CPUobj_angledeg<0 THEN CPUobj_angledeg=CPUobj_angledeg+360
>         IF CPUobj_angledeg>359 THEN CPUobj_angledeg=CPUobj_angledeg-360
>         ' Convert the CPU object's angle from degrees to radians.
>         CPUobj_anglerad = (CPUobj_angledeg*PI)/180
>         ' Move the CPU object according to angle and speed
>         ' (note how SIN and COS functions are used).
>         ' Since positive y axis goes down in FB, we need to reduce
>         ' the y position of the object by the COS function and not add
>         ' it as in normal Cartesian coordinate system.
>         CPUobjectx = CPUobjectx + sin(CPUobj_anglerad)*CPUobjectspeed
>         CPUobjecty = CPUobjecty - cos(CPUobj_anglerad)*CPUobjectspeed
>     END IF

The formulas for calculating the shorter distance to reach resultangle from one or the other direction might confuse you, but they are really simple. For example, let's say computer controlled object's angle is 275 ° and the angle between the two objects is 25 °.
First condition...
275 > 25 --> CPUobj_angledeg > resultangledeg
It's obvious that...
360 - 275 + 25 --> 360 - CPUobj_angledeg + resultangledeg (distance clockwise from CPUobj_angledeg to resultangle)
...is less than...
275 - 25 --> CPUobj_angledeg - resultangledeg (distance counter-clockwise from CPUobj_angledeg to resultangle)
...so we need to increase the angle (clockwise rotation) until it reaches 25 °. When the angle exceeds 360 ° during this increasing, it jumps back to 360 - CPUobj_angledeg (which is above 360 °).

Movement is done like with the player, but in this example we'll tell the computer controlled object to move constantly. Since the computer controlled object will be always rotating toward the player, the effect will be satisfactory. Perhaps in the second edition of this tutorial I can show you how to use different AS modes for one object so it wouldn't behave the same way ALL the time.

ASMode variable is used to turn on/off the movement of the computer controlled object and it only serves a purpose in this example program. Still, this kind of variable might come in handy during development of a game, allowing you to position player's object on any location near the computer controlled object in order to test its behavior in various situations.

Put the last piece of code after the resultangle calculation, compile it, and test it. Neat, eh?

For the very end of the first part of this tutorial, add the following lines in the code after CLS to make it all look more informative:

>     ' We draw an help Cartesian coordinate system.
>     LINE (50, 80)-(110,80), RGB(255,255,255)
>     LINE (80, 50)-(80,110), RGB(255,255,255)
>     Draw String (77,40), "0", RGB(0, 176, 214)
>     Draw String (69,114), "180", RGB(0, 176, 214)
>     Draw String (114,76), "90", RGB(0, 176, 214)
>     Draw String (23,76), "270", RGB(0, 176, 214)

>     Draw String (12,200), "1 - AI On", RGB(0, 176, 214)
>     Draw String (12,210), "2 - AI Off", RGB(0, 176, 214)

>     ' We display some useful textual information.
>     Draw String (320,10), "Main object's angle:"+STR$(angledeg), RGB(2,117, 190)
>     Draw String (320,20), "Computer controlled object's angle:"+STR$(CPUobj_angledeg), RGB(2,117, 190)
>     Draw String (320,30), "Angle between objects:"+STR$(resultangledeg), RGB(2,117, 190)

The entire code so far: codever2.txt

Screenshot of the result:

Compile the last source and enjoy. You can play with the rotation speed of the computer controlled object if curious.

Now let's move on.

### Part Two

In the second part of this tutorial we'll add multiple objects and ability to player to shoot projectiles. Buckle up!

As I did in my second edition of "How To Program A Game With FreeBASIC" (QB Express #20), we'll declare computer controlled objects as an array, with each element in the array representing one single object. With each program start, computer controlled objects' positions will be randomized.

For this section, just read the tutorial. The old code will be tumbled up and down a lot, so it's best for you compile the entire source when I provide it later.

First, we will change the way we declare computer controlled objects. We'll use a custom defined type (check my other tutorial, previously mentioned).

After constants declarations we need to have this code:

>     TYPE ObjType
>     X             AS SINGLE   ' Used to flag object's x position.
>     Y             AS SINGLE   ' Used to flag object's y position.
>     AngleDeg      AS INTEGER  ' Used to flag object's angle in degrees.
>     AngleRad      AS SINGLE   ' Used to flag object's angle in radians.
>     Speed         AS SINGLE   ' Used to flag object's speed.
>     RotationSpeed AS SINGLE   ' Used to flag object's rotation speed
>     Active        AS INTEGER  ' Used to flag object's status
>     ActiveTime    AS INTEGER  ' Use to expire object's activity (once we activate it).
>     Typ    AS INTEGER         ' Used to flag type of the object (if we want to
>                               ' have more kinds of the same object -> different
>                               ' ships, projectiles, etc.).
>     END TYPE

It contains all variables our computer controlled objects need, and what later projectiles will need to be managed.

After this we need to declare our computer controlled objects with:

>     DIM SHARED CPUobject(numofCPUobjects) AS ObjType

numofCPUobjects needs to be declared before as a variable or a constant. numofCPUobjects will represent the maximum number of computer controlled objects that can be active in a single moment, and it's good to declare it as a constant or a variable so we can change our program parameters easily. I've set it to 20.

We must initiate the positions of the computer controlled objects and their speeds like we did before with a single computer controlled object. This is done with the following code:

>     ' Get the random seed from the seconds past midnight (the
>     ' best way to get random numbers).
>     RANDOMIZE TIMER

>     FOR initCPUobj AS INTEGER = 1 TO numofCPUobjects
>         CPUobject(initCPUobj).X = INT(RND * 600) + 20 ' Randomize cpu object's position from 20 to 620
>         CPUobject(initCPUobj).Y = INT(RND * 440) + 20 ' Randomize cpu object's position from 20 to 460
>         CPUobject(initCPUobj).AngleDeg = INT(RND * 360) + 1 ' Randomize cpu object's angle from 1 to 360
>         CPUobject(initCPUobj).AngleRad = (CPUobject(initCPUobj).AngleDeg*PI)/180
>         CPUobject(initCPUobj).RotationSpeed = INT(RND * 2) + 2 ' Randomize cpu object's rotation speed from 2 to 3
>         CPUobject(initCPUobj).Speed = INT(RND * 3) + 1 ' Randomize cpu object's rotation speed from 1 to 3
>         CPUobject(initCPUobj).Active = TRUE ' All object active (alive) by default.
>     NEXT initCPUobj

We loop hrough our objects and randomize their positions, regular and rotation speeds (to get a better effect since all objects won't move and rotate with the same speeds).

The whole phylosophy after this is in altering the artificial smart code and computer controlled objects' drawing code so if would draw and move all the computer controlled objects.

Observe the following code:

>     FOR countCPUobj AS INTEGER = 1 TO numofCPUobjects ' Loop through all the objects

>         ' If ASMode is true apply the artificial smart code on the current CPU controlled object
>         IF ASMode = TRUE THEN

>             ' The following lines calculate the angle of direction
>             ' from the current controlled object toward the main object.
>             resultanglerad = ATAN2((-1)*(mainy-CPUobject(countCPUobj).Y),(mainx-CPUobject(countCPUobj).X))
>             resultanglerad = PI/2 - resultanglerad
>             IF resultanglerad < 0 THEN resultanglerad = resultanglerad + 2*PI

>             ' The following line converts the result angle between the
>             ' two objects from radians to degrees.
>             resultangledeg = (resultanglerad*180)/PI

>             ' If the CPU controled object's angle is larger than the
>             ' result angle (angle between the CPU object and player's object)...
>             IF CPUobject(countCPUobj).AngleDeg > resultangledeg THEN
>                 ' If the difference between the current angle of the
>                 ' CPU controlled object and the result angle going
>                 ' counter-clockwise is less than this difference
>                 ' clockwise, rotate the CPU object counter-clockwise
>                 IF (360-CPUobject(countCPUobj).AngleDeg+resultangledeg) >= (CPUobject(countCPUobj).AngleDeg-resultangledeg) THEN CPUobject(countCPUobj).AngleDeg = CPUobject(countCPUobj).AngleDeg - CPUobject(countCPUobj).RotationSpeed
>                 ' If the difference between the current angle of the
>                 ' CPU controlled object and the result angle going
>                 ' clockwise is less that this difference counter-clockwise,
>                 ' rotate the CPU object clockwise
>                 IF (360-CPUobject(countCPUobj).AngleDeg+resultangledeg) < (CPUobject(countCPUobj).AngleDeg-resultangledeg) THEN CPUobject(countCPUobj).AngleDeg = CPUobject(countCPUobj).AngleDeg + CPUobject(countCPUobj).RotationSpeed
>             END IF
>             ' Same as above but for situation when CPU object's angle is
>             ' less that the result angle.
>             IF CPUobject(countCPUobj).AngleDeg < resultangledeg THEN
>                 IF (360-resultangledeg+CPUobject(countCPUobj).AngleDeg) >= (resultangledeg-CPUobject(countCPUobj).AngleDeg) THEN CPUobject(countCPUobj).AngleDeg = CPUobject(countCPUobj).AngleDeg + CPUobject(countCPUobj).RotationSpeed
>                 IF (360-resultangledeg+CPUobject(countCPUobj).AngleDeg) < (resultangledeg-CPUobject(countCPUobj).AngleDeg) THEN CPUobject(countCPUobj).AngleDeg = CPUobject(countCPUobj).AngleDeg - CPUobject(countCPUobj).RotationSpeed
>             END IF
>             ' The following lines keep the current CPU object's angle within
>             ' 0 to 360 degrees area.
>             IF CPUobject(countCPUobj).AngleDeg<0 THEN CPUobject(countCPUobj).AngleDeg=CPUobject(countCPUobj).AngleDeg+360
>             IF CPUobject(countCPUobj).AngleDeg>359 THEN CPUobject(countCPUobj).AngleDeg=CPUobject(countCPUobj).AngleDeg-360
>             ' Convert the CPU object's angle from degrees to radians.
>             CPUobject(countCPUobj).AngleRad = (CPUobject(countCPUobj).AngleDeg*PI)/180
>             ' Move the current CPU object according to angle and speed
>             ' (note how SIN and COS functions are used).
>             ' Since positive y axis goes down in FB, we need to reduce
>             ' the y position of the object by the COS function and not add
>             ' it as in normal Cartesian coordinate system.
>             CPUobject(countCPUobj).X = CPUobject(countCPUobj).X + sin(CPUobject(countCPUobj).AngleRad)*CPUobject(countCPUobj).Speed
>             CPUobject(countCPUobj).Y = CPUobject(countCPUobj).Y - cos(CPUobject(countCPUobj).AngleRad)*CPUobject(countCPUobj).Speed

>         END IF

>         ' We draw the current computer controlled object if active (alive).
>         IF CPUobject(countCPUobj).Active = TRUE THEN
>             LINE (CPUobject(countCPUobj).X, CPUobject(countCPUobj).Y)-(CPUobject(countCPUobj).X+sin(CPUobject(countCPUobj).AngleRad)*20,CPUobject(countCPUobj).Y-cos(CPUobject(countCPUobj).AngleRad)*20), RGB(2,117, 250)
>             CIRCLE (CPUobject(countCPUobj).X, CPUobject(countCPUobj).Y), 3, RGB(2,117, 250)
>         END IF

>     NEXT countCPUobj

The code is identical to that where a single computer controlled object is present, only now, like with variable initiation, we loop through all the computer controlled objects, check their angle with the main object, and rotate them accordingly. Drawing of the computer controlled objects also had to be placed in the FOR loop.

The entire code so far: codever3.txt

Few things are left to be done. First, let's implement projectiles. This was covered in my other tutorial (QB Expresse #20), but no harm in repeating some of the stuff.

For contolling projectiles we'll use ObjType custom defined type.

Projectiles will be declared with:

>     DIM SHARED Projectile(numofprojectiles) AS ObjType

Where numofprojectiles will be declared earlier as a constant or a variable, representing the maximum number of projectile that can be active simultaneously. I've set it to 50.

We should now construct a subroutine that will initiate a new projectile. I named it InitiateProjectile and constructed it like this:

>     SUB InitiateProjectile (px AS SINGLE, py AS SINGLE, pangle AS SINGLE, ptyp AS INTEGER)

>     ' We loop through our projectiles looking for a free one (never used
>     ' before or expired -> ActiveTime = 0).
>     FOR initproj AS INTEGER = 1 TO numofprojectiles

>         ' When a free projectile is found we set its position, type,
>         ' life time (ActiveTime = 30 -> 30 loops) and angle. After this
>         ' the subroutine is exited so that a next free projectile wouldn't
>         ' be initiated too.
>         IF Projectile(initproj).ActiveTime = 0 THEN
>             Projectile(initproj).X = px
>             Projectile(initproj).Y = py
>             Projectile(initproj).AngleRad = pangle
>             Projectile(initproj).Typ = ptyp
>             ' According to given projectile's type we can
>             ' set other projectile's characteristics.
>             ' In this program we'll only set projectile's
>             ' speed according to its type.

>             ' Projectile type 1 will and should only be used
>             ' with the main player since we added a condition
>             ' that the projectile's speed will be increased
>             ' by the main object's speed if it is moving.
>             IF Projectile(initproj).Typ = 1 THEN
>                 Projectile(initproj).Speed = 5
>                 IF MULTIKEY(SC_UP) THEN Projectile(initproj).Speed =  Projectile(initproj).Speed + mainspeed
>             END IF
>             Projectile(initproj).ActiveTime = 80
>             EXIT SUB
>         END IF

>     NEXT initproj

>     END SUB

If you read my previous tutorials this should be clear. An projectile is initiated with:

>     InitiateProjectile start_x_position, start_y_position, projectiles_angle, projectile_type

...with the sections shown filled properly.

Inside the sub program goes through the projectiles, looks for an inactive one (ActiveTime = 0). When a "free" projectle is found, its values are set according to values inputted when the sub is called. After this the sub is exited so that another free projectile wouldn't be initiated in the same manner.

This sub should be declared after constants declarations with:

>     DECLARE SUB InitiateProjectile (px AS SINGLE, py AS SINGLE, pangle AS SINGLE, ptyp AS INTEGER)

The second sub we need is the one that moves and draws activated projectiles. Just observe the following code:

>     SUB DrawProjectiles ()

>     FOR countproj AS INTEGER = 1 TO numofprojectiles

>         ' We loop through our projectiles and if an active one is
>         ' found, we move and draw it.
>         IF Projectile(countproj).ActiveTime > 0 THEN

>             ' The next line is used to expire the projectile so it wouldn't
>             ' be active infinitely. We can do this on different ways, like
>             ' by deactivating an object once it passes the edge of screen.
>             ' Still, this is a very handy way of setting the "life time" of an object.
>             Projectile(countproj).ActiveTime = Projectile(countproj).ActiveTime - 1

>             ' Projectiles are moved just like the main and computer controlled
>             ' objects.
>             Projectile(countproj).X = Projectile(countproj).X + sin(Projectile(countproj).AngleRad)*Projectile(countproj).Speed
>             Projectile(countproj).Y = Projectile(countproj).Y - cos(Projectile(countproj).AngleRad)*Projectile(countproj).Speed

>             ' According to projectile type, we draw it.
>             IF Projectile(countproj).Typ = 1 THEN
>                 LINE (Projectile(countproj).X, Projectile(countproj).Y)-(Projectile(countproj).X+sin(Projectile(countproj).Anglerad)*3,Projectile(countproj).Y-cos(Projectile(countproj).AngleRad)*3), RGB(192, 192, 0)
>             END IF

>         END IF

>     NEXT countproj

>     END SUB

The comments explain most of it. Only active objects are managed (ActiveTime > 0), the movement is done like with the main and computer controlled objects, and drawing is done according to projectile's type. Since we only have one type of projectiles in our example program, the use of that IF clause doesn't seem much logical, but will most likely be in a proper game where more kinds of projectiles (weapons) are used.

Above the last "END IF", inside the countproj FOR loop, we should add this code:

>     FOR colcheckobj AS INTEGER = 1 TO numofCPUobjects

>           ' If the current projectiles is less that 4 pixels horizontally
>           ' and vertically to an computer controlled object, diactivate
>           ' that object and the projectile.
>           IF (CPUObject(colcheckobj).Active = TRUE AND ABS(CPUObject(colcheckobj).X-Projectile(countproj).X) < 5 AND ABS(CPUObject(colcheckobj).Y-Projectile(countproj).Y) < 5) THEN
>                 ' Initiate some explosions (once you implement an explosion layer)
>                 ' Add score to player
>                 ' Etc.
>                 CPUObject(colcheckobj).Active = FALSE
>                 Projectile(countproj).ActiveTime = 0
>           END IF

>     NEXT colcheckobj

This FOR loop goes through all the active (alive) computer controlled objects, checks for pixel distances between that object and the current projectile, and if the distance is less than 5 pixels horizontally and vertically, the computer controlled objects is deactivated (killed), as well as the projectile (since it exploded, right?).

This sub should be declared after the constants with:

>     DECLARE SUB DrawProjectiles ()

For the "object killing" to work, we need to place computer controlled objects drawing inside and IF clause that will draw them only if they are active. Like this (alter the countCPUobj FOR loop):

>     IF CPUobject(countCPUobj).Active = TRUE THEN
>             LINE (CPUobject(countCPUobj).X, CPUobject(countCPUobj).Y)-(CPUobject(countCPUobj).X+sin(CPUobject(countCPUobj).AngleRad)*20,CPUobject(countCPUobj).Y-cos(CPUobject(countCPUobj).AngleRad)*20), RGB(2,117, 250)
>             CIRCLE (CPUobject(countCPUobj).X, CPUobject(countCPUobj).Y), 3, RGB(2,117, 250)
>     END IF

The last thing that needs to be done is projectile initiation. A projectile will be initiated when the user presses SPACE. I recommend some other key for you, since SPACE doesn't work well with arrow keys (it's not always registered). I've been told this can't be fixed.

Put this code after the other main object's movement control code:

>     IF MULTIKEY(SC_SPACE) AND main_reload = 0 THEN
>     main_reload = 10
>     ' The next line initiates a new projectile from the
>     ' top of the main object's direction line, with the
>     ' current main object angle, and using projectile
>     ' type 1.
>     InitiateProjectile mainx+sin(anglerad)*20,mainy-cos(anglerad)*20, anglerad, 1
>     END IF
>     IF main_reload > 0 THEN main_reload = main_reload - 1

Observe how a new projectile is initiated from the top of the main object's direction line (the SIN and COS stuff), and with the main object's current angle (anglerad). What is the purpose of the main_reload variable? It's used to "time" the max ratio of auto-fire. When the user holds the fire key, a new projectile will only be fired every 10 loops since with every projectile initiation main_reload is set to 10, and a new projectile can be fired only when main_reload is 0. We need to add a line that reduces main_reload in every loop by 1, as I did it in the code above. Be sure to declare main_reload as a SHARED INTEGER variable, like it's done with other variables.

The entire code so far: codever4.txt

Compile it and enjoy.

Screenshot of the result:

And that's it for this tutorial. I hope you learned something useful and can expand on this tutorial with your own ideas and tricks.

The example programs with single and multiple computer controlled object's in source and compiled can be found here: dealing_with_angles_examples.zip

Mini-game from the "How To Program A Game With FreeBASIC - Lesson #2" tutorial compiled and with source for FreeBASIC ver.0.17: mini_game_FB017.zip

For the next tutorial I plan to add this:

- scrolling
- more "smarter" artificial smarts
- homing projectile

I'm excited. Stay tuned!

A tutorial written by Lachie D. (lachie13@yahoo.com ; The Maker Of Stuff)

Download this tutorial: Dealing_with_angles_tut.zip or dealing_with_angles_2D.html

## A Space Trading Sim is born
3D Game programming for beginners

### Episode 1: The real world and your flat screen

### Written by Kevin R. (August, 2007)

### Introduction

Welcome everyone to the first tutorials in this series which is aimed at beginners to 3D programming and maybe to game programming. I know many people (at least 90%) will skip this introduction so I will keep it short. All the code in this text is written for FreeBasic. I will not use any external package or library such as DirectX or OpenGL... That was shocking... No DirectX or OpenGL, so how we gonna produce some 3d graphics??? Simple... with our own FBgfx.

Why would I ignore DirectX or OpenGL and don't use them??? Every game produced nowadays uses them. I can program applications using OpenGL and DirectX, so that's the problem neither. The answer is simple and summarised with one word: understanding.

Understanding the concepts which are working within 3D engines is very important and can make your life much easier. When you write applications with OpenGL and use matrix equation, rotation and shading commands, these commands are black boxes. You know they produce nice graphics, but how they do it is unknown. The big problems arrive when you get a nasty runtime error somewhere in the rendering of your flashy games. Good understanding is important. Besides that, it’s fun to look if your routine is faster than DirectX or OpenGL.

### What we are going to do?

In this series I’m planning to do a space trading sim like Elite or some more up-to-date Freelancer. This type of game can easily be separated in little pieces for a tutorial in order that at the end of the series we got an entire game ready and running.

This episode I will explain the conversion from 3D to a 2D screen, the base of an 3d engine. Not very difficult and for some maybe way to easy, but look at the top of the text:
3D game programming for BEGINNERS, so I will start at lowest level possible. Later on in the series I will get to more difficult problems.

Alright, what do we need in random order:

- A brain (Preferably your own)
- Freebasic (I’m running version 0.17b)
- IDE (like FBIde or FBEdit)

### 2D and 3D worlds

Alright, what is the difference between 2D and 3D worlds? Or in gamer language, what is the difference between Super Mario and Quake? The answer is again simple: a coordinate. In a 3D world we define a point by 3 coordinates respectively (x, y, z). In 2D we use 2 coordinates (x, y). So we've to find a way to convert a 3D coordinate to a 2D coordinate.

Before we can do that, we have to define the 3D coordinate system. I'm using a right handed system:
z-axis into the screen, y up the screen and x positive to the right. This is called a right handed system because when you point your thumb of your right hand to the screen, your four fingers point to the direction of positive rotation. This sounds like hocus-pocus right know, I know... But I will explain it later in a tutorial on rotations. For now just assume z into the screen, y upwards and x to the right, OK?

Well, that's done. Now take a good look at the screen in front of you. It is flat. How we are going to simulate a 3D effect on this screen? The screen has only x and y coordinates, so we need a conversion which is depending on the z-coordinate. Could it be so easy...? Well actually it is... Look at it this way: a point is defined as (x,y,z). We can multiply this point by a constant.

>     constant * (x, y, z)

What value must this constant have in order to make the formula depending on z but staying in a 2D plane? Again simple:

>     constant = 1 / z because (1 / z) * (x, y, z) = (x / z, y / z, 1)

As you see x and y are variable but the z coordinate always equals 1 thus for all z only x and y can vary, but the plane remains the same.

So we are working currently at the plane z = 1. If we want to change this plane, to make it further away or closer by we simply change the constant a little:

>     constant = b / z

Where b can be every value. With b you can adjust the perspective. Usually values like 128, 256 or 512 are used for b, but I encourage you to experiment with this value to achieve a desired effect or view. From here on b is called the scale.

The transformation can now be done with:

>     x2d = x3d * Scale / z3d + (screen width / 2)
>     y2d = y3d * Scale / z3d + (screen height / 2)

First one note to the transformation formulas. The first runtime error which can occur and is sometimes hard to find is the division by zero: If z3d equals zero we get and error so be careful. Second note is the screen height and screen width used in the formulas. It's common in 3D engines to use the middle point of the screen as the origin. So if you are using a 640 x 480 resolution the origin lies at (320, 240) which is (0,0,z) in 3D.

I know that the information above looks simple at a first glance, but I bet (and I was one too) that you have to think hard to understand the concept. Therefore I present to you all: Some source code!!!

>     ' Screen dimensions of mode 13
>     Const SCRN_X = 320
>     Const SCRN_Y = 200

>     Type Point3D
>     	x As Integer
>     	y As Integer
>     	z As Integer
>     End Type

>     Type Point2D
>     	x As Integer
>     	y As Integer
>     End Type

>     Dim p3d As Point3D	' 3D point
>     Dim p2d As Point2D	' 2D point
>     Dim Scale As Integer	' Perspective variable

>     ' Define perspective
>     Scale = 256
>     ' Define starting coordinates
>     p3d.x = 1
>     p3d.y = 1
>     p3d.z = 256

>     ' For sake of simplicity i use screen 13 (320 x 200)
>     ' Change for other resolution. (don't forget the constants)
>     Screen 13

>     Do While (p2d.x < SCRN_X) and (p2d.x > -1) and (p2d.y < SCRN_Y) and (p2d.y > -1)

>     	' Convert 3d to 2d
>     	p2d.x = p3d.x * Scale / p3d.z + (SCRN_X / 2)
>     	p2d.y = p3d.y * Scale / p3d.z + (SCRN_Y / 2)
>     	' Put pixel
>     	PSet (p2d.x, p2d.y), 40
>     	' reduce z and check if z > 0 (no division by zero)
>     	p3d.z = p3d.z - 1
>     	If p3d.z = 0 Then p3d.z = -1

>     Loop

>     Do
>     Loop Until InKey <> ""

Well I think the code above speaks for itself. First two types are created for 2d points and 3d points. Then the perspective is determined as 256. I like this value, but feel free to choose your own. The loop contains only a z decrement and a conversion from 3D to 2D. This code is the base of the 3D starfield which I present to you below. This source is not optimised and if you like you can do that by yourself (a good exercise). Like I said before, this tutorial is for beginners and optimising is a subject that I may treat in one of the last tutorials.

>     ' =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
>     '				3D Starfield
>     '
>     '				The Flipside
>     '
>     ' =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

>     Declare Sub GenerateStar(index As Integer)

>     Const MAX_STARS = 500
>     Const SCRN_WIDTH = 320
>     Const SCRN_HEIGHT = 200
>     Const SPEED = 0.015      ' Adjust for different speed

>     Type Point3D
>     	x As Integer
>     	y As Integer
>     	z As Integer
>     End Type

>     Type Point2D
>     	x As Integer
>     	y As Integer
>     End Type

>     Dim shared Star(0 To (MAX_STARS - 1)) As Point3D
>     Dim p2d(0 To (MAX_STARS - 1)) As Point2D

>     Dim i As Integer
>     Dim Scale As Integer
>     Dim OldTime As double
>     Dim TimeDummy As double

>     ' Initialize starfield and variables
>     Randomize Timer					' Random seed
>     For i = 0 To (MAX_STARS - 1)
>     	GenerateStar(i)				' Create stars!!!
>     Next

>     Scale = 256					    ' Perspective

>     ' =-=-=-=-=-= Main Loop =-=-=-=-=-=
>     Screen 13 ' Screen mode 13, change if you like but don't forget the constants!

>     Do While Inkey <> Chr(27) ' Loop until esc is pressed

>     	For i = 0 To (MAX_STARS - 1)
>     		' Delete old star
>     		PSet (p2d(i).x, p2d(i).y), 0 ' draw a black pixel on old position

>     		' Transformation from 3D to 2D
>     		' Origin lies in the middle of the screen
>     		p2d(i).x = star(i).x * Scale / star(i).z + (SCRN_WIDTH / 2)
>     		p2d(i).y = star(i).y * Scale / star(i).z + (SCRN_HEIGHT / 2)

>     		' Draw star
>     		PSet (p2d(i).x, p2d(i).y), 15

>     		' reduce z and check if z > 0 (no division by zero)
>     		star(i).z = star(i).z - 1

>     		If Star(i).z <= 0 Then ' star has passed us, lets create a new one
>     			GenerateStar(i)
>     		EndIf

>     	Next

>     	' Some timing code to reduce the speed of the starfield
>     	' Waiting time = SPEED seconds
>     	OldTime = Timer
>     	Do
>     		TimeDummy = Timer
>             SLEEP 1
>     		' Does nothing but wait. (Old QB way of timing im always using)
>     		' I know there are better ways, but this works
>     	Loop Until (TimeDummy - OldTime) >= SPEED

>     Loop

>     Sub GenerateStar(index As Integer)

>     	' The origin of the screen lies at the middle of the screen.
>     	' So in case of screen 13 the possible screen coordinates are:
>     	' -160 to 160, -100 to 100
>     	' This procedure divides the stars array in four and assign every
>     	' screen quadrant with it. Sounds complicated?
>     	' Look at it this way:
>     	' The first IF puts one fourth of MAX_STARS in the box constaining
>     	' the coordinates (0, 0), (100, 160)
>     	' Then the ElseIf puts stars in the box containing the coordinates
>     	' (0, 0), (-160, 100)
>     	' And so on! Get it already???
>     	' If not change the signs before the (SCRN_WIDTH / 2) or (SCRN_HEIGTH / 2)
>     	' and see what happens!

>     	If index < (MAX_STARS / 4) THEN
>     		Star(index).x = Rnd(1) * (SCRN_WIDTH / 2) + 1
>     		Star(index).y = Rnd(1) * (SCRN_HEIGHT / 2) + 1
>     	ElseIf (index > (MAX_STARS / 4)) And (index < (MAX_STARS / 2)) Then
>     		Star(index).x = Rnd(1) * -(SCRN_WIDTH / 2) + 1
>     		Star(index).y = Rnd(1) * (SCRN_HEIGHT / 2) + 1
>     	ElseIf (index > (MAX_STARS / 2)) And (index < (3 * MAX_STARS / 4)) Then
>     		Star(index).x = Rnd(1) * (SCRN_WIDTH / 2) + 1
>     		Star(index).y = Rnd(1) * -(SCRN_HEIGHT / 2) + 1
>     	ElseIf (index > (3 * MAX_STARS / 4)) And (index < MAX_STARS) Then
>     		Star(index).x = Rnd(1) * -(SCRN_WIDTH / 2) + 1
>     		Star(index).y = Rnd(1) * -(SCRN_HEIGHT / 2) + 1
>     	EndIf
>     	Star(index).z = Rnd(1) * 256

>     End Sub

### Closing down

Hope you liked this tutorial. I did while writing. I know that not much of true game code and game concepts are explained in this tutorial. That's something I leave to later tutorials. First I’m gonna explain some basic techniques and after that the game elements are introduced. However, don't be disappointed. This will be soon enough.

Some tips for experimenting:

- Use color differences to create spacy effects.
- Change the code in order to create rain or snow (the y coordinate is decreased) and use a nice blue color. You could even change the colorcode of the drops which are further away (z-coordinate) to a darker color. This looks very cool!
- Use a circle command instead of pset and calculate the radius with the distance to the viewer (or in easier language, from the z coordinate). You get some planets you fly by!
- Next tutorial we are going to do something more interesting, namely rotations of points. Very interesting but a little heavy in the math. I'll introduce you to the biggest friends of a 3D coder; mr. Vector and mrs. Matrix.

Have fun and see you next time,

The Flipside

OnlyOneCanKnowMe@gmail.com

Proofreading and minor changes by Lachie Dazdarian - lachie13@yahoo.com

Download the source code: 3D_Tut_1.bas
Download this tutorial: 3D_Tut_1.html or Kevin_R_3DTutorial_Part1.zip

## The Difference Between MOD and REMAINDER

### Written by Moneo

#### The QB manual says:

> "Modulo arithmetic is denoted by the modulus operator MOD. Modulo arithmetic provides the REMAINDER, rather than the quotient, of an integer division."

The confusion is, that in the QB manual, the MOD operator is called "MOD", but actualy provides the REMAINDER. This gives some programmers the false impression that the MOD operator always provides a mathematical MOD.

#### What is a mathematical MOD?

A mathematical MOD function is defined as the amount by which a number exceeds the largest integer multiple of the divisor that is not greater than that number.

#### When do I need to use a mathematical MOD?

My experience has been when trying to implement other people's algorithms into QB code. You suddenly encounter a MOD function in the algorithm and don't know whether to use a QB MOD or not. If you use the QB MOD and have problems, then most probably a mathematical MOD was required.

For positive numbers, or numbers with the same sign, if you the programmer are thinking mathematical MOD or thinking REMAINDER, you'll get the desired result by using the QB MOD operator.

However, if only one of the numbers is negative, the QB MOD operator continues to provide a REMAINDER, as stated in the manual, and NOT a mathematical MOD as we were led to believe when we used numbers having the same sign. This is the crux of the problem, that is, if one of the numbers is negative, the result of a QB MOD will not equal the result of a mathematical MOD.

#### SUMMARY:

1) If you want a MOD, and have numbers with the same sign, use the QB MOD operator, or the MathMOD algorithm.

2) If you want a REMAINDER, regardless of signs of the numbers, use the QB MOD operator only.

3) If you want a mathematical MOD, and have only one negative number, use the MathMOD algorithm only.

#### MathMOD algorithm for computing a mathematical MOD:

> Replace (a MOD b) with:
> a-b*INT(a/b)

>     EXAMPLES:
>      7, 3 ... QB MOD = 1 ... MathMOD = 1
>     -7,-3 ... QB MOD =-1 ... MathMOD =-1
>      7,-3 ... QB MOD = 1 ... MathMOD =-2
>     -7, 3 ... QB MOD =-1 ... MathMOD = 2

-Moneo, June 2007

Download a copy of this tutorial: modtut.txt

## Simple AI with Value Points

### Written by Mentat

This tutorial is meant for beginners and others who want to try new AI ideas (so much for an introduction). This is for QBASIC but works for any language.

Now for the good stuff.

One night (August 8th), I was thinking about chess and suddenly it hit me. Decent AI could be made by counting moves by value points: the better the move the higher the value. Quickly, I jumped to Tic Tac Toe. So I wrote a TI BASIC program for TTT the next day (today). It was just AI taking a value from a matrix and acting on it. Simple and easy (and I’m a beginner myself).

If you don’t understand the code, don’t worry; just continue on to the end. Alright, let me show you what I wrote (Keep in mind, this is TI-83 BASIC. Also, the program finds the best move! It doesn’t actually play.):

#### Pseudo Code, nice and easy:

>     Program TTT
>     :Variable stuff, like dimensioning and clearing matrices
>     :FOR loops, so to go through all of the Tic Tac Toe slots
>     :Fun part, assigning values to a second matrix to keep track of best moves. Cool to watch.
>     :FOR loop ends (sigh, you can’t play forever).
>     :FOR loops, so to read the value matrix and pick the best one.
>     :Pick the best, store it to a variable or list, and maybe some randomness for same values
>     :End FOR loops.
>     :Plot best choice on TTT matrix

#### Full Code:

>     Program TTT          		          ‘	QBASIC Equiv.
>     :Delvar [B]                                        ‘clears out previous data; [B] is the calc’s value matrix
>     :{3,3} --> Dim([B])                              ‘same as DIM B[3,3]
>     :{3,3} --> Dim([A])		           ‘[A] is TTT matrix; dim is just in case
>     :For(R,1,3)				‘FOR R=1 TO 3, R and C are coordinates
>     :For(C,1,3)				‘FOR C=1 TO 3
>     :If [A](R,C)?0				‘Checks for matrix value, prevents AI overwriting used spot
>     :0 --> [B](R,C)                                       ‘will be multiplied by values. Nullifies any value
>     :Else
>     :1 --> [B](R,C)				‘Says the CPU can use the spot
>     :0.1(fpart((R+C)/2)?0)+		‘fpart truncates whole left side of decimal, leaving a fraction.
>         [B](R,C) --> [B](R,C)                        ‘Gives corners and center slightly more priority
>     :End					‘END IF
>     :If [A](R,1)+[A](R,2)+                        ‘Sums the values of a row. –1=computer(O), 1=human(X). –2
>        [A](R,3)=-2                                       ‘ means 2 O’s, which has highest priority. It wins.
>     :[B](R,C)*5 --> [B](R,C)                        ‘Assigns five points if that coordinate is not occupied.
>     :If [A](1,C)+[A](2,C)+		‘Checks that particular column for Os. Same as above.
>        [A](3,C)=-2
>     :[B](R,C)*5 --> [B](R,C)
>                                                                    ‘Now for the hard part: Diagonals. This split is just some info.
>               ‘Ok. R*C=3 at the top right and lower left. This is how to find one diagonal, from a math trick.
>     :IF RC=3 and [A](1,3)+[A](2,2)	‘Checks / diagonal from corner viewpoints
>         +[A](3,1)=-2
>     :[B](R,C)*5 --> [B](R,C)
>              ‘Now, the \ diagonal is trickier, but the R*C value are Squares, which don’t occur anywhere else
>     :If (RC=1 or RC=9) and		‘Messy, but essentially the same as the other value slots
>         [A](1,1)+[A](2,2)+
>         [A](3,3)=-2
>     :[B](R,C)*5 --> [B](R,C)
>     :If RC=4 and ([A](1,1)+	           ‘Now for the diagonals’ center. I treated it separately to avoid bugs.
>         [A](3,3)=-2 Or [A](1,3)
>         +[A](3,1)=-2)
>     :[B](R,C)*5 --> [B](R,C)		‘Finally, I finished calculating values for Os. But what about Xs?
>     :If [A](R,1)+[A](R,2)+		‘Luckily for me, I have copy and paste. With a little editing, it’ll
>         [A](R,3)=2				‘be finished. You could edit QBASIC in Notepad and use copy and
>     :[B](R,C)*2 --> [B](R,C)		‘paste. Just make sure to save to .bas. Look closely, the values are
>     :If [A](	1,C)+[A](2,C)+		‘changed from –2 to 2 and from 5 to 2. I’ll explain later.
>         [A](3,C)=2
>     :[B](R,C)*2 --> [B](R,C)
>     :IF RC=3 and [A](1,3)+[A](2,2)
>         +[A](3,1)=2
>     : [B](R,C)*2 --> [B](R,C)
>     : If (RC=1 or RC=9) and
>         [A](1,1)+[A](2,2)+
>         [A](3,3)=2
>     :[B](R,C)*2 --> [B](R,C)		‘Almost there
>     :If RC=4 and ([A](1,1)+
>         [A](3,3)=-2 Or [A](1,3)
>         +[A](3,1)=-2)
>     :End
>     :End				‘Finally, the values are done. Now to read and use them. Much easier
>     :Delvar L1				‘List one will hold the max values.
>     :For(R,1,3)
>     :For(C,1,3)				‘ “Oh god! Not again!” Don’t worry, you’re almost there.
>     :If [B](R,C)=L1(1)
>     :[B](R,C) -->  L1(dim(L1)+1)     	‘Stores equal values
>     :If [B](R,C) L1(1)			‘Checks for highest value
>     :[B](R,C) -->  L1(1)
>     :End
>     :End
>     : L1(1) --> D 				‘For convenience
>     :RandInt(1,dim(L1)) --> A	          ‘Variable A will be a counter to find the rand pick between positions
>     :For(R,1,3)            			‘that have the same value.
>     :For(C,1,3)
>     :If A=1 and [B](R,C)=D		‘Finds the right position. If true, It exits with the correct R and C
>     :goto 1
>     :If [B](R,C)=D			‘Right value but wrong random pick
>     :A-1 --> A
>     :End
>     :Lbl 1
>     :-2 --> A[R,C]				‘Done

Finally, I did put it on my TI. The reason it isn’t in QB is because I developed it before classes but not at home. And my QB IDE isn’t working.

#### Summary

For those of you who did not read or understand it, here we go:

The basis for this type of AI is assigning a point value to all of its choices. Most of the work is figuring how what gets X many points. In TTT, computer winning moves were assigned the most. Next were player winning moves (Why block when you can win?). Then, corner and center moves had a little bit more than edge moves because the former have more winning opportunities. Now, this looked ahead only to its own move, and not further (It’s a calculator, not a super computer). For chess, you would go through pieces, not spots. So, if you still don’t get it:

>                 Steps..............................Extra help
>     1)	Go though all choices..............probably using for loops
>     2)	Assign values......................Hard, makes AI smart or dumb
>     3)	Find highest values................or lower randomly for easier AI levels
>     4)	Find location of highest value............if same high values, pick randomly
>     5)	Act accordingly....................like plot on best spot

Really good AI adjust values according to the opponent’s style, nature of the immediate game, and the period of the game (End game tactics are different from opening tactics).

A good way to practice AI programming is to have different styles compete against each other. Maybe, there will be a competition for making AI directly compete against other programmers’ AI...

Feel free to copy and use any part of this. I would appreciate it if somebody would use this to make a better tutorial, or for a new game. If you do, please include my name, not to say that this was mine, but I like to know if my contributions aren’t in vain.

Download this tutorial: AI_Tutorial_Value_Points.doc

## How To Write A Chess Program in QBASIC

### Written by Dean Menezes

Since the 1950's, people have been trying to make progrms and machines that play chess. This tutorial tries to teach chess programming by using a commented QuickBASIC program:

>     'This is basically a chess program I wrote
>     'It includes comments to show some of the theory on making a chess program
>     'You can modify it to include things like opening book, mouse/graphics, etc.
>     DEFINT A-Z 'to speed things up
>     ' First, you need an 8 by 8 array for the board:
>     DIM SHARED BOARD(0 TO 7, 0 TO 7)
>     ' Then, you need to store a list of best moves:
>     DIM SHARED BESTA(0 TO 7), BESTB(0 TO 7), BESTX(0 TO 7), BESTY(0 TO 7)
>     ' Since the evaluation subroutine is recursive, you also need to store current level and maximum level
>     DIM SHARED LEVEL, MAXLEVEL, SCORE, CFLAG
>     CFLAG = 0
>     LEVEL = 0
>     MAXLEVEL = 5
>     ' Now we need to fill the starting position into the board array
>     DATA -500,-270,-300,-900,-7500,-300,-270,-500
>     DATA -100,-100,-100,-100, -100,-100,-100,-100
>     DATA 0, 0, 0, 0, 0, 0, 0, 0
>     DATA 0, 0, 0, 0, 0, 0, 0, 0
>     DATA 0, 0, 0, 0, 0, 0, 0, 0
>     DATA 0, 0, 0, 0, 0, 0, 0, 0
>     DATA 100, 100, 100, 100, 100, 100, 100, 100
>     DATA 500, 270, 300, 900, 5000, 300, 270, 500
>     FOR X = 0 TO 7
>      FOR Y = 0 TO 7
>        READ Z
>        BOARD(X, Y) = Z
>      NEXT Y
>     NEXT X
>     A = -1
>     RESULT = 0
>     ' Now we make the main loop:
>     DO
>      SCORE = 0
>      CALL IO(A, B, X, Y, RESULT) ' Get white's move
>      CLS
>      CALL SHOWBD ' Update board to show white's move
>      RESULT = EVALUATE(-1, 10000) 'Get black's move
>      A = BESTA(1) 'start column for black's move
>      B = BESTB(1) 'start row for black's move
>      X = BESTX(1) 'end column for black's move
>      Y = BESTY(1) 'end rom for black's move
>     LOOP
>     ' Now we're going to look at the evaluation function
>     DEFINT A-Z
>     FUNCTION EVALUATE (ID, PRUNE)
>      DIM XX(0 TO 26), YY(0 TO 26)
>      LEVEL = LEVEL + 1 ' Update recursion level
>      BESTSCORE = 10000 * ID
>      FOR B = 7 TO 0 STEP -1 ' Loop through each square
>        FOR A = 7 TO 0 STEP -1
>          IF SGN(BOARD(B, A)) <> ID THEN GOTO 1 ' If the square does not have right color piece go to next square
>          IF (LEVEL = 1) THEN CALL SHOWMAN(A, B, 8) ' This is just to show the move currently trying
>          CALL MOVELIST(A, B, XX(), YY(), NDX) ' Get list of moves for current piece
>          FOR I = 0 TO NDX ' Loop through each possible move
>            X = XX(I)
>            Y = YY(I)
>            IF LEVEL = 1 THEN
>              LOCATE 1, 1
>              PRINT "TRYING: "; CHR$(65 + A); 8 - B; "-"; CHR$(65 + X); 8 - Y ' show the move currently trying
>              CALL SHOWMAN(X, Y, 8)
>            END IF
>            OLDSCORE = SCORE
>            MOVER = BOARD(B, A) ' Store these locations
>            TARGET = BOARD(Y, X) ' so we can set the move back
>            CALL MAKEMOVE(A, B, X, Y) ' Make the move so we can evaluate
>            IF (LEVEL < MAXLEVEL) THEN SCORE = SCORE + EVALUATE(-ID, BESTSCORE - TARGET + ID * (8 - ABS(4 - X) - ABS(4 - Y)))
>            SCORE = SCORE + TARGET - ID * (8 - ABS(4 - X) - ABS(4 - Y)) 'work out score for move
>            IF (ID < 0 AND SCORE > BESTSCORE) OR (ID > 0 AND SCORE < BESTSCORE) THEN 'update current best score
>              BESTA(LEVEL) = A
>              BESTB(LEVEL) = B
>              BESTX(LEVEL) = X
>              BESTY(LEVEL) = Y
>              BESTSCORE = SCORE
>              IF (ID < 0 AND BESTSCORE >= PRUNE) OR (ID > 0 AND BESTSCORE <= PRUNE) THEN 'prune to avoid wasting time
>                BOARD(B, A) = MOVER 'reset position back to before modified
>                BOARD(Y, X) = TARGET
>                SCORE = OLDSCORE
>                IF (LEVEL = 1) THEN CALL SHOWMAN(X, Y, 0) 'Show the move currently trying
>                IF (LEVEL = 1) THEN CALL SHOWMAN(A, B, 0) 'Show the move currently trying
>                LEVEL = LEVEL - 1 'update recursion level
>                EVALUATE = BESTSCORE 'return
>                EXIT FUNCTION
>              END IF
>            END IF
>            BOARD(B, A) = MOVER 'reset position back to before modified
>            BOARD(Y, X) = TARGET
>            SCORE = OLDSCORE
>            IF (LEVEL = 1) THEN CALL SHOWMAN(X, Y, 0) 'show move currently trying
>     1   NEXT
>          IF (LEVEL = 1) THEN CALL SHOWMAN(A, B, 0) show move currently trying
>       NEXT
>        NEXT
>        LEVEL = LEVEL - 1 'update recursion level
>        EVALUATE = BESTSCORE 'return
>      END FUNCTION
>     'This generates a list of moves
>     DEFINT A-Z
>     SUB MOVELIST (A, B, XX(), YY(), NDX)
>      PIECE = INT(ABS(BOARD(B, A))) 'get value corresponding to piece
>      NDX = -1
>      IF PIECE = 100 THEN
>        CALL PAWN(A, B, XX(), YY(), NDX) 'call proper move listing routine depending on piece
>      ELSEIF PIECE = 270 THEN CALL KNIGHT(A, B, XX(), YY(), NDX)
>      ELSEIF PIECE = 300 THEN CALL BISHOP(A, B, XX(), YY(), NDX)
>      ELSEIF PIECE = 500 THEN CALL ROOK(A, B, XX(), YY(), NDX)
>      ELSEIF PIECE = 900 THEN CALL QUEEN(A, B, XX(), YY(), NDX)
>      ELSE CALL KING(A, B, XX(), YY(), NDX)
>      END IF
>     END SUB
>     ' This is the move-list generator for the pawn
>     DEFINT A-Z
>     SUB PAWN (A, B, XX(), YY(), NDX)
>      ID = SGN(BOARD(B, A)) 'get color
>      IF (A - 1) >= 0 AND (A - 1) <= 7 AND (B - ID) >= 0 AND (B - ID) <= 7 THEN 'capture
>        IF SGN(BOARD((B - ID), (A - 1))) = -ID THEN 'make sure there is piece to capture
>          NDX = NDX + 1
>          XX(NDX) = A - 1
>          YY(NDX) = B - ID
>        END IF
>      END IF
>      IF (A + 1) >= 0 AND (A + 1) <= 7 AND (B - ID) >= 0 AND (B - ID) <= 7 THEN  'capture
>        IF SGN(BOARD((B - ID), (A + 1))) = -ID THEN 'make sure there is piece to capture
>          NDX = NDX + 1
>          XX(NDX) = A + 1
>          YY(NDX) = B - ID
>        END IF
>      END IF
>      IF A >= 0 AND A <= 7 AND (B - ID) >= 0 AND (B - ID) <= 7 THEN 'one square forward
>        IF BOARD((B - ID), A) = 0 THEN 'make sure square is empty
>          NDX = NDX + 1
>          XX(NDX) = A
>          YY(NDX) = B - ID
>          IF (ID < 0 AND B = 1) OR (ID > 0 AND B = 6) THEN '2 squares forward
>            IF BOARD((B - ID - ID), A) = 0 THEN  'make sure square is empty
>              NDX = NDX + 1
>              XX(NDX) = A
>              YY(NDX) = B - 2 * ID
>            END IF
>          END IF
>        END IF
>      END IF
>     END SUB
>     ' Move list generator for knight
>     DEFINT A-Z
>     SUB KNIGHT (A, B, XX(), YY(), NDX)
>      ID = SGN(BOARD(B, A)) 'get color
>      X = A - 1 'work out each of the knight's eight moves
>      Y = B - 2
>      GOSUB 5
>      X = A - 2
>      Y = B - 1
>      GOSUB 5
>      X = A + 1
>      Y = B - 2
>      GOSUB 5
>      X = A + 2
>      Y = B - 1
>      GOSUB 5
>      X = A - 1
>      Y = B + 2
>      GOSUB 5
>      X = A - 2
>      Y = B + 1
>      GOSUB 5
>      X = A + 1
>      Y = B + 2
>      GOSUB 5
>      X = A + 2
>      Y = B + 1
>      GOSUB 5
>      EXIT SUB
>     5 IF X < 0 OR X > 7 OR Y < 0 OR Y > 7 THEN RETURN 'make sure on board
>      IF ID <> SGN(BOARD(Y, X)) THEN NDX = NDX + 1: XX(NDX) = X: YY(NDX) = Y 'make sure no piece of same color
>      RETURN
>     END SUB
>     'Gen. for bishop
>     DEFINT A-Z
>     SUB BISHOP (A, B, XX(), YY(), NDX)
>      ID = SGN(BOARD(B, A))
>      FOR DXY = 1 TO 7 'work out diagonally one direction
>        X = A - DXY
>        Y = B + DXY
>        IF X < 0 OR X > 7 OR Y < 0 OR Y > 7 THEN EXIT FOR 'stop when go off board
>        GOSUB 3
>        IF BOARD(Y, X) THEN EXIT FOR 'stop when hit piece
>      NEXT
>      FOR DXY = 1 TO 7
>        X = A + DXY
>        Y = B + DXY
>        IF X < 0 OR X > 7 OR Y < 0 OR Y > 7 THEN EXIT FOR
>        GOSUB 3
>        IF BOARD(Y, X) THEN EXIT FOR
>      NEXT
>      FOR DXY = 1 TO 7
>        X = A - DXY
>        Y = B - DXY
>        IF X < 0 OR X > 7 OR Y < 0 OR Y > 7 THEN EXIT FOR
>        GOSUB 3
>        IF BOARD(Y, X) THEN EXIT FOR
>      NEXT
>      FOR DXY = 1 TO 7
>        X = A + DXY
>        Y = B - DXY
>        IF X < 0 OR X > 7 OR Y < 0 OR Y > 7 THEN EXIT FOR
>        GOSUB 3
>        IF BOARD(Y, X) THEN EXIT FOR
>      NEXT
>      EXIT SUB
>     3 REM
>      IF ID <> SGN(BOARD(Y, X)) THEN 'make sure no piece of same color
>        NDX = NDX + 1
>        XX(NDX) = X
>        YY(NDX) = Y
>      END IF
>      RETURN
>     END SUB
>     'Gen for rook
>     DEFINT A-Z
>     SUB ROOK (A, B, XX(), YY(), NDX)
>      ID = SGN(BOARD(B, A))
>      FOR X = A - 1 TO 0 STEP -1 'work out vert/horiz each dir.
>        IF ID <> SGN(BOARD(B, X)) THEN 'make sure no piece of same color
>          NDX = NDX + 1
>          XX(NDX) = X
>          YY(NDX) = B
>        END IF
>        IF (BOARD(B, X)) THEN EXIT FOR
>      NEXT
>      FOR X = A + 1 TO 7 STEP 1
>        IF ID <> SGN(BOARD(B, X)) THEN
>          NDX = NDX + 1
>          XX(NDX) = X
>          YY(NDX) = B
>        END IF
>        IF (BOARD(B, X)) THEN EXIT FOR
>      NEXT
>      FOR Y = B - 1 TO 0 STEP -1
>        IF ID <> SGN(BOARD(Y, A)) THEN
>          NDX = NDX + 1
>          XX(NDX) = A
>          YY(NDX) = Y
>        END IF
>        IF (BOARD(Y, A)) THEN EXIT FOR
>      NEXT
>      FOR Y = B + 1 TO 7 STEP 1
>        IF ID <> SGN(BOARD(Y, A)) THEN
>          NDX = NDX + 1
>          XX(NDX) = A
>          YY(NDX) = Y
>        END IF
>        IF (BOARD(Y, A)) THEN EXIT FOR
>      NEXT
>     END SUB
>     'gen for queen
>     DEFINT A-Z
>     SUB QUEEN (A, B, XX(), YY(), NDX)
>      CALL BISHOP(A, B, XX(), YY(), NDX) 'queen's move = bishop + rook
>      CALL ROOK(A, B, XX(), YY(), NDX)
>     END SUB
>     'gen for king
>     DEFINT A-Z
>     SUB KING (A, B, XX(), YY(), NDX)
>      ID = SGN(BOARD(B, A))
>      FOR DY = -1 TO 1 'go through each of 8 king moves, checking for same color and off board
>        IF B + DY < 0 OR B + DY > 7 THEN GOTO 12
>        FOR DX = -1 TO 1
>          IF A + DX < 0 OR A + DX > 7 THEN GOTO 11
>          IF ID <> SGN(BOARD(B + DY, A + DX)) THEN
>            NDX = NDX + 1
>            XX(NDX) = A + DX
>            YY(NDX) = B + DY
>          END IF
>     11 NEXT
>     12 NEXT
>     END SUB
>     FUNCTION INCHECK (X)
>      DIM XX(27), YY(27), NDX
>      FOR B = 0 TO 7
>        FOR A = 0 TO 7
>          IF BOARD(B, A) >= 0 THEN GOTO 6
>          CALL MOVELIST(A, B, XX(), YY(), NDX)
>          FOR I = 0 TO NDX STEP 1
>            X = XX(I)
>            Y = YY(I)
>            IF BOARD(Y, X) = 5000 THEN
>              PRINT "YOU ARE IN CHECK!"
>              PRINT " "
>              PRINT " "
>              INCHECK = 1
>              EXIT FUNCTION
>            END IF
>          NEXT
>     6 NEXT
>        NEXT
>        INCHECK = 0
>     END FUNCTION
>     'Routine to make a move on the chessboard
>     DEFINT A-Z
>     SUB MAKEMOVE (A, B, X, Y)
>      BOARD(Y, X) = BOARD(B, A) 'the piece moves to the target square
>      BOARD(B, A) = 0 'the old square is now empty
>      IF Y = 0 AND BOARD(Y, X) = 100 THEN BOARD(Y, X) = 900 'simple pawn promotion routine
>      IF Y = 7 AND BOARD(Y, X) = -100 THEN BOARD(Y, X) = -900
>     END SUB
>     'Routine to get player move
>     DEFINT A-Z
>     SUB IO (A, B, X, Y, RESULT)
>      DIM XX(0 TO 26), YY(0 TO 26)
>      CLS
>      IF A >= 0 THEN
>        IF RESULT < -2500 THEN
>          PRINT "I RESIGN"
>          SLEEP
>          SYSTEM
>        END IF
>        PIECE = BOARD(Y, X)
>        CALL MAKEMOVE(A, B, X, Y)
>        PRINT "MY MOVE: "; CHR$(65 + A); 8 - B; "-"; CHR$(65 + X); 8 - Y 'show computer move
>        IF PIECE THEN
>          PRINT "I TOOK YOUR ";
>          IF PIECE = 100 THEN PRINT "PAWN"
>          IF PIECE = 270 THEN PRINT "KNIGHT"
>          IF PIECE = 300 THEN PRINT "BISHOP"
>          IF PIECE = 500 THEN PRINT "ROOK"
>          IF PIECE = 900 THEN PRINT "QUEEN"
>          IF PIECE = 5000 THEN PRINT "KING"
>        END IF
>        NULL = INCHECK(0)
>      END IF
>      DO
>        CALL SHOWBD
>        LOCATE 24, 1
>        INPUT "YOUR MOVE (ex: E2-E4): ", IN$
>        IF UCASE$(IN$) = "QUIT" THEN CLS : END
>        IF UCASE$(IN$) = "O-O" OR IN$ = "0-0" THEN
>          IF CFLAG THEN GOTO 16
>          IF BOARD(7, 7) <> 500 THEN GOTO 16
>          IF BOARD(7, 6) OR BOARD(7, 5) THEN GOTO 16
>          BOARD(7, 6) = 5000
>          BOARD(7, 4) = 0
>          BOARD(7, 5) = 500
>          BOARD(7, 7) = 0
>          CFLAG = 1
>          EXIT SUB
>        END IF
>        IF UCASE$(IN$) = "O-O-O" OR IN$ = "0-0-0" THEN
>          IF CFLAG THEN GOTO 16
>          IF BOARD(7, 0) <> 500 THEN GOTO 16
>          IF BOARD(7, 1) OR BOARD(7, 2) OR BOARD(7, 3) THEN GOTO 16
>          BOARD(7, 2) = 5000
>          BOARD(7, 4) = 0
>          BOARD(7, 3) = 500
>          BOARD(7, 0) = 0
>          CFLAG = 1
>          EXIT SUB
>        END IF
>        IF LEN(IN$) < 5 THEN GOTO 16
>        B = 8 - (ASC(MID$(IN$, 2, 1)) - 48)
>        A = ASC(UCASE$(MID$(IN$, 1, 1))) - 65
>        X = ASC(UCASE$(MID$(IN$, 4, 1))) - 65
>        Y = 8 - (ASC(MID$(IN$, 5, 1)) - 48)
>        IF B > 7 OR B < 0 OR A > 7 OR A < 0 OR X > 7 OR X < 0 OR Y > 7 OR Y < 0 THEN GOTO 16
>        IF BOARD(B, A) <= 0 THEN GOTO 16
>        CALL MOVELIST(A, B, XX(), YY(), NDX)
>        FOR K = 0 TO NDX STEP 1 'validate move
>          IF X = XX(K) AND Y = YY(K) THEN
>            MOVER = BOARD(B, A)
>            TARGET = BOARD(Y, X)
>            CALL MAKEMOVE(A, B, X, Y)
>            LOCATE 1, 1
>            IF INCHECK(0) = 0 THEN EXIT SUB 'make sure move out of check
>            BOARD(B, A) = MOVER  'if not move out of check reset board
>            BOARD(Y, X) = TARGET
>            GOTO 16
>          END IF
>        NEXT
>     16 CLS
>      LOOP
>     END SUB
>     'Show board
>     DEFINT A-Z
>     SUB SHOWBD
>      LOCATE 3, 30
>      COLOR 7, 0
>      PRINT "A  B  C  D  E  F  G  H"
>      FOR K = 0 TO 25
>        LOCATE 4, 28 + K
>        COLOR 6, 0
>        PRINT CHR$(220)
>      NEXT
>      FOR B = 0 TO 7
>        LOCATE 2 * B + 5, 26
>        COLOR 7, 0
>        PRINT CHR$(56 - B)
>        LOCATE 2 * B + 5, 28
>        COLOR 6, 0
>        PRINT CHR$(219)
>        LOCATE 2 * B + 6, 28
>        COLOR 6, 0
>        PRINT CHR$(219)
>        FOR A = 0 TO 7
>          IF ((A + B) MOD 2) THEN
>            COLOUR = 8
>          ELSE COLOUR = 12
>          END IF
>          CALL SQUARE(3 * A + 31, 2 * B + 5, COLOUR)
>        NEXT
>        LOCATE 2 * B + 5, 53
>        COLOR 6, 0
>        PRINT CHR$(219)
>        LOCATE 2 * B + 6, 53
>        COLOR 6, 0
>        PRINT CHR$(219)
>        LOCATE 2 * B + 6, 55
>        COLOR 7, 0
>        PRINT CHR$(56 - B)
>      NEXT
>      FOR K = 0 TO 25
>        LOCATE 21, 28 + K
>        COLOR 6, 0
>        PRINT CHR$(223)
>      NEXT
>      LOCATE 22, 30
>      COLOR 7, 0
>      PRINT "A  B  C  D  E  F  G  H"
>      FOR B = 0 TO 7
>        FOR A = 0 TO 7
>          CALL SHOWMAN(A, B, 0)
>        NEXT
>      NEXT
>      COLOR 7, 0
>     END SUB
>     'Show piece
>     DEFINT A-Z
>     SUB SHOWMAN (A, B, FLAG)
>      IF BOARD(B, A) < 0 THEN BACK = 0
>      IF BOARD(B, A) > 0 THEN BACK = 7
>      FORE = 7 - BACK + FLAG
>      IF BOARD(B, A) = 0 THEN
>        IF (A + B) AND 1 THEN BACK = 8 ELSE BACK = 12
>        FORE = BACK + -1 * (FLAG > 0)
>      END IF
>      N$ = " "
>      PIECE = INT(ABS(BOARD(B, A)))
>      IF PIECE = 0 THEN N$ = CHR$(219)
>      IF PIECE = 100 THEN N$ = "P"
>      IF PIECE = 270 THEN N$ = "N"
>      IF PIECE = 300 THEN N$ = "B"
>      IF PIECE = 500 THEN N$ = "R"
>      IF PIECE = 900 THEN N$ = "Q"
>      IF PIECE = 5000 OR PIECE = 7500 THEN N$ = "K"
>      LOCATE 2 * B + 5 - (BOARD(B, A) > 0), 3 * A + 30
>      COLOR FORE, BACK
>      PRINT N$
>      LOCATE 1, 1
>      COLOR 7, 0
>     END SUB
>     'Display a square
>     DEFINT A-Z
>     SUB SQUARE (A, B, C)
>      MT$ = CHR$(219)
>      MT$ = MT$ + MT$ + MT$
>      LOCATE B, A - 2
>      COLOR C, C
>      PRINT MT$
>      LOCATE B + 1, A - 2
>      COLOR C, C
>      PRINT MT$
>      COLOR 7, 0
>     END SUB

Download a copy of this tutorial: Tut_QB_Chess.txt

## Down With Some Lingo

### Written by stylin

Here are some common terms you'll likely see everywhere on the internet, especially when reading C++ or Java pages. If you're not familiar with some of these terms, here are how they relate to FreeBASIC code.

>     ' TYPE (or object class) definition:
>     type Foo
>     public:        ' Member access rights specifier
>          declare constructor                               ' Default constructor declaration
>          declare constructor (byref as Foo)                ' Copy constructor declaration
>          declare destructor                                ' Destructor declaration
>          declare sub Bar                                   ' Non-static member procedure declaration
>          declare static function Baz as integer            ' Static member procedure declaration
>     private:       ' Member access rights specifier
>          m_data as integer                                 ' Member data declaration
>     end type

>     ' TYPE (or object class) member procedure definitions:
>     constructor Foo                                        ' Default constructor definition
>     ' ...
>     end constructor

>     constructor Foo (byref x as Foo)                       ' Copy constructor definition
>     ' ...
>     end constructor

>     destructor Foo                                         ' Destructor definition
>     ' ...
>     end destructor

>     function Foo.Bar as integer                            ' Non-static member procedure definition.
>     ' ...
>     end function

>     function Foo.Baz as integer                            ' Static member procedure definition.
>     ' ...
>     end function

>     ' ...

>     scope
>          dim x as Foo                                      ' A local object (or instance) 'x' is default-constructed.
>          dim p as Foo ptr = new Foo(x)                     ' A dynamically allocated object (or instance) is copy-
>                                                            ' constructed from 'x'. A local pointer (or reference) 'p' is
>                                                            ' initialized with the address of that object (or instance).
>          delete p                                          ' The object (or instance) that 'p' points to (or references)
>                                                            ' is destroyed.
>     end scope                                              ' <- The local object (or instance) 'x' is destroyed.

"Foo.Bar" and "Foo.Baz" are called "qualified names"; "Bar" and "Baz" are qualified with the name of the type they are members of using "Foo" and the scope resolution operator (operator .).

When referring to subs or functions, I like to say "procedure" rather than "function" to avoid confusion. Member procedures are sometimes called "methods" (by Java people in particular). Member procedures, member data and other things within the type definition (like constants and enums - and eventually nested type or class definitions) are all called "members".

When objects (or "instances") are created, they are said to be "instantiated". Local objects are "destroyed" when leaving scope and objects dynamically allocated with new or new[] are "destroyed" when their address is given to delete or delete[], respectively. Instantiating an object means to allocate memory for and construct it (call one of its constructors), while destroying an object means to destruct it (call its destructor) and deallocate its memory.

Pointers are sometimes called "references", because they point to - or "reference" - some memory. "references" includes both pointers and reference types:

>     sub f (byval p as Foo ptr)                             ' The parameter 'p' is a pointer (or reference) to some
>     ' ...                                                  ' Foo object instance.
>     end sub

>     sub g (byref x as Foo)                                 ' The parameter 'x' is a reference (or alias) to some
>     ' ...                                                  ' Foo object instance.
>     end sub

>     sub h (byref rp as Foo ptr)                            ' The parameter 'rp' is a reference to a pointer
>     ' ...                                                  ' to some Foo object instance.
>     end sub

Reference types "reference" existing variables or objects. In FreeBASIC, like in C++, reference types behave like aliases; they provide a different name for another variable or object instance - just as type aliases

>     type AnotherNameForTypeFoo as Foo

provide a different name for a data type. For all intents and purposes, references are the variable or object instance that they reference, as opposed to pointers, which should be considered separate from what they reference. Some like to say that reference types are like pointers that are implicitly dereferenced - that's how they are implemented "behind the scenes" by fbc. This view is OK as long as you remember that reference types, unlike pointers, are guaranteed to reference existing variables or objects, and that they can never be "reseated" - they always reference the same variable or object.

Download a copy of this tutorial: Stylin_Lingo.txt

## Programming Simple Cellular Automata with Freebasic

### Written by Mathias

Cellular Autmata (CA) are great fun and they can look quite cool, and the best is, they're easy to make.

So what actually are CA? You may have heard of Cornwell's Game Of Life, it is the most popular CA. A CA is a discrete model, consisting of cells structured in a grid, which can be in any natural number of dimensions. these cells do have states, which are determined by a bunch of other cells, they are called the neighbourhood of this cell. The time t is also discrete, and the neighbourhood determines a cell in t-1. Sounds confusing to you? I guess so, therefore we look an example.

The most simple CA are 1-Dimensional and have two states, 1 or 0, black/white, gay/straight, female/straight, whatever you want. Their neighbourhood consists of 3 cells, the cell itself, and the cells to the immedate left and right:

> fig 1: The neighbourhood
> The cells to the left and right are in the state black, the cell itself has the state white --
> (at time t-1).

Now we can define the function which defines the state of our cell at time t.

As we have 3 neighbours, and every one can have two states, there are 2^3 (8) possible patterns.

> Possible patterns = number of states^number of neighbors

Here they are:

Now we have to map each of these patterns to a state.

For example:

Now this is called the rule of the CA. In terms it means, make the cell white if all the neighbours are white, make the cell black if only the left neighbour is black, make it white if only the middle cell is black, and so on.

For our CA there are 2^8 (256) possble rules. To calculate the number of rules for a CA we use:

> number of possible rules = number of states ^ number of patterns

That's basically all of the theory for now. Let's make a Freebasic app!

We want to draw the cells as pixels, therefore we have to set up a grafic mode. This is done by usung the SCREEN command. For having the most cells, I set mode to 21, but you can take whatever you like.

>     SCREEN 21

As the number of cells in our grid we take the number of horicontal pixels that our screen mode provides. To Get this number use:

>     SCREENINFO number_of_cells, number_of_generations

We now set up two 1-dimensional arrays, one for saving the states in time t-1, and one for t.

>     DIM AS UBYTE grid_at_t1(-1 TO number_of_cells+1), grid_at_t(-1 TO number_of_cells+1)

Yout might wonder why our array has to more indexes as our screen pixels has. This is because, if we want to give the first cell a new state, we can't really do so because it doesn't have a left neighbour, accordingly, the most right cell doesn't have a right member. Therefore we will later copy the state of the last cell to -1 and the state of the first cell to number_of_cells+1. This makes the topology of our CA a cylinder.

We now set the rule for our CA:

>     DIM AS UBYTE rulenumber 'a unsigned byte goes from 0 TO 255, x-actly what we need

The Rule is the Binary Representation of the rulenumber. We want to have it as a string.

>     DIM AS STRING RULE
>     Rule = Str$(Bin$(rulenumber)

>     'The BIN$()-Function takes an integer and returns it as a binary
>     'number. The Str$()-Function converts the binary number to a string.

Now we make a FOR-loop that makes generations, a new one for every vertical line of pixel until we reach the bottom of our screen.

>     FOR i = 0 TO number_of_generations
>     	grid_at_t1(-1) = grid_at_t1(number_of cells) 'making the topology of cylinder
>     	grid_at_t1(number_of cells + 1)  =  grid_at_t1(0) 'making the topology of cylinder
>     	FOR  j = 0 TO number_of cells 'now we look at each cell of the generation at time t
>     	'first we define a local string variable
>     	DIM AS UBYTE Pattern
>     	Pattern = 1+(grid_at_t1(j-1)*2^2) + (grid_at_t1(j)*2^1) + (grid_at_t1(j+1)*2^0)
>     	'every pattern can be seen as a 3 digit binary number, we convert this to a decimal number.
>     	' this decimal number gives us binary place to look for in the rule-number:
>       	grid_at_t(j)= valint(MID$(Rule, INT(Pattern), 1))
>     	'according to this we draw the actual cell to the screen:
>     	PSET (j,i), grid_at_t(j)
>                 NEXT j 'continue with the next cell
>                 'once we're finished with a generation, we make t become t-1
>         	FOR k = 0 TO number_of_cells
>             		grid_at_t1(k) = grid_at_t(k)
>         	NEXT k
>     	NEXT i

Compile and start!

Download the source code: Cellular_Automata.bas
Download this tutorial: ca_fb_tutorial.doc

## Speed Equalization in QB and FB

### Written by Stoves

### INTRODUCTION

How often have you begun to run an old QBasic program that you’ve always wanted to check out or perhaps one you’ve written yourself, only to discover that the program runs so fast it’s completely unplayable? Or maybe you’ve programmed a game on one computer, only to find out it’s way too fast or way too slow on a friend’s machine. It’s annoying to have to adjust a hard-coded delay every time you run a program that was programmed on a different machine. How do you, as a programmer, ensure that the speed of your program remains nearly the same regardless of who’s running it and where?

You’ve probably already identified the key programming function affecting this phenomenon: the delay loop. Often this delay loop consists of an empty FOR/NEXT statement with a large delay number or, in the case of FreeBasic, a SLEEP statement with the number of milliseconds for the delay duration. (As you probably already know, QBasic’s SLEEP statement only accepts numbers of whole seconds to delay.) This tutorial will present a method for consistently controlling the speed of any qbasic program regardless of the speed of the cpu it’s running on. (Ignoring the factor of whether other programs are running simultaneously on the cpu.)

### In Brief

#### PART 1 IN BRIEF

The first part of this tutorial shows how to code a function that simply delays a specific amount of time consistently, disregarding anything else that might be going on in the rest of the program. After being passed a variable indicating the desired number of milliseconds to delay, create a loop that delays that exact amount of time using SLEEP (FB only), or FOR/NEXT commands.

#### PART 2 IN BRIEF

The second part of this tutorial shows how to adjust the delay mechanism to ensure that all functions of the program perform at the appropriate speed. Determine the average amount of milliseconds the main program loop or the individual functions of the program take to execute, and then implement the subroutine created in part 1 to compensate for the speed of the cpu.

#### PART 3 IN BRIEF

The final part of this tutorial shows how to ensure that the process of gauging a machine’s speed doesn’t occur every time the program is run in order to avoid annoying users with long waits every time the program begins.

### THE GUINEA PIG

For illustration, let’s work with a simple program that displays a ball bouncing around the screen. Here’s the original code:

>     DECLARE SUB changePosition ()
>     DECLARE SUB dopause ()
>     DECLARE SUB drawObject ()

>     SCREEN 13
>     CLS

>     'Share all the variables for simplicity.
>     DIM SHARED ball(1000) AS INTEGER
>     DIM SHARED ox AS INTEGER, oy AS INTEGER
>     DIM SHARED oxl AS INTEGER, oyl AS INTEGER, oc AS INTEGER
>     DIM SHARED oxdir AS INTEGER, oydir AS INTEGER
>     DIM SHARED screenXLimit AS INTEGER, screenYLimit AS INTEGER

>     'Note the dimensions of the current screen mode
>     screenXLimit = 320
>     screenYLimit = 200
>     'Define the starting point of the ball.
>     ox = 30
>     oy = 100
>     'Define the diameter of the ball.
>     oxl = 10
>     oyl = oxl
>     'Define the ball’s starting direction.
>     oxdir = 1
>     oydir = 1
>     'Define the ball’s color
>     oc = 4

>     'Draw and capture the ball picture.
>     CIRCLE (ox, oy), (oxl / 2), oc
>     PAINT (ox, oy), oc
>     GET (ox - (oxl / 2) - 1, oy - (oyl / 2) - 1)-(ox + (oxl / 2) + 1, oy + (oyl / 2) + 1), ball

>     CLS
>     DO
>      changePosition
>      doPause
>      drawObject
>     LOOP UNTIL INKEY$ = CHR$(27)

>     END

>     SUB changePosition

>     IF (ox + oxdir) > (screenXLimit - oxl) OR (ox + oxdir) < 0 THEN
>         oxdir = oxdir * -1
>     END IF
>     IF (oy + oydir) > (screenYLimit - oyl) OR (oy + oydir) < 0 THEN
>         oydir = oydir * -1
>     END IF
>     ox = ox + oxdir
>     oy = oy + oydir

>     END SUB

>     SUB doPause

>     DIM delayVar AS INTEGER

>     FOR delayVar = 0 TO 2500000
>     NEXT

>     END SUB

>     SUB drawObject ()

>     PUT (ox, oy), ball, PSET

>     END SUB

### PART 1 IN DETAIL

The goal of the first step is to create a subroutine that can be passed an integer and delay the program for that number of milliseconds.

FB users can simply use the SLEEP command with the number of milliseconds. No additional programming is required.

QB users can determine the how many empty FOR/NEXT loops per second the cpu can process by running an empty FOR/NEXT loop for a large number of cycles to calculate loops per second using either TIMER or TIME$. Then a function can be called using a FOR/NEXT loop to delay for a given number of milliseconds. (TIMER could also be used with DO/LOOP to delay for up to hundredths of a second.) Store the cycles per second in a SHARED variable for later reference.

#### GAUGE CYCLES PER SECOND

TIMER

>     ‘Beginning of program
>     DIM SHARED cyclesPerSecond AS LONG

>     	SUB gaugeCyclesPerSecond (cyclesToTest AS LONG)

>     		DIM startTime as DOUBLE
>     		DIM endTime  as DOUBLE
>     		DIM cycles AS LONG
>     		DIM secondsPast AS DOUBLE

>     		‘Ensure that cyclesToTest is a positive number.
>     		IF cyclesToTest < 1 THEN cyclesToTest = 100000

>     		‘Run test.
>     		startTime = TIMER
>     		FOR cycles = 1 TO cyclesToTest
>     		NEXT
>     		endTime = TIMER

>     		‘Compensate for a test begun just before 12:00 am system time.
>     IF startTime > endTime THEN endTime = endTime + 24 * 3600

>     		‘Calculate duration of test.
>     		secondsPast = endTime – startTime

>     		IF secondsPast > 0 THEN
>     			‘Calculate cycles per second.
>     			cyclesPerSecond = INT(cyclesToTest/secondsPast)
>     		ELSE
>     			‘Cpu is too fast for test. Set cycles per second to the cyclesToTest parameter.
>     cyclesPerSecond = cyclesToTest
>     		END IF

>     	END SUB

TIME$

>     ‘Beginning of program
>     	DIM SHARED cyclesPerSecond AS LONG

>     	SUB gaugeCyclesPerSecond (cyclesToTest AS LONG)

>     		DIM startTime as STRING, startTimeINT as LONG
>     		DIM endTime as STRING, endTimeINT as LONG
>     		DIM cycles AS LONG
>     		DIM secondsPast AS LONG

>     ‘Ensure that cyclesToTest is a positive number.
>     		IF cyclesToTest < 1 THEN cyclesToTest = 100000

>     		‘Run test.
>     		startTime = TIME$
>     		FOR cycles = 1 TO cyclesToTest
>     		NEXT
>     		endTime = TIME$

>     		‘Convert the start and end times to seconds.
>     		startTimeINT = INT(VAL(MID$(startTime, 1, 2)) * 360) + INT(VAL(MID$(startTime, 4, 2)) * 60) + INT(VAL(MID$(startTime, 7, 2)))
>     		endTimeINT = INT(VAL(MID$(endTime, 1, 2)) * 360) + INT(VAL(MID$(endTime, 4, 2)) * 60) + INT(VAL(MID$(endTime, 7, 2)))

>     ‘Compensate for a test begun just before 12:00 am system time.
>     IF startTimeINT > endTimeINT THEN endTimeINT = endTimeINT + 24 * 3600

>     		‘Calculate duration of test.
>     		secondsPast = endTimeINT - startTimeINT

>     		IF secondsPast > 0 THEN
>     			‘Calculate cycles per second.
>     			cyclesPerSecond = INT(cyclesToTest/secondsPast)
>     		ELSE
>     			‘Cpu is too fast for test. Set cycles per second to the cyclesToTest parameter.
>     cyclesPerSecond = cyclesToTest
>     		END IF

>     	END SUB

### THE DELAY SUBROUTINE

>     SUB pleaseDelay (milliseconds AS LONG)

>     		DIM delayCycles AS LONG
>     		DIM delay AS LONG

>     		‘Calculate number of cycles to loop through.
>     		delayCycles = milliseconds * (cyclesPerSecond/1000)
>     		‘Delay program.
>     		FOR delay = 0 TO delayCycles
>     		NEXT

>     	END SUB

### PART 2 IN DETAIL

With the first step complete, we can now consistently delay a program for a given number of milliseconds regardless of the speed of the cpu. The goal of the second step is to determine how long particular sections of our code should take to run, and implement the appropriate delay accordingly. In a simple program, it may be possible to employ one delay to compensate for the main program loop. More complex programs may require implementing multiple delays. For each section of code that needs a delay, execute the following steps.

#### 1. CALCULATE THE CODE RUN TIME

Calculate how many milliseconds the code in question takes to run on the current cpu using FOR/NEXT with TIMER or TIME$. This step is similar to the gaugeCyclesPerSecond function above. Store the code execution duration in a SHARED variable for later reference.

TIMER

>     ‘Beginning of code
>     	DIM SHARED codeExecuteDuration AS LONG

>     ‘Calculate run time using TIMER
>     SUB gaugeCodeDuration (cyclesToTest AS LONG)

>     DIM startTime AS DOUBLE
>     DIM endTime AS DOUBLE
>     DIM cycle AS LONG

>     		‘Decide how many times to run the code. (Default is 1000 if parameter is 0.)
>     		IF cyclesToTest < 1 THEN cyclesToTest = 1000

>     		‘Note the start time.
>     		startTime = TIMER

>     		‘Run the code.
>     		FOR cycle = 1 TO cyclesToTest
>     			‘**Code to test goes here.**
>     		NEXT

>     		‘Note the end time.
>     		endTime = TIMER

>     ‘Compensate for a test run just before 12:00 am system time.
>     IF startTime > endTime THEN endTime = endTime + 24 * 3600

>     ‘Calculate how many seconds the test took to run.
>     codeExecuteDuration = endTime – startTime
>     ‘Subtract the amount of time that just running an empty FOR/NEXT loop would have taken.
>     ‘(cyclesPerSecond was determined in the gaugeCyclesPerSecond SUB above.)
>     codeExecuteDuration = codeExecuteDuration – (cyclesToTest/cyclesPerSecond)
>     ‘Calculate how long running the code once takes.
>     codeExecuteDuration = codeExecuteDuration / cyclesToTest
>     ‘Adjust to milliseconds.
>     codeExecuteDuration = codeExecuteDuration * 1000

>     END SUB

TIME$

>     ‘Beginning of code
>     	DIM SHARED codeExecuteDuration AS LONG

>     ‘Calculate run time using TIME$
>     SUB gaugeCodeDuration (cyclesToTest AS LONG)

>     DIM startTime AS STRING, startTimeINT AS INTEGER
>     DIM endTime AS STRING, endTimeINT AS INTEGER
>     DIM cycle AS LONG

>     		‘Decide how many times to run the code. (Default is 1000 if parameter is 0.)
>     		IF cyclesToTest < 1 THEN cyclesToTest = 1000

>     		‘Note the start time.
>     		startTime = TIME$

>     		‘Run the code.
>     		FOR cycle = 1 TO cyclesToTest
>     			‘**Code to test goes here.**
>     		NEXT

>     		‘Note the end time.
>     		endTime = TIME$

>     ‘Convert the start and end times to seconds.
>     		startTimeINT = INT(VAL(MID$(startTime, 1, 2)) * 360) + INT(VAL(MID$(startTime, 4, 2)) * 60) + INT(VAL(MID$(startTime, 7, 2)))
>     		endTimeINT = INT(VAL(MID$(endTime, 1, 2)) * 360) + INT(VAL(MID$(endTime, 4, 2)) * 60) + INT(VAL(MID$(endTime, 7, 2)))

>     ‘Compensate for a test run just before 12:00 am system time.
>     IF startTimeINT > endTimeINT THEN endTimeINT = endTimeINT + 24 * 3600

>     ‘Calculate how many seconds the test took to run.
>     codeExecuteDuration = endTimeINT – startTimeINT
>     ‘Subtract the amount of time that just running an empty FOR/NEXT loop would have taken.
>     ‘(cyclesPerSecond was determined in the gaugeCyclesPerSecond SUB above.)
>     codeExecuteDuration = codeExecuteDuration – (cyclesToTest/cyclesPerSecond)
>     ‘Calculate how long running the code once takes.
>     codeExecuteDuration = codeExecuteDuration / cyclesToTest
>     ‘Adjust to milliseconds.
>     codeExecuteDuration = codeExecuteDuration * 1000

>     END SUB

#### 2. DETERMINE THE OPTIMUM RUN TIME

Create a duration variable for the code in question, and test (through trial and error) running for various durations until the optimum amount of time is discovered (using the code in the next step). Initialize the duration variable at the beginning of the program or in the appropriate initialization sub/function.

>     ‘Beginning of code
>     	DIM SHARED codeDurationTime AS INTEGER

>     	‘Initialize the duration variable in milliseconds.
>     	‘The exact value of this variable is usually determined through trial and error.
>     	‘For example, if the ball in our sample program is traveling too fast, increase the codeDurationTime to slow it down.
>     	codeDurationTime = 450

#### 3. IMPLEMENT THE APPROPRIATE DELAY

Implement the delay subroutine at the end of each instance of the code that needs to be speed equalized, checking to make sure the delay is actually necessary. The amount of the delay should be the codeDurationTime minus the codeExecuteDuration.

>     ‘Code Loop
>     DO
>     		‘**Code**
>     		‘Obviously the delay can only slow the code down, not speed it up, so if the time it takes for the code to execute is longer than the time desired, there’s no reason to run a delay.
>     		IF codeExecuteDuration < codeDurationTime THEN pleaseDelay(codeDurationTime - codeExecuteDuration)
>     		LOOP

Of course, FB users would replace the pleaseDelay call with the SLEEP command.

### PART 3 IN DETAIL

Now that the code has been created to automatically equalize our code’s speed for multiple cpus, a couple additional considerations should be addressed. One potential negative result of speed equalization is the long pauses program startup generates by running subroutines that gauge cpu speed. The goal of the third step is to determine the most efficient way of gauging cpu speed without annoying users every time the program runs. It is recommended to run a speed gauge the first time a program is run, but then save the cpu’s speed settings and afterwards only run the speed gauge if the user chooses to do so.

#### LIMITING SPEED GAUGE FREQUENCY

>     DIM SHARED cyclesPerSecond AS LONG
>     DIM SHARED codeExecuteDuration AS LONG
>     DIM SHARED fileExists AS INTEGER
>     DIM fnum AS INTEGER

>     'Check to see if speed has been gauged yet
>     SHELL "dir/a/on/b cpuspeed > t.tmp"

>     fileExists = 0
>     frnum = FREEFILE
>     OPEN "t.tmp" FOR INPUT AS #frnum
>         DO WHILE NOT EOF(frnum) AND fileExists = 0
>     	fileExists = 1
>         LOOP
>     CLOSE #frnum

>     'FreeBasic check for if a file exists:
>     'fileExists = LEN(DIR$("cpuspeed"))
>     IF fileExists = 0 THEN
>         'Gauge the current cpu speed
>         LOCATE 10, 6
>         PRINT "Gauging Machine. Please Wait..."
>     	gaugeCyclesPerSecond 8999999
>     	gaugeCodeDuration 89999
>         frnum = FREEFILE
>         OPEN "cpuspeed" FOR OUTPUT AS #frnum
>     	WRITE #frnum, cyclesPerSecond, codeExecuteDuration
>         CLOSE #frnum
>     ELSE
>         frnum = FREEFILE
>         OPEN "cpuspeed" FOR INPUT AS #frnum
>     		INPUT #frnum, cyclesPerSecond, codeExecuteDuration
>         CLOSE #frnum
>     END IF
>     SHELL "erase t.tmp"

#### DYNAMIC SPEED CONTROL

In addition to having the program gauge the cpu only as much as necessary, it might be helpful in some cases to allow the user to control how fast the program runs. Code can be added to allow for the user to adjust the speed of the program by changing the codeDurationTime variable(s).

#### TESTING FOR CYCLES VS. SECONDS

It could be argued that attempting to test a cpu for a specific number of cycles brings one back to the original issue of problems with cpus of extremely different speeds. Very slow computers could take a VERY long time to run the tests and discourage users from running the program at all. Very fast computers could complete the tests too quickly to achieve reliable results. This legitimate concern can be addressed by changing the testing code from running a particular number of test cycles to testing the code for a particular amount of time while counting how many loops where completed. Just keep in mind that the extra code it takes to check whether the time is up will need to be accounted for in the calculations of codeExecuteDuration variable(s) to maintain consistent speed control.

### PUTTING IT ALL TOGETHER

Here’s our bouncing ball program implementing the new speed optimized code:

>     DECLARE SUB changePosition ()
>     DECLARE SUB dopause ()
>     DECLARE SUB drawObject ()
>     '------------------------------------------------
>     'BEGIN new code
>     DECLARE SUB gaugeCyclesPerSecond (cyclesToTest AS LONG)
>     DECLARE SUB pleaseDelay (milliseconds AS LONG)
>     DECLARE SUB gaugeCodeDuration (cyclesToTest AS LONG)
>     'END new code
>     '------------------------------------------------

>     SCREEN 13
>     CLS

>     'Share all the variables for simplicity.
>     DIM SHARED ball(1000) AS INTEGER
>     DIM SHARED endProg AS INTEGER, ox AS INTEGER, oy AS INTEGER
>     DIM SHARED oxl AS INTEGER, oyl AS INTEGER, oc AS INTEGER
>     DIM SHARED oxdir AS INTEGER, oydir AS INTEGER
>     DIM SHARED screenXLimit AS INTEGER, screenYLimit AS INTEGER
>     '------------------------------------------------
>     'BEGIN new code
>     DIM SHARED keyInput AS STRING
>     DIM SHARED cyclesPerSecond AS LONG
>     DIM SHARED codeExecuteDuration AS LONG
>     DIM SHARED codeDurationTime AS INTEGER
>     DIM SHARED initDurationTime AS INTEGER
>     DIM SHARED fileExists AS INTEGER
>     DIM frnum AS INTEGER
>     DIM displayDuration AS INTEGER

>     initDurationTime = 5
>     codeDurationTime = initDurationTime
>     displayDuration = -1
>     'END new code
>     '------------------------------------------------

>     'Note the dimensions of the current screen mode
>     screenXLimit = 320
>     screenYLimit = 200
>     'Initialize the variable triggering when the program should end.
>     endProg = 0
>     'Define the starting point of the ball.
>     ox = 30
>     oy = 100
>     'Define the diameter of the ball.
>     oxl = 10
>     oyl = oxl
>     'Define the ball’s starting direction.
>     oxdir = 1
>     oydir = 1
>     'Define the ball’s color
>     oc = 4

>     'Draw and capture the ball picture.
>     CIRCLE (ox, oy), (oxl / 2), oc
>     PAINT (ox, oy), oc
>     GET (ox - (oxl / 2) - 1, oy - (oyl / 2) - 1)-(ox + (oxl / 2) + 1, oy + (oyl / 2) + 1), ball

>     '------------------------------------------------
>     'BEGIN new code
>     checkgauge:
>     CLS

>     fileExists = 0
>     'Qbasic check for if a file exists:
>     SHELL "dir/a/on/b cpuspeed > t.tmp"

>     frnum = FREEFILE
>     OPEN "t.tmp" FOR INPUT AS #frnum
>         DO WHILE NOT EOF(frnum) AND fileExists = 0
>     	fileExists = 1
>         LOOP
>     CLOSE #frnum

>     'FreeBasic check for if a file exists:
>     'fileExists = LEN(DIR$("cpuspeed"))

>     IF fileExists = 0 THEN
>         'Gauge the current cpu speed
>         LOCATE 10, 6
>         PRINT "Gauging Machine. Please Wait..."
>     	gaugeCyclesPerSecond 8999999
>     	gaugeCodeDuration 89999
>         frnum = FREEFILE
>         OPEN "cpuspeed" FOR OUTPUT AS #frnum
>     	WRITE #frnum, cyclesPerSecond, codeExecuteDuration
>         CLOSE #frnum
>     ELSE
>         frnum = FREEFILE
>         OPEN "cpuspeed" FOR INPUT AS #frnum
>     		INPUT #frnum, cyclesPerSecond, codeExecuteDuration
>         CLOSE #frnum
>     END IF
>     SHELL "erase t.tmp"
>     'END new code
>     '------------------------------------------------

>     CLS
>     DO
>     changePosition
>     '------------------------------------------------
>     'BEGIN new code
>     IF codeExecuteDuration < codeDurationTime THEN pleaseDelay (codeDurationTime - codeExecuteDuration)
>     drawObject
>     IF displayDuration > 0 THEN
>         LOCATE 1, 1
>         PRINT LTRIM$(RTRIM$(STR$(codeDurationTime))) + " milliseconds      "
>     END IF

>     'Capture any user keystroke.
>     keyInput = INKEY$
>     SELECT CASE UCASE$(keyInput)
>         'Toggle displaying the codeDurationTime if the 'D' key is pressed.
>         CASE "D"
>             displayDuration = displayDuration * -1
>             LOCATE 1, 1
>             PRINT SPACE$(40)
>         'Reset codeDurationTime to initDurationTime
>         CASE "R"
>             codeDurationTime = initDurationTime
>         'Decrease the code duration by 1 millisecond if the down arrow key is pressed.
>         CASE CHR$(255) + "P"
>             codeDurationTime = codeDurationTime - 1
>             IF codeDurationTime < 0 THEN codeDurationTime = 0
>         'Increase the code duration by 1 millisecond if the up arrow key is pressed.
>         CASE CHR$(255) + "H"
>             codeDurationTime = codeDurationTime + 1
>         'Decrease the code duration by 1 millisecond if the down arrow key is pressed.
>         CASE CHR$(0) + "P"
>             codeDurationTime = codeDurationTime - 1
>             IF codeDurationTime < 0 THEN codeDurationTime = 0
>         'Increase the code duration by 1 millisecond if the up arrow key is pressed.
>         CASE CHR$(0) + "H"
>             codeDurationTime = codeDurationTime + 1
>         'Exit program if the Escape key is pressed.
>         CASE CHR$(27)
>             endProg = 1
>         'Re-gauge computer if the Enter key is pressed.
>         CASE CHR$(13)
>             SHELL "erase cpuspeed"
>             GOTO checkgauge
>     END SELECT
>     'END new code
>     '------------------------------------------------
>     LOOP UNTIL endProg = 1

>     END

>     SUB changePosition

>     IF (ox + oxdir) >= (screenXLimit - oxl - 2) OR (ox + oxdir) < 0 THEN
>         oxdir = oxdir * -1
>     END IF
>     IF (oy + oydir) >= (screenYLimit - oyl - 2) OR (oy + oydir) < 0 THEN
>         oydir = oydir * -1
>     END IF
>     ox = ox + oxdir
>     oy = oy + oydir

>     END SUB

>     SUB drawObject

>     PUT (ox, oy), ball, PSET

>     END SUB

>     '------------------------------------------------
>     'BEGIN new code
>     SUB gaugeCodeDuration (cyclesToTest AS LONG)

>     DIM startTime AS DOUBLE
>     DIM endTime AS DOUBLE
>     DIM cycle AS LONG
>     DIM secondsPast AS DOUBLE

>     'Decide how many times to run the code. (Default is 1000 if parameter is 0.)
>     IF cyclesToTest < 1 THEN cyclesToTest = 1000

>     'Note the start time.
>     startTime = TIMER

>     'Run the code.
>     FOR cycle = 1 TO cyclesToTest
>     	IF (ox + oxdir) > (screenXLimit - oxl) OR (ox + oxdir) < 0 THEN
>             oxdir = oxdir * -1
>         END IF
>         IF (oy + oydir) > (screenYLimit - oyl) OR (oy + oydir) < 0 THEN
>             oydir = oydir * -1
>         END IF
>     	ox = ox + 0
>     	oy = oy + 0
>     	PUT (ox, oy), ball, AND
>         'Capture any user keystroke.
>         keyInput = INKEY$
>         SELECT CASE UCASE$(keyInput)
>             'Toggle displaying the codeDurationTime if the 'D' key is pressed.
>             CASE "D"
>                 displayDuration = displayDuration
>                 LOCATE 1, 1
>                 PRINT SPACE$(40)
>             'Reset codeDurationTime to initDurationTime
>             CASE "R"
>                 codeDurationTime = initDurationTime
>             'Decrease the code duration by 1 millisecond if the down arrow key is pressed.
>             CASE CHR$(255) + "P"
>                 codeDurationTime = codeDurationTime - 0
>                 IF codeDurationTime < 0 THEN codeDurationTime = 0
>             'Increase the code duration by 1 millisecond if the up arrow key is pressed.
>             CASE CHR$(255) + "H"
>                 codeDurationTime = codeDurationTime + 0
>             'Decrease the code duration by 1 millisecond if the down arrow key is pressed.
>             CASE CHR$(0) + "P"
>                 codeDurationTime = codeDurationTime - 0
>                 IF codeDurationTime < 0 THEN codeDurationTime = 0
>             'Increase the code duration by 1 millisecond if the up arrow key is pressed.
>             CASE CHR$(0) + "H"
>                 codeDurationTime = codeDurationTime + 0
>             'Exit program if the Escape key is pressed.
>             CASE CHR$(27)
>                 endProg = 0
>             'Re-gauge computer if the Enter key is pressed.
>             CASE CHR$(13)
>         END SELECT
>     NEXT

>     'Note the end time.
>     endTime = TIMER

>     'Compensate for a test run just before 12:00 am system time.
>     IF startTime > endTime THEN endTime = endTime + 24 * 3600

>     'Calculate how many seconds the test took to run.
>     secondsPast = endTime - startTime
>     'Subtract the amount of time that just running an empty FOR/NEXT loop would have taken.
>     '(cyclesPerSecond was determined in the gaugeCyclesPerSecond SUB above.)
>     secondsPast = secondsPast - (cyclesToTest / cyclesPerSecond)
>     'Calculate how long running the code once takes.
>     secondsPast = secondsPast / cyclesToTest
>     'Adjust to milliseconds.
>     codeExecuteDuration = secondsPast * 1000

>     END SUB

>     SUB gaugeCyclesPerSecond (cyclesToTest AS LONG)

>     	DIM startTime AS DOUBLE
>     	DIM endTime  AS DOUBLE
>     	DIM cycles AS LONG
>     	DIM secondsPast AS DOUBLE

>     	'Ensure that cyclesToTest is a positive number.
>     	IF cyclesToTest < 1 THEN cyclesToTest = 100000

>     	'Run test.
>     	startTime = TIMER
>     	FOR cycles = 1 TO cyclesToTest
>     	NEXT
>     	endTime = TIMER

>     	'Compensate for a test begun just before 12:00 am system time.
>     	IF startTime > endTime THEN endTime = endTime + 24 * 3600

>     	'Calculate duration of test.
>     	secondsPast = endTime - startTime

>     	IF secondsPast > 0 THEN
>     		'Calculate cycles per second.
>     		cyclesPerSecond = INT(cyclesToTest / secondsPast)
>     	ELSE
>     		'Cpu is too fast for test. Set cycles per second to the cyclesToTest parameter.
>     		cyclesPerSecond = cyclesToTest
>         END IF

>     END SUB

>     SUB pleaseDelay (milliseconds AS LONG)

>     	DIM delayCycles AS LONG
>     	DIM delay AS LONG

>     	'Calculate number of cycles to loop through.
>     	delayCycles = milliseconds * (cyclesPerSecond / 1000)
>     	'Delay program.
>     	FOR delay = 0 TO delayCycles
>     	NEXT

>     END SUB
>     'END new code
>     '------------------------------------------------

Download a copy of this tutorial: Speed_Equalization.doc or Speed_Equalization.mht

## Singletons

### Written by stylin

Singletons are evil, at least, that's what everyone seems to say. But like anything, they have their place.

Singletons are object classes that guarantee at least two things:

1. One and only one object can be instantiated, and
2. A global access point to that instantiation will be provided.

The problem some see with singletons is that the idiom can be overused; they are, essentially, global objects, and many try to avoid global objects like the plague. But sometimes singletons are practical, and logical, even necessary. Consider a logger or kernel. It makes sense that only one of these object classes need be instantiated at any one time, and for most applications, it would also be necessary to provide a global point of access to them - all parts of an application might want to log information or request kernel services at some point.

There are a few ways to control object instantiation. Here's a simple example of the 'static member procedure' method, using a bare-bones logger class. First, the code:

>     type Logger
>     public:
>     	declare static function GetInstance () as Logger ptr
>     	' ...
>     private:
>     	declare constructor ()
>     	declare constructor (byref as Logger)
>     	declare destructor ()
>     	declare operator let (byref as Logger)
>     	' ...
>     end type

>     function Logger.GetInstance () as Logger ptr
>     	static theinstance as Logger
>     	return @theinstance
>     end function

The idea is to create a static member procedure that contains a static singleton object - theinstance - which it then returns the address of. Static variables and objects are created when they are first encountered - the first time Logger.GetInstance is called - and are destroyed when the program ends. This has the benefit of delaying creation of the singleton object until it is needed, and also guaranteeing its existence thereafter.

All constructors - in this case the default and copy constructor - as well as the destructor and assignment operator are declared private, to prevent user code from creating, copying or destroying Logger objects; only Logger member procedures are allowed to do that. In fact, in this small example, the copy constructor and assignment operator are not even defined. That's the basic idea, here is the rest of Logger's TYPE and member procedure definitions:

>     type Logger
>     public:
>     	' .. as before ..

>     	declare function Open (byref filename as string, byval doAppend as integer = -1) as integer
>     	declare sub Close ()

>     	declare sub Log (byref text as string)

>     private:
>     	' .. as before ..

>     	const m_invalidFilenum as integer = &Hdeadc0de
>     	m_filenum as integer = m_invalidFilenum
>     end type

>     constructor Logger ()
>     end constructor

>     destructor Logger ()
>     	this.Close()
>     end destructor

>     function Logger.Open (byref filename as string, byval doAppend as integer) as integer
>     	var filenum = ..freefile()

>     	var res = iif (doAppend, _
>     		..open(filename, for append, as filenum), _
>     		..open(filename, for output, as filenum))

>     	m_filenum = iif (res, m_invalidFilenum, filenum)
>     	return res
>     end function

>     sub Logger.Close ()
>     	if (m_filenum <> m_invalidFilenum) then
>     		..close(m_filenum)
>     		m_filenum = m_invalidFilenum
>     	end if
>     end sub

>     sub Logger.Log (byref text as string)
>     	if not (m_filenum = m_invalidFilenum) then
>     		print #m_filenum, text
>     	end if
>     end sub

The default constructor does nothing in this example. Logger.Open opens a file for logging, appending to it by default. Logger.Log logs text to the file and Logger.Close closes the file. Here is an example of usage:

>     scope
>     	var thelogger = Logger.GetInstance()

>     	if (thelogger->Open("filename.txt")) then
>     		print "error: Could not open log file."
>     		end -1
>     	end if

>     	thelogger->Log("some text")
>     	thelogger->Log("some more text")

>     	thelogger->Close()
>     end scope

Download a copy of this tutorial: Stylin_Singletons.txt

## Using procedure pointers to vary behavior

### Written by stylin

Procedures contain program statements that perform a certain job a certain way. The job they perform is their function, and the way they do it can be called their behavior, so, procedures encapsulate function and behavior. Often it is the case that a particular behavior needs to be chosen out of a set of behaviors.

For example, if you are given an array of values and are asked to accumulate them in some way, by adding or multiplying them. A first attempt at this problem might look like:

>     enum Accumulator
>     	AddAccumulator
>     	MultiplyAccumulator
>     end enum

>     function Accumulate (array() as integer, accum as Accumulator, initial as integer) as integer
>     	var state = initial

>     	for index as integer = lbound(array) to ubound(array)
>     		select case as const accum
>     		case AddAccumulator
>     			state += array(index)
>     		case MultiplyAccumulator
>     			state *= array(index)
>     		end select
>     	next

>     	return state
>     end function

>     dim array(3) as integer = { 1, 2, 3, 4 }

>     print Accumulate(array(), AddAccumulator, 10) ' 20
>     print Accumulate(array(), MultiplyAccumulator, 10) ' 240

Accumulate takes an array of values, a constant determining which accumulation behavior to use, and a n initial value to accumulate from. Accumulate uses a SELECT block to decide which behavior to use. The choice of accumulation behavior then lies in two places: the enumerator Accumulator and the body of Accumulate.

When more accumulation behaviors are needed, perhaps those that accumulate values by subtraction or division, both Accumulator and Accumulate would need to be modified. This type of design is OK, particularly in open-sourced projects where users can freely - but not necessarily easily - modify the source for their needs. But other designs put the choice of behavior in the users' hands.

This is where procedure pointers come in. Since procedure pointers can point to any number of procedures (behaviors), you could say they encapsulate the choice of a particular behavior (just as pointers to variables encapsulate the choice of a particular value). Here is how one might modify the above code to use procedure pointers:

>     type Accumulator as sub (byref state as integer, value as integer)

>     function Accumulate (array() as integer, accum as Accumulator, initial as integer) as integer
>     	var state = initial
>     	for index as integer = lbound(array) to ubound(array)
>     		accum(state, array(index))
>     	next
>     	return state
>     end function

>     sub AddAccumulator (byref state as integer, value as integer)
>     	state += value
>     end sub

>     sub MultiplyAccumulator (byref state as integer, value as integer)
>     	state *= value
>     end sub

>     dim array(3) as integer = { 1, 2, 3, 4 }

>     print Accumulate(array(), @AddAccumulator, 10) ' 20
>     print Accumulate(array(), @MultiplyAccumulator, 10) ' 240

Basically, the enumerator Accumulator has been replaced with a procedure pointer type. Accumulate no longer requires a SELECT block to choose between behaviors, that choice has already been made by whoever coded the procedure that accum points to. All it needs to do now is pass state and value information to the supplied accumulator procedure, and return the result. Adding more accumulation behaviors or modifying existing ones does not require modification of Accumulate at all; once debugged, it can safely be compiled into a neat little library and forgotten about. Users need only code a new accumulation procedure to support their own behaviors.

Download a copy of this tutorial: Stylin_Procedure_Pointers.txt

## COMMERCIAL AND PROFESSIONAL SOFTWARE DEVELOPMENT
Part Seven - Project Maintenance And Future

### Written by Stephane Richard (MystikShadows)

Welcome to the seventh and final part of the series on commercial and professional software development. As I introduced in the sixth part of the series, this final part will essentially be about software maintenance and the future of of software projects. As I said, a programming project, whether it's a game or an application is never quite finished. It seems there's always that one last thing you want to add, that one last little thing. Problem is often, you get another idea after that last thing you had before and the list just keeps getting longer.

One of the biggest questions when you're creating your own game or application project is essentially where to draw the line. You often find yourself adding to your list and that can often bring you to a place where you'll never see version 1.0 of your project out because you'll always be adding to your program. This seventh part will try to give you some tips and tricks in order to split features and fixes into versions and sub versions so you can get a grip on how to "logically" stop the development of a version and leave everything else to future versions. In other words, you'll learn where to draw the infamous version number lines. We'll also look at what maintenance is all about when it comes to software development. So let's start by looking at the maintenance of a project and then we'll look at the future of current projects.

### INTRODUCTION:

The first thing I can tell you here is that main starts when you fix the first bug in your program. And that typically happens even before version 1.0 is out. The maintenance of a project is just that, fixing bugs, adding features when you or the users think of them. Project maintenance is an ongoing cycle that servers the purpose to keep the program running smoothly and/or updated when needed. Programs like payrolls and inventory might need constant maintenance to update tax rate tables or prices and so forth. In part five of the series I explained the different types of bugs and presented some software that were designed to help keep track of the bugs These systems, because of the way they are designed to handle bugs, often offer a great way to keep track of feature requests. The main reason for this is because they typically holds all the information you need to document a feature as well as you'd document a bug so it's a great way to organize features. Much like the bugs, features can also have levels of priorities attached to them. Some features can be critical to the operation at hand. Others can be almost insignificant but be important to the user that suggested it. hence, here is a great way to classify feature requests.

- Essential Features: When you make a specific program, how good it is depends on exactly what you know of the program you're making. It's very possible that you skipped something that really should have been there in the first place. It all depends on who's giving you the information and if that information is complete. Hence people that know the business very well are the best type of people to tell you how your program should work. If they tell you that you forgot a whole set of steps to do this or that chances are you did forget it. Some will tell you perhaps more abruptly than others, don't take it as critic per se. Just make it like it's supposed to be.
- Popular Features: Again, This all depends on where you got your initial information about the type of program you are doing. There is such a thing as a set of highly in demand features I like to call them. If you got your information right the first time, chances are you probably have alot of (maybe even most of) these popular features. But some of these popular features can be very specific and sometimes unexpectedly useful. So when you get them, they definitely deserve the 2nd most important level of priority since we can assume you made this program so your users would love to use it.
- Design Features: These types of features are known as alternatives. This typically means that you've thought about the feature, it exists in your program but perhaps some of your users are just used to do this particular thing a different way. You made the program, but it doesn't mean your users don't have a say in it per se. I wouldn't implement every feature every time I get one right away. Especially those features meant to replace the current way you implemented a functionality. But if enough users request a similar feature then it might be time to reconsider the way you implemented it and give them what they want. This could also be to add a field of information on a form, and other things that add to the program's design and functionality. One last type of design feature might be to change terminology. You might have called something a report, they might prefer to refer to it as a list rather than a report. People in an industry are used to call things certain ways. And little things like adapting to their terminology shows a great degree of respect to your users (which all paying customers love to get).
- Minor Features: These can be design features but very quick to implement like adding a keystroke to access a functionality or other quick things like that. They are the quickest to implement and even if it seems insignificant to you, they will be liked by your users. Once the program is done, you as a the programmer don't matter. If you want your users to buy your program (keep the business so to speak) you might want to consider implementing these, they're quick and they show your users that what they want matters.

These are the typical types of features you're likely to come across in the life of a software development project. None of them should be neglected as they all serve the purpose of answering the needs of your users. There are limits however. If a user tells you that all your program should be changed he/she atleast better have some indisputable reasons to tell you that. Very big features (like whole sub systems or complicated features) might also best be implemented in a subsequent version (as you'll see later in this document).

At this point, the maintenance has to consider what exactly is being maintained and for what reason. Because the type of request and it's complexity will determine which version they will be implemented in (if it does get implemented). So the combination of feature requests from your users (or from you) and the correction of bugs, together, consitutes the maintenance part of a software development project. Now, let's see how and when to draw the infamous version numbering line.

### WHERE DOES VERSION 1.0 REALLY END:

There's more than one reason to have more than one version of a project. Whatever the reason may be, there are ways to determine this. Hence, before we go any further, let's start by listing here what the different reasons may be for breaking down your project into several versions.

- The Project Is Too Big For One Release Only: Let's face it, sometimes you get specifications that are just too big to do in one shot in a realistic kind of way. This is where the notion of project phases come into play. At this point you need to split the project into essential functionalities and wanted functionalities. I will give an example of this later in this document. But the main thing to remember here is to give the essential functionality in version 1.0 and the wanted functionality in subsequent versions. If there is alot of wanted functionality, a great way to break them down would be by grouping them in functional modules such as reporting, dafa entry and the likes so that you need to think about one thing at a time when implementing them.
- The Project Needs To Keep Up With Reality: These are the types of projects that start off with a base of functionality as it is currently need with the knowledge that it will need to change on a regular basis because the needs may change that fast in that particular type of project. Knowing this in advance will make you code accordingly hence making your code very adaptable to new situations and needs because you already know they will need to change. Since in these cases it's very unlikely that the situation won't stop changing you can imagine that there will be alot of versions to come for this particular type of project.
- Customers And Users Ask For Many Maybe Too Many Things) To Add: This is the kind of situation that is often beyond your control. People use the program you made. They like it but they would love it so much if only it could do this or that. So being they paid to use your software, they won't be affraid to ask for things. You as the creator of this project probably need to do your best to give your paying customers what they want. Some of the requests can be done quickly but others can be quite long to implement.
- Sequels To Gaming Projects: This one doesn't need alot of explaining as you can imagine. Why would you make a sequel to a game? I'll answer that by aking you why would they make a sequel to a movie? It's not uncommon to either make an ending to a game that just yells sequel out loud or to just create a new plot where your characters in an existing game fit in perfectly into this new plot. Imagination is the only real limit to gaming projects and I'm sure you know that imagination doesn't have that many limits. So Sequels can definitely happen and probably more often than you might imagine.

These are the four main reasons for future versions. There's others but typically they'll relate to one of these four. The main thing to remember is that when you do decide to split up a project in more than one version You have more than one reason to organize these versions and more than one way to do it. As most of the other parts of this series, an example is the best way to visualize some of these things. Hence let's take our now famous Finance CAD example and see how the maintenance phase and future of this project applies.

### FINANCE CAD MAINTENANCE:

We already know the specifications that made the first version of Finance CAD. Since there's no users we will look at Finance CAD and see what we would like added to it for say version 2.0 (and any sub sequent version if needed. As we described it so far it seems like a pretty complete application right? You'd be surprised to see the creativity of your users. So let's start enumerating some suggestions here and after, we'll take these and classify them accordingly to give you a real world example of how things might work.

- Adding project management features so that reports on the creation of a house can be given by time interval and date as well as by construction phases if needed or desired.
- Adding the ability to view the layout of the house in 3D to get a perspective view of the house.
- Related to the previous one, adding textures and maps to use the textures on our 3D view of the house to make it look realistic.
- Ability to compare house plans scenarios (with costs related issues) so that we can pick the best way.
- Addition of a physics engine to simulate and test the solidity of materials picked for the construction of a house.
- Create an accounting system so that everything can be accounted for and billed for automatically.
- Add a subtable to the items price list so that we can keep a history of the price changes

### THE FINANCE CAD FEATURE DETAILED:

These are just seven features and as you can see, they vary alot in degrees of complexity and involvement in the whole project. Of course there could be more features but this should be plenty to examplify what I mean by classification. So let's take each of these features, explain some of the details so we can what kind of feature it is as well as when and where to implement it.

- Our first feature is project management features. What we want here is the ability to plan out the construction of house. Hence bring the notion of time (and related expenses) to the construction of the house so that when we estimate the cost of building the house we can now also include labor in the estimate. This has the ability to give a more complete picture of what it would really cost to build the house. This would typically mean the addition of certain fields in the database and a little design work to add the respective fields to be entered on the screens. It would also mean some basic time and date related functionality so that we can calculate the amount of time needed to build the house. So it's a simple feature to ask for but not all that simple to implemeent. It is, however, an important feature because of the added accuracy of the estimate.
- The second and third feature will be combined together since they are so closely related to each other. This way the user will have the ability to view the house in wireframe 3D and/or texture rendered view (to give a more realistic view of the resulting house). This means adding fields to the database to accommodate for the 3D extra fields as well as material bitmaps needed for the rendering. Then comes the 3D engine to allow to view the house in actual 3D Now this is a very big feature of course and like the previous feature it would be a very wanted feature.
- The ability to compare house building scenarios means the ability to take a house plan, define the types of materials used to build the house (hard wood floors versus tiled floor, 2 layer windows isntead of one layered window) and different things like that for building materials and be able to compare the prices of all of those based on the quantity of each materials used in the house plans. This can be a very useful information to you and the clients you are creating the house for. Give them budgeted options so that they can get the most features for the most affordable prices. This is a great feature, however it's nothing they can't do right now just by copying a house plan, changing the materials used and saving that as a different name, print both plans and see for themselves. Technically this means to add a scenarios table to the database that is attached to the main house layout. And adding some queries to interrogate this table when needed. It also means changes to the data entry screens to allow to add more than one set of materials used for a house plan. Medium size modification. Ultimately it could be implemented at any desired time.
- The physics to test the solidity of a given material. Well this speaks for itself really. When you give the house the materials used for the construction. Once this is in place in the house layout you could, with the proper formulas, evaluate what the construction can take as far as abuse goes. This can depend on the materials used, the house (or building) structure as well as what kind of abuse that house could be subjected to (storms, tornadoes, hurricanes, earthquakes and the likes). This, like the 3D engine is a very big add on. But you know that not every single one of your customers will actually want this kind of evaluation on their house. Typically, the more expensive the building is to build, the more important this type of evaluation becomes. Hence, this is a cool feature to have, but not an absolute most, not yet anyways.
- The Integrated Accounting System Built Right In. This is of course a personal feature request. Of course once the house is in the database it would be nice if every time you billed something for that house, or bought something from supplier for that house, if everything was automatically part of your accounting. You could then concentrate on what you like to do most. And leave the boring accounting part up to Finance CAD. To be sure, not everyone that might use Finance CAD will want this feature or even understand why it's in there. But it would be nice for your company. Hence this can wait since you probably already have people doing this part right now. It's a big alteration since alot of different modifications to the database are needed including the addition of several tables to accommodate for the accounting needs of the program. Plenty of new forms as well and let's not forget all the reports that will be needed for the accounting. Since it's not a crucial feature, just a wanted feature. This can wait as it shouldn't influence much on the success of Finance CAD as the tool it is
- Adding a sub table to the Items table to keep track of price changes as they occur. This is a quick feature to add. A couple queries, a small change on the Items Data entry screen and in related reports so that the prices used at the date the floor layouts were created can be retrieved and used as needed. It is a very useful feature because it eliminate a great deal of repetition (redundancy) in the table structures so this can be done as soon as possible and will be welcomed by most of your users. These are little details that not everybody thinks about so when they are indeed thought of, it just gives you that much better of an image. It's always good practice to show attention to details in your software creations and this is, in this case, a great way (and a quick way) to do just that.

And there you have, these are the details of the new features to add. As you can see, by knowing more of the details of these features we can already start to think differently about each of these features. Information, in any development project is always very important and plays crucial roles in the overall realization of the project itself. With this extra information at hand, let's classify our features and see when would be a good time to implement them.

### FEATURE CLASSIFICATION AND PRIORITY LEVELS EXPLAINED:

Now that we have our feature requests defined, we can go ahead and define their order of realization. Is there any written rule on this? The answer is yes. Indeed, there's 2 main reasons to give a feature any priority. Priority will of course play a big role in how soon a feature is implemented in the future. One of them depends on the users the other depends on the programmers. Let's see what these reasons are.

- The Feature Is Definitely Needed: By this I mean that the feature has the potential to become very crucial in the success of the project. This could be, for example a feature that all other competing products have but that you just didn't think of putting it in yet. It could be a feature that isn't in any other products that would help set your product apart from your competitors. Depending on the industry your project exists in, this kind of advantage might definitely be worth a second (or even third) consideration. If it's for your own personal non commercial project well that all depends on what you are willing and ready to do for your users. Nonetheless they shouldn't just be ignored or skipped over.
- The Feature Can Become Popular: For applications, features that can help make your project popular will typically be features that make your project work better and faster. Time is money so anything that make your project work faster (hence save time) without making your project more complicated to use in the process are very likely to become very popular very fast. This is also something you should think about when creating the program initially. But sometimes, something you forgot about will resurface in the form of a feature request so then it's worth taking the time to implement.

From this point, the type of project you are making will influence alot of how certain features can be classified. But with these two guidelines you can begin to evaluate the importance of a given feature request. Now you need to add to that some business knowledge so that when you see a given feature you can evaluate if it applies to your project's intended purpose or not as well as if the feature should apply to it. Combining these two guidelines, the business knowledge and the types of features as described at the beginning of this document will give you the tools to position the features where they go in the future time line of your project. We will now illustrate this by taking each feature mentioned and described above their priority and realization order.

### VERSION NUMBERING SYSTEM:

This might be because of my Visual Basic background so this section is a suggestion more than a rule per se. This means you may like and want to use this or you may want to forget the last element described here. This exists in more than one language so this is the version numbering method I like to use. A version number to me is separated in four elements. Each play a specific role in what I put where when it comes to product maintenance. Let's review these elements so you can see how I use them.

- Major Version Number: This is the main version number. Basically this version changes when the specifications of the program changes dramatically. This does not include bug corrections as these belong in the revision and release numbers. This number stays at zero until everything in the specification and other documents that define what the application currently should do in version 1 are not completed. Once done though (and debugged of course) then you can say that you are at version 1.00.000a. In database applications anything that changes the structure of the database and it's relationships should be considered major changes as well.
- Minor Version Number: This second part of the version number to me is for rather major changes that are still about the specifications as they were designed for the current version. In our example, the different way of doing things would be included as a minor version number for example. It changes the design but is aimed at accomplishing the same original task. As another example that commonly happens in the IT (Information technology) industry. Many times when you release your first version of a product one of the main thing you'll get asked for is to add new reports that are often very useful to the user. If this can be done with the existing information in the database them great it can be a minor change. But if this means adding a table (or two) hence adding data entry screens to accommodate the new table, changing and/or adding relationships to other tables then this should be done as a major change hence in the next major release. This is because there are users already using the current database structure as it is hence don't expect it to change on them because of a minor change to the program.
- Revision Version Number: Revisions are bugs that can only be fix by bringing minor changes to the way things are currently done in the program. Or things that should have been there but aren't. These do not mean changes to the database or other major items. As an example, say you have a report done that should have expected a starting and ending date as it's input parameters. But for some reason you only have a starting date. Something was missed for some reason and the ending date isn't there on the report form. Then you need to add it because the report doesn't work without that ending date. No database changes, no major work to be done per se. just an input field to add to accommodate the ending date. Typically, revision level bugs (and release level bugs) are rather quick to find and fix. Not everyone uses the Revision level per se. I've seen people stop and the major and minor version numbers (Version 1.00 for example). I have also seen people skipping the revision but using the release letter (much like they would use the revision number so you would see something like version 1.00b).
- Release Letter: The release letter are quick internal bugs. Hence there's nothing visually wrong with the program or the particular form they are using. But it just doesn't do the job it's supposed to. Could be due to anything. They are all very quick to fix, but sometimes can be a bit trickier to find. For example. You rounded off a percentage in a formula that shouldn't have been rounded off. Many of these types of bugs are better found by the people that use the program and know what to expect as results of those formulas. Because sometimes, you as a programmer might think it makes sense to round off that percentage and for all intents and purposes Other types of release level bugs can be much more direct to find however. And can be corrected rapidly. Like revision numbers not everyone uses the release letters. But you'll find that if you are making a rather big system, having all four parts of the version number can help organize when and how a given feature can be added.

### CLASSIFICATION OF THE FEATURE REQUESTS:

Here we will give each of the six features (there were seven but we combined the two 3D related features into one). Their priority and explain why these priorities were given to them.

- Project Management as described above can quickly become a great organizational tool for a house building project. Let's face it with all the intricate details of creating a plan for a house and going ahead and building the house (dealing with contractors and such). It can become easy to get lost in all the details even if they are right there listed in front of you. Hence we need to give this one a high priority because it plays a crucial role in the program's "expected" behavior. Now an architect firm might not need this because they typically only need to worry about the plans themselves, not how it's built. As far as version number goes, this would be considered to be released in the next minor version number. Version 1.01.000a because it's a much needed feature. but because certain fields would be added to existing tables, we would need to remember to create a script to update the database structures accordingly.
- The 3D rendering of house plans is our next feature. Now, if you've never moved a 2D to a 3D system before chances are you might think it's as simple as adding a Z coordinate to the whole system. That's often not the case. Especially not today. Today 3D means either using DirectX, OpenGL, Allegro or some other 3D engine. It means a learning curve if you haven't used these 3D engine before. It also means a whole set of calculations, new file formats and basically an all around change in the program. Because of all the outside implications involved as well as the internal ones This has no choice. It goes in Version 2.00.000a because there's just too much to do. However, since it is a very nice feature to have this might be enough to decide to bring version 2.0 to it's release as soon as possible and give it a couple notches up the priority scale.
- The house building scenario comparisons is a feature that is nice to have. It's a shortcut however to already existing functionality. As I mentioned before, you could already to this by creating a house plans, adding the elements needed. Adding the first set of materials to use to get a first price estimate. Save that under a different file name, change the materials for the second time to get a second price and so on. it would be a bit longer to do so yes, speed is the only reason why this short cut is a much wanted feature. who says speed says time saved (hence money). This feature means adding a table to separate the scenarios from the floor plans themselves. Possibly a change to the input screens to present the materials in a grid instead of a set of fields (depending on how you originally created that part). it' a big change but it doesn't change the internal design of the program per se. Hence, this should be in the next minor version number. Version 1.02.
- The physics Engine is a feature that visually will completely depend on the addition of the 3D rendering module. Internally however the physics engine could be a separate and completely independent tool. Why? Because not everyone (not even a majority of your users) are likely to need or want this feature. It's fun to have. But this would also mean raising the price tag of your program (as would adding 3D). In that case you have to ask yourself how many people would pay for that difference? Hence this should be either a separate application or an option feature that people get only if they want it or need it. Perhaps here, aside version number, the notion of specific Editions are in order. This would be in the professional edition of your software in this case. So people that want this feature would pay extra to get. Don't get me wrong here, for those that do need this feature, it is probably a crucial feature to have hence paying extra wouldn't be a problem as it would be more than justifiable for them to get the feature. For this article though, we would put that with the 3D in version 2.00.000a. Or 2.01.000a as 3D would probably be more wanted than the physics engine per se.
- The integrated accounting system, much like the project management system is a nice to have feature as in making one's life easier feature. Because it takes care of many internal things automatically for the users. But it's a hard to sell feature because most companies that would buy Finance CAD typically already have their existing accounting systems at hand. So for people opening up their new businesses this feature would be easy to sell. For the others, you might want to think about interfacing Finance CAD to their respecting accounting systems. Fortunately, many accounting system makers offer the documents needed (such as database structures and table dependencies) so that you know what goes where and what table needs data from what other table so you can interface rather straightforwardly. Now as far as which version this feature belongs to. Assuming most of your customers already have their own accounting systems. I would put this feature in Version 3.00.000a since again it doesn't represent the majority of your users. In relation to this feature, you could also decide to add compatibility with the more popular accounting systems just a M.Y.O.B. Quickbooks and other widespread accounting systems in lieu of integrating the accounting per se. That all depends on what the market is asking for at the time of the release.
- The pricing history is a quick add-on as I already mentioned. It's a great way to allow to retrieve the situation of a house plan as it was back when it was originally created without needed some kind of esoteric information storage. This table basically has the house plan number, a date, an Item number and a price. When retrieving the floor plan we take it's creation date and search for that date and that item in the table to get the prices of the materials used at the date the house plan was created. Rather simple to do, non intrusive to the rest of the system as far as database structure is concerned. If you do that, you might suddenly thing that this system could work for other reasons in the program too and might decide to add this in more than one place (such as the accounting system when it's implemented for example). This could go in the next revision number (which would be version 1.00.001a).

NOTE: You might have noticed that I didn't release any of these feature under any specific release letter. They are all organized in major, minor and revision numbers. The main reason for this is that in my case, release letters are used specifically for bug fixes. A correction to one of these features, when added, would be in the next release letter if a bug occurs. But to me, any feature should be released as a major, minor or revision level change.

### THE REAL FINAL WORD OF THE SERIES:

And there you have it. We are now at the real end of this series on commercial and professional software development. It's important to remember that this series (all seven parts of it) constitute a typical scenario on what taking a project from the start to the end (and beyond as shown in this last part) is all about. As you can probably imagine after reading all seven parts, there's are many things to consider when developing a big project. You could think of this series as a guideline, if you will, into what the steps are, and possibly what you will need to achieve these steps.

The main rule of thumb as you might have guessed after reading this series is that preparation is the most important part of creating a program. This goes for games, applications, tools, utilities and the likes. Now when you are presented with a project to do you simply have to do that project. But sometimes that project will be an idea you have for a specific kind of program. Hence knowing what the potential customers might want becomes your responsibility to discover. It's not an uncommon practice to have surveys on your website that users can fill out in order to give you an idea of what would work with them. Keep them short so it doesn't take them hours to fill out 10 questions max is a good standard to follow, and keep all of the questions related to the one subject you want to find out about. If you have more than one thing you need to know about, create one survey for each of them. This way people can fill out just the ones that concerns them and this helps you keep this knowledge organized and manageable.

I'm always open to suggestions as I have mentioned many times in this series. Just contact me and ask me questions, give me suggestions if you have any. This whole software development academy project is about teaching you what you need to know. So if there's a given specific subject that I have overlooked int his series or that you would particularly like to know about. Just let me know about it and we'll see what we can do about. And most importantly I hope you've enjoyed reading this series as much as I've enjoyed creating it for you and of course, that you've learned something from it which is it's main intended goal.

Download a copy of this tutorial: Commercial_And_Professional_Development_Part_7.doc

## Final Word

Sure, we claimed QB Express was back in the last issue, but it wasn't until this issue that I feel I can safely say that QB Express is BACK! This is one of our strongest issues ever, and I have to thank the overwhelming community support that we received to put this together. If you guys keep it up, there will be many more issues to come.

Speaking of which...

Issue #24 Deadline: September 1st

As always, we need tutorials, reviews, articles/editorials, comics, news briefs, letters to the editor, and content for the gallery.

Make sure you get all of your articles and content in by September 1st! (It's a short turnaround time, but I'm hoping to get the next issue out before September 10th, which is the day that I leave for yet another crosscountry trip, when I move to Los Angeles...for good! That's right, I'm all grown up. I graduated college and am moving out to LA to work in the Television Industry.)

Sure, I've got a busy few months ahead of me, but I promise I'll make time for QB Express if you promise that you'll help me out and contribute!

Until next time...END.

-Pete

Copyright © Pete Berg and contributors, 2007. All rights reserverd.
This design is based loosely on St.ndard Bea.er by Altherac.