• Please review our updated Terms and Rules here

ASCII Video game

Jtterbug

Experienced Member
Joined
Sep 13, 2010
Messages
158
Location
Pacific NW
Hello,
I am wondering how one could program a game like Ladder for CP/M in BASIC? Here is what Ladder looks like;

http://ostermiller.org/ladder/

You are the P in the picture at the lower left. I want the "BASICS" ;) for creating a game like this. I would like the main character to be the Hash symbol, and "O"s to appear at the top left. "H"s can be ladders and = can be the floor. Kind of like Donkey Kong.

Sorry if this is off topic.
 
Last edited:
I wouldn't call it off topic -- you don't get much more 'vintage programming' than doing an ASCII game in CP/M using Basic.

Though the question becomes WHAT basic? There were several different flavors for CP/M machines -- though the most powerful was cBasic, which was at least compiled and in many ways a half-decade ahead of it's time. ESPECIALLY cBasic 2, which included the ability to drop using line numbers, build functions instead of goto, typecast to integer, code chaining, etc, etc...

Then there's Microsoft Basic (mbasic) which was an interpreted language... which being uncompiled was nowhere near as useful for writing games especially when a certain shortcoming (that I'll soon mention) is brought into the equation.

Now, it's been almost thirty years since I dealt with cbasic, but I maintained a pretty large program in it at one point -- so I can tell you what will and will not work.

Something you might find handy is the CBASIC 2 manual.
http://www.cpm.z80.de/manuals/cbasic-m.pdf

The deal breaker for making a cBasic or mBasic game rears it's ugly head in the COMPLETE LACK of commands for positioning the cursor on screen to draw things. This means you either have to know where video memory starts and what format it's in to peek/poke it directly, or try to implement cursor positioning using the extended console codes (hoping they are actually supported), or non-standard ones specific to the hardware. This is why when targeting CP/M many programmers moved over to Turbo Pascal 3 in the first place. There is no "print at", "print@" or "gotoxy" in CBasic... at least not until GSX in CP/M 3 came along, and not all devices supported it. This alone could prevent you from writing a CP/M game, or at least a portable one for anything less than CP/M 3.

This all stems from CP/M's only real output being a generic 'console' -- said console device could be a screen-- or it could be a teletype, printer, modem terminal, tickertape, cardpunch -- pretty much any serial in-order output device. As such the idea of 'going backwards' on the page was pretty much limited to backspace on the current line and that's IT. This lack of screen control is part of why CP/M quickly fell into the "also ran" category, with the release of CP/M 3 in '82 being a case of "too little, too late" compared to the much more powerful MS-DOS and the capabilities of the various ROM basics and proprietary DOS flavors from the likes of Apple and Tandy... and even third tier vendors like Logical Systems.

In that way, I'm really not sure BASIC is the language you would want to try to use under CP/M in the first place. Even if you were to peek/poke your way past it you'd need to know where video memory is located which entirely restricts you to one hardware target -- since no two machines generally put video RAM in the same place.

I'm going to write up a somewhat longer post outlining the general process of building a game in a language like basic -- we'll assume you have some way to get past the CP/M console limitation, and it will be a 'generic' outline of the design process so the information will be relevant to programming in other languages. I have a process of 'breaking it down into pieces' which can help a good deal in the planning stages -- and the best software always starts out with a plan and is built using good organizational skills.

See my "paku-paku' pac-man ripoff, which is actually a character based game, not graphics -- even if it looks like graphics that's just a tweak to 80x25 text mode.
 
Last edited:
As I said in my previous post, it's all about planning and organization -- when it comes to the planning and building the code, I like to use the WWH model instead of more complex (and time wasting) things like flowcharts. WWH means "What (variables), What (do we need to do with them) and How (do we do it).

I also like to use good naming practices -- thankfully cBasic lets you use meaningful names on items, and allows periods in your variable names to be used as space replacements. There's a very good article on the IBM linux developers site I like to point people at:

http://www.ibm.com/developerworks/linux/library/l-clear-code/

