snake detect collision with self

hello all, i have been bored this week and made a few old school games "Asteroids and Space invaders" and today i decided to make snake, yes i know many have made this but i like to learn by myself as much as possible. I designed the game so it can grow the snake however much "unsigned int max" and up to 128 turns using a byte array. The array pushes forward one each time the snake moves thus the movements of the snake go back 1px each time. Any how the snake part was easy and i am trying to make it as efficient as possible and to that extent i am struggling to work out how to generate a position to load the coins and bonus treat in relation to the space NOT occupied by the snake, i don't even want to try a random seed and compare it to the snake occupied space, then re roll if a collision occurs as that could loop forever and i can't think what else i can try while maintaining a random position to load the coins? :confused:

Oh i amusing the u8g2lib so i have to implement an extra functions for increment outside the picture loop, in case you wonder about the duplication.

What would be the best way to load a coin - treat in a space not occupied by the snake while keeping it random?

Here is my code so far for snake

#include <Arduino.h>
#include <U8g2lib.h> //#include "U8glib.h"
#include <EEPROM.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

// ====================== Pins used ============================
const byte top_left_Button_Pin      = 4;
const byte bottom_left_Button_Pin   = 16;
const byte center_Button_Pin        = 18;
const byte top_right_Button_Pin     = 15;
const byte bottom_right_Button_Pin  = 2;
// ====================== SET SCREEN ADDRESS ===================
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// ======================= SYSTEM VARIABLES ====================
byte user_x = 64, user_y = 32;
byte coin_x = 0, coin_y = 0;
byte bonusTreat_x = 0, bonusTreat_y = 0;
static unsigned int treatTimer = 1000;
int snakeLength = 10;
byte directions[128] {2}; // start with moving right
byte directionPosition[128] {0}; // the position the direction relates too within the snake
unsigned int score = 0;
byte lifes = 3;
unsigned int highScore = 0;
bool pauseGame = false;
// TO DO's
// load coins and treats in a zone not occupied by the snake
// coins and bonus treats
// collision with snake body
// score, lifes - pause game

void setup() {
  Serial.begin(115200);
  u8g2.begin();
  u8g2.setColorIndex(1);
  u8g2.setFontPosTop();
  u8g2.setFont(u8g_font_profont12);
  randomSeed(analogRead(36));
  loadHighScore();
}

void loop() {
  buttons();
  unsigned long currentMillis = millis();
  static unsigned long previousMillis = 0;
  const int speed = 100;
  if (currentMillis - previousMillis >= speed) {
    u8g2.firstPage();
    do {
      loadBlock();
      loadCoin();
    }
    while (u8g2.nextPage());
    movement();
    previousMillis = currentMillis;
  }
  loadCoin();
  collisions();
}

void loadHighScore() {
  const byte EEPROM_Key = 101;
  if (!EEPROM.begin(64)) { // try to allocate 64 bytes
    Serial.println(F("Failed to initialise EEPROM"));
  }
  if (EEPROM.read(0) != EEPROM_Key) {
    EEPROM.put(0, EEPROM_Key);
    EEPROM.commit();
    Serial.println(F("First save ?"));
  }
  else {
    EEPROM.get(1, highScore);
    Serial.print(F("Loaded highScore ")); Serial.println(highScore);
  }
}

void buttons() { // 1 up, 2 right, 3 down , 4 left
  if (digitalRead(bottom_left_Button_Pin) == HIGH && directions[0] != 2) {
    pushArray(4);
  }
  else if (digitalRead(bottom_right_Button_Pin) == HIGH && directions[0] != 4) {
    pushArray(2);
  }
  else if (digitalRead(top_left_Button_Pin) == HIGH && directions[0] != 3) {
    pushArray(1);
  }
  else if (digitalRead(top_right_Button_Pin) == HIGH && directions[0] != 1) {
    pushArray(3);
  }
  else if (digitalRead(center_Button_Pin) == HIGH) {
    unsigned long currentMillis = millis();
    static unsigned long previous_Center_Button_Millis = 0;
    if (currentMillis - previous_Center_Button_Millis >= 500UL) {
      pauseGame = !pauseGame;
      previous_Center_Button_Millis = currentMillis;
    }
  }
}

