More strategies
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
#ifndef LSYSTEM_STRATEGY_H
|
||||
#define LSYSTEM_STRATEGY_H
|
||||
|
||||
#include "MelodyStrategy.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
struct Rule {
|
||||
char from;
|
||||
const char* to;
|
||||
};
|
||||
|
||||
struct RuleSet {
|
||||
const char* axiom;
|
||||
const Rule* rules;
|
||||
uint8_t numRules;
|
||||
};
|
||||
|
||||
// --- L-System Rule Definitions ---
|
||||
const Rule rules1[] = { {'A', "B-A-B"}, {'B', "A+B+A"} };
|
||||
const Rule rules2[] = { {'F', "F+F-F-F+F"} };
|
||||
const Rule rules3[] = { {'X', "F+[[X]-X]-F[-FX]+X"}, {'F', "FF"} };
|
||||
|
||||
const RuleSet ruleSets[] = {
|
||||
{"A", rules1, 2}, // Sierpinski triangle
|
||||
{"F", rules2, 1}, // Koch curve
|
||||
{"X", rules3, 2} // Fractal plant
|
||||
};
|
||||
const uint8_t numRuleSets = sizeof(ruleSets) / sizeof(RuleSet);
|
||||
|
||||
class LSystemStrategy : public MelodyStrategy {
|
||||
public:
|
||||
void generate(Step (*sequence)[NUM_STEPS], int track, int numSteps, int* scaleNotes, int numScaleNotes, int seed) override {
|
||||
randomSeed(seed);
|
||||
if (numScaleNotes == 0) {
|
||||
// Fill with silence if no scale
|
||||
for (int i = 0; i < numSteps; i++) {
|
||||
sequence[track][i].note = -1;
|
||||
sequence[track][i].accent = false;
|
||||
sequence[track][i].tie = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Select a rule set and expand the axiom
|
||||
const RuleSet& selectedRuleSet = ruleSets[random(numRuleSets)];
|
||||
String currentString = selectedRuleSet.axiom;
|
||||
int iterations = random(2, 5); // 2-4 iterations
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
String nextString = "";
|
||||
for (char c : currentString) {
|
||||
bool replaced = false;
|
||||
for (int j = 0; j < selectedRuleSet.numRules; j++) {
|
||||
if (c == selectedRuleSet.rules[j].from) {
|
||||
nextString += selectedRuleSet.rules[j].to;
|
||||
replaced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!replaced) {
|
||||
nextString += c;
|
||||
}
|
||||
if (nextString.length() > 256) break; // Memory safety
|
||||
}
|
||||
currentString = nextString;
|
||||
if (currentString.length() > 256) break;
|
||||
}
|
||||
|
||||
// 2. Interpret the string to create the sequence
|
||||
int stepIndex = 0;
|
||||
int noteIndex = random(numScaleNotes);
|
||||
int octave = 4;
|
||||
|
||||
// Stack for branching rules
|
||||
int note_stack[8];
|
||||
int octave_stack[8];
|
||||
int stack_ptr = 0;
|
||||
|
||||
for (char c : currentString) {
|
||||
if (stepIndex >= numSteps) break;
|
||||
|
||||
switch(c) {
|
||||
case 'F': case 'G': case 'A': case 'B': // Characters that draw notes
|
||||
sequence[track][stepIndex].note = 12 * octave + scaleNotes[noteIndex];
|
||||
sequence[track][stepIndex].accent = (random(100) < 25);
|
||||
sequence[track][stepIndex].tie = (random(100) < 10);
|
||||
stepIndex++;
|
||||
break;
|
||||
case '+': // Go up in scale
|
||||
noteIndex = (noteIndex + 1) % numScaleNotes;
|
||||
break;
|
||||
case '-': // Go down in scale
|
||||
noteIndex = (noteIndex - 1 + numScaleNotes) % numScaleNotes;
|
||||
break;
|
||||
case '[': // Push current state
|
||||
if(stack_ptr < 8) {
|
||||
note_stack[stack_ptr] = noteIndex;
|
||||
octave_stack[stack_ptr] = octave;
|
||||
stack_ptr++;
|
||||
}
|
||||
break;
|
||||
case ']': // Pop state
|
||||
if(stack_ptr > 0) {
|
||||
stack_ptr--;
|
||||
noteIndex = note_stack[stack_ptr];
|
||||
octave = octave_stack[stack_ptr];
|
||||
}
|
||||
break;
|
||||
// Any other characters (like 'X' in rule 3) are ignored for drawing
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Fill any remaining steps with rests
|
||||
for (int i = 0; i < numSteps; i++) {
|
||||
if (i >= stepIndex) {
|
||||
sequence[track][i].note = -1;
|
||||
sequence[track][i].accent = false;
|
||||
sequence[track][i].tie = false;
|
||||
}
|
||||
}
|
||||
randomSeed(micros());
|
||||
}
|
||||
|
||||
void generateScale(int* scaleNotes, int& numScaleNotes) override {
|
||||
numScaleNotes = random(5, 8);
|
||||
for (int i = 0; i < 12; i++) {
|
||||
scaleNotes[i] = i;
|
||||
}
|
||||
for (int i = 0; i < 12; i++) {
|
||||
int j = random(12);
|
||||
int temp = scaleNotes[i];
|
||||
scaleNotes[i] = scaleNotes[j];
|
||||
scaleNotes[j] = temp;
|
||||
}
|
||||
sortArray(scaleNotes, numScaleNotes);
|
||||
}
|
||||
|
||||
void mutate(Step (*sequence)[NUM_STEPS], int track, int numSteps, int* scaleNotes, int numScaleNotes) override {
|
||||
// Swap two non-rest steps to create a variation
|
||||
int s1 = random(numSteps);
|
||||
int s2 = random(numSteps);
|
||||
int attempts = 0;
|
||||
|
||||
// Try to find two different, non-rest steps
|
||||
while((sequence[track][s1].note == -1 || sequence[track][s2].note == -1 || s1 == s2) && attempts < 20) {
|
||||
s1 = random(numSteps);
|
||||
s2 = random(numSteps);
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (sequence[track][s1].note != -1 && sequence[track][s2].note != -1) {
|
||||
Step temp = sequence[track][s1];
|
||||
sequence[track][s1] = sequence[track][s2];
|
||||
sequence[track][s2] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
const char* getName() override {
|
||||
return "L-System";
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user