I'm currently re-making an old Missile Command-esque ZX Spectrum Next game I wrote a couple of years back that, to be honest, could have been way better (but I really didn't know any better back in those days). There are some graphical differences I want to make, and one of the major considerations regarding graphics is how I can get more speed when it comes to creating linear missile trails.
Showing posts with label sinclair. Show all posts
Showing posts with label sinclair. Show all posts
Saturday, 3 September 2022
Thursday, 21 May 2020
The NEXT Generation...
Posted on 02:27 by kevman3d
Sunday, 15 January 2017
Beeps and bleeps - playing Speccy music in python
Posted on 02:08 by kevman3d
While I was messing about with converting old UDG graphics to PNG files, I figured one of the other things I was curious to recreate was the beepy music from the ZX Spectrum. BASIC code loaded with BEEP commands created that "I wonder what does that sound like?" curiosity that made me wonder just how easily I could use python to listen to the classic Sinclair hits of the 80's...
One problem I did run into was getting the beepy-sound out nicely. I tried many of the tried-n-tested code examples online that generated a sine wave - however while it worked, the final audio sounded pretty odd and didn't have that nice clean bleep I was expecting. I figured I'd come back to this later...
Exporting aside, I felt there must be a better way to just listen to the music - afterall, its that curiosity of hearing what it sounded like I was after. Lucky python has a module designed just for the task... The winsound module. I also wanted to create pauses in the music, so I imported the sleep function from the time module as well.
Yup, winsound has a Beep function (note the uppercase B). Much like the ZX Spectrum's own beep, you just pass the note and duration. Sounds like it should be a real doddle!
Frequency = base-note * a^semitone
Where base-note is the lowest frequency of your musical scale (in this case, I decided to go for 3 octaves below which is 32.70 hertz). The value of a is calculated as the 12th root of 2 ( in nerdy math terms, its 2^(1/12) ).
In case you're wondering about the code below, 0.083 is 1 / 12.
As there were often pauses added through music, I needed a way to indicate this. I used a note value of 99 to signal a pause.
Looping through this list, I read the duration and note value. The duration is slightly different between the winsound.Beep function, and the sleep() function that I used to introduce pauses. The Beep function requires the length in milliseconds. This is simply the duration from the list multiplied by 1000. The sleep function just uses the value (number of seconds) directly.
The rest of the code was a piece of cake. I feel there's no real explanation necessary as the code can speak for itself...
To export or just listen?
I tried two methods. One was to make use of python's wave module. This is a module that allows you to work with audio files. That includes the ability to both read and write. Being able to export the music to a .wav mean't that it could be bought into those recreated games and used.One problem I did run into was getting the beepy-sound out nicely. I tried many of the tried-n-tested code examples online that generated a sine wave - however while it worked, the final audio sounded pretty odd and didn't have that nice clean bleep I was expecting. I figured I'd come back to this later...
Exporting aside, I felt there must be a better way to just listen to the music - afterall, its that curiosity of hearing what it sounded like I was after. Lucky python has a module designed just for the task... The winsound module. I also wanted to create pauses in the music, so I imported the sleep function from the time module as well.
import winsound
from time import sleep
from time import sleep
Yup, winsound has a Beep function (note the uppercase B). Much like the ZX Spectrum's own beep, you just pass the note and duration. Sounds like it should be a real doddle!
Hmmm, note vs. frequency
ZX Spectrum audio uses semitone numbers. A value of 0 is middle C. 1 is the next semitone of C# / Db, 2 is D and so on. However winsound.Beep required a frequency value (in hertz). How do I translate that number into a frequency?Did I mention I suck at maths?
Maths was never my strong suit at school, but luckily for me that's where the internet comes in with the answers! The formula for calculating a frequency is simplyFrequency = base-note * a^semitone
Where base-note is the lowest frequency of your musical scale (in this case, I decided to go for 3 octaves below which is 32.70 hertz). The value of a is calculated as the 12th root of 2 ( in nerdy math terms, its 2^(1/12) ).
Maths always looks easier in code
I created a function to calculate the correct frequency from the beep value. I calculated this from the lowest frequency of 32.70... As I knew middle C (beep value of 0) was three octaves higher, I just added 36 (which was 3 * 12 semitones) to the value first...In case you're wondering about the code below, 0.083 is 1 / 12.
def beepFreq(ZXVal):
zxNote = ZXVal + 36
a = 2.0 ** 0.083
freq = 32.70 * (a**zxNote)
return freq
zxNote = ZXVal + 36
a = 2.0 ** 0.083
freq = 32.70 * (a**zxNote)
return freq
Getting them tunes down...
The music data itself I passed as a sequence of tuples in a list, copied directly from the BEEP parameters in the spectrum listing. This isn't the most musical of pieces, but it came from a listing so it was a good test...As there were often pauses added through music, I needed a way to indicate this. I used a note value of 99 to signal a pause.
musicData = [ (.1,0),(.1,0),(.1,2),(.1,2),(1,0),(1,99),
(.1,0),(.1,4),(.1,4),(.1,0),(.1,0),(.1,2),(.1,2),
(.1,-1),(.1,-1),(.1,0),(.1,0)]
(.1,0),(.1,4),(.1,4),(.1,0),(.1,0),(.1,2),(.1,2),
(.1,-1),(.1,-1),(.1,0),(.1,0)]
Looping through this list, I read the duration and note value. The duration is slightly different between the winsound.Beep function, and the sleep() function that I used to introduce pauses. The Beep function requires the length in milliseconds. This is simply the duration from the list multiplied by 1000. The sleep function just uses the value (number of seconds) directly.
The rest of the code was a piece of cake. I feel there's no real explanation necessary as the code can speak for itself...
for musicPlay in musicData:
# Calculate the duration (in milliseconds)
duration = int(musicPlay[0] * 1000)
# Work out if we play a note, or whether this is a pause
if musicPlay[1] == 99:
sleep(musicPlay[0])
else:
note = int(beepFreq(musicPlay[1]))
# Call the Winsound Beep
winsound.Beep(note,duration)
# Calculate the duration (in milliseconds)
duration = int(musicPlay[0] * 1000)
# Work out if we play a note, or whether this is a pause
if musicPlay[1] == 99:
sleep(musicPlay[0])
else:
note = int(beepFreq(musicPlay[1]))
# Call the Winsound Beep
winsound.Beep(note,duration)
Budum-tish!
And there you have it. Go grab those old ZX Spectrum basic listings and type in the beep values to enjoy all of those bleepy tunes that were part and parcel of games in the 80'sSaturday, 14 January 2017
Retro bytes to PNG pixels with Python
Posted on 21:08 by kevman3d
After a long break from doing any retro game recreation in Python, I decided that since I had a few old personal game listings laying around from my ZX Speccy days that the idea of converting these to python could be fun at some later date.
However one thing the ZX Spectrum had that we didn't see on the ZX81 (with its lack of any real graphics capability) was user defined graphics, or UDG's as they were termed.
However one thing the ZX Spectrum had that we didn't see on the ZX81 (with its lack of any real graphics capability) was user defined graphics, or UDG's as they were termed.
Friday, 29 April 2016
Whats more relaxing then painting?
Posted on 20:53 by kevman3d
Good question... Well, how about painting iconic people from the 80's computing era! As I had to teach some art and design skills the other week, I needed to prep up some digital painting skills as its not something I've ever really looked into doing.
Afterall - I've never really use my Wacom for more then painting texture maps for 3D models, or scribbling/doodling in Photoshop. However after running the class (with thanks to my teaching side-kick Ken at work for his tips and guide on how to best deliver this) I enjoyed it so much that I felt I wanted to do more. And of course being somewhat obsessed with the 80's mean't I knew what subject matter I would probably target...
OK, so surprisingly (I know its hard to believe) he's not really my uncle (though I'm pretty sure he's someones!)
He's a huge part of the 1980's computing era. Sir Clive Sinclair - the man who invented the Sinclair ZX81 and ZX Spectrum. Amazing machines, which made a huge impact on the home computing market and helped forge the gaming industry as well. If you want to hear about just where it all fitted into the history of gaming, check out From bedrooms to billions. Its a great doco!
This was done on a Sunday afternoon over a couple of hours - and I have to say just how much fun it was. Very relaxing, gradually building up layer-upon-layer (which sounds a tad like a Sarah Lee Cheesecake commercial) and softly smudging the colours together.
Only real issue I have with MyPaint is that it can get quite laggy with high resolution work. These paintings were no problem - however after exporting the images I realised that I've painted them fairly low res unfortunately. Other than that I like that I can work full screen and operate it easily with a few easy to remember keyboard shortcuts (rather then rely on menus and tool bars). Plus it has an infinite canvas.
Note : I demonstrated Krita recently at a meetup I run each month, and it was fairly sluggish. The delay between drawing a stroke on my Wacom and the brush catching up was pretty bad and really didn't impress anybody who was watching the demo. Whether this was my PC (its a pretty hefty box with 32Gb, 256Gb SSD, core i7 and Quadro K4200) or the software, who knows... Its a free download - I'd be tempted to recommend downloading it for yourself and trying it.
If its just doodling and sketching, then Mischief is an impressive tool. Its got a hybrid vector approach to artwork (you can zoom in and out without seeing pixels at all). It also has an infinite canvas and very fluid response (literally never gets laggy from what I've experienced). Its that fluidity that impresses me the most.
Unlike the others, this is a commercial product. There's a very cool free version (minus the oh-so-useful layers). Its not as feature-packed as the others (basic sketching brushes mostly) but that's not a bad thing - it has the tools you need for drawing and removes the bloating which is likely why its so quick and clean to work with.
Its also quite cheap to upgrade (or just purchase)at US$25... Well worth looking into if you love to sketch (and the idea of super-smooth line work thanks to its ingenious technology)
The general process blocking in areas of solid colour by pressing hard, then softly smudging them together couldn't be any easier.
There are also a large range of other brushes, though apart from smudge+paint, the only other brushes I needed were a splattering brush (course-bulk) which I mention later on and a simple pencil brush to sketch out the general illustration.
Jack Tramiel was the founder of Commodore computers, a company who also greatly impacted the 80's computing market with machines such as the Commodore 64, VIC20, PET and Amiga. Sadly he passed away in 2012, but of course his legacy still lives on.
He was a little more challenging to paint - mostly due to all the folds and wrinkles in his skin - but I learnt a lot about painting detail and shading from doing it, so it was well worth the effort.
Interestingly he had a lot of freckles on his forehead, which I managed to pull off just as easily using another of MyPaint's paint brushes mentioned previously - the course-bulk which produces a randomly splattered pattern as you paint. I also used a very small sized one to add some subtle stubble around the mouth...
In fact, I actually used that same brush to add the small amount of speckled skin marks on Sir Clives head as well.
Like any piece of art, I could keep working on this - I realise that there were a lot of finer wrinkles on his skin such as the ones on his forehead, etc that I didn't put in. However I like the softer look of the painting, so I'm pretty happy with the result.
Placing that in a layer of its own, the reference photo to one side, I added a layer below the sketch and I just started to block in the colours in large areas. The photo helped give me a guide to the colour tones I needed, and then it was down to a lot of painting and smudging to blend it into a final piece of artwork.
Funnily I felt it always starts off looking rather 'bad' when you're just laying in the colours. Nothing feels more embarrassing then having people look over your shoulder early and see something that has the appeal of a painting made by a pre-schooler with blobs of colour that look flat and splattered on the screen.
Once the key shades are in place that define the shapes in the sketch, I switch the sketch off and it becomes a hand painting process - observation of the reference and just painting in the lighting and tones by eye is really enjoyable.
Back in the early 90's I decided to teach myself to portrait sketch. Obviously it was easier back then when there were no distractions like Facebook... or the web in general! What I'm loving about painting is that it brings back that thing I loved about pencil sketching - the way that your mind starts to see the detail...
I'll definitely be doing more soon. That satisfaction of finishing a piece of art that you feel proud of as well as the pure joy of sitting quietly and gradually throwing colours onto a virtual canvas is somewhat addictive...
I've been considering keeping this 80's theme rolling and possibly including some of those unsung heroes who had an impact on the gaming industry back in the early 80's. Carol Shaw (who wrote River Raid for the Atari 2600) is said to be the first female game programmer (or at least one of the first). Dona Bailey who, along with Ed logg developed one of my arcade favorites, Centipede.
There are many name and faces behind the amazing games of the past that people just don't know about (or in many cases, care). The hard part is the lack of decent high resolution photography for these people back in the past. Of course I also love the well known icons as well - Wozniak, Miyamoto, Bushnell, etc
What I'll paint?? Well, I guess we'll just have to see what inspires me next...
Afterall - I've never really use my Wacom for more then painting texture maps for 3D models, or scribbling/doodling in Photoshop. However after running the class (with thanks to my teaching side-kick Ken at work for his tips and guide on how to best deliver this) I enjoyed it so much that I felt I wanted to do more. And of course being somewhat obsessed with the 80's mean't I knew what subject matter I would probably target...
Numero-Uno : Uncle Clive
![]() |
| Sir Clive Sinclair |
OK, so surprisingly (I know its hard to believe) he's not really my uncle (though I'm pretty sure he's someones!)
He's a huge part of the 1980's computing era. Sir Clive Sinclair - the man who invented the Sinclair ZX81 and ZX Spectrum. Amazing machines, which made a huge impact on the home computing market and helped forge the gaming industry as well. If you want to hear about just where it all fitted into the history of gaming, check out From bedrooms to billions. Its a great doco!
This was done on a Sunday afternoon over a couple of hours - and I have to say just how much fun it was. Very relaxing, gradually building up layer-upon-layer (which sounds a tad like a Sarah Lee Cheesecake commercial) and softly smudging the colours together.
Online Art supplies - there's more then Photoshop
I mostly work with Photoshop when it comes to making textures for spaceships, however I used MyPaint for this because of its more specialised brushes. MyPaint is a great opensource digital art program. What I like about it most is that its got a great brush engine. Loads of flexibility and ways in which you can get brushes to behave. Some change size based on velocity - which helps a lot if you find you have to work with a mouse (if you do, really, really consider getting a wacom tablet) yet want that more natural 'pen' stroke.| MyPaint - great tool! |
Only real issue I have with MyPaint is that it can get quite laggy with high resolution work. These paintings were no problem - however after exporting the images I realised that I've painted them fairly low res unfortunately. Other than that I like that I can work full screen and operate it easily with a few easy to remember keyboard shortcuts (rather then rely on menus and tool bars). Plus it has an infinite canvas.
Other options...
There are more great tools then MyPaint - I've toyed with a few others, and while I like them, I still find myself popping back to MyPaint... If you're interested in a few options, there is a very similiar tool called Krita. Krita is feature packed (a lot more then MyPaint), however it lacks an infinite canvas. The concept of infinite canvas is really appealing and feels a lot less restrictive.| Krita - another great tool loaded with features |
Note : I demonstrated Krita recently at a meetup I run each month, and it was fairly sluggish. The delay between drawing a stroke on my Wacom and the brush catching up was pretty bad and really didn't impress anybody who was watching the demo. Whether this was my PC (its a pretty hefty box with 32Gb, 256Gb SSD, core i7 and Quadro K4200) or the software, who knows... Its a free download - I'd be tempted to recommend downloading it for yourself and trying it.
Unlike the others, this is a commercial product. There's a very cool free version (minus the oh-so-useful layers). Its not as feature-packed as the others (basic sketching brushes mostly) but that's not a bad thing - it has the tools you need for drawing and removes the bloating which is likely why its so quick and clean to work with.
Its also quite cheap to upgrade (or just purchase)at US$25... Well worth looking into if you love to sketch (and the idea of super-smooth line work thanks to its ingenious technology)
| Michief - fast, fluid, minimalistic... Awesome... |
But - lets get back on topic.
I found that MyPaint has a great brush called smudge+paint - it does exactly what it says... Based on the pressure of the stylus, it behaves very much like a chalk or soft pastels. Hard pressure paints a solid colour, lighter pressure smudges things.The general process blocking in areas of solid colour by pressing hard, then softly smudging them together couldn't be any easier.
![]() |
| MyPaint has a large library of awesome brushes to choose from! |
There are also a large range of other brushes, though apart from smudge+paint, the only other brushes I needed were a splattering brush (course-bulk) which I mention later on and a simple pencil brush to sketch out the general illustration.
Numero-two-no : Jack Tramiel
Jack Tramiel was the founder of Commodore computers, a company who also greatly impacted the 80's computing market with machines such as the Commodore 64, VIC20, PET and Amiga. Sadly he passed away in 2012, but of course his legacy still lives on.
He was a little more challenging to paint - mostly due to all the folds and wrinkles in his skin - but I learnt a lot about painting detail and shading from doing it, so it was well worth the effort.
Interestingly he had a lot of freckles on his forehead, which I managed to pull off just as easily using another of MyPaint's paint brushes mentioned previously - the course-bulk which produces a randomly splattered pattern as you paint. I also used a very small sized one to add some subtle stubble around the mouth...
In fact, I actually used that same brush to add the small amount of speckled skin marks on Sir Clives head as well.
Like any piece of art, I could keep working on this - I realise that there were a lot of finer wrinkles on his skin such as the ones on his forehead, etc that I didn't put in. However I like the softer look of the painting, so I'm pretty happy with the result.
The Process
I found the process of painting really easy to get in to. A very rough sketch was made using the photograph as a reference. I just need markers to position the details - but its the same for many artists. Even if not laying out a sketch first, there is reference of some kind whether its a model, a photo or a personal doodle. Of course, there's always those amazing artists that just have a feel for form and capable of creating something amazing from their imagination. One day...Placing that in a layer of its own, the reference photo to one side, I added a layer below the sketch and I just started to block in the colours in large areas. The photo helped give me a guide to the colour tones I needed, and then it was down to a lot of painting and smudging to blend it into a final piece of artwork.
![]() |
| Sketch, paint, ta-daaaaaa! |
Funnily I felt it always starts off looking rather 'bad' when you're just laying in the colours. Nothing feels more embarrassing then having people look over your shoulder early and see something that has the appeal of a painting made by a pre-schooler with blobs of colour that look flat and splattered on the screen.
Once the key shades are in place that define the shapes in the sketch, I switch the sketch off and it becomes a hand painting process - observation of the reference and just painting in the lighting and tones by eye is really enjoyable.
Its all about seeing what's in front of you...
What I love so much about the process is that it helps get your creative side of the brain to take over, and then you start to really see what it is you're painting. It clicks, and you get that epiphany where it all makes sense.Back in the early 90's I decided to teach myself to portrait sketch. Obviously it was easier back then when there were no distractions like Facebook... or the web in general! What I'm loving about painting is that it brings back that thing I loved about pencil sketching - the way that your mind starts to see the detail...
![]() |
| 1992 - yup, its me... In glorious pencil |
I'll definitely be doing more soon. That satisfaction of finishing a piece of art that you feel proud of as well as the pure joy of sitting quietly and gradually throwing colours onto a virtual canvas is somewhat addictive...
So, who's next?
I've been considering keeping this 80's theme rolling and possibly including some of those unsung heroes who had an impact on the gaming industry back in the early 80's. Carol Shaw (who wrote River Raid for the Atari 2600) is said to be the first female game programmer (or at least one of the first). Dona Bailey who, along with Ed logg developed one of my arcade favorites, Centipede.
There are many name and faces behind the amazing games of the past that people just don't know about (or in many cases, care). The hard part is the lack of decent high resolution photography for these people back in the past. Of course I also love the well known icons as well - Wozniak, Miyamoto, Bushnell, etc
What I'll paint?? Well, I guess we'll just have to see what inspires me next...
Saturday, 2 April 2016
New Sinclair products look sexier... in CG...
Posted on 21:42 by kevman3d
Recently Retro-computers in the UK put together a crowd-funding campaign to raise money to create and release a second ZX Spectrum Vega classic gaming device. This time, unlike the original, it would be portable - with a built in screen, battery and retaining the ability to connect to the TV as well.
It also had something the original didn't - curves - and curves are sexy...
It also had something the original didn't - curves - and curves are sexy...
Sunday, 7 February 2016
Rejuvenating my geriatric childhood friend
Posted on 23:11 by kevman3d
I was really happy a few years ago to get my first home computer back in my hands from a friend who'd kept it safe. It was one of those 'worked when I last used it' machines, and I had made the assumption all would be good...
I had broken the ends of the keyboard membrane when I decided to open it once, but I knew I could replace that as there were new replacements available.
I had broken the ends of the keyboard membrane when I decided to open it once, but I knew I could replace that as there were new replacements available.
Sunday, 5 July 2015
BASIC games in Python - 1982 would be proud (Part 3)
Posted on 14:29 by kevman3d
Ok, this is it - the one and only part 3, the final chapter. We looked at pygame in part 1, we deciphered the BASIC code in part 2,and now we're gonna produce a python version of this simple little 1K ZX81 game.
Is it purely to learn about converting old 1980's programs to python? Well, in a way yes - its something I've wanted to do given how much python reminded me of my youth. I am enthralled with the fact I can code games and have fun just as much as I did back at age 11.
But one thing I am hoping to instill on everybody who reads this article is that writing games is a great way to learn to program. Programming is a a technical art form that has mostly disappeared these days. Lets face it, nobody really goes and buys a high specced PC or Mac to sit and write programs - its mainly about playing the latest games, running business applications, browsing the web and making media. And that's not a bad thing, but its also a sad thing.
If you're just getting into programming and need a challenge to help you learn, consider looking at the past (I've provided links at the bottom of this page). This article is about using that 80's approach of learn through example. By reverse-engineering a game from another language, you really can learn a LOT. Concepts that were used back in that era to make the most of the hardware (ie. tricks like using boolean logic for scores), the way game logic works and of course you pick up a lot about the programming language you're using by trying to work out how to create code that works the same way.
Don't forget - its a case of practise makes perfect. In the 80's, typing in games and understanding how they worked was how I learned to create my own code... Today, the same concept is just as relevant - the more you do, the more you learn - and the more your expertise grows.
Its these reasons I've written this 3-part project for. To try and bring a little of the past to the current day, and just show how much fun it can be to code games rather then click-drag them.
That said, programming is a skill that's started being taught again in schools - and its great to see young people getting this opportunity. In fact, I'm off to a secondary school in a couple of weeks to teach kids some python! Very exciting.
So, lets get on with it.
When files sit in the same folder as your python project, we can load them easily by just specifying the filename - so put those fonts there. There are two .ttf files - zx81.ttf and zxspectr.ttf... Great font, and does a great job too.
If you don't want to use a custom font, for a project like this you'll need to consider using a monospaced font - one where all the characters (including space) are identical widths. Courier for instance works just fine.
However, as great as the font is, we still need those clunky graphic characters. The best way to handle this is to simply these draw up yourself. 8 pixels high by 16 pixels wide... The thickness of the black blocks are 4 pixels.
OK - enough natter, lets start to code this game and see how we fare. I'll be showing code here in this article without my usual python comments in it - but don't fear as I'll be explaining what each part does below the code itself.
In this block of code, its worth noting that I've loaded all (indicated by the *) of pygame.locals into the root namespace. What's meant by this is that rather then referring to a command using its module namespace we can now refer to it directly. I did this because it saves me having to write pygame.locals before each constant. Now I can just refer to pygame.locals.QUIT simply as QUIT.
You don't need to do this, mind you. In the end you can just load them as you would otherwise if you prefer, but its worth considering.
Of course, we also need to bring in randint - a function that lets us ask for a random integer (used to determine where the coin would fall). This I'm also loading into the root namespace from the random module.
So that our code will be easier to understand, and to save us repeating ourselves (plus in case we wanted to change a value used throughout a program) we will define a few constants here that we can use everywhere.
BLACK and WHITE are defining the RGB values that we can use for drawing text, wiping the screen, etc. The TXT constant is for text scaling - for instance, using the original pixel size of the ZX81 on a PC with a 1920x1080 resolution will be tiny and hard to look at. This value will be used to let us change the size of all of our character elements. In this case, I've used a size of 16 pixels per character.
Obviously before we can use pygame, we need to set it up (see part 1 for an explanation about how this works). This includes not just the pygame system, but loading in fonts and graphics that we will be using.
The ZX81 screen is 32 characters wide, 22 characters high (each character taking up 8 x 8 pixels). We therefore need to set up our window appropriately, using our TXT scale constant.
We want to import our zx81.ttf font. Providing you've placed it into the same folder, we can refer to it directly by file name. If you've instead installed the font into your system - or you want to use an alternate installed font - then you'll need to request the path to the font file itself. That's fairly easy to do by asking for it using pygame.font.match_font('nameoffont').
For now, lets just load up the ZX81 font, and set its pixel size to TXT
We can import our nifty 8 x 16 pixel graphic and then resize it to match the rest of the game as follows. In this line of code, I'm using pygame's transform.scale command to resize the file that I'm loading from disk... You could of course break this into two lines - load image, and then scale - if you prefer.
As the width of the players graphic is double the height, we set the scale values to ( 2 * TXT, TXT)
Note : If you are unfamiliar with python, long code that is encapsulated between brackets (such as our parameters for the pygame.transform.scale command) can be broken apart into multiple lines. This is termed implicit line continuation (python regards anything inside brackets, strings or parentheses as continuous until the brackets/etc are closed. This includes code that is broken up over multiple lines)
We'll now define some functions... For our first one, lets define a function that takes care of simulating BASIC's PRINT AT command. This will make life a lot easier for us. If we can pass the same settings as the BASIC code, we'll end up making this code a real doddle to complete.
In the case of pygame, text and graphics are treated the same way. Text, as we'll see in our game code, is rendered to an image first before its drawn (blitted) to the gameWindow.
The game itself - that part of the BASIC listing that does 10 rounds of coin catching - is being defined as a function. Why, you may ask, would we want our game in a function?
Its simple... Our game loop (which is the infinite loop that we write at the end of the program after the function) needs to be able to restart the game. By making the game a function, we can call it from our main game loop. When the game is over, if we then have the function return back to our main game loop (passing the score back as well), we can print the score and wait for a key press to restart the game (ie. call the game function).
We start by defining our score, and our players position in the game. In Python we can do it in one line, rather then then two like BASIC.
As in the original version, we have a for loop that gives us our 10 rounds of coin catching. the gameWindow.fill does the equivalent of the CLS command by filling the display with white.
After that, the score is rendered to an image that is stored in the variable bText, then we draw it to our display using our handy bPRINT function
Next we choose our random value we'll use to position our coin at. And then we're ready to catch that coin with our Y loop...
Obviously we add in our Y loop. The coin drops 10 characters downwards from the top of the screen. Amusingly this does make it impossible to catch coins that fall over 10 characters away from the player... But hey, we'll keep to the original here and retain that particular retro-feature.
Before we jump into the game code, the first thing I've added is a test to see if the user has quit the game. Quitting the game is called when the user closes the window... We want to make sure we clean up nicely in that case. Obviously this isn't something that we needed in BASIC - you'd never quit a program since it was the only thing running on the computer of course...
You'd notice that by importing the pygame.locals into the root namespace, I only needed to type the constant name QUIT here. It looks cleaner, and its easier to type.
For the key press - the code is literally the same. The only difference is that INKEY$ is replaced by pygame's pygame.key.getpressed() function. As expected, both of these also return a True (1) or False (0) result.
Note : much like the transform.scale code, we've broken up this line with a line continuation character (back slash). If your code isn't continuously encapsulated between brackets, parentheses or a string, we need this character to indicate the line continues.
Again, almost exactly the same as the original code - we check for our bounds, and then we print the coin (the letter "O") to the screen. This is just too easy! (which is a good thing)
We draw our player graphic...
Now that we've drawn our graphics, we simply need to update it to show it on screen.
Finally we delete our graphics (the coin, and the player) - we can do this by simply printing a space character (or in the case of the players graphic, two spaces). We can only do this, of course, as long as we are using a monospace font... Any other type of font tends to have thinner spaces, which will only partially blank out half (or less) of the graphic.
We update our position to use the new position we calculated, and we set the frames per second to delay the game to keep it running at the right speed. On my PC, I found a frame rate of 6 fps felt the most accurate to embrace the processing power of the ZX81
And that's the end of the Y loop.
Once the coin has dropped all the way down (10 characters), the loop will end and continue into the next line... This is where we'll update our score (again using the same logic from the original program) before the Z loop repeats.
Once the Z loop is complete, the function exits. As we'll want to print the final score, this function simply needs to return T...
We call the playDropout() function which will run the game, and then exit on completion of 10 rounds of coin dropping.
We returned the score back, which will be stored in fSc. Lets print that back to the player...
We won't see this until we refresh the display.
Finally, we want to pause until we press a key - well, in this case I'm going to set it to be the space bar, simply because its possible we may still be holding down a key from playing the game - which of course will jump us straight back into the game. We also want to make sure we check for the user quitting the application.
This can all be done with a while loop that waits for the space bar to be pressed.
And that's it! Once the space bar is pressed, the code will jump back into our while loop, running the playDropout() function once more. Game on!
If your catching the letter O and experiencing the excitement of doing that 10 times then you've successfully managed to write your own recreation of an old BASIC game in python! In fact, what the aim of doing this was to demonstrate that you can code games very easily with python, as easily as kids did 33 years ago using BASIC.
Of course, BASIC was a language found on most home computers. While the dialect may be different, the logic behind how the language worked is no different - and there were just as many amazing games on other platforms as there were on just the ZX81. Challenge yourself to find some of the amazing games that were around and learn to convert them into python. You'll be amazed how much you will discover from looking at how people developed their code.
Books, including some that explain how to program in a particular flavor of BASIC can also be found online. Some other archives and sites worth looking into include Atariarchives, Folkscanomy, and the original publisher of well-loved books - Usborne (who have released a lot of their old 80's computer programming books for free - I wrote an article here). You can actually read the ZX81 programming guide online if you want to understand the language more.
...and surprisingly the excellent collection of cassette software at ZX81stuff also display basic listings on screen when you click on them. Back in the day, a lot of early 'commercial' games software was in fact written in BASIC.
Why, oh why am I doing this?!
If you've sat through the previous 2 parts, then that's a question you're probably asking...Is it purely to learn about converting old 1980's programs to python? Well, in a way yes - its something I've wanted to do given how much python reminded me of my youth. I am enthralled with the fact I can code games and have fun just as much as I did back at age 11.
But one thing I am hoping to instill on everybody who reads this article is that writing games is a great way to learn to program. Programming is a a technical art form that has mostly disappeared these days. Lets face it, nobody really goes and buys a high specced PC or Mac to sit and write programs - its mainly about playing the latest games, running business applications, browsing the web and making media. And that's not a bad thing, but its also a sad thing.
If you're just getting into programming and need a challenge to help you learn, consider looking at the past (I've provided links at the bottom of this page). This article is about using that 80's approach of learn through example. By reverse-engineering a game from another language, you really can learn a LOT. Concepts that were used back in that era to make the most of the hardware (ie. tricks like using boolean logic for scores), the way game logic works and of course you pick up a lot about the programming language you're using by trying to work out how to create code that works the same way.
Don't forget - its a case of practise makes perfect. In the 80's, typing in games and understanding how they worked was how I learned to create my own code... Today, the same concept is just as relevant - the more you do, the more you learn - and the more your expertise grows.
Its these reasons I've written this 3-part project for. To try and bring a little of the past to the current day, and just show how much fun it can be to code games rather then click-drag them.
That said, programming is a skill that's started being taught again in schools - and its great to see young people getting this opportunity. In fact, I'm off to a secondary school in a couple of weeks to teach kids some python! Very exciting.
So, lets get on with it.
Step 1 - make sure we have those things we can't program...
While the game can be coded, to really achieve that classic look we need to have the classic font and graphics! There are some great free ZX-style fonts that people have created - I grabbed this one, and unzipped the files into the folder where I was writing my python script.| These are not the droids you are looking for, but definitely that font is... |
When files sit in the same folder as your python project, we can load them easily by just specifying the filename - so put those fonts there. There are two .ttf files - zx81.ttf and zxspectr.ttf... Great font, and does a great job too.
If you don't want to use a custom font, for a project like this you'll need to consider using a monospaced font - one where all the characters (including space) are identical widths. Courier for instance works just fine.
However, as great as the font is, we still need those clunky graphic characters. The best way to handle this is to simply these draw up yourself. 8 pixels high by 16 pixels wide... The thickness of the black blocks are 4 pixels.
Get the files here...
You can download the extremely tiny graphic (along with the source code for this game) here... Obviously for permission reasons, I can't re-distribute the font - but here's that link to it again for convenience.OK - enough natter, lets start to code this game and see how we fare. I'll be showing code here in this article without my usual python comments in it - but don't fear as I'll be explaining what each part does below the code itself.
Modules
To start off, we'll need to import our modules of course.
import pygame
import pygame.time
import sys
from pygame.locals import *
from random import randint
import pygame.time
import sys
from pygame.locals import *
from random import randint
In this block of code, its worth noting that I've loaded all (indicated by the *) of pygame.locals into the root namespace. What's meant by this is that rather then referring to a command using its module namespace we can now refer to it directly. I did this because it saves me having to write pygame.locals before each constant. Now I can just refer to pygame.locals.QUIT simply as QUIT.
You don't need to do this, mind you. In the end you can just load them as you would otherwise if you prefer, but its worth considering.
Of course, we also need to bring in randint - a function that lets us ask for a random integer (used to determine where the coin would fall). This I'm also loading into the root namespace from the random module.
Constants
So that our code will be easier to understand, and to save us repeating ourselves (plus in case we wanted to change a value used throughout a program) we will define a few constants here that we can use everywhere.
BLACK = (0,0,0)
WHITE = (255,255,255)
TXT = 16
WHITE = (255,255,255)
TXT = 16
BLACK and WHITE are defining the RGB values that we can use for drawing text, wiping the screen, etc. The TXT constant is for text scaling - for instance, using the original pixel size of the ZX81 on a PC with a 1920x1080 resolution will be tiny and hard to look at. This value will be used to let us change the size of all of our character elements. In this case, I've used a size of 16 pixels per character.
Getting pygame set up
pygame.init()
fpsClock = pygame.time.Clock()
fpsClock = pygame.time.Clock()
Obviously before we can use pygame, we need to set it up (see part 1 for an explanation about how this works). This includes not just the pygame system, but loading in fonts and graphics that we will be using.
gameWindow = pygame.display.set_mode(( 32 * TXT, 22 * TXT ))
The ZX81 screen is 32 characters wide, 22 characters high (each character taking up 8 x 8 pixels). We therefore need to set up our window appropriately, using our TXT scale constant.
Fonts
We want to import our zx81.ttf font. Providing you've placed it into the same folder, we can refer to it directly by file name. If you've instead installed the font into your system - or you want to use an alternate installed font - then you'll need to request the path to the font file itself. That's fairly easy to do by asking for it using pygame.font.match_font('nameoffont').
bFont = pygame.font.Font('zx81.ttf', TXT)
For now, lets just load up the ZX81 font, and set its pixel size to TXT
Player graphic
pGfx = pygame.transform.scale(pygame.image.load('ZX81_block.png'),
(2*TXT,TXT))
(2*TXT,TXT))
We can import our nifty 8 x 16 pixel graphic and then resize it to match the rest of the game as follows. In this line of code, I'm using pygame's transform.scale command to resize the file that I'm loading from disk... You could of course break this into two lines - load image, and then scale - if you prefer.
As the width of the players graphic is double the height, we set the scale values to ( 2 * TXT, TXT)
Note : If you are unfamiliar with python, long code that is encapsulated between brackets (such as our parameters for the pygame.transform.scale command) can be broken apart into multiple lines. This is termed implicit line continuation (python regards anything inside brackets, strings or parentheses as continuous until the brackets/etc are closed. This includes code that is broken up over multiple lines)
Functions - its where the fun begins...
def bPRINT ( cRow, cCol, cGFX ):
gameWindow.blit(cGFX, (cCol * TXT, cRow * TXT))
gameWindow.blit(cGFX, (cCol * TXT, cRow * TXT))
We'll now define some functions... For our first one, lets define a function that takes care of simulating BASIC's PRINT AT command. This will make life a lot easier for us. If we can pass the same settings as the BASIC code, we'll end up making this code a real doddle to complete.
In the case of pygame, text and graphics are treated the same way. Text, as we'll see in our game code, is rendered to an image first before its drawn (blitted) to the gameWindow.
The Game - a function?
The game itself - that part of the BASIC listing that does 10 rounds of coin catching - is being defined as a function. Why, you may ask, would we want our game in a function?
Its simple... Our game loop (which is the infinite loop that we write at the end of the program after the function) needs to be able to restart the game. By making the game a function, we can call it from our main game loop. When the game is over, if we then have the function return back to our main game loop (passing the score back as well), we can print the score and wait for a key press to restart the game (ie. call the game function).
playDropout - where BASIC and Python meet...
Get ready because this is where we translate that old BASIC code into its python equivalent. We'll stick to using the same variable names as well as the FOR loops as the original code so that we can see the similarities...
def playDropout():
T,P = 0,0
T,P = 0,0
We start by defining our score, and our players position in the game. In Python we can do it in one line, rather then then two like BASIC.
for Z in range(10):
gameWindow.fill(WHITE)
bText = bFont.renders("%d" % T, True, BLACK, WHITE)
bPRINT(12,0,bText)
gameWindow.fill(WHITE)
bText = bFont.renders("%d" % T, True, BLACK, WHITE)
bPRINT(12,0,bText)
As in the original version, we have a for loop that gives us our 10 rounds of coin catching. the gameWindow.fill does the equivalent of the CLS command by filling the display with white.
After that, the score is rendered to an image that is stored in the variable bText, then we draw it to our display using our handy bPRINT function
R = randint(0,16)
Next we choose our random value we'll use to position our coin at. And then we're ready to catch that coin with our Y loop...
The coin falling...
for Y in range(10):
Obviously we add in our Y loop. The coin drops 10 characters downwards from the top of the screen. Amusingly this does make it impossible to catch coins that fall over 10 characters away from the player... But hey, we'll keep to the original here and retain that particular retro-feature.
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == QUIT:
pygame.quit()
sys.exit()
Before we jump into the game code, the first thing I've added is a test to see if the user has quit the game. Quitting the game is called when the user closes the window... We want to make sure we clean up nicely in that case. Obviously this isn't something that we needed in BASIC - you'd never quit a program since it was the only thing running on the computer of course...
You'd notice that by importing the pygame.locals into the root namespace, I only needed to type the constant name QUIT here. It looks cleaner, and its easier to type.
N = P+(pygame.key.getpressed()[K_4])- \
(pygame.key.getpressed()[K_1])
(pygame.key.getpressed()[K_1])
For the key press - the code is literally the same. The only difference is that INKEY$ is replaced by pygame's pygame.key.getpressed() function. As expected, both of these also return a True (1) or False (0) result.
Note : much like the transform.scale code, we've broken up this line with a line continuation character (back slash). If your code isn't continuously encapsulated between brackets, parentheses or a string, we need this character to indicate the line continues.
if N < 0 or N > 15:
N = P
bText = bFont.render("O", True, BLACK, WHITE)
bPRINT(Y,R,bText)
N = P
bText = bFont.render("O", True, BLACK, WHITE)
bPRINT(Y,R,bText)
Again, almost exactly the same as the original code - we check for our bounds, and then we print the coin (the letter "O") to the screen. This is just too easy! (which is a good thing)
bPRINT(11,N,pGfx)
We draw our player graphic...
pygame.display.update()
Now that we've drawn our graphics, we simply need to update it to show it on screen.
bText = bFont.render(" ", True, BLACK, WHITE)
bPRINT(Y,R,bText)
bText = bFont.render(" ", True, BLACK, WHITE)
bPRINT(11,N,bText)
bPRINT(Y,R,bText)
bText = bFont.render(" ", True, BLACK, WHITE)
bPRINT(11,N,bText)
Finally we delete our graphics (the coin, and the player) - we can do this by simply printing a space character (or in the case of the players graphic, two spaces). We can only do this, of course, as long as we are using a monospace font... Any other type of font tends to have thinner spaces, which will only partially blank out half (or less) of the graphic.
P = N
fpsClock.tick(6)
fpsClock.tick(6)
We update our position to use the new position we calculated, and we set the frames per second to delay the game to keep it running at the right speed. On my PC, I found a frame rate of 6 fps felt the most accurate to embrace the processing power of the ZX81
And that's the end of the Y loop.
T = T + (P == R or P+1 == R)
Once the coin has dropped all the way down (10 characters), the loop will end and continue into the next line... This is where we'll update our score (again using the same logic from the original program) before the Z loop repeats.
return T
Once the Z loop is complete, the function exits. As we'll want to print the final score, this function simply needs to return T...
On to the main event, eh, loop
This is the end - our game is written as a function, all our initialisation is finished so we simply need to now put it all together in our game loop...
while True:
fSc = playDropout()
fSc = playDropout()
We call the playDropout() function which will run the game, and then exit on completion of 10 rounds of coin dropping.
bText = bFont.render("YOU SCORED %d/10" % fSc, True,BLACK, WHITE)
bPRINT(12,0,bText)
bPRINT(12,0,bText)
We returned the score back, which will be stored in fSc. Lets print that back to the player...
pygame.display.update()
We won't see this until we refresh the display.
replay = False
while not replay:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_SPACE:
replay = True
if event.type == QUIT:
pygame.quit()
sys.exit()
while not replay:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_SPACE:
replay = True
if event.type == QUIT:
pygame.quit()
sys.exit()
Finally, we want to pause until we press a key - well, in this case I'm going to set it to be the space bar, simply because its possible we may still be holding down a key from playing the game - which of course will jump us straight back into the game. We also want to make sure we check for the user quitting the application.
This can all be done with a while loop that waits for the space bar to be pressed.
And that's it! Once the space bar is pressed, the code will jump back into our while loop, running the playDropout() function once more. Game on!
Final Comparison
If we remove all that additional Python code for rendering text, event catching, etc and look at the core code that actually is the game, we can see the way that the two languages compare - and its really not that different!Congratulations!
If your catching the letter O and experiencing the excitement of doing that 10 times then you've successfully managed to write your own recreation of an old BASIC game in python! In fact, what the aim of doing this was to demonstrate that you can code games very easily with python, as easily as kids did 33 years ago using BASIC.
Keep going!
If you don't write games at all - or want to learn more about how to develop your skills as both a programmer and a game developer then don't dismiss the opportunity presented before you. Learn through typing in others games.Of course, BASIC was a language found on most home computers. While the dialect may be different, the logic behind how the language worked is no different - and there were just as many amazing games on other platforms as there were on just the ZX81. Challenge yourself to find some of the amazing games that were around and learn to convert them into python. You'll be amazed how much you will discover from looking at how people developed their code.
Resources online
There are lots of BASIC listings to be found online thanks the those people who are passionately preserving the past. Magazine's are preserved and available from web archives such as retropdfs, the computer magazines archive, Atarimagazines, DPLibrary, etc.Books, including some that explain how to program in a particular flavor of BASIC can also be found online. Some other archives and sites worth looking into include Atariarchives, Folkscanomy, and the original publisher of well-loved books - Usborne (who have released a lot of their old 80's computer programming books for free - I wrote an article here). You can actually read the ZX81 programming guide online if you want to understand the language more.
...and surprisingly the excellent collection of cassette software at ZX81stuff also display basic listings on screen when you click on them. Back in the day, a lot of early 'commercial' games software was in fact written in BASIC.
So...
I'm definitely happy to be able to see how easy it is to code games the same way I did 33 years ago... And no - I've no real plans to continue just re-writing old BASIC code in python. Roll on new, modern games!Saturday, 4 July 2015
BASIC games in Python - 1982 returns (Part 2)
Posted on 19:43 by kevman3d
Welcome to part 2...
We're going to break down an old 1k ZX81 game written in BASIC in 1982. This code is a scan I made from the first-ever book I bought... "34 Amazing Games for the 1K ZX81" by Alastair Gourlay. It taught me a load of techniques that would have taken a long time to pick up on my own, such as reading keys and drawing characters moving on screen.
In the books foreword, written by Tim Hartnell (a name well known in the 80's when it came to programming books), it's pretty clear that the aim of the book is more then just a collection of games to play...
You'll probably want to enter the programs just as they are the first time you read through the book. We'd like you to adapt them, improve and modify them to put your own stamp on them. And we're sure the programming tricks Alastair has used to compress the programs into 1K can be used by you in other programs.
This book is much more then just a collection of games. It is a guidebook to show you how to make the most of your 1K ZX81
Its simple, but when you compare the game play to something like the runner game concept that is all over the mobile market where the aim is to just collect as many items while avoiding the obstacles, the concept of simplicity is really no different. The appeal comes in the whole 'one more go to get a better score' of what is a very basic game play model.
Breaking down the code into blocks, there are 4 things that are happening in here.
(A) We initialise the game by resetting the score, and setting the player starting position.
(B) This is followed by the game in not one, but two loops... The blue loop (game loop (a)) runs the game 10 times - or in the game, this is the 10 coins that drop down and the player has to catch.
(C) The inner white loop (game loop (b)) is the main game - a coin falls down and the player has to get underneath to catch it. Once the coin reaches the bottom, the blue loop repeats this game loop until 10 coins have been dropped.
(D) The last section of the game prints the players score - how many coins were caught out of the 10 that dropped, then waits for a key to be pressed before starting a new game.
T is a variable that is going to keep track of our score, and P is the horizontal position of our player on screen. In here, we're setting both to a starting value of 0. But in line 20, why not just type LET T=0 rather the use LET T=P? Code takes up precious memory in the computer. Because of the lack of it (1K), using a variable to set another saves memory (when you've only got 1024 every byte counts).
As far as the technical explanation goes, a numeric value like "0" is stored in the computer as a binary representation based on its value, and the number of digits (ie. the text characters) entered... This could be up to 6-8 bytes of memory.
Referencing another variable (P) means no need to store a value - instead just storing the reference to the P variable which takes less memory. Yes - the 1980's were a very primitive time for computing, and we're talking about things here that you will mostly never-ever have to even think about these days.
Alastair Gourlay, the author of this book, has a section at the back of it that lists a LOT of optimisation tips such as this. Valuable sage advice for that time!
(Note that the BASIC commands (like LET) were stored as a single byte - a numeric value that represented the command - rather then the 3 characters of the command. The Sinclair BASIC language had a keyword mode you would use to enter these (usually the first thing BASIC would do, only allowing numbers to be entered first), where all the available program commands were accessed through a single key. That's why the keyboard of these machines appeared to have a lot of commands and symbols printed on them)
The game is all about catching falling coins. Its really a game of getting "best out of 10", and this loop here repeats the game 10 times.
When we start each round, we start by clearing the screen using CLS (CLear Screen)
This line prints the score stored in T into a character location of the screen. The command AT was followed by the vertical(row) and horizontal(column) positions. In this case, 12 characters down, and 0 characters (the left side of the screen) across.
R is a random value between 0 and 17. As with todays languages, INT forces the value to be a whole number and not a floating point number. And as this game is all about catching one coin at a time, the coin falling down is done by looping from top to bottom (10 characters is how far a coin will travel). In a way, this is a game-loop within a game loop.
The first line inside this Y loop is to draw a coin (the letter "O") on screen, at Y (vertically - starting at the top) and R (our random location across the screen)
N is a new variable that will be used to work out the new position of our player on screen, alongside P which stores the previous location. Sounds confusing perhaps, but once we get further down in the code, it will probably start to make more sense. Likewise, instead of using a +/- value here, instead we're relying on the True/False value (which is of course, 1 or 0) of a key check (the command INKEY$ checks if a key has been pressed). Without using typed-in numbers in our code, we're saving a few bytes of precious memory...
If 4 pressed, move P + (1 (True)) - (0 (False)). That add's 1 to our position
If 1 pressed, move P + (0 (false)) - (1 (True)). That subtracts 1 from our position
N contains the new location of the player. This line simply tests the location is within the playing area. If it goes outside left or right then set N back to P (previous location).
The first AT deletes the players graphic by printing 2 spaces over it. The next AT prints the players graphic (2 graphical characters) at its new location (N) and the last AT deletes the coin from the screen at its current location, ready to be moved and redrawn.
(Unlike modern systems like pygame where all the drawing is done on a surface in memory before its updated to the screen, all screen printing and drawing is done directly - think of the screen as being a "live update". We delete the players graphic first by printing 2 spaces to blank the previous one out. We then quickly print the player at the new location. After that - as we've already seen the coin printed a few lines above - now we have to delete it from screen as we're going to move it and then print it at a new location.
If we don't delete it, we'll get a line of the letter "O" running down the screen. This is why you'll often see old BASIC games have fairly flickery graphics - even more flickery if the machine is very slow).
We'll copy the N location into P (to make it the previous location - remembering that N will of course be recalculated again to move it if a key was pressed)
And we jump back and repeat our Y loop. The loop essentially draws the coin coming down the screen until it reaches the bottom. While this happens, the player can move left and right to position it underneath to catch the coin.
Once the Y loop has completed, the code continues... This is another of those ways to save bytes by using variables and a little math to add 1 to the score if the character was underneath the coin. Like the INKEY$ calculation used for moving the character, we're again using a boolean (true/false) to add a point to the score. P is of course our position on screen for the player. R is the horizontal location of the coin... Because the player graphic is 2 characters wide, we simply need check whether the coin was lined up with the first or second character to score a point (ie. +1 (if it was True))
We now roll back up to our next coin drop. And it all happens again 10 times...
Once we're finished the loop, we print the players score out of 10. PAUSE 4E4 makes the code pause and wait for a key press, and RUN just executes the BASIC program again (RUN is of course the command for running a program)
In part 3, I'm going to finally show you how I would re-write this in python, using of course pygame to relive the excitement of a 12 year old, some 33 years ago...
After that, you can grab your mobile devices, play some flappy-roads, or crossy-bird games (with gameplay that is literally no more complex then something from the 80's) and journey back to 2015.
See you soon...
We're going to break down an old 1k ZX81 game written in BASIC in 1982. This code is a scan I made from the first-ever book I bought... "34 Amazing Games for the 1K ZX81" by Alastair Gourlay. It taught me a load of techniques that would have taken a long time to pick up on my own, such as reading keys and drawing characters moving on screen.
In the books foreword, written by Tim Hartnell (a name well known in the 80's when it came to programming books), it's pretty clear that the aim of the book is more then just a collection of games to play...
You'll probably want to enter the programs just as they are the first time you read through the book. We'd like you to adapt them, improve and modify them to put your own stamp on them. And we're sure the programming tricks Alastair has used to compress the programs into 1K can be used by you in other programs.
This book is much more then just a collection of games. It is a guidebook to show you how to make the most of your 1K ZX81
The game - DROPOUT, catching coins
Lets take a look at what's going on here.
This game is, like most of that era (and for that little memory) very simple. The player gets the chance to catch a coin that drops from the top of the screen (each time positioned randomly across the screen) ten times. The aim of the game is to get the most catches out of 10.Its simple, but when you compare the game play to something like the runner game concept that is all over the mobile market where the aim is to just collect as many items while avoiding the obstacles, the concept of simplicity is really no different. The appeal comes in the whole 'one more go to get a better score' of what is a very basic game play model.
Breaking down the code into blocks, there are 4 things that are happening in here.
(A) We initialise the game by resetting the score, and setting the player starting position.
(B) This is followed by the game in not one, but two loops... The blue loop (game loop (a)) runs the game 10 times - or in the game, this is the 10 coins that drop down and the player has to catch.
(C) The inner white loop (game loop (b)) is the main game - a coin falls down and the player has to get underneath to catch it. Once the coin reaches the bottom, the blue loop repeats this game loop until 10 coins have been dropped.
(D) The last section of the game prints the players score - how many coins were caught out of the 10 that dropped, then waits for a key to be pressed before starting a new game.
Onto the code...
So what is happening here... What does all that BASIC code do, and how does this game work? I'll break down the code into small snippets and explain what each is doing. Once we've got the whole thing broken down, we'll be ready to jump forward to modern day python and re-create this 33 year old game in mear seconds... Eh, maybe more like between 10-20 minutes...Is BASIC really basic? Sure is...
T is a variable that is going to keep track of our score, and P is the horizontal position of our player on screen. In here, we're setting both to a starting value of 0. But in line 20, why not just type LET T=0 rather the use LET T=P? Code takes up precious memory in the computer. Because of the lack of it (1K), using a variable to set another saves memory (when you've only got 1024 every byte counts).
Memory? Limits? What?
Today, this level of "optimisation" doesn't really make much sense I'm sure - after all, machines have gigabytes of memory.As far as the technical explanation goes, a numeric value like "0" is stored in the computer as a binary representation based on its value, and the number of digits (ie. the text characters) entered... This could be up to 6-8 bytes of memory.
Referencing another variable (P) means no need to store a value - instead just storing the reference to the P variable which takes less memory. Yes - the 1980's were a very primitive time for computing, and we're talking about things here that you will mostly never-ever have to even think about these days.
Alastair Gourlay, the author of this book, has a section at the back of it that lists a LOT of optimisation tips such as this. Valuable sage advice for that time!
(Note that the BASIC commands (like LET) were stored as a single byte - a numeric value that represented the command - rather then the 3 characters of the command. The Sinclair BASIC language had a keyword mode you would use to enter these (usually the first thing BASIC would do, only allowing numbers to be entered first), where all the available program commands were accessed through a single key. That's why the keyboard of these machines appeared to have a lot of commands and symbols printed on them)
Lets continue...
The game is all about catching falling coins. Its really a game of getting "best out of 10", and this loop here repeats the game 10 times.
When we start each round, we start by clearing the screen using CLS (CLear Screen)
This line prints the score stored in T into a character location of the screen. The command AT was followed by the vertical(row) and horizontal(column) positions. In this case, 12 characters down, and 0 characters (the left side of the screen) across.
R is a random value between 0 and 17. As with todays languages, INT forces the value to be a whole number and not a floating point number. And as this game is all about catching one coin at a time, the coin falling down is done by looping from top to bottom (10 characters is how far a coin will travel). In a way, this is a game-loop within a game loop.
The first line inside this Y loop is to draw a coin (the letter "O") on screen, at Y (vertically - starting at the top) and R (our random location across the screen)
Letter O? Where's the graphics?!
In the ZX81, there really were almost no graphics capabilities other then some custom graphical characters and the ability to plot a chunky block to a location on screen (something like 64 x 44 blocks (what they called "pixels" but at a (very) low-resolution)). Drawing blocks one at a time was SLOW - so this is why so many games you would use text for their graphics elements. That is why the letter O is used to represent a coin.Nuff said, what's next...
N is a new variable that will be used to work out the new position of our player on screen, alongside P which stores the previous location. Sounds confusing perhaps, but once we get further down in the code, it will probably start to make more sense. Likewise, instead of using a +/- value here, instead we're relying on the True/False value (which is of course, 1 or 0) of a key check (the command INKEY$ checks if a key has been pressed). Without using typed-in numbers in our code, we're saving a few bytes of precious memory...
Run me through this... Hows that work?
Moving the player left and right by one character at a time just means adding or subtracting 1 from its horizontal value (which is currently being stored as N). Only one key can be pressed at a time, so the maths are fairly simple.If 4 pressed, move P + (1 (True)) - (0 (False)). That add's 1 to our position
If 1 pressed, move P + (0 (false)) - (1 (True)). That subtracts 1 from our position
Don't fall off the screen!
N contains the new location of the player. This line simply tests the location is within the playing area. If it goes outside left or right then set N back to P (previous location).
Drawing the graphics, ahem, characters...
In Sinclair BASIC, multiple print location's can be placed in one line by separating them with semi-colons (;). Rather then 3 lines (of course, which would consume memory) we can do three things in one.The first AT deletes the players graphic by printing 2 spaces over it. The next AT prints the players graphic (2 graphical characters) at its new location (N) and the last AT deletes the coin from the screen at its current location, ready to be moved and redrawn.
(Unlike modern systems like pygame where all the drawing is done on a surface in memory before its updated to the screen, all screen printing and drawing is done directly - think of the screen as being a "live update". We delete the players graphic first by printing 2 spaces to blank the previous one out. We then quickly print the player at the new location. After that - as we've already seen the coin printed a few lines above - now we have to delete it from screen as we're going to move it and then print it at a new location.
If we don't delete it, we'll get a line of the letter "O" running down the screen. This is why you'll often see old BASIC games have fairly flickery graphics - even more flickery if the machine is very slow).
Update the players previous position
We'll copy the N location into P (to make it the previous location - remembering that N will of course be recalculated again to move it if a key was pressed)
Make the coin fall...
And we jump back and repeat our Y loop. The loop essentially draws the coin coming down the screen until it reaches the bottom. While this happens, the player can move left and right to position it underneath to catch the coin.
Did I win the game? Did I, huh, did I???
Once the Y loop has completed, the code continues... This is another of those ways to save bytes by using variables and a little math to add 1 to the score if the character was underneath the coin. Like the INKEY$ calculation used for moving the character, we're again using a boolean (true/false) to add a point to the score. P is of course our position on screen for the player. R is the horizontal location of the coin... Because the player graphic is 2 characters wide, we simply need check whether the coin was lined up with the first or second character to score a point (ie. +1 (if it was True))
We now roll back up to our next coin drop. And it all happens again 10 times...
Lets play again...
Once we're finished the loop, we print the players score out of 10. PAUSE 4E4 makes the code pause and wait for a key press, and RUN just executes the BASIC program again (RUN is of course the command for running a program)
Now you're a BASIC wizz-kid!
Sure - the games simple - but for 1982 it was a serious learning exercise and lots of fun (given there was nothing else to compare it to). If you're already savvy with python programming then you have probably already re-written that game in your head without much effort.In part 3, I'm going to finally show you how I would re-write this in python, using of course pygame to relive the excitement of a 12 year old, some 33 years ago...
After that, you can grab your mobile devices, play some flappy-roads, or crossy-bird games (with gameplay that is literally no more complex then something from the 80's) and journey back to 2015.
See you soon...
BASIC games in Python - 1982 returns (Part 1)
Posted on 00:03 by kevman3d
One thing I always enjoyed back in the 80's, and what made computing just so much fun was
the ability to write my own software - in particular - Games.
It was not just a lot of fun writing the games themselves, it was the exploration of what
the computer was capable of doing through code. Home computing was all new - the wild west of computer gaming. It was about invention, discovery and mastering the machine and its programming language.
Nothing had really been invented yet. No optimised game engines, no hardware accelerated
graphics and sound. You had limited resources when it came to memory and CPU speeds,
which meant some pretty creative ways to make things happen...
Now - some 32+ years later along comes the modern age of computing. Mobile devices are the
new gaming platform. Tools to develop games are everywhere - hardware and software has been
honed for performance and the capabilities of the hardware and software are well documented.
There's no real need to invent your own graphics and sound routines, and even some applications
don't need you to understand how to program a computer at all - click a few options, drag in a
few resource file and be creative with the logic - viole! An awesome game without the technical
requirements pops out.
But I love to code - and I like the simplicity that BASIC used to give... In fact, I really
enjoyed playing some of those old typed-in games that I was inspired to code them again. And that's what this post is all about...
However, while BASIC is a relatively standard language, each machine had a different 'flavor', or 'dialect' of it which made code incompatible across the range of computers of that time.
BASIC could do a lot (it had to - operating the computer relied on it being able to do stuff!), but compared to modern programming languages it was, eh, basic.
It really is (for me at least) the home computing language for the modern age. Its easy to learn - like BASIC was. Its more human readable then most other languages (such as Java or C++/C#). The best part for me is that its a lot of fun to program with as well. This makes it the perfect language (imho) for those new to coding.
Unlike the BASIC days, Python is more standardized - its the same across various platforms - you write a game on Windows, its likely it will work on an Apple Mac, Linux and even on that little Raspberry Pi you may have bought to play around with at home.
But Python has one thing that makes it an even better language for gaming, and that is its modular nature. Python has all the bells and whistles of a standard language, but additional functions and features can be imported to extend it even further extremely easily...
To write games, then there's one free module you need to get...
You can download the installer for it from pygame.org
I also like pygame because not only does it extend the base Python language with a huge library of game-related features, the way games are programmed is literally the same as traditional BASIC games. In fact, they're so similiar that it does make translating a BASIC program to Python very easy.
We start by importing the modules we need. Obviously you should install Pygame before you get this far - but I'm sure you already know that...
(Note that I import the sys module mostly for its command sys.exit() - this is needed to help ensure that any game we're running gets properly shut down by Windows)
Next, we initialise pygame itself to make sure its all loaded in and ready to run.
We'll want to control the speed the game runs at of course - if we're running on a super-fast system then things can of course scream along too quickly. For that, we create a Clock() object that takes care of maintaining the programs playback speed.
Note : gameWindow technically defines a surface, (imagine a blank image in memory). This surface represents the pixel contents of our game window that we see on the screen. When we want to place things 'on screen', we can draw, or blit them onto this surface. Because this surface is in memory, this drawing takes place in the background.
Once we're inside this loop, this is where the fun happens. We take care of erasing graphics, moving objects, checking for collisions and redrawing graphics back to our window. We can call functions from inside this game loop to show title screens, update levels, and so forth. Since this is a generic structure example, I've got no code here (yet) - just know that we'll be developing our game here - honest...
In the example above, I've asked a QUIT event to call a function called exitGame() where I will setup code that will cleanly close the game down.
(Note here that I'm referring to pygame.locals.QUIT for the event name. We could load the locals into the root namespace of our program and just refer to locals using the event name (in this case, I could have just used the word QUIT without the pygame.locals namespace...). This is easily done at the start of the script by importing the module using from pygame.locals import * )
Finally, at the very end of this game loop, we can use our Clock we created to force the loop to run at a specific speed in frames-per-second. In this example, I'm forcing the game to run at 24 frames per second. For an 80's game, this would be even slower since we'd be forcing the game to run as though its taking time to process on a slow CPU.
And that in a nutshell is what makes for a generic pygame program structure. We just need to fill in the gaps of course with our actual game code, but with this in place, we're ready to roll...
pygame.quit() unloads the pygame system, and sys.exit() makes sure the python game window is shut down properly. Without sys.exit(), pygame.quit() will produce an error... So using both is usually required.
See you in the part 2...
the ability to write my own software - in particular - Games.
It was not just a lot of fun writing the games themselves, it was the exploration of what
the computer was capable of doing through code. Home computing was all new - the wild west of computer gaming. It was about invention, discovery and mastering the machine and its programming language.
Nothing had really been invented yet. No optimised game engines, no hardware accelerated
graphics and sound. You had limited resources when it came to memory and CPU speeds,
which meant some pretty creative ways to make things happen...
Now - some 32+ years later along comes the modern age of computing. Mobile devices are the
new gaming platform. Tools to develop games are everywhere - hardware and software has been
honed for performance and the capabilities of the hardware and software are well documented.
There's no real need to invent your own graphics and sound routines, and even some applications
don't need you to understand how to program a computer at all - click a few options, drag in a
few resource file and be creative with the logic - viole! An awesome game without the technical
requirements pops out.
But I love to code - and I like the simplicity that BASIC used to give... In fact, I really
enjoyed playing some of those old typed-in games that I was inspired to code them again. And that's what this post is all about...
What is BASIC?
Its the standard language that came with every home computer. It was chosen because it was a relatively simple language to learn (in comparison to others around). One machine (the Jupiter Ace) came with Forth rather then BASIC, but it didn't take off as much as the others.However, while BASIC is a relatively standard language, each machine had a different 'flavor', or 'dialect' of it which made code incompatible across the range of computers of that time.
![]() |
| Well, hello there to you too, Mister BASIC! |
So - tell me more of this BASIC you speak of...
BASIC stands for Beginners All-purpose Symbolic Instruction Code. Unlike modern languages like Python, it was linear - written line-by-line, each one starting with a line number - each line of code would be executed from one line to the next (though that's how most code works of course - line by line - however without the numbering). Below is a very simple example of a BASIC program.
10 REM This is a program that says hello to you
20 PRINT "What is your name?"
25 INPUT A$
30 PRINT "Hello "; A$
40 IF A$="Kevin" THEN GOSUB 100
50 GOTO 20
100 REM A subroutine to celebrate
110 PRINT "Awesome Name! Lets Celebrate!"
120 FOR A=1 TO 100
130 PRINT "Hip-hip-hooooray! ";
140 NEXT A
150 RETURN
20 PRINT "What is your name?"
25 INPUT A$
30 PRINT "Hello "; A$
40 IF A$="Kevin" THEN GOSUB 100
50 GOTO 20
100 REM A subroutine to celebrate
110 PRINT "Awesome Name! Lets Celebrate!"
120 FOR A=1 TO 100
130 PRINT "Hip-hip-hooooray! ";
140 NEXT A
150 RETURN
There was more to it then just writing programs
These commands were also typed into the computer directly (without line numbers) to operate the computer itself and make it do stuff. For example, To save a BASIC program, you'd have to type the command SAVE "filename". To display something on screen, you'd type PRINT "some text". To reset the computer, you could just type NEW and it would clear the memory. That said, sometimes you'd just switch the machine off and back on instead to do that!BASIC could do a lot (it had to - operating the computer relied on it being able to do stuff!), but compared to modern programming languages it was, eh, basic.
PYTHON
There's one language I feel makes coding as much fun as it was back then, and that's Python.It really is (for me at least) the home computing language for the modern age. Its easy to learn - like BASIC was. Its more human readable then most other languages (such as Java or C++/C#). The best part for me is that its a lot of fun to program with as well. This makes it the perfect language (imho) for those new to coding.
Unlike the BASIC days, Python is more standardized - its the same across various platforms - you write a game on Windows, its likely it will work on an Apple Mac, Linux and even on that little Raspberry Pi you may have bought to play around with at home.
But Python has one thing that makes it an even better language for gaming, and that is its modular nature. Python has all the bells and whistles of a standard language, but additional functions and features can be imported to extend it even further extremely easily...
To write games, then there's one free module you need to get...
Pygame
Pygame is awesome... Its a game development suite that contains all the commands and features you need to draw graphics, play sounds and read keys/mouse when it comes to gaming.You can download the installer for it from pygame.org
I also like pygame because not only does it extend the base Python language with a huge library of game-related features, the way games are programmed is literally the same as traditional BASIC games. In fact, they're so similiar that it does make translating a BASIC program to Python very easy.
A look at the basic structure of a pygame program
Setting up a simple base program to work with pygame is very easy and goes something like this.We start by importing the modules we need. Obviously you should install Pygame before you get this far - but I'm sure you already know that...
import pygame
import pygame.time
import pygame.locals
import sys
import pygame.time
import pygame.locals
import sys
(Note that I import the sys module mostly for its command sys.exit() - this is needed to help ensure that any game we're running gets properly shut down by Windows)
Next, we initialise pygame itself to make sure its all loaded in and ready to run.
pygame.init()
We'll want to control the speed the game runs at of course - if we're running on a super-fast system then things can of course scream along too quickly. For that, we create a Clock() object that takes care of maintaining the programs playback speed.
fpsClock = pygame.time.Clock()
Give me something to look at
Games are just no fun if you can't see them on screen. We next create a window for our game to be displayed in. In this example, I'm making a 640 x 480 pixel window.
gameWindow = pygame.display.set_mode((640,480))
Note : gameWindow technically defines a surface, (imagine a blank image in memory). This surface represents the pixel contents of our game window that we see on the screen. When we want to place things 'on screen', we can draw, or blit them onto this surface. Because this surface is in memory, this drawing takes place in the background.
Lets play!
And here's where the magic happens - the game loop itself. Basically the game will repeatedly run until we force an exit from the game. We create the game inside a while loop - in this case, an infinite one...
while True:
Once we're inside this loop, this is where the fun happens. We take care of erasing graphics, moving objects, checking for collisions and redrawing graphics back to our window. We can call functions from inside this game loop to show title screens, update levels, and so forth. Since this is a generic structure example, I've got no code here (yet) - just know that we'll be developing our game here - honest...
Continuing on...
Next thing is that we should also check for any events - that is, asking pygame to let us know if something occured such as the mouse was clicked, a key was pressed or some other type of event like the user closing the window (essentially quitting the game). This is done by iterating through a list provided by the function pygame.event.get() and checking if a particular event has occured. The pygame.locals contains a collection of constants that allow us to check events by some name, rather then an obscure numeric value...
for event in pygame.event.get():
if event.type == pygame.locals.QUIT:
exitGame()
if event.type == pygame.locals.QUIT:
exitGame()
In the example above, I've asked a QUIT event to call a function called exitGame() where I will setup code that will cleanly close the game down.
(Note here that I'm referring to pygame.locals.QUIT for the event name. We could load the locals into the root namespace of our program and just refer to locals using the event name (in this case, I could have just used the word QUIT without the pygame.locals namespace...). This is easily done at the start of the script by importing the module using from pygame.locals import * )
Anyway...
Once we're done doing the biz of drawing and moving, we just need to tell pygame to update the game window. This will copy the contents of our gameWindow surface from memory back to the screen.
pygame.display.update()
Finally, at the very end of this game loop, we can use our Clock we created to force the loop to run at a specific speed in frames-per-second. In this example, I'm forcing the game to run at 24 frames per second. For an 80's game, this would be even slower since we'd be forcing the game to run as though its taking time to process on a slow CPU.
fpsClock.tick(24)
And that in a nutshell is what makes for a generic pygame program structure. We just need to fill in the gaps of course with our actual game code, but with this in place, we're ready to roll...
Before I finish up
What about that exitGame() function I made my QUIT event call? Well, its as simple as this...
def exitGame():
pygame.quit()
sys.exit()
pygame.quit()
sys.exit()
pygame.quit() unloads the pygame system, and sys.exit() makes sure the python game window is shut down properly. Without sys.exit(), pygame.quit() will produce an error... So using both is usually required.
Phew! That's a lot to take in...
I'll end this article before it becomes immensely long, and let you digest that information. If you've gotten this far, hopefully you're getting the buzz that you're keen to start writing your own games... In part 2, I'll look at how we can reverse-engineer an old BASIC game from a ZX81 1kb listing and translate that to Python... We'll attempt to keep the whole old-school look and style - so groovy retro graphics and all...See you in the part 2...
Subscribe to:
Posts (Atom)












