New "Game" - Terrarium: Flora

This is a great approach specially if you have the memory to spare. My position was always that memory (even the 300 bytes or so that Blinks have free) is easy to manage. A lot more than storage.

That being said, I did not look but you should double check what it compiles to. You might be surprised.

I have 7 calls (so far) to determine the opposite face. I don’t know what the correct way to compare usage is, so I’ll just replace one with the other and paste what the compiler spits out. :slight_smile:

Approach #1 (array access, ternary not defined):

Sketch uses 4956 bytes (84%) of program storage space. Maximum is 5888 bytes.
Global variables use 733 bytes (71%) of dynamic memory, leaving 291 bytes for local variables. Maximum is 1024 bytes.

Approach #2 (ternary, array not defined):

Sketch uses 4972 bytes (84%) of program storage space. Maximum is 5888 bytes.
Global variables use 733 bytes (71%) of dynamic memory, leaving 291 bytes for local variables. Maximum is 1024 bytes.

I’m not sure this proves anything without more testing / data, which tracks to your section heading above “Sometimes better, sometimes worse” :man_shrugging: :smiley:

I tried that data-centric method for the CW and CCW algorithms a while back and it wasn’t any better. This confused me at the time. Maybe I need to revisit.

The biggest advantage of a platform like Blinks concerning development is that the code we create tends to be relatively straight-forward so looking at the instructions the compiler spits out is “easy” (for some definition of easy). That is a powerful tool.

I finally figured out how to view the assembly output from a compiled sketch. I don’t see a topic about this so I’ll write one up soon.

Unfortunately, because the sketch is also compiled with -Os (to optimize for space) the resulting assembly code isn’t directly linear relative to the C source and thus pretty hard to follow.

However, I did experiment with @Freddicus’s suggestion to use the data-oriented approach for the CW, CCW, and OPPOSITE macros. So far they seem better! Saved about 80 bytes of code space, which is awesome. I haven’t actually run it to see if I messed anything up, but here’s what I have anyway:

// WARNING UNTESTED!
byte faceOffsetArray[] = { 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5 };
#define CW_FROM_FACE(f, amt) faceOffsetArray[(f) + (amt)]
#define CCW_FROM_FACE(f, amt) faceOffsetArray[6 + (f) - (amt)]
#define OPPOSITE_FACE(f) CW_FROM_FACE((f), 3)

This works as long as f is 0-5 and amt is 0-6, which should be fine for most uses.

(Gotta make sure I can shave 12 bytes somewhere to get this to fit :grimacing:)

2 Likes

Something else I just learned. If you use bit fields to compact structures, try to ensure no fields cross a byte boundary. That will generate more complicated code as the compiler unpacks it.

I added a dummy bit to my plant node data structure so that exitFace would be byte aligned and it shaved a dozen-ish bytes off my code usage. The savings is more dramatic the more often the field is used.

struct PlantStateNode
{
  // Energy and state progression
  byte growState1         : 2;  // state offset when growing (1)
  byte growState2         : 2;  // state offset when growing (2) [same, but also added to growState1]
  
  // Flags
  byte isWitherState      : 1;  // state plant will jump back to when withering
  byte waitForGrowth      : 1;  // flag to only proceed once a child branch has grown
  byte advanceStage       : 1;  // plant advances to next stage in child
  byte UNUSED             : 1;  // BUFFER TO BYTE ALIGN THE NEXT FIELD

  // Growth into neighbors
  PlantExitFace exitFace  : 2;  // none, one across, fork

  // Render info
  byte faceRenderIndex    : 5;
};
1 Like

Just checked in an updated version with a fourth plant type. I moved it to a branch so I can continue mainline dev work on master without affecting it.

This is the branch:

You’ll need to install @BGA’s custom blinklib to compile it. Follow the instructions in his thread. Be sure to use the latest and not the one given in the initial post.

My planned follow-on/sequel was going to be “Terrarium: Fauna” and remove most of the plants to give me code space to add new types of critters.

After switching to @BGA’s custom blinklib I now have enough code space to try to fit both Flora and Fauna in one sketch. Let’s see how this goes…

2 Likes

Glad I was able to help. I noticed that you are not using datagrams. I have a change that allows you to fully disable datagrams and it gives you another 100 bytes or so. I also was able to shave another 50 bytes or so in my current development version (although I am pretty sure I am very close to the limit of what I can get without removing features. Maybe I’ve got to this limit already). I will try to push an update later today.

Othar than that, I was also able to shave another 150 bytes or so in Hexxagon itself so you might want to check my commits there to see what I did. It might also help your game.

2 Likes

just shaved off 100 bytes. I was using % a lot…from within the setColorOnFace method, so those lines were crazy expensive.

2 Likes

I posted about an odd behavior with an optimization, but deleted it because it turned out to be wrong.

Basically I was trying to decrement a value pointed to by a (byte*) like this:

*value--;

But that actually decrements the pointer itself, not the value it is pointing to.

(*value)++ should do the trick. You hit and operator precedence issue.

Just had weird behavior while optimizing. I have a variable that gets set to a magic number of 7 within a function. This value is a #define so the compiler knows exactly what it is at compile time.

Changing that constant to another value, like 6 or 8, reduced the compiled size by 30 bytes :face_with_raised_eyebrow:

Every other value I tried from 0-255 is fine. It’s only 7 that adds 30.

Also, I have other functions that set other variables to that same #define. They have a similar bloat, but on a smaller scale. Making the value 7 only adds 2-4 bytes everywhere else. It’s just that one function that adds 30.

Oh, I see this kind of stuff all the time now. There are several comments on my code explaining why some things are the way they are when, logically, they did not have to be. :slight_smile: That being said, checking the generated code might shed some light.

1 Like

Glad I’m not alone :rofl: :joy:

BTW, you can now easily disable datagram support for your project as you are not using it:

1 - Create a “config” folder in the same dirtectory your sketch is.
2 - Create a “blinklib_config.h” inside this directory with the following contents:

#define BGA_CUSTOM_BLINKLIB_DISABLE_DATAGRAM

3 - That is it.

When you compile your program next time, you should get over 150 bytes of storage back and 200 bytes of RAM!

Let me know if it works for you.

1 Like

Doing some testing and thought I had everything squeezed within the code & data limits. Then I noticed that waking up tiles from deep sleep would put them into the dreaded 4-blink of death - meaning a stack overrun.

So I guess the code path to wake from deep sleep uses more local variables than any of my sketch’s code paths.

Just something to watch out for, I guess. Be sure to test sleep+wake for your games - both warm and deep.

I did some heavy data optimization (the code gets uglier the more I do) and believe I have freed up enough data space to fix this.

If I correctly read the code, at any point there are at most 8 bytes being used by the warm_sleep cycle thing. SO I guess you might be pretty tight with memory. Is this after disabling datagrams in the custom blinklib?

Is that 8 bytes on top of the user code? I thought I had more wiggle room than that.

Is this after disabling datagrams in the custom blinklib?

Oh yeah, I disabled datagrams a while back and burned through all the extra code and data space that freed up. Always working on the razor’s edge :wink:

1 Like

Well, 8 bytes on top of all global/static data in your code, but yes. It obviously does not count local variables in your code as whenever it enters the warm sleep loop, none of your code is being executed.

I am getting really curious with what you come up with in the end. :slight_smile: It better be good! :wink:

It better be good! :wink:

:open_mouth: :grinning: :sunglasses: