Learn C Games

Learn C Games

Learn C Games Programming Book coverThis blog is about C and Games programming (in C mainly) and now C# on mobile with MonoGame. It’s written by David Bolton, author of the Learn C Games Programming for beginners EBook. This is the Windows version, with a Raspberry Pi/Linux one due out isometime n 2021.  

The first 20 chapters introduce and teach C programming with many examples. This link is to an .mp4 of the asteroids game from the book. It’s about 90 seconds long and demonstrates all of the features of the game. High score table, rotating asteroids (four sizes), sound, explosions, ship hyper-jump and shields.

The remaining 30 chapters builds up to full source code, about 2,000 lines, in 13 stages and I explain how each feature works and is implemented.  All of the book’s source code is on GithubMore about me. Buy it on Amazon(UK), Amazon(US).

 

Everything and fsearch

Everything and fsearch

Everything File SearchI’m currently operating on a Linux laptop as the M2 SSD on my new desktop PC decided to stop working the other day.  But I thought I’d mention a couple of utilities that I’ve recently started using.

My new PC, just three months old when working has a 1TB M2 SSD with Windows on it. Most software is installed here. All the data, backups, everything else is stored on a 10 TB Hard disk. This is my 10th PC since 1989 so it has the name PC10. Yes, original I know.

I have been copying everything I’ve created and written since 1989 and like a snowball rolling down a hill it has grown. When I finished copying everything from my old PC; a process that took a large chunk of four days, I’d used 2.3 TB out of the 10 TB hard disk.   I just copied across the Gigabit network connection and it maxxed out at 113 MB/S. That’s even with anti-virus running. Of course Windows has an overhead so sometimes the transfer speed drops down to KB/S for small files. I worked it out that over the 48 hours (4 days- 12 hours a day), it copied at 13 MB/S average.

In future it might make sense to zip up folders with small files in them. In the meantime I need to do a bit of pruning of files, removing duplicates etc.

Everything

I’d been looking for a utility to let me find files quickly and somebody had suggested Everything. It’s brilliant. It tells me that I have just over 4 million files. I can filter on document types, images, music files or just search on matching names. And it is very very fast. If you want to find the massive file that’s eating up disk space or duplicates, it does it. But it’s only for Windows. Lacking a Windows system at the mo, I’ve just borrowed the image from the Everything home page.

Fsearch

FSearch window

Developer Christian Boxdörfer also liked Everything and decided to create a Unix clone of it which is FSearch. And it’s written in C. That’s it on the left.

I set it to find all C files and sorted by size.

 

Why I hate Assembly language…

Why I hate Assembly language…

6502 assembler listingI spent several years in the 1980s programming games.

I have a memory of 26 year old me sat hunched over a computer late at night back in 1985. I was working a 60-70 hour week as a partner in a games company. My current game was an American Civil War tactical wargame called Johnny Reb II. I was struggling with some ‘artificial intelligence’ code for the attackers (Confederate troops) to cross a bridge over a river. On the other side the defenders (Union) were trying to defend the bridge.

Johnny Reb II

Artificial Intelligence in games is a completely different thing from ML and Data Science nowadays. Back then it was just a control algorithm for troops reacting to the presence of enemy troops and working out the best routes, targets to attack, whether to retreat and so on.

What made it worse was that the whole thing was written in 6502 assembly language (and later converted to Z80). Back then you had two choices: Basic which was to be honest slow and clunky for writing games or assembly language. If I was doing it now, without a moments hesitation I’d program it in C. But C compilers for 6502 didn’t exist back then.

The Problem with assembly language

The problem with assembly language is (a) it’s slow to write. You can write 10 lines of C in the same time as ten lines of assembly code. Those ten lines of C will do far more than ten lines of assembly code. In 6502 all you are doing is moving values between registers or register <> memory. Maybe add a number or increment one of the three available registers A, X or Y. These were all 8-bit registers so you couldn’t even index easily through 64-bit memory. To do 16-bit indexing you stored the 16-bit address in two successive page-0 locations (addresses 0-255) and then used Y as an 8-bit index. You could do the same in page 0- memory with the X register.

