The final maprewrite has a, as we think, nice compromise between size and readeability. If we forget about the "Tile *next;" pointer, then this Tile struct is only 80 bits (10 bytes) packed, and 96 bits (12 bytes) unpacked. This seems a nice compromise in comparison with the 50 bits of the original map and much more freedom. The final size would be 128bits (16 bytes) unpacked, which is a nice number for both 32bit and 64bit machines.
Some comments need to be reworked (especially sizewise), and there are some minor changes here and there, but this would be the final layout. We have taken the liberty of wasting some bits in areas where they are not important, eg. because of the union there is a ton of space left anyways, such as treest_t or fields_t (hedges struct). I will update this later on, but I gotta run now :)
The Tile struct
#include <stdio.h> #define MAPSIZEX 1024 #define MAPSIZEY 1024 typedef signed char int8; typedef unsigned char uint8; typedef unsigned short uint16; typedef unsigned long uint32; //#define PACK // uncomment at will #ifdef PACK #pragma pack(push, 1) #endif // Maybe the numbers should be changed later on but I'll dig into this later. enum Transport_Types { // WARNING! // Be sure to code as such that changing one (or more) of these numbers don't break the code! TT_ROAD = 0, TT_RAIL = 1, TT_ERAIL = 2, TT_MONO = 3, TT_MAGLV = 4, TT_TRAM = 5, TT_WATER = 14, TT_AIR = 15, }; typedef struct Height_t { uint16 altitude:4; // height of tile uint16 height_N:3; // offset of North corner from height uint16 height_E:3; // offset of East corner from height uint16 height_W:3; // offset of West corner from height uint16 height_S:3; // offset of South corner from height /* 0 unused bits */ } Height_t; /* 16 bits */ /********************** Surface Tile types ********************/ /* just a basic ground tile */ typedef struct Ground_t { uint8 ground_type:4; // grass, desert, rocks, snow .. uint8 amount:2; // amount of grass, desert and stuff. uint8 counter:2; // update counter /* 8 unused bits */ } Ground_t; /* 16 bits */ /* underground for roads as grass, paved street, etc. Also for trams, crossings */ typedef struct Foundation_t { uint8 type:4; // uses 3 bits now, could be more in the future uint8 ground_type:4; // backup of Ground_t.ground_type to grow back farms, etc. /* 8 unused bits */ } Foundation_t; /* 16 bits */ typedef struct Water_t { uint8 type:4; // water/coast/canal uint8 shores:4; // cache for which edge is a shore/water /* 8 unused bits */ } Water_t; /* 16 bits */ typedef struct Bridge_t { uint8 type:4; // type of bridge, e.g. wooden, concrete, tubular uint8 part:4; // hmm, donnu what this is supposed to be?? uint8 dir:3; // direction of bridge uint8 section:1; // is it a middle part, or a "endcap"? endcaps will not be needed for cliffs maybe uint8 ending:1; // northern/southern end of bridge /* 3 unused bits */ } Bridge_t; /* 16 bits */ /* Blathijs needs to detail this */ typedef struct Support_t { uint8 type; // different types of support, also depending on year uint8 reserved; // whatever we need to put there /* 0 unused bits */ } Support_t; /* 16 bits */ /* here come all things that are underground, for example tunnels, underground stations and the like */ typedef struct Underground_t { uint8 type:8; // something /* 8 unused bits */ } Underground_t; /* 16 bits */ /********************** Build Tile types **********************/ typedef struct Industry_t { uint16 gfx; // index into industries array uint8 type; // type of the industry (graphics) uint8 animation; // animation states of industry (6 bits, some toyland 8 bits) uint8 sound:1; // sound-effect generated uint8 built:1; // under construction uint8 counter:3; // counstruction counter uint8 stage:2; // stage of construction /* 9 unused bits */ } Industry_t; /* 48 bits */ typedef struct Stop_t { uint8 status; // status of the RV stop /* here we store which of the two bays is in use, if there is a vehicle driving around in the stop and the like. maybe we can merge the slots in there. */ uint8 slotA:4; // TTL of the first slot uint8 slotB:4; // TTL of the second stot /* 0 unused bits */ } Stop_t; /* 16 bits */ typedef struct Station_t { uint16 index; // index of the station uint16 gfx:12; // type of station (graphics) uint16 transport_type:4;// rail, electric rail, monorail, trams, maglev ... union { Stop_t stop; // this clarifies the status of a stop (16 bits) /* in case we put other things here for different station types (as identified by transport_type), they should be a maximum of 16 bits in lengh */ } status; /* 0 unused bits */ } Station_t ; /* 48 bits */ typedef struct Checkpoint_t { uint16 index; // index of the checkpoint (allowing checkpoints to spawn across tiles) uint8 gfx; // type of checkpoint (graphics) uint8 dir:2; // direction, unused for buoys uint8 transport_type:4; // road/rail/water(buoys) /* 18 unused bits */ } Checkpoint_t; /* 48 bits */ typedef struct Depot_t { uint8 part; // part of the depot (ship depots, large train depots .. ) uint8 transport_type:4; // road/rail/water uint8 dir:2; // direction (facing of opening) /* 34 unused bits */ } Depot_t; /* 48 bits */ /* REAL locks sometime, we need status of the doors/lift, which will be placed here. Someone just has to code the graphics and the system in the program */ typedef struct Locks_t { uint8 status; // locks status open/closed, etc. uint8 dir:2; // direction of locks uint8 type:3; // Sea or in-land locks ??? uint8 part:3; // part of the lock /* 32 unused bits */ } Locks_t; /* 48 bits */ typedef struct Track_t { uint8 transport_type:4; // type of transport to be used train/bus/tram, etc. uint8 ground_fence:1; // is the ground fence for this part present? uint8 reserved:1; // this part of track is reserved /* signal part */ uint8 sig_status1:2; // red / green / yellow / blue one way uint8 sig_status2:2; // the other way uint8 sig_present:2; // which directions are found uint8 sig_type:3; // presignals, normal signals, new types ... uint8 sig_semaphore:1; // signal or semaphores /* 0 unused bits */ } Track_t; /* 16 bits */ /* All road and rail types go here */ typedef struct Transport_t { uint8 pieces; // what bits of transport are present (8 bits for road) //the "other half" is either the southern or the western part of the tile uint8 second_owner; // who owns the "other half" of a 'doubly-built' tile? struct Track_t track_info[2]; // track information as layout, signal, etc. (2x16 bits) } Transport_t; /* 48 bits */ typedef struct Lift_t { uint8 pos:7; // position of the lift uint8 moving:1; // is the lift moving or not? uint8 dest:6; // final position of lift /* 2 unused bits */ } Lift_t; /* 16 bits */ typedef struct Town_t { struct Lift_t lift; // lift (16 bits) uint16 index:11; // index to which town a building belongs to uint16 stage:2; // stage of construction uint16 counter:3; // construction counter uint8 gfx; // town building type (graphics) /* 8 unused bits */ } Town_t; /* 48 bits */ typedef struct Hedge_t { uint8 hedge_SW:4; // hedge-type on SW border uint8 hedge_SE:4; // hedge-type on SE border uint8 hedge_NE:4; // hedge-type on NE border uint8 hedge_NW:4; // hedge-type on NW border /* 0 unused bits */ } Hedge_t; /* 16 bits */ typedef struct Trees_t { uint8 type; // type of trees uint8 counter:4; // update counter uint8 count:2; // number of trees /* 2 unused bits */ struct Hedge_t hedge; // type of hedge for wild-reverse (16 bits) uint16 growthA:3; // growth status of tree A uint16 growthB:3; // growth status of tree B uint16 growthC:3; // growth status of tree C uint16 growthD:3; // growth status of tree D /* 4 unused bits */ } Trees_t; /* 48 bits */ typedef struct Fields_t { struct Hedge_t hedge; // type of hedge for farmfields (16 bits) uint8 type:4; // type of farm-fields uint8 counter:2; // update counter for fields /* 26 unused bits */ } Fields_t; /* 48 bits */ typedef struct Unmovable_t { uint8 type; // unmoveables like transmitter, lighthouse, HQ /* 40 unused bits */ } Unmovable_t; /* 48 bits */ /********************** _map Tile struct **********************/ typedef struct Tile { struct Tile *next; // pointer to bridge/tunnel, etc. 32/64 bits arch dep. Height_t height; // altitude information uint8 owner; // owner of a tile, 255 no-owner ?? uint8 surface_class:3; // class of a surface on a tile. Directs to surface_t union uint8 build_class:5; // class of a building on a tile. Directs to build_t union union { Ground_t ground; // basic ground type Foundation_t foundation; // underground for roads/trams as paved street, etc. Water_t water; // water, coast, canals Bridge_t bridge; // bridge type, section, directoin, etc. Support_t support; // Blathijs needs to detail this Underground_t underground;// all underground stuff } surface; /* 16 bits */ union { Industry_t industry; // industry tiles Station_t station; // station tiles Checkpoint_t checkpoint; // checkpoint tiles Depot_t depot; // depot tiles, road/train but also ship depots Locks_t locks; // locks for canals Transport_t transport; // all kinds of roads, and rail, incl. tram Town_t town; // town (houses, offices) tiles Trees_t trees; // tree tile types Fields_t fields; // farm fields tile types Unmovable_t unmovable; } build; /* 48 bits */ /* 0 unused bits */ } Tile; /* 128 bits */ #ifdef PACK #pragma pack(pop) #endif Tile _map[MAPSIZEX][MAPSIZEY]; int main ( void ) { printf("Tile\n"); printf("|-- *next: %3d\n", sizeof(_map[0][0].next)*8); printf("|-- height: %3d\n", sizeof(_map[0][0].height)*8); printf("|-- owner: %3d\n", sizeof(_map[0][0].owner)*8); printf("|-- class: 8\n"); printf("|-- surface: %3d\n",sizeof(_map[0][0].surface)*8); printf(" |-- barren: %3d\n",sizeof(_map[0][0].surface.ground)*8); printf(" |-- foundation: %3d\n",sizeof(_map[0][0].surface.foundation)*8); printf(" |-- water: %3d\n",sizeof(_map[0][0].surface.water)*8); printf(" |-- bridge: %3d\n",sizeof(_map[0][0].surface.bridge)*8); printf(" |-- support: %3d\n",sizeof(_map[0][0].surface.support)*8); printf(" |-- underground: %3d\n",sizeof(_map[0][0].surface.underground)*8); printf("|-- build: %3d\n",sizeof(_map[0][0].build)*8); printf(" |-- industry %3d\n",sizeof(_map[0][0].build.industry)*8); printf(" |-- station %3d\n",sizeof(_map[0][0].build.station)*8); printf(" |-- checkpoint %3d\n",sizeof(_map[0][0].build.checkpoint)*8); printf(" |-- depot %3d\n",sizeof(_map[0][0].build.depot)*8); printf(" |-- locks: %3d\n",sizeof(_map[0][0].build.locks)*8); printf(" |-- transport: %3d\n",sizeof(_map[0][0].build.transport)*8); printf(" |-- trackinfo: %3d\n",sizeof(_map[0][0].build.transport.track_info[0]) * 8); printf(" |-- trackinfo: %3d\n",sizeof(_map[0][0].build.transport.track_info[1]) * 8); printf(" |-- town: %3d\n",sizeof(_map[0][0].build.town)*8); printf(" |-- lift %3d\n",sizeof(_map[0][0].build.town.lift)*8); printf(" |-- trees: %3d\n",sizeof(_map[0][0].build.trees)*8); printf(" |-- hedges %3d\n", sizeof(_map[0][0].build.trees.hedge)*8); printf(" |-- fields: %3d\n",sizeof(_map[0][0].build.fields)*8); printf(" |-- hedges %3d\n", sizeof(_map[0][0].build.fields.hedge)*8); printf(" |-- unmovable: %3d\n\n",sizeof(_map[0][0].build.unmovable)*8); printf("Tile (total size): %3d (Mapsize %dx%d %dKB)\n",sizeof(_map[0][0])*8, MAPSIZEX, MAPSIZEY, sizeof(_map)/1024); printf("Compr. (50/%3d): %2.1f%%\n", sizeof(_map[0][0])*8, 5000.0/(sizeof(_map[0][0])*8)); printf("Compr. without *next(50/%3d): %2.1f%%\n", (sizeof(_map[0][0])-sizeof(_map[0][0].next))*8, 5000.0/((sizeof(_map[0][0])-sizeof(_map[0][0].next))*8)); printf("--The higher value the better the compression.\n"); printf("Old-Tile (total): %3.0f (Mapsize %dx%d %.0fKB)\n", 6.25*8, MAPSIZEX, MAPSIZEY, (6.25*MAPSIZEX*MAPSIZEY)/1024); return 0; }
Considerations
I was a bit worried about the amount of computation some of these structs, as you would first need to get the split part, then & or | that, and it would take too much CPU (in contrast to now of course). My worse fears became reality when I saw the assembly output of "if (tile.height & 2) ..." which resulted in:
; 199 : if (tile.height & 2) return 1; mov al, BYTE PTR ?tile@@3UTile@@A+4 shr al, 4 and al, 15 ; 0000000fH movzx ecx, al and ecx, 2 je SHORT $L876
Thank god it was debug build, and optimized it resulted into the following, so all my doubts are gone like clouds in the sun:
; 199 : if (tile.height & 2) return 1; mov al, BYTE PTR ?tile@@3UTile@@A+4 add esp, 32 ; 00000020H test al, 32 ; 00000020H je SHORT $L876