QB Express

Issue #3  ~  October 15, 2004

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

In This Issue

From The Editor's Desk

Written by Pete

Here we are again, the third issue of QB Express in three months. I think this magazine has done something nearly impossible in the scope of QB magazines, in that it has actually gotten bigger with each issue. Generally, QB mags start out big, then gradually decline in size.

And by bigger, I mean BIG. QB Express this month is abot 215KB of text alone! That's a whole lot of text for a QB magazine. And that's not even mentioning all the images and downloadables that are included with this issue too.

This month, we've got some great tutorials that I'm really excited about-- VonGodric's GUI series begins with an article on creating buttons, we've got Chapter 4 of Neo's QBNow!, the first part of a two-part series on QB Scripting by Chaoticmass, and the beginning of a new ASM series by Peter O'Rourke. Then there's the "lost" Mock-3D RPG engine tutorial by DarkDread that is of course phenomenal, (as you'd suspect from someone as talented as DarkDread). Oh, and a little thing on making a "Star-Sky" graphical effect, by VonGodric. And those are just the tutorials!

This issue also has a whole lot more QB news than I have published in the previous two issues. It took me *forever* to gather together all that news for you, absolutely scouring the Internet for the best and most up-to-date QB news. So that's another good thing. Also, Adigun A. Polack has submitted a neat article / ad for his 7-Line Pure Text Screensaver Competition, which could be pretty interesting if you're into that kind of stuff.

Then there are all of my articles... the regular columns. The Blast From The Past! is on the Qlympics this month, and I think you'll like it. There's also a great preview of Saga of the Marshes by Potato in the Gallery section. And I've also got the results to last month's programming competition (The Four Line Game Compo), and a brand spankin' new compo for you this month.

So what are you waiting for? Get reading!

QB Express Needs Submissions

I say it every month, but I can't stress this enough: QB Express needs writers! If I don't have a steady stream of contributions, this magazine is going to die. So if you want QB Express to keep on coming out every month ... and being good ... then you'd better submit stuff!

Whatever you write and submit, as long as it's QB related, will be printed. This is an open magazine, written by the QB community, for the QB community. That means you!

I'm especially looking for peoplet to be regular staff writers, who will write a column every single month for me. Also, I'm looking for people to write opinion articles or editorials, or articles about QB history. I have a lot of people writing tutorials, but not many people writing articles about the softer side of QBasic, from a gamer's perspective, or a webmaster's perspective, or a message forum member's perspective. If you think you have what it takes to rant and rave and argue about QB stuff, or have some fond QB memories you'd like to write about, I especially want you to write!

That doesn't mean I don't want tutorials, though! Anything you've got, send it in! I guarantee you that I can find a place for it!

Okay, enough begging. I'll let you go now. Enjoy the issue!



Letter From Barok


I am speechless.

Never have I read a magazine that was that thorough in its articles... although everything done in it was excellent, the article that sticks to my mind the most would have to be TheGame. Never have I seen that much dedication and determination in a single article. You have truly covered all the angles, from the beginning right to the bitter end. It brings a tear to my eye. I thought I knew alot about Tsugumo and TheGame... and I was dead wrong. Much of what you wrote in that article I did not know... I guess lots of that information was lost. The article was informative and engrossing, and I found that I could not take my eyes off it for one minute to look at other things.

I also thought the QB community to be pretty much dead in water right now... but I was only looking on the surface. Underneath there is much going on, from the Sonic clone to MOORPG You truly know what's going on around here, and I salute you for that!!!

Another article that deserves notice, despite not being as large, would have to be your QB magazines article. It was very well done, and I'm sure if handed in as an english assignment, it would take top marks. It was something you can dive right into without having to force yourself to read. You give a rich and detailed history on the QB magazines that i'm sure no one else could duplicate. Excellent job!

You were completely right about my archive I made back then... You give me too much credit. Credit also goes out to Jason (aka Tek) who gave me the QB Gazette, and um... I guess that's all for now. Unfortunately I can't seem to find all the emails I received during that time period. :( But kudos to the people at QB45.com who pointed me to the editor the QB Times, who then sent me his magazines.

By the end I was growing tired of doing this, and in the end didn't really care about it. I was just too lazy to go and actually download some of the magazines to .html or even to make them as a pdf file. ;) I took the cheap and fast way out.

Oh, and about overlooking the diskmag: I never really overlooked it... I just couldn't get ahold of anyone who had a copy. :D Nothing big.

What you have done is probably more than I have ever done. Most people who have 56k modems probably never gave my archive a second look. I just took what I could find and slapped them together into a zip folder. However, you took the time to organize them, upload them and find more that I overlooked. For that, I salute you!

I wish I could comment on every aspect of your magazine... Really, I wish I could! However, not only would it be very time consuming, but It would end up being as large as your magazine itself!!! So in conclusion, I must say that what you are doing for the community is wonderful, and hopefully it will breath new life into it. I hope all your magazines will turn out as excellent as this issue has.

Wishing you all the best

Thanks for all the great compliments -- they were very flattering, I must admit. I got a huge response from the TheGame article from just about everyone. I guess QB history is a popular subject for the readers of this mag.

On the surface, the QB community does look pretty dead in the water right now. But if you look hard enough, you'll find quite a few projects that are still going on. Certainly there are not as many as there once were, and it takes a whole lot more searching to find QB news these days, but it's still out there if you look hard enough. I am the first to admit that the QB community is declining, but it's far from dead.

Now, about the magazines archive -- I still think you deserve all the credit in the world. Perhaps I've put a bit more work into it than you have, but it was your idea in the first place. If you didn't start the ball rolling, I would have never gone out and finished the deed. You deserve a huge amount of credit. Don't be so self-deprecating, dude! What you did was incredible!

Anyway, Barok, thanks a lot for the great letter! And folks, never fear -- this isn't the last you'll hear of Barok for this issue. Barok also wrote a review this month. Make sure you check that out!


Letter From Gianfranco Berardi (Pigeon)

Hey Pete,

Congrats on making the second issue just as full of good info as the first!

Related to the article "Liberate QB From DOS" by Martin Rampersad, I also found that QBasic works perfectly fine in Linux if you use DOSBox. There is no need to have a FAT16/32 partition either.

In my own tests, I found that my Pac-man clone runs fine in it, flickers and all. B-)

I make use of Gnu Make for my programming (I use C++ mostly), as well as Subversion to do revision control. Perhaps an article on the importance of revision control? If I have the time, I can see about writing an article myself. I'm in grad school and working a lot, plus I do game reviews for GameTunnel.com, but if I do find the time, I will definitely submit an article to you.


It's awesome that QB runs so well on Linux! I've heard some other stories of people emulating old DOS games on their Linux computers better than they can run them on Windows XP. Personally, I think it's a shame that Microsoft's own operating system is less compatible with its older generation OSes than a completely foreign operating system like Linux. In order to run QB programs in Windows XP these days, you have to use emulators like VDMSound, change BIOS settings, mess with EMS options and then use the shoddy, second-rate WinXP DOS emulator "Command Prompt". It's really frustrating, and I think that's the main reason why people are leaving the QB community and new people aren't joining. QB is no longer for newbies with little programming experience, because everything is so hard to run.

An article on Revision Control would be awesome -- too bad you didn't have the time to write it this month. (In case you don't know, Revision Control is "any practice which tracks and provides controls over changes to a project's source code." [Wikipedia.org] It helps you immensely in debugging your programs, because you can check back through the changes and figure out which change made your program screw up.) This would definitely be an interesting and valuable article if someone decides to write it.

If only Microsoft had done a better job with revision control when developing Windows XP... Somewhere along the line in development from Windows 95 to Windows NT to Windows XP, DOS compatability went completely haywire. If the DOS compatability of Win95/98/ME still existed, the QB community wouldn't be dying at anywhere near this rate.


Letter From Acidus


I ran across your website today while looking at some other stuff, and was amazed so see my old Qbasic Developer's Forum EZine mentioned in your QB Express. I'm glad it had such an effect on you. I really enjoyed writing it, and was amazed that it people enjoyed it so much. I've even seen people, such as that bastard Matthew Knight with his QB Cult Magazine, actually take old QDF articles are try to pass them off as their own. Regardless, I hope it helped you.

I read in your Issue 2 about why people have dissappeared from the scene. I think there are a few issues in place here. 1, QBasic is severly limited in what it can do compared to other options today. If you want to do more, but still be using some form of Basic, you have to pay for something like Liberty Basic (is that even still around?), or Power Basic, or some other such thing. And most people using Qbasic are kids who are just learning programming. They don't really want to spend that, considering no one uses the language. Don't get me wrong, I think its very important to have some kind of free, fun language that people can use to learn programming. C isn't bad, but doing graphics in C is a lot more complex that SCREEN 13, LINE (100,100)-(200,300), 7, BF.

I think the QB scene will never develop into what it once was, but I still think that magazines such as yours are very important. If I hadn't had QBasic when I was 15, and all the great online resources at the time. I certainly wouldn't be where I am today: Lead developer of an Open Source Project Stripe Snoop; Accomplished writer with numerous publications in print magazines; and Guest Speaker at various Hacker and Computer Security conferences around the US. Feeding people's curiousity is not only some we should do, its somethingwe *must* do. The 14 year old making his first tile based RPG today in Qbasic, is going to be an inventor or engineer tomorrow. Your magazine is helping that process along, so I salute you.

Keep up the good work.

LordAcidus... well, just Acidus
QBasic Developer's Forum Editor-in-Chief

Thanks a lot for the great comments, man. QBasic Developer's Forum was a great magazine, with some of the best QB tutorials ever written. (It's no wonder that people have stolen them in future QB mags...)

QBasic was my first language, and I still have fond memories of making my first games, and logging on to my first QB websites back in middle school. Those memories are so fond, in fact, that that's the reason why I came back to the scene this summer. I thought I'd try to invigorate the QB community a bit, by redoing my site and starting this magazine. Hopefully it works. You're right that the QB Community will never develop into what it once was, but it's definitely possible to breathe some new life into it with a little hard work. Creating something new for QBers to get excited about again might just get some action to start happening around here again. The "Good Ol' Days" are gone, but 2004 and beyond will hopefully be far from bad.


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!

How long have you programmed in QB?

How LongVotesPercentGraph
Less than 1 year1419%
1 year45%
2-3 years1520%
4-5 years57%
6-7 years1419%
8-10 years1317%
11-15 years57%
15+ years57%
75 Total Votes

One of the reasons why I asked this poll question was to see how the internet QB community has shifted over the years. I asked this same question on Pete's QBasic Site in the month of December 1999 to January 2000, and I got the following results:

How long have you programmed in QB?

It's definitely clear that there are fewer newbie QB programmers out there than there used to be, and a whole lot more "seasoned veterans." This poll indicates that the QB community is not growing as fast as it used to...there's less fresh blood than before.

But it doesn't take a poll to show this -- just visit any message board in the Qmunity, and you'll see a whole lot fewer newbies than in years past. Now the QB board members are mostly older programmers who hang around for the community aspect, and don't program in QB anymore. They've moved on to other interests and other languages, and just come back to the QB sites and boards for the social aspect.

News Briefs

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

QB Site News

QB71 Attacked By SpamBot

On September 16th, Mike Doise's QB71.com site was attacked by a vicious PHPBB forum spambot. The spambot posted over 1000 advertisements through randomly generated user accounts, advertising things like penis enlargement pills and perverse pornography including beastiality and "grandparents" porn. The spam attack came very soon after Frognik posted the following on the QB45.com forum: "OMG. Mike's QB71.COM site is getting more traffic than... Here or QBasicNews.Com etc".