It's talking in C, but the lessons you can learn from that page apply to most every modern language... Of course if you are in mBasic or rom basics where you can only use one letter names for variables, well -- you are best off just REALLY documenting everything really well as you go along.

Moving on -- The first step is very simple -- WHAT information do we need to track. (variables)

For a game like this the biggest element will be the game map -- this would obviously make a nice two-dimensional array; assuming the language you are using supports two dimensional arrays. (Not all basic flavors do, cBasic does, I think mBasic does). We probably don't need anything more complex than integers for the locations, Some people would try to track it in video memory but as a rule that is even more work and video memory is slower than system ram. An 80x24 array is going to suck down memory like a pig, but there's not a lot you can do about that... though I would strip a line off the top and a line off the bottom for showing non-gameplay elements like score and lives left.

So we dim an array
dim playfield%(80,22)

What fills that array? Well, best practice is to define some named variables to say what each value in that array means. (mind you in Pascal these would be CONST, not variables... in C they would be DEFINE's)

playfield.empty=0
playfield.floor=1
playfield.ladder=2

etc, etc, etc... that way we aren't always guessing what our numbers mean. Thankfully with cBasic being compiled these don't suck all that much overhead and we can use names as long and as clear as we like. If we were talking mBasic, you'd still want them in variables as variables are faster than immediates in an interpreted language, but you'd be stuck with one letter names (making you run out of variables VERY quickly).

Then you have the various elements on the screen that are moving around -- the sprites. Even using characters as sprites the name 'sprite' works best for this. What values do sprites have? Well, there's the x and y coordinate of the screen, the previous x and y positions, the direction it wants to move, the direction it is actually moving, and what character code should be displayed for it. (or tile number in a graphics game)

How to handle that? Best bet is also an array. Let's say you are going to have four barrels max on screen, and the player. Storing them all in the same array lets you use one tight loop to show them on screen instead of iterating through each one with a different piece of code. I would make this a one dimensional array and use a offset.

So, we dim another array, setting up some values to calculate it's size.
sprite.count%=5
sprite.x%=0
sprite.y%=1
sprite.old.x%=2
sprite.old.y%=3;
sprite.inputDirection%=4
sprite.direction%=5
sprite.tile%=6
sprite.variableCount%=7 / x,y,old.x,old.y,inputDirection,direction,tile

dim sprites%(sprite.count%*sprite.variableCount%)

and some values to index it with.
sprite.player%=0;
sprite.barrel1%=1*sprite.variableCount%;
sprite.barrel2%=2*sprite.variableCount%;
etc, etc...

That way in your code, to access a sprite's y position for example you would just refer to it as:

sprites(sprite.player+sprite.y)

Which believe it or not is faster than letting it do the multiply in realtime for a two dimensional array.

Which is basically using an array to replicate some of the functionality objects or even records/structs bring you in more modern languages.

From there you have things like score, high.score, lives, max.lives, starting.lives, points.to.gain.a.life, etc, etc.

It is always easier to figure out what information you need to process before you lay down a single line of code to process it.

THEN on to the next step, WHAT are we doing with it?

Figuring out the central loop of your game code sounds simple -- and it is if you take the time to break it into smaller tasks. This is where MANY programmers screw it up as they will try to do everything on every single loop, or they will just put it all together in any order or do everything for a single screen element before moving on to the next one. This does not translate well when you get into more advanced programming relying upon timers, does not translate well if you use co-op multitasking internally or timer breakups -- and in general ends up a bit slopppy.

For my games, I always grouped like tasks into groups even if performed on different elements... so my breakdown of "what we're doing" ends up thus:

update the screen -- go through every sprite with a for loop and erase old.x,old.y and draw at x,y. By doing screen updates on their own we avoid 'flicker' type issues and do all of our screen interctions in one fell swoop. I do this first so that on the first time throug the loop everything is drawn.

Check inputs -- once we've drawn the old stuff, we want to read in any changes the user wants.

AI Logic these do basically the same thing as checking inputs, just for the elements the player does not control. This can actually be the most complex of things to do given it's often endless nested IF statements.

update positions -- take our inputs, compare them to the playfield to see if they are valid moves, if they are, change 'direction' to match and then do it. If they aren't, check the existing direction to see if it's valid, if it isn't stop moving, otherwise keep going.

check sprite collisions -- usually in games bad things happen when certain sprites collide, or good things could happen (like eating a super-power pellet in pac-man). Since we moved everything to it's position, it's now time to test for those events.

That's basically the top level of inside our loop -- from there we have to ask what other actions could occur. Classic 'other events' would be things like scoring points or losing a life. Instead of wasting time redrawing the number of lives and the score on every loop of the game logic, we just call a routine to redraw just those parts when it happens. This helps distribute the load and keep the game running as smoothly as possible.


Which covers the basic What/what -- just leaving "how"... and for how, well.. that's when you need to start writing real code.

Which I don't have the time to explain right now.
 
What is your ultimate goal? To learn how to program in BASIC? Or to be able to play Ladder?

I want to learn to program in BASIC(Or whatever language is best for this project) And Be able to say "I wrote this!".
 
The question is whether the general purpose CP/M operating system is your most ideal target, given what Deathshadow writes above regarding limited options of cursor addressing, at least methods that will work on most all CP/M compatible computers. If you aim at a specific machine, hardware support for graphics, sound and inputs will be more clearly defined.

By the way, the definition of arrays above looks like it contains some kind of enum or struct. I don't know cbasic and its capacities, but I have never seen that form of syntax in any traditional Microsoft based Basic. To me it looks more like higher level pseudo code in some Pascal or C-like language.

For a learning exercise, I would recommend a primer on the language. Try to solve some exercises and when you feel ready, study some old listings in order to try and understand what is going on. Make modifications and see what the differerence becomes. Once you are there, you may get somewhere useful when developing your own program from scratch. If you try to take a too big bite at once, the risk is you never get past the welcome screen of your game.
 
Well, if you are willing to go a little further afield, UCSD Pascal will support all the features and run on CP/M (plus a host of other systems).

As an example, look at this simple robot program, though designed for the Apple II. The "USES APPLESTUFF" statement would have to be changed to the appropiate one for your chosen system; I think the code presented here would work on a PDP-11 UCSD implementation and so should be easily modified to any other cursor equipped terminal UCSD Pascal version. Donkey Kong will be much more difficult and stress system to the limit.

http://robotchase.sourceforge.net/overview-summary.html#listing1
 
By the way, the definition of arrays above looks like it contains some kind of enum or struct. I don't know cbasic and its capacities, but I have never seen that form of syntax in any traditional Microsoft based Basic. To me it looks more like higher level pseudo code in some Pascal or C-like language.
cBasic was really ahead of it's time on things like that -- when it came to data structure handling you could do some stuff you didn't see outside C or Pascal.

Though even Gee-Whiz had a lot of capabilities programmers didn't use... like variable names that could be up to 40 characters in length, and periods being a valid character in variable names. It even allowed for typecasting via % (16 bit signed), ! (single precision real) and # (double precision) in addition to the various DEFxxx commands.

GW even had DEF FN, though that was limited to creating expressions instead of true functions.

These went unused by most programmers because 1) in an interpreted language long names parse slower, dragging execution under, and 2) because most people were coming to it from ROM basic on machines like the Commodore, Atari and TRS-80, and as such were unaware of even trying to do this sort of thing in BASIC.

