1212/* *
1313 * @brief A living entity in the simulation that can move, eat, fight, and reproduce.
1414 *
15- * Organisms have gene-derived attributes (speed, size, awareness) that determine
16- * their behavior and survival. Each iteration, organisms react to nearby objects
17- * (deciding movement direction) and interact with overlapping objects (eating food,
18- * killing smaller organisms). Reproduction creates a mutated offspring and halves
19- * the parent's lifespan.
15+ * Organism behaviour is driven by two pluggable strategy callbacks:
16+ * - **ReactionStrategy** -- decides movement direction based on nearby objects.
17+ * - **InteractionStrategy** -- performs close-range actions (eating, fighting).
18+ *
19+ * When no custom strategy is set, built-in defaults are used. Custom strategies
20+ * are inherited by offspring produced via reproduce(), enabling Python-side
21+ * behaviour injection that persists across generations.
2022 */
2123class Organism : public EnvironmentObject {
2224public:
@@ -28,6 +30,26 @@ class Organism : public EnvironmentObject {
2830 */
2931 using LifeConsumptionCalculator = std::function<uint32_t (const Organism &)>;
3032
33+ /* *
34+ * @brief Strategy that decides how an organism reacts to nearby objects.
35+ *
36+ * Given a reference to the organism and a list of nearby objects (within
37+ * the reaction radius), the strategy returns a (dx, dy) movement direction.
38+ * Returning {0, 0} signals "no reaction" and the organism keeps wandering.
39+ */
40+ using ReactionStrategy = std::function<std::pair<float , float >(
41+ Organism &, const std::vector<std::shared_ptr<EnvironmentObject>> &)>;
42+
43+ /* *
44+ * @brief Strategy that defines close-range interactions with nearby objects.
45+ *
46+ * Given a reference to the organism and objects within its size radius,
47+ * the strategy mutates state directly (e.g. consuming food, killing smaller
48+ * organisms). The default eats food and preys on organisms less than 2/3 its size.
49+ */
50+ using InteractionStrategy = std::function<void (
51+ Organism &, const std::vector<std::shared_ptr<EnvironmentObject>> &)>;
52+
3153 /* * @brief Construct a default organism with preset genes and 500 lifespan. */
3254 Organism ();
3355
@@ -84,32 +106,75 @@ class Organism : public EnvironmentObject {
84106 */
85107 bool canReproduce () const ;
86108
109+ /* *
110+ * @brief Add (or subtract) life-span points.
111+ *
112+ * Exposed publicly so that custom InteractionStrategy callbacks (including
113+ * those written in Python) can reward or penalise organisms.
114+ *
115+ * @param amount Points to add; negative values reduce life-span.
116+ */
117+ void addLifeSpan (float amount);
118+
87119 ~Organism () {};
88120
121+ // ── Behaviour injection ─────────────────────────────────────────────
122+
123+ /* *
124+ * @brief Replace the reaction strategy with a custom implementation.
125+ *
126+ * The strategy is propagated to offspring during reproduce(). Pass nullptr
127+ * or an empty std::function to revert to the built-in default.
128+ *
129+ * @param strategy Callable matching the ReactionStrategy signature.
130+ */
131+ void setReactionStrategy (ReactionStrategy strategy);
132+
133+ /* *
134+ * @brief Replace the interaction strategy with a custom implementation.
135+ *
136+ * The strategy is propagated to offspring during reproduce(). Pass nullptr
137+ * or an empty std::function to revert to the built-in default.
138+ *
139+ * @param strategy Callable matching the InteractionStrategy signature.
140+ */
141+ void setInteractionStrategy (InteractionStrategy strategy);
142+
143+ /* *
144+ * @brief Check whether this organism has any custom (non-default) strategy set.
145+ * @return true if either reactionStrategy or interactionStrategy is set.
146+ *
147+ * Used by Environment to decide whether multi-threaded execution is safe.
148+ * Custom strategies may involve Python callbacks that require the GIL.
149+ */
150+ bool hasCustomStrategy () const ;
151+
152+ // ── Actions ─────────────────────────────────────────────────────────
153+
89154 /* *
90155 * @brief Decide movement direction based on nearby objects within reaction radius.
91156 * @param reactableObjects Objects detected within the organism's reaction radius.
92157 *
93- * Finds the nearest valid object and sets movement direction accordingly:
94- * flee from larger organisms, chase smaller organisms, move toward food.
95- * Only triggers once per iteration (guarded by reactionCounter).
96- * Safe to call in parallel -- only writes to this organism's own fields.
158+ * Delegates to the custom ReactionStrategy if one has been set, otherwise
159+ * falls back to defaultReaction(). Only triggers once per iteration
160+ * (guarded by reactionCounter).
97161 */
98162 void react (const std::vector<std::shared_ptr<EnvironmentObject>> &reactableObjects);
99163
100164 /* *
101165 * @brief Interact with objects within the organism's body size range.
102166 * @param interactableObjects Objects overlapping the organism's size radius.
103167 *
104- * Eats available food (gaining its energy) and kills smaller organisms
105- * (absorbing their remaining lifespan ). Must run single-threaded because
106- * it mutates shared state (food eaten flags, other organisms' lifespans).
168+ * Delegates to the custom InteractionStrategy if set, otherwise uses
169+ * defaultInteraction( ). Must run single-threaded because it mutates
170+ * shared state (food eaten flags, other organisms' lifespans).
107171 */
108172 void interact (const std::vector<std::shared_ptr<EnvironmentObject>> &interactableObjects);
109173
110174 /* *
111175 * @brief Create a mutated offspring organism.
112- * @return A new organism with mutated genes, inheriting the life consumption calculator.
176+ * @return A new organism with mutated genes, inheriting the life consumption
177+ * calculator and any custom behavior strategies.
113178 *
114179 * The parent's lifespan is halved. The child is placed at a small offset
115180 * from the parent's position.
@@ -124,6 +189,8 @@ class Organism : public EnvironmentObject {
124189private:
125190 Genes genes; // /< Genetic data driving attributes
126191 LifeConsumptionCalculator lifeConsumptionCalculator; // /< Optional custom life drain formula
192+ ReactionStrategy reactionStrategy; // /< Optional custom reaction behaviour
193+ InteractionStrategy interactionStrategy; // /< Optional custom interaction behaviour
127194 float lifeSpan; // /< Remaining life points
128195
129196 /* *
@@ -144,6 +211,12 @@ class Organism : public EnvironmentObject {
144211 * not exceed the organism's speed.
145212 */
146213 void makeMove ();
214+
215+ // Default built-in strategies
216+ static std::pair<float , float > defaultReaction (
217+ Organism &self, const std::vector<std::shared_ptr<EnvironmentObject>> &objects);
218+ static void defaultInteraction (
219+ Organism &self, const std::vector<std::shared_ptr<EnvironmentObject>> &objects);
147220};
148221
149222#endif
0 commit comments