How about some help, guys?


public void main(String[] args)
{
    int[] badguy = new int[768]; // 256*3

    // random lookup
    for(int i=0; i<768; i+=3)
       draw(badguy[i], badguy[i+1], badguy[i+2]);

    // sequence lookup
    for(int i=0; i<768;)
       draw(badguy[i++], badguy[i++], badguy[i++]);
}

I can’t compile on this machine, but my gutt-feeling tells me it’s smaller :slight_smile:

Are the values for i well defined for this usage?
What order does Java evaluate the parameters?

That’s fine, they are evaluated left to right, with the value of i being updated immediately after each array dereference.

I put all four ways into my base code for my new 4k game.
Results after proguard and kzip, sorted by size:

Interlaced sequential: 893 bytes


int[] badguy = new int[768]; // 256*3

for(int i=0; i<768;)
   pixels[badguy[i++]+badguy[i++]*640]=badguy[i++];

One array per variable: 898 bytes


int[] badguy_x = new int[256];
int[] badguy_y = new int[256];
int[] badguy_t = new int[256];

for(int i=0; i<256; i++)
   pixels[badguy_x[i]+badguy_y[i]*640]=badguy_t[i];

Interlaced random: 899 bytes


int[] badguy = new int[768]; // 256*3

for(int i=0; i<768; i+=3)
   pixels[badguy[i]+badguy[i+1]*640]=badguy[i+2];

2d array: 908 bytes


int[][] badguy = new int[256][3];

for(int i=0; i<256; i++)
   pixels[badguy[i][0]+badguy[i][1]*640]=badguy[i][2];

The biggest downside of 2d array and interlaced random is that it requires a numerical constant in the constant pool for each variable you have.
Interlaced sequential is by far the most repetetive/easy to compress, but it could be hard to work with.

EDIT:
Forgot *640 in 2d array, adding two more bytes


final int X = 0;
final int Y = 1;
final int TYPE = 2;
final int HEALTH = 3;

int[] badguys = new int[256][4];

// then you can *read* the bad guys like this:
badguys[i][X]
badguys[i][Y]
badguys[i][TYPE]
badguys[i][HEALTH]


The compiler or the obfuscators should delete the final variables declerations and insert their values where they are referenced.

But obviously not doing the double dereferencing of the array every time you access it.

final int BADGUY_COUNT = 256;

final int X = 0;
final int Y = 1;
final int TYPE = 2;
final int HEALTH = 3;
final int BADGUY_ATTRIBUTE_COUNT = 4;

int[] badguys = new int[BADGUY_COUNT][BADGUY_ATTRIBUTE_COUNT];

for(int i = 0;i < BADGUY_COUNT;i++)
{
   final int [] currentBadguy = badguys[i];

   // then you can *read* the bad guys like this:
   currentBadguy[X]
   currentBadguy[Y]
   currentBadguy[TYPE]
   currentBadguy[HEALTH]
}

That still takes up more space than to just have one array per variable.

Skipping the double dereferencing doesn’t actually save you that much (it didn’t save a single byte for me when I tried it in Miners4k) after compression.

Indeed, I can imagine it depends a great deal on the structure of your code, and what you are doing with the arrays.

Keeping just one int[][] within scope may allow more efficient use of the direct access local variables.
However this may not help at all if you have a smart bytecode optimiser that can reorder local variable usage for optimum size.

also, if the object you are representing with the int [] has alot of attributes,
Is it safe to assume zip compression will compress :-

int [] a = new int[200];
int [] b = new int[200];

int [] y = new int[200];
int [] z = new int[200];

Down to less than :-

int [][] aa = new int[26][200];

Perhaps it is a safe assumption, perhaps not.

Either way, I would imagine the difference between the 2 approaches will be less than 30bytes in the vast majority of cases.
Algorithmic optimisations are always a better place to start =)

:edit:

The Interlaced sequential is kinda funky, and is better for that simplistic test case - however I can’t think when I would ever want to access every element of the array sequencially (and only once!)

More often than not you will be accessing the elements randomly.

FYI markus I took your source for the Miners4k game and was able to compact it about 50 bytes more.

I used a combination of JoGa, Proguard and JShrink…and also BJWFlate for compression.

edit: link removed at request.

Cool, but could you please not distribute any binaries (or modified source code versions) of it?

How come it seems like most folks use ints for everything? In the end does the JVM not account for the actual size difference between a int and short? And it seems like bit twiddling isn’t really used anymore, why is that?

Depends on what you mean. SuperPackME does all kinds of bit twiddling, for example. But you have to be careful. It may actually cost you more in instructions to twiddle bits than it would to use a straight int (which is “free” on the stack anyway). Remember, we’re targetting size, not memory. :slight_smile:

ints and shorts (and bytes) both use the CONSTANT_Integer_info entries in the constant pool. =)

edit:

No, bytes don’t seem to use the constant pool! :smiley: Makes sense.

More so by bit twiddling I meant using ints to compact things into one value instead of storing massive amounts of data in seperate things… Take tiling for instance, if your trying to store a map and are storing a few tiles only across a map, can’t you compact that map into a smaller space to be stored for us in the game as a resource by having ints hold multiple peices of information vs multiple ints for the same information? Just curious.

I had all locations of the suits on all cards for Poker4k, encoded in 1 long.

It was fun, but the bit-shifting overhead removed quite a lot of the benefit.

[quote=“grazi,post:34,topic:25487”]
Of course. Like I said, SuperPackME is an excellent example of this. It reduces the number of colors in an image down to the absolute minimum necessary, then stores it in a bit-packed format. That bit-packed format is converted to a hex-string which is inserted into the class data. The resulting class compresses extremely well.

To extract the data, the hex codes are converted back into nibbles. The graphics data is then twiddled out of the nibbles to produce a list of pixels and their colors. The process is described in more detail here.

As for someone who stashes levels that way, I think Kevglass’s rendition of Tower Toppler might have packed data at the bit level, but don’t quote me on that. Many (most?) developers use procedurally generated levels to avoid the overhead of storing level data.

Does that better answer your question?