void pushArray(byte num) {
  if (directions[0] != num) {
    for (byte i = 127; i > 0; i--) {
      directions[i] = directions[i - 1];
      directionPosition[i] = directionPosition[i - 1];
    }
    directions[0] = num;
    directionPosition[0] = 1;
  }
}

byte temp_x = user_x, temp_y = user_y;

void drawSnake(byte pos) {
  if (directions[pos] == 1) {   // 1 up
    if (temp_y < 64) {
      temp_y += 2;
    }
    else {
      temp_y = 0;
    }
  }
  else if (directions[pos] == 2) {  // 2 right
    if (temp_x > 0) {
      temp_x -= 2;
    }
    else {
      temp_x = 128;
    }
  }
  else if (directions[pos] == 3) { // 3 down
    if (temp_y > 0) {
      temp_y -= 2;
    }
    else {
      temp_y = 64;
    }
  }
  else if (directions[pos] == 4) { // 4 left
    if (temp_x < 128) {
      temp_x += 2;
    }
    else {
      temp_x = 0;
    }
  }
  u8g2.drawBox(temp_x, temp_y, 2, 2); // draw snake
}

void loadBlock() {
  // how many increments before next dir block
  temp_x = user_x, temp_y = user_y;
  byte pix = 0;
  for (int i = 1; i < 128; i++) {
    if (directionPosition[i] > 0 && directionPosition[i] < snakeLength) { // load the next direction position
      for (int n = 0; n < directionPosition[i]; n++) {
        if (pix < snakeLength) {
          drawSnake(i - 1); // draw snake in current direction until next direction position
          pix++;
        }
        else {
          break;
        }
      }
    }
    else if (directionPosition[i] == 0 || directionPosition[i] >= snakeLength) {
      for (int n = pix; n < snakeLength; n++) { // if no next direction block, continue with current
        drawSnake(i - 1);
      }
      break;
    }
  }
}

void loadCoin() {
  if (coin_x == 0) {
    coin_x = random(2, 126);
    coin_y = random(2, 62);
  }
  u8g2.drawDisc(coin_x, coin_y, 3);
}

void collisions() {
  // TO DO
  // coins and bonus treats
  // collision with snake body
}

void movement() {
  if (directions[0] == 1) {   // 1 up
    if (user_y > 0) {
      user_y -= 2;
    }
    else {
      user_y = 64;
    }
  }
  else if (directions[0] == 2) {  // 2 right
    if (user_x < 128) {
      user_x += 2;
    }
    else {
      user_x = 0;
    }
  }
  else if (directions[0] == 3) { // 3 down
    if (user_y < 64) {
      user_y += 2;
    }
    else {
      user_y = 0;
    }
  }
  else if (directions[0] == 4) { // 4 left
    if (user_x > 0) {
      user_x -= 2;
    }
    else {
      user_x = 128;
    }
  }
  // keep track of direction in relation to snake position
  for (byte i = 1; i < 128; i++) {
    if (directionPosition[i] > 0 && directionPosition[i] < snakeLength) {
      directionPosition[i]++;
    }
  }
}

Asteroids
Space invaders

For example Pick a random start position, if occupied go through the 2D pixels, moving right all the way to the border and down to next row and left, etc (possibly rolling over the top) until you find an empty cell. In worst case will take N2 attempts(for a NxN grid) if there is only one cell left and you started randomly in the adjacent right cell

Of course stop when you come back to same cell, would mean nothing left available

One approach is to keep an array of empty spaces and then just randomly pick one of those locations. It does mean you will have to adjust this array after every move to keep it in sync with the snake