QB71 logosThe malicious posting ended within a few days, and all the spam posts and spammer accounts were deleted. Anarky, a moderator at QB71, reported on September 25th: "The offending posts (and (oops) some of the others) have been deleted. It seems to have stopped now and all I can say is happy posting." The person responsible for the spamming has not been caught, because he/she used a masked IP address. (The pattern of spamming, though, does lead us to wonder if this spammer is the same one who flooded The Basic Network's forum a few weeks later [see below].)

But why would someone spam QB71? Lingering dislike from days past, I suppose. Mike Doise (and thus his QB71 site) are widely disliked by members of the QB community, who blame him for the downfall of QB45, a site which he once owned.

QB71 has fallen into same type of disrepair that led to the downfall of QB45, and recently, visitors have been complaining about the Downloads being broken, and the poor quality of images used on the site. According to board moderator Anarky, Mike Doise hasn't even been online in quite some time, and has not done anything about the poor quality of the site. The site's logo was so poor that Piptol actually stepped up and designed the site a new logo. (You can see the original logo and Piptol's new logo above.) Despite his kindness, Piptol's redesigned logo been taken for granted; the terrible old logo is still up on the site.

Spam-Related Downtime At The BASIC Network

Only a few days after QB71.com was attacked by a spambot, the PHPBB Forum at The Basic Network was also avalanched with spam postings and illegitimate user accounts. Responding quickly to the insurgence of porn advertisements and obtrusive spam, Nekrophidius temporarily shut down the entire site (which is heavily based around its forum).

Basic Network Logo
The Basic Network is home to one of the Qmunity's most active message forums.

Luckily, only a few days later, on September 25th, Nekrophidius resolved this issue, posting the following on the main page of The Basic Network:

"The insurgence of spammers here at the TBN forum has prompted a purge of the user database. We've deleted over 100 accounts that are either obvious spam accounts (usually porn links) or believed to be spam accounts. If your account was deleted in error, please let us know."

The odds that two QB sites were attacked in almost identical fashion within days of each other is too great of a coincidence. Judging by the pattern of the two spambot attacks, it's not a huge leap to assume that the same unscrupulous person launched the attacks on both QB71 and TBN. Many other major QB sites, including QBasic News, QBasic New Zealand, QBasic.tk and Pete's QBasic Site run from PHPBB forums, so it wouldn't be a big surprise if the spammer strikes again within the next month. Here's hoping that this was just a one-time annoyance, and won't become a regular thing.

Dav's QuickBasic Knowledge Base Updated

On September 23rd, Dav did a big update to Dav's QBasic Site, adding new articles to the QB Knowledge Base and adding a few files -- including a standalone version of the QBKB. The Knowledge Base is one of the best resources available in the QB community, with hundreds of public domain QB-related documents that have been collected over the years from newsgroups and BBS boards. Check it out at this address.

QB45.com Progress Update

Recently the head of the QB45 reconstruction project, Fling-master, lamented a bit on the current direction of the site's redesign. In this post, he questioned whether the plan to make QB45 a multi-language site is a good idea, or if they're "setting their sites too high." Here's an excerpt:

"My main problem is that... at least in my opinion... we're setting our sights to high for no reason. We're aiming to make a big programming site for all sorts of languages. Why? Before, during the "Jorden-era" even... those other sites weren't that popular. And they sure didn't get more popular over time. The opposite is probably what happened. So... why are we aiming to make all these sections for C/C++, Perl, PHP etc when they will probably not be used too much? Personally, after thinking about it on and off for the past month, I can't see any good reason at all.

"Personally, If I were to take complete control over this, I would just make a small site with news, a forum, and downloads/tutorials only for Qbasic. I really don't feel like working on a "mega-programming" site at the moment..."

At the end of the decision, it wasn't clear if the decision was or wasn't made to return QB45 to its roots as just a QBasic site. Personally, I agree with Fling-master on this issue-- QB45's best days were when it was "Future Software The QBasic Site" and nothing more.

Project News

Ninja Night Will Be Finished

Ninja Night ScreenshotTheMariuZ recently announced on the QBasic News forums that he will be finishing his entry to the Darkness Ethereal Pure QBasic Mini RPG Contest from a few years ago, Ninja Night. According to a November of 2002 V Planet! article, this game won the Best Graphics, Best Gameplay, and Best Technical Achievement categories, and garnered first place in the entire competition. (That demo release is available here.)

Ninja Night is an action RPG starring the female assassin Scar who is out for revenge after one of her understudies was killed in an odd genetics experiment. The impressive 2002 demo reportedly featured forty minutes of gameplay, as well as very smooth and fluid animation -- quite an accomplishment for pure QBasic code.

According to TheMariuZ (also known as BlueSckR), "I am definitly gonna finish Ninja Night. Ninety percent of the code is done; just gotta make alot of sprites, tiles and write a lot of scripts, and design a lot of levels. I'm going for long cutscenes and an exciting story... It could take a while, but I'll keep you up to date!"

This sounds like a very exciting project, and a lot of QBasic News Forum members were quite excited to hear from TheMariuZ. According to Jocke The Beast, MariuZ is "just like santa...visiting once per year with presents," citing his year-long absence from the QB community.

Barok Releases Beginning Map Editor v2.0

Barok recently released a new version of his RPG map editor. It has several improvements over previous versions, mostly in the GUI department. Here's the description from the RPGDX site where it was released:

Barok's Map EditorI created this while working on my RPG. It's a map editor that has 4 layers (base layer, ceiling layer, object layer, and finally the collision layer. 90% of all the functions in the map editor are contained in a drop-down menu similiar to the windows start menu. You can choose what layers you wish to see, select quickly the layer you want to edit, load new tilesets, load maps. You can also load multiple tilesets, so if you have a seperate tileset for floors than tables, you can load them individually. 95% of all functions are used with the mouse, with the exception of typing in file names and holding the "f" button down to do a flood fill of the map with the selected tile. The editor even remembers what tile you last selected for each layer, so you don't have to reselect the tile when you switch layers. Uses both the left and right mouse buttons."

You can download Beginning Map Editor version 2 at this link.

Neo Deus Ex Machina's New Compression Library

The QBasic News community was impressed a few weeks ago when Neo Deus Ex Machina released a set of very useful compression and decompression libraries for QB, called NeoCL. Both Version 1 and 1.1b have been released recently. Here's Neo's official press release, straight from the Readme file:

"A while ago, I (Neo) needed to have several routines that were able to compress and decompress certain files. I looked everywhere on internet to find some QB routines that could do these things. The only thing I found was that the famous DirectQB library had some simple RLE routines. Since I considered this a major gap in the QB Community (no compression! ;) ) it was a great opportunity for myself to fill this gap, and I started making NeoCL. After being busy with it for almost a year in secret, here it is! By Neo, for the QB Community!

"NeoCL stands for Neo Compression Library. Using this library, people can compress and decompress files for themselves. This could e.g. be used to pack all external files of a game or program in one package, with a compression ratio of about 20% - 80%. But it could also be used when files contain delicate data, as compression is also a good kind of encryption."

The NeoCL package is available here, and more information can be found in this topic.

MOORPG Progress

Since we previewed Z!re's Massive Offline Open RPG last month, another demo of the game has been released. In this post on the QBasic.tk forum, Z!re announced some of the new features in the latest demo.

Here's a list:

  • Added XMS support; ("XMS made it o v e r 200% faster. MOoRPG requires 14MB free XMS memory to run. 8MB for Tiles, 1MB for characters and sprites, 2MB for objects, 1MB for buffering, 2MB for scripts.")
  • MOORPG Logo
  • Added More tiles
  • Added Waves and water effects
  • Added Mod option
  • "Main" quest written d o w n.
  • 14 side quests written d o w n
  • Major optimizations in the core engine, making it a lot faster. ("Strangely, I get ~100'000DFN whereas a computer twice as fast, running XP, only get ~30'000DFN (Higher = Better)")
  • The current version is only 1.8MB, which is less then 50% of the previous version.

The current version, which does not come with source, can be found here.

In other MOORPG news, Z!re recently decided that he will be including members of the QB community in his game as NPCs. In a pretty humorous thred, QBasic News forum members volunteered to have themselves turned into digital inhabitants of the MOORPG game world. Z!re described his ideas for the characterization of several QBers. Here are some of them: "Nekrophidius, you're a holy knight, on the mission to find the lost socks"; "Dark_Prevail, white angel thingy, with a black aura thingy"; "Mech, a tiger with underwear on its head"; and "Barok, a gay canadian lumberjack." Interesting.

Nemesis Releases QuestKit Beta

On September 22nd, Nemesis released a beta version of his new RPG development kit, called QuestKit. This kit allows you to quickly and easily design your own RPG with limited programming knowledge. QuestKit is still far from finished, but you can take a look at the preliminary engine. According to Nemesis: "You can mess around with the engine, doing neat stuff like changing camera size, map size, tile size, and other junk." This engine demo is still in a very barebones state, but if you're interested in RPG programming but don't have much experience, it's worth a look. Check out this thread or download the beta release here.

VisualQB GUI Library In The Works

VonGodric announced last month on the QBasic News forums that he is working on an ambitious GUI library for QB that will allow you to design Visual Basic-like user interfaces in QB. This project, called VisualQB, was described as follows:

"[VisualQB] is a GUI for QBasic, but with a little twist... It is not yet another "fakeos" for QBasic and is not meant to be one. Rather it is a library, which will allow to use GUI controls (such as buttons, menus and etc...) in QB. A scripting language would allow you to create graphical layouts, [and] control some widgets.

"[I am aiming to] create a sort of Visual Basic for QB -- a program where you can define where some button is, how big it is, how it looks like and etc. It will be like M$ Visual Basic -- in the general idea I mean. After you done with the form, the program generates script code for you (that you can modify yourself if you wish)...once it is ready, you can compile it [and use it in your own QB program]."

The official VisualQB site can be found here.

Legend Of Noname General Postponed Indefinitely

Last month, we brought you news that Lachie Dazdarian planned on finishing his dark adventure game, LONG: Legend Of Noname General, by the end of the year. Recently, however, unforseen problems with his monitor (and entire system, actually) caused Lachie to ditch his entire programming computer:

"In case you missed the news on my site my monitor got broke. Since my crap of a PC was giving me problems too I ditched all of it in the trash. LONG ScreenshotTrust me, I share the disappointment with all the people who were waiting for LONG to finally come out. This couldn't happen in worse time because I was ready to throw away my old hardware after finishing LONG and then to take a longer break from the coding and Internet. I would probably throw away that stupid old PC before but LONG was there...calling me. I just had to finish it. I needed a closure.

"What is even worse, I cannot release any kind of demo if, for example, my situation doesn't change in 6 or more months since the current code is not suitable for any kind of release. It's full of dead ends and not compiled. I just don't feel like releasing the code to public, let them butcher it while searching for demo limits in frustration."

This was quite a disappointment to the QB community who eagerly awaited LONG. Hopefully Lachie will be able to resolve his computer problems and bring LONG back into development, but we can't make any promises. For more information, you can check out this thread or the Kentauri site.

Assembly ToolKit Released By abionnnn

Recently, abionnnn (a QB coder who has been around the community since 1997 or so and has a newfound interest in helping keep the Qmunity alive), released an archive of source code and tutorials to help people learn assembler. Here's what he announced on the Pete's QB Site message forum on October 4th:

"I thought a few (any?) people would be interested in the ATK the Assembler ToolKit. Contains tutorials by myself and others, generally directed towards people wanting to learn assembler.

"More interestingly, it contains an implementation of an inline assembler. This initially was a quick 5 hour job when I was bored in a holiday overseas back in 1999. I found an old dodgy computer in Syria and coded up an inline assembler for QB. (it's amazing what you can achieve when you are almost bored to death). Also contained various utilities ranging from a make utility for QLBs with an example (highly commented) mouse library, to a COM to BAS converter. Don't ask about the last one, just ... don't ... ask. =)"

You can download the ATK at this link.

Wallace Software's Contact Impresses

Wallace Software's Contact was brought to light at QBasic News recently. This is an ambitious first person shooter, written in QuickBasic, that everybody should check out. Check out some of the specs, straight from Wallace:

contact"Contact is a first person shooter written in Qbasic, which utilized some of the most popular concepts of the moderm first person shooter. This includes multiple weapons: handgun, machine gun, laser gun, and flamethrower. The levels are designed in such a way that navigating the alien world is easy, but in the same time fun. There is extensive background information provided which makes the game more intersting if you know what the story behind the game is. There is a complicated in-game story line with plot-twists and turns, the story is told through a system of movie scenes that play at certain checkpoints."

Check out this link for more information about Contact, or visit Wallace Software for the latest updates (which have come quite frequently as of late).

Competition News

Hecate's Autumn RPG Contest Begins Tomorrow!

RPGDX.net is famous for its weekend long mini-RPG programming competitions, which were invented by DarkDread a few years back. Starting tomorrow, there is a two day RPG programming contest where entrants must make an Autumn-inspired RPG in any language they so choose (including QB). In fact, in the past, a lot of entries to RPGDX compos have been written in QB, and the site is actually managed by a lot of former QBers (like Mandrake). Here's a snippet from the official rules:

  • Environment requirement: Needs to look like Autumn.
  • Technical requirement: Have some leaves falling down from trees. Technical bonus for having interaction between characters and the leaves falling or on the ground or wind blowing them around.

This contest begins at midnight on October 16th, which is TOMORROW. You've got to register for this contest by midnight, so hurry up! The official site for this contest can be found here!

Rob Vonk's BASIC Programming Challenge

Rob Vonk has been running a BASIC Programming Challenge since July 1st over at this address. The challenge is to make the best game you can in pure BASIC code...but you may use any dialect of BASIC you'd like. So far, there have been three entries: two written in Commodore 64 BASIC, and one in Sinclair ZX Spectrum BASIC. However, QBasic games can also be submitted, so get to work! Let's have a QBer win this contest! (Entries are due by the end of this month. After that, a month-long voting period will open up to decide the winner.)

The QBNZ Frozen Bubble Clone Competition

Frozen BubbleOracle's latest QBasic New Zealand programming competition challenges QB programmers to make a clone of the popular Linux game Frozen Bubble, or any game that uses the same concept (such as the mega-popular Bust A Move series). This competition is very open, allowing programmers to use as many or as few libraries as they'd like. Entries do not have to be graphical, and people can enter as many times as they'd like. And you also have a huge time window for this competition; entries are due by Christmas Eve, New Zealand time. For more information, check out the official rules or this thread on the QBNZ forum.

'Use Each Keyword Once' Compo Results

Last month's QBNZ was pretty interesting. It challenged programmers to make a program where they can use each QB keyword only once (so just one IF...THEN, one DO...LOOP and one PSET and one PRINT statement, for example). There were a total of six entries, and Oracle posted the final results on September 17th:

"I've finished marking the QBNZ compo 0204, and we have winners! Well done to Joakim for his entry, Stack-based Scripting Language getting first prize! Second prize goes to Frognik for his "Matrix" demo, and third to HQSneaker for his binary-denary converter. A big thanks to all those who competed :)"

The next QBNZ challenge is the "Frozen Bubble Clone Competition", which you probably just read about. :)

Nekrophidius Discusses 2004 Gaming Golds
The 2004 edition of V Planet's Gaming Gold Awards is coming soon, and Nekrophidius (the new chief editor of V Planet! -- though he hasn't really done much yet), has revealed some information about the upcoming contest:

Gaming Gold Awards"Since there was no 2003 awards, I am considering allowing all games from 2003 and 2004 to be entered. I think it's only fair, since there wasn't one last year, and a lot of great stuff came out last year. The judges are the Qmunity itself. The competitors are chosen by public nomination, so if you're nominated, then you have a chance of winning an award... It's still awhile away so more details need to be ironed out, but I'll keep everyone posted."

This comment was posted in late September, and we haven't heard a peep on the Gaming Golds since. Hopefully the contest (and the oft-delayed V Planet! redesign) get released soon. QBers are frothing at the bit, Nek!

SonicX Level-Making Competition

JO, the developer of the long-running Sonic The Hedgehog clone, Sonic X-treme, has started a level-designing contest to help build up steam for the November release of the game's final version. He announced the contest at the QB45 message boards a few weeks ago:

SonicX Map Utility"SonicX, the long running qb project, is now subject to a competetition whereby the winners can win Gmail invites. Basically, the best six levels that are created using the sonicX level editor over the next two weeks will garner their creator an invite. I want to see some levels by cool Qb people, even containing qb characters if possible. The levels can include bitmap images, and an extensive tutorial points out how to do this nicely. Check out the tutorial at this link."

In other news, JO's site, QBasic Central, has had a major redesign in the last month. Check out this site, and make sure you enter the level design contest!


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!

Saga of the Marshes

Although RPGs are by far the most popular genre of QBasic game (and have been for years), the tactical RPG genre has been almost entirely overlooked by the QB community -- until quite recently. In fact, Potato's Saga of the Marshes is the first serious attempt at a strategy RPG in QBasic that I have ever seen... and it's looking good.

Potato (David O'Trakoun) has been quietly working on Saga of the Marshes since the summer of 2003, working to design what may be QBasic's first complex tactical RPG battle engine. Using a grid system similar to that found in Final Fantasy Tactics (PSX), Shining Force Gaiden (NES), or Fire Emblem Gaiden (NES), his three main inspirations, Potato has crafted the basis of what may be QBasic's most ambitious RPG battle engine to date. Promising impressive AI, and complex strategy elements that are compounded by the various environments you will battle in, Potato's battle engine is on course to set a new standard for RPG battling in the QB community.

Saga of the Marshes 1
A demo of the Saga of the Marshes battle engine. The blue area is where your character can move to during this turn.

Since July 18, 2003, Potato has released four demos of his game engine, each getting progressively better. Though the engine is far from finished, it is really starting to have the right "feel" to it. Fighting off enemies and moving around the map is already an intuitive process, and the in-game menus are very well-organized, complete with colorful icons representing all the different actions your character is able to perform. With a few more tweaks, some improvements of the AI and the addition of some missing elements (like magic spells, for instance), SotM could be one of the best-playing QB games ever conceived.

I asked Potato to list off some of the upcoming features of the SotM battle engine, and he listed off some pretty cool and innovative stuff:

Pretty neat stuff, eh? Of course, there's a lot more that will be going into the final product, but you'll have to stay tuned to find out.

SOM2One of the aspects of SotM that really impressed me were the graphics. Potato has demonstrated a keen sense of style and taste in both his web design and pixel art, and it really shows through in Saga of the Marshes. The anime-inspired chibi graphics really fit the game well, as do the menu icons and the simplistic but attractive environmental graphics that have been used in the first four demos. The graphics on the world map outside of the battle engine are also attractive, though they were clearly not as well-developed in the demos as the battle graphics. Obviously, many sprites have not yet been completed (that's why those black boxes with red slashes are in place), but the artwork so far has proven to be quite good. I personally can't wait to see the oversized boss sprites that will be in the final version.

SOM2The story of Saga of the Marshes is very highly developed, and quite interesting. Some of the characters in this game are based on the novel Outlaws of the Marsh, as well as a lot of the game world, but the story is completely original. Rather than summarize it for you, I thought I'd just include a brief teaser / introduction summary provided to me by Potato himself. It's quite interesting...far better than the "save the princess and/or world" plots of most QB RPGs.

Check it out:

Five gods divided the Earth equally amongst each other. The Swamp god was poisoned by the fumes of his property and began a series of events that lead to a great war. The gods attacked one another, excluding the Swamp God, whose connection to the events could not be traced.

The Land God sent his human forces to attack the mountains first, since they lead to the sky, which leads to the sea. To get to the Mountains, one must traverse through the Poison Marshes. Advanced technology, available only in limited quantities, was needed to breathe in the swamps. The General Zhuang, directed by the King of Daishan, led a small force into the Swamps.

A scout runs to the Red Marsh village, and tells everyone of the approaching army. He was unaware of the bandits around the village, though. The bandits from Xiejing's Seep were planning on stealing some food and gold. When they see the armor wearing Daishan soldiers approaching they attack. In the Poison Swamps, only the Infinity Army wears armor, and the army had been pursuing the bandits for some time.

General Zhuang commands a charge, and the bandits and the village are attacked at once. The bandits, villagers, and the Daishan soldiers battle for hours, and then the soldiers are defeated. Rad Edish, the main character, was knocked unconscious in this battle and was taken prisoner by the Daishan soldiers in their retreat.

Rad was secretly a member of the Xiejing Bandits, so the bandits and the villagers collaborate on how to rescue him. When Rad wakes up in the prison of Daishan Castle, he begins learning startling things. He obviously gets out, but how? And will he go back and defend the Swamps? Will he get revenge against the Daishan Army? Or will what he has learned lead him into doing the unimaginable? It's up to you!

SOM2Potato's also done a thorough job of describing the major characters in the SotM story. You can find these character synopses here. Judging by the plot summary and the character descriptions, this game hopes to have something that few QB RPGs have: a character-driven plot. If the story of SotM lives up to the potential I've seen so far, this game could be absolutely gripping.

When asked when he would finish Saga of the Marshes, Potato said that "at this rate, I might be finished by the end of next summer, before I go off to college." As far as I'm concerned, next summer can't come soon enough!

Visit the Saga of the Marshes official site for demos and more information.

Dies Irae Remake Review

Reviewed by Barok

"A game with a horror theme; take on a terrorist that threatens a highly flawed society. Fight against the forces of the undead, sadistic war machines, and the terrifying leader of it all -- Dr. Biose Sinclair."

-- I Am Har Har Har

When I first downloaded the original DI, I thought it was simplistic but fun. It was a well balanced game and quite fun to play. I just wish I could say the same thing with the remake… However, I Am Har Har Har seems to have gone in the totally opposite direction.


Over the past few weeks, Jonathan had been having disturbing dreams at night. Floating out in space, he would speak to a figure that shone bright white. It would ask him to help take over the world. When he refused, the thing struck him down with a horrible sword. Then he would wake up.

At school, one morning, he resolved to tell his friends about the dreams, but never got the chance. Mr. Butler, the band director, called them up and told them about a motivational speaker. A man named Dr. Biose Sinclair.

The man was rude and seemed self-absorbed. He talked until Mr. Butler literally kicked him out of the band hall. Jonathan, meanwhile, was asleep, talking to God in another dream. God told him exactly where to go, that he would find all the information he needed to have Dr. Sinclair arrested.

Though the game seems to be different, deep down it’s merely about a kid that has to save the world. Like most rpgs, the kid is also unaware of what he’s going to do until later on. However, the game throws in a few interesting twists along the way that helps keep the story fresh.

The game also attempts to add some humour throughout the story, but with limited success. All of the humour is forced, and thrown in wherever there’s room. At most, the game will make you chuckle.


I Am Har Har Har put a lot of work into remaking the graphics and it shows. The tiles aren’t just shapes that are filled in with the appropriate colour. I Am Har Har Har put a lot of detail into the tiles. You can see the cracks in the walls. Grass looks like grass. The trees aren’t just gradient cones… I Am Har Har Har truly put a lot of effort into making his game eye candy. Unfortunately, despite all the effort put into the tiles, the game still looks very tiled. In the forest, everything looks quite square and rectangular… opposite of what a forest is supposed to be like. The graphics further on in the game aren’t as well made either. It is easy to tell the I Am Har Har Har didn’t put as much effort into them, as there are fewer tiles further in the game, and the tiles are of less quality than the previous ones. The playing area isn’t quite full screen either. The playing area takes up over ¾ of the screen, which is fine… except that the playing area is in the top left part of the screen. It doesn’t effect playability or anything, but it takes makes the game look unprofessional. Another flaw is the flickering. Although it isn’t as big of a deal, at least once a step the player will flicker. Although it doesn’t flicker much, it keeps the user from immersing himself in the game.

The battle graphics were well done as well. I was very impressed with the battle graphics at the start. The enemy has an attack frame, hit frame, and dead frame. Sadly there is no animation, but it is something we can’t very well expect. Further on in the game though, the monster graphics become sloppier. The graphics aren’t as well pixeled and the frames are cheap, as they are usually the same thing as the standing frame, with a few additions such as explosions or glowing.


The controls for this game are simple. Use space bar to talk to people or inspect objects, esc to bring up the menu, and the arrows to move Jonathon around and to move around in the menu. Unfortunately, I Am Har Har Har used possibly the worst command to do all this with: $INKEY. Navigating maps is made much too difficult, as holding onto the direction you wish to go will move you one tile, stop for a second, and then move you in that direction until you let go of the key your pressing. This makes it much more difficult than it has to be when maneuvering tight spaces, which are everywhere in the game. Inkey works fine in the battles and the menu, but unfortunately it doesn’t work well anywhere else. Besides that, controls are good. They’re simple and easy to use.


The most unplayable aspect of the game is the Gameplay. In concept, it is quite good: battle enemies, gain EXP, and then spend the EXP on the areas you wish to bring up. (Want to be a magician? Spend your exp on magic points and intelligence. Want to be a berserker? Spend your exp on strength and HP.) However, this was executed most horribly. The amount of exp needed to up stats is static, so the amount of exp you will need to spend will not differ from the first time you upgrade that stat to the 20th time. For HP and MP this is quite good, but for everything else this is quite terrible. You see, to bring up your HP stat, you need 250 exp. And then to bring up your Strength stat, you need 3000 exp. This is a 1200% difference in amount in exp needed. And when your fighting monsters that can take out half of your hitpoints in one strike, this can be the game’s death kiss. At the beginning it is virtually impossible to raise anything but your hp, and when you are fighting monsters that give you only 20 exp at a time, it can be very frustrating. Perhaps this wouldn’t be so bad if the monsters stats were more reasonable and the maps weren’t so much of a labyrinth. Sadly, monsters are incredibly strong and maps are completely maze like, which makes it impossible to get anywhere in the game.

Other than that, gameplay is pretty much what’s in every linear rpg, there’s really nothing else to talk about.


Maps were poorly designed. They’re basically "move from point a to point b." And what more, you must follow a set path. No shortcuts. You can’t explore. You can’t wander off the path, so the maps can’t even be declared mazes. Think of them as winding tunnels. This makes moving around very monotonous. Combine this with constant battles, bad keyboard handling and we have a sure loser here.


The battle system is fashioned off of dragon warrior. You see the enemy in the middle of the screen and you have a menu at the bottom. There’s the classics: Attack, defend, item, magic and run. There truly is nothing to it. Sadly, abilities don’t help very much. Healing won’t heal as much as the enemy can do, and the attack spells won’t do as much damage as your regular attack. Using items are the same way. The healing items won’t heal as much as you’re going to take, with the exception of the rare item that will heal every stat. Defending is pointless, as there is only you in the party, and most of the time you fail to run. This turns the game into a "you hit me and I hit you." After five minutes into the game I just held down to space bar when I got in battles and hoped for the best. Whenever an action was taken, the top and bottom parts of the screen would flash red, blue or white. Quite frankly, this annoyed me more than anything in the game.


Sadly, there is none. Sound and music would definitely of raised the quality of this game.


This game has potential. Honestly, I like the leveling up system. Sadly, I Am Har Har Har didn’t take the time to test his product and polish it. This should be an example for all programmers out there what NOT to do. I Am Har Har Har called this "A true console style masterpiece." Sadly, This is far from the truth. Besides graphics, this game has no polish. On top of that, the game is so badly designed that it has been rendered unplayable. This is truly the example that should be shown to people that graphics do NOT make the game.

Score: 6/10
"Good in concept, but poorly executed."
Da Good:
  • Different leveling up system
  • Graphics
  • Cut scenes
Da Bad:
  • Keyboard handling
  • Flickering
  • Unbalanced battles
  • Unbalanced leveling up system
  • Forced Humour
  • Poorly Designed maps
  • No sound or music

Download Dies Irae Remake here.

Blast From The Past!

Written by Pete

Every issue, we take you back in time to the early days of QB on the Internet and show you a lost relic of the QB Community. This is your Blast From The Past!

The Qlympics

Back in early 1998, the QB "community" was a collection of random Geocities and Xoom sites with one thing in common: The QBasic / QuickBasic Top 50. At the time, the QBT50 was the most-visited QB site on the Internet, and it became the hub of that connected a diverse collection of QBasic programmers. Through the QBT50, the dozens of small outposts of QBers, communicating through their own Server.com DiscApp Discussion Boards, and little mailing lists, came together.

The the early months of 1998 were when the QB community as we know it really started to come together. This was due in no small part to the competition and sharing of information on the QBT50. In fact, through the help of the QBT50, the first universal, successful QB message board sprang up: The NeoZones QBoard. The QBoard became a shared forum between Tek's incredible NeoZones Productions site, and the wildly popular QBT50, which both directed traffic to the same Disc.App board. (It's gone now, but that famous board was here. Still, though, Archive.org has some back-ups of it.)

Qlympics logo
SonicBlue's logo for the incredibly successful 1999 Qlympics.

Anyway, one day in the Summer of 1998, Steve Martin, the owner of the QBT50 and some of the people at the QBoard came up with an idea for a QBasic programming competition. It would be called the Qlympics -- for "QBasic Olympics", and it would allow QB programmers to engage in friendly competition to see who was the best programmer in the QB community. Steve envisioned a competition that would let all QB programmers enter, and then after a preliminary round, allow members of the QB community to vote and decide the winners. As V Planet! once wrote, "[They] would choose the best among these projects and hold them up proudly for all the world to see, as if to say 'These QB projects currently represent the standard for which all QB games now and the future will be judged.'"

So Steve went ahead and organized the first Qlympics through his QBT50 site. In June and July of 1998, he allowed QB programmers to submit their programs in a whole slew of different categories, ranging from "RPG" to "Business" programs. The response to the first Qlympics was phenomenal; there were dozens of submissions over the two month block, and QB programmers went all out to submit their best work.

Take, for example, Danny Gump of VirtuaSoft. He had quite a bit of anxiety developing the demo of his RPG The Mystical Journey to be submitted to the 1998 Qlympics. Check out this news post from VirtuaSoft:

Jun 18th, 1998

The demo to The Mystical Journey has been finished ahead of schedule! Try it now. Give me any feedback and help me with bugs because when I enter it in QBT50's Qlympics, I want it to be the best I can possibly make it!

Danny went on to submit several other programs to the 1998 Qlympics, including something specially named for the competition -- Ripple: Qlympics'98 Edition.

The preliminary rounds of the 1998 Qlympics went quite smoothly, but when Steve Martin opened up the polls for voters to rate their favorite programs, he neglected to prevent people from voting multiple times. And unscrupulous people had no problem abusing this security flaw in order to weight the vote for their favorite program to win. Cheating was rampant in Qlympics 1998, and it led to many upset victories. For example, Angelo Mottola's heralded Wetspot 2 game, still considered one of the greatest QB games ever, lost to Pantera55's crude-looking RPG, Elysian Fields. Elysian Fields was a decent game, but it was nowhere near the caliber of Mottola's masterpiece. As Danny Gump reported in VSNews, "Qlympics '98 has not only been considered the 'biggest QB programming competition of all time,' but it has been dubbed the 'competition with the most cheating of all time.'

Despite the cheating, the first Qlympics brought together the QB community in a way that nothing had before. For the first time, there was one common goal among all the QB programmers from all the different sites and message boards around the Internet -- to win gold at the Qlympics. Everyone eagerly awaited a follow-up to Qlympics 1998.

After the cheating debacle of the '98 Qlympics, Steve Martin decided to let two other QBers take control of the next year's Qlympics. SonicBlue (Eric Schneider) and VirtuaSoft's Danny Gump stepped up to the task, and SonicBlue completely renovated the flawed voting system of the previous year. The December '98 issue reported the upcoming sequel:

The second Qlympics is here. And now the competition is better than ever! Last summer's Qlympics contained so much cheating that the results were skewed to the point of disaster, so this year, one must vote for EVERY program in a category or the votes will be discarded. This will let every program in the category get an equal number of votes so people can't vote for one program hundreds of times.

Entries were accepted from December of 1998 until February 0f 1999, at which point voting was opened up to the public. SonicBlue and Danny Gump also allowed all the programs that were entered in the '98 edition of the Qlympics to be resubmitted in the 1999 contest, in order to make up for the skewed voting in the original. (The official Qlympics 1999 website is still available in archive form right here.) In the end, over 200 programs were entered in the 1999 competition, a number unrivaled by any QB contest before or since.

Voting went much more smoothly in the 1999 Qlympics, because of SonicBlue's protections, but there were still a few problems. In the April '99 QB:TM, Zkman reported:

"Before we get to the leaders as of yet for the 1999 Qlympics, a special message from our main man Sonicblue: Despite how clever you might think you are, Sonicblue *is* noticing people who vote themselves a 10 and everyone else a 1, or vote multiple times. So you might as well not try. Also, if your votes seem to be an attempt to cheat, all of your votes will be discredited. Sonic is working hard to avoid the mistakes of last year's competition and make this one the best yet."

In April, the final votes were tallied (by hand, of course), and the results of the 1999 Qlympics were made public. The results were pretty consistent with what was expected; the winners included SB Bricks for best Action game, Wetspot 2 for best Adventure game, Dark Ages for best RPG, and The RPG Developer's Kit for best Library. You can find the full results in QB Times Issue #3.

After the huge success of the 1999 Qlympics, everyone expected a similar success in 2000. But what actually ended up happening was anything but a success.

In 2000, Tek of NeoZones Productions, the most popular site in the QB community, decided to chair the annual Qlympics competition. The one and only issue of QB:TM's "spiritual successor" magazine, Razor Diskmag reported the following on the 2000 Qlympics in its December 1999 issue:

Qlympics 2000 is here!

Thats right, the Qlympics have come around once again and its time to get proggin'!

This year, Tek from Neozones is our host for the event and the entries will be assessed by a panel of judges made up of a few well known figures in the QB world. This is good in someways as it will be much more fair than the old system where everyone could vote...and vote...and vote.

The deadline for entries is February 2000 and there looks to be some quite fierce competition out there including ProjectRT in direct competition with the rumoured RT Killer by Pasco and the team.

Tek introduced the new judging system, created a new section for the Qlympics at NeoZones and created a new category system for the entries to be judged, which he announced in the QB Gazette:

The 2000 Qlympics was all set to be a huge success, except it was mismanaged. It was not promoted enough, so there were not enough entries. Then, once the submission deadline was over, Tek slacked off and never organized the judging panel. The whole event just kind of fizzled out, and people forgot about it.

A lot of people lost confidence in the Qlympics after the failure of the 2000 contest, and the competition which once brought the QB community together now divided it. While the NeoZones crowd feigned confidence in the Qlympics and enjoyed their monthly "CodeX" challenges, the upstart QB gaming magazine V Planet! and Jorden Chamid's Future Software teamed up to create a competing competition: the QuickBasic Gaming Gold Awards. When it was clear that the 2000 Qlympics wasn't going to work, Vance Velez ran the successful 2000 Gaming Golds. The Gaming Gold awards went on to have a successful run in 2001 and 2002, and after a year of abscence in 2003, the new V Planet! editor, Nekrophidius claims that the Golds will be back this year.

Qlympics 2001 logo
The 2001 Qlympics hosted by QBasic News never really got off the ground.

But the Qlympics weren't quite dead yet. Out of the blue on September 27th, 2000, Magus, the head of the fairly young QBasic / QuickBasic News posted the following:

"For all you remember Qlympics 99 by Sonicblue Productions, QQN is thinking about starting one very soon for the 2000-2001 period. I will let you guys know about the progress. If this works out submissions will start in a few weeks then voting will start in 2001. This hopefully will be a very big event. Please post in the forum your thoughts about this."

On October 20th, Magus opened the official site for the 2001 Qlympics, and allowed people to begin sending in submissions. The following day, V Planet! ran an article about the competition.

QBasic News accepted entries all the way up until December 31st of 2000 (and got some pretty sweet entries, I might add), but then once the "voting window" in early 2001 was supposed to start, nothing really happened. It seems like the 2001 Qlympics just disappeared from the face of the planet...

And so that's the glorious and tragic history of the Qlympics, a competition which had its ups and downs, uniting the QB community, then later splitting it apart, bringing false hope, but having some incredible shining moments. Despite the failure of its final two runs, the first two Qlympics competitions are still remembered fondly by all who were around to share in their excitement. On DarkDread's How QB Are You? quiz, he made sure to include two questions about the Qlympics, indicating that participation in this competition truly characterizes you as a true QB coder.

Qlympics Award Qlympics Award Qlympics Award
Buff's three Qlympics awards, still proudly displayed on his site.

Qlympic medals from 1998 and 1999 are a treasured prize among QB programmers. Even mediocre placement in one of these competitions is considered an accomplishment, just because you were there and programming way back then. (Check out the Jungle Warrior page, where Jerm announces "this game picked up 10th place in the 1998 Qlympics." Fred Buffington, or Buff, as he's better known, still proudly displays his Qlympics 1998 Silver Medal and 1999 Silver and Gold medals on his site.  And I'm sure he will for years to come.  I know I would.

The 7-Line QB Text-Based Graphical Screensaver Challenge Is On!

Written by Adigun Azikiwe Polack

Originally and officially launched by me in the "Challenges" section for the QBasicNews.com forums on Sunday, September 19, 2004, this *very* special challenge will give you quite a heck of a workout since Relsoft's unforgettably successful 9-liners! Here's why.

First thing: for this very original mini-compo event, the only mode you are to use is plain-ol' Screen 0 text mode of any kind you like (and not graphics mode like Screen 13 and all of that sort of thing!). Secondly, you can use any version of QuickBASIC/QBasic you want!! Third, you have no more and no less than just SEVEN (7) lines of QB code to come up with a fresh and inspiring text-based graphics demo/screensaver using ONLY these following four characters:


And fourthly, I will DEFINITELY show you some amazing pics from some of my own original demos of this kind I am talking about that show you what absolute awesomeness can be achieved in just seven lines of Pure-QB code, *without* any libraries whatsoever:

7-Line #1 7-Line #2 7-Line #3

Fifth and finally, here is an example of what code you are to put down that will really help you in my mini-compo here:

In as many remark lines ( ' ) as you want (which do not count as a single line), you are to name your own demo......

'Chilly Ice Plasma in 7 lines

.........who you are that is writing it.......

'by J.Z. Ramlock

.......and a brief description of what your thing is about.....

'This original work is a plasma-based text demo
'that simulates pure coolness in such icy
'colors.  Enjoy!  ;)

......THEN you can go ahead with your seven lines to show me what you got! See, it is *that* easy!! ^_^ !

For Official Rules on how to enter this mini-compo, please check in right here. And, the deadline for this most important event is November 1, 2004.

So, what are you waiting for anyway, hmmm? I want to see some AMAZING 7-liners from you, so get right on coding on it today while you have a sure shot!!! ^_-=b !!

Adigun A. Polack
Adigun Azikiwe Polack
One of the Founders of "Aura Flow"
Continuing Developer of "Frantic Journey"
Current Developer of "Star Angelic Slugger"
Webmaster of the "AAP Official Projects Squad"

Four Line Game Competition Results

Organized by Pete

The Challenge

Last month's competition was to make a complete QBasic game using only four lines of QB code. In case you can't remember, here's a quick review of the rules from last time:

Your challenge is to make the best possible QBasic game program using just FOUR lines of QBasic code. Games may be of any genre or style, but players must be able to win or lose depending on their input (otherwise, it's not a game).

For this challenge, you may link as many commands together as you can on a single line, using colons or otherwise. Since QBasic is limited to 256 characters per line, your game is limited to a maximum of 1024 bytes of code. You may not use any libraries or external files with the exception of the standard QuickBasic library, QB.QLB. All games must be compatible with QBasic v1.1.

The Entries

Though there were not as many entries as I had hoped, I was quite happy with the quality of the programes entered, considering the severe limitations of this challenge.

There were a total of three entries:

  1. "One Line Text Adventure" by Thomas Nixon
    You, A loyal servant of the king have been sent forth to find a rare and mystical rock in the cave to the South-East of the City.
  2. "Remmy Racing" by Martin Rampersad
    You are Alex Speedly. Race for your fans! Tap the arrow keys to avoid walls as you race down the twisting racetrack.
  3. "BreakBox" by Shudda (Seth Jones)
    You are the yellow box and have to avoid getting hit by the constant avalanche of white boxes.

You can download all three entries in one zip archive.


The winners of this challenge will receive an award image that they can place on their websites. As of right now, they have not been made yet, but I hope to get them created with in the next two or three days. Until then, you've just got bragging rights, heheh.

First Place: "Remmy Racing" by Martin Rampersad

Although your car is just a one-pixel column of red dots, and the speedway is represented by two randomly-shifting white barriers, this game is quite fun and challenging. There's quite a sense of speed, and I actually got a bit nervous about hitting the edge as I tried to beat my own high score. This game is a lot of fun, and well-deserving of first prize. And besides, what other game lets you play as the great "Alex Speedly"?

Second Place: "BreakBox" by Shudda

BreakBox is a pretty fun game, where you have to dodge falling boxes. This type of game has been around forever, but it's still quite fun. It reminds me of some old "Game 'N Watch" games I had when I was a kid. If you jack up the speed of this game, it does get quite intense, and you can always challenge yourself to beat your own high score.

Third Place: "One Line Text Adventure" by Thomas Nixon

Thomas Nixon's one-line text adventure has a nice look to it, with brightly-colored commands, and a pretty challenging tunnel maze at the end of the adventure. This game actually uses only one line of code (which somehow exceeds the 256 character limit), and still manages to pack in a pretty significant amount of gameplay. This is also the only game of the three that really features a story, though it was a very simple plot. This text adventure is pretty standard fare, with very abbreviated descriptions to save space.

Upon further inspection, I found that this program uses a pretty neat trick to get around the 4-line limit: it creates a new .bas file called "game.bas" every time it is run that actually has twelve lines of code. This is a very sneaky work-around, but it technically does not violate the rules. For ingenuity, I'd have to give Nixon first place (what a great idea), but as far as gameplay goes, this text adventure is the least exciting of the three entries.

So there you have it -- the results of the first QB Express programming competition. Now scroll down to read about this month's challenge!

"Text Map Game" Competition!

Organized by Pete

This month marks QB Express's second challenge. After the lack of interest in the first challenge, I considered discontinuing this feature...but I was convinced otherwise by visitors to my site. So...here we go again!

The Challenge

Your challenge is to create a text-based game using only one "map" made entirely out of text. Your game must have a character (represented by the DOS smily face character) that you move around with the arrow keys. Everything else is up to you.

This challenge was inspired by one of my personal favorite QB games of all time, Virtual Pimp. This game was made by Mike Kristopeit and Eric Boudreau, and uses a very simple text-based interface. It is a crime-based RPG, reminiscent of Grand Theft Auto and all those economy simulating games like Drug Wars. It's got very addictive gameplay, and a lot of cool random events that keep things interesting. Your game doesn't have to be anything like this; the only thing it has to have in common is that you play as a smily face on a text-based map. Other games that have used text graphics in a similar style are: Jocke The Beast's Dark Woods 2, Lost 2 by Groovy Concepts, P.R.O.G.U.E. by Dunric.

Entries will be due on or before November 10th, 2004 at midnight, Eastern Standard Time. Results will be revealed in Issue #3 of QB Express, which will come out on or around November 15th.

Here is a complete listing of the rules for this competition:


Visit the official message forum for the latest information on this competition. This is where you go to ask questions and chat about your progress. You can also check here to get updates of when entries are submitted, and how the judging is going.

Good luck!

Send your entries to pberg1@gmail.com!


By Matt2Jones

Matt2Jones returns this month another "Bobby The QBasic Maniac" comic! Warning: This comic contains strong language.

Bobby The QBasic Maniac Comic

Bobby The QBasic Maniac Comic

Programming GUIs In QBasic
Part 1: Buttons

Written by VonGodric

I am very pleased to announce the beginning of this new series on Graphic User Interphase programming in QB by VonGodric. If you've never heard of VonGodric's ambitious QB GUI project, WizGUI, you should definitely check out the two official sites: here and here. As you can tell, VonGodric is definitely up to the task of writing tutorials about QB GUI programming.

This tutorial series is for anyone that wants to create more intuitive and attractive user interfaces for their QB games or utilities, people who are interested in making mock OSes, and for anyone that wants help designing a more user-friendly interface. Future editions of this series will appear in QB Express, so make sure you stay tuned!



Hello to everyone who reads this tutorial! Before you read on, please be patient with my bad English –- but I’ll try to do my best. As the title said it’s about GUI (Graphical User Interface) programming in Qbasic. I want to try to start a small series of tutorials on this subject starting from basic widget controls and move step by step to more complex GUI object such as Windows.

What is This GUI? GUI programs basically interpret scripted information about dialogue boxes, button placement and where to place text and images. So define how program looks like and helps to make it more interactive for the user. For example Windows’s Explorer is also a GUI. Whole Windows’s graphical environment is. For what is this? GUI or even more UI (just user interface) is a very important part of every program. Let’s say you have a game written in Qbasic –if it lacks of good and acceptable UI then most people would just throw it aside. Even if your’ game actually was good.

In this tutorial I want to show the ways creating and maintaining GUI routines rather then just tell step by step what needs to be done. It is because there are so many different ways to create GUI system that I don’t know which the best is. Even more one way is better for one thing while another is better for the third thing. I also don’t say that the logic here presented is the best/fastest/slowest/worst or whatsoever. I’m sure there are always better algorithms and ways of approaching things, but if you know of some such way then please tell me too.

In the first part I’ll try to explain and show the basics of the Buttons. This will be the subject of the first part. Once you have read through it I hope you will be able (or if you already are then perhaps better) understand (or flame me) how to create button-handling system. Also with this tutorial comes GuiQB.bas file on what I demonstrate what I write here. It runs (at least for me) on any qbasic(including Qbasic 1.0/1.1) though for never versions you need to start with QuickLibrary. For qb45: "qb /l qb.qlb" and for qbx7.1: "qbx /l qbx.qlb"

To understand this tutorial you don’t need to be any real expert in Qbasic (because neither am I), but you need to understand the fundamentals. You need to know and understand how work such things as: variables, arrays, user defined types, functions and subs, Qbasic sentence structures (such as IF...THEN and SELECT CASE) and cycles (DO LOOP, FOR NEXT) Well let’s start then...

Part 1 – The Buttons on your screen...

I’ve been coding and trying to find ways to code GUI’s for some time now. And I have come to realisation that there is no such thing as THE ONLY way to do buttons. But most of the ways come down to very simple "rules" if you say so.

Every button has its coordinates on the screen and every button has a special ID (identification). I will not try to explain such a thing how to create a simple "One-button" system, but rather automated button subroutines what are able to work in "multitask" with your own code. So if button exists on the screen, you can do whatever you wish to do, let’s say until button (for example Cancel) is clicked. And of course to create let’s say 2000 buttons at the same time on the screen. And yes it is possible! For example, they can make RPG battle engine mouse-clickable, or create a navigation panel to read through disk magazine program.

So Let’s start. The most simplistic button is probably simply mouse driven. To do so, there is simple system how it works:

1.   Check if Mouse is over the button or not.
2.   Check if Mouse button is pressed or not.
3.   Return value into certain variable (I call them event-flags).

The button routines what I’ll try to explain (and the source code what I put with this tutorial) allows: To have theoretically unlimited number of buttons (one limiter though is available memory), disable / enable buttons, read different button status’s such as, if mouse is over the button or not and etc, and create and delete buttons.

So let’s start with button data. Every button has some data it needs to have to function properly.

TYPE ButtonData
   Xpos   AS INTEGER
   Ypos   AS INTEGER
   Xsize   AS INTEGER 
   Ysize   AS INTEGER
   Status   AS INTEGER
   Style   AS INTEGER
   Caption   AS STRING * 10

These variables mean the following:

Buttons X Y

To give you the idea of what Status is, then here’s status table:

Button Status table:
  0  - Button does not exist.
  1  - Button is in normal mode (not pressed and no mouse over it).
  2  - Mouse cursor is over the button.
  3  - Button is pressed down.
  4  - Button has been clicked (stays for one cycle).
 -1  - Button is disabled.

As you see from the picture, button is just a rectangle on the screen specified by four points (see the red dots in each corner). Every point has two coordinates – length from screen left and upper border of the screen. In previous type declaration I used for simplicity Xsize and Ysize.

And all about the buttons is to check that is mouse cursor (which is one point specified by two coordinates) on it or not and whether the mouse button was clicked or not. One way to check if the mouse is over is:

MouseX and MouseY are mouse coordinates.

IF MouseX >= X THEN ‘Equal or above to x variable
   IF MouseY >= Y THEN ‘Equal or above to y variable
      IF MouseX <= X1 THEN ‘Equal or below of x1 variable
         IF MouseY <= Y1 THEN ‘Equal or below of y1 variable
            ‘If it makes here ->then mouse is on the button.
         END IF
      END IF
END IF             

Now we need to create an array containing button data. The bigger array is the more buttons you can have. Here’s comes the fundamental of my button engine logic. It has an array filled with different buttons (every button has a different location) and system checks in the for...next cycle if the mouse is on the button or not, If not then move to the next entry and so on until Array is looked through.

CONST NumberOfButtons = 100
DIM SHARED Button(1 TO NumberOfButtons) AS ButtonData

Now we can have maximum 100 buttons at the same time on the screen. My button routine also uses 4 special variables:

DIM SHARED MouseOnButton 
DIM SHARED ButtonClicked
DIM SHARED ButtonPressed

So now that we have set up all needed variables, we can move onto more complex things. First is the FUNCTION that create button and returns ID of the new created button.

DECLARE FUNCTION CreateButton (Xpos, Ypos, XSize, Ysize, Caption AS STRING)

I assume that you use DEFINT A-Z keyword. (DEFINT A-Z in the beginning of your code forces all variables to be handled as integers, unless specified otherwise) Now this function accepts button parameters that I explained before and also will return Button ID number, or if there was an error then returns 0. ID number is unique to every button and is used to read and control the button. Also if you use qb4.5 or better then you can use BYVAL (passes data by Value rather then reference –amd is a bit faster) keyword to pass variables, however Qbasic (1.0 and 1.1) don’t allow it.

Now here I put whole function, and try to explain what each thing means, this code is also ready for copy & paste. See the comments:

FUNCTION CreateButton (Xpos, Ypos, XSize, YSize, Style, Caption AS STRING)
'This function creates a new button, saves variables into our user defined type and if,
'no error occurred then also returns ID number.

'First Find free button ID. If there's none, we exit function returning 0
FOR Search = 1 TO NumberOfButtons
   IF Button(Search).Status = 0 THEN
      'Free button ID found.
      'If button exists then Status is > 0, else
      'Status = 0
      ButID = Search
      EXIT FOR

'if no free button socket was found in previous for...next cycle, then
'ButID is 0. So exit the function.

'Now that we have a button ID we can save button information.
Button(ButID).Xpos = Xpos
Button(ButID).Ypos = Ypos
Button(ButID).XSize = XSize
Button(ButID).YSize = YSize
Button(ButID).Style = Style
Button(ButID).Status = 1
Button(ButID).Caption = Caption

'Now draw the button.
DrawButton ButID, 1

'Return the ID number
CreateButton = ButID

Now that we have successfully created a button we need some mechanism to control it. It can be done in many different ways. But I use one simple one. It checks if mouse is one the button, if is then whether the button is pressed or not. If is, then it controls on every turn if mouse is on the same button or not until mouse button is released. If it was released on the same button then the said button has been clicked. I’ll put here the code of the sub again and follow the comments. There are two parts in the ButtonSystem sub, please start from the second part (in the middle) after it move to first part.

SUB ButtonSystem
'First PART
'If some button is already pressed down:
'Remember ButtonTemp variable we set in the second part if mouse was on the button
'and mouse button was pressed down. Well here we are.
IF ButtonTemp > 0 THEN
   'Now we check if the mouse is still on the button or not.
   'Remember that ButtonTemp holds button ID number. So we can use it.
   IF MouseX >= Button(ButtonTemp).Xpos THEN
      IF MouseY >= Button(ButtonTemp).Ypos THEN
         IF MouseX <= Button(ButtonTemp).Xpos + Button(ButtonTemp).XSize THEN
            IF MouseY <= Button(ButtonTemp).Ypos + Button(ButtonTemp).YSize THEN
               'If the mouse cursor is on the button then the following code
               'is executed

               'Check for mousebutton status.
               IF MouseB = 0 THEN
                  'if not pressed down then it means it has been
                  'released. so The button was clicked. It is so because,
                  'Button could not have been pressed down if mouse button
                  'wasn't first.
                  'Set variables. Burron status is 4 –remember out status table.
                  Button(ButtonTemp).Status = 4
                  'Also set what button was clicked. It stays true for one cycle.
                  ButtonClicked = ButtonTemp

                     'Draw button.
                     DrawButton ButtonTemp, 1

                  'Set control variables to 0.
                  ButtonPressed = 0
                  ButtonTemp = 0

                  'And exit sub.
                  EXIT SUB
               END IF

                  'if mousebutton is held down then check is the
                  'button drawn in pressed state or not.
                  'Fot that I use ButtonPressed variable. If button is pressed down, then
                  'this variable > 0.
                  IF ButtonPressed = 0 THEN
                     'If not then do so
                     'Also if not then it means then mouse cursor has been moved away from
                     'the button. So set the MouseOnButton variable again.
                     MouseOnButton = ButtonTemp
                     ButtonPressed = ButtonTemp
                     Button(ButtonTemp).Status = 3

                        'Draw button. Button status table.
                        DrawButton ButtonTemp, 3
                  END IF
               'And exit sub.
               EXIT SUB
            END IF
         END IF
      END IF
   'If program reaches her then it means mouse cursor is no longer on the button so
   'we’ll check if the Button is drawn in pressed state or not.
   IF ButtonPressed > 0 THEN

      'If is then draw normal button, set ButtonPressed to 0 and also Button status to 1
      ButtonPressed = 0
      Button(ButtonTemp).Status = 1

         DrawButton ButtonTemp, 1

   'it also means that mouse cursor isn’t on the button anymore.
   MouseOnButton = 0

   'Also check if the mousebutton is pressed or not.
   'If is then just exit the sub
   IF MouseB > 0 THEN EXIT SUB

   'If not then release button and null ButtonTemp variable
   'It means then next cycle it starts again searching for button.
   ButtonTemp = 0

'Second PART –start here
'First set clicked button variable to 0. so we have clean start.
'It also makes sure that buttonClicked stays above 0 only for one cycle.
ButtonClicked = 0

'Find on what button(if at all) mouse cursor is. Remember when I showed how
'to detect if mouse is on rectangle –so now the same only through the array.
FOR Check = 1 TO NumberOfButtons
   IF Button(Check).Status > 0 THEN          'Check if button exists or not. Remember our status table!
      IF MouseX >= Button(Check).Xpos THEN
         IF MouseY >= Button(Check).Ypos THEN
            IF MouseX <= Button(Check).Xpos + Button(Check).XSize THEN
               IF MouseY <= Button(Check).Ypos + Button(Check).YSize THEN
                  'To avoid errors we first check if mouse has previously been on some button.
                  IF MouseOnButton > 0 THEN
                     'And if that button is not the current one then change previous button status back to normal (1)
                     IF MouseOnButton <> Check THEN
                        button(MouseOnButton).Status = 1
                     END IF
                  END IF
                  'If it makes here, then mouse cursor is on the button.
                  MouseOnButton = Check   'This variable holds the ID of the button on what mouse cursor is.
                  'Check for the mousebutton state. If MouseB>0 then it is pressed.
                  'Note that you may have some other mouse routine what works differently!
                  IF MouseB = 0 THEN
                     'Mouse button is not pressed, so only return Button status 2 –status table.
                     'and exit sub.
                     Button(Check).Status = 2
                     EXIT SUB
                  END IF
                  'If it makes here then mouse button is clicked. Set these variables to hold values.
                  ButtonTemp = Check
                  ButtonPressed = Check
                  Button(Check).Status = 3

                     'Draw button is subroutine what draws the button. Number 3 is the type of button to draw
                     'and this number is based on statustable.
                     DrawButton Check, 3

                  'Exit sub.
                  EXIT SUB
               END IF
            END IF
         END IF
      END IF
NEXT Check

'If the system reaches here then it means that mouse is not located on any button. So if MouseOnButton
'abover 0 then set it to 0 but also the button’s status to 1. –Non active button.
IF MouseOnButton > 0 THEN
   Button(MouseOnButton).Status = 1
   MouseOnButton = 0

Now that this routine is done I want to say that this is only one way to control button system. For example you can cycle through all buttons on every cycle or use some other logic. Of course much more complicated button system is using object database. But we’ll not get into it right no (I plan to write about it in Part 3) Also you can assign to every button some quick-key. For this you’d need to assign for every button one string variable what holds key character on what to act. Then you do something like this:

Key$ = INKEY$
IF Key$ > "" THEN
   FOR Search = 1 TO NumberOfButtons
      IF Button(Search).QuickKey = Key$ THEN
         ClickedButton = Search
         Button(Search).Status = 4
         ButtonTemp = 0
         EXIT SUB
      END IF

Of course this particular peace of code is only the example, but it should give you the idea. For example –hold button in pressed state until key is released. And cancel the click if for example esc has been pressed. There are many ways to perfect it.

Now we can move to last major SUB –DrawButton. This routine draws our button. I again just post the working code and comment it.

SUB DrawButton (ButID, Mode)
' This routine just draws the button.
' ButID – The ID number of the button.
' Mode – Defines the state of the button (pressed / not pressed)

' Usually it is wise to turn off the mouse cursor while drawing so this is
' what this command does.
MousePointer (2)

' Let's for simplicity assign some variables. It’s only for simplicity.
DIM Xpos, Ypos, X1pos, Y1pos, TextX, TextY, Caption AS STRING

' And assign values to these variables. It’s easier to use Xpos instead of
' Button(ButID).Xpos all the time.
Xpos = Button(ButID).Xpos
Ypos = Button(ButID).Ypos
X1pos = Xpos + Button(ButID).XSize
Y1pos = Ypos + Button(ButID).YSize
Caption = RTRIM$(Button(ButID).Caption)

'Because the text on the button is centered, then we need to calculate positions.
TextX = (Button(ButID).XSize / 2 + Xpos) - LEN(Caption) * 4
TextY = (Button(ButID).YSize / 2 + Ypos) - 3

' As we have style variable then we can draw many different style buttons.
' Remember our button usertype declaration.

' Mode number used below comes from button status-table.

SELECT CASE Button(ButID).Style
CASE 0 'If style is 0 then use these drawing routines. Can be used also
             'const values. Const win95 = 0
             'Remember I use standard QB drawing routines. If you use library,
             'then you need to change them.
   'Standard button. Let's make it look like windows 95. Simple
   IF Mode = 1 THEN
      'Draw normal button. It means not pressed down.
      LINE (Xpos, Ypos)-(X1pos, Y1pos), 7, BF 'Filled light-grey rectangle

      LINE (Xpos, Ypos)-(X1pos, Ypos), 15 'White upper borders
      LINE (Xpos, Ypos)-(Xpos, Y1pos), 15 'White left border

      LINE (Xpos, Y1pos)-(X1pos, Y1pos), 0 'Black bottom border
      LINE (X1pos, Ypos)-(X1pos, Y1pos), 0 'Black right border.

      PrintF Caption, TextX, TextY, 0 'Place text on the button.

   ELSEIF Mode = 3 THEN
      'Draw pressed button
      'When button is hold down.

      LINE (Xpos, Ypos)-(X1pos, Y1pos), 7, BF

      LINE (Xpos, Ypos)-(X1pos, Ypos), 8
      LINE (Xpos, Ypos)-(Xpos, Y1pos), 8

      LINE (Xpos, Y1pos)-(X1pos, Y1pos), 15
      LINE (X1pos, Ypos)-(X1pos, Y1pos), 15

      PrintF Caption, TextX + 1, TextY + 1, 0

   ELSEIF Mode = -1 THEN
      'Draw disabled button
      ‘This draws when button is disabled.
      LINE (Xpos, Ypos)-(X1pos, Y1pos), 7, BF

      LINE (Xpos, Ypos)-(X1pos, Ypos), 15
      LINE (Xpos, Ypos)-(Xpos, Y1pos), 15

      LINE (Xpos, Y1pos)-(X1pos, Y1pos), 0
      LINE (X1pos, Ypos)-(X1pos, Y1pos), 0

      PrintF Caption, TextX + 1, TextY + 1, 15
      PrintF Caption, TextX, TextY, 8
      'Here you can define your own button styles. Just follw as previous.
      'See in demo too. There are three different styles.

MousePointer (1)
MousePointer (3)

Now that we have button drawing routine ready we can test it out. So we can move onto subject as how to use it all in your program. As this system is so to say "polling" button routine then ButtonSystem sub must be called inside the cycle to make it work. So here’s small example (but better one explaining more about that system is in the GuiQB.BAS file.

'First let’s create a button. Remember the CreateButton routine.
NewButton = CreateButton(100, 100, 100, 20, 0, "My Button")
ExitButton = CreateButton(100, 200, 100, 20, 0, "Exit")

'Now we put it between DO ... LOOP cycle.
'As I use mine mouse system (I don’t know what you use)
‘Then I first call my mouse update routine (more about it in the GUIQB.BAS file
'Update mouse
CALL mouse(MouseY, MouseX, MouseB)
'Then call the ButtonSystem routine
'Now remember ButtonClicked variable? We now use it here.
'If no button has been clicked then it’s value is 0. Otherwise it
‘it’s value is the ID number of the certain button.

IF ButtonClicked > 0 THEN
   IF NewButton = ButtonClicked THEN 'if NewButton = ButtonClicked then you can guess what happened ?
      'Button has been clicked.
      'Do something. For example
   ELSEIF ExitButton = ButtonClicked THEN
      'If exit button has been clicked then exit do
      EXIT DO

Now I recommend you to try and understand the example program GuiQB.BAS. It features many interesting features that are not listed here, but are well described in the file. For example: how to disable and enable these buttons. There’s also a little demo on how to use "moduled" Gui code in your programs (see the function called: "Question"). Well I hope this tutorial has been helpful to you. If you have questions/suggestions or anything to say about the matter then please feel free to email me. Also all kind of suggestions or so on are welcomed for the next part of the tutorial which will be focussed on the textbox systems and some other controls.

Thank you for reading this tutorial!

October 2004

Download a rar archive of this tutorial and all source code!
You can contact VonGodric at: vongodric@hotmal.com or wizgui@hot.ee

QB Scripting (Part 1)

Written by Chaoticmass

This is part one of an awesome two-part scripting tutorial by Chaoticmass. The second half will appear in the November issue, so be sure to check in next month!



I hate introductions. Creating script languages is a topic that has been covered many times over. So why yet another one? We've got tutorials on creating script languages for RPGs and games. There's nothing else to be said on the topic. Well, not quite.

This article, and hopefully series, is about creating a script language for the sake of scripting. Within I will detail the basics on how to create a versatile script language that is powerful enough to work on it's own as a fun and useful general purpose language, but also flexible enough to find it's way into almost any program you create. It will not be purpose built for any particular task, which will make it impossible, impractical, or impossible to use it for certain things- but the techniques learned will allow you to create your own script language that suits your needs.

Because I didn't want to further delay this article, I've decided to split it into two parts. This part covers loading a script from a plain text file, and also covers tokenizing the script and holding it in an array. In the next part of this article we'll cover how to implement a system for variables in the script, and then finally interpreting the script that is loaded in memory.


The foundation for this article is the work I've done on my own script language called Explo Script. Years back, I started working on a script language in Qbasic. Soon I was using it to script a GUI and made mouse driven GUI applications. Next it was ported to Visual Basic, and over the next few years Explo Script evolved into a powerful general purpose language that I was using to build games and applications from the ground up.

Visual Basic freed Explo Script from the memory limitations of Qbasic and made many things possible. While remaining a fully interpreted language, where no compiling takes place, it learned many advanced language features. You could write scripts in a structured language, with Subroutines and Functions with their own variable scope. Events could be triggered and then handled with the Event Queue. Even some object oriented concepts leaked in, such as Classes.

One day, I had a yearning for some Qbasic. I wanted to bask in the warm blue glow of the old Qbasic IDE. I don't exactly know what it was, perhaps a kind of salmon like instinct, but I decided to port Explo back to it's spawn point. Though I knew, like a fish swimming upstream, it wasn't going to be easy.


That was a few months ago. Explo Script has already made it's trek back home to Qbasic, and it made it there with everything intact- procedures, variable scope, class objects and all. Along the way, certain sacrifices had to be made, such as conservative memory use over blazing speed. It still stays in line with the original goal: A script language built for the sake of scripting. What kind of things can we do with it, and how much fun can we have with it, were higher priorities than speed.

The script language techniques you will learn herein are based of Explo Script, which is designed to be easy to extend and use in other projects. Therefore, we wont be using a byte code. By parsing and interpreting the script as is, without compiling, you use more memory and loose speed, but the result is a script engine that is easy to extend and manipulate without making a lot of changes, which makes it more enjoyable for you to work on.

The Core

In this first article, we will start our script language by creating what I call the Core. The Core is the most basic collection of pieces that work together to interpret a script and make things happen. Later, we will add other parts and modify parts of the core to add more functionality, but to begin we're only going to do the basics.

We're going to create a script engine that can load a script into memory. This is done very simply. After the script is in memory, we will create a tokenizer which will split the script into separate parts, based on our language syntax. The script, now split into tokens, will be loaded into an array. We will create a system for storing variables. Finally, we will build the heart of the script engine, which will move through the script, interpret what it sees, and then perform actions based on the script.

Loader, tokenizer, variable system, interpreter. These are the parts of the Core, and with it we can do nearly anything. The Core is a good script engine for simple script tasks, and is well suited for games like RPGs. It can be expanded for more capabilities, or optimized for greater speed. In later issues, we'll keep adding more language features, but for now it will remain simple.

Loading the Script

To work with our script file, we're going to create an array that we will use later to store the script.


Next we will load our script from a file, and store it in a variable.

SUB LoadScript (Script AS STRING)

    IF DIR$(Script) THEN
      OPEN Script FOR INPUT AS #1
        DO WHILE NOT EOF(1)
          LINE INPUT #1, C$
          Text$ = Text$ + CHR$(13) + C$
      CLOSE #1
      Tokenize Text$
    END IF


As you can see in this Subroutine, the script is loaded into a variable called Text$, which is then passed on to the Subroutine Tokenize, and that brings us to our next section.

The Tokenizer

This step is a big one. Important, too. Tokenizing the script means we're splitting it down into it's smallest elements, or tokens. The Tokenizer will break our script file up into the smallest usable pieces based on the rules of our language's syntax. We haven't discussed the syntax for our language yet, so now is the best time to do so, because our language syntax will determine what rules the tokenizer must follow to split our script up properly.

Think of Qbasic. How does the most basic syntax in Qbasic work? Lets look at some examples and get an idea.

OPEN “File.txt” FOR OUTPUT AS #1
PRINT #1, “A veneer says as well!”

A = 5

Looking at this code, we see that each statement is separated by a line feed-carriage return. Also, we see that a space is commonly used to separate each token within each statement. So our delimiter is a space. Pretty simple? Lets practice tokenizing the above code using our rules we've discovered.


The above is what would be stored in our array if we were to tokenize that code by following the rules define above. Can you see a problem? Look at tokens 9 through 13. That used to be a String Literal. If we were to run that script in our finished script engine, it wouldn't write the whole string to the file, instead it'd write simply 'A'.

So our rules are a little incomplete. To tokenize the above code properly, we need these rules.

Now, following these rules, we get this result:

“A veneer says as well!”

That is the way it should look. Lets move on then. Based on this, here is our finished Tokenize subroutine:

SUB Tokenize (StrText AS STRING)

  NewLine = CHR$(13)
  LastI = 1
  DblQuote = CHR$(34)
  txtLength = LEN(StrText)
  FOR i = 1 TO txtLength
    IF i > txtLength THEN
      EXIT FOR
    END IF
    A$ = MID$(StrText, i, 1)
    IF A$ = DblQuote THEN
      i = INSTR(i + 1, StrText, DblQuote)
    END IF
      CASE " ", ",", NewLine
        Token$ = MID$(StrText, LastI, i – LastI)
        Main(UBOUND(Main)) = Text
        LastI = i + 1
  NEXT i

Check in next month's issue for Part 2! Chaoticmass can be reached at Chaoticmass@Hotmail.com.

The Mock-3D RPG Engine

Written by DarkDread

Editor's Note: Back in 1999, DarkDread began an excellent series of RPG tutorials for QBasic: The Magazine, two of which were published in the final months of the magazine's 12-issue run. Unfortunately, QB:TM disbanded in August of 1999, and the third issue of this excellent series was never published. Though it was available in .zip format on the Darkness Ethereal website for some time, it has never received a proper publication. This month, Part 3 of this series (on mock-3D RPG engines) will run in QB Express, and next month, the fourth issue (on Pixel*Tile Scrolling Engines) will be published. Enjoy!


Issue 3, Volume 2: 3D RPG Engine


Welcome to the third installment of my new RPG tutorials. This time around... The major chunk of the tutorial is reprinted in the 3drpg.bas file. But, I still reccommend that you read this, so that you may familiarize yourself with what we'll be disscussing.

Click here to download the source code for the engine described in this tutorial.

1. A 3D Engine?

Well, not a TRUE 3D engine. If you've ever played Legend of Lith II, or any of the AD&D Eye of the Beholder games, then you'll know what this is about.

Legend of Lith II
The Legend of Lith II by DarkDread.
Eye of the Beholder
AD&D: Eye of the Beholder

This engine allows you to create a first person 3D view, where moving is a character is limited to cell-by-cell movement. What do I mean when I say cell-by-cell? Picture a normal 2d tile engine, moving tile-by-tile, only in a first person perspective.

Don't let that fool you though. With this kind of an engine, it is possible to create a very nice game. Just take a look at Shining the Holy Ark for the Sega Saturn system if you don't belive me.

2. The code

The Qbasic code is fairly simple to learn. Not as easy as a tile engine, mind you, but I've kept it as simple as possible. If you wish, you can fire up QB and run the code now. Take a look at it, and play around with it. I've added a lot of comments into the code, especially in the important parts. The main thing that you may wish to look at, is the DrawLocation sub. This is the meat of the code, and the part that is used to determine what to draw on the screen.

Okay... That's enough chatter here. Go look at the code!

Initialization Code


DECLARE SUB PutSprite (FileName$, X%, Y%, PutType%)
DECLARE SUB ReadData (MaxX%, MaxY%)
DECLARE SUB DrawLocation ()
DECLARE SUB MoveRight ()

Here are all of the variables that we will need. Basically, the array for holding the map data, one array for loading and displaying our graphics, plus a few variables to hold the player location, position, and if they have moved or not. We use the last one to determine if we need to update the graphics on screen to show the player's new location.

DIM SHARED Map%(-3 TO 173, -3 TO 173)
DIM SHARED PlayerDir$, Moved%, PlayerX%, PlayerY%
DIM SHARED Sprite%(5000)

True/False constants. They're used in a lot of the code.

CONST True = -1, False = 0

These are some constants which you may wish to pay attention to. They are used to determine where on the screen our graphics are displayed. You can change the values of these to move the display to different areas of the screen. Make sure that, when you change the value of one constant, you change the value of all of them accordingly. An easy way to do this is to subtract or add, the same amount to each constant.

For example, to move the graphics to the upper left of the screen, subtract 60 from each Y constant and 112 from each X constant. Note that the wall aheads are X constants.

CONST CellY = 60, CellX = 112
CONST Cell1LeftX = 112, Cell2LeftX = 130, Cell3LeftX = 142
CONST Cell1RightX = 192, Cell2RightX = 180, Cell3RightX = 172
CONST Cell1Ahead = 129, Cell2Ahead = 142, Cell3Ahead = 150

Here, we call a SUB to read out map data (As well as set our maximum X and Y values for the maze. We also give values to the player's default location, and default direction which they are facing. Also we must set the Moved% variable to true, so that when the program is run, the screen will display the graphics for the player's location in the maze.

ReadData 10, 10
PlayerX% = 0: PlayerY% = 0: PlayerDir$ = "N"
Moved% = True

Switch to mode 13h (300x200, 256 colour VGA mode), load a background graphic for the demonstration, and create empty boxes where our stats and graphics will be shown.

DEF SEG = &HA000
BLOAD "back.bsv"
LINE (7, 7)-(127, 34), 0, BF
LINE (6, 6)-(128, 35), 4, B
LINE (111, 59)-(210, 161), 4, B

The Main Loop

Here's the main loop. Notice the CASE statements. We use these to check the keyboard buffer to see if the user has pressed a key. Also, the PSET statements are used to draw our mini map on the screen, as well as the player's current location.


    PSET (PlayerX% + 112, PlayerY% + 24), 120
        CASE CHR$(0) + CHR$(72)
        CASE CHR$(0) + CHR$(80)
        CASE CHR$(0) + CHR$(75)
        CASE CHR$(0) + CHR$(77)
        CASE CHR$(27)
            Quit = True
    PSET (PlayerX% + 112, PlayerY% + 24), 10
    IF Moved% THEN DrawLocation
    Moved% = False

    LOCATE 2, 2: PRINT "Location:"; PlayerX%; PlayerY%
    LOCATE 3, 2: PRINT "Compass:     "; PlayerDir$
    LOCATE 4, 2: PRINT "Map:         "



This is the data for our maze. Of course, it doesn't have to be located in the code. You could just as easily put the numbers into a seperate file and modify the map loader so that it reads from a file instead of data. But, for the purpose of this tutorial, I've left the map as DATA statements.

DATA 00,00,00,00,00,00,00,00,00,00
DATA 00,10,00,10,10,10,10,10,10,00
DATA 00,10,00,10,00,00,00,00,10,00
DATA 00,10,00,10,00,10,10,00,10,00
DATA 00,10,00,10,00,10,10,00,10,00
DATA 00,10,00,00,00,00,00,00,10,00
DATA 00,10,00,10,10,10,10,10,10,00
DATA 00,10,00,00,00,00,00,00,10,00
DATA 00,10,10,00,10,10,00,10,10,00
DATA 00,00,00,00,00,00,00,00,00,00

The DrawLocation Sub

SUB DrawLocation

Here's the meat of this code. This is the SUB that checks the cells around the player and draws the area around them. It may look a bit complicated at first, but read through the code and try to understand how it works. Once you've discovered the methods used here, you'll be able to work with this type of engine easily.

Basically, picture six cells, laid out like so, with X denoting the player (Fig. a):

   Fig. a          Fig. b          Fig. c          Fig. d   
    _ _ _           _ _ _           _ _ _          _ _ _    
   |_|_|_|         |Û|_|_|         |Û|_|_|        |_|Û|_|   
   |_|_|_|         |±|_|_|         |Û|_|_|        |_|_|_|   
   |_|_|_|         |_|_|_|         |_|_|_|        |_|_|_|   
      X               X               X              X      

To draw the map in a first person view, the code checks each cell to see if the player could pass through it or not. For example, if there is a wall (Û) in the upper left cell and a walkable space (±) in the cell below it, then the code will put a 'turn' type wall in the upper left cell (Fig. b). However, if there was a wall in the cell below the upper left cell as well, the code would put a regular wall there (Fig. c). The code goes through the left and right cells, checking for walls and walkable spaces such as those in examples b and c. Finally, the middle row of cells is used to determine if there is a wall directly ahead of the player, and if so, how many spaces (or cells) away. For example, if there is a wall three cells away from the player, then the code will know to draw a wall, three cells ahead of the player (Fig. d).

A bit tougher than a 2d scrolling engine, isn't it? Don't worry if you don't quite understand it yet. The best thing to do, is to run the code, play around with it, play around with the map (The DATA statements in the main code), and get a feeling for how it works first. Once you are familiar with it, you can try editing parts of the code here to add other types of walls, or windows, doors, etc. Well, back to the code. ;)

First, we put a background on the screen. For the purpose of this tutorial, there is no background, to keep it simple. However, you could easily throw in a moving background simply by cycling a few sprites every time this sub is called. You could do this like so:

First, make a dim shared variable, call it Frames% for example, in your main program. Then, put code similar to this below (Assuming that you have 5 background pics that you wish to cycle, and you've named them back0.pic, back2.pic, etc, etc):

PutSprite "back" + RIGHT$(STR$(Frames%), 1) + ".pic", CellX, CellY
Frames% = Frames% + 1
IF Frames% = 5 THEN Frames% = 0

Simple, isn't it? Of course, without a proper screen buffer, the display will flicker a lot. To correct this, you'd want to use a graphics library such as GSLib, Blast!, Dash, DirectQB, or any other mode 13h lib. If you have a graphics library, please refer to the documentation that came with it, if you wish to use it.

For this tutorial, I am simply using a black box for the background, like so.

LINE (CellX, CellY)-(CellX + 97, CellY + 100), 0, BF

Next, we determine which direction the player is facing. This could be north, east, west, or south.

IF PlayerDir$ = "N" THEN

If the player is facing north, we check the cells around them and determine if the are empty or if they are a wall type.

First, we will place walls, and/or turns in the three cells to the left of the player.

If the cell one left and three up from the player is not walkable and the cell one left and two up from the player is walkable then we need to put a 'turn' wall there.

IF Map%(PlayerX% - 1, PlayerY% - 3) > 9 AND Map%(PlayerX% - 1, PlayerY% - 2) < 10 THEN
          PutSprite "T3.BSG", Cell3LeftX, CellY, 0

Otherwise, if the cell one left and two up from the player is not walkable, we put a regular wall there.

      ELSEIF Map%(PlayerX% - 1, PlayerY% - 2) > 9 THEN
          PutSprite "LW3.BSG", Cell3LeftX, CellY, 0

If the cell one left and two up from the player is not walkable and the cell one left and one up from the player is walkable then we need to put a 'turn' wall there.

IF Map%(PlayerX% - 1, PlayerY% - 2) > 9 AND Map%(PlayerX% - 1, PlayerY% - 1) < 10 THEN
          PutSprite "T2.BSG", Cell2LeftX, CellY, 0

Otherwise, if the cell one left and one up from the player is not walkable, we put a regular wall there.

     ELSEIF Map%(PlayerX% - 1, PlayerY% - 1) > 9 THEN
          PutSprite "LW2.BSG", Cell2LeftX, CellY, 0

If the cell one left and one up from the player is not walkable and the cell one left from the player is walkable then we need to put a 'turn' wall there.

IF Map%(PlayerX% - 1, PlayerY% - 1) > 9 AND Map%(PlayerX% - 1, PlayerY%) < 10 THEN
          PutSprite "T1.BSG", Cell1LeftX, CellY, 0

Otherwise, if the cell one left from the player is not walkable then we need to put a regular wall there.

     ELSEIF Map%(PlayerX% - 1, PlayerY%) > 9 THEN
          PutSprite "LW1.BSG", Cell1LeftX, CellY, 0

Now, we will place walls and/or turns in the three cells to the right of the player.

If the cell one right and three up from the player is not walkable and the cell one right and two up from the player is walkable then we need to put a 'turn' wall there.

IF Map%(PlayerX% + 1, PlayerY% - 3) > 9 AND Map%(PlayerX% + 1, PlayerY% - 2) < 10 THEN
          PutSprite "T3.BSG", Cell3RightX, CellY, 0

Otherwise, if the cell one right and two up from the player is not walkable then we need to put a regular wall there.

     ELSEIF Map%(PlayerX% + 1, PlayerY% - 2) > 9 THEN
          PutSprite "RW3.BSG", Cell3RightX, CellY, 0

If the cell one right and two up from the player is not walkable and the cell one right and one up from the player is walkable then we need to put a 'turn' wall there.

IF Map%(PlayerX% + 1, PlayerY% - 2) > 9 AND Map%(PlayerX% + 1, PlayerY% - 1) < 10 THEN
          PutSprite "T2.BSG", Cell2RightX, CellY, 0

Otherwise, if the cell one right and one up from the player is not walkable then we need to put a regular wall there.

     ELSEIF Map%(PlayerX% + 1, PlayerY% - 1) > 9 THEN
          PutSprite "RW2.BSG", Cell2RightX, CellY, 0

If the cell one right and one up from the player is not walkable and the cell one right from the player is walkable then we need to put a 'turn' wall there.

IF Map%(PlayerX% + 1, PlayerY% - 1) > 9 AND Map%(PlayerX% + 1, PlayerY%) < 10 THEN
          PutSprite "T1.BSG", Cell1RightX, CellY, 0

Otherwise, if the cell one right from the player is not walkable then we need to put a regular wall there.

     ELSEIF Map%(PlayerX% + 1, PlayerY%) > 9 THEN
          PutSprite "RW1.BSG", Cell1RightX, CellY, 0

Finally, we check the middle set of cells, to see if there is a wall ahead of the player in any one of these cells. If the cell three up from the player is not walkable then we need to put a wall there.

IF Map%(PlayerX%, PlayerY% - 1) > 9 THEN
          PutSprite "WA1.BSG", Cell1Ahead, CellY, 0

If the cell two up from the player is not walkable then we need to put a wall there.

     ELSEIF Map%(PlayerX%, PlayerY% - 2) > 9 THEN
          PutSprite "WA2.BSG", Cell2Ahead, CellY, 0

If the cell one up from the player is not walkable then we need to put a wall there.

     ELSEIF Map%(PlayerX%, PlayerY% - 3) > 9 THEN
          PutSprite "WA3.BSG", Cell3Ahead, CellY, 0

You'll notice that when we are setting our walls, we always draw the ones furthest away from the player first. This is because what is closest to the player, will always determine what can be seen further ahead. Therefore, if there is a wall directly in front of the player, that wall needs to be drawn last, so it covers any other walls drawn further ahead, which the player is not able to see, because there is a wall in front of them!

The only exception to this rule is the walls placed ahead of the player. You'll notice that, the IF...THEN...ELSE statement ends as soon as the first wall ahead is placed. Therefore, we don't need to start from the furthest wall and work our way in.

The rest of this code is the same as the above, only it is used when the player is facing a different direction. This way, if the player is facing east, it must draw parts of the map which are to the right of them instead of straight ahead.

I haven't added comments to the rest of this, as, it would make for unnecessary reading, and based on what is above, you should be able to figure out the minor changes that must be made when the player is facing in a different direction.

        ELSEIF PlayerDir$ = "E" THEN
            IF Map%(PlayerX% + 3, PlayerY% - 1) > 9 AND Map%(PlayerX% + 2, PlayerY% - 1) < 10 THEN
                    PutSprite "T3.BSG", Cell3LeftX, CellY, 0
                ELSEIF Map%(PlayerX% + 2, PlayerY% - 1) > 9 THEN
                    PutSprite "LW3.BSG", Cell3LeftX, CellY, 0
            END IF
            IF Map%(PlayerX% + 2, PlayerY% - 1) > 9 AND Map%(PlayerX% + 1, PlayerY% - 1) < 10 THEN
                    PutSprite "T2.BSG", Cell2LeftX, CellY, 0
                ELSEIF Map%(PlayerX% + 1, PlayerY% - 1) > 9 THEN
                    PutSprite "LW2.BSG", Cell2LeftX, CellY, 0
            END IF
            IF Map%(PlayerX% + 1, PlayerY% - 1) > 9 AND Map%(PlayerX%, PlayerY% - 1) < 10 THEN
                    PutSprite "T1.BSG", Cell1LeftX, CellY, 0
                ELSEIF Map%(PlayerX%, PlayerY% - 1) > 9 THEN
                    PutSprite "LW1.BSG", Cell1LeftX, CellY, 0
            END IF
            IF Map%(PlayerX% + 3, PlayerY% + 1) > 9 AND Map%(PlayerX% + 2, PlayerY% + 1) < 10 THEN
                    PutSprite "T3.BSG", Cell3RightX, CellY, 0
                ELSEIF Map%(PlayerX% + 2, PlayerY% + 1) > 9 THEN
                    PutSprite "RW3.BSG", Cell3RightX, CellY, 0
            END IF
            IF Map%(PlayerX% + 2, PlayerY% + 1) > 9 AND Map%(PlayerX% + 1, PlayerY% + 1) < 10 THEN
                    PutSprite "T2.BSG", Cell2RightX, CellY, 0
                ELSEIF Map%(PlayerX% + 1, PlayerY% + 1) > 9 THEN
                    PutSprite "RW2.BSG", Cell2RightX, CellY, 0
            END IF
            IF Map%(PlayerX% + 1, PlayerY% + 1) > 9 AND Map%(PlayerX%, PlayerY% + 1) < 10 THEN
                    PutSprite "T1.BSG", Cell1RightX, CellY, 0
                ELSEIF Map%(PlayerX%, PlayerY% + 1) > 9 THEN
                    PutSprite "RW1.BSG", Cell1RightX, CellY, 0
            END IF
            IF Map%(PlayerX% + 1, PlayerY%) > 9 THEN
                    PutSprite "WA1.BSG", Cell1Ahead, CellY, 0
                ELSEIF Map%(PlayerX% + 2, PlayerY%) > 9 THEN
                    PutSprite "WA2.BSG", Cell2Ahead, CellY, 0
                ELSEIF Map%(PlayerX% + 3, PlayerY%) > 9 THEN
                    PutSprite "WA3.BSG", Cell3Ahead, CellY, 0
            END IF
        ELSEIF PlayerDir$ = "S" THEN
            IF Map%(PlayerX% + 1, PlayerY% + 3) > 9 AND Map%(PlayerX% + 1, PlayerY% + 2) < 10 THEN
                    PutSprite "T3.BSG", Cell3LeftX, CellY, 0
                ELSEIF Map%(PlayerX% + 1, PlayerY% + 2) > 9 THEN
                    PutSprite "LW3.BSG", Cell3LeftX, CellY, 0
            END IF
            IF Map%(PlayerX% + 1, PlayerY% + 2) > 9 AND Map%(PlayerX% + 1, PlayerY% + 1) < 10 THEN
                    PutSprite "T2.BSG", Cell2LeftX, CellY, 0
                ELSEIF Map%(PlayerX% + 1, PlayerY% + 1) > 9 THEN
                    PutSprite "LW2.BSG", Cell2LeftX, CellY, 0
            END IF
            IF Map%(PlayerX% + 1, PlayerY% + 1) > 9 AND Map%(PlayerX% + 1, PlayerY%) < 10 THEN
                    PutSprite "T1.BSG", Cell1LeftX, CellY, 0
                ELSEIF Map%(PlayerX% + 1, PlayerY%) > 9 THEN
                    PutSprite "LW1.BSG", Cell1LeftX, CellY, 0
            END IF
            IF Map%(PlayerX% - 1, PlayerY% + 3) > 9 AND Map%(PlayerX% - 1, PlayerY% + 2) < 10 THEN
                    PutSprite "T3.BSG", Cell3RightX, CellY, 0
                ELSEIF Map%(PlayerX% - 1, PlayerY% + 2) > 9 THEN
                    PutSprite "RW3.BSG", Cell3RightX, CellY, 0
            END IF
            IF Map%(PlayerX% - 1, PlayerY% + 2) > 9 AND Map%(PlayerX% - 1, PlayerY% + 1) < 10 THEN
                    PutSprite "T2.BSG", Cell2RightX, CellY, 0
                ELSEIF Map%(PlayerX% - 1, PlayerY% + 1) > 9 THEN
                    PutSprite "RW2.BSG", Cell2RightX, CellY, 0
            END IF
            IF Map%(PlayerX% - 1, PlayerY% + 1) > 9 AND Map%(PlayerX% - 1, PlayerY%) < 10 THEN
                    PutSprite "T1.BSG", Cell1RightX, CellY, 0
                ELSEIF Map%(PlayerX% - 1, PlayerY%) > 9 THEN
                    PutSprite "RW1.BSG", Cell1RightX, CellY, 0
            END IF
            IF Map%(PlayerX%, PlayerY% + 1) > 9 THEN
                    PutSprite "WA1.BSG", Cell1Ahead, CellY, 0
                ELSEIF Map%(PlayerX%, PlayerY% + 2) > 9 THEN
                    PutSprite "WA2.BSG", Cell2Ahead, CellY, 0
                ELSEIF Map%(PlayerX%, PlayerY% + 3) > 9 THEN
                    PutSprite "WA3.BSG", Cell3Ahead, CellY, 0
            END IF
        ELSEIF PlayerDir$ = "W" THEN
            IF Map%(PlayerX% - 3, PlayerY% + 1) > 9 AND Map%(PlayerX% - 2, PlayerY% + 1) < 10 THEN
                    PutSprite "T3.BSG", Cell3LeftX, CellY, 0
                ELSEIF Map%(PlayerX% - 2, PlayerY% + 1) > 9 THEN
                    PutSprite "LW3.BSG", Cell3LeftX, CellY, 0
            END IF
            IF Map%(PlayerX% - 2, PlayerY% + 1) > 9 AND Map%(PlayerX% - 1, PlayerY% + 1) < 10 THEN
                    PutSprite "T2.BSG", Cell2LeftX, CellY, 0
                ELSEIF Map%(PlayerX% - 1, PlayerY% + 1) > 9 THEN
                    PutSprite "LW2.BSG", Cell2LeftX, CellY, 0
            END IF
            IF Map%(PlayerX% - 1, PlayerY% + 1) > 9 AND Map%(PlayerX%, PlayerY% + 1) < 10 THEN
                    PutSprite "T1.BSG", Cell1LeftX, CellY, 0
                ELSEIF Map%(PlayerX%, PlayerY% + 1) > 9 THEN
                    PutSprite "LW1.BSG", Cell1LeftX, CellY, 0
            END IF
            IF Map%(PlayerX% - 3, PlayerY% - 1) > 9 AND Map%(PlayerX% - 2, PlayerY% - 1) < 10 THEN
                    PutSprite "T3.BSG", Cell3RightX, CellY, 0
                ELSEIF Map%(PlayerX% - 2, PlayerY% - 1) > 9 THEN
                    PutSprite "RW3.BSG", Cell3RightX, CellY, 0
            END IF
            IF Map%(PlayerX% - 2, PlayerY% - 1) > 9 AND Map%(PlayerX% - 1, PlayerY% - 1) < 10 THEN
                    PutSprite "T2.BSG", Cell2RightX, CellY, 0
                ELSEIF Map%(PlayerX% - 1, PlayerY% - 1) > 9 THEN
                    PutSprite "RW2.BSG", Cell2RightX, CellY, 0
            END IF
            IF Map%(PlayerX% - 1, PlayerY% - 1) > 9 AND Map%(PlayerX%, PlayerY% - 1) < 10 THEN
                    PutSprite "T1.BSG", Cell1RightX, CellY, 0
                ELSEIF Map%(PlayerX%, PlayerY% - 1) > 9 THEN
                    PutSprite "RW1.BSG", Cell1RightX, CellY, 0
            END IF
            IF Map%(PlayerX% - 1, PlayerY%) > 9 THEN
                    PutSprite "WA1.BSG", Cell1Ahead, CellY, 0
                ELSEIF Map%(PlayerX% - 2, PlayerY%) > 9 THEN
                    PutSprite "WA2.BSG", Cell2Ahead, CellY, 0
                ELSEIF Map%(PlayerX% - 3, PlayerY%) > 9 THEN
                    PutSprite "WA3.BSG", Cell3Ahead, CellY, 0
            END IF
    END IF


The MoveDown Sub

This SUB is called if the player presses the down arrow key. We have to first, check which direction the player is facing, then check if the cell directly ahead of the player is walkable or not. If it is, we change the player's location accordingly, and make the Moved% variable True so that the screen will be updated to reflect the player's new position in the maze.

SUB MoveDown
    IF PlayerDir$ = "N" THEN
            IF Map%(PlayerX%, PlayerY% + 1) < 10 THEN
                Moved% = True: PlayerY% = PlayerY% + 1
            END IF
        ELSEIF PlayerDir$ = "E" THEN
            IF Map%(PlayerX% - 1, PlayerY%) < 10 THEN
                Moved% = True: PlayerX% = PlayerX% - 1
            END IF
        ELSEIF PlayerDir$ = "S" THEN
            IF Map%(PlayerX%, PlayerY% - 1) < 10 THEN
                Moved% = True: PlayerY% = PlayerY% - 1
            END IF
        ELSEIF PlayerDir$ = "W" THEN
            IF Map%(PlayerX% + 1, PlayerY%) < 10 THEN
                Moved% = True: PlayerX% = PlayerX% + 1
            END IF
    END IF


The MoveLeft Sub

A very simple SUB. This just changes the direction the player is facing in a counterclockwise movement each time the left arrow key is pressed.

SUB MoveLeft

    Moved% = True
    IF PlayerDir$ = "S" THEN
            PlayerDir$ = "E"
        ELSEIF PlayerDir$ = "E" THEN
            PlayerDir$ = "N"
        ELSEIF PlayerDir$ = "N" THEN
            PlayerDir$ = "W"
        ELSEIF PlayerDir$ = "W" THEN
            PlayerDir$ = "S"
    END IF


The MoveRight Sub

A very simple SUB. This just changes the direction the player is facing in a clockwise movement each time the right arrow key is pressed.

SUB MoveRight
    Moved% = True
    IF PlayerDir$ = "S" THEN
            PlayerDir$ = "W"
        ELSEIF PlayerDir$ = "E" THEN
            PlayerDir$ = "S"
        ELSEIF PlayerDir$ = "N" THEN
            PlayerDir$ = "E"
        ELSEIF PlayerDir$ = "W" THEN
            PlayerDir$ = "N"
    END IF


The MoveUp Sub

This SUB is called if the player presses the up arrow key. We have to first, check which direction the player is facing, then check if the cell directly ahead of the player is walkable or not. If it is, we change the player's location accordingly, and make the Moved% variable True so that the screen will be updated to reflect the player's new position in the maze.

SUB MoveUp
    IF PlayerDir$ = "N" THEN
            IF Map%(PlayerX%, PlayerY% - 1) < 10 THEN
                Moved% = True: PlayerY% = PlayerY% - 1
            END IF
        ELSEIF PlayerDir$ = "E" THEN
            IF Map%(PlayerX% + 1, PlayerY%) < 10 THEN
                Moved% = True: PlayerX% = PlayerX% + 1
            END IF
        ELSEIF PlayerDir$ = "S" THEN
            IF Map%(PlayerX%, PlayerY% + 1) < 10 THEN
                Moved% = True: PlayerY% = PlayerY% + 1
            END IF
        ELSEIF PlayerDir$ = "W" THEN
            IF Map%(PlayerX% - 1, PlayerY%) < 10 THEN
                Moved% = True: PlayerX% = PlayerX% - 1
            END IF
    END IF


The PutSprite Sub

This is the SUB which we use to put all of our graphics on the screen. So why the trouble for a sepearate SUB instead of just using PUT statements? Well, if you plan on using a graphics lib with this code, it'll be very handy. This will be the only place where you'll need to replace the PUT statement. Plus, with this routine, we're only using one sprite array instead of many. This significantly reduces the amount of array memory needed.

SUB PutSprite (FileName$, X%, Y%, PutType%)
    DEF SEG = VARSEG(Sprite%(0)): BLOAD FileName$, VARPTR(Sprite%(0))
    IF PutType% = 0 THEN
            PUT (X%, Y%), Sprite%, PSET
        ELSEIF PutType% = 1 THEN
            PUT (X%, Y%), Sprite%, AND
        ELSEIF PutType% = 2 THEN
            PUT (X%, Y%), Sprite%, XOR
    END IF


The ReadData Sub

Here, we fill our Map% array with values of 10, which we use to designate a plain wall. Notice that the maximum X and Y values of the current map are passed into this SUB. If our map was bigger, say 20 by 20, then we would pass values of 20 to the MaxX% and MaxY% variables. This setup also allows you to have maps with differing X and Y values, such as, say, 20 and 10 or 14 and 31, etc. The current map array supports maps up to 170x170 in size, which, trust me, is big enough. :)

SUB ReadData (MaxX%, MaxY%)

    FOR Y% = -3 TO MaxY% + 3
        FOR X% = -3 TO MaxX% + 3
            Map%(X%, Y%) = 10
        NEXT X%
    NEXT Y%

Now, we read our map values from the data. Notice that reading begins 3 columns after the beginning of the Map% array and ends 3 columns before the end of the Map% array. This is 'cause we have to fill some 'dummy' data for the edges of our map.

We need the 'padding' on the outside for our DrawLocation sub, which will check the map values 3 spaces ahead of the player's current direction to draw the display correctly.

    FOR Y% = 0 TO 9
        FOR X% = 0 TO 9
            READ Map%(X%, Y%)
        NEXT X%
    NEXT Y%


3. Final words

Well... These tutorials are written for YOU. So tell me what you want to read about next. I'm almost out of ideas here, so I need YOU to tell me what you want to know. How about an explanation of shops and inns? Does that sound good? Or maybe something else... Let me know!

E-mail me (darkdread@funeralart.net) and let me know what you want!



4. Licence


Download a zip archive with this tutorial and all source code / data files!

Absolute Beginner's ASM Tutorial

Written by Peter O'Rourke

Hello! Welcome to yet another ASM tutorial. My name is Peter O'Rourke, and I'll be your guide throught the amazing world of ASM.

Right, over-the-top introduction out of the way, we can begin.In writing this tutorial, I'm assuming that you know no ASM at all, but are reasonablycompetent with QuickBASIC programming (as some will be required to run the programs you create in ASM).

ASM, or assambler is a very low-level language. In fact it is as low level as you can get without writing your programs manually in bytes of machine code! ASM gives you almost complete control over your PC's operation, enabling you to use the mouse, make your own keyboard handlers and even make operating systems. However, operating systems and keyboard handlers are beyond the scope of this beginner's tutorial. For now I'll concentrate on getting a mouse cursor on your screen. To start with we'll look at Debug. It's an old DOS relic that is present on every Windows PC nowadays, even XP(!). To get to it, go Start > Run, and type "command" in the box and hit enter. You will be taken to the DOS command prompt (If it does not fill the screen, hit Alt+Enter so it does). Now type "debug".

You will be greeted by a less than intuitive interface:


This is where you control Debug from. To see a list of commands type ? and hit enter. Pretty amazing, huh? No? Oh well. To enter your ASM code, you'll have to type a and hit enter. Do this and you'll see something like this:


The part before the colon doesn't matter, it varies. now type in the following:

push ax
mov ax,12
int 10
mov ax,4c
int 21
pop ax

(Press enter again until you get back to the - prompt.)

Now, let's go through the code step by step. Firstly, push ax and pop ax are a safety precaution. They save the initial value of ax in case another program needs it. mov ax,12 is a very simple statement. It tells the processor to put the hexadecimal number 12h into ax, just as ax% = &H12 would put 12h in the variable ax% in QBASIC. Also you can move values from variable to variable e.g. mov ax,bx would copy the value of bx into ax. What you can't do is copy a 16 bit (big) value into an 8 bit (small) register. Hence you cannot do mov bl,ax as bl is an 8 bit register and ax is a 16 bit register.

Here is a list of the registers:

8 bit registers
16 bit registers
al, ah
bl, ah
cl, ch
dl, dh
ax, bx
cx, dx
si, di
bp, sp

I'll cover those in more depth in a later tutorial. Now, back to the program. The next line is int 10. This calls interrupt 10h, which does something depending on the values in the registers. In this case, it sets the screen mode to 12h, which is coincidentally screen 12. The rest of the program tells DOS it's finished (this is required after this particular interrupt call.) You should now be at the - prompt. type g and you will enter Screen 12. Type q at the - prompt and you will leave debug, but don't go back to Windows yet! Type debug again to return to debug, and type a to get into assembler mode. Now type the following:

push ax
mov ax,1
int 33
pop ax

(Press enter again until you get back to the - prompt.)

The push and pop do the same thing as before - save and restore the state of ax. However, AX is now being set to 1, and the interrupt called is 33h. 33h is the mouse interrupt, which lets you show the mouse and hide the mouse and lots of other things. Because AX is set to 1, the mouse driver shows the mouse on the screen. Type g at the - prompt and you should now see the mouse cursor on the screen. You can move it about just like you would in Windows. Now type q, to get out of debug and type exit at the command prompt to leave the DOS box. There, that wasn't too hard, was it?

That's the end of this tutorial, and hopefully I'll be making another one soon. Have Fun!

- Peter O'Rourke

Written on 9/10/2004 - 10/10/2004. Time of completion: 10:33.

Contact Peter O'Rourke through this link.

A Pure-QB Star-Sky Routine

Written by VonGodric


I found on your forum (on your site) that you still want some content for the issue #3. So I dunno if you still want stuff, but here I put together small star-sky routine in pure qb. It's small and rather simple in my opinion, but it could give some creative ideas (or flames) to people. So if you want it then here it is:


Pete says: "Sounds good to me!"  Here's the code:

'Small useless star routine in pure QB.
'Nothing much, just enjoy. I don't care how do you use this code
'so feel free to do with it whatever you want to do.
'By VonGodric
'October 2004

SCREEN 12               'Set Screen mode
CONST NofS = 200        'Number of stars
TYPE Stardata
  Sx    AS INTEGER     'Xpos
  Sy    AS INTEGER     'Ypos
  SxS   AS INTEGER     'X speed
  SyS   AS INTEGER     'Y speed
  Sc    AS INTEGER     'Color
DIM Star(NofS) AS Stardata

FOR i = 1 TO NofS       'Set star data
  Star(i).Sx = 320
  Star(i).Sy = 240
  Star(i).SxS = RND * 32 - 16   'Random X and Y moving
  Star(i).SyS = RND * 24 - 12   'angle.
  Star(i).Sc = RND * 14 + 1     'Random color

  'First draw a star to the screen
  FOR St = 0 TO NofS
     IF Star(St).Sx < 160 OR Star(St).Sy < 120 OR Star(St).Sx > 480 OR
Star(St).Sy > 360 THEN
        'If Star is closer to the edge then draw bigger bigger one
        CIRCLE (Star(St).Sx, Star(St).Sy), 1, Star(St).Sc
      END IF
     PSET (Star(St).Sx, Star(St).Sy), Star(St).Sc

  'Do some delay
  Stopper# = TIMER
  DO: LOOP UNTIL TIMER - Stopper# > .01#

  FOR St = 0 TO NofS
     'Erase old star
     IF Star(St).Sx < 160 OR Star(St).Sy < 120 OR Star(St).Sx > 480 OR
Star(St).Sy > 360 THEN
        'If it was a bigger on then delete bigger one.
        CIRCLE (Star(St).Sx, Star(St).Sy), 1, 0
     END IF
     PSET (Star(St).Sx, Star(St).Sy), 0

     'Move it.
     Star(St).Sx = Star(St).Sx + Star(St).SxS
     Star(St).Sy = Star(St).Sy + Star(St).SyS

     'If star is out of the screen then set it again to center
     IF Star(St).Sx < 0 OR Star(St).Sx > 640 OR Star(St).Sy < 0 OR
Star(St).Sy > 480 THEN

        Star(St).Sx = 320       '* RND Add these for a nice effect.
        Star(St).Sy = 240       '* RND
        Star(St).SxS = RND * 32 - 16
        Star(St).SyS = RND * 24 - 12
        Star(St).Sc = RND * 14 + 1
     END IF
LOOP UNTIL INKEY$ > ""  'wait till key is pressed.

'Hope you enjoyed it!

Download this source code: stars.bas, or visit VonGodric's WizGUI sites: here and here.

Part I: The Basics, Chapter 4

Written by Neo Deus Ex Machina

This is the second edition of Neo Deus Ex Machina's new QBasic tutorial series, QBNow! Starting last issue with the absolute basics of QB, this monthly tutorial series will turn a complete newbie into a seasoned veteran in no time! Every month, a new edition of QB Now! will be published in QB Express, which will build off of what you learned in the previous issue. Make sure you stay tuned!

In this issue, we present you Chapter 4 of QB Now! Part I: The Basics, which will teach you about QBasic structure, and modifying the content of your variables to a more useful format.

If you can't wait for the next issue, you can download a PDF version of QBNow! Part I: The Basics in its entirety right here! (This includes Chapters 1-5 and all appendices.)




In this chapter, structuring your code will be discussed, with topics like making choices and data modifiers. These are things you really need to know, because they are used very often. So study this chapter well.


In this chapter the main topic is structuring your code. A very important aspect of this structuring is indenting. Indenting is the addition of tabs to your code to make it more readable. The position of these tabs is usually when a separate code-section is created. All will become clear when you arrive at the section about choices. My advice to you is to get used to indenting yourself, because it really makes your code better to understand. Now, first let’s go to the data modifiers.


In QB there are functions (note functions) for accessing and retrieving data from strings, and for converting one datatype into an other.

Let’s first start with the String Modifiers.


Run the following example to see some string modifiers in action:

INPUT "Enter your name: ", yourname$
PRINT "Your name in capitals: "; UCASE$(yourname$)
PRINT "Your name in lowercase: "; LCASE$(yourname$)
PRINT "Length of your name: "; LEN(yourname$)
PRINT "First character: "; LEFT$(yourname$, 1)
PRINT "Last character: "; RIGHT$(yourname$, 1)
PRINT "2nd character: "; MID$(yourname$, 2, 1)
INPUT "Enter spaces, a word, and again spaces: ", lstr$
PRINT "Clipped off first spaces: "; LTRIM$(lstr$)
PRINT "Clipped off last spaces: "; RTRIM$(lstr$)
PRINT "Here are 10 spaces: "; SPACE$(10)
INPUT "Enter a number: ", n%
PRINT "Stringed number:"; STR$(n%)
INPUT "Enter again a number: ", m$
PRINT "Numerical value:"; VAL(m$)
INPUT "Now enter 1 character: ", char$
PRINT "ASCII code of character:"; ASC(char$)
PRINT "20 of these chars: "; STRING$(20, char$)
INPUT "Now enter a value between 33 and 255: ", ascii%
PRINT "Corresponding ASCII character: "; CHR$(ascii%)

Phew, that was a whole lot. Let’s explain now.

UCASE$(s$)Returns a string equivalent to the string s$ in upper case. (A-Z).
LCASE$(s$)Returns a string equivalent to the string s$ in lower case. (a-z).
LEN(s$)Returns the length of the string s$.
LEFT$(s$, n%)Returns the first n% characters of the string s$.
RIGHT$(s$, n%)Returns the last n% characters of the string s$.
MID$(s$, p%, n%)Returns n% characters starting at character p% in string s$. The first character in the string s$ is number 1, the last is LEN(s$).
LTRIM$(s$)Removes all spaces in front of s$ and returns this.
RTRIM$(s$)Removes all spaces at the back of s$ and returns this.
SPACE$(n%)Returns a string consisting of n% spaces.
STR$(n%)Returns the string equivalent of number n%. E.g. a% = 5, then STR$(a%) will return " 5".
VAL(s$)Returns the number equivalent of string s$. It’s like STR$ reversed.
ASC(char$)Returns the ASCII Code of a character in char$.
STRING$(n%, c$)
STRING$(n%, c%)
Generates and returns a string consisting of n% times the same character, c$. If instead of a string c$ you enter a number c% as the second parameter, this is the ASCII code of the character to duplicate.
CHR$(ascii%)Returns the character connected with the ASCII code ascii%. It’s like ASC reversed.

I don’t assume anyone would understand this fully in the beginning, that’s why I advice you to practice a bit with the different Functions described above.

I’ll now go a little bit deeper into the subject of Functions.

First read the section Commands: Statements and Functions? on page 4 to begin with, if you hadn’t done it already.

If you would just type LEFT$("Hello", 2) in the QB IDE and try to run it, you’ll get an error. This is because Left is a function, and functions return values. It is required in QB to capture the values returned. Something has to be done with the values, a return value simply cannot be ignored. You could e.g. do A$ = LEFT$("Hello", 2) to store the return value of the function Left into a string A$ (which will obviously contain "He"). So, QB requires you to do something with the return value. On the other hand, statements do not have this since they do not return a value, e.g. the Print or Color statement. Try to keep this in mind.

Note that in C/C++, return values can be ignored.

Also, there is also a MID$ statement. See the following example.

A$ = "Hello World"
MID$(A$, 8, 4) = "ario"

I’m sure you know what I mean after running this example. It’s actually the MID$ function, but reversed. In this case, you don’t want to retrieve some characters from the string, but change some characters in the string. Further, it’s just the same as MID$ Function.

Now let’s change subject to other data modifiers.


A very important part of data modifiers are the conversion functions. They are used to convert data from one datatype to another. Usually data gets lost during this operation. Some of these modifiers are also mathematical operators, and I will show them here as well. Since this is quite easy to understand for anyone who has little maths knowledge, I don’t give an example here. I’ll just go to the function table right away.

CINT(number)Returns an integer that is number converted to integer. Note that when the decimal <= .5 then CINT will round towards zero, if decimal > .5 then CINT will round away from zero.
CLNG(number)Same as CINT, only converts to long.
CSNG(number)Converts a number to single precision and returns this.
CDBL(number)Converts a number to double precision and returns this.
INT(number)Converts to integer by always rounding down.
FIX(number)Converts to integer by chopping off decimals.
ABS(number)Gets the absolute value of number (erase sign).
COS(number)Gets the cosine of number.
SIN(number)Gets the sine of number.
TAN(number)Gets the tangent of number.
ATN(number)Gets the arctangent of number.
SGN(number)Gets the sign of number. If number = 0 then SGN will return zero, if number < 0 then SGN will return –1, else if number > 0 then SGN will return 1.
EXP(number)Gets e ^ number (e = 2.718281828...)
LOG(number)Gets the natural logarithm of number (EXP inversed).
SQR(pos.number)Gets the cube root of a positive number.
HEX$(number)Gets the hexadecimal equivalent of number.
OCT$(number)Gets the octal equivalent of number.
CVI(s$)Binary conversion of a 2 byte-string to integer.
CVL(s$)Binary conversion of a 4 byte-string to long.
CVS(s$)Binary conversion of a 4 byte-string to single.
CVD(s$)Binary conversion of a 8 byte-string to double.
MKI$(number)Binary conversion of an integer to a 2 byte-string.
MKL$(number)Binary conversion of a long to a 4 byte-string.
MKS$(number)Binary conversion of a single to a 4 byte-string.
MKD$(number)Binary conversion of a double to a 8 byte-string.

Everything except maybe the last 10 should be clear. Math-experts may already have noticed that QB doesn’t have arcsine or arccosine functions. That is because they can be derived from other routines. Anyone who doesn’t like maths, now go to the next page! :)

ArcsineArcsin(x) = ATN(x / SQR(1 – x * x))
ArccosineArccos(x) = 2 * ATN(1) – ATN(x / SQR(1 – x * x))
Sine HyperbolicusSinh(x) = (EXP(x) – EXP(-x)) / 2
Cosine HyperbolicusCosh(x) = (EXP(x) + EXP(-x)) / 2
Tangent HyperbolicusTanh(x) = (EXP(x) – EXP(-x)) / (EXP(x) + EXP(-x))
AreasineArcsinh(x) = LOG(x + SQR(x * x + 1))
AreacosineArccosh(x) = LOG(x + SQR(x * x – 1))
AreatangentArctanh(x) = LOG((1 + x) / (1 – x)) / 2
SecantSec(x) = 1 / COS(x)
CosecantCosec(x) = 1 / SIN(x)
ContangentCotan(x) = 1 / TAN(x)
ArcsecantArcsec(x) = 2 * ATN(1) – SGN(x) * ATN(1 / SQR(x * x – 1))
ArccosecantArccosec(x) = SGN(x) * ATN(1 / SQR(x * x – 1))
ArccotangentArccotan(x) = 2 * ATN(1) – ATN(x)
Secant HyperbolicusSech(x) = 2 / (EXP(x) + EXP(-x))
Cosecant HyperbolicusCosech(x) = 2 / (EXP(x) – EXP(-x))
Cotangent HyperbolicusCotanh(x) = (EXP(x) + EXP(-x)) / (EXP(x) – EXP(-x))
AreasecantArcsech(x) = LOG((SQR(1 – x * x) + 1) / x)
AreacosecantArccosech(x) = SGN(x) * LOG((SQR(x * x + 1) + 1) / SGN(x) * x)
AreatangentArccotanh(x) = LOG((x + 1) / (x – 1)) / 2
LogarithmLogN(x, n) = LOG(x) / LOG(n)

Well, so far about the maths business, that’s why we have maths... The point I wanted to make here is that if you miss your own mathematical function, you can almost always make it yourself. Anyway, let’s get on with structuring!

There are still 8 conversion routines which no one ever uses (I think). These are CVIMBF, CVLMBF, CVSMBF, CVDMBF, MKIMBF$, MKLMBF$, MKSMBF$ and MKDMBF$. These are the same as their respective functions without ‘MBF’, but the only difference is that these work with (old?) Microsoft Binary Format. I’ve never used them... :)


In every programming language, making choices is an all-time thing you do. Here I’ll discuss the most popular way of making choices, namely IF ... THEN. Take a look at the following example:

INPUT "Enter a number: ", Number
IF Number = 0 THEN PRINT "Number equals zero"
IF Number <> 0 THEN PRINT "Number differs from zero"

This code should be clear when you pronounce the code for yourself. If number is zero, then print this on the screen. If number is not zero, then print that on the screen.

In QB, there is no character for for "not equal to". Instead you should use <> (above comma and point on your keyboard) for not equal to.

In the previous example, the Ifs were inefficiently used. There are some features of IF I’ll discuss now. Take a look at this:

INPUT "Enter a number: ", Number
IF Number = 0 THEN PRINT "Is zero" ELSE PRINT "Not zero"

This is the same example as previous, but now in three lines. An ELSE clause has been used in this example. Every IF can also contain one ELSE clause, which will be run if the condition in the IF statement is false. Obviously, the ELSE clause will not be run if the IF statement is true.

Another option of the IF ... THEN is to have it execute multiple lines when true or false:

PRINT "Division program, calculates 1 / x"
INPUT "Enter x: ", x%
IF x% = 0 THEN
	PRINT "Division by zero is not allowed"
	PRINT "Quitting.."
	PRINT "Division is allowed by x ="; x%
	PRINT "The answer is:"; CDBL(1# / x%)

This program asks the user to input a value to calculate 1 / x with. Since division by zero is not allowed, it checks if x = 0, and if so it prints a message and doesn’t calculate anything. If it is not zero, the calculation will be executed.

So, in this case, everything between IF and ELSE will be executed when the IF condition is true, and everything between ELSE and END IF will be executed when the IF condition is false. You can however also make IF statements on more lines without ELSE. ELSE is optional:

PRINT "Division program, calculates 1 / x"
INPUT "Enter x: ", x%
IF x% = 0 THEN
	PRINT "Division by zero is not allowed"
	PRINT "Quitting.."
PRINT "Division is allowed by x ="; x%
PRINT "The answer is:"; CDBL(1# / x%)

Now the program will be automatically quit when x = 0, so it doesn’t even reach the division. The point is that ELSE is optional, as well as the thing we’ll discuss now: ELSEIF.

If you want to make an IF on multiple lines, an END IF clause is required to specify the end of the IF block, else QB won’t know what should or what shouldn’t be executed when IF is false or true. If you make an IF on one line, END IF is not to be used.

Now about ELSEIF, take a look at the example:

INPUT "Enter a number: ", n%
IF n% < 17 THEN
	PRINT "Number is below 17!"
	PRINT "Number equals 17!"
	PRINT "Number is above 17!"

As you can see in this example, ELSEIF actually is a second IF. The only difference is that ELSEIF will only be checked for true or false if the IF is false. If the ELSEIF is also false, QB will then go to the ELSE, only then. The advantage of ELSEIF is that you can use large numbers of them in one if.

To see the difference between using 2 IF’s and using one IF and one ELSEIF, see the following example.

INPUT "Enter a number: ", n%
PRINT CHR$(13) + CHR$(10)
PRINT "This is the output of the 2 IFs way:"
IF n% < 10 THEN PRINT "Smaller than 10"
IF n% < 25 THEN PRINT "Smaller than 25"
PRINT CHR$(13) + CHR$(10)
PRINT "This is the output of the IF + ELSEIF way:"
IF n% < 10 THEN
	PRINT "Smaller than 10"
	PRINT "Smaller than 25"

When you run this and enter a number like 7, you’ll see the difference clearly. :)

If you want an IF statement on one line, you cannot use ELSEIF. ELSEIF can only be used in multiple-line IF statements.

IF ... THENThis is the general IF clause. If the condition on the points is true, then the code after THEN is executed. If it is false, if goes on to any elseif or else if there.
ELSEIF ... THENTo extend an IF clause with more options, this is just like IF.
ELSETo extend an IF clause with a general option, this clause will only be executed if no other clause is true.
END IFDenotes the end of an IF block.


If you have 100 options for an IF clause, you’ll have to type like hell to implement all 100 options. An other, and overall better choice making command is SELECT. It is less typing, keeps better structure in your program, is easy to use and is sometimes faster. Here’s an example:

INPUT "Enter a number: ", n%
	CASE 1, 2, 3
		PRINT "It is one, two or three"
	CASE 4 TO 6
		PRINT "It is in the range 4-6"
	CASE IS > 6
		PRINT "Larger than three"
	CASE IS < 0
		PRINT "Smaller than zero"
		PRINT "It is zero"

As you can see in this example, with select it is easy to add multiple options at once. The structure of the select block is as follows. To open the block you type SELECT CASE and thereafter the variable to check. Then you add CASEs with thereafter the options the variable may have. You can do option comma option etc, or you could do option TO option. With IS you can make greater/smaller equations, and finally there’s ELSE if no other case was true. It’s easy to understand after a couple of practices.

Only one CASE can be executed per SELECT block, just like the IF-ELSEIF construction.

Just like with IF, select is automatically multiple lines, which means everything between the CASE keywords (or END SELECT) will be executed when the specific condition is true. Now a small summary.

SELECT CASE varStarts the select block with variable var to check.
CASE option [, option, ......]
CASE IS expression
CASE range1 TO range2
These are the possible case clauses. Firstly you can add as many options as you want. The case will be true when one or more of these options exactly equal the variable (keep this in mind when using floating point variables!). Secondly, you can add an expression to a case by means of IS. If the expression is true, the case will be executed. Thirdly, you can specify a range in which the variable can be. If the variable is in the range, the case is true. Fourthly, when all other cases are false, the case else will be executed.
END SELECTThis denotes the end of a select block.


The last paragraph in this chapter is about jumping. Not much experienced programmers use jumping though, only when it’s really necessary. That’s also the point, you still need to know it. Also, I’ll explain it now because many beginning programmers use it a lot. :) Here’s an example on how to use it:

PRINT "Hello"; CHR$(13); "Please enter you name: ";
INPUT "", yourname$
IF yourname$ = "" THEN
	PRINT "You entered nothing!"; CHR$(13)
	GOTO Again
PRINT "So your name is "; yourname$
PRINT "Is this correct (Y/N)?"
		GOTO Again
		GOTO EndNow
		GOTO TypeAgain


As you can see in this program, GOTO can be used to control the flow of your program. With GOTO, you can specify a label and the program will go to the label in your program. A label is a name with a colon behind it on a separate line. Your program will then just continue normally from the label.

Label:A label in your program. Must consist of normal characters.
GOTO LabelThis statement makes the program jump to Label. From there, the program will run further.

If you specify a label in Goto that doesn’t exist, you’ll get an error.

The main disadvantage of using goto’s is that the more goto’s you use, the more your code will look like spaghetti. That’s why unreadable, criss-cross linked programs are called programs with spaghetti code. Therefore they invented another jumping statement, which is able to induce less spaghetti :). This statement is GOSUB. Here’s an example:

PRINT "ABC Formula."
A! = 0
B! = 0
C! = 0
N% = 0
X1! = 0
X2! = 0
GOSUB GetValues
GOSUB Calculate
GOSUB PrintForm

INPUT "Enter A: ", A!
INPUT "Enter B: ", B!
INPUT "Enter C: ", C!

PRINT "The function:"
PRINT A!; " * x ^ 2 ";
PRINT ABS(B!); " * x ";
PRINT "Has"; N%; " intersections with the x-axis"
IF N% >= 1 THEN PRINT "X1 ="; X1!
IF N% = 2 THEN PRINT "X2 ="; X2!

D# = B! ^ 2 – 4 * A! * C!
IF D# < 0 THEN
N% = 0
	N% = 1
	X1! = (-B! + SQR(D#)) / (2 * A!)
	N% = 2
	X1! = (-B! – SQR(D#)) / (2 * A!)
	X2! = (-B! + SQR(D#)) / (2 * A!)

This quite large example is actually the ABC Formula, taught on school in Math classes of... what is it? Year 3? Anyway, it’s math :). In the above example, Gosub is used to do various sections of the program in a separate part of the program. That’s why Gosub is some better at this than Goto; you can more easily read it. Unlike Goto, Gosub requires a Return at the end of the block to jump to. This is the great difference with Goto. Goto just jumps to a label and runs the program further from there. Gosub jumps to a label, runs the program normally, until it encounters a Return statement. The program will then jump back to the place of the last Gosub, in other words, the Gosub that jumped to the certain section. Then the program will run further. Try to keep this in mind and look again at the example. Now you should see why it still runs as it should! :)

GOSUB LabelJumps to Label and executes code from there until it reaches a Return statement, then the program will contain after the Gosub statement.
RETURNThis statement causes the program to return to the last Gosub called.

Avoid the nesting of Gosub statements. This will eventually result in an ‘Out of Stack Space’ error, which will immediately halt your program.

Nesting is consequently putting blocks of code in other blocks of code, etc. E.g. putting an IF in an IF, which also is in an IF, etc. Nesting can be done with various commands like Gosub, If, Select and later on you’ll learn it can also be done with a.o. loops and procedures.

I advice you to put your Labels referred to by Gosub at the end of your program, after an end. This way they’ll never be run without Gosub calling it.


Ok, this chapter program is about palindromes, which are words with a special feature. Viz. that when they are reversed, it still is the same. E.g. RADAR is a palindrome. Here the program comes:

DIM Original AS STRING, Reversed AS STRING

PRINT "@@@@@@@@@@@@@@@@@@"
PRINT " @@@@@@@@@@@@@@@@"
PRINT "  Palindromotron"
PRINT " @@@@@@@@@@@@@@@@"
PRINT "@@@@@@@@@@@@@@@@@@"

PRINT CHR$(13); "Enter a word below to see if it is"
INPUT "a palindrome: ", Original
IF LEN(Original) = 0 THEN GOTO AgainTypeWord
Original = UCASE$(LTRIM$(RTRIM$(Original)))

GOSUB ReverseWord

PRINT "The word "; Original;
IF Original = Reversed THEN
	PRINT " is a palindrome"
	PRINT " is not a palindrome"

PRINT CHR$(13); "Do you want to enter another word?"
		GOTO Again
		GOTO AgainTypeYesOrNo


‘this is a crappy inefficient algorithm, but since I have
‘to use goto.. lol ;)
Reversed = ""
CurrentPosition% = LEN(Original)
Reversed = Reversed + MID$(Original, CurrentPosition%, 1)
CurrentPosition% = CurrentPosition% - 1
IF CurrentPosition% > 0 THEN GOTO NextPosition

Now a few exercises to remember what you saw in this chapter... :)

Exercise #1: Make a kind of quiz with about 20 questions. At the end of the questions, show how many the player guessed wrong.

Exercise #2: Ask the user to input a whole english sentence. Then ask the user to input a letter (a till z). The program outputs the number of those letters found it the sentence, either lower case or capital.

Exercise #3: Ask the user for as much numerical values as he likes to. Then, calculate the average and sum of all these values and show them to the user.

Exercise #4 (harder): Show a character on the screen. Then ask the user what way it should go. (Ask for values like ‘left’, ‘right’, ‘up’ and ‘down’). Then move the character on the screen in the direction the user entered. Let the user be able to walk around the screen if he wants to, i.e. the program has to repeat itself after moving the character. Exit the program when the user types something like ‘exit’ or ‘quit’. Also make sure the character cannot move out of the screen (you’ll get an error then :) ).



Exercise #1: Make a kind of quiz with about 20 questions. At the end of the questions, show how many the player guessed wrong. (I’ll do 5 questions here... lol).

Correct = 0
Wrong = 0

Question = "What does HTML stand for?"
Answer = "hypertext markup language"
GOSUB PoseQuestion

Question = "What does PHP stand for?"
Answer = "php hypertext protocol"
GOSUB PoseQuestion

Question = "Are there already harddisks of 1024 GigaByte?"
Answer = "yes"
GOSUB PoseQuestion

Question = "Do you like QB?"
Answer = "yes"
GOSUB PoseQuestion

Question = "Did you crib to make this exercise?"
Answer = "no"
GOSUB PoseQuestion

PRINT "You answered"; Wrong; "questions incorrectly"
PRINT "You answered"; Correct; "questions correctly"

INPUT Question + " ", Q$
IF LEN(Q$) = 0 THEN GOTO AgainQuestion
IF Q$ = Answer THEN
	Correct = Correct + 1
	Wrong = Wrong + 1


Exercise #2: Ask the user to input a whole english sentence. Then ask the user to input a letter (a till z). The program outputs the number of those letters found it the sentence, either lower case or capital.

INPUT "Enter a sentence: ", sentence$
GoodSentence$ = LCASE$(LTRIM$(RTRIM$(sentence$)))
IF LEN(GoodSentence$) = 0 THEN GOTO Nothing
INPUT "Enter one letter (a-z): ", letter$
Letter$ = LCASE$(letter$)
IF LEN(letter$) <> 1 THEN GOTO AgainNothing
IF letter$ < "a" OR letter$ > "z" THEN GOTO AgainNothing

N% = 0
GOSUB CountLetters
PRINT "The sentence:"
PRINT sentence$
PRINT "Contains"; N%; letter$; "’s"

NowPos% = 1
IF MID$(GoodSentence$, NowPos%, 1) = letter$ THEN N% = N% + 1
NowPos% = NowPos% + 1
IF NowPos% <= LEN(GoodSentence$) THEN GOTO NextPos


Exercise #3: Ask the user for as much numerical values as he likes to. Then, calculate the average and sum of all these values and show them to the user.

Values = 0
Sum = 0

INPUT "Please enter a number: ", Value
Values = Values + 1
Sum = Sum + Value
INPUT "Do you want to enter another number? (Y/N) ", YESNO$
	GOTO AnotherValue
	GOTO AgainQ

PRINT "You entered"; Values; "values"
PRINT "The sum is"; Sum
PRINT "The average is"; Sum / CSNG(Values)


Exercise #4 (harder): Show a character on the screen. Then ask the user what way it should go. (Ask for values like ‘left’, ‘right’, ‘up’ and ‘down’). Then move the character on the screen in the direction the user entered. Let the user be able to walk around the screen if he wants to, i.e. the program has to repeat itself after moving the character. Exit the program when the user types something like ‘exit’ or ‘quit’. Also make sure the character cannot move out of the screen (you’ll get an error then :) ).

X% = 40
Y% = 12

GOSUB MakeScreen
		IF X% > 1 THEN X% = X% - 1
		IF X% < 80 THEN X% = X% + 1
		IF Y% > 4 THEN Y% = Y% - 1
		IF Y% < 25 THEN Y% = Y% + 1
		GOTO EndNow
		LOCATE 2, 1
		PRINT "Invalid input"
GOTO Again


PRINT STRING$(80, 205)
PRINT "Enter left, right, up, down or quit"

Next Month: Chapter 5 of QBNow! will show you how to program loops and do boolean operations.

You can email Neo at arecinos@gmx.net.

Final Word

People say the QB community is dying. Well, not on my watch! Sure, it's in a slump right now, but if you look hard enough, there's a whole lot going on. And hopefully now that you've read this issue, you're inspired to go out and do some programming of your own. Or update your QB site. Or enter a coding competition. Or help some newbies learn how to program. Or write an article for our next issue (!).

Though I had my doubts a few days ago, this turned out to be a really great issue, and I'm hoping next month's is just as good, if not better. But if that's going to happen, YOU need to help me. Send stuff in! You know you want to! (This is your magazine too, you know.)

Now, if you'd excuse me, I've got to go catch some Z's. I'm one tired little QB programmer.

Anyhoo, I'll see you next month.

But until then...END.


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