├── LICENSE ├── README.md ├── Trees.txt ├── actors ├── Actor.gd ├── Actor.scn ├── Actors_Tileset.scn ├── Fox.scn ├── Pheromon.gd ├── Pheromon.scn ├── Pheromon_Fox.scn ├── Pheromon_Rabbit.scn ├── Rabbit.scn ├── Tileset.scn ├── Tree.gd └── Tree.scn ├── data_structures └── stree_controller.gd ├── engine.cfg ├── export.cfg ├── global.gd ├── icon.png ├── icon.png.flags ├── icons ├── fox.png ├── fox_pher.png ├── grass.png ├── rabbit.png ├── rabbit_pher.png └── tree.png ├── main.gd ├── main.scn └── simulations ├── Simulation_RabbitRace.gd ├── Simulation_RabbitRace.scn ├── actors_tileset.res └── tileset.res /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Henrique Lacreta Alves 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Genetic Programming Predator Prey Simulation 2 | A small and chaotic Genetic Programming simulation of a Predator Prey system, using Godot Engine. 3 | 4 | The idea of the initial simulation was to explore a simple Predator and Prey environment using pheromones as a "hunting" mechanic - blue icons are the Rabbits, Red icons are the foxes. 5 | 6 | ## How to play the simulation 7 | To play the simulation, you'll need the Godot engine (http://godotengine.org/). 8 | 9 | 1. Clone this repository. 10 | 2. Open the project with the Godot engine. 11 | 3. Play the project (F5). 12 | 13 | The simulation window should open then. To start the simulation, press the "Play" button under the "Simulation Speed" label, on the side menu. The simulation will happen on the screen to the right. 14 | 15 | The simulation characteristics (including Genetic settings, such as Mutation probability, and if either Foxes or Rabbits are going to evolve on the next generation) can be changed on-the-fly. To check the Genetic-Programming generated on each generation, press the "Save trees in txt" button, and check the generated "Trees.txt" file generated on the Project folder. 16 | 17 | ## About the "Rabbit Race" simulation 18 | 19 | In this initial simulation, the "objective" of the Rabbits is to reach the top of the screen (preferably without getting caught by the foxes), while the objective of the foxes is to hunt the biggest number of rabbits possible in the time limit (50 turns is the default). 20 | 21 | Usually, what it can be observed is that the rabbits stay as simpletons for a long time (because its very possible that at least one rabbit reach the top of the screen by simply moving forward, and therefore it is probably going to be the parent during the Fitness selection phase), while the foxes stay as simpletons during the first generations (usually under 50), until the mutation chance triggers a behavior that is responsible in "eating" a rabbit - then the foxes starts to get increasily complex, but under a very big variation (since this is a small and chaotic simulation). 22 | 23 | ## How the Genetic-Programming works 24 | 25 | This simulation uses a full-tree generation to produce the initial random children, and for each generation, a simple Fitness-proportional selection is used to choose the parents that are going to participate in the Crossover reproduction. 26 | 27 | The entire Genetic trees are just a String, in which the operations are performed using Godots GDScript. 28 | 29 | One should be able to easily change the simulation and Fitness method for other tree-based Genetic-Programming simulations on the "Simulation_RabbitRace.gd". All the Genetic Programming algorithm and Syntax tree is built on the "stree_controller.gd" script (Syntax Tree Controller, the singleton responsible in controlling all Syntax tree pools and Genetic operators). -------------------------------------------------------------------------------- /Trees.txt: -------------------------------------------------------------------------------- 1 | Fox ================================ 2 | 3 | (sniff_fox(progn(advance)(turn_left)(turn_left))(advance)) 4 | 5 | (sniff_fox(advance)(advance)) 6 | 7 | (sniff_fox(advance)(advance)) 8 | 9 | (sniff_fox(advance)(advance)) 10 | 11 | 12 | Rabbit: ============================= 13 | 14 | (progn(progn(turn_left)(progn(advance)))(advance)) 15 | 16 | (progn(progn(turn_left)(advance))(advance)) 17 | 18 | (progn(progn(turn_left)(advance))(progn(advance))) 19 | 20 | (progn(turn_left)(turn_right)) 21 | 22 | (progn(progn(turn_right)(turn_left))(turn_right)) 23 | 24 | (progn(turn_left)(progn(advance))) 25 | 26 | -------------------------------------------------------------------------------- /actors/Actor.gd: -------------------------------------------------------------------------------- 1 | extends Sprite 2 | 3 | export (String, "Rabbit", "Fox") var actor_type = "Fox" 4 | export (float) var pheromon_decay 5 | 6 | # ======= Constants ========= 7 | 8 | const DIRECTION = { 9 | "UP":0, 10 | "RIGHT":1, 11 | "DOWN":2, 12 | "LEFT":3, 13 | } 14 | 15 | const SNIFF = { 16 | "FOX":0, 17 | "RABBIT":1, 18 | } 19 | 20 | # =========== Variables ============== 21 | onready var alive = true 22 | onready var kill_count = 0 23 | onready var step_count = 0 24 | 25 | onready var _current_direction 26 | onready var _matrix 27 | onready var _alive = true 28 | onready var _alive_tick = 0 29 | onready var _global = get_node("/root/global") 30 | 31 | # =========== Actions ================== 32 | # Move actor in a direction 33 | func advance(): 34 | var vec 35 | if(_current_direction == DIRECTION["UP"]): 36 | vec = (get_pos()-Vector2(0,_global.TILE_SIZE)) 37 | elif(_current_direction == DIRECTION["RIGHT"]): 38 | vec = (get_pos()+Vector2(_global.TILE_SIZE,0)) 39 | elif(_current_direction == DIRECTION["DOWN"]): 40 | vec = (get_pos()+Vector2(0,_global.TILE_SIZE)) 41 | elif(_current_direction == DIRECTION["LEFT"]): 42 | vec = (get_pos()-Vector2(_global.TILE_SIZE,0)) 43 | 44 | if(!_check_actor_pos(vec, actor_type) and !_check_actor_pos(vec, "Tree") and _inbounds(vec)): 45 | set_pos(vec) 46 | step_count += 1 47 | 48 | if(actor_type == "Rabbit"): 49 | var ref = [] 50 | if(_check_actor_pos(get_pos(), "Fox", ref)): 51 | kill() 52 | ref[0].kill_count += 1 53 | elif(actor_type == "Fox"): 54 | var ref = [] 55 | if(_check_actor_pos(get_pos(), "Rabbit", ref)): 56 | ref[0].kill() 57 | kill_count += 1 58 | 59 | # Produce a pheromon node, belonging to this actor type 60 | func produce_pheromon(): 61 | var pheromon 62 | if(actor_type == "Fox"): 63 | pheromon = _global.Actors["Pheromon_Fox"].instance() 64 | else: 65 | pheromon = _global.Actors["Pheromon_Rabbit"].instance() 66 | get_parent().add_child(pheromon) 67 | pheromon.set_pos(get_pos()) 68 | pheromon.set_decay_time(pheromon_decay) 69 | 70 | # Check if it is following pheromon trail 71 | func sniff(pheromon): 72 | var vec 73 | if(_current_direction == DIRECTION["UP"]): 74 | vec = (get_pos()-Vector2(0,_global.TILE_SIZE)) 75 | elif(_current_direction == DIRECTION["RIGHT"]): 76 | vec = (get_pos()+Vector2(_global.TILE_SIZE,0)) 77 | elif(_current_direction == DIRECTION["DOWN"]): 78 | vec = (get_pos()+Vector2(0,_global.TILE_SIZE)) 79 | elif(_current_direction == DIRECTION["LEFT"]): 80 | vec = (get_pos()-Vector2(_global.TILE_SIZE,0)) 81 | 82 | var pheromon_front = _get_pheromon_pos(vec, pheromon) 83 | var pheromon_current = _get_pheromon_pos(get_pos(),pheromon) 84 | if pheromon_front > pheromon_current: 85 | return true 86 | return false 87 | 88 | # Set an specific direction 89 | func set_direction(direction): 90 | _current_direction = direction 91 | if _current_direction == DIRECTION["UP"]: 92 | set_rot(0) 93 | elif _current_direction == DIRECTION["RIGHT"]: 94 | set_rot(3*PI/2) 95 | elif _current_direction == DIRECTION["DOWN"]: 96 | set_rot(PI) 97 | elif _current_direction == DIRECTION["LEFT"]: 98 | set_rot(PI/2) 99 | 100 | # Rotate the Actor 101 | func turn_left(): 102 | set_direction((_current_direction+3)%4) 103 | 104 | func turn_right(): 105 | set_direction((_current_direction+1)%4) 106 | 107 | # Prey got killed 108 | func kill(): 109 | hide() 110 | alive = false 111 | 112 | # ============= Private functions ================ 113 | func _ready(): 114 | add_to_group(actor_type) 115 | if(_current_direction == null): 116 | set_direction(DIRECTION["UP"]) 117 | pass 118 | 119 | func _inbounds(pos): 120 | if pos.x >= 0 and pos.x <= _global.MAX_SIZE_X and pos.y >= 0 and pos.y <= _global.MAX_SIZE_Y: 121 | return true 122 | return false 123 | 124 | func _check_actor_pos(pos, type, ref = null): 125 | var group = get_tree().get_nodes_in_group(type) 126 | for actor in group: 127 | if actor.alive == true and actor.get_pos() == pos: 128 | if (ref != null): 129 | ref.push_back(actor) 130 | return true 131 | return false 132 | 133 | func _get_pheromon_pos(pos,pheromon): 134 | var ret = 0 135 | for p in get_tree().get_nodes_in_group(pheromon): 136 | if p.get_pos() == pos: 137 | ret = p._decay_time 138 | break 139 | return ret -------------------------------------------------------------------------------- /actors/Actor.scn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/actors/Actor.scn -------------------------------------------------------------------------------- /actors/Actors_Tileset.scn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/actors/Actors_Tileset.scn -------------------------------------------------------------------------------- /actors/Fox.scn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/actors/Fox.scn -------------------------------------------------------------------------------- /actors/Pheromon.gd: -------------------------------------------------------------------------------- 1 | extends Sprite 2 | 3 | export (String, "Rabbit", "Fox") var pheromon_type 4 | 5 | var _decay_time = 0 6 | 7 | func _ready(): 8 | add_to_group("Pheromon_" + pheromon_type) 9 | pass 10 | 11 | func set_decay_time(n): 12 | _decay_time = n 13 | 14 | func decay(): 15 | _decay_time -= 1 16 | if(_decay_time <= 0): 17 | queue_free() 18 | -------------------------------------------------------------------------------- /actors/Pheromon.scn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/actors/Pheromon.scn -------------------------------------------------------------------------------- /actors/Pheromon_Fox.scn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/actors/Pheromon_Fox.scn -------------------------------------------------------------------------------- /actors/Pheromon_Rabbit.scn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/actors/Pheromon_Rabbit.scn -------------------------------------------------------------------------------- /actors/Rabbit.scn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/actors/Rabbit.scn -------------------------------------------------------------------------------- /actors/Tileset.scn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/actors/Tileset.scn -------------------------------------------------------------------------------- /actors/Tree.gd: -------------------------------------------------------------------------------- 1 | 2 | extends Sprite 3 | 4 | onready var alive = true 5 | 6 | func _ready(): 7 | # Called every time the node is added to the scene. 8 | # Initialization here 9 | pass 10 | 11 | 12 | -------------------------------------------------------------------------------- /actors/Tree.scn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/actors/Tree.scn -------------------------------------------------------------------------------- /data_structures/stree_controller.gd: -------------------------------------------------------------------------------- 1 | # Made by HENRIQUE ALVES 2 | # A simple global node to control the creation of Syntax Trees (LISP-like). 3 | 4 | extends Node 5 | 6 | var mutation_probability = 0.1 7 | var _tree_pools = {} 8 | var _function_set = [] 9 | var _terminal_set = [] 10 | 11 | # _________________________________________________________________ 12 | # Public functions 13 | 14 | func reset(): 15 | _tree_pools.clear() 16 | _function_set.clear() 17 | _terminal_set.clear() 18 | 19 | func reset_pools(): 20 | for pool in _tree_pools: 21 | _tree_pools[pool].clear() 22 | print(_tree_pools) 23 | 24 | func add_function(function): 25 | var name = (function["name"]) 26 | var n_arguments = (function["n_arguments"]) 27 | var type_argument = Array(function["type_argument"]) 28 | var type_function = (function["type_function"]) 29 | _function_set.append({"name":name,"n_arguments":n_arguments,"type_argument":type_argument,"type_function":type_function}) 30 | 31 | func add_terminal(terminal): 32 | var value = terminal["value"] 33 | var type = terminal["type"] 34 | _terminal_set.append({"value":value,"type":type}) 35 | 36 | func add_pool(name): 37 | if not _tree_pools.has(name): 38 | _tree_pools[name] = [] 39 | else: 40 | _tree_pools[name].clear() 41 | 42 | func initialize_pool(p,population,max_depth): # Initialize trees with specified number of rabbit and fox 43 | if(not _tree_pools.has(p)): 44 | print("Error: No pool called ", p) 45 | return 46 | # Using full method of initializing the population: 47 | # random functions are added from the set, until max_depth 48 | # is reached - from where only terminals can be chosen 49 | 50 | print("\nINITIALIZING TREES\n") 51 | 52 | # Create the pool 53 | for n in range(0,population): 54 | _tree_pools[p].append(SyntaxTree.new()) 55 | 56 | # For each pool, set the algorithm 57 | for pool in _tree_pools[p]: 58 | _create_tree_from_node(pool,0,max_depth) 59 | 60 | func get_pool(pool): 61 | if _tree_pools.has(pool): 62 | return _tree_pools[pool] 63 | return null 64 | 65 | func crossover(pool, fitness): 66 | var new_pool = [] 67 | 68 | for i in range(0, _tree_pools[pool].size()): 69 | var individuals = _select(fitness) 70 | 71 | # Create new child: 72 | # Inherits from the first individual with a 73 | # random crossover with the second individual 74 | var new_tree = SyntaxTree.new() 75 | new_tree.set_string(_tree_pools[pool][individuals[0]].get_string()) 76 | 77 | # Get random node from the both trees 78 | # More chances to get a leaf from the first, 79 | # More chances to get a root from the second 80 | var random_node0 = randi()%(new_tree.get_number_nodes()) 81 | var random_node1 = randi()%(_tree_pools[pool][individuals[1]].get_number_nodes()) 82 | # Get that node branch string 83 | var random_node1_string = _tree_pools[pool][individuals[1]].get_branch_string(random_node1) 84 | # Exchange branches 85 | new_tree.replace_branch(random_node0, random_node1_string) 86 | 87 | new_pool.append(new_tree) 88 | 89 | _tree_pools[pool] = new_pool 90 | 91 | pass 92 | 93 | func crossover_pair(pool, fitness): 94 | var new_pool = [] 95 | 96 | for i in range(0, ceil(float(_tree_pools[pool].size())/2)): 97 | var individuals = _select(fitness) 98 | 99 | # Create new child: 100 | # Inherits from the first individual with a 101 | # random crossover with the second individual 102 | var new_tree0 = SyntaxTree.new() 103 | var new_tree1 = SyntaxTree.new() 104 | new_tree0.set_string(_tree_pools[pool][individuals[0]].get_string()) 105 | new_tree1.set_string(_tree_pools[pool][individuals[1]].get_string()) 106 | 107 | # Get random node from the both trees 108 | # More chances to get a leaf from the first, 109 | # More chances to get a root from the second 110 | var random_node0 = randi()%(new_tree0.get_number_nodes()) 111 | var random_node1 = randi()%(new_tree1.get_number_nodes()) 112 | # Get that node branch string 113 | var random_node0_string = new_tree0.get_branch_string(random_node0) 114 | var random_node1_string = new_tree1.get_branch_string(random_node1) 115 | # Exchange branches 116 | new_tree0.replace_branch(random_node0, random_node1_string) 117 | new_tree1.replace_branch(random_node1, random_node0_string) 118 | 119 | new_pool.append(new_tree0) 120 | new_pool.append(new_tree1) 121 | 122 | _tree_pools[pool] = new_pool 123 | 124 | pass 125 | 126 | func mutation(pool): 127 | for i in range(0, _tree_pools[pool].size()): 128 | # Test mutation probability 129 | var rng = randf() 130 | if rng > mutation_probability: 131 | continue 132 | # Choose random node from the first half ones 133 | var random_node = randi()%int(ceil(float(_tree_pools[pool][i].get_number_nodes())/2)) 134 | # Create random branch 135 | var random_tree = SyntaxTree.new() 136 | _create_tree_from_node(random_tree, 0, 0) 137 | _tree_pools[pool][i].replace_branch(random_node,random_tree.get_branch_string(0)) 138 | pass 139 | 140 | # _________________________________________________________________ 141 | # Private functions 142 | 143 | func _init(): 144 | randomize() 145 | pass 146 | 147 | # Select an integer within a range, according to a simple exponential law 148 | func _random_exp_int(max_range): 149 | var prob = [1] 150 | for i in range(1, max_range): 151 | prob.append(prob[i-1]/2) 152 | var r = randf() 153 | var selected = 0 154 | for i in range(0, prob.size()-1): 155 | if r < prob[i+1]: 156 | selected+=1 157 | else: 158 | break 159 | return selected 160 | 161 | func _random_invexp_int(max_range): 162 | var prob = [1] 163 | for i in range(1, max_range): 164 | prob.push_front(prob[0]/2) 165 | var r = randf() 166 | var selected = 0 167 | for i in range(0, prob.size()-1): 168 | if r > prob[i]: 169 | selected+=1 170 | else: 171 | break 172 | return selected 173 | 174 | # Create a tree from a starting node 175 | func _create_tree_from_node(pool,node_number,max_depth): 176 | # Add random root 177 | var random_f = (_function_set[randi()%_function_set.size()]) 178 | pool.add_node(random_f["name"],node_number) 179 | 180 | # Full-filling algorithm 181 | var buffer = [] 182 | buffer.append(random_f) 183 | var depth = 0 184 | var buffer_size = 1 185 | var node = node_number 186 | 187 | # Filling functions 188 | while(depth < max_depth): 189 | for i in range(0,buffer_size): 190 | var repeat = 1 191 | if(buffer[0]["n_arguments"] == 0): 192 | repeat += randi()%3 193 | while(repeat > 0): 194 | for type in buffer[0]["type_argument"]: 195 | # Create candidates and choose one 196 | var candidates = [] 197 | for candidate in _function_set: 198 | if candidate["type_function"].match(type): 199 | candidates.append(candidate) 200 | random_f = candidates[randi()%candidates.size()] 201 | buffer.push_back(random_f) 202 | pool.add_node(random_f["name"],node) 203 | repeat -= 1 204 | node += 1 205 | buffer.remove(0) 206 | depth += 1 207 | buffer_size = buffer.size() 208 | 209 | # Filling terminals 210 | for i in range(0,buffer_size): 211 | var repeat = 1 212 | if(buffer[i]["n_arguments"] == 0): 213 | repeat += randi()%3 214 | while(repeat > 0): 215 | for type in buffer[i]["type_argument"]: 216 | # Create candidates and choose one 217 | var candidates = [] 218 | for candidate in _terminal_set: 219 | if candidate["type"].match(type): 220 | candidates.append(candidate) 221 | random_f = candidates[randi()%candidates.size()] 222 | pool.add_node(random_f["value"],node) 223 | repeat -= 1 224 | node += 1 225 | print(pool.get_string()) 226 | 227 | # Fitness proportional selection 228 | # Returns a vector with the chosen two individuals 229 | func _select(fit): 230 | 231 | var fitness = Array(fit) 232 | 233 | var total_fitness = 0 234 | var individuals = [] 235 | # Calculate total fitness and selection probability array 236 | for f in fitness: 237 | total_fitness += f 238 | var selection_array = [fitness[0]/total_fitness] 239 | for i in range(1, fitness.size()): 240 | selection_array.append((fitness[i]/total_fitness)+selection_array[i-1]) 241 | # Select two individuals 242 | 243 | var r1 = randf() 244 | var r2 = randf() 245 | 246 | # First individual 247 | var individual = 0 248 | for i in range(0, fitness.size()-1): 249 | if selection_array[i] < r1: 250 | individual += 1 251 | else: 252 | break 253 | individuals.append(individual) 254 | 255 | # Second individual; should not be the same 256 | var same = true 257 | while(same): 258 | same = false 259 | var individual = 0 260 | for i in range(0, fitness.size()-1): 261 | if selection_array[i] < r2: 262 | individual += 1 263 | else: 264 | break 265 | if (individual == individuals[0]): 266 | same = true 267 | r2 = randf() 268 | else: 269 | individuals.append(individual) 270 | 271 | return individuals 272 | 273 | # ================================================================= 274 | # ======================= Tree subclass =========================== 275 | # ================================================================= 276 | # e.g. (RootFunction(terminal1)(Function(terminal2))) 277 | class SyntaxTree: 278 | var _tree_string = "" 279 | var _iterator = [1] 280 | 281 | func get_iterate(): 282 | return get_node_string_pos(_iterator[0]) 283 | 284 | func get_iterate_children(): 285 | return _get_node_children(_iterator[0]) 286 | 287 | func iterate(): 288 | _iterator.remove(0) 289 | if _iterator.empty(): # Reset to root 290 | _iterator.push_back(1) 291 | 292 | func iterate_children(n): 293 | var children = _get_node_children(_iterator[0]) 294 | _iterator.remove(0) 295 | _iterator.insert(0,children[n]) 296 | 297 | func iterate_anchor_children(n): 298 | var children = _get_node_children(_iterator[0]) 299 | _iterator.remove(0) 300 | for n in range(0,children.size()): 301 | _iterator.insert(n,children[n]) 302 | 303 | func set_string(s): 304 | _tree_string = String(s) 305 | 306 | func get_string(): 307 | return _tree_string 308 | 309 | func get_number_nodes(): 310 | var pos = 0 311 | var n = 0 312 | pos = _tree_string.findn("(",pos) 313 | while(pos != -1): 314 | n+=1 315 | pos = _tree_string.findn("(",pos+1) 316 | return n 317 | 318 | func add_node(node, parent): 319 | if _tree_string.empty(): 320 | _tree_string = "(" + node + ")" 321 | return 322 | 323 | var pos_parent = _get_node_pos(parent) 324 | 325 | var child_pos = 0 326 | if (_is_terminal(pos_parent) == 1): 327 | child_pos = _tree_string.findn(")", pos_parent) 328 | _tree_string = _tree_string.left(child_pos) + "(" + node + ")" + _tree_string.right(child_pos) 329 | else: 330 | child_pos = _tree_string.findn("(", pos_parent) 331 | while(_tree_string[child_pos] != ")"): 332 | child_pos += 1 333 | child_pos = _jump_branch(child_pos) 334 | _tree_string = _tree_string.left(child_pos) + "(" + node + ")" + _tree_string.right(child_pos) 335 | return 336 | 337 | func get_node_string(n): 338 | var pos = _get_node_pos(n) 339 | var l_pos = pos 340 | while(_tree_string[l_pos] != "(" and _tree_string[l_pos] != ")"): 341 | l_pos+=1 342 | return _tree_string.substr(pos,l_pos-pos) 343 | 344 | func get_node_string_pos(pos): 345 | var l_pos = pos 346 | while(_tree_string[l_pos] != "(" and _tree_string[l_pos] != ")"): 347 | l_pos+=1 348 | return _tree_string.substr(pos,l_pos-pos) 349 | 350 | func get_branch_string(n): 351 | var pos = _get_node_pos(n) 352 | var end_pos = _jump_branch(pos) - 1 353 | return _tree_string.substr(pos,end_pos-pos) 354 | 355 | func replace_branch(n, new_branch): 356 | var pos = _get_node_pos(n) 357 | var end_pos = _jump_branch(pos) - 1 358 | _tree_string = _tree_string.left(pos) + _tree_string.right(end_pos) 359 | _tree_string = _tree_string.insert(pos,new_branch) 360 | 361 | # Breadth first search position 362 | func _get_node_pos(n): 363 | if(_tree_string.empty()): 364 | print("Error: Tree is empty.") 365 | return -1 366 | 367 | if(n == 0): 368 | return 1 369 | 370 | var buffer = [1] 371 | var depth = 0 372 | var layer_size = 1 373 | 374 | var total = 0 375 | 376 | while(n > total + buffer.size()-1): 377 | 378 | for i in range(0,layer_size): 379 | for c in _get_node_children(buffer[0]): 380 | buffer.append(c) 381 | buffer.remove(0) 382 | total += layer_size 383 | layer_size = buffer.size() 384 | return buffer[n - total] 385 | 386 | func _is_terminal(pos): 387 | # print(_tree_string[pos]) 388 | while(_tree_string[pos] != ")" and _tree_string[pos] != "("): 389 | pos+=1 390 | if(_tree_string[pos] == ")"): 391 | return 1 # Terminal 392 | else: 393 | return 0 # Function 394 | 395 | func _get_node_children(pos): 396 | var children = [] 397 | if(_is_terminal(pos) == 1): 398 | return children 399 | pos = _tree_string.findn("(",pos) 400 | while(_tree_string[pos] != ")"): 401 | pos += 1 402 | children.append(pos) 403 | pos = _jump_branch(pos) 404 | return children 405 | 406 | func _jump_branch(pos): 407 | if(_is_terminal(pos) == 1): 408 | return _tree_string.findn(")", pos) + 1 409 | pos = _tree_string.findn("(", pos) 410 | var numb = 1 411 | while(numb >= 0): 412 | pos += 1 413 | if(_tree_string[pos] == "("): 414 | numb += 1 415 | elif(_tree_string[pos] == ")"): 416 | numb -= 1 417 | return pos+1 -------------------------------------------------------------------------------- /engine.cfg: -------------------------------------------------------------------------------- 1 | [application] 2 | 3 | name="GeneticProgrammingSimulation" 4 | main_scene="res://main.scn" 5 | icon="res://icon.png" 6 | 7 | [autoload] 8 | 9 | stree_controller="*res://data_structures/stree_controller.gd" 10 | global="*res://global.gd" 11 | 12 | [display] 13 | 14 | driver="GLES2" 15 | 16 | [render] 17 | 18 | thread_model=2 19 | -------------------------------------------------------------------------------- /export.cfg: -------------------------------------------------------------------------------- 1 | [convert_images] 2 | 3 | action="none" 4 | compress_quality=0.7 5 | formats="png" 6 | shrink=1.0 7 | 8 | [convert_samples] 9 | 10 | action="none" 11 | max_hz=44100 12 | trim=false 13 | 14 | [convert_scenes] 15 | 16 | convert_text_scenes=true 17 | 18 | [export_filter] 19 | 20 | filter="" 21 | filter_exclude="" 22 | type="resources" 23 | 24 | [platform:Android] 25 | 26 | apk_expansion/SALT="" 27 | apk_expansion/enable=false 28 | apk_expansion/public_key="" 29 | architecture/arm=true 30 | architecture/x86=false 31 | command_line/extra_args="" 32 | custom_package/debug="" 33 | custom_package/release="" 34 | keystore/release="" 35 | keystore/release_password="" 36 | keystore/release_user="" 37 | one_click_deploy/clear_previous_install=true 38 | package/icon="" 39 | package/name="" 40 | package/signed=true 41 | package/unique_name="org.godotengine.$genname" 42 | permissions/access_checkin_properties=false 43 | permissions/access_coarse_location=false 44 | permissions/access_fine_location=false 45 | permissions/access_location_extra_commands=false 46 | permissions/access_mock_location=false 47 | permissions/access_network_state=false 48 | permissions/access_surface_flinger=false 49 | permissions/access_wifi_state=false 50 | permissions/account_manager=false 51 | permissions/add_voicemail=false 52 | permissions/authenticate_accounts=false 53 | permissions/battery_stats=false 54 | permissions/bind_accessibility_service=false 55 | permissions/bind_appwidget=false 56 | permissions/bind_device_admin=false 57 | permissions/bind_input_method=false 58 | permissions/bind_nfc_service=false 59 | permissions/bind_notification_listener_service=false 60 | permissions/bind_print_service=false 61 | permissions/bind_remoteviews=false 62 | permissions/bind_text_service=false 63 | permissions/bind_vpn_service=false 64 | permissions/bind_wallpaper=false 65 | permissions/bluetooth=false 66 | permissions/bluetooth_admin=false 67 | permissions/bluetooth_privileged=false 68 | permissions/brick=false 69 | permissions/broadcast_package_removed=false 70 | permissions/broadcast_sms=false 71 | permissions/broadcast_sticky=false 72 | permissions/broadcast_wap_push=false 73 | permissions/call_phone=false 74 | permissions/call_privileged=false 75 | permissions/camera=false 76 | permissions/capture_audio_output=false 77 | permissions/capture_secure_video_output=false 78 | permissions/capture_video_output=false 79 | permissions/change_component_enabled_state=false 80 | permissions/change_configuration=false 81 | permissions/change_network_state=false 82 | permissions/change_wifi_multicast_state=false 83 | permissions/change_wifi_state=false 84 | permissions/clear_app_cache=false 85 | permissions/clear_app_user_data=false 86 | permissions/control_location_updates=false 87 | permissions/delete_cache_files=false 88 | permissions/delete_packages=false 89 | permissions/device_power=false 90 | permissions/diagnostic=false 91 | permissions/disable_keyguard=false 92 | permissions/dump=false 93 | permissions/expand_status_bar=false 94 | permissions/factory_test=false 95 | permissions/flashlight=false 96 | permissions/force_back=false 97 | permissions/get_accounts=false 98 | permissions/get_package_size=false 99 | permissions/get_tasks=false 100 | permissions/get_top_activity_info=false 101 | permissions/global_search=false 102 | permissions/hardware_test=false 103 | permissions/inject_events=false 104 | permissions/install_location_provider=false 105 | permissions/install_packages=false 106 | permissions/install_shortcut=false 107 | permissions/internal_system_window=false 108 | permissions/internet=false 109 | permissions/kill_background_processes=false 110 | permissions/location_hardware=false 111 | permissions/manage_accounts=false 112 | permissions/manage_app_tokens=false 113 | permissions/manage_documents=false 114 | permissions/master_clear=false 115 | permissions/media_content_control=false 116 | permissions/modify_audio_settings=false 117 | permissions/modify_phone_state=false 118 | permissions/mount_format_filesystems=false 119 | permissions/mount_unmount_filesystems=false 120 | permissions/nfc=false 121 | permissions/persistent_activity=false 122 | permissions/process_outgoing_calls=false 123 | permissions/read_calendar=false 124 | permissions/read_call_log=false 125 | permissions/read_contacts=false 126 | permissions/read_external_storage=false 127 | permissions/read_frame_buffer=false 128 | permissions/read_history_bookmarks=false 129 | permissions/read_input_state=false 130 | permissions/read_logs=false 131 | permissions/read_phone_state=false 132 | permissions/read_profile=false 133 | permissions/read_sms=false 134 | permissions/read_social_stream=false 135 | permissions/read_sync_settings=false 136 | permissions/read_sync_stats=false 137 | permissions/read_user_dictionary=false 138 | permissions/reboot=false 139 | permissions/receive_boot_completed=false 140 | permissions/receive_mms=false 141 | permissions/receive_sms=false 142 | permissions/receive_wap_push=false 143 | permissions/record_audio=false 144 | permissions/reorder_tasks=false 145 | permissions/restart_packages=false 146 | permissions/send_respond_via_message=false 147 | permissions/send_sms=false 148 | permissions/set_activity_watcher=false 149 | permissions/set_alarm=false 150 | permissions/set_always_finish=false 151 | permissions/set_animation_scale=false 152 | permissions/set_debug_app=false 153 | permissions/set_orientation=false 154 | permissions/set_pointer_speed=false 155 | permissions/set_preferred_applications=false 156 | permissions/set_process_limit=false 157 | permissions/set_time=false 158 | permissions/set_time_zone=false 159 | permissions/set_wallpaper=false 160 | permissions/set_wallpaper_hints=false 161 | permissions/signal_persistent_processes=false 162 | permissions/status_bar=false 163 | permissions/subscribed_feeds_read=false 164 | permissions/subscribed_feeds_write=false 165 | permissions/system_alert_window=false 166 | permissions/transmit_ir=false 167 | permissions/uninstall_shortcut=false 168 | permissions/update_device_stats=false 169 | permissions/use_credentials=false 170 | permissions/use_sip=false 171 | permissions/vibrate=false 172 | permissions/wake_lock=false 173 | permissions/write_apn_settings=false 174 | permissions/write_calendar=false 175 | permissions/write_call_log=false 176 | permissions/write_contacts=false 177 | permissions/write_external_storage=false 178 | permissions/write_gservices=false 179 | permissions/write_history_bookmarks=false 180 | permissions/write_profile=false 181 | permissions/write_secure_settings=false 182 | permissions/write_settings=false 183 | permissions/write_sms=false 184 | permissions/write_social_stream=false 185 | permissions/write_sync_settings=false 186 | permissions/write_user_dictionary=false 187 | screen/immersive_mode=true 188 | screen/orientation=0 189 | screen/support_large=true 190 | screen/support_normal=true 191 | screen/support_small=true 192 | screen/support_xlarge=true 193 | screen/use_32_bits_view=true 194 | user_permissions/0="" 195 | user_permissions/1="" 196 | user_permissions/10="" 197 | user_permissions/11="" 198 | user_permissions/12="" 199 | user_permissions/13="" 200 | user_permissions/14="" 201 | user_permissions/15="" 202 | user_permissions/16="" 203 | user_permissions/17="" 204 | user_permissions/18="" 205 | user_permissions/19="" 206 | user_permissions/2="" 207 | user_permissions/3="" 208 | user_permissions/4="" 209 | user_permissions/5="" 210 | user_permissions/6="" 211 | user_permissions/7="" 212 | user_permissions/8="" 213 | user_permissions/9="" 214 | version/code=1 215 | version/name="1.0" 216 | 217 | [platform:BlackBerry 10] 218 | 219 | package/category="core.games" 220 | package/custom_template="" 221 | package/description="Game made with Godot Engine" 222 | package/icon="" 223 | package/name="" 224 | package/unique_name="com.godot.noname" 225 | release/author="Cert. Name" 226 | release/author_id="Cert. ID" 227 | version/code=1 228 | version/name="1.0" 229 | 230 | [platform:HTML5] 231 | 232 | browser/enable_run=false 233 | custom_package/debug="" 234 | custom_package/release="" 235 | html/controls_enabled=true 236 | html/font_family="arial,sans-serif" 237 | html/head_include="" 238 | html/style_include="" 239 | html/title="" 240 | options/memory_size=3 241 | 242 | [platform:Linux X11] 243 | 244 | binary/64_bits=true 245 | custom_binary/debug="" 246 | custom_binary/release="" 247 | resources/bundle_dependencies_(for_optical_disc)=false 248 | resources/pack_mode=1 249 | 250 | [platform:Mac OSX] 251 | 252 | application/64_bits=false 253 | application/copyright="" 254 | application/icon="" 255 | application/identifier="com.godot.macgame" 256 | application/info="This Game is Nice" 257 | application/name="" 258 | application/short_version="1.0" 259 | application/signature="godotmacgame" 260 | application/version="1.0" 261 | custom_package/debug="" 262 | custom_package/release="" 263 | display/high_res=false 264 | 265 | [platform:Windows Desktop] 266 | 267 | binary/64_bits=true 268 | custom_binary/debug="" 269 | custom_binary/release="" 270 | resources/bundle_dependencies_(for_optical_disc)=false 271 | resources/pack_mode=0 272 | 273 | [script] 274 | 275 | action="compile" 276 | encrypt_key="" 277 | -------------------------------------------------------------------------------- /global.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | const TILE_SIZE = 20 4 | const MAX_SIZE_X = 20*TILE_SIZE 5 | const MAX_SIZE_Y = 20*TILE_SIZE 6 | 7 | onready var Actors = { 8 | "Fox":load("actors/Fox.scn"), 9 | "Rabbit":load("actors/Rabbit.scn"), 10 | "Pheromon_Fox":load("actors/Pheromon_Fox.scn"), 11 | "Pheromon_Rabbit":load("actors/Pheromon_Rabbit.scn"), 12 | "Tree":load("actors/Tree.scn"), 13 | } 14 | 15 | func _ready(): 16 | # Create trees 17 | 18 | pass 19 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/icon.png -------------------------------------------------------------------------------- /icon.png.flags: -------------------------------------------------------------------------------- 1 | gen_mipmaps=false 2 | -------------------------------------------------------------------------------- /icons/fox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/icons/fox.png -------------------------------------------------------------------------------- /icons/fox_pher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/icons/fox_pher.png -------------------------------------------------------------------------------- /icons/grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/icons/grass.png -------------------------------------------------------------------------------- /icons/rabbit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/icons/rabbit.png -------------------------------------------------------------------------------- /icons/rabbit_pher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/icons/rabbit_pher.png -------------------------------------------------------------------------------- /icons/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/icons/tree.png -------------------------------------------------------------------------------- /main.gd: -------------------------------------------------------------------------------- 1 | 2 | extends Control 3 | 4 | # member variables here, example: 5 | # var a=2 6 | # var b="textvar" 7 | 8 | func _ready(): 9 | get_node("SimulationStage").connect("new_generation",self,"new_generation") 10 | pass 11 | 12 | func new_generation(n): 13 | get_node("Controller/Info/Generation_Number").set_text(str(n)) 14 | 15 | func _on_Stop_pressed(): 16 | get_node("SimulationStage").play_speed = 0 17 | 18 | 19 | func _on_Play_pressed(): 20 | get_node("SimulationStage").play_speed = 1 21 | 22 | 23 | func _on_Turbo_pressed(): 24 | get_node("SimulationStage").play_speed = 2 25 | 26 | 27 | func _on_Mutation_Probability_Slider_value_changed( value ): 28 | get_node("Controller/Genetic_Parameters/Label_Mutation_Probability_Value").set_text(str(value) + "%") 29 | get_node("/root/stree_controller").mutation_probability = value/100 30 | 31 | 32 | func _on_Turn_Limit_Slider_value_changed( value ): 33 | get_node("Controller/Genetic_Parameters/Label_Turn_Limit_Value").set_text(str(value)) 34 | get_node("SimulationStage").max_turns = value 35 | 36 | 37 | func _on_Evolve_Fox_toggled( pressed ): 38 | get_node("SimulationStage").evolve_fox = pressed 39 | 40 | 41 | func _on_Evolve_Rabbit_toggled( pressed ): 42 | get_node("SimulationStage").evolve_rabbit = pressed 43 | 44 | 45 | func _on_TreesToTxt_pressed(): 46 | var f = File.new() 47 | f.open("res://Trees.txt", File.WRITE) 48 | 49 | f.store_string("Fox ================================\n\n") 50 | 51 | for t in get_node("/root/stree_controller").get_pool("Fox"): 52 | f.store_string(t.get_string()) 53 | f.store_string("\n\n") 54 | 55 | f.store_string("\nRabbit: =============================\n\n") 56 | 57 | for t in get_node("/root/stree_controller").get_pool("Rabbit"): 58 | f.store_string(t.get_string()) 59 | f.store_string("\n\n") 60 | 61 | f.close() -------------------------------------------------------------------------------- /main.scn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/main.scn -------------------------------------------------------------------------------- /simulations/Simulation_RabbitRace.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | const PLAY = { 4 | "STOP":0, 5 | "SLOW":1, 6 | "FAST":2, 7 | } 8 | 9 | # Simulation properties 10 | export (int) var max_turns = 50 11 | export (int) var play_speed = PLAY["FAST"] 12 | 13 | onready var evolve_rabbit = true 14 | onready var evolve_fox = true 15 | 16 | # Frame Stuff 17 | const FRAME_UPDATE = 0.1 18 | onready var delay = 0.0 19 | 20 | # Genetic Programming nodes 21 | const STREE_FUNCTIONS = [ 22 | {"name":"sniff_rabbit", # Sniff function - if following pheromon gradient, first argument; else, second argument 23 | "n_arguments":2, 24 | "type_argument":["null","null"], 25 | "type_function":"null"}, 26 | {"name":"sniff_fox", # Same, but following the other pheromon 27 | "n_arguments":2, 28 | "type_argument":["null","null"], 29 | "type_function":"null"}, 30 | {"name":"progn", # Set an order of instructions, can take any number of arguments 31 | "n_arguments":0, 32 | "type_argument":["null"], 33 | "type_function":"null"}, 34 | ] 35 | 36 | const STREE_TERMINALS = [ 37 | {"value":"advance", # Move forward 38 | "type": "null"}, 39 | {"value":"turn_right", # Turn right 40 | "type": "null"}, 41 | {"value":"turn_left", # Turn left 42 | "type": "null"}, 43 | ] 44 | 45 | # Tree controller reference 46 | onready var stree = get_node("/root/stree_controller") 47 | onready var matrix = {} 48 | onready var turn = 0 49 | onready var generation = 0 50 | 51 | onready var global = get_node("/root/global") 52 | 53 | func _ready(): 54 | add_user_signal("new_generation") 55 | get_node("Actors").hide() 56 | set_process(true) 57 | 58 | # Add nodes to tree global controller 59 | stree.reset() 60 | for function in STREE_FUNCTIONS: 61 | stree.add_function(function) 62 | for terminal in STREE_TERMINALS: 63 | stree.add_terminal(terminal) 64 | 65 | # Create scene from tilemap 66 | add_actors_from_tilemap() 67 | 68 | # Add tree pools and initialize 69 | stree.add_pool("Rabbit") 70 | stree.add_pool("Fox") 71 | 72 | stree.initialize_pool("Rabbit", get_tree().get_nodes_in_group("Rabbit").size(),0) 73 | stree.initialize_pool("Fox", get_tree().get_nodes_in_group("Fox").size(),0) 74 | 75 | func _process(delta): 76 | # Controls update speed 77 | if(play_speed == PLAY.STOP): 78 | return 79 | var update = false 80 | if(play_speed == PLAY.SLOW): 81 | delay += delta 82 | if delay > FRAME_UPDATE: 83 | delay -= FRAME_UPDATE 84 | update = true 85 | else: 86 | update = true 87 | # Updates 88 | if(update): 89 | turn += 1 90 | 91 | var actors 92 | var pool 93 | # Update Rabbits 94 | actors = get_tree().get_nodes_in_group("Rabbit") 95 | pool = stree.get_pool("Rabbit") 96 | for i in range(0,actors.size()): 97 | if(actors[i].alive == false): 98 | continue 99 | actors[i].produce_pheromon() 100 | var progn = true 101 | while(progn == true): 102 | progn = false 103 | var command = pool[i].get_iterate() 104 | if(command == "sniff_rabbit"): 105 | var following = actors[i].sniff("Pheromon_Rabbit") 106 | if following: 107 | pool[i].iterate_children(0) 108 | else: 109 | pool[i].iterate_children(1) 110 | elif(command == "sniff_fox"): 111 | var following = actors[i].sniff("Pheromon_Fox") 112 | if following: 113 | pool[i].iterate_children(0) 114 | else: 115 | pool[i].iterate_children(1) 116 | elif(command == "advance"): 117 | actors[i].advance() 118 | pool[i].iterate() 119 | elif(command == "turn_right"): 120 | actors[i].turn_right() 121 | pool[i].iterate() 122 | elif(command == "turn_left"): 123 | actors[i].turn_left() 124 | pool[i].iterate() 125 | elif(command == "progn"): 126 | pool[i].iterate_anchor_children(0) 127 | progn = true 128 | # Update Foxes 129 | actors = get_tree().get_nodes_in_group("Fox") 130 | pool = stree.get_pool("Fox") 131 | for i in range(0,actors.size()): 132 | actors[i].produce_pheromon() 133 | var repeat = true 134 | while(repeat == true): 135 | repeat = false 136 | var command = pool[i].get_iterate() 137 | if(command == "sniff_rabbit"): 138 | var following = actors[i].sniff("Pheromon_Rabbit") 139 | if following: 140 | pool[i].iterate_children(0) 141 | else: 142 | pool[i].iterate_children(1) 143 | # repeat = true 144 | elif(command == "sniff_fox"): 145 | var following = actors[i].sniff("Pheromon_Fox") 146 | if following: 147 | pool[i].iterate_children(0) 148 | else: 149 | pool[i].iterate_children(1) 150 | # repeat = true 151 | elif(command == "advance"): 152 | actors[i].advance() 153 | pool[i].iterate() 154 | elif(command == "turn_right"): 155 | actors[i].turn_right() 156 | pool[i].iterate() 157 | elif(command == "turn_left"): 158 | actors[i].turn_left() 159 | pool[i].iterate() 160 | elif(command == "progn"): 161 | pool[i].iterate_anchor_children(0) 162 | repeat = true 163 | # Decay pheromones 164 | for p in get_tree().get_nodes_in_group("Pheromon_Rabbit"): 165 | p.decay() 166 | for p in get_tree().get_nodes_in_group("Pheromon_Fox"): 167 | p.decay() 168 | 169 | # End simulation after last turn 170 | if (turn >= max_turns): 171 | var fitness = calculate_fitness() 172 | if(evolve_rabbit): 173 | stree.crossover_pair("Rabbit", fitness[0]) 174 | stree.mutation("Rabbit") 175 | if(evolve_fox): 176 | stree.crossover_pair("Fox", fitness[1]) 177 | stree.mutation("Fox") 178 | 179 | delete_actors() 180 | add_actors_from_tilemap() 181 | turn = 0 182 | generation += 1 183 | emit_signal("new_generation", generation) 184 | 185 | # Calculate fitness of the Actors; return an array with the fitness for each 186 | # type of actor 187 | func calculate_fitness(): 188 | var rabbit_fitness = [] 189 | var fox_fitness = [] 190 | # Rabbit fitness 191 | var rabbits = get_tree().get_nodes_in_group("Rabbit") 192 | for i in range (0, rabbits.size()): 193 | var fitness = 10.0 194 | fitness += (global.MAX_SIZE_Y - rabbits[i].get_pos().y) 195 | fitness += rabbits[i].step_count * 2 196 | if(rabbits[i].alive == false): 197 | fitness /= 2 198 | rabbit_fitness.append(fitness) 199 | # Fox fitness 200 | var fox = get_tree().get_nodes_in_group("Fox") 201 | for i in range (0, fox.size()): 202 | var fitness = 10.0 203 | fitness += fox[i].kill_count * 300 204 | fitness += fox[i].step_count * 5 205 | fox_fitness.append(fitness) 206 | return [rabbit_fitness,fox_fitness] 207 | 208 | func delete_actors(): 209 | # Delete Actors 210 | var g = ["Rabbit","Fox","Pheromon_Rabbit","Pheromon_Fox","Tree"] 211 | for group in g: 212 | var nodes = get_tree().get_nodes_in_group(group) 213 | for node in nodes: 214 | node.queue_free() 215 | 216 | # Add actors according to the tilemap placement 217 | func add_actors_from_tilemap(): 218 | var tileset = get_node("Actors").get_tileset() 219 | for cell in get_node("Actors").get_used_cells(): 220 | var tile_name = tileset.tile_get_name(get_node("Actors").get_cell(cell.x,cell.y)) 221 | var new_child = global.Actors[tile_name].instance() 222 | new_child.set_global_pos(Vector2(cell.x*20+10,cell.y*20+10)) 223 | if(tile_name != "Tree"): 224 | var xflip = (get_node("Actors").is_cell_x_flipped(cell.x,cell.y)) 225 | var yflip = (get_node("Actors").is_cell_y_flipped(cell.x,cell.y)) 226 | var trans = (get_node("Actors").is_cell_transposed(cell.x,cell.y)) 227 | # Don't quite understand how these properties works, though- 228 | # what a "transposed" cell means? Therefore, I'm just using 229 | # them as "bits" to change the rotation. 230 | if(!xflip and !yflip and !trans): 231 | new_child.set_direction(0) 232 | elif(xflip and !yflip and trans): 233 | new_child.set_direction(1) 234 | elif(xflip and yflip and !trans): 235 | new_child.set_direction(2) 236 | elif(!xflip and yflip and trans): 237 | new_child.set_direction(3) 238 | 239 | add_child(new_child) -------------------------------------------------------------------------------- /simulations/Simulation_RabbitRace.scn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/simulations/Simulation_RabbitRace.scn -------------------------------------------------------------------------------- /simulations/actors_tileset.res: -------------------------------------------------------------------------------- 1 | RSRCTileSet#resource/name0/name 2 | 0/texture 0/tex_offset 0/material 0/region0/occluder_offset 0/occluder0/navigation_offset 0/navigation0/shape_offset 0/shapes1/name 3 | 1/texture 1/tex_offset 1/material 1/region1/occluder_offset 1/occluder1/navigation_offset 1/navigation1/shape_offset 1/shapes2/name 4 | 2/texture 2/tex_offset 2/material 2/region2/occluder_offset 2/occluder2/navigation_offset 2/navigation2/shape_offset 2/shapesscript/scriptTextureres://icons/fox.pngTextureres://icons/rabbit.pngTextureres://icons/tree.png%res://simulations/actors_tileset.resQTileSet!Fox 5 |   6 | A A 7 | A A  8 | 9 |  Rabbit  10 |   11 | A A 12 | A A 13 | Tree 14 |   15 | A A 16 | A A 17 | !RSRC -------------------------------------------------------------------------------- /simulations/tileset.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henriquelalves/Genetic-Programming-Predator-Prey-Simulation/220db17d5be3fe3323c17355e5b5b745a58d37e5/simulations/tileset.res --------------------------------------------------------------------------------