(b). It takes a lot of code to do anything in assembly language. You want floating point arithmetic in 6502? Take a look.  I think Steve Wozniak wrote those for the Apple I/II. What we take for granted in languages like C# or Java or JavaScript is code for high level data structures like dictionaries. I’m sure it could be done but it takes a fair bit of programming. You don’t have those in assembly language; all you have to use is simple and not very long arrays.

In C# I wrote a program to read a 46 MB text file and produce a sorted count of all words in the file.  It used a Dictionary, took me 30 minutes to write and it ran in 5 seconds.  It would take weeks to do the same in assembler. 

6502 Page 0 locations were valuable because they made your code both shorter and faster.

I wrote a cross-assembler for 6502 in Z80 as a way to learn Z80. Assemblers use labels (L20, L30, L31 etc. in the screenshot) and I needed a way to hold them efficiently in memory. I ended up with a 26 x 26 index table of 2 byte pointers. If you had a label ‘ROUTE’ then there would be a pointer to a chain at the location for [‘R’][‘O’]. Each entry in the chain was like this

  • 1 byte length of rest of label (i.e. 3 for ‘UTE’) – 0 marks the end of the chain.
  • 3 bytes to hold ‘UTE’.
  • 2 byte address value

No need to hold the whole word as you know the first two letters. It also makes comparing a label against one in the table was faster because it only needed to match against len(label)-2 characters.

So the next value in the chain would start after that or be a 0 for the end of the chain. Yes most of the index table might be empty (all 26x26x2= 1352 bytes) but every label in a chain used 2 bytes less than the full label text. So with more than 676 labels you saved memory. Searching for a label was just a matter of walking a chain. Labels were just addresses; so a location could hold a value like a count. You’d identify it with a label and use that label in 6502 instructions. No variables in assembler; it’s all addresses…

With 6502 you need to do two passes to generate code. If you have a label in the first page of memory (0-255) then instructions are only two bytes long and are faster to execute than the three byte instructions. So on the first pass you don’t know if a LDA label will be 2 or 3 bytes long. After the first pass through though you do know now, so on the 2nd pass it can output the correct size instructions.

Programming in assembler means you have to write a lot of code and in the early days before I had a development machine that meant I had to save the source code to tape and compile it using a cartridge assembler. The CBM-64 could take cartridges and one of them stored assembly language in RAM just like Basic. If the game did something wrong then the CBM-64 would reset and you’d lose your source and have to reload it from the slow tape. Let’s hope you didn’t forget to save changes before you ran it. I spent a few hours gnashing my teeth over a persistent crash. I was calling a CLR routine when it should have been CLS! d’oh…

Note, from memory it was the Mikro cartridge assembler. See screenshot below.

Miro assembler start up screen.
Mikro Assembler Startup From Github

Jump Tables

So a game back then might be 5,000 lines of code or longer. That’s quite a bit to hold in memory, given that you need space for the game machine code, sprites, graphics etc. as well. Plus it’s wasteful having to recompile the same code over and over again. My cross-assembler did 250 lines per second but divide that by two for the two passes.

So I split up long files into smaller ones and created a jump table at the start. There was no linker so the code was loaded into RAM at fixed addresses. If you had five subroutines in one file then there’d be five jumps at the start to the actual function. And the files that called those functions just had a block of five calls at the start.

That way you didn’t have to worry exactly where the function was located in RAM so long as that file was always loaded at the same address.

 

Switching to Development Machines

It got easier when we switched to development machines. The CBM-64 had a parallel port as did the development machine (Tatung Einstein-a CP/M computer) so a little bit of handler code in the CBM-64 set up the CIA chip to wait for data sent down the parallel cable and put the code directly in RAM. It took no time to load the handler from tape after a crash and then send down the whole file.

Modern CPUs do all sort of optimizing tricks and that’s even before you use vectorization. Compiler writers know how to generate code that uses these tricks but it would take quite a while to learn them so you could use them in hand-written assembly.

Conclusion

Writing in assembler in the 80s was easy to learn. Nowadays I wouldn’t know where to start- the Intel and AMD CPUs have a lot of different chips in their families so there are variations in what instructions are available. Oh and don’t forget there’s ARM CPUs as well.

Writing in C (or even C++) is a lot easier to get into and I very much doubt if you’d get any better performance in writing things in assembly. Also, it would take a lot longer.

