Category: Source code

About that Empire Map Generator

About that Empire Map Generator

The Circle data used in the Empire map generatorThis post refers to the c sources in the AboutEmpire.zip file in the Github C Games repository.

I first developed the algorithm in 1986 and wrote it in Z80 assembler. It was then rewritten in Turbo Pascal and used in both the Warlord and Quest postal games that are still run by kjcgames.com. It has been rewritten in C in the Empire game. So how does it work?

The idea is to generate continents. Typically between 2 and 3 continents between 1500 and 1800 squares in total on an 80 x 50 map. The map is made up of land squares and sea squares but starts out blank.

First I throw down 30 land points and 50 sea points. I.e. the program randomly picks points.

void AddPoints(enum loctype lt,int numpoints) {
  int i,x,y;

for (i=0;i<numpoints;i++) {
  do {
    x=Random(MAPWIDTH-40)+20;
    y=Random(MAPHEIGHT-40)+20;
    }
  while (map[x][y].locis != lctnull);
  map[x][y].locis = lt;
  genpointsx[pointindex]=x;
  genpointsy[pointindex++]=y;
  }
}

Some of the constants and the loctype enum are defined in common.h. The map itself is built up by adding extra points to each land point and sea point. I’ve defined an array of points in data.h that has 35 rings of points.  Well they’re more squares than rings.  That text file shown in the image is what these rings are derived from. I’ve translated all the points into 35 sets of X,Y offsets in data.h.

These points get added in layers. Starting with the first layer which has 8 points in it around the centre – that’s the A’s in the image, the next layer has 12 B’s and 4 C’s in it and so on.

But I only add a land or sea point if the spot its going into is empty; once it is defined as sea or land it can never change. So sea points and land points bump up against each other as these ‘circles’ expand and you get coasts and all sorts of interesting shapes. After that I fill in all empty spaces as sea, and the count up the connected land and sea squares.

If there are any tiny seas, i.e. less than 5 squares area they get filled in as land and any land point that isn’t surrounded by at least 3 other land point gets sunk. Then so long as there are the right number of continents and the total land squares is between 1500 and 1800, the map is accepted. If not a new one is generated. Adjusting the number of initial land and sea points helps to generate more acceptable maps.

Added Empire 9 to the C Games repository

Added Empire 9 to the C Games repository

Splash screen from the Empire gameBack in 2012/2013 I was writing the C/C++/C# column for About.com and I was doing games tutorials with SDL. This is an Empire type game, much like the Z80 game I mentioned in yesterday’s post except coded in C and with hexagons instead of squares.

It is not complete but includes a working map generator and a simple GUI that I devised based on a very crude OOP type of coding using function pointers and macros.

I’ve put it on the C Games repository and In the empire9src.zip (in the aboutempire.zip)  file you’ll see sdlgui.h and c. These implement it and (years ahead of Flutter and Dart!) it redraws the GUI at 60 fps.  The controls are built in a linked list of sdlcontrols.  This is a sdlbase which is the base for all controls.

#define sdlbase enum controltype ctype;\
int x,y,width,height,color,clickable;\
SDL_Color textcolor;\
void (*pRender)(struct sdlcontrol * self);\
void (*pFree)(struct sdlcontrol * self);\
void (*pClick)(struct sdlcontrol * self);\
void (*pPreClick)(struct sdlcontrol * self);\
struct sdlcontrol * nextcontrol

The four void (*..) are the function pointers. The pRender function draws the controls, pFree frees it up.. pClick handles clicks and PreClick provides extra functionality.

struct sdlcontrol { sdlbase; };
typedef struct sdlcontrol * psdlcontrol;
typedef char * pchar;

struct sdlbutton {
  sdlbase;
  pchar labeltext;
  int isDown;
  int countDown;
};

struct sdllabel {
  sdlbase;
  pchar labeltext;
};

Those are the definitions for sdlbutton and sdllabel and all controls have sdlbase (Everything in the big macro) and additional info.

This is the code that rendered a label.

void RenderLabel(psdlcontrol self) {
  int result,x,y;
  char buff[60];
  struct sdllabel * label= (struct sdllabel *)self;
  SDL_Rect rect = {(Sint16)self->x,(Sint16)self->y,(Uint16)self->width,(Uint16)self->height};
  x= self->x;
  y=self->y;

result=SDL_FillRect( screen, &rect, self->color );
  sprintf(buff,"%s",label->labeltext);
  ttf_print(x+2,y+2,buff,self->textcolor);
}

So every frame, the program would render all controls to the off-screen buffer by walking the linked list of controls and calling the pRender pointer for each. For buttons this would include a simple animation to show the button being clicked down and then released etc.

If you’ve ever wondered how a GUI is implemented take a look at the code. The sdlgui.c is less than 600 lines but manages to do panels, buttons, labels, checkbox, listbox and images.

Update on the sdldemo

Update on the sdldemo

I’ve redone the hr_time,h and c files and am much happier with them now and also updated the code on github.  Apparently the reason for having <linux/time.h> as well as <time.h> and including both is because the <time.h> is set in stone and any additions have to be done as an extra include file. This is what hr_time.h looks like now.