But what I was doing above could just as easily have been done with GW-Basic... you put line-numbers before that code above and there's little reason that GW would choke on any of it excepting the two-dimensional array for the playfield map. The indexed array for the sprites would work just fine.

Though again, the long names would slow it to a crawl, which is one of the reasons GWBasic programmers weren't all that verbose.

I'd also point out the periods in basic are like underscores or camelbacks in modern languages, they do not indicate a record, they're just a space replacement. Like doing This_variable or thisVariable -- so it's not a structural element like you'd find in Pascal (where it's the same as -> in C) or PHP (where a period is string addition).

In general though, Microsoft Basic flavors greatest limitation was arrays -- monodimensional only, typically a maximum of 255 elements as even on 16 bit machines they used byte sized indexes -- It really relegated it to being a toy language that was cute to play with -- but real programmers went to ASM -- at least until high level language compilers they could afford like Turbo Pascal and Co. came along... at least so far as the guy in his basement on a shoestring budget was concerned.

Turbo really was a slap in the face to the whole industry -- a reasonably efficient high-level language compiler that could be run off a 160k single sided floppy with OS for less than $60, in an age where the nearest real competitor was five times that price, spanned two or more floppies and you had to exit the editor and manually link? Was SO quick to develop software when you had to boot from one floppy, edit on another, compile with a third, start the linker on a fourth, switch floppies to the library disk halfway through linking, and then write the executable to a sixth disk... Which was the normal behavior from the big name compilers of the time.

I mean take cBasic 2, which at it's 1981 release price was 1300 bucks -- the price at the time of a base model 16k Apple IIe or a fully loaded trash-80 Model III... (see why I laugh at Woz's alleged 'cost cutting') which is why very few people outside the corporate environment even got to play with it.

