Lichterkette für 'in den Baum'

Hi

Habe die letzten Tage damit verbracht, meine letzte chinesische Lieferung, einem Grafik-LCD, Leben einzuhauchen.
Dazu nur so viel: Es wehrt Sich ... ich brauchte 'einen Erfolg'!

Da ich auch eine Rolle WS2812 bekommen hatte (5 Meter a 30 LED/m), wurde Die an einen Nano getackert und die Bude die Nacht über illuminiert.

Heute kramte ich den Sketch für das Auf-/Abdimmen benachbarter Pixel hervor (als Beispiel für eine Uhr -> Sekundenzeiger entstanden).
Diesen umgeschrieben/angepasst, daß jetzt ein 'Lichtpunkt' den Stripe entlang 'gleitet'.
Ab und zu glitzern vereinzelt Sterne (Blitze), Die den Lichtpunkt natürlich wieder zurück 'in Wagenfarbe' verlassen, sollte der Blitz in aktiv leuchtendem Bereich aufgeleuchtet sein.

Ganz nebenbei fiel mir die Funktion setBrightness(xx); der Adafruit_NeoPixel Lib in die Finger (v1.1.3 ... sehe gerade, 1.1.5 ist aktuell).
Mit Dieser kann die 'Grundhelligkeit' eingestellt werden - nachfolgende Farben werden auf diese Helligkeit 'herunter gedimmt' - also ein 255,255,255 ist dann keinesfalls 'volle Kanne weiß', sondern weiß in 'brightness'-Wert.

Das aber nur nebenbei bemerkt - vll. hat's ja wer noch nicht entdeckt und kann Damit 'was anfangen'.

Edit 04.02. @19:11 Hier Beitrag #8 gibt's den halbwegs laufenden Sketch

Dann der Sketch:

/*
    Weihnachtsbaum-Lichterkette

    An der Kette sollen grüne Leuchtpunkte (mehr oder minder) gemächlich rauf/runter gleiten
    Zufällig werden Blitze (funkelnde Sterne ;) ) erzeugt, Diese werden nicht durch die Animation gestört
    Akut ist nur ein Leuchtpunkt unterwegs - geplant sind MEEEHR, Die mit unterschiedlicher Geschwindigkeit
    den Stripe rauf oder runter gleiten. Wenn diese Leuchtpunkte sich überschneiden, bestimmt die hellere
    LED, wie hell der Punkt leuchten soll. Vll. die Leuchtpunkte nur mit 30% Helligkeit laufen lassen und
    die Helligkeit addieren - wird auf einen Test hinaus laufen, wie Das aussieht.
*/

#include <Adafruit_NeoPixel.h>    //v1.1.3, aus der Bibkliotheks-Verwaltung

//Anzahl der verbauten WS2812, bei der 4x4-Platine dann hier 16
const uint16_t PixelCount = 150; // Anzahl der Pixel

const uint8_t PixelPin = 13;  // angeschlossen an Pin

//Die Maximal-Farbe für 100% - r Rot, g Grün, b Blau - also hier den RGB-Wert der maximalen Helligkeit eintragen
const byte rmax = 18;    //Wert 0...255 bei maximaler Helligkeit
const byte gmax = 127;
const byte bmax = 11;
//ein Grün - für 'in den Baum'

//Die Wartezeit zwischen zwei 'benachbarten' LED - für den Sekundenzeiger hier 1000 für 1000ms verwenden, unter 300 ist von dem Aus/Abdimmen nicht mehr viel zu sehen.
const int wartezeit = 30;   //Zeit zwischen den Einzelpixeln
const int animationszeit = 30; //Wartezeit für die Anfangs-Animation
const int blitzwartezeit = 10;  //wie lange soll der Blitz AN sein?

//Hilfsvariablen, um die vergangene Zeit mitzuhalten
long lasttime;      //Zeit, als der letzte Mittel-Pixel gewechselt wurde
long aktutime;      //aktuelle Zeit, wird im Script laufend neu auf millis() gesetzt

//mit welcher LED soll begonnen werden? Hier müssen händisch die LEDs davor/dahinter eingetragen werden
//wenn der Abstand größer als 1 ist, leuchten halt mehrere LEDs im Zwischenraum in voller Helligkeit
//die LEDs davor und danach werden je nach 'Abstand' zur Zeit hoch/runter gedimmt
//es ist immer eine LED voll an
byte LED_davor = 0, LED_danach = 1;       //mindestens 1 Abstand!!

//Den Stripe initialisieren
Adafruit_NeoPixel rgbstripe = Adafruit_NeoPixel(PixelCount, PixelPin, NEO_GRB + NEO_KHZ800);

void setup() {
  rgbstripe.begin();          //Stripe initiieren, RAM vorbereiten
  rgbstripe.setBrightness(255);  //Helligkeit auf bestimmten Wert

  //Der Sketch soll geschwätzig sein
  Serial.begin(9600);

  //Das Auge will auch was davon haben - eine 'Start-Animation'
  aktutime = millis();
  for (int t = 0; t < PixelCount; t++) {
    rgbstripe.setPixelColor(t, rmax, gmax, bmax);
    rgbstripe.show();
    while (millis() - aktutime < animationszeit);     //ohne delay ;)
    aktutime += animationszeit;
  }
  for (int t = 0; t < PixelCount; t++) {
    rgbstripe.setPixelColor(t, 0, 0, 0);
    rgbstripe.show();
    while (millis() - aktutime < animationszeit);     //außerdem bleibt so das 'Raster' auf festen ms-Werten
    aktutime += animationszeit;
  }

  //damit der Start nett aussieht, werden alle LEDs von 'davor' bis 'dahinter' auf 100% gesetzt
  //im Sketch werden die 'äußeren' LEDs dann hoch/runter gedimmt
  byte x = LED_davor;
  while (x != LED_danach) {
    rgbstripe.setPixelColor(x, rmax, gmax, bmax);
    x = (x + 1) % PixelCount;
  }
  //  die eingestellten Farben im Stripe anzeigen/die Daten in die WS2812 schicken
  rgbstripe.show();
  lasttime = millis();    //unsere Berechnungsgrundlage - die 'allererste' Zeit
}

void loop() {
  static long unsigned blitzstart = 0;
  static boolean blitz = 0;
  static int blitzpixel;
  if (millis() - blitzstart > blitzwartezeit) {
    if (blitz == 1) {
      //war der Blitz in einem voll leuchtendem Segment? Dann auf Leuchtfarbe, sonst löschen
      //(die dimmenden Segmente werden eh neu gesetzt, daher egal ... sonst <= und >=)
      Serial.print("von ");
      Serial.print(LED_davor);
      Serial.print(" bis ");
      Serial.print(LED_danach);
      Serial.print(" Blitz:");
      Serial.print(blitzpixel);
      if ((LED_davor < blitzpixel && LED_danach > blitzpixel) || (LED_davor>LED_danach&&(LED_davor > blitzpixel || LED_danach < blitzpixel) ) ) {
        Serial.println(" INNEN -> auf Farbe");
        rgbstripe.setPixelColor(blitzpixel, rmax, gmax, bmax);
      } else {
        Serial.println(" -> auf Schwarz");
        rgbstripe.setPixelColor(blitzpixel, 0, 0, 0);
      }
      blitz = 0;
    }
    blitzpixel = random(0, 0xFFFF);      //Variable wird für Zufallszahl missbraucht - ist hier in diesem Moment eh unbenutzt
    if (blitzpixel > 0xFF00) {
      //Zufallspixel aufblitzen lassen
      blitzstart = millis();              //die Zeit merken, wann der Blitz begonnen hat
      blitzpixel = random(0, PixelCount); //den Pixel bestimmen
      //blitzpixel = 3;                     //zu Testzwecken, damit man weiß, wo's gleich blitzt
      rgbstripe.setPixelColor(blitzpixel, 255, 255, 255); //auf Weiß und 100% setzen
      blitz = 1;
    }
  }

  //die aktuelle Zeit einlesen
  aktutime = millis();
  //die Differenz zwischen dem letzten Umsetzen der Mittel-Pixel
  int milisek = aktutime - lasttime; //ergibt irgenbdwas bis knapp über 1000, alsp die ms, seit dem letzten Pixel-Wechsel
  //wenn die bereits vergangene Wartezeit größer oder zumindest gleich der Wartezeit zwischen den Pixeln ist, werden die Nachbar-Pixel an, bzw. aus gestellt.
  if (milisek >= wartezeit) {
    //die 'Umsetz-Zeit' wird um die Wartezeit erhöht - so wird die Wartezeit immer eingehalten, auch, wenn der Script Mal irgendwo etwas langsamer war
    lasttime += wartezeit;
    //die Pixel davor/danach voll an/aus schalten
    if (blitzpixel != LED_danach) rgbstripe.setPixelColor(LED_danach, rmax, gmax, bmax);
    if (blitzpixel != LED_davor) rgbstripe.setPixelColor(LED_davor, 0, 0, 0);
    LED_davor = (LED_davor + 1) % PixelCount;
    LED_danach = (LED_danach + 1) % PixelCount;
  } else {
    //ansonsten, werden die Farben je nach 'Abstand' rauf/runter gedimmt, 100% entspricht der anfangs definierten Farbe
    if (blitzpixel != LED_danach) rgbstripe.setPixelColor(LED_danach, map(milisek, 0, wartezeit, 0, rmax), map(milisek, 0, wartezeit, 0, gmax), map(milisek, 0, wartezeit, 0, bmax));
    if (blitzpixel != LED_davor) rgbstripe.setPixelColor(LED_davor, rmax - map(milisek, 0, wartezeit, 0, rmax), gmax - map(milisek, 0, wartezeit, 0, gmax), bmax - map(milisek, 0, wartezeit, 0, bmax));
  }
  //Und wieder raus mit den Daten an den Stripe
  rgbstripe.show();
}

Vll. bin ich nicht ganz 'auf der Zeit' - aber das nächste Weihnachten kommt bestimmt - und dann bin ich gerüstet.

MfG

postmaster-ino:
... ich brauchte 'einen Erfolg'!

Wenn ich Entspannung brauche, lade ich mir TwinkleFOX auf die LEDs. Auch gut als Vorbild für einfache Programmierung effektvoller Illumination.

Wenn ich Dein Programm auf APA102 umgeschrieben habe, schaue ich es mir auch mal an.

Hi

Habe noch einen Fehler drin.
Die Ausgabe:
von 149 bis 0 Blitz:64 INNEN -> auf Farbe
kann so nicht passen - der Stripe geht bis 149, da waren gerade die letzte und die erste LED an.
Also NUR.
Trotzdem schlug meine IF-Abfrage oben zu und setzte den Pixel zurück in Grün.

... hätte ja auch klappen können ...

Den Link klicke ich mir gleich noch an - vll. wird die Bude ja heute doch anders beleuchtet :wink:

MfG

Habe nun getestet und bekomme Warnungen (die Zeilen beziehen sich logischerweise auf meine Variante):

In function 'void setup()':
64:23: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
70:23: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
In function 'void loop()':
113:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]

Hier nun meine Variante für DotStar, bei der die seriellen Ausgaben nur bei Bedarf eingeschaltet werden können:

/*
    Weihnachtsbaum-Lichterkette

    An der Kette sollen grüne Leuchtpunkte (mehr oder minder) gemächlich rauf/runter gleiten
    Zufällig werden Blitze (funkelnde Sterne ;) ) erzeugt, Diese werden nicht durch die Animation gestört
    Akut ist nur ein Leuchtpunkt unterwegs - geplant sind MEEEHR, Die mit unterschiedlicher Geschwindigkeit
    den Stripe rauf oder runter gleiten. Wenn diese Leuchtpunkte sich überschneiden, bestimmt die hellere
    LED, wie hell der Punkt leuchten soll. Vll. die Leuchtpunkte nur mit 30% Helligkeit laufen lassen und
    die Helligkeit addieren - wird auf einen Test hinaus laufen, wie Das aussieht.
*/
// #define DEBUG Serial // Debugging, wenn aktiv

#ifdef DEBUG
#define debug(...) DEBUG.print(__VA_ARGS__)
#define debugln(...) DEBUG.println(__VA_ARGS__)
#define debugbegin(...) DEBUG.begin(__VA_ARGS__)
#else
#define debug(...)
#define debugln(...)
#define debugbegin(...)
#endif

#include <Adafruit_DotStar.h>
#include <SPI.h>         // COMMENT OUT THIS LINE FOR GEMMA OR TRINKET
#define NUMPIXELS  122    // Number of LEDs in strip
#define BRIGHTNESS 96
Adafruit_DotStar rgbstripe = Adafruit_DotStar(NUMPIXELS, DOTSTAR_BGR);

//Anzahl der verbauten WS2812, bei der 4x4-Platine dann hier 16
const uint16_t PixelCount = NUMPIXELS; // Anzahl der Pixel