#ifndef _timeh
#define _timeh 1
#include <linux/time.h>
#include 

struct _times {
  struct timespec start;
  struct timespec stop;
};

typedef struct _times stopWatch;

void startTimer(stopWatch *timer);
void stopTimer(stopWatch *timer);
double diff(stopWatch *timer);

#endif

 

That Clang C compilation

That Clang C compilation

I spent about five hours trying to get the timing code to compile before I got it compiling and working. Now I’m used to the concept of include guards in C. Those are the #ifndef that you see like this:

#ifndef _timeh
  #include <linux/time.h>
  #define _timeh 1
#endif

But in the hr_time.c file these include guards are on steroids. Not only did I need to include <time.h>, I also had to include <linux/time.h> but with a couple of extra #defines in there. It doesn’t seem right and wasn’t needed with the Windows version.  I’d welcome any comments on this.

#ifndef _timeh
  #include <linux/time.h>
  #define __timespec_defined 1 
  #define __itimerspec_defined 1
  #include <time.h>
  #define _timeh 1
#endif

The sdldemo program with timing whown in the window caption.Without these, I’d get compile errors like __timespec redefined errors.

I’ve uploaded the source files and Visual Studio Code JSON files for this in the file asteroids_ch25.zip in the new repository for the Learn C on Linux Ebook

So feel free to try it. The only difference between this and the version shown in an earlier post is the time (in the window caption) to draw all 100,000 rectangles,  You’ll need to install SDL2 if you want to compile and run the program.

The joys of Linux C Compilation

The joys of Linux C Compilation

I’m slowly working my way through 30 odd examples of C code that I wrote for the first 20 chapters in my Ebook., And finding odd compile errors. Here’s an example.

I’ve rewritten my stopWatch object for high-precision timing. This is the header.

#include 

struct _stopWatch {
  struct timespec start; 
  struct timespec stop;
} stopWatch;

typedef struct _stopWatch stopWatch;

void startTimer(stopWatch *timer);
void stopTimer(stopWatch *timer);
double diff(stopWatch *timer);

It won’t compile, and keeps on complaining that timespec is an incomplete type. I saw one solution which was to redefine timespec like this:

struct timespec {
  time_t tv_sec; /* seconds */
  long tv_nsec; /* nanoseconds */
};

But that got Clang wound up about timespec redefined errors. I searched and searched and eventually found the answer. Instead of

#include <time>

Use this as well

#include <time> 
#include <linux/time.h>

Simple when you know how!

SDL on Linux

SDL on Linux

In my forthcoming ebook, which is the Linux equivalent of the first one I use Visual Studio Code (VSC) as the IDE to develop along with the Microsoft C/C++ extension. I’m using the SDL2 library for fast graphics and Clang as the compiler.

Thankfully Microsoft have documented most of the process of using Clang with VSC, albeit on a Mac. I’m using Ubuntu but it’s mostly the same.

Before I could configure SDL I had to add it and I never realised about apt-cache on Ubuntu (and Debian). The command

apt- cache search libsdl2

Outputs this, showing what's available.
libsdl2-2.0-0 - Simple DirectMedia Layer
libsdl2-dev - Simple DirectMedia Layer development files
libsdl2-doc - Reference manual for libsdl2
libsdl2-gfx-1.0-0 - drawing and graphical effects extension for SDL2
libsdl2-gfx-dev - development files for SDL2_gfx
libsdl2-gfx-doc - documentation files for SDL2_gfx
libsdl2-image-2.0-0 - Image loading library for Simple DirectMedia Layer 2, libraries
libsdl2-image-dev - Image loading library for Simple DirectMedia Layer 2, development files
libsdl2-mixer-2.0-0 - Mixer library for Simple DirectMedia Layer 2, libraries
libsdl2-mixer-dev - Mixer library for Simple DirectMedia Layer 2, development files
libsdl2-net-2.0-0 - Network library for Simple DirectMedia Layer 2, libraries
libsdl2-net-dev - Network library for Simple DirectMedia Layer 2, development files
libsdl2-ttf-2.0-0 - TrueType Font library for Simple DirectMedia Layer 2, libraries
libsdl2-ttf-dev - TrueType Font library for Simple DirectMedia Layer 2, development files

So a quick

sudo apt-get install libsdl2-dev

Installed 73 MB of files including all the header files. I used the files app to search for SDL files and it found a folder SDL2 in /usr/include.

And all I needed in my program was

[perl]

#include <SDL2/SDL.h>

[/perl]

And I had to add “/usr/include/SDL2/” into the includePath section of c_cpp_properties.json and “-lSDL2” into the args section of tasks.json. These two JSON files are included in C/C++ projects in VSC.

At that point I could compile and run my first SDL program on Ubuntu. It throws 10,000 random sized coloured rectangles onto the screen

The first SDL demo program

 

The missing SGN function

The missing SGN function