Which is interesting since three years later Microsoft qBasic seemed to have drawn from it's influence a LOT. Qbasic 1.0 being almost a clone of CBasic 3 with the GSX extensions... though Microsoft did the smart thing and moved over most of the GW-Basic graphics syntax... Which is just part of why DOS pwned and CP/M died alone with nobody to love it.
 
Last edited:
Oh dear. I think you may have misunderstood, when I said;

"I am wondering how one could program a game like Ladder for CP/M in BASIC? Here is what Ladder looks like;"

I meant;

Ladder is for CP/M. Can I make A game like it in BASIC?
 
For what it is worth, the DEF FN syntax exists in most Basics, at least the Microsoft derivates. I've used it plenty in Commodore Basic, which though only recognizes the two most significant letters of a variable name.
 
To clarify for those who aren't sure "Ladder is essentually a game which has been done in CP/M", some smuck simply came along and made it in Java or whatever! Of course it's not quite like the original (as mentioned on their site) cause CP/M has a few tricks up it's sleve unique to it! :D

I looked high and low for this game and I simply couldn't find it - I even looked on CPMUG and SIGM thinking it was a MBASIC game under those archieves, without luck. It appears to be something which would run under a majority (if not all) CP/M systems, though I read somewhere it was done on a Kaypro? I don't understand why it's hard to find cause Wikipedia mentions this game - is it copyrighted or something, or it's one of those hard to track down games or perhaps it comes under a different game?

The concept with Ladders is obviously a Platform game, I've seen a number of Platform games done in BASIC, though understanding them is always complicated when dealing with Spaghetti BASIC programs! :mad: Naturally you need arrays to store where everything is, platforms & ladders, so you can move your man up or down them, fall though holes if standing over them and if something collides with you (like with Donkey Kong), then there needs to be something to check if this is happening. Platform games from that perspective need constant checking based on where you man is onscreen, so having an array based on where they are maybe ideal.
 
It appears he was misunderstood. He had seen a CP/M game which he liked to make his own version of, not necessarily running in CP/M. I haven't checked how close FreeBASIC is to the classic dialects, i.e. if skills gained from learning that language will be easily transferred to other, older systems. If it contains very many powerful features, it can simplify the programming task but make it a bigger step to go back to some vintage interpreter. The same probably is true for the compiled CP/M cBasic mentioned above: very useful but may make the developer dependable on features not found elsewhere. If multiple formats are not desired, it won't be a problem of course.
 
carlsson wrote:

It appears he was misunderstood. He had seen a CP/M game which he liked to make his own version of, not necessarily running in CP/M.

