diff --git a/.gitignore b/.gitignore index 31ff65d..ff60cd4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,6 @@ Debug __pycache__ .vs -images x64 -simulations python_files/NN_visualizer.py python_files/natural_selection_(outdated).py diff --git a/Natural-Selection-Proj.exe b/Natural-Selection-Proj.exe new file mode 100644 index 0000000..6d850b7 Binary files /dev/null and b/Natural-Selection-Proj.exe differ diff --git a/README.md b/README.md index e6efd63..ec84d7a 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,9 @@ Prey are represented as green dots. The field of view of a prey, represented as The properties of prey are as follows: - A wide yet short field of view -- The split charge restores over time. Once it has reached a certain period, they split. -- When their energy drops to 0, they must rest and not move until they have reached a minimum level of energy. +- The split charge restores over time. Once it has reached a certain period, they split +- When their energy drops to 0, they must rest and not move until they have reached a minimum level of energy +- They can also move backwards ## Predators Predators are represented as red dots, and their field of view is shown in the next image. @@ -30,7 +31,8 @@ The properties of predators are as follows: - The ability to eat prey, recover energy, and fill their split charge - The split charge empties over time - When their energy drops to 0, they die -- After eating, they have a digestion period during which they cannot restore energy or split charge. This is to prevent massive and pointless over-reproduction of predators when hunting large prey colonies. +- After eating, they have a digestion period during which they cannot restore energy or split charge. This is to prevent massive and pointless over-reproduction of predators when hunting large prey colonies +- They can only move forward ## Neural network All entities possess an artificial neural network as their brain. The inputs to the network are the lidar rays of the field of view, and the outputs are the entity's speed and angular velocity. The neural network has weights (represented in orange if w > 0), biases (the neuron is filled if b > 0), and activation functions (tanh). @@ -79,3 +81,39 @@ Here are some observations made during the simulation: # Conclusion Survival in this environment is a daunting task, and in most simulations, the entities do not overcome the second crisis. The constant parameters such as energy depletion, maximum speed, and field of view range have been meticulously adjusted to enhance survival. However, after numerous simulations, the pattern described above remains consistent. The best-adapted individuals that can cope with the prevailing conditions tend to survive longer. > "It is not the strongest of the species that survives, nor the most intelligent that survives. It is the one that is the most adaptable to change." - Charles Darwin + +# How to run the simulator +The project has been done in 2 languages: C++ for running and building the simulation, and Python for visualizing it in a vide-like format. You can visualize an already run simulation, run a new simulation with fixed parameters, or adjust the parameters, build and run the simulation. + +## Visualize an already run simulation +### Requirements +Apart from Python 3, the following python libraries are required to be installed: +- numpy (1.19.5) +- pygame (2.1.2) +### Steps +1. Go to `python_files/` directory. +2. Run the `natural_selection_interface.py` program with python. +3. A pygame window, named "Simulation", should open. Here you can visualize the simulation. + +## Run a new simulation with fixed parameters +### Requirements +- The same as in "Visualize an already run simulation". +### Steps +1. Run `Natural-Selection-Proj.exe` to run a new simulation. This might take a few minutes, depending on the computer. +2. The simulation will be automatically saved in `simulations/` in 2 files: `simulation1.bin` and `neural_nets1.bin`. So open `python_files/natural_selection_interface.py` to edit the code and change the `file_num` to `"1"` in line 234. +3. Run `natural_selection_interface.py` again to visualize the new simulation. + +## Adjust the parameters, build and run a new simulation +### Requirements +- C++ installed, as well as the compiler. +- An IDE (such as Visual Studio) to edit the code, build and compile the project. +- The same as in "Visualize an already run simulation". +### Steps +1. Open the repository folder with the IDE and access the `cpp_files/`. +2. Edit the parameters you want. These are found in `entity.h`, `prey.h`, `predator.h`, `neural_network.h` and `main.cpp`. In the header files, the parameters are the ones that are `static` in the class declaration. +3. You can optionally change the `file_num` parameter in `main.cpp` and in `python_files/natural_selection_interface.py`, so that the simulation is saved in another file instead of replacing the old ones. You will also have to create a new `simulation.bin` and `neural_nets.bin` in the `simulations/` folder. For example, if `file_num = 2`, then name the files `simulation2.bin` and `neural_nets2.bin`. +4. Build and compile the project. Find the `Natural-Selection-Proj.exe` file (probably in `x64/Debug/`), copy it and paste it to the repository folder, replacing the old one. +5. Do the same as in "Run a new simulation with fixed parameters". + +# References +[Evolving AIs - Predator vs Prey, who will win?](https://www.youtube.com/watch?v=qwrp3lB-jkQ) \ No newline at end of file diff --git a/cpp_files/constants.h b/cpp_files/constants.h index c1a92c9..7115eff 100644 --- a/cpp_files/constants.h +++ b/cpp_files/constants.h @@ -3,9 +3,9 @@ using namespace std; -constexpr int MAP_W = 2500; -constexpr int MAP_H = 1800; -constexpr int MAX_PREYS = 900; -constexpr int MAX_PREDATORS = 500; -constexpr int STARTING_PREYS = 300; -constexpr int STARTING_PREDATORS = 250; \ No newline at end of file +constexpr int MAP_W = 1600; +constexpr int MAP_H = 1400; +constexpr int MAX_PREYS = 800; +constexpr int MAX_PREDATORS = 400; +constexpr int STARTING_PREYS = 150; +constexpr int STARTING_PREDATORS = 50; \ No newline at end of file diff --git a/cpp_files/functions.cpp b/cpp_files/functions.cpp index ef66432..7ee6ef9 100644 --- a/cpp_files/functions.cpp +++ b/cpp_files/functions.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include "constants.h" #include "functions.h" @@ -126,7 +125,6 @@ float ang(float x1, float y1, float x2, float y2) void activationFunction1(float &x) { - // x = 1/(1+pow(M_E,-x)); float a = 2.5; x = (pow(a, x) - pow(a, -x)) / (pow(a, x) + pow(a, -x)); } diff --git a/cpp_files/main.cpp b/cpp_files/main.cpp index fc6307a..8c2bfe7 100644 --- a/cpp_files/main.cpp +++ b/cpp_files/main.cpp @@ -22,7 +22,7 @@ int main() N = 30000; - file_num = "2"; + file_num = "1"; sim_file.open("simulations/simulation" + file_num + ".bin", ios::binary); brains_file.open("simulations/neural_nets" + file_num + ".bin", ios::binary); diff --git a/cpp_files/neural_network.cpp b/cpp_files/neural_network.cpp index 963f286..31255d7 100644 --- a/cpp_files/neural_network.cpp +++ b/cpp_files/neural_network.cpp @@ -39,18 +39,6 @@ NeuralNetwork::NeuralNetwork(int n_inputs, NeuralNetwork *parent) biases = parent->biases; } - /*for (l = 0; l < N_LAYERS; l++) - { - if (weights[l].size() == 0) - { - cout << "constructor: " << endl; - cout << this << ": " << l << " " << weights[l].size() << endl; - cout << N_LAYERS << endl; - cout << weights.size() << " " << weights[0].size() << " " << weights[1].size() << " " << weights[2].size() << endl; - cout << count << endl; - } - }*/ - history.push_back(this); count++; } @@ -86,19 +74,6 @@ vector NeuralNetwork::getEncodedInfo() unsigned __int32 val; vector encoded_info, encoded_w, encoded_b; - /*for (layer = 0; layer < N_LAYERS; layer++) - { - if (weights[layer].size() == 0) - { - cout << "getEncodedInfo: " << endl; - cout << this << ": " << layer << " " << weights[layer].size() << endl; - cout << N_LAYERS << endl; - cout << weights.size() << " " << weights[0].size() << " " << weights[1].size() << " " << weights[2].size() << endl; - cout << count << endl; - } - }*/ - - //cout << "0"; for (layer = 0; layer < N_LAYERS + 1; layer++) { val = 0; @@ -107,27 +82,9 @@ vector NeuralNetwork::getEncodedInfo() if (layer == 0) n_neurons = n_inputs; else n_neurons = N_NEURONS[layer - 1]; - //cout << "1"; + for (neuron = 0; neuron < n_neurons; neuron++) { - //cout << layer << " " << weights[layer].size() << " " << neuron << "|"; - /*if (weights[layer].size() == 0) - { - cout << this << ": " << n_neurons << " " << layer << " " << weights[layer].size() << " " << neuron << endl; - cout << weights.size() << " " << weights[0].size() << " " << weights[1].size() << " " << weights[2].size() << endl; - cout << count << endl; - - cout << history.size() << endl; - cout << history[count - 1] << endl; - if (find(history.begin(), history.end(), this) != history.end()) - { - cout << this << " is in the vector." << endl; - } - else - { - cout << this << " is not in the vector." << endl; - } - }*/ for (w = 0; w < weights[layer][neuron].size(); w++) { if (j == 32) @@ -146,7 +103,7 @@ vector NeuralNetwork::getEncodedInfo() j++; } } - //cout << "2"; + while (i < 4) { encoded_w.push_back(val); @@ -159,7 +116,7 @@ vector NeuralNetwork::getEncodedInfo() if (layer == N_LAYERS) n_neurons = 2; else n_neurons = N_NEURONS[layer]; - //cout << "3"; + for (b = 0; b < biases[layer].size(); b++) { if (biases[layer][b] != 0) @@ -169,15 +126,15 @@ vector NeuralNetwork::getEncodedInfo() j++; } - //cout << "4"; + encoded_b.push_back(val); } - //cout << "5"; + encoded_info.reserve(encoded_w.size() + encoded_b.size()); encoded_info.insert(encoded_info.end(), encoded_w.begin(), encoded_w.end()); encoded_info.insert(encoded_info.end(), encoded_b.begin(), encoded_b.end()); - //cout << "6"; + return encoded_info; } diff --git a/python_files/images/backward.png b/python_files/images/backward.png new file mode 100644 index 0000000..e03edec Binary files /dev/null and b/python_files/images/backward.png differ diff --git a/python_files/images/forward.png b/python_files/images/forward.png new file mode 100644 index 0000000..b58755c Binary files /dev/null and b/python_files/images/forward.png differ diff --git a/python_files/images/next.png b/python_files/images/next.png new file mode 100644 index 0000000..3202be9 Binary files /dev/null and b/python_files/images/next.png differ diff --git a/python_files/images/pause.png b/python_files/images/pause.png new file mode 100644 index 0000000..da2f3a4 Binary files /dev/null and b/python_files/images/pause.png differ diff --git a/python_files/images/play.png b/python_files/images/play.png new file mode 100644 index 0000000..d55f4fc Binary files /dev/null and b/python_files/images/play.png differ diff --git a/python_files/images/previous.png b/python_files/images/previous.png new file mode 100644 index 0000000..caea51d Binary files /dev/null and b/python_files/images/previous.png differ diff --git a/python_files/natural_selection_interface.py b/python_files/natural_selection_interface.py index 50099f3..99ea398 100644 --- a/python_files/natural_selection_interface.py +++ b/python_files/natural_selection_interface.py @@ -1,6 +1,6 @@ import numpy as np import pygame -from math import pi, sin, cos, sqrt, atan2 +from math import sqrt from abc import ABC, abstractmethod def sign(x): @@ -231,7 +231,7 @@ def update_screen(tick): draw_interface(tick) pygame.display.update() -file_num = "2" +file_num = "0" # read raw data with open("simulations/simulation" + file_num + ".bin", "rb") as sim_file: binary_data = sim_file.read() diff --git a/simulations/neural_nets0.bin b/simulations/neural_nets0.bin new file mode 100644 index 0000000..56d4e23 Binary files /dev/null and b/simulations/neural_nets0.bin differ diff --git a/simulations/neural_nets1.bin b/simulations/neural_nets1.bin new file mode 100644 index 0000000..c403a7b Binary files /dev/null and b/simulations/neural_nets1.bin differ diff --git a/simulations/simulation0.bin b/simulations/simulation0.bin new file mode 100644 index 0000000..7c30c08 Binary files /dev/null and b/simulations/simulation0.bin differ diff --git a/simulations/simulation1.bin b/simulations/simulation1.bin new file mode 100644 index 0000000..0523a16 Binary files /dev/null and b/simulations/simulation1.bin differ