A New PC

A New PC

Computer monitors
Image by Gerd Altmann from Pixabay

Moving to a new PC can be a time consuming process. This is my 10th PC since 1989 and I’ve been accumulating files, old programs, websites, manuals etc. as I moved from PC to PC. My first PC had a 20 MB disk, the 2nd PC had a hard disk with 240 MB capacity. On my new PC it’s 10 TB and 1 TB SSD.

It’s another i7 but 11th Gen Intel(R) Core(TM) i7-11700K. Only 32 GB RAM for now as they didn’t have 64 GB in stock but the motherboard can go up to 128 GB.

I estimated that I had accumulated just over 2 TB of my stuff and once I got my new PC setup started the process of moving files across. 2 TB seems a lot but VMs under Hyper-V can easily eat up a few hundred GBs.

Both PCs had a gigabit network card and were plugged into a gigabit switch so what speed did it give me? 11 MB/S. That’s not gigabit speed which would be about 1000 MB/s but closer to 100 Mbs. Trouble was the switch was plugged into a router and I think that pulled things down. I borrowed a crossover cable. Setup both Pcs with static IP and connected them directly. That was much better and it was soon maxing out at 112 MBs.

Windows of course put its spoke in. If you are copying large files- anything over about 1 MB then it uses the full bandwidth but smaller files pull it down and I’d see as slow as KB/s speeds. So I’d have three or four files copying at the same time.

As well as just copying files to the new PC, I was cleaning up my old PC to sell it so I was either deleting stuff or moving it. Overall my total elapsed time was something like 28 hours over four days and evenings. Plus of course installing software (Visual Studio, VS Code, Android Studio, MS Office). It all takes time. Luckily I’ve been quite disciplined and kept all licences and serial numbers in an encrypted text file.

Then I discovered that my new PC’s video cards had different connectors. My old one had DVIs but this one I could only find an HDMI and 2 DisplayPorts. There was a DVI connector on the video card but the firm that did it put the card in the top slot and the DVI (2nd row) was covered up by a blanking plate! It’s a RTX 2060 card. I removed it and all was well.

RTX 2060

And finally the domain is correctly hosted

And finally the domain is correctly hosted

Google dig tool for learncgames.comSomething had gone wrong and I couldn’t change the DNS because it was showing up in somebody else’s account. After I proved ownership of the domain by adding a TXT record they fixed it and I added the hosting name servers in.

Within a few minutes the domain was hitting the server and after I edited the A record to get rid of the parking, it correctly picked up my dummy place holder index.html.

Interestingly I could only add TXT/SPF records at 123-reg.co.uk and Google’s online dig tool (shown) couldn’t pick them up.

Hiding the VPS

I decided that I’d let the main web domain take the strain from hackers trying to access the VPS. So the web game will save orders into gamedomain.org/n/orders and get results from gamedomain.org/n/results for game n. (Not the real game domain!) But behind the scenes the VPS that runs the game processing code will pull orders from that orders folder. The VPS is located elsewhere, not even in the same hosting and after processing, it will copy the results back to the relevant results folder.

This solves a couple of problems.

  1. Getting the orders in (and results out) requires a web interface. Because the game is based on the postal model, it’s quite feasible to keep the web interface and the processing separate.
  2. Increased security. If you don’t know where the VPS is located, it’s somewhat harder to hack it!

I’m also experimenting with having a web command interface. Commands get picked up pulled to the VPS and executed. For example creating a new game, pulling in game processing logs and so on.

New domain appears to be cursed

New domain appears to be cursed

Web design
span>Image by EstudioWebDoce from Pixabay

Back about ten years ago I gave a few lectures on what it took to setup a website. I’d ask the audience (who were not technical) for ideas, showed them how to search on a registrar, find a domain name, register it and it would be live usually within 15-30 minutes.  Then I’d add some content and publish it. It worked every time.

So you’d think the process would be smooth as silk now. Think again. I registered a domain about three weeks ago. I’d had the .eu version for a game idea but of course being .eu and me not living in a EU country and so on so I decided not to renew it. However the .org of it was available and I registered that.