const uint8_t PixelPin = 4;  // angeschlossen an Pin

//Die Maximal-Farbe für 100% - r Rot, g Grün, b Blau - also hier den RGB-Wert der maximalen Helligkeit eintragen
const byte rmax = 18;    //Wert 0...255 bei maximaler Helligkeit
const byte gmax = 127;
const byte bmax = 11;
//ein Grün - für 'in den Baum'

//Die Wartezeit zwischen zwei 'benachbarten' LED - für den Sekundenzeiger hier 1000 für 1000ms verwenden, unter 300 ist von dem Aus/Abdimmen nicht mehr viel zu sehen.
const int wartezeit = 30;   //Zeit zwischen den Einzelpixeln
const int animationszeit = 30; //Wartezeit für die Anfangs-Animation
const int blitzwartezeit = 10;  //wie lange soll der Blitz AN sein?

//Hilfsvariablen, um die vergangene Zeit mitzuhalten
long lasttime;      //Zeit, als der letzte Mittel-Pixel gewechselt wurde
long aktutime;      //aktuelle Zeit, wird im Script laufend neu auf millis() gesetzt

//mit welcher LED soll begonnen werden? Hier müssen händisch die LEDs davor/dahinter eingetragen werden
//wenn der Abstand größer als 1 ist, leuchten halt mehrere LEDs im Zwischenraum in voller Helligkeit
//die LEDs davor und danach werden je nach 'Abstand' zur Zeit hoch/runter gedimmt
//es ist immer eine LED voll an
byte LED_davor = 0, LED_danach = 1;       //mindestens 1 Abstand!!

void setup() {
  rgbstripe.begin();          //Stripe initiieren, RAM vorbereiten
  rgbstripe.setBrightness(BRIGHTNESS);  //Helligkeit auf bestimmten Wert

  //Der Sketch soll geschwätzig sein
  debugbegin(9600);

  //Das Auge will auch was davon haben - eine 'Start-Animation'
  aktutime = millis();
  for (int t = 0; t < PixelCount; t++) {
    rgbstripe.setPixelColor(t, rmax, gmax, bmax);
    rgbstripe.show();
    while (millis() - aktutime < animationszeit);     //ohne delay ;)
    aktutime += animationszeit;
  }
  for (int t = 0; t < PixelCount; t++) {
    rgbstripe.setPixelColor(t, 0, 0, 0);
    rgbstripe.show();
    while (millis() - aktutime < animationszeit);     //außerdem bleibt so das 'Raster' auf festen ms-Werten
    aktutime += animationszeit;
  }

  //damit der Start nett aussieht, werden alle LEDs von 'davor' bis 'dahinter' auf 100% gesetzt
  //im Sketch werden die 'äußeren' LEDs dann hoch/runter gedimmt
  byte x = LED_davor;
  while (x != LED_danach) {
    rgbstripe.setPixelColor(x, rmax, gmax, bmax);
    x = (x + 1) % PixelCount;
  }
  //  die eingestellten Farben im Stripe anzeigen/die Daten in die WS2812 schicken
  rgbstripe.show();
  lasttime = millis();    //unsere Berechnungsgrundlage - die 'allererste' Zeit
}

void loop() {
  static long unsigned blitzstart = 0;
  static boolean blitz = 0;
  static int blitzpixel;
  if (millis() - blitzstart > blitzwartezeit) {
    if (blitz == 1) {
      //war der Blitz in einem voll leuchtendem Segment? Dann auf Leuchtfarbe, sonst löschen
      //(die dimmenden Segmente werden eh neu gesetzt, daher egal ... sonst <= und >=)
      debug("von ");
      debug(LED_davor);
      debug(" bis ");
      debug(LED_danach);
      debug(" Blitz:");
      debug(blitzpixel);
      if ((LED_davor < blitzpixel && LED_danach > blitzpixel) || (LED_davor > LED_danach && (LED_davor > blitzpixel || LED_danach < blitzpixel) ) ) {
        debugln(" INNEN -> auf Farbe");
        rgbstripe.setPixelColor(blitzpixel, rmax, gmax, bmax);
      } else {
        debugln(" -> auf Schwarz");
        rgbstripe.setPixelColor(blitzpixel, 0, 0, 0);
      }
      blitz = 0;
    }
    blitzpixel = random(0, 0xFFFF);      //Variable wird für Zufallszahl missbraucht - ist hier in diesem Moment eh unbenutzt
    if (blitzpixel > 0xFF00) {
      //Zufallspixel aufblitzen lassen
      blitzstart = millis();              //die Zeit merken, wann der Blitz begonnen hat
      blitzpixel = random(0, PixelCount); //den Pixel bestimmen
      //blitzpixel = 3;                     //zu Testzwecken, damit man weiß, wo's gleich blitzt
      rgbstripe.setPixelColor(blitzpixel, 255, 255, 255); //auf Weiß und 100% setzen
      blitz = 1;
    }
  }

  //die aktuelle Zeit einlesen
  aktutime = millis();
  //die Differenz zwischen dem letzten Umsetzen der Mittel-Pixel
  int milisek = aktutime - lasttime; //ergibt irgenbdwas bis knapp über 1000, alsp die ms, seit dem letzten Pixel-Wechsel
  //wenn die bereits vergangene Wartezeit größer oder zumindest gleich der Wartezeit zwischen den Pixeln ist, werden die Nachbar-Pixel an, bzw. aus gestellt.
  if (milisek >= wartezeit) {
    //die 'Umsetz-Zeit' wird um die Wartezeit erhöht - so wird die Wartezeit immer eingehalten, auch, wenn der Script Mal irgendwo etwas langsamer war
    lasttime += wartezeit;
    //die Pixel davor/danach voll an/aus schalten
    if (blitzpixel != LED_danach) rgbstripe.setPixelColor(LED_danach, rmax, gmax, bmax);
    if (blitzpixel != LED_davor) rgbstripe.setPixelColor(LED_davor, 0, 0, 0);
    LED_davor = (LED_davor + 1) % PixelCount;
    LED_danach = (LED_danach + 1) % PixelCount;
  } else {
    //ansonsten, werden die Farben je nach 'Abstand' rauf/runter gedimmt, 100% entspricht der anfangs definierten Farbe
    if (blitzpixel != LED_danach) rgbstripe.setPixelColor(LED_danach, map(milisek, 0, wartezeit, 0, rmax), map(milisek, 0, wartezeit, 0, gmax), map(milisek, 0, wartezeit, 0, bmax));
    if (blitzpixel != LED_davor) rgbstripe.setPixelColor(LED_davor, rmax - map(milisek, 0, wartezeit, 0, rmax), gmax - map(milisek, 0, wartezeit, 0, gmax), bmax - map(milisek, 0, wartezeit, 0, bmax));
  }
  //Und wieder raus mit den Daten an den Stripe
  rgbstripe.show();
}