Yeah sadly I picked up on that after half-way going though my bonkers post. I've just had a few mad days of hard work and the TV was driving me mad cause my favourite shows were cancelled due to media coverage of the New Zealand Earthquake! :( I don't mean any disrespect to the Kiwi's and I feel for those who were caught up in this disaster or have lost loved ones, though continous coverage of this disaster is horrible - even worse for here in Australia cause we're supposed to be going Digital TV starting Next Year and some stations are repeating this on their main channel and their News Channel - it drives me mad. Going Digital means more stations - so these idiots should lift their game and all have a "News Channel" each!!

I haven't checked how close FreeBASIC is to the classic dialects, i.e. if skills gained from learning that language will be easily transferred to other, older systems. If it contains very many powerful features, it can simplify the programming task but make it a bigger step to go back to some vintage interpreter. The same probably is true for the compiled CP/M cBasic mentioned above: very useful but may make the developer dependable on features not found elsewhere. If multiple formats are not desired, it won't be a problem of course.

The main problem which is based on the original CP/M game is how CP/M Deals with Input from the Keyboard. This was discussed in the Java-based version of the game and I know what they mean cause what happens is a delay happens for when you hold a key down. What happens is your character moves one position, stops briefly and then starts moving. I've got a simple platformer written in Locomotive BASIC which demonstrates this too and in that it's horribly bad because of the fast pace, your character moves one spot pauses and then gets squashed as your character waits. In Locomotive BASIC there are two Functions which are nearly identical. INKEY is used in conjunction a key number which functions very quickly, quite suitable for fast paced games. INKEY$ on the other is used to test if a certain String has been pressed, this is the one which causes something to pause and in the case of the CP/M Ladder program, they would use the CP/M equivalent.

At the moment I've been doing some Turbo Pascal examples which demonstrate this, the advantages of returning a String from a Key Press means a program is very portable from CP/M System to CP/M System. However to generate fast key press responces from a program in CP/M I think means one has to resort to system specific code or turning to the hardware. :nervous: My CP/M system can handle that since it allows you to Enter the System Firmware and use the relevant routine, though it's somewhat cheeky to apply this in CP/M given it wasn't really meant for Fast-paced games! :inlove:

Having said all that, I wonder how it would go in GEM using Locomotive BASIC 2.0. The Amstrad systems I use Locomotive had BASIC 1.0 and 1.1 which used the INKEY and INKEY$ as I mentioned earlier. I just don't know how hard it would be to get Locomotive BASIC 2.0 in GEM since the one I saw came on an Amstrad PC 1512. "Locomotive Software" probably still have a copyright condition on it as well! :nervous:
 
Having said all that, I wonder how it would go in GEM using Locomotive BASIC 2.0. The Amstrad systems I use Locomotive had BASIC 1.0 and 1.1 which used the INKEY and INKEY$ as I mentioned earlier. I just don't know how hard it would be to get Locomotive BASIC 2.0 in GEM since the one I saw came on an Amstrad PC 1512. "Locomotive Software" probably still have a copyright condition on it as well! :nervous:

IIRC, the OpenGEM collection includes Locomotive Basic along with all the other GEM software allowed to be freely distributed now.
 
However to generate fast key press responces from a program in CP/M I think means one has to resort to system specific code or turning to the hardware. :nervous: My CP/M system can handle that since it allows you to Enter the System Firmware and use the relevant routine, though it's somewhat cheeky to apply this in CP/M given it wasn't really meant for Fast-paced games! :inlove:
That's typical of inkey$ in all basic flavors, and the keyboard handling in pascal dialects too as TP4/later uses keypressed/readkey and tp3/earlier use keywaiting and read -- all of which basically wrap the DOS interrupts.

BUT -- there is a trick... paku paku for example uses int $21 for the keyboard having all those same 'problems' of press a key, move once, hold the key and you have to wait for the key-repeat. The trick? when they press the direction key just set the direction and do NOT stop moving until they hit something (like a wall) or another valid direction. If you read the keyboard often enough (your main untimed loop) you can trap all keypresses and keep the buffer clear... and the user is none-the wiser.

Simple enough -- the solution to 'each keypress moves only one' is to have the keypress start the movement and not stop/change just because of key-release or extra presses of the same key.
 
Back
Top