diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6aca823..268338f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,3 +4,4 @@ project(evolution-sim)
add_subdirectory(common)
add_subdirectory(test-save-load)
+add_subdirectory(evolution-sim)
diff --git a/README.md b/README.md
index 895f94f..bd6b483 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,170 @@
# Evolution-Simulation
-Evolution simulator
-
- Copyright (C) 2021 Matej Gomboc
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
+A genetic programming framework that simulates evolution through virtual machines executing instruction-based programs with biological features like energy, mating, and food consumption.
+
+## Overview
+
+Evolution-Simulation is a C++ framework for evolutionary computation where programs are represented as sequences of instructions that manipulate memory. These programs control organisms that must survive, find food, mate, and pass on their genetic information to the next generation.
+
+### Features
+
+- **Virtual Machine**: Execute programs consisting of various instruction types
+- **Rich Instruction Set**: 26 instructions including arithmetic, logic, control flow, memory operations, and biological behaviors
+- **Biological Simulation**:
+ - Energy system with depletion over time
+ - Mating and reproduction mechanics
+ - Food consumption for survival
+ - Conflict resolution through rock-paper-scissors and energy comparison
+- **Subprogram Support**: Programs can contain multiple subprograms that can call each other
+- **Memory System**: Programs have access to memory for computation and data storage
+- **Serialization**: Save and load programs from text files
+- **Random Program Generation**: Generate random programs for evolutionary algorithms
+
+### Instruction Set
+
+The framework supports the following instructions:
+
+#### Basic Instructions
+- **Arithmetic**: ADD, SUBTRACT, MULTIPLY, DIVIDE, INCREASE, DECREASE, NEGATE
+- **Logic**: AND, OR, NOT, INVERT
+- **Comparison**: EQUAL, GREATER, SMALLER
+- **Memory**: INIT, COPY, SET, CLEAR
+- **Control Flow**: CONDITION, LOOP, RETURN
+- **Other**: NOP (no operation)
+
+#### Biological Instructions
+- **MATE_REQUEST**: Request mating with another organism (parameters: partner_index_addr, result_addr)
+- **MATE_ACCEPT**: Accept a mating request (parameters: requester_index_addr, result_addr)
+- **CONSUME_FOOD**: Attempt to consume food (parameters: food_index_addr, result_addr)
+- **ROCK_PAPER_SCISSORS**: Play rock-paper-scissors for conflict resolution (parameters: choice_addr, opponent_addr, result_addr)
+
+## Building
+
+### Requirements
+
+- CMake 3.0 or higher
+- C++17 compatible compiler
+- Standard C++ library
+
+### Build Instructions
+
+```bash
+mkdir build
+cd build
+cmake ..
+make
+```
+
+## Usage
+
+### Running the Evolution Simulation
+
+```bash
+./evolution-sim/evolution-sim [population_size] [mutation_rate] [generations] [food_spawn_rate]
+
+# Example with default parameters
+./evolution-sim/evolution-sim
+
+# Example with custom parameters
+./evolution-sim/evolution-sim 100 0.15 200 30
+```
+
+Parameters:
+- `population_size`: Initial number of organisms (default: 50)
+- `mutation_rate`: Probability of mutation during reproduction (default: 0.2)
+- `generations`: Number of generations to simulate (default: 100)
+- `food_spawn_rate`: Number of food items spawned per generation (default: 20)
+
+### Creating a Random Program
+
+```cpp
+#include "program.h"
+
+// Generate a random program
+Program program = Program::random();
+
+// Execute the program
+program.execute();
+```
+
+### Loading a Program from File
+
+```cpp
+#include "txtfileparser.h"
+
+std::vector> subprogram;
+size_t status = TxtFileParser::subprogramFromTxt(file_content, subprogram);
+if (status == TxtFileParser::STATUS_SUCCESS) {
+ // Program loaded successfully
+}
+```
+
+### Program File Format
+
+Programs are stored in text files with instructions on separate lines. Example:
+
+```
+INIT 0 100
+INIT 1 50
+MATE_REQUEST 0 2
+CONDITION 2 5
+CONSUME_FOOD 1 3
+RETURN
+```
+
+### Biological Simulation Details
+
+#### Energy System
+- Organisms start with 100 energy units
+- Energy depletes by 1 unit per time step
+- Organisms die when energy reaches 0
+- Food provides 30 energy units when consumed
+- Mating costs 20 energy units for each parent
+
+#### Conflict Resolution
+When organisms compete, the winner is determined by:
+1. Rock-Paper-Scissors game (based on organism's choice)
+2. If tied, higher energy wins
+3. If still tied, random selection (50/50 chance)
+
+## Project Structure
+
+- `common/` - Core library with instruction and program implementations
+ - `instruction.h/cpp` - Base instruction class
+ - `program.h/cpp` - Program execution engine
+ - `biological_sim.h/cpp` - Biological simulation components
+ - `instructions/` - Individual instruction implementations
+ - `txtfileparser.h/cpp` - File I/O for programs
+ - `utils.h/cpp` - Utility functions
+- `evolution-sim/` - Main evolution simulation program
+- `test-save-load/` - Test program for file I/O functionality
+- `examples/` - Example program files
+
+## Examples
+
+See the `examples/` directory for sample programs demonstrating various features:
+- `simple_math.txt` - Basic arithmetic operations
+- `loop_example.txt` - Loop control flow
+- `condition_example.txt` - Conditional execution
+- `logic_operations.txt` - Logical operations
+
+## Contributing
+
+Contributions are welcome! Please feel free to submit issues or pull requests.
+
+## License
+
+Copyright (C) 2021 Matej Gomboc
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 66f3304..8752b87 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -16,6 +16,8 @@ add_library(common STATIC
txtfileparser.h
instruction.cpp
instruction.h
+ biological_sim.cpp
+ biological_sim.h
instructions/multiply.cpp
instructions/multiply.h
instructions/add.cpp
@@ -60,6 +62,14 @@ add_library(common STATIC
instructions/clear.h
instructions/invert.cpp
instructions/invert.h
+ instructions/mate_request.cpp
+ instructions/mate_request.h
+ instructions/consume_food.cpp
+ instructions/consume_food.h
+ instructions/mate_accept.cpp
+ instructions/mate_accept.h
+ instructions/rock_paper_scissors.cpp
+ instructions/rock_paper_scissors.h
)
target_include_directories(common PRIVATE
diff --git a/common/biological_sim.cpp b/common/biological_sim.cpp
new file mode 100644
index 0000000..d1da92f
--- /dev/null
+++ b/common/biological_sim.cpp
@@ -0,0 +1,215 @@
+#include "biological_sim.h"
+#include
+#include
+
+// Organism implementation
+Organism::Organism(size_t organism_id)
+ : fitness(0.0), energy(INITIAL_ENERGY), id(organism_id), age(0) {
+ program = Program::random();
+}
+
+bool Organism::depleteEnergy() {
+ energy -= ENERGY_DEPLETION_RATE;
+ age++;
+ return energy > 0;
+}
+
+bool Organism::hasEnergy(double required_energy) const {
+ return energy >= required_energy;
+}
+
+void Organism::consumeEnergy(double amount) {
+ energy = std::max(0.0, energy - amount);
+}
+
+void Organism::gainEnergy(double amount) {
+ energy += amount;
+}
+
+RPSChoice Organism::makeRPSChoice() const {
+ // Use organism's internal state to make a choice
+ // This could be based on memory values or other factors
+ int choice = static_cast(id + age) % 3;
+ return static_cast(choice);
+}
+
+// Food implementation
+Food::Food(size_t food_id, double energy)
+ : id(food_id), energy_value(energy), consumed(false) {}
+
+double Food::consume() {
+ if (!consumed) {
+ consumed = true;
+ return energy_value;
+ }
+ return 0.0;
+}
+
+// World implementation
+World::World() : next_organism_id(0), next_food_id(0) {
+ auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
+ rng.seed(seed);
+}
+
+Organism* World::addOrganism() {
+ auto organism = std::make_unique(next_organism_id++);
+ Organism* ptr = organism.get();
+ organisms.push_back(std::move(organism));
+ return ptr;
+}
+
+void World::addFood(size_t count) {
+ for (size_t i = 0; i < count; ++i) {
+ food_items.push_back(std::make_unique(next_food_id++));
+ }
+}
+
+bool World::processMatingRequest(size_t requester_id, size_t target_id) {
+ Organism* requester = getOrganism(requester_id);
+ Organism* target = getOrganism(target_id);
+
+ if (!requester || !target) return false;
+ if (!requester->hasEnergy(Organism::MATING_ENERGY_COST)) return false;
+ if (requester_id == target_id) return false; // Can't mate with self
+
+ // Register the mating request
+ requester->pending_mating_request = MatingRequest{requester_id, target_id, false};
+ target->mating_requests_received.push_back(requester_id);
+
+ return true;
+}
+
+Organism* World::processMatingAcceptance(size_t acceptor_id, size_t requester_id) {
+ Organism* acceptor = getOrganism(acceptor_id);
+ Organism* requester = getOrganism(requester_id);
+
+ if (!acceptor || !requester) return nullptr;
+
+ // Check if the requester actually made a request to this acceptor
+ if (!requester->pending_mating_request.has_value() ||
+ requester->pending_mating_request->target_id != acceptor_id) {
+ return nullptr;
+ }
+
+ // Check energy requirements
+ if (!acceptor->hasEnergy(Organism::MATING_ENERGY_COST) ||
+ !requester->hasEnergy(Organism::MATING_ENERGY_COST)) {
+ return nullptr;
+ }
+
+ // Consume energy from both parents
+ acceptor->consumeEnergy(Organism::MATING_ENERGY_COST);
+ requester->consumeEnergy(Organism::MATING_ENERGY_COST);
+
+ // Create offspring
+ Organism* child = addOrganism();
+
+ // Simple genetic combination: randomly choose instructions from parents
+ // In a more sophisticated implementation, this would involve crossover and mutation
+ std::uniform_int_distribution dist(0, 1);
+
+ // Inherit some fitness from parents (with variation)
+ std::normal_distribution fitness_dist(
+ (acceptor->fitness + requester->fitness) / 2.0, 0.1);
+ child->fitness = std::max(0.0, fitness_dist(rng));
+
+ // Give child some initial energy from parents
+ child->energy = (acceptor->energy + requester->energy) * 0.1 + Organism::INITIAL_ENERGY * 0.5;
+
+ // Clear mating request
+ requester->pending_mating_request.reset();
+
+ // Remove request from acceptor's list
+ auto& requests = acceptor->mating_requests_received;
+ requests.erase(std::remove(requests.begin(), requests.end(), requester_id), requests.end());
+
+ return child;
+}
+
+bool World::consumeFood(size_t organism_id, size_t food_id) {
+ Organism* organism = getOrganism(organism_id);
+ Food* food = getFood(food_id);
+
+ if (!organism || !food) return false;
+
+ double energy_gained = food->consume();
+ if (energy_gained > 0) {
+ organism->gainEnergy(energy_gained);
+ return true;
+ }
+
+ return false;
+}
+
+RPSResult World::playRockPaperScissors(RPSChoice player1, RPSChoice player2) {
+ if (player1 == player2) return RPSResult::TIE;
+
+ if ((player1 == RPSChoice::ROCK && player2 == RPSChoice::SCISSORS) ||
+ (player1 == RPSChoice::PAPER && player2 == RPSChoice::ROCK) ||
+ (player1 == RPSChoice::SCISSORS && player2 == RPSChoice::PAPER)) {
+ return RPSResult::PLAYER1_WINS;
+ }
+
+ return RPSResult::PLAYER2_WINS;
+}
+
+size_t World::resolveConflict(size_t org1_id, size_t org2_id) {
+ Organism* org1 = getOrganism(org1_id);
+ Organism* org2 = getOrganism(org2_id);
+
+ if (!org1 || !org2) return org1 ? org1_id : org2_id;
+
+ // First: Rock-Paper-Scissors
+ RPSChoice choice1 = org1->makeRPSChoice();
+ RPSChoice choice2 = org2->makeRPSChoice();
+ RPSResult rps_result = playRockPaperScissors(choice1, choice2);
+
+ if (rps_result == RPSResult::PLAYER1_WINS) return org1_id;
+ if (rps_result == RPSResult::PLAYER2_WINS) return org2_id;
+
+ // Tie: Use energy as indicator of dominance
+ if (org1->energy > org2->energy) return org1_id;
+ if (org2->energy > org1->energy) return org2_id;
+
+ // Still tied: Random selection (Bernoulli distribution)
+ std::bernoulli_distribution dist(0.5);
+ return dist(rng) ? org1_id : org2_id;
+}
+
+void World::update() {
+ // Remove dead organisms
+ organisms.erase(
+ std::remove_if(organisms.begin(), organisms.end(),
+ [](const std::unique_ptr& org) {
+ return !const_cast(org.get())->depleteEnergy();
+ }),
+ organisms.end()
+ );
+
+ // Remove consumed food
+ food_items.erase(
+ std::remove_if(food_items.begin(), food_items.end(),
+ [](const std::unique_ptr& food) {
+ return food->consumed;
+ }),
+ food_items.end()
+ );
+}
+
+Organism* World::getOrganism(size_t id) {
+ auto it = std::find_if(organisms.begin(), organisms.end(),
+ [id](const std::unique_ptr& org) {
+ return org->id == id;
+ });
+
+ return (it != organisms.end()) ? it->get() : nullptr;
+}
+
+Food* World::getFood(size_t id) {
+ auto it = std::find_if(food_items.begin(), food_items.end(),
+ [id](const std::unique_ptr& food) {
+ return food->id == id;
+ });
+
+ return (it != food_items.end()) ? it->get() : nullptr;
+}
diff --git a/common/biological_sim.h b/common/biological_sim.h
new file mode 100644
index 0000000..730bdfe
--- /dev/null
+++ b/common/biological_sim.h
@@ -0,0 +1,202 @@
+#ifndef BIOLOGICAL_SIM_H
+#define BIOLOGICAL_SIM_H
+
+#include
+#include
+#include
+#include
+#include "program.h"
+
+// Forward declarations
+class Organism;
+class Food;
+class World;
+
+/**
+ * @brief Represents the choice in rock-paper-scissors game
+ */
+enum class RPSChoice {
+ ROCK = 0,
+ PAPER = 1,
+ SCISSORS = 2
+};
+
+/**
+ * @brief Result of rock-paper-scissors game
+ */
+enum class RPSResult {
+ PLAYER1_WINS,
+ PLAYER2_WINS,
+ TIE
+};
+
+/**
+ * @brief Represents a mating request between organisms
+ */
+struct MatingRequest {
+ size_t requester_id;
+ size_t target_id;
+ bool accepted = false;
+};
+
+/**
+ * @class Organism
+ * @brief Extended organism class with energy and biological behaviors
+ */
+class Organism {
+public:
+ Program program;
+ double fitness;
+ double energy;
+ size_t id;
+ size_t age;
+ std::optional pending_mating_request;
+ std::vector mating_requests_received;
+
+ static constexpr double INITIAL_ENERGY = 100.0;
+ static constexpr double ENERGY_DEPLETION_RATE = 1.0;
+ static constexpr double MATING_ENERGY_COST = 20.0;
+ static constexpr double FOOD_ENERGY_GAIN = 30.0;
+
+ Organism(size_t organism_id);
+
+ /**
+ * @brief Deplete energy over time
+ * @return true if organism is still alive, false if dead
+ */
+ bool depleteEnergy();
+
+ /**
+ * @brief Check if organism has enough energy for an action
+ * @param required_energy Energy required for the action
+ * @return true if organism has enough energy
+ */
+ bool hasEnergy(double required_energy) const;
+
+ /**
+ * @brief Consume energy for an action
+ * @param amount Amount of energy to consume
+ */
+ void consumeEnergy(double amount);
+
+ /**
+ * @brief Gain energy from food
+ * @param amount Amount of energy to gain
+ */
+ void gainEnergy(double amount);
+
+ /**
+ * @brief Make RPS choice based on internal state
+ * @return Rock, paper, or scissors choice
+ */
+ RPSChoice makeRPSChoice() const;
+};
+
+/**
+ * @class Food
+ * @brief Represents food that organisms can consume
+ */
+class Food {
+public:
+ size_t id;
+ double energy_value;
+ bool consumed;
+
+ Food(size_t food_id, double energy = Organism::FOOD_ENERGY_GAIN);
+
+ /**
+ * @brief Attempt to consume this food
+ * @return Energy value if successful, 0 if already consumed
+ */
+ double consume();
+};
+
+/**
+ * @class World
+ * @brief The simulation world containing organisms and food
+ */
+class World {
+private:
+ std::mt19937 rng;
+
+public:
+ std::vector> organisms;
+ std::vector> food_items;
+ size_t next_organism_id;
+ size_t next_food_id;
+
+ World();
+
+ /**
+ * @brief Add a new organism to the world
+ * @return Pointer to the added organism
+ */
+ Organism* addOrganism();
+
+ /**
+ * @brief Add food to the world
+ * @param count Number of food items to add
+ */
+ void addFood(size_t count);
+
+ /**
+ * @brief Process mating request between organisms
+ * @param requester_id ID of organism making request
+ * @param target_id ID of target organism
+ * @return true if request was registered successfully
+ */
+ bool processMatingRequest(size_t requester_id, size_t target_id);
+
+ /**
+ * @brief Process mating acceptance
+ * @param acceptor_id ID of organism accepting
+ * @param requester_id ID of original requester
+ * @return Pointer to child organism if successful, nullptr otherwise
+ */
+ Organism* processMatingAcceptance(size_t acceptor_id, size_t requester_id);
+
+ /**
+ * @brief Attempt to consume food
+ * @param organism_id ID of organism attempting to eat
+ * @param food_id ID of food to consume
+ * @return true if successful
+ */
+ bool consumeFood(size_t organism_id, size_t food_id);
+
+ /**
+ * @brief Resolve conflict using rock-paper-scissors
+ * @param player1 First player choice
+ * @param player2 Second player choice
+ * @return Result of the game
+ */
+ static RPSResult playRockPaperScissors(RPSChoice player1, RPSChoice player2);
+
+ /**
+ * @brief Resolve conflict between two organisms
+ * @param org1_id First organism ID
+ * @param org2_id Second organism ID
+ * @return ID of winner
+ */
+ size_t resolveConflict(size_t org1_id, size_t org2_id);
+
+ /**
+ * @brief Update world state (deplete energy, remove dead organisms)
+ */
+ void update();
+
+ /**
+ * @brief Get organism by ID
+ * @param id Organism ID
+ * @return Pointer to organism or nullptr if not found
+ */
+ Organism* getOrganism(size_t id);
+
+ /**
+ * @brief Get food by ID
+ * @param id Food ID
+ * @return Pointer to food or nullptr if not found
+ */
+ Food* getFood(size_t id);
+};
+
+#endif // BIOLOGICAL_SIM_H
diff --git a/common/instruction.cpp b/common/instruction.cpp
index 3e63a93..612b5d0 100644
--- a/common/instruction.cpp
+++ b/common/instruction.cpp
@@ -23,6 +23,10 @@
#include "set.h"
#include "smaller.h"
#include "subtract.h"
+#include "mate_request.h"
+#include "consume_food.h"
+#include "mate_accept.h"
+#include "rock_paper_scissors.h"
Instruction::~Instruction() = default;
@@ -51,7 +55,48 @@ std::unique_ptr Instruction::fromStringTokens(const std::vector& tokens) -> std::unique_ptr {
+ if (tokens.size() != 3) return nullptr;
+ try {
+ uint16_t partner_addr = std::stoi(tokens[1]);
+ uint16_t result_addr = std::stoi(tokens[2]);
+ return std::make_unique(partner_addr, result_addr);
+ } catch (...) {
+ return nullptr;
+ }
+ }},
+ { "CONSUME_FOOD", [](const std::vector& tokens) -> std::unique_ptr {
+ if (tokens.size() != 3) return nullptr;
+ try {
+ uint16_t food_addr = std::stoi(tokens[1]);
+ uint16_t result_addr = std::stoi(tokens[2]);
+ return std::make_unique(food_addr, result_addr);
+ } catch (...) {
+ return nullptr;
+ }
+ }},
+ { "MATE_ACCEPT", [](const std::vector& tokens) -> std::unique_ptr {
+ if (tokens.size() != 3) return nullptr;
+ try {
+ uint16_t requester_addr = std::stoi(tokens[1]);
+ uint16_t result_addr = std::stoi(tokens[2]);
+ return std::make_unique(requester_addr, result_addr);
+ } catch (...) {
+ return nullptr;
+ }
+ }},
+ { "ROCK_PAPER_SCISSORS", [](const std::vector& tokens) -> std::unique_ptr {
+ if (tokens.size() != 4) return nullptr;
+ try {
+ uint16_t choice_addr = std::stoi(tokens[1]);
+ uint16_t opponent_addr = std::stoi(tokens[2]);
+ uint16_t result_addr = std::stoi(tokens[3]);
+ return std::make_unique(choice_addr, opponent_addr, result_addr);
+ } catch (...) {
+ return nullptr;
+ }
+ }}
};
if (tokens.size() < 1) {
@@ -91,7 +136,45 @@ std::unique_ptr Instruction::fromByteArray(const std::vector(Id::RETURN), Return::fromByteArray },
{ static_cast(Id::SET), Set::fromByteArray },
{ static_cast(Id::SMALLER), Smaller::fromByteArray },
- { static_cast(Id::SUBTRACT), Subtract::fromByteArray }
+ { static_cast(Id::SUBTRACT), Subtract::fromByteArray },
+ { static_cast(Id::MATE_REQUEST), [](const std::vector& array, size_t& offset) -> std::unique_ptr {
+ if (array.size() - offset < 5) return nullptr;
+ offset++;
+ uint16_t partner_addr = array[offset] | (array[offset + 1] << 8);
+ offset += 2;
+ uint16_t result_addr = array[offset] | (array[offset + 1] << 8);
+ offset += 2;
+ return std::make_unique(partner_addr, result_addr);
+ }},
+ { static_cast(Id::CONSUME_FOOD), [](const std::vector& array, size_t& offset) -> std::unique_ptr {
+ if (array.size() - offset < 5) return nullptr;
+ offset++;
+ uint16_t food_addr = array[offset] | (array[offset + 1] << 8);
+ offset += 2;
+ uint16_t result_addr = array[offset] | (array[offset + 1] << 8);
+ offset += 2;
+ return std::make_unique(food_addr, result_addr);
+ }},
+ { static_cast(Id::MATE_ACCEPT), [](const std::vector& array, size_t& offset) -> std::unique_ptr {
+ if (array.size() - offset < 5) return nullptr;
+ offset++;
+ uint16_t requester_addr = array[offset] | (array[offset + 1] << 8);
+ offset += 2;
+ uint16_t result_addr = array[offset] | (array[offset + 1] << 8);
+ offset += 2;
+ return std::make_unique(requester_addr, result_addr);
+ }},
+ { static_cast(Id::ROCK_PAPER_SCISSORS), [](const std::vector& array, size_t& offset) -> std::unique_ptr {
+ if (array.size() - offset < 7) return nullptr;
+ offset++;
+ uint16_t choice_addr = array[offset] | (array[offset + 1] << 8);
+ offset += 2;
+ uint16_t opponent_addr = array[offset] | (array[offset + 1] << 8);
+ offset += 2;
+ uint16_t result_addr = array[offset] | (array[offset + 1] << 8);
+ offset += 2;
+ return std::make_unique(choice_addr, opponent_addr, result_addr);
+ }}
};
if (array.size() - offset < 1) {
diff --git a/common/instruction.h b/common/instruction.h
index 90d3551..0dfaac3 100644
--- a/common/instruction.h
+++ b/common/instruction.h
@@ -31,7 +31,12 @@ class Instruction
RETURN = 18,
SET = 19,
SMALLER = 20,
- SUBTRACT = 21
+ SUBTRACT = 21,
+ // New biological simulation instructions
+ MATE_REQUEST = 22, // Request mating with another organism
+ CONSUME_FOOD = 23, // Attempt to consume food
+ MATE_ACCEPT = 24, // Accept mating request from another organism
+ ROCK_PAPER_SCISSORS = 25 // Play rock-paper-scissors (for conflict resolution)
};
virtual ~Instruction() = 0;
diff --git a/common/instructions/consume_food.cpp b/common/instructions/consume_food.cpp
new file mode 100644
index 0000000..283d33e
--- /dev/null
+++ b/common/instructions/consume_food.cpp
@@ -0,0 +1,43 @@
+#include "consume_food.h"
+
+ConsumeFood::ConsumeFood(uint16_t food_index_addr, uint16_t result_addr)
+ : m_food_index_addr(food_index_addr), m_result_addr(result_addr) {}
+
+void ConsumeFood::operator()(std::vector& memory, uint8_t& subprogram_index,
+ std::vector& instruction_addresses, std::vector& return_indices) const
+{
+ instruction_addresses[subprogram_index]++;
+
+ // Get food index from memory
+ if (m_food_index_addr < memory.size() && m_result_addr < memory.size()) {
+ int32_t food_index = memory[m_food_index_addr];
+
+ // In actual implementation, this would interface with the World class
+ // For now, we just set a result flag in memory
+ // 0 = consumption failed, 1 = consumption successful
+ memory[m_result_addr] = (food_index >= 0) ? 1 : 0;
+ }
+}
+
+std::vector ConsumeFood::toStringTokens() const
+{
+ return {
+ "CONSUME_FOOD",
+ std::to_string(m_food_index_addr),
+ std::to_string(m_result_addr)
+ };
+}
+
+std::vector ConsumeFood::toByteArray() const
+{
+ std::vector result;
+ result.push_back(static_cast(Id::CONSUME_FOOD));
+
+ result.push_back(static_cast(m_food_index_addr));
+ result.push_back(static_cast(m_food_index_addr >> 8));
+
+ result.push_back(static_cast(m_result_addr));
+ result.push_back(static_cast(m_result_addr >> 8));
+
+ return result;
+}
diff --git a/common/instructions/consume_food.h b/common/instructions/consume_food.h
new file mode 100644
index 0000000..3f4f7da
--- /dev/null
+++ b/common/instructions/consume_food.h
@@ -0,0 +1,20 @@
+#ifndef CONSUME_FOOD_H
+#define CONSUME_FOOD_H
+
+#include "../instruction.h"
+#include
+
+class ConsumeFood : public Instruction
+{
+public:
+ uint16_t m_food_index_addr;
+ uint16_t m_result_addr;
+
+ ConsumeFood(uint16_t food_index_addr, uint16_t result_addr);
+ void operator()(std::vector& memory, uint8_t& subprogram_index,
+ std::vector& instruction_addresses, std::vector& return_indices) const override;
+ std::vector toStringTokens() const override;
+ std::vector toByteArray() const override;
+};
+
+#endif // CONSUME_FOOD_H
diff --git a/common/instructions/mate_accept.cpp b/common/instructions/mate_accept.cpp
new file mode 100644
index 0000000..e483386
--- /dev/null
+++ b/common/instructions/mate_accept.cpp
@@ -0,0 +1,43 @@
+#include "mate_accept.h"
+
+MateAccept::MateAccept(uint16_t requester_index_addr, uint16_t result_addr)
+ : m_requester_index_addr(requester_index_addr), m_result_addr(result_addr) {}
+
+void MateAccept::operator()(std::vector& memory, uint8_t& subprogram_index,
+ std::vector& instruction_addresses, std::vector& return_indices) const
+{
+ instruction_addresses[subprogram_index]++;
+
+ // Get requester index from memory
+ if (m_requester_index_addr < memory.size() && m_result_addr < memory.size()) {
+ int32_t requester_index = memory[m_requester_index_addr];
+
+ // In actual implementation, this would interface with the World class
+ // For now, we just set a result flag in memory
+ // 0 = acceptance failed, 1 = mating successful
+ memory[m_result_addr] = (requester_index >= 0) ? 1 : 0;
+ }
+}
+
+std::vector MateAccept::toStringTokens() const
+{
+ return {
+ "MATE_ACCEPT",
+ std::to_string(m_requester_index_addr),
+ std::to_string(m_result_addr)
+ };
+}
+
+std::vector MateAccept::toByteArray() const
+{
+ std::vector result;
+ result.push_back(static_cast(Id::MATE_ACCEPT));
+
+ result.push_back(static_cast(m_requester_index_addr));
+ result.push_back(static_cast(m_requester_index_addr >> 8));
+
+ result.push_back(static_cast(m_result_addr));
+ result.push_back(static_cast(m_result_addr >> 8));
+
+ return result;
+}
diff --git a/common/instructions/mate_accept.h b/common/instructions/mate_accept.h
new file mode 100644
index 0000000..d83131f
--- /dev/null
+++ b/common/instructions/mate_accept.h
@@ -0,0 +1,20 @@
+#ifndef MATE_ACCEPT_H
+#define MATE_ACCEPT_H
+
+#include "../instruction.h"
+#include
+
+class MateAccept : public Instruction
+{
+public:
+ uint16_t m_requester_index_addr;
+ uint16_t m_result_addr;
+
+ MateAccept(uint16_t requester_index_addr, uint16_t result_addr);
+ void operator()(std::vector& memory, uint8_t& subprogram_index,
+ std::vector& instruction_addresses, std::vector& return_indices) const override;
+ std::vector toStringTokens() const override;
+ std::vector toByteArray() const override;
+};
+
+#endif // MATE_ACCEPT_H
diff --git a/common/instructions/mate_request.cpp b/common/instructions/mate_request.cpp
new file mode 100644
index 0000000..dd95cb0
--- /dev/null
+++ b/common/instructions/mate_request.cpp
@@ -0,0 +1,43 @@
+#include "mate_request.h"
+
+MateRequest::MateRequest(uint16_t partner_index_addr, uint16_t result_addr)
+ : m_partner_index_addr(partner_index_addr), m_result_addr(result_addr) {}
+
+void MateRequest::operator()(std::vector& memory, uint8_t& subprogram_index,
+ std::vector& instruction_addresses, std::vector& return_indices) const
+{
+ instruction_addresses[subprogram_index]++;
+
+ // Get partner index from memory
+ if (m_partner_index_addr < memory.size() && m_result_addr < memory.size()) {
+ int32_t partner_index = memory[m_partner_index_addr];
+
+ // In actual implementation, this would interface with the World class
+ // For now, we just set a result flag in memory
+ // 0 = request failed, 1 = request sent successfully
+ memory[m_result_addr] = (partner_index >= 0) ? 1 : 0;
+ }
+}
+
+std::vector MateRequest::toStringTokens() const
+{
+ return {
+ "MATE_REQUEST",
+ std::to_string(m_partner_index_addr),
+ std::to_string(m_result_addr)
+ };
+}
+
+std::vector MateRequest::toByteArray() const
+{
+ std::vector result;
+ result.push_back(static_cast(Id::MATE_REQUEST));
+
+ result.push_back(static_cast(m_partner_index_addr));
+ result.push_back(static_cast(m_partner_index_addr >> 8));
+
+ result.push_back(static_cast(m_result_addr));
+ result.push_back(static_cast(m_result_addr >> 8));
+
+ return result;
+}
diff --git a/common/instructions/mate_request.h b/common/instructions/mate_request.h
new file mode 100644
index 0000000..7fa4e72
--- /dev/null
+++ b/common/instructions/mate_request.h
@@ -0,0 +1,20 @@
+#ifndef MATE_REQUEST_H
+#define MATE_REQUEST_H
+
+#include "../instruction.h"
+#include
+
+class MateRequest : public Instruction
+{
+public:
+ uint16_t m_partner_index_addr;
+ uint16_t m_result_addr;
+
+ MateRequest(uint16_t partner_index_addr, uint16_t result_addr);
+ void operator()(std::vector& memory, uint8_t& subprogram_index,
+ std::vector& instruction_addresses, std::vector& return_indices) const override;
+ std::vector toStringTokens() const override;
+ std::vector toByteArray() const override;
+};
+
+#endif // MATE_REQUEST_H
diff --git a/common/instructions/rock_paper_scissors.cpp b/common/instructions/rock_paper_scissors.cpp
new file mode 100644
index 0000000..7494a25
--- /dev/null
+++ b/common/instructions/rock_paper_scissors.cpp
@@ -0,0 +1,51 @@
+#include "rock_paper_scissors.h"
+
+RockPaperScissors::RockPaperScissors(uint16_t choice_addr, uint16_t opponent_addr, uint16_t result_addr)
+ : m_choice_addr(choice_addr), m_opponent_addr(opponent_addr), m_result_addr(result_addr) {}
+
+void RockPaperScissors::operator()(std::vector& memory, uint8_t& subprogram_index,
+ std::vector& instruction_addresses, std::vector& return_indices) const
+{
+ instruction_addresses[subprogram_index]++;
+
+ // Get choice and opponent from memory
+ if (m_choice_addr < memory.size() &&
+ m_opponent_addr < memory.size() &&
+ m_result_addr < memory.size()) {
+
+ int32_t choice = memory[m_choice_addr] % 3; // Ensure valid choice (0-2)
+ int32_t opponent_id = memory[m_opponent_addr];
+
+ // In actual implementation, this would interface with the World class
+ // to resolve the conflict. For now, we just store a placeholder result
+ // -1 = loss, 0 = tie, 1 = win
+ memory[m_result_addr] = 0; // Default to tie
+ }
+}
+
+std::vector RockPaperScissors::toStringTokens() const
+{
+ return {
+ "ROCK_PAPER_SCISSORS",
+ std::to_string(m_choice_addr),
+ std::to_string(m_opponent_addr),
+ std::to_string(m_result_addr)
+ };
+}
+
+std::vector RockPaperScissors::toByteArray() const
+{
+ std::vector result;
+ result.push_back(static_cast(Id::ROCK_PAPER_SCISSORS));
+
+ result.push_back(static_cast(m_choice_addr));
+ result.push_back(static_cast(m_choice_addr >> 8));
+
+ result.push_back(static_cast(m_opponent_addr));
+ result.push_back(static_cast(m_opponent_addr >> 8));
+
+ result.push_back(static_cast(m_result_addr));
+ result.push_back(static_cast(m_result_addr >> 8));
+
+ return result;
+}
diff --git a/common/instructions/rock_paper_scissors.h b/common/instructions/rock_paper_scissors.h
new file mode 100644
index 0000000..43c3a8d
--- /dev/null
+++ b/common/instructions/rock_paper_scissors.h
@@ -0,0 +1,21 @@
+#ifndef ROCK_PAPER_SCISSORS_H
+#define ROCK_PAPER_SCISSORS_H
+
+#include "../instruction.h"
+#include
+
+class RockPaperScissors : public Instruction
+{
+public:
+ uint16_t m_choice_addr; // Memory address containing choice (0=rock, 1=paper, 2=scissors)
+ uint16_t m_opponent_addr; // Memory address containing opponent ID
+ uint16_t m_result_addr; // Memory address to store result
+
+ RockPaperScissors(uint16_t choice_addr, uint16_t opponent_addr, uint16_t result_addr);
+ void operator()(std::vector& memory, uint8_t& subprogram_index,
+ std::vector& instruction_addresses, std::vector& return_indices) const override;
+ std::vector toStringTokens() const override;
+ std::vector toByteArray() const override;
+};
+
+#endif // ROCK_PAPER_SCISSORS_H
diff --git a/common/program.h b/common/program.h
index e6dda4b..9c41c44 100644
--- a/common/program.h
+++ b/common/program.h
@@ -4,32 +4,114 @@
#include
#include "instruction.h"
+/**
+ * @class Program
+ * @brief Represents an executable program consisting of instructions organized in subprograms
+ *
+ * A Program is the main execution unit in the evolution simulation. It contains:
+ * - Multiple subprograms (collections of instructions)
+ * - Memory space for data storage and manipulation
+ * - Execution state (instruction pointers, return addresses)
+ *
+ * Programs can be randomly generated for evolutionary algorithms or loaded from files.
+ */
class Program
{
public:
+ /// Maximum number of instructions per subprogram
static constexpr uint16_t MAX_INSTRUCTIONS = std::numeric_limits::max();
+
+ /// Maximum size of program memory
static constexpr uint16_t MAX_DATA = std::numeric_limits::max();
+
+ /// Maximum number of subprograms in a program
static constexpr uint8_t MAX_SUBPROGRAMS = std::numeric_limits::max();
+ /**
+ * @brief Generate a random program
+ * @return A newly created program with random instructions
+ */
static Program random();
+ /**
+ * @brief Default constructor
+ */
Program();
+
+ /**
+ * @brief Execute the program
+ *
+ * Starts execution from subprogram 0 and continues until a RETURN
+ * instruction is encountered or an error occurs.
+ */
void execute();
private:
+ /// Collection of subprograms, each containing a sequence of instructions
std::vector>> m_subprograms;
+
+ /// Program memory for data storage
std::vector m_memory;
+
+ /// Current subprogram being executed
uint8_t m_subprogram_index = 0;
+
+ /// Stack of instruction addresses for each subprogram
std::vector m_instruction_addresses;
+
+ /// Stack of return addresses for subprogram calls
std::vector m_return_addresses;
+ /**
+ * @brief Generate a random memory value
+ * @return Random 32-bit integer value
+ */
static int32_t generateRandomMemoryValue();
+ /**
+ * @brief Generate a random memory address
+ * @param adding_allowed Whether to allow adding new memory locations
+ * @param max_allowed_memory Maximum memory address allowed
+ * @return Random memory address within bounds
+ */
uint16_t generateRandomMemoryAddress(bool adding_allowed = false, uint16_t max_allowed_memory = 0);
+
+ /**
+ * @brief Add a NOP (no operation) instruction to a subprogram
+ * @param subprogram_index Target subprogram
+ */
void addNop(uint8_t subprogram_index);
+
+ /**
+ * @brief Add a random INIT instruction to a subprogram
+ * @param subprogram_index Target subprogram
+ * @param adding_memory_allowed Whether to allow expanding memory
+ * @param max_allowed_memory Maximum memory address allowed
+ */
void addRandomInit(uint8_t subprogram_index, bool adding_memory_allowed = false, uint16_t max_allowed_memory = 0);
+
+ /**
+ * @brief Add a random COPY instruction to a subprogram
+ * @param subprogram_index Target subprogram
+ * @param adding_memory_allowed Whether to allow expanding memory
+ * @param max_allowed_memory Maximum memory address allowed
+ */
void addRandomCopy(uint8_t subprogram_index, bool adding_memory_allowed = false, uint16_t max_allowed_memory = 0);
+
+ /**
+ * @brief Add a random unary math/logic instruction to a subprogram
+ * @param subprogram_index Target subprogram
+ * @param adding_memory_allowed Whether to allow expanding memory
+ * @param max_allowed_memory Maximum memory address allowed
+ */
void addRandomMathLogicInstruction1(uint8_t subprogram_index, bool adding_memory_allowed = false, uint16_t max_allowed_memory = 0);
+
+ /**
+ * @brief Add a random binary math/logic instruction to a subprogram
+ * @param subprogram_index Target subprogram
+ * @param adding_memory_allowed Whether to allow expanding memory
+ * @param max_allowed_memory Maximum memory address allowed
+ */
void addRandomMathLogicInstruction2(uint8_t subprogram_index, bool adding_memory_allowed = false, uint16_t max_allowed_memory = 0);
};
diff --git a/evolution-sim/CMakeLists.txt b/evolution-sim/CMakeLists.txt
new file mode 100644
index 0000000..1869b33
--- /dev/null
+++ b/evolution-sim/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.0)
+project(evolution-sim-main)
+
+# Set C++ standard
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+# Add the common library
+include_directories(${CMAKE_SOURCE_DIR}/common)
+
+# Create the executable
+add_executable(evolution-sim main.cpp)
+
+# Link with the common library
+target_link_libraries(evolution-sim common)
diff --git a/evolution-sim/main.cpp b/evolution-sim/main.cpp
new file mode 100644
index 0000000..5bd1324
--- /dev/null
+++ b/evolution-sim/main.cpp
@@ -0,0 +1,295 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "biological_sim.h"
+
+/**
+ * @class BiologicalEvolutionSimulation
+ * @brief Evolution simulation with biological features like energy, mating, and food
+ */
+class BiologicalEvolutionSimulation {
+private:
+ World world;
+ size_t population_size;
+ double mutation_rate;
+ size_t food_spawn_rate;
+ size_t generation;
+
+public:
+ BiologicalEvolutionSimulation(size_t pop_size, double mut_rate, size_t food_rate)
+ : population_size(pop_size), mutation_rate(mut_rate),
+ food_spawn_rate(food_rate), generation(0) {
+
+ // Create initial population
+ for (size_t i = 0; i < population_size; ++i) {
+ world.addOrganism();
+ }
+
+ // Add initial food
+ world.addFood(food_spawn_rate * 2);
+ }
+
+ // Execute programs and let organisms interact
+ void simulateInteractions() {
+ // Execute each organism's program
+ for (auto& organism : world.organisms) {
+ try {
+ // Execute the program multiple times to allow for complex behaviors
+ for (int i = 0; i < 5; ++i) {
+ organism->program.execute();
+ }
+
+ // Update fitness based on energy and age
+ organism->fitness = organism->energy * 0.7 + organism->age * 0.3;
+
+ } catch (...) {
+ // Programs that crash get lower fitness
+ organism->fitness *= 0.5;
+ }
+ }
+
+ // Simulate some random interactions (in a real implementation,
+ // these would be driven by the instruction execution)
+ simulateRandomInteractions();
+ }
+
+ // Simulate random biological interactions
+ void simulateRandomInteractions() {
+ std::random_device rd;
+ std::mt19937 rng(rd());
+ std::uniform_int_distribution org_dist(0, world.organisms.size() - 1);
+ std::uniform_int_distribution food_dist(0, world.food_items.size() - 1);
+ std::uniform_real_distribution prob_dist(0.0, 1.0);
+
+ // Simulate mating attempts
+ for (size_t i = 0; i < world.organisms.size() / 4; ++i) {
+ if (world.organisms.size() < 2) break;
+
+ size_t org1_idx = org_dist(rng) % world.organisms.size();
+ size_t org2_idx = org_dist(rng) % world.organisms.size();
+
+ if (org1_idx != org2_idx) {
+ Organism* org1 = world.organisms[org1_idx].get();
+ Organism* org2 = world.organisms[org2_idx].get();
+
+ // Simulate mating request
+ if (prob_dist(rng) < 0.3 && org1->hasEnergy(Organism::MATING_ENERGY_COST)) {
+ world.processMatingRequest(org1->id, org2->id);
+
+ // Simulate acceptance
+ if (prob_dist(rng) < 0.5 && org2->hasEnergy(Organism::MATING_ENERGY_COST)) {
+ world.processMatingAcceptance(org2->id, org1->id);
+ }
+ }
+ }
+ }
+
+ // Simulate food consumption attempts
+ for (size_t i = 0; i < world.organisms.size() / 2; ++i) {
+ if (world.organisms.empty() || world.food_items.empty()) break;
+
+ size_t org_idx = org_dist(rng) % world.organisms.size();
+ size_t food_idx = food_dist(rng) % world.food_items.size();
+
+ Organism* org = world.organisms[org_idx].get();
+ Food* food = world.food_items[food_idx].get();
+
+ if (!food->consumed) {
+ world.consumeFood(org->id, food->id);
+ }
+ }
+
+ // Simulate conflicts
+ for (size_t i = 0; i < world.organisms.size() / 10; ++i) {
+ if (world.organisms.size() < 2) break;
+
+ size_t org1_idx = org_dist(rng) % world.organisms.size();
+ size_t org2_idx = org_dist(rng) % world.organisms.size();
+
+ if (org1_idx != org2_idx) {
+ Organism* org1 = world.organisms[org1_idx].get();
+ Organism* org2 = world.organisms[org2_idx].get();
+
+ size_t winner_id = world.resolveConflict(org1->id, org2->id);
+
+ // Winner gains some energy, loser loses some
+ if (winner_id == org1->id) {
+ org1->gainEnergy(5.0);
+ org2->consumeEnergy(10.0);
+ } else {
+ org2->gainEnergy(5.0);
+ org1->consumeEnergy(10.0);
+ }
+ }
+ }
+ }
+
+ // Natural selection based on energy
+ void naturalSelection() {
+ // Sort organisms by fitness (energy * age factor)
+ std::sort(world.organisms.begin(), world.organisms.end(),
+ [](const std::unique_ptr& a, const std::unique_ptr& b) {
+ return a->fitness > b->fitness;
+ });
+
+ // Keep top 50% of organisms
+ size_t survivors = world.organisms.size() / 2;
+ if (survivors < 10) survivors = std::min(size_t(10), world.organisms.size());
+
+ // Remove weakest organisms
+ while (world.organisms.size() > survivors) {
+ world.organisms.pop_back();
+ }
+ }
+
+ // Reproduce to maintain population
+ void reproduce() {
+ std::random_device rd;
+ std::mt19937 rng(rd());
+ std::uniform_real_distribution mut_dist(0.0, 1.0);
+ std::uniform_int_distribution parent_dist(0, world.organisms.size() - 1);
+
+ size_t target_pop = population_size;
+
+ while (world.organisms.size() < target_pop && !world.organisms.empty()) {
+ // Select random parent
+ size_t parent_idx = parent_dist(rng) % world.organisms.size();
+ Organism* parent = world.organisms[parent_idx].get();
+
+ // Create offspring
+ Organism* child = world.addOrganism();
+
+ // Inherit from parent with possible mutation
+ if (mut_dist(rng) < mutation_rate) {
+ // Mutate: generate new random program
+ child->program = Program::random();
+ } else {
+ // Inherit: copy parent's program (simplified - should do crossover)
+ child->program = parent->program;
+ }
+
+ // Give child initial energy
+ child->energy = Organism::INITIAL_ENERGY * 0.8;
+ }
+ }
+
+ // Print statistics
+ void printStats() {
+ if (world.organisms.empty()) {
+ std::cout << "Generation " << std::setw(4) << generation
+ << " | Population extinct!" << std::endl;
+ return;
+ }
+
+ double total_energy = 0.0;
+ double total_fitness = 0.0;
+ double max_energy = 0.0;
+ double max_fitness = 0.0;
+ size_t max_age = 0;
+ size_t total_age = 0;
+
+ for (const auto& organism : world.organisms) {
+ total_energy += organism->energy;
+ total_fitness += organism->fitness;
+ max_energy = std::max(max_energy, organism->energy);
+ max_fitness = std::max(max_fitness, organism->fitness);
+ max_age = std::max(max_age, organism->age);
+ total_age += organism->age;
+ }
+
+ double avg_energy = total_energy / world.organisms.size();
+ double avg_fitness = total_fitness / world.organisms.size();
+ double avg_age = static_cast(total_age) / world.organisms.size();
+
+ std::cout << "Generation " << std::setw(4) << generation
+ << " | Pop: " << std::setw(3) << world.organisms.size()
+ << " | Avg Energy: " << std::fixed << std::setprecision(1) << avg_energy
+ << " | Max Energy: " << std::fixed << std::setprecision(1) << max_energy
+ << " | Avg Age: " << std::fixed << std::setprecision(1) << avg_age
+ << " | Max Age: " << max_age
+ << " | Food: " << world.food_items.size()
+ << std::endl;
+ }
+
+ // Run the simulation
+ void run(size_t num_generations) {
+ std::cout << "Starting Biological Evolution Simulation" << std::endl;
+ std::cout << "Initial population: " << population_size << std::endl;
+ std::cout << "Mutation rate: " << mutation_rate << std::endl;
+ std::cout << "Food spawn rate: " << food_spawn_rate << " per generation" << std::endl;
+ std::cout << std::string(80, '-') << std::endl;
+
+ for (generation = 0; generation < num_generations; ++generation) {
+ // Spawn new food
+ world.addFood(food_spawn_rate);
+
+ // Simulate interactions
+ simulateInteractions();
+
+ // Update world (deplete energy, remove dead)
+ world.update();
+
+ // Natural selection
+ if (world.organisms.size() > 0) {
+ naturalSelection();
+ }
+
+ // Print statistics
+ printStats();
+
+ // Check for extinction
+ if (world.organisms.empty()) {
+ std::cout << "Population extinct at generation " << generation << "!" << std::endl;
+ break;
+ }
+
+ // Reproduce to maintain population
+ reproduce();
+ }
+
+ std::cout << std::string(80, '-') << std::endl;
+ std::cout << "Biological evolution simulation completed!" << std::endl;
+
+ // Final statistics
+ if (!world.organisms.empty()) {
+ std::cout << "Final population size: " << world.organisms.size() << std::endl;
+ std::cout << "Oldest organism age: ";
+ size_t max_age = 0;
+ for (const auto& org : world.organisms) {
+ max_age = std::max(max_age, org->age);
+ }
+ std::cout << max_age << " generations" << std::endl;
+ }
+ }
+};
+
+int main(int argc, char* argv[]) {
+ // Default parameters
+ size_t population_size = 50;
+ double mutation_rate = 0.2;
+ size_t generations = 100;
+ size_t food_spawn_rate = 20;
+
+ // Parse command line arguments
+ if (argc > 1) population_size = std::stoul(argv[1]);
+ if (argc > 2) mutation_rate = std::stod(argv[2]);
+ if (argc > 3) generations = std::stoul(argv[3]);
+ if (argc > 4) food_spawn_rate = std::stoul(argv[4]);
+
+ std::cout << "Biological Evolution Simulation - Genetic Programming with Life" << std::endl;
+ std::cout << "==============================================================" << std::endl;
+
+ try {
+ BiologicalEvolutionSimulation sim(population_size, mutation_rate, food_spawn_rate);
+ sim.run(generations);
+ } catch (const std::exception& e) {
+ std::cerr << "Error: " << e.what() << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..359982c
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,31 @@
+# Example Programs
+
+This directory contains example programs that can be loaded and executed by the Evolution Simulation framework.
+
+## File Format
+
+Programs are stored as text files with one instruction per line. Each instruction consists of:
+- Instruction name (uppercase)
+- Parameters (space-separated)
+
+## Examples
+
+- `simple_math.txt` - Basic arithmetic operations
+- `loop_example.txt` - Demonstrates loop control flow
+- `condition_example.txt` - Shows conditional execution
+- `subprogram_example.txt` - Example with multiple subprograms
+
+## Loading Programs
+
+To load a program from a text file:
+
+```cpp
+#include "txtfileparser.h"
+
+std::vector> subprogram;
+size_t status = TxtFileParser::subprogramFromTxt(file_content, subprogram);
+```
+
+## Creating Your Own Programs
+
+You can create your own program files following the instruction format. See the instruction set documentation in the main README for available instructions and their parameters.
diff --git a/examples/biological_behavior.txt b/examples/biological_behavior.txt
new file mode 100644
index 0000000..5175eff
--- /dev/null
+++ b/examples/biological_behavior.txt
@@ -0,0 +1,14 @@
+INIT 0 1
+INIT 1 0
+INIT 2 0
+INIT 3 2
+INIT 4 0
+CONSUME_FOOD 0 1
+CONDITION 1 9
+MATE_REQUEST 2 4
+CONDITION 4 11
+RETURN
+INCREASE 3
+COPY 3 0
+LOOP 5 1
+RETURN
\ No newline at end of file
diff --git a/examples/condition_example.txt b/examples/condition_example.txt
new file mode 100644
index 0000000..64b7b67
--- /dev/null
+++ b/examples/condition_example.txt
@@ -0,0 +1,8 @@
+INIT 0 15
+INIT 1 10
+GREATER 0 1 2
+CONDITION 2 5
+COPY 0 3
+RETURN
+COPY 1 3
+RETURN
\ No newline at end of file
diff --git a/examples/conflict_resolution.txt b/examples/conflict_resolution.txt
new file mode 100644
index 0000000..8cb2a84
--- /dev/null
+++ b/examples/conflict_resolution.txt
@@ -0,0 +1,11 @@
+INIT 0 0
+INIT 1 3
+INIT 2 1
+INIT 3 0
+ROCK_PAPER_SCISSORS 0 1 3
+EQUAL 3 2 4
+CONDITION 4 9
+COPY 0 5
+RETURN
+COPY 1 5
+RETURN
\ No newline at end of file
diff --git a/examples/logic_operations.txt b/examples/logic_operations.txt
new file mode 100644
index 0000000..7dda009
--- /dev/null
+++ b/examples/logic_operations.txt
@@ -0,0 +1,7 @@
+INIT 0 255
+INIT 1 170
+AND 0 1 2
+OR 0 1 3
+NOT 0 4
+INVERT 1 5
+RETURN
\ No newline at end of file
diff --git a/examples/loop_example.txt b/examples/loop_example.txt
new file mode 100644
index 0000000..8a3d002
--- /dev/null
+++ b/examples/loop_example.txt
@@ -0,0 +1,7 @@
+INIT 0 0
+INIT 1 1
+INIT 2 10
+LOOP 2 4
+ADD 0 1 0
+INCREASE 1
+RETURN
\ No newline at end of file
diff --git a/examples/mating_sequence.txt b/examples/mating_sequence.txt
new file mode 100644
index 0000000..0eb30b8
--- /dev/null
+++ b/examples/mating_sequence.txt
@@ -0,0 +1,8 @@
+INIT 0 5
+INIT 1 0
+INIT 2 0
+MATE_REQUEST 0 1
+CONDITION 1 7
+INIT 3 2
+MATE_ACCEPT 3 2
+RETURN
\ No newline at end of file
diff --git a/examples/simple_math.txt b/examples/simple_math.txt
new file mode 100644
index 0000000..6bd7e19
--- /dev/null
+++ b/examples/simple_math.txt
@@ -0,0 +1,7 @@
+INIT 0 10
+INIT 1 20
+ADD 0 1 2
+MULTIPLY 2 0 3
+SUBTRACT 3 1 4
+DIVIDE 4 0 5
+RETURN
\ No newline at end of file
diff --git a/examples/survival_strategy.txt b/examples/survival_strategy.txt
new file mode 100644
index 0000000..59268dd
--- /dev/null
+++ b/examples/survival_strategy.txt
@@ -0,0 +1,16 @@
+INIT 0 0
+INIT 1 0
+INIT 2 1
+INIT 3 0
+INIT 4 0
+LOOP 10 20
+CONSUME_FOOD 0 1
+CONDITION 1 10
+INCREASE 0
+COPY 2 3
+ROCK_PAPER_SCISSORS 3 0 4
+CONDITION 4 15
+MATE_REQUEST 0 1
+RETURN
+MATE_ACCEPT 0 1
+RETURN
\ No newline at end of file
diff --git a/test-save-load/s0.txt b/test-save-load/s0.txt
new file mode 100644
index 0000000..1b587bc
--- /dev/null
+++ b/test-save-load/s0.txt
@@ -0,0 +1,6 @@
+NOP
+INIT 0 100
+INIT 1 50
+ADD 0 1 2
+COPY 2 3
+RETURN