Last week I decided to configure hosting, buy a SSL certificate and start using it. Only when I added it to the server I rent (a reseller account), it didn’t show up on the domain page. That’s where you get the name servers from. Rather than faff around with A recs and MX records, I usually set up Name Servers to point to the hosting server. For that of course you need to know the name servers.  But the domain was not on the GoDaddy DNS page so no name server informtion. The hassle factor was ramping up a bit quickish. Very odd.

I faffed around for a couple of days and got onto GoDaddy’s technical support. It turned out, well they pointed out that (a) the registrar (123-reg.co.uk) had put parking on it. and (b) there was something a bit weird with the DNS hosting. Weirdly, on Chrome, the page is blank which is why I’d not seen it. When I looked at it on iPhone sure enough there was a parking page with adverts.

So I got name servers from GoDaddy and set them on 123. I gave it two days and looked and no change. The name servers were set but The DNS page on their site did not say that it was being managed by external name servers. You get that on other domains; this one for instance.

Onto 123 support and to be fair to them, they sorted it pretty quickly. Also I found out that 123-reg.co.uk and GoDaddy are sister companies. You learn something every day. So I looked at the site and it was the same Parking page except it now said Parked on GoDaddy instead of Parked on 123-reg.co.uk. Me I find this a bit cheeky; I’m paying for registering a domain and hosting but those companies are making a little (maybe more than a little) by parking my domain. I can understand that they’ve probably got a lot of domains in limbo; alone and unhosted so why not park em. But it seems a bit cheeky just doing it without saying anything.

How long to Wait?

So back to GoDaddy support which isn’t always easy. You can do it online by a chat Window and at one point I got this gem. 371 Minutes! Normally its 4 or 5 minutes… I closed the browser window and came back 30 minutes later.

Slight Delay

So the oddity I mentioned earlier; somehow, the domain was added for hosting against somebody else’s account. I can’t explain it, they can’t explain it. So they asked me to verify ownership by reverting the name servers to 123’s and adding a TXT record in the DNS with a certain name and value. Once they can see that I really do own it, they’ll change ownership of its hosting (not even the domain just the hosting!) to me.

So what used to take me 15-30 minutes is about nine days elapsed time. And it’s not finished yet. I really do think this domain is cursed.

 

A minor whinge about Linux

A minor whinge about Linux

Screenshot of Virtualmin update screenI’ve been a Windows developer since the late 1990s and I got into Linux about 2008. I’m still a Windows developer but now that .NET applications can run on Mac and Linux as well as Windows, it makes it easier to write software in C# that runs on Linux.  Using a Linux VPS is probably the cheapest way to do that.

I still use Visual Studio on Windows for developing and just by changing the publish target can run it on Windows or Linux. If you manage a VPS as I do, you want to keep it up to date and that’s what my whinge is.

It used to be that Windows was a pain with having to reboot after installing software. These days that’s only when you’re doing a major update. But Linux? I’m finding that I have to reboot it after an update on average perhaps once a week. This week it happened twice! I grabbed a screenshot to show it.

The screenshot is of the bottom of the VirtualMin update page. I use Virtualmin for administering it because I prefer to spend my time programming. That blue Reboot Now button appeared because one of the packages that got updated required a reboot. I don’t think there’s anything that can be done except click it; the reboot has to happen and only seems to take a minute at most.  It’s just the irony that for years people complained about Windows reboots and now it’s actually Linux that is the major culprit!

And with a bit of help I got it fixed

And with a bit of help I got it fixed

Visual Studio Publish Target runtime dialogThanks to suggestions in the Reddit CSharp subreddit (you can view the entire thread here), I fixed the missing file issue and SkiaSharp is now happily outputting graphics on the VPS.  I’m referring to the More Programming Joys blog entry I posted yesterday.

There were two things that were needed to fix it.

  1. Add a package to the solution. This was the SkiaSharp.NativeAssets.Linux package and I used Nuget to add it to my Windows solution. If you haven’t used Nuget, it’s a great package manager and makes it very easy to add packages.
  2. On the Visual Studio Publish page, I needed to change the Target Runtime to linux-x64. That’s the dialog above. When I clicked the Publish it put all the files including the library that was missing (libSkiaSharp.so) into the Target location and once uploaded it worked.
More Programming Joys