Ich bin dann mal gespannt auf die nächste Version von Dir :slight_smile:

bekomme Warnungen

Mach sie doch weg!

const int wartezeit = 30;  //Zeit zwischen den Einzelpixeln (etc.)

int milisek = aktutime - lasttime;

int ist oft ein Zeichen dafür, dass der Programmierer sich nicht genügend Gedanken gemacht hat

Gibt es auch negative Wartezeiten?
Macht auch ein Wert > 255 evtl. Sinn ?

Hi

int habe ich hier bewusst gewählt, da mir 65 Sekunden 'Zwischenzeit' als dicke genug erscheinen.
Wenn die Pixel noch langsamer laufen sollen, als alle 65 Sekunden 'einen Pixel weiter' zu kommen, ist int natürlich zu klein.
Aber ja, Es müsste ein unsigned int sein - ist in der 'nächsten Version' aber eh Alles zig Mal umgeworfen worden :confused:
(ganz viel früher war int 8 Bit lang und reichte von 0 bis 255 ... lange ist's her)
Beim Arduino ist int 16bit breit, somit Wartezeiten im ms-Bereich möglich.
Die Warnungen signed<->unsigned ignoriere ich gerade recht erfolgreich :frowning: (ja, Ihr habt ja recht!)

Momentan will Es mir nicht gelingen, die Leuchtpunkte (ok, erst Mal Einen) in beliebiger Länge in beiden Richtungen korrekt anzuzeigen.
In der Art:

  • Start bei Pixel 10
  • Richtung 'aufwärts'
  • Stop nach 33 Pixeln
  • Länge des Leuchtpunkt 5
  • Verweilzeit pro Pixel 1000ms

... dabei entstand Das als Entspannung zum Graphik-Display :slight_smile:

Noch bin ich aber dran - habe für die Werte des Leuchtpunk mittlerweile ein struct eingesetzt, scheint so zu funktionieren, wie ich's verstanden habe :wink:

MfG

michael_x:
Mach sie doch weg!

Kein Problem, ich hatte es aber als Tipp für den TO gemeint.

@postmaster-ino: Wird schon werden :slight_smile:

Hi

Noch wird's Nichts :confused:
Mein Ansatz: Ich merke mir, wo ein Wurm (heißen die Leuchtpunkte mittlerweile) ist, dazu erzeuge ich ein Array mit der Größe der Anzahl der Pixel.
Wenn ein Wurm generiert wird, wird der Kopfpixel (Start-Ort) um 1 erhöht.
Wenn die Verweilzeit vergangen ist und ich den Kopf um 1 versetze (den zuvor hochgedimmten Pixel auf Voll setze), wird auch der Wert der neue Kopf-Position per ++ um 1 erhöht.
Vermindert wird das Ganze nach dem Herunterdimmen, wenn der Pixel endgültig gelöscht wird - diese Verminderung wird auch durchgeführt, wenn ein weiterer Wurm hier her gekommen ist, also mehrere Würmer aufeinander liegen.
Gelöscht wird der Pixel nur, wenn Das der letzte Wurm auf diesem Pixel war.
Gleicher gilt für das Auf- und Abdimmen - hier werden die Dimmwerte nur ausgegeben, wenn 'wir der einzige Wurm hier' sind.

... klappt aber nur leidlich ...

Selbst mit nur einem Wurm habe ich teilweise das Problem, daß das Merk-Array falsche Werte aufweist.

Dazu lasse ich mir die Pixel in verschiedenen Farben angeben:

  • Geld Startpixel (Portal ? nur Spielerei)
  • Blau End-Pixel (Portal ? nur Spielerei)
  • Wurm-Grün Wurmkörper, voll auf gedummt
  • Grün ein Dimmpixel, Der akut nicht gedimmt werden darf, da 'wir nicht alleine sind'
  • Weiß der Blitz/Funkelstern

Der Blitz kam heute erst wieder dazu (aka Erfolgserlebnis und so) und funktioniert, wie's aussieht, tadellos.

Für die Nachbauer habe ich die Stripe-Aufrufe in eigene Funktionen gepackt, hoffe, so kommt Ihr leichter ans Ziel (z.B. agmue(

Seltsamerweise wird der Sketch auch ohne Murren kompiliert, wenn ich die printwurm-Funktion nicht oben ankündige, kam erst eben gerade da rein - soll ja schön aussehen.

Bis auf die Warnungen zu den zig debugPrint-Aufrufen kommt jetzt Nichts mehr - gerade noch zwei boolean raus genommen, Die keine Verwendung mehr hatten.

Im Folgepost der Sketch, die Kommentare sollten 'durch die Bank durch' aktuell sein ... hoffe ich :wink:

Wer Da den Pferdefuß sieht, Der mir 'mehrere Würmer' vermiest - immer her damit.

So, gleich kommt Besuch, werde wohl die Nacht mit nur einem Wurm leben müssen ... wenn ich dran denke, was hier Alles Strom frisst, NUR, damit das Gelump schön vor sich hin leuchtet oder Temperaturen anzeigt ... ist Das bei Euch auch so??

Nun aber
MfG

Teil 1

/*
    Weihnachtsbaum-Lichterkette

    An der Kette sollen grüne Leuchtpunkte (mehr oder minder) gemächlich rauf/runter gleiten
    Zufällig werden Blitze (funkelnde Sterne ;) ) erzeugt, Diese werden nicht durch die Animation gestört
    Akut ist nur ein Leuchtpunkt unterwegs - geplant sind MEEEHR, Die mit unterschiedlicher Geschwindigkeit
    den Stripe rauf oder runter gleiten. Wenn diese Leuchtpunkte sich überschneiden, bestimmt die hellere
    LED, wie hell der Punkt leuchten soll. Vll. die Leuchtpunkte nur mit 30$ Helligkeit laufen lassen und
    die Helligkeit addieren - wird auf einen Test hinaus laufen, wie Das aussieht.
*/
//noch kein randomseed() enthalten, da so die Zufälle identisch sind und Fehler reproduzierbar

//mit nur einem Wurm klappt das Dimmen von Kopf & Fussende halbwegs
//beim Runterdimmen wird statt zu Dimmen auf Voll-Grün gesetzt, wenn der Pixel noch fremd gesetzt ist - sollte bei einem Wurm nicht möglich sein - kommt aber
//Start-Positionen werden Gelb, Ziel-Positionen Blau angezeigt

#define DEBUG    //ausklammern, wenn keine serielle Ausgabe zum Terminal mehr gebraucht wird

#ifdef DEBUG
const byte debugablevel = 0x00;    //  unterhalb diesem Level keine Ausgabe, ** hier anpassen **
const boolean debugother = true;   //  Anzeigen der Zeilennummern von nicht angezeigten Debug-Ausgaben - false lässt auch Diese verschwinden, ** hier anpassen **
byte debuglevel = 0;            // wird per debuglevel(xx); angepasst, je höher der Level, desto 'eher' die Ausgabe
#define debugBegin(...) Serial.begin(__VA_ARGS__)
#define debugPrint(...)  {if (debuglevel>=debugablevel){Serial.print(__VA_ARGS__);}else if (debugother){Serial.print("(");Serial.print(debuglevel,HEX);Serial.print("@"); Serial.print(__LINE__);Serial.print(")");} }
#define debugPrintln(...)  {if (debuglevel>=debugablevel){Serial.println(__VA_ARGS__);}else if (debugother){Serial.print("(");Serial.print(debuglevel,HEX);Serial.print("@"); Serial.print(__LINE__);Serial.print(")");} }
#define debuglevel(...) debuglevel=__VA_ARGS__;
//
//Quelle: https://forum.arduino.cc/index.php?topic=46900.0
#define DEBUG_PRINT(str)    \
  Serial.print(millis());     \
  Serial.print(": ");    \
  Serial.print(__PRETTY_FUNCTION__); \
  Serial.print(' ');      \
  Serial.print(__FILE__);     \
  Serial.print(':');      \
  Serial.print(__LINE__);     \
  Serial.print(' ');      \
  Serial.println(str);
#pragma message ("DEBUG angegeben")  //Anzeige/Hinweis beim Compilieren
#else
#define debugBegin(...)
#define debugPrint(...)
#define debugPrintln(...)
#define debuglevel(...)
#define DEBUG_PRINT(str)
#endif
//***************************************************

//eigene Funktionen
void randomwurm(uint8_t t);                                           //erzeugt einen neuen 'Wurm' - 255 Würmer sollten reichen
void printwurm(uint8_t t);                                            //Wurmdaten ausgeben
void setpixelrgb(uint16_t pixelnr, uint8_t r, uint8_t g, uint8_t b);  //zum Anpassen an andere LIBs einziger Pixel-Ändern-Aufruf in dieser Funktion
void showpixelrgb();                                                  //dito Stripe-Daten an Stripe ausgeben
void rgbinit();                                                       //hier wird der Stripe erstellt und der Brithness-Wert gesetzt - alle Drei am Code-Ende

#include <Adafruit_NeoPixel.h>    //v1.1.3, aus der Bibkliotheks-Verwaltung

//Anzahl der verbauten WS2812, bei der 4x4-Platine dann hier 16
const uint16_t PixelCount = 150; // Anzahl der Pixel
uint16_t stripehell[PixelCount]; //  Merker für besetzte Pixel    //auf 16bit erweitert, sehen, ob hoch oder runter gezählt wird
const uint8_t PixelPin = 13;  // angeschlossen an Pin
const uint16_t minverweilzeit = 100;   //Verweilzeit pro Pixel Mindestens
const uint16_t maxverweilzeit = 900;  //Verweilzeit pro Pixel Maximal
const uint16_t maxlang = 60;          //die Max-Länge eines Wurm
//eine Min-Länge macht keinen Sinn, da ein Wurm maximal so lang sein kann, wie die Entfernung zwischen Start und Ziel-Punkt. Mit Min-Wert ergäbe die Random-Berechnung für die Länge längere Würmer, als 'Platz' und somit stehende Pixel

struct wurmdata             //Aufbau der Struktur wurmdata
{
  uint16_t kopfposition;   //Da ist der Kopf
  uint16_t fussposition;   //Da der Fuss des Wurm
  uint8_t schritte;        //Anzahl an noch fehlenden Schritten
  boolean richtung;        //Vor oder Zurück
  uint8_t schrittnr;        //die aktuelle Schrittnummer
  uint16_t verweilzeit;     //Wartezeit bis zum nächsten Pixel
  uint32_t lasttime;        //zuletzt umgesetzt um
  uint8_t laenge;           //die Länge des Wurm (< Abstand Start/Ziel)
};
typedef struct wurmdata Wurm;     //erstellen des Datentyp Wurm, Aufbau nach definierter Struktur !! Hier die Anzahl der Würmer festlegen
Wurm leuchtpunkt[3];              //zu Testzwecken erst nur einen Wurm

const byte wurmanzahl = sizeof(leuchtpunkt) / sizeof(leuchtpunkt[0]); //Berechnung , wie viele Würmer generiert werden
boolean blitzinwurm = false; //wenn der Blitz in einem Wurm auftritt, wird Dieser in 'Wagenfarbe' gelöscht

//Die Maximal-Farbe für 100% - r Rot, g Grün, b Blau - also hier den RGB-Wert der maximalen Helligkeit eintragen
const byte rmax = 18;    //Wert 0...255 bei maximaler Helligkeit
const byte gmax = 127;
const byte bmax = 11;
//ein Grün - für 'in den Baum'
const byte rmin = 0;    //Wert 0...255 bei maximaler Helligkeit
const byte gmin = 0;
const byte bmin = 0;
//Hintergrundfarbe 0.0.0 = AUS

uint32_t aktutime = millis();
uint32_t lasttime;
const int animationszeit = 10;    //Wartezeit bei Start-Animation
const int blitzwartezeit = 10;    //Wartezeit, bis ein Blitz wieder verlischt

//Den Stripe initialisieren
Adafruit_NeoPixel rgbstripe = Adafruit_NeoPixel(PixelCount, PixelPin, NEO_GRB + NEO_KHZ800);

void setup() {
  rgbinit();

  //Der Sketch soll geschwätzig sein
  Serial.begin(9600);

  //Das Auge will auch was davon haben - eine 'Start-Animation'
  aktutime = millis();
  for (uint8_t t = 0; t < PixelCount; t++) {
    setpixelrgb(t, rmax, gmax, bmax); //    rgbstripe.setPixelColor(t, rmax, gmax, bmax);
    showpixelrgb();
    while (millis() - aktutime < animationszeit);     //ohne delay ;)
    aktutime += animationszeit;
  }

  for (uint8_t t = 0; t < PixelCount; t++) {
    setpixelrgb(t, 0, 0, 0); //rgbstripe.setPixelColor(t, 0, 0, 0);
    showpixelrgb();
    while (millis() - aktutime < animationszeit);     //außerdem bleibt so das 'Raster' auf festen ms-Werten
    aktutime += animationszeit;
  }

  lasttime = millis();    //unsere Berechnungsgrundlage - die 'allererste' Zeit
  debugPrint("Wurmanzahl:");
  debugPrintln(wurmanzahl);

  for (uint8_t t = 0; t < wurmanzahl; t++) {
    randomwurm(t);
    debuglevel(10);
    printwurm(t);
  }
}

Edit
Update @18:51
Ich glaube, ich habe Es!

Derzeit 3 Würmer, Zwei mit festem Weg, Einer per Zufall.

Teil 2

void loop() {
  static long unsigned blitzstart = 0;
  static boolean blitz = 0;
  static uint16_t blitzpixel;
  //Blitze / Funkelsterne
  if (millis() - blitzstart > blitzwartezeit) {
    if (blitz == 1) {
      //war der Blitz in einem voll leuchtendem Segment? Dann auf Leuchtfarbe, sonst löschen
      //(die dimmenden Segmente werden eh neu gesetzt, daher egal ... sonst <= und >=)
      debugPrint(" Blitz:");
      debugPrint(blitzpixel);
      //wenn der Blitz in einem Wurm war, dann auf 'Wagenfarbe' zurück setzen
      if (stripehell[blitzpixel]) {
        debugPrintln(" INNEN -> auf Farbe");
        setpixelrgb(blitzpixel, rmax, gmax, bmax); //          rgbstripe.setPixelColor(blitzpixel, rmax, gmax, bmax);
      } else {
        debugPrintln(" -> auf Schwarz");
        setpixelrgb(blitzpixel, rmin, gmin, bmin);    //          rgbstripe.setPixelColor(blitzpixel, rmin, gmin, bmin);
      }
      blitz = 0;
    }
    blitzpixel = random(0, 0xFFFF);      //Variable wird für Zufallszahl missbraucht - ist hier in diesem Moment eh unbenutzt
    if (blitzpixel > 0xFF00) {
      //Zufallspixel aufblitzen lassen
      blitzstart = millis();              //die Zeit merken, wann der Blitz begonnen hat
      blitzpixel = random(0, PixelCount); //den Pixel bestimmen
      //blitzpixel = 26;                     //zu Testzwecken, damit man weiß, wo's gleich blitzt
      setpixelrgb(blitzpixel, 255, 255, 255);//        rgbstripe.setPixelColor(blitzpixel, 255, 255, 255); //auf Weiß und 100% setzen
      blitz = 1;
    }
  }

  //alle Würmer durcharbeiten. Position, Ziel, Geschwindigkeit
  //Wenn blitz==1 && blitzpixel innerhalb des Wurm, 'blitzinwurm' auf true setzen, den Pixel NICHT verändern
  //Bei Geschwindigkeit 1000 alle Sekunde den nächsten Pixel hoch dimmen, den letzten Pixel runter dimmen
  //Position, Richtung, Anzahl, Geschwindigkeit, Lasttime, Länge des Wurm  static byte wurmnummer;
  static byte wurmnummer;
  static uint16_t milisek;
  static uint16_t kopfpixel, fusspixel;
  static boolean richtung;
  static byte laenge, schritte, schrittnr;

  for (wurmnummer = 0; wurmnummer < wurmanzahl; wurmnummer++) {
    //Kopf anzeigen, wenn schrittnr<=schritte-laenge, Position!=blitzpixel
    //Fuß anzeigen, wenn schrittnr>laenge, Position!=blitzpixel

    //Zeit seit Setzen berechnen
    //wenn Zeit >= Verweilzeit, Kopfpixel 100%, Fußpixel 0%(blitzinwurm=0, wenn = blitzpixel), schrittnr++, Zeit+=Verweilzeit
    //Dimmstufen für Kopf und Fuß berechnen
    //wenn blitzpixel==Kopf blitzinwurm=1, sonst anzeigen, wenn schrittnr<=schritte-laenge
    //wenn blitzpixel==Fuß blitzinwurm=1, sonst anzeigen, wenn schrittnr>laenge, Position!=blitzpixel

    milisek = millis() - leuchtpunkt[wurmnummer].lasttime;
    richtung = leuchtpunkt[wurmnummer].richtung;
    kopfpixel = leuchtpunkt[wurmnummer].kopfposition;
    fusspixel = leuchtpunkt[wurmnummer].fussposition;
    laenge = leuchtpunkt[wurmnummer].laenge;
    schrittnr = leuchtpunkt[wurmnummer].schrittnr;
    schritte = leuchtpunkt[wurmnummer].schritte;

    if (milisek >= leuchtpunkt[wurmnummer].verweilzeit) {
      //Verweilzeit erreicht, Kopfpixel komplett setzen, Fusspixel komplett löschen
      //den Zeitpunkt merken
      leuchtpunkt[wurmnummer].lasttime += leuchtpunkt[wurmnummer].verweilzeit;

      //Wenn der Kopf-Pixel kein Blitz ist und wir noch nicht über das Ende sind, dann setzen
      //      if (kopfpixel != blitzpixel && schrittnr <= schritte) { //aufteilen, damit wir uns merken können, daß der Pixel besetzt ist - im Anschluß Ausgeben, wenn wir kein Blitz sind
      if (schrittnr <= schritte) {
        //hier merken, daß wir diesen Pixel 'besetzt' haben +1
        //wird schon beim Umsetzen und bei der Wurm'erstellung' gemacht
        //        stripehell[kopfpixel]++;
        if ((kopfpixel != blitzpixel && blitz == 1) || blitz == 0) {
          if (schrittnr < schritte) {
            if (schrittnr == 0) {
              setpixelrgb(kopfpixel, 255, 255, 0);//              rgbstripe.setPixelColor(kopfpixel, 255, 255, 0); //Start-Pixel auf GELB setzen, IMMER
            } else {
              if (stripehell[kopfpixel] == 1) {   //nur Anzeigen, wenn der Pixel zuvor leer war
                setpixelrgb(kopfpixel, rmax, gmax, bmax);//                rgbstripe.setPixelColor(kopfpixel, rmax, gmax, bmax); //Körper-Pixel auf 'Wurmfarbe' setzen
              }
            }
          } else {
            setpixelrgb(kopfpixel, 0, 0, 255);//            rgbstripe.setPixelColor(kopfpixel, 0, 0, 255); //auf BLAU setzen - Endpunkt des Wurm, IMMER
          }
        }
      }

      //Wenn der Fusspixel kein Blitz ist und wir schon mit dem Fuss am Anfang sind, dann löschen
      //      if (fusspixel != blitzpixel && schrittnr >= laenge) {
      if (schrittnr >= laenge) {
        //Merker dieses Pixel um 1 veringern - bei einem Wurm sollte hier dann Null raus kommen
        stripehell[fusspixel]--;
        if ((fusspixel != blitzpixel && blitz == 1) || blitz == 0) {
          if (stripehell[fusspixel] == 0) {
            setpixelrgb(fusspixel, rmin, gmin, bmin);//            rgbstripe.setPixelColor(fusspixel, rmin, gmin, bmin); //auf Schwarz/Hintergrund setzen, wenn Pixel jetzt leer
//          } else {
//            setpixelrgb(fusspixel, 0, 10, 0);                   //Debug
//            debugPrint("*");
//            debugPrint(fusspixel);
//            debugPrint(" ");
//            debugPrint(stripehell[fusspixel]);
//            debugPrint(" ");
          }
        }
      }

      //Nach Richtung beide Pixel um 1 versetzen
      if (richtung) {
        leuchtpunkt[wurmnummer].kopfposition++;
        leuchtpunkt[wurmnummer].fussposition++;
      } else {
        leuchtpunkt[wurmnummer].kopfposition--;
        leuchtpunkt[wurmnummer].fussposition--;
      }
      //den neu besetzten Pixel als 'besetzt' markieren - nur, wenn schrittnr< schritte ist (weiter oben <=, da wir hier aber bereits um 1 versetzt haben, müsste schrittnr ebenfalls +1
      if (schrittnr < schritte)       stripehell[leuchtpunkt[wurmnummer].kopfposition]++;

      //Die Schrittnummer erhöhen
      leuchtpunkt[wurmnummer].schrittnr++;

      //Wenn der Wurm bereits komplett wieder verschwunden ist, dann einen Neuen generieren
      if (leuchtpunkt[wurmnummer].schrittnr > laenge) {
        //mindestens Teile des Wurm sind bereits entfernt
        if (schritte + laenge <= schrittnr) {   //<=, da wir mit der 'alten' Variable arbeiten
          //Alle Pixel des Wurm sind entfernt
          debuglevel(20);
          //          debugPrintln("Erzeuge neuen Wurm");
          randomwurm(wurmnummer);
          printwurm(wurmnummer);
        }
      }
    } else {
      //wir sind noch an diesem Pixel, also Kopf/Fuss dimmen
      //wenn der Kopfpixel noch nicht über das Ziel hinaus ist UND kein Blitz ist, dann Dimmwert ausgeben
      if (((blitz == 1 && kopfpixel != blitzpixel) || blitz == 0) &&  schrittnr <= schritte) {
        if (stripehell[kopfpixel] == 1) {   //Dimmen nur zeigen, wenn nicht bereits ein Körper hier liegt
          setpixelrgb(kopfpixel, map(milisek, 0, leuchtpunkt[wurmnummer].verweilzeit, 0, rmax), map(milisek, 0, leuchtpunkt[wurmnummer].verweilzeit, 0, gmax), map(milisek, 0, leuchtpunkt[wurmnummer].verweilzeit, 0, bmax));//          rgbstripe.setPixelColor(kopfpixel, map(milisek, 0, leuchtpunkt[wurmnummer].verweilzeit, 0, rmax), map(milisek, 0, leuchtpunkt[wurmnummer].verweilzeit, 0, gmax), map(milisek, 0, leuchtpunkt[wurmnummer].verweilzeit, 0, bmax));
        }
      }
      //wenn der Fusspixel bereits am Start angelangt ist UND kein Blitz ist, dann Dimmwert ausgeben
      if (((blitz == 1 && fusspixel != blitzpixel) || blitz == 0) && schrittnr >= laenge) {
        if (stripehell[fusspixel] == 1) {   //Dimmen nur zeigen, wenn nicht bereits ein Körper hier liegt
          milisek = leuchtpunkt[wurmnummer].verweilzeit - milisek; //milisek 'umdrehen'
          setpixelrgb(fusspixel , map(milisek, 0, leuchtpunkt[wurmnummer].verweilzeit, 0, rmax), map(milisek, 0, leuchtpunkt[wurmnummer].verweilzeit, 0, gmax), map(milisek, 0, leuchtpunkt[wurmnummer].verweilzeit, 0, bmax));//          rgbstripe.setPixelColor(fusspixel , map(milisek, 0, leuchtpunkt[wurmnummer].verweilzeit, 0, rmax), map(milisek, 0, leuchtpunkt[wurmnummer].verweilzeit, 0, gmax), map(milisek, 0, leuchtpunkt[wurmnummer].verweilzeit, 0, bmax));
//        } else {
//          setpixelrgb(fusspixel , 0, 255, 0);//          rgbstripe.setPixelColor(fusspixel , 0, 255, 0); //wenn mehr als ein Wurm, dann als GRÜN setzen, nicht dimmen
//          debugPrint("-");
//          debugPrint(fusspixel);
//          debugPrint(" ");
//          debugPrint(stripehell[fusspixel]);
//          debugPrint(" ");
        }
      }
    }
  }
  showpixelrgb();
}

Edit
@18:54 - aktueller Sketch, sollte so jetzt klappen

Teil 3

void randomwurm(uint8_t t) {
  //  anzahlneuewuermer++;    //für Debug, nach 20 Würmern wird Geprüft

  switch (t) {
    case 0:   leuchtpunkt[t].kopfposition = 0;
      stripehell[leuchtpunkt[t].kopfposition]++;
      leuchtpunkt[t].richtung = 1;
      leuchtpunkt[t].schritte = PixelCount-1;   //-1, damit der Endpixel angezeigt werden kann
      leuchtpunkt[t].verweilzeit = 30;
      leuchtpunkt[t].laenge = 10;
      leuchtpunkt[t].fussposition = leuchtpunkt[t].richtung == 1 ? leuchtpunkt[t].kopfposition - leuchtpunkt[t].laenge : leuchtpunkt[t].kopfposition + leuchtpunkt[t].laenge;
      leuchtpunkt[t].lasttime = millis();                         //aktuelle Zeit, wurde jetzt erstellt
      leuchtpunkt[t].schrittnr = 0;                               //noch kein Schritt gemacht
      break;
    case 1:   leuchtpunkt[t].kopfposition = PixelCount-1;     //bei 150 Pixeln Nummern 0-149
      stripehell[leuchtpunkt[t].kopfposition]++;
      leuchtpunkt[t].richtung = 0;
      leuchtpunkt[t].schritte = PixelCount-1;   //-1, damit der Endpixel angezeigt werden kann
      leuchtpunkt[t].verweilzeit = 100;
      leuchtpunkt[t].laenge = 10;
      leuchtpunkt[t].fussposition = leuchtpunkt[t].richtung == 1 ? leuchtpunkt[t].kopfposition - leuchtpunkt[t].laenge : leuchtpunkt[t].kopfposition + leuchtpunkt[t].laenge;
      leuchtpunkt[t].lasttime = millis();                         //aktuelle Zeit, wurde jetzt erstellt
      leuchtpunkt[t].schrittnr = 0;                               //noch kein Schritt gemacht
      break;
    default:
      //Wurm t mit Zufallswerten bestücken
      leuchtpunkt[t].kopfposition = random(0, PixelCount - 30) + 15; //bleibt 15 von den Enden des Stripe weg
      //den neu besetzten Pixel als 'besetzt' markieren
      stripehell[leuchtpunkt[t].kopfposition]++;

      leuchtpunkt[t].richtung = random() % 2;  //vor oder zurück
      leuchtpunkt[t].schritte = random(leuchtpunkt[t].richtung == 0 ? leuchtpunkt[t].kopfposition : PixelCount - leuchtpunkt[t].kopfposition); //Schritte bis zum Ziel, kann auch Null sein, dann dimmt der Wurm nur kurz auf und direkt wieder ab (je binnen einer Verweilzeit)
      leuchtpunkt[t].verweilzeit = random(maxverweilzeit - minverweilzeit) + minverweilzeit;           //Verweilzeit 'pro Pixel' --- millis() wird durch die ganzen Unterbrechungen (CLI) zur Pixelsetzung im Stripe ... langsamer - ca. halbe Geschwindigkeit

      leuchtpunkt[t].laenge = random(min(leuchtpunkt[t].schritte, maxlang)) + 1; //random(leuchtpunkt[t].schritte)+1;    //Länge des Wurm, mindestens 1!!
      leuchtpunkt[t].fussposition = leuchtpunkt[t].richtung == 1 ? leuchtpunkt[t].kopfposition - leuchtpunkt[t].laenge : leuchtpunkt[t].kopfposition + leuchtpunkt[t].laenge;
      leuchtpunkt[t].lasttime = millis();                         //aktuelle Zeit, wurde jetzt erstellt
      leuchtpunkt[t].schrittnr = 0;                               //noch kein Schritt gemacht
  }
}

void printwurm(uint8_t t) {
  //  debugPrint(anzahlneuewuermer);
  debugPrint(" Wurm:");
  debugPrint(t);
  debugPrint (" Kopf:");
  debugPrint(leuchtpunkt[t].kopfposition);
  debugPrint (" Richtung:");
  debugPrint(leuchtpunkt[t].richtung);
  debugPrint(" Fuss:");
  debugPrint(leuchtpunkt[t].fussposition);
  debugPrint (" Schritte:");
  debugPrint(leuchtpunkt[t].schritte);
  debugPrint(" Verweilzeit:");
  debugPrint(leuchtpunkt[t].verweilzeit);
  debugPrint(" Laenge:");
  debugPrintln(leuchtpunkt[t].laenge);
}

//zum Anpassen an die eigene Stripe-Lib
void setpixelrgb(uint16_t pixelnr, uint8_t r, uint8_t g, uint8_t b) {
  rgbstripe.setPixelColor(pixelnr , r, g, b);
}
void showpixelrgb() {
  rgbstripe.show();
}
void rgbinit() {
  rgbstripe.begin();          //Stripe initiieren, RAM vorbereiten
  rgbstripe.setBrightness(127);  //MaxHelligkeit auf bestimmten Wert
}

Edit
@18:55, alle Teile aktuell

... ist ja fast mehr Arbeit, den Sketch hier aufzuteilen, als zu Erstellen :o

MfG

Edit @ 19:06
Ein Problem habe ich noch:
Wenn zwei Würmer aufeinander laufen, wird das Dimmen des Kopfpixel unterbrochen, sobald Beide den selben Pixel 'besetzen' - Dieser verbleibt akut gedimmt, bis beide Würmer diesen Pixel wieder verlassen haben.

Soll mir 'für Heute' aber so reichen.

PS: Bei 80 Würmer meckert der Sketch den Speicherplatz an - ab 12 Würmer ist es auf einem 150er Stripe 'arg voll' - akut lasse ich drei oder vier Würmer 'rennen', wobei die ersten Zwei noch den kompletten Stripe hoch bzw. runter laufen, also feste Vorgaben haben.

MfG und viel Spaß beim Anschauen

Hi

Nachtrag:
Bei zwei 'Würmern' keinerlei Beeinträchtigungen.
Bei Fünf verbleiben (über den Tag laufend) mehrere Stellen mit leuchtenden Pixeln, Die kein aktiver Wurm sind - noch sehe ich aber nicht, WO ich Das SO geschrieben habe - zumal Das mit Zwei funktioniert.

Ich lasse beide Würmer Mal in die gleiche Richtung laufen ... hmm, noch sieht der 'Doppelwurm' unauffällig aus.

Einen Tag später, in beiden Richtungen waren je zwei Würmer mit minimal unterschiedlicher Geschwindigkeit unterwegs: kein einziger Fehl-Pixel.
Vll. Morgen zwei zufällige Würmer gegenwinander antreten lassen - wobei mir nicht klar ist, worin sich Das unterscheiden können soll.
In allen Fällen werden die Würmer 'der Reihe nach' berechnen.
Wenn der Wurm den nächsten Schritt machen soll (Zeit ist um), wird der nächste Pixel 'eingenommen' (also +1) - wenn der Wert >1 ist, wird der Dimm-Wert NICHT ausgegeben.
Der letzte Pixel wird ebenso umgesetzt - davor wird der Pixel frei gegeben (-1), bei ==0 (oder vor der Freigabe dann ==1) wird der Pixel schwarz gefärbt, also entfernt.
Eigentlich sollte Da Nichts dran falsch laufen können.

Muß schauen, wann ich Morgen dazu komme.

MfG