QB Express

Issue #23  ~  August 13, 2007

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

In This Issue



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?

GenreVotesPercentGraph
Yes00%
No1100%
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):


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.

Mighty Line by Lachie Dazdarian Crane by ChangeV
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?:

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

PSET Episode 1

PSET Episode 2

GodObject Comics by sir_mud

godObject 0001

godObject 0002

godObject 0003

Comic by diffeecult

PC Waterboarding

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:

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:

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:


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.

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.

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.


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.

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.

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.


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.

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.

QB Top 50 - The best QBasic and QuickBasic Sites    FreeBasic Top 50