[DEVLOG]Pandemic (working title)


I will make the game in Python with Online Pokitto Python Editor (PyInSky) because that is so easy and integrated environment. I will use the LowRes mode, 110x88, because I like making the graphics for that more that for the HighRes

At first I am going to think about how to implement the world map (1280x963).
From here: https://fi.m.wikipedia.org/wiki/Tiedosto:World_map_(Miller_cylindrical_projection,_blank).svg
License: CC-BY-SA

Below is the portion of map that fills the Pokitto screen (110x88). The tile size is 16x16.

There are a lot of open questions like: What would be the best tile size? What would be the best world map size? Is it worthwhile at all to use tilemap or just a rom bitmap?

A 1280x963, 16 color bitmap would take over 600 KB which is too much. Using RLE encoding would make it much less. Also reducing the ROM bitmap to B/W bitmap would reduce the size significantly. As MicroPython is somewhat limited and simplified environment in it can be hard or impossible to use that kind of optimizations with the current uPyGame API.

A tilemap can be also thought of some kind of compression. For large areas of the same color, only one tile can be used. As you can see from the small picture. The 16x16 tile size is too big, almost every tile is unique, which does not compress it well. So maybe 8x8 would be better.
So I am not still sure if the ROM in Pokitto is big enough to hold the map but we will see. There are ways to make the memory footprint still smaller if needed, like removing the country borders or reducing the source world map resolution. Dealing with the limits is what makes this interesting :slight_smile:



As always, there was one tiny detail that forced me to “upend the tea table” i.e. radically change the plans. The max amount of different tiles in the tilemap is 16! There is no way I could use 8x8 tiles because there would be about 1000 different tiles that way for the whole World Map :slight_smile:

The new plans: Reduce the source world map bitmap resolution to 512x512. Use a 256x256 tilemap to map 16 tiles to it. Each tile would be 2x2! With that small tile size it would be interesting to see how the TAS manages in performance wise.

Then I needed to find a program that can convert the original 512x512 bitmap to a tilemap and tiles in Windows. I though that would be easy to find but it wasn’t. I finally found one script, image2map.py, here: https://github.com/bjorn/tiled/wiki/Import-from-Image. All good and nice! Nope, it did not output the tilemap and I needed to convert it to Python3 compatible first! After some sweaty hours I got it to work for me.

Here is the result:

FPS is actually 37, there was an error in calculation :slight_smile: There is still 12 KB RAM left for the program. The size of the ROM image is now 159 KB.

Edit: Do not mind the mouse cursor in the video.
Edit: the gif has been recorded at 16 fps. The game looks more fluent on HW.



Here is what the tilemap looks like, 256x166 tiles, 16 values for the tile index.

And the tiles:

In addition to save ROM, having the world map as a tilemap instead of a bitmap makes it possible to easily do effects like this in Python:

The source files are now in GitHub: https://github.com/haviital/pandemic


Excuse my ramblings, but I couldn’t help noticing that the first 5 of your tiles follow a distinct pattern where the corners correspond to bits in a nibble, but then the following tiles break that pattern.

May I ask, is that a coincidence or intentional?

(Also having visible source and a licence gets an immediate +1 from me.)

1 Like

Thanks for giving credits about the sources.
The order comes from the tool I used, but it really do not matter for me :wink:

1 Like

Ah, ok, don’t worry then. Probably just my imagination running wild again.
The curse of being a programmer - you start seeing powers of two and mathematical patterns in otherwise innocuous things. :P



Made a message panel at the top of the screen. It can switch messages in a timed loop. That is because it minimizes the number of simultaneous sprites (each letter is one sprite in the TAS mode) on screen and also saves the screen space.

I also started to add the targets, and made animated bitmaps for the target and the plane. For controlling those I took into use the GameObject class.

It is suprisingly slow to go over Atlantic with an ultralight experimental plane :wink:


Great! How many places are we going to learn? :sunglasses:

1 Like

I hope to have several hundreds of cities :slight_smile: , but I do not think I have time to write down that many city names and xy-coordinates by myself. I might need to ask help from the community :wink:



Time to add sound effects :drum:. This time I will not do it last as usually. Adding a sound effect in the Pokitto Online Python Editor is dead-simple. Just drag and drop a wav file to the source file :slight_smile: The editor automatically converts the wav to the 4-bit, 8 kHz, mono raw audio data. I usually get the sound from freesound.org.

