Challenge: Maximum Pixel-art, Minimum Code

@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?..

(i%128<127)?b[i+1]:(c<1?(i==511)*32:((((((((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))+7)%8)<4)<<1)|(((((((b[i-1]&3)<<1)|((b[(i+895)%1024]&128)>>7))+7)%8)<4))));

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’.

"H`\374~{\3710\30"[i%8]

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

1 Like

Weekend Golf anyone ?

I’ve realised I can compact this diagonal, 2px line pattern from:

72^"Px(\210\311KND"[i%8]

to a more mathematical:

i%8==4?129:3<<(i+3)%8

Is it possible to shave off a couple more characters? :thinking:
(Must produce bit identical pattern).

I can’t beat it offhand, but I can match it:

i+=3,i%=8,3<<i|3>>8-i
1 Like

Nice stuff! :star_struck:
I’ve not considered using | in these statements… also I don’t think I ever explored multiple expressions (perhaps it feels like cheating!?.. :sweat_smile: ). I’m also waiting to find a good use for the ‘space-ship’ operator <=>

1 Like

I’m using that because after inspecting the binary encoding of 129 I noticed that you’re basically attempting to left-rotate 3 and offset the pattern.

x << n | x >> (c - n) is the idiom for a left-rotate, where:

  • x is the value to be rotated
  • n is the number of bits to rotate by
  • c is the target bit width

Effectively it shifts the bits left by n and right by the ‘inverse’ of n and combines the result, thus mimmicking a rotate once the outlier bits are discarded. Normally you’d need an & to mask off the excess, but in this case the implicit cast to uin8_t has the same effect.

The comma operator is 100% legal C++, even if it’s pretty much only ever useful for operator overloading and nasty tricks like this.

The += and %= trick would work without the comma operator, but you need at least one comma to ensure that the modification is sequenced before the rotate, otherwise you’ll fall foul of the fact the evaluation order of binary operator operands is unsequenced. (I.e. the compiler is allowed to evaluate them in whichever order it wants.)

Unfortunately that won’t work on Arduboy because it’s a C++20 feature.

1 Like

Ah! So it allows shifting with wrapping around those ‘lost bits’ ? i.e. it can be used for thicker line patterns.

No!!! I thought that was gonna be neat! :scream:

Update: @Pharap your code doesn’t seem to work for me…? Just a blank screen…

Full code ~
#include <Arduboy2.h>
Arduboy2  a;
uint8_t  *b = a.sBuffer;
uint16_t &c = a.frameCount;

void setup() {
  a.begin();
}

void loop() {
  if (!a.nextFrame())
    return;

  for (int16_t i = 0; i < 1024; i++) {
    a.sBuffer[i] = i+=3,i%=8,3<<i|3>>8-i;
  }
  a.display();  
}

Building off of @Pharap’s answer… bake the +3 shift into the shifted constant 3:

24<<i%8|24>>8-i%8
2 Likes

Wow! Down to 17 characters! That’s amazing! :exploding_head:
– I will need to try with other patterns now…

Update: Yes! It’s simple to change the constant 2428 to change the line width. (A nice feature of macOS’s calculator in ‘Programmer’ view, is being able to click on binary digits and quickly get a value). Thanks so much @Pharap & @brow1067 !

Needless to say, I always test my noodling in Ardens (the coloured pixel grid is vital!).
… But I do miss the old emulator with built in compiler, to quickly iterate on these little challenges…

2 Likes

It’s not a perfect solution, but the desktop builds of Ardens watch the hex file and automatically reload and restart the sim when the file is modified. So you can iterate and build while watching Ardens in the background.

2 Likes

I’m not sure what you mean by ‘lost bits’.

Basically:

  • 0b0000_0011 rol 70b1000_0001
  • 0b0000_0011 << 70b1000_0000
  • 0b0000_0011 >> (8 - 7)0b0000_0001
  • 0b0000_0011 << 7 | 0b0000_0011 >> (8 - 7)0b1000_0001

So you’re using the left shift to move the bits leftwards, discarding what would have been wrapped around, and then the right shift generates the part of the pattern that would have resulted from the wraparound.

Do it manually a few times and you’ll see what’s happening.

Ah, sorry, I forgot you were doing it inline rather than implementing the expression as a function.

I was depending on the assumption that i would be a fresh variable each time as if by:

uint8_t f(uint16_t i)
{
	return i+=3,i%=8,3<<i|3>>8-i;
}

I had been testing with:

std::uint8_t get_a(std::uint16_t i)
{
	return 72 ^ "Px(\210\311KND"[i % 8];
}

std::uint8_t get_b(std::uint16_t i)
{
	return i += 3, i %= 8, 3 << i | 3 >> 8 - i;
}

bool test()
{
	for(std::uint16_t index = 0; index < 1024; ++index)
		if(get_a(index) != get_b(index))
			return false;

	return true;
}

When i is persistant as it is in a for loop, that means you can’t just modify it as I did there because it’ll break the loop condition early and skip steps.

It’s times like this I wish C++ had an equivalent of let x = y in e, like most functional/lambda-calculus-derived languages do, then I could have just declared a variable for the duration of the expression.

let j=(i+3)%8in3<<j|3>>8-j

Though to be fair, that would have ended up being longer anyway.

Here’s what I do in Microsoft land:

MSCalc

1 Like

Fair comment :laughing: … of course I should have said the bits that overflow on shifting. Your example demonstrates nicely this function; thanks!


This shows my complete, blind faith in you!.. Happily copy&pasting anything you post :crazy_face: … which also answers the question, “If @Pharap jumped off to an undefined address… would you ?!” …


Nice! I assumed this was a standard… I just never tried clicking those binary digits until last year. Also loving your ‘Vapourwave’ magenta theme! :wink:

1 Like

Did anyone submit something like:

1;

Does that count?

2 Likes

lol… well, yes, constants are valid I guess…

Just realised, @Pharap has been immortalised in the collection:

"\36w7o7w\36"[i%8]*2

… so now I need to add an Invader…

1 Like

Whether they overflow or not depends on how big the datatype being operated on is…

Arithmetic operators don’t actually work on types smaller than int and on Arduboy int is 16 bits, so technically in this case the bits shifted out by the right shift are automatically discarded, but the bits shifted to the left are actually moved into the upper byte and aren’t discarded until the result is implicitly cast back to uint8_t.

(I know, I’m being pedantic, but no more so than the compiler would be.)

After all, if you right shift a negative value when using a signed type (like int) you could get a nasty surprise if you were expecting zeroes to be shifted in. (It’s only safe here because 3 isn’t negative.)

The tragic thing is that this was one of the few times I actually tested rather than just blindly throwing code up. Alas, my testing methodology was flawed.

Everybody follow me down this nop slide(!)
I promise there’s nothing nasty at the bottom(!)

For a moment I read that as ‘vapourware’ and felt my unfinished projects stare back at me.

There’s no special aesthetic involved, I just particularly like purple.
If I had an aesthetic, it would probably be ‘Victorian’.
(And now I’ve gone down an aesthetics rabbit hole…)

1 Like

Ode to @filmote :space_invader: ~

    constexpr uint8_t invadingBug[] PROGMEM {
        8, 8, //  8x8 px image
        0x68, //  ▓░░▓░▓▓▓
        0x1D, //  ▓▓▓░░░▓░
        0x36, //  ▓▓░░▓░░▓
        0x1C, //  ▓▓▓░░░▓▓
        0x36, //  ▓▓░░▓░░▓
        0x1D, //  ▓▓▓░░░▓░
        0x68, //  ▓░░▓░▓▓▓
        0x00, //  ▓▓▓▓▓▓▓▓
    };
    // Magic: 67^"+^u_u^+C"[i%8]

    constexpr uint8_t invadingCrab[] PROGMEM {
        8, 8, //  8x8 px image
        0x34, //  ▓▓░░▓░▓▓
        0x4E, //  ▓░▓▓░░░▓
        0x16, //  ▓▓▓░▓░░▓
        0x0F, //  ▓▓▓▓░░░░
        0x16, //  ▓▓▓░▓░░▓
        0x4E, //  ▓░▓▓░░░▓
        0x34, //  ▓▓░░▓░▓▓
        0x00, //  ▓▓▓▓▓▓▓▓
    };
    // Magic: 96^"T.vov.T`"[i%8]

    constexpr uint8_t invadingSquid[] PROGMEM {
        8, 8, //  8x8 px image
        0x08, //  ▓▓▓▓░▓▓▓
        0x5C, //  ▓░▓░░░▓▓
        0x36, //  ▓▓░░▓░░▓
        0x1F, //  ▓▓▓░░░░░
        0x36, //  ▓▓░░▓░░▓
        0x5C, //  ▓░▓░░░▓▓
        0x08, //  ▓▓▓▓░▓▓▓
        0x00, //  ▓▓▓▓▓▓▓▓
    };
    // Magic: ")}W@W})!"[i%8]-33

(Quite hard to compact the ‘magic’ expressions here…)

1 Like