Challenge: Maximum Pixel-art, Minimum Code

It probably shouldn’t compile at all, but Arduino uses -fpermissive which might as well have been called -fabomination.

More about not having a better way to access the logo on ProjectABE’s version of the library.

While it is possible to use getPixel/drawPixel as Pharap pointed out, what you really want to do is to invert your logic: instead of telling something that a pixel needs to be set, your oneliner needs to respond with whether a pixel is set or not. In other words, instead of drawPixel(5, 10) you convert i into x and y (x = i % 128 and y / 8 = (i / 128)) then use that to set the desired pixel:
(i % 128 == 5) && ((i / 128) == (10/8)) ? 1 << (10/8) : 0

The same idea can be applied to multiple points anywhere in the screen. This is probably not the best way to tackle the problem of “Maximum Pixel-art, Minimum Code”.

“Flies on a :poop:”:

(<::>(int16_t i){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);}}return r;})(i);
Spoiler

ArduboyRecording(2)

3 Likes

Wow - another nice animation!
I feel this is moving away from the spirit of the challenge of using just “maths formula”… perhaps we should add some rules? :thinking:
e.g. Entries may not create new variables or functions …?

I didn’t mean for that last one to be taken seriously, I was just illustrating my previous point. It might be best/simpler to just have a maximum char count.

Would #defines separate from the line be legal in that case?

I’d guess no, since defines can’t be “in place of MAGIC”?

Nice challenge!

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

i

Ancient city:

((((i&128)>>4)^(i&8))*31)&(i*191)

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
2 Likes
    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
        // https://mathworld.wolfram.com/Rule30.html
        (
          // 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.

ArduboyCapture

3 Likes

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.

Checkers:

(1<<(c/4+i&7))^(1<<((c/4-i)&7))^-((i/8^i/128)&1)
Spoiler

ArduboyRecording(7)

2 Likes

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:

A:

b[i]^"\0p\30|\276\273>>>\273\276|\30\16\0"[(!(c&8)^!(i&128)?i:-i)&0xF]
Spoiler

Needs high framerate emulator/hardware, flashes too much as a gif
ArduboyRecording(9)

B:

(!(c&8)^!(i&128)?i%16:-i&0xF)["\0p\30|\276\273>>>\273\276|\30\16\0"]
Spoiler

How I’ve been making these strings…
imagem

ArduboyRecording(10)

2 Likes

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.

2 Likes

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)