Both C and C++ lack a sgn (short for signum apparently!) function (unless C++ has added it in recent changes. It’s also available in Boost but this blog is not about C++ so who cares!

Sgn() for those who don’t know is a function that returns -1 if the passed in int value is negative, 1 if the value was positive or 0 otherwise. Even BASIC includes it but not C.

However it’s easy to add. Something like

int sgn(in x) {
  if (x=0) return 0;
    else
  if (x>0) return 1;
  else 
    return -1;
}

Of course if you are using longints or floats or doubles, you have to write those as well. However an alternative is to make it into a macro.

#define sgn(x) (x < 0) ? -1 : (x > 0)
int a = sgn(10);

The only downside if you make lots of calls to sgn() is that it will slightly bulk up your code compared to calling it as a function but it will run fractionally faster!

High speed timing in C

High speed timing in C

One of the things I like doing is timing code. Not to benchmark it per se, but to get an idea of performance. I have a small set of functions to do this on most CPUs for the last ten or fifteen years. It uses the high frequency clock, which. on my PC (a five year old I7-5930K), this counts at the rate of 3,500,000,000 per second.

You just read this timer twice, subtract the difference and then divide by the frequency (3500,000,000) to get a time in fractions of a second accurate to nano-seconds. (10-9 seconds).

Here’s the code for Windows. It’s in the several of the ebook chapters, e.g. chapter 48 (download the file asteroids_ch48.zip and unzip) . Or you can just copy from here.

hr_time.h

#include <windows.h>

typedef struct {
LARGE_INTEGER start;
LARGE_INTEGER stop;
} stopWatch;

void startTimer(stopWatch *timer);
void stopTimer(stopWatch *timer);
double LIToSecs(LARGE_INTEGER * L);
double getElapsedTime(stopWatch *timer);

and hr_time.c

#include 

#ifndef hr_timer
#include "hr_time.h"
#define hr_timer
#endif

void startTimer(stopWatch *timer) {
QueryPerformanceCounter(&timer->start);
}

void stopTimer(stopWatch *timer) {
  QueryPerformanceCounter(&timer->stop);
}

double LIToSecs(LARGE_INTEGER * L) {
  LARGE_INTEGER frequency;
  QueryPerformanceFrequency(&frequency);
return ((double)L->QuadPart / (double)frequency.QuadPart);
}
double getElapsedTime(stopWatch *timer) {
LARGE_INTEGER time;
  time.QuadPart = timer->stop.QuadPart - timer->start.QuadPart;
return LIToSecs(&time);
}

Use it like this:

stopWatch s; // declare a stopwatch variable

startTimer(&s);

// your code to be timed here

stopTimer(&s);

printf('It took %10.6f secs',getElapsedTime(&s));
More on pointers in C. The use of typedef

More on pointers in C. The use of typedef

Asteroid about to be destroyedThis bit is slightly controversial. I find all the * makes it harder to read code so I use typedefs to hide them. Here’s an example from the game. Try replacing every pbte with byte * and see if reading it is harder for you.

typedef byte * pbyte;

// mask arrays
byte bulletmask[1][3][3];
byte plmask[24][64][64];
byte a1mask[24][280][280];
byte a2mask[24][140][140];
byte a3mask[24][70][70];
byte a4mask[24][35][35];
byte alienmask[64][64];

pbyte GetMask(int type, int rotation, int size) {
  switch (type) {
    case tAsteroid: // asteroid
      {
        switch (size)
          {
            case 280:
              return (pbyte)&a1mask[rotation];
            case 140:
              return (pbyte)&a2mask[rotation];
            case 70:
              return (pbyte)&a3mask[rotation];
            case 35:
              return (pbyte)&a4mask[rotation];
          }
      };
    case tBullet: // bullet
      return (pbyte)&bulletmask;
    case tPlayer: // player
      return (pbyte)&plmask[rotation];
    case tAlien:
      return (pbyte)&alienmask;
    } 
  return 0; // null - should never get here!
}

In my post about collision detection I mentioned getting mask bytes. This function GetMask returns a pointer to a byte (i.e. the first byte in a particular mask for a particular type of object (asteroid, bullet, player, alien) and for asteroids and the player a particular rotation. The many (pbyte) are needed because the arrays have different sizes. There are 24 player and asteroid masks.

A look at pointers.

A look at pointers.

You cannot be a C programmer without using pointers. It’s the one feature of the language that makes possible much of what you can do in C. Pointers seem to scare novice programmers and it’s true that you can crash a program if you make a mistake, but otherwise they’re not that bad. Pointers as parameters in functions let you change the value of an external variable.

A pointer is just a variable that holds the address of another variable. So here’s my take on pointers.

You define a pointer as a pointer to a variable type like a pointer to an int or a char. There is also a “wildcard” where you define a pointer to a void. That has its uses when passing general pointers into functions. With types, the C compiler can verify assignments.

[perl]int * pInt; // pInt is a pointer to an int
char * pChar; // pChar is a pointer to a char
int a;

void ZeroInt(int * pInt) {
if (pInt) // Check pointer does not have a null value
*pInt = 0;
}

ZeroInt(&a); // Sets a to 0.[/perl]

That ZeroInt() function is a long-winded way of setting whatever int variable it is called with to zero. Yes you can just do a = 0; but that misses the point. What if a was a struct and the ZeroInt was a function to initialise all the fields of the struct?