I always first edit the sample in Audacity. I convert it to mono, 8 kHz audio data there and possibly amplify the sample there. if it is not loud enough. I am using only 4-bits per sample in Python to save ROM, so it is good to use the full dynamic range. I might also add fade-in and fade-out effects to remove “crack” sounds. But as said before, any editing is not necessarily needed.

Also playing the sample is very simple. Create pygame.mixer.Sound() at start and then call play_sfx() for playing each sound. Like this:

sound = pygame.mixer.Sound()
sound.play_sfx(alertSfx, len(alertSfx), True)

Note: I updated the sources in GitHub.
Here is the rom image: build (57).bin (172.1 KB)


How about a simpler retro look for the map like c64 era or earlier? It could make it smaller and give more room for finely honed code.

And perhaps larger zones rather than all national borders kinda link similar areas together with near/touching ones that have similar enough biomes.

If I run out of Rom I might need to lower the map resolution. Or I can use the current tilemap with 4x4 tiles. That effectively zooms in 2x the world map.

I am in two minds with the national borders. The map looks much better without, but they give useful info.

Is there a simple way to convert longitude and latitude to cartesian coordinates on the map?

If so you might be able to just grab some data from a website and run a script over it.

1 Like

There is not. That is the whole issue with map projections


Maybe tabulate some cities coordinates and their bitmap coords and use them as reference for Bicubic interpolation?

I would expect there must be some way to mathematically describe it though, even if it’s not simple.

Perhaps not the kind of things us mere mortals can understand,
but some kind of projection matrix or something along those lines.

(Wikipedia has some mercator projection maths here but I can only understand a tiny fraction of it.)

That seems reasonable.
There’s some code for it on my favourite interpolation resource:

Alternatively using the range mapping function to map from the longitude-latitude range to the x-y range might give reasonable results.

Essentially something like:

const auto x = map(latitude, minLatitude, maxLatitude, minX, maxX);
const auto y = map(longitude, minLongitude, maxLongitude, minX, maxX);

With map being something like:

constexpr float map(float input, float inputMin, float inputMax, float outputMin, float outputMax)
	return (outputMin + (((input - inputMin) / (inputMax - inputMin)) * (outputMax - outputMin)));

constexpr double map(double input, double inputMin, double inputMax, double outputMin, double outputMax)
	return (outputMin + (((input - inputMin) / (inputMax - inputMin)) * (outputMax - outputMin)));

constexpr long double map(long double input, long double inputMin, long double inputMax, long double outputMin, long double outputMax)
	return (outputMin + (((input - inputMin) / (inputMax - inputMin)) * (outputMax - outputMin)));
1 Like

I might try this. Thanks for the tip!

1 Like

This seems to work:

static const double CENTRAL_MERIDIAN_OFFSET = -140.0;
static const double W = 6340.0;
static const double H = 4770.0;

void millerProject(double lat, double lon, double &x, double &y)
	lon = TORADIANS(lon);
	lat = TORADIANS(lat);
	x = lon;
	y = 1.25 * log( tan( 0.25 * PI + 0.4 * lat ) );
	x = ( W / 2 ) + ( W / (2 * PI) ) * x + CENTRAL_MERIDIAN_OFFSET;
	//Northern hemisphere behaves differently from southern hemisphere
	//most likely from floating point precision errors
	//this helps compensate for the difference
	if (lon > 0.0)
		y = H - (( H / 2 ) - ( H / ( 2 * 2.303412543 ) ) * y);
		y = H - (( H / 2 ) - ( H / ( 2 * 2.11896 ) ) * y);

Slightly modified the code found here: https://stackoverflow.com/questions/9318429/latitude-longtitude-conversion-to-image-x-y-coordinates-miller-cylindrical-proj

plugged in coordinates for a few cities (latitude N is negative and S is positive, longitude W is negative and E is positive).
Seattle WA USA: (47.6062° N, 122.3321° W) -> (875.596, 1372.18)
Moscow Russia: (55.7558° N, 37.6173° E) -> (3692.48, 1256.85)
Melbourne Australia: (37.8136° S, 144.9631° E) -> (5582.96, 3102.51)


This is precisely the kind of mathematical magic I was hoping for.

All that’s needed now is that list of places and coordinates and a means to put this code into practice.

If it weren’t for the need of log and tan I’d say make this constexpr and do the calculations at compile time.
But failing that this could be turned into a simple command line program to run as a pre-build step over some appropriately formatted data.

I’m getting a bit carried away though, ultimately this is @Hanski’s call.


But @Hanski 's map is Mercator and the given math is for Miller. The cities will be in the wrong place