1use crate::brain::Brain;
2use crate::config::Config;
3use crate::neuron::{NeuronID, Position};
4use crate::Scalar;
5use rand::{thread_rng, Rng};
6use serde::{Deserialize, Serialize};
7use std::f64::consts::PI;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct BrainBuilder {
11 config: Config,
12 neurons: usize,
13 connections: usize,
14 radius: Scalar,
15 min_neurogenesis_range: Scalar,
16 max_neurogenesis_range: Scalar,
17 sensors: usize,
18 effectors: usize,
19 no_loop_connections: bool,
20 max_connecting_tries: usize,
21}
22
23impl Default for BrainBuilder {
24 fn default() -> Self {
25 Self {
26 config: Default::default(),
27 neurons: 100,
28 connections: 0,
29 radius: 10.0,
30 min_neurogenesis_range: 0.1,
31 max_neurogenesis_range: 1.0,
32 sensors: 1,
33 effectors: 1,
34 no_loop_connections: true,
35 max_connecting_tries: 10,
36 }
37 }
38}
39
40impl BrainBuilder {
41 pub fn new() -> Self {
42 Self::default()
43 }
44
45 pub fn config(mut self, config: Config) -> Self {
46 self.config = config;
47 self
48 }
49
50 pub fn neurons(mut self, value: usize) -> Self {
51 self.neurons = value;
52 self
53 }
54
55 pub fn connections(mut self, value: usize) -> Self {
56 self.connections = value;
57 self
58 }
59
60 pub fn radius(mut self, value: Scalar) -> Self {
61 self.radius = value;
62 self
63 }
64
65 pub fn min_neurogenesis_range(mut self, value: Scalar) -> Self {
66 self.min_neurogenesis_range = value;
67 self
68 }
69
70 pub fn max_neurogenesis_range(mut self, value: Scalar) -> Self {
71 self.max_neurogenesis_range = value;
72 self
73 }
74
75 pub fn sensors(mut self, value: usize) -> Self {
76 self.sensors = value;
77 self
78 }
79
80 pub fn effectors(mut self, value: usize) -> Self {
81 self.effectors = value;
82 self
83 }
84
85 pub fn no_loop_connections(mut self, value: bool) -> Self {
86 self.no_loop_connections = value;
87 self
88 }
89
90 pub fn max_connecting_tries(mut self, value: usize) -> Self {
91 self.max_connecting_tries = value;
92 self
93 }
94
95 pub fn build(mut self) -> Brain {
96 let mut brain = Brain::new();
97 brain.set_config(self.config.clone());
98 let mut rng = thread_rng();
99
100 let mut neurons = vec![];
101 neurons.push(brain.create_neuron(Position {
102 x: 0.0,
103 y: 0.0,
104 z: 0.0,
105 }));
106 for _ in 0..self.neurons {
107 neurons.push(self.make_neighbor_neuron(&neurons, &mut brain, &mut rng));
108 }
109
110 let neuron_positions = neurons
111 .iter()
112 .map(|id| (*id, brain.neuron(*id).unwrap().position()))
113 .collect::<Vec<_>>();
114 for _ in 0..self.sensors {
115 let mut tries = self.max_connecting_tries + 1;
116 while tries > 0 && !self.make_peripheral_sensor(&neuron_positions, &mut brain, &mut rng)
117 {
118 tries -= 1;
119 }
120 }
121 for _ in 0..self.effectors {
122 let mut tries = self.max_connecting_tries + 1;
123 while tries > 0
124 && !self.make_peripheral_effector(&neuron_positions, &mut brain, &mut rng)
125 {
126 tries -= 1;
127 }
128 }
129 for _ in 0..self.connections {
130 let mut tries = self.max_connecting_tries + 1;
131 while tries > 0
132 && !self.connect_neighbor_neurons(&neuron_positions, &mut brain, &mut rng)
133 {
134 tries -= 1;
135 }
136 }
137 for id in brain.get_neurons() {
138 if !brain.does_neuron_has_connections(id) {
139 drop(brain.kill_neuron(id));
140 }
141 }
142
143 brain
144 }
145
146 fn make_peripheral_sensor<R>(
147 &self,
148 neuron_positions: &[(NeuronID, Position)],
149 brain: &mut Brain,
150 rng: &mut R,
151 ) -> bool
152 where
153 R: Rng,
154 {
155 let pos = self.make_new_peripheral_position(rng);
156 let index = neuron_positions
157 .iter()
158 .map(|(_, p)| p.distance_sqr(pos))
159 .enumerate()
160 .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
161 .unwrap()
162 .0;
163 brain.create_sensor(neuron_positions[index].0).is_ok()
164 }
165
166 fn make_peripheral_effector<R>(
167 &self,
168 neuron_positions: &[(NeuronID, Position)],
169 brain: &mut Brain,
170 rng: &mut R,
171 ) -> bool
172 where
173 R: Rng,
174 {
175 let pos = self.make_new_peripheral_position(rng);
176 let index = neuron_positions
177 .iter()
178 .map(|(_, p)| p.distance_sqr(pos))
179 .enumerate()
180 .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
181 .unwrap()
182 .0;
183 brain.create_effector(neuron_positions[index].0).is_ok()
184 }
185
186 fn make_neighbor_neuron<R>(
187 &mut self,
188 neurons: &[NeuronID],
189 brain: &mut Brain,
190 rng: &mut R,
191 ) -> NeuronID
192 where
193 R: Rng,
194 {
195 let distance = rng.gen_range(self.min_neurogenesis_range, self.max_neurogenesis_range);
196 let origin = neurons[rng.gen_range(0, neurons.len()) % neurons.len()];
197 let origin_pos = brain.neuron(origin).unwrap().position();
198 let new_position = self.make_new_position(origin_pos, distance, rng);
199 brain.create_neuron(new_position)
200 }
201
202 fn connect_neighbor_neurons<R>(
203 &mut self,
204 neuron_positions: &[(NeuronID, Position)],
205 brain: &mut Brain,
206 rng: &mut R,
207 ) -> bool
208 where
209 R: Rng,
210 {
211 let origin =
212 neuron_positions[rng.gen_range(0, neuron_positions.len()) % neuron_positions.len()];
213 let filtered = neuron_positions
214 .iter()
215 .filter_map(|(id, p)| {
216 if p.distance(origin.1) <= self.max_neurogenesis_range {
217 Some(id)
218 } else {
219 None
220 }
221 })
222 .collect::<Vec<_>>();
223 let target = *filtered[rng.gen_range(0, filtered.len()) % filtered.len()];
224 origin.0 != target
225 && (!self.no_loop_connections
226 || (!brain.are_neurons_connected(origin.0, target)
227 && !brain.are_neurons_connected(target, origin.0)))
228 && brain.bind_neurons(origin.0, target).is_ok()
229 }
230
231 fn make_new_position<R>(&self, pos: Position, scale: Scalar, rng: &mut R) -> Position
232 where
233 R: Rng,
234 {
235 let phi = rng.gen_range(0.0, PI * 2.0);
236 let theta = rng.gen_range(-PI, PI);
237 let pos = Position {
238 x: pos.x + theta.cos() * phi.cos() * scale,
239 y: pos.y + theta.cos() * phi.sin() * scale,
240 z: pos.z + theta.sin() * scale,
241 };
242 let magnitude = pos.magnitude();
243 if magnitude > self.radius {
244 Position {
245 x: self.radius * pos.x / magnitude,
246 y: self.radius * pos.y / magnitude,
247 z: self.radius * pos.z / magnitude,
248 }
249 } else {
250 pos
251 }
252 }
253
254 fn make_new_peripheral_position<R>(&self, rng: &mut R) -> Position
255 where
256 R: Rng,
257 {
258 let phi = rng.gen_range(0.0, PI * 2.0);
259 let theta = rng.gen_range(-PI, PI);
260 Position {
261 x: theta.cos() * phi.cos() * self.radius,
262 y: theta.cos() * phi.sin() * self.radius,
263 z: theta.sin() * self.radius,
264 }
265 }
266}