More Programming Joys

Game MapSo I’ve been doing some .NET programming. First on Windows because I can debug it there then “Publish it” which puts all the files (compiled in release) needed, including any dlls into one folder and upload it to a VPS using WinSCP. I’ve also got a terminal session connected by Putty. I use Serilog for logging and SkiaSharp for graphics.

First bug was a missing file but it was a stupid error on my part. I had the file in a path under the home folder and had used ~ in the file’s path. You can use ~ in Bash- it means your home folder, but not in a file path in an application. Putting in the full path fixed the bug. D’oh.

Because it’s .NET (6) that I’m using, you don’t get configuration stuff the same as you did with .NET Framework. I store some config information in a file and define the path with these lines of code which work on both Linux and Windows.

 bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
 var appSettingsFilePath = IsWindows ? @"c:\turfwar\twConfig.json" : "/home/user/turfwar/twConfig.json";

I like Serilog ( with project on Github at ). Nice and simple logging but flexible enough. As this application is a command line app run whenever, it’s easier to view the logs afterwards.

Why use SkiaSharp?

I was quite happy to use System.Drawing but according to this, There are issues with libgdiplus. So I thought I’d try SkiaSharp as I’m doing stuff with Flutter and Skia is the library that underpins that. This is what it takes to create a 100 x100 grey coloured Bitmap in SkiaSharp, add yellow dots and save it to disk as a .jpg. That’s what you see up above.

        internal void SaveBitMap(int id, string filename, Gang gang)
        {
            var info = new SKImageInfo(100, 100);
            using var surface = SKSurface.Create(info);
            SKCanvas myCanvas = surface.Canvas;

            // clear the canvas / fill with white
            myCanvas.DrawColor(new SKColor(0x99, 0x99, 0x99));

            // draw the dots, one for each block
            foreach (var block in Blocks)
            {
                if (block.Owner == id)
                {
                    myCanvas.DrawPoint(block.X*5/2, block.Y*5/2, SKColors.Yellow);
                }
            }

            // Now save to filename
            var mainCanvasImage = surface.Snapshot();
            var TempTIFbitmap1 = SKBitmap.Decode(mainCanvasImage.Encode());
            using (var image = SKImage.FromBitmap(TempTIFbitmap1))
            using (var data = image.Encode(SKEncodedImageFormat.Jpeg, 100))
            {
                // save the data to a stream
                using (var stream = File.OpenWrite(filename))
                {
                    data.SaveTo(stream);
                }
            }
        }

It works fine on Windows but on Linux, (Ubuntu 20.04) I hit bug #2. “System.TypeInitializationException: The type initializer for ‘SkiaSharp.SKImageInfo’ threw an exception.”. And that’s where I am at the moment.  This is similar.  I’ll keep you informed.

Playdate gets nearer to release

Playdate gets nearer to release

playdateBack in May 2020 (nearly two years ago) it was originally announced and the updated release date now looks to be late 2022. Also it’s $179 not $149. But they have given a lot more details; you can for instance see the hardware spec.

Also more relevant is the way of creating games. Either in Lua + C or just C. The dev tools illustrated are running on a Mac but according to this Twitter thread (worth a read), they’re available for Windows, Mac and Linux.  The SDK looks impressive enough- “It includes helpful functions for things like font handling, drawing, animation, sprites, tilemaps, collision detection, A* pathfinding, audio synthesis, crank handling and more. You can download the SDK which includes a simulator now from the dev page..

Would I get one? I’m toying with the idea. A mono display of 400 x 200 is a bit retroish and my days of creating those types of games are probably long behind me, but it’s tempting.

The Two Dice Calendar Puzzle

The Two Dice Calendar Puzzle

Two dice calendarA few years back I owned a calendar made up of two dice (a bit like the image from Wikipedia) to show the day of the month. The two dice had all the digits needed to display any number from 01 to 31. It’s a neat little puzzle to ask someone if they can figure out what the numbers on each of the two dice must be for this to work.

You can probably find the answer on the web, and it took me just 30 seconds to find one.  But still, it is a fun little puzzle.

What I thought was, could I write a program to solve it? So I’m going to try it. It will be in C# to start with but if that works, I’ll redo it in C and publish both on here.