Challenge: Maximum Pixel-art, Minimum Code

Nice challenge!

Cheat answer first: the pixel layout of video ram gives this formula a pretty good density :stuck_out_tongue:


Ancient city:


I tested on a pico-8 using the following snippet to map bytes 0x0…0x3ff to video – hopefully it comes out the same.

cls()pal(1,7)for j=0,7do for x=0,127do for y=0,7do
pset(x,32+j*8+y,(@(x+j*128) & (1<<y)) >> y) end end end
    a.sBuffer[i] =
      // copy from one column to the right, scrolling the whole screen left
      (i%128<127) ? b[i+1] : (
        // except the last column, which gets seeded with one pixel on the first frame
        a.frameCount == 0 ? (i==511)*32 : 
        // and calculated from the previous column via Rule 30 afterward
          // MSB relies on the LSB of the byte to the southwest
          (((((((b[i-1] & 192) >> 6) | ((b[(i+127)%1024] & 1) << 2))        + 7) % 8) < 4) << 7) |
          ((((( (b[i-1] & 224) >> 5)                                        + 7) % 8) < 4) << 6) |
          ((((( (b[i-1] & 112) >> 4)                                        + 7) % 8) < 4) << 5) |
          ((((( (b[i-1] &  56) >> 3)                                        + 7) % 8) < 4) << 4) |
          ((((( (b[i-1] &  28) >> 2)                                        + 7) % 8) < 4) << 3) |
          ((((( (b[i-1] &  14) >> 1)                                        + 7) % 8) < 4) << 2) |
          ((((( (b[i-1] &   7) >> 0)                                        + 7) % 8) < 4) << 1) |
          // LSB relies on the MSB of the byte to the northwest
          (((((((b[i-1] &   3) << 1) | ((b[(i+1024-129)%1024] & 128) >> 7)) + 7) % 8) < 4) << 0)
          // Check for previous generaiton values 1,2,3,4 ---------------->\_____________/
          // Put new bits in the correct positions in the current byte ------------------>\____/

This almost works. Something goes subtly wrong approximately every 9 columns. The output is still nice and chaotic and rule-30-esque, and you can’t even tell once it wraps around so the gaps on the north slope disappear, but those gaps are clear evidence of the problem.



There was a project at some point to create a game with generative art. The method the creator was using was to just have one formula and each sprite was a seed number. They would step through these manually and find results that seemed to be visually distinct or interesting, and then decide what that sprite could be used for.





Looks like this is your thing, Felipe!

1 Like

It’s a good fit for the amount of time I have these days. :smiling_face_with_tear:



Needs high framerate emulator/hardware, flashes too much as a gif



How I’ve been making these strings…



I think it would technically be legal C++ with a reinterpret_cast to convert the function address to a const uint8_t *, but while it would probably compile and run it would very likely be classed as undefined behaviour.

(Though I must admit, I have no clue what the standard would have to say on the matter, it’s not something I’ve ever needed to look up.)

I wasn’t aware ProjectABE used a different version of the library.

The official Arduboy2 library has exposed the logo image as Arduboy2Base::arduboy_logo since July 2020.

Careful, at this rate you’ll be converting people to functional programming. :P

Getting the lambda syntax mixed up with another language?

The library wasn’t updated, the logo still wasn’t exposed there.

Looks like the compiler got it mixed up too, then. :stuck_out_tongue:

Of course, if you’re going to go relying on compiler extensions, why bother with an ‘iimmediately invoked’ function when you can just use statement expressions?

({static int16_t t=-1,F[16][4];if(t-a.frameCount){if(t==-1){for(int j=0;j<16;++j){F[j][0]=(21+j)*383^j*479;F[j][1]=(49+j)*7^j*503;}}t=a.frameCount;for(int j=0;j<16;++j){F[j][0]+=F[j][2]-=(F[j][0]>>15|1)*3;F[j][1]+=F[j][3]-=(F[j][1]>>15|1)*3;}}int r=0;for (int j=0;j<16;++j){if((i&0x7F)==((F[j][0]>>8)+64)&&(i>>7)==((F[j][1]>>8)+32)>>3){r|=1<<(((F[j][1]>>8)+32)&7);}} r;});

It shaves off a few characters to say the least.

Unless I misunderstood, alternative operators are part of the standard, not compiler extensions. :thinking:

1 Like

Ah, those abominations.

Got to be honest, I forgot those digraphs existed. Fair enough.
(Not the trigraphs though, I remembered those being deprecated.)

Though surely now you’re just trying to obfuscate things?
The alternative operators use more characters than the ‘proper’ ones.

1 Like

Yeah, there wasn’t much point in trying to codegolf that one so I figured I’d throw in the <::> as a little prank.

1 Like

Then consider me pranked.


No time to make an animation, but here’s a really simple single-pixel chequerboard:

for(size_t index = 0; index < 1024; ++index)
	arduboy.sBuffer[index] = ((index % 2) == 0) ? 0xAA : 0x55;

(Full code in this gist.)

Hideous, badly-written golfed form for character-counting loons:

for(int i=0;i<1024;++i)b[i]=i&1?0x55:0xAA;
1 Like

You can further golf the expression into: 170>>(i&1)

and one more with


Oh even better:


That actually produces the opposite sequence.
My original sequence was 0xAA, 0x55, 0xAA, 0x55, ...,
this produces 0x55, 0xFFAA, 0x55, 0xFFAA, ....

To produce the right sequence you’d need to use either -i%2^170 or ~i%2^85.

~i%2 produces the opposite of -i%2 because -x == (~x + 1).

1 Like

@sparr - I struggled to get the code to run for some reason- but glad I persisted! This is wonderful stuff! I like watching it slowly develop with a.setFrameRate = 1;. It’s great you provided such readable code- really helped. I golfed your code a little, to get it down to 361(!) characters. Perhaps a collaboration with @Mr.Blinky and @FManga could get that lower?..


Thanks again for sharing this!

PS - Welcome to the forum ~ what a great first post! :partying_face:

I think we’ve reached peak ‘Checkers’! :checkered_flag:

It’s interesting that while @Mr.Blinky’s 7 characters (-i%2^85) is 3 characters smaller than @Pharap’s original (i&1?85:170), the compiled code is 102 bytes more! (14 → 116 bytes). I presume this is the use of modulus function?..

Of course, it highlights the issue of golfing a compiled languages, rather than interpreted languages :slight_smile:

Further inspiration:

  • Hitomezashi Stitch Patterns.
  • Images ‘hidden’ in the digits of pi.
  • Drawing larger images- rather than repeating patterns. E.g. a screen-filling shape like a star or circle would be fun.
  • Pokemon-style screen wipes could be useful.

Thank you everyone for such great entries.

A classic woven pattern- ‘Houndstooth’.


I’m collating nice 8x8px fill patterns at :slight_smile:

1 Like