From f0fdaa7af93d0a43031c372de9527a11a5989d3b Mon Sep 17 00:00:00 2001 From: vandomej Date: Fri, 8 Mar 2024 15:46:26 -0800 Subject: [PATCH 01/26] Undoing LISP spikes --- carp_spike/.gitignore | 1 - carp_spike/main.carp | 10 ---------- sbcl_spike/.gitignore | 0 sbcl_spike/main.lisp | 12 ------------ 4 files changed, 23 deletions(-) delete mode 100644 carp_spike/.gitignore delete mode 100644 carp_spike/main.carp delete mode 100644 sbcl_spike/.gitignore delete mode 100644 sbcl_spike/main.lisp diff --git a/carp_spike/.gitignore b/carp_spike/.gitignore deleted file mode 100644 index 466e248..0000000 --- a/carp_spike/.gitignore +++ /dev/null @@ -1 +0,0 @@ -out/ \ No newline at end of file diff --git a/carp_spike/main.carp b/carp_spike/main.carp deleted file mode 100644 index 344241e..0000000 --- a/carp_spike/main.carp +++ /dev/null @@ -1,10 +0,0 @@ -(use Random) -(Project.config "title" "gemla") - -(deftype SimulationNode [population-size Int, population-cutoff Int]) - -;; (let [test (SimulationNode.init 10 3)] -;; (do -;; (SimulationNode.set-population-size test 20) -;; (SimulationNode.population-size &test) -;; )) \ No newline at end of file diff --git a/sbcl_spike/.gitignore b/sbcl_spike/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/sbcl_spike/main.lisp b/sbcl_spike/main.lisp deleted file mode 100644 index cf6e008..0000000 --- a/sbcl_spike/main.lisp +++ /dev/null @@ -1,12 +0,0 @@ -;; Define a type that contains a population size and a population cutoff -(defclass simulation-node () ((population-size :initarg :population-size :accessor population-size) - (population-cutoff :initarg :population-cutoff :accessor population-cutoff) - (population :initform () :accessor population))) - -;; Define a method that initializes population-size number of children in a population each with a random value -(defmethod initialize-instance :after ((node simulation-node) &key) - (setf (population node) (make-list (population-size node) :initial-element (random 100)))) - -(let ((node (make-instance 'simulation-node :population-size 100 :population-cutoff 10))) - (print (population-size node)) - (population node)) \ No newline at end of file From 371e78fe4c80dbdf24904112f293cdde90ae5e28 Mon Sep 17 00:00:00 2001 From: vandomej Date: Fri, 8 Mar 2024 17:06:54 -0800 Subject: [PATCH 02/26] Update dependencies and remove unused code --- gemla/Cargo.toml | 20 +++++++++--------- gemla/cli.yml | 9 -------- gemla/src/bin/bin.rs | 41 ++++++++++++++++++------------------- gemla/src/constants/args.rs | 2 -- gemla/src/constants/mod.rs | 1 - gemla/src/core/mod.rs | 2 +- gemla/src/lib.rs | 1 - 7 files changed, 31 insertions(+), 45 deletions(-) delete mode 100644 gemla/cli.yml delete mode 100644 gemla/src/constants/args.rs delete mode 100644 gemla/src/constants/mod.rs diff --git a/gemla/Cargo.toml b/gemla/Cargo.toml index 77c42e1..4fc28e1 100644 --- a/gemla/Cargo.toml +++ b/gemla/Cargo.toml @@ -15,18 +15,18 @@ categories = ["simulation"] [dependencies] serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -uuid = { version = "0.8", features = ["serde", "v4"] } -clap = { version = "~2.27.0", features = ["yaml"] } -toml = "0.5.8" +uuid = { version = "1.7", features = ["serde", "v4"] } +clap = { version = "4.5.2", features = ["derive"] } +toml = "0.8.10" regex = "1" file_linked = { version = "0.1.0", path = "../file_linked" } thiserror = "1.0" anyhow = "1.0" -rand = "0.8.4" -log = "0.4.14" -env_logger = "0.9.0" -futures = "0.3.17" -smol = "1.2.5" +rand = "0.8.5" +log = "0.4.21" +env_logger = "0.11.3" +futures = "0.3.30" +smol = "2.0.0" smol-potat = "1.1.2" -num_cpus = "1.13.0" -easy-parallel = "3.1.0" \ No newline at end of file +num_cpus = "1.16.0" +easy-parallel = "3.3.1" diff --git a/gemla/cli.yml b/gemla/cli.yml deleted file mode 100644 index bb8a0cc..0000000 --- a/gemla/cli.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: GEMLA -version: "0.1" -autor: Jacob VanDomelen -about: Uses a genetic algorithm to generate a machine learning algorithm. -args: - - FILE: - help: Sets the input/output file for the program. - required: true - index: 1 \ No newline at end of file diff --git a/gemla/src/bin/bin.rs b/gemla/src/bin/bin.rs index 69bfdac..c12cd33 100644 --- a/gemla/src/bin/bin.rs +++ b/gemla/src/bin/bin.rs @@ -1,4 +1,3 @@ -#[macro_use] extern crate clap; extern crate gemla; #[macro_use] @@ -6,17 +5,23 @@ extern crate log; mod test_state; -use anyhow::anyhow; -use clap::App; use easy_parallel::Parallel; use gemla::{ - constants::args::FILE, core::{Gemla, GemlaConfig}, error::{log_error, Error}, }; use smol::{channel, channel::RecvError, future, Executor}; use std::{path::PathBuf, time::Instant}; use test_state::TestState; +use clap::Parser; + +#[derive(Parser)] +#[command(version, about, long_about = None)] +struct Args { + /// The file to read/write the dataset from/to. + #[arg(short, long)] + file: String, +} /// Runs a simluation of a genetic algorithm against a dataset. /// @@ -42,27 +47,21 @@ fn main() -> anyhow::Result<()> { smol::block_on(async { drop(signal); - // Command line arguments are parsed with the clap crate. And this program uses - // the yaml method with clap. - let yaml = load_yaml!("../../cli.yml"); - let matches = App::from_yaml(yaml).get_matches(); + // Command line arguments are parsed with the clap crate. + let args = Args::parse(); // Checking that the first argument is a valid file - if let Some(file_path) = matches.value_of(FILE) { - let mut gemla = log_error(Gemla::::new( - &PathBuf::from(file_path), - GemlaConfig { - generations_per_node: 3, - overwrite: true, - }, - ))?; + let mut gemla = log_error(Gemla::::new( + &PathBuf::from(args.file), + GemlaConfig { + generations_per_node: 3, + overwrite: true, + }, + ))?; - log_error(gemla.simulate(3).await)?; + log_error(gemla.simulate(3).await)?; - Ok(()) - } else { - Err(Error::Other(anyhow!("Invalid argument for FILE"))) - } + Ok(()) }) }); diff --git a/gemla/src/constants/args.rs b/gemla/src/constants/args.rs deleted file mode 100644 index d833fd1..0000000 --- a/gemla/src/constants/args.rs +++ /dev/null @@ -1,2 +0,0 @@ -/// Corresponds to the FILE command line argument used in accordance with the clap crate. -pub const FILE: &str = "FILE"; diff --git a/gemla/src/constants/mod.rs b/gemla/src/constants/mod.rs deleted file mode 100644 index 6e10f4a..0000000 --- a/gemla/src/constants/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod args; diff --git a/gemla/src/core/mod.rs b/gemla/src/core/mod.rs index 7564423..4a75b6c 100644 --- a/gemla/src/core/mod.rs +++ b/gemla/src/core/mod.rs @@ -20,7 +20,7 @@ type SimulationTree = Box>>; /// Provides configuration options for managing a [`Gemla`] object as it executes. /// /// # Examples -/// ``` +/// ```rust,ignore /// #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] /// struct TestState { /// pub score: f64, diff --git a/gemla/src/lib.rs b/gemla/src/lib.rs index 77d1371..d69f277 100644 --- a/gemla/src/lib.rs +++ b/gemla/src/lib.rs @@ -1,5 +1,4 @@ #[macro_use] pub mod tree; -pub mod constants; pub mod core; pub mod error; From c44c389fbe90b87938289304a4ba65cd17997310 Mon Sep 17 00:00:00 2001 From: vandomej Date: Fri, 8 Mar 2024 17:48:58 -0800 Subject: [PATCH 03/26] Add data_format module and enum --- file_linked/Cargo.toml | 3 +- file_linked/src/constants/data_format.rs | 5 ++ file_linked/src/constants/mod.rs | 1 + file_linked/src/lib.rs | 80 ++++++++++++++++-------- gemla/src/bin/bin.rs | 2 + gemla/src/core/mod.rs | 18 +++--- 6 files changed, 74 insertions(+), 35 deletions(-) create mode 100644 file_linked/src/constants/data_format.rs create mode 100644 file_linked/src/constants/mod.rs diff --git a/file_linked/Cargo.toml b/file_linked/Cargo.toml index abf3367..3a4e5b6 100644 --- a/file_linked/Cargo.toml +++ b/file_linked/Cargo.toml @@ -19,4 +19,5 @@ serde = { version = "1.0", features = ["derive"] } thiserror = "1.0" anyhow = "1.0" bincode = "1.3.3" -log = "0.4.14" \ No newline at end of file +log = "0.4.14" +serde_json = "1.0.114" diff --git a/file_linked/src/constants/data_format.rs b/file_linked/src/constants/data_format.rs new file mode 100644 index 0000000..a50f00e --- /dev/null +++ b/file_linked/src/constants/data_format.rs @@ -0,0 +1,5 @@ +#[derive(Debug)] +pub enum DataFormat { + Bincode, + Json +} \ No newline at end of file diff --git a/file_linked/src/constants/mod.rs b/file_linked/src/constants/mod.rs new file mode 100644 index 0000000..e01d262 --- /dev/null +++ b/file_linked/src/constants/mod.rs @@ -0,0 +1 @@ +pub mod data_format; \ No newline at end of file diff --git a/file_linked/src/lib.rs b/file_linked/src/lib.rs index 5b73c1d..de7e075 100644 --- a/file_linked/src/lib.rs +++ b/file_linked/src/lib.rs @@ -1,8 +1,10 @@ //! A wrapper around an object that ties it to a physical file pub mod error; +pub mod constants; use anyhow::{anyhow, Context}; +use constants::data_format::DataFormat; use error::Error; use log::info; use serde::{de::DeserializeOwned, Serialize}; @@ -14,6 +16,8 @@ use std::{ thread::JoinHandle, }; + + /// A wrapper around an object `T` that ties the object to a physical file #[derive(Debug)] pub struct FileLinked @@ -24,6 +28,7 @@ where path: PathBuf, temp_file_path: PathBuf, file_thread: Option>, + data_format: DataFormat, } impl Drop for FileLinked @@ -48,6 +53,7 @@ where /// # Examples /// ``` /// # use file_linked::*; + /// # use file_linked::constants::data_format::DataFormat; /// # use serde::{Deserialize, Serialize}; /// # use std::fmt; /// # use std::string::ToString; @@ -67,7 +73,7 @@ where /// c: 3.0 /// }; /// - /// let linked_test = FileLinked::new(test, &PathBuf::from("./temp")) + /// let linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Json) /// .expect("Unable to create file linked object"); /// /// assert_eq!(linked_test.readonly().a, 1); @@ -88,6 +94,7 @@ where /// # Examples /// ``` /// # use file_linked::*; + /// # use file_linked::constants::data_format::DataFormat; /// # use serde::{Deserialize, Serialize}; /// # use std::fmt; /// # use std::string::ToString; @@ -107,7 +114,7 @@ where /// c: 3.0 /// }; /// - /// let linked_test = FileLinked::new(test, &PathBuf::from("./temp")) + /// let linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Json) /// .expect("Unable to create file linked object"); /// /// assert_eq!(linked_test.readonly().a, 1); @@ -119,7 +126,7 @@ where /// # std::fs::remove_file("./temp").expect("Unable to remove file"); /// # } /// ``` - pub fn new(val: T, path: &Path) -> Result, Error> { + pub fn new(val: T, path: &Path, data_format: DataFormat) -> Result, Error> { let mut temp_file_path = path.to_path_buf(); temp_file_path.set_file_name(format!( ".temp{}", @@ -134,6 +141,7 @@ where path: path.to_path_buf(), temp_file_path, file_thread: None, + data_format }; result.write_data()?; @@ -143,8 +151,12 @@ where fn write_data(&mut self) -> Result<(), Error> { let thread_path = self.path.clone(); let thread_temp_path = self.temp_file_path.clone(); - let thread_val = bincode::serialize(&self.val) - .with_context(|| "Unable to serialize object into bincode".to_string())?; + let thread_val = match self.data_format { + DataFormat::Bincode => bincode::serialize(&self.val) + .with_context(|| "Unable to serialize object into bincode".to_string())?, + DataFormat::Json => serde_json::to_vec(&self.val) + .with_context(|| "Unable to serialize object into JSON".to_string())?, + }; if let Some(file_thread) = self.file_thread.take() { file_thread @@ -190,6 +202,7 @@ where /// ``` /// # use file_linked::*; /// # use file_linked::error::Error; + /// # use file_linked::constants::data_format::DataFormat; /// # use serde::{Deserialize, Serialize}; /// # use std::fmt; /// # use std::string::ToString; @@ -209,7 +222,7 @@ where /// c: 0.0 /// }; /// - /// let mut linked_test = FileLinked::new(test, &PathBuf::from("./temp")) + /// let mut linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Bincode) /// .expect("Unable to create file linked object"); /// /// assert_eq!(linked_test.readonly().a, 1); @@ -239,6 +252,7 @@ where /// ``` /// # use file_linked::*; /// # use file_linked::error::Error; + /// # use file_linked::constants::data_format::DataFormat; /// # use serde::{Deserialize, Serialize}; /// # use std::fmt; /// # use std::string::ToString; @@ -258,7 +272,7 @@ where /// c: 0.0 /// }; /// - /// let mut linked_test = FileLinked::new(test, &PathBuf::from("./temp")) + /// let mut linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Bincode) /// .expect("Unable to create file linked object"); /// /// assert_eq!(linked_test.readonly().a, 1); @@ -295,6 +309,7 @@ where /// ``` /// # use file_linked::*; /// # use file_linked::error::Error; + /// # use file_linked::constants::data_format::DataFormat; /// # use serde::{Deserialize, Serialize}; /// # use std::fmt; /// # use std::string::ToString; @@ -327,7 +342,7 @@ where /// /// bincode::serialize_into(file, &test).expect("Unable to serialize object"); /// - /// let mut linked_test = FileLinked::::from_file(&path) + /// let mut linked_test = FileLinked::::from_file(&path, DataFormat::Bincode) /// .expect("Unable to create file linked object"); /// /// assert_eq!(linked_test.readonly().a, test.a); @@ -341,7 +356,7 @@ where /// # Ok(()) /// # } /// ``` - pub fn from_file(path: &Path) -> Result, Error> { + pub fn from_file(path: &Path, data_format: DataFormat) -> Result, Error> { let mut temp_file_path = path.to_path_buf(); temp_file_path.set_file_name(format!( ".temp{}", @@ -352,15 +367,21 @@ where )); match File::open(path).map_err(Error::from).and_then(|file| { - bincode::deserialize_from::(file) - .with_context(|| format!("Unable to deserialize file {}", path.display())) - .map_err(Error::from) + match data_format { + DataFormat::Bincode => bincode::deserialize_from::(file) + .with_context(|| format!("Unable to deserialize file {}", path.display())) + .map_err(Error::from), + DataFormat::Json => serde_json::from_reader(file) + .with_context(|| format!("Unable to deserialize file {}", path.display())) + .map_err(Error::from), + } }) { Ok(val) => Ok(FileLinked { val, path: path.to_path_buf(), temp_file_path, file_thread: None, + data_format, }), Err(err) => { info!( @@ -370,7 +391,7 @@ where ); // Try to use temp file instead and see if that file exists and is serializable - let val = FileLinked::from_temp_file(&temp_file_path, path) + let val = FileLinked::from_temp_file(&temp_file_path, path, &data_format) .map_err(|_| err) .with_context(|| format!("Failed to read/deserialize the object from the file {} and temp file {}", path.display(), temp_file_path.display()))?; @@ -379,21 +400,30 @@ where path: path.to_path_buf(), temp_file_path, file_thread: None, + data_format, }) } } } - fn from_temp_file(temp_file_path: &Path, path: &Path) -> Result { + fn from_temp_file(temp_file_path: &Path, path: &Path, data_format: &DataFormat) -> Result { let file = File::open(temp_file_path) .with_context(|| format!("Unable to open file {}", temp_file_path.display()))?; - let val = bincode::deserialize_from(file).with_context(|| { - format!( - "Could not deserialize from temp file {}", - temp_file_path.display() - ) - })?; + let val = match data_format { + DataFormat::Bincode => bincode::deserialize_from(file).with_context(|| { + format!( + "Could not deserialize from temp file {}", + temp_file_path.display() + ) + })?, + DataFormat::Json => serde_json::from_reader(file).with_context(|| { + format!( + "Could not deserialize from temp file {}", + temp_file_path.display() + ) + })?, + }; info!("Successfully deserialized value from temp file"); @@ -441,7 +471,7 @@ mod tests { cleanup.run(|p| { let val = vec!["one", "two", ""]; - let linked_object = FileLinked::new(val.clone(), &p)?; + let linked_object = FileLinked::new(val.clone(), &p, DataFormat::Json)?; assert_eq!(*linked_object.readonly(), val); Ok(()) @@ -455,7 +485,7 @@ mod tests { cleanup.run(|p| { let val = "test"; - FileLinked::new(val, &p)?; + FileLinked::new(val, &p, DataFormat::Bincode)?; let file = File::open(&p)?; let result: String = @@ -472,7 +502,7 @@ mod tests { let cleanup = CleanUp::new(&path); cleanup.run(|p| { let list = vec![1, 2, 3, 4]; - let mut file_linked_list = FileLinked::new(list, &p)?; + let mut file_linked_list = FileLinked::new(list, &p, DataFormat::Json)?; assert_eq!(*file_linked_list.readonly(), vec![1, 2, 3, 4]); file_linked_list.mutate(|v1| v1.push(5))?; @@ -493,7 +523,7 @@ mod tests { cleanup.run(|p| { let val1 = String::from("val1"); let val2 = String::from("val2"); - let mut file_linked_list = FileLinked::new(val1.clone(), &p)?; + let mut file_linked_list = FileLinked::new(val1.clone(), &p, DataFormat::Bincode)?; assert_eq!(*file_linked_list.readonly(), val1); file_linked_list.replace(val2.clone())?; @@ -515,7 +545,7 @@ mod tests { bincode::serialize_into(&file, &value).expect("Unable to serialize into file"); drop(file); - let linked_object: FileLinked> = FileLinked::from_file(&p)?; + let linked_object: FileLinked> = FileLinked::from_file(&p, DataFormat::Bincode)?; assert_eq!(*linked_object.readonly(), value); drop(linked_object); diff --git a/gemla/src/bin/bin.rs b/gemla/src/bin/bin.rs index c12cd33..2618232 100644 --- a/gemla/src/bin/bin.rs +++ b/gemla/src/bin/bin.rs @@ -6,6 +6,7 @@ extern crate log; mod test_state; use easy_parallel::Parallel; +use file_linked::constants::data_format::DataFormat; use gemla::{ core::{Gemla, GemlaConfig}, error::{log_error, Error}, @@ -57,6 +58,7 @@ fn main() -> anyhow::Result<()> { generations_per_node: 3, overwrite: true, }, + DataFormat::Json, ))?; log_error(gemla.simulate(3).await)?; diff --git a/gemla/src/core/mod.rs b/gemla/src/core/mod.rs index 4a75b6c..64f006f 100644 --- a/gemla/src/core/mod.rs +++ b/gemla/src/core/mod.rs @@ -4,7 +4,7 @@ pub mod genetic_node; use crate::{error::Error, tree::Tree}; -use file_linked::FileLinked; +use file_linked::{constants::data_format::DataFormat, FileLinked}; use futures::{future, future::BoxFuture}; use genetic_node::{GeneticNode, GeneticNodeWrapper, GeneticState}; use log::{info, trace, warn}; @@ -77,21 +77,21 @@ impl<'a, T: 'a> Gemla<'a, T> where T: GeneticNode + Serialize + DeserializeOwned + Debug + Clone + Send, { - pub fn new(path: &Path, config: GemlaConfig) -> Result { + pub fn new(path: &Path, config: GemlaConfig, data_format: DataFormat) -> Result { match File::open(path) { // If the file exists we either want to overwrite the file or read from the file // based on the configuration provided Ok(_) => Ok(Gemla { data: if config.overwrite { - FileLinked::new((None, config), path)? + FileLinked::new((None, config), path, data_format)? } else { - FileLinked::from_file(path)? + FileLinked::from_file(path, data_format)? }, threads: HashMap::new(), }), // If the file doesn't exist we must create it Err(error) if error.kind() == ErrorKind::NotFound => Ok(Gemla { - data: FileLinked::new((None, config), path)?, + data: FileLinked::new((None, config), path, data_format)?, threads: HashMap::new(), }), Err(error) => Err(Error::IO(error)), @@ -400,7 +400,7 @@ mod tests { generations_per_node: 1, overwrite: true }; - let mut gemla = Gemla::::new(&p, config)?; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; smol::block_on(gemla.simulate(2))?; assert_eq!(gemla.data.readonly().0.as_ref().unwrap().height(), 2); @@ -409,7 +409,7 @@ mod tests { assert!(path.exists()); // Testing overwriting data - let mut gemla = Gemla::::new(&p, config)?; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; smol::block_on(gemla.simulate(2))?; assert_eq!(gemla.data.readonly().0.as_ref().unwrap().height(), 2); @@ -419,7 +419,7 @@ mod tests { // Testing not-overwriting data config.overwrite = false; - let mut gemla = Gemla::::new(&p, config)?; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; smol::block_on(gemla.simulate(2))?; assert_eq!(gemla.tree_ref().unwrap().height(), 4); @@ -440,7 +440,7 @@ mod tests { generations_per_node: 10, overwrite: true }; - let mut gemla = Gemla::::new(&p, config)?; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; smol::block_on(gemla.simulate(5))?; let tree = gemla.tree_ref().unwrap(); From d21d0fcd3a5494aaf8a754a97ea1d257c6701e66 Mon Sep 17 00:00:00 2001 From: vandomej Date: Sun, 10 Mar 2024 17:41:28 -0700 Subject: [PATCH 04/26] Adding basic fighter implementation for testing --- gemla/Cargo.toml | 1 + gemla/build.rs | 11 ++ gemla/config.json | 3 + gemla/nodes.toml | 15 --- gemla/src/bin/bin.rs | 5 +- gemla/src/bin/fighter_nn/mod.rs | 230 ++++++++++++++++++++++++++++++++ 6 files changed, 248 insertions(+), 17 deletions(-) create mode 100644 gemla/build.rs create mode 100644 gemla/config.json delete mode 100644 gemla/nodes.toml create mode 100644 gemla/src/bin/fighter_nn/mod.rs diff --git a/gemla/Cargo.toml b/gemla/Cargo.toml index 4fc28e1..6119210 100644 --- a/gemla/Cargo.toml +++ b/gemla/Cargo.toml @@ -30,3 +30,4 @@ smol = "2.0.0" smol-potat = "1.1.2" num_cpus = "1.16.0" easy-parallel = "3.3.1" +fann = "0.1.8" diff --git a/gemla/build.rs b/gemla/build.rs new file mode 100644 index 0000000..e6b8ca6 --- /dev/null +++ b/gemla/build.rs @@ -0,0 +1,11 @@ +fn main() { + // Replace this with the path to the directory containing `fann.lib` + let lib_dir = "F://vandomej/Downloads/vcpkg/packages/fann_x64-windows/lib"; + + println!("cargo:rustc-link-search=native={}", lib_dir); + println!("cargo:rustc-link-lib=static=fann"); + // Use `dylib=fann` instead of `static=fann` if you're linking dynamically + + // If there are any additional directories where the compiler can find header files, you can specify them like this: + // println!("cargo:include={}", path_to_include_directory); +} diff --git a/gemla/config.json b/gemla/config.json new file mode 100644 index 0000000..6c1c941 --- /dev/null +++ b/gemla/config.json @@ -0,0 +1,3 @@ +{ + "base_dir": "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations" +} \ No newline at end of file diff --git a/gemla/nodes.toml b/gemla/nodes.toml deleted file mode 100644 index 976061d..0000000 --- a/gemla/nodes.toml +++ /dev/null @@ -1,15 +0,0 @@ -[[nodes]] -fabric_addr = "10.0.0.1:9999" -bridge_bind = "10.0.0.1:8888" -mem = "100 GiB" -cpu = 8 - -# [[nodes]] -# fabric_addr = "10.0.0.2:9999" -# mem = "100 GiB" -# cpu = 16 - -# [[nodes]] -# fabric_addr = "10.0.0.3:9999" -# mem = "100 GiB" -# cpu = 16 \ No newline at end of file diff --git a/gemla/src/bin/bin.rs b/gemla/src/bin/bin.rs index 2618232..1455d7c 100644 --- a/gemla/src/bin/bin.rs +++ b/gemla/src/bin/bin.rs @@ -4,6 +4,7 @@ extern crate gemla; extern crate log; mod test_state; +mod fighter_nn; use easy_parallel::Parallel; use file_linked::constants::data_format::DataFormat; @@ -13,7 +14,7 @@ use gemla::{ }; use smol::{channel, channel::RecvError, future, Executor}; use std::{path::PathBuf, time::Instant}; -use test_state::TestState; +use fighter_nn::FighterNN; use clap::Parser; #[derive(Parser)] @@ -52,7 +53,7 @@ fn main() -> anyhow::Result<()> { let args = Args::parse(); // Checking that the first argument is a valid file - let mut gemla = log_error(Gemla::::new( + let mut gemla = log_error(Gemla::::new( &PathBuf::from(args.file), GemlaConfig { generations_per_node: 3, diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs new file mode 100644 index 0000000..4633ffb --- /dev/null +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -0,0 +1,230 @@ +extern crate fann; + +use std::{fs::{self, File}, path::PathBuf}; +use fann::{ActivationFunc, Fann}; +use gemla::{core::genetic_node::GeneticNode, error::Error}; +use rand::prelude::*; +use serde::{Deserialize, Serialize}; +use serde_json; +use anyhow::Context; +use std::collections::HashMap; + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct Config { + base_dir: String, +} + +// Here is the folder structure for the FighterNN: +// base_dir/fighter_nn_{fighter_id}/{generation}/{fighter_id}_fighter_nn_{nn_id}.net + +// A neural network that utilizes the fann library to save and read nn's from files +// FighterNN contains a list of file locations for the nn's stored, all of which are stored under the same folder which is also contained. +// there is no training happening to the neural networks +// the neural networks are only used to simulate the nn's and to save and read the nn's from files +// Filenames are stored in the format of "{fighter_id}_fighter_nn_{generation}.net". +// The folder name is stored in the format of "fighter_nn_xxxxxx" where xxxxxx is an incrementing number, checking for the highest number and incrementing it by 1 +// The main folder contains a subfolder for each generation, containing a population of 10 nn's + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct FighterNN { + pub id: u64, + pub folder: PathBuf, + pub generation: u64, + // A map of each nn identifier in a generation and their physics score + pub scores: Vec>, +} + +impl GeneticNode for FighterNN { + // Check for the highest number of the folder name and increment it by 1 + fn initialize() -> Result, Error> { + // Load the configuration + let config: Config = serde_json::from_reader(File::open("config.json")?) + .with_context(|| format!("Failed to read config"))?; + + let base_path = PathBuf::from(config.base_dir); + + // Ensure the base directory exists, create it if not + if !base_path.exists() { + fs::create_dir_all(&base_path)?; + } + + let mut highest = 0; + let mut folder = base_path.join(format!("fighter_nn_{:06}", highest)); + while folder.exists() { + highest += 1; + folder = base_path.join(format!("fighter_nn_{:06}", highest)); + } + + fs::create_dir(&folder)?; + + //Create a new directory for the first generation + let gen_folder = folder.join("0"); + fs::create_dir(&gen_folder)?; + + // Create the first generation in this folder + for i in 0..10 { + // Filenames are stored in the format of "xxxxxx_fighter_nn_0.net", "xxxxxx_fighter_nn_1.net", etc. Where xxxxxx is the folder name + let nn = gen_folder.join(format!("{:06}_fighter_nn_{}.net", highest, i)); + let mut fann = Fann::new(&[10, 10, 10]) + .with_context(|| format!("Failed to create nn"))?; + fann.set_activation_func_hidden(ActivationFunc::SigmoidSymmetric); + fann.set_activation_func_output(ActivationFunc::SigmoidSymmetric); + fann.save(&nn) + .with_context(|| format!("Failed to save nn"))?; + } + + Ok(Box::new(FighterNN { + id: highest, + folder, + generation: 0, + scores: vec![HashMap::new()], + })) + } + + fn simulate(&mut self) -> Result<(), Error> { + // For each nn in the current generation: + for i in 0..10 { + // load the nn + let nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, i)); + let fann = Fann::from_file(&nn) + .with_context(|| format!("Failed to load nn"))?; + + // Simulate the nn against the random nn + let mut score = 0.0; + + // Using the same original nn, repeat the simulation with 5 random nn's from the current generation + for _ in 0..5 { + let random_nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, thread_rng().gen_range(0..10))); + let random_fann = Fann::from_file(&random_nn) + .with_context(|| format!("Failed to load random nn"))?; + + let inputs: Vec = (0..10).map(|_| thread_rng().gen_range(-1.0..1.0)).collect(); + let outputs = fann.run(&inputs) + .with_context(|| format!("Failed to run nn"))?; + let random_outputs = random_fann.run(&inputs) + .with_context(|| format!("Failed to run random nn"))?; + + // Average the difference between the outputs of the nn and random_nn and add the result to score + let mut round_score = 0.0; + for (o, r) in outputs.iter().zip(random_outputs.iter()) { + round_score += o - r; + } + score += round_score / fann.get_num_output() as f32; + + } + + score /= 5.0; + self.scores[self.generation as usize].insert(i, score); + } + + Ok(()) + } + + + fn mutate(&mut self) -> Result<(), Error> { + // Create the new generation folder + let new_gen_folder = self.folder.join(format!("{}", self.generation + 1)); + fs::create_dir(&new_gen_folder)?; + + // Remove the 5 nn's with the lowest scores + let mut sorted_scores: Vec<_> = self.scores[self.generation as usize].iter().collect(); + sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); + let to_keep = sorted_scores[5..].iter().map(|(k, _)| *k).collect::>(); + + // Save the remaining 5 nn's to the new generation folder + for i in 0..5 { + let nn_id = to_keep[i]; + let nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, nn_id)); + let new_nn = new_gen_folder.join(format!("{:06}_fighter_nn_{}.net", self.id, i)); + fs::copy(&nn, &new_nn)?; + } + + // Take the remaining 5 nn's and create 5 new nn's by the following: + for i in 0..5 { + let nn_id = to_keep[i]; + let nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, nn_id)); + let mut fann = Fann::from_file(&nn) + .with_context(|| format!("Failed to load nn"))?; + + // For each weight in the 5 new nn's there is a 20% chance of a minor mutation (a random number between -0.1 and 0.1 is added to the weight) + // And a 5% chance of a major mutation (a random number between -0.3 and 0.3 is added to the weight) + let mut connections = fann.get_connections(); // Vector of connections + for c in &mut connections { + if thread_rng().gen_range(0..100) < 20 { + c.weight += thread_rng().gen_range(-0.1..0.1); + } else if thread_rng().gen_range(0..100) < 5 { + c.weight += thread_rng().gen_range(-0.3..0.3); + } + } + fann.set_connections(&connections); + + // Save the new nn's to the new generation folder + let new_nn = new_gen_folder.join(format!("{:06}_fighter_nn_{}.net", self.id, i + 5)); + fann.save(&new_nn) + .with_context(|| format!("Failed to save nn"))?; + } + + self.generation += 1; + self.scores.push(HashMap::new()); + + Ok(()) + } + + fn merge(left: &FighterNN, right: &FighterNN) -> Result, Error> { + // Find next highest + // Load the configuration + let config: Config = serde_json::from_reader(File::open("config.json")?) + .with_context(|| format!("Failed to read config"))?; + + let base_path = PathBuf::from(config.base_dir); + + // Ensure the base directory exists, create it if not + if !base_path.exists() { + fs::create_dir_all(&base_path)?; + } + + let mut highest = 0; + let mut folder = base_path.join(format!("fighter_nn_{:06}", highest)); + while folder.exists() { + highest += 1; + folder = base_path.join(format!("fighter_nn_{:06}", highest)); + } + + fs::create_dir(&folder)?; + + //Create a new directory for the first generation + let gen_folder = folder.join("0"); + fs::create_dir(&gen_folder)?; + + // Take the 5 nn's with the highest scores from the left nn's and save them to the new fighter folder + let mut sorted_scores: Vec<_> = left.scores[left.generation as usize].iter().collect(); + sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); + let mut remaining = sorted_scores[5..].iter().map(|(k, _)| *k).collect::>(); + for i in 0..5 { + let nn = left.folder.join(format!("{}", left.generation)).join(format!("{:06}_fighter_nn_{}.net", left.id, remaining.pop().unwrap())); + let new_nn = folder.join(format!("0")).join(format!("{:06}_fighter_nn_{}.net", highest, i)); + trace!("From: {:?}, To: {:?}", &nn, &new_nn); + fs::copy(&nn, &new_nn) + .with_context(|| format!("Failed to copy left nn"))?; + } + + // Take the 5 nn's with the highest scores from the right nn's and save them to the new fighter folder + sorted_scores = right.scores[right.generation as usize].iter().collect(); + sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); + remaining = sorted_scores[5..].iter().map(|(k, _)| *k).collect::>(); + for i in 5..10 { + let nn = right.folder.join(format!("{}", right.generation)).join(format!("{:06}_fighter_nn_{}.net", right.id, remaining.pop().unwrap())); + let new_nn = folder.join(format!("0")).join(format!("{:06}_fighter_nn_{}.net", highest, i)); + trace!("From: {:?}, To: {:?}", &nn, &new_nn); + fs::copy(&nn, &new_nn) + .with_context(|| format!("Failed to copy right nn"))?; + } + + Ok(Box::new(FighterNN { + id: highest, + folder, + generation: 0, + scores: vec![HashMap::new()], + })) + } +} From 774a0df5d70d29aac86fad4e8dfcfe2422620e2c Mon Sep 17 00:00:00 2001 From: vandomej Date: Mon, 11 Mar 2024 00:28:54 -0700 Subject: [PATCH 05/26] Added constants to drive logic --- gemla/config.json | 3 -- gemla/src/bin/fighter_nn/mod.rs | 66 +++++++++++++-------------------- 2 files changed, 25 insertions(+), 44 deletions(-) delete mode 100644 gemla/config.json diff --git a/gemla/config.json b/gemla/config.json deleted file mode 100644 index 6c1c941..0000000 --- a/gemla/config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "base_dir": "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations" -} \ No newline at end of file diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index 4633ffb..c63116c 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -1,18 +1,18 @@ extern crate fann; -use std::{fs::{self, File}, path::PathBuf}; +use std::{fs, path::PathBuf}; use fann::{ActivationFunc, Fann}; use gemla::{core::genetic_node::GeneticNode, error::Error}; use rand::prelude::*; use serde::{Deserialize, Serialize}; -use serde_json; use anyhow::Context; use std::collections::HashMap; -#[derive(Serialize, Deserialize, Debug, Clone)] -struct Config { - base_dir: String, -} +const BASE_DIR: &str = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations"; +const POPULATION: usize = 100; +const NEURAL_NETWORK_SHAPE: &[u32; 3] = &[10, 10, 10]; +const SIMULATION_ROUNDS: usize = 10; +const SURVIVAL_RATE: f32 = 0.5; // Here is the folder structure for the FighterNN: // base_dir/fighter_nn_{fighter_id}/{generation}/{fighter_id}_fighter_nn_{nn_id}.net @@ -37,16 +37,7 @@ pub struct FighterNN { impl GeneticNode for FighterNN { // Check for the highest number of the folder name and increment it by 1 fn initialize() -> Result, Error> { - // Load the configuration - let config: Config = serde_json::from_reader(File::open("config.json")?) - .with_context(|| format!("Failed to read config"))?; - - let base_path = PathBuf::from(config.base_dir); - - // Ensure the base directory exists, create it if not - if !base_path.exists() { - fs::create_dir_all(&base_path)?; - } + let base_path = PathBuf::from(BASE_DIR); let mut highest = 0; let mut folder = base_path.join(format!("fighter_nn_{:06}", highest)); @@ -62,10 +53,10 @@ impl GeneticNode for FighterNN { fs::create_dir(&gen_folder)?; // Create the first generation in this folder - for i in 0..10 { + for i in 0..POPULATION { // Filenames are stored in the format of "xxxxxx_fighter_nn_0.net", "xxxxxx_fighter_nn_1.net", etc. Where xxxxxx is the folder name let nn = gen_folder.join(format!("{:06}_fighter_nn_{}.net", highest, i)); - let mut fann = Fann::new(&[10, 10, 10]) + let mut fann = Fann::new(NEURAL_NETWORK_SHAPE) .with_context(|| format!("Failed to create nn"))?; fann.set_activation_func_hidden(ActivationFunc::SigmoidSymmetric); fann.set_activation_func_output(ActivationFunc::SigmoidSymmetric); @@ -83,7 +74,7 @@ impl GeneticNode for FighterNN { fn simulate(&mut self) -> Result<(), Error> { // For each nn in the current generation: - for i in 0..10 { + for i in 0..POPULATION { // load the nn let nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, i)); let fann = Fann::from_file(&nn) @@ -93,8 +84,8 @@ impl GeneticNode for FighterNN { let mut score = 0.0; // Using the same original nn, repeat the simulation with 5 random nn's from the current generation - for _ in 0..5 { - let random_nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, thread_rng().gen_range(0..10))); + for _ in 0..SIMULATION_ROUNDS { + let random_nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, thread_rng().gen_range(0..POPULATION))); let random_fann = Fann::from_file(&random_nn) .with_context(|| format!("Failed to load random nn"))?; @@ -114,7 +105,7 @@ impl GeneticNode for FighterNN { } score /= 5.0; - self.scores[self.generation as usize].insert(i, score); + self.scores[self.generation as usize].insert(i as u64, score); } Ok(()) @@ -122,6 +113,8 @@ impl GeneticNode for FighterNN { fn mutate(&mut self) -> Result<(), Error> { + let survivor_count = (POPULATION as f32 * SURVIVAL_RATE) as usize; + // Create the new generation folder let new_gen_folder = self.folder.join(format!("{}", self.generation + 1)); fs::create_dir(&new_gen_folder)?; @@ -129,10 +122,10 @@ impl GeneticNode for FighterNN { // Remove the 5 nn's with the lowest scores let mut sorted_scores: Vec<_> = self.scores[self.generation as usize].iter().collect(); sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); - let to_keep = sorted_scores[5..].iter().map(|(k, _)| *k).collect::>(); + let to_keep = sorted_scores[survivor_count..].iter().map(|(k, _)| *k).collect::>(); // Save the remaining 5 nn's to the new generation folder - for i in 0..5 { + for i in 0..survivor_count { let nn_id = to_keep[i]; let nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, nn_id)); let new_nn = new_gen_folder.join(format!("{:06}_fighter_nn_{}.net", self.id, i)); @@ -140,7 +133,7 @@ impl GeneticNode for FighterNN { } // Take the remaining 5 nn's and create 5 new nn's by the following: - for i in 0..5 { + for i in 0..survivor_count { let nn_id = to_keep[i]; let nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, nn_id)); let mut fann = Fann::from_file(&nn) @@ -159,7 +152,7 @@ impl GeneticNode for FighterNN { fann.set_connections(&connections); // Save the new nn's to the new generation folder - let new_nn = new_gen_folder.join(format!("{:06}_fighter_nn_{}.net", self.id, i + 5)); + let new_nn = new_gen_folder.join(format!("{:06}_fighter_nn_{}.net", self.id, i + survivor_count)); fann.save(&new_nn) .with_context(|| format!("Failed to save nn"))?; } @@ -171,18 +164,9 @@ impl GeneticNode for FighterNN { } fn merge(left: &FighterNN, right: &FighterNN) -> Result, Error> { + let base_path = PathBuf::from(BASE_DIR); + // Find next highest - // Load the configuration - let config: Config = serde_json::from_reader(File::open("config.json")?) - .with_context(|| format!("Failed to read config"))?; - - let base_path = PathBuf::from(config.base_dir); - - // Ensure the base directory exists, create it if not - if !base_path.exists() { - fs::create_dir_all(&base_path)?; - } - let mut highest = 0; let mut folder = base_path.join(format!("fighter_nn_{:06}", highest)); while folder.exists() { @@ -199,8 +183,8 @@ impl GeneticNode for FighterNN { // Take the 5 nn's with the highest scores from the left nn's and save them to the new fighter folder let mut sorted_scores: Vec<_> = left.scores[left.generation as usize].iter().collect(); sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); - let mut remaining = sorted_scores[5..].iter().map(|(k, _)| *k).collect::>(); - for i in 0..5 { + let mut remaining = sorted_scores[(POPULATION / 2)..].iter().map(|(k, _)| *k).collect::>(); + for i in 0..(POPULATION / 2) { let nn = left.folder.join(format!("{}", left.generation)).join(format!("{:06}_fighter_nn_{}.net", left.id, remaining.pop().unwrap())); let new_nn = folder.join(format!("0")).join(format!("{:06}_fighter_nn_{}.net", highest, i)); trace!("From: {:?}, To: {:?}", &nn, &new_nn); @@ -211,8 +195,8 @@ impl GeneticNode for FighterNN { // Take the 5 nn's with the highest scores from the right nn's and save them to the new fighter folder sorted_scores = right.scores[right.generation as usize].iter().collect(); sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); - remaining = sorted_scores[5..].iter().map(|(k, _)| *k).collect::>(); - for i in 5..10 { + remaining = sorted_scores[(POPULATION / 2)..].iter().map(|(k, _)| *k).collect::>(); + for i in (POPULATION / 2)..POPULATION { let nn = right.folder.join(format!("{}", right.generation)).join(format!("{:06}_fighter_nn_{}.net", right.id, remaining.pop().unwrap())); let new_nn = folder.join(format!("0")).join(format!("{:06}_fighter_nn_{}.net", highest, i)); trace!("From: {:?}, To: {:?}", &nn, &new_nn); From 7ffd48f1862b5bc158c536eb6ce068457886e6c6 Mon Sep 17 00:00:00 2001 From: vandomej Date: Mon, 11 Mar 2024 01:23:43 -0700 Subject: [PATCH 06/26] Adding context information to genetic node --- gemla/src/bin/fighter_nn/mod.rs | 57 ++++++++++++++----------------- gemla/src/bin/test_state/mod.rs | 59 ++++++++++++++++++++++++++------- gemla/src/core/genetic_node.rs | 38 +++++++++++++++------ gemla/src/core/mod.rs | 12 ++++--- 4 files changed, 105 insertions(+), 61 deletions(-) diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index c63116c..3504e82 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -2,10 +2,11 @@ extern crate fann; use std::{fs, path::PathBuf}; use fann::{ActivationFunc, Fann}; -use gemla::{core::genetic_node::GeneticNode, error::Error}; +use gemla::{core::genetic_node::{GeneticNode, GeneticNodeContext}, error::Error}; use rand::prelude::*; use serde::{Deserialize, Serialize}; use anyhow::Context; +use uuid::Uuid; use std::collections::HashMap; const BASE_DIR: &str = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations"; @@ -22,13 +23,13 @@ const SURVIVAL_RATE: f32 = 0.5; // there is no training happening to the neural networks // the neural networks are only used to simulate the nn's and to save and read the nn's from files // Filenames are stored in the format of "{fighter_id}_fighter_nn_{generation}.net". -// The folder name is stored in the format of "fighter_nn_xxxxxx" where xxxxxx is an incrementing number, checking for the highest number and incrementing it by 1 // The main folder contains a subfolder for each generation, containing a population of 10 nn's #[derive(Serialize, Deserialize, Debug, Clone)] pub struct FighterNN { - pub id: u64, + pub id: Uuid, pub folder: PathBuf, + pub population_size: usize, pub generation: u64, // A map of each nn identifier in a generation and their physics score pub scores: Vec>, @@ -36,16 +37,10 @@ pub struct FighterNN { impl GeneticNode for FighterNN { // Check for the highest number of the folder name and increment it by 1 - fn initialize() -> Result, Error> { + fn initialize(context: &GeneticNodeContext) -> Result, Error> { let base_path = PathBuf::from(BASE_DIR); - let mut highest = 0; - let mut folder = base_path.join(format!("fighter_nn_{:06}", highest)); - while folder.exists() { - highest += 1; - folder = base_path.join(format!("fighter_nn_{:06}", highest)); - } - + let mut folder = base_path.join(format!("fighter_nn_{:06}", context.id)); fs::create_dir(&folder)?; //Create a new directory for the first generation @@ -55,7 +50,7 @@ impl GeneticNode for FighterNN { // Create the first generation in this folder for i in 0..POPULATION { // Filenames are stored in the format of "xxxxxx_fighter_nn_0.net", "xxxxxx_fighter_nn_1.net", etc. Where xxxxxx is the folder name - let nn = gen_folder.join(format!("{:06}_fighter_nn_{}.net", highest, i)); + let nn = gen_folder.join(format!("{:06}_fighter_nn_{}.net", context.id, i)); let mut fann = Fann::new(NEURAL_NETWORK_SHAPE) .with_context(|| format!("Failed to create nn"))?; fann.set_activation_func_hidden(ActivationFunc::SigmoidSymmetric); @@ -65,16 +60,17 @@ impl GeneticNode for FighterNN { } Ok(Box::new(FighterNN { - id: highest, + id: context.id, folder, + population_size: POPULATION, generation: 0, scores: vec![HashMap::new()], })) } - fn simulate(&mut self) -> Result<(), Error> { + fn simulate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { // For each nn in the current generation: - for i in 0..POPULATION { + for i in 0..self.population_size { // load the nn let nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, i)); let fann = Fann::from_file(&nn) @@ -85,7 +81,7 @@ impl GeneticNode for FighterNN { // Using the same original nn, repeat the simulation with 5 random nn's from the current generation for _ in 0..SIMULATION_ROUNDS { - let random_nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, thread_rng().gen_range(0..POPULATION))); + let random_nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, thread_rng().gen_range(0..self.population_size))); let random_fann = Fann::from_file(&random_nn) .with_context(|| format!("Failed to load random nn"))?; @@ -112,8 +108,8 @@ impl GeneticNode for FighterNN { } - fn mutate(&mut self) -> Result<(), Error> { - let survivor_count = (POPULATION as f32 * SURVIVAL_RATE) as usize; + fn mutate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { + let survivor_count = (self.population_size as f32 * SURVIVAL_RATE) as usize; // Create the new generation folder let new_gen_folder = self.folder.join(format!("{}", self.generation + 1)); @@ -163,17 +159,11 @@ impl GeneticNode for FighterNN { Ok(()) } - fn merge(left: &FighterNN, right: &FighterNN) -> Result, Error> { + fn merge(left: &FighterNN, right: &FighterNN, id: &Uuid) -> Result, Error> { let base_path = PathBuf::from(BASE_DIR); // Find next highest - let mut highest = 0; - let mut folder = base_path.join(format!("fighter_nn_{:06}", highest)); - while folder.exists() { - highest += 1; - folder = base_path.join(format!("fighter_nn_{:06}", highest)); - } - + let folder = base_path.join(format!("fighter_nn_{:06}", id)); fs::create_dir(&folder)?; //Create a new directory for the first generation @@ -183,10 +173,10 @@ impl GeneticNode for FighterNN { // Take the 5 nn's with the highest scores from the left nn's and save them to the new fighter folder let mut sorted_scores: Vec<_> = left.scores[left.generation as usize].iter().collect(); sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); - let mut remaining = sorted_scores[(POPULATION / 2)..].iter().map(|(k, _)| *k).collect::>(); - for i in 0..(POPULATION / 2) { + let mut remaining = sorted_scores[(left.population_size / 2)..].iter().map(|(k, _)| *k).collect::>(); + for i in 0..(left.population_size / 2) { let nn = left.folder.join(format!("{}", left.generation)).join(format!("{:06}_fighter_nn_{}.net", left.id, remaining.pop().unwrap())); - let new_nn = folder.join(format!("0")).join(format!("{:06}_fighter_nn_{}.net", highest, i)); + let new_nn = folder.join(format!("0")).join(format!("{:06}_fighter_nn_{}.net", id, i)); trace!("From: {:?}, To: {:?}", &nn, &new_nn); fs::copy(&nn, &new_nn) .with_context(|| format!("Failed to copy left nn"))?; @@ -195,19 +185,20 @@ impl GeneticNode for FighterNN { // Take the 5 nn's with the highest scores from the right nn's and save them to the new fighter folder sorted_scores = right.scores[right.generation as usize].iter().collect(); sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); - remaining = sorted_scores[(POPULATION / 2)..].iter().map(|(k, _)| *k).collect::>(); - for i in (POPULATION / 2)..POPULATION { + remaining = sorted_scores[(right.population_size / 2)..].iter().map(|(k, _)| *k).collect::>(); + for i in (right.population_size / 2)..right.population_size { let nn = right.folder.join(format!("{}", right.generation)).join(format!("{:06}_fighter_nn_{}.net", right.id, remaining.pop().unwrap())); - let new_nn = folder.join(format!("0")).join(format!("{:06}_fighter_nn_{}.net", highest, i)); + let new_nn = folder.join(format!("0")).join(format!("{:06}_fighter_nn_{}.net", id, i)); trace!("From: {:?}, To: {:?}", &nn, &new_nn); fs::copy(&nn, &new_nn) .with_context(|| format!("Failed to copy right nn"))?; } Ok(Box::new(FighterNN { - id: highest, + id: *id, folder, generation: 0, + population_size: POPULATION, scores: vec![HashMap::new()], })) } diff --git a/gemla/src/bin/test_state/mod.rs b/gemla/src/bin/test_state/mod.rs index fac0305..35ef7cc 100644 --- a/gemla/src/bin/test_state/mod.rs +++ b/gemla/src/bin/test_state/mod.rs @@ -1,6 +1,7 @@ -use gemla::{core::genetic_node::GeneticNode, error::Error}; +use gemla::{core::genetic_node::{GeneticNode, GeneticNodeContext}, error::Error}; use rand::prelude::*; use serde::{Deserialize, Serialize}; +use uuid::Uuid; const POPULATION_SIZE: u64 = 5; const POPULATION_REDUCTION_SIZE: u64 = 3; @@ -11,7 +12,7 @@ pub struct TestState { } impl GeneticNode for TestState { - fn initialize() -> Result, Error> { + fn initialize(_context: &GeneticNodeContext) -> Result, Error> { let mut population: Vec = vec![]; for _ in 0..POPULATION_SIZE { @@ -21,7 +22,7 @@ impl GeneticNode for TestState { Ok(Box::new(TestState { population })) } - fn simulate(&mut self) -> Result<(), Error> { + fn simulate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { let mut rng = thread_rng(); self.population = self @@ -33,7 +34,7 @@ impl GeneticNode for TestState { Ok(()) } - fn mutate(&mut self) -> Result<(), Error> { + fn mutate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { let mut rng = thread_rng(); let mut v = self.population.clone(); @@ -71,7 +72,7 @@ impl GeneticNode for TestState { Ok(()) } - fn merge(left: &TestState, right: &TestState) -> Result, Error> { + fn merge(left: &TestState, right: &TestState, id: &Uuid) -> Result, Error> { let mut v = left.population.clone(); v.append(&mut right.population.clone()); @@ -82,7 +83,11 @@ impl GeneticNode for TestState { let mut result = TestState { population: v }; - result.mutate()?; + result.mutate(&GeneticNodeContext { + id: id.clone(), + generation: 0, + max_generations: 0, + })?; Ok(Box::new(result)) } @@ -95,7 +100,13 @@ mod tests { #[test] fn test_initialize() { - let state = TestState::initialize().unwrap(); + let state = TestState::initialize( + &GeneticNodeContext { + id: Uuid::new_v4(), + generation: 0, + max_generations: 0, + } + ).unwrap(); assert_eq!(state.population.len(), POPULATION_SIZE as usize); } @@ -108,14 +119,32 @@ mod tests { let original_population = state.population.clone(); - state.simulate().unwrap(); + state.simulate( + &GeneticNodeContext { + id: Uuid::new_v4(), + generation: 0, + max_generations: 0, + } + ).unwrap(); assert!(original_population .iter() .zip(state.population.iter()) .all(|(&a, &b)| b >= a - 1 && b <= a + 2)); - state.simulate().unwrap(); - state.simulate().unwrap(); + state.simulate( + &GeneticNodeContext { + id: Uuid::new_v4(), + generation: 0, + max_generations: 0, + } + ).unwrap(); + state.simulate( + &GeneticNodeContext { + id: Uuid::new_v4(), + generation: 0, + max_generations: 0, + } + ).unwrap(); assert!(original_population .iter() .zip(state.population.iter()) @@ -128,7 +157,13 @@ mod tests { population: vec![4, 3, 3], }; - state.mutate().unwrap(); + state.mutate( + &GeneticNodeContext { + id: Uuid::new_v4(), + generation: 0, + max_generations: 0, + } + ).unwrap(); assert_eq!(state.population.len(), POPULATION_SIZE as usize); } @@ -143,7 +178,7 @@ mod tests { population: vec![0, 1, 3, 7], }; - let merged_state = TestState::merge(&state1, &state2).unwrap(); + let merged_state = TestState::merge(&state1, &state2, &Uuid::new_v4()).unwrap(); assert_eq!(merged_state.population.len(), POPULATION_SIZE as usize); assert!(merged_state.population.iter().any(|&x| x == 7)); diff --git a/gemla/src/core/genetic_node.rs b/gemla/src/core/genetic_node.rs index aeb1b3a..c1e669f 100644 --- a/gemla/src/core/genetic_node.rs +++ b/gemla/src/core/genetic_node.rs @@ -24,6 +24,12 @@ pub enum GeneticState { Finish, } +pub struct GeneticNodeContext { + pub generation: u64, + pub max_generations: u64, + pub id: Uuid, +} + /// A trait used to interact with the internal state of nodes within the [`Bracket`] /// /// [`Bracket`]: crate::bracket::Bracket @@ -32,17 +38,17 @@ pub trait GeneticNode { /// /// # Examples /// TODO - fn initialize() -> Result, Error>; + fn initialize(context: &GeneticNodeContext) -> Result, Error>; - fn simulate(&mut self) -> Result<(), Error>; + fn simulate(&mut self, context: &GeneticNodeContext) -> Result<(), Error>; /// Mutates members in a population and/or crossbreeds them to produce new offspring. /// /// # Examples /// TODO - fn mutate(&mut self) -> Result<(), Error>; + fn mutate(&mut self, context: &GeneticNodeContext) -> Result<(), Error>; - fn merge(left: &Self, right: &Self) -> Result, Error>; + fn merge(left: &Self, right: &Self, id: &Uuid) -> Result, Error>; } /// Used externally to wrap a node implementing the [`GeneticNode`] trait. Processes state transitions for the given node as @@ -101,18 +107,28 @@ where self.max_generations } + pub fn generation(&self) -> u64 { + self.generation + } + pub fn state(&self) -> GeneticState { self.state } pub fn process_node(&mut self) -> Result { + let context = GeneticNodeContext { + generation: self.generation, + max_generations: self.max_generations, + id: self.id, + }; + match (self.state, &mut self.node) { (GeneticState::Initialize, _) => { - self.node = Some(*T::initialize()?); + self.node = Some(*T::initialize(&context)?); self.state = GeneticState::Simulate; } (GeneticState::Simulate, Some(n)) => { - n.simulate() + n.simulate(&context) .with_context(|| format!("Error simulating node: {:?}", self))?; self.state = if self.generation >= self.max_generations { @@ -122,7 +138,7 @@ where }; } (GeneticState::Mutate, Some(n)) => { - n.mutate() + n.mutate(&context) .with_context(|| format!("Error mutating node: {:?}", self))?; self.generation += 1; @@ -148,20 +164,20 @@ mod tests { } impl GeneticNode for TestState { - fn simulate(&mut self) -> Result<(), Error> { + fn simulate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { self.score += 1.0; Ok(()) } - fn mutate(&mut self) -> Result<(), Error> { + fn mutate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { Ok(()) } - fn initialize() -> Result, Error> { + fn initialize(_context: &GeneticNodeContext) -> Result, Error> { Ok(Box::new(TestState { score: 0.0 })) } - fn merge(_l: &TestState, _r: &TestState) -> Result, Error> { + fn merge(_l: &TestState, _r: &TestState, _id: &Uuid) -> Result, Error> { Err(Error::Other(anyhow!("Unable to merge"))) } } diff --git a/gemla/src/core/mod.rs b/gemla/src/core/mod.rs index 64f006f..61df62e 100644 --- a/gemla/src/core/mod.rs +++ b/gemla/src/core/mod.rs @@ -195,7 +195,7 @@ where { info!("Merging nodes {} and {}", l.val.id(), r.val.id()); if let (Some(left_node), Some(right_node)) = (l.val.as_ref(), r.val.as_ref()) { - let merged_node = GeneticNode::merge(left_node, right_node)?; + let merged_node = GeneticNode::merge(left_node, right_node, &tree.val.id())?; tree.val = GeneticNodeWrapper::from( *merged_node, tree.val.max_generations(), @@ -337,6 +337,8 @@ mod tests { use std::path::PathBuf; use std::fs; + use self::genetic_node::GeneticNodeContext; + struct CleanUp { path: PathBuf, } @@ -367,20 +369,20 @@ mod tests { } impl genetic_node::GeneticNode for TestState { - fn simulate(&mut self) -> Result<(), Error> { + fn simulate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { self.score += 1.0; Ok(()) } - fn mutate(&mut self) -> Result<(), Error> { + fn mutate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { Ok(()) } - fn initialize() -> Result, Error> { + fn initialize(_context: &GeneticNodeContext) -> Result, Error> { Ok(Box::new(TestState { score: 0.0 })) } - fn merge(left: &TestState, right: &TestState) -> Result, Error> { + fn merge(left: &TestState, right: &TestState, _id: &Uuid) -> Result, Error> { Ok(Box::new(if left.score > right.score { left.clone() } else { From 69b026593ed5d2f322ccc68b97eb03c74696a50f Mon Sep 17 00:00:00 2001 From: vandomej Date: Mon, 11 Mar 2024 01:56:48 -0700 Subject: [PATCH 07/26] Add cross-breeding functionality to FighterNN --- gemla/src/bin/fighter_nn/mod.rs | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index 3504e82..cabbb29 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -4,6 +4,7 @@ use std::{fs, path::PathBuf}; use fann::{ActivationFunc, Fann}; use gemla::{core::genetic_node::{GeneticNode, GeneticNodeContext}, error::Error}; use rand::prelude::*; +use rand::distributions::{Distribution, Uniform}; use serde::{Deserialize, Serialize}; use anyhow::Context; use uuid::Uuid; @@ -135,6 +136,39 @@ impl GeneticNode for FighterNN { let mut fann = Fann::from_file(&nn) .with_context(|| format!("Failed to load nn"))?; + // Load another nn from the current generation and cross breed it with the current nn + let cross_nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, to_keep[thread_rng().gen_range(0..survivor_count)])); + let cross_fann = Fann::from_file(&cross_nn) + .with_context(|| format!("Failed to load cross nn"))?; + + let mut connections = fann.get_connections(); // Vector of connections + let cross_connections = cross_fann.get_connections(); // Vector of connections + let segment_count: usize = 3; // For example, choose 3 segments to swap + let segment_distribution = Uniform::from(1..connections.len() / segment_count); // Ensure segments are not too small + + let mut start_points = vec![]; + + for _ in 0..segment_count { + let start_point = segment_distribution.sample(&mut rand::thread_rng()); + start_points.push(start_point); + } + start_points.sort_unstable(); // Ensure segments are in order + + for (j, &start) in start_points.iter().enumerate() { + let end = if j < segment_count - 1 { + start_points[j + 1] + } else { + connections.len() + }; + + // Swap segments + for k in start..end { + connections[k] = cross_connections[k].clone(); + } + } + + fann.set_connections(&connections); + // For each weight in the 5 new nn's there is a 20% chance of a minor mutation (a random number between -0.1 and 0.1 is added to the weight) // And a 5% chance of a major mutation (a random number between -0.3 and 0.3 is added to the weight) let mut connections = fann.get_connections(); // Vector of connections From ca3989421db3b3c582f7057cca65d457e46e4e98 Mon Sep 17 00:00:00 2001 From: vandomej Date: Mon, 11 Mar 2024 15:37:52 -0700 Subject: [PATCH 08/26] Update logic for increasing height --- gemla/src/bin/bin.rs | 10 ++-- gemla/src/bin/fighter_nn/mod.rs | 84 ++++++++++++++++----------------- gemla/src/core/mod.rs | 36 +++++++++----- 3 files changed, 68 insertions(+), 62 deletions(-) diff --git a/gemla/src/bin/bin.rs b/gemla/src/bin/bin.rs index 1455d7c..4cb05c8 100644 --- a/gemla/src/bin/bin.rs +++ b/gemla/src/bin/bin.rs @@ -56,15 +56,15 @@ fn main() -> anyhow::Result<()> { let mut gemla = log_error(Gemla::::new( &PathBuf::from(args.file), GemlaConfig { - generations_per_node: 3, - overwrite: true, + generations_per_height: 3, + overwrite: false, }, DataFormat::Json, ))?; - log_error(gemla.simulate(3).await)?; - - Ok(()) + loop { + log_error(gemla.simulate(5).await)?; + } }) }); diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index cabbb29..2033667 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -40,26 +40,30 @@ impl GeneticNode for FighterNN { // Check for the highest number of the folder name and increment it by 1 fn initialize(context: &GeneticNodeContext) -> Result, Error> { let base_path = PathBuf::from(BASE_DIR); - - let mut folder = base_path.join(format!("fighter_nn_{:06}", context.id)); - fs::create_dir(&folder)?; - - //Create a new directory for the first generation + + let folder = base_path.join(format!("fighter_nn_{:06}", context.id)); + // Ensures directory is created if it doesn't exist and does nothing if it exists + fs::create_dir_all(&folder) + .with_context(|| format!("Failed to create or access the folder: {:?}", folder))?; + + //Create a new directory for the first generation, using create_dir_all to avoid errors if it already exists let gen_folder = folder.join("0"); - fs::create_dir(&gen_folder)?; - + fs::create_dir_all(&gen_folder) + .with_context(|| format!("Failed to create or access the generation folder: {:?}", gen_folder))?; + // Create the first generation in this folder for i in 0..POPULATION { // Filenames are stored in the format of "xxxxxx_fighter_nn_0.net", "xxxxxx_fighter_nn_1.net", etc. Where xxxxxx is the folder name let nn = gen_folder.join(format!("{:06}_fighter_nn_{}.net", context.id, i)); let mut fann = Fann::new(NEURAL_NETWORK_SHAPE) - .with_context(|| format!("Failed to create nn"))?; + .with_context(|| "Failed to create nn")?; fann.set_activation_func_hidden(ActivationFunc::SigmoidSymmetric); fann.set_activation_func_output(ActivationFunc::SigmoidSymmetric); + // This will overwrite any existing file with the same name fann.save(&nn) - .with_context(|| format!("Failed to save nn"))?; + .with_context(|| format!("Failed to save nn at {:?}", nn))?; } - + Ok(Box::new(FighterNN { id: context.id, folder, @@ -114,7 +118,7 @@ impl GeneticNode for FighterNN { // Create the new generation folder let new_gen_folder = self.folder.join(format!("{}", self.generation + 1)); - fs::create_dir(&new_gen_folder)?; + fs::create_dir_all(&new_gen_folder).with_context(|| format!("Failed to create or access new generation folder: {:?}", new_gen_folder))?; // Remove the 5 nn's with the lowest scores let mut sorted_scores: Vec<_> = self.scores[self.generation as usize].iter().collect(); @@ -195,44 +199,36 @@ impl GeneticNode for FighterNN { fn merge(left: &FighterNN, right: &FighterNN, id: &Uuid) -> Result, Error> { let base_path = PathBuf::from(BASE_DIR); - - // Find next highest let folder = base_path.join(format!("fighter_nn_{:06}", id)); - fs::create_dir(&folder)?; - - //Create a new directory for the first generation - let gen_folder = folder.join("0"); - fs::create_dir(&gen_folder)?; - - // Take the 5 nn's with the highest scores from the left nn's and save them to the new fighter folder - let mut sorted_scores: Vec<_> = left.scores[left.generation as usize].iter().collect(); - sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); - let mut remaining = sorted_scores[(left.population_size / 2)..].iter().map(|(k, _)| *k).collect::>(); - for i in 0..(left.population_size / 2) { - let nn = left.folder.join(format!("{}", left.generation)).join(format!("{:06}_fighter_nn_{}.net", left.id, remaining.pop().unwrap())); - let new_nn = folder.join(format!("0")).join(format!("{:06}_fighter_nn_{}.net", id, i)); - trace!("From: {:?}, To: {:?}", &nn, &new_nn); - fs::copy(&nn, &new_nn) - .with_context(|| format!("Failed to copy left nn"))?; - } - - // Take the 5 nn's with the highest scores from the right nn's and save them to the new fighter folder - sorted_scores = right.scores[right.generation as usize].iter().collect(); - sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); - remaining = sorted_scores[(right.population_size / 2)..].iter().map(|(k, _)| *k).collect::>(); - for i in (right.population_size / 2)..right.population_size { - let nn = right.folder.join(format!("{}", right.generation)).join(format!("{:06}_fighter_nn_{}.net", right.id, remaining.pop().unwrap())); - let new_nn = folder.join(format!("0")).join(format!("{:06}_fighter_nn_{}.net", id, i)); - trace!("From: {:?}, To: {:?}", &nn, &new_nn); - fs::copy(&nn, &new_nn) - .with_context(|| format!("Failed to copy right nn"))?; - } - + + // Ensure the folder exists, including the generation subfolder. + fs::create_dir_all(&folder.join("0")) + .with_context(|| format!("Failed to create directory {:?}", folder.join("0")))?; + + // Function to copy NNs from a source FighterNN to the new folder. + let copy_nns = |source: &FighterNN, folder: &PathBuf, id: &Uuid, start_idx: usize| -> Result<(), Error> { + let mut sorted_scores: Vec<_> = source.scores[source.generation as usize].iter().collect(); + sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); + let remaining = sorted_scores[(source.population_size / 2)..].iter().map(|(k, _)| *k).collect::>(); + + for (i, nn_id) in remaining.into_iter().enumerate() { + let nn_path = source.folder.join(source.generation.to_string()).join(format!("{:06}_fighter_nn_{}.net", source.id, nn_id)); + let new_nn_path = folder.join("0").join(format!("{:06}_fighter_nn_{}.net", id, start_idx + i)); + fs::copy(&nn_path, &new_nn_path) + .with_context(|| format!("Failed to copy nn from {:?} to {:?}", nn_path, new_nn_path))?; + } + Ok(()) + }; + + // Copy the top half of NNs from each parent to the new folder. + copy_nns(left, &folder, id, 0)?; + copy_nns(right, &folder, id, left.population_size as usize / 2)?; + Ok(Box::new(FighterNN { id: *id, folder, generation: 0, - population_size: POPULATION, + population_size: left.population_size, // Assuming left and right have the same population size. scores: vec![HashMap::new()], })) } diff --git a/gemla/src/core/mod.rs b/gemla/src/core/mod.rs index 61df62e..152ed6d 100644 --- a/gemla/src/core/mod.rs +++ b/gemla/src/core/mod.rs @@ -55,7 +55,7 @@ type SimulationTree = Box>>; /// ``` #[derive(Serialize, Deserialize, Copy, Clone)] pub struct GemlaConfig { - pub generations_per_node: u64, + pub generations_per_height: u64, pub overwrite: bool, } @@ -103,12 +103,22 @@ where } pub async fn simulate(&mut self, steps: u64) -> Result<(), Error> { - // Before we can process nodes we must create blank nodes in their place to keep track of which nodes have been processed - // in the tree and which nodes have not. - self.data.mutate(|(d, c)| { - let mut tree: Option> = Gemla::increase_height(d.take(), c, steps); - mem::swap(d, &mut tree); - })?; + // Only increase height if the tree is uninitialized or completed + if self.tree_ref().is_none() || + self + .tree_ref() + .map(|t| Gemla::is_completed(t)) + .unwrap_or(true) + { + // Before we can process nodes we must create blank nodes in their place to keep track of which nodes have been processed + // in the tree and which nodes have not. + self.data.mutate(|(d, c)| { + let mut tree: Option> = Gemla::increase_height(d.take(), c, steps); + mem::swap(d, &mut tree); + })?; + } + + info!( "Height of simulation tree increased to {}", @@ -286,16 +296,16 @@ where if amount == 0 { tree } else { - let left_branch_right = + let left_branch_height = tree.as_ref().map(|t| t.height() as u64).unwrap_or(0) + amount - 1; Some(Box::new(Tree::new( - GeneticNodeWrapper::new(config.generations_per_node), + GeneticNodeWrapper::new(config.generations_per_height), Gemla::increase_height(tree, config, amount - 1), // The right branch height has to equal the left branches total height - if left_branch_right > 0 { + if left_branch_height > 0 { Some(Box::new(btree!(GeneticNodeWrapper::new( - left_branch_right * config.generations_per_node + left_branch_height * config.generations_per_height )))) } else { None @@ -399,7 +409,7 @@ mod tests { // Testing initial creation let mut config = GemlaConfig { - generations_per_node: 1, + generations_per_height: 1, overwrite: true }; let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; @@ -439,7 +449,7 @@ mod tests { CleanUp::new(&path).run(|p| { // Testing initial creation let config = GemlaConfig { - generations_per_node: 10, + generations_per_height: 10, overwrite: true }; let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; From c9b746e59d177d6a06cebe9a86b479ef85786160 Mon Sep 17 00:00:00 2001 From: vandomej Date: Tue, 19 Mar 2024 22:09:08 -0700 Subject: [PATCH 09/26] Adjusting NN size --- gemla/src/bin/fighter_nn/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index 2033667..248dbb0 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -12,7 +12,7 @@ use std::collections::HashMap; const BASE_DIR: &str = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations"; const POPULATION: usize = 100; -const NEURAL_NETWORK_SHAPE: &[u32; 3] = &[10, 10, 10]; +const NEURAL_NETWORK_SHAPE: &[u32; 5] = &[14, 20, 20, 12, 8]; const SIMULATION_ROUNDS: usize = 10; const SURVIVAL_RATE: f32 = 0.5; @@ -90,7 +90,7 @@ impl GeneticNode for FighterNN { let random_fann = Fann::from_file(&random_nn) .with_context(|| format!("Failed to load random nn"))?; - let inputs: Vec = (0..10).map(|_| thread_rng().gen_range(-1.0..1.0)).collect(); + let inputs: Vec = (0..NEURAL_NETWORK_SHAPE[0]).map(|_| thread_rng().gen_range(-1.0..1.0)).collect(); let outputs = fann.run(&inputs) .with_context(|| format!("Failed to run nn"))?; let random_outputs = random_fann.run(&inputs) @@ -179,9 +179,10 @@ impl GeneticNode for FighterNN { for c in &mut connections { if thread_rng().gen_range(0..100) < 20 { c.weight += thread_rng().gen_range(-0.1..0.1); - } else if thread_rng().gen_range(0..100) < 5 { - c.weight += thread_rng().gen_range(-0.3..0.3); } + // else if thread_rng().gen_range(0..100) < 5 { + // c.weight += thread_rng().gen_range(-0.3..0.3); + // } } fann.set_connections(&connections); From 97086fdbe0b5737223450af469834992e68ee1f6 Mon Sep 17 00:00:00 2001 From: vandomej Date: Thu, 21 Mar 2024 10:21:15 -0700 Subject: [PATCH 10/26] Modifying executor runtime for efficiency --- analysis.py | 101 +++++++++++++++++++++++ gemla/Cargo.toml | 4 +- gemla/src/bin/bin.rs | 65 +++++++-------- gemla/src/bin/fighter_nn/mod.rs | 130 ++++++++++++++++++++++------- gemla/src/bin/test_state/mod.rs | 30 +++---- gemla/src/core/genetic_node.rs | 43 +++++----- gemla/src/core/mod.rs | 140 +++++++++++++++++++------------- 7 files changed, 356 insertions(+), 157 deletions(-) create mode 100644 analysis.py diff --git a/analysis.py b/analysis.py new file mode 100644 index 0000000..f5db293 --- /dev/null +++ b/analysis.py @@ -0,0 +1,101 @@ +# Re-importing necessary libraries +import json +import matplotlib.pyplot as plt +import networkx as nx +import random + +def hierarchy_pos(G, root=None, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5): + if not nx.is_tree(G): + raise TypeError('cannot use hierarchy_pos on a graph that is not a tree') + + if root is None: + if isinstance(G, nx.DiGraph): + root = next(iter(nx.topological_sort(G))) + else: + root = random.choice(list(G.nodes)) + + def _hierarchy_pos(G, root, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5, pos=None, parent=None): + if pos is None: + pos = {root: (xcenter, vert_loc)} + else: + pos[root] = (xcenter, vert_loc) + children = list(G.successors(root)) # Use successors to get children for DiGraph + if not isinstance(G, nx.DiGraph): + if parent is not None: + children.remove(parent) + if len(children) != 0: + dx = width / len(children) + nextx = xcenter - width / 2 - dx / 2 + for child in children: + nextx += dx + pos = _hierarchy_pos(G, child, width=dx, vert_gap=vert_gap, + vert_loc=vert_loc - vert_gap, xcenter=nextx, + pos=pos, parent=root) + return pos + + return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter) + +# Simplified JSON data for demonstration +with open('gemla/test.json', 'r') as file: + simplified_json_data = json.load(file) + +# Function to traverse the tree and create a graph +def traverse(node, graph, parent=None): + if node is None: + return + + node_id = node["val"]["id"] + if "node" in node["val"] and node["val"]["node"]: + scores = node["val"]["node"]["scores"] + generations = node["val"]["node"]["generation"] + population_size = node["val"]["node"]["population_size"] + # Prepare to track the highest score across all generations and the corresponding individual + overall_max_score = float('-inf') + overall_max_score_individual = None + overall_max_score_gen = None + + for gen, gen_scores in enumerate(scores): + if gen_scores: # Ensure the dictionary is not empty + # Find the max score and the individual for this generation + max_score_for_gen = max(gen_scores.values()) + individual_with_max_score_for_gen = max(gen_scores, key=gen_scores.get) + + # if max_score_for_gen > overall_max_score: + overall_max_score = max_score_for_gen + overall_max_score_individual = individual_with_max_score_for_gen + overall_max_score_gen = gen + + label = f"{node_id}\nGenerations: {generations}, Population: {population_size}\nMax score: {overall_max_score:.6f} (Individual {overall_max_score_individual} in Gen {overall_max_score_gen})" + else: + label = node_id + + graph.add_node(node_id, label=label) + if parent: + graph.add_edge(parent, node_id) + + traverse(node.get("left"), graph, parent=node_id) + traverse(node.get("right"), graph, parent=node_id) + + +# Create a directed graph +G = nx.DiGraph() + +# Populate the graph +traverse(simplified_json_data[0], G) + +# Find the root node (a node with no incoming edges) +root_candidates = [node for node, indeg in G.in_degree() if indeg == 0] + +if root_candidates: + root_node = root_candidates[0] # Assuming there's only one root candidate +else: + root_node = None # This should ideally never happen in a properly structured tree + +# Use the determined root node for hierarchy_pos +if root_node is not None: + pos = hierarchy_pos(G, root=root_node) + labels = nx.get_node_attributes(G, 'label') + nx.draw(G, pos, labels=labels, with_labels=True, arrows=True) + plt.show() +else: + print("No root node found. Cannot draw the tree.") \ No newline at end of file diff --git a/gemla/Cargo.toml b/gemla/Cargo.toml index 6119210..ee4951d 100644 --- a/gemla/Cargo.toml +++ b/gemla/Cargo.toml @@ -26,8 +26,8 @@ rand = "0.8.5" log = "0.4.21" env_logger = "0.11.3" futures = "0.3.30" -smol = "2.0.0" -smol-potat = "1.1.2" +tokio = { version = "1.36.0", features = ["full"] } num_cpus = "1.16.0" easy-parallel = "3.3.1" fann = "0.1.8" +async-trait = "0.1.78" diff --git a/gemla/src/bin/bin.rs b/gemla/src/bin/bin.rs index 4cb05c8..afb2ba7 100644 --- a/gemla/src/bin/bin.rs +++ b/gemla/src/bin/bin.rs @@ -6,16 +6,17 @@ extern crate log; mod test_state; mod fighter_nn; -use easy_parallel::Parallel; use file_linked::constants::data_format::DataFormat; use gemla::{ core::{Gemla, GemlaConfig}, - error::{log_error, Error}, + error::log_error, }; -use smol::{channel, channel::RecvError, future, Executor}; use std::{path::PathBuf, time::Instant}; use fighter_nn::FighterNN; use clap::Parser; +use anyhow::Result; + +// const NUM_THREADS: usize = 12; #[derive(Parser)] #[command(version, about, long_about = None)] @@ -29,48 +30,40 @@ struct Args { /// /// Use the -h, --h, or --help flag to see usage syntax. /// TODO -fn main() -> anyhow::Result<()> { +fn main() -> Result<()> { env_logger::init(); info!("Starting"); - let now = Instant::now(); - // Obtainning number of threads to use - let num_threads = num_cpus::get().max(1); - let ex = Executor::new(); - let (signal, shutdown) = channel::unbounded::<()>(); + // Manually configure the Tokio runtime + let runtime: Result<()> = tokio::runtime::Builder::new_multi_thread() + .worker_threads(num_cpus::get()) + // .worker_threads(NUM_THREADS) + .build()? + .block_on(async { + let args = Args::parse(); // Assuming Args::parse() doesn't need to be async + let mut gemla = log_error(Gemla::::new( + &PathBuf::from(args.file), + GemlaConfig { + generations_per_height: 10, + overwrite: false, + }, + DataFormat::Json, + ))?; - // Create an executor thread pool. - let (_, result): (Vec>, Result<(), Error>) = Parallel::new() - .each(0..num_threads, |_| { - future::block_on(ex.run(shutdown.recv())) - }) - .finish(|| { - smol::block_on(async { - drop(signal); + // let gemla_arc = Arc::new(gemla); - // Command line arguments are parsed with the clap crate. - let args = Args::parse(); + // Setup your application logic here + // If `gemla::simulate` needs to run sequentially, simply call it in sequence without spawning new tasks - // Checking that the first argument is a valid file - let mut gemla = log_error(Gemla::::new( - &PathBuf::from(args.file), - GemlaConfig { - generations_per_height: 3, - overwrite: false, - }, - DataFormat::Json, - ))?; - - loop { - log_error(gemla.simulate(5).await)?; - } - }) + // Example placeholder loop to continuously run simulate + loop { // Arbitrary loop count for demonstration + gemla.simulate(5).await?; + } }); - result?; + runtime?; // Handle errors from the block_on call info!("Finished in {:?}", now.elapsed()); - Ok(()) -} +} \ No newline at end of file diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index 248dbb0..bb641d8 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -1,7 +1,8 @@ extern crate fann; -use std::{fs, path::PathBuf}; +use std::{fs::{self, File}, io::{self, BufRead, BufReader}, path::{Path, PathBuf}}; use fann::{ActivationFunc, Fann}; +use futures::future::join_all; use gemla::{core::genetic_node::{GeneticNode, GeneticNodeContext}, error::Error}; use rand::prelude::*; use rand::distributions::{Distribution, Uniform}; @@ -9,12 +10,15 @@ use serde::{Deserialize, Serialize}; use anyhow::Context; use uuid::Uuid; use std::collections::HashMap; +use tokio::process::Command; +use async_trait::async_trait; const BASE_DIR: &str = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations"; -const POPULATION: usize = 100; +const POPULATION: usize = 50; const NEURAL_NETWORK_SHAPE: &[u32; 5] = &[14, 20, 20, 12, 8]; -const SIMULATION_ROUNDS: usize = 10; +const SIMULATION_ROUNDS: usize = 5; const SURVIVAL_RATE: f32 = 0.5; +const GAME_EXECUTABLE_PATH: &str = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Package\\Windows\\AI_Fight_Sim.exe"; // Here is the folder structure for the FighterNN: // base_dir/fighter_nn_{fighter_id}/{generation}/{fighter_id}_fighter_nn_{nn_id}.net @@ -36,9 +40,10 @@ pub struct FighterNN { pub scores: Vec>, } +#[async_trait] impl GeneticNode for FighterNN { // Check for the highest number of the folder name and increment it by 1 - fn initialize(context: &GeneticNodeContext) -> Result, Error> { + fn initialize(context: GeneticNodeContext) -> Result, Error> { let base_path = PathBuf::from(BASE_DIR); let folder = base_path.join(format!("fighter_nn_{:06}", context.id)); @@ -57,6 +62,7 @@ impl GeneticNode for FighterNN { let nn = gen_folder.join(format!("{:06}_fighter_nn_{}.net", context.id, i)); let mut fann = Fann::new(NEURAL_NETWORK_SHAPE) .with_context(|| "Failed to create nn")?; + fann.randomize_weights(-0.8, 0.8); fann.set_activation_func_hidden(ActivationFunc::SigmoidSymmetric); fann.set_activation_func_output(ActivationFunc::SigmoidSymmetric); // This will overwrite any existing file with the same name @@ -73,47 +79,89 @@ impl GeneticNode for FighterNN { })) } - fn simulate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { + async fn simulate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { // For each nn in the current generation: for i in 0..self.population_size { // load the nn let nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, i)); - let fann = Fann::from_file(&nn) - .with_context(|| format!("Failed to load nn"))?; - - // Simulate the nn against the random nn - let mut score = 0.0; - - // Using the same original nn, repeat the simulation with 5 random nn's from the current generation + let mut simulations = Vec::new(); + + // Using the same original nn, repeat the simulation with 5 random nn's from the current generation concurrently for _ in 0..SIMULATION_ROUNDS { - let random_nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, thread_rng().gen_range(0..self.population_size))); - let random_fann = Fann::from_file(&random_nn) - .with_context(|| format!("Failed to load random nn"))?; + let random_nn_index = thread_rng().gen_range(0..self.population_size); + let id = self.id.clone(); + let folder = self.folder.clone(); + let generation = self.generation; - let inputs: Vec = (0..NEURAL_NETWORK_SHAPE[0]).map(|_| thread_rng().gen_range(-1.0..1.0)).collect(); - let outputs = fann.run(&inputs) - .with_context(|| format!("Failed to run nn"))?; - let random_outputs = random_fann.run(&inputs) - .with_context(|| format!("Failed to run random nn"))?; - - // Average the difference between the outputs of the nn and random_nn and add the result to score - let mut round_score = 0.0; - for (o, r) in outputs.iter().zip(random_outputs.iter()) { - round_score += o - r; - } - score += round_score / fann.get_num_output() as f32; + let random_nn = folder.join(format!("{}", generation)).join(format!("{:06}_fighter_nn_{}.net", id, random_nn_index)); + let nn_clone = nn.clone(); // Clone the path to use in the async block + + let config1_arg = format!("-NN1Config=\"{}\"", nn_clone.to_str().unwrap()); + let config2_arg = format!("-NN2Config=\"{}\"", random_nn.to_str().unwrap()); + let disable_unreal_rendering_arg = "-nullrhi".to_string(); + + let future = async move { + // Construct the score file path + let nn_id = format!("{:06}_fighter_nn_{}", id, i); + let random_nn_id = format!("{:06}_fighter_nn_{}", id, random_nn_index); + let score_file_name = format!("{}_vs_{}.txt", nn_id, random_nn_id); + let score_file = folder.join(format!("{}", generation)).join(&score_file_name); + // Check if score file already exists before running the simulation + if score_file.exists() { + let round_score = read_score_from_file(&score_file, &nn_id) + .with_context(|| format!("Failed to read score from file: {:?}", score_file_name))?; + return Ok::(round_score); + } + + // Check if the opposite round score has been determined + let opposite_score_file = folder.join(format!("{}", generation)).join(format!("{}_vs_{}.txt", random_nn_id, nn_id)); + if opposite_score_file.exists() { + let round_score = read_score_from_file(&opposite_score_file, &nn_id) + .with_context(|| format!("Failed to read score from file: {:?}", opposite_score_file))?; + return Ok::(1.0 - round_score); + } + + if thread_rng().gen_range(0..100) < 4 { + let _output = Command::new(GAME_EXECUTABLE_PATH) + .arg(&config1_arg) + .arg(&config2_arg) + .output() + .await + .expect("Failed to execute game"); + } else { + let _output = Command::new(GAME_EXECUTABLE_PATH) + .arg(&config1_arg) + .arg(&config2_arg) + .arg(&disable_unreal_rendering_arg) + .output() + .await + .expect("Failed to execute game"); + } + + // Read the score from the file + let round_score = read_score_from_file(&score_file, &nn_id) + .with_context(|| format!("Failed to read score from file: {:?}", score_file_name))?; + + Ok::(round_score) + }; + + simulations.push(future); } - - score /= 5.0; + + // Wait for all simulation rounds to complete + let results: Result, Error> = join_all(simulations).await.into_iter().collect(); + + let score = results?.into_iter().sum::() / SIMULATION_ROUNDS as f32; + trace!("NN {:06}_fighter_nn_{} scored {}", self.id, i, score); self.scores[self.generation as usize].insert(i as u64, score); } - + Ok(()) } - fn mutate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { + fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { let survivor_count = (self.population_size as f32 * SURVIVAL_RATE) as usize; // Create the new generation folder @@ -234,3 +282,23 @@ impl GeneticNode for FighterNN { })) } } + +fn read_score_from_file(file_path: &Path, nn_id: &str) -> Result { + let file = File::open(file_path)?; + let reader = BufReader::new(file); + + for line in reader.lines() { + let line = line?; + if line.starts_with(nn_id) { + let parts: Vec<&str> = line.split(':').collect(); + if parts.len() == 2 { + return parts[1].trim().parse::().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)); + } + } + } + + Err(io::Error::new( + io::ErrorKind::NotFound, + "NN ID not found in scores file", + )) +} \ No newline at end of file diff --git a/gemla/src/bin/test_state/mod.rs b/gemla/src/bin/test_state/mod.rs index 35ef7cc..0e8d11d 100644 --- a/gemla/src/bin/test_state/mod.rs +++ b/gemla/src/bin/test_state/mod.rs @@ -2,6 +2,7 @@ use gemla::{core::genetic_node::{GeneticNode, GeneticNodeContext}, error::Error} use rand::prelude::*; use serde::{Deserialize, Serialize}; use uuid::Uuid; +use async_trait::async_trait; const POPULATION_SIZE: u64 = 5; const POPULATION_REDUCTION_SIZE: u64 = 3; @@ -11,8 +12,9 @@ pub struct TestState { pub population: Vec, } +#[async_trait] impl GeneticNode for TestState { - fn initialize(_context: &GeneticNodeContext) -> Result, Error> { + fn initialize(_context: GeneticNodeContext) -> Result, Error> { let mut population: Vec = vec![]; for _ in 0..POPULATION_SIZE { @@ -22,7 +24,7 @@ impl GeneticNode for TestState { Ok(Box::new(TestState { population })) } - fn simulate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { + async fn simulate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { let mut rng = thread_rng(); self.population = self @@ -34,7 +36,7 @@ impl GeneticNode for TestState { Ok(()) } - fn mutate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { + fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { let mut rng = thread_rng(); let mut v = self.population.clone(); @@ -83,7 +85,7 @@ impl GeneticNode for TestState { let mut result = TestState { population: v }; - result.mutate(&GeneticNodeContext { + result.mutate(GeneticNodeContext { id: id.clone(), generation: 0, max_generations: 0, @@ -101,7 +103,7 @@ mod tests { #[test] fn test_initialize() { let state = TestState::initialize( - &GeneticNodeContext { + GeneticNodeContext { id: Uuid::new_v4(), generation: 0, max_generations: 0, @@ -111,8 +113,8 @@ mod tests { assert_eq!(state.population.len(), POPULATION_SIZE as usize); } - #[test] - fn test_simulate() { + #[tokio::test] + async fn test_simulate() { let mut state = TestState { population: vec![1, 1, 2, 3], }; @@ -120,31 +122,31 @@ mod tests { let original_population = state.population.clone(); state.simulate( - &GeneticNodeContext { + GeneticNodeContext { id: Uuid::new_v4(), generation: 0, max_generations: 0, } - ).unwrap(); + ).await.unwrap(); assert!(original_population .iter() .zip(state.population.iter()) .all(|(&a, &b)| b >= a - 1 && b <= a + 2)); state.simulate( - &GeneticNodeContext { + GeneticNodeContext { id: Uuid::new_v4(), generation: 0, max_generations: 0, } - ).unwrap(); + ).await.unwrap(); state.simulate( - &GeneticNodeContext { + GeneticNodeContext { id: Uuid::new_v4(), generation: 0, max_generations: 0, } - ).unwrap(); + ).await.unwrap(); assert!(original_population .iter() .zip(state.population.iter()) @@ -158,7 +160,7 @@ mod tests { }; state.mutate( - &GeneticNodeContext { + GeneticNodeContext { id: Uuid::new_v4(), generation: 0, max_generations: 0, diff --git a/gemla/src/core/genetic_node.rs b/gemla/src/core/genetic_node.rs index c1e669f..92a7f48 100644 --- a/gemla/src/core/genetic_node.rs +++ b/gemla/src/core/genetic_node.rs @@ -8,6 +8,7 @@ use anyhow::Context; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use uuid::Uuid; +use async_trait::async_trait; /// An enum used to control the state of a [`GeneticNode`] /// @@ -24,6 +25,7 @@ pub enum GeneticState { Finish, } +#[derive(Clone)] pub struct GeneticNodeContext { pub generation: u64, pub max_generations: u64, @@ -33,20 +35,21 @@ pub struct GeneticNodeContext { /// A trait used to interact with the internal state of nodes within the [`Bracket`] /// /// [`Bracket`]: crate::bracket::Bracket -pub trait GeneticNode { +#[async_trait] +pub trait GeneticNode: Send { /// Initializes a new instance of a [`GeneticState`]. /// /// # Examples /// TODO - fn initialize(context: &GeneticNodeContext) -> Result, Error>; + fn initialize(context: GeneticNodeContext) -> Result, Error>; - fn simulate(&mut self, context: &GeneticNodeContext) -> Result<(), Error>; + async fn simulate(&mut self, context: GeneticNodeContext) -> Result<(), Error>; /// Mutates members in a population and/or crossbreeds them to produce new offspring. /// /// # Examples /// TODO - fn mutate(&mut self, context: &GeneticNodeContext) -> Result<(), Error>; + fn mutate(&mut self, context: GeneticNodeContext) -> Result<(), Error>; fn merge(left: &Self, right: &Self, id: &Uuid) -> Result, Error>; } @@ -76,7 +79,7 @@ impl Default for GeneticNodeWrapper { impl GeneticNodeWrapper where - T: GeneticNode + Debug, + T: GeneticNode + Debug + Send, { pub fn new(max_generations: u64) -> Self { GeneticNodeWrapper:: { @@ -115,7 +118,7 @@ where self.state } - pub fn process_node(&mut self) -> Result { + pub async fn process_node(&mut self) -> Result { let context = GeneticNodeContext { generation: self.generation, max_generations: self.max_generations, @@ -124,11 +127,11 @@ where match (self.state, &mut self.node) { (GeneticState::Initialize, _) => { - self.node = Some(*T::initialize(&context)?); + self.node = Some(*T::initialize(context.clone())?); self.state = GeneticState::Simulate; } (GeneticState::Simulate, Some(n)) => { - n.simulate(&context) + n.simulate(context.clone()).await .with_context(|| format!("Error simulating node: {:?}", self))?; self.state = if self.generation >= self.max_generations { @@ -138,7 +141,7 @@ where }; } (GeneticState::Mutate, Some(n)) => { - n.mutate(&context) + n.mutate(context.clone()) .with_context(|| format!("Error mutating node: {:?}", self))?; self.generation += 1; @@ -157,23 +160,25 @@ mod tests { use super::*; use crate::error::Error; use anyhow::anyhow; + use async_trait::async_trait; #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] struct TestState { pub score: f64, } + #[async_trait] impl GeneticNode for TestState { - fn simulate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { + async fn simulate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { self.score += 1.0; Ok(()) } - fn mutate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { + fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { Ok(()) } - fn initialize(_context: &GeneticNodeContext) -> Result, Error> { + fn initialize(_context: GeneticNodeContext) -> Result, Error> { Ok(Box::new(TestState { score: 0.0 })) } @@ -270,16 +275,16 @@ mod tests { Ok(()) } - #[test] - fn test_process_node() -> Result<(), Error> { + #[tokio::test] + async fn test_process_node() -> Result<(), Error> { let mut genetic_node = GeneticNodeWrapper::::new(2); assert_eq!(genetic_node.state(), GeneticState::Initialize); - assert_eq!(genetic_node.process_node()?, GeneticState::Simulate); - assert_eq!(genetic_node.process_node()?, GeneticState::Mutate); - assert_eq!(genetic_node.process_node()?, GeneticState::Simulate); - assert_eq!(genetic_node.process_node()?, GeneticState::Finish); - assert_eq!(genetic_node.process_node()?, GeneticState::Finish); + assert_eq!(genetic_node.process_node().await?, GeneticState::Simulate); + assert_eq!(genetic_node.process_node().await?, GeneticState::Mutate); + assert_eq!(genetic_node.process_node().await?, GeneticState::Simulate); + assert_eq!(genetic_node.process_node().await?, GeneticState::Finish); + assert_eq!(genetic_node.process_node().await?, GeneticState::Finish); Ok(()) } diff --git a/gemla/src/core/mod.rs b/gemla/src/core/mod.rs index 152ed6d..42a1fc7 100644 --- a/gemla/src/core/mod.rs +++ b/gemla/src/core/mod.rs @@ -5,10 +5,11 @@ pub mod genetic_node; use crate::{error::Error, tree::Tree}; use file_linked::{constants::data_format::DataFormat, FileLinked}; -use futures::{future, future::BoxFuture}; +use futures::future; use genetic_node::{GeneticNode, GeneticNodeWrapper, GeneticState}; use log::{info, trace, warn}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use tokio::task::JoinHandle; use std::{ collections::HashMap, fmt::Debug, fs::File, io::ErrorKind, marker::Send, mem, path::Path, time::Instant, @@ -65,15 +66,15 @@ pub struct GemlaConfig { /// individuals. /// /// [`GeneticNode`]: genetic_node::GeneticNode -pub struct Gemla<'a, T> +pub struct Gemla where T: Serialize + Clone, { pub data: FileLinked<(Option>, GemlaConfig)>, - threads: HashMap, Error>>>, + threads: HashMap, Error>>>, } -impl<'a, T: 'a> Gemla<'a, T> +impl Gemla where T: GeneticNode + Serialize + DeserializeOwned + Debug + Clone + Send, { @@ -147,7 +148,9 @@ where trace!("Adding node to process list {}", node.id()); self.threads - .insert(node.id(), Box::pin(Gemla::process_node(node))); + .insert(node.id(), tokio::spawn(async move { + Gemla::process_node(node).await + })); } else { trace!("No node found to process, joining threads"); @@ -163,9 +166,10 @@ where trace!("Joining threads for nodes {:?}", self.threads.keys()); let results = future::join_all(self.threads.values_mut()).await; + // Converting a list of results into a result wrapping the list let reduced_results: Result>, Error> = - results.into_iter().collect(); + results.into_iter().flatten().collect(); self.threads.clear(); // We need to retrieve the processed nodes from the resulting list and replace them in the original list @@ -323,7 +327,12 @@ where let node_state_time = Instant::now(); let node_state = node.state(); - node.process_node()?; + node.process_node().await?; + + if node.state() == GeneticState::Simulate + { + node.process_node().await?; + } trace!( "{:?} completed in {:?} for {}", @@ -346,6 +355,8 @@ mod tests { use serde::{Deserialize, Serialize}; use std::path::PathBuf; use std::fs; + use async_trait::async_trait; + use tokio::runtime::Runtime; use self::genetic_node::GeneticNodeContext; @@ -378,17 +389,18 @@ mod tests { pub score: f64, } + #[async_trait] impl genetic_node::GeneticNode for TestState { - fn simulate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { + async fn simulate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { self.score += 1.0; Ok(()) } - fn mutate(&mut self, _context: &GeneticNodeContext) -> Result<(), Error> { + fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { Ok(()) } - fn initialize(_context: &GeneticNodeContext) -> Result, Error> { + fn initialize(_context: GeneticNodeContext) -> Result, Error> { Ok(Box::new(TestState { score: 0.0 })) } @@ -401,66 +413,84 @@ mod tests { } } - #[test] - fn test_new() -> Result<(), Error> { + #[tokio::test] + async fn test_new() -> Result<(), Error> { let path = PathBuf::from("test_new_non_existing"); - CleanUp::new(&path).run(|p| { - assert!(!path.exists()); + // Use `spawn_blocking` to run synchronous code that needs to call async code internally. + tokio::task::spawn_blocking(move || { + let rt = Runtime::new().unwrap(); // Create a new Tokio runtime for the async block. + CleanUp::new(&path).run(move |p| { + rt.block_on(async { + assert!(!path.exists()); - // Testing initial creation - let mut config = GemlaConfig { - generations_per_height: 1, - overwrite: true - }; - let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; + // Testing initial creation + let mut config = GemlaConfig { + generations_per_height: 1, + overwrite: true, + }; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; - smol::block_on(gemla.simulate(2))?; - assert_eq!(gemla.data.readonly().0.as_ref().unwrap().height(), 2); - - drop(gemla); - assert!(path.exists()); + // Now we can use `.await` within the spawned blocking task. + gemla.simulate(2).await?; + assert_eq!(gemla.data.readonly().0.as_ref().unwrap().height(), 2); - // Testing overwriting data - let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; + drop(gemla); + assert!(path.exists()); - smol::block_on(gemla.simulate(2))?; - assert_eq!(gemla.data.readonly().0.as_ref().unwrap().height(), 2); + // Testing overwriting data + let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; - drop(gemla); - assert!(path.exists()); + gemla.simulate(2).await?; + assert_eq!(gemla.data.readonly().0.as_ref().unwrap().height(), 2); - // Testing not-overwriting data - config.overwrite = false; - let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; + drop(gemla); + assert!(path.exists()); - smol::block_on(gemla.simulate(2))?; - assert_eq!(gemla.tree_ref().unwrap().height(), 4); + // Testing not-overwriting data + config.overwrite = false; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; - drop(gemla); - assert!(path.exists()); + gemla.simulate(2).await?; + assert_eq!(gemla.tree_ref().unwrap().height(), 4); - Ok(()) - }) + drop(gemla); + assert!(path.exists()); + + Ok(()) + }) + }) + }).await.unwrap()?; // Wait for the blocking task to complete, then handle the Result. + + Ok(()) } - #[test] - fn test_simulate() -> Result<(), Error> { + #[tokio::test] + async fn test_simulate() -> Result<(), Error> { let path = PathBuf::from("test_simulate"); - CleanUp::new(&path).run(|p| { - // Testing initial creation - let config = GemlaConfig { - generations_per_height: 10, - overwrite: true - }; - let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; + // Use `spawn_blocking` to run the synchronous closure that internally awaits async code. + tokio::task::spawn_blocking(move || { + let rt = Runtime::new().unwrap(); // Create a new Tokio runtime for the async block. + CleanUp::new(&path).run(move |p| { + rt.block_on(async { + // Testing initial creation + let config = GemlaConfig { + generations_per_height: 10, + overwrite: true, + }; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; - smol::block_on(gemla.simulate(5))?; - let tree = gemla.tree_ref().unwrap(); - assert_eq!(tree.height(), 5); - assert_eq!(tree.val.as_ref().unwrap().score, 50.0); + // Now we can use `.await` within the spawned blocking task. + gemla.simulate(5).await?; + let tree = gemla.tree_ref().unwrap(); + assert_eq!(tree.height(), 5); + assert_eq!(tree.val.as_ref().unwrap().score, 50.0); - Ok(()) - }) + Ok(()) + }) + }) + }).await.unwrap()?; // Wait for the blocking task to complete, then handle the Result. + + Ok(()) } } From ac71b28c7c38348f767390973f134b38477d4986 Mon Sep 17 00:00:00 2001 From: vandomej Date: Thu, 21 Mar 2024 12:55:42 -0700 Subject: [PATCH 11/26] Adding global sempahore to better control resources. --- gemla/src/bin/bin.rs | 3 +- gemla/src/bin/fighter_nn/mod.rs | 178 +++++++++++++++++++------------- gemla/src/bin/test_state/mod.rs | 6 ++ gemla/src/core/genetic_node.rs | 20 ++-- gemla/src/core/mod.rs | 20 ++-- 5 files changed, 140 insertions(+), 87 deletions(-) diff --git a/gemla/src/bin/bin.rs b/gemla/src/bin/bin.rs index afb2ba7..200bb2e 100644 --- a/gemla/src/bin/bin.rs +++ b/gemla/src/bin/bin.rs @@ -16,7 +16,7 @@ use fighter_nn::FighterNN; use clap::Parser; use anyhow::Result; -// const NUM_THREADS: usize = 12; +// const NUM_THREADS: usize = 2; #[derive(Parser)] #[command(version, about, long_about = None)] @@ -47,6 +47,7 @@ fn main() -> Result<()> { GemlaConfig { generations_per_height: 10, overwrite: false, + shared_semaphore_concurrency_limit: 30, }, DataFormat::Json, ))?; diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index bb641d8..2de6b96 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -1,6 +1,6 @@ extern crate fann; -use std::{fs::{self, File}, io::{self, BufRead, BufReader}, path::{Path, PathBuf}}; +use std::{fs::{self, File}, io::{self, BufRead, BufReader}, path::{Path, PathBuf}, sync::Arc}; use fann::{ActivationFunc, Fann}; use futures::future::join_all; use gemla::{core::genetic_node::{GeneticNode, GeneticNodeContext}, error::Error}; @@ -8,9 +8,9 @@ use rand::prelude::*; use rand::distributions::{Distribution, Uniform}; use serde::{Deserialize, Serialize}; use anyhow::Context; +use tokio::process::Command; use uuid::Uuid; use std::collections::HashMap; -use tokio::process::Command; use async_trait::async_trait; const BASE_DIR: &str = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations"; @@ -79,82 +79,116 @@ impl GeneticNode for FighterNN { })) } - async fn simulate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { + async fn simulate(&mut self, context: GeneticNodeContext) -> Result<(), Error> { + trace!("Context: {:?}", context); + let mut tasks = Vec::new(); + // For each nn in the current generation: for i in 0..self.population_size { - // load the nn - let nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, i)); - let mut simulations = Vec::new(); - - // Using the same original nn, repeat the simulation with 5 random nn's from the current generation concurrently - for _ in 0..SIMULATION_ROUNDS { - let random_nn_index = thread_rng().gen_range(0..self.population_size); - let id = self.id.clone(); - let folder = self.folder.clone(); - let generation = self.generation; + let self_clone = self.clone(); + let semaphore_clone = Arc::clone(context.semaphore.as_ref().unwrap()); - let random_nn = folder.join(format!("{}", generation)).join(format!("{:06}_fighter_nn_{}.net", id, random_nn_index)); - let nn_clone = nn.clone(); // Clone the path to use in the async block - - let config1_arg = format!("-NN1Config=\"{}\"", nn_clone.to_str().unwrap()); - let config2_arg = format!("-NN2Config=\"{}\"", random_nn.to_str().unwrap()); - let disable_unreal_rendering_arg = "-nullrhi".to_string(); - - let future = async move { - // Construct the score file path - let nn_id = format!("{:06}_fighter_nn_{}", id, i); - let random_nn_id = format!("{:06}_fighter_nn_{}", id, random_nn_index); - let score_file_name = format!("{}_vs_{}.txt", nn_id, random_nn_id); - let score_file = folder.join(format!("{}", generation)).join(&score_file_name); + let task = async move { + let nn = self_clone.folder.join(format!("{}", self_clone.generation)).join(format!("{:06}_fighter_nn_{}.net", self_clone.id, i)); + let mut simulations = Vec::new(); + + // Using the same original nn, repeat the simulation with 5 random nn's from the current generation concurrently + for _ in 0..SIMULATION_ROUNDS { + let random_nn_index = thread_rng().gen_range(0..self_clone.population_size); + let id = self_clone.id.clone(); + let folder = self_clone.folder.clone(); + let generation = self_clone.generation; + let semaphore_clone = Arc::clone(&semaphore_clone); - // Check if score file already exists before running the simulation - if score_file.exists() { + let random_nn = folder.join(format!("{}", generation)).join(format!("{:06}_fighter_nn_{}.net", id, random_nn_index)); + let nn_clone = nn.clone(); // Clone the path to use in the async block + + let config1_arg = format!("-NN1Config=\"{}\"", nn_clone.to_str().unwrap()); + let config2_arg = format!("-NN2Config=\"{}\"", random_nn.to_str().unwrap()); + let disable_unreal_rendering_arg = "-nullrhi".to_string(); + + + + let future = async move { + let permit = semaphore_clone.acquire_owned().await.with_context(|| "Failed to acquire semaphore permit")?; + + // Construct the score file path + let nn_id = format!("{:06}_fighter_nn_{}", id, i); + let random_nn_id = format!("{:06}_fighter_nn_{}", id, random_nn_index); + let score_file_name = format!("{}_vs_{}.txt", nn_id, random_nn_id); + let score_file = folder.join(format!("{}", generation)).join(&score_file_name); + + // Check if score file already exists before running the simulation + if score_file.exists() { + let round_score = read_score_from_file(&score_file, &nn_id) + .with_context(|| format!("Failed to read score from file: {:?}", score_file_name))?; + return Ok::(round_score); + } + + // Check if the opposite round score has been determined + let opposite_score_file = folder.join(format!("{}", generation)).join(format!("{}_vs_{}.txt", random_nn_id, nn_id)); + if opposite_score_file.exists() { + let round_score = read_score_from_file(&opposite_score_file, &nn_id) + .with_context(|| format!("Failed to read score from file: {:?}", opposite_score_file))?; + return Ok::(1.0 - round_score); + } + + let _output = if thread_rng().gen_range(0..100) < 0 { + Command::new(GAME_EXECUTABLE_PATH) + .arg(&config1_arg) + .arg(&config2_arg) + .output() + .await + .expect("Failed to execute game") + } else { + Command::new(GAME_EXECUTABLE_PATH) + .arg(&config1_arg) + .arg(&config2_arg) + .arg(&disable_unreal_rendering_arg) + .output() + .await + .expect("Failed to execute game") + }; + + drop(permit); + + // Read the score from the file let round_score = read_score_from_file(&score_file, &nn_id) .with_context(|| format!("Failed to read score from file: {:?}", score_file_name))?; - return Ok::(round_score); - } - // Check if the opposite round score has been determined - let opposite_score_file = folder.join(format!("{}", generation)).join(format!("{}_vs_{}.txt", random_nn_id, nn_id)); - if opposite_score_file.exists() { - let round_score = read_score_from_file(&opposite_score_file, &nn_id) - .with_context(|| format!("Failed to read score from file: {:?}", opposite_score_file))?; - return Ok::(1.0 - round_score); - } - - if thread_rng().gen_range(0..100) < 4 { - let _output = Command::new(GAME_EXECUTABLE_PATH) - .arg(&config1_arg) - .arg(&config2_arg) - .output() - .await - .expect("Failed to execute game"); - } else { - let _output = Command::new(GAME_EXECUTABLE_PATH) - .arg(&config1_arg) - .arg(&config2_arg) - .arg(&disable_unreal_rendering_arg) - .output() - .await - .expect("Failed to execute game"); - } - - // Read the score from the file - let round_score = read_score_from_file(&score_file, &nn_id) - .with_context(|| format!("Failed to read score from file: {:?}", score_file_name))?; - - Ok::(round_score) + Ok::(round_score) + }; + + simulations.push(future); + } + + // Wait for all simulation rounds to complete + let results: Result, Error> = join_all(simulations).await.into_iter().collect(); + + let score = match results { + Ok(scores) => scores.into_iter().sum::() / SIMULATION_ROUNDS as f32, + Err(e) => return Err(e), // Return the error if results collection failed }; - - simulations.push(future); + trace!("NN {:06}_fighter_nn_{} scored {}", self_clone.id, i, score); + Ok((i, score)) + }; + + tasks.push(task); + } + + let results = join_all(tasks).await; + + for result in results { + match result { + Ok((index, score)) => { + // Update the original `self` object with the score. + self.scores[self.generation as usize].insert(index as u64, score); + }, + Err(e) => { + // Handle task panic or execution error + return Err(Error::Other(anyhow::anyhow!(format!("Task failed: {:?}", e)))); + }, } - - // Wait for all simulation rounds to complete - let results: Result, Error> = join_all(simulations).await.into_iter().collect(); - - let score = results?.into_iter().sum::() / SIMULATION_ROUNDS as f32; - trace!("NN {:06}_fighter_nn_{} scored {}", self.id, i, score); - self.scores[self.generation as usize].insert(i as u64, score); } Ok(()) @@ -228,9 +262,9 @@ impl GeneticNode for FighterNN { if thread_rng().gen_range(0..100) < 20 { c.weight += thread_rng().gen_range(-0.1..0.1); } - // else if thread_rng().gen_range(0..100) < 5 { - // c.weight += thread_rng().gen_range(-0.3..0.3); - // } + else if thread_rng().gen_range(0..100) < 5 { + c.weight += thread_rng().gen_range(-0.3..0.3); + } } fann.set_connections(&connections); diff --git a/gemla/src/bin/test_state/mod.rs b/gemla/src/bin/test_state/mod.rs index 0e8d11d..73df36e 100644 --- a/gemla/src/bin/test_state/mod.rs +++ b/gemla/src/bin/test_state/mod.rs @@ -89,6 +89,7 @@ impl GeneticNode for TestState { id: id.clone(), generation: 0, max_generations: 0, + semaphore: None, })?; Ok(Box::new(result)) @@ -107,6 +108,7 @@ mod tests { id: Uuid::new_v4(), generation: 0, max_generations: 0, + semaphore: None, } ).unwrap(); @@ -126,6 +128,7 @@ mod tests { id: Uuid::new_v4(), generation: 0, max_generations: 0, + semaphore: None, } ).await.unwrap(); assert!(original_population @@ -138,6 +141,7 @@ mod tests { id: Uuid::new_v4(), generation: 0, max_generations: 0, + semaphore: None, } ).await.unwrap(); state.simulate( @@ -145,6 +149,7 @@ mod tests { id: Uuid::new_v4(), generation: 0, max_generations: 0, + semaphore: None, } ).await.unwrap(); assert!(original_population @@ -164,6 +169,7 @@ mod tests { id: Uuid::new_v4(), generation: 0, max_generations: 0, + semaphore: None, } ).unwrap(); diff --git a/gemla/src/core/genetic_node.rs b/gemla/src/core/genetic_node.rs index 92a7f48..0838dc8 100644 --- a/gemla/src/core/genetic_node.rs +++ b/gemla/src/core/genetic_node.rs @@ -6,7 +6,8 @@ use crate::error::Error; use anyhow::Context; use serde::{Deserialize, Serialize}; -use std::fmt::Debug; +use tokio::sync::Semaphore; +use std::{fmt::Debug, sync::Arc}; use uuid::Uuid; use async_trait::async_trait; @@ -25,11 +26,12 @@ pub enum GeneticState { Finish, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct GeneticNodeContext { pub generation: u64, pub max_generations: u64, pub id: Uuid, + pub semaphore: Option>, } /// A trait used to interact with the internal state of nodes within the [`Bracket`] @@ -118,11 +120,12 @@ where self.state } - pub async fn process_node(&mut self) -> Result { + pub async fn process_node(&mut self, semaphore: Arc) -> Result { let context = GeneticNodeContext { generation: self.generation, max_generations: self.max_generations, id: self.id, + semaphore: Some(semaphore), }; match (self.state, &mut self.node) { @@ -278,13 +281,14 @@ mod tests { #[tokio::test] async fn test_process_node() -> Result<(), Error> { let mut genetic_node = GeneticNodeWrapper::::new(2); + let semaphore = Arc::new(Semaphore::new(1)); assert_eq!(genetic_node.state(), GeneticState::Initialize); - assert_eq!(genetic_node.process_node().await?, GeneticState::Simulate); - assert_eq!(genetic_node.process_node().await?, GeneticState::Mutate); - assert_eq!(genetic_node.process_node().await?, GeneticState::Simulate); - assert_eq!(genetic_node.process_node().await?, GeneticState::Finish); - assert_eq!(genetic_node.process_node().await?, GeneticState::Finish); + assert_eq!(genetic_node.process_node(semaphore.clone()).await?, GeneticState::Simulate); + assert_eq!(genetic_node.process_node(semaphore.clone()).await?, GeneticState::Mutate); + assert_eq!(genetic_node.process_node(semaphore.clone()).await?, GeneticState::Simulate); + assert_eq!(genetic_node.process_node(semaphore.clone()).await?, GeneticState::Finish); + assert_eq!(genetic_node.process_node(semaphore.clone()).await?, GeneticState::Finish); Ok(()) } diff --git a/gemla/src/core/mod.rs b/gemla/src/core/mod.rs index 42a1fc7..b3e7faa 100644 --- a/gemla/src/core/mod.rs +++ b/gemla/src/core/mod.rs @@ -10,9 +10,9 @@ use genetic_node::{GeneticNode, GeneticNodeWrapper, GeneticState}; use log::{info, trace, warn}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use tokio::task::JoinHandle; +use tokio::sync::Semaphore; use std::{ - collections::HashMap, fmt::Debug, fs::File, io::ErrorKind, marker::Send, mem, path::Path, - time::Instant, + collections::HashMap, fmt::Debug, fs::File, io::ErrorKind, marker::Send, mem, path::Path, sync::Arc, time::Instant }; use uuid::Uuid; @@ -58,6 +58,7 @@ type SimulationTree = Box>>; pub struct GemlaConfig { pub generations_per_height: u64, pub overwrite: bool, + pub shared_semaphore_concurrency_limit: usize, } /// Creates a tournament style bracket for simulating and evaluating nodes of type `T` implementing [`GeneticNode`]. @@ -72,6 +73,7 @@ where { pub data: FileLinked<(Option>, GemlaConfig)>, threads: HashMap, Error>>>, + semaphore: Arc, } impl Gemla @@ -89,11 +91,13 @@ where FileLinked::from_file(path, data_format)? }, threads: HashMap::new(), + semaphore: Arc::new(Semaphore::new(config.shared_semaphore_concurrency_limit)), }), // If the file doesn't exist we must create it Err(error) if error.kind() == ErrorKind::NotFound => Ok(Gemla { data: FileLinked::new((None, config), path, data_format)?, threads: HashMap::new(), + semaphore: Arc::new(Semaphore::new(config.shared_semaphore_concurrency_limit)), }), Err(error) => Err(Error::IO(error)), } @@ -147,9 +151,11 @@ where { trace!("Adding node to process list {}", node.id()); + let semaphore = self.semaphore.clone(); + self.threads .insert(node.id(), tokio::spawn(async move { - Gemla::process_node(node).await + Gemla::process_node(node, semaphore).await })); } else { trace!("No node found to process, joining threads"); @@ -323,15 +329,15 @@ where tree.val.state() == GeneticState::Finish } - async fn process_node(mut node: GeneticNodeWrapper) -> Result, Error> { + async fn process_node(mut node: GeneticNodeWrapper, semaphore: Arc) -> Result, Error> { let node_state_time = Instant::now(); let node_state = node.state(); - node.process_node().await?; + node.process_node(semaphore.clone()).await?; if node.state() == GeneticState::Simulate { - node.process_node().await?; + node.process_node(semaphore.clone()).await?; } trace!( @@ -427,6 +433,7 @@ mod tests { let mut config = GemlaConfig { generations_per_height: 1, overwrite: true, + shared_semaphore_concurrency_limit: 1, }; let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; @@ -476,6 +483,7 @@ mod tests { let config = GemlaConfig { generations_per_height: 10, overwrite: true, + shared_semaphore_concurrency_limit: 1, }; let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; From 1301d457a9c390d7465071c04f3516cc3e2f0f49 Mon Sep 17 00:00:00 2001 From: vandomej Date: Sat, 30 Mar 2024 21:08:56 -0700 Subject: [PATCH 12/26] Setting up dynamic nn shapes --- analysis.py | 4 +- gemla/src/bin/bin.rs | 6 +- gemla/src/bin/fighter_nn/mod.rs | 148 +++++++++++++++++++++++--------- 3 files changed, 111 insertions(+), 47 deletions(-) diff --git a/analysis.py b/analysis.py index f5db293..b239e4b 100644 --- a/analysis.py +++ b/analysis.py @@ -14,7 +14,7 @@ def hierarchy_pos(G, root=None, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5) else: root = random.choice(list(G.nodes)) - def _hierarchy_pos(G, root, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5, pos=None, parent=None): + def _hierarchy_pos(G, root, width=2., vert_gap=0.2, vert_loc=0, xcenter=0.5, pos=None, parent=None): if pos is None: pos = {root: (xcenter, vert_loc)} else: @@ -28,7 +28,7 @@ def hierarchy_pos(G, root=None, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5) nextx = xcenter - width / 2 - dx / 2 for child in children: nextx += dx - pos = _hierarchy_pos(G, child, width=dx, vert_gap=vert_gap, + pos = _hierarchy_pos(G, child, width=dx*2.0, vert_gap=vert_gap, vert_loc=vert_loc - vert_gap, xcenter=nextx, pos=pos, parent=root) return pos diff --git a/gemla/src/bin/bin.rs b/gemla/src/bin/bin.rs index 200bb2e..16539fb 100644 --- a/gemla/src/bin/bin.rs +++ b/gemla/src/bin/bin.rs @@ -45,9 +45,9 @@ fn main() -> Result<()> { let mut gemla = log_error(Gemla::::new( &PathBuf::from(args.file), GemlaConfig { - generations_per_height: 10, + generations_per_height: 5, overwrite: false, - shared_semaphore_concurrency_limit: 30, + shared_semaphore_concurrency_limit: 50, }, DataFormat::Json, ))?; @@ -59,7 +59,7 @@ fn main() -> Result<()> { // Example placeholder loop to continuously run simulate loop { // Arbitrary loop count for demonstration - gemla.simulate(5).await?; + gemla.simulate(1).await?; } }); diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index 2de6b96..3baae77 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -15,7 +15,18 @@ use async_trait::async_trait; const BASE_DIR: &str = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations"; const POPULATION: usize = 50; -const NEURAL_NETWORK_SHAPE: &[u32; 5] = &[14, 20, 20, 12, 8]; + +const NEURAL_NETWORK_INPUTS: usize = 14; +const NEURAL_NETWORK_OUTPUTS: usize = 8; +const NEURAL_NETWORK_HIDDEN_LAYERS_MIN: usize = 1; +const NEURAL_NETWORK_HIDDEN_LAYERS_MAX: usize = 10; +const NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN: usize = 3; +const NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MAX: usize = 35; +const NEURAL_NETWORK_INITIAL_WEIGHT_MIN: f32 = -2.0; +const NEURAL_NETWORK_INITIAL_WEIGHT_MAX: f32 = 2.0; +const NEURAL_NETWORK_CROSSBREED_SEGMENTS_MIN: usize = 1; +const NEURAL_NETWORK_CROSSBREED_SEGMENTS_MAX: usize = 20; + const SIMULATION_ROUNDS: usize = 5; const SURVIVAL_RATE: f32 = 0.5; const GAME_EXECUTABLE_PATH: &str = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Package\\Windows\\AI_Fight_Sim.exe"; @@ -38,6 +49,9 @@ pub struct FighterNN { pub generation: u64, // A map of each nn identifier in a generation and their physics score pub scores: Vec>, + // A map of the id of the nn in the current generation and their neural network shape + pub nn_shapes: HashMap>, + pub crossbreed_segments: usize } #[async_trait] @@ -55,14 +69,26 @@ impl GeneticNode for FighterNN { let gen_folder = folder.join("0"); fs::create_dir_all(&gen_folder) .with_context(|| format!("Failed to create or access the generation folder: {:?}", gen_folder))?; + + let nn_shapes = HashMap::new(); // Create the first generation in this folder for i in 0..POPULATION { // Filenames are stored in the format of "xxxxxx_fighter_nn_0.net", "xxxxxx_fighter_nn_1.net", etc. Where xxxxxx is the folder name let nn = gen_folder.join(format!("{:06}_fighter_nn_{}.net", context.id, i)); - let mut fann = Fann::new(NEURAL_NETWORK_SHAPE) + + // Randomly generate a neural network shape based on constants + let hidden_layers = thread_rng().gen_range(NEURAL_NETWORK_HIDDEN_LAYERS_MIN..NEURAL_NETWORK_HIDDEN_LAYERS_MAX); + let mut nn_shape = vec![NEURAL_NETWORK_INPUTS as u32]; + for _ in 0..hidden_layers { + nn_shape.push(thread_rng().gen_range(NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN..NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MAX) as u32); + } + nn_shape.push(NEURAL_NETWORK_OUTPUTS as u32); + nn_shapes.insert(i as u64, nn_shape.clone()); + + let mut fann = Fann::new(nn_shape.as_slice()) .with_context(|| "Failed to create nn")?; - fann.randomize_weights(-0.8, 0.8); + fann.randomize_weights(thread_rng().gen_range(NEURAL_NETWORK_INITIAL_WEIGHT_MIN..0.0), thread_rng().gen_range(0.0..=NEURAL_NETWORK_INITIAL_WEIGHT_MAX)); fann.set_activation_func_hidden(ActivationFunc::SigmoidSymmetric); fann.set_activation_func_output(ActivationFunc::SigmoidSymmetric); // This will overwrite any existing file with the same name @@ -76,6 +102,8 @@ impl GeneticNode for FighterNN { population_size: POPULATION, generation: 0, scores: vec![HashMap::new()], + nn_shapes, + crossbreed_segments: thread_rng().gen_range(NEURAL_NETWORK_CROSSBREED_SEGMENTS_MIN..NEURAL_NETWORK_CROSSBREED_SEGMENTS_MAX) })) } @@ -120,43 +148,59 @@ impl GeneticNode for FighterNN { // Check if score file already exists before running the simulation if score_file.exists() { - let round_score = read_score_from_file(&score_file, &nn_id) + let round_score = read_score_from_file(&score_file, &nn_id).await .with_context(|| format!("Failed to read score from file: {:?}", score_file_name))?; + + trace!("{} scored {}", nn_id, round_score); + return Ok::(round_score); } // Check if the opposite round score has been determined let opposite_score_file = folder.join(format!("{}", generation)).join(format!("{}_vs_{}.txt", random_nn_id, nn_id)); if opposite_score_file.exists() { - let round_score = read_score_from_file(&opposite_score_file, &nn_id) + let round_score = read_score_from_file(&opposite_score_file, &nn_id).await .with_context(|| format!("Failed to read score from file: {:?}", opposite_score_file))?; + + trace!("{} scored {}", nn_id, round_score); + return Ok::(1.0 - round_score); } - let _output = if thread_rng().gen_range(0..100) < 0 { - Command::new(GAME_EXECUTABLE_PATH) - .arg(&config1_arg) - .arg(&config2_arg) - .output() - .await - .expect("Failed to execute game") - } else { - Command::new(GAME_EXECUTABLE_PATH) - .arg(&config1_arg) - .arg(&config2_arg) - .arg(&disable_unreal_rendering_arg) - .output() - .await - .expect("Failed to execute game") - }; + // Run simulation until score file is generated + while !score_file.exists() { + let _output = if thread_rng().gen_range(0..100) < 1 { + Command::new(GAME_EXECUTABLE_PATH) + .arg(&config1_arg) + .arg(&config2_arg) + .output() + .await + .expect("Failed to execute game") + } else { + Command::new(GAME_EXECUTABLE_PATH) + .arg(&config1_arg) + .arg(&config2_arg) + .arg(&disable_unreal_rendering_arg) + .output() + .await + .expect("Failed to execute game") + }; + } drop(permit); - - // Read the score from the file - let round_score = read_score_from_file(&score_file, &nn_id) - .with_context(|| format!("Failed to read score from file: {:?}", score_file_name))?; - Ok::(round_score) + // Read the score from the file + if score_file.exists() { + let round_score = read_score_from_file(&score_file, &nn_id).await + .with_context(|| format!("Failed to read score from file: {:?}", score_file_name))?; + + trace!("{} scored {}", nn_id, round_score); + + Ok(round_score) + } else { + trace!("Score file not found: {:?}", score_file_name); + Ok(0.0) + } }; simulations.push(future); @@ -219,9 +263,11 @@ impl GeneticNode for FighterNN { for i in 0..survivor_count { let nn_id = to_keep[i]; let nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, nn_id)); - let mut fann = Fann::from_file(&nn) + let fann = Fann::from_file(&nn) .with_context(|| format!("Failed to load nn"))?; + let new_fann = fann.try_clone(); + // Load another nn from the current generation and cross breed it with the current nn let cross_nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, to_keep[thread_rng().gen_range(0..survivor_count)])); let cross_fann = Fann::from_file(&cross_nn) @@ -239,6 +285,7 @@ impl GeneticNode for FighterNN { start_points.push(start_point); } start_points.sort_unstable(); // Ensure segments are in order + trace!("Crossbreeding Start points: {:?}", start_points); for (j, &start) in start_points.iter().enumerate() { let end = if j < segment_count - 1 { @@ -317,22 +364,39 @@ impl GeneticNode for FighterNN { } } -fn read_score_from_file(file_path: &Path, nn_id: &str) -> Result { - let file = File::open(file_path)?; - let reader = BufReader::new(file); +async fn read_score_from_file(file_path: &Path, nn_id: &str) -> Result { + let mut attempts = 0; - for line in reader.lines() { - let line = line?; - if line.starts_with(nn_id) { - let parts: Vec<&str> = line.split(':').collect(); - if parts.len() == 2 { - return parts[1].trim().parse::().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)); - } + loop { + match File::open(file_path) { + Ok(file) => { + let reader = BufReader::new(file); + + for line in reader.lines() { + let line = line?; + if line.starts_with(nn_id) { + let parts: Vec<&str> = line.split(':').collect(); + if parts.len() == 2 { + return parts[1].trim().parse::().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)); + } + } + } + + return Err(io::Error::new( + io::ErrorKind::NotFound, + "NN ID not found in scores file", + )); + }, + Err(e) if e.kind() == io::ErrorKind::WouldBlock || e.kind() == io::ErrorKind::PermissionDenied || e.kind() == io::ErrorKind::Other => { + if attempts >= 5 { // Attempt 5 times before giving up. + return Err(e); + } + + attempts += 1; + // wait 1 second to ensure the file is written + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + }, + Err(e) => return Err(e), } } - - Err(io::Error::new( - io::ErrorKind::NotFound, - "NN ID not found in scores file", - )) } \ No newline at end of file From 567064922779fd22acedeb9fac06d065f6426df0 Mon Sep 17 00:00:00 2001 From: vandomej Date: Sun, 31 Mar 2024 15:53:47 -0700 Subject: [PATCH 13/26] Debugging crossbreeding logic --- .vscode/launch.json | 18 +- gemla/build.rs | 4 +- gemla/src/bin/fighter_nn/mod.rs | 410 +++++++++++++++++++++++++++++--- 3 files changed, 393 insertions(+), 39 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 3b9c326..bc9cad2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,23 @@ "name": "Debug", "program": "${workspaceFolder}/gemla/target/debug/gemla.exe", "args": ["./gemla/temp/"], - "cwd": "${workspaceFolder}" + "cwd": "${workspaceFolder}/gemla" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug Rust Tests", + "cargo": { + "args": [ + "test", + "--manifest-path", "${workspaceFolder}/gemla/Cargo.toml", + "--no-run", // Compiles the tests without running them + "--package=gemla", // Specify your package name if necessary + "--bin=bin" + ], + "filter": { } + }, + "args": [], } ] } \ No newline at end of file diff --git a/gemla/build.rs b/gemla/build.rs index e6b8ca6..d50ed3e 100644 --- a/gemla/build.rs +++ b/gemla/build.rs @@ -1,9 +1,9 @@ fn main() { // Replace this with the path to the directory containing `fann.lib` - let lib_dir = "F://vandomej/Downloads/vcpkg/packages/fann_x64-windows/lib"; + let lib_dir = "/opt/homebrew/Cellar/fann/2.2.0/lib"; println!("cargo:rustc-link-search=native={}", lib_dir); - println!("cargo:rustc-link-lib=static=fann"); + println!("cargo:rustc-link-lib=dylib=fann"); // Use `dylib=fann` instead of `static=fann` if you're linking dynamically // If there are any additional directories where the compiler can find header files, you can specify them like this: diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index 3baae77..729d234 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -1,6 +1,6 @@ extern crate fann; -use std::{fs::{self, File}, io::{self, BufRead, BufReader}, path::{Path, PathBuf}, sync::Arc}; +use std::{cmp::min, fs::{self, File}, io::{self, BufRead, BufReader}, path::{Path, PathBuf}, sync::Arc}; use fann::{ActivationFunc, Fann}; use futures::future::join_all; use gemla::{core::genetic_node::{GeneticNode, GeneticNodeContext}, error::Error}; @@ -24,7 +24,7 @@ const NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN: usize = 3; const NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MAX: usize = 35; const NEURAL_NETWORK_INITIAL_WEIGHT_MIN: f32 = -2.0; const NEURAL_NETWORK_INITIAL_WEIGHT_MAX: f32 = 2.0; -const NEURAL_NETWORK_CROSSBREED_SEGMENTS_MIN: usize = 1; +const NEURAL_NETWORK_CROSSBREED_SEGMENTS_MIN: usize = 2; const NEURAL_NETWORK_CROSSBREED_SEGMENTS_MAX: usize = 20; const SIMULATION_ROUNDS: usize = 5; @@ -70,7 +70,7 @@ impl GeneticNode for FighterNN { fs::create_dir_all(&gen_folder) .with_context(|| format!("Failed to create or access the generation folder: {:?}", gen_folder))?; - let nn_shapes = HashMap::new(); + let mut nn_shapes = HashMap::new(); // Create the first generation in this folder for i in 0..POPULATION { @@ -95,6 +95,11 @@ impl GeneticNode for FighterNN { fann.save(&nn) .with_context(|| format!("Failed to save nn at {:?}", nn))?; } + + let mut crossbreed_segments = thread_rng().gen_range(NEURAL_NETWORK_CROSSBREED_SEGMENTS_MIN..NEURAL_NETWORK_CROSSBREED_SEGMENTS_MAX); + if crossbreed_segments % 2 != 0 { + crossbreed_segments += 1; + } Ok(Box::new(FighterNN { id: context.id, @@ -103,7 +108,8 @@ impl GeneticNode for FighterNN { generation: 0, scores: vec![HashMap::new()], nn_shapes, - crossbreed_segments: thread_rng().gen_range(NEURAL_NETWORK_CROSSBREED_SEGMENTS_MIN..NEURAL_NETWORK_CROSSBREED_SEGMENTS_MAX) + // we need crossbreed segments to be even + crossbreed_segments, })) } @@ -266,45 +272,16 @@ impl GeneticNode for FighterNN { let fann = Fann::from_file(&nn) .with_context(|| format!("Failed to load nn"))?; - let new_fann = fann.try_clone(); - // Load another nn from the current generation and cross breed it with the current nn let cross_nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, to_keep[thread_rng().gen_range(0..survivor_count)])); let cross_fann = Fann::from_file(&cross_nn) .with_context(|| format!("Failed to load cross nn"))?; - let mut connections = fann.get_connections(); // Vector of connections - let cross_connections = cross_fann.get_connections(); // Vector of connections - let segment_count: usize = 3; // For example, choose 3 segments to swap - let segment_distribution = Uniform::from(1..connections.len() / segment_count); // Ensure segments are not too small - - let mut start_points = vec![]; - - for _ in 0..segment_count { - let start_point = segment_distribution.sample(&mut rand::thread_rng()); - start_points.push(start_point); - } - start_points.sort_unstable(); // Ensure segments are in order - trace!("Crossbreeding Start points: {:?}", start_points); - - for (j, &start) in start_points.iter().enumerate() { - let end = if j < segment_count - 1 { - start_points[j + 1] - } else { - connections.len() - }; - - // Swap segments - for k in start..end { - connections[k] = cross_connections[k].clone(); - } - } - - fann.set_connections(&connections); + let mut new_fann = crossbreed(&fann, &cross_fann, self.crossbreed_segments)?; // For each weight in the 5 new nn's there is a 20% chance of a minor mutation (a random number between -0.1 and 0.1 is added to the weight) // And a 5% chance of a major mutation (a random number between -0.3 and 0.3 is added to the weight) - let mut connections = fann.get_connections(); // Vector of connections + let mut connections = new_fann.get_connections(); // Vector of connections for c in &mut connections { if thread_rng().gen_range(0..100) < 20 { c.weight += thread_rng().gen_range(-0.1..0.1); @@ -313,7 +290,7 @@ impl GeneticNode for FighterNN { c.weight += thread_rng().gen_range(-0.3..0.3); } } - fann.set_connections(&connections); + new_fann.set_connections(&connections); // Save the new nn's to the new generation folder let new_nn = new_gen_folder.join(format!("{:06}_fighter_nn_{}.net", self.id, i + survivor_count)); @@ -360,10 +337,280 @@ impl GeneticNode for FighterNN { generation: 0, population_size: left.population_size, // Assuming left and right have the same population size. scores: vec![HashMap::new()], + crossbreed_segments: left.crossbreed_segments, + nn_shapes: HashMap::new(), })) } } +/// Crossbreeds two neural networks of different shapes by finding cut points, and swapping neurons between the two networks. +/// Algorithm tries to ensure similar functionality is maintained between the two networks. +/// It does this by preserving connections between the same neurons from the original to the new network, and if a connection cannot be found +/// it will create a new connection with a random weight. +fn crossbreed(primary: &Fann, secondary: &Fann, crossbreed_segments: usize) -> Result { + // First we need to get the shape of the networks and transform this into a format that is easier to work with + // We want a list of every neuron id, and the layer it is in + let primary_shape = primary.get_layer_sizes(); + let secondary_shape = secondary.get_layer_sizes(); + let primary_neurons = generate_neuron_datastructure(&primary_shape); + let secondary_neurons = generate_neuron_datastructure(&secondary_shape); + + // Now we need to find the cut points for the crossbreed + let start = primary_shape[0] + 1; // Start at the first hidden layer + let end = min(primary_shape.iter().sum::() - primary_shape.last().unwrap(), secondary_shape.iter().sum::() - secondary_shape.last().unwrap()); // End at the last hidden layer + let segment_distribution = Uniform::from(start..end); // Ensure segments are not too small + + let mut cut_points = Vec::new(); + for _ in 0..crossbreed_segments { + let cut_point = segment_distribution.sample(&mut thread_rng()); + if !cut_points.contains(&cut_point) { + cut_points.push(cut_point); + } + } + // Sort the cut points to make it easier to iterate over them + cut_points.sort_unstable(); + + // We need to transform the cut_points vector to a vector of tuples that contain the start and end of each segment + let mut segments = Vec::new(); + let mut previous = 0; + for &cut_point in cut_points.iter() { + segments.push((previous, cut_point)); + previous = cut_point; + } + + let new_neurons = crossbreed_neuron_arrays(segments, primary_neurons, secondary_neurons); + + // Now we need to create the new network with the shape we've determined + let mut new_shape = vec![]; + for (_, _, layer, _) in new_neurons.iter() { + // Check if new_shape has an entry for layer in it + if new_shape.len() <= *layer as usize { + new_shape.push(1); + } + else { + new_shape[*layer as usize] += 1; + } + } + + let mut new_fann = Fann::new(new_shape.as_slice()) + .with_context(|| "Failed to create new fann")?; + // We need to randomize the weights to a small value + new_fann.randomize_weights(-0.1, 0.1); + new_fann.set_activation_func_hidden(ActivationFunc::SigmoidSymmetric); + new_fann.set_activation_func_output(ActivationFunc::SigmoidSymmetric); + + consolidate_old_connections(primary, secondary, new_shape, new_neurons, &mut new_fann); + + Ok(new_fann) +} + +fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: Vec, new_neurons: Vec<(u32, bool, usize, u32)>, new_fann: &mut Fann) { + // Now we need to copy the connections from the original networks to the new network + // We can do this by referencing our connections array, it will contain the original id's of the neurons + // and their new id as well as their layer. We can iterate one layer at a time and copy the connections + + // Start by iterating layer by later + let primary_connections = primary.get_connections(); + let secondary_connections = secondary.get_connections(); + for layer in 1..new_shape.len() { + // filter out the connections that are in the current layer and previous layer + let current_layer_connections = new_neurons.iter().filter(|(_, _, l, _)| l == &layer).collect::>(); + let previous_layer_connections = new_neurons.iter().filter(|(_, _, l, _)| l == &(layer - 1)).collect::>(); + + // Now we need to iterate over the connections in the current layer + for (neuron_id, is_primary, _, new_id) in current_layer_connections.iter() { + // We need to find the connections from the previous layer to this neuron + for (previous_neuron_id, _, _, previous_new_id) in previous_layer_connections.iter() { + // First we use primary to and check the correct connections array to see if the connection exists + // If it does, we add it to the new network + let connection = if *is_primary { + primary_connections.iter().find(|connection| &connection.from_neuron == previous_neuron_id && &connection.to_neuron == neuron_id) + } + else { + secondary_connections.iter().find(|connection| &connection.from_neuron == previous_neuron_id && &connection.to_neuron == neuron_id) + }; + + // If the connection exists, we need to add it to the new network + if let Some(connection) = connection { + new_fann.set_weight(*previous_new_id, *new_id, connection.weight); + } + } + } + } +} + +fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32, usize)>, secondary_neurons: Vec<(u32, usize)>) -> Vec<(u32, bool, usize, u32)> { + // We now need to determine the resulting location of the neurons in the new network. + // To do this we need a new structure that keeps track of the following information: + // - The neuron id from the original network + // - Which network it originated from (primary or secondary) + // - The layer the neuron is in + // - The resulting neuron id in the new network which will be calculated after the fact + let mut new_neurons = Vec::new(); + let mut current_layer = 0; + let mut is_primary = true; + for (i, &segment) in segments.iter().enumerate() { + // If it's the first slice, copy neurons from the primary network up to the cut_point + if i == 0 { + for (neuron_id, layer) in primary_neurons.iter() { + if neuron_id <= &segment.1 { + if layer > ¤t_layer { + current_layer += 1; + } + new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + } + else { + break; + } + } + } + else { + let target_neurons = if is_primary { &primary_neurons } else { &secondary_neurons }; + + for (neuron_id, layer) in target_neurons.iter() { + // Iterate until neuron_id equals the cut_point + if neuron_id >= &segment.0 && neuron_id <= &segment.1 { + // We need to do something different depending on whether the neuron layer is, lower, higher or equal to the target layer + + // Equal + if layer == ¤t_layer { + new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + } + // Earlier + else if layer < ¤t_layer { + // If it's in an earlier layer, add it to the earlier layer + // Check if there's a lower id from the same individual in that earlier layer + // As long as there isn't a neuron from the other individual in between the lower id and current id, add the id values from the same individual + let earlier_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| l == layer).collect::>(); + // get max id from that layer + let highest_id = earlier_layer_neurons.iter().max_by_key(|(id, _, _, _)| id); + if let Some(highest_id) = highest_id { + if highest_id.1 == is_primary { + let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && id <= neuron_id && l == layer).collect::>(); + for (neuron_id, _) in neurons_to_add { + new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + } + } + } + } + // Later + else if layer > ¤t_layer { + // If the highest id in the current layer is from the same individual, add anything with a higher id to the current layer before moving to the next layer + // First filter new_neurons to look at neurons from the current layer + let current_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| l == ¤t_layer).collect::>(); + let highest_id = current_layer_neurons.iter().max_by_key(|(id, _, _, _)| id); + if let Some(highest_id) = highest_id { + if highest_id.1 == is_primary { + let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && *l == layer - 1).collect::>(); + for (neuron_id, _) in neurons_to_add { + new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + } + } + } + + // If it's in a future layer, move to the next layer + current_layer += 1; + + // Add the neuron to the new network + // Along with any neurons that have a lower id in the future layer + let neurons_to_add = target_neurons.iter().filter(|(id, l)| id <= &neuron_id && l == layer).collect::>(); + for (neuron_id, _) in neurons_to_add { + new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + } + } + + } + else if neuron_id >= &segment.1 { + break; + } + } + } + + // Switch to the other network + is_primary = !is_primary; + } + + // For the last segment, copy the remaining neurons + let target_neurons = if is_primary { &primary_neurons } else { &secondary_neurons }; + // Get output layer number + let output_layer = target_neurons.iter().max_by_key(|(_, l)| l).unwrap().1; + + // For the last segment, copy the remaining neurons from the target network + // But when we reach the output layer, we need to add a new layer to the end of new_neurons regardless of it's length + // and copy the output neurons to that layer + for (neuron_id, layer) in target_neurons.iter() { + if neuron_id > &segments.last().unwrap().1 { + if layer == &output_layer { + // Calculate which layer the neurons should be in + current_layer = new_neurons.iter().max_by_key(|(_, _, l, _)| l).unwrap().2 + 1; + for (neuron_id, _) in target_neurons.iter().filter(|(_, l)| l == &output_layer) { + new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + } + break; + } + else if *neuron_id == &segments.last().unwrap().1 + 1 { + let earlier_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| l == layer).collect::>(); + // get max id from that layer + let highest_id = earlier_layer_neurons.iter().max_by_key(|(id, _, _, _)| id); + if let Some(highest_id) = highest_id { + if highest_id.1 == is_primary { + let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && id <= neuron_id && l == layer).collect::>(); + for (neuron_id, _) in neurons_to_add { + new_neurons.push((*neuron_id, is_primary, *layer, 0)); + } + } + } + } + else { + new_neurons.push((*neuron_id, is_primary, *layer, 0)); + } + } + } + + // Filtering layers with too few neurons, if necessary + let layer_counts = new_neurons.iter().fold(vec![0; current_layer + 1], |mut counts, &(_, _, layer, _)| { + counts[layer] += 1; + counts + }); + + // Filter out layers based on the minimum number of neurons per layer + new_neurons = new_neurons.into_iter() + .filter(|&(_, _, layer, _)| layer_counts[layer] >= NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN) + .collect::>(); + + // Collect and sort unique layer numbers + let mut unique_layers = new_neurons.iter() + .map(|(_, _, layer, _)| *layer) + .collect::>(); + unique_layers.sort(); + unique_layers.dedup(); // Removes duplicates, keeping only unique layer numbers + + // Create a mapping from old layer numbers to new (gap-less) layer numbers + let layer_mapping = unique_layers.iter().enumerate() + .map(|(new_layer, &old_layer)| (old_layer, new_layer)) + .collect::>(); + + // Apply the mapping to renumber layers in new_neurons + new_neurons.iter_mut().for_each(|(_, _, layer, _)| { + *layer = *layer_mapping.get(layer).unwrap_or(layer); // Fallback to original layer if not found, though it should always find a match + }); + + // Assign new IDs + // new_neurons must be sorted by layer, then by neuron ID within the layer + new_neurons.sort_unstable_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); + new_neurons.iter_mut().enumerate().for_each(|(new_id, neuron)| { + neuron.3 = new_id as u32; + }); + + new_neurons +} + +fn generate_neuron_datastructure(shape: &[u32]) -> Vec<(u32, usize)> { + shape.iter().enumerate().flat_map(|(i, &size)| { + (0..size).enumerate().map(move |(j, _)| (j as u32, i)) + }).collect() +} + async fn read_score_from_file(file_path: &Path, nn_id: &str) -> Result { let mut attempts = 0; @@ -399,4 +646,95 @@ async fn read_score_from_file(file_path: &Path, nn_id: &str) -> Result return Err(e), } } +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use super::*; + + #[test] + fn crossbreed_neuron_arrays_test() { + // Assign + let segments = vec![(0, 3), (4, 6), (7, 8), (9, 10)]; + + let primary_network = vec![ + // Input layer + (0, 0), (1, 0), (2, 0), (3, 0), + // Hidden layer 1 + (4, 1), (5, 1), (6, 1), (7, 1), (8, 1), (9, 1), (10, 1), (11, 1), + // Hidden layer 2 + (12, 2), (13, 2), (14, 2), (15, 2), (16, 2), (17, 2), + // Output layer + (18, 3), (19, 3), (20, 3), (21, 3), + ]; + + let secondary_network = vec![ + // Input layer + (0, 0), (1, 0), (2, 0), (3, 0), + // Hidden layer 1 + (4, 1), (5, 1), (6, 1), + // Hidden layer 2 + (7, 2), (8, 2), (9, 2), + // Hiden Layer 3 + (10, 3), (11, 3), (12, 3), + // Hidden Layer 4 + (13, 4), (14, 4), (15, 4), + // Hiden Layer 5 + (16, 5), (17, 5), (18, 5), + // Output layer + (19, 6), (20, 6), (21, 6), (22, 6), + ]; + + // Act + let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 8 + (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, true, 1, 7), (8, true, 1, 8), (9, true, 1, 9), (10, true, 1, 10), (11, true, 1, 11), + // Hidden Layer 2: Expect 9 + (7, false, 2, 12), (8, false, 2, 13), (9, false, 2, 14), (12, true, 2, 15), (13, true, 2, 16), (14, true, 2, 17), (15, true, 2, 18), (16, true, 2, 19), (17, true, 2, 20), + // Output Layer: Expect 4 + (18, true, 3, 21), (19, true, 3, 22), (20, true, 3, 23), (21, true, 3, 24), + ].into_iter().collect(); + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + + // Now we test the ooposite case + // Act + let result = crossbreed_neuron_arrays(segments.clone(), secondary_network.clone(), primary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 7 + (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, false, 1, 7), (8, false, 1, 8), (9, false, 1, 9), (10, false, 1, 10), + // Hidden Layer 2: Expect 3 + (7, true, 2, 11), (8, true, 2, 12), (9, true, 2, 13), + // Hidden Layer 3: Expect 3 + (10, true, 3, 14), (11, true, 3, 15), (12, true, 3, 16), + // Hidden Layer 4: Expect 3 + (13, true, 4, 17), (14, true, 4, 18), (15, true, 4, 19), + // Hidden Layer 5: Expect 3 + (16, true, 5, 20), (17, true, 5, 21), (18, true, 5, 22), + // Output Layer: Expect 4 + (19, true, 6, 23), (20, true, 6, 24), (21, true, 6, 25), (22, true, 6, 26), + ].into_iter().collect(); + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + } + } \ No newline at end of file From be02823c4c9ccf3e27dbcce0fea6c67cf1ebd941 Mon Sep 17 00:00:00 2001 From: vandomej Date: Wed, 3 Apr 2024 21:53:54 -0700 Subject: [PATCH 14/26] unit testing neural network operations --- gemla/src/bin/fighter_nn/mod.rs | 606 +++++++++++++++++++++++++++++--- 1 file changed, 563 insertions(+), 43 deletions(-) diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index 729d234..8aa31c2 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -408,6 +408,11 @@ fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: Vec< // Now we need to copy the connections from the original networks to the new network // We can do this by referencing our connections array, it will contain the original id's of the neurons // and their new id as well as their layer. We can iterate one layer at a time and copy the connections + + let primary_shape = primary.get_layer_sizes(); + let secondary_shape = secondary.get_layer_sizes(); + debug!("Primary shape: {:?}", primary_shape); + debug!("Secondary shape: {:?}", secondary_shape); // Start by iterating layer by later let primary_connections = primary.get_connections(); @@ -423,16 +428,87 @@ fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: Vec< for (previous_neuron_id, _, _, previous_new_id) in previous_layer_connections.iter() { // First we use primary to and check the correct connections array to see if the connection exists // If it does, we add it to the new network - let connection = if *is_primary { - primary_connections.iter().find(|connection| &connection.from_neuron == previous_neuron_id && &connection.to_neuron == neuron_id) + let mut connection ; + let mut found_in_primary = false; + if *is_primary { + connection = primary_connections.iter() + .find(|connection| { + let from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); + let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); + + // If both neurons have a Some value + if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { + from_neuron == *previous_neuron_id && to_neuron == *neuron_id + } else { + false + } + }); + + if let None = connection { + connection = secondary_connections.iter() + .find(|connection| { + let from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); + let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + + // If both neurons have a Some value + if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { + from_neuron == *previous_neuron_id && to_neuron == *neuron_id + } else { + false + } + }); + } else { + found_in_primary = true; + } } else { - secondary_connections.iter().find(|connection| &connection.from_neuron == previous_neuron_id && &connection.to_neuron == neuron_id) + connection = secondary_connections.iter() + .find(|connection| { + let from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); + let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + + // If both neurons have a Some value + if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { + from_neuron == *previous_neuron_id && to_neuron == *neuron_id + } else { + false + } + }); + + if let None = connection { + connection = primary_connections.iter() + .find(|connection| { + let from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); + let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); + + // If both neurons have a Some value + if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { + from_neuron == *previous_neuron_id && to_neuron == *neuron_id + } else { + false + } + }); + } else { + found_in_primary = true; + } }; // If the connection exists, we need to add it to the new network if let Some(connection) = connection { - new_fann.set_weight(*previous_new_id, *new_id, connection.weight); + if *is_primary { + let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); + let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); + debug!("Primary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", previous_new_id, new_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, previous_neuron_id, neuron_id); + } else { + let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); + let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + debug!("Secondary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", previous_new_id, new_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, previous_neuron_id, neuron_id); + } + let translated_from = to_bias_network_id(previous_new_id, &new_shape); + let translated_to = to_bias_network_id(new_id, &new_shape); + new_fann.set_weight(translated_from, translated_to, connection.weight); + } else { + debug!("Connection not found for ({}, {}) -> ({}, {})", previous_new_id, new_id, previous_neuron_id, neuron_id); } } } @@ -448,6 +524,9 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32 // - The resulting neuron id in the new network which will be calculated after the fact let mut new_neurons = Vec::new(); let mut current_layer = 0; + // keep track of the last layer that we inserted a neuron into for each network + let mut primary_last_layer = 0; + let mut secondary_last_layer = 0; let mut is_primary = true; for (i, &segment) in segments.iter().enumerate() { // If it's the first slice, copy neurons from the primary network up to the cut_point @@ -458,6 +537,12 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32 current_layer += 1; } new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + if is_primary { + primary_last_layer = current_layer; + } + else { + secondary_last_layer = current_layer; + } } else { break; @@ -475,6 +560,13 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32 // Equal if layer == ¤t_layer { new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + + if is_primary { + primary_last_layer = current_layer; + } + else { + secondary_last_layer = current_layer; + } } // Earlier else if layer < ¤t_layer { @@ -483,15 +575,31 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32 // As long as there isn't a neuron from the other individual in between the lower id and current id, add the id values from the same individual let earlier_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| l == layer).collect::>(); // get max id from that layer - let highest_id = earlier_layer_neurons.iter().max_by_key(|(id, _, _, _)| id); + let highest_id = earlier_layer_neurons.iter().max_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); if let Some(highest_id) = highest_id { if highest_id.1 == is_primary { - let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && id <= neuron_id && l == layer).collect::>(); - for (neuron_id, _) in neurons_to_add { - new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && id < neuron_id && l == layer).collect::>(); + for (neuron_id, layer) in neurons_to_add { + new_neurons.push((*neuron_id, is_primary, *layer, 0)); + + if is_primary { + primary_last_layer = *layer; + } + else { + secondary_last_layer = *layer; + } } } } + + new_neurons.push((*neuron_id, is_primary, *layer, 0)); + + if is_primary { + primary_last_layer = *layer; + } + else { + secondary_last_layer = *layer; + } } // Later else if layer > ¤t_layer { @@ -504,6 +612,13 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32 let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && *l == layer - 1).collect::>(); for (neuron_id, _) in neurons_to_add { new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + + if is_primary { + primary_last_layer = current_layer; + } + else { + secondary_last_layer = current_layer; + } } } } @@ -516,6 +631,13 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32 let neurons_to_add = target_neurons.iter().filter(|(id, l)| id <= &neuron_id && l == layer).collect::>(); for (neuron_id, _) in neurons_to_add { new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + + if is_primary { + primary_last_layer = current_layer; + } + else { + secondary_last_layer = current_layer; + } } } @@ -549,17 +671,22 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32 break; } else if *neuron_id == &segments.last().unwrap().1 + 1 { - let earlier_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| l == layer).collect::>(); + let target_layer = if is_primary { primary_last_layer } else { secondary_last_layer }; + let earlier_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| *l >= target_layer && l <= layer).collect::>(); + // get max neuron from with both + // The highest layer // get max id from that layer - let highest_id = earlier_layer_neurons.iter().max_by_key(|(id, _, _, _)| id); + let highest_id = earlier_layer_neurons.iter().max_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); if let Some(highest_id) = highest_id { if highest_id.1 == is_primary { - let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && id <= neuron_id && l == layer).collect::>(); - for (neuron_id, _) in neurons_to_add { - new_neurons.push((*neuron_id, is_primary, *layer, 0)); + let neurons_to_add = target_neurons.iter().filter(|(id, _)| id > &highest_id.0 && id < neuron_id).collect::>(); + for (neuron_id, l) in neurons_to_add { + new_neurons.push((*neuron_id, is_primary, *l, 0)); } } } + + new_neurons.push((*neuron_id, is_primary, *layer, 0)); } else { new_neurons.push((*neuron_id, is_primary, *layer, 0)); @@ -606,11 +733,64 @@ fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32 } fn generate_neuron_datastructure(shape: &[u32]) -> Vec<(u32, usize)> { - shape.iter().enumerate().flat_map(|(i, &size)| { - (0..size).enumerate().map(move |(j, _)| (j as u32, i)) - }).collect() + let mut result = Vec::new(); + let mut global_index = 0; // Keep a global index that does not reset + + for (layer_index, &neurons) in shape.iter().enumerate() { + for _ in 0..neurons { + result.push((global_index, layer_index)); + global_index += 1; // Increment global index for each neuron + } + // global_index += 1; // Skip index for bias neuron at the end of each layer + } + + result } + +fn to_bias_network_id(id: &u32, shape: &Vec) -> u32 { + // The given id comes from a network without a bias neuron at the end of every layer + // We need to translate this id to the id in the network with bias neurons + let mut translated_id = 0; + for (layer_index, &neurons) in shape.iter().enumerate() { + for _ in 0..neurons { + if &translated_id == id { + return translated_id + layer_index as u32; + } + translated_id += 1; + } + } + + // If the id is not found, return the id + translated_id +} + +fn to_non_bias_network_id(id: u32, shape: &[u32]) -> Option { + let mut bias_count = 0; // Count of bias neurons encountered up to the current ID + let mut total_neurons = 0; // Total count of neurons (excluding bias neurons) processed + + for &neurons in shape { + let layer_end = total_neurons + neurons; // End of the current layer, excluding the bias neuron + if id < layer_end { + // ID is within the current layer (excluding the bias neuron) + return Some(id - bias_count); + } + if id == layer_end { + // ID matches the position where a bias neuron would be + return None; + } + + // Update counts after considering the current layer + total_neurons += neurons + 1; // Move to the next layer, accounting for the bias neuron + bias_count += 1; // Increment bias count as we've moved past where a bias neuron would be + } + + // If the ID is beyond the range of all neurons (including bias), it's treated as invalid + // Adjust this behavior based on your application's needs + None +} + + async fn read_score_from_file(file_path: &Path, nn_id: &str) -> Result { let mut attempts = 0; @@ -659,33 +839,9 @@ mod tests { // Assign let segments = vec![(0, 3), (4, 6), (7, 8), (9, 10)]; - let primary_network = vec![ - // Input layer - (0, 0), (1, 0), (2, 0), (3, 0), - // Hidden layer 1 - (4, 1), (5, 1), (6, 1), (7, 1), (8, 1), (9, 1), (10, 1), (11, 1), - // Hidden layer 2 - (12, 2), (13, 2), (14, 2), (15, 2), (16, 2), (17, 2), - // Output layer - (18, 3), (19, 3), (20, 3), (21, 3), - ]; + let primary_network = generate_neuron_datastructure(&vec![4, 8, 6, 4]); - let secondary_network = vec![ - // Input layer - (0, 0), (1, 0), (2, 0), (3, 0), - // Hidden layer 1 - (4, 1), (5, 1), (6, 1), - // Hidden layer 2 - (7, 2), (8, 2), (9, 2), - // Hiden Layer 3 - (10, 3), (11, 3), (12, 3), - // Hidden Layer 4 - (13, 4), (14, 4), (15, 4), - // Hiden Layer 5 - (16, 5), (17, 5), (18, 5), - // Output layer - (19, 6), (20, 6), (21, 6), (22, 6), - ]; + let secondary_network = generate_neuron_datastructure(&vec![4, 3, 3, 3, 3, 3, 4]); // Act let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); @@ -735,6 +891,370 @@ mod tests { // Assert assert_eq!(result_set, expected); + + // Testing with a different segment + // Assign + let segments = vec![(0, 4), (5, 14), (15, 15), (16, 16)]; + + // Act + let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 3 + (4, true, 1, 4), (5, false, 1, 5), (6, false, 1, 6), + // Hidden Layer 2: Expect 6 + (7, false, 2, 7), (8, false, 2, 8), (9, false, 2, 9), (15, true, 2, 10), (16, true, 2, 11), (17, true, 2, 12), + // Hidden Layer 3: Expect 3 + (10, false, 3, 13), (11, false, 3, 14), (12, false, 3, 15), + // Hidden Layer 4: Expect 3 + (13, false, 4, 16), (14, false, 4, 17), (15, false, 4, 18), + // Output Layer: Expect 4 + (18, true, 5, 19), (19, true, 5, 20), (20, true, 5, 21), (21, true, 5, 22), + ].into_iter().collect(); + + // print result before comparison + for r in result.iter() { + println!("{:?}", r); + } + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + + // Swapping order + let result = crossbreed_neuron_arrays(segments.clone(), secondary_network.clone(), primary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 8 + (4, true, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, false, 1, 7), (8, false, 1, 8), (9, false, 1, 9), (10, false, 1, 10), (11, false, 1, 11), + // Hidden Layer 2: Expect 5 + (12, false, 2, 12), (13, false, 2, 13), (14, false, 2, 14), (15, false, 2, 15), (16, false, 2, 16), + // Hidden Layer 3: Expect 3 + (13, true, 3, 17), (14, true, 3, 18), (15, true, 3, 19), + // Hidden Layer 4: Expect 3 + (16, true, 4, 20), (17, true, 4, 21), (18, true, 4, 22), + // Output Layer: Expect 4 + (19, true, 5, 23), (20, true, 5, 24), (21, true, 5, 25), (22, true, 5, 26), + ].into_iter().collect(); + + // print result before comparison + for r in result.iter() { + println!("{:?}", r); + } + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + + // Testing with a different segment + // Assign + let segments = vec![(0, 7), (8, 9), (10, 10), (11, 12)]; + + // Act + let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 7 + (4, true, 1, 4), (5, true, 1, 5), (6, true, 1, 6), (7, true, 1, 7), (8, true, 1, 8), (9, true, 1, 9), (10, true, 1, 10), + // Hidden Layer 2: Expect 8 + (7, false, 2, 11), (8, false, 2, 12), (9, false, 2, 13), (13, true, 2, 14), (14, true, 2, 15), (15, true, 2, 16), (16, true, 2, 17), (17, true, 2, 18), + // Hidden Layer 3: Expect 3 + (10, false, 3, 19), (11, false, 3, 20), (12, false, 3, 21), + // Output Layer: Expect 4 + (18, true, 4, 22), (19, true, 4, 23), (20, true, 4, 24), (21, true, 4, 25), + ].into_iter().collect(); + + // print result before comparison + for r in result.iter() { + println!("{:?}", r); + } + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + + // Swapping order + let result = crossbreed_neuron_arrays(segments.clone(), secondary_network.clone(), primary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 7 + (4, true, 1, 4), (5, true, 1, 5), (6, true, 1, 6), (8, false, 1, 7), (9, false, 1, 8), (10, false, 1, 9), (11, false, 1, 10), + // Hidden Layer 2: Expect 4 + (7, true, 2, 11), (8, true, 2, 12), (9, true, 2, 13), (12, false, 2, 14), + // Hidden Layer 3: Expect 3 + (10, true, 3, 15), (11, true, 3, 16), (12, true, 3, 17), + // Hidden Layer 4: Expect 3 + (13, true, 4, 18), (14, true, 4, 19), (15, true, 4, 20), + // Hidden Layer 5: Expect 3 + (16, true, 5, 21), (17, true, 5, 22), (18, true, 5, 23), + // Output Layer: Expect 4 + (19, true, 6, 24), (20, true, 6, 25), (21, true, 6, 26), (22, true, 6, 27), + ].into_iter().collect(); + + // print result before comparison + for r in result.iter() { + println!("{:?}", r); + } + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + + // Testing networks with the same size + // Assign + let segments = vec![(0, 3), (4, 6), (7, 8), (9, 11)]; + + let primary_network = generate_neuron_datastructure(&vec![4, 3, 4, 5, 4]); + + vec![ + // Input layer + (0, 0), (1, 0), (2, 0), (3, 0), + // Hidden layer 1: 3 neurons + (4, 1), (5, 1), (6, 1), + // Hidden Layer 2: 4 neurons + (7, 2), (8, 2), (9, 2), (10, 2), + // Hidden Layer 3: 5 neurons + (11, 3), (12, 3), (13, 3), (14, 3), (15, 3), + // Output layer + (16, 4), (17, 4), (18, 4), (19, 4), + ]; + + let secondary_network = primary_network.clone(); + + // Act + let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 3 + (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), + // Hidden Layer 2: Expect 4 + (7, true, 2, 7), (8, true, 2, 8), (9, false, 2, 9), (10, false, 2, 10), + // Hidden Layer 3: Expect 5 + (11, false, 3, 11), (12, true, 3, 12), (13, true, 3, 13), (14, true, 3, 14), (15, true, 3, 15), + // Output Layer: Expect 4 + (16, true, 4, 16), (17, true, 4, 17), (18, true, 4, 18), (19, true, 4, 19), + ].into_iter().collect(); + + // print result before comparison + for r in result.iter() { + println!("{:?}", r); + } + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + + // Testing with different segment + let segments = vec![(0, 5), (6, 6), (7, 11), (12, 13)]; + + // Act + let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 3 + (4, true, 1, 4), (5, true, 1, 5), (6, false, 1, 6), + // Hidden Layer 2: Expect 4 + (7, true, 2, 7), (8, true, 2, 8), (9, true, 2, 9), (10, true, 2, 10), + // Hidden Layer 3: Expect 5 + (11, true, 3, 11), (12, false, 3, 12), (13, false, 3, 13), (14, true, 3, 14), (15, true, 3, 15), + // Output Layer: Expect 4 + (16, true, 4, 16), (17, true, 4, 17), (18, true, 4, 18), (19, true, 4, 19), + ].into_iter().collect(); + + // print result before comparison + for r in result.iter() { + println!("{:?}", r); + } + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + } + + #[test] + fn generate_neuron_datastructure_test() { + // Assign + let shape = vec![4, 3, 5, 4]; + + // Act + let result = generate_neuron_datastructure(shape.as_slice()); + + // Expected Result + let expected: Vec<(u32, usize)> = vec![ + (0, 0), (1, 0), (2, 0), (3, 0), + (4, 1), (5, 1), (6, 1), + (7, 2), (8, 2), (9, 2), (10, 2), (11, 2), + (12, 3), (13, 3), (14, 3), (15, 3), + ]; + + // Assert + assert_eq!(result, expected); + } + + #[test] + fn translate_neuron_id_test() { + // Assign + let shape = vec![4, 3, 5, 4]; + + let expected = vec![ + // (input, expected output) + (0, 0), (1, 1), (2, 2), (3, 3), + (4, 5), (5, 6), (6, 7), + (7, 9), (8, 10), (9, 11), (10, 12), (11, 13), + (12, 15), (13, 16), (14, 17), (15, 18), + ]; + + // Act + for (input, expected_output) in expected { + let result = to_bias_network_id(&input, &shape); + // Assert + assert_eq!(result, expected_output); + + // Go the other direction too + let result = to_non_bias_network_id(expected_output, &shape); + + // Assert + if let Some(result) = result { + assert_eq!(result, input); + } + else { + assert!(false, "Expected Some, got None"); + } + } + + // Validate bias neuron values + let bias_neurons = vec![4, 8, 14, 19]; + + for &bias_neuron in bias_neurons.iter() { + let result = to_non_bias_network_id(bias_neuron, &shape); + + // Assert + assert!(result.is_none()); + } + } + + #[test] + fn consolidate_old_connections_test() -> Result<(), Box>{ + // Assign + let primary_shape = vec![4, 8, 6, 4]; + let secondary_shape = vec![4, 3, 3, 3, 3, 3, 4]; + + let mut primary_fann = Fann::new(&primary_shape)?; + let mut secondary_fann = Fann::new(&secondary_shape)?; + + let mut primary_connections = primary_fann.get_connections(); + for connection in primary_connections.iter_mut() { + connection.weight = ((connection.from_neuron * 100) + connection.to_neuron) as f32; + } + primary_fann.set_connections(&primary_connections); + + let mut secondary_connections = secondary_fann.get_connections(); + for connection in secondary_connections.iter_mut() { + connection.weight = ((connection.from_neuron * 100) + connection.to_neuron) as f32; + connection.weight = connection.weight * -1.0; + } + secondary_fann.set_connections(&secondary_connections); + + let new_neurons = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 8 + (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, true, 1, 7), (8, true, 1, 8), (9, true, 1, 9), (10, true, 1, 10), (11, true, 1, 11), + // Hidden Layer 2: Expect 9 + (7, false, 2, 12), (8, false, 2, 13), (9, false, 2, 14), (12, true, 2, 15), (13, true, 2, 16), (14, true, 2, 17), (15, true, 2, 18), (16, true, 2, 19), (17, true, 2, 20), + // Output Layer: Expect 4 + (18, true, 3, 21), (19, true, 3, 22), (20, true, 3, 23), (21, true, 3, 24), + ]; + let new_shape = vec![4, 8, 9, 4]; + let mut new_fann = Fann::new(&[4, 8, 9, 4])?; + // Initialize weights to 0 + let mut new_connections = new_fann.get_connections(); + for connection in new_connections.iter_mut() { + connection.weight = 0.0; + } + new_fann.set_connections(&new_connections); + + // Act + consolidate_old_connections(&primary_fann, &secondary_fann, new_shape, new_neurons, &mut new_fann); + + // Bias neurons + // Layer 1: 4 + // Layer 2: 13 + // Layer 3: 23 + let expected_connections = vec![ + // (from_neuron, to_neuron, weight) + // Hidden Layer 1 (5-12) + (0, 5, -5.0), (1, 5, -105.0), (2, 5, -205.0), (3, 5, -305.0), + (0, 6, -6.0), (1, 6, -106.0), (2, 6, -206.0), (3, 6, -306.0), + (0, 7, -7.0), (1, 7, -107.0), (2, 7, -207.0), (3, 7, -307.0), + (0, 8, 8.0), (1, 8, 108.0), (2, 8, 208.0), (3, 8, 308.0), + (0, 9, 9.0), (1, 9, 109.0), (2, 9, 209.0), (3, 9, 309.0), + (0, 10, 10.0), (1, 10, 110.0), (2, 10, 210.0), (3, 10, 310.0), + (0, 11, 11.0), (1, 11, 111.0), (2, 11, 211.0), (3, 11, 311.0), + (0, 12, 12.0), (1, 12, 112.0), (2, 12, 212.0), (3, 12, 312.0), + // Hidden Layer 2 (14-22) + (5, 14, -509.0), (6, 14, -609.0), (7, 14, -709.0), (8, 14, 0.0), (9, 14, 0.0), (10, 14, 0.0), (11, 14, 0.0), (12, 14, 0.0), + (5, 15, -510.0), (6, 15, -610.0), (7, 15, -710.0), (8, 15, 0.0), (9, 15, 0.0), (10, 15, 0.0), (11, 15, 0.0), (12, 15, 0.0), + (5, 16, -511.0), (6, 16, -611.0), (7, 16, -711.0), (8, 16, 0.0), (9, 16, 0.0), (10, 16, 0.0), (11, 16, 0.0), (12, 16, 0.0), + (5, 17, 514.0), (6, 17, 614.0), (7, 17, 714.0), (8, 17, 814.0), (9, 17, 914.0), (10, 17, 1014.0), (11, 17, 1114.0), (12, 17, 1214.0), + (5, 18, 515.0), (6, 18, 615.0), (7, 18, 715.0), (8, 18, 815.0), (9, 18, 915.0), (10, 18, 1015.0), (11, 18, 1115.0), (12, 18, 1215.0), + (5, 19, 516.0), (6, 19, 616.0), (7, 19, 716.0), (8, 19, 816.0), (9, 19, 916.0), (10, 19, 1016.0), (11, 19, 1116.0), (12, 19, 1216.0), + (5, 20, 517.0), (6, 20, 617.0), (7, 20, 717.0), (8, 20, 817.0), (9, 20, 917.0), (10, 20, 1017.0), (11, 20, 1117.0), (12, 20, 1217.0), + (5, 21, 518.0), (6, 21, 618.0), (7, 21, 718.0), (8, 21, 818.0), (9, 21, 918.0), (10, 21, 1018.0), (11, 21, 1118.0), (12, 21, 1218.0), + (5, 22, 519.0), (6, 22, 619.0), (7, 22, 719.0), (8, 22, 819.0), (9, 22, 919.0), (10, 22, 1019.0), (11, 22, 1119.0), (12, 22, 1219.0), + // Output layer (24-27) + (14, 24, 0.0), (15, 24, 0.0), (16, 24, 0.0), (17, 24, 1421.0), (18, 24, 1521.0), (19, 24, 1621.0), (20, 24, 1721.0), (21, 24, 1821.0), (22, 24, 1921.0), + (14, 25, 0.0), (15, 25, 0.0), (16, 25, 0.0), (17, 25, 1422.0), (18, 25, 1522.0), (19, 25, 1622.0), (20, 25, 1722.0), (21, 25, 1822.0), (22, 25, 1922.0), + (14, 26, 0.0), (15, 26, 0.0), (16, 26, 0.0), (17, 26, 1423.0), (18, 26, 1523.0), (19, 26, 1623.0), (20, 26, 1723.0), (21, 26, 1823.0), (22, 26, 1923.0), + (14, 27, 0.0), (15, 27, 0.0), (16, 27, 0.0), (17, 27, 1424.0), (18, 27, 1524.0), (19, 27, 1624.0), (20, 27, 1724.0), (21, 27, 1824.0), (22, 27, 1924.0), + ]; + + for connection in new_fann.get_connections().iter() { + println!("{:?}", connection); + } + + // Assert + // Compare each connection to the expected connection + let new_connections = new_fann.get_connections(); + for connection in expected_connections.iter() { + let matching_connection = new_connections.iter().find(|&c| c.from_neuron == connection.0 && c.to_neuron == connection.1); + if let Some(matching_connection) = matching_connection { + assert_eq!(matching_connection.weight, connection.2, "Connection: {:?}", matching_connection); + } else { + assert!(false, "Connection not found: {:?}", connection); + } + } + + Ok(()) } - } \ No newline at end of file From d4739703253930d65caf71d8aef4f9d0c59dd2ca Mon Sep 17 00:00:00 2001 From: vandomej Date: Thu, 4 Apr 2024 16:15:16 -0700 Subject: [PATCH 15/26] Finished testing neural network utils --- gemla/src/bin/fighter_nn/mod.rs | 939 +------------ .../bin/fighter_nn/neural_network_utility.rs | 1237 +++++++++++++++++ 2 files changed, 1278 insertions(+), 898 deletions(-) create mode 100644 gemla/src/bin/fighter_nn/neural_network_utility.rs diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index 8aa31c2..4161079 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -1,11 +1,12 @@ extern crate fann; -use std::{cmp::min, fs::{self, File}, io::{self, BufRead, BufReader}, path::{Path, PathBuf}, sync::Arc}; +pub mod neural_network_utility; + +use std::{fs::{self, File}, io::{self, BufRead, BufReader}, ops::Range, path::{Path, PathBuf}, sync::Arc}; use fann::{ActivationFunc, Fann}; use futures::future::join_all; use gemla::{core::genetic_node::{GeneticNode, GeneticNodeContext}, error::Error}; use rand::prelude::*; -use rand::distributions::{Distribution, Uniform}; use serde::{Deserialize, Serialize}; use anyhow::Context; use tokio::process::Command; @@ -13,6 +14,8 @@ use uuid::Uuid; use std::collections::HashMap; use async_trait::async_trait; +use self::neural_network_utility::{crossbreed, major_mutation}; + const BASE_DIR: &str = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations"; const POPULATION: usize = 50; @@ -51,7 +54,11 @@ pub struct FighterNN { pub scores: Vec>, // A map of the id of the nn in the current generation and their neural network shape pub nn_shapes: HashMap>, - pub crossbreed_segments: usize + pub crossbreed_segments: usize, + pub weight_initialization_range: Range, + pub minor_mutation_rate: f32, + pub major_mutation_rate: f32, + pub mutation_weight_range: Range, } #[async_trait] @@ -71,6 +78,7 @@ impl GeneticNode for FighterNN { .with_context(|| format!("Failed to create or access the generation folder: {:?}", gen_folder))?; let mut nn_shapes = HashMap::new(); + let weight_initialization_range = thread_rng().gen_range(NEURAL_NETWORK_INITIAL_WEIGHT_MIN..0.0)..thread_rng().gen_range(0.0..=NEURAL_NETWORK_INITIAL_WEIGHT_MAX); // Create the first generation in this folder for i in 0..POPULATION { @@ -88,7 +96,7 @@ impl GeneticNode for FighterNN { let mut fann = Fann::new(nn_shape.as_slice()) .with_context(|| "Failed to create nn")?; - fann.randomize_weights(thread_rng().gen_range(NEURAL_NETWORK_INITIAL_WEIGHT_MIN..0.0), thread_rng().gen_range(0.0..=NEURAL_NETWORK_INITIAL_WEIGHT_MAX)); + fann.randomize_weights(weight_initialization_range.start, weight_initialization_range.end); fann.set_activation_func_hidden(ActivationFunc::SigmoidSymmetric); fann.set_activation_func_output(ActivationFunc::SigmoidSymmetric); // This will overwrite any existing file with the same name @@ -97,9 +105,11 @@ impl GeneticNode for FighterNN { } let mut crossbreed_segments = thread_rng().gen_range(NEURAL_NETWORK_CROSSBREED_SEGMENTS_MIN..NEURAL_NETWORK_CROSSBREED_SEGMENTS_MAX); - if crossbreed_segments % 2 != 0 { + if crossbreed_segments % 2 == 0 { crossbreed_segments += 1; } + + let mutation_weight_amplitude = thread_rng().gen_range(0.0..1.0); Ok(Box::new(FighterNN { id: context.id, @@ -110,11 +120,15 @@ impl GeneticNode for FighterNN { nn_shapes, // we need crossbreed segments to be even crossbreed_segments, + weight_initialization_range, + minor_mutation_rate: thread_rng().gen_range(0.0..1.0), + major_mutation_rate: thread_rng().gen_range(0.0..1.0), + mutation_weight_range: -mutation_weight_amplitude..mutation_weight_amplitude, })) } async fn simulate(&mut self, context: GeneticNodeContext) -> Result<(), Error> { - trace!("Context: {:?}", context); + debug!("Context: {:?}", context); let mut tasks = Vec::new(); // For each nn in the current generation: @@ -157,7 +171,7 @@ impl GeneticNode for FighterNN { let round_score = read_score_from_file(&score_file, &nn_id).await .with_context(|| format!("Failed to read score from file: {:?}", score_file_name))?; - trace!("{} scored {}", nn_id, round_score); + debug!("{} scored {}", nn_id, round_score); return Ok::(round_score); } @@ -168,7 +182,7 @@ impl GeneticNode for FighterNN { let round_score = read_score_from_file(&opposite_score_file, &nn_id).await .with_context(|| format!("Failed to read score from file: {:?}", opposite_score_file))?; - trace!("{} scored {}", nn_id, round_score); + debug!("{} scored {}", nn_id, round_score); return Ok::(1.0 - round_score); } @@ -200,11 +214,11 @@ impl GeneticNode for FighterNN { let round_score = read_score_from_file(&score_file, &nn_id).await .with_context(|| format!("Failed to read score from file: {:?}", score_file_name))?; - trace!("{} scored {}", nn_id, round_score); + debug!("{} scored {}", nn_id, round_score); Ok(round_score) } else { - trace!("Score file not found: {:?}", score_file_name); + warn!("Score file not found: {:?}", score_file_name); Ok(0.0) } }; @@ -277,24 +291,28 @@ impl GeneticNode for FighterNN { let cross_fann = Fann::from_file(&cross_nn) .with_context(|| format!("Failed to load cross nn"))?; - let mut new_fann = crossbreed(&fann, &cross_fann, self.crossbreed_segments)?; + let mut new_fann = crossbreed(&self, &fann, &cross_fann, self.crossbreed_segments)?; // For each weight in the 5 new nn's there is a 20% chance of a minor mutation (a random number between -0.1 and 0.1 is added to the weight) - // And a 5% chance of a major mutation (a random number between -0.3 and 0.3 is added to the weight) + // And a 5% chance of a major mutation a new neuron is randomly added to a hidden layer let mut connections = new_fann.get_connections(); // Vector of connections for c in &mut connections { - if thread_rng().gen_range(0..100) < 20 { - c.weight += thread_rng().gen_range(-0.1..0.1); - } - else if thread_rng().gen_range(0..100) < 5 { - c.weight += thread_rng().gen_range(-0.3..0.3); + if thread_rng().gen_range(0.0..1.0) < self.minor_mutation_rate { + debug!("Minor mutation on connection {:?}", c); + c.weight += thread_rng().gen_range(self.weight_initialization_range.clone()); + debug!("New weight: {}", c.weight); } } + new_fann.set_connections(&connections); + + if thread_rng().gen_range(0.0..1.0) < self.major_mutation_rate { + new_fann = major_mutation(&new_fann, self.weight_initialization_range.clone())?; + } // Save the new nn's to the new generation folder let new_nn = new_gen_folder.join(format!("{:06}_fighter_nn_{}.net", self.id, i + survivor_count)); - fann.save(&new_nn) + new_fann.save(&new_nn) .with_context(|| format!("Failed to save nn"))?; } @@ -338,459 +356,15 @@ impl GeneticNode for FighterNN { population_size: left.population_size, // Assuming left and right have the same population size. scores: vec![HashMap::new()], crossbreed_segments: left.crossbreed_segments, - nn_shapes: HashMap::new(), + nn_shapes: left.nn_shapes.clone(), // Assuming left and right have the same nn_shapes. + weight_initialization_range: left.weight_initialization_range.clone(), + minor_mutation_rate: left.minor_mutation_rate, + major_mutation_rate: left.major_mutation_rate, + mutation_weight_range: left.mutation_weight_range.clone(), })) } } -/// Crossbreeds two neural networks of different shapes by finding cut points, and swapping neurons between the two networks. -/// Algorithm tries to ensure similar functionality is maintained between the two networks. -/// It does this by preserving connections between the same neurons from the original to the new network, and if a connection cannot be found -/// it will create a new connection with a random weight. -fn crossbreed(primary: &Fann, secondary: &Fann, crossbreed_segments: usize) -> Result { - // First we need to get the shape of the networks and transform this into a format that is easier to work with - // We want a list of every neuron id, and the layer it is in - let primary_shape = primary.get_layer_sizes(); - let secondary_shape = secondary.get_layer_sizes(); - let primary_neurons = generate_neuron_datastructure(&primary_shape); - let secondary_neurons = generate_neuron_datastructure(&secondary_shape); - - // Now we need to find the cut points for the crossbreed - let start = primary_shape[0] + 1; // Start at the first hidden layer - let end = min(primary_shape.iter().sum::() - primary_shape.last().unwrap(), secondary_shape.iter().sum::() - secondary_shape.last().unwrap()); // End at the last hidden layer - let segment_distribution = Uniform::from(start..end); // Ensure segments are not too small - - let mut cut_points = Vec::new(); - for _ in 0..crossbreed_segments { - let cut_point = segment_distribution.sample(&mut thread_rng()); - if !cut_points.contains(&cut_point) { - cut_points.push(cut_point); - } - } - // Sort the cut points to make it easier to iterate over them - cut_points.sort_unstable(); - - // We need to transform the cut_points vector to a vector of tuples that contain the start and end of each segment - let mut segments = Vec::new(); - let mut previous = 0; - for &cut_point in cut_points.iter() { - segments.push((previous, cut_point)); - previous = cut_point; - } - - let new_neurons = crossbreed_neuron_arrays(segments, primary_neurons, secondary_neurons); - - // Now we need to create the new network with the shape we've determined - let mut new_shape = vec![]; - for (_, _, layer, _) in new_neurons.iter() { - // Check if new_shape has an entry for layer in it - if new_shape.len() <= *layer as usize { - new_shape.push(1); - } - else { - new_shape[*layer as usize] += 1; - } - } - - let mut new_fann = Fann::new(new_shape.as_slice()) - .with_context(|| "Failed to create new fann")?; - // We need to randomize the weights to a small value - new_fann.randomize_weights(-0.1, 0.1); - new_fann.set_activation_func_hidden(ActivationFunc::SigmoidSymmetric); - new_fann.set_activation_func_output(ActivationFunc::SigmoidSymmetric); - - consolidate_old_connections(primary, secondary, new_shape, new_neurons, &mut new_fann); - - Ok(new_fann) -} - -fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: Vec, new_neurons: Vec<(u32, bool, usize, u32)>, new_fann: &mut Fann) { - // Now we need to copy the connections from the original networks to the new network - // We can do this by referencing our connections array, it will contain the original id's of the neurons - // and their new id as well as their layer. We can iterate one layer at a time and copy the connections - - let primary_shape = primary.get_layer_sizes(); - let secondary_shape = secondary.get_layer_sizes(); - debug!("Primary shape: {:?}", primary_shape); - debug!("Secondary shape: {:?}", secondary_shape); - - // Start by iterating layer by later - let primary_connections = primary.get_connections(); - let secondary_connections = secondary.get_connections(); - for layer in 1..new_shape.len() { - // filter out the connections that are in the current layer and previous layer - let current_layer_connections = new_neurons.iter().filter(|(_, _, l, _)| l == &layer).collect::>(); - let previous_layer_connections = new_neurons.iter().filter(|(_, _, l, _)| l == &(layer - 1)).collect::>(); - - // Now we need to iterate over the connections in the current layer - for (neuron_id, is_primary, _, new_id) in current_layer_connections.iter() { - // We need to find the connections from the previous layer to this neuron - for (previous_neuron_id, _, _, previous_new_id) in previous_layer_connections.iter() { - // First we use primary to and check the correct connections array to see if the connection exists - // If it does, we add it to the new network - let mut connection ; - let mut found_in_primary = false; - if *is_primary { - connection = primary_connections.iter() - .find(|connection| { - let from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); - let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); - - // If both neurons have a Some value - if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { - from_neuron == *previous_neuron_id && to_neuron == *neuron_id - } else { - false - } - }); - - if let None = connection { - connection = secondary_connections.iter() - .find(|connection| { - let from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); - let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); - - // If both neurons have a Some value - if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { - from_neuron == *previous_neuron_id && to_neuron == *neuron_id - } else { - false - } - }); - } else { - found_in_primary = true; - } - } - else { - connection = secondary_connections.iter() - .find(|connection| { - let from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); - let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); - - // If both neurons have a Some value - if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { - from_neuron == *previous_neuron_id && to_neuron == *neuron_id - } else { - false - } - }); - - if let None = connection { - connection = primary_connections.iter() - .find(|connection| { - let from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); - let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); - - // If both neurons have a Some value - if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { - from_neuron == *previous_neuron_id && to_neuron == *neuron_id - } else { - false - } - }); - } else { - found_in_primary = true; - } - }; - - // If the connection exists, we need to add it to the new network - if let Some(connection) = connection { - if *is_primary { - let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); - let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); - debug!("Primary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", previous_new_id, new_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, previous_neuron_id, neuron_id); - } else { - let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); - let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); - debug!("Secondary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", previous_new_id, new_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, previous_neuron_id, neuron_id); - } - let translated_from = to_bias_network_id(previous_new_id, &new_shape); - let translated_to = to_bias_network_id(new_id, &new_shape); - new_fann.set_weight(translated_from, translated_to, connection.weight); - } else { - debug!("Connection not found for ({}, {}) -> ({}, {})", previous_new_id, new_id, previous_neuron_id, neuron_id); - } - } - } - } -} - -fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32, usize)>, secondary_neurons: Vec<(u32, usize)>) -> Vec<(u32, bool, usize, u32)> { - // We now need to determine the resulting location of the neurons in the new network. - // To do this we need a new structure that keeps track of the following information: - // - The neuron id from the original network - // - Which network it originated from (primary or secondary) - // - The layer the neuron is in - // - The resulting neuron id in the new network which will be calculated after the fact - let mut new_neurons = Vec::new(); - let mut current_layer = 0; - // keep track of the last layer that we inserted a neuron into for each network - let mut primary_last_layer = 0; - let mut secondary_last_layer = 0; - let mut is_primary = true; - for (i, &segment) in segments.iter().enumerate() { - // If it's the first slice, copy neurons from the primary network up to the cut_point - if i == 0 { - for (neuron_id, layer) in primary_neurons.iter() { - if neuron_id <= &segment.1 { - if layer > ¤t_layer { - current_layer += 1; - } - new_neurons.push((*neuron_id, is_primary, current_layer, 0)); - if is_primary { - primary_last_layer = current_layer; - } - else { - secondary_last_layer = current_layer; - } - } - else { - break; - } - } - } - else { - let target_neurons = if is_primary { &primary_neurons } else { &secondary_neurons }; - - for (neuron_id, layer) in target_neurons.iter() { - // Iterate until neuron_id equals the cut_point - if neuron_id >= &segment.0 && neuron_id <= &segment.1 { - // We need to do something different depending on whether the neuron layer is, lower, higher or equal to the target layer - - // Equal - if layer == ¤t_layer { - new_neurons.push((*neuron_id, is_primary, current_layer, 0)); - - if is_primary { - primary_last_layer = current_layer; - } - else { - secondary_last_layer = current_layer; - } - } - // Earlier - else if layer < ¤t_layer { - // If it's in an earlier layer, add it to the earlier layer - // Check if there's a lower id from the same individual in that earlier layer - // As long as there isn't a neuron from the other individual in between the lower id and current id, add the id values from the same individual - let earlier_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| l == layer).collect::>(); - // get max id from that layer - let highest_id = earlier_layer_neurons.iter().max_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); - if let Some(highest_id) = highest_id { - if highest_id.1 == is_primary { - let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && id < neuron_id && l == layer).collect::>(); - for (neuron_id, layer) in neurons_to_add { - new_neurons.push((*neuron_id, is_primary, *layer, 0)); - - if is_primary { - primary_last_layer = *layer; - } - else { - secondary_last_layer = *layer; - } - } - } - } - - new_neurons.push((*neuron_id, is_primary, *layer, 0)); - - if is_primary { - primary_last_layer = *layer; - } - else { - secondary_last_layer = *layer; - } - } - // Later - else if layer > ¤t_layer { - // If the highest id in the current layer is from the same individual, add anything with a higher id to the current layer before moving to the next layer - // First filter new_neurons to look at neurons from the current layer - let current_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| l == ¤t_layer).collect::>(); - let highest_id = current_layer_neurons.iter().max_by_key(|(id, _, _, _)| id); - if let Some(highest_id) = highest_id { - if highest_id.1 == is_primary { - let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && *l == layer - 1).collect::>(); - for (neuron_id, _) in neurons_to_add { - new_neurons.push((*neuron_id, is_primary, current_layer, 0)); - - if is_primary { - primary_last_layer = current_layer; - } - else { - secondary_last_layer = current_layer; - } - } - } - } - - // If it's in a future layer, move to the next layer - current_layer += 1; - - // Add the neuron to the new network - // Along with any neurons that have a lower id in the future layer - let neurons_to_add = target_neurons.iter().filter(|(id, l)| id <= &neuron_id && l == layer).collect::>(); - for (neuron_id, _) in neurons_to_add { - new_neurons.push((*neuron_id, is_primary, current_layer, 0)); - - if is_primary { - primary_last_layer = current_layer; - } - else { - secondary_last_layer = current_layer; - } - } - } - - } - else if neuron_id >= &segment.1 { - break; - } - } - } - - // Switch to the other network - is_primary = !is_primary; - } - - // For the last segment, copy the remaining neurons - let target_neurons = if is_primary { &primary_neurons } else { &secondary_neurons }; - // Get output layer number - let output_layer = target_neurons.iter().max_by_key(|(_, l)| l).unwrap().1; - - // For the last segment, copy the remaining neurons from the target network - // But when we reach the output layer, we need to add a new layer to the end of new_neurons regardless of it's length - // and copy the output neurons to that layer - for (neuron_id, layer) in target_neurons.iter() { - if neuron_id > &segments.last().unwrap().1 { - if layer == &output_layer { - // Calculate which layer the neurons should be in - current_layer = new_neurons.iter().max_by_key(|(_, _, l, _)| l).unwrap().2 + 1; - for (neuron_id, _) in target_neurons.iter().filter(|(_, l)| l == &output_layer) { - new_neurons.push((*neuron_id, is_primary, current_layer, 0)); - } - break; - } - else if *neuron_id == &segments.last().unwrap().1 + 1 { - let target_layer = if is_primary { primary_last_layer } else { secondary_last_layer }; - let earlier_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| *l >= target_layer && l <= layer).collect::>(); - // get max neuron from with both - // The highest layer - // get max id from that layer - let highest_id = earlier_layer_neurons.iter().max_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); - if let Some(highest_id) = highest_id { - if highest_id.1 == is_primary { - let neurons_to_add = target_neurons.iter().filter(|(id, _)| id > &highest_id.0 && id < neuron_id).collect::>(); - for (neuron_id, l) in neurons_to_add { - new_neurons.push((*neuron_id, is_primary, *l, 0)); - } - } - } - - new_neurons.push((*neuron_id, is_primary, *layer, 0)); - } - else { - new_neurons.push((*neuron_id, is_primary, *layer, 0)); - } - } - } - - // Filtering layers with too few neurons, if necessary - let layer_counts = new_neurons.iter().fold(vec![0; current_layer + 1], |mut counts, &(_, _, layer, _)| { - counts[layer] += 1; - counts - }); - - // Filter out layers based on the minimum number of neurons per layer - new_neurons = new_neurons.into_iter() - .filter(|&(_, _, layer, _)| layer_counts[layer] >= NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN) - .collect::>(); - - // Collect and sort unique layer numbers - let mut unique_layers = new_neurons.iter() - .map(|(_, _, layer, _)| *layer) - .collect::>(); - unique_layers.sort(); - unique_layers.dedup(); // Removes duplicates, keeping only unique layer numbers - - // Create a mapping from old layer numbers to new (gap-less) layer numbers - let layer_mapping = unique_layers.iter().enumerate() - .map(|(new_layer, &old_layer)| (old_layer, new_layer)) - .collect::>(); - - // Apply the mapping to renumber layers in new_neurons - new_neurons.iter_mut().for_each(|(_, _, layer, _)| { - *layer = *layer_mapping.get(layer).unwrap_or(layer); // Fallback to original layer if not found, though it should always find a match - }); - - // Assign new IDs - // new_neurons must be sorted by layer, then by neuron ID within the layer - new_neurons.sort_unstable_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); - new_neurons.iter_mut().enumerate().for_each(|(new_id, neuron)| { - neuron.3 = new_id as u32; - }); - - new_neurons -} - -fn generate_neuron_datastructure(shape: &[u32]) -> Vec<(u32, usize)> { - let mut result = Vec::new(); - let mut global_index = 0; // Keep a global index that does not reset - - for (layer_index, &neurons) in shape.iter().enumerate() { - for _ in 0..neurons { - result.push((global_index, layer_index)); - global_index += 1; // Increment global index for each neuron - } - // global_index += 1; // Skip index for bias neuron at the end of each layer - } - - result -} - - -fn to_bias_network_id(id: &u32, shape: &Vec) -> u32 { - // The given id comes from a network without a bias neuron at the end of every layer - // We need to translate this id to the id in the network with bias neurons - let mut translated_id = 0; - for (layer_index, &neurons) in shape.iter().enumerate() { - for _ in 0..neurons { - if &translated_id == id { - return translated_id + layer_index as u32; - } - translated_id += 1; - } - } - - // If the id is not found, return the id - translated_id -} - -fn to_non_bias_network_id(id: u32, shape: &[u32]) -> Option { - let mut bias_count = 0; // Count of bias neurons encountered up to the current ID - let mut total_neurons = 0; // Total count of neurons (excluding bias neurons) processed - - for &neurons in shape { - let layer_end = total_neurons + neurons; // End of the current layer, excluding the bias neuron - if id < layer_end { - // ID is within the current layer (excluding the bias neuron) - return Some(id - bias_count); - } - if id == layer_end { - // ID matches the position where a bias neuron would be - return None; - } - - // Update counts after considering the current layer - total_neurons += neurons + 1; // Move to the next layer, accounting for the bias neuron - bias_count += 1; // Increment bias count as we've moved past where a bias neuron would be - } - - // If the ID is beyond the range of all neurons (including bias), it's treated as invalid - // Adjust this behavior based on your application's needs - None -} - - async fn read_score_from_file(file_path: &Path, nn_id: &str) -> Result { let mut attempts = 0; @@ -826,435 +400,4 @@ async fn read_score_from_file(file_path: &Path, nn_id: &str) -> Result return Err(e), } } -} - -#[cfg(test)] -mod tests { - use std::collections::HashSet; - - use super::*; - - #[test] - fn crossbreed_neuron_arrays_test() { - // Assign - let segments = vec![(0, 3), (4, 6), (7, 8), (9, 10)]; - - let primary_network = generate_neuron_datastructure(&vec![4, 8, 6, 4]); - - let secondary_network = generate_neuron_datastructure(&vec![4, 3, 3, 3, 3, 3, 4]); - - // Act - let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); - - // Expected Result Set - let expected: HashSet<(u32, bool, usize, u32)> = vec![ - // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), - // Hidden Layer 1: Expect 8 - (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, true, 1, 7), (8, true, 1, 8), (9, true, 1, 9), (10, true, 1, 10), (11, true, 1, 11), - // Hidden Layer 2: Expect 9 - (7, false, 2, 12), (8, false, 2, 13), (9, false, 2, 14), (12, true, 2, 15), (13, true, 2, 16), (14, true, 2, 17), (15, true, 2, 18), (16, true, 2, 19), (17, true, 2, 20), - // Output Layer: Expect 4 - (18, true, 3, 21), (19, true, 3, 22), (20, true, 3, 23), (21, true, 3, 24), - ].into_iter().collect(); - - // Convert Result to HashSet for Comparison - let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); - - // Assert - assert_eq!(result_set, expected); - - // Now we test the ooposite case - // Act - let result = crossbreed_neuron_arrays(segments.clone(), secondary_network.clone(), primary_network.clone()); - - // Expected Result Set - let expected: HashSet<(u32, bool, usize, u32)> = vec![ - // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), - // Hidden Layer 1: Expect 7 - (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, false, 1, 7), (8, false, 1, 8), (9, false, 1, 9), (10, false, 1, 10), - // Hidden Layer 2: Expect 3 - (7, true, 2, 11), (8, true, 2, 12), (9, true, 2, 13), - // Hidden Layer 3: Expect 3 - (10, true, 3, 14), (11, true, 3, 15), (12, true, 3, 16), - // Hidden Layer 4: Expect 3 - (13, true, 4, 17), (14, true, 4, 18), (15, true, 4, 19), - // Hidden Layer 5: Expect 3 - (16, true, 5, 20), (17, true, 5, 21), (18, true, 5, 22), - // Output Layer: Expect 4 - (19, true, 6, 23), (20, true, 6, 24), (21, true, 6, 25), (22, true, 6, 26), - ].into_iter().collect(); - - // Convert Result to HashSet for Comparison - let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); - - // Assert - assert_eq!(result_set, expected); - - // Testing with a different segment - // Assign - let segments = vec![(0, 4), (5, 14), (15, 15), (16, 16)]; - - // Act - let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); - - // Expected Result Set - let expected: HashSet<(u32, bool, usize, u32)> = vec![ - // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), - // Hidden Layer 1: Expect 3 - (4, true, 1, 4), (5, false, 1, 5), (6, false, 1, 6), - // Hidden Layer 2: Expect 6 - (7, false, 2, 7), (8, false, 2, 8), (9, false, 2, 9), (15, true, 2, 10), (16, true, 2, 11), (17, true, 2, 12), - // Hidden Layer 3: Expect 3 - (10, false, 3, 13), (11, false, 3, 14), (12, false, 3, 15), - // Hidden Layer 4: Expect 3 - (13, false, 4, 16), (14, false, 4, 17), (15, false, 4, 18), - // Output Layer: Expect 4 - (18, true, 5, 19), (19, true, 5, 20), (20, true, 5, 21), (21, true, 5, 22), - ].into_iter().collect(); - - // print result before comparison - for r in result.iter() { - println!("{:?}", r); - } - - // Convert Result to HashSet for Comparison - let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); - - // Assert - assert_eq!(result_set, expected); - - // Swapping order - let result = crossbreed_neuron_arrays(segments.clone(), secondary_network.clone(), primary_network.clone()); - - // Expected Result Set - let expected: HashSet<(u32, bool, usize, u32)> = vec![ - // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), - // Hidden Layer 1: Expect 8 - (4, true, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, false, 1, 7), (8, false, 1, 8), (9, false, 1, 9), (10, false, 1, 10), (11, false, 1, 11), - // Hidden Layer 2: Expect 5 - (12, false, 2, 12), (13, false, 2, 13), (14, false, 2, 14), (15, false, 2, 15), (16, false, 2, 16), - // Hidden Layer 3: Expect 3 - (13, true, 3, 17), (14, true, 3, 18), (15, true, 3, 19), - // Hidden Layer 4: Expect 3 - (16, true, 4, 20), (17, true, 4, 21), (18, true, 4, 22), - // Output Layer: Expect 4 - (19, true, 5, 23), (20, true, 5, 24), (21, true, 5, 25), (22, true, 5, 26), - ].into_iter().collect(); - - // print result before comparison - for r in result.iter() { - println!("{:?}", r); - } - - // Convert Result to HashSet for Comparison - let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); - - // Assert - assert_eq!(result_set, expected); - - // Testing with a different segment - // Assign - let segments = vec![(0, 7), (8, 9), (10, 10), (11, 12)]; - - // Act - let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); - - // Expected Result Set - let expected: HashSet<(u32, bool, usize, u32)> = vec![ - // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), - // Hidden Layer 1: Expect 7 - (4, true, 1, 4), (5, true, 1, 5), (6, true, 1, 6), (7, true, 1, 7), (8, true, 1, 8), (9, true, 1, 9), (10, true, 1, 10), - // Hidden Layer 2: Expect 8 - (7, false, 2, 11), (8, false, 2, 12), (9, false, 2, 13), (13, true, 2, 14), (14, true, 2, 15), (15, true, 2, 16), (16, true, 2, 17), (17, true, 2, 18), - // Hidden Layer 3: Expect 3 - (10, false, 3, 19), (11, false, 3, 20), (12, false, 3, 21), - // Output Layer: Expect 4 - (18, true, 4, 22), (19, true, 4, 23), (20, true, 4, 24), (21, true, 4, 25), - ].into_iter().collect(); - - // print result before comparison - for r in result.iter() { - println!("{:?}", r); - } - - // Convert Result to HashSet for Comparison - let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); - - // Assert - assert_eq!(result_set, expected); - - // Swapping order - let result = crossbreed_neuron_arrays(segments.clone(), secondary_network.clone(), primary_network.clone()); - - // Expected Result Set - let expected: HashSet<(u32, bool, usize, u32)> = vec![ - // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), - // Hidden Layer 1: Expect 7 - (4, true, 1, 4), (5, true, 1, 5), (6, true, 1, 6), (8, false, 1, 7), (9, false, 1, 8), (10, false, 1, 9), (11, false, 1, 10), - // Hidden Layer 2: Expect 4 - (7, true, 2, 11), (8, true, 2, 12), (9, true, 2, 13), (12, false, 2, 14), - // Hidden Layer 3: Expect 3 - (10, true, 3, 15), (11, true, 3, 16), (12, true, 3, 17), - // Hidden Layer 4: Expect 3 - (13, true, 4, 18), (14, true, 4, 19), (15, true, 4, 20), - // Hidden Layer 5: Expect 3 - (16, true, 5, 21), (17, true, 5, 22), (18, true, 5, 23), - // Output Layer: Expect 4 - (19, true, 6, 24), (20, true, 6, 25), (21, true, 6, 26), (22, true, 6, 27), - ].into_iter().collect(); - - // print result before comparison - for r in result.iter() { - println!("{:?}", r); - } - - // Convert Result to HashSet for Comparison - let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); - - // Assert - assert_eq!(result_set, expected); - - // Testing networks with the same size - // Assign - let segments = vec![(0, 3), (4, 6), (7, 8), (9, 11)]; - - let primary_network = generate_neuron_datastructure(&vec![4, 3, 4, 5, 4]); - - vec![ - // Input layer - (0, 0), (1, 0), (2, 0), (3, 0), - // Hidden layer 1: 3 neurons - (4, 1), (5, 1), (6, 1), - // Hidden Layer 2: 4 neurons - (7, 2), (8, 2), (9, 2), (10, 2), - // Hidden Layer 3: 5 neurons - (11, 3), (12, 3), (13, 3), (14, 3), (15, 3), - // Output layer - (16, 4), (17, 4), (18, 4), (19, 4), - ]; - - let secondary_network = primary_network.clone(); - - // Act - let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); - - // Expected Result Set - let expected: HashSet<(u32, bool, usize, u32)> = vec![ - // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), - // Hidden Layer 1: Expect 3 - (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), - // Hidden Layer 2: Expect 4 - (7, true, 2, 7), (8, true, 2, 8), (9, false, 2, 9), (10, false, 2, 10), - // Hidden Layer 3: Expect 5 - (11, false, 3, 11), (12, true, 3, 12), (13, true, 3, 13), (14, true, 3, 14), (15, true, 3, 15), - // Output Layer: Expect 4 - (16, true, 4, 16), (17, true, 4, 17), (18, true, 4, 18), (19, true, 4, 19), - ].into_iter().collect(); - - // print result before comparison - for r in result.iter() { - println!("{:?}", r); - } - - // Convert Result to HashSet for Comparison - let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); - - // Assert - assert_eq!(result_set, expected); - - // Testing with different segment - let segments = vec![(0, 5), (6, 6), (7, 11), (12, 13)]; - - // Act - let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); - - // Expected Result Set - let expected: HashSet<(u32, bool, usize, u32)> = vec![ - // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), - // Hidden Layer 1: Expect 3 - (4, true, 1, 4), (5, true, 1, 5), (6, false, 1, 6), - // Hidden Layer 2: Expect 4 - (7, true, 2, 7), (8, true, 2, 8), (9, true, 2, 9), (10, true, 2, 10), - // Hidden Layer 3: Expect 5 - (11, true, 3, 11), (12, false, 3, 12), (13, false, 3, 13), (14, true, 3, 14), (15, true, 3, 15), - // Output Layer: Expect 4 - (16, true, 4, 16), (17, true, 4, 17), (18, true, 4, 18), (19, true, 4, 19), - ].into_iter().collect(); - - // print result before comparison - for r in result.iter() { - println!("{:?}", r); - } - - // Convert Result to HashSet for Comparison - let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); - - // Assert - assert_eq!(result_set, expected); - } - - #[test] - fn generate_neuron_datastructure_test() { - // Assign - let shape = vec![4, 3, 5, 4]; - - // Act - let result = generate_neuron_datastructure(shape.as_slice()); - - // Expected Result - let expected: Vec<(u32, usize)> = vec![ - (0, 0), (1, 0), (2, 0), (3, 0), - (4, 1), (5, 1), (6, 1), - (7, 2), (8, 2), (9, 2), (10, 2), (11, 2), - (12, 3), (13, 3), (14, 3), (15, 3), - ]; - - // Assert - assert_eq!(result, expected); - } - - #[test] - fn translate_neuron_id_test() { - // Assign - let shape = vec![4, 3, 5, 4]; - - let expected = vec![ - // (input, expected output) - (0, 0), (1, 1), (2, 2), (3, 3), - (4, 5), (5, 6), (6, 7), - (7, 9), (8, 10), (9, 11), (10, 12), (11, 13), - (12, 15), (13, 16), (14, 17), (15, 18), - ]; - - // Act - for (input, expected_output) in expected { - let result = to_bias_network_id(&input, &shape); - // Assert - assert_eq!(result, expected_output); - - // Go the other direction too - let result = to_non_bias_network_id(expected_output, &shape); - - // Assert - if let Some(result) = result { - assert_eq!(result, input); - } - else { - assert!(false, "Expected Some, got None"); - } - } - - // Validate bias neuron values - let bias_neurons = vec![4, 8, 14, 19]; - - for &bias_neuron in bias_neurons.iter() { - let result = to_non_bias_network_id(bias_neuron, &shape); - - // Assert - assert!(result.is_none()); - } - } - - #[test] - fn consolidate_old_connections_test() -> Result<(), Box>{ - // Assign - let primary_shape = vec![4, 8, 6, 4]; - let secondary_shape = vec![4, 3, 3, 3, 3, 3, 4]; - - let mut primary_fann = Fann::new(&primary_shape)?; - let mut secondary_fann = Fann::new(&secondary_shape)?; - - let mut primary_connections = primary_fann.get_connections(); - for connection in primary_connections.iter_mut() { - connection.weight = ((connection.from_neuron * 100) + connection.to_neuron) as f32; - } - primary_fann.set_connections(&primary_connections); - - let mut secondary_connections = secondary_fann.get_connections(); - for connection in secondary_connections.iter_mut() { - connection.weight = ((connection.from_neuron * 100) + connection.to_neuron) as f32; - connection.weight = connection.weight * -1.0; - } - secondary_fann.set_connections(&secondary_connections); - - let new_neurons = vec![ - // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), - // Hidden Layer 1: Expect 8 - (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, true, 1, 7), (8, true, 1, 8), (9, true, 1, 9), (10, true, 1, 10), (11, true, 1, 11), - // Hidden Layer 2: Expect 9 - (7, false, 2, 12), (8, false, 2, 13), (9, false, 2, 14), (12, true, 2, 15), (13, true, 2, 16), (14, true, 2, 17), (15, true, 2, 18), (16, true, 2, 19), (17, true, 2, 20), - // Output Layer: Expect 4 - (18, true, 3, 21), (19, true, 3, 22), (20, true, 3, 23), (21, true, 3, 24), - ]; - let new_shape = vec![4, 8, 9, 4]; - let mut new_fann = Fann::new(&[4, 8, 9, 4])?; - // Initialize weights to 0 - let mut new_connections = new_fann.get_connections(); - for connection in new_connections.iter_mut() { - connection.weight = 0.0; - } - new_fann.set_connections(&new_connections); - - // Act - consolidate_old_connections(&primary_fann, &secondary_fann, new_shape, new_neurons, &mut new_fann); - - // Bias neurons - // Layer 1: 4 - // Layer 2: 13 - // Layer 3: 23 - let expected_connections = vec![ - // (from_neuron, to_neuron, weight) - // Hidden Layer 1 (5-12) - (0, 5, -5.0), (1, 5, -105.0), (2, 5, -205.0), (3, 5, -305.0), - (0, 6, -6.0), (1, 6, -106.0), (2, 6, -206.0), (3, 6, -306.0), - (0, 7, -7.0), (1, 7, -107.0), (2, 7, -207.0), (3, 7, -307.0), - (0, 8, 8.0), (1, 8, 108.0), (2, 8, 208.0), (3, 8, 308.0), - (0, 9, 9.0), (1, 9, 109.0), (2, 9, 209.0), (3, 9, 309.0), - (0, 10, 10.0), (1, 10, 110.0), (2, 10, 210.0), (3, 10, 310.0), - (0, 11, 11.0), (1, 11, 111.0), (2, 11, 211.0), (3, 11, 311.0), - (0, 12, 12.0), (1, 12, 112.0), (2, 12, 212.0), (3, 12, 312.0), - // Hidden Layer 2 (14-22) - (5, 14, -509.0), (6, 14, -609.0), (7, 14, -709.0), (8, 14, 0.0), (9, 14, 0.0), (10, 14, 0.0), (11, 14, 0.0), (12, 14, 0.0), - (5, 15, -510.0), (6, 15, -610.0), (7, 15, -710.0), (8, 15, 0.0), (9, 15, 0.0), (10, 15, 0.0), (11, 15, 0.0), (12, 15, 0.0), - (5, 16, -511.0), (6, 16, -611.0), (7, 16, -711.0), (8, 16, 0.0), (9, 16, 0.0), (10, 16, 0.0), (11, 16, 0.0), (12, 16, 0.0), - (5, 17, 514.0), (6, 17, 614.0), (7, 17, 714.0), (8, 17, 814.0), (9, 17, 914.0), (10, 17, 1014.0), (11, 17, 1114.0), (12, 17, 1214.0), - (5, 18, 515.0), (6, 18, 615.0), (7, 18, 715.0), (8, 18, 815.0), (9, 18, 915.0), (10, 18, 1015.0), (11, 18, 1115.0), (12, 18, 1215.0), - (5, 19, 516.0), (6, 19, 616.0), (7, 19, 716.0), (8, 19, 816.0), (9, 19, 916.0), (10, 19, 1016.0), (11, 19, 1116.0), (12, 19, 1216.0), - (5, 20, 517.0), (6, 20, 617.0), (7, 20, 717.0), (8, 20, 817.0), (9, 20, 917.0), (10, 20, 1017.0), (11, 20, 1117.0), (12, 20, 1217.0), - (5, 21, 518.0), (6, 21, 618.0), (7, 21, 718.0), (8, 21, 818.0), (9, 21, 918.0), (10, 21, 1018.0), (11, 21, 1118.0), (12, 21, 1218.0), - (5, 22, 519.0), (6, 22, 619.0), (7, 22, 719.0), (8, 22, 819.0), (9, 22, 919.0), (10, 22, 1019.0), (11, 22, 1119.0), (12, 22, 1219.0), - // Output layer (24-27) - (14, 24, 0.0), (15, 24, 0.0), (16, 24, 0.0), (17, 24, 1421.0), (18, 24, 1521.0), (19, 24, 1621.0), (20, 24, 1721.0), (21, 24, 1821.0), (22, 24, 1921.0), - (14, 25, 0.0), (15, 25, 0.0), (16, 25, 0.0), (17, 25, 1422.0), (18, 25, 1522.0), (19, 25, 1622.0), (20, 25, 1722.0), (21, 25, 1822.0), (22, 25, 1922.0), - (14, 26, 0.0), (15, 26, 0.0), (16, 26, 0.0), (17, 26, 1423.0), (18, 26, 1523.0), (19, 26, 1623.0), (20, 26, 1723.0), (21, 26, 1823.0), (22, 26, 1923.0), - (14, 27, 0.0), (15, 27, 0.0), (16, 27, 0.0), (17, 27, 1424.0), (18, 27, 1524.0), (19, 27, 1624.0), (20, 27, 1724.0), (21, 27, 1824.0), (22, 27, 1924.0), - ]; - - for connection in new_fann.get_connections().iter() { - println!("{:?}", connection); - } - - // Assert - // Compare each connection to the expected connection - let new_connections = new_fann.get_connections(); - for connection in expected_connections.iter() { - let matching_connection = new_connections.iter().find(|&c| c.from_neuron == connection.0 && c.to_neuron == connection.1); - if let Some(matching_connection) = matching_connection { - assert_eq!(matching_connection.weight, connection.2, "Connection: {:?}", matching_connection); - } else { - assert!(false, "Connection not found: {:?}", connection); - } - } - - Ok(()) - } } \ No newline at end of file diff --git a/gemla/src/bin/fighter_nn/neural_network_utility.rs b/gemla/src/bin/fighter_nn/neural_network_utility.rs new file mode 100644 index 0000000..1dcc5ae --- /dev/null +++ b/gemla/src/bin/fighter_nn/neural_network_utility.rs @@ -0,0 +1,1237 @@ +use std::{cmp::min, collections::HashMap, ops::Range}; + +use anyhow::Context; +use fann::{ActivationFunc, Fann}; +use gemla::error::Error; +use rand::{distributions::{Distribution, Uniform}, seq::IteratorRandom, thread_rng, Rng}; + +use super::{FighterNN, NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN}; + + +/// Crossbreeds two neural networks of different shapes by finding cut points, and swapping neurons between the two networks. +/// Algorithm tries to ensure similar functionality is maintained between the two networks. +/// It does this by preserving connections between the same neurons from the original to the new network, and if a connection cannot be found +/// it will create a new connection with a random weight. +pub fn crossbreed(fighter_nn: &FighterNN, primary: &Fann, secondary: &Fann, crossbreed_segments: usize) -> Result { + // First we need to get the shape of the networks and transform this into a format that is easier to work with + // We want a list of every neuron id, and the layer it is in + let primary_shape = primary.get_layer_sizes(); + let secondary_shape = secondary.get_layer_sizes(); + let primary_neurons = generate_neuron_datastructure(&primary_shape); + let secondary_neurons = generate_neuron_datastructure(&secondary_shape); + + let segments = generate_segments(primary_shape, secondary_shape, crossbreed_segments); + + let new_neurons = crossbreed_neuron_arrays(segments, primary_neurons, secondary_neurons); + + // Now we need to create the new network with the shape we've determined + let mut new_shape = vec![]; + for (_, _, layer, _) in new_neurons.iter() { + // Check if new_shape has an entry for layer in it + if new_shape.len() <= *layer as usize { + new_shape.push(1); + } + else { + new_shape[*layer as usize] += 1; + } + } + + let mut new_fann = Fann::new(new_shape.as_slice()) + .with_context(|| "Failed to create new fann")?; + // We need to randomize the weights to a small value + new_fann.randomize_weights(fighter_nn.weight_initialization_range.start, fighter_nn.weight_initialization_range.end); + new_fann.set_activation_func_hidden(ActivationFunc::SigmoidSymmetric); + new_fann.set_activation_func_output(ActivationFunc::SigmoidSymmetric); + + consolidate_old_connections(primary, secondary, new_shape, new_neurons, &mut new_fann); + + Ok(new_fann) +} + +pub fn generate_segments(primary_shape: Vec, secondary_shape: Vec, crossbreed_segments: usize) -> Vec<(u32, u32)> { + // Now we need to find the cut points for the crossbreed + let start = primary_shape[0] + 1; + // Start at the first hidden layer + let end = min(primary_shape.iter().sum::() - primary_shape.last().unwrap(), secondary_shape.iter().sum::() - secondary_shape.last().unwrap()); + // End at the last hidden layer + let segment_distribution = Uniform::from(start..end); + // Ensure segments are not too small + + let mut cut_points = Vec::new(); + for _ in 0..crossbreed_segments { + let cut_point = segment_distribution.sample(&mut thread_rng()); + if !cut_points.contains(&cut_point) { + cut_points.push(cut_point); + } + } + // Sort the cut points to make it easier to iterate over them + cut_points.sort_unstable(); + + // We need to transform the cut_points vector to a vector of tuples that contain the start and end of each segment + let mut segments = Vec::new(); + let mut previous = 0; + for &cut_point in cut_points.iter() { + segments.push((previous, cut_point - 1)); + previous = cut_point; + } + segments +} + +pub fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: Vec, new_neurons: Vec<(u32, bool, usize, u32)>, new_fann: &mut Fann) { + // Now we need to copy the connections from the original networks to the new network + // We can do this by referencing our connections array, it will contain the original id's of the neurons + // and their new id as well as their layer. We can iterate one layer at a time and copy the connections + + let primary_shape = primary.get_layer_sizes(); + let secondary_shape = secondary.get_layer_sizes(); + debug!("Primary shape: {:?}", primary_shape); + debug!("Secondary shape: {:?}", secondary_shape); + + // Start by iterating layer by later + let primary_connections = primary.get_connections(); + let secondary_connections = secondary.get_connections(); + for layer in 1..new_shape.len() { + // filter out the connections that are in the current layer and previous layer + let current_layer_connections = new_neurons.iter().filter(|(_, _, l, _)| l == &layer).collect::>(); + let previous_layer_connections = new_neurons.iter().filter(|(_, _, l, _)| l == &(layer - 1)).collect::>(); + + // Now we need to iterate over the connections in the current layer + for (neuron_id, is_primary, _, new_id) in current_layer_connections.iter() { + // We need to find the connections from the previous layer to this neuron + for (previous_neuron_id, _, _, previous_new_id) in previous_layer_connections.iter() { + // First we use primary to and check the correct connections array to see if the connection exists + // If it does, we add it to the new network + let mut connection; + let mut found_in_primary = false; + if *is_primary { + connection = primary_connections.iter() + .find(|connection| { + let from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); + let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); + + // If both neurons have a Some value + if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { + from_neuron == *previous_neuron_id && to_neuron == *neuron_id + } else { + false + } + }); + + if let None = connection { + connection = secondary_connections.iter() + .find(|connection| { + let from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); + let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + + // If both neurons have a Some value + if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { + from_neuron == *previous_neuron_id && to_neuron == *neuron_id + } else { + false + } + }); + } else { + found_in_primary = true; + } + } + else { + connection = secondary_connections.iter() + .find(|connection| { + let from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); + let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + + // If both neurons have a Some value + if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { + from_neuron == *previous_neuron_id && to_neuron == *neuron_id + } else { + false + } + }); + + if let None = connection { + connection = primary_connections.iter() + .find(|connection| { + let from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); + let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); + + // If both neurons have a Some value + if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { + from_neuron == *previous_neuron_id && to_neuron == *neuron_id + } else { + false + } + }); + } else { + found_in_primary = true; + } + }; + + // If the connection exists, we need to add it to the new network + if let Some(connection) = connection { + if *is_primary { + let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); + let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); + debug!("Primary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", previous_new_id, new_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, previous_neuron_id, neuron_id); + } else { + let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); + let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + debug!("Secondary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", previous_new_id, new_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, previous_neuron_id, neuron_id); + } + let translated_from = to_bias_network_id(previous_new_id, &new_shape); + let translated_to = to_bias_network_id(new_id, &new_shape); + new_fann.set_weight(translated_from, translated_to, connection.weight); + } else { + debug!("Connection not found for ({}, {}) -> ({}, {})", previous_new_id, new_id, previous_neuron_id, neuron_id); + } + } + } + + // Add bias neuron connections + let bias_neuron = get_bias_neuron_for_layer(layer, &new_shape); + if let Some(bias_neuron) = bias_neuron { + // Loop through neurons in current layer + for (neuron_id, is_primary, _, new_id) in current_layer_connections.iter() { + let translated_neuron_id = to_bias_network_id(new_id, &new_shape); + + let mut connection; + let mut found_in_primary = false; + if *is_primary { + let primary_bias_neuron = get_bias_neuron_for_layer(layer, &primary_shape).unwrap(); + connection = primary_connections.iter() + .find(|connection| { + let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); + + if let Some(to_neuron) = to_neuron { + connection.from_neuron == primary_bias_neuron && to_neuron == *neuron_id + } else { + false + } + }); + + if let None = connection { + let secondary_bias_neuron = get_bias_neuron_for_layer(layer, &secondary_shape).unwrap(); + connection = secondary_connections.iter() + .find(|connection| { + let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + + if let Some(to_neuron) = to_neuron { + connection.from_neuron == secondary_bias_neuron && to_neuron == *neuron_id + } else { + false + } + }); + } else { + found_in_primary = true; + } + } else { + let secondary_bias_neuron = get_bias_neuron_for_layer(layer, &secondary_shape).unwrap(); + connection = secondary_connections.iter() + .find(|connection| { + let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + + if let Some(to_neuron) = to_neuron { + connection.from_neuron == secondary_bias_neuron && to_neuron == *neuron_id + } else { + false + } + }); + + if let None = connection { + let primary_bias_neuron = get_bias_neuron_for_layer(layer, &primary_shape).unwrap(); + connection = primary_connections.iter() + .find(|connection| { + let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); + + if let Some(to_neuron) = to_neuron { + connection.from_neuron == primary_bias_neuron && to_neuron == *neuron_id + } else { + false + } + }); + } else { + found_in_primary = true; + } + } + + if let Some(connection) = connection { + if *is_primary { + let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); + let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); + debug!("Primary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", bias_neuron, translated_neuron_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, bias_neuron, neuron_id); + } else { + let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); + let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + debug!("Secondary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", bias_neuron, translated_neuron_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, bias_neuron, neuron_id); + } + new_fann.set_weight(bias_neuron, translated_neuron_id, connection.weight); + } else { + debug!("Connection not found for bias ({}, {}) -> ({}, {}) primary: {}", bias_neuron, neuron_id, bias_neuron, translated_neuron_id, is_primary); + } + } + } + } +} + +pub fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32, usize)>, secondary_neurons: Vec<(u32, usize)>) -> Vec<(u32, bool, usize, u32)> { + // We now need to determine the resulting location of the neurons in the new network. + // To do this we need a new structure that keeps track of the following information: + // - The neuron id from the original network + // - Which network it originated from (primary or secondary) + // - The layer the neuron is in + // - The resulting neuron id in the new network which will be calculated after the fact + let mut new_neurons = Vec::new(); + let mut current_layer = 0; + // keep track of the last layer that we inserted a neuron into for each network + let mut primary_last_layer = 0; + let mut secondary_last_layer = 0; + let mut is_primary = true; + for (i, &segment) in segments.iter().enumerate() { + // If it's the first slice, copy neurons from the primary network up to the cut_point + if i == 0 { + for (neuron_id, layer) in primary_neurons.iter() { + if neuron_id <= &segment.1 { + if layer > ¤t_layer { + current_layer += 1; + } + new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + if is_primary { + primary_last_layer = current_layer; + } + else { + secondary_last_layer = current_layer; + } + } + else { + break; + } + } + } + else { + let target_neurons = if is_primary { &primary_neurons } else { &secondary_neurons }; + + for (neuron_id, layer) in target_neurons.iter() { + // Iterate until neuron_id equals the cut_point + if neuron_id >= &segment.0 && neuron_id <= &segment.1 { + // We need to do something different depending on whether the neuron layer is, lower, higher or equal to the target layer + + // Equal + if layer == ¤t_layer { + new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + + if is_primary { + primary_last_layer = current_layer; + } + else { + secondary_last_layer = current_layer; + } + } + // Earlier + else if layer < ¤t_layer { + // If it's in an earlier layer, add it to the earlier layer + // Check if there's a lower id from the same individual in that earlier layer + // As long as there isn't a neuron from the other individual in between the lower id and current id, add the id values from the same individual + let earlier_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| l == layer).collect::>(); + // get max id from that layer + let highest_id = earlier_layer_neurons.iter().max_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); + if let Some(highest_id) = highest_id { + if highest_id.1 == is_primary { + let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && id < neuron_id && l == layer).collect::>(); + for (neuron_id, layer) in neurons_to_add { + new_neurons.push((*neuron_id, is_primary, *layer, 0)); + + if is_primary { + primary_last_layer = *layer; + } + else { + secondary_last_layer = *layer; + } + } + } + } + + new_neurons.push((*neuron_id, is_primary, *layer, 0)); + + if is_primary { + primary_last_layer = *layer; + } + else { + secondary_last_layer = *layer; + } + } + // Later + else if layer > ¤t_layer { + // If the highest id in the current layer is from the same individual, add anything with a higher id to the current layer before moving to the next layer + // First filter new_neurons to look at neurons from the current layer + let current_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| l == ¤t_layer).collect::>(); + let highest_id = current_layer_neurons.iter().max_by_key(|(id, _, _, _)| id); + if let Some(highest_id) = highest_id { + if highest_id.1 == is_primary { + let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && *l == layer - 1).collect::>(); + for (neuron_id, _) in neurons_to_add { + new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + + if is_primary { + primary_last_layer = current_layer; + } + else { + secondary_last_layer = current_layer; + } + } + } + } + + // If it's in a future layer, move to the next layer + current_layer += 1; + + // Add the neuron to the new network + // Along with any neurons that have a lower id in the future layer + let neurons_to_add = target_neurons.iter().filter(|(id, l)| id <= &neuron_id && l == layer).collect::>(); + for (neuron_id, _) in neurons_to_add { + new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + + if is_primary { + primary_last_layer = current_layer; + } + else { + secondary_last_layer = current_layer; + } + } + } + + } + else if neuron_id >= &segment.1 { + break; + } + } + } + + // Switch to the other network + is_primary = !is_primary; + } + + // For the last segment, copy the remaining neurons + let target_neurons = if is_primary { &primary_neurons } else { &secondary_neurons }; + // Get output layer number + let output_layer = target_neurons.iter().max_by_key(|(_, l)| l).unwrap().1; + + // For the last segment, copy the remaining neurons from the target network + // But when we reach the output layer, we need to add a new layer to the end of new_neurons regardless of it's length + // and copy the output neurons to that layer + for (neuron_id, layer) in target_neurons.iter() { + if neuron_id > &segments.last().unwrap().1 { + if layer == &output_layer { + // Calculate which layer the neurons should be in + current_layer = new_neurons.iter().max_by_key(|(_, _, l, _)| l).unwrap().2 + 1; + for (neuron_id, _) in target_neurons.iter().filter(|(_, l)| l == &output_layer) { + new_neurons.push((*neuron_id, is_primary, current_layer, 0)); + } + break; + } + else if *neuron_id == &segments.last().unwrap().1 + 1 { + let target_layer = if is_primary { primary_last_layer } else { secondary_last_layer }; + let earlier_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| *l >= target_layer && l <= layer).collect::>(); + // get max neuron from with both + // The highest layer + // get max id from that layer + let highest_id = earlier_layer_neurons.iter().max_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); + if let Some(highest_id) = highest_id { + if highest_id.1 == is_primary { + let neurons_to_add = target_neurons.iter().filter(|(id, _)| id > &highest_id.0 && id < neuron_id).collect::>(); + for (neuron_id, l) in neurons_to_add { + new_neurons.push((*neuron_id, is_primary, *l, 0)); + } + } + } + + new_neurons.push((*neuron_id, is_primary, *layer, 0)); + } + else { + new_neurons.push((*neuron_id, is_primary, *layer, 0)); + } + } + } + + // Filtering layers with too few neurons, if necessary + let layer_counts = new_neurons.iter().fold(vec![0; current_layer + 1], |mut counts, &(_, _, layer, _)| { + counts[layer] += 1; + counts + }); + + // Filter out layers based on the minimum number of neurons per layer + new_neurons = new_neurons.into_iter() + .filter(|&(_, _, layer, _)| layer_counts[layer] >= NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN) + .collect::>(); + + // Collect and sort unique layer numbers + let mut unique_layers = new_neurons.iter() + .map(|(_, _, layer, _)| *layer) + .collect::>(); + unique_layers.sort(); + unique_layers.dedup(); // Removes duplicates, keeping only unique layer numbers + + // Create a mapping from old layer numbers to new (gap-less) layer numbers + let layer_mapping = unique_layers.iter().enumerate() + .map(|(new_layer, &old_layer)| (old_layer, new_layer)) + .collect::>(); + + // Apply the mapping to renumber layers in new_neurons + new_neurons.iter_mut().for_each(|(_, _, layer, _)| { + *layer = *layer_mapping.get(layer).unwrap_or(layer); // Fallback to original layer if not found, though it should always find a match + }); + + // Assign new IDs + // new_neurons must be sorted by layer, then by neuron ID within the layer + new_neurons.sort_unstable_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); + new_neurons.iter_mut().enumerate().for_each(|(new_id, neuron)| { + neuron.3 = new_id as u32; + }); + + new_neurons +} + +pub fn major_mutation(fann: &Fann, weight_initialization_range: Range) -> Result { + // add or remove a random neuron from a hidden layer + let mut mutated_shape = fann.get_layer_sizes().to_vec(); + let mut mutated_neurons = generate_neuron_datastructure(&mutated_shape).iter().map(|(id, layer)| (*id, true, *layer, *id)).collect::>(); + + // Determine first whether to add or remove a neuron + if thread_rng().gen_range(0..2) == 0 { + // To add a neuron we need to create a new fann object with the new layer sizes, then copy the information and connections over + let max_id = mutated_neurons.iter().max_by_key(|(id, _, _, _)| id).unwrap().0; + + // Now we inject the new neuron into mutated_neurons + let layer = thread_rng().gen_range(1..fann.get_num_layers() - 1) as usize; + let new_id = max_id + 1; + mutated_neurons.push((new_id, true, layer, new_id)); + mutated_shape[layer] += 1; + } + else { + // Remove a neuron + let layer = thread_rng().gen_range(1..fann.get_num_layers() - 1) as usize; + // Do not remove from layer if it would result in less than NEURALNETWORK_HIDDEN_LAYER_SIZE_MIN neurons + if mutated_shape[layer] > NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN as u32 { + let remove_id = mutated_neurons.iter().filter(|(_, _, l, _)| l == &layer).choose(&mut thread_rng()).unwrap().0; + mutated_neurons.retain(|(id, _, _, _)| id != &remove_id); + mutated_shape[layer] -= 1; + } + } + + let mut mutated_fann = Fann::new(mutated_shape.as_slice()) + .with_context(|| "Failed to create new fann")?; + mutated_fann.randomize_weights(weight_initialization_range.start, weight_initialization_range.end); + mutated_fann.set_activation_func_hidden(ActivationFunc::SigmoidSymmetric); + mutated_fann.set_activation_func_output(ActivationFunc::SigmoidSymmetric); + + // We need to regenerate the new_id's in mutated_neurons (the 4th item in the tuple) we can do this by iterating over the mutated_neurons all over again starting from ZERO + mutated_neurons.sort_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); + let mut i = 0; + for (_, _, _, new_id) in mutated_neurons.iter_mut() { + *new_id = i; + i += 1; + } + + // We need to copy the connections from the old fann to the new fann + consolidate_old_connections(&fann, &fann, mutated_shape, mutated_neurons, &mut mutated_fann); + + Ok(mutated_fann) +} + +pub fn generate_neuron_datastructure(shape: &[u32]) -> Vec<(u32, usize)> { + let mut result = Vec::new(); + let mut global_index = 0; // Keep a global index that does not reset + + for (layer_index, &neurons) in shape.iter().enumerate() { + for _ in 0..neurons { + result.push((global_index, layer_index)); + global_index += 1; // Increment global index for each neuron + } + // global_index += 1; // Skip index for bias neuron at the end of each layer + } + + result +} + + +fn to_bias_network_id(id: &u32, shape: &Vec) -> u32 { + // The given id comes from a network without a bias neuron at the end of every layer + // We need to translate this id to the id in the network with bias neurons + let mut translated_id = 0; + for (layer_index, &neurons) in shape.iter().enumerate() { + for _ in 0..neurons { + if &translated_id == id { + return translated_id + layer_index as u32; + } + translated_id += 1; + } + } + + // If the id is not found, return the id + translated_id +} + +fn to_non_bias_network_id(id: u32, shape: &[u32]) -> Option { + let mut bias_count = 0; // Count of bias neurons encountered up to the current ID + let mut total_neurons = 0; // Total count of neurons (excluding bias neurons) processed + + for &neurons in shape { + let layer_end = total_neurons + neurons; // End of the current layer, excluding the bias neuron + if id < layer_end { + // ID is within the current layer (excluding the bias neuron) + return Some(id - bias_count); + } + if id == layer_end { + // ID matches the position where a bias neuron would be + return None; + } + + // Update counts after considering the current layer + total_neurons += neurons + 1; // Move to the next layer, accounting for the bias neuron + bias_count += 1; // Increment bias count as we've moved past where a bias neuron would be + } + + // If the ID is beyond the range of all neurons (including bias), it's treated as invalid + // Adjust this behavior based on your application's needs + None +} + +fn get_bias_neuron_for_layer(layer: usize, shape: &[u32]) -> Option { + if layer == 0 || layer >= shape.len() { + // No bias neuron for the first and last layers + None + } else { + // Compute the bias neuron for intermediate layers + let mut bias = 0; + for i in 0..layer { + bias += shape[i]; + } + Some(bias + layer as u32 - 1) + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use super::*; + + #[test] + fn major_mutation_test() -> Result<(), Box> { + // Assign + let primary_shape = vec![2, 8, 5, 3, 1]; + // [2, 11, 17, 21] + // [0, 1, 2, 3] + + let mut primary_fann = Fann::new(&primary_shape)?; + + let mut primary_connections = primary_fann.get_connections(); + for connection in primary_connections.iter_mut() { + connection.weight = ((connection.from_neuron * 100) + connection.to_neuron) as f32; + } + primary_fann.set_connections(&primary_connections); + + let weight_initialization_range = -1.0..-0.5; + + for _ in 0..100 { + let result = major_mutation(&primary_fann, weight_initialization_range.clone())?; + + let connections = result.get_connections(); + for connection in connections.iter() { + println!("Connection: {:?}", connection); + } + + let new_shape = result.get_layer_sizes(); + println!("New Shape: {:?}", new_shape); + + // Assert that input and output layers have the same size + assert_eq!(primary_shape[0], new_shape[0]); + assert_eq!(primary_shape[primary_shape.len() - 1], new_shape[new_shape.len() - 1]); + + // Determine if a neuron was removed or added + if new_shape.iter().sum::() == primary_shape.iter().sum::() + 1 { + //Neuron was added + // Find id of neuron that was added + let mut added_neuron_id = 0; + let matching_layers = new_shape.iter().zip(primary_shape.iter()); + for (i, (new_layer, primary_layer)) in matching_layers.enumerate() { + if new_layer > primary_layer { + added_neuron_id += primary_layer + i as u32; + break; + } + added_neuron_id += primary_layer; + } + + for connection in connections.iter() { + if connection.from_neuron == added_neuron_id || connection.to_neuron == added_neuron_id { + assert!(connection.weight < 0.0, "Connection: {:?}, Added Neuron: {}", connection, added_neuron_id); + } else { + assert!(connection.weight > 0.0, "Connection: {:?}, Added Neuron: {}", connection, added_neuron_id); + } + } + } + else if new_shape.iter().sum::() == primary_shape.iter().sum::() - 1 { + //Neuron was removed + for connection in connections.iter() { + assert!(connection.weight > 0.0, "Connection: {:?}", connection); + } + + for (i, layer) in new_shape.iter().enumerate() { + // if layer isn't input or output + if i != 0 && i as u32 != new_shape.len() as u32 - 1 { + assert!(*layer >= NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN as u32, "Layer: {}", layer); + } + } + } + else { + //Neuron was neither added nor removed + for connection in connections.iter() { + assert!(connection.weight > 0.0, "Connection: {:?}", connection); + } + } + } + + Ok(()) + } + + #[test] + fn generate_segments_test() { + // Assign + let primary_shape = vec![4, 8, 6, 4]; + let secondary_shape = vec![4, 3, 3, 3, 3, 3, 4]; + let crossbreed_segments = 5; + + // Act + let result = generate_segments(primary_shape.clone(), secondary_shape.clone(), crossbreed_segments); + + println!("{:?}", result); + + // Assert + assert!(result.len() <= crossbreed_segments, "Segments: {:?}", result); + //Assert that segments are within the bounds of the layers + for (start, end) in result.iter() { + // Bounds are the end of the first layer to the end of the second to last layer + let bounds = 3..17; + + assert!(bounds.contains(end)); + assert!(start < &bounds.end); + } + + //Assert that segments start and end are in ascending order + for (start, end) in result.iter() + { + assert!(*start <= *end, "Start: {}, End: {}", start, end); + } + + // Test that segments are contiguous + for i in 0..result.len() - 1 { + assert_eq!(result[i].1 + 1, result[i + 1].0); + } + + // Testing with more segments than possible + let crossbreed_segments = 15; + + // Act + let result = generate_segments(primary_shape.clone(), secondary_shape.clone(), crossbreed_segments); + + println!("{:?}", result); + + //Assert that segments are within the bounds of the layers + for (start, end) in result.iter() { + // Bounds are the end of the first layer to the end of the second to last layer + let bounds = 3..17; + + assert!(bounds.contains(end)); + assert!(start < &bounds.end); + } + + //Assert that segments start and end are in ascending order + for (start, end) in result.iter() + { + assert!(*start <= *end, "Start: {}, End: {}", start, end); + } + + // Test that segments are contiguous + for i in 0..result.len() - 1 { + assert_eq!(result[i].1 + 1, result[i + 1].0); + } + } + + #[test] + fn get_bias_neuron_for_layer_test() { + // Assign + let shape = vec![4, 8, 6, 4]; + + // Act + let result = get_bias_neuron_for_layer(0, &shape); + + // Assert + assert_eq!(result, None); + + // Act + let result = get_bias_neuron_for_layer(1, &shape); + + // Assert + assert_eq!(result, Some(4)); + + // Act + let result = get_bias_neuron_for_layer(2, &shape); + + // Assert + assert_eq!(result, Some(13)); + + // Act + let result = get_bias_neuron_for_layer(3, &shape); + + // Assert + assert_eq!(result, Some(20)); + + // Act + let result = get_bias_neuron_for_layer(4, &shape); + + // Assert + assert_eq!(result, None); + } + + #[test] + fn crossbreed_neuron_arrays_test() { + // Assign + let segments = vec![(0, 3), (4, 6), (7, 8), (9, 10)]; + + let primary_network = generate_neuron_datastructure(&vec![4, 8, 6, 4]); + + let secondary_network = generate_neuron_datastructure(&vec![4, 3, 3, 3, 3, 3, 4]); + + // Act + let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 8 + (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, true, 1, 7), (8, true, 1, 8), (9, true, 1, 9), (10, true, 1, 10), (11, true, 1, 11), + // Hidden Layer 2: Expect 9 + (7, false, 2, 12), (8, false, 2, 13), (9, false, 2, 14), (12, true, 2, 15), (13, true, 2, 16), (14, true, 2, 17), (15, true, 2, 18), (16, true, 2, 19), (17, true, 2, 20), + // Output Layer: Expect 4 + (18, true, 3, 21), (19, true, 3, 22), (20, true, 3, 23), (21, true, 3, 24), + ].into_iter().collect(); + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + + // Now we test the ooposite case + // Act + let result = crossbreed_neuron_arrays(segments.clone(), secondary_network.clone(), primary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 7 + (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, false, 1, 7), (8, false, 1, 8), (9, false, 1, 9), (10, false, 1, 10), + // Hidden Layer 2: Expect 3 + (7, true, 2, 11), (8, true, 2, 12), (9, true, 2, 13), + // Hidden Layer 3: Expect 3 + (10, true, 3, 14), (11, true, 3, 15), (12, true, 3, 16), + // Hidden Layer 4: Expect 3 + (13, true, 4, 17), (14, true, 4, 18), (15, true, 4, 19), + // Hidden Layer 5: Expect 3 + (16, true, 5, 20), (17, true, 5, 21), (18, true, 5, 22), + // Output Layer: Expect 4 + (19, true, 6, 23), (20, true, 6, 24), (21, true, 6, 25), (22, true, 6, 26), + ].into_iter().collect(); + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + + // Testing with a different segment + // Assign + let segments = vec![(0, 4), (5, 14), (15, 15), (16, 16)]; + + // Act + let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 3 + (4, true, 1, 4), (5, false, 1, 5), (6, false, 1, 6), + // Hidden Layer 2: Expect 6 + (7, false, 2, 7), (8, false, 2, 8), (9, false, 2, 9), (15, true, 2, 10), (16, true, 2, 11), (17, true, 2, 12), + // Hidden Layer 3: Expect 3 + (10, false, 3, 13), (11, false, 3, 14), (12, false, 3, 15), + // Hidden Layer 4: Expect 3 + (13, false, 4, 16), (14, false, 4, 17), (15, false, 4, 18), + // Output Layer: Expect 4 + (18, true, 5, 19), (19, true, 5, 20), (20, true, 5, 21), (21, true, 5, 22), + ].into_iter().collect(); + + // print result before comparison + for r in result.iter() { + println!("{:?}", r); + } + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + + // Swapping order + let result = crossbreed_neuron_arrays(segments.clone(), secondary_network.clone(), primary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 8 + (4, true, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, false, 1, 7), (8, false, 1, 8), (9, false, 1, 9), (10, false, 1, 10), (11, false, 1, 11), + // Hidden Layer 2: Expect 5 + (12, false, 2, 12), (13, false, 2, 13), (14, false, 2, 14), (15, false, 2, 15), (16, false, 2, 16), + // Hidden Layer 3: Expect 3 + (13, true, 3, 17), (14, true, 3, 18), (15, true, 3, 19), + // Hidden Layer 4: Expect 3 + (16, true, 4, 20), (17, true, 4, 21), (18, true, 4, 22), + // Output Layer: Expect 4 + (19, true, 5, 23), (20, true, 5, 24), (21, true, 5, 25), (22, true, 5, 26), + ].into_iter().collect(); + + // print result before comparison + for r in result.iter() { + println!("{:?}", r); + } + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + + // Testing with a different segment + // Assign + let segments = vec![(0, 7), (8, 9), (10, 10), (11, 12)]; + + // Act + let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 7 + (4, true, 1, 4), (5, true, 1, 5), (6, true, 1, 6), (7, true, 1, 7), (8, true, 1, 8), (9, true, 1, 9), (10, true, 1, 10), + // Hidden Layer 2: Expect 8 + (7, false, 2, 11), (8, false, 2, 12), (9, false, 2, 13), (13, true, 2, 14), (14, true, 2, 15), (15, true, 2, 16), (16, true, 2, 17), (17, true, 2, 18), + // Hidden Layer 3: Expect 3 + (10, false, 3, 19), (11, false, 3, 20), (12, false, 3, 21), + // Output Layer: Expect 4 + (18, true, 4, 22), (19, true, 4, 23), (20, true, 4, 24), (21, true, 4, 25), + ].into_iter().collect(); + + // print result before comparison + for r in result.iter() { + println!("{:?}", r); + } + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + + // Swapping order + let result = crossbreed_neuron_arrays(segments.clone(), secondary_network.clone(), primary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 7 + (4, true, 1, 4), (5, true, 1, 5), (6, true, 1, 6), (8, false, 1, 7), (9, false, 1, 8), (10, false, 1, 9), (11, false, 1, 10), + // Hidden Layer 2: Expect 4 + (7, true, 2, 11), (8, true, 2, 12), (9, true, 2, 13), (12, false, 2, 14), + // Hidden Layer 3: Expect 3 + (10, true, 3, 15), (11, true, 3, 16), (12, true, 3, 17), + // Hidden Layer 4: Expect 3 + (13, true, 4, 18), (14, true, 4, 19), (15, true, 4, 20), + // Hidden Layer 5: Expect 3 + (16, true, 5, 21), (17, true, 5, 22), (18, true, 5, 23), + // Output Layer: Expect 4 + (19, true, 6, 24), (20, true, 6, 25), (21, true, 6, 26), (22, true, 6, 27), + ].into_iter().collect(); + + // print result before comparison + for r in result.iter() { + println!("{:?}", r); + } + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + + // Testing networks with the same size + // Assign + let segments = vec![(0, 3), (4, 6), (7, 8), (9, 11)]; + + let primary_network = generate_neuron_datastructure(&vec![4, 3, 4, 5, 4]); + + vec![ + // Input layer + (0, 0), (1, 0), (2, 0), (3, 0), + // Hidden layer 1: 3 neurons + (4, 1), (5, 1), (6, 1), + // Hidden Layer 2: 4 neurons + (7, 2), (8, 2), (9, 2), (10, 2), + // Hidden Layer 3: 5 neurons + (11, 3), (12, 3), (13, 3), (14, 3), (15, 3), + // Output layer + (16, 4), (17, 4), (18, 4), (19, 4), + ]; + + let secondary_network = primary_network.clone(); + + // Act + let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 3 + (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), + // Hidden Layer 2: Expect 4 + (7, true, 2, 7), (8, true, 2, 8), (9, false, 2, 9), (10, false, 2, 10), + // Hidden Layer 3: Expect 5 + (11, false, 3, 11), (12, true, 3, 12), (13, true, 3, 13), (14, true, 3, 14), (15, true, 3, 15), + // Output Layer: Expect 4 + (16, true, 4, 16), (17, true, 4, 17), (18, true, 4, 18), (19, true, 4, 19), + ].into_iter().collect(); + + // print result before comparison + for r in result.iter() { + println!("{:?}", r); + } + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + + // Testing with different segment + let segments = vec![(0, 5), (6, 6), (7, 11), (12, 13)]; + + // Act + let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + + // Expected Result Set + let expected: HashSet<(u32, bool, usize, u32)> = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 3 + (4, true, 1, 4), (5, true, 1, 5), (6, false, 1, 6), + // Hidden Layer 2: Expect 4 + (7, true, 2, 7), (8, true, 2, 8), (9, true, 2, 9), (10, true, 2, 10), + // Hidden Layer 3: Expect 5 + (11, true, 3, 11), (12, false, 3, 12), (13, false, 3, 13), (14, true, 3, 14), (15, true, 3, 15), + // Output Layer: Expect 4 + (16, true, 4, 16), (17, true, 4, 17), (18, true, 4, 18), (19, true, 4, 19), + ].into_iter().collect(); + + // print result before comparison + for r in result.iter() { + println!("{:?}", r); + } + + // Convert Result to HashSet for Comparison + let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); + + // Assert + assert_eq!(result_set, expected); + } + + #[test] + fn generate_neuron_datastructure_test() { + // Assign + let shape = vec![4, 3, 5, 4]; + + // Act + let result = generate_neuron_datastructure(shape.as_slice()); + + // Expected Result + let expected: Vec<(u32, usize)> = vec![ + (0, 0), (1, 0), (2, 0), (3, 0), + (4, 1), (5, 1), (6, 1), + (7, 2), (8, 2), (9, 2), (10, 2), (11, 2), + (12, 3), (13, 3), (14, 3), (15, 3), + ]; + + // Assert + assert_eq!(result, expected); + } + + #[test] + fn translate_neuron_id_test() { + // Assign + let shape = vec![4, 3, 5, 4]; + + let expected = vec![ + // (input, expected output) + (0, 0), (1, 1), (2, 2), (3, 3), + (4, 5), (5, 6), (6, 7), + (7, 9), (8, 10), (9, 11), (10, 12), (11, 13), + (12, 15), (13, 16), (14, 17), (15, 18), + ]; + + // Act + for (input, expected_output) in expected { + let result = to_bias_network_id(&input, &shape); + // Assert + assert_eq!(result, expected_output); + + // Go the other direction too + let result = to_non_bias_network_id(expected_output, &shape); + + // Assert + if let Some(result) = result { + assert_eq!(result, input); + } + else { + assert!(false, "Expected Some, got None"); + } + } + + // Validate bias neuron values + let bias_neurons = vec![4, 8, 14, 19]; + + for &bias_neuron in bias_neurons.iter() { + let result = to_non_bias_network_id(bias_neuron, &shape); + + // Assert + assert!(result.is_none()); + } + } + + #[test] + fn consolidate_old_connections_test() -> Result<(), Box>{ + // Assign + let primary_shape = vec![4, 8, 6, 4]; + let secondary_shape = vec![4, 3, 3, 3, 3, 3, 4]; + + let mut primary_fann = Fann::new(&primary_shape)?; + let mut secondary_fann = Fann::new(&secondary_shape)?; + + let mut primary_connections = primary_fann.get_connections(); + for connection in primary_connections.iter_mut() { + connection.weight = ((connection.from_neuron * 100) + connection.to_neuron) as f32; + } + primary_fann.set_connections(&primary_connections); + + let mut secondary_connections = secondary_fann.get_connections(); + for connection in secondary_connections.iter_mut() { + connection.weight = ((connection.from_neuron * 100) + connection.to_neuron) as f32; + connection.weight = connection.weight * -1.0; + } + secondary_fann.set_connections(&secondary_connections); + + let new_neurons = vec![ + // Input layer: Expect 4 + (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + // Hidden Layer 1: Expect 8 + (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, true, 1, 7), (8, true, 1, 8), (9, true, 1, 9), (10, true, 1, 10), (11, true, 1, 11), + // Hidden Layer 2: Expect 9 + (7, false, 2, 12), (8, false, 2, 13), (9, false, 2, 14), (12, true, 2, 15), (13, true, 2, 16), (14, true, 2, 17), (15, true, 2, 18), (16, true, 2, 19), (17, true, 2, 20), + // Output Layer: Expect 4 + (18, true, 3, 21), (19, true, 3, 22), (20, true, 3, 23), (21, true, 3, 24), + ]; + let new_shape = vec![4, 8, 9, 4]; + let mut new_fann = Fann::new(&[4, 8, 9, 4])?; + // Initialize weights to 0 + let mut new_connections = new_fann.get_connections(); + for connection in new_connections.iter_mut() { + connection.weight = 0.0; + } + new_fann.set_connections(&new_connections); + + // Act + consolidate_old_connections(&primary_fann, &secondary_fann, new_shape, new_neurons, &mut new_fann); + + // Bias neurons + // Layer 1: 4 + // Layer 2: 13 + // Layer 3: 23 + let expected_connections = vec![ + // (from_neuron, to_neuron, weight) + // Hidden Layer 1 (5-12) + (0, 5, -5.0), (1, 5, -105.0), (2, 5, -205.0), (3, 5, -305.0), + (0, 6, -6.0), (1, 6, -106.0), (2, 6, -206.0), (3, 6, -306.0), + (0, 7, -7.0), (1, 7, -107.0), (2, 7, -207.0), (3, 7, -307.0), + (0, 8, 8.0), (1, 8, 108.0), (2, 8, 208.0), (3, 8, 308.0), + (0, 9, 9.0), (1, 9, 109.0), (2, 9, 209.0), (3, 9, 309.0), + (0, 10, 10.0), (1, 10, 110.0), (2, 10, 210.0), (3, 10, 310.0), + (0, 11, 11.0), (1, 11, 111.0), (2, 11, 211.0), (3, 11, 311.0), + (0, 12, 12.0), (1, 12, 112.0), (2, 12, 212.0), (3, 12, 312.0), + // Hidden Layer 2 (14-22) + (5, 14, -509.0), (6, 14, -609.0), (7, 14, -709.0), (8, 14, 0.0), (9, 14, 0.0), (10, 14, 0.0), (11, 14, 0.0), (12, 14, 0.0), + (5, 15, -510.0), (6, 15, -610.0), (7, 15, -710.0), (8, 15, 0.0), (9, 15, 0.0), (10, 15, 0.0), (11, 15, 0.0), (12, 15, 0.0), + (5, 16, -511.0), (6, 16, -611.0), (7, 16, -711.0), (8, 16, 0.0), (9, 16, 0.0), (10, 16, 0.0), (11, 16, 0.0), (12, 16, 0.0), + (5, 17, 514.0), (6, 17, 614.0), (7, 17, 714.0), (8, 17, 814.0), (9, 17, 914.0), (10, 17, 1014.0), (11, 17, 1114.0), (12, 17, 1214.0), + (5, 18, 515.0), (6, 18, 615.0), (7, 18, 715.0), (8, 18, 815.0), (9, 18, 915.0), (10, 18, 1015.0), (11, 18, 1115.0), (12, 18, 1215.0), + (5, 19, 516.0), (6, 19, 616.0), (7, 19, 716.0), (8, 19, 816.0), (9, 19, 916.0), (10, 19, 1016.0), (11, 19, 1116.0), (12, 19, 1216.0), + (5, 20, 517.0), (6, 20, 617.0), (7, 20, 717.0), (8, 20, 817.0), (9, 20, 917.0), (10, 20, 1017.0), (11, 20, 1117.0), (12, 20, 1217.0), + (5, 21, 518.0), (6, 21, 618.0), (7, 21, 718.0), (8, 21, 818.0), (9, 21, 918.0), (10, 21, 1018.0), (11, 21, 1118.0), (12, 21, 1218.0), + (5, 22, 519.0), (6, 22, 619.0), (7, 22, 719.0), (8, 22, 819.0), (9, 22, 919.0), (10, 22, 1019.0), (11, 22, 1119.0), (12, 22, 1219.0), + // Output layer (24-27) + (14, 24, 0.0), (15, 24, 0.0), (16, 24, 0.0), (17, 24, 1421.0), (18, 24, 1521.0), (19, 24, 1621.0), (20, 24, 1721.0), (21, 24, 1821.0), (22, 24, 1921.0), + (14, 25, 0.0), (15, 25, 0.0), (16, 25, 0.0), (17, 25, 1422.0), (18, 25, 1522.0), (19, 25, 1622.0), (20, 25, 1722.0), (21, 25, 1822.0), (22, 25, 1922.0), + (14, 26, 0.0), (15, 26, 0.0), (16, 26, 0.0), (17, 26, 1423.0), (18, 26, 1523.0), (19, 26, 1623.0), (20, 26, 1723.0), (21, 26, 1823.0), (22, 26, 1923.0), + (14, 27, 0.0), (15, 27, 0.0), (16, 27, 0.0), (17, 27, 1424.0), (18, 27, 1524.0), (19, 27, 1624.0), (20, 27, 1724.0), (21, 27, 1824.0), (22, 27, 1924.0), + ]; + + for connection in new_fann.get_connections().iter() { + println!("{:?}", connection); + } + + // Assert + // Compare each connection to the expected connection + let new_connections = new_fann.get_connections(); + for connection in expected_connections.iter() { + let matching_connection = new_connections.iter().find(|&c| c.from_neuron == connection.0 && c.to_neuron == connection.1); + if let Some(matching_connection) = matching_connection { + assert_eq!(matching_connection.weight, connection.2, "Connection: {:?}", matching_connection); + } else { + assert!(false, "Connection not found: {:?}", connection); + } + } + + let expected_bias_neuron_connections = vec![ + // (from_neuron, to_neuron, weight) + // Bias Neurons + // Layer 2: bias neuron_id 4 + (4, 5, -405.0), (4, 6, -406.0), (4, 7, -407.0), (4, 8, 408.0), (4, 9, 409.0), (4, 10, 410.0), (4, 11, 411.0), (4, 12, 412.0), + // Layer 3: bias neuron_id 13 + (13, 14, -809.0), (13, 15, -810.0), (13, 16, -811.0), (13, 17, 1314.0), (13, 18, 1315.0), (13, 19, 1316.0), (13, 20, 1317.0), (13, 21, 1318.0), (13, 22, 1319.0), + // Layer 4: bias neuron_id 23 + (23, 24, 2021.0), (23, 25, 2022.0), (23, 26, 2023.0), (23, 27, 2024.0), + ]; + + for connection in expected_bias_neuron_connections.iter() { + let matching_connection = new_connections.iter().find(|&c| c.from_neuron == connection.0 && c.to_neuron == connection.1); + if let Some(matching_connection) = matching_connection { + assert_eq!(matching_connection.weight, connection.2, "Connection: {:?}", matching_connection); + } else { + assert!(false, "Connection not found: {:?}", connection); + } + } + + Ok(()) + } +} \ No newline at end of file From 95699bd47eabd0f7d1b7d88fa1fa8b4cb498b03a Mon Sep 17 00:00:00 2001 From: vandomej Date: Thu, 4 Apr 2024 18:40:17 -0700 Subject: [PATCH 16/26] Adding user defined, shared context --- gemla/Cargo.toml | 1 + gemla/src/bin/bin.rs | 1 - gemla/src/bin/fighter_nn/fighter_context.rs | 48 ++++++ gemla/src/bin/fighter_nn/mod.rs | 156 +++++++++++--------- gemla/src/bin/test_state/mod.rs | 42 +++--- gemla/src/core/genetic_node.rs | 51 ++++--- gemla/src/core/mod.rs | 61 ++++---- 7 files changed, 212 insertions(+), 148 deletions(-) create mode 100644 gemla/src/bin/fighter_nn/fighter_context.rs diff --git a/gemla/Cargo.toml b/gemla/Cargo.toml index ee4951d..161d544 100644 --- a/gemla/Cargo.toml +++ b/gemla/Cargo.toml @@ -31,3 +31,4 @@ num_cpus = "1.16.0" easy-parallel = "3.3.1" fann = "0.1.8" async-trait = "0.1.78" +async-recursion = "1.1.0" diff --git a/gemla/src/bin/bin.rs b/gemla/src/bin/bin.rs index 16539fb..464f1aa 100644 --- a/gemla/src/bin/bin.rs +++ b/gemla/src/bin/bin.rs @@ -47,7 +47,6 @@ fn main() -> Result<()> { GemlaConfig { generations_per_height: 5, overwrite: false, - shared_semaphore_concurrency_limit: 50, }, DataFormat::Json, ))?; diff --git a/gemla/src/bin/fighter_nn/fighter_context.rs b/gemla/src/bin/fighter_nn/fighter_context.rs new file mode 100644 index 0000000..503bbf8 --- /dev/null +++ b/gemla/src/bin/fighter_nn/fighter_context.rs @@ -0,0 +1,48 @@ +use std::sync::Arc; + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use tokio::sync::Semaphore; + +const SHARED_SEMAPHORE_CONCURRENCY_LIMIT: usize = 20; + + +#[derive(Debug, Clone)] +pub struct FighterContext { + pub shared_semaphore: Arc, +} + +impl Default for FighterContext { + fn default() -> Self { + FighterContext { + shared_semaphore: Arc::new(Semaphore::new(SHARED_SEMAPHORE_CONCURRENCY_LIMIT)), + } + } +} + + +// Custom serialization to just output the concurrency limit. +impl Serialize for FighterContext { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // Assuming the semaphore's available permits represent the concurrency limit. + // This part is tricky since Semaphore does not expose its initial permits. + // You might need to store the concurrency limit as a separate field if this assumption doesn't hold. + let concurrency_limit = SHARED_SEMAPHORE_CONCURRENCY_LIMIT; + serializer.serialize_u64(concurrency_limit as u64) + } +} + +// Custom deserialization to reconstruct the FighterContext from a concurrency limit. +impl<'de> Deserialize<'de> for FighterContext { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let concurrency_limit = u64::deserialize(deserializer)?; + Ok(FighterContext { + shared_semaphore: Arc::new(Semaphore::new(concurrency_limit as usize)), + }) + } +} \ No newline at end of file diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index 4161079..b7b2c43 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -1,6 +1,7 @@ extern crate fann; pub mod neural_network_utility; +pub mod fighter_context; use std::{fs::{self, File}, io::{self, BufRead, BufReader}, ops::Range, path::{Path, PathBuf}, sync::Arc}; use fann::{ActivationFunc, Fann}; @@ -63,8 +64,10 @@ pub struct FighterNN { #[async_trait] impl GeneticNode for FighterNN { + type Context = fighter_context::FighterContext; + // Check for the highest number of the folder name and increment it by 1 - fn initialize(context: GeneticNodeContext) -> Result, Error> { + async fn initialize(context: GeneticNodeContext) -> Result, Error> { let base_path = PathBuf::from(BASE_DIR); let folder = base_path.join(format!("fighter_nn_{:06}", context.id)); @@ -127,100 +130,37 @@ impl GeneticNode for FighterNN { })) } - async fn simulate(&mut self, context: GeneticNodeContext) -> Result<(), Error> { + async fn simulate(&mut self, context: GeneticNodeContext) -> Result<(), Error> { debug!("Context: {:?}", context); let mut tasks = Vec::new(); // For each nn in the current generation: for i in 0..self.population_size { let self_clone = self.clone(); - let semaphore_clone = Arc::clone(context.semaphore.as_ref().unwrap()); + let semaphore_clone = context.gemla_context.shared_semaphore.clone(); let task = async move { - let nn = self_clone.folder.join(format!("{}", self_clone.generation)).join(format!("{:06}_fighter_nn_{}.net", self_clone.id, i)); + let nn = self_clone.folder.join(format!("{}", self_clone.generation)).join(self_clone.get_individual_id(i as u64)); let mut simulations = Vec::new(); // Using the same original nn, repeat the simulation with 5 random nn's from the current generation concurrently for _ in 0..SIMULATION_ROUNDS { let random_nn_index = thread_rng().gen_range(0..self_clone.population_size); - let id = self_clone.id.clone(); let folder = self_clone.folder.clone(); let generation = self_clone.generation; let semaphore_clone = Arc::clone(&semaphore_clone); - let random_nn = folder.join(format!("{}", generation)).join(format!("{:06}_fighter_nn_{}.net", id, random_nn_index)); + let random_nn = folder.join(format!("{}", generation)).join(self_clone.get_individual_id(random_nn_index as u64)); let nn_clone = nn.clone(); // Clone the path to use in the async block - let config1_arg = format!("-NN1Config=\"{}\"", nn_clone.to_str().unwrap()); - let config2_arg = format!("-NN2Config=\"{}\"", random_nn.to_str().unwrap()); - let disable_unreal_rendering_arg = "-nullrhi".to_string(); - - - let future = async move { let permit = semaphore_clone.acquire_owned().await.with_context(|| "Failed to acquire semaphore permit")?; - // Construct the score file path - let nn_id = format!("{:06}_fighter_nn_{}", id, i); - let random_nn_id = format!("{:06}_fighter_nn_{}", id, random_nn_index); - let score_file_name = format!("{}_vs_{}.txt", nn_id, random_nn_id); - let score_file = folder.join(format!("{}", generation)).join(&score_file_name); - - // Check if score file already exists before running the simulation - if score_file.exists() { - let round_score = read_score_from_file(&score_file, &nn_id).await - .with_context(|| format!("Failed to read score from file: {:?}", score_file_name))?; - - debug!("{} scored {}", nn_id, round_score); - - return Ok::(round_score); - } - - // Check if the opposite round score has been determined - let opposite_score_file = folder.join(format!("{}", generation)).join(format!("{}_vs_{}.txt", random_nn_id, nn_id)); - if opposite_score_file.exists() { - let round_score = read_score_from_file(&opposite_score_file, &nn_id).await - .with_context(|| format!("Failed to read score from file: {:?}", opposite_score_file))?; - - debug!("{} scored {}", nn_id, round_score); - - return Ok::(1.0 - round_score); - } - - // Run simulation until score file is generated - while !score_file.exists() { - let _output = if thread_rng().gen_range(0..100) < 1 { - Command::new(GAME_EXECUTABLE_PATH) - .arg(&config1_arg) - .arg(&config2_arg) - .output() - .await - .expect("Failed to execute game") - } else { - Command::new(GAME_EXECUTABLE_PATH) - .arg(&config1_arg) - .arg(&config2_arg) - .arg(&disable_unreal_rendering_arg) - .output() - .await - .expect("Failed to execute game") - }; - } + let score = run_1v1_simulation(&nn_clone, &random_nn).await?; drop(permit); - // Read the score from the file - if score_file.exists() { - let round_score = read_score_from_file(&score_file, &nn_id).await - .with_context(|| format!("Failed to read score from file: {:?}", score_file_name))?; - - debug!("{} scored {}", nn_id, round_score); - - Ok(round_score) - } else { - warn!("Score file not found: {:?}", score_file_name); - Ok(0.0) - } + Ok(score) }; simulations.push(future); @@ -259,7 +199,7 @@ impl GeneticNode for FighterNN { } - fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { + async fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { let survivor_count = (self.population_size as f32 * SURVIVAL_RATE) as usize; // Create the new generation folder @@ -322,7 +262,7 @@ impl GeneticNode for FighterNN { Ok(()) } - fn merge(left: &FighterNN, right: &FighterNN, id: &Uuid) -> Result, Error> { + async fn merge(left: &FighterNN, right: &FighterNN, id: &Uuid, _: Self::Context) -> Result, Error> { let base_path = PathBuf::from(BASE_DIR); let folder = base_path.join(format!("fighter_nn_{:06}", id)); @@ -365,6 +305,78 @@ impl GeneticNode for FighterNN { } } +impl FighterNN { + pub fn get_individual_id(&self, nn_id: u64) -> String { + format!("{:06}_fighter_nn_{}", self.id, nn_id) + } +} + +async fn run_1v1_simulation(nn_path_1: &PathBuf, nn_path_2: &PathBuf) -> Result { + // Construct the score file path + let base_folder = nn_path_1.parent().unwrap(); + let nn_1_id = nn_path_1.file_stem().unwrap().to_str().unwrap(); + let nn_2_id = nn_path_2.file_stem().unwrap().to_str().unwrap(); + let score_file = base_folder.join(format!("{}_vs_{}.txt", nn_1_id, nn_2_id)); + + // Check if score file already exists before running the simulation + if score_file.exists() { + let round_score = read_score_from_file(&score_file, &nn_1_id).await + .with_context(|| format!("Failed to read score from file: {:?}", score_file))?; + + trace!("{} scored {}", nn_1_id, round_score); + + return Ok::(round_score); + } + + // Check if the opposite round score has been determined + let opposite_score_file = base_folder.join(format!("{}_vs_{}.txt", nn_2_id, nn_1_id)); + if opposite_score_file.exists() { + let round_score = read_score_from_file(&opposite_score_file, &nn_1_id).await + .with_context(|| format!("Failed to read score from file: {:?}", opposite_score_file))?; + + trace!("{} scored {}", nn_1_id, round_score); + + return Ok::(1.0 - round_score); + } + + // Run simulation until score file is generated + let config1_arg = format!("-NN1Config=\"{}\"", nn_path_1.to_str().unwrap()); + let config2_arg = format!("-NN2Config=\"{}\"", nn_path_2.to_str().unwrap()); + let disable_unreal_rendering_arg = "-nullrhi".to_string(); + + while !score_file.exists() { + let _output = if thread_rng().gen_range(0..100) < 1 { + Command::new(GAME_EXECUTABLE_PATH) + .arg(&config1_arg) + .arg(&config2_arg) + .output() + .await + .expect("Failed to execute game") + } else { + Command::new(GAME_EXECUTABLE_PATH) + .arg(&config1_arg) + .arg(&config2_arg) + .arg(&disable_unreal_rendering_arg) + .output() + .await + .expect("Failed to execute game") + }; + } + + // Read the score from the file + if score_file.exists() { + let round_score = read_score_from_file(&score_file, &nn_1_id).await + .with_context(|| format!("Failed to read score from file: {:?}", score_file))?; + + trace!("{} scored {}", nn_1_id, round_score); + + Ok(round_score) + } else { + warn!("Score file not found: {:?}", score_file); + Ok(0.0) + } +} + async fn read_score_from_file(file_path: &Path, nn_id: &str) -> Result { let mut attempts = 0; diff --git a/gemla/src/bin/test_state/mod.rs b/gemla/src/bin/test_state/mod.rs index 73df36e..c11c668 100644 --- a/gemla/src/bin/test_state/mod.rs +++ b/gemla/src/bin/test_state/mod.rs @@ -14,7 +14,9 @@ pub struct TestState { #[async_trait] impl GeneticNode for TestState { - fn initialize(_context: GeneticNodeContext) -> Result, Error> { + type Context = (); + + async fn initialize(_context: GeneticNodeContext) -> Result, Error> { let mut population: Vec = vec![]; for _ in 0..POPULATION_SIZE { @@ -24,7 +26,7 @@ impl GeneticNode for TestState { Ok(Box::new(TestState { population })) } - async fn simulate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { + async fn simulate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { let mut rng = thread_rng(); self.population = self @@ -36,7 +38,7 @@ impl GeneticNode for TestState { Ok(()) } - fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { + async fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { let mut rng = thread_rng(); let mut v = self.population.clone(); @@ -74,7 +76,7 @@ impl GeneticNode for TestState { Ok(()) } - fn merge(left: &TestState, right: &TestState, id: &Uuid) -> Result, Error> { + async fn merge(left: &TestState, right: &TestState, id: &Uuid, gemla_context: Self::Context) -> Result, Error> { let mut v = left.population.clone(); v.append(&mut right.population.clone()); @@ -89,8 +91,8 @@ impl GeneticNode for TestState { id: id.clone(), generation: 0, max_generations: 0, - semaphore: None, - })?; + gemla_context: gemla_context + }).await?; Ok(Box::new(result)) } @@ -101,16 +103,16 @@ mod tests { use super::*; use gemla::core::genetic_node::GeneticNode; - #[test] - fn test_initialize() { + #[tokio::test] + async fn test_initialize() { let state = TestState::initialize( GeneticNodeContext { id: Uuid::new_v4(), generation: 0, max_generations: 0, - semaphore: None, + gemla_context: (), } - ).unwrap(); + ).await.unwrap(); assert_eq!(state.population.len(), POPULATION_SIZE as usize); } @@ -128,7 +130,7 @@ mod tests { id: Uuid::new_v4(), generation: 0, max_generations: 0, - semaphore: None, + gemla_context: (), } ).await.unwrap(); assert!(original_population @@ -141,7 +143,7 @@ mod tests { id: Uuid::new_v4(), generation: 0, max_generations: 0, - semaphore: None, + gemla_context: (), } ).await.unwrap(); state.simulate( @@ -149,7 +151,7 @@ mod tests { id: Uuid::new_v4(), generation: 0, max_generations: 0, - semaphore: None, + gemla_context: (), } ).await.unwrap(); assert!(original_population @@ -158,8 +160,8 @@ mod tests { .all(|(&a, &b)| b >= a - 3 && b <= a + 6)) } - #[test] - fn test_mutate() { + #[tokio::test] + async fn test_mutate() { let mut state = TestState { population: vec![4, 3, 3], }; @@ -169,15 +171,15 @@ mod tests { id: Uuid::new_v4(), generation: 0, max_generations: 0, - semaphore: None, + gemla_context: (), } - ).unwrap(); + ).await.unwrap(); assert_eq!(state.population.len(), POPULATION_SIZE as usize); } - #[test] - fn test_merge() { + #[tokio::test] + async fn test_merge() { let state1 = TestState { population: vec![1, 2, 4, 5], }; @@ -186,7 +188,7 @@ mod tests { population: vec![0, 1, 3, 7], }; - let merged_state = TestState::merge(&state1, &state2, &Uuid::new_v4()).unwrap(); + let merged_state = TestState::merge(&state1, &state2, &Uuid::new_v4(), ()).await.unwrap(); assert_eq!(merged_state.population.len(), POPULATION_SIZE as usize); assert!(merged_state.population.iter().any(|&x| x == 7)); diff --git a/gemla/src/core/genetic_node.rs b/gemla/src/core/genetic_node.rs index 0838dc8..eec30bc 100644 --- a/gemla/src/core/genetic_node.rs +++ b/gemla/src/core/genetic_node.rs @@ -5,9 +5,8 @@ use crate::error::Error; use anyhow::Context; -use serde::{Deserialize, Serialize}; -use tokio::sync::Semaphore; -use std::{fmt::Debug, sync::Arc}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use std::fmt::Debug; use uuid::Uuid; use async_trait::async_trait; @@ -27,33 +26,35 @@ pub enum GeneticState { } #[derive(Clone, Debug)] -pub struct GeneticNodeContext { +pub struct GeneticNodeContext { pub generation: u64, pub max_generations: u64, pub id: Uuid, - pub semaphore: Option>, + pub gemla_context: S } /// A trait used to interact with the internal state of nodes within the [`Bracket`] /// /// [`Bracket`]: crate::bracket::Bracket #[async_trait] -pub trait GeneticNode: Send { +pub trait GeneticNode : Send { + type Context; + /// Initializes a new instance of a [`GeneticState`]. /// /// # Examples /// TODO - fn initialize(context: GeneticNodeContext) -> Result, Error>; + async fn initialize(context: GeneticNodeContext) -> Result, Error>; - async fn simulate(&mut self, context: GeneticNodeContext) -> Result<(), Error>; + async fn simulate(&mut self, context: GeneticNodeContext) -> Result<(), Error>; /// Mutates members in a population and/or crossbreeds them to produce new offspring. /// /// # Examples /// TODO - fn mutate(&mut self, context: GeneticNodeContext) -> Result<(), Error>; + async fn mutate(&mut self, context: GeneticNodeContext) -> Result<(), Error>; - fn merge(left: &Self, right: &Self, id: &Uuid) -> Result, Error>; + async fn merge(left: &Self, right: &Self, id: &Uuid, context: Self::Context) -> Result, Error>; } /// Used externally to wrap a node implementing the [`GeneticNode`] trait. Processes state transitions for the given node as @@ -82,6 +83,7 @@ impl Default for GeneticNodeWrapper { impl GeneticNodeWrapper where T: GeneticNode + Debug + Send, + T::Context: Send + Sync + Clone + Debug + Serialize + DeserializeOwned + 'static + Default, { pub fn new(max_generations: u64) -> Self { GeneticNodeWrapper:: { @@ -120,17 +122,17 @@ where self.state } - pub async fn process_node(&mut self, semaphore: Arc) -> Result { + pub async fn process_node(&mut self, gemla_context: T::Context) -> Result { let context = GeneticNodeContext { generation: self.generation, max_generations: self.max_generations, id: self.id, - semaphore: Some(semaphore), + gemla_context, }; match (self.state, &mut self.node) { (GeneticState::Initialize, _) => { - self.node = Some(*T::initialize(context.clone())?); + self.node = Some(*T::initialize(context.clone()).await?); self.state = GeneticState::Simulate; } (GeneticState::Simulate, Some(n)) => { @@ -144,7 +146,7 @@ where }; } (GeneticState::Mutate, Some(n)) => { - n.mutate(context.clone()) + n.mutate(context.clone()).await .with_context(|| format!("Error mutating node: {:?}", self))?; self.generation += 1; @@ -172,20 +174,22 @@ mod tests { #[async_trait] impl GeneticNode for TestState { - async fn simulate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { + type Context = (); + + async fn simulate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { self.score += 1.0; Ok(()) } - fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { + async fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { Ok(()) } - fn initialize(_context: GeneticNodeContext) -> Result, Error> { + async fn initialize(_context: GeneticNodeContext) -> Result, Error> { Ok(Box::new(TestState { score: 0.0 })) } - fn merge(_l: &TestState, _r: &TestState, _id: &Uuid) -> Result, Error> { + async fn merge(_l: &TestState, _r: &TestState, _id: &Uuid, _: Self::Context) -> Result, Error> { Err(Error::Other(anyhow!("Unable to merge"))) } } @@ -281,14 +285,13 @@ mod tests { #[tokio::test] async fn test_process_node() -> Result<(), Error> { let mut genetic_node = GeneticNodeWrapper::::new(2); - let semaphore = Arc::new(Semaphore::new(1)); assert_eq!(genetic_node.state(), GeneticState::Initialize); - assert_eq!(genetic_node.process_node(semaphore.clone()).await?, GeneticState::Simulate); - assert_eq!(genetic_node.process_node(semaphore.clone()).await?, GeneticState::Mutate); - assert_eq!(genetic_node.process_node(semaphore.clone()).await?, GeneticState::Simulate); - assert_eq!(genetic_node.process_node(semaphore.clone()).await?, GeneticState::Finish); - assert_eq!(genetic_node.process_node(semaphore.clone()).await?, GeneticState::Finish); + assert_eq!(genetic_node.process_node(()).await?, GeneticState::Simulate); + assert_eq!(genetic_node.process_node(()).await?, GeneticState::Mutate); + assert_eq!(genetic_node.process_node(()).await?, GeneticState::Simulate); + assert_eq!(genetic_node.process_node(()).await?, GeneticState::Finish); + assert_eq!(genetic_node.process_node(()).await?, GeneticState::Finish); Ok(()) } diff --git a/gemla/src/core/mod.rs b/gemla/src/core/mod.rs index b3e7faa..9d831a3 100644 --- a/gemla/src/core/mod.rs +++ b/gemla/src/core/mod.rs @@ -4,15 +4,15 @@ pub mod genetic_node; use crate::{error::Error, tree::Tree}; +use async_recursion::async_recursion; use file_linked::{constants::data_format::DataFormat, FileLinked}; -use futures::future; +use futures::{executor::block_on, future}; use genetic_node::{GeneticNode, GeneticNodeWrapper, GeneticState}; use log::{info, trace, warn}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use tokio::task::JoinHandle; -use tokio::sync::Semaphore; use std::{ - collections::HashMap, fmt::Debug, fs::File, io::ErrorKind, marker::Send, mem, path::Path, sync::Arc, time::Instant + collections::HashMap, fmt::Debug, fs::File, io::ErrorKind, marker::Send, mem, path::Path, time::Instant }; use uuid::Uuid; @@ -58,7 +58,6 @@ type SimulationTree = Box>>; pub struct GemlaConfig { pub generations_per_height: u64, pub overwrite: bool, - pub shared_semaphore_concurrency_limit: usize, } /// Creates a tournament style bracket for simulating and evaluating nodes of type `T` implementing [`GeneticNode`]. @@ -69,16 +68,17 @@ pub struct GemlaConfig { /// [`GeneticNode`]: genetic_node::GeneticNode pub struct Gemla where - T: Serialize + Clone, + T: GeneticNode + Serialize + DeserializeOwned + Debug + Clone + Send, + T::Context: Send + Sync + Clone + Debug + Serialize + DeserializeOwned + 'static + Default, { - pub data: FileLinked<(Option>, GemlaConfig)>, + pub data: FileLinked<(Option>, GemlaConfig, T::Context)>, threads: HashMap, Error>>>, - semaphore: Arc, } impl Gemla where T: GeneticNode + Serialize + DeserializeOwned + Debug + Clone + Send, + T::Context: Send + Sync + Clone + Debug + Serialize + DeserializeOwned + 'static + Default, { pub fn new(path: &Path, config: GemlaConfig, data_format: DataFormat) -> Result { match File::open(path) { @@ -86,18 +86,16 @@ where // based on the configuration provided Ok(_) => Ok(Gemla { data: if config.overwrite { - FileLinked::new((None, config), path, data_format)? + FileLinked::new((None, config, T::Context::default()), path, data_format)? } else { FileLinked::from_file(path, data_format)? }, threads: HashMap::new(), - semaphore: Arc::new(Semaphore::new(config.shared_semaphore_concurrency_limit)), }), // If the file doesn't exist we must create it Err(error) if error.kind() == ErrorKind::NotFound => Ok(Gemla { - data: FileLinked::new((None, config), path, data_format)?, + data: FileLinked::new((None, config, T::Context::default()), path, data_format)?, threads: HashMap::new(), - semaphore: Arc::new(Semaphore::new(config.shared_semaphore_concurrency_limit)), }), Err(error) => Err(Error::IO(error)), } @@ -117,7 +115,7 @@ where { // Before we can process nodes we must create blank nodes in their place to keep track of which nodes have been processed // in the tree and which nodes have not. - self.data.mutate(|(d, c)| { + self.data.mutate(|(d, c, _)| { let mut tree: Option> = Gemla::increase_height(d.take(), c, steps); mem::swap(d, &mut tree); })?; @@ -151,11 +149,11 @@ where { trace!("Adding node to process list {}", node.id()); - let semaphore = self.semaphore.clone(); + let gemla_context = self.data.readonly().2.clone(); self.threads .insert(node.id(), tokio::spawn(async move { - Gemla::process_node(node, semaphore).await + Gemla::process_node(node, gemla_context).await })); } else { trace!("No node found to process, joining threads"); @@ -180,7 +178,7 @@ where // We need to retrieve the processed nodes from the resulting list and replace them in the original list reduced_results.and_then(|r| { - self.data.mutate(|(d, _)| { + self.data.mutate(|(d, _, context)| { if let Some(t) = d { let failed_nodes = Gemla::replace_nodes(t, r); // We receive a list of nodes that were unable to be found in the original tree @@ -192,7 +190,7 @@ where } // Once the nodes are replaced we need to find nodes that can be merged from the completed children nodes - Gemla::merge_completed_nodes(t) + block_on(Gemla::merge_completed_nodes(t, context.clone())) } else { warn!("Unable to replce nodes {:?} in empty tree", r); Ok(()) @@ -204,7 +202,8 @@ where Ok(()) } - fn merge_completed_nodes(tree: &mut SimulationTree) -> Result<(), Error> { + #[async_recursion] + async fn merge_completed_nodes(tree: &mut SimulationTree, gemla_context: T::Context) -> Result<(), Error> { if tree.val.state() == GeneticState::Initialize { match (&mut tree.left, &mut tree.right) { // If the current node has been initialized, and has children nodes that are completed, then we need @@ -215,7 +214,7 @@ where { info!("Merging nodes {} and {}", l.val.id(), r.val.id()); if let (Some(left_node), Some(right_node)) = (l.val.as_ref(), r.val.as_ref()) { - let merged_node = GeneticNode::merge(left_node, right_node, &tree.val.id())?; + let merged_node = GeneticNode::merge(left_node, right_node, &tree.val.id(), gemla_context.clone()).await?; tree.val = GeneticNodeWrapper::from( *merged_node, tree.val.max_generations(), @@ -224,8 +223,8 @@ where } } (Some(l), Some(r)) => { - Gemla::merge_completed_nodes(l)?; - Gemla::merge_completed_nodes(r)?; + Gemla::merge_completed_nodes(l, gemla_context.clone()).await?; + Gemla::merge_completed_nodes(r, gemla_context.clone()).await?; } // If there is only one child node that's completed then we want to copy it to the parent node (Some(l), None) if l.val.state() == GeneticState::Finish => { @@ -239,7 +238,7 @@ where ); } } - (Some(l), None) => Gemla::merge_completed_nodes(l)?, + (Some(l), None) => Gemla::merge_completed_nodes(l, gemla_context.clone()).await?, (None, Some(r)) if r.val.state() == GeneticState::Finish => { trace!("Copying node {}", r.val.id()); @@ -251,7 +250,7 @@ where ); } } - (None, Some(r)) => Gemla::merge_completed_nodes(r)?, + (None, Some(r)) => Gemla::merge_completed_nodes(r, gemla_context.clone()).await?, (_, _) => (), } } @@ -329,15 +328,15 @@ where tree.val.state() == GeneticState::Finish } - async fn process_node(mut node: GeneticNodeWrapper, semaphore: Arc) -> Result, Error> { + async fn process_node(mut node: GeneticNodeWrapper, gemla_context: T::Context) -> Result, Error> { let node_state_time = Instant::now(); let node_state = node.state(); - node.process_node(semaphore.clone()).await?; + node.process_node(gemla_context.clone()).await?; if node.state() == GeneticState::Simulate { - node.process_node(semaphore.clone()).await?; + node.process_node(gemla_context.clone()).await?; } trace!( @@ -397,20 +396,22 @@ mod tests { #[async_trait] impl genetic_node::GeneticNode for TestState { - async fn simulate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { + type Context = (); + + async fn simulate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { self.score += 1.0; Ok(()) } - fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { + async fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { Ok(()) } - fn initialize(_context: GeneticNodeContext) -> Result, Error> { + async fn initialize(_context: GeneticNodeContext) -> Result, Error> { Ok(Box::new(TestState { score: 0.0 })) } - fn merge(left: &TestState, right: &TestState, _id: &Uuid) -> Result, Error> { + async fn merge(left: &TestState, right: &TestState, _id: &Uuid, _: Self::Context) -> Result, Error> { Ok(Box::new(if left.score > right.score { left.clone() } else { @@ -433,7 +434,6 @@ mod tests { let mut config = GemlaConfig { generations_per_height: 1, overwrite: true, - shared_semaphore_concurrency_limit: 1, }; let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; @@ -483,7 +483,6 @@ mod tests { let config = GemlaConfig { generations_per_height: 10, overwrite: true, - shared_semaphore_concurrency_limit: 1, }; let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; From 0ccd824ee67781613be7354daa2625bf3d471a52 Mon Sep 17 00:00:00 2001 From: vandomej Date: Thu, 4 Apr 2024 20:30:08 -0700 Subject: [PATCH 17/26] Defining merge operation --- gemla/Cargo.toml | 1 + gemla/src/bin/fighter_nn/mod.rs | 138 +++++++++++++++++++++++++++----- 2 files changed, 119 insertions(+), 20 deletions(-) diff --git a/gemla/Cargo.toml b/gemla/Cargo.toml index 161d544..2dd14c4 100644 --- a/gemla/Cargo.toml +++ b/gemla/Cargo.toml @@ -32,3 +32,4 @@ easy-parallel = "3.3.1" fann = "0.1.8" async-trait = "0.1.78" async-recursion = "1.1.0" +lerp = "0.5.0" diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index b7b2c43..e781a9d 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -3,10 +3,11 @@ extern crate fann; pub mod neural_network_utility; pub mod fighter_context; -use std::{fs::{self, File}, io::{self, BufRead, BufReader}, ops::Range, path::{Path, PathBuf}, sync::Arc}; +use std::{cmp::max, fs::{self, File}, io::{self, BufRead, BufReader}, ops::Range, path::{Path, PathBuf}}; use fann::{ActivationFunc, Fann}; use futures::future::join_all; use gemla::{core::genetic_node::{GeneticNode, GeneticNodeContext}, error::Error}; +use lerp::Lerp; use rand::prelude::*; use serde::{Deserialize, Serialize}; use anyhow::Context; @@ -148,7 +149,7 @@ impl GeneticNode for FighterNN { let random_nn_index = thread_rng().gen_range(0..self_clone.population_size); let folder = self_clone.folder.clone(); let generation = self_clone.generation; - let semaphore_clone = Arc::clone(&semaphore_clone); + let semaphore_clone = semaphore_clone.clone(); let random_nn = folder.join(format!("{}", generation)).join(self_clone.get_individual_id(random_nn_index as u64)); let nn_clone = nn.clone(); // Clone the path to use in the async block @@ -156,7 +157,7 @@ impl GeneticNode for FighterNN { let future = async move { let permit = semaphore_clone.acquire_owned().await.with_context(|| "Failed to acquire semaphore permit")?; - let score = run_1v1_simulation(&nn_clone, &random_nn).await?; + let (score, _) = run_1v1_simulation(&nn_clone, &random_nn).await?; drop(permit); @@ -262,16 +263,66 @@ impl GeneticNode for FighterNN { Ok(()) } - async fn merge(left: &FighterNN, right: &FighterNN, id: &Uuid, _: Self::Context) -> Result, Error> { + async fn merge(left: &FighterNN, right: &FighterNN, id: &Uuid, gemla_context: Self::Context) -> Result, Error> { let base_path = PathBuf::from(BASE_DIR); let folder = base_path.join(format!("fighter_nn_{:06}", id)); // Ensure the folder exists, including the generation subfolder. fs::create_dir_all(&folder.join("0")) .with_context(|| format!("Failed to create directory {:?}", folder.join("0")))?; + + let get_highest_scores = |fighter: &FighterNN| -> Vec<(u64, f32)> { + let mut sorted_scores: Vec<_> = fighter.scores[fighter.generation as usize].iter().collect(); + sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); + sorted_scores.iter().take(fighter.population_size / 2).map(|(k, v)| (**k, **v)).collect() + }; + + let left_scores = get_highest_scores(left); + let right_scores = get_highest_scores(right); + + debug!("Left scores: {:?}", left_scores); + debug!("Right scores: {:?}", right_scores); + + let mut simulations = Vec::new(); + + for _ in 0..max(left.population_size, right.population_size)*SIMULATION_ROUNDS { + let left_nn_id = left_scores[thread_rng().gen_range(0..left_scores.len())].0; + let right_nn_id = right_scores[thread_rng().gen_range(0..right_scores.len())].0; + + let left_nn_path = left.folder.join(left.generation.to_string()).join(left.get_individual_id(left_nn_id)); + let right_nn_path = right.folder.join(right.generation.to_string()).join(right.get_individual_id(right_nn_id)); + let semaphore_clone = gemla_context.shared_semaphore.clone(); + + let future = async move { + let permit = semaphore_clone.acquire_owned().await.with_context(|| "Failed to acquire semaphore permit")?; + + let (left_score, right_score) = run_1v1_simulation(&left_nn_path, &right_nn_path).await?; + + drop(permit); + + Ok::<(f32, f32), Error>((left_score, right_score)) + }; + + simulations.push(future); + } + + let results: Result, Error> = join_all(simulations).await.into_iter().collect(); + let scores = results?; + + let total_left_score = scores.iter().map(|(l, _)| l).sum::(); + let total_right_score = scores.iter().map(|(_, r)| r).sum::(); + + debug!("Total left score: {}", total_left_score); + debug!("Total right score: {}", total_right_score); + + let score_difference = total_right_score - total_left_score; + // Use the sigmoid function to determine lerp amount + let lerp_amount = 1.0 / (1.0 + (-score_difference).exp()); + + let mut nn_shapes = HashMap::new(); // Function to copy NNs from a source FighterNN to the new folder. - let copy_nns = |source: &FighterNN, folder: &PathBuf, id: &Uuid, start_idx: usize| -> Result<(), Error> { + let mut copy_nns = |source: &FighterNN, folder: &PathBuf, id: &Uuid, start_idx: usize| -> Result<(), Error> { let mut sorted_scores: Vec<_> = source.scores[source.generation as usize].iter().collect(); sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); let remaining = sorted_scores[(source.population_size / 2)..].iter().map(|(k, _)| *k).collect::>(); @@ -281,26 +332,62 @@ impl GeneticNode for FighterNN { let new_nn_path = folder.join("0").join(format!("{:06}_fighter_nn_{}.net", id, start_idx + i)); fs::copy(&nn_path, &new_nn_path) .with_context(|| format!("Failed to copy nn from {:?} to {:?}", nn_path, new_nn_path))?; + + nn_shapes.insert((start_idx + i) as u64, source.nn_shapes.get(&nn_id).unwrap().clone()); } + Ok(()) }; // Copy the top half of NNs from each parent to the new folder. copy_nns(left, &folder, id, 0)?; copy_nns(right, &folder, id, left.population_size as usize / 2)?; + + debug!("nn_shapes: {:?}", nn_shapes); + + // Lerp the mutation rates and weight ranges + let crossbreed_segments = (left.crossbreed_segments as f32).lerp(right.crossbreed_segments as f32, lerp_amount) as usize; + + let weight_initialization_range_start = left.weight_initialization_range.start.lerp(right.weight_initialization_range.start, lerp_amount); + let weight_initialization_range_end = left.weight_initialization_range.end.lerp(right.weight_initialization_range.end, lerp_amount); + // Have to ensure the range is valid + let weight_initialization_range = if weight_initialization_range_start < weight_initialization_range_end { + weight_initialization_range_start..weight_initialization_range_end + } else { + weight_initialization_range_end..weight_initialization_range_start + }; + + debug!("weight_initialization_range: {:?}", weight_initialization_range); + + let minor_mutation_rate = left.minor_mutation_rate.lerp(right.minor_mutation_rate, lerp_amount); + let major_mutation_rate = left.major_mutation_rate.lerp(right.major_mutation_rate, lerp_amount); + + debug!("minor_mutation_rate: {}", minor_mutation_rate); + debug!("major_mutation_rate: {}", major_mutation_rate); + let mutation_weight_range_start = left.mutation_weight_range.start.lerp(right.mutation_weight_range.start, lerp_amount); + let mutation_weight_range_end = left.mutation_weight_range.end.lerp(right.mutation_weight_range.end, lerp_amount); + // Have to ensure the range is valid + let mutation_weight_range = if mutation_weight_range_start < mutation_weight_range_end { + mutation_weight_range_start..mutation_weight_range_end + } else { + mutation_weight_range_end..mutation_weight_range_start + }; + + debug!("mutation_weight_range: {:?}", mutation_weight_range); + Ok(Box::new(FighterNN { id: *id, folder, generation: 0, - population_size: left.population_size, // Assuming left and right have the same population size. + population_size: nn_shapes.len(), scores: vec![HashMap::new()], - crossbreed_segments: left.crossbreed_segments, - nn_shapes: left.nn_shapes.clone(), // Assuming left and right have the same nn_shapes. - weight_initialization_range: left.weight_initialization_range.clone(), - minor_mutation_rate: left.minor_mutation_rate, - major_mutation_rate: left.major_mutation_rate, - mutation_weight_range: left.mutation_weight_range.clone(), + crossbreed_segments, + nn_shapes, + weight_initialization_range, + minor_mutation_rate, + major_mutation_rate, + mutation_weight_range, })) } } @@ -311,7 +398,7 @@ impl FighterNN { } } -async fn run_1v1_simulation(nn_path_1: &PathBuf, nn_path_2: &PathBuf) -> Result { +async fn run_1v1_simulation(nn_path_1: &PathBuf, nn_path_2: &PathBuf) -> Result<(f32, f32), Error> { // Construct the score file path let base_folder = nn_path_1.parent().unwrap(); let nn_1_id = nn_path_1.file_stem().unwrap().to_str().unwrap(); @@ -323,9 +410,13 @@ async fn run_1v1_simulation(nn_path_1: &PathBuf, nn_path_2: &PathBuf) -> Result< let round_score = read_score_from_file(&score_file, &nn_1_id).await .with_context(|| format!("Failed to read score from file: {:?}", score_file))?; - trace!("{} scored {}", nn_1_id, round_score); + let opposing_score = read_score_from_file(&score_file, &nn_2_id).await + .with_context(|| format!("Failed to read score from file: {:?}", score_file))?; - return Ok::(round_score); + trace!("{} scored {}, while {} scored {}", nn_1_id, round_score, nn_2_id, opposing_score); + + + return Ok((round_score, opposing_score)); } // Check if the opposite round score has been determined @@ -334,9 +425,12 @@ async fn run_1v1_simulation(nn_path_1: &PathBuf, nn_path_2: &PathBuf) -> Result< let round_score = read_score_from_file(&opposite_score_file, &nn_1_id).await .with_context(|| format!("Failed to read score from file: {:?}", opposite_score_file))?; - trace!("{} scored {}", nn_1_id, round_score); + let opposing_score = read_score_from_file(&opposite_score_file, &nn_2_id).await + .with_context(|| format!("Failed to read score from file: {:?}", opposite_score_file))?; - return Ok::(1.0 - round_score); + trace!("{} scored {}, while {} scored {}", nn_1_id, round_score, nn_2_id, opposing_score); + + return Ok((round_score, opposing_score)); } // Run simulation until score file is generated @@ -368,12 +462,16 @@ async fn run_1v1_simulation(nn_path_1: &PathBuf, nn_path_2: &PathBuf) -> Result< let round_score = read_score_from_file(&score_file, &nn_1_id).await .with_context(|| format!("Failed to read score from file: {:?}", score_file))?; - trace!("{} scored {}", nn_1_id, round_score); + let opposing_score = read_score_from_file(&score_file, &nn_2_id).await + .with_context(|| format!("Failed to read score from file: {:?}", score_file))?; - Ok(round_score) + trace!("{} scored {}, while {} scored {}", nn_1_id, round_score, nn_2_id, opposing_score); + + + return Ok((round_score, opposing_score)) } else { warn!("Score file not found: {:?}", score_file); - Ok(0.0) + Ok((0.0, 0.0)) } } From 05c7dcbe1132bed107e430d6e00ad1e4c40eb879 Mon Sep 17 00:00:00 2001 From: vandomej Date: Fri, 5 Apr 2024 14:04:45 -0700 Subject: [PATCH 18/26] debugging merge nodes --- gemla/Cargo.toml | 3 +- gemla/src/bin/bin.rs | 2 + gemla/src/bin/fighter_nn/fighter_context.rs | 2 +- gemla/src/bin/fighter_nn/mod.rs | 58 +++++++------- .../bin/fighter_nn/neural_network_utility.rs | 78 +++++++++++-------- gemla/src/core/mod.rs | 9 +-- 6 files changed, 82 insertions(+), 70 deletions(-) diff --git a/gemla/Cargo.toml b/gemla/Cargo.toml index 2dd14c4..4208653 100644 --- a/gemla/Cargo.toml +++ b/gemla/Cargo.toml @@ -26,10 +26,11 @@ rand = "0.8.5" log = "0.4.21" env_logger = "0.11.3" futures = "0.3.30" -tokio = { version = "1.36.0", features = ["full"] } +tokio = { version = "1.37.0", features = ["full"] } num_cpus = "1.16.0" easy-parallel = "3.3.1" fann = "0.1.8" async-trait = "0.1.78" async-recursion = "1.1.0" lerp = "0.5.0" +console-subscriber = "0.2.0" diff --git a/gemla/src/bin/bin.rs b/gemla/src/bin/bin.rs index 464f1aa..f032e96 100644 --- a/gemla/src/bin/bin.rs +++ b/gemla/src/bin/bin.rs @@ -32,6 +32,8 @@ struct Args { /// TODO fn main() -> Result<()> { env_logger::init(); + // console_subscriber::init(); + info!("Starting"); let now = Instant::now(); diff --git a/gemla/src/bin/fighter_nn/fighter_context.rs b/gemla/src/bin/fighter_nn/fighter_context.rs index 503bbf8..a87706b 100644 --- a/gemla/src/bin/fighter_nn/fighter_context.rs +++ b/gemla/src/bin/fighter_nn/fighter_context.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use tokio::sync::Semaphore; -const SHARED_SEMAPHORE_CONCURRENCY_LIMIT: usize = 20; +const SHARED_SEMAPHORE_CONCURRENCY_LIMIT: usize = 50; #[derive(Debug, Clone)] diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index e781a9d..d4683b9 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -3,15 +3,15 @@ extern crate fann; pub mod neural_network_utility; pub mod fighter_context; -use std::{cmp::max, fs::{self, File}, io::{self, BufRead, BufReader}, ops::Range, path::{Path, PathBuf}}; +use std::{cmp::max, collections::{HashSet, VecDeque}, fs::{self, File}, io::{self, BufRead, BufReader}, ops::Range, panic::{catch_unwind, AssertUnwindSafe}, path::{Path, PathBuf}, sync::{Arc, Mutex}, time::Duration}; use fann::{ActivationFunc, Fann}; -use futures::future::join_all; +use futures::{executor::block_on, future::{join, join_all, select_all}, stream::FuturesUnordered, FutureExt, StreamExt}; use gemla::{core::genetic_node::{GeneticNode, GeneticNodeContext}, error::Error}; use lerp::Lerp; use rand::prelude::*; use serde::{Deserialize, Serialize}; use anyhow::Context; -use tokio::process::Command; +use tokio::{process::Command, sync::{mpsc, Semaphore}, task, time::{sleep, timeout, Sleep}}; use uuid::Uuid; use std::collections::HashMap; use async_trait::async_trait; @@ -21,7 +21,7 @@ use self::neural_network_utility::{crossbreed, major_mutation}; const BASE_DIR: &str = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations"; const POPULATION: usize = 50; -const NEURAL_NETWORK_INPUTS: usize = 14; +const NEURAL_NETWORK_INPUTS: usize = 18; const NEURAL_NETWORK_OUTPUTS: usize = 8; const NEURAL_NETWORK_HIDDEN_LAYERS_MIN: usize = 1; const NEURAL_NETWORK_HIDDEN_LAYERS_MAX: usize = 10; @@ -239,9 +239,9 @@ impl GeneticNode for FighterNN { let mut connections = new_fann.get_connections(); // Vector of connections for c in &mut connections { if thread_rng().gen_range(0.0..1.0) < self.minor_mutation_rate { - debug!("Minor mutation on connection {:?}", c); + trace!("Minor mutation on connection {:?}", c); c.weight += thread_rng().gen_range(self.weight_initialization_range.clone()); - debug!("New weight: {}", c.weight); + trace!("New weight: {}", c.weight); } } @@ -413,7 +413,7 @@ async fn run_1v1_simulation(nn_path_1: &PathBuf, nn_path_2: &PathBuf) -> Result< let opposing_score = read_score_from_file(&score_file, &nn_2_id).await .with_context(|| format!("Failed to read score from file: {:?}", score_file))?; - trace!("{} scored {}, while {} scored {}", nn_1_id, round_score, nn_2_id, opposing_score); + debug!("{} scored {}, while {} scored {}", nn_1_id, round_score, nn_2_id, opposing_score); return Ok((round_score, opposing_score)); @@ -428,7 +428,7 @@ async fn run_1v1_simulation(nn_path_1: &PathBuf, nn_path_2: &PathBuf) -> Result< let opposing_score = read_score_from_file(&opposite_score_file, &nn_2_id).await .with_context(|| format!("Failed to read score from file: {:?}", opposite_score_file))?; - trace!("{} scored {}, while {} scored {}", nn_1_id, round_score, nn_2_id, opposing_score); + debug!("{} scored {}, while {} scored {}", nn_1_id, round_score, nn_2_id, opposing_score); return Ok((round_score, opposing_score)); } @@ -438,24 +438,28 @@ async fn run_1v1_simulation(nn_path_1: &PathBuf, nn_path_2: &PathBuf) -> Result< let config2_arg = format!("-NN2Config=\"{}\"", nn_path_2.to_str().unwrap()); let disable_unreal_rendering_arg = "-nullrhi".to_string(); - while !score_file.exists() { - let _output = if thread_rng().gen_range(0..100) < 1 { - Command::new(GAME_EXECUTABLE_PATH) - .arg(&config1_arg) - .arg(&config2_arg) - .output() - .await - .expect("Failed to execute game") - } else { - Command::new(GAME_EXECUTABLE_PATH) - .arg(&config1_arg) - .arg(&config2_arg) - .arg(&disable_unreal_rendering_arg) - .output() - .await - .expect("Failed to execute game") - }; - } + // debug!("the following command {} {} {} {}", GAME_EXECUTABLE_PATH, config1_arg, config2_arg, disable_unreal_rendering_arg); + + trace!("Running simulation for {} vs {}", nn_1_id, nn_2_id); + + let _output = if thread_rng().gen_range(0..100) < 1 { + Command::new(GAME_EXECUTABLE_PATH) + .arg(&config1_arg) + .arg(&config2_arg) + .output() + .await + .expect("Failed to execute game") + } else { + Command::new(GAME_EXECUTABLE_PATH) + .arg(&config1_arg) + .arg(&config2_arg) + .arg(&disable_unreal_rendering_arg) + .output() + .await + .expect("Failed to execute game") + }; + + trace!("Simulation completed for {} vs {}: {}", nn_1_id, nn_2_id, score_file.exists()); // Read the score from the file if score_file.exists() { @@ -465,7 +469,7 @@ async fn run_1v1_simulation(nn_path_1: &PathBuf, nn_path_2: &PathBuf) -> Result< let opposing_score = read_score_from_file(&score_file, &nn_2_id).await .with_context(|| format!("Failed to read score from file: {:?}", score_file))?; - trace!("{} scored {}, while {} scored {}", nn_1_id, round_score, nn_2_id, opposing_score); + debug!("{} scored {}, while {} scored {}", nn_1_id, round_score, nn_2_id, opposing_score); return Ok((round_score, opposing_score)) diff --git a/gemla/src/bin/fighter_nn/neural_network_utility.rs b/gemla/src/bin/fighter_nn/neural_network_utility.rs index 1dcc5ae..a3e5bc1 100644 --- a/gemla/src/bin/fighter_nn/neural_network_utility.rs +++ b/gemla/src/bin/fighter_nn/neural_network_utility.rs @@ -171,17 +171,17 @@ pub fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: if *is_primary { let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); - debug!("Primary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", previous_new_id, new_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, previous_neuron_id, neuron_id); + trace!("Primary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", previous_new_id, new_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, previous_neuron_id, neuron_id); } else { let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); - debug!("Secondary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", previous_new_id, new_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, previous_neuron_id, neuron_id); + trace!("Secondary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", previous_new_id, new_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, previous_neuron_id, neuron_id); } let translated_from = to_bias_network_id(previous_new_id, &new_shape); let translated_to = to_bias_network_id(new_id, &new_shape); new_fann.set_weight(translated_from, translated_to, connection.weight); } else { - debug!("Connection not found for ({}, {}) -> ({}, {})", previous_new_id, new_id, previous_neuron_id, neuron_id); + trace!("Connection not found for ({}, {}) -> ({}, {})", previous_new_id, new_id, previous_neuron_id, neuron_id); } } } @@ -193,11 +193,13 @@ pub fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: for (neuron_id, is_primary, _, new_id) in current_layer_connections.iter() { let translated_neuron_id = to_bias_network_id(new_id, &new_shape); - let mut connection; + let mut connection = None; let mut found_in_primary = false; if *is_primary { - let primary_bias_neuron = get_bias_neuron_for_layer(layer, &primary_shape).unwrap(); - connection = primary_connections.iter() + let primary_bias_neuron = get_bias_neuron_for_layer(layer, &primary_shape); + if let Some(primary_bias_neuron) = primary_bias_neuron + { + connection = primary_connections.iter() .find(|connection| { let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); @@ -207,9 +209,29 @@ pub fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: false } }); + } + if let None = connection { - let secondary_bias_neuron = get_bias_neuron_for_layer(layer, &secondary_shape).unwrap(); + let secondary_bias_neuron = get_bias_neuron_for_layer(layer, &secondary_shape); + if let Some(secondary_bias_neuron) = secondary_bias_neuron { + connection = secondary_connections.iter() + .find(|connection| { + let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + + if let Some(to_neuron) = to_neuron { + connection.from_neuron == secondary_bias_neuron && to_neuron == *neuron_id + } else { + false + } + }); + } + } else { + found_in_primary = true; + } + } else { + let secondary_bias_neuron = get_bias_neuron_for_layer(layer, &secondary_shape); + if let Some(secondary_bias_neuron) = secondary_bias_neuron { connection = secondary_connections.iter() .find(|connection| { let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); @@ -220,34 +242,22 @@ pub fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: false } }); - } else { - found_in_primary = true; } - } else { - let secondary_bias_neuron = get_bias_neuron_for_layer(layer, &secondary_shape).unwrap(); - connection = secondary_connections.iter() - .find(|connection| { - let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); - - if let Some(to_neuron) = to_neuron { - connection.from_neuron == secondary_bias_neuron && to_neuron == *neuron_id - } else { - false - } - }); if let None = connection { - let primary_bias_neuron = get_bias_neuron_for_layer(layer, &primary_shape).unwrap(); - connection = primary_connections.iter() - .find(|connection| { - let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); + let primary_bias_neuron = get_bias_neuron_for_layer(layer, &primary_shape); + if let Some(primary_bias_neuron) = primary_bias_neuron { + connection = primary_connections.iter() + .find(|connection| { + let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); - if let Some(to_neuron) = to_neuron { - connection.from_neuron == primary_bias_neuron && to_neuron == *neuron_id - } else { - false - } - }); + if let Some(to_neuron) = to_neuron { + connection.from_neuron == primary_bias_neuron && to_neuron == *neuron_id + } else { + false + } + }); + } } else { found_in_primary = true; } @@ -257,15 +267,15 @@ pub fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: if *is_primary { let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); - debug!("Primary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", bias_neuron, translated_neuron_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, bias_neuron, neuron_id); + trace!("Primary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", bias_neuron, translated_neuron_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, bias_neuron, neuron_id); } else { let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); - debug!("Secondary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", bias_neuron, translated_neuron_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, bias_neuron, neuron_id); + trace!("Secondary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", bias_neuron, translated_neuron_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, bias_neuron, neuron_id); } new_fann.set_weight(bias_neuron, translated_neuron_id, connection.weight); } else { - debug!("Connection not found for bias ({}, {}) -> ({}, {}) primary: {}", bias_neuron, neuron_id, bias_neuron, translated_neuron_id, is_primary); + trace!("Connection not found for bias ({}, {}) -> ({}, {}) primary: {}", bias_neuron, neuron_id, bias_neuron, translated_neuron_id, is_primary); } } } diff --git a/gemla/src/core/mod.rs b/gemla/src/core/mod.rs index 9d831a3..4c2b85d 100644 --- a/gemla/src/core/mod.rs +++ b/gemla/src/core/mod.rs @@ -6,7 +6,7 @@ pub mod genetic_node; use crate::{error::Error, tree::Tree}; use async_recursion::async_recursion; use file_linked::{constants::data_format::DataFormat, FileLinked}; -use futures::{executor::block_on, future}; +use futures::{executor::{block_on, LocalPool}, future, task::{LocalFutureObj, LocalSpawn, LocalSpawnExt}, FutureExt}; use genetic_node::{GeneticNode, GeneticNodeWrapper, GeneticState}; use log::{info, trace, warn}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -334,12 +334,7 @@ where node.process_node(gemla_context.clone()).await?; - if node.state() == GeneticState::Simulate - { - node.process_node(gemla_context.clone()).await?; - } - - trace!( + info!( "{:?} completed in {:?} for {}", node_state, node_state_time.elapsed(), From b56e37d41179112cea8e92cabcaba6bd54e36566 Mon Sep 17 00:00:00 2001 From: vandomej Date: Fri, 5 Apr 2024 21:26:03 -0700 Subject: [PATCH 19/26] Asynchronous conversion --- .vscode/launch.json | 32 +++++ file_linked/Cargo.toml | 2 + file_linked/src/lib.rs | 210 +++++++++++++++++++---------- gemla/.cargo/config.toml | 0 gemla/src/bin/bin.rs | 2 +- gemla/src/core/genetic_node.rs | 16 ++- gemla/src/core/mod.rs | 233 +++++++++++++++++++-------------- gemla/src/tree/mod.rs | 2 +- 8 files changed, 327 insertions(+), 170 deletions(-) create mode 100644 gemla/.cargo/config.toml diff --git a/.vscode/launch.json b/.vscode/launch.json index bc9cad2..a6c15e4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -27,6 +27,38 @@ "filter": { } }, "args": [], + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug gemla Lib Tests", + "cargo": { + "args": [ + "test", + "--manifest-path", "${workspaceFolder}/gemla/Cargo.toml", + "--no-run", // Compiles the tests without running them + "--package=gemla", // Specify your package name if necessary + "--lib" + ], + "filter": { } + }, + "args": [], + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug Rust FileLinked Tests", + "cargo": { + "args": [ + "test", + "--manifest-path", "${workspaceFolder}/file_linked/Cargo.toml", + "--no-run", // Compiles the tests without running them + "--package=file_linked", // Specify your package name if necessary + "--lib" + ], + "filter": { } + }, + "args": [], } ] } \ No newline at end of file diff --git a/file_linked/Cargo.toml b/file_linked/Cargo.toml index 3a4e5b6..14ba835 100644 --- a/file_linked/Cargo.toml +++ b/file_linked/Cargo.toml @@ -21,3 +21,5 @@ anyhow = "1.0" bincode = "1.3.3" log = "0.4.14" serde_json = "1.0.114" +tokio = { version = "1.37.0", features = ["full"] } +futures = "0.3.30" diff --git a/file_linked/src/lib.rs b/file_linked/src/lib.rs index de7e075..4c027fd 100644 --- a/file_linked/src/lib.rs +++ b/file_linked/src/lib.rs @@ -6,14 +6,12 @@ pub mod constants; use anyhow::{anyhow, Context}; use constants::data_format::DataFormat; use error::Error; +use futures::executor::block_on; use log::info; use serde::{de::DeserializeOwned, Serialize}; +use tokio::sync::RwLock; use std::{ - fs::{copy, remove_file, File}, - io::{ErrorKind, Write}, - path::{Path, PathBuf}, - thread, - thread::JoinHandle, + borrow::Borrow, fs::{copy, remove_file, File}, io::{ErrorKind, Write}, path::{Path, PathBuf}, sync::Arc, thread::{self, JoinHandle} }; @@ -24,7 +22,7 @@ pub struct FileLinked where T: Serialize, { - val: T, + val: Arc>, path: PathBuf, temp_file_path: PathBuf, file_thread: Option>, @@ -85,8 +83,8 @@ where /// # std::fs::remove_file("./temp").expect("Unable to remove file"); /// # } /// ``` - pub fn readonly(&self) -> &T { - &self.val + pub fn readonly(&self) -> Arc> { + self.val.clone() } /// Creates a new [`FileLinked`] object of type `T` stored to the file given by `path`. @@ -126,7 +124,7 @@ where /// # std::fs::remove_file("./temp").expect("Unable to remove file"); /// # } /// ``` - pub fn new(val: T, path: &Path, data_format: DataFormat) -> Result, Error> { + pub async fn new(val: T, path: &Path, data_format: DataFormat) -> Result, Error> { let mut temp_file_path = path.to_path_buf(); temp_file_path.set_file_name(format!( ".temp{}", @@ -137,24 +135,26 @@ where )); let mut result = FileLinked { - val, + val: Arc::new(RwLock::new(val)), path: path.to_path_buf(), temp_file_path, file_thread: None, data_format }; - result.write_data()?; + result.write_data().await?; Ok(result) } - fn write_data(&mut self) -> Result<(), Error> { + async fn write_data(&mut self) -> Result<(), Error> { let thread_path = self.path.clone(); let thread_temp_path = self.temp_file_path.clone(); + let val = self.val.read().await; + let thread_val = match self.data_format { - DataFormat::Bincode => bincode::serialize(&self.val) + DataFormat::Bincode => bincode::serialize(&*val) .with_context(|| "Unable to serialize object into bincode".to_string())?, - DataFormat::Json => serde_json::to_vec(&self.val) + DataFormat::Json => serde_json::to_vec(&*val) .with_context(|| "Unable to serialize object into JSON".to_string())?, }; @@ -238,10 +238,15 @@ where /// # Ok(()) /// # } /// ``` - pub fn mutate U>(&mut self, op: F) -> Result { - let result = op(&mut self.val); + pub async fn mutate U>(&mut self, op: F) -> Result { + let val_clone = self.val.clone(); // Arc> + let mut val = val_clone.write().await; // RwLockWriteGuard - self.write_data()?; + let result = op(&mut val); + + drop(val); + + self.write_data().await?; Ok(result) } @@ -292,10 +297,31 @@ where /// # Ok(()) /// # } /// ``` - pub fn replace(&mut self, val: T) -> Result<(), Error> { - self.val = val; + pub async fn replace(&mut self, val: T) -> Result<(), Error> { + self.val = Arc::new(RwLock::new(val)); - self.write_data() + self.write_data().await + } +} + +impl FileLinked +where + T: Serialize + DeserializeOwned + Send + 'static, +{ + /// Asynchronously modifies the data contained in a `FileLinked` object using an async callback `op`. + pub async fn mutate_async(&mut self, op: F) -> Result + where + F: FnOnce(Arc>) -> Fut, + Fut: std::future::Future + Send, + U: Send, + { + let val_clone = self.val.clone(); + let result = op(val_clone).await; + + self.write_data().await?; + + + Ok(result) } } @@ -377,7 +403,7 @@ where } }) { Ok(val) => Ok(FileLinked { - val, + val: Arc::new(RwLock::new(val)), path: path.to_path_buf(), temp_file_path, file_thread: None, @@ -396,7 +422,7 @@ where .with_context(|| format!("Failed to read/deserialize the object from the file {} and temp file {}", path.display(), temp_file_path.display()))?; Ok(FileLinked { - val, + val: Arc::new(RwLock::new(val)), path: path.to_path_buf(), temp_file_path, file_thread: None, @@ -451,8 +477,12 @@ mod tests { } } - pub fn run Result<(), Error>>(&self, op: F) -> Result<(), Error> { - op(&self.path) + pub async fn run(&self, op: F) -> () + where + F: FnOnce(PathBuf) -> Fut, + Fut: std::future::Future + { + op(self.path.clone()).await } } @@ -464,92 +494,136 @@ mod tests { } } - #[test] - fn test_readonly() -> Result<(), Error> { + #[tokio::test] + async fn test_readonly() { let path = PathBuf::from("test_readonly"); let cleanup = CleanUp::new(&path); - cleanup.run(|p| { + cleanup.run(|p| async move { let val = vec!["one", "two", ""]; - let linked_object = FileLinked::new(val.clone(), &p, DataFormat::Json)?; - assert_eq!(*linked_object.readonly(), val); - - Ok(()) - }) + let linked_object = FileLinked::new(val.clone(), &p, DataFormat::Json).await.expect("Unable to create file linked object"); + let linked_object_arc = linked_object.readonly(); + let linked_object_ref = linked_object_arc.read().await; + assert_eq!(*linked_object_ref, val); + }).await; } - #[test] - fn test_new() -> Result<(), Error> { + #[tokio::test] + async fn test_new() { let path = PathBuf::from("test_new"); let cleanup = CleanUp::new(&path); - cleanup.run(|p| { + cleanup.run(|p| async move { let val = "test"; - FileLinked::new(val, &p, DataFormat::Bincode)?; + FileLinked::new(val, &p, DataFormat::Bincode).await.expect("Unable to create file linked object"); - let file = File::open(&p)?; + let file = File::open(&p).expect("Unable to open file"); let result: String = bincode::deserialize_from(file).expect("Unable to deserialize from file"); assert_eq!(result, val); - - Ok(()) - }) + }).await; } - #[test] - fn test_mutate() -> Result<(), Error> { + #[tokio::test] + async fn test_mutate() { let path = PathBuf::from("test_mutate"); let cleanup = CleanUp::new(&path); - cleanup.run(|p| { + cleanup.run(|p| async move { let list = vec![1, 2, 3, 4]; - let mut file_linked_list = FileLinked::new(list, &p, DataFormat::Json)?; - assert_eq!(*file_linked_list.readonly(), vec![1, 2, 3, 4]); + let mut file_linked_list = FileLinked::new(list, &p, DataFormat::Json).await.expect("Unable to create file linked object"); + let file_linked_list_arc = file_linked_list.readonly(); + let file_linked_list_ref = file_linked_list_arc.read().await; - file_linked_list.mutate(|v1| v1.push(5))?; - assert_eq!(*file_linked_list.readonly(), vec![1, 2, 3, 4, 5]); + assert_eq!(*file_linked_list_ref, vec![1, 2, 3, 4]); - file_linked_list.mutate(|v1| v1[1] = 1)?; - assert_eq!(*file_linked_list.readonly(), vec![1, 1, 3, 4, 5]); + drop(file_linked_list_ref); + file_linked_list.mutate(|v1| v1.push(5)).await.expect("Error mutating file linked object"); + let file_linked_list_arc = file_linked_list.readonly(); + let file_linked_list_ref = file_linked_list_arc.read().await; + + assert_eq!(*file_linked_list_ref, vec![1, 2, 3, 4, 5]); + + drop(file_linked_list_ref); + file_linked_list.mutate(|v1| v1[1] = 1).await.expect("Error mutating file linked object"); + let file_linked_list_arc = file_linked_list.readonly(); + let file_linked_list_ref = file_linked_list_arc.read().await; + + assert_eq!(*file_linked_list_ref, vec![1, 1, 3, 4, 5]); drop(file_linked_list); - Ok(()) - }) + }).await; } - #[test] - fn test_replace() -> Result<(), Error> { + #[tokio::test] + async fn test_async_mutate() { + let path = PathBuf::from("test_async_mutate"); + let cleanup = CleanUp::new(&path); + cleanup.run(|p| async move { + let list = vec![1, 2, 3, 4]; + let mut file_linked_list = FileLinked::new(list, &p, DataFormat::Json).await.expect("Unable to create file linked object"); + let file_linked_list_arc = file_linked_list.readonly(); + let file_linked_list_ref = file_linked_list_arc.read().await; + + assert_eq!(*file_linked_list_ref, vec![1, 2, 3, 4]); + + drop(file_linked_list_ref); + file_linked_list.mutate_async(|v1| async move { + let mut v = v1.write().await; + v.push(5); + v[1] = 1; + Ok::<(), Error>(()) + }).await.expect("Error mutating file linked object").expect("Error mutating file linked object"); + + let file_linked_list_arc = file_linked_list.readonly(); + let file_linked_list_ref = file_linked_list_arc.read().await; + + assert_eq!(*file_linked_list_ref, vec![1, 1, 3, 4, 5]); + + drop(file_linked_list); + }).await; + } + + #[tokio::test] + async fn test_replace() { let path = PathBuf::from("test_replace"); let cleanup = CleanUp::new(&path); - cleanup.run(|p| { + cleanup.run(|p| async move { let val1 = String::from("val1"); let val2 = String::from("val2"); - let mut file_linked_list = FileLinked::new(val1.clone(), &p, DataFormat::Bincode)?; - assert_eq!(*file_linked_list.readonly(), val1); + let mut file_linked_list = FileLinked::new(val1.clone(), &p, DataFormat::Bincode).await.expect("Unable to create file linked object"); + let file_linked_list_arc = file_linked_list.readonly(); + let file_linked_list_ref = file_linked_list_arc.read().await; - file_linked_list.replace(val2.clone())?; - assert_eq!(*file_linked_list.readonly(), val2); + assert_eq!(*file_linked_list_ref, val1); + + file_linked_list.replace(val2.clone()).await.expect("Error replacing file linked object"); + let file_linked_list_arc = file_linked_list.readonly(); + let file_linked_list_ref = file_linked_list_arc.read().await; + + assert_eq!(*file_linked_list_ref, val2); drop(file_linked_list); - Ok(()) - }) + }).await; } - #[test] - fn test_from_file() -> Result<(), Error> { + #[tokio::test] + async fn test_from_file(){ let path = PathBuf::from("test_from_file"); let cleanup = CleanUp::new(&path); - cleanup.run(|p| { + cleanup.run(|p| async move { let value: Vec = vec![2.0, 3.0, 5.0]; - let file = File::create(&p)?; + let file = File::create(&p).expect("Unable to create file"); bincode::serialize_into(&file, &value).expect("Unable to serialize into file"); drop(file); - let linked_object: FileLinked> = FileLinked::from_file(&p, DataFormat::Bincode)?; - assert_eq!(*linked_object.readonly(), value); + let linked_object: FileLinked> = FileLinked::from_file(&p, DataFormat::Bincode).expect("Unable to create file linked object"); + let linked_object_arc = linked_object.readonly(); + let linked_object_ref = linked_object_arc.read().await; + + assert_eq!(*linked_object_ref, value); drop(linked_object); - Ok(()) - }) + }).await; } } diff --git a/gemla/.cargo/config.toml b/gemla/.cargo/config.toml new file mode 100644 index 0000000..e69de29 diff --git a/gemla/src/bin/bin.rs b/gemla/src/bin/bin.rs index f032e96..a6f8b38 100644 --- a/gemla/src/bin/bin.rs +++ b/gemla/src/bin/bin.rs @@ -51,7 +51,7 @@ fn main() -> Result<()> { overwrite: false, }, DataFormat::Json, - ))?; + ).await)?; // let gemla_arc = Arc::new(gemla); diff --git a/gemla/src/core/genetic_node.rs b/gemla/src/core/genetic_node.rs index eec30bc..d9d7bc0 100644 --- a/gemla/src/core/genetic_node.rs +++ b/gemla/src/core/genetic_node.rs @@ -60,7 +60,10 @@ pub trait GeneticNode : Send { /// Used externally to wrap a node implementing the [`GeneticNode`] trait. Processes state transitions for the given node as /// well as signal recovery. Transition states are given by [`GeneticState`] #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub struct GeneticNodeWrapper { +pub struct GeneticNodeWrapper +where + T: Clone +{ node: Option, state: GeneticState, generation: u64, @@ -68,7 +71,10 @@ pub struct GeneticNodeWrapper { id: Uuid, } -impl Default for GeneticNodeWrapper { +impl Default for GeneticNodeWrapper +where + T: Clone +{ fn default() -> Self { GeneticNodeWrapper { node: None, @@ -82,7 +88,7 @@ impl Default for GeneticNodeWrapper { impl GeneticNodeWrapper where - T: GeneticNode + Debug + Send, + T: GeneticNode + Debug + Send + Clone, T::Context: Send + Sync + Clone + Debug + Serialize + DeserializeOwned + 'static + Default, { pub fn new(max_generations: u64) -> Self { @@ -106,6 +112,10 @@ where self.node.as_ref() } + pub fn take(&mut self) -> Option { + self.node.take() + } + pub fn id(&self) -> Uuid { self.id } diff --git a/gemla/src/core/mod.rs b/gemla/src/core/mod.rs index 4c2b85d..e779318 100644 --- a/gemla/src/core/mod.rs +++ b/gemla/src/core/mod.rs @@ -6,13 +6,13 @@ pub mod genetic_node; use crate::{error::Error, tree::Tree}; use async_recursion::async_recursion; use file_linked::{constants::data_format::DataFormat, FileLinked}; -use futures::{executor::{block_on, LocalPool}, future, task::{LocalFutureObj, LocalSpawn, LocalSpawnExt}, FutureExt}; +use futures::future; use genetic_node::{GeneticNode, GeneticNodeWrapper, GeneticState}; use log::{info, trace, warn}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use tokio::task::JoinHandle; +use tokio::{sync::RwLock, task::JoinHandle}; use std::{ - collections::HashMap, fmt::Debug, fs::File, io::ErrorKind, marker::Send, mem, path::Path, time::Instant + collections::HashMap, fmt::Debug, fs::File, io::ErrorKind, marker::Send, mem, path::Path, sync::Arc, time::Instant }; use uuid::Uuid; @@ -68,7 +68,7 @@ pub struct GemlaConfig { /// [`GeneticNode`]: genetic_node::GeneticNode pub struct Gemla where - T: GeneticNode + Serialize + DeserializeOwned + Debug + Clone + Send, + T: GeneticNode + Serialize + DeserializeOwned + Debug + Send + Clone, T::Context: Send + Sync + Clone + Debug + Serialize + DeserializeOwned + 'static + Default, { pub data: FileLinked<(Option>, GemlaConfig, T::Context)>, @@ -77,16 +77,16 @@ where impl Gemla where - T: GeneticNode + Serialize + DeserializeOwned + Debug + Clone + Send, + T: GeneticNode + Serialize + DeserializeOwned + Debug + Send + Sync + Clone, T::Context: Send + Sync + Clone + Debug + Serialize + DeserializeOwned + 'static + Default, { - pub fn new(path: &Path, config: GemlaConfig, data_format: DataFormat) -> Result { + pub async fn new(path: &Path, config: GemlaConfig, data_format: DataFormat) -> Result { match File::open(path) { // If the file exists we either want to overwrite the file or read from the file // based on the configuration provided Ok(_) => Ok(Gemla { data: if config.overwrite { - FileLinked::new((None, config, T::Context::default()), path, data_format)? + FileLinked::new((None, config, T::Context::default()), path, data_format).await? } else { FileLinked::from_file(path, data_format)? }, @@ -94,62 +94,78 @@ where }), // If the file doesn't exist we must create it Err(error) if error.kind() == ErrorKind::NotFound => Ok(Gemla { - data: FileLinked::new((None, config, T::Context::default()), path, data_format)?, + data: FileLinked::new((None, config, T::Context::default()), path, data_format).await?, threads: HashMap::new(), }), Err(error) => Err(Error::IO(error)), } } - pub fn tree_ref(&self) -> Option<&SimulationTree> { - self.data.readonly().0.as_ref() + pub fn tree_ref(&self) -> Arc>, GemlaConfig, T::Context)>> { + self.data.readonly().clone() } pub async fn simulate(&mut self, steps: u64) -> Result<(), Error> { - // Only increase height if the tree is uninitialized or completed - if self.tree_ref().is_none() || - self - .tree_ref() - .map(|t| Gemla::is_completed(t)) - .unwrap_or(true) { - // Before we can process nodes we must create blank nodes in their place to keep track of which nodes have been processed - // in the tree and which nodes have not. - self.data.mutate(|(d, c, _)| { - let mut tree: Option> = Gemla::increase_height(d.take(), c, steps); - mem::swap(d, &mut tree); - })?; + // Only increase height if the tree is uninitialized or completed + let data_arc = self.data.readonly(); + let data_ref = data_arc.read().await; + let tree_ref = data_ref.0.as_ref(); + + if tree_ref.is_none() || + tree_ref + .map(|t| Gemla::is_completed(t)) + .unwrap_or(true) + { + // Before we can process nodes we must create blank nodes in their place to keep track of which nodes have been processed + // in the tree and which nodes have not. + self.data.mutate(|(d, c, _)| { + let mut tree: Option> = Gemla::increase_height(d.take(), c, steps); + mem::swap(d, &mut tree); + }).await?; + } + + info!( + "Height of simulation tree increased to {}", + tree_ref + .map(|t| format!("{}", t.height())) + .unwrap_or_else(|| "Tree is not defined".to_string()) + ); } - - - info!( - "Height of simulation tree increased to {}", - self.tree_ref() - .map(|t| format!("{}", t.height())) - .unwrap_or_else(|| "Tree is not defined".to_string()) - ); - loop { - // We need to keep simulating until the tree has been completely processed. - if self - .tree_ref() - .map(|t| Gemla::is_completed(t)) - .unwrap_or(false) + let is_tree_processed; + { + let data_arc = self.data.readonly(); + let data_ref = data_arc.read().await; + let tree_ref = data_ref.0.as_ref(); + + is_tree_processed = tree_ref + .map(|t| Gemla::is_completed(t)) + .unwrap_or(false) + } + + + // We need to keep simulating until the tree has been completely processed. + if is_tree_processed + { + self.join_threads().await?; info!("Processed tree"); break; } - if let Some(node) = self - .tree_ref() + if let Some(node) = tree_ref .and_then(|t| self.get_unprocessed_node(t)) { trace!("Adding node to process list {}", node.id()); - let gemla_context = self.data.readonly().2.clone(); + let data_arc = self.data.readonly(); + let data_ref2 = data_arc.read().await; + let gemla_context = data_ref2.2.clone(); + drop(data_ref2); self.threads .insert(node.id(), tokio::spawn(async move { @@ -177,33 +193,44 @@ where self.threads.clear(); // We need to retrieve the processed nodes from the resulting list and replace them in the original list - reduced_results.and_then(|r| { - self.data.mutate(|(d, _, context)| { - if let Some(t) = d { - let failed_nodes = Gemla::replace_nodes(t, r); - // We receive a list of nodes that were unable to be found in the original tree - if !failed_nodes.is_empty() { - warn!( - "Unable to find {:?} to replace in tree", - failed_nodes.iter().map(|n| n.id()) - ) - } + match reduced_results { + Ok(r) => { + self.data.mutate_async(|d| async move { + // Scope to limit the duration of the read lock + let (_, context) = { + let data_read = d.read().await; + (data_read.1.clone(), data_read.2.clone()) + }; // Read lock is dropped here - // Once the nodes are replaced we need to find nodes that can be merged from the completed children nodes - block_on(Gemla::merge_completed_nodes(t, context.clone())) - } else { - warn!("Unable to replce nodes {:?} in empty tree", r); - Ok(()) - } - })? - })?; + let mut data_write = d.write().await; + + if let Some(t) = data_write.0.as_mut() { + let failed_nodes = Gemla::replace_nodes(t, r); + // We receive a list of nodes that were unable to be found in the original tree + if !failed_nodes.is_empty() { + warn!( + "Unable to find {:?} to replace in tree", + failed_nodes.iter().map(|n| n.id()) + ) + } + + // Once the nodes are replaced we need to find nodes that can be merged from the completed children nodes + Gemla::merge_completed_nodes(t, context.clone()).await + } else { + warn!("Unable to replce nodes {:?} in empty tree", r); + Ok(()) + } + }).await??; + } + Err(e) => return Err(e), + } } Ok(()) } #[async_recursion] - async fn merge_completed_nodes(tree: &mut SimulationTree, gemla_context: T::Context) -> Result<(), Error> { + async fn merge_completed_nodes<'a>(tree: &'a mut SimulationTree, gemla_context: T::Context) -> Result<(), Error> { if tree.val.state() == GeneticState::Initialize { match (&mut tree.left, &mut tree.right) { // If the current node has been initialized, and has children nodes that are completed, then we need @@ -213,8 +240,8 @@ where && r.val.state() == GeneticState::Finish => { info!("Merging nodes {} and {}", l.val.id(), r.val.id()); - if let (Some(left_node), Some(right_node)) = (l.val.as_ref(), r.val.as_ref()) { - let merged_node = GeneticNode::merge(left_node, right_node, &tree.val.id(), gemla_context.clone()).await?; + if let (Some(left_node), Some(right_node)) = (l.val.take(), r.val.take()) { + let merged_node = GeneticNode::merge(&left_node, &right_node, &tree.val.id(), gemla_context.clone()).await?; tree.val = GeneticNodeWrapper::from( *merged_node, tree.val.max_generations(), @@ -230,9 +257,9 @@ where (Some(l), None) if l.val.state() == GeneticState::Finish => { trace!("Copying node {}", l.val.id()); - if let Some(left_node) = l.val.as_ref() { + if let Some(left_node) = l.val.take() { GeneticNodeWrapper::from( - left_node.clone(), + left_node, tree.val.max_generations(), tree.val.id(), ); @@ -242,9 +269,9 @@ where (None, Some(r)) if r.val.state() == GeneticState::Finish => { trace!("Copying node {}", r.val.id()); - if let Some(right_node) = r.val.as_ref() { + if let Some(right_node) = r.val.take() { tree.val = GeneticNodeWrapper::from( - right_node.clone(), + right_node, tree.val.max_generations(), tree.val.id(), ); @@ -430,31 +457,41 @@ mod tests { generations_per_height: 1, overwrite: true, }; - let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json).await?; // Now we can use `.await` within the spawned blocking task. gemla.simulate(2).await?; - assert_eq!(gemla.data.readonly().0.as_ref().unwrap().height(), 2); - + let data = gemla.data.readonly(); + let data_lock = data.read().await; + assert_eq!(data_lock.0.as_ref().unwrap().height(), 2); + + drop(data_lock); drop(gemla); assert!(path.exists()); // Testing overwriting data - let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json).await?; gemla.simulate(2).await?; - assert_eq!(gemla.data.readonly().0.as_ref().unwrap().height(), 2); + let data = gemla.data.readonly(); + let data_lock = data.read().await; + assert_eq!(data_lock.0.as_ref().unwrap().height(), 2); + drop(data_lock); drop(gemla); assert!(path.exists()); // Testing not-overwriting data config.overwrite = false; - let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json).await?; gemla.simulate(2).await?; - assert_eq!(gemla.tree_ref().unwrap().height(), 4); + let data = gemla.data.readonly(); + let data_lock = data.read().await; + let tree = data_lock.0.as_ref().unwrap(); + assert_eq!(tree.height(), 4); + drop(data_lock); drop(gemla); assert!(path.exists()); @@ -466,33 +503,35 @@ mod tests { Ok(()) } - #[tokio::test] - async fn test_simulate() -> Result<(), Error> { - let path = PathBuf::from("test_simulate"); - // Use `spawn_blocking` to run the synchronous closure that internally awaits async code. - tokio::task::spawn_blocking(move || { - let rt = Runtime::new().unwrap(); // Create a new Tokio runtime for the async block. - CleanUp::new(&path).run(move |p| { - rt.block_on(async { - // Testing initial creation - let config = GemlaConfig { - generations_per_height: 10, - overwrite: true, - }; - let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; + // #[tokio::test] + // async fn test_simulate() -> Result<(), Error> { + // let path = PathBuf::from("test_simulate"); + // // Use `spawn_blocking` to run the synchronous closure that internally awaits async code. + // tokio::task::spawn_blocking(move || { + // let rt = Runtime::new().unwrap(); // Create a new Tokio runtime for the async block. + // CleanUp::new(&path).run(move |p| { + // rt.block_on(async { + // // Testing initial creation + // let config = GemlaConfig { + // generations_per_height: 10, + // overwrite: true, + // }; + // let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; - // Now we can use `.await` within the spawned blocking task. - gemla.simulate(5).await?; - let tree = gemla.tree_ref().unwrap(); - assert_eq!(tree.height(), 5); - assert_eq!(tree.val.as_ref().unwrap().score, 50.0); + // // Now we can use `.await` within the spawned blocking task. + // gemla.simulate(5).await?; + // let data = gemla.data.readonly(); + // let data_lock = data.read().unwrap(); + // let tree = data_lock.0.as_ref().unwrap(); + // assert_eq!(tree.height(), 5); + // assert_eq!(tree.val.as_ref().unwrap().score, 50.0); - Ok(()) - }) - }) - }).await.unwrap()?; // Wait for the blocking task to complete, then handle the Result. + // Ok(()) + // }) + // }) + // }).await.unwrap()?; // Wait for the blocking task to complete, then handle the Result. - Ok(()) - } + // Ok(()) + // } } diff --git a/gemla/src/tree/mod.rs b/gemla/src/tree/mod.rs index c1a2b39..1388aaf 100644 --- a/gemla/src/tree/mod.rs +++ b/gemla/src/tree/mod.rs @@ -36,7 +36,7 @@ use std::cmp::max; /// t.right = Some(Box::new(btree!(3))); /// assert_eq!(t.right.unwrap().val, 3); /// ``` -#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Debug)] +#[derive(Default, Serialize, Deserialize, PartialEq, Debug)] pub struct Tree { pub val: T, pub left: Option>>, From 7a1f82ac63ea5e4e2bf058b8fe736f62f5123d63 Mon Sep 17 00:00:00 2001 From: vandomej Date: Sat, 6 Apr 2024 00:07:10 -0700 Subject: [PATCH 20/26] Finalizing async implementation --- file_linked/src/lib.rs | 77 +- gemla/src/bin/bin.rs | 36 +- gemla/src/bin/fighter_nn/fighter_context.rs | 4 +- gemla/src/bin/fighter_nn/mod.rs | 379 ++++-- .../bin/fighter_nn/neural_network_utility.rs | 1163 ++++++++++++----- gemla/src/bin/test_state/mod.rs | 84 +- gemla/src/core/genetic_node.rs | 50 +- gemla/src/core/mod.rs | 272 ++-- 8 files changed, 1439 insertions(+), 626 deletions(-) diff --git a/file_linked/src/lib.rs b/file_linked/src/lib.rs index 4c027fd..54dcf30 100644 --- a/file_linked/src/lib.rs +++ b/file_linked/src/lib.rs @@ -6,12 +6,11 @@ pub mod constants; use anyhow::{anyhow, Context}; use constants::data_format::DataFormat; use error::Error; -use futures::executor::block_on; use log::info; use serde::{de::DeserializeOwned, Serialize}; use tokio::sync::RwLock; use std::{ - borrow::Borrow, fs::{copy, remove_file, File}, io::{ErrorKind, Write}, path::{Path, PathBuf}, sync::Arc, thread::{self, JoinHandle} + fs::{copy, remove_file, File}, io::{ErrorKind, Write}, path::{Path, PathBuf}, sync::Arc, thread::{self, JoinHandle} }; @@ -56,6 +55,7 @@ where /// # use std::fmt; /// # use std::string::ToString; /// # use std::path::PathBuf; + /// # use tokio; /// # /// # #[derive(Deserialize, Serialize)] /// # struct Test { @@ -64,19 +64,22 @@ where /// # pub c: f64 /// # } /// # - /// # fn main() { + /// # #[tokio::main] + /// # async fn main() { /// let test = Test { /// a: 1, /// b: String::from("two"), /// c: 3.0 /// }; /// - /// let linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Json) + /// let linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Json).await /// .expect("Unable to create file linked object"); /// - /// assert_eq!(linked_test.readonly().a, 1); - /// assert_eq!(linked_test.readonly().b, String::from("two")); - /// assert_eq!(linked_test.readonly().c, 3.0); + /// let readonly = linked_test.readonly(); + /// let readonly_ref = readonly.read().await; + /// assert_eq!(readonly_ref.a, 1); + /// assert_eq!(readonly_ref.b, String::from("two")); + /// assert_eq!(readonly_ref.c, 3.0); /// # /// # drop(linked_test); /// # @@ -97,6 +100,7 @@ where /// # use std::fmt; /// # use std::string::ToString; /// # use std::path::PathBuf; + /// # use tokio; /// # /// #[derive(Deserialize, Serialize)] /// struct Test { @@ -105,19 +109,22 @@ where /// pub c: f64 /// } /// - /// # fn main() { + /// #[tokio::main] + /// # async fn main() { /// let test = Test { /// a: 1, /// b: String::from("two"), /// c: 3.0 /// }; /// - /// let linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Json) + /// let linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Json).await /// .expect("Unable to create file linked object"); /// - /// assert_eq!(linked_test.readonly().a, 1); - /// assert_eq!(linked_test.readonly().b, String::from("two")); - /// assert_eq!(linked_test.readonly().c, 3.0); + /// let readonly = linked_test.readonly(); + /// let readonly_ref = readonly.read().await; + /// assert_eq!(readonly_ref.a, 1); + /// assert_eq!(readonly_ref.b, String::from("two")); + /// assert_eq!(readonly_ref.c, 3.0); /// # /// # drop(linked_test); /// # @@ -207,6 +214,7 @@ where /// # use std::fmt; /// # use std::string::ToString; /// # use std::path::PathBuf; + /// # use tokio; /// # /// # #[derive(Deserialize, Serialize)] /// # struct Test { @@ -215,21 +223,28 @@ where /// # pub c: f64 /// # } /// # - /// # fn main() -> Result<(), Error> { + /// # #[tokio::main] + /// # async fn main() -> Result<(), Error> { /// let test = Test { /// a: 1, /// b: String::from(""), /// c: 0.0 /// }; /// - /// let mut linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Bincode) + /// let mut linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Bincode).await /// .expect("Unable to create file linked object"); /// - /// assert_eq!(linked_test.readonly().a, 1); + /// { + /// let readonly = linked_test.readonly(); + /// let readonly_ref = readonly.read().await; + /// assert_eq!(readonly_ref.a, 1); + /// } /// - /// linked_test.mutate(|t| t.a = 2)?; + /// linked_test.mutate(|t| t.a = 2).await?; /// - /// assert_eq!(linked_test.readonly().a, 2); + /// let readonly = linked_test.readonly(); + /// let readonly_ref = readonly.read().await; + /// assert_eq!(readonly_ref.a, 2); /// # /// # drop(linked_test); /// # @@ -262,6 +277,7 @@ where /// # use std::fmt; /// # use std::string::ToString; /// # use std::path::PathBuf; + /// # use tokio; /// # /// # #[derive(Deserialize, Serialize)] /// # struct Test { @@ -270,25 +286,30 @@ where /// # pub c: f64 /// # } /// # - /// # fn main() -> Result<(), Error> { + /// # #[tokio::main] + /// # async fn main() -> Result<(), Error> { /// let test = Test { /// a: 1, /// b: String::from(""), /// c: 0.0 /// }; /// - /// let mut linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Bincode) + /// let mut linked_test = FileLinked::new(test, &PathBuf::from("./temp"), DataFormat::Bincode).await /// .expect("Unable to create file linked object"); /// - /// assert_eq!(linked_test.readonly().a, 1); + /// let readonly = linked_test.readonly(); + /// let readonly_ref = readonly.read().await; + /// assert_eq!(readonly_ref.a, 1); /// /// linked_test.replace(Test { /// a: 2, /// b: String::from(""), /// c: 0.0 - /// })?; + /// }).await?; /// - /// assert_eq!(linked_test.readonly().a, 2); + /// let readonly = linked_test.readonly(); + /// let readonly_ref = readonly.read().await; + /// assert_eq!(readonly_ref.a, 2); /// # /// # drop(linked_test); /// # @@ -343,6 +364,7 @@ where /// # use std::fs::OpenOptions; /// # use std::io::Write; /// # use std::path::PathBuf; + /// # use tokio; /// # /// # #[derive(Deserialize, Serialize)] /// # struct Test { @@ -351,7 +373,8 @@ where /// # pub c: f64 /// # } /// # - /// # fn main() -> Result<(), Error> { + /// # #[tokio::main] + /// # async fn main() -> Result<(), Error> { /// let test = Test { /// a: 1, /// b: String::from("2"), @@ -371,9 +394,11 @@ where /// let mut linked_test = FileLinked::::from_file(&path, DataFormat::Bincode) /// .expect("Unable to create file linked object"); /// - /// assert_eq!(linked_test.readonly().a, test.a); - /// assert_eq!(linked_test.readonly().b, test.b); - /// assert_eq!(linked_test.readonly().c, test.c); + /// let readonly = linked_test.readonly(); + /// let readonly_ref = readonly.read().await; + /// assert_eq!(readonly_ref.a, test.a); + /// assert_eq!(readonly_ref.b, test.b); + /// assert_eq!(readonly_ref.c, test.c); /// # /// # drop(linked_test); /// # diff --git a/gemla/src/bin/bin.rs b/gemla/src/bin/bin.rs index a6f8b38..e41e417 100644 --- a/gemla/src/bin/bin.rs +++ b/gemla/src/bin/bin.rs @@ -3,18 +3,18 @@ extern crate gemla; #[macro_use] extern crate log; -mod test_state; mod fighter_nn; +mod test_state; +use anyhow::Result; +use clap::Parser; +use fighter_nn::FighterNN; use file_linked::constants::data_format::DataFormat; use gemla::{ core::{Gemla, GemlaConfig}, error::log_error, }; use std::{path::PathBuf, time::Instant}; -use fighter_nn::FighterNN; -use clap::Parser; -use anyhow::Result; // const NUM_THREADS: usize = 2; @@ -39,19 +39,22 @@ fn main() -> Result<()> { // Manually configure the Tokio runtime let runtime: Result<()> = tokio::runtime::Builder::new_multi_thread() - .worker_threads(num_cpus::get()) - // .worker_threads(NUM_THREADS) + .worker_threads(num_cpus::get()) + // .worker_threads(NUM_THREADS) .build()? .block_on(async { let args = Args::parse(); // Assuming Args::parse() doesn't need to be async - let mut gemla = log_error(Gemla::::new( - &PathBuf::from(args.file), - GemlaConfig { - generations_per_height: 5, - overwrite: false, - }, - DataFormat::Json, - ).await)?; + let mut gemla = log_error( + Gemla::::new( + &PathBuf::from(args.file), + GemlaConfig { + generations_per_height: 5, + overwrite: false, + }, + DataFormat::Json, + ) + .await, + )?; // let gemla_arc = Arc::new(gemla); @@ -59,7 +62,8 @@ fn main() -> Result<()> { // If `gemla::simulate` needs to run sequentially, simply call it in sequence without spawning new tasks // Example placeholder loop to continuously run simulate - loop { // Arbitrary loop count for demonstration + loop { + // Arbitrary loop count for demonstration gemla.simulate(1).await?; } }); @@ -68,4 +72,4 @@ fn main() -> Result<()> { info!("Finished in {:?}", now.elapsed()); Ok(()) -} \ No newline at end of file +} diff --git a/gemla/src/bin/fighter_nn/fighter_context.rs b/gemla/src/bin/fighter_nn/fighter_context.rs index a87706b..c631627 100644 --- a/gemla/src/bin/fighter_nn/fighter_context.rs +++ b/gemla/src/bin/fighter_nn/fighter_context.rs @@ -5,7 +5,6 @@ use tokio::sync::Semaphore; const SHARED_SEMAPHORE_CONCURRENCY_LIMIT: usize = 50; - #[derive(Debug, Clone)] pub struct FighterContext { pub shared_semaphore: Arc, @@ -19,7 +18,6 @@ impl Default for FighterContext { } } - // Custom serialization to just output the concurrency limit. impl Serialize for FighterContext { fn serialize(&self, serializer: S) -> Result @@ -45,4 +43,4 @@ impl<'de> Deserialize<'de> for FighterContext { shared_semaphore: Arc::new(Semaphore::new(concurrency_limit as usize)), }) } -} \ No newline at end of file +} diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index d4683b9..ee99b8c 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -1,20 +1,29 @@ extern crate fann; -pub mod neural_network_utility; pub mod fighter_context; +pub mod neural_network_utility; -use std::{cmp::max, collections::{HashSet, VecDeque}, fs::{self, File}, io::{self, BufRead, BufReader}, ops::Range, panic::{catch_unwind, AssertUnwindSafe}, path::{Path, PathBuf}, sync::{Arc, Mutex}, time::Duration}; +use anyhow::Context; +use async_trait::async_trait; use fann::{ActivationFunc, Fann}; -use futures::{executor::block_on, future::{join, join_all, select_all}, stream::FuturesUnordered, FutureExt, StreamExt}; -use gemla::{core::genetic_node::{GeneticNode, GeneticNodeContext}, error::Error}; +use futures::future::join_all; +use gemla::{ + core::genetic_node::{GeneticNode, GeneticNodeContext}, + error::Error, +}; use lerp::Lerp; use rand::prelude::*; use serde::{Deserialize, Serialize}; -use anyhow::Context; -use tokio::{process::Command, sync::{mpsc, Semaphore}, task, time::{sleep, timeout, Sleep}}; -use uuid::Uuid; use std::collections::HashMap; -use async_trait::async_trait; +use std::{ + cmp::max, + fs::{self, File}, + io::{self, BufRead, BufReader}, + ops::Range, + path::{Path, PathBuf}, +}; +use tokio::process::Command; +use uuid::Uuid; use self::neural_network_utility::{crossbreed, major_mutation}; @@ -34,13 +43,14 @@ const NEURAL_NETWORK_CROSSBREED_SEGMENTS_MAX: usize = 20; const SIMULATION_ROUNDS: usize = 5; const SURVIVAL_RATE: f32 = 0.5; -const GAME_EXECUTABLE_PATH: &str = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Package\\Windows\\AI_Fight_Sim.exe"; +const GAME_EXECUTABLE_PATH: &str = + "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Package\\Windows\\AI_Fight_Sim.exe"; // Here is the folder structure for the FighterNN: // base_dir/fighter_nn_{fighter_id}/{generation}/{fighter_id}_fighter_nn_{nn_id}.net // A neural network that utilizes the fann library to save and read nn's from files -// FighterNN contains a list of file locations for the nn's stored, all of which are stored under the same folder which is also contained. +// FighterNN contains a list of file locations for the nn's stored, all of which are stored under the same folder which is also contained. // there is no training happening to the neural networks // the neural networks are only used to simulate the nn's and to save and read the nn's from files // Filenames are stored in the format of "{fighter_id}_fighter_nn_{generation}.net". @@ -70,37 +80,48 @@ impl GeneticNode for FighterNN { // Check for the highest number of the folder name and increment it by 1 async fn initialize(context: GeneticNodeContext) -> Result, Error> { let base_path = PathBuf::from(BASE_DIR); - + let folder = base_path.join(format!("fighter_nn_{:06}", context.id)); // Ensures directory is created if it doesn't exist and does nothing if it exists fs::create_dir_all(&folder) .with_context(|| format!("Failed to create or access the folder: {:?}", folder))?; - + //Create a new directory for the first generation, using create_dir_all to avoid errors if it already exists let gen_folder = folder.join("0"); - fs::create_dir_all(&gen_folder) - .with_context(|| format!("Failed to create or access the generation folder: {:?}", gen_folder))?; + fs::create_dir_all(&gen_folder).with_context(|| { + format!( + "Failed to create or access the generation folder: {:?}", + gen_folder + ) + })?; let mut nn_shapes = HashMap::new(); - let weight_initialization_range = thread_rng().gen_range(NEURAL_NETWORK_INITIAL_WEIGHT_MIN..0.0)..thread_rng().gen_range(0.0..=NEURAL_NETWORK_INITIAL_WEIGHT_MAX); - + let weight_initialization_range = thread_rng() + .gen_range(NEURAL_NETWORK_INITIAL_WEIGHT_MIN..0.0) + ..thread_rng().gen_range(0.0..=NEURAL_NETWORK_INITIAL_WEIGHT_MAX); + // Create the first generation in this folder for i in 0..POPULATION { // Filenames are stored in the format of "xxxxxx_fighter_nn_0.net", "xxxxxx_fighter_nn_1.net", etc. Where xxxxxx is the folder name let nn = gen_folder.join(format!("{:06}_fighter_nn_{}.net", context.id, i)); // Randomly generate a neural network shape based on constants - let hidden_layers = thread_rng().gen_range(NEURAL_NETWORK_HIDDEN_LAYERS_MIN..NEURAL_NETWORK_HIDDEN_LAYERS_MAX); + let hidden_layers = thread_rng() + .gen_range(NEURAL_NETWORK_HIDDEN_LAYERS_MIN..NEURAL_NETWORK_HIDDEN_LAYERS_MAX); let mut nn_shape = vec![NEURAL_NETWORK_INPUTS as u32]; for _ in 0..hidden_layers { - nn_shape.push(thread_rng().gen_range(NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN..NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MAX) as u32); + nn_shape.push(thread_rng().gen_range( + NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN..NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MAX, + ) as u32); } nn_shape.push(NEURAL_NETWORK_OUTPUTS as u32); nn_shapes.insert(i as u64, nn_shape.clone()); - let mut fann = Fann::new(nn_shape.as_slice()) - .with_context(|| "Failed to create nn")?; - fann.randomize_weights(weight_initialization_range.start, weight_initialization_range.end); + let mut fann = Fann::new(nn_shape.as_slice()).with_context(|| "Failed to create nn")?; + fann.randomize_weights( + weight_initialization_range.start, + weight_initialization_range.end, + ); fann.set_activation_func_hidden(ActivationFunc::SigmoidSymmetric); fann.set_activation_func_output(ActivationFunc::SigmoidSymmetric); // This will overwrite any existing file with the same name @@ -108,13 +129,15 @@ impl GeneticNode for FighterNN { .with_context(|| format!("Failed to save nn at {:?}", nn))?; } - let mut crossbreed_segments = thread_rng().gen_range(NEURAL_NETWORK_CROSSBREED_SEGMENTS_MIN..NEURAL_NETWORK_CROSSBREED_SEGMENTS_MAX); + let mut crossbreed_segments = thread_rng().gen_range( + NEURAL_NETWORK_CROSSBREED_SEGMENTS_MIN..NEURAL_NETWORK_CROSSBREED_SEGMENTS_MAX, + ); if crossbreed_segments % 2 == 0 { crossbreed_segments += 1; } let mutation_weight_amplitude = thread_rng().gen_range(0.0..1.0); - + Ok(Box::new(FighterNN { id: context.id, folder, @@ -141,9 +164,12 @@ impl GeneticNode for FighterNN { let semaphore_clone = context.gemla_context.shared_semaphore.clone(); let task = async move { - let nn = self_clone.folder.join(format!("{}", self_clone.generation)).join(self_clone.get_individual_id(i as u64)); + let nn = self_clone + .folder + .join(format!("{}", self_clone.generation)) + .join(self_clone.get_individual_id(i as u64)); let mut simulations = Vec::new(); - + // Using the same original nn, repeat the simulation with 5 random nn's from the current generation concurrently for _ in 0..SIMULATION_ROUNDS { let random_nn_index = thread_rng().gen_range(0..self_clone.population_size); @@ -151,11 +177,16 @@ impl GeneticNode for FighterNN { let generation = self_clone.generation; let semaphore_clone = semaphore_clone.clone(); - let random_nn = folder.join(format!("{}", generation)).join(self_clone.get_individual_id(random_nn_index as u64)); + let random_nn = folder + .join(format!("{}", generation)) + .join(self_clone.get_individual_id(random_nn_index as u64)); let nn_clone = nn.clone(); // Clone the path to use in the async block - + let future = async move { - let permit = semaphore_clone.acquire_owned().await.with_context(|| "Failed to acquire semaphore permit")?; + let permit = semaphore_clone + .acquire_owned() + .await + .with_context(|| "Failed to acquire semaphore permit")?; let (score, _) = run_1v1_simulation(&nn_clone, &random_nn).await?; @@ -163,13 +194,14 @@ impl GeneticNode for FighterNN { Ok(score) }; - + simulations.push(future); } - + // Wait for all simulation rounds to complete - let results: Result, Error> = join_all(simulations).await.into_iter().collect(); - + let results: Result, Error> = + join_all(simulations).await.into_iter().collect(); + let score = match results { Ok(scores) => scores.into_iter().sum::() / SIMULATION_ROUNDS as f32, Err(e) => return Err(e), // Return the error if results collection failed @@ -188,34 +220,46 @@ impl GeneticNode for FighterNN { Ok((index, score)) => { // Update the original `self` object with the score. self.scores[self.generation as usize].insert(index as u64, score); - }, + } Err(e) => { // Handle task panic or execution error - return Err(Error::Other(anyhow::anyhow!(format!("Task failed: {:?}", e)))); - }, + return Err(Error::Other(anyhow::anyhow!(format!( + "Task failed: {:?}", + e + )))); + } } } - + Ok(()) } - async fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { let survivor_count = (self.population_size as f32 * SURVIVAL_RATE) as usize; // Create the new generation folder let new_gen_folder = self.folder.join(format!("{}", self.generation + 1)); - fs::create_dir_all(&new_gen_folder).with_context(|| format!("Failed to create or access new generation folder: {:?}", new_gen_folder))?; + fs::create_dir_all(&new_gen_folder).with_context(|| { + format!( + "Failed to create or access new generation folder: {:?}", + new_gen_folder + ) + })?; // Remove the 5 nn's with the lowest scores let mut sorted_scores: Vec<_> = self.scores[self.generation as usize].iter().collect(); sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); - let to_keep = sorted_scores[survivor_count..].iter().map(|(k, _)| *k).collect::>(); + let to_keep = sorted_scores[survivor_count..] + .iter() + .map(|(k, _)| *k) + .collect::>(); // Save the remaining 5 nn's to the new generation folder - for i in 0..survivor_count { - let nn_id = to_keep[i]; - let nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, nn_id)); + for (i, nn_id) in to_keep.iter().enumerate().take(survivor_count) { + let nn = self + .folder + .join(format!("{}", self.generation)) + .join(format!("{:06}_fighter_nn_{}.net", self.id, nn_id)); let new_nn = new_gen_folder.join(format!("{:06}_fighter_nn_{}.net", self.id, i)); fs::copy(&nn, &new_nn)?; } @@ -223,16 +267,25 @@ impl GeneticNode for FighterNN { // Take the remaining 5 nn's and create 5 new nn's by the following: for i in 0..survivor_count { let nn_id = to_keep[i]; - let nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, nn_id)); - let fann = Fann::from_file(&nn) - .with_context(|| format!("Failed to load nn"))?; + let nn = self + .folder + .join(format!("{}", self.generation)) + .join(format!("{:06}_fighter_nn_{}.net", self.id, nn_id)); + let fann = Fann::from_file(&nn).with_context(|| "Failed to load nn")?; // Load another nn from the current generation and cross breed it with the current nn - let cross_nn = self.folder.join(format!("{}", self.generation)).join(format!("{:06}_fighter_nn_{}.net", self.id, to_keep[thread_rng().gen_range(0..survivor_count)])); - let cross_fann = Fann::from_file(&cross_nn) - .with_context(|| format!("Failed to load cross nn"))?; + let cross_nn = self + .folder + .join(format!("{}", self.generation)) + .join(format!( + "{:06}_fighter_nn_{}.net", + self.id, + to_keep[thread_rng().gen_range(0..survivor_count)] + )); + let cross_fann = + Fann::from_file(&cross_nn).with_context(|| "Failed to load cross nn")?; - let mut new_fann = crossbreed(&self, &fann, &cross_fann, self.crossbreed_segments)?; + let mut new_fann = crossbreed(self, &fann, &cross_fann, self.crossbreed_segments)?; // For each weight in the 5 new nn's there is a 20% chance of a minor mutation (a random number between -0.1 and 0.1 is added to the weight) // And a 5% chance of a major mutation a new neuron is randomly added to a hidden layer @@ -246,15 +299,20 @@ impl GeneticNode for FighterNN { } new_fann.set_connections(&connections); - + if thread_rng().gen_range(0.0..1.0) < self.major_mutation_rate { new_fann = major_mutation(&new_fann, self.weight_initialization_range.clone())?; } // Save the new nn's to the new generation folder - let new_nn = new_gen_folder.join(format!("{:06}_fighter_nn_{}.net", self.id, i + survivor_count)); - new_fann.save(&new_nn) - .with_context(|| format!("Failed to save nn"))?; + let new_nn = new_gen_folder.join(format!( + "{:06}_fighter_nn_{}.net", + self.id, + i + survivor_count + )); + new_fann + .save(&new_nn) + .with_context(|| "Failed to save nn")?; } self.generation += 1; @@ -263,18 +321,28 @@ impl GeneticNode for FighterNN { Ok(()) } - async fn merge(left: &FighterNN, right: &FighterNN, id: &Uuid, gemla_context: Self::Context) -> Result, Error> { + async fn merge( + left: &FighterNN, + right: &FighterNN, + id: &Uuid, + gemla_context: Self::Context, + ) -> Result, Error> { let base_path = PathBuf::from(BASE_DIR); let folder = base_path.join(format!("fighter_nn_{:06}", id)); - + // Ensure the folder exists, including the generation subfolder. - fs::create_dir_all(&folder.join("0")) + fs::create_dir_all(folder.join("0")) .with_context(|| format!("Failed to create directory {:?}", folder.join("0")))?; let get_highest_scores = |fighter: &FighterNN| -> Vec<(u64, f32)> { - let mut sorted_scores: Vec<_> = fighter.scores[fighter.generation as usize].iter().collect(); + let mut sorted_scores: Vec<_> = + fighter.scores[fighter.generation as usize].iter().collect(); sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); - sorted_scores.iter().take(fighter.population_size / 2).map(|(k, v)| (**k, **v)).collect() + sorted_scores + .iter() + .take(fighter.population_size / 2) + .map(|(k, v)| (**k, **v)) + .collect() }; let left_scores = get_highest_scores(left); @@ -285,18 +353,28 @@ impl GeneticNode for FighterNN { let mut simulations = Vec::new(); - for _ in 0..max(left.population_size, right.population_size)*SIMULATION_ROUNDS { + for _ in 0..max(left.population_size, right.population_size) * SIMULATION_ROUNDS { let left_nn_id = left_scores[thread_rng().gen_range(0..left_scores.len())].0; let right_nn_id = right_scores[thread_rng().gen_range(0..right_scores.len())].0; - let left_nn_path = left.folder.join(left.generation.to_string()).join(left.get_individual_id(left_nn_id)); - let right_nn_path = right.folder.join(right.generation.to_string()).join(right.get_individual_id(right_nn_id)); + let left_nn_path = left + .folder + .join(left.generation.to_string()) + .join(left.get_individual_id(left_nn_id)); + let right_nn_path = right + .folder + .join(right.generation.to_string()) + .join(right.get_individual_id(right_nn_id)); let semaphore_clone = gemla_context.shared_semaphore.clone(); let future = async move { - let permit = semaphore_clone.acquire_owned().await.with_context(|| "Failed to acquire semaphore permit")?; + let permit = semaphore_clone + .acquire_owned() + .await + .with_context(|| "Failed to acquire semaphore permit")?; - let (left_score, right_score) = run_1v1_simulation(&left_nn_path, &right_nn_path).await?; + let (left_score, right_score) = + run_1v1_simulation(&left_nn_path, &right_nn_path).await?; drop(permit); @@ -306,7 +384,8 @@ impl GeneticNode for FighterNN { simulations.push(future); } - let results: Result, Error> = join_all(simulations).await.into_iter().collect(); + let results: Result, Error> = + join_all(simulations).await.into_iter().collect(); let scores = results?; let total_left_score = scores.iter().map(|(l, _)| l).sum::(); @@ -320,53 +399,93 @@ impl GeneticNode for FighterNN { let lerp_amount = 1.0 / (1.0 + (-score_difference).exp()); let mut nn_shapes = HashMap::new(); - - // Function to copy NNs from a source FighterNN to the new folder. - let mut copy_nns = |source: &FighterNN, folder: &PathBuf, id: &Uuid, start_idx: usize| -> Result<(), Error> { - let mut sorted_scores: Vec<_> = source.scores[source.generation as usize].iter().collect(); - sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); - let remaining = sorted_scores[(source.population_size / 2)..].iter().map(|(k, _)| *k).collect::>(); - - for (i, nn_id) in remaining.into_iter().enumerate() { - let nn_path = source.folder.join(source.generation.to_string()).join(format!("{:06}_fighter_nn_{}.net", source.id, nn_id)); - let new_nn_path = folder.join("0").join(format!("{:06}_fighter_nn_{}.net", id, start_idx + i)); - fs::copy(&nn_path, &new_nn_path) - .with_context(|| format!("Failed to copy nn from {:?} to {:?}", nn_path, new_nn_path))?; - nn_shapes.insert((start_idx + i) as u64, source.nn_shapes.get(&nn_id).unwrap().clone()); + // Function to copy NNs from a source FighterNN to the new folder. + let mut copy_nns = |source: &FighterNN, + folder: &PathBuf, + id: &Uuid, + start_idx: usize| + -> Result<(), Error> { + let mut sorted_scores: Vec<_> = + source.scores[source.generation as usize].iter().collect(); + sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); + let remaining = sorted_scores[(source.population_size / 2)..] + .iter() + .map(|(k, _)| *k) + .collect::>(); + + for (i, nn_id) in remaining.into_iter().enumerate() { + let nn_path = source + .folder + .join(source.generation.to_string()) + .join(format!("{:06}_fighter_nn_{}.net", source.id, nn_id)); + let new_nn_path = + folder + .join("0") + .join(format!("{:06}_fighter_nn_{}.net", id, start_idx + i)); + fs::copy(&nn_path, &new_nn_path).with_context(|| { + format!("Failed to copy nn from {:?} to {:?}", nn_path, new_nn_path) + })?; + + nn_shapes.insert( + (start_idx + i) as u64, + source.nn_shapes.get(nn_id).unwrap().clone(), + ); } Ok(()) }; - + // Copy the top half of NNs from each parent to the new folder. copy_nns(left, &folder, id, 0)?; - copy_nns(right, &folder, id, left.population_size as usize / 2)?; + copy_nns(right, &folder, id, left.population_size / 2)?; debug!("nn_shapes: {:?}", nn_shapes); // Lerp the mutation rates and weight ranges - let crossbreed_segments = (left.crossbreed_segments as f32).lerp(right.crossbreed_segments as f32, lerp_amount) as usize; + let crossbreed_segments = (left.crossbreed_segments as f32) + .lerp(right.crossbreed_segments as f32, lerp_amount) + as usize; - let weight_initialization_range_start = left.weight_initialization_range.start.lerp(right.weight_initialization_range.start, lerp_amount); - let weight_initialization_range_end = left.weight_initialization_range.end.lerp(right.weight_initialization_range.end, lerp_amount); + let weight_initialization_range_start = left + .weight_initialization_range + .start + .lerp(right.weight_initialization_range.start, lerp_amount); + let weight_initialization_range_end = left + .weight_initialization_range + .end + .lerp(right.weight_initialization_range.end, lerp_amount); // Have to ensure the range is valid - let weight_initialization_range = if weight_initialization_range_start < weight_initialization_range_end { - weight_initialization_range_start..weight_initialization_range_end - } else { - weight_initialization_range_end..weight_initialization_range_start - }; + let weight_initialization_range = + if weight_initialization_range_start < weight_initialization_range_end { + weight_initialization_range_start..weight_initialization_range_end + } else { + weight_initialization_range_end..weight_initialization_range_start + }; - debug!("weight_initialization_range: {:?}", weight_initialization_range); + debug!( + "weight_initialization_range: {:?}", + weight_initialization_range + ); - let minor_mutation_rate = left.minor_mutation_rate.lerp(right.minor_mutation_rate, lerp_amount); - let major_mutation_rate = left.major_mutation_rate.lerp(right.major_mutation_rate, lerp_amount); + let minor_mutation_rate = left + .minor_mutation_rate + .lerp(right.minor_mutation_rate, lerp_amount); + let major_mutation_rate = left + .major_mutation_rate + .lerp(right.major_mutation_rate, lerp_amount); debug!("minor_mutation_rate: {}", minor_mutation_rate); debug!("major_mutation_rate: {}", major_mutation_rate); - - let mutation_weight_range_start = left.mutation_weight_range.start.lerp(right.mutation_weight_range.start, lerp_amount); - let mutation_weight_range_end = left.mutation_weight_range.end.lerp(right.mutation_weight_range.end, lerp_amount); + + let mutation_weight_range_start = left + .mutation_weight_range + .start + .lerp(right.mutation_weight_range.start, lerp_amount); + let mutation_weight_range_end = left + .mutation_weight_range + .end + .lerp(right.mutation_weight_range.end, lerp_amount); // Have to ensure the range is valid let mutation_weight_range = if mutation_weight_range_start < mutation_weight_range_end { mutation_weight_range_start..mutation_weight_range_end @@ -398,7 +517,7 @@ impl FighterNN { } } -async fn run_1v1_simulation(nn_path_1: &PathBuf, nn_path_2: &PathBuf) -> Result<(f32, f32), Error> { +async fn run_1v1_simulation(nn_path_1: &Path, nn_path_2: &Path) -> Result<(f32, f32), Error> { // Construct the score file path let base_folder = nn_path_1.parent().unwrap(); let nn_1_id = nn_path_1.file_stem().unwrap().to_str().unwrap(); @@ -407,14 +526,18 @@ async fn run_1v1_simulation(nn_path_1: &PathBuf, nn_path_2: &PathBuf) -> Result< // Check if score file already exists before running the simulation if score_file.exists() { - let round_score = read_score_from_file(&score_file, &nn_1_id).await + let round_score = read_score_from_file(&score_file, nn_1_id) + .await .with_context(|| format!("Failed to read score from file: {:?}", score_file))?; - let opposing_score = read_score_from_file(&score_file, &nn_2_id).await + let opposing_score = read_score_from_file(&score_file, nn_2_id) + .await .with_context(|| format!("Failed to read score from file: {:?}", score_file))?; - debug!("{} scored {}, while {} scored {}", nn_1_id, round_score, nn_2_id, opposing_score); - + debug!( + "{} scored {}, while {} scored {}", + nn_1_id, round_score, nn_2_id, opposing_score + ); return Ok((round_score, opposing_score)); } @@ -422,13 +545,22 @@ async fn run_1v1_simulation(nn_path_1: &PathBuf, nn_path_2: &PathBuf) -> Result< // Check if the opposite round score has been determined let opposite_score_file = base_folder.join(format!("{}_vs_{}.txt", nn_2_id, nn_1_id)); if opposite_score_file.exists() { - let round_score = read_score_from_file(&opposite_score_file, &nn_1_id).await - .with_context(|| format!("Failed to read score from file: {:?}", opposite_score_file))?; + let round_score = read_score_from_file(&opposite_score_file, nn_1_id) + .await + .with_context(|| { + format!("Failed to read score from file: {:?}", opposite_score_file) + })?; - let opposing_score = read_score_from_file(&opposite_score_file, &nn_2_id).await - .with_context(|| format!("Failed to read score from file: {:?}", opposite_score_file))?; + let opposing_score = read_score_from_file(&opposite_score_file, nn_2_id) + .await + .with_context(|| { + format!("Failed to read score from file: {:?}", opposite_score_file) + })?; - debug!("{} scored {}, while {} scored {}", nn_1_id, round_score, nn_2_id, opposing_score); + debug!( + "{} scored {}, while {} scored {}", + nn_1_id, round_score, nn_2_id, opposing_score + ); return Ok((round_score, opposing_score)); } @@ -459,20 +591,29 @@ async fn run_1v1_simulation(nn_path_1: &PathBuf, nn_path_2: &PathBuf) -> Result< .expect("Failed to execute game") }; - trace!("Simulation completed for {} vs {}: {}", nn_1_id, nn_2_id, score_file.exists()); + trace!( + "Simulation completed for {} vs {}: {}", + nn_1_id, + nn_2_id, + score_file.exists() + ); // Read the score from the file if score_file.exists() { - let round_score = read_score_from_file(&score_file, &nn_1_id).await + let round_score = read_score_from_file(&score_file, nn_1_id) + .await .with_context(|| format!("Failed to read score from file: {:?}", score_file))?; - let opposing_score = read_score_from_file(&score_file, &nn_2_id).await + let opposing_score = read_score_from_file(&score_file, nn_2_id) + .await .with_context(|| format!("Failed to read score from file: {:?}", score_file))?; - debug!("{} scored {}, while {} scored {}", nn_1_id, round_score, nn_2_id, opposing_score); + debug!( + "{} scored {}, while {} scored {}", + nn_1_id, round_score, nn_2_id, opposing_score + ); - - return Ok((round_score, opposing_score)) + Ok((round_score, opposing_score)) } else { warn!("Score file not found: {:?}", score_file); Ok((0.0, 0.0)) @@ -492,7 +633,10 @@ async fn read_score_from_file(file_path: &Path, nn_id: &str) -> Result = line.split(':').collect(); if parts.len() == 2 { - return parts[1].trim().parse::().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)); + return parts[1] + .trim() + .parse::() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)); } } } @@ -501,17 +645,22 @@ async fn read_score_from_file(file_path: &Path, nn_id: &str) -> Result { - if attempts >= 5 { // Attempt 5 times before giving up. + } + Err(e) + if e.kind() == io::ErrorKind::WouldBlock + || e.kind() == io::ErrorKind::PermissionDenied + || e.kind() == io::ErrorKind::Other => + { + if attempts >= 5 { + // Attempt 5 times before giving up. return Err(e); } attempts += 1; // wait 1 second to ensure the file is written tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; - }, + } Err(e) => return Err(e), } } -} \ No newline at end of file +} diff --git a/gemla/src/bin/fighter_nn/neural_network_utility.rs b/gemla/src/bin/fighter_nn/neural_network_utility.rs index a3e5bc1..04a0992 100644 --- a/gemla/src/bin/fighter_nn/neural_network_utility.rs +++ b/gemla/src/bin/fighter_nn/neural_network_utility.rs @@ -3,16 +3,24 @@ use std::{cmp::min, collections::HashMap, ops::Range}; use anyhow::Context; use fann::{ActivationFunc, Fann}; use gemla::error::Error; -use rand::{distributions::{Distribution, Uniform}, seq::IteratorRandom, thread_rng, Rng}; +use rand::{ + distributions::{Distribution, Uniform}, + seq::IteratorRandom, + thread_rng, Rng, +}; use super::{FighterNN, NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN}; - /// Crossbreeds two neural networks of different shapes by finding cut points, and swapping neurons between the two networks. /// Algorithm tries to ensure similar functionality is maintained between the two networks. /// It does this by preserving connections between the same neurons from the original to the new network, and if a connection cannot be found /// it will create a new connection with a random weight. -pub fn crossbreed(fighter_nn: &FighterNN, primary: &Fann, secondary: &Fann, crossbreed_segments: usize) -> Result { +pub fn crossbreed( + fighter_nn: &FighterNN, + primary: &Fann, + secondary: &Fann, + crossbreed_segments: usize, +) -> Result { // First we need to get the shape of the networks and transform this into a format that is easier to work with // We want a list of every neuron id, and the layer it is in let primary_shape = primary.get_layer_sizes(); @@ -21,25 +29,27 @@ pub fn crossbreed(fighter_nn: &FighterNN, primary: &Fann, secondary: &Fann, cros let secondary_neurons = generate_neuron_datastructure(&secondary_shape); let segments = generate_segments(primary_shape, secondary_shape, crossbreed_segments); - + let new_neurons = crossbreed_neuron_arrays(segments, primary_neurons, secondary_neurons); - + // Now we need to create the new network with the shape we've determined let mut new_shape = vec![]; for (_, _, layer, _) in new_neurons.iter() { // Check if new_shape has an entry for layer in it - if new_shape.len() <= *layer as usize { + if new_shape.len() <= *layer { new_shape.push(1); - } - else { - new_shape[*layer as usize] += 1; + } else { + new_shape[*layer] += 1; } } - let mut new_fann = Fann::new(new_shape.as_slice()) - .with_context(|| "Failed to create new fann")?; + let mut new_fann = + Fann::new(new_shape.as_slice()).with_context(|| "Failed to create new fann")?; // We need to randomize the weights to a small value - new_fann.randomize_weights(fighter_nn.weight_initialization_range.start, fighter_nn.weight_initialization_range.end); + new_fann.randomize_weights( + fighter_nn.weight_initialization_range.start, + fighter_nn.weight_initialization_range.end, + ); new_fann.set_activation_func_hidden(ActivationFunc::SigmoidSymmetric); new_fann.set_activation_func_output(ActivationFunc::SigmoidSymmetric); @@ -48,11 +58,18 @@ pub fn crossbreed(fighter_nn: &FighterNN, primary: &Fann, secondary: &Fann, cros Ok(new_fann) } -pub fn generate_segments(primary_shape: Vec, secondary_shape: Vec, crossbreed_segments: usize) -> Vec<(u32, u32)> { +pub fn generate_segments( + primary_shape: Vec, + secondary_shape: Vec, + crossbreed_segments: usize, +) -> Vec<(u32, u32)> { // Now we need to find the cut points for the crossbreed let start = primary_shape[0] + 1; // Start at the first hidden layer - let end = min(primary_shape.iter().sum::() - primary_shape.last().unwrap(), secondary_shape.iter().sum::() - secondary_shape.last().unwrap()); + let end = min( + primary_shape.iter().sum::() - primary_shape.last().unwrap(), + secondary_shape.iter().sum::() - secondary_shape.last().unwrap(), + ); // End at the last hidden layer let segment_distribution = Uniform::from(start..end); // Ensure segments are not too small @@ -77,23 +94,35 @@ pub fn generate_segments(primary_shape: Vec, secondary_shape: Vec, cro segments } -pub fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: Vec, new_neurons: Vec<(u32, bool, usize, u32)>, new_fann: &mut Fann) { +pub fn consolidate_old_connections( + primary: &Fann, + secondary: &Fann, + new_shape: Vec, + new_neurons: Vec<(u32, bool, usize, u32)>, + new_fann: &mut Fann, +) { // Now we need to copy the connections from the original networks to the new network - // We can do this by referencing our connections array, it will contain the original id's of the neurons + // We can do this by referencing our connections array, it will contain the original id's of the neurons // and their new id as well as their layer. We can iterate one layer at a time and copy the connections let primary_shape = primary.get_layer_sizes(); let secondary_shape = secondary.get_layer_sizes(); debug!("Primary shape: {:?}", primary_shape); debug!("Secondary shape: {:?}", secondary_shape); - + // Start by iterating layer by later let primary_connections = primary.get_connections(); let secondary_connections = secondary.get_connections(); for layer in 1..new_shape.len() { // filter out the connections that are in the current layer and previous layer - let current_layer_connections = new_neurons.iter().filter(|(_, _, l, _)| l == &layer).collect::>(); - let previous_layer_connections = new_neurons.iter().filter(|(_, _, l, _)| l == &(layer - 1)).collect::>(); + let current_layer_connections = new_neurons + .iter() + .filter(|(_, _, l, _)| l == &layer) + .collect::>(); + let previous_layer_connections = new_neurons + .iter() + .filter(|(_, _, l, _)| l == &(layer - 1)) + .collect::>(); // Now we need to iterate over the connections in the current layer for (neuron_id, is_primary, _, new_id) in current_layer_connections.iter() { @@ -104,10 +133,26 @@ pub fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: let mut connection; let mut found_in_primary = false; if *is_primary { - connection = primary_connections.iter() - .find(|connection| { - let from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); - let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); + connection = primary_connections.iter().find(|connection| { + let from_neuron = + to_non_bias_network_id(connection.from_neuron, &primary_shape); + let to_neuron = + to_non_bias_network_id(connection.to_neuron, &primary_shape); + + // If both neurons have a Some value + if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { + from_neuron == *previous_neuron_id && to_neuron == *neuron_id + } else { + false + } + }); + + if connection.is_none() { + connection = secondary_connections.iter().find(|connection| { + let from_neuron = + to_non_bias_network_id(connection.from_neuron, &secondary_shape); + let to_neuron = + to_non_bias_network_id(connection.to_neuron, &secondary_shape); // If both neurons have a Some value if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { @@ -116,29 +161,30 @@ pub fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: false } }); - - if let None = connection { - connection = secondary_connections.iter() - .find(|connection| { - let from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); - let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); - - // If both neurons have a Some value - if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { - from_neuron == *previous_neuron_id && to_neuron == *neuron_id - } else { - false - } - }); } else { found_in_primary = true; } - } - else { - connection = secondary_connections.iter() - .find(|connection| { - let from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); - let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + } else { + connection = secondary_connections.iter().find(|connection| { + let from_neuron = + to_non_bias_network_id(connection.from_neuron, &secondary_shape); + let to_neuron = + to_non_bias_network_id(connection.to_neuron, &secondary_shape); + + // If both neurons have a Some value + if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { + from_neuron == *previous_neuron_id && to_neuron == *neuron_id + } else { + false + } + }); + + if connection.is_none() { + connection = primary_connections.iter().find(|connection| { + let from_neuron = + to_non_bias_network_id(connection.from_neuron, &primary_shape); + let to_neuron = + to_non_bias_network_id(connection.to_neuron, &primary_shape); // If both neurons have a Some value if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { @@ -147,20 +193,6 @@ pub fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: false } }); - - if let None = connection { - connection = primary_connections.iter() - .find(|connection| { - let from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); - let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); - - // If both neurons have a Some value - if let (Some(from_neuron), Some(to_neuron)) = (from_neuron, to_neuron) { - from_neuron == *previous_neuron_id && to_neuron == *neuron_id - } else { - false - } - }); } else { found_in_primary = true; } @@ -169,19 +201,29 @@ pub fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: // If the connection exists, we need to add it to the new network if let Some(connection) = connection { if *is_primary { - let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); - let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); + let original_from_neuron = + to_non_bias_network_id(connection.from_neuron, &primary_shape); + let original_to_neuron = + to_non_bias_network_id(connection.to_neuron, &primary_shape); trace!("Primary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", previous_new_id, new_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, previous_neuron_id, neuron_id); } else { - let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); - let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + let original_from_neuron = + to_non_bias_network_id(connection.from_neuron, &secondary_shape); + let original_to_neuron = + to_non_bias_network_id(connection.to_neuron, &secondary_shape); trace!("Secondary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", previous_new_id, new_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, previous_neuron_id, neuron_id); } let translated_from = to_bias_network_id(previous_new_id, &new_shape); let translated_to = to_bias_network_id(new_id, &new_shape); new_fann.set_weight(translated_from, translated_to, connection.weight); } else { - trace!("Connection not found for ({}, {}) -> ({}, {})", previous_new_id, new_id, previous_neuron_id, neuron_id); + trace!( + "Connection not found for ({}, {}) -> ({}, {})", + previous_new_id, + new_id, + previous_neuron_id, + neuron_id + ); } } } @@ -197,34 +239,35 @@ pub fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: let mut found_in_primary = false; if *is_primary { let primary_bias_neuron = get_bias_neuron_for_layer(layer, &primary_shape); - if let Some(primary_bias_neuron) = primary_bias_neuron - { - connection = primary_connections.iter() - .find(|connection| { - let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); + if let Some(primary_bias_neuron) = primary_bias_neuron { + connection = primary_connections.iter().find(|connection| { + let to_neuron = + to_non_bias_network_id(connection.to_neuron, &primary_shape); if let Some(to_neuron) = to_neuron { - connection.from_neuron == primary_bias_neuron && to_neuron == *neuron_id + connection.from_neuron == primary_bias_neuron + && to_neuron == *neuron_id } else { false } }); } - - if let None = connection { - let secondary_bias_neuron = get_bias_neuron_for_layer(layer, &secondary_shape); + if connection.is_none() { + let secondary_bias_neuron = + get_bias_neuron_for_layer(layer, &secondary_shape); if let Some(secondary_bias_neuron) = secondary_bias_neuron { - connection = secondary_connections.iter() - .find(|connection| { - let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + connection = secondary_connections.iter().find(|connection| { + let to_neuron = + to_non_bias_network_id(connection.to_neuron, &secondary_shape); - if let Some(to_neuron) = to_neuron { - connection.from_neuron == secondary_bias_neuron && to_neuron == *neuron_id - } else { - false - } - }); + if let Some(to_neuron) = to_neuron { + connection.from_neuron == secondary_bias_neuron + && to_neuron == *neuron_id + } else { + false + } + }); } } else { found_in_primary = true; @@ -232,31 +275,33 @@ pub fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: } else { let secondary_bias_neuron = get_bias_neuron_for_layer(layer, &secondary_shape); if let Some(secondary_bias_neuron) = secondary_bias_neuron { - connection = secondary_connections.iter() - .find(|connection| { - let to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + connection = secondary_connections.iter().find(|connection| { + let to_neuron = + to_non_bias_network_id(connection.to_neuron, &secondary_shape); + + if let Some(to_neuron) = to_neuron { + connection.from_neuron == secondary_bias_neuron + && to_neuron == *neuron_id + } else { + false + } + }); + } + + if connection.is_none() { + let primary_bias_neuron = get_bias_neuron_for_layer(layer, &primary_shape); + if let Some(primary_bias_neuron) = primary_bias_neuron { + connection = primary_connections.iter().find(|connection| { + let to_neuron = + to_non_bias_network_id(connection.to_neuron, &primary_shape); if let Some(to_neuron) = to_neuron { - connection.from_neuron == secondary_bias_neuron && to_neuron == *neuron_id + connection.from_neuron == primary_bias_neuron + && to_neuron == *neuron_id } else { false } }); - } - - if let None = connection { - let primary_bias_neuron = get_bias_neuron_for_layer(layer, &primary_shape); - if let Some(primary_bias_neuron) = primary_bias_neuron { - connection = primary_connections.iter() - .find(|connection| { - let to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); - - if let Some(to_neuron) = to_neuron { - connection.from_neuron == primary_bias_neuron && to_neuron == *neuron_id - } else { - false - } - }); } } else { found_in_primary = true; @@ -265,24 +310,39 @@ pub fn consolidate_old_connections(primary: &Fann, secondary: &Fann, new_shape: if let Some(connection) = connection { if *is_primary { - let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &primary_shape); - let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &primary_shape); + let original_from_neuron = + to_non_bias_network_id(connection.from_neuron, &primary_shape); + let original_to_neuron = + to_non_bias_network_id(connection.to_neuron, &primary_shape); trace!("Primary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", bias_neuron, translated_neuron_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, bias_neuron, neuron_id); } else { - let original_from_neuron = to_non_bias_network_id(connection.from_neuron, &secondary_shape); - let original_to_neuron = to_non_bias_network_id(connection.to_neuron, &secondary_shape); + let original_from_neuron = + to_non_bias_network_id(connection.from_neuron, &secondary_shape); + let original_to_neuron = + to_non_bias_network_id(connection.to_neuron, &secondary_shape); trace!("Secondary: Adding connection from ({} -> {}) translated to ({:?} -> {:?}) with weight {} for primary:{} [{} -> {}] [{} -> {}]", bias_neuron, translated_neuron_id, original_from_neuron, original_to_neuron, connection.weight, found_in_primary, connection.from_neuron, connection.to_neuron, bias_neuron, neuron_id); } new_fann.set_weight(bias_neuron, translated_neuron_id, connection.weight); } else { - trace!("Connection not found for bias ({}, {}) -> ({}, {}) primary: {}", bias_neuron, neuron_id, bias_neuron, translated_neuron_id, is_primary); + trace!( + "Connection not found for bias ({}, {}) -> ({}, {}) primary: {}", + bias_neuron, + neuron_id, + bias_neuron, + translated_neuron_id, + is_primary + ); } } } } } -pub fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec<(u32, usize)>, secondary_neurons: Vec<(u32, usize)>) -> Vec<(u32, bool, usize, u32)> { +pub fn crossbreed_neuron_arrays( + segments: Vec<(u32, u32)>, + primary_neurons: Vec<(u32, usize)>, + secondary_neurons: Vec<(u32, usize)>, +) -> Vec<(u32, bool, usize, u32)> { // We now need to determine the resulting location of the neurons in the new network. // To do this we need a new structure that keeps track of the following information: // - The neuron id from the original network @@ -306,32 +366,32 @@ pub fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec< new_neurons.push((*neuron_id, is_primary, current_layer, 0)); if is_primary { primary_last_layer = current_layer; - } - else { + } else { secondary_last_layer = current_layer; } - } - else { + } else { break; } } - } - else { - let target_neurons = if is_primary { &primary_neurons } else { &secondary_neurons }; + } else { + let target_neurons = if is_primary { + &primary_neurons + } else { + &secondary_neurons + }; for (neuron_id, layer) in target_neurons.iter() { // Iterate until neuron_id equals the cut_point if neuron_id >= &segment.0 && neuron_id <= &segment.1 { // We need to do something different depending on whether the neuron layer is, lower, higher or equal to the target layer - + // Equal if layer == ¤t_layer { new_neurons.push((*neuron_id, is_primary, current_layer, 0)); if is_primary { primary_last_layer = current_layer; - } - else { + } else { secondary_last_layer = current_layer; } } @@ -340,19 +400,28 @@ pub fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec< // If it's in an earlier layer, add it to the earlier layer // Check if there's a lower id from the same individual in that earlier layer // As long as there isn't a neuron from the other individual in between the lower id and current id, add the id values from the same individual - let earlier_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| l == layer).collect::>(); + let earlier_layer_neurons = new_neurons + .iter() + .filter(|(_, _, l, _)| l == layer) + .collect::>(); // get max id from that layer - let highest_id = earlier_layer_neurons.iter().max_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); + let highest_id = earlier_layer_neurons + .iter() + .max_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); if let Some(highest_id) = highest_id { if highest_id.1 == is_primary { - let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && id < neuron_id && l == layer).collect::>(); + let neurons_to_add = target_neurons + .iter() + .filter(|(id, l)| { + id > &highest_id.0 && id < neuron_id && l == layer + }) + .collect::>(); for (neuron_id, layer) in neurons_to_add { new_neurons.push((*neuron_id, is_primary, *layer, 0)); if is_primary { primary_last_layer = *layer; - } - else { + } else { secondary_last_layer = *layer; } } @@ -363,8 +432,7 @@ pub fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec< if is_primary { primary_last_layer = *layer; - } - else { + } else { secondary_last_layer = *layer; } } @@ -372,18 +440,24 @@ pub fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec< else if layer > ¤t_layer { // If the highest id in the current layer is from the same individual, add anything with a higher id to the current layer before moving to the next layer // First filter new_neurons to look at neurons from the current layer - let current_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| l == ¤t_layer).collect::>(); - let highest_id = current_layer_neurons.iter().max_by_key(|(id, _, _, _)| id); + let current_layer_neurons = new_neurons + .iter() + .filter(|(_, _, l, _)| l == ¤t_layer) + .collect::>(); + let highest_id = + current_layer_neurons.iter().max_by_key(|(id, _, _, _)| id); if let Some(highest_id) = highest_id { if highest_id.1 == is_primary { - let neurons_to_add = target_neurons.iter().filter(|(id, l)| id > &highest_id.0 && *l == layer - 1).collect::>(); + let neurons_to_add = target_neurons + .iter() + .filter(|(id, l)| id > &highest_id.0 && *l == layer - 1) + .collect::>(); for (neuron_id, _) in neurons_to_add { new_neurons.push((*neuron_id, is_primary, current_layer, 0)); if is_primary { primary_last_layer = current_layer; - } - else { + } else { secondary_last_layer = current_layer; } } @@ -395,21 +469,21 @@ pub fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec< // Add the neuron to the new network // Along with any neurons that have a lower id in the future layer - let neurons_to_add = target_neurons.iter().filter(|(id, l)| id <= &neuron_id && l == layer).collect::>(); + let neurons_to_add = target_neurons + .iter() + .filter(|(id, l)| id <= neuron_id && l == layer) + .collect::>(); for (neuron_id, _) in neurons_to_add { new_neurons.push((*neuron_id, is_primary, current_layer, 0)); if is_primary { primary_last_layer = current_layer; - } - else { + } else { secondary_last_layer = current_layer; } } } - - } - else if neuron_id >= &segment.1 { + } else if neuron_id >= &segment.1 { break; } } @@ -420,7 +494,11 @@ pub fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec< } // For the last segment, copy the remaining neurons - let target_neurons = if is_primary { &primary_neurons } else { &secondary_neurons }; + let target_neurons = if is_primary { + &primary_neurons + } else { + &secondary_neurons + }; // Get output layer number let output_layer = target_neurons.iter().max_by_key(|(_, l)| l).unwrap().1; @@ -436,17 +514,28 @@ pub fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec< new_neurons.push((*neuron_id, is_primary, current_layer, 0)); } break; - } - else if *neuron_id == &segments.last().unwrap().1 + 1 { - let target_layer = if is_primary { primary_last_layer } else { secondary_last_layer }; - let earlier_layer_neurons = new_neurons.iter().filter(|(_, _, l, _)| *l >= target_layer && l <= layer).collect::>(); + } else if *neuron_id == &segments.last().unwrap().1 + 1 { + let target_layer = if is_primary { + primary_last_layer + } else { + secondary_last_layer + }; + let earlier_layer_neurons = new_neurons + .iter() + .filter(|(_, _, l, _)| *l >= target_layer && l <= layer) + .collect::>(); // get max neuron from with both // The highest layer // get max id from that layer - let highest_id = earlier_layer_neurons.iter().max_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); + let highest_id = earlier_layer_neurons + .iter() + .max_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); if let Some(highest_id) = highest_id { if highest_id.1 == is_primary { - let neurons_to_add = target_neurons.iter().filter(|(id, _)| id > &highest_id.0 && id < neuron_id).collect::>(); + let neurons_to_add = target_neurons + .iter() + .filter(|(id, _)| id > &highest_id.0 && id < neuron_id) + .collect::>(); for (neuron_id, l) in neurons_to_add { new_neurons.push((*neuron_id, is_primary, *l, 0)); } @@ -454,33 +543,39 @@ pub fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec< } new_neurons.push((*neuron_id, is_primary, *layer, 0)); - } - else { + } else { new_neurons.push((*neuron_id, is_primary, *layer, 0)); } } } // Filtering layers with too few neurons, if necessary - let layer_counts = new_neurons.iter().fold(vec![0; current_layer + 1], |mut counts, &(_, _, layer, _)| { - counts[layer] += 1; - counts - }); + let layer_counts = new_neurons.iter().fold( + vec![0; current_layer + 1], + |mut counts, &(_, _, layer, _)| { + counts[layer] += 1; + counts + }, + ); // Filter out layers based on the minimum number of neurons per layer - new_neurons = new_neurons.into_iter() - .filter(|&(_, _, layer, _)| layer_counts[layer] >= NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN) - .collect::>(); + new_neurons = new_neurons + .into_iter() + .filter(|&(_, _, layer, _)| layer_counts[layer] >= NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN) + .collect::>(); // Collect and sort unique layer numbers - let mut unique_layers = new_neurons.iter() + let mut unique_layers = new_neurons + .iter() .map(|(_, _, layer, _)| *layer) .collect::>(); unique_layers.sort(); unique_layers.dedup(); // Removes duplicates, keeping only unique layer numbers // Create a mapping from old layer numbers to new (gap-less) layer numbers - let layer_mapping = unique_layers.iter().enumerate() + let layer_mapping = unique_layers + .iter() + .enumerate() .map(|(new_layer, &old_layer)| (old_layer, new_layer)) .collect::>(); @@ -492,9 +587,12 @@ pub fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec< // Assign new IDs // new_neurons must be sorted by layer, then by neuron ID within the layer new_neurons.sort_unstable_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); - new_neurons.iter_mut().enumerate().for_each(|(new_id, neuron)| { - neuron.3 = new_id as u32; - }); + new_neurons + .iter_mut() + .enumerate() + .for_each(|(new_id, neuron)| { + neuron.3 = new_id as u32; + }); new_neurons } @@ -502,46 +600,64 @@ pub fn crossbreed_neuron_arrays(segments: Vec<(u32, u32)>, primary_neurons: Vec< pub fn major_mutation(fann: &Fann, weight_initialization_range: Range) -> Result { // add or remove a random neuron from a hidden layer let mut mutated_shape = fann.get_layer_sizes().to_vec(); - let mut mutated_neurons = generate_neuron_datastructure(&mutated_shape).iter().map(|(id, layer)| (*id, true, *layer, *id)).collect::>(); + let mut mutated_neurons = generate_neuron_datastructure(&mutated_shape) + .iter() + .map(|(id, layer)| (*id, true, *layer, *id)) + .collect::>(); // Determine first whether to add or remove a neuron if thread_rng().gen_range(0..2) == 0 { // To add a neuron we need to create a new fann object with the new layer sizes, then copy the information and connections over - let max_id = mutated_neurons.iter().max_by_key(|(id, _, _, _)| id).unwrap().0; + let max_id = mutated_neurons + .iter() + .max_by_key(|(id, _, _, _)| id) + .unwrap() + .0; // Now we inject the new neuron into mutated_neurons let layer = thread_rng().gen_range(1..fann.get_num_layers() - 1) as usize; let new_id = max_id + 1; mutated_neurons.push((new_id, true, layer, new_id)); mutated_shape[layer] += 1; - } - else { + } else { // Remove a neuron let layer = thread_rng().gen_range(1..fann.get_num_layers() - 1) as usize; // Do not remove from layer if it would result in less than NEURALNETWORK_HIDDEN_LAYER_SIZE_MIN neurons if mutated_shape[layer] > NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN as u32 { - let remove_id = mutated_neurons.iter().filter(|(_, _, l, _)| l == &layer).choose(&mut thread_rng()).unwrap().0; + let remove_id = mutated_neurons + .iter() + .filter(|(_, _, l, _)| l == &layer) + .choose(&mut thread_rng()) + .unwrap() + .0; mutated_neurons.retain(|(id, _, _, _)| id != &remove_id); mutated_shape[layer] -= 1; } } - let mut mutated_fann = Fann::new(mutated_shape.as_slice()) - .with_context(|| "Failed to create new fann")?; - mutated_fann.randomize_weights(weight_initialization_range.start, weight_initialization_range.end); + let mut mutated_fann = + Fann::new(mutated_shape.as_slice()).with_context(|| "Failed to create new fann")?; + mutated_fann.randomize_weights( + weight_initialization_range.start, + weight_initialization_range.end, + ); mutated_fann.set_activation_func_hidden(ActivationFunc::SigmoidSymmetric); mutated_fann.set_activation_func_output(ActivationFunc::SigmoidSymmetric); // We need to regenerate the new_id's in mutated_neurons (the 4th item in the tuple) we can do this by iterating over the mutated_neurons all over again starting from ZERO mutated_neurons.sort_by(|a, b| a.2.cmp(&b.2).then(a.0.cmp(&b.0))); - let mut i = 0; - for (_, _, _, new_id) in mutated_neurons.iter_mut() { - *new_id = i; - i += 1; + for (i, (_, _, _, new_id)) in mutated_neurons.iter_mut().enumerate() { + *new_id = i as u32; } // We need to copy the connections from the old fann to the new fann - consolidate_old_connections(&fann, &fann, mutated_shape, mutated_neurons, &mut mutated_fann); + consolidate_old_connections( + fann, + fann, + mutated_shape, + mutated_neurons, + &mut mutated_fann, + ); Ok(mutated_fann) } @@ -561,8 +677,7 @@ pub fn generate_neuron_datastructure(shape: &[u32]) -> Vec<(u32, usize)> { result } - -fn to_bias_network_id(id: &u32, shape: &Vec) -> u32 { +fn to_bias_network_id(id: &u32, shape: &[u32]) -> u32 { // The given id comes from a network without a bias neuron at the end of every layer // We need to translate this id to the id in the network with bias neurons let mut translated_id = 0; @@ -580,14 +695,13 @@ fn to_bias_network_id(id: &u32, shape: &Vec) -> u32 { } fn to_non_bias_network_id(id: u32, shape: &[u32]) -> Option { - let mut bias_count = 0; // Count of bias neurons encountered up to the current ID let mut total_neurons = 0; // Total count of neurons (excluding bias neurons) processed - for &neurons in shape { + for (bias_count, &neurons) in shape.iter().enumerate() { let layer_end = total_neurons + neurons; // End of the current layer, excluding the bias neuron if id < layer_end { // ID is within the current layer (excluding the bias neuron) - return Some(id - bias_count); + return Some(id - bias_count as u32); } if id == layer_end { // ID matches the position where a bias neuron would be @@ -596,7 +710,6 @@ fn to_non_bias_network_id(id: u32, shape: &[u32]) -> Option { // Update counts after considering the current layer total_neurons += neurons + 1; // Move to the next layer, accounting for the bias neuron - bias_count += 1; // Increment bias count as we've moved past where a bias neuron would be } // If the ID is beyond the range of all neurons (including bias), it's treated as invalid @@ -611,8 +724,8 @@ fn get_bias_neuron_for_layer(layer: usize, shape: &[u32]) -> Option { } else { // Compute the bias neuron for intermediate layers let mut bias = 0; - for i in 0..layer { - bias += shape[i]; + for layer_count in shape.iter().take(layer) { + bias += layer_count; } Some(bias + layer as u32 - 1) } @@ -654,7 +767,10 @@ mod tests { // Assert that input and output layers have the same size assert_eq!(primary_shape[0], new_shape[0]); - assert_eq!(primary_shape[primary_shape.len() - 1], new_shape[new_shape.len() - 1]); + assert_eq!( + primary_shape[primary_shape.len() - 1], + new_shape[new_shape.len() - 1] + ); // Determine if a neuron was removed or added if new_shape.iter().sum::() == primary_shape.iter().sum::() + 1 { @@ -671,14 +787,25 @@ mod tests { } for connection in connections.iter() { - if connection.from_neuron == added_neuron_id || connection.to_neuron == added_neuron_id { - assert!(connection.weight < 0.0, "Connection: {:?}, Added Neuron: {}", connection, added_neuron_id); + if connection.from_neuron == added_neuron_id + || connection.to_neuron == added_neuron_id + { + assert!( + connection.weight < 0.0, + "Connection: {:?}, Added Neuron: {}", + connection, + added_neuron_id + ); } else { - assert!(connection.weight > 0.0, "Connection: {:?}, Added Neuron: {}", connection, added_neuron_id); + assert!( + connection.weight > 0.0, + "Connection: {:?}, Added Neuron: {}", + connection, + added_neuron_id + ); } } - } - else if new_shape.iter().sum::() == primary_shape.iter().sum::() - 1 { + } else if new_shape.iter().sum::() == primary_shape.iter().sum::() - 1 { //Neuron was removed for connection in connections.iter() { assert!(connection.weight > 0.0, "Connection: {:?}", connection); @@ -687,11 +814,14 @@ mod tests { for (i, layer) in new_shape.iter().enumerate() { // if layer isn't input or output if i != 0 && i as u32 != new_shape.len() as u32 - 1 { - assert!(*layer >= NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN as u32, "Layer: {}", layer); + assert!( + *layer >= NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN as u32, + "Layer: {}", + layer + ); } } - } - else { + } else { //Neuron was neither added nor removed for connection in connections.iter() { assert!(connection.weight > 0.0, "Connection: {:?}", connection); @@ -710,12 +840,20 @@ mod tests { let crossbreed_segments = 5; // Act - let result = generate_segments(primary_shape.clone(), secondary_shape.clone(), crossbreed_segments); + let result = generate_segments( + primary_shape.clone(), + secondary_shape.clone(), + crossbreed_segments, + ); println!("{:?}", result); // Assert - assert!(result.len() <= crossbreed_segments, "Segments: {:?}", result); + assert!( + result.len() <= crossbreed_segments, + "Segments: {:?}", + result + ); //Assert that segments are within the bounds of the layers for (start, end) in result.iter() { // Bounds are the end of the first layer to the end of the second to last layer @@ -726,8 +864,7 @@ mod tests { } //Assert that segments start and end are in ascending order - for (start, end) in result.iter() - { + for (start, end) in result.iter() { assert!(*start <= *end, "Start: {}, End: {}", start, end); } @@ -740,7 +877,11 @@ mod tests { let crossbreed_segments = 15; // Act - let result = generate_segments(primary_shape.clone(), secondary_shape.clone(), crossbreed_segments); + let result = generate_segments( + primary_shape.clone(), + secondary_shape.clone(), + crossbreed_segments, + ); println!("{:?}", result); @@ -754,8 +895,7 @@ mod tests { } //Assert that segments start and end are in ascending order - for (start, end) in result.iter() - { + for (start, end) in result.iter() { assert!(*start <= *end, "Start: {}, End: {}", start, end); } @@ -805,25 +945,52 @@ mod tests { fn crossbreed_neuron_arrays_test() { // Assign let segments = vec![(0, 3), (4, 6), (7, 8), (9, 10)]; - + let primary_network = generate_neuron_datastructure(&vec![4, 8, 6, 4]); let secondary_network = generate_neuron_datastructure(&vec![4, 3, 3, 3, 3, 3, 4]); // Act - let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + let result = crossbreed_neuron_arrays( + segments.clone(), + primary_network.clone(), + secondary_network.clone(), + ); // Expected Result Set let expected: HashSet<(u32, bool, usize, u32)> = vec![ // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + (0, true, 0, 0), + (1, true, 0, 1), + (2, true, 0, 2), + (3, true, 0, 3), // Hidden Layer 1: Expect 8 - (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, true, 1, 7), (8, true, 1, 8), (9, true, 1, 9), (10, true, 1, 10), (11, true, 1, 11), + (4, false, 1, 4), + (5, false, 1, 5), + (6, false, 1, 6), + (7, true, 1, 7), + (8, true, 1, 8), + (9, true, 1, 9), + (10, true, 1, 10), + (11, true, 1, 11), // Hidden Layer 2: Expect 9 - (7, false, 2, 12), (8, false, 2, 13), (9, false, 2, 14), (12, true, 2, 15), (13, true, 2, 16), (14, true, 2, 17), (15, true, 2, 18), (16, true, 2, 19), (17, true, 2, 20), + (7, false, 2, 12), + (8, false, 2, 13), + (9, false, 2, 14), + (12, true, 2, 15), + (13, true, 2, 16), + (14, true, 2, 17), + (15, true, 2, 18), + (16, true, 2, 19), + (17, true, 2, 20), // Output Layer: Expect 4 - (18, true, 3, 21), (19, true, 3, 22), (20, true, 3, 23), (21, true, 3, 24), - ].into_iter().collect(); + (18, true, 3, 21), + (19, true, 3, 22), + (20, true, 3, 23), + (21, true, 3, 24), + ] + .into_iter() + .collect(); // Convert Result to HashSet for Comparison let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); @@ -833,25 +1000,51 @@ mod tests { // Now we test the ooposite case // Act - let result = crossbreed_neuron_arrays(segments.clone(), secondary_network.clone(), primary_network.clone()); + let result = crossbreed_neuron_arrays( + segments.clone(), + secondary_network.clone(), + primary_network.clone(), + ); // Expected Result Set let expected: HashSet<(u32, bool, usize, u32)> = vec![ // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + (0, true, 0, 0), + (1, true, 0, 1), + (2, true, 0, 2), + (3, true, 0, 3), // Hidden Layer 1: Expect 7 - (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, false, 1, 7), (8, false, 1, 8), (9, false, 1, 9), (10, false, 1, 10), + (4, false, 1, 4), + (5, false, 1, 5), + (6, false, 1, 6), + (7, false, 1, 7), + (8, false, 1, 8), + (9, false, 1, 9), + (10, false, 1, 10), // Hidden Layer 2: Expect 3 - (7, true, 2, 11), (8, true, 2, 12), (9, true, 2, 13), + (7, true, 2, 11), + (8, true, 2, 12), + (9, true, 2, 13), // Hidden Layer 3: Expect 3 - (10, true, 3, 14), (11, true, 3, 15), (12, true, 3, 16), + (10, true, 3, 14), + (11, true, 3, 15), + (12, true, 3, 16), // Hidden Layer 4: Expect 3 - (13, true, 4, 17), (14, true, 4, 18), (15, true, 4, 19), + (13, true, 4, 17), + (14, true, 4, 18), + (15, true, 4, 19), // Hidden Layer 5: Expect 3 - (16, true, 5, 20), (17, true, 5, 21), (18, true, 5, 22), + (16, true, 5, 20), + (17, true, 5, 21), + (18, true, 5, 22), // Output Layer: Expect 4 - (19, true, 6, 23), (20, true, 6, 24), (21, true, 6, 25), (22, true, 6, 26), - ].into_iter().collect(); + (19, true, 6, 23), + (20, true, 6, 24), + (21, true, 6, 25), + (22, true, 6, 26), + ] + .into_iter() + .collect(); // Convert Result to HashSet for Comparison let result_set: HashSet<(u32, bool, usize, u32)> = result.into_iter().collect(); @@ -864,23 +1057,46 @@ mod tests { let segments = vec![(0, 4), (5, 14), (15, 15), (16, 16)]; // Act - let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + let result = crossbreed_neuron_arrays( + segments.clone(), + primary_network.clone(), + secondary_network.clone(), + ); // Expected Result Set let expected: HashSet<(u32, bool, usize, u32)> = vec![ // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + (0, true, 0, 0), + (1, true, 0, 1), + (2, true, 0, 2), + (3, true, 0, 3), // Hidden Layer 1: Expect 3 - (4, true, 1, 4), (5, false, 1, 5), (6, false, 1, 6), + (4, true, 1, 4), + (5, false, 1, 5), + (6, false, 1, 6), // Hidden Layer 2: Expect 6 - (7, false, 2, 7), (8, false, 2, 8), (9, false, 2, 9), (15, true, 2, 10), (16, true, 2, 11), (17, true, 2, 12), + (7, false, 2, 7), + (8, false, 2, 8), + (9, false, 2, 9), + (15, true, 2, 10), + (16, true, 2, 11), + (17, true, 2, 12), // Hidden Layer 3: Expect 3 - (10, false, 3, 13), (11, false, 3, 14), (12, false, 3, 15), + (10, false, 3, 13), + (11, false, 3, 14), + (12, false, 3, 15), // Hidden Layer 4: Expect 3 - (13, false, 4, 16), (14, false, 4, 17), (15, false, 4, 18), + (13, false, 4, 16), + (14, false, 4, 17), + (15, false, 4, 18), // Output Layer: Expect 4 - (18, true, 5, 19), (19, true, 5, 20), (20, true, 5, 21), (21, true, 5, 22), - ].into_iter().collect(); + (18, true, 5, 19), + (19, true, 5, 20), + (20, true, 5, 21), + (21, true, 5, 22), + ] + .into_iter() + .collect(); // print result before comparison for r in result.iter() { @@ -894,23 +1110,50 @@ mod tests { assert_eq!(result_set, expected); // Swapping order - let result = crossbreed_neuron_arrays(segments.clone(), secondary_network.clone(), primary_network.clone()); + let result = crossbreed_neuron_arrays( + segments.clone(), + secondary_network.clone(), + primary_network.clone(), + ); // Expected Result Set let expected: HashSet<(u32, bool, usize, u32)> = vec![ // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + (0, true, 0, 0), + (1, true, 0, 1), + (2, true, 0, 2), + (3, true, 0, 3), // Hidden Layer 1: Expect 8 - (4, true, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, false, 1, 7), (8, false, 1, 8), (9, false, 1, 9), (10, false, 1, 10), (11, false, 1, 11), + (4, true, 1, 4), + (5, false, 1, 5), + (6, false, 1, 6), + (7, false, 1, 7), + (8, false, 1, 8), + (9, false, 1, 9), + (10, false, 1, 10), + (11, false, 1, 11), // Hidden Layer 2: Expect 5 - (12, false, 2, 12), (13, false, 2, 13), (14, false, 2, 14), (15, false, 2, 15), (16, false, 2, 16), + (12, false, 2, 12), + (13, false, 2, 13), + (14, false, 2, 14), + (15, false, 2, 15), + (16, false, 2, 16), // Hidden Layer 3: Expect 3 - (13, true, 3, 17), (14, true, 3, 18), (15, true, 3, 19), + (13, true, 3, 17), + (14, true, 3, 18), + (15, true, 3, 19), // Hidden Layer 4: Expect 3 - (16, true, 4, 20), (17, true, 4, 21), (18, true, 4, 22), + (16, true, 4, 20), + (17, true, 4, 21), + (18, true, 4, 22), // Output Layer: Expect 4 - (19, true, 5, 23), (20, true, 5, 24), (21, true, 5, 25), (22, true, 5, 26), - ].into_iter().collect(); + (19, true, 5, 23), + (20, true, 5, 24), + (21, true, 5, 25), + (22, true, 5, 26), + ] + .into_iter() + .collect(); // print result before comparison for r in result.iter() { @@ -928,21 +1171,48 @@ mod tests { let segments = vec![(0, 7), (8, 9), (10, 10), (11, 12)]; // Act - let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + let result = crossbreed_neuron_arrays( + segments.clone(), + primary_network.clone(), + secondary_network.clone(), + ); // Expected Result Set let expected: HashSet<(u32, bool, usize, u32)> = vec![ // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + (0, true, 0, 0), + (1, true, 0, 1), + (2, true, 0, 2), + (3, true, 0, 3), // Hidden Layer 1: Expect 7 - (4, true, 1, 4), (5, true, 1, 5), (6, true, 1, 6), (7, true, 1, 7), (8, true, 1, 8), (9, true, 1, 9), (10, true, 1, 10), + (4, true, 1, 4), + (5, true, 1, 5), + (6, true, 1, 6), + (7, true, 1, 7), + (8, true, 1, 8), + (9, true, 1, 9), + (10, true, 1, 10), // Hidden Layer 2: Expect 8 - (7, false, 2, 11), (8, false, 2, 12), (9, false, 2, 13), (13, true, 2, 14), (14, true, 2, 15), (15, true, 2, 16), (16, true, 2, 17), (17, true, 2, 18), + (7, false, 2, 11), + (8, false, 2, 12), + (9, false, 2, 13), + (13, true, 2, 14), + (14, true, 2, 15), + (15, true, 2, 16), + (16, true, 2, 17), + (17, true, 2, 18), // Hidden Layer 3: Expect 3 - (10, false, 3, 19), (11, false, 3, 20), (12, false, 3, 21), + (10, false, 3, 19), + (11, false, 3, 20), + (12, false, 3, 21), // Output Layer: Expect 4 - (18, true, 4, 22), (19, true, 4, 23), (20, true, 4, 24), (21, true, 4, 25), - ].into_iter().collect(); + (18, true, 4, 22), + (19, true, 4, 23), + (20, true, 4, 24), + (21, true, 4, 25), + ] + .into_iter() + .collect(); // print result before comparison for r in result.iter() { @@ -956,25 +1226,52 @@ mod tests { assert_eq!(result_set, expected); // Swapping order - let result = crossbreed_neuron_arrays(segments.clone(), secondary_network.clone(), primary_network.clone()); + let result = crossbreed_neuron_arrays( + segments.clone(), + secondary_network.clone(), + primary_network.clone(), + ); // Expected Result Set let expected: HashSet<(u32, bool, usize, u32)> = vec![ // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + (0, true, 0, 0), + (1, true, 0, 1), + (2, true, 0, 2), + (3, true, 0, 3), // Hidden Layer 1: Expect 7 - (4, true, 1, 4), (5, true, 1, 5), (6, true, 1, 6), (8, false, 1, 7), (9, false, 1, 8), (10, false, 1, 9), (11, false, 1, 10), + (4, true, 1, 4), + (5, true, 1, 5), + (6, true, 1, 6), + (8, false, 1, 7), + (9, false, 1, 8), + (10, false, 1, 9), + (11, false, 1, 10), // Hidden Layer 2: Expect 4 - (7, true, 2, 11), (8, true, 2, 12), (9, true, 2, 13), (12, false, 2, 14), + (7, true, 2, 11), + (8, true, 2, 12), + (9, true, 2, 13), + (12, false, 2, 14), // Hidden Layer 3: Expect 3 - (10, true, 3, 15), (11, true, 3, 16), (12, true, 3, 17), + (10, true, 3, 15), + (11, true, 3, 16), + (12, true, 3, 17), // Hidden Layer 4: Expect 3 - (13, true, 4, 18), (14, true, 4, 19), (15, true, 4, 20), + (13, true, 4, 18), + (14, true, 4, 19), + (15, true, 4, 20), // Hidden Layer 5: Expect 3 - (16, true, 5, 21), (17, true, 5, 22), (18, true, 5, 23), + (16, true, 5, 21), + (17, true, 5, 22), + (18, true, 5, 23), // Output Layer: Expect 4 - (19, true, 6, 24), (20, true, 6, 25), (21, true, 6, 26), (22, true, 6, 27), - ].into_iter().collect(); + (19, true, 6, 24), + (20, true, 6, 25), + (21, true, 6, 26), + (22, true, 6, 27), + ] + .into_iter() + .collect(); // print result before comparison for r in result.iter() { @@ -990,40 +1287,76 @@ mod tests { // Testing networks with the same size // Assign let segments = vec![(0, 3), (4, 6), (7, 8), (9, 11)]; - + let primary_network = generate_neuron_datastructure(&vec![4, 3, 4, 5, 4]); - + vec![ // Input layer - (0, 0), (1, 0), (2, 0), (3, 0), + (0, 0), + (1, 0), + (2, 0), + (3, 0), // Hidden layer 1: 3 neurons - (4, 1), (5, 1), (6, 1), + (4, 1), + (5, 1), + (6, 1), // Hidden Layer 2: 4 neurons - (7, 2), (8, 2), (9, 2), (10, 2), + (7, 2), + (8, 2), + (9, 2), + (10, 2), // Hidden Layer 3: 5 neurons - (11, 3), (12, 3), (13, 3), (14, 3), (15, 3), + (11, 3), + (12, 3), + (13, 3), + (14, 3), + (15, 3), // Output layer - (16, 4), (17, 4), (18, 4), (19, 4), + (16, 4), + (17, 4), + (18, 4), + (19, 4), ]; let secondary_network = primary_network.clone(); // Act - let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + let result = crossbreed_neuron_arrays( + segments.clone(), + primary_network.clone(), + secondary_network.clone(), + ); // Expected Result Set let expected: HashSet<(u32, bool, usize, u32)> = vec![ // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + (0, true, 0, 0), + (1, true, 0, 1), + (2, true, 0, 2), + (3, true, 0, 3), // Hidden Layer 1: Expect 3 - (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), + (4, false, 1, 4), + (5, false, 1, 5), + (6, false, 1, 6), // Hidden Layer 2: Expect 4 - (7, true, 2, 7), (8, true, 2, 8), (9, false, 2, 9), (10, false, 2, 10), + (7, true, 2, 7), + (8, true, 2, 8), + (9, false, 2, 9), + (10, false, 2, 10), // Hidden Layer 3: Expect 5 - (11, false, 3, 11), (12, true, 3, 12), (13, true, 3, 13), (14, true, 3, 14), (15, true, 3, 15), + (11, false, 3, 11), + (12, true, 3, 12), + (13, true, 3, 13), + (14, true, 3, 14), + (15, true, 3, 15), // Output Layer: Expect 4 - (16, true, 4, 16), (17, true, 4, 17), (18, true, 4, 18), (19, true, 4, 19), - ].into_iter().collect(); + (16, true, 4, 16), + (17, true, 4, 17), + (18, true, 4, 18), + (19, true, 4, 19), + ] + .into_iter() + .collect(); // print result before comparison for r in result.iter() { @@ -1040,21 +1373,42 @@ mod tests { let segments = vec![(0, 5), (6, 6), (7, 11), (12, 13)]; // Act - let result = crossbreed_neuron_arrays(segments.clone(), primary_network.clone(), secondary_network.clone()); + let result = crossbreed_neuron_arrays( + segments.clone(), + primary_network.clone(), + secondary_network.clone(), + ); // Expected Result Set let expected: HashSet<(u32, bool, usize, u32)> = vec![ // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + (0, true, 0, 0), + (1, true, 0, 1), + (2, true, 0, 2), + (3, true, 0, 3), // Hidden Layer 1: Expect 3 - (4, true, 1, 4), (5, true, 1, 5), (6, false, 1, 6), + (4, true, 1, 4), + (5, true, 1, 5), + (6, false, 1, 6), // Hidden Layer 2: Expect 4 - (7, true, 2, 7), (8, true, 2, 8), (9, true, 2, 9), (10, true, 2, 10), + (7, true, 2, 7), + (8, true, 2, 8), + (9, true, 2, 9), + (10, true, 2, 10), // Hidden Layer 3: Expect 5 - (11, true, 3, 11), (12, false, 3, 12), (13, false, 3, 13), (14, true, 3, 14), (15, true, 3, 15), + (11, true, 3, 11), + (12, false, 3, 12), + (13, false, 3, 13), + (14, true, 3, 14), + (15, true, 3, 15), // Output Layer: Expect 4 - (16, true, 4, 16), (17, true, 4, 17), (18, true, 4, 18), (19, true, 4, 19), - ].into_iter().collect(); + (16, true, 4, 16), + (17, true, 4, 17), + (18, true, 4, 18), + (19, true, 4, 19), + ] + .into_iter() + .collect(); // print result before comparison for r in result.iter() { @@ -1078,10 +1432,22 @@ mod tests { // Expected Result let expected: Vec<(u32, usize)> = vec![ - (0, 0), (1, 0), (2, 0), (3, 0), - (4, 1), (5, 1), (6, 1), - (7, 2), (8, 2), (9, 2), (10, 2), (11, 2), - (12, 3), (13, 3), (14, 3), (15, 3), + (0, 0), + (1, 0), + (2, 0), + (3, 0), + (4, 1), + (5, 1), + (6, 1), + (7, 2), + (8, 2), + (9, 2), + (10, 2), + (11, 2), + (12, 3), + (13, 3), + (14, 3), + (15, 3), ]; // Assert @@ -1095,10 +1461,22 @@ mod tests { let expected = vec![ // (input, expected output) - (0, 0), (1, 1), (2, 2), (3, 3), - (4, 5), (5, 6), (6, 7), - (7, 9), (8, 10), (9, 11), (10, 12), (11, 13), - (12, 15), (13, 16), (14, 17), (15, 18), + (0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 5), + (5, 6), + (6, 7), + (7, 9), + (8, 10), + (9, 11), + (10, 12), + (11, 13), + (12, 15), + (13, 16), + (14, 17), + (15, 18), ]; // Act @@ -1113,8 +1491,7 @@ mod tests { // Assert if let Some(result) = result { assert_eq!(result, input); - } - else { + } else { assert!(false, "Expected Some, got None"); } } @@ -1131,7 +1508,7 @@ mod tests { } #[test] - fn consolidate_old_connections_test() -> Result<(), Box>{ + fn consolidate_old_connections_test() -> Result<(), Box> { // Assign let primary_shape = vec![4, 8, 6, 4]; let secondary_shape = vec![4, 3, 3, 3, 3, 3, 4]; @@ -1154,13 +1531,34 @@ mod tests { let new_neurons = vec![ // Input layer: Expect 4 - (0, true, 0, 0), (1, true, 0, 1), (2, true, 0, 2), (3, true, 0, 3), + (0, true, 0, 0), + (1, true, 0, 1), + (2, true, 0, 2), + (3, true, 0, 3), // Hidden Layer 1: Expect 8 - (4, false, 1, 4), (5, false, 1, 5), (6, false, 1, 6), (7, true, 1, 7), (8, true, 1, 8), (9, true, 1, 9), (10, true, 1, 10), (11, true, 1, 11), + (4, false, 1, 4), + (5, false, 1, 5), + (6, false, 1, 6), + (7, true, 1, 7), + (8, true, 1, 8), + (9, true, 1, 9), + (10, true, 1, 10), + (11, true, 1, 11), // Hidden Layer 2: Expect 9 - (7, false, 2, 12), (8, false, 2, 13), (9, false, 2, 14), (12, true, 2, 15), (13, true, 2, 16), (14, true, 2, 17), (15, true, 2, 18), (16, true, 2, 19), (17, true, 2, 20), + (7, false, 2, 12), + (8, false, 2, 13), + (9, false, 2, 14), + (12, true, 2, 15), + (13, true, 2, 16), + (14, true, 2, 17), + (15, true, 2, 18), + (16, true, 2, 19), + (17, true, 2, 20), // Output Layer: Expect 4 - (18, true, 3, 21), (19, true, 3, 22), (20, true, 3, 23), (21, true, 3, 24), + (18, true, 3, 21), + (19, true, 3, 22), + (20, true, 3, 23), + (21, true, 3, 24), ]; let new_shape = vec![4, 8, 9, 4]; let mut new_fann = Fann::new(&[4, 8, 9, 4])?; @@ -1172,7 +1570,13 @@ mod tests { new_fann.set_connections(&new_connections); // Act - consolidate_old_connections(&primary_fann, &secondary_fann, new_shape, new_neurons, &mut new_fann); + consolidate_old_connections( + &primary_fann, + &secondary_fann, + new_shape, + new_neurons, + &mut new_fann, + ); // Bias neurons // Layer 1: 4 @@ -1181,29 +1585,148 @@ mod tests { let expected_connections = vec![ // (from_neuron, to_neuron, weight) // Hidden Layer 1 (5-12) - (0, 5, -5.0), (1, 5, -105.0), (2, 5, -205.0), (3, 5, -305.0), - (0, 6, -6.0), (1, 6, -106.0), (2, 6, -206.0), (3, 6, -306.0), - (0, 7, -7.0), (1, 7, -107.0), (2, 7, -207.0), (3, 7, -307.0), - (0, 8, 8.0), (1, 8, 108.0), (2, 8, 208.0), (3, 8, 308.0), - (0, 9, 9.0), (1, 9, 109.0), (2, 9, 209.0), (3, 9, 309.0), - (0, 10, 10.0), (1, 10, 110.0), (2, 10, 210.0), (3, 10, 310.0), - (0, 11, 11.0), (1, 11, 111.0), (2, 11, 211.0), (3, 11, 311.0), - (0, 12, 12.0), (1, 12, 112.0), (2, 12, 212.0), (3, 12, 312.0), + (0, 5, -5.0), + (1, 5, -105.0), + (2, 5, -205.0), + (3, 5, -305.0), + (0, 6, -6.0), + (1, 6, -106.0), + (2, 6, -206.0), + (3, 6, -306.0), + (0, 7, -7.0), + (1, 7, -107.0), + (2, 7, -207.0), + (3, 7, -307.0), + (0, 8, 8.0), + (1, 8, 108.0), + (2, 8, 208.0), + (3, 8, 308.0), + (0, 9, 9.0), + (1, 9, 109.0), + (2, 9, 209.0), + (3, 9, 309.0), + (0, 10, 10.0), + (1, 10, 110.0), + (2, 10, 210.0), + (3, 10, 310.0), + (0, 11, 11.0), + (1, 11, 111.0), + (2, 11, 211.0), + (3, 11, 311.0), + (0, 12, 12.0), + (1, 12, 112.0), + (2, 12, 212.0), + (3, 12, 312.0), // Hidden Layer 2 (14-22) - (5, 14, -509.0), (6, 14, -609.0), (7, 14, -709.0), (8, 14, 0.0), (9, 14, 0.0), (10, 14, 0.0), (11, 14, 0.0), (12, 14, 0.0), - (5, 15, -510.0), (6, 15, -610.0), (7, 15, -710.0), (8, 15, 0.0), (9, 15, 0.0), (10, 15, 0.0), (11, 15, 0.0), (12, 15, 0.0), - (5, 16, -511.0), (6, 16, -611.0), (7, 16, -711.0), (8, 16, 0.0), (9, 16, 0.0), (10, 16, 0.0), (11, 16, 0.0), (12, 16, 0.0), - (5, 17, 514.0), (6, 17, 614.0), (7, 17, 714.0), (8, 17, 814.0), (9, 17, 914.0), (10, 17, 1014.0), (11, 17, 1114.0), (12, 17, 1214.0), - (5, 18, 515.0), (6, 18, 615.0), (7, 18, 715.0), (8, 18, 815.0), (9, 18, 915.0), (10, 18, 1015.0), (11, 18, 1115.0), (12, 18, 1215.0), - (5, 19, 516.0), (6, 19, 616.0), (7, 19, 716.0), (8, 19, 816.0), (9, 19, 916.0), (10, 19, 1016.0), (11, 19, 1116.0), (12, 19, 1216.0), - (5, 20, 517.0), (6, 20, 617.0), (7, 20, 717.0), (8, 20, 817.0), (9, 20, 917.0), (10, 20, 1017.0), (11, 20, 1117.0), (12, 20, 1217.0), - (5, 21, 518.0), (6, 21, 618.0), (7, 21, 718.0), (8, 21, 818.0), (9, 21, 918.0), (10, 21, 1018.0), (11, 21, 1118.0), (12, 21, 1218.0), - (5, 22, 519.0), (6, 22, 619.0), (7, 22, 719.0), (8, 22, 819.0), (9, 22, 919.0), (10, 22, 1019.0), (11, 22, 1119.0), (12, 22, 1219.0), + (5, 14, -509.0), + (6, 14, -609.0), + (7, 14, -709.0), + (8, 14, 0.0), + (9, 14, 0.0), + (10, 14, 0.0), + (11, 14, 0.0), + (12, 14, 0.0), + (5, 15, -510.0), + (6, 15, -610.0), + (7, 15, -710.0), + (8, 15, 0.0), + (9, 15, 0.0), + (10, 15, 0.0), + (11, 15, 0.0), + (12, 15, 0.0), + (5, 16, -511.0), + (6, 16, -611.0), + (7, 16, -711.0), + (8, 16, 0.0), + (9, 16, 0.0), + (10, 16, 0.0), + (11, 16, 0.0), + (12, 16, 0.0), + (5, 17, 514.0), + (6, 17, 614.0), + (7, 17, 714.0), + (8, 17, 814.0), + (9, 17, 914.0), + (10, 17, 1014.0), + (11, 17, 1114.0), + (12, 17, 1214.0), + (5, 18, 515.0), + (6, 18, 615.0), + (7, 18, 715.0), + (8, 18, 815.0), + (9, 18, 915.0), + (10, 18, 1015.0), + (11, 18, 1115.0), + (12, 18, 1215.0), + (5, 19, 516.0), + (6, 19, 616.0), + (7, 19, 716.0), + (8, 19, 816.0), + (9, 19, 916.0), + (10, 19, 1016.0), + (11, 19, 1116.0), + (12, 19, 1216.0), + (5, 20, 517.0), + (6, 20, 617.0), + (7, 20, 717.0), + (8, 20, 817.0), + (9, 20, 917.0), + (10, 20, 1017.0), + (11, 20, 1117.0), + (12, 20, 1217.0), + (5, 21, 518.0), + (6, 21, 618.0), + (7, 21, 718.0), + (8, 21, 818.0), + (9, 21, 918.0), + (10, 21, 1018.0), + (11, 21, 1118.0), + (12, 21, 1218.0), + (5, 22, 519.0), + (6, 22, 619.0), + (7, 22, 719.0), + (8, 22, 819.0), + (9, 22, 919.0), + (10, 22, 1019.0), + (11, 22, 1119.0), + (12, 22, 1219.0), // Output layer (24-27) - (14, 24, 0.0), (15, 24, 0.0), (16, 24, 0.0), (17, 24, 1421.0), (18, 24, 1521.0), (19, 24, 1621.0), (20, 24, 1721.0), (21, 24, 1821.0), (22, 24, 1921.0), - (14, 25, 0.0), (15, 25, 0.0), (16, 25, 0.0), (17, 25, 1422.0), (18, 25, 1522.0), (19, 25, 1622.0), (20, 25, 1722.0), (21, 25, 1822.0), (22, 25, 1922.0), - (14, 26, 0.0), (15, 26, 0.0), (16, 26, 0.0), (17, 26, 1423.0), (18, 26, 1523.0), (19, 26, 1623.0), (20, 26, 1723.0), (21, 26, 1823.0), (22, 26, 1923.0), - (14, 27, 0.0), (15, 27, 0.0), (16, 27, 0.0), (17, 27, 1424.0), (18, 27, 1524.0), (19, 27, 1624.0), (20, 27, 1724.0), (21, 27, 1824.0), (22, 27, 1924.0), + (14, 24, 0.0), + (15, 24, 0.0), + (16, 24, 0.0), + (17, 24, 1421.0), + (18, 24, 1521.0), + (19, 24, 1621.0), + (20, 24, 1721.0), + (21, 24, 1821.0), + (22, 24, 1921.0), + (14, 25, 0.0), + (15, 25, 0.0), + (16, 25, 0.0), + (17, 25, 1422.0), + (18, 25, 1522.0), + (19, 25, 1622.0), + (20, 25, 1722.0), + (21, 25, 1822.0), + (22, 25, 1922.0), + (14, 26, 0.0), + (15, 26, 0.0), + (16, 26, 0.0), + (17, 26, 1423.0), + (18, 26, 1523.0), + (19, 26, 1623.0), + (20, 26, 1723.0), + (21, 26, 1823.0), + (22, 26, 1923.0), + (14, 27, 0.0), + (15, 27, 0.0), + (16, 27, 0.0), + (17, 27, 1424.0), + (18, 27, 1524.0), + (19, 27, 1624.0), + (20, 27, 1724.0), + (21, 27, 1824.0), + (22, 27, 1924.0), ]; for connection in new_fann.get_connections().iter() { @@ -1214,9 +1737,15 @@ mod tests { // Compare each connection to the expected connection let new_connections = new_fann.get_connections(); for connection in expected_connections.iter() { - let matching_connection = new_connections.iter().find(|&c| c.from_neuron == connection.0 && c.to_neuron == connection.1); + let matching_connection = new_connections + .iter() + .find(|&c| c.from_neuron == connection.0 && c.to_neuron == connection.1); if let Some(matching_connection) = matching_connection { - assert_eq!(matching_connection.weight, connection.2, "Connection: {:?}", matching_connection); + assert_eq!( + matching_connection.weight, connection.2, + "Connection: {:?}", + matching_connection + ); } else { assert!(false, "Connection not found: {:?}", connection); } @@ -1226,17 +1755,41 @@ mod tests { // (from_neuron, to_neuron, weight) // Bias Neurons // Layer 2: bias neuron_id 4 - (4, 5, -405.0), (4, 6, -406.0), (4, 7, -407.0), (4, 8, 408.0), (4, 9, 409.0), (4, 10, 410.0), (4, 11, 411.0), (4, 12, 412.0), + (4, 5, -405.0), + (4, 6, -406.0), + (4, 7, -407.0), + (4, 8, 408.0), + (4, 9, 409.0), + (4, 10, 410.0), + (4, 11, 411.0), + (4, 12, 412.0), // Layer 3: bias neuron_id 13 - (13, 14, -809.0), (13, 15, -810.0), (13, 16, -811.0), (13, 17, 1314.0), (13, 18, 1315.0), (13, 19, 1316.0), (13, 20, 1317.0), (13, 21, 1318.0), (13, 22, 1319.0), + (13, 14, -809.0), + (13, 15, -810.0), + (13, 16, -811.0), + (13, 17, 1314.0), + (13, 18, 1315.0), + (13, 19, 1316.0), + (13, 20, 1317.0), + (13, 21, 1318.0), + (13, 22, 1319.0), // Layer 4: bias neuron_id 23 - (23, 24, 2021.0), (23, 25, 2022.0), (23, 26, 2023.0), (23, 27, 2024.0), + (23, 24, 2021.0), + (23, 25, 2022.0), + (23, 26, 2023.0), + (23, 27, 2024.0), ]; for connection in expected_bias_neuron_connections.iter() { - let matching_connection = new_connections.iter().find(|&c| c.from_neuron == connection.0 && c.to_neuron == connection.1); + let matching_connection = new_connections + .iter() + .find(|&c| c.from_neuron == connection.0 && c.to_neuron == connection.1); if let Some(matching_connection) = matching_connection { - assert_eq!(matching_connection.weight, connection.2, "Connection: {:?}", matching_connection); + assert_eq!( + matching_connection.weight, connection.2, + "Connection: {:?}", + matching_connection + ); } else { assert!(false, "Connection not found: {:?}", connection); } @@ -1244,4 +1797,4 @@ mod tests { Ok(()) } -} \ No newline at end of file +} diff --git a/gemla/src/bin/test_state/mod.rs b/gemla/src/bin/test_state/mod.rs index c11c668..af5b316 100644 --- a/gemla/src/bin/test_state/mod.rs +++ b/gemla/src/bin/test_state/mod.rs @@ -1,8 +1,11 @@ -use gemla::{core::genetic_node::{GeneticNode, GeneticNodeContext}, error::Error}; +use async_trait::async_trait; +use gemla::{ + core::genetic_node::{GeneticNode, GeneticNodeContext}, + error::Error, +}; use rand::prelude::*; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use async_trait::async_trait; const POPULATION_SIZE: u64 = 5; const POPULATION_REDUCTION_SIZE: u64 = 3; @@ -76,7 +79,12 @@ impl GeneticNode for TestState { Ok(()) } - async fn merge(left: &TestState, right: &TestState, id: &Uuid, gemla_context: Self::Context) -> Result, Error> { + async fn merge( + left: &TestState, + right: &TestState, + id: &Uuid, + gemla_context: Self::Context, + ) -> Result, Error> { let mut v = left.population.clone(); v.append(&mut right.population.clone()); @@ -87,12 +95,14 @@ impl GeneticNode for TestState { let mut result = TestState { population: v }; - result.mutate(GeneticNodeContext { - id: id.clone(), - generation: 0, - max_generations: 0, - gemla_context: gemla_context - }).await?; + result + .mutate(GeneticNodeContext { + id: *id, + generation: 0, + max_generations: 0, + gemla_context, + }) + .await?; Ok(Box::new(result)) } @@ -105,14 +115,14 @@ mod tests { #[tokio::test] async fn test_initialize() { - let state = TestState::initialize( - GeneticNodeContext { - id: Uuid::new_v4(), - generation: 0, - max_generations: 0, - gemla_context: (), - } - ).await.unwrap(); + let state = TestState::initialize(GeneticNodeContext { + id: Uuid::new_v4(), + generation: 0, + max_generations: 0, + gemla_context: (), + }) + .await + .unwrap(); assert_eq!(state.population.len(), POPULATION_SIZE as usize); } @@ -125,35 +135,38 @@ mod tests { let original_population = state.population.clone(); - state.simulate( - GeneticNodeContext { + state + .simulate(GeneticNodeContext { id: Uuid::new_v4(), generation: 0, max_generations: 0, gemla_context: (), - } - ).await.unwrap(); + }) + .await + .unwrap(); assert!(original_population .iter() .zip(state.population.iter()) .all(|(&a, &b)| b >= a - 1 && b <= a + 2)); - state.simulate( - GeneticNodeContext { + state + .simulate(GeneticNodeContext { id: Uuid::new_v4(), generation: 0, max_generations: 0, gemla_context: (), - } - ).await.unwrap(); - state.simulate( - GeneticNodeContext { + }) + .await + .unwrap(); + state + .simulate(GeneticNodeContext { id: Uuid::new_v4(), generation: 0, max_generations: 0, gemla_context: (), - } - ).await.unwrap(); + }) + .await + .unwrap(); assert!(original_population .iter() .zip(state.population.iter()) @@ -166,14 +179,15 @@ mod tests { population: vec![4, 3, 3], }; - state.mutate( - GeneticNodeContext { + state + .mutate(GeneticNodeContext { id: Uuid::new_v4(), generation: 0, max_generations: 0, gemla_context: (), - } - ).await.unwrap(); + }) + .await + .unwrap(); assert_eq!(state.population.len(), POPULATION_SIZE as usize); } @@ -188,7 +202,9 @@ mod tests { population: vec![0, 1, 3, 7], }; - let merged_state = TestState::merge(&state1, &state2, &Uuid::new_v4(), ()).await.unwrap(); + let merged_state = TestState::merge(&state1, &state2, &Uuid::new_v4(), ()) + .await + .unwrap(); assert_eq!(merged_state.population.len(), POPULATION_SIZE as usize); assert!(merged_state.population.iter().any(|&x| x == 7)); diff --git a/gemla/src/core/genetic_node.rs b/gemla/src/core/genetic_node.rs index d9d7bc0..020d2c6 100644 --- a/gemla/src/core/genetic_node.rs +++ b/gemla/src/core/genetic_node.rs @@ -5,10 +5,10 @@ use crate::error::Error; use anyhow::Context; +use async_trait::async_trait; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::fmt::Debug; use uuid::Uuid; -use async_trait::async_trait; /// An enum used to control the state of a [`GeneticNode`] /// @@ -30,14 +30,14 @@ pub struct GeneticNodeContext { pub generation: u64, pub max_generations: u64, pub id: Uuid, - pub gemla_context: S + pub gemla_context: S, } /// A trait used to interact with the internal state of nodes within the [`Bracket`] /// /// [`Bracket`]: crate::bracket::Bracket #[async_trait] -pub trait GeneticNode : Send { +pub trait GeneticNode: Send { type Context; /// Initializes a new instance of a [`GeneticState`]. @@ -54,15 +54,20 @@ pub trait GeneticNode : Send { /// TODO async fn mutate(&mut self, context: GeneticNodeContext) -> Result<(), Error>; - async fn merge(left: &Self, right: &Self, id: &Uuid, context: Self::Context) -> Result, Error>; + async fn merge( + left: &Self, + right: &Self, + id: &Uuid, + context: Self::Context, + ) -> Result, Error>; } /// Used externally to wrap a node implementing the [`GeneticNode`] trait. Processes state transitions for the given node as /// well as signal recovery. Transition states are given by [`GeneticState`] #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub struct GeneticNodeWrapper -where - T: Clone +pub struct GeneticNodeWrapper +where + T: Clone, { node: Option, state: GeneticState, @@ -71,9 +76,9 @@ where id: Uuid, } -impl Default for GeneticNodeWrapper +impl Default for GeneticNodeWrapper where - T: Clone + T: Clone, { fn default() -> Self { GeneticNodeWrapper { @@ -146,7 +151,8 @@ where self.state = GeneticState::Simulate; } (GeneticState::Simulate, Some(n)) => { - n.simulate(context.clone()).await + n.simulate(context.clone()) + .await .with_context(|| format!("Error simulating node: {:?}", self))?; self.state = if self.generation >= self.max_generations { @@ -156,7 +162,8 @@ where }; } (GeneticState::Mutate, Some(n)) => { - n.mutate(context.clone()).await + n.mutate(context.clone()) + .await .with_context(|| format!("Error mutating node: {:?}", self))?; self.generation += 1; @@ -186,20 +193,33 @@ mod tests { impl GeneticNode for TestState { type Context = (); - async fn simulate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { + async fn simulate( + &mut self, + _context: GeneticNodeContext, + ) -> Result<(), Error> { self.score += 1.0; Ok(()) } - async fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { + async fn mutate( + &mut self, + _context: GeneticNodeContext, + ) -> Result<(), Error> { Ok(()) } - async fn initialize(_context: GeneticNodeContext) -> Result, Error> { + async fn initialize( + _context: GeneticNodeContext, + ) -> Result, Error> { Ok(Box::new(TestState { score: 0.0 })) } - async fn merge(_l: &TestState, _r: &TestState, _id: &Uuid, _: Self::Context) -> Result, Error> { + async fn merge( + _l: &TestState, + _r: &TestState, + _id: &Uuid, + _: Self::Context, + ) -> Result, Error> { Err(Error::Other(anyhow!("Unable to merge"))) } } diff --git a/gemla/src/core/mod.rs b/gemla/src/core/mod.rs index e779318..4cf9b7a 100644 --- a/gemla/src/core/mod.rs +++ b/gemla/src/core/mod.rs @@ -10,37 +10,38 @@ use futures::future; use genetic_node::{GeneticNode, GeneticNodeWrapper, GeneticState}; use log::{info, trace, warn}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use tokio::{sync::RwLock, task::JoinHandle}; use std::{ - collections::HashMap, fmt::Debug, fs::File, io::ErrorKind, marker::Send, mem, path::Path, sync::Arc, time::Instant + collections::HashMap, fmt::Debug, fs::File, io::ErrorKind, marker::Send, mem, path::Path, + sync::Arc, time::Instant, }; +use tokio::{sync::RwLock, task::JoinHandle}; use uuid::Uuid; type SimulationTree = Box>>; /// Provides configuration options for managing a [`Gemla`] object as it executes. -/// +/// /// # Examples /// ```rust,ignore /// #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] /// struct TestState { /// pub score: f64, /// } -/// +/// /// impl genetic_node::GeneticNode for TestState { /// fn simulate(&mut self) -> Result<(), Error> { /// self.score += 1.0; /// Ok(()) /// } -/// +/// /// fn mutate(&mut self) -> Result<(), Error> { /// Ok(()) /// } -/// +/// /// fn initialize() -> Result, Error> { /// Ok(Box::new(TestState { score: 0.0 })) /// } -/// +/// /// fn merge(left: &TestState, right: &TestState) -> Result, Error> { /// Ok(Box::new(if left.score > right.score { /// left.clone() @@ -49,7 +50,7 @@ type SimulationTree = Box>>; /// })) /// } /// } -/// +/// /// fn main() { /// /// } @@ -80,13 +81,18 @@ where T: GeneticNode + Serialize + DeserializeOwned + Debug + Send + Sync + Clone, T::Context: Send + Sync + Clone + Debug + Serialize + DeserializeOwned + 'static + Default, { - pub async fn new(path: &Path, config: GemlaConfig, data_format: DataFormat) -> Result { + pub async fn new( + path: &Path, + config: GemlaConfig, + data_format: DataFormat, + ) -> Result { match File::open(path) { - // If the file exists we either want to overwrite the file or read from the file + // If the file exists we either want to overwrite the file or read from the file // based on the configuration provided Ok(_) => Ok(Gemla { data: if config.overwrite { - FileLinked::new((None, config, T::Context::default()), path, data_format).await? + FileLinked::new((None, config, T::Context::default()), path, data_format) + .await? } else { FileLinked::from_file(path, data_format)? }, @@ -94,7 +100,8 @@ where }), // If the file doesn't exist we must create it Err(error) if error.kind() == ErrorKind::NotFound => Ok(Gemla { - data: FileLinked::new((None, config, T::Context::default()), path, data_format).await?, + data: FileLinked::new((None, config, T::Context::default()), path, data_format) + .await?, threads: HashMap::new(), }), Err(error) => Err(Error::IO(error)), @@ -106,24 +113,32 @@ where } pub async fn simulate(&mut self, steps: u64) -> Result<(), Error> { - { + let tree_completed = { // Only increase height if the tree is uninitialized or completed let data_arc = self.data.readonly(); let data_ref = data_arc.read().await; let tree_ref = data_ref.0.as_ref(); - if tree_ref.is_none() || - tree_ref - .map(|t| Gemla::is_completed(t)) - .unwrap_or(true) - { - // Before we can process nodes we must create blank nodes in their place to keep track of which nodes have been processed - // in the tree and which nodes have not. - self.data.mutate(|(d, c, _)| { - let mut tree: Option> = Gemla::increase_height(d.take(), c, steps); + tree_ref.is_none() || tree_ref.map(|t| Gemla::is_completed(t)).unwrap_or(true) + }; + + if tree_completed { + // Before we can process nodes we must create blank nodes in their place to keep track of which nodes have been processed + // in the tree and which nodes have not. + self.data + .mutate(|(d, c, _)| { + let mut tree: Option> = + Gemla::increase_height(d.take(), c, steps); mem::swap(d, &mut tree); - }).await?; - } + }) + .await?; + } + + { + // Only increase height if the tree is uninitialized or completed + let data_arc = self.data.readonly(); + let data_ref = data_arc.read().await; + let tree_ref = data_ref.0.as_ref(); info!( "Height of simulation tree increased to {}", @@ -141,36 +156,36 @@ where let data_ref = data_arc.read().await; let tree_ref = data_ref.0.as_ref(); - is_tree_processed = tree_ref - .map(|t| Gemla::is_completed(t)) - .unwrap_or(false) + is_tree_processed = tree_ref.map(|t| Gemla::is_completed(t)).unwrap_or(false) } - // We need to keep simulating until the tree has been completely processed. - if is_tree_processed - { - + if is_tree_processed { self.join_threads().await?; info!("Processed tree"); break; } - if let Some(node) = tree_ref - .and_then(|t| self.get_unprocessed_node(t)) - { + let (node, gemla_context) = { + let data_arc = self.data.readonly(); + let data_ref = data_arc.read().await; + let (tree_ref, _, gemla_context) = &*data_ref; // (Option>>, GemlaConfig, T::Context) + + let node = tree_ref.as_ref().and_then(|t| self.get_unprocessed_node(t)); + + (node, gemla_context.clone()) + }; + + if let Some(node) = node { trace!("Adding node to process list {}", node.id()); - let data_arc = self.data.readonly(); - let data_ref2 = data_arc.read().await; - let gemla_context = data_ref2.2.clone(); - drop(data_ref2); + let gemla_context = gemla_context.clone(); - self.threads - .insert(node.id(), tokio::spawn(async move { - Gemla::process_node(node, gemla_context).await - })); + self.threads.insert( + node.id(), + tokio::spawn(async move { Gemla::process_node(node, gemla_context).await }), + ); } else { trace!("No node found to process, joining threads"); @@ -186,7 +201,7 @@ where trace!("Joining threads for nodes {:?}", self.threads.keys()); let results = future::join_all(self.threads.values_mut()).await; - + // Converting a list of results into a result wrapping the list let reduced_results: Result>, Error> = results.into_iter().flatten().collect(); @@ -195,32 +210,34 @@ where // We need to retrieve the processed nodes from the resulting list and replace them in the original list match reduced_results { Ok(r) => { - self.data.mutate_async(|d| async move { - // Scope to limit the duration of the read lock - let (_, context) = { - let data_read = d.read().await; - (data_read.1.clone(), data_read.2.clone()) - }; // Read lock is dropped here + self.data + .mutate_async(|d| async move { + // Scope to limit the duration of the read lock + let (_, context) = { + let data_read = d.read().await; + (data_read.1, data_read.2.clone()) + }; // Read lock is dropped here - let mut data_write = d.write().await; - - if let Some(t) = data_write.0.as_mut() { - let failed_nodes = Gemla::replace_nodes(t, r); - // We receive a list of nodes that were unable to be found in the original tree - if !failed_nodes.is_empty() { - warn!( - "Unable to find {:?} to replace in tree", - failed_nodes.iter().map(|n| n.id()) - ) + let mut data_write = d.write().await; + + if let Some(t) = data_write.0.as_mut() { + let failed_nodes = Gemla::replace_nodes(t, r); + // We receive a list of nodes that were unable to be found in the original tree + if !failed_nodes.is_empty() { + warn!( + "Unable to find {:?} to replace in tree", + failed_nodes.iter().map(|n| n.id()) + ) + } + + // Once the nodes are replaced we need to find nodes that can be merged from the completed children nodes + Gemla::merge_completed_nodes(t, context.clone()).await + } else { + warn!("Unable to replce nodes {:?} in empty tree", r); + Ok(()) } - - // Once the nodes are replaced we need to find nodes that can be merged from the completed children nodes - Gemla::merge_completed_nodes(t, context.clone()).await - } else { - warn!("Unable to replce nodes {:?} in empty tree", r); - Ok(()) - } - }).await??; + }) + .await??; } Err(e) => return Err(e), } @@ -230,7 +247,10 @@ where } #[async_recursion] - async fn merge_completed_nodes<'a>(tree: &'a mut SimulationTree, gemla_context: T::Context) -> Result<(), Error> { + async fn merge_completed_nodes<'a>( + tree: &'a mut SimulationTree, + gemla_context: T::Context, + ) -> Result<(), Error> { if tree.val.state() == GeneticState::Initialize { match (&mut tree.left, &mut tree.right) { // If the current node has been initialized, and has children nodes that are completed, then we need @@ -241,7 +261,13 @@ where { info!("Merging nodes {} and {}", l.val.id(), r.val.id()); if let (Some(left_node), Some(right_node)) = (l.val.take(), r.val.take()) { - let merged_node = GeneticNode::merge(&left_node, &right_node, &tree.val.id(), gemla_context.clone()).await?; + let merged_node = GeneticNode::merge( + &left_node, + &right_node, + &tree.val.id(), + gemla_context.clone(), + ) + .await?; tree.val = GeneticNodeWrapper::from( *merged_node, tree.val.max_generations(), @@ -286,15 +312,18 @@ where } fn get_unprocessed_node(&self, tree: &SimulationTree) -> Option> { - // If the current node has been processed or exists in the thread list then we want to stop recursing. Checking if it exists in the thread list + // If the current node has been processed or exists in the thread list then we want to stop recursing. Checking if it exists in the thread list // should be fine because we process the tree from bottom to top. if tree.val.state() != GeneticState::Finish && !self.threads.contains_key(&tree.val.id()) { match (&tree.left, &tree.right) { - // If the children are finished we can start processing the currrent node. The current node should be merged from the children already + // If the children are finished we can start processing the currrent node. The current node should be merged from the children already // during join_threads. (Some(l), Some(r)) if l.val.state() == GeneticState::Finish - && r.val.state() == GeneticState::Finish => Some(tree.val.clone()), + && r.val.state() == GeneticState::Finish => + { + Some(tree.val.clone()) + } (Some(l), Some(r)) => self .get_unprocessed_node(l) .or_else(|| self.get_unprocessed_node(r)), @@ -334,7 +363,7 @@ where } else { let left_branch_height = tree.as_ref().map(|t| t.height() as u64).unwrap_or(0) + amount - 1; - + Some(Box::new(Tree::new( GeneticNodeWrapper::new(config.generations_per_height), Gemla::increase_height(tree, config, amount - 1), @@ -352,10 +381,13 @@ where fn is_completed(tree: &SimulationTree) -> bool { // If the current node is finished, then by convention the children should all be finished as well - tree.val.state() == GeneticState::Finish + tree.val.state() == GeneticState::Finish } - async fn process_node(mut node: GeneticNodeWrapper, gemla_context: T::Context) -> Result, Error> { + async fn process_node( + mut node: GeneticNodeWrapper, + gemla_context: T::Context, + ) -> Result, Error> { let node_state_time = Instant::now(); let node_state = node.state(); @@ -379,10 +411,10 @@ where #[cfg(test)] mod tests { use crate::core::*; - use serde::{Deserialize, Serialize}; - use std::path::PathBuf; - use std::fs; use async_trait::async_trait; + use serde::{Deserialize, Serialize}; + use std::fs; + use std::path::PathBuf; use tokio::runtime::Runtime; use self::genetic_node::GeneticNodeContext; @@ -420,20 +452,33 @@ mod tests { impl genetic_node::GeneticNode for TestState { type Context = (); - async fn simulate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { + async fn simulate( + &mut self, + _context: GeneticNodeContext, + ) -> Result<(), Error> { self.score += 1.0; Ok(()) } - async fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { + async fn mutate( + &mut self, + _context: GeneticNodeContext, + ) -> Result<(), Error> { Ok(()) } - async fn initialize(_context: GeneticNodeContext) -> Result, Error> { + async fn initialize( + _context: GeneticNodeContext, + ) -> Result, Error> { Ok(Box::new(TestState { score: 0.0 })) } - async fn merge(left: &TestState, right: &TestState, _id: &Uuid, _: Self::Context) -> Result, Error> { + async fn merge( + left: &TestState, + right: &TestState, + _id: &Uuid, + _: Self::Context, + ) -> Result, Error> { Ok(Box::new(if left.score > right.score { left.clone() } else { @@ -464,7 +509,7 @@ mod tests { let data = gemla.data.readonly(); let data_lock = data.read().await; assert_eq!(data_lock.0.as_ref().unwrap().height(), 2); - + drop(data_lock); drop(gemla); assert!(path.exists()); @@ -498,40 +543,43 @@ mod tests { Ok(()) }) }) - }).await.unwrap()?; // Wait for the blocking task to complete, then handle the Result. + }) + .await + .unwrap()?; // Wait for the blocking task to complete, then handle the Result. Ok(()) } - // #[tokio::test] - // async fn test_simulate() -> Result<(), Error> { - // let path = PathBuf::from("test_simulate"); - // // Use `spawn_blocking` to run the synchronous closure that internally awaits async code. - // tokio::task::spawn_blocking(move || { - // let rt = Runtime::new().unwrap(); // Create a new Tokio runtime for the async block. - // CleanUp::new(&path).run(move |p| { - // rt.block_on(async { - // // Testing initial creation - // let config = GemlaConfig { - // generations_per_height: 10, - // overwrite: true, - // }; - // let mut gemla = Gemla::::new(&p, config, DataFormat::Json)?; + #[tokio::test] + async fn test_simulate() -> Result<(), Error> { + let path = PathBuf::from("test_simulate"); + // Use `spawn_blocking` to run the synchronous closure that internally awaits async code. + tokio::task::spawn_blocking(move || { + let rt = Runtime::new().unwrap(); // Create a new Tokio runtime for the async block. + CleanUp::new(&path).run(move |p| { + rt.block_on(async { + // Testing initial creation + let config = GemlaConfig { + generations_per_height: 10, + overwrite: true, + }; + let mut gemla = Gemla::::new(&p, config, DataFormat::Json).await?; - // // Now we can use `.await` within the spawned blocking task. - // gemla.simulate(5).await?; - // let data = gemla.data.readonly(); - // let data_lock = data.read().unwrap(); - // let tree = data_lock.0.as_ref().unwrap(); - // assert_eq!(tree.height(), 5); - // assert_eq!(tree.val.as_ref().unwrap().score, 50.0); + // Now we can use `.await` within the spawned blocking task. + gemla.simulate(5).await?; + let data = gemla.data.readonly(); + let data_lock = data.read().await; + let tree = data_lock.0.as_ref().unwrap(); + assert_eq!(tree.height(), 5); + assert_eq!(tree.val.as_ref().unwrap().score, 50.0); - // Ok(()) - // }) - // }) - // }).await.unwrap()?; // Wait for the blocking task to complete, then handle the Result. - - // Ok(()) - // } + Ok(()) + }) + }) + }) + .await + .unwrap()?; // Wait for the blocking task to complete, then handle the Result. + Ok(()) + } } From a11def630a25b088c10535317bbcac48f7f9d666 Mon Sep 17 00:00:00 2001 From: vandomej Date: Sat, 6 Apr 2024 09:55:18 -0700 Subject: [PATCH 21/26] Final adjustments for round 2 --- analysis.py | 2 +- file_linked/src/constants/data_format.rs | 4 +- file_linked/src/constants/mod.rs | 2 +- file_linked/src/lib.rs | 220 ++++++++++++-------- gemla/src/bin/fighter_nn/fighter_context.rs | 40 +++- gemla/src/bin/fighter_nn/mod.rs | 44 +++- 6 files changed, 209 insertions(+), 103 deletions(-) diff --git a/analysis.py b/analysis.py index b239e4b..b205d7c 100644 --- a/analysis.py +++ b/analysis.py @@ -36,7 +36,7 @@ def hierarchy_pos(G, root=None, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5) return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter) # Simplified JSON data for demonstration -with open('gemla/test.json', 'r') as file: +with open('gemla/round2.json', 'r') as file: simplified_json_data = json.load(file) # Function to traverse the tree and create a graph diff --git a/file_linked/src/constants/data_format.rs b/file_linked/src/constants/data_format.rs index a50f00e..9a9940e 100644 --- a/file_linked/src/constants/data_format.rs +++ b/file_linked/src/constants/data_format.rs @@ -1,5 +1,5 @@ #[derive(Debug)] pub enum DataFormat { Bincode, - Json -} \ No newline at end of file + Json, +} diff --git a/file_linked/src/constants/mod.rs b/file_linked/src/constants/mod.rs index e01d262..d17427a 100644 --- a/file_linked/src/constants/mod.rs +++ b/file_linked/src/constants/mod.rs @@ -1 +1 @@ -pub mod data_format; \ No newline at end of file +pub mod data_format; diff --git a/file_linked/src/lib.rs b/file_linked/src/lib.rs index 54dcf30..694b114 100644 --- a/file_linked/src/lib.rs +++ b/file_linked/src/lib.rs @@ -1,19 +1,21 @@ //! A wrapper around an object that ties it to a physical file -pub mod error; pub mod constants; +pub mod error; use anyhow::{anyhow, Context}; use constants::data_format::DataFormat; use error::Error; use log::info; use serde::{de::DeserializeOwned, Serialize}; -use tokio::sync::RwLock; use std::{ - fs::{copy, remove_file, File}, io::{ErrorKind, Write}, path::{Path, PathBuf}, sync::Arc, thread::{self, JoinHandle} + fs::{copy, remove_file, File}, + io::{ErrorKind, Write}, + path::{Path, PathBuf}, + sync::Arc, + thread::{self, JoinHandle}, }; - - +use tokio::sync::RwLock; /// A wrapper around an object `T` that ties the object to a physical file #[derive(Debug)] @@ -146,7 +148,7 @@ where path: path.to_path_buf(), temp_file_path, file_thread: None, - data_format + data_format, }; result.write_data().await?; @@ -341,7 +343,6 @@ where self.write_data().await?; - Ok(result) } } @@ -417,16 +418,16 @@ where .ok_or_else(|| anyhow!("Unable to get filename for tempfile {}", path.display()))? )); - match File::open(path).map_err(Error::from).and_then(|file| { - match data_format { + match File::open(path) + .map_err(Error::from) + .and_then(|file| match data_format { DataFormat::Bincode => bincode::deserialize_from::(file) .with_context(|| format!("Unable to deserialize file {}", path.display())) .map_err(Error::from), DataFormat::Json => serde_json::from_reader(file) .with_context(|| format!("Unable to deserialize file {}", path.display())) .map_err(Error::from), - } - }) { + }) { Ok(val) => Ok(FileLinked { val: Arc::new(RwLock::new(val)), path: path.to_path_buf(), @@ -457,7 +458,11 @@ where } } - fn from_temp_file(temp_file_path: &Path, path: &Path, data_format: &DataFormat) -> Result { + fn from_temp_file( + temp_file_path: &Path, + path: &Path, + data_format: &DataFormat, + ) -> Result { let file = File::open(temp_file_path) .with_context(|| format!("Unable to open file {}", temp_file_path.display()))?; @@ -505,7 +510,7 @@ mod tests { pub async fn run(&self, op: F) -> () where F: FnOnce(PathBuf) -> Fut, - Fut: std::future::Future + Fut: std::future::Future, { op(self.path.clone()).await } @@ -523,132 +528,169 @@ mod tests { async fn test_readonly() { let path = PathBuf::from("test_readonly"); let cleanup = CleanUp::new(&path); - cleanup.run(|p| async move { - let val = vec!["one", "two", ""]; + cleanup + .run(|p| async move { + let val = vec!["one", "two", ""]; - let linked_object = FileLinked::new(val.clone(), &p, DataFormat::Json).await.expect("Unable to create file linked object"); - let linked_object_arc = linked_object.readonly(); - let linked_object_ref = linked_object_arc.read().await; - assert_eq!(*linked_object_ref, val); - }).await; + let linked_object = FileLinked::new(val.clone(), &p, DataFormat::Json) + .await + .expect("Unable to create file linked object"); + let linked_object_arc = linked_object.readonly(); + let linked_object_ref = linked_object_arc.read().await; + assert_eq!(*linked_object_ref, val); + }) + .await; } #[tokio::test] async fn test_new() { let path = PathBuf::from("test_new"); let cleanup = CleanUp::new(&path); - cleanup.run(|p| async move { - let val = "test"; + cleanup + .run(|p| async move { + let val = "test"; - FileLinked::new(val, &p, DataFormat::Bincode).await.expect("Unable to create file linked object"); + FileLinked::new(val, &p, DataFormat::Bincode) + .await + .expect("Unable to create file linked object"); - let file = File::open(&p).expect("Unable to open file"); - let result: String = - bincode::deserialize_from(file).expect("Unable to deserialize from file"); - assert_eq!(result, val); - }).await; + let file = File::open(&p).expect("Unable to open file"); + let result: String = + bincode::deserialize_from(file).expect("Unable to deserialize from file"); + assert_eq!(result, val); + }) + .await; } #[tokio::test] async fn test_mutate() { let path = PathBuf::from("test_mutate"); let cleanup = CleanUp::new(&path); - cleanup.run(|p| async move { - let list = vec![1, 2, 3, 4]; - let mut file_linked_list = FileLinked::new(list, &p, DataFormat::Json).await.expect("Unable to create file linked object"); - let file_linked_list_arc = file_linked_list.readonly(); - let file_linked_list_ref = file_linked_list_arc.read().await; + cleanup + .run(|p| async move { + let list = vec![1, 2, 3, 4]; + let mut file_linked_list = FileLinked::new(list, &p, DataFormat::Json) + .await + .expect("Unable to create file linked object"); + let file_linked_list_arc = file_linked_list.readonly(); + let file_linked_list_ref = file_linked_list_arc.read().await; - assert_eq!(*file_linked_list_ref, vec![1, 2, 3, 4]); + assert_eq!(*file_linked_list_ref, vec![1, 2, 3, 4]); - drop(file_linked_list_ref); - file_linked_list.mutate(|v1| v1.push(5)).await.expect("Error mutating file linked object"); - let file_linked_list_arc = file_linked_list.readonly(); - let file_linked_list_ref = file_linked_list_arc.read().await; + drop(file_linked_list_ref); + file_linked_list + .mutate(|v1| v1.push(5)) + .await + .expect("Error mutating file linked object"); + let file_linked_list_arc = file_linked_list.readonly(); + let file_linked_list_ref = file_linked_list_arc.read().await; - assert_eq!(*file_linked_list_ref, vec![1, 2, 3, 4, 5]); + assert_eq!(*file_linked_list_ref, vec![1, 2, 3, 4, 5]); - drop(file_linked_list_ref); - file_linked_list.mutate(|v1| v1[1] = 1).await.expect("Error mutating file linked object"); - let file_linked_list_arc = file_linked_list.readonly(); - let file_linked_list_ref = file_linked_list_arc.read().await; + drop(file_linked_list_ref); + file_linked_list + .mutate(|v1| v1[1] = 1) + .await + .expect("Error mutating file linked object"); + let file_linked_list_arc = file_linked_list.readonly(); + let file_linked_list_ref = file_linked_list_arc.read().await; - assert_eq!(*file_linked_list_ref, vec![1, 1, 3, 4, 5]); + assert_eq!(*file_linked_list_ref, vec![1, 1, 3, 4, 5]); - drop(file_linked_list); - }).await; + drop(file_linked_list); + }) + .await; } #[tokio::test] async fn test_async_mutate() { let path = PathBuf::from("test_async_mutate"); let cleanup = CleanUp::new(&path); - cleanup.run(|p| async move { - let list = vec![1, 2, 3, 4]; - let mut file_linked_list = FileLinked::new(list, &p, DataFormat::Json).await.expect("Unable to create file linked object"); - let file_linked_list_arc = file_linked_list.readonly(); - let file_linked_list_ref = file_linked_list_arc.read().await; + cleanup + .run(|p| async move { + let list = vec![1, 2, 3, 4]; + let mut file_linked_list = FileLinked::new(list, &p, DataFormat::Json) + .await + .expect("Unable to create file linked object"); + let file_linked_list_arc = file_linked_list.readonly(); + let file_linked_list_ref = file_linked_list_arc.read().await; - assert_eq!(*file_linked_list_ref, vec![1, 2, 3, 4]); + assert_eq!(*file_linked_list_ref, vec![1, 2, 3, 4]); - drop(file_linked_list_ref); - file_linked_list.mutate_async(|v1| async move { - let mut v = v1.write().await; - v.push(5); - v[1] = 1; - Ok::<(), Error>(()) - }).await.expect("Error mutating file linked object").expect("Error mutating file linked object"); + drop(file_linked_list_ref); + file_linked_list + .mutate_async(|v1| async move { + let mut v = v1.write().await; + v.push(5); + v[1] = 1; + Ok::<(), Error>(()) + }) + .await + .expect("Error mutating file linked object") + .expect("Error mutating file linked object"); - let file_linked_list_arc = file_linked_list.readonly(); - let file_linked_list_ref = file_linked_list_arc.read().await; + let file_linked_list_arc = file_linked_list.readonly(); + let file_linked_list_ref = file_linked_list_arc.read().await; - assert_eq!(*file_linked_list_ref, vec![1, 1, 3, 4, 5]); + assert_eq!(*file_linked_list_ref, vec![1, 1, 3, 4, 5]); - drop(file_linked_list); - }).await; + drop(file_linked_list); + }) + .await; } #[tokio::test] async fn test_replace() { let path = PathBuf::from("test_replace"); let cleanup = CleanUp::new(&path); - cleanup.run(|p| async move { - let val1 = String::from("val1"); - let val2 = String::from("val2"); - let mut file_linked_list = FileLinked::new(val1.clone(), &p, DataFormat::Bincode).await.expect("Unable to create file linked object"); - let file_linked_list_arc = file_linked_list.readonly(); - let file_linked_list_ref = file_linked_list_arc.read().await; + cleanup + .run(|p| async move { + let val1 = String::from("val1"); + let val2 = String::from("val2"); + let mut file_linked_list = FileLinked::new(val1.clone(), &p, DataFormat::Bincode) + .await + .expect("Unable to create file linked object"); + let file_linked_list_arc = file_linked_list.readonly(); + let file_linked_list_ref = file_linked_list_arc.read().await; - assert_eq!(*file_linked_list_ref, val1); + assert_eq!(*file_linked_list_ref, val1); - file_linked_list.replace(val2.clone()).await.expect("Error replacing file linked object"); - let file_linked_list_arc = file_linked_list.readonly(); - let file_linked_list_ref = file_linked_list_arc.read().await; + file_linked_list + .replace(val2.clone()) + .await + .expect("Error replacing file linked object"); + let file_linked_list_arc = file_linked_list.readonly(); + let file_linked_list_ref = file_linked_list_arc.read().await; - assert_eq!(*file_linked_list_ref, val2); + assert_eq!(*file_linked_list_ref, val2); - drop(file_linked_list); - }).await; + drop(file_linked_list); + }) + .await; } #[tokio::test] - async fn test_from_file(){ + async fn test_from_file() { let path = PathBuf::from("test_from_file"); let cleanup = CleanUp::new(&path); - cleanup.run(|p| async move { - let value: Vec = vec![2.0, 3.0, 5.0]; - let file = File::create(&p).expect("Unable to create file"); + cleanup + .run(|p| async move { + let value: Vec = vec![2.0, 3.0, 5.0]; + let file = File::create(&p).expect("Unable to create file"); - bincode::serialize_into(&file, &value).expect("Unable to serialize into file"); - drop(file); + bincode::serialize_into(&file, &value).expect("Unable to serialize into file"); + drop(file); - let linked_object: FileLinked> = FileLinked::from_file(&p, DataFormat::Bincode).expect("Unable to create file linked object"); - let linked_object_arc = linked_object.readonly(); - let linked_object_ref = linked_object_arc.read().await; + let linked_object: FileLinked> = + FileLinked::from_file(&p, DataFormat::Bincode) + .expect("Unable to create file linked object"); + let linked_object_arc = linked_object.readonly(); + let linked_object_ref = linked_object_arc.read().await; - assert_eq!(*linked_object_ref, value); + assert_eq!(*linked_object_ref, value); - drop(linked_object); - }).await; + drop(linked_object); + }) + .await; } } diff --git a/gemla/src/bin/fighter_nn/fighter_context.rs b/gemla/src/bin/fighter_nn/fighter_context.rs index c631627..4b2a90b 100644 --- a/gemla/src/bin/fighter_nn/fighter_context.rs +++ b/gemla/src/bin/fighter_nn/fighter_context.rs @@ -1,19 +1,23 @@ use std::sync::Arc; +use serde::ser::SerializeTuple; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use tokio::sync::Semaphore; const SHARED_SEMAPHORE_CONCURRENCY_LIMIT: usize = 50; +const VISIBLE_SIMULATIONS_CONCURRENCY_LIMIT: usize = 1; #[derive(Debug, Clone)] pub struct FighterContext { pub shared_semaphore: Arc, + pub visible_simulations: Arc, } impl Default for FighterContext { fn default() -> Self { FighterContext { shared_semaphore: Arc::new(Semaphore::new(SHARED_SEMAPHORE_CONCURRENCY_LIMIT)), + visible_simulations: Arc::new(Semaphore::new(VISIBLE_SIMULATIONS_CONCURRENCY_LIMIT)), } } } @@ -28,19 +32,47 @@ impl Serialize for FighterContext { // This part is tricky since Semaphore does not expose its initial permits. // You might need to store the concurrency limit as a separate field if this assumption doesn't hold. let concurrency_limit = SHARED_SEMAPHORE_CONCURRENCY_LIMIT; - serializer.serialize_u64(concurrency_limit as u64) + let visible_concurrency_limit = VISIBLE_SIMULATIONS_CONCURRENCY_LIMIT; + // serializer.serialize_u64(concurrency_limit as u64) + + // Serialize the concurrency limit as a tuple + let mut state = serializer.serialize_tuple(2)?; + state.serialize_element(&concurrency_limit)?; + state.serialize_element(&visible_concurrency_limit)?; + state.end() } } // Custom deserialization to reconstruct the FighterContext from a concurrency limit. impl<'de> Deserialize<'de> for FighterContext { - fn deserialize(deserializer: D) -> Result + fn deserialize(_: D) -> Result where D: Deserializer<'de>, { - let concurrency_limit = u64::deserialize(deserializer)?; + // Deserialize the tuple Ok(FighterContext { - shared_semaphore: Arc::new(Semaphore::new(concurrency_limit as usize)), + shared_semaphore: Arc::new(Semaphore::new(SHARED_SEMAPHORE_CONCURRENCY_LIMIT)), + visible_simulations: Arc::new(Semaphore::new(VISIBLE_SIMULATIONS_CONCURRENCY_LIMIT)), }) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialization() { + let context = FighterContext::default(); + let serialized = serde_json::to_string(&context).unwrap(); + let deserialized: FighterContext = serde_json::from_str(&serialized).unwrap(); + assert_eq!( + context.shared_semaphore.available_permits(), + deserialized.shared_semaphore.available_permits() + ); + assert_eq!( + context.visible_simulations.available_permits(), + deserialized.visible_simulations.available_permits() + ); + } +} diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index ee99b8c..9d642a9 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -162,6 +162,7 @@ impl GeneticNode for FighterNN { for i in 0..self.population_size { let self_clone = self.clone(); let semaphore_clone = context.gemla_context.shared_semaphore.clone(); + let display_simulation_semaphore = context.gemla_context.visible_simulations.clone(); let task = async move { let nn = self_clone @@ -176,6 +177,7 @@ impl GeneticNode for FighterNN { let folder = self_clone.folder.clone(); let generation = self_clone.generation; let semaphore_clone = semaphore_clone.clone(); + let display_simulation_semaphore = display_simulation_semaphore.clone(); let random_nn = folder .join(format!("{}", generation)) @@ -188,7 +190,19 @@ impl GeneticNode for FighterNN { .await .with_context(|| "Failed to acquire semaphore permit")?; - let (score, _) = run_1v1_simulation(&nn_clone, &random_nn).await?; + let display_simulation = + match display_simulation_semaphore.try_acquire_owned() { + Ok(s) => Some(s), + Err(_) => None, + }; + + let (score, _) = if let Some(display_simulation) = display_simulation { + let result = run_1v1_simulation(&nn_clone, &random_nn, true).await?; + drop(display_simulation); + result + } else { + run_1v1_simulation(&nn_clone, &random_nn, false).await? + }; drop(permit); @@ -206,7 +220,7 @@ impl GeneticNode for FighterNN { Ok(scores) => scores.into_iter().sum::() / SIMULATION_ROUNDS as f32, Err(e) => return Err(e), // Return the error if results collection failed }; - trace!("NN {:06}_fighter_nn_{} scored {}", self_clone.id, i, score); + debug!("NN {:06}_fighter_nn_{} scored {}", self_clone.id, i, score); Ok((i, score)) }; @@ -366,6 +380,7 @@ impl GeneticNode for FighterNN { .join(right.generation.to_string()) .join(right.get_individual_id(right_nn_id)); let semaphore_clone = gemla_context.shared_semaphore.clone(); + let display_simulation_semaphore = gemla_context.visible_simulations.clone(); let future = async move { let permit = semaphore_clone @@ -373,8 +388,19 @@ impl GeneticNode for FighterNN { .await .with_context(|| "Failed to acquire semaphore permit")?; - let (left_score, right_score) = - run_1v1_simulation(&left_nn_path, &right_nn_path).await?; + let display_simulation = match display_simulation_semaphore.try_acquire_owned() { + Ok(s) => Some(s), + Err(_) => None, + }; + + let (left_score, right_score) = if let Some(display_simulation) = display_simulation + { + let result = run_1v1_simulation(&left_nn_path, &right_nn_path, true).await?; + drop(display_simulation); + result + } else { + run_1v1_simulation(&left_nn_path, &right_nn_path, false).await? + }; drop(permit); @@ -398,6 +424,8 @@ impl GeneticNode for FighterNN { // Use the sigmoid function to determine lerp amount let lerp_amount = 1.0 / (1.0 + (-score_difference).exp()); + debug!("Lerp amount: {}", lerp_amount); + let mut nn_shapes = HashMap::new(); // Function to copy NNs from a source FighterNN to the new folder. @@ -517,7 +545,11 @@ impl FighterNN { } } -async fn run_1v1_simulation(nn_path_1: &Path, nn_path_2: &Path) -> Result<(f32, f32), Error> { +async fn run_1v1_simulation( + nn_path_1: &Path, + nn_path_2: &Path, + display_simulation: bool, +) -> Result<(f32, f32), Error> { // Construct the score file path let base_folder = nn_path_1.parent().unwrap(); let nn_1_id = nn_path_1.file_stem().unwrap().to_str().unwrap(); @@ -574,7 +606,7 @@ async fn run_1v1_simulation(nn_path_1: &Path, nn_path_2: &Path) -> Result<(f32, trace!("Running simulation for {} vs {}", nn_1_id, nn_2_id); - let _output = if thread_rng().gen_range(0..100) < 1 { + let _output = if display_simulation { Command::new(GAME_EXECUTABLE_PATH) .arg(&config1_arg) .arg(&config2_arg) From 98803b370003ec01298dcf7390a265a2751e0dfa Mon Sep 17 00:00:00 2001 From: vandomej Date: Mon, 8 Apr 2024 15:39:29 -0700 Subject: [PATCH 22/26] Finished round 2 adjustments --- analysis.py | 3 + gemla/build.rs | 4 +- gemla/round2.json | 1 + gemla/src/bin/fighter_nn/fighter_context.rs | 3 +- gemla/src/bin/fighter_nn/mod.rs | 249 ++++++++++++++---- .../bin/fighter_nn/neural_network_utility.rs | 35 ++- gemla/src/core/mod.rs | 14 +- 7 files changed, 236 insertions(+), 73 deletions(-) create mode 100644 gemla/round2.json diff --git a/analysis.py b/analysis.py index b205d7c..419d0e1 100644 --- a/analysis.py +++ b/analysis.py @@ -65,6 +65,9 @@ def traverse(node, graph, parent=None): overall_max_score_individual = individual_with_max_score_for_gen overall_max_score_gen = gen + # print debug statement + # print(f"Node {node_id}: Max score: {overall_max_score:.6f} (Individual {overall_max_score_individual} in Gen {overall_max_score_gen})") + # print(f"Left: {node.get('left')}, Right: {node.get('right')}") label = f"{node_id}\nGenerations: {generations}, Population: {population_size}\nMax score: {overall_max_score:.6f} (Individual {overall_max_score_individual} in Gen {overall_max_score_gen})" else: label = node_id diff --git a/gemla/build.rs b/gemla/build.rs index d50ed3e..e6b8ca6 100644 --- a/gemla/build.rs +++ b/gemla/build.rs @@ -1,9 +1,9 @@ fn main() { // Replace this with the path to the directory containing `fann.lib` - let lib_dir = "/opt/homebrew/Cellar/fann/2.2.0/lib"; + let lib_dir = "F://vandomej/Downloads/vcpkg/packages/fann_x64-windows/lib"; println!("cargo:rustc-link-search=native={}", lib_dir); - println!("cargo:rustc-link-lib=dylib=fann"); + println!("cargo:rustc-link-lib=static=fann"); // Use `dylib=fann` instead of `static=fann` if you're linking dynamically // If there are any additional directories where the compiler can find header files, you can specify them like this: diff --git a/gemla/round2.json b/gemla/round2.json new file mode 100644 index 0000000..b2401a8 --- /dev/null +++ b/gemla/round2.json @@ -0,0 +1 @@ +[{"val":{"node":null,"state":"Initialize","generation":1,"max_generations":5,"id":"5ec4f3dc-8123-409e-825c-662681228ab7"},"left":{"val":{"node":{"id":"50ebdb65-5d8c-4ae0-a287-cf1752ee3e7a","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_50ebdb65-5d8c-4ae0-a287-cf1752ee3e7a","population_size":50,"generation":4,"scores":[{"31":-0.13612905,"33":-1.0776138,"49":1.0980284,"30":-1.9793463,"17":1.7722206,"35":0.5282012,"43":1.0499356,"10":-1.0785636,"40":1.1580324,"21":0.55297184,"19":2.3659606,"8":-1.1596327,"45":1.2928445,"15":-0.38365746,"7":-1.3709673,"18":2.3096547,"22":2.0469928,"28":-0.4978822,"41":4.144234,"5":0.07915659,"26":0.90480375,"37":1.494353,"39":-2.3988552,"47":0.34005648,"6":-7.033618,"27":1.0777633,"16":2.901632,"14":1.9946716,"20":3.7013183,"3":-2.2309659,"36":0.70247716,"4":-6.9653826,"29":0.45382744,"1":-2.6762161,"11":0.036121655,"13":-0.96337116,"34":-0.2231338,"38":-0.55380166,"42":1.7713648,"44":1.4386444,"9":0.028786521,"48":-4.3657136,"12":2.421192,"23":3.9476352,"24":-0.66796416,"25":-3.155373,"32":1.4074609,"0":-0.1740944,"46":0.6339666,"2":-0.53725517},{"4":0.7436782,"3":3.2122047,"5":2.1668441,"20":1.110868,"39":-2.9981642,"13":2.538597,"41":2.149324,"2":4.4572535,"10":1.7422009,"26":-4.81853,"29":-2.5022538,"15":0.740168,"23":5.280815,"32":0.2042454,"24":1.3782952,"8":1.3916748,"16":1.8820988,"27":-1.0135753,"44":4.2513857,"45":-7.277108,"48":-1.9649296,"28":-2.5748737,"36":-2.348684,"38":-4.5012045,"9":2.8673189,"21":0.24331684,"0":0.81623286,"40":-1.0026308,"43":-1.384711,"33":0.810703,"11":1.3250918,"12":1.6499374,"42":-2.7954354,"47":-1.0316321,"18":0.75795716,"37":1.5436422,"35":-2.120893,"49":1.3816634,"14":0.5867568,"17":4.5584574,"19":1.022777,"25":-2.384229,"46":-2.6336,"22":-3.5684505,"6":2.907734,"34":0.3625866,"1":1.4271891,"7":3.1036994,"31":0.25269595,"30":-4.3955135},{"29":-8.933199,"25":0.8984653,"11":0.40797964,"13":2.9981232,"16":1.4885747,"19":2.711595,"30":-1.1489832,"32":-2.470939,"33":-6.3783817,"35":-2.0185058,"37":-2.388184,"12":0.82435036,"9":3.27996,"28":-4.4827237,"39":-2.178865,"41":-1.7175516,"8":0.8589166,"24":1.9250387,"42":-1.1390394,"2":4.301921,"10":0.27182764,"31":-2.491881,"3":4.3458285,"44":-3.2156835,"17":0.30330983,"45":-1.0126257,"48":-7.657384,"49":-2.3429596,"38":-1.107363,"22":0.24029942,"15":3.652279,"26":0.6514605,"34":-3.7715619,"21":-0.17929637,"7":0.930509,"47":-6.146611,"18":-0.08523829,"5":4.084977,"20":0.6663896,"23":2.6454415,"1":0.7503186,"14":-2.896947,"40":-4.771443,"43":-4.230453,"46":-6.056484,"6":2.0080876,"27":-1.3334317,"36":-1.4744998,"4":2.8780289,"0":1.2278525},{"25":-3.211018,"12":0.7650175,"5":1.7488182,"46":4.561215,"39":-0.8045467,"45":0.5278336,"6":1.3769066,"9":1.4209664,"21":-1.5987527,"35":-3.4995422,"34":2.4080498,"15":1.3436749,"20":-0.8802644,"24":0.43879455,"16":1.7340788,"33":-0.36120838,"49":-0.82267416,"17":-0.90683824,"41":-4.4410596,"3":3.8727448,"8":-0.1619916,"0":-1.2115911,"14":0.1691064,"18":0.17399707,"26":-0.35883158,"31":-1.3777262,"30":-1.5434114,"13":1.8553307,"44":-1.6750336,"10":1.5830498,"48":-2.2214565,"32":-1.914746,"1":3.3461983,"28":-0.8201621,"40":-2.3096998,"19":-0.114658594,"43":-3.153609,"38":0.8486921,"7":-0.6367208,"22":2.2888017,"4":1.4505427,"37":-1.3729974,"27":0.19734183,"36":-0.059907578,"42":-0.3139604,"23":3.9043097,"47":1.7215679,"11":1.9946696,"29":-5.348122,"2":3.916179},{"48":0.8495762,"33":-1.5749425,"7":1.8273497,"37":-0.0934452,"9":0.8297514,"32":-1.9380062,"5":2.6225371,"0":2.9893174,"28":1.1498768,"30":-3.0286043,"16":0.7810628,"36":-3.065391,"39":-0.6764022,"29":-2.6871326,"40":-1.1660333,"34":-7.6445327,"3":3.5084794,"38":-2.1503594,"12":-1.3775728,"17":-0.28026563,"2":0.8047453,"26":-0.2800478,"10":1.8954436,"41":-0.9755514,"43":-2.3991253,"44":1.7079166,"45":-3.5458252,"8":1.9785389,"24":1.5254066,"11":0.24450493,"18":1.1810827,"15":2.0651846,"14":2.0105624,"13":3.0678258,"19":1.1483902,"20":0.7748546,"23":1.3448334,"25":0.0561754,"35":-3.5130627,"46":-1.4552925,"27":2.6987588,"49":0.06736002,"4":4.091585,"22":0.54525864,"6":1.2180727,"21":2.4670484,"42":3.6728942,"1":3.7313595,"31":-1.5604084,"47":2.743227}],"nn_shapes":{"44":[18,27,29,27,13,22,23,30,21,8],"32":[18,29,18,8],"12":[18,7,5,6,30,29,31,13,7,8],"7":[18,5,15,22,7,8],"17":[18,3,5,8],"31":[18,33,16,11,21,24,8],"25":[18,12,31,18,17,22,11,8],"18":[18,4,22,8],"34":[18,3,20,14,27,3,23,32,15,16,8],"35":[18,19,12,33,8],"43":[18,11,8],"6":[18,25,22,27,18,8],"36":[18,23,6,34,15,15,19,31,8],"21":[18,4,27,26,8],"0":[18,27,21,25,6,8],"20":[18,29,24,16,20,32,30,23,8],"23":[18,4,30,26,15,30,12,5,21,27,8],"39":[18,16,8],"10":[18,13,8],"9":[18,21,9,17,4,23,4,8],"19":[18,24,6,5,31,8],"29":[18,23,18,30,9,4,6,25,10,8],"24":[18,8,8],"11":[18,19,21,6,32,31,9,10,8],"3":[18,34,21,18,23,20,29,4,8],"8":[18,9,28,14,34,19,30,25,8],"4":[18,24,19,16,19,5,25,3,13,4,8],"5":[18,16,24,20,16,8],"49":[18,12,14,8],"47":[18,6,7,8],"28":[18,16,20,14,14,8],"27":[18,12,33,31,24,8],"46":[18,5,7,30,5,14,8],"13":[18,23,31,19,31,17,10,3,8],"26":[18,6,30,9,30,13,34,11,31,4,8],"14":[18,29,23,7,5,29,9,8],"15":[18,10,6,8],"30":[18,31,27,7,24,24,10,34,17,8],"38":[18,34,8],"41":[18,7,17,12,15,23,21,8,14,8],"45":[18,4,8],"48":[18,31,18,12,28,8],"42":[18,21,10,15,3,30,14,8,9,8],"1":[18,30,8],"16":[18,16,29,18,5,6,22,23,8],"2":[18,11,12,6,8],"40":[18,33,11,9,11,33,12,29,8],"22":[18,10,14,28,29,11,18,20,10,8],"37":[18,27,28,25,25,8],"33":[18,8,32,17,16,20,14,8]},"crossbreed_segments":17,"weight_initialization_range":{"start":-0.8264158,"end":0.878737},"minor_mutation_rate":0.6007006,"major_mutation_rate":0.69530445,"mutation_weight_range":{"start":-0.4734988,"end":0.4734988}},"state":"Finish","generation":5,"max_generations":5,"id":"50ebdb65-5d8c-4ae0-a287-cf1752ee3e7a"},"left":{"val":{"node":{"id":"d3819667-2b18-4b95-9a28-26c6e03dc476","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_d3819667-2b18-4b95-9a28-26c6e03dc476","population_size":50,"generation":4,"scores":[{"36":0.52841055,"15":-1.1991132,"37":-3.2981744,"49":-1.8880517,"13":-1.6512722,"32":1.0124598,"43":2.494171,"26":-1.3937424,"19":3.493512,"21":2.3532522,"17":2.689321,"31":2.2110536,"46":-0.4825391,"6":1.8422686,"10":-0.32022244,"22":-0.3459033,"42":-1.683349,"23":4.053544,"47":-2.2043169,"1":1.863919,"12":2.0911965,"38":0.24497437,"20":1.547824,"18":1.7499046,"5":0.49471003,"14":1.9232328,"30":0.8192028,"9":2.6325006,"4":1.8692623,"11":1.515634,"24":3.58217,"25":-1.2022996,"28":1.0040348,"29":-1.4738817,"41":3.5459945,"2":-3.6696267,"0":0.4801852,"3":-0.09719024,"39":-3.908245,"33":-0.91764957,"40":0.8810172,"44":2.2009876,"45":-1.3817523,"27":-0.19515392,"35":-0.027263165,"34":-3.189738,"16":3.8335247,"48":0.9000354,"7":1.028139,"8":2.6590905},{"10":1.3170006,"17":2.231814,"34":-1.2865344,"45":2.3518786,"41":3.6458764,"21":0.8024928,"16":1.9739323,"22":1.8600712,"25":-9.58074,"1":0.24613556,"32":-3.8310456,"5":2.9235337,"12":3.2730007,"38":0.218226,"42":3.8644855,"13":2.9329138,"27":-6.526806,"40":1.8908739,"49":-5.292937,"44":-1.129288,"28":-6.468558,"3":4.0713334,"36":-1.6670258,"0":3.819156,"23":-1.2354233,"24":3.5202663,"37":-12.305478,"39":-5.751393,"29":-1.6295974,"4":4.000201,"15":1.8389952,"20":2.0815675,"48":0.855487,"26":-1.5036308,"30":-3.9943376,"14":2.4486287,"47":2.6005633,"31":-3.4889805,"43":2.9267437,"46":-0.8074959,"35":-4.0209146,"11":0.9406686,"2":3.3860729,"9":4.1048174,"33":-6.9062905,"8":5.9823117,"19":-0.19810537,"18":-1.2411083,"7":2.8926134,"6":-3.7741578},{"7":0.039615013,"19":2.1138473,"14":-3.9792695,"26":1.5719395,"2":-1.1006941,"11":0.29687914,"31":-1.6652381,"46":-4.654462,"48":1.0881627,"12":0.6574399,"13":2.580459,"47":0.5501457,"30":1.0701891,"1":3.955607,"4":2.6093411,"5":3.8768296,"23":-0.16426,"28":-5.369283,"42":-0.7794517,"39":-4.815303,"25":-11.748198,"27":-6.5224624,"3":3.2421424,"6":1.9797084,"16":0.32828337,"24":2.2681708,"8":3.5486038,"40":-0.5599359,"18":1.7272246,"41":-0.6892798,"49":3.3327858,"35":-6.8564734,"15":2.197027,"43":1.1656187,"9":0.5838804,"10":1.7632539,"44":1.3067775,"38":-4.1817884,"29":-0.010108185,"17":1.4017338,"20":1.1528027,"21":-2.0243993,"0":1.8688208,"37":-3.9298809,"32":1.608084,"34":0.29795903,"22":-1.0562317,"33":1.5627401,"36":1.9605706,"45":-4.1273623},{"22":3.7864814,"48":-5.1172805,"44":-7.1750364,"45":-7.1881766,"27":-3.211064,"18":2.5377452,"21":-0.0047807456,"23":-2.5914617,"38":-2.026207,"13":2.7289197,"14":-0.86196077,"47":2.3228345,"32":-3.6571102,"33":-1.8783233,"6":2.7211597,"26":-1.2817702,"40":-5.2434363,"43":-1.3648752,"29":-3.2677646,"37":-5.4217234,"49":6.32213,"31":0.65605927,"0":4.0934405,"16":-1.9610307,"15":-0.5933281,"35":1.0039356,"42":-5.741083,"41":0.1720238,"3":-0.526399,"7":0.4030818,"36":3.3475738,"17":-1.7582073,"30":-1.572907,"46":-3.8359694,"24":2.9443383,"8":1.5571549,"1":3.898974,"2":4.2115107,"11":0.589545,"12":2.5865626,"25":0.7350615,"34":-3.614159,"10":3.3951313,"19":-0.8030449,"20":0.31260815,"28":1.0681746,"9":1.0806137,"4":3.06236,"39":-2.972804,"5":0.6295648},{"47":-1.8156517,"10":2.70822,"11":0.013542938,"48":-6.912471,"5":-0.35663193,"44":-3.8048778,"49":-1.3085688,"14":1.7900337,"24":-5.8084245,"8":2.2216375,"25":-6.1367373,"7":-0.45275903,"30":-1.7194979,"34":0.13795105,"42":-3.814258,"16":3.908256,"9":2.6561737,"13":-1.8770411,"21":1.3039081,"40":-0.24722286,"17":-2.5117145,"41":-5.126404,"4":-4.3981633,"15":2.1462803,"18":-1.8711113,"12":2.7365608,"6":-3.9147289,"20":1.0436176,"29":0.31122702,"2":4.1983523,"39":-0.13585219,"43":-7.8228645,"45":-1.9057821,"3":3.946886,"1":3.267223,"35":0.2371548,"31":1.5849909,"46":0.8540207,"37":0.3498088,"26":-7.9124327,"0":-0.50944835,"19":2.1885152,"32":4.8325567,"27":-4.7971716,"36":-2.634483,"22":2.0692124,"23":-0.3195792,"38":-8.581514,"28":-3.5876858,"33":-5.4614477}],"nn_shapes":{"49":[18,21,7,8],"19":[18,10,6,8],"15":[18,29,23,7,5,29,9,8],"21":[18,13,8],"46":[18,9,28,14,34,19,30,25,8],"20":[18,21,9,17,4,23,4,8],"29":[18,25,22,27,18,8],"41":[18,29,20,13,4,10,18,3,28,8],"27":[18,19,29,28,19,9,23,8],"31":[18,19,21,6,32,31,9,10,8],"12":[18,24,6,5,31,8],"2":[18,4,30,26,15,30,12,5,21,27,8],"34":[18,24,19,16,19,5,25,3,13,4,8],"45":[18,3,5,29,21,8],"28":[18,26,34,8],"0":[18,22,24,26,20,26,7,23,4,4,8],"13":[18,29,25,31,16,20,9,20,8,8],"7":[18,16,11,12,31,23,8],"11":[18,34,21,18,23,20,29,4,8],"38":[18,16,4,3,8,8,34,15,8],"44":[18,25,9,13,26,10,30,28,8],"16":[18,4,27,26,8],"6":[18,30,29,12,8],"39":[18,11,12,6,8],"18":[18,12,11,13,20,25,8],"5":[18,7,23,7,5,15,7,8],"22":[18,23,31,19,31,17,10,3,8],"9":[18,3,5,8],"23":[18,27,21,25,6,8],"26":[18,15,34,8],"24":[18,23,13,23,6,22,17,12,8],"30":[18,32,4,6,5,7,6,31,8],"32":[18,8,8],"35":[18,16,24,20,16,8],"37":[18,5,15,22,7,8],"40":[18,30,8],"42":[18,30,27,6,31,6,8],"43":[18,4,21,19,8],"10":[18,4,22,8],"47":[18,31,19,8],"3":[18,10,14,28,29,11,18,20,10,8],"25":[18,32,20,20,33,3,26,27,19,14,8],"14":[18,7,5,6,30,29,31,13,7,8],"17":[18,15,4,15,25,8],"33":[18,32,8],"4":[18,12,14,3,22,20,8],"36":[18,23,8,3,20,8,5,11,28,12,8],"8":[18,16,29,18,5,6,22,23,8],"48":[18,17,11,11,23,34,8],"1":[18,29,24,16,20,32,30,23,8]},"crossbreed_segments":17,"weight_initialization_range":{"start":-1.4009845,"end":0.71760863},"minor_mutation_rate":0.58211184,"major_mutation_rate":0.3748024,"mutation_weight_range":{"start":-0.7133639,"end":0.7133639}},"state":"Finish","generation":5,"max_generations":5,"id":"d3819667-2b18-4b95-9a28-26c6e03dc476"},"left":{"val":{"node":{"id":"0c1e64dc-6ddf-4dbb-bf6e-e8218b925194","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_0c1e64dc-6ddf-4dbb-bf6e-e8218b925194","population_size":50,"generation":4,"scores":[{"16":2.7842956,"17":3.2546368,"18":2.834027,"22":3.6374855,"23":0.55781853,"39":1.5122628,"30":-0.0950552,"38":-1.6534564,"42":0.9564872,"45":2.796212,"32":3.1485744,"1":-0.056508802,"26":0.27695063,"27":-0.9529072,"35":-0.33414435,"41":0.9368752,"49":1.2474676,"4":1.9148009,"29":-0.8555522,"19":0.7134926,"20":4.182901,"36":2.0954838,"25":1.1097888,"24":1.1636646,"48":2.0578096,"2":0.13178372,"11":3.930512,"21":2.7500353,"28":-2.7004008,"8":-0.14735499,"0":2.4760363,"12":1.8176826,"33":-0.8801649,"5":1.1445743,"43":1.6762855,"44":1.7246205,"14":0.12700202,"13":-1.0921293,"3":0.8263828,"9":-0.63989145,"40":1.0889086,"15":0.38060492,"6":2.162825,"34":0.8519516,"7":1.5481932,"37":-0.30656368,"47":0.12655163,"31":-0.334538,"46":-0.10322921,"10":2.1048977},{"2":4.0336885,"11":1.9123293,"28":1.1152416,"0":2.6964252,"8":2.643211,"5":1.9824579,"14":1.9350994,"20":-2.117892,"27":-1.8575739,"32":3.808883,"40":-1.701194,"43":1.2392315,"45":-1.0804688,"4":-1.0602609,"41":-2.600569,"47":-3.656672,"48":-1.1821935,"22":3.038847,"18":2.4507322,"17":1.0593803,"26":4.082426,"37":3.3133476,"15":2.7473907,"42":3.6408525,"33":2.8012383,"6":1.2478402,"3":3.200624,"44":2.551259,"16":2.6077023,"34":-3.2371726,"30":-5.005698,"38":-2.7984025,"19":0.5384928,"23":2.693526,"12":0.55265176,"25":-3.2805107,"36":1.1476547,"46":-2.3774133,"1":3.9180245,"29":1.2915124,"7":2.743547,"9":1.824632,"31":-1.9200878,"35":-8.691515,"49":-9.31593,"39":-0.28261098,"10":2.151708,"21":2.0202444,"24":-0.98241556,"13":1.4065293},{"42":-0.1698114,"45":-0.9050466,"12":0.33145928,"20":2.1155891,"48":1.5600955,"0":3.3518238,"36":-3.1458013,"3":-0.07596121,"8":2.8887057,"24":1.2255046,"40":-2.242703,"46":-0.57636845,"37":-6.9247026,"47":-0.058634616,"10":0.91950357,"5":0.44212827,"13":2.6170678,"28":2.0092816,"30":-6.610072,"33":-8.984638,"18":2.6534104,"35":0.76075226,"6":3.1536205,"41":-0.43077993,"27":2.3785758,"19":3.0031989,"4":1.9033867,"7":2.1948285,"17":1.896376,"2":3.6457944,"11":3.13304,"25":3.723556,"29":-3.2704506,"32":1.8960766,"14":0.113289595,"39":-0.10378437,"34":-3.1352968,"15":0.8491783,"38":0.1912586,"49":-1.1872209,"9":2.6683042,"16":5.404914,"22":2.237846,"1":3.7422562,"21":1.3533652,"23":2.1378658,"44":3.8486843,"26":-0.4825582,"43":0.19282512,"31":0.47917405},{"28":-7.894442,"18":-0.5081554,"12":4.472903,"19":2.968141,"29":0.3170418,"30":-2.707025,"49":-0.5579669,"27":-0.058833785,"17":2.36423,"31":1.5799856,"39":-4.114499,"3":0.97037774,"8":2.5219383,"34":-10.829961,"6":3.2782185,"35":-4.850678,"22":1.2201262,"42":-1.1182394,"44":1.4363219,"48":-7.355731,"7":4.1624565,"11":2.2376986,"32":-0.18231583,"1":0.35062942,"15":3.3270416,"20":2.3351607,"26":3.0582035,"40":2.0540967,"5":0.055399038,"47":-3.5113704,"45":-3.669038,"4":3.82812,"2":3.748457,"9":2.0209565,"33":0.15889016,"21":1.0599203,"0":5.94346,"38":0.7334972,"41":2.1650863,"37":4.926632,"10":2.7536874,"14":1.9314429,"36":-2.9116273,"24":2.108854,"43":-6.7626557,"46":-4.723344,"25":-0.56377923,"23":1.9115753,"16":1.0394429,"13":2.78011},{"15":1.4749693,"13":1.7178307,"11":2.5947552,"20":1.5962856,"5":3.9152215,"10":1.9538119,"1":-0.40396795,"29":-2.258254,"37":1.105455,"17":2.5183578,"16":0.46422988,"24":2.2004254,"12":3.1671283,"48":4.001414,"2":1.536825,"21":1.3735349,"27":-1.3144708,"39":-5.0491495,"49":2.1212723,"34":-0.9968918,"40":-0.9705099,"7":3.0003047,"44":2.7472353,"47":-0.22038798,"45":-4.9677887,"26":-7.6167555,"38":-1.8368845,"22":2.5509207,"23":1.6071596,"30":-4.427607,"9":-1.5331998,"6":1.4454396,"18":1.8494036,"19":2.0788915,"31":1.3762347,"32":-1.3371822,"33":-2.4997528,"41":-1.9396175,"43":3.1764946,"28":-3.7262979,"8":-0.8827422,"3":5.203038,"14":1.4151531,"36":1.4748793,"42":3.4031627,"4":4.065404,"0":1.4889172,"25":2.7390487,"46":-0.332081,"35":-1.5676318}],"nn_shapes":{"32":[18,15,28,16,12,9,17,13,34,20,8],"4":[18,27,21,25,6,8],"12":[18,12,11,13,20,25,8],"22":[18,29,25,31,16,20,9,20,8,8],"30":[18,11,30,9,13,27,32,8],"27":[18,19,29,14,29,3,8,8],"24":[18,34,21,18,23,20,29,4,8],"34":[18,26,8,9,29,8],"21":[18,11,3,20,7,8],"1":[18,10,8],"44":[18,4,27,26,8],"9":[18,26,13,16,17,7,3,8],"5":[18,13,8],"48":[18,23,31,19,31,17,10,3,8],"31":[18,10,33,8],"28":[18,12,12,10,27,23,30,7,8],"40":[18,28,30,21,8],"33":[18,8,6,5,21,34,11,25,10,8],"46":[18,18,31,14,34,23,30,8],"29":[18,28,5,6,18,31,4,6,13,8],"13":[18,30,29,12,8],"23":[18,7,23,7,5,15,7,8],"16":[18,13,12,32,16,34,8],"35":[18,4,5,20,15,8],"11":[18,7,5,6,30,29,31,13,7,8],"8":[18,7,9,24,24,24,29,24,8],"14":[18,12,4,27,27,16,8],"19":[18,3,5,8],"18":[18,16,11,12,31,23,8],"38":[18,25,24,23,22,19,8],"42":[18,21,9,17,4,23,4,8],"15":[18,29,24,16,20,32,30,23,8],"37":[18,25,21,17,12,8],"3":[18,23,13,23,6,22,17,12,8],"17":[18,24,6,5,31,8],"20":[18,12,14,3,22,20,8],"6":[18,23,7,18,18,19,6,6,33,33,8],"36":[18,22,24,26,20,26,7,23,4,4,8],"43":[18,10,6,8],"45":[18,20,31,28,33,23,8],"49":[18,4,22,8],"47":[18,33,27,24,13,7,30,21,19,8],"25":[18,29,23,7,5,29,9,8],"2":[18,10,14,28,29,11,18,20,10,8],"10":[18,16,29,18,5,6,22,23,8],"39":[18,5,11,33,33,22,19,8],"41":[18,34,8],"7":[18,15,4,15,25,8],"26":[18,23,4,8],"0":[18,4,30,26,15,30,12,5,21,27,8]},"crossbreed_segments":16,"weight_initialization_range":{"start":-1.3029977,"end":0.51041603},"minor_mutation_rate":0.4312437,"major_mutation_rate":0.2126301,"mutation_weight_range":{"start":-0.86694086,"end":0.86694086}},"state":"Finish","generation":5,"max_generations":5,"id":"0c1e64dc-6ddf-4dbb-bf6e-e8218b925194"},"left":{"val":{"node":{"id":"fd37c853-1504-4464-8897-1daff7a72be5","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_fd37c853-1504-4464-8897-1daff7a72be5","population_size":50,"generation":4,"scores":[{"42":-2.2721553,"22":1.0232317,"40":-5.9998856,"1":-3.994938,"25":-9.930249,"27":-7.8477364,"37":-5.496006,"5":1.1646187,"46":-4.96828,"3":-1.1402376,"13":1.9225012,"26":-1.3158404,"14":2.4945993,"30":-7.240546,"23":3.8293235,"29":-3.8496826,"21":3.7215874,"49":-9.225942,"31":-8.099161,"11":0.88319874,"7":-2.0457823,"2":-0.12721822,"45":-5.7793913,"28":-4.1380286,"15":2.9860673,"20":2.128657,"32":-3.8224225,"9":2.4512286,"24":5.1030297,"19":0.27070022,"41":-4.9243727,"17":-1.272063,"47":-3.7681346,"12":0.56694984,"48":1.2148348,"44":-5.1761513,"34":-7.1951623,"36":-6.7270365,"33":-8.101049,"35":-5.470336,"38":-2.4845133,"39":-7.134122,"43":-8.256643,"4":1.1818645,"6":0.9297161,"8":-0.4258314,"10":-0.3768764,"16":-0.06290223,"18":2.514954,"0":-2.4905317},{"27":0.95811284,"46":2.4985795,"8":-1.7966896,"6":3.3313987,"37":2.4091609,"9":0.89002323,"10":-0.24809322,"28":2.7617157,"15":1.6055084,"30":-1.4701109,"31":0.90125,"33":-1.4433162,"36":-1.8132203,"12":-0.7547363,"3":2.346582,"44":-4.4732213,"45":0.81152236,"48":4.4929247,"40":-4.770377,"11":0.87109774,"16":0.63175476,"35":-5.9830647,"1":3.976232,"39":-2.9760668,"7":1.3261242,"20":0.057714842,"34":-3.805713,"17":2.653192,"43":-2.0348437,"2":3.822759,"21":0.73448956,"29":-6.0006204,"49":-1.1507516,"25":1.983723,"22":0.6264278,"38":2.6556149,"26":-4.3593946,"42":0.35124415,"4":2.6484601,"24":0.44920617,"18":-1.1584501,"0":3.052092,"14":1.2551721,"41":-1.5921788,"13":-0.44852152,"47":-2.7429378,"23":-3.6057796,"19":2.7150357,"5":2.6166,"32":-5.0448375},{"11":0.24148598,"16":1.1744473,"23":0.87205726,"27":0.3939582,"26":-4.8855453,"7":0.73869574,"33":-3.6728272,"3":2.1907496,"37":-7.545512,"44":-1.4448053,"14":-0.97644913,"6":1.1288795,"30":-1.8206075,"35":2.1864667,"40":1.5490462,"8":2.7416787,"45":-5.4727135,"9":2.8946023,"47":1.2556527,"19":1.3244388,"31":-0.78719276,"5":3.572733,"18":1.7603016,"42":0.83883345,"43":-1.3601041,"49":-0.20906155,"46":-0.31403762,"13":2.7593741,"34":-3.5647533,"36":-4.0503144,"25":1.7323751,"28":-3.2712636,"10":1.7972724,"38":1.6502354,"39":-0.40534177,"41":-0.043547057,"0":3.3609338,"48":-2.3421752,"21":1.6295173,"12":-0.112102196,"24":0.1910994,"17":0.79251236,"22":0.5870294,"29":1.0588541,"4":3.7645988,"32":1.7781379,"1":4.139815,"2":3.7459385,"15":2.458321,"20":3.5645466},{"28":-0.3401602,"19":1.3713418,"5":3.5159538,"22":6.9944353,"31":-4.0907526,"36":1.895592,"4":-1.5086001,"15":2.4590595,"39":3.181663,"3":1.6952648,"47":-4.1927,"43":-0.6410026,"1":4.124709,"8":2.1984046,"42":-0.8253414,"25":-0.8266258,"27":-2.1104884,"41":-8.608764,"9":2.3678985,"0":4.0736403,"24":0.827624,"16":1.4439828,"32":0.6308866,"34":-1.3887271,"45":-0.5371846,"49":0.9086536,"12":2.6437063,"7":3.4995968,"13":0.5635818,"17":1.8616883,"23":1.22412,"26":2.1465325,"37":-2.5422812,"33":-2.0116632,"6":3.1111484,"14":2.2081165,"29":-1.4582347,"18":1.7700018,"38":-3.9882941,"2":3.620486,"10":1.5943378,"44":3.4898422,"46":-3.2846375,"40":-5.23321,"48":1.3899428,"21":2.149033,"11":3.0369506,"20":1.2599217,"30":-3.8670013,"35":-11.008142},{"12":2.35251,"13":2.1651278,"37":-7.216863,"20":2.3004658,"32":2.0750155,"7":-1.6450062,"38":-5.5044084,"21":2.023457,"9":2.7243361,"24":1.5391018,"17":-1.7105582,"23":1.9464728,"22":-0.13848563,"1":3.4145145,"29":1.8971004,"6":1.8432477,"2":4.0936923,"14":1.5620744,"28":-1.6894335,"31":2.6148155,"36":-0.6197856,"39":-7.6506586,"35":2.5756009,"8":3.1064951,"40":-3.3752155,"43":7.8085213,"46":-4.395103,"5":3.348603,"16":3.7527301,"47":1.8600842,"15":3.301286,"11":1.1328014,"18":0.058104564,"30":-1.3595872,"19":0.6658308,"10":2.5431685,"49":-1.0252998,"0":1.3744335,"4":2.5909457,"45":-3.7512455,"27":-1.452537,"34":0.92168653,"42":5.8911624,"26":-1.8472269,"44":-0.4115014,"41":-1.6939585,"25":-0.563695,"48":1.5778819,"33":-0.27565208,"3":2.497078}],"nn_shapes":{"42":[18,7,23,7,5,15,7,8],"37":[18,29,17,25,6,8],"13":[18,7,9,24,24,24,29,24,8],"33":[18,7,6,7,8],"45":[18,21,13,8],"3":[18,7,5,6,30,29,31,13,7,8],"0":[18,19,10,34,8],"8":[18,24,6,5,31,8],"23":[18,13,8],"10":[18,12,11,13,20,25,8],"25":[18,7,30,30,25,19,20,5,8],"17":[18,10,15,22,8],"24":[18,34,28,11,11,29,11,12,8],"35":[18,30,29,12,8],"38":[18,34,22,28,13,14,8],"41":[18,24,12,30,17,33,8],"31":[18,29,24,16,20,32,30,23,8],"40":[18,28,11,20,6,13,29,8],"2":[18,29,25,31,16,20,9,20,8,8],"16":[18,11,3,20,7,8],"1":[18,12,14,3,22,20,8],"34":[18,17,18,30,31,3,6,12,25,11,8],"20":[18,26,13,16,17,7,3,8],"39":[18,17,8,8],"27":[18,19,8,19,26,6,25,18,21,23,8],"21":[18,23,7,18,18,19,6,6,33,33,8],"26":[18,31,6,20,8],"28":[18,5,21,9,11,10,13,27,17,16,8],"19":[18,14,10,33,8,11,4,21,28,8],"29":[18,27,21,25,6,8],"46":[18,22,8],"49":[18,33,34,13,8],"6":[18,10,14,28,29,11,18,20,10,8],"47":[18,23,13,23,6,22,17,12,8],"48":[18,10,8],"44":[18,14,27,5,6,12,16,17,19,8],"7":[18,18,3,14,8],"4":[18,12,4,27,27,16,8],"11":[18,19,8],"15":[18,16,11,12,31,23,8],"12":[18,16,29,18,5,6,22,23,8],"14":[18,4,30,26,15,30,12,5,21,27,8],"30":[18,4,19,18,8],"9":[18,13,12,32,16,34,8],"5":[18,3,5,8],"32":[18,15,4,15,25,8],"36":[18,18,16,18,11,33,15,15,27,8],"18":[18,16,16,27,18,8],"22":[18,17,12,14,21,14,6,21,19,8],"43":[18,34,21,18,23,20,29,4,8]},"crossbreed_segments":12,"weight_initialization_range":{"start":-0.7746323,"end":0.64793515},"minor_mutation_rate":0.89887977,"major_mutation_rate":0.37956625,"mutation_weight_range":{"start":-0.9222891,"end":0.9222891}},"state":"Finish","generation":5,"max_generations":5,"id":"fd37c853-1504-4464-8897-1daff7a72be5"},"left":{"val":{"node":{"id":"0ad6c0a9-268e-4f36-b986-a095ce958e87","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_0ad6c0a9-268e-4f36-b986-a095ce958e87","population_size":50,"generation":4,"scores":[{"28":1.4615542,"29":-3.351231,"41":0.11434956,"47":3.353677,"24":-2.3349676,"27":-3.0977936,"43":4.7768126,"39":1.232477,"20":2.131682,"10":0.65271276,"32":3.4047546,"37":0.24620943,"31":1.5759578,"23":-0.32563427,"3":1.1822358,"40":-0.42240685,"19":0.9445168,"2":-1.0627751,"42":3.125601,"33":0.80939466,"46":3.1065967,"11":-4.4918694,"6":0.7574291,"49":-3.4887753,"8":-4.1754675,"0":-9.796589,"5":-0.2148296,"13":1.5807844,"17":-1.4137825,"34":-3.5904312,"9":-4.3053155,"16":-1.9381826,"15":0.35073262,"18":4.652893,"44":3.688664,"35":0.5031668,"1":-1.327118,"45":3.9197388,"48":-2.0562541,"21":0.21452656,"30":1.4445921,"36":0.4763755,"4":-0.6387031,"12":-1.0553591,"38":-1.564517,"25":-1.0317254,"26":0.948751,"7":0.051721383,"14":2.7838516,"22":0.3971446},{"8":-0.4450551,"15":3.0406582,"22":0.1862926,"10":-1.0695591,"42":-4.579817,"11":-0.03542762,"43":-2.354273,"20":3.1427789,"5":3.5550492,"24":-2.7110906,"44":-5.6810613,"1":1.412229,"14":3.0840802,"17":1.0067269,"35":-5.2434473,"46":-1.7983131,"2":3.8090882,"19":1.1941998,"21":1.5723072,"30":-8.034946,"12":2.1055703,"36":-4.125515,"9":0.80242157,"6":3.3065217,"23":1.9119755,"25":1.5494883,"13":5.389744,"37":0.41448742,"40":1.6354656,"26":2.8828807,"28":1.547584,"49":1.1942029,"48":-4.0498896,"7":1.1680777,"45":-3.7617173,"4":-0.83832073,"27":-7.9582443,"32":0.027616406,"33":0.85028744,"38":-3.1612341,"47":-4.9559927,"3":3.7921467,"31":-2.7395563,"29":-5.5815687,"34":0.15367377,"39":1.9094998,"18":1.4039291,"41":6.31805,"0":1.6352074,"16":1.0272758},{"17":1.4303916,"10":2.0075524,"6":-1.0944701,"1":-6.3332605,"45":-1.1300482,"21":1.5855234,"41":-3.5284915,"15":2.2013316,"49":-6.670298,"35":-0.72289884,"46":-3.121521,"13":0.12067218,"4":1.492248,"24":-1.156515,"22":1.3185016,"30":-0.1220412,"20":1.0285561,"33":0.25970158,"38":-2.8305871,"9":-3.185809,"43":-2.4596064,"18":0.10767321,"44":-6.2140107,"12":0.65513146,"8":3.7638183,"27":-2.3283656,"37":-5.2938666,"5":2.8565912,"7":-1.6208298,"11":-0.5699972,"31":-1.8019298,"39":-3.8167872,"42":0.62593377,"40":-3.3886192,"47":0.55262935,"34":4.8451715,"48":-0.4967394,"19":0.8086522,"26":-0.16367832,"25":-1.7693208,"2":3.8625455,"29":-2.7617586,"3":3.8275685,"28":2.4664369,"32":-2.9393113,"23":2.0031307,"0":2.9781694,"14":2.3455732,"16":-1.9647186,"36":-2.0843992},{"5":2.3199508,"17":2.4556768,"6":0.99130905,"2":3.7347991,"21":-0.27123898,"27":-5.349469,"37":0.2067368,"0":4.260925,"10":2.0144703,"45":-1.8468329,"49":0.3703224,"29":-1.9481332,"28":-10.377831,"41":-6.3821745,"20":0.505881,"19":2.3852916,"42":-3.1312802,"46":-1.7531841,"1":3.7902591,"3":0.75912887,"25":-3.2048562,"32":3.359387,"23":0.85752183,"39":-0.91159695,"14":0.66083515,"16":1.6131394,"12":4.2249913,"48":-3.1543155,"36":-3.265039,"26":-0.2300566,"47":-2.798001,"33":-1.2588333,"13":3.555592,"15":1.6225817,"30":1.1084969,"4":-0.80275536,"22":1.080758,"31":-3.770164,"11":3.1434116,"35":-3.1652713,"9":4.3430805,"34":-1.3829889,"18":0.41391096,"7":2.5888634,"43":1.8247061,"40":-1.506657,"44":1.1751028,"8":0.24729638,"38":-0.5430144,"24":0.84149456},{"26":-1.5581883,"17":0.45890158,"13":2.6077938,"20":1.5608526,"6":0.7407518,"14":1.8970188,"21":2.307454,"24":-2.2616897,"31":-1.2380049,"36":-3.3436444,"2":4.3228645,"44":-7.0414248,"45":-4.322558,"37":-2.0368595,"16":-0.7485286,"0":1.4736874,"8":2.7514439,"9":2.7842798,"38":-2.5296543,"29":-2.35339,"33":-2.799434,"42":1.5451206,"28":0.9679454,"34":-5.0157824,"49":0.44937867,"27":-4.0082808,"4":3.7921321,"7":3.8526225,"12":1.0690136,"22":-0.85086995,"19":-2.555062,"47":-2.2651973,"25":-3.2443783,"15":0.97329867,"46":-3.4256077,"41":-7.144478,"1":1.1129873,"10":1.4387215,"32":-1.0147102,"40":-11.496406,"48":-4.2306504,"5":3.3042362,"30":0.15390578,"43":0.9680899,"3":4.003913,"11":3.149682,"23":-2.0422919,"18":2.985143,"35":3.154074,"39":-0.3716902}],"nn_shapes":{"4":[18,23,7,18,18,19,6,6,33,33,8],"6":[18,29,25,31,16,20,9,20,8,8],"45":[18,8,25,8],"36":[18,18,8],"39":[18,17,28,4,8],"9":[18,11,3,20,7,8],"25":[18,30,11,14,24,26,9,29,15,13,8],"30":[18,4,24,19,15,33,10,34,15,8],"27":[18,6,29,26,8],"23":[18,34,12,10,13,28,21,15,28,8],"20":[18,19,8],"28":[18,7,5,6,30,29,31,13,7,8],"11":[18,16,16,27,18,8],"18":[18,10,15,22,8],"37":[18,12,13,12,20,32,8],"41":[18,3,20,20,8,8],"7":[18,17,12,14,21,14,6,21,19,8],"34":[18,25,8],"38":[18,15,31,25,7,30,33,33,8,8],"2":[18,34,28,11,11,29,11,12,8],"35":[18,14,10,33,8,11,4,21,28,8],"43":[18,12,4,27,27,16,8],"48":[18,31,17,8],"32":[18,33,14,8,15,8],"47":[18,30,33,8,24,22,13,28,3,8],"17":[18,12,14,3,22,20,8],"22":[18,5,21,4,7,9,13,28,8],"31":[18,31,18,8,29,8],"19":[18,7,19,18,9,26,13,32,30,8],"33":[18,17,8,23,30,23,8,33,27,8],"44":[18,32,15,3,11,5,11,3,8],"10":[18,24,6,5,31,8],"8":[18,16,11,12,31,23,8],"29":[18,25,8],"0":[18,13,12,32,16,34,8],"3":[18,13,8],"5":[18,26,13,16,17,7,3,8],"1":[18,18,3,14,8],"15":[18,3,5,8],"14":[18,16,29,18,5,6,22,23,8],"12":[18,10,14,28,29,11,18,20,10,8],"13":[18,4,30,26,15,30,12,5,21,27,8],"24":[18,23,8,8],"16":[18,15,30,8],"26":[18,10,29,8],"40":[18,11,8],"42":[18,12,11,13,20,25,8],"46":[18,19,8],"49":[18,19,10,34,8],"21":[18,7,9,24,24,24,29,24,8]},"crossbreed_segments":12,"weight_initialization_range":{"start":-0.79062134,"end":0.6353039},"minor_mutation_rate":0.9082846,"major_mutation_rate":0.37210783,"mutation_weight_range":{"start":-0.9348126,"end":0.9348126}},"state":"Finish","generation":5,"max_generations":5,"id":"0ad6c0a9-268e-4f36-b986-a095ce958e87"},"left":{"val":{"node":{"id":"2230be79-f940-4689-972a-7008e3f4c998","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_2230be79-f940-4689-972a-7008e3f4c998","population_size":50,"generation":4,"scores":[{"1":-2.3773646,"0":-2.1571355,"12":1.4148271,"21":-2.421327,"33":-1.934478,"17":0.138854,"34":-0.4928736,"44":1.4438745,"15":1.8977562,"36":0.9711355,"4":1.9662882,"22":-0.73786503,"3":1.9117216,"11":-1.7609564,"8":4.3882666,"37":-0.9355248,"38":0.67264163,"2":0.89526814,"20":1.2228067,"41":-0.69157594,"35":0.2589668,"42":0.7870628,"9":0.29466486,"26":0.046588603,"39":-2.6555886,"10":-1.7522205,"45":0.22093217,"47":2.8745866,"23":2.3134782,"24":0.2931752,"13":-1.7469364,"31":-0.48889366,"46":-1.4888307,"18":1.6355522,"28":-1.6607144,"29":-0.091461204,"14":2.8278089,"30":-1.5403664,"5":0.28095183,"27":-2.2598128,"40":0.17108059,"43":1.2745006,"25":1.8205446,"48":0.19277701,"19":0.7784854,"7":1.7413013,"49":-0.46817464,"6":0.6888849,"32":-1.3222702,"16":-0.4496932},{"17":-2.3785129,"3":2.9041004,"26":-1.5274507,"29":-4.735349,"48":-1.8411024,"9":-0.77052134,"11":-0.08661283,"14":-1.7952125,"16":2.9026654,"15":-1.8081363,"41":-2.6796186,"12":1.0565262,"49":0.60432214,"19":-1.4364105,"34":-1.241155,"36":-0.7845459,"35":-5.2296076,"28":-4.3732424,"30":-0.015474224,"2":0.133679,"47":-1.1977552,"22":-1.8609911,"44":-4.1988745,"40":-5.811055,"23":-0.8860939,"43":-2.7589722,"0":-1.8361069,"6":0.9826768,"33":-7.5137763,"37":-1.3062632,"39":-1.9096992,"5":-1.5289714,"25":-2.9348187,"7":1.6092517,"8":0.8130643,"20":-2.0613518,"24":-0.57125616,"27":-1.2508152,"32":-3.5545921,"42":0.017004633,"13":1.1759713,"10":4.379163,"18":0.5535432,"1":1.4643844,"4":-0.1658,"46":-0.48931485,"38":-1.1717209,"21":1.2540135,"45":-2.5831802,"31":-1.0906768},{"19":2.758,"23":-0.1547494,"16":-0.24027161,"27":-4.7461,"1":1.9463758,"0":5.49269,"38":-4.5831237,"39":-0.752874,"49":-3.560801,"18":-1.372174,"25":-0.24467158,"2":-1.4239506,"10":-1.7297821,"45":-0.550079,"20":-2.916182,"24":-1.0988299,"3":1.0242682,"9":-0.9811675,"21":-0.55461586,"32":-4.361062,"7":-1.9895757,"44":-0.217258,"46":1.3056983,"48":0.027753597,"12":0.4383232,"13":1.7189554,"36":-2.2416625,"5":0.8830037,"11":-1.4609058,"8":-0.16048487,"22":-3.4026666,"41":-3.1433291,"29":-2.5550942,"6":0.9628898,"30":2.8254886,"4":3.1442847,"31":-3.0069652,"14":-2.2467086,"28":-2.3709545,"17":-5.425764,"26":-0.6892742,"34":0.4522006,"37":2.8570902,"42":-8.46829,"47":-4.117214,"35":-0.49623865,"15":0.57778114,"33":-1.0330775,"40":-2.8532898,"43":-1.4928547},{"40":-0.7036076,"30":-0.5719384,"1":2.25287,"14":-1.288854,"16":1.8005784,"44":1.8507036,"47":-3.295477,"17":0.53820264,"24":-4.5791464,"33":-2.7593288,"15":-1.035099,"18":0.6495812,"35":-7.1353035,"13":-1.0557783,"8":3.2177284,"43":2.1751304,"3":0.6698944,"7":0.34247094,"46":-2.612004,"31":3.3641772,"26":-1.1441598,"29":-2.781163,"2":-0.13234572,"5":1.5759518,"45":-8.701353,"48":-4.505052,"36":0.5430554,"10":0.24432358,"49":-4.2009077,"20":-1.2227812,"19":-0.1535736,"23":-1.3555021,"22":-2.4631474,"34":0.70411575,"12":1.7876114,"27":-5.090214,"11":2.5564446,"32":-3.6049714,"28":-0.011442797,"37":-5.639792,"39":-2.1910725,"25":6.6744385,"0":0.80345714,"4":-0.25791565,"21":-0.5257034,"6":1.4308555,"38":0.21877857,"41":-0.8577938,"42":-3.589934,"9":1.4351888},{"3":0.99701536,"30":-1.2227508,"40":-1.5728966,"47":-0.9126034,"2":1.7522328,"17":-0.95693076,"42":0.54109496,"43":-4.4988546,"46":-2.362868,"23":-0.06628381,"20":-2.6594634,"31":-1.575641,"18":0.302563,"6":0.8362234,"4":3.3849263,"25":-5.9296846,"13":-0.29305857,"26":-2.5544639,"27":-5.2713065,"36":-0.62222356,"49":-3.8993137,"5":-4.794875,"1":4.0791264,"12":2.951697,"15":0.21713142,"22":0.8922955,"24":0.20521124,"41":-7.768069,"48":-2.7011123,"21":-3.9334495,"16":-2.0758946,"34":-7.4594717,"35":-7.6091437,"37":-4.4366155,"9":2.1347003,"38":0.23643506,"33":-7.0710535,"28":-1.8344015,"11":2.30687,"45":-2.57183,"19":2.1262608,"7":1.5562618,"39":-1.2651775,"14":1.1440918,"44":-6.099185,"32":-1.6038958,"10":1.5246786,"0":-1.7383505,"8":1.2577031,"29":-6.3010983}],"nn_shapes":{"6":[18,16,16,27,18,8],"21":[18,30,24,7,23,15,6,8],"0":[18,6,7,18,8],"14":[18,16,29,18,5,6,22,23,8],"27":[18,21,14,31,15,27,26,34,7,8],"40":[18,14,10,4,8],"41":[18,7,10,28,26,16,23,31,28,8],"39":[18,30,8,33,7,25,17,18,26,31,8],"43":[18,15,15,8],"47":[18,34,28,11,11,29,11,12,8],"38":[18,16,11,12,31,23,8],"4":[18,34,12,10,13,28,21,15,28,8],"20":[18,29,30,28,15,21,16,4,7,9,8],"31":[18,15,7,20,8],"45":[18,13,8],"29":[18,13,8],"9":[18,19,8],"28":[18,15,8],"44":[18,28,8],"11":[18,7,9,24,24,24,29,24,8],"24":[18,29,25,31,16,20,9,20,8,8],"36":[18,13,8],"17":[18,18,3,14,8],"15":[18,17,12,14,21,14,6,21,19,8],"10":[18,15,30,8],"32":[18,25,8],"30":[18,13,12,32,16,34,8],"2":[18,10,15,22,8],"25":[18,14,34,25,18,11,27,23,17,8],"5":[18,28,28,22,12,26,16,8],"3":[18,4,30,26,15,30,12,5,21,27,8],"18":[18,11,3,20,7,8],"23":[18,26,13,16,17,7,3,8],"34":[18,19,5,34,8,11,8],"46":[18,5,14,4,10,8],"1":[18,23,8,8],"33":[18,21,17,21,8],"12":[18,5,21,4,7,9,13,28,8],"8":[18,3,5,8],"19":[18,7,19,18,9,26,13,32,30,8],"37":[18,13,11,5,32,8],"48":[18,14,5,4,5,8],"49":[18,20,8],"22":[18,10,14,28,29,11,18,20,10,8],"7":[18,12,14,3,22,20,8],"16":[18,7,18,32,5,14,8],"42":[18,24,6,5,31,8],"35":[18,28,8],"26":[18,30,7,34,8,13,24,26,27,8],"13":[18,23,7,18,18,19,6,6,33,33,8]},"crossbreed_segments":11,"weight_initialization_range":{"start":-1.215388,"end":1.5817409},"minor_mutation_rate":0.77005047,"major_mutation_rate":0.7637305,"mutation_weight_range":{"start":-0.6824868,"end":0.6824868}},"state":"Finish","generation":5,"max_generations":5,"id":"2230be79-f940-4689-972a-7008e3f4c998"},"left":{"val":{"node":{"id":"5830af23-37c2-45fd-bc48-4be19bb47989","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_5830af23-37c2-45fd-bc48-4be19bb47989","population_size":50,"generation":4,"scores":[{"4":-0.4398686,"13":0.6435866,"25":-1.1424577,"7":2.2064023,"17":-1.1729944,"29":-2.188397,"36":-0.7252862,"41":-0.06775464,"27":-3.0315576,"9":-1.8444555,"23":0.47301316,"6":0.5633224,"18":0.9158624,"31":-2.2378566,"24":2.491937,"3":-1.7823699,"37":0.29173023,"5":0.6846366,"20":1.0448742,"32":-3.6604862,"38":-1.1224955,"39":-0.30770817,"8":0.31371704,"42":-0.2333417,"45":-0.7877932,"46":-0.55997026,"47":1.1051524,"19":2.861733,"48":-1.1579436,"30":-0.6199918,"43":-1.3103287,"0":0.073735476,"22":1.867547,"1":0.40812922,"16":0.16757026,"28":-5.616639,"40":3.373998,"11":-2.1214867,"14":-0.3355748,"44":0.52112305,"33":-6.5058165,"26":-2.0824904,"21":1.0021068,"10":0.48167562,"49":-0.87629604,"2":-0.0054346086,"34":-1.2387748,"35":0.28463203,"15":-0.5290295,"12":0.7119624},{"13":0.8524078,"39":-1.6857016,"7":-1.7376686,"11":-2.2416666,"27":-4.389194,"40":-4.86255,"5":0.4651522,"25":1.8499638,"42":-1.521401,"12":-0.790142,"20":0.776415,"30":-0.6416456,"32":-1.8126523,"45":-6.633146,"1":-2.1365325,"21":1.382251,"6":0.81977844,"17":-0.80237997,"2":1.1953188,"15":3.9234138,"46":-3.14258,"19":1.2063272,"31":-1.2679306,"47":-3.2304752,"29":0.713826,"22":-0.07156279,"35":0.57997036,"43":-1.6351938,"44":-2.756394,"0":1.2175782,"8":0.32304603,"18":-1.7216289,"48":-2.5695126,"37":-7.174632,"24":-0.32638264,"23":0.7591497,"16":-0.09428916,"26":-2.6883242,"38":-2.8030906,"34":-0.2926392,"41":-2.8787274,"3":1.6496121,"10":2.4494653,"14":0.769758,"28":-2.195226,"36":-0.8310772,"33":-1.4455681,"49":-0.66453344,"9":0.2630516,"4":0.21751109},{"35":-1.2995249,"39":-4.3697367,"49":-5.5780034,"1":1.9374145,"43":-5.1250587,"3":0.29240862,"11":-1.3891447,"12":0.593122,"17":0.4923257,"28":-0.6408792,"47":-6.84591,"32":-2.3980255,"4":-0.40212068,"2":-0.30574152,"15":1.4105376,"18":3.3082592,"23":0.95887107,"25":2.410621,"26":-7.540658,"31":-0.2849912,"38":-0.06010101,"40":-3.8810935,"6":-1.9693276,"16":-0.35065803,"29":-1.5561249,"42":-9.490059,"27":-1.828867,"22":1.1340528,"45":-2.825584,"48":-0.9286879,"0":-4.842866,"24":0.6481105,"7":2.5768476,"36":-3.6143699,"41":-1.6077397,"8":-0.5062324,"34":-1.9471012,"13":1.0228976,"20":-0.8251265,"14":0.8828863,"5":1.4987826,"10":0.5583974,"37":-1.1287113,"46":-0.537475,"30":0.0993626,"33":-5.52423,"44":-2.2344456,"21":0.2783533,"9":0.026928205,"19":0.14576219},{"17":-2.6396623,"2":1.2169989,"37":-8.008817,"45":2.6028926,"46":-1.5116042,"6":3.4691956,"31":-0.165995,"8":0.32855827,"10":0.15786763,"19":-0.16524422,"27":-1.9287678,"23":-0.1792428,"28":-4.0277762,"22":0.37375817,"36":-2.5048773,"14":-0.28106713,"25":1.5299038,"21":0.91780585,"15":-1.4555721,"40":-4.9625564,"48":-7.442425,"39":-3.0028021,"1":-0.29292637,"13":-0.034496427,"49":-1.8842728,"9":-1.4072593,"41":-0.3804465,"35":-7.516181,"4":-2.062589,"33":-6.622129,"7":1.1507305,"34":-5.826934,"47":-1.2597684,"24":-0.73715174,"18":0.9347435,"26":-0.58290416,"29":1.3509219,"42":-0.2881294,"43":-2.917752,"0":2.1211834,"30":-0.30634144,"20":-3.504623,"38":-2.5137649,"3":1.5459003,"11":-2.5077972,"16":-1.1970947,"32":1.4198091,"12":-0.53543484,"44":-0.65983814,"5":1.3583586},{"27":-4.6737294,"47":-0.9961362,"48":-1.5854882,"11":-1.8830795,"37":-1.1362636,"34":-6.0494013,"7":-0.3092016,"2":1.2706958,"15":-2.2915282,"25":-1.0481479,"29":-4.1591725,"9":-0.04948742,"31":-0.43341333,"40":1.9337126,"49":0.19426656,"13":0.230414,"1":2.1338658,"23":-1.7014267,"3":-0.30542445,"33":4.549133,"35":-4.031805,"17":-1.013982,"32":-7.554162,"36":-4.573109,"46":0.38773063,"20":1.140489,"5":1.2783581,"14":0.28659493,"43":-0.90195465,"8":-0.5335846,"19":0.97964114,"18":0.7644297,"21":2.3947785,"10":0.17983632,"12":-2.02406,"16":-0.095197245,"24":0.14134718,"28":1.012642,"26":-5.5137,"22":-2.4713922,"30":-3.1684103,"4":-1.7671635,"38":0.7613734,"39":-3.4774318,"6":1.4320437,"41":-2.7136345,"42":-3.4777806,"0":-1.8335673,"44":-4.0395794,"45":-5.5930243}],"nn_shapes":{"20":[18,18,3,14,8],"13":[18,15,30,8],"28":[18,7,18,32,5,14,8],"12":[18,25,14,12,8,16,7,8],"17":[18,8,12,8,24,18,8],"3":[18,34,12,10,13,28,21,15,28,8],"24":[18,12,14,3,22,20,8],"19":[18,17,12,14,21,14,6,21,19,8],"40":[18,30,24,7,23,15,6,8],"2":[18,11,3,20,7,8],"41":[18,15,28,8],"42":[18,6,19,16,27,11,21,18,24,8],"44":[18,33,8],"36":[18,8,7,4,15,15,8],"25":[18,3,13,28,33,14,9,11,10,8],"7":[18,4,30,26,15,30,12,5,21,27,8],"11":[18,18,22,6,19,12,24,34,8],"37":[18,30,18,14,24,26,33,25,28,33,8],"6":[18,29,30,28,15,21,16,4,7,9,8],"8":[18,23,8,8],"4":[18,20,18,4,12,8],"33":[18,29,25,31,16,20,9,20,8,8],"39":[18,7,20,20,14,4,17,8],"43":[18,6,7,18,8],"35":[18,26,9,8],"45":[18,4,4,19,8],"10":[18,3,5,8],"14":[18,7,9,24,24,24,29,24,8],"34":[18,6,7,20,8],"22":[18,8,22,29,16,25,8,23,10,21,8],"30":[18,3,4,6,8],"16":[18,28,28,22,12,26,16,8],"15":[18,16,10,13,10,14,3,11,8],"5":[18,7,19,18,9,26,13,32,30,8],"9":[18,16,16,27,18,8],"26":[18,6,13,11,8],"18":[18,16,29,18,5,6,22,23,8],"27":[18,27,11,26,3,17,8,8],"31":[18,10,15,22,8],"38":[18,23,7,18,18,19,6,6,33,33,8],"49":[18,19,8],"29":[18,18,20,9,19,13,4,8],"48":[18,30,32,22,16,10,13,16,27,24,8],"32":[18,5,29,18,16,9,31,8],"0":[18,6,21,24,5,31,27,8],"21":[18,26,13,16,17,7,3,8],"47":[18,16,34,19,20,8],"1":[18,10,14,28,29,11,18,20,10,8],"23":[18,25,13,20,21,12,18,8,17,8],"46":[18,5,21,4,7,9,13,28,8]},"crossbreed_segments":13,"weight_initialization_range":{"start":-1.6086724,"end":1.7038059},"minor_mutation_rate":0.36123097,"major_mutation_rate":0.60697865,"mutation_weight_range":{"start":-0.86246336,"end":0.86246336}},"state":"Finish","generation":5,"max_generations":5,"id":"5830af23-37c2-45fd-bc48-4be19bb47989"},"left":{"val":{"node":{"id":"d7dc25e5-16bd-4f5d-bc97-d6c4cf90012c","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_d7dc25e5-16bd-4f5d-bc97-d6c4cf90012c","population_size":50,"generation":4,"scores":[{"36":-2.443375,"37":1.1562526,"39":-1.4698045,"43":1.9774078,"22":-1.2957172,"11":-3.543326,"45":1.1007092,"0":0.13766284,"18":-1.5081861,"27":-0.3628974,"2":-1.5020592,"15":-1.8695787,"17":-0.80195874,"21":-3.627962,"30":-5.9871097,"47":-0.5287416,"31":0.6174214,"13":-1.3917341,"38":-0.4312748,"16":-1.3465332,"33":0.92242736,"35":-0.14989817,"49":4.272779,"14":-1.5017221,"24":1.4262507,"6":-3.547879,"10":-2.916342,"4":-1.141561,"32":-1.5708299,"41":-0.026479607,"20":-0.16278799,"19":-1.0929224,"12":-1.6322893,"29":-3.2093625,"28":-1.5375618,"44":0.67286664,"48":-1.4393549,"40":2.4525964,"26":-3.0939457,"3":-0.638409,"1":0.39259672,"9":-1.5627674,"46":2.6744673,"34":-0.57457024,"42":-0.70243216,"8":-2.3440309,"25":5.3659525,"5":-3.8967907,"23":-2.4046814,"7":-1.3113697},{"43":-3.5765548,"32":-2.1894574,"40":-0.056246854,"44":-1.1792139,"3":1.122174,"25":-0.17178361,"29":-3.5132928,"7":-0.00574913,"4":1.2176167,"45":1.701143,"1":-0.41889125,"17":-0.2031568,"2":0.7972824,"15":-0.5265984,"23":0.3870982,"20":-0.8553486,"28":-1.6365826,"42":-2.0912087,"26":-1.9612887,"16":-1.4218798,"31":-0.1845134,"47":-2.828169,"49":-2.789091,"34":-4.926751,"19":-1.0707716,"39":-2.5893881,"13":-1.0763242,"12":-1.3562354,"27":-4.985302,"24":-0.39440042,"6":-1.4130237,"35":-3.373304,"37":-2.530553,"48":-1.9251556,"30":-5.641555,"46":-3.7538414,"9":0.46599898,"18":3.1130311,"8":-0.59825116,"33":-3.1443129,"36":-6.31389,"21":-1.6509422,"10":0.79040325,"5":-2.260046,"0":3.8864777,"38":-0.5514248,"41":-2.7165523,"22":-0.4323124,"11":1.0230696,"14":-0.2367413},{"11":1.9855608,"27":-1.4113008,"32":-3.47152,"34":-0.8354224,"35":-3.2586715,"4":1.4771069,"19":-3.441225,"5":-0.73485357,"14":-0.8333043,"23":-0.11942999,"0":4.844884,"25":-0.5912832,"3":0.92040044,"1":-0.76377815,"20":-2.0964956,"33":-0.37407398,"38":0.5312749,"44":-0.2386789,"49":-0.033635713,"12":-1.5284121,"22":0.57188094,"29":1.2233804,"31":-3.2222774,"48":1.4144858,"8":1.0541319,"21":-1.6275711,"40":-1.5101508,"6":-0.439957,"2":0.8432387,"26":-2.0842004,"41":-0.9327258,"15":-4.2353735,"18":-0.5040212,"10":2.760879,"17":2.14171,"24":0.4595502,"28":-2.1864777,"13":0.118149005,"36":-0.7745973,"7":0.67550546,"16":-0.50533116,"9":-2.03656,"37":-2.1948545,"42":-1.2179236,"43":-5.7127614,"45":0.96074045,"30":-2.7779126,"46":1.2078952,"47":-2.6667004,"39":-2.3364167},{"29":-1.49889,"47":-2.1373265,"49":-3.668885,"19":-1.9121835,"43":-4.294961,"42":-2.510038,"5":-6.127954,"18":-0.4878006,"24":-0.50900334,"35":-6.8719015,"37":-3.2887795,"17":-6.090243,"9":0.98944414,"26":1.0927036,"34":-4.9022627,"10":0.84963435,"20":-3.501018,"25":0.13348675,"1":0.02200427,"16":-4.878383,"4":2.2152524,"39":-5.288896,"11":0.9839722,"45":-1.3084223,"31":0.835742,"38":-4.1196938,"6":-0.4101388,"14":-5.849814,"8":1.5794041,"27":-6.5084815,"48":-3.34514,"0":-2.3548312,"33":-1.4235249,"7":3.1076987,"44":-0.27251154,"15":-0.2992934,"32":-5.1047645,"40":-0.7286312,"28":-0.87923414,"41":0.9774147,"3":-1.7796749,"13":1.8801941,"23":-2.5226865,"22":-0.39809638,"21":0.9538826,"2":0.3000278,"12":-0.7506649,"30":-1.2880104,"36":3.7323055,"46":-3.7501194},{"4":1.0086969,"46":-0.08042721,"18":-1.179418,"44":-4.586928,"5":4.1943126,"13":-0.15399918,"0":-1.765187,"40":-5.726017,"45":-7.071813,"32":-0.38681263,"33":-3.899791,"48":-7.4941225,"30":-2.2340627,"28":-4.9254932,"49":-1.9380242,"3":2.641837,"47":0.88087475,"20":-2.7248514,"6":-1.6207275,"10":-0.9453846,"14":-0.588112,"17":0.2461802,"8":-2.4899194,"24":-0.06998464,"25":-0.8650482,"12":1.0286778,"16":-0.3848494,"2":-0.7636143,"22":-0.87525463,"21":-0.65220153,"35":-3.7077782,"38":-0.86415654,"11":-1.1537424,"26":-1.1066633,"41":2.4212365,"39":-8.511892,"34":0.031094,"31":-2.6876805,"19":0.20523944,"36":0.41543216,"9":-0.22608939,"7":-3.7244701,"15":2.012662,"37":-4.712545,"1":6.5607452,"23":0.22806843,"27":0.32851115,"29":-6.4183474,"42":3.8774147,"43":1.1255028}],"nn_shapes":{"3":[18,26,13,16,17,7,3,8],"0":[18,26,8],"35":[18,19,20,30,9,30,18,20,4,21,8],"16":[18,20,18,4,12,8],"31":[18,14,32,33,3,8],"27":[18,15,30,8],"28":[18,22,11,15,13,13,24,5,34,8],"11":[18,23,32,20,18,8],"37":[18,28,24,8],"45":[18,21,8],"7":[18,25,22,14,4,33,9,22,12,9,8],"6":[18,5,34,8],"2":[18,6,21,24,5,31,27,8],"5":[18,25,13,20,21,12,18,8,17,8],"8":[18,12,33,33,12,4,15,34,17,8],"30":[18,24,34,14,6,20,22,15,6,6,8],"32":[18,34,12,10,13,28,21,15,28,8],"23":[18,18,22,6,19,12,24,34,8],"25":[18,12,30,8],"40":[18,31,6,34,27,15,34,33,8],"41":[18,18,3,14,8],"20":[18,20,31,6,17,15,19,25,31,8],"48":[18,7,24,32,29,19,20,8],"13":[18,29,30,28,15,21,16,4,7,9,8],"19":[18,3,5,8],"42":[18,8,22,29,16,25,8,23,10,21,8],"22":[18,28,4,10,11,7,34,19,3,8],"24":[18,23,8,8],"44":[18,25,19,16,19,30,8],"34":[18,16,16,27,18,8],"49":[18,21,3,18,11,8],"29":[18,3,28,30,23,4,8],"26":[18,22,27,15,12,9,8],"43":[18,16,29,18,5,6,22,23,8],"10":[18,31,27,12,3,24,31,8],"12":[18,8,12,8,24,18,8],"18":[18,31,6,29,30,32,32,14,11,7,8],"33":[18,24,21,12,8],"38":[18,6,17,28,12,4,22,30,8],"39":[18,3,22,17,3,28,7,25,16,22,8],"17":[18,25,14,12,8,16,7,8],"46":[18,4,30,26,15,30,12,5,21,27,8],"9":[18,7,19,18,9,26,13,32,30,8],"14":[18,11,3,20,7,8],"4":[18,28,28,22,12,26,16,8],"21":[18,10,14,28,29,11,18,20,10,8],"47":[18,16,10,13,10,14,3,11,8],"15":[18,17,12,14,21,14,6,21,19,8],"36":[18,7,9,24,24,24,29,24,8],"1":[18,12,14,3,22,20,8]},"crossbreed_segments":17,"weight_initialization_range":{"start":-1.7525467,"end":1.6750996},"minor_mutation_rate":0.19365302,"major_mutation_rate":0.94476026,"mutation_weight_range":{"start":-0.84336406,"end":0.84336406}},"state":"Finish","generation":5,"max_generations":5,"id":"d7dc25e5-16bd-4f5d-bc97-d6c4cf90012c"},"left":{"val":{"node":{"id":"206450bc-272b-41f7-ae89-fc4f12be753c","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_206450bc-272b-41f7-ae89-fc4f12be753c","population_size":50,"generation":4,"scores":[{"4":-0.25408143,"41":-1.7904724,"36":2.1737509,"49":0.20309429,"0":-0.66195565,"1":-1.8757442,"25":-3.5965798,"38":-6.863303,"37":0.46780187,"10":-2.0059624,"5":-5.5871625,"15":-2.403483,"18":-0.8859708,"23":-1.2753251,"27":-4.9748425,"6":-1.0913737,"8":-6.0900583,"28":-7.0202684,"9":-4.8177304,"19":-7.19686,"32":-2.7875524,"2":-7.0430055,"16":-0.27839962,"12":-0.2691624,"34":-1.479561,"35":-7.1771135,"39":-1.6123358,"11":-7.47368,"33":-6.8079133,"14":-2.8988595,"22":-4.932962,"42":-3.2319355,"43":-1.6130539,"47":1.0813572,"21":-5.6781116,"30":0.978965,"45":-2.5380788,"26":-2.465695,"17":-5.9036236,"20":-3.9086146,"31":-1.2463293,"3":-3.2792873,"24":-5.369993,"7":-1.4513847,"46":6.1682773,"13":-3.4915504,"29":-6.566017,"44":-8.941375,"40":-5.520095,"48":-1.0354421},{"35":-0.2504428,"2":-2.866654,"38":-2.699653,"22":-3.781758,"27":-1.5028169,"41":-2.1340575,"46":0.68042946,"48":2.0310922,"29":-1.670792,"5":-2.4222636,"8":-3.758992,"13":-2.5876176,"39":-2.672819,"42":-5.368996,"4":-1.3166544,"11":-1.0092744,"14":-3.5402977,"33":-2.136273,"6":-1.0932586,"15":-2.5740986,"37":-7.9974265,"9":-0.7977718,"25":-1.6041262,"1":0.63081247,"16":-2.3773406,"20":-1.7264913,"28":-0.089106,"12":-1.3272784,"36":0.61650956,"40":0.2590232,"43":-1.9761422,"31":-2.0207806,"7":0.1699496,"24":-3.923205,"0":0.35101363,"18":-2.2779722,"19":-3.3019118,"26":-6.8219934,"10":-2.0487385,"30":-5.7969003,"21":-2.0886593,"32":-5.1758175,"3":-0.2843196,"45":-3.3521304,"17":-2.688929,"47":-0.36435205,"44":-2.9112706,"23":-6.120496,"34":-6.7256265,"49":-1.3379397},{"35":-1.9879253,"6":0.4575228,"25":-5.48533,"33":0.63332397,"8":0.315913,"42":-6.9842176,"5":-2.0301785,"13":-0.19631381,"10":-0.439648,"9":-0.047661208,"37":-0.032142986,"16":-1.9775356,"46":-5.1132545,"48":-2.9102721,"14":-1.6374485,"18":-2.2513611,"39":-5.9638968,"40":-3.6864495,"2":1.8884017,"43":-3.5454178,"24":1.7852094,"45":-2.5057929,"17":-1.4369357,"32":-1.0873439,"30":-1.1533614,"44":-1.3637924,"41":0.17533419,"27":-3.9604943,"36":-2.5420253,"7":-1.3091543,"38":-3.7847595,"47":-3.3268592,"0":-2.795135,"1":0.38470575,"12":-1.0349476,"26":-1.6940864,"23":-2.038426,"28":-2.5738132,"49":0.49575263,"31":-1.5538758,"22":-0.2278616,"19":-0.84008443,"11":-3.588379,"4":4.72171,"3":-2.2519011,"15":-1.9365683,"29":-3.695866,"34":-8.262297,"20":-2.5298142,"21":2.4993515},{"4":-2.017178,"41":-3.7915883,"27":-1.1440285,"28":-0.910886,"14":-1.0659852,"26":-5.911423,"20":-2.0808756,"35":-1.6167637,"46":-6.1646094,"9":-3.002094,"25":-2.4201577,"47":-6.4115615,"37":-1.7778126,"8":0.04078598,"1":-1.8136123,"13":-3.4314759,"12":-1.7723328,"38":0.24750924,"31":-4.733556,"16":-1.5186998,"23":-0.7909092,"30":-2.4318392,"32":1.933485,"11":-0.15842238,"21":-2.3946443,"34":-2.4807293,"33":-2.7696655,"45":-7.775463,"40":-2.2423255,"19":2.3957248,"18":-2.4072661,"39":-4.6428156,"42":-0.5141532,"29":-7.422221,"0":4.457864,"3":-1.9035103,"2":-2.3215656,"5":-1.0763216,"36":-1.583903,"6":-1.5136465,"44":-2.0852666,"15":-0.5611115,"7":0.07060242,"24":-0.743223,"48":-2.7856417,"49":-1.4144045,"10":0.5483734,"22":-2.5478017,"43":-2.7007813,"17":-1.5073609},{"30":-3.0501373,"7":-0.8361124,"47":-0.8469876,"5":-1.766114,"36":0.30409366,"48":-1.212121,"1":-1.1675081,"10":-2.3685074,"16":-0.24671106,"24":-2.2479625,"20":-0.7164024,"0":1.5040326,"28":-2.7426717,"11":-1.6075907,"19":-1.1831938,"32":-1.2883797,"41":-0.28320545,"45":-1.947333,"33":0.015564804,"27":1.0732508,"42":-4.9141226,"46":-6.5679398,"49":-2.7856412,"31":-6.4782953,"44":-1.108532,"15":-1.4689306,"25":-6.487939,"2":-2.7836912,"29":-1.2318416,"13":-1.128869,"18":-2.0082943,"4":-3.6522014,"40":-3.9933362,"6":-0.90794164,"35":-1.8104624,"8":-0.654948,"9":-1.4459306,"23":1.4575778,"3":-0.0406546,"14":-0.2458098,"17":-1.2613282,"12":-1.3665266,"37":-9.234129,"43":0.8146294,"22":-0.017080784,"26":-1.0855381,"34":-0.053871382,"21":-0.73696125,"38":-2.8070807,"39":-1.2034365}],"nn_shapes":{"49":[18,18,13,34,33,11,13,25,8],"22":[18,31,6,29,30,32,32,14,11,7,8],"15":[18,23,32,18,30,3,8],"47":[18,12,33,33,12,4,15,34,17,8],"2":[18,34,17,27,31,8],"8":[18,8,12,8,24,18,8],"34":[18,20,18,4,12,8],"38":[18,5,8,26,8],"11":[18,3,9,20,5,21,8,21,20,19,8],"6":[18,25,22,14,4,33,9,22,12,9,8],"21":[18,31,27,12,3,24,31,8],"39":[18,12,14,3,22,20,8],"18":[18,5,17,23,3,21,8,21,23,27,8],"33":[18,3,5,8],"17":[18,29,27,8],"30":[18,25,18,17,6,20,21,33,19,3,8],"42":[18,30,9,34,20,6,24,8],"44":[18,25,13,20,21,12,18,8,17,8],"46":[18,14,20,8,22,5,9,8],"10":[18,30,29,5,12,9,18,8],"3":[18,25,14,12,8,16,7,8],"16":[18,11,3,20,7,8],"23":[18,18,22,6,19,12,24,34,8],"25":[18,24,3,19,6,29,24,19,13,8],"13":[18,28,28,22,12,26,16,8],"14":[18,17,12,14,21,14,6,21,19,8],"27":[18,28,4,10,11,7,34,19,3,8],"24":[18,19,34,19,10,10,13,25,8,8],"26":[18,5,34,8],"0":[18,23,8,8],"4":[18,27,33,33,12,18,8],"5":[18,19,14,3,16,11,28,16,13,34,8],"29":[18,14,28,17,8],"35":[18,3,27,13,8],"36":[18,20,31,6,17,15,19,25,31,8],"43":[18,10,14,28,29,11,18,20,10,8],"1":[18,26,13,16,17,7,3,8],"7":[18,7,19,18,9,26,13,32,30,8],"9":[18,12,25,8],"28":[18,10,7,6,8,20,23,11,12,18,8],"20":[18,23,32,20,18,8],"40":[18,15,21,8],"41":[18,29,30,28,15,21,16,4,7,9,8],"48":[18,26,8],"32":[18,15,10,34,23,21,29,15,8],"31":[18,7,8],"37":[18,15,19,24,14,24,20,20,23,30,8],"12":[18,15,26,32,3,30,19,14,8],"19":[18,6,21,24,5,31,27,8],"45":[18,5,13,6,8]},"crossbreed_segments":19,"weight_initialization_range":{"start":-1.9261076,"end":1.5660605},"minor_mutation_rate":0.81368506,"major_mutation_rate":0.21178806,"mutation_weight_range":{"start":-0.09049201,"end":0.09049201}},"state":"Finish","generation":5,"max_generations":5,"id":"206450bc-272b-41f7-ae89-fc4f12be753c"},"left":null,"right":null},"right":{"val":{"node":{"id":"a21eff6c-ff0d-4f4f-8354-14ac42930850","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_a21eff6c-ff0d-4f4f-8354-14ac42930850","population_size":50,"generation":4,"scores":[{"11":-6.4714723,"21":-2.4387584,"32":0.51196724,"46":-2.8112407,"47":3.9526958,"17":-2.534247,"49":0.6575879,"38":-0.69729745,"15":-5.533148,"2":-6.5250335,"25":-3.6730926,"1":0.1370612,"27":-4.329281,"33":-5.7648,"43":0.31441694,"13":-6.8382897,"41":-5.1048594,"5":-1.4107186,"26":-5.659274,"12":-3.595717,"18":-2.075016,"9":-0.36810058,"19":-5.8094816,"20":-6.8285494,"6":-8.124129,"40":-1.8836892,"42":-4.3344374,"14":-4.4880095,"30":-1.9241444,"0":-0.9877204,"4":-2.1065373,"16":-6.3015647,"22":-4.8700056,"24":-4.32249,"37":-2.2750306,"44":-3.5271213,"48":-0.4754526,"28":-5.6138673,"23":-3.8899643,"39":-5.5279393,"3":-5.776387,"7":-4.5177007,"45":-3.2805505,"10":-3.3764129,"29":-0.6382438,"31":-3.962623,"8":-1.1595395,"34":-2.9952676,"35":-6.2117767,"36":1.2942022},{"0":-0.42869464,"23":-6.562229,"41":-5.1660156,"42":-1.74071,"21":-1.4251636,"4":2.1877246,"44":-7.116065,"47":-2.257065,"18":-3.0274513,"49":2.1844962,"48":-5.5216503,"35":0.2611336,"14":-1.730476,"46":-0.45125613,"7":-0.44024363,"9":-3.730825,"26":-5.3071184,"28":-5.733193,"33":-1.6490171,"31":2.2274585,"38":-0.042269394,"16":-3.5550365,"40":-0.4170471,"10":-3.7582068,"20":-7.434947,"24":-4.267804,"27":1.0609788,"2":0.6224848,"30":-1.226848,"15":-2.1411896,"29":-2.7852437,"39":-3.7156053,"17":-3.113152,"5":0.056865405,"3":0.4255442,"8":-5.3668776,"36":0.56464416,"37":-2.1761982,"19":-3.4220695,"32":-4.987323,"11":-1.0636063,"25":-0.7849502,"13":-3.4652767,"6":-0.0017968237,"34":-4.096925,"43":-4.2301006,"45":-4.2687526,"22":-5.219239,"12":-2.5277166,"1":0.93786526},{"18":-2.842162,"29":-2.5924702,"13":3.463049,"35":-2.066756,"37":-2.5072942,"5":-0.624416,"7":1.4464777,"8":1.3839967,"45":-1.4903508,"46":-5.7676916,"25":-1.5863268,"12":-2.2818654,"26":-5.7843394,"10":-0.81996393,"38":-0.504665,"48":-4.511069,"32":-4.463644,"1":-1.7803532,"6":1.3676453,"27":-0.81607056,"41":-5.172019,"39":-6.267836,"21":-1.0194148,"3":-2.6400383,"24":-3.0220032,"30":-2.9699166,"43":-0.94422615,"17":-1.2235672,"14":-1.1192089,"15":-2.770475,"36":-6.417554,"19":-3.1521173,"9":1.0718931,"47":-4.5135217,"49":-0.22810516,"0":-0.1726802,"4":0.04109261,"23":-1.9920948,"22":-4.0239234,"33":0.08560918,"31":0.64719695,"40":-0.19472441,"2":0.025729995,"34":-2.4242225,"16":-1.0669054,"42":-4.6856117,"11":-0.54649603,"28":1.372547,"20":-6.1615343,"44":-2.9095218},{"8":0.58357877,"15":-0.06683802,"32":0.9100934,"28":-0.27048746,"24":-1.6066669,"10":-0.7108048,"35":-0.62085783,"1":1.0821325,"25":-5.284899,"44":-5.060845,"0":-0.78118956,"17":-0.6072541,"37":-1.7003189,"45":-3.2974827,"47":-4.4146976,"22":0.2507886,"41":-1.6168216,"23":-1.3340887,"4":-2.2199645,"2":0.9136928,"38":-4.916292,"42":-3.5366962,"27":-6.0338736,"34":-2.4532602,"43":-1.3611755,"14":0.5956539,"3":-0.675242,"11":-3.1293192,"9":-0.6097592,"33":0.0664752,"40":-3.3125527,"13":-2.857099,"31":1.6122506,"6":-2.9755213,"36":-4.948716,"16":-1.6437817,"19":-0.82884026,"20":-1.425541,"21":0.71509355,"29":-6.4884863,"5":-0.49564084,"39":-2.5433745,"46":-3.3904083,"26":-0.3979296,"49":-0.5887252,"7":-0.1689198,"30":-3.2371833,"12":-1.3743494,"18":-0.56168216,"48":-6.2913656},{"3":0.1732178,"10":-1.8926271,"28":-1.4953603,"33":0.805235,"36":-3.458831,"41":-2.7929707,"39":1.3030131,"16":-0.117981456,"29":-3.1151898,"35":-2.2739785,"38":-3.272089,"19":1.1487312,"46":-1.2794657,"20":-0.22847109,"7":-0.32170042,"2":0.45393562,"13":-0.024807762,"18":-0.2644976,"42":-5.8328943,"1":-0.3120924,"15":-0.7801078,"6":0.57796335,"9":0.008637002,"34":-0.16548419,"43":-2.8915856,"24":-1.9777126,"49":-4.152351,"45":-1.0009283,"11":-2.4516919,"8":-2.7317948,"17":0.66630775,"30":-2.788486,"26":-0.05352392,"31":-4.2538157,"44":1.4022282,"0":-0.34089082,"47":-3.231812,"12":-1.8993992,"23":-3.3586304,"32":-4.4242287,"22":-0.8754101,"37":-5.9391913,"40":-5.3672366,"48":-2.2119036,"14":-0.8015324,"25":-0.62322587,"27":-2.5147645,"4":-0.23502302,"21":-1.553767,"5":-0.9330942}],"nn_shapes":{"42":[18,33,33,8],"49":[18,3,33,5,27,33,19,14,23,8],"31":[18,26,26,9,16,9,22,22,8],"47":[18,4,16,11,17,3,8],"8":[18,6,29,13,30,24,33,31,20,8],"17":[18,21,8],"16":[18,6,17,28,12,4,22,30,8],"15":[18,3,28,30,23,4,8],"3":[18,8,22,29,16,25,8,23,10,21,8],"2":[18,16,29,18,5,6,22,23,8],"5":[18,22,27,15,12,9,8],"22":[18,15,30,8],"23":[18,10,31,30,26,3,28,30,8],"24":[18,27,26,26,26,33,8],"26":[18,3,22,17,3,28,7,25,16,22,8],"29":[18,26,21,12,14,19,8],"11":[18,24,3,12,17,5,8],"34":[18,28,24,8],"37":[18,23,20,8,18,22,18,27,8],"41":[18,12,18,30,11,3,29,8],"48":[18,11,11,21,8,13,8],"40":[18,10,14,8],"1":[18,24,21,12,8],"14":[18,22,11,15,13,13,24,5,34,8],"19":[18,16,10,13,10,14,3,11,8],"46":[18,18,21,23,19,7,24,8],"36":[18,33,3,21,28,8],"0":[18,14,32,33,3,8],"18":[18,16,16,27,18,8],"10":[18,29,27,13,5,17,26,34,8],"13":[18,31,6,34,27,15,34,33,8],"38":[18,20,23,8],"21":[18,8,13,24,8,9,31,8],"9":[18,18,3,14,8],"12":[18,14,8],"20":[18,7,9,24,24,24,29,24,8],"25":[18,24,34,14,6,20,22,15,6,6,8],"27":[18,23,32,17,33,28,8],"4":[18,19,20,30,9,30,18,20,4,21,8],"28":[18,30,3,9,8],"30":[18,7,31,25,22,34,18,8],"32":[18,25,5,19,7,23,8],"33":[18,4,30,26,15,30,12,5,21,27,8],"39":[18,7,24,32,29,19,20,8],"35":[18,29,15,29,14,12,6,8],"43":[18,5,12,24,28,14,16,8],"7":[18,34,12,10,13,28,21,15,28,8],"44":[18,21,3,18,11,8],"45":[18,12,30,8],"6":[18,25,19,16,19,30,8]},"crossbreed_segments":17,"weight_initialization_range":{"start":-1.7432716,"end":1.6809266},"minor_mutation_rate":0.16051888,"major_mutation_rate":0.9839299,"mutation_weight_range":{"start":-0.88359714,"end":0.88359714}},"state":"Finish","generation":5,"max_generations":5,"id":"a21eff6c-ff0d-4f4f-8354-14ac42930850"},"left":null,"right":null}},"right":{"val":{"node":{"id":"203368ce-5df8-47cc-85a3-8737f2f61d5b","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_203368ce-5df8-47cc-85a3-8737f2f61d5b","population_size":50,"generation":9,"scores":[{"13":-6.0615363,"12":-2.6796837,"4":1.5202897,"15":-1.3500706,"18":-0.5050914,"7":0.6162976,"19":-0.1533648,"21":-1.0203056,"22":-1.3992598,"23":-2.641868,"28":-4.299928,"30":-5.302378,"1":0.94063365,"29":-2.8476875,"17":-2.6706123,"31":-6.235944,"3":-4.7235327,"27":-2.455749,"32":-2.7329144,"34":-3.846684,"45":-5.355481,"48":-5.3916907,"33":-6.618595,"40":-0.61342084,"38":-6.691718,"41":-0.71297365,"42":-2.482652,"47":-4.85316,"8":-5.761569,"36":-3.1418037,"37":-1.5414846,"11":-1.3640214,"0":-3.1706002,"10":-4.0832353,"24":-0.0047788084,"5":-0.06780057,"6":1.4726968,"9":-2.7802906,"46":-5.0855565,"16":-1.5577738,"2":-7.291587,"26":-3.848475,"39":-2.580203,"44":-8.082107,"25":-7.188414,"49":-2.8559015,"20":-4.22116,"43":-3.6626022,"14":-3.942197,"35":-2.3295014},{"18":-0.11432342,"22":-0.9443925,"41":-1.9059979,"48":-3.2894027,"43":-5.4873667,"9":-0.45641842,"29":-3.8709152,"35":-8.257012,"44":-8.091996,"23":-1.6757748,"14":-2.8090603,"12":-0.45822677,"31":-5.4818435,"27":-9.313177,"47":-0.7598516,"6":-0.3413308,"17":-2.7760038,"25":-2.5767207,"33":-1.674531,"40":-3.7255013,"46":-1.3680242,"20":-1.4310821,"3":-4.8296347,"5":-1.0949562,"19":-4.3625565,"37":1.8395226,"26":-0.514341,"42":-0.20106061,"36":-4.6662865,"16":-3.7663884,"11":-1.0167248,"15":-1.8159335,"2":0.73568285,"24":-1.5104856,"28":-2.4822166,"45":-4.814537,"1":-5.2182913,"7":-2.1969981,"49":-5.352608,"4":-1.80509,"13":-2.6319325,"8":-0.20569114,"10":-1.935244,"32":-3.320692,"38":0.6912533,"30":-2.2159657,"0":-3.753378,"21":-2.7289367,"34":-4.2699256,"39":-0.92220813},{"31":-6.2261367,"34":-2.2532945,"19":-2.9685862,"24":-0.36538887,"29":-1.8620571,"1":-1.0877621,"20":0.275602,"39":-3.096109,"9":-1.3850116,"38":-9.6056385,"43":-0.26035717,"44":-4.687214,"32":-2.6568785,"16":-0.41369098,"3":-0.3260142,"7":1.6613563,"15":-1.6330436,"46":-7.197299,"22":-1.6352446,"4":-1.3589034,"49":-5.975999,"10":0.7375352,"30":-6.0976777,"40":0.54419273,"42":-1.4632086,"2":-5.911438,"26":-2.3958082,"5":-5.546512,"6":-1.177063,"23":-0.3758726,"0":-1.5063884,"21":-0.48584405,"25":-5.264371,"33":-3.4972088,"8":-2.2249763,"12":-4.6143556,"13":-2.613673,"14":-0.2013896,"35":-2.59116,"18":-2.9167368,"36":-1.2123888,"37":-4.6822767,"45":-4.273552,"27":-2.7699463,"47":-0.6502439,"48":-1.493132,"17":-1.741444,"11":-0.5439457,"41":-0.808615,"28":-2.9987345},{"5":-1.8213758,"43":-2.7431512,"38":-5.95363,"49":0.035115886,"32":-5.1510925,"12":-2.8091362,"25":-6.496497,"33":-7.949338,"48":-0.99491465,"8":-2.0829463,"2":-0.031106567,"15":-0.372649,"34":-1.6739277,"42":-0.619355,"16":-1.4548671,"37":-3.4394093,"9":-1.5828683,"30":-0.9453615,"45":-1.724397,"21":-1.6225876,"0":0.15423045,"36":-1.7103798,"28":-4.6417975,"19":-2.4957173,"41":-1.5402925,"6":0.33565888,"10":-1.8752396,"24":-2.7322052,"40":-1.9615946,"44":-5.6492515,"17":1.014154,"26":-6.536299,"7":-1.3930643,"13":-2.5884836,"3":-1.262034,"14":-2.5048103,"46":-6.4057937,"1":-0.21534681,"47":-6.278834,"27":-4.356794,"31":-1.4171464,"39":-6.406414,"20":-6.073895,"22":-0.71730816,"29":-0.8216996,"23":-1.5029604,"35":-4.992949,"11":-3.7783446,"4":0.0031358034,"18":-0.67258096},{"10":-0.25090164,"18":-0.858119,"8":-5.777336,"19":-0.7887672,"39":-4.918338,"49":-0.7818918,"7":-0.5166468,"21":0.13674489,"37":-4.7251687,"43":0.14045382,"11":0.17619798,"1":0.021573782,"32":-3.2923455,"29":-7.159108,"14":-1.3452873,"48":-2.3901944,"36":-3.0151825,"0":2.8386433,"6":-1.8094699,"25":-5.2633567,"40":0.6209098,"5":-1.658753,"23":-0.737769,"41":-3.5934289,"47":-4.8464775,"22":-1.9721928,"38":-2.089369,"15":-0.538136,"27":2.3752265,"31":-6.2390184,"44":-5.0705123,"3":-2.6324131,"2":1.2862574,"26":-5.131711,"20":-4.543001,"13":-3.8828483,"16":-0.790923,"9":2.1973586,"33":-1.910883,"17":-1.5043161,"4":0.075564995,"46":-2.2381697,"12":-1.9475548,"30":-5.2913437,"24":1.4310465,"28":-7.7879343,"42":-6.175967,"34":-5.14563,"35":-0.025316978,"45":-5.904042},{"25":-2.8692286,"47":-7.627328,"10":0.17991559,"12":-2.7248616,"31":-6.1483893,"15":-1.9559215,"33":-7.1912904,"49":-1.3946544,"2":2.2620661,"39":-2.7464297,"41":-2.714857,"42":-0.036035158,"6":0.288,"13":-1.0198475,"40":-5.378584,"36":-8.762684,"45":-4.758359,"46":-3.1634154,"48":-5.722167,"22":-1.2965059,"11":-0.07008378,"14":-0.43319377,"16":-0.95561635,"26":-5.4947495,"30":-3.170524,"44":-5.551297,"1":-1.5958624,"4":-0.7176281,"35":-6.031193,"17":-4.579427,"0":-2.0472035,"37":-2.643792,"28":-0.97088355,"38":-3.72789,"27":-3.2043538,"5":0.62878126,"7":-0.8526117,"18":-1.6840101,"43":-4.0878997,"9":-0.6471418,"24":-2.3522565,"20":-5.3582354,"3":0.73508096,"19":-1.8957741,"21":-3.051081,"34":-5.6870265,"8":-2.418698,"32":0.38842565,"23":0.5827245,"29":-3.823841},{"5":-0.220507,"23":-4.410055,"27":-6.706713,"39":-3.3960824,"40":-2.1484895,"49":-1.5460713,"9":-2.1876059,"41":-4.4184256,"31":-5.663493,"14":-1.3139164,"6":-0.5312986,"36":-6.3686433,"37":-5.154157,"45":-2.2323058,"32":-4.058252,"4":-6.0212264,"10":-0.86265737,"46":1.7008975,"18":4.498134,"8":-2.7613544,"42":-4.4026065,"17":0.39508438,"2":-1.616938,"21":1.1348299,"13":-3.777708,"35":0.43933,"26":-0.13403341,"47":-5.7819333,"7":-2.3441997,"30":-8.241878,"44":-5.7271743,"12":-3.0233912,"34":-5.5483613,"25":-6.8587112,"29":-4.544421,"43":-4.879415,"48":-3.8700097,"24":-1.362485,"1":-3.36082,"11":0.27863663,"20":-1.9567833,"22":-2.7686324,"15":-0.5434549,"3":0.9506604,"16":3.0620127,"19":-3.2280064,"28":-0.0035861984,"0":-0.4407722,"33":-3.465736,"38":0.223805},{"32":-4.2679415,"27":-4.617057,"29":0.087170504,"42":-2.9107833,"7":-0.39363545,"8":-2.871464,"9":-1.2108052,"16":-0.97661984,"21":-4.908767,"22":-1.2127631,"38":-0.31704503,"23":-2.939609,"31":-0.25758976,"24":-1.0036132,"14":-0.3476418,"26":-2.7195346,"35":-2.4430473,"36":-5.7113047,"46":-3.8022473,"45":-2.2357075,"3":-2.2639267,"20":-1.5970081,"49":-0.8887558,"47":-0.7757422,"11":0.94219744,"48":-0.28411922,"13":-2.093788,"37":-3.340915,"4":-1.825848,"2":-1.4276224,"0":1.2062461,"39":-11.106636,"41":-1.3248581,"19":-0.17546004,"43":0.76113236,"6":-2.7164958,"40":-5.0853443,"44":-4.2800694,"5":-0.65610963,"18":1.6902949,"30":-4.7505875,"12":-2.4398055,"33":-1.3714097,"15":-0.63636,"10":0.9763905,"1":0.3065812,"25":-5.893094,"28":-2.331335,"17":-1.6994712,"34":-7.236337},{"4":4.092096,"28":-3.767767,"37":-1.5373563,"17":-2.157727,"13":-0.79626095,"2":0.9916604,"30":-2.0188994,"44":-1.2411817,"22":-3.8631191,"48":-3.4246116,"29":-0.97772866,"14":-3.3033214,"18":-1.3941777,"6":-4.4819703,"5":-0.4289755,"32":-3.4806926,"12":-0.33485812,"39":-6.292939,"43":0.92196214,"45":-0.6753839,"33":-0.25819525,"34":-3.035078,"25":0.004158786,"21":-2.043839,"16":-1.2684958,"27":-3.249688,"10":-0.87576705,"11":-1.8128872,"15":-0.4661776,"46":-4.3860717,"38":-5.7667212,"3":-0.5464066,"26":-2.9459498,"35":-0.5777134,"36":-4.628657,"40":-4.2499084,"42":0.51436794,"49":-7.9355116,"31":-6.1434374,"20":-0.3625256,"23":-3.510929,"41":-0.4129098,"7":0.92192286,"8":-1.9173721,"0":-0.23905472,"1":0.26135007,"9":-3.2764,"47":-5.172612,"24":0.75993913,"19":0.6675881},{"30":-6.456954,"16":0.036798805,"33":-3.7803955,"34":-1.9072559,"38":-3.089058,"19":0.58897,"39":-4.736411,"26":-1.6644833,"2":1.2184676,"40":-1.6352108,"41":0.77811116,"44":-2.814549,"36":-4.2947717,"45":-9.383311,"46":0.2331924,"37":-5.1677117,"32":-5.427541,"12":-1.1785867,"47":-1.0297588,"48":-6.8122406,"0":0.9402604,"18":-2.762963,"49":-1.5772594,"24":-0.3033608,"7":-0.3254214,"6":-3.3805523,"42":-2.89198,"5":-0.10723499,"31":-6.986254,"23":-1.6471126,"10":-3.4487329,"29":-8.496641,"11":3.7004852,"35":-7.657138,"17":-1.7617385,"20":-1.5303608,"28":-5.2911096,"22":-3.5617645,"43":-5.972586,"3":0.3634794,"13":-1.9006294,"9":0.3458802,"8":0.38095504,"27":-6.3094463,"25":-3.316194,"1":0.64339125,"14":6.0491147,"4":-0.6245182,"15":-1.1051981,"21":-0.5828692}],"nn_shapes":{"19":[18,6,7,18,8],"17":[18,29,27,20,15,8],"40":[18,27,11,26,3,17,8,8],"11":[18,30,32,22,16,10,13,16,27,24,8],"21":[18,6,7,20,8],"42":[18,28,8],"45":[18,27,26,5,16,26,8],"47":[18,5,29,18,16,9,31,8],"10":[18,31,12,22,25,24,6,31,8],"24":[18,8,7,4,15,15,8],"0":[18,5,21,4,7,9,13,28,8],"48":[18,31,6,29,33,14,30,14,8],"2":[18,16,34,19,20,8],"38":[18,5,31,12,15,34,30,21,8,34,8],"32":[18,29,28,32,22,13,16,25,14,18,8],"1":[18,33,8],"6":[18,29,12,5,18,21,29,8],"9":[18,30,24,7,23,15,6,8],"3":[18,15,28,8],"20":[18,18,20,9,19,13,4,8],"30":[18,23,8],"5":[18,30,18,14,24,26,33,25,28,33,8],"33":[18,20,16,32,28,27,20,14,7,8],"35":[18,33,5,8],"39":[18,19,20,16,8],"13":[18,32,32,15,8],"41":[18,4,4,19,8],"43":[18,20,18,28,17,13,15,8],"23":[18,6,13,11,8],"44":[18,29,8],"46":[18,7,20,20,14,4,17,8],"49":[18,7,18,32,5,14,8],"18":[18,15,29,8,8],"16":[18,23,7,18,18,19,6,6,33,33,8],"7":[18,26,9,8],"12":[18,3,4,6,8],"28":[18,32,12,22,9,19,26,8],"29":[18,11,32,12,8],"15":[18,10,15,22,8],"27":[18,30,22,13,25,16,25,10,4,8],"37":[18,25,24,33,24,33,16,8],"4":[18,29,25,31,16,20,9,20,8,8],"14":[18,19,8],"25":[18,17,16,26,21,13,23,7,34,8],"31":[18,34,7,21,12,34,8],"36":[18,15,24,10,8],"8":[18,6,19,16,27,11,21,18,24,8],"22":[18,30,8],"26":[18,3,13,28,33,14,9,11,10,8],"34":[18,12,8]},"crossbreed_segments":9,"weight_initialization_range":{"start":-1.3771327,"end":1.7500036},"minor_mutation_rate":0.6309173,"major_mutation_rate":0.06338024,"mutation_weight_range":{"start":-0.89320016,"end":0.89320016}},"state":"Finish","generation":10,"max_generations":10,"id":"203368ce-5df8-47cc-85a3-8737f2f61d5b"},"left":null,"right":null}},"right":{"val":{"node":{"id":"63af491b-9b84-4549-bf28-d5c3fcb72ffd","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_63af491b-9b84-4549-bf28-d5c3fcb72ffd","population_size":50,"generation":14,"scores":[{"8":-6.069092,"33":-5.0940948,"4":-4.470458,"42":-4.660769,"25":-4.349419,"47":-8.300748,"20":-1.9045479,"43":-3.161159,"36":-5.716647,"28":-4.0584264,"26":-4.338761,"39":-6.4248137,"23":-1.626778,"40":-8.266317,"49":-4.4791436,"16":-1.9763448,"3":-1.400876,"7":-7.603074,"11":-0.6352552,"21":-1.0142238,"32":-6.2223997,"2":-4.106714,"18":-4.8280454,"17":-4.45882,"12":-2.1958303,"5":-2.794752,"10":-4.4663258,"19":-2.1331758,"44":2.5687604,"48":-3.2384803,"46":-6.0489435,"1":-0.1744744,"35":-0.8893156,"9":-1.0875275,"15":-1.1706532,"27":-2.2872546,"30":-2.0954628,"13":-0.7405074,"14":-4.169658,"34":-4.5048933,"37":1.6277792,"41":-7.645879,"31":-0.7182592,"38":-5.6641545,"24":0.78242075,"22":-6.6202226,"45":-3.6109688,"6":-4.854441,"0":-3.7418995,"29":0.9188182},{"49":-8.088182,"27":-4.4094706,"11":0.47737116,"29":-2.8574414,"31":-6.4447417,"34":-7.420352,"42":-5.0692816,"7":-1.9054571,"28":-3.26824,"17":-2.0616498,"48":-5.1362467,"21":-3.938991,"44":-6.1047955,"36":-3.2295728,"22":-2.1624913,"4":-0.09455097,"33":-0.25262243,"9":0.7189634,"26":-5.600524,"15":0.42617542,"40":-0.0046018003,"46":-4.7301927,"35":-3.7268822,"0":8.1669,"24":-4.9653997,"23":-2.7329252,"41":-10.61446,"6":-1.5024382,"1":-0.6274835,"12":-2.2065892,"10":-3.2536988,"16":-5.0924234,"18":-1.4048216,"2":-4.133996,"37":-10.1552105,"14":-2.3844306,"38":-6.710885,"43":1.4886957,"8":-0.360426,"32":-5.816176,"39":-2.983708,"3":-2.1701016,"13":-1.825918,"20":-5.24152,"25":-5.1760826,"5":-0.58427346,"30":-5.0981026,"45":-5.697856,"47":-5.226863,"19":-1.560044},{"42":-4.790041,"20":-1.1890002,"43":-6.0043163,"28":-6.9555635,"48":-0.510232,"9":-0.4068926,"26":-8.332675,"34":-1.0714524,"37":2.1537495,"30":-3.716394,"46":-2.1185315,"47":-5.9434023,"24":-3.1260734,"11":-2.7178226,"19":-1.5212322,"14":-1.7723862,"13":-0.8461763,"32":-5.6576056,"4":-3.228576,"0":1.411445,"35":-7.955058,"22":-0.82116145,"38":-4.0411196,"49":-3.5768852,"25":2.5552037,"17":-3.8672116,"7":-0.0842596,"27":-3.3282406,"10":2.898662,"39":-5.368108,"40":-1.03172,"6":-1.0901622,"2":0.6594648,"31":-0.2705296,"18":-2.2509701,"29":2.1199079,"12":-0.2960566,"36":-5.92122,"15":-2.254159,"41":-5.7638197,"33":-6.19812,"23":-1.9294733,"21":-3.52288,"5":-0.1741896,"16":-1.6020119,"45":-3.3413723,"8":-1.7656858,"1":-1.4515138,"44":-5.3893876,"3":-0.79005444},{"7":0.16130401,"21":-1.9730005,"15":-0.1315082,"40":-5.3528476,"34":-4.213156,"48":-4.282601,"47":-5.5874953,"25":-1.2648476,"8":-0.22879839,"1":1.4288082,"6":-0.94857055,"26":-7.524634,"39":-6.158347,"35":-4.27282,"43":-8.628385,"13":0.10465145,"44":-3.2539196,"0":-0.11162619,"5":-1.043254,"42":-2.0195737,"24":-1.6932726,"2":3.3416386,"23":-1.5218649,"36":-8.102966,"41":-6.2159843,"9":-1.6141726,"12":-0.74325323,"20":-1.4542454,"27":-7.2206926,"18":4.016916,"30":-0.874025,"29":-7.2742605,"14":-2.3179727,"45":-8.892599,"10":-0.69393337,"46":-6.7053704,"4":2.1962924,"16":-14.738788,"19":-0.21545644,"33":-4.486966,"38":-2.6419265,"49":-1.9328684,"11":-1.293656,"31":-3.5481186,"3":0.45642534,"22":-0.5492472,"37":3.1110644,"32":-3.7383697,"17":-0.85649097,"28":-7.8166184},{"47":-4.3279033,"35":-5.373517,"11":-0.74563074,"40":-5.3293886,"5":0.13447618,"22":-1.7905525,"38":-7.99418,"29":-5.6509647,"33":-1.2474883,"19":-1.3597196,"27":-2.762928,"3":-4.4732842,"17":-0.046969604,"32":-1.0133835,"48":-4.5410213,"23":-1.0213821,"20":-3.5897534,"26":-1.9709444,"18":-0.6774128,"2":-0.13767043,"0":-4.548816,"14":-0.295729,"4":-2.9405982,"36":-4.5952272,"28":-2.512223,"46":-2.4868076,"9":0.3691674,"24":-1.3622794,"25":-6.683936,"34":-5.349573,"1":-0.67742556,"6":-1.951775,"7":-1.3647935,"39":-4.418799,"41":-5.764389,"12":-0.98820055,"13":-0.3580936,"16":-0.54273975,"8":1.8473701,"43":-1.8787056,"42":-5.4131083,"49":-5.41763,"31":-5.7524247,"15":-1.1466728,"30":-7.7752686,"37":0.27733213,"45":-7.140503,"44":-6.461798,"21":-1.8989708,"10":-0.46728197},{"2":-1.56248,"13":-0.7525544,"43":-5.837439,"6":0.47565785,"46":-1.5897272,"39":-5.68252,"45":-2.3996503,"44":-4.9558625,"23":-2.2326112,"41":-9.368122,"9":-1.9876497,"11":-1.8456295,"15":-1.3360106,"29":-3.2400506,"20":-2.602886,"32":-0.4177026,"22":0.64007074,"19":-0.45666522,"1":-0.5352438,"27":-4.078212,"5":-0.3983138,"17":1.3219271,"16":1.6173245,"0":1.1778939,"33":-7.54399,"42":-5.606898,"34":-0.9048826,"47":-3.0472536,"38":-2.98173,"18":-2.141915,"40":-5.1117973,"10":-0.34259522,"7":-0.556374,"36":-1.6564305,"25":-10.329073,"48":-3.1240704,"3":1.0361704,"28":-8.048054,"35":-3.0688634,"24":-0.1821236,"37":1.7575128,"8":-1.3200384,"4":-0.1346158,"49":-6.504126,"26":0.5167214,"12":0.96005666,"14":-1.1138606,"31":-3.9105728,"21":-2.5513926,"30":-9.554128},{"13":-1.4711908,"44":-5.9292803,"32":-1.6712959,"10":-0.11659022,"15":-0.22578481,"20":-0.26050824,"14":-3.9976869,"8":0.5072394,"16":-0.20553902,"45":-6.6763563,"37":-6.550061,"29":-6.1376,"3":0.9823904,"24":-1.8991677,"25":-2.7527473,"40":-6.401937,"28":-5.2939734,"7":-0.048050977,"21":-1.5307443,"1":-1.0779276,"17":-0.76480997,"23":-1.3948135,"6":0.12415314,"9":-0.26064882,"11":-0.9512538,"36":-4.4375963,"41":-3.6213593,"42":-6.099228,"48":0.09729838,"2":1.8621299,"4":-0.72417676,"33":-7.120556,"30":-3.1712918,"18":0.010256794,"35":-6.842204,"19":-0.20973177,"0":-2.6267352,"22":-3.4517379,"27":-1.727238,"34":-4.3673334,"43":-6.9841127,"26":-5.9439487,"12":-1.3332065,"47":-1.0936369,"39":-3.052627,"49":-5.076259,"5":0.1122748,"38":-6.282195,"46":-6.4391375,"31":-1.3574141},{"10":-0.2211164,"35":-6.6669593,"48":-1.0380865,"20":2.0287774,"29":-3.3574173,"9":-0.5559626,"14":0.14803696,"12":0.41750318,"38":-7.112405,"4":0.1335312,"18":-1.0399034,"23":-1.4316481,"36":-3.5204754,"41":-8.614304,"26":1.4097984,"1":4.035802,"30":-6.4118285,"17":0.36632484,"22":3.0402958,"21":-1.0572317,"19":2.5371234,"24":-0.9996996,"15":-1.104537,"0":-0.7000558,"28":-3.8471859,"3":-1.2423786,"5":-0.808416,"32":-8.684097,"33":-4.9276524,"43":-6.9978704,"42":1.5353154,"8":-0.47881657,"45":0.5173014,"46":-0.34014502,"47":-6.6365633,"44":-7.32023,"27":-6.893125,"31":-6.360853,"34":-5.0873036,"40":-2.3171132,"49":-2.8701851,"6":-1.2537096,"7":-4.2261634,"2":-0.70211,"25":-6.5653243,"37":-4.22389,"11":0.2990488,"13":-0.20701341,"39":-4.641257,"16":0.0112473965},{"39":-3.6825485,"44":-2.4370027,"9":-0.1743464,"47":-7.0503654,"13":-0.0332822,"41":-2.5515766,"1":-1.541414,"0":1.541014,"4":0.940028,"27":-2.8493986,"36":-4.726627,"24":-1.6463757,"25":-3.654139,"7":0.4505847,"3":0.89223623,"23":-2.0576584,"29":-3.908837,"30":-4.5101647,"21":-2.445579,"31":-3.649403,"40":-6.0176225,"17":-0.31697503,"34":-4.48388,"12":-0.9229352,"45":-1.9503323,"18":0.9324938,"33":0.15854302,"37":-1.6256497,"35":-7.331717,"48":-6.6231103,"15":-0.5374302,"38":-5.9349833,"32":-4.645023,"42":-5.042142,"43":-3.789404,"46":-1.1764994,"6":5.882359,"2":3.8521378,"10":1.499598,"16":-1.7752663,"19":-0.90521586,"22":-1.8889548,"5":0.6613487,"28":-8.877123,"49":-7.0020742,"8":-0.87334347,"26":-7.4668303,"20":-2.512741,"11":-1.0245552,"14":-0.81175613},{"22":-0.49079657,"33":-4.0775614,"37":-0.62546676,"0":1.7051964,"10":-0.047491405,"40":-4.3915215,"1":2.0448983,"5":-1.378012,"4":0.3188538,"16":-1.7123814,"15":-1.483071,"34":-5.7027273,"41":-3.6069686,"42":-4.998118,"39":-6.598256,"43":-4.106491,"7":-1.4503245,"12":-0.5346658,"17":0.398249,"14":0.2996894,"25":-6.3164377,"31":-7.430293,"38":-5.887355,"44":-5.486763,"21":-3.704354,"2":-0.19979087,"47":-5.257516,"28":0.5822532,"35":0.44862086,"11":0.1961306,"13":-4.0350695,"24":-0.28154978,"26":2.9913385,"32":-5.9540772,"8":-1.8885313,"18":0.0213396,"46":-6.612848,"23":0.1444552,"48":-2.0025697,"9":1.4237278,"20":-0.028476572,"49":2.3796742,"3":0.8991828,"19":-2.0367374,"27":-5.2373004,"30":-5.489272,"45":-1.1280714,"29":2.535732,"6":1.3464735,"36":-5.648479},{"11":0.11987443,"13":-1.3703808,"8":-0.7758378,"35":-1.3983872,"18":-0.27867323,"27":-4.3856654,"44":-1.2755907,"15":-0.32736063,"24":-3.7100956,"32":-0.0764086,"43":0.18626317,"45":-3.5338511,"4":1.0853077,"25":-9.29453,"16":-4.446281,"12":0.1594002,"36":-1.7182869,"20":-2.3400369,"37":-4.4235787,"47":-10.831648,"48":-3.013094,"49":-8.558775,"30":-7.007659,"7":3.540858,"23":-0.28038746,"42":-6.7514243,"22":-0.8520716,"3":0.22084627,"10":-0.72354466,"41":-2.6133766,"34":-7.1538353,"29":-5.3938837,"40":-0.40576425,"9":-4.1087017,"17":0.1366714,"28":-1.7240318,"38":-1.3967516,"46":-4.3899465,"31":-6.127557,"33":2.3046422,"39":-9.954111,"26":-9.14213,"14":-3.0409713,"2":3.2370842,"6":0.40174532,"0":-4.177638,"5":-0.3059289,"19":-1.0527039,"1":0.79302967,"21":-0.58490145},{"12":2.1832337,"49":-7.0401483,"16":-1.6947788,"30":-1.3135207,"18":0.703499,"44":-5.152332,"11":0.570145,"26":-3.0996785,"0":0.070637405,"8":-0.7534913,"20":-1.954236,"27":0.9747921,"7":0.5450776,"29":-5.49309,"40":-5.0619526,"41":-0.27579817,"1":-2.5208938,"6":2.2722094,"37":-5.502732,"13":2.1581044,"22":-2.8321745,"38":-0.6764842,"39":-6.8810205,"42":-4.752784,"21":-1.3444358,"46":-5.083355,"36":-8.058732,"17":-0.6713782,"23":-0.039514005,"3":-1.5181656,"14":0.388066,"15":0.17807738,"10":1.4885564,"4":-1.7827431,"2":1.1435935,"33":0.776235,"43":-4.068568,"25":-4.0459485,"47":-5.918283,"28":-7.9561453,"19":-0.061852597,"48":-4.999099,"31":-6.3655405,"9":-0.084318206,"5":-2.254188,"35":-2.5133655,"45":-0.6381622,"24":1.5963113,"34":-4.9767027,"32":-1.4229188},{"14":-0.26403242,"2":2.1234822,"16":-0.171707,"42":-1.957675,"8":-0.4832286,"27":-4.791644,"46":-5.7846956,"13":6.383906,"10":0.82343644,"32":-6.1985607,"21":-0.2600368,"35":-3.293181,"15":-0.16612342,"39":-5.487646,"17":-0.90918285,"18":-0.4506496,"45":-4.5066237,"28":-2.034595,"36":-3.2557678,"31":-6.3209476,"7":0.9062312,"25":-4.8203287,"23":-1.4385079,"38":2.3662407,"4":1.4349756,"33":-3.0348454,"34":2.8219178,"11":0.971183,"41":-6.6662207,"5":3.1754055,"19":-0.5304278,"47":-4.7532735,"37":-6.768679,"48":-1.6132733,"6":-1.2470607,"30":-1.7141603,"9":0.0972314,"22":-0.8169382,"3":0.66940355,"29":-7.715441,"12":0.0965532,"43":-2.8710086,"20":-0.96138203,"0":2.461893,"40":0.16619243,"26":-1.75643,"24":-1.9928026,"44":-4.8273773,"49":-1.7080085,"1":3.1069047},{"23":-1.0567077,"41":-0.36431417,"15":-0.735537,"46":-2.1280885,"49":-6.228997,"8":1.2514383,"37":-6.034368,"0":3.241253,"38":-5.155979,"11":2.625294,"4":2.5957713,"26":-4.7352057,"30":-5.138303,"1":1.9234915,"40":-5.213203,"47":-5.9502745,"29":-0.72342,"22":-1.8519977,"21":-0.5797606,"5":-0.3990143,"24":-1.5207313,"48":-4.497856,"2":1.6721871,"33":-4.990741,"35":-0.5015704,"9":-0.97921765,"45":-3.2262008,"31":-1.5186932,"14":1.0603493,"13":-0.7976054,"32":4.094207,"34":0.33811378,"44":-7.761998,"16":-0.13641839,"42":1.3870549,"3":0.11979119,"25":-8.261856,"27":-5.808234,"20":-0.6997692,"10":-1.6402384,"19":-1.2573239,"17":0.55972064,"7":0.021158999,"39":-4.507757,"28":-1.2109119,"18":-0.9441657,"12":-0.21774468,"36":-2.7474074,"43":-8.356656,"6":1.5366848},{"46":1.1030022,"13":0.7303882,"48":-4.0814185,"49":-7.3009596,"11":2.099044,"34":-2.6376014,"33":-2.0458329,"15":3.298797,"7":-1.5549805,"6":-0.97469556,"21":-0.19640112,"30":-4.087301,"18":-3.4629655,"41":-3.6512809,"25":-3.2870553,"0":2.9551253,"28":-6.6697874,"37":-6.231577,"39":-3.4252715,"44":-5.2634215,"27":-3.615893,"12":-1.2823393,"17":0.2736004,"22":0.521864,"4":2.4086232,"14":-0.578271,"29":-4.471439,"3":1.3296368,"20":-0.71489245,"35":-0.4940032,"40":-8.11772,"2":1.6850106,"45":-0.2666122,"5":1.3215994,"26":-5.663763,"31":-6.1445756,"47":-3.7819877,"32":-3.4226127,"9":-0.1418186,"19":-0.45779124,"38":-1.9440556,"42":0.12248763,"1":-1.4773883,"10":-1.0335268,"36":-9.659117,"8":1.0191154,"24":0.284077,"43":-4.3620725,"16":-2.0288436,"23":0.27707738}],"nn_shapes":{"15":[18,20,8],"26":[18,32,31,24,22,8],"41":[18,22,25,25,31,23,8],"8":[18,7,10,28,26,16,23,31,28,8],"10":[18,30,7,34,8,13,24,26,27,8],"19":[18,15,7,20,8],"28":[18,29,17,9,34,6,14,13,28,27,8],"34":[18,16,11,21,16,20,18,5,8],"44":[18,28,11,11,19,17,8,31,32,5,8],"13":[18,14,10,4,8],"17":[18,13,8],"1":[18,25,21,8],"20":[18,15,8],"14":[18,13,8],"12":[18,14,34,25,18,11,27,23,17,8],"22":[18,30,8,33,7,25,17,18,26,31,8],"6":[18,21,14,31,15,27,26,34,7,8],"11":[18,5,14,4,10,8],"29":[18,14,17,24,25,10,18,13,8],"36":[18,30,8,19,28,18,30,21,24,8],"42":[18,28,8],"48":[18,10,6,20,8],"35":[18,13,12,32,16,34,8],"23":[18,13,11,5,32,8],"45":[18,25,8],"43":[18,23,8],"9":[18,19,5,34,8,11,8],"33":[18,34,8],"5":[18,15,15,8],"3":[18,28,8],"2":[18,13,8],"27":[18,27,25,7,31,9,3,3,19,10,8],"21":[18,21,17,21,8],"49":[18,13,27,27,32,3,8],"31":[18,15,32,8],"37":[18,10,9,8],"18":[18,24,12,8],"46":[18,24,6,5,31,8],"47":[18,31,3,25,32,24,28,4,25,8],"0":[18,14,5,4,5,8],"40":[18,5,31,8],"7":[18,34,4,10,11,30,8],"24":[18,16,11,12,31,23,8],"30":[18,11,18,16,33,12,9,21,11,15,8],"38":[18,17,33,19,8],"39":[18,26,16,31,22,8],"4":[18,34,28,11,11,29,11,12,8],"16":[18,27,6,14,10,19,10,34,27,8],"25":[18,28,10,10,32,32,12,8],"32":[18,30,7,24,28,8]},"crossbreed_segments":11,"weight_initialization_range":{"start":-1.0402882,"end":1.5273945},"minor_mutation_rate":0.9520668,"major_mutation_rate":0.8335203,"mutation_weight_range":{"start":-0.6023569,"end":0.6023569}},"state":"Finish","generation":15,"max_generations":15,"id":"63af491b-9b84-4549-bf28-d5c3fcb72ffd"},"left":null,"right":null}},"right":{"val":{"node":{"id":"48ec59ed-40e8-4fe0-a929-92cc324873ca","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_48ec59ed-40e8-4fe0-a929-92cc324873ca","population_size":50,"generation":19,"scores":[{"13":-1.4142692,"47":-1.6592379,"49":0.3410964,"22":-6.591704,"1":-3.781786,"24":1.5314912,"23":-0.24905081,"35":0.6497308,"39":-4.099218,"46":0.30892056,"19":0.5735318,"14":-0.015305,"40":2.8135312,"6":-1.1633298,"8":-1.510386,"29":0.176255,"3":1.6047258,"10":-1.4822628,"28":-0.61696815,"2":0.80709714,"26":-1.1791419,"43":-2.7403412,"27":-5.292212,"48":-0.61211836,"20":-3.3156848,"15":2.4606733,"32":-0.427847,"4":-4.128209,"34":2.834388,"17":-0.9344204,"18":0.4177352,"41":-1.2587774,"42":-5.070863,"38":1.3624249,"9":-4.3788576,"37":-1.8078709,"12":-7.796138,"0":1.6523159,"5":-0.6745032,"25":-0.48673376,"30":-2.525943,"21":-3.7513194,"33":-3.6008232,"31":-3.4886208,"36":0.6533136,"11":-5.4849296,"44":1.2480085,"45":-4.5369644,"7":-0.630715,"16":-3.6863465},{"43":-1.4187799,"3":0.5602858,"15":0.28763258,"0":-2.1157553,"29":-4.9932585,"30":-4.8374114,"25":0.70482737,"32":-7.191799,"42":-0.229579,"21":-0.78822863,"28":-1.3480982,"27":-4.935256,"13":2.2695951,"5":1.0831163,"1":3.186741,"23":-0.53452784,"35":-4.816189,"17":-0.5340606,"37":-5.2658195,"46":-5.6004496,"39":-0.5162036,"4":1.5308871,"8":0.8792552,"34":-4.257403,"40":-0.035599984,"47":-2.2445347,"48":-4.9693174,"49":-5.36049,"22":-0.679153,"2":1.9149898,"24":-1.1383171,"41":-2.8188515,"45":-2.6315355,"7":1.0400218,"31":-4.352787,"14":0.30300403,"33":5.4634247,"11":-0.5809352,"36":2.6106982,"44":0.5219575,"6":1.0872599,"19":-0.41813737,"16":-0.3034976,"26":-1.6733954,"9":0.40437204,"20":-0.161498,"38":0.44429082,"18":-0.828228,"12":0.028000206,"10":1.2287343},{"16":-0.110653974,"32":-5.0137234,"6":3.2855735,"35":-4.916234,"33":-2.922092,"3":3.0281954,"4":2.7760167,"38":-3.822181,"0":-2.673564,"15":-0.79444206,"19":-0.10265,"22":-1.8150918,"39":-0.63781416,"45":0.86532545,"31":-3.163527,"26":0.078408144,"12":0.5580987,"7":0.95455945,"18":0.88184404,"2":1.7036226,"21":-2.5584326,"20":-0.4360312,"14":-5.642728,"30":-4.7311277,"40":-1.9300216,"37":1.6188036,"43":0.52707785,"44":-1.8107672,"47":-1.0798619,"48":-2.6738892,"8":0.6588317,"28":-7.9935713,"49":-3.944384,"5":2.0550141,"29":-3.8474987,"42":-1.0989082,"46":-5.9515686,"27":-2.0485811,"9":1.471608,"1":2.47185,"24":0.12452121,"34":-1.3555843,"10":1.3148601,"36":-3.0478368,"41":0.3570898,"11":3.7797947,"13":-0.10295038,"25":-0.8727354,"23":-0.35180458,"17":-0.96149045},{"35":-2.0437932,"5":2.0670333,"15":-0.46692443,"16":0.2679208,"0":-0.7605384,"12":0.59541285,"28":-4.173994,"20":-0.3992396,"45":-1.051142,"10":0.8140138,"46":-2.7517638,"42":-6.171156,"23":-4.831238,"48":-2.677819,"1":1.224031,"4":3.2673473,"30":-2.8519773,"32":-4.4643426,"33":-0.44000491,"37":1.3480432,"40":-2.1318023,"43":-1.1509007,"44":-2.0562027,"24":-1.8844818,"26":-5.3923025,"41":0.20562944,"34":0.51221,"38":-0.11386959,"7":1.5406876,"13":0.7819358,"29":-3.4840672,"2":-0.3486044,"31":-1.7830808,"8":1.2974102,"22":-0.5343738,"17":-1.6286198,"25":-4.005146,"36":-5.415494,"9":0.115413606,"27":-5.5485926,"47":-1.6215239,"49":-4.2820125,"21":-1.1757715,"14":0.635203,"18":1.8389509,"39":-0.49644622,"11":-0.68393385,"6":2.645306,"3":1.2012576,"19":0.08734958},{"6":-0.20685184,"7":0.7482052,"23":-0.116231404,"28":-0.40918478,"25":-3.7985291,"12":1.1636417,"15":-4.469844,"39":0.32581556,"29":-1.8403037,"42":-4.6972175,"9":-2.5952392,"45":-2.2271256,"49":-8.009915,"48":0.097842194,"2":0.9440371,"26":-1.4332052,"35":-3.9221509,"27":0.43948522,"40":-1.195525,"8":2.398372,"10":0.8457554,"41":2.879052,"14":-0.44750994,"46":-5.801083,"43":-5.6808176,"11":-4.294608,"5":-1.3787876,"20":0.21462278,"32":0.9640708,"38":3.164453,"47":-1.9674394,"33":-1.3166887,"17":-0.4287854,"13":0.6798086,"4":1.280604,"19":-0.33062363,"22":1.0917785,"16":-0.44355115,"18":1.1119808,"24":-3.979077,"31":-1.5345764,"36":0.22797139,"34":-5.955845,"37":-3.3212132,"44":-3.5280128,"21":1.5814302,"1":2.6665463,"3":-1.297233,"30":-1.5187546,"0":1.5335789},{"9":2.5269558,"34":-0.31695202,"15":0.358236,"16":1.9090137,"28":-7.2770643,"7":0.827785,"22":0.624541,"24":-0.49286598,"27":-0.047854356,"0":0.014746046,"32":-3.046439,"35":-1.970074,"43":-4.0212536,"12":1.1749474,"8":0.629521,"21":1.0198265,"26":-3.8477929,"47":1.4195983,"5":2.8749852,"17":0.3194898,"45":0.15612099,"46":0.57873833,"33":-3.400888,"18":-0.64871776,"31":0.9258426,"20":-0.2123458,"1":-0.018678535,"49":-2.536954,"44":-2.3766665,"48":-2.7109916,"42":2.4054458,"29":-0.365946,"19":-0.8580963,"4":3.024848,"36":2.353439,"6":0.7704121,"3":1.8559345,"11":0.67827654,"14":0.5944872,"40":-1.1564267,"25":-1.254179,"2":2.2601426,"13":0.11195178,"23":-0.7038632,"41":-4.349866,"30":2.0492432,"38":-1.8046792,"10":0.7969944,"37":-1.6574113,"39":0.6443548},{"38":-5.593236,"29":-2.0681195,"41":-0.22347467,"47":-0.20740981,"10":0.6398827,"43":-4.9407096,"27":-1.6263736,"3":0.618173,"25":-5.5301485,"42":-1.0599917,"1":2.8082507,"11":-0.21492091,"19":-0.210343,"46":0.38218263,"28":-4.883363,"40":-1.1004293,"16":1.3583285,"14":1.1719106,"35":-7.591609,"37":-2.6224945,"7":-1.7977062,"44":-2.5597596,"8":1.5589517,"23":0.6407778,"0":3.2596047,"12":-1.5043256,"31":-4.082364,"32":0.26498643,"9":1.5094626,"18":0.44769382,"24":1.0350058,"26":-3.0634084,"2":1.4039166,"30":-4.710024,"48":-3.0759435,"36":0.31134498,"49":-6.624237,"6":-2.9722295,"4":1.5669414,"15":1.6458206,"45":0.085735396,"21":1.7907193,"33":-1.297011,"39":-0.017494207,"22":-0.11674581,"5":2.5079103,"13":1.0968001,"34":-2.7541168,"20":0.5292344,"17":0.1906384},{"45":0.5252536,"26":0.3974368,"13":0.5588312,"30":-1.8244877,"7":1.4933112,"9":-3.8389251,"25":-1.1505449,"32":-2.7444942,"35":-5.6595144,"41":-3.4394646,"48":-1.361736,"37":2.154315,"23":-0.4231196,"42":-5.1911764,"47":-2.6004348,"34":0.2603728,"49":-3.3825104,"36":3.3940125,"46":-2.8885045,"3":2.6000814,"40":0.288439,"12":0.0005240202,"5":0.5214776,"15":-0.6024442,"18":0.5623234,"33":-2.7948456,"21":1.0384016,"31":-5.3817234,"38":0.6145694,"44":-2.7523022,"0":1.7473558,"4":1.6743968,"22":0.098448396,"27":-6.411023,"1":0.13419405,"8":0.6231524,"29":-1.2262142,"39":0.6235626,"10":1.0708561,"19":0.43949756,"28":-6.0392623,"14":-1.1028479,"16":-0.49937558,"2":2.3430197,"17":-0.55631053,"11":1.1489971,"20":0.100311995,"24":-0.96297103,"6":1.6375496,"43":4.015054},{"2":1.0920179,"14":1.3236622,"28":-4.63235,"34":1.0205047,"1":-5.815806,"4":2.558737,"5":-0.28819573,"35":1.8724258,"43":-3.1752496,"8":1.3220638,"20":-0.47128558,"22":-0.6295472,"16":-1.069982,"24":-0.2182076,"32":-3.0007422,"36":1.177172,"11":-0.2960864,"29":-3.1426556,"18":-0.321199,"39":-0.7286054,"40":-3.574768,"13":-1.7908977,"19":-1.566545,"6":0.10560825,"31":-2.9546175,"21":0.5344242,"46":0.8421229,"48":-5.4846654,"26":-0.16800079,"30":-0.63499004,"12":0.9751353,"17":0.080357805,"37":-5.7761393,"47":0.1455886,"0":-0.5469036,"25":-3.657659,"33":-5.989932,"42":-2.2691529,"49":3.8069737,"3":2.483134,"9":0.4608814,"10":-0.21474338,"23":1.5727831,"41":-1.7126083,"44":-2.4735823,"7":1.8333137,"27":-3.513314,"45":-4.305055,"38":0.33684176,"15":0.3030752},{"41":-2.4863777,"42":-1.4586465,"49":0.809266,"13":-1.2854989,"28":1.3336452,"17":0.2468174,"2":2.4948452,"5":2.9562654,"1":1.420696,"10":2.5979302,"19":1.0988202,"21":1.2759278,"25":-1.7183332,"37":-5.066767,"11":-1.7485008,"47":1.6204903,"24":1.452265,"8":0.07814765,"29":-2.434204,"0":-0.70899695,"9":0.15161927,"39":-4.978727,"46":-2.8493817,"12":-3.0307314,"15":0.14451161,"16":0.30487078,"33":-7.813867,"3":0.26481026,"34":-0.8905366,"48":-3.2962487,"18":1.7813027,"23":2.5804691,"7":0.616895,"14":1.2282082,"26":-1.2754395,"35":-4.9574194,"36":-7.887868,"38":1.2359992,"45":1.3187423,"44":-4.939965,"43":-0.731094,"32":-7.854181,"22":0.2931754,"40":-3.4965339,"27":-8.200976,"4":-1.6270396,"31":-3.2845535,"20":-0.0813784,"6":1.2323984,"30":1.2903303},{"29":0.76983786,"10":1.1695973,"30":2.3881211,"13":-2.1178145,"5":1.032727,"7":0.4232266,"14":1.4746369,"42":-3.0101175,"24":0.6749432,"3":2.5838737,"47":0.3716606,"12":0.034056615,"19":-0.5428704,"43":-2.3864942,"11":0.1684568,"33":2.047521,"36":1.3975251,"4":1.4912655,"49":-1.9071302,"20":0.66135,"31":0.3048658,"9":0.3718818,"39":-4.9778905,"34":-5.4845343,"21":0.264103,"28":-4.6927276,"2":3.9047546,"22":1.7412987,"26":-0.34287968,"0":0.79953676,"8":-2.503515,"18":0.5331046,"45":-3.80481,"40":-3.3307064,"35":-0.66795295,"23":2.5979419,"32":-5.551989,"15":-1.5478857,"37":0.6220144,"38":1.4604373,"41":0.97876513,"46":2.3414655,"48":0.4169334,"27":3.265742,"44":3.1393316,"25":-4.0014124,"16":1.7088041,"6":0.92421687,"1":-2.643936,"17":1.5321608},{"18":0.15271512,"28":0.8231074,"31":-1.9563223,"11":1.4094507,"5":2.5155084,"44":-1.3571329,"46":1.8211524,"47":-4.676179,"10":0.56388783,"48":-5.832213,"8":1.566211,"34":0.5341672,"39":2.360628,"41":-0.81608117,"22":-0.647547,"36":-3.0471568,"6":-0.59395313,"4":1.2839394,"25":-5.5545883,"37":-2.5384088,"20":2.9726455,"12":1.1182439,"13":1.8803022,"30":-3.0120084,"40":-7.0986595,"43":1.8841732,"0":0.6630294,"35":-3.9670842,"45":-3.7290092,"49":-5.0952806,"17":0.2583828,"9":0.7004884,"2":2.3868642,"14":1.1022385,"7":1.5477122,"33":2.143043,"38":0.5190696,"15":0.14373502,"29":-4.386737,"3":-3.0224242,"26":-3.8995755,"32":-0.51439965,"42":-0.383613,"24":0.26088682,"1":3.7029004,"16":1.1914532,"21":1.7543865,"19":2.4447637,"23":2.213075,"27":3.2780387},{"2":-0.65427446,"17":0.90136564,"9":-0.5912026,"45":-3.1104972,"30":3.8257012,"1":-1.1222456,"33":3.2083735,"42":-0.87039006,"48":-2.8021472,"18":1.5804374,"35":-0.44894782,"3":2.8484106,"21":1.9852188,"25":0.368119,"29":1.5137444,"4":3.0808597,"5":0.16939983,"43":-0.07154502,"20":-2.8535187,"46":-10.316307,"22":1.0970856,"41":3.381101,"15":-4.5189314,"47":-4.271408,"32":-0.12736262,"6":1.4997796,"8":0.8192856,"11":-1.1406939,"28":-3.0016181,"26":1.0549641,"19":0.3062792,"24":1.4364198,"38":-7.554895,"34":-5.6036787,"12":1.6562859,"37":0.65752596,"39":-5.1502852,"13":1.6195282,"40":-3.1926622,"49":-5.48314,"31":3.1437995,"16":2.4158304,"7":2.427155,"23":1.5658007,"10":4.544055,"0":3.7469585,"27":-3.7847276,"36":-0.1779578,"44":-2.9680314,"14":2.4946625},{"3":3.298818,"4":-0.100204185,"10":2.4243913,"31":-0.25114003,"18":0.24044621,"36":0.8928568,"7":1.5746257,"14":0.15602557,"2":2.7182584,"12":0.825092,"8":3.3791401,"0":0.8052031,"22":0.8128505,"34":1.0762432,"41":2.5831215,"17":2.047896,"33":-1.9972169,"46":0.7778258,"20":-1.6034877,"1":4.514162,"28":0.11998816,"48":-0.5316952,"35":-1.5996503,"11":0.22015977,"43":1.3659737,"26":-1.7325569,"27":-8.253664,"13":0.1734304,"37":-0.3869438,"29":4.442569,"47":-1.8515161,"21":-1.5478828,"25":0.3130408,"45":-1.9054098,"16":1.3335179,"49":0.8774155,"19":1.4240228,"40":-2.9783475,"42":-8.505626,"44":-0.31775862,"23":1.3930626,"15":1.3329651,"6":2.955215,"32":-3.1671722,"5":-3.956508,"30":-0.19470882,"38":2.264006,"39":-2.97102,"9":3.0682359,"24":-3.2080452},{"5":-0.5408281,"34":1.6181517,"47":1.5908577,"48":1.7172235,"49":-2.4024558,"17":1.9604908,"45":0.4115004,"37":0.3741082,"21":0.6393524,"13":1.7402023,"41":-1.8253998,"12":1.2494247,"23":0.70641744,"9":2.8593893,"10":4.1445603,"28":0.7264148,"20":-0.022489607,"22":-0.05633059,"11":-0.87277544,"32":-1.2561429,"42":1.4251055,"7":2.4510393,"1":-0.94481486,"31":-1.175452,"14":5.030714,"16":0.5764612,"29":-3.5285103,"6":2.1434236,"0":1.7296807,"43":2.7396176,"39":4.023013,"2":2.3528953,"25":1.9261501,"38":-0.88579595,"8":1.4411128,"4":1.5460316,"18":0.282081,"33":2.3182895,"26":-0.9694773,"40":-3.379509,"24":0.13257396,"35":2.2814608,"27":1.6139975,"3":2.6575274,"36":-3.9818718,"44":2.7506993,"30":-8.778745,"15":-2.0349305,"46":-3.470742,"19":-3.8818722},{"0":-0.06885891,"17":0.636088,"22":2.3478549,"24":-0.045837425,"2":-0.7956078,"12":1.9855839,"32":-2.3023667,"30":-0.97484815,"33":-7.4737372,"36":0.2148408,"40":1.4850098,"38":-2.8117013,"35":-3.077861,"41":3.9098945,"21":1.5247008,"42":-7.0531464,"31":0.36817628,"23":1.3593233,"39":1.0669391,"45":-3.938919,"47":1.2769856,"18":6.957845,"26":-5.302268,"44":-4.660885,"9":2.2920365,"7":2.529813,"1":2.1732376,"16":0.5333187,"14":-2.2619777,"3":2.3606544,"4":3.9950047,"6":1.7795098,"15":3.1897218,"5":0.10226059,"19":0.28279838,"10":3.0701108,"20":4.0169177,"27":2.5359845,"8":1.9472278,"29":-4.2149224,"34":-0.7892742,"13":1.2534057,"37":-1.3440574,"43":-6.9592667,"48":1.8630025,"25":0.82092935,"49":-2.3563805,"11":3.297667,"28":-0.329632,"46":3.2459197},{"22":0.50588465,"25":-4.7370954,"19":-0.91318095,"28":0.31480804,"31":0.065649465,"3":0.5063764,"6":5.1110444,"29":-2.489795,"32":-11.229839,"33":2.4709823,"34":2.8695674,"36":0.62174416,"40":0.1279682,"42":0.47100592,"44":-6.3088784,"15":-2.8494515,"24":1.8442934,"9":2.8121595,"46":-1.0751657,"27":-9.398052,"13":1.7741573,"17":4.1550884,"23":-2.0619445,"26":0.017771998,"38":2.193775,"41":-4.4600706,"49":1.5474513,"48":-0.37165743,"0":1.003366,"1":-0.32828838,"11":-1.8789746,"39":-2.3800955,"21":0.25985384,"35":-4.9107614,"4":4.0792146,"43":0.9346365,"47":-3.9471307,"37":0.43831158,"5":3.9157176,"16":2.5956256,"20":-0.17795548,"30":-0.44199666,"14":0.76868117,"45":-1.9965626,"7":-4.6487746,"18":0.8212639,"12":0.53507257,"8":2.3825238,"10":1.4968458,"2":-3.0374155},{"31":-7.249073,"2":5.348681,"32":-3.786815,"49":-8.196339,"4":-0.8796919,"3":3.8961625,"19":2.259184,"28":3.560927,"48":3.826062,"6":4.164523,"38":0.7781402,"46":-4.5676928,"29":-5.356247,"44":0.15922546,"23":-3.1550167,"21":-4.3266816,"45":1.2732226,"30":-1.4273186,"16":1.6312279,"36":0.24064521,"40":8.496379,"39":-2.6050453,"0":3.1275628,"8":-1.777848,"5":2.8627615,"12":-3.394359,"17":2.5028915,"35":-3.440471,"11":1.8028643,"10":0.92121077,"1":2.924457,"22":-3.813434,"24":-0.678315,"25":-0.17476197,"34":-1.3690792,"27":2.1548553,"14":-3.4256318,"20":1.4138949,"42":-4.0063763,"43":-0.44322735,"37":-3.5964768,"47":-3.10027,"13":0.79479057,"15":-1.8431025,"41":-6.513213,"7":-6.554456,"26":-0.3503102,"9":-3.2855153,"33":-6.1897845,"18":-1.7627401},{"0":-1.379682,"9":1.7693253,"24":-1.7425396,"29":-1.3095939,"12":0.3712794,"10":2.8009505,"39":2.0196414,"40":-5.442218,"19":0.07471542,"38":-8.772409,"30":-4.0180197,"11":1.4203637,"41":-4.7962756,"42":-1.1986992,"49":1.1919396,"4":0.32527694,"36":-1.9501377,"20":3.3796432,"43":0.95696867,"45":-1.8301255,"46":-8.758689,"25":-6.2930193,"6":2.598937,"47":-1.7618761,"48":5.545912,"2":-0.18258914,"16":1.6644957,"13":1.4507275,"27":-1.2420224,"44":-1.6392796,"21":-1.834655,"17":4.7461395,"32":1.9383072,"33":-0.09801979,"37":2.1693015,"15":3.7099216,"18":-0.849576,"3":2.982232,"31":1.9015796,"7":0.56306016,"14":1.4726218,"1":4.4150147,"23":-2.4777474,"28":-2.066594,"35":-9.634148,"8":0.754855,"22":0.9184534,"5":0.77764595,"26":3.8850017,"34":-6.85335},{"6":3.9536424,"37":-1.7106135,"3":3.952736,"41":0.7060195,"29":2.3967164,"33":-0.43785763,"12":1.9563553,"32":2.9527333,"34":-1.0106162,"20":0.13315716,"27":-0.92878675,"46":6.8867826,"49":0.93543357,"4":1.1506236,"14":1.0140021,"24":2.2763062,"28":3.0574925,"5":0.7614522,"18":0.8230068,"48":3.324789,"38":-1.2328963,"8":0.42752314,"39":1.5602262,"43":2.0240207,"31":0.43064317,"36":-1.9770912,"13":1.6254408,"15":2.7690034,"19":1.0028356,"22":-0.08353281,"21":5.3307166,"7":1.3423629,"25":1.2356588,"10":0.9194766,"30":-5.161771,"1":2.7103047,"35":-3.3447888,"42":-2.481101,"40":4.1213207,"0":-1.4099376,"11":0.052261163,"23":3.419119,"16":1.7044884,"9":-1.8273771,"17":1.8015019,"44":-7.9374723,"26":2.2453463,"2":4.293378,"47":-2.043562,"45":-0.16055045}],"nn_shapes":{"20":[18,32,11,18,5,8],"8":[18,30,27,26,14,8],"12":[18,17,8,23,30,23,8,33,27,8],"9":[18,7,33,20,15,33,18,8],"25":[18,6,29,26,8],"48":[18,12,11,13,20,25,8],"37":[18,24,24,8],"47":[18,8,24,6,10,16,30,11,8],"19":[18,4,23,8],"44":[18,31,16,7,19,23,25,7,12,6,8],"43":[18,25,8],"31":[18,3,11,15,15,24,24,24,28,23,8],"36":[18,32,7,10,17,9,7,17,11,8],"7":[18,7,5,6,30,29,31,13,7,8],"24":[18,18,8],"26":[18,14,10,33,8,11,4,21,28,8],"18":[18,26,7,6,8],"6":[18,8,25,8],"23":[18,12,4,27,27,16,8],"1":[18,15,31,25,7,30,33,33,8,8],"28":[18,3,20,20,8,8],"2":[18,30,33,8,24,22,13,28,3,8],"17":[18,33,14,8,15,8],"30":[18,8,31,31,20,32,33,8],"27":[18,28,20,20,15,8],"3":[18,32,15,3,11,5,11,3,8],"16":[18,31,18,8,29,8],"22":[18,16,18,34,34,12,18,8],"4":[18,10,29,8],"11":[18,29,29,30,29,8],"32":[18,11,8],"13":[18,4,24,19,15,33,10,34,15,8],"41":[18,4,8,5,23,33,6,5,8],"45":[18,22,20,18,34,7,15,8],"49":[18,24,8],"0":[18,13,19,14,20,15,8],"39":[18,25,8],"5":[18,17,8],"10":[18,30,5,28,4,27,8],"29":[18,12,13,12,20,32,8],"40":[18,19,8],"14":[18,30,11,14,24,26,9,29,15,13,8],"21":[18,31,17,8],"33":[18,24,11,8],"35":[18,29,17,13,20,28,25,19,8],"15":[18,17,28,4,8],"38":[18,34,30,3,8],"34":[18,34,15,23,8],"42":[18,33,9,33,29,5,17,24,8],"46":[18,19,10,34,8]},"crossbreed_segments":13,"weight_initialization_range":{"start":-0.69214654,"end":0.41588885},"minor_mutation_rate":0.9403318,"major_mutation_rate":0.28131688,"mutation_weight_range":{"start":-0.99331,"end":0.99331}},"state":"Finish","generation":20,"max_generations":20,"id":"48ec59ed-40e8-4fe0-a929-92cc324873ca"},"left":null,"right":null}},"right":{"val":{"node":{"id":"55eaae5a-e128-4417-ab98-89653f77e382","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_55eaae5a-e128-4417-ab98-89653f77e382","population_size":50,"generation":24,"scores":[{"37":-7.1222725,"12":-3.6037624,"27":-5.202844,"21":-6.3283415,"4":-6.0053186,"8":-4.040202,"13":-4.0050435,"17":-5.8206105,"40":-7.5448103,"42":-8.027704,"15":-5.1600137,"10":-7.9063845,"1":-6.9830275,"7":-3.3323112,"16":-6.1065326,"23":-6.417853,"25":-6.410652,"14":-6.5887403,"3":-6.3966584,"19":0.1242948,"28":-4.806827,"18":-6.3310747,"30":-5.8972425,"31":-6.398958,"22":-7.042196,"29":-5.7098813,"9":-8.931531,"33":-5.9806275,"6":-6.5489874,"26":-5.892653,"34":-6.4281516,"35":-5.5369387,"38":-5.495344,"43":0.9552175,"44":-6.2549844,"45":-8.42142,"24":-7.121878,"47":-5.373896,"48":-6.445716,"39":-6.053849,"11":-5.8320975,"49":-10.014197,"46":-7.0919595,"20":-6.033137,"5":-6.3501267,"32":-4.203919,"2":-5.743471,"36":-8.493466,"41":-7.60419,"0":-7.388545},{"18":-6.048934,"39":-1.1448132,"48":-7.921489,"38":-6.0117235,"27":-6.30289,"9":-6.5567093,"29":-5.905172,"25":-4.2305975,"40":-5.1198816,"24":-7.232001,"46":-6.5581756,"20":-6.7987585,"8":-9.346154,"2":-7.6944494,"3":-6.487195,"16":-8.379641,"32":-7.292016,"33":-7.91467,"41":-7.4449363,"21":-6.0500197,"19":-5.357873,"10":-6.9984064,"7":-5.6824636,"13":-8.154273,"45":-7.8713655,"47":-5.279138,"49":-1.915852,"6":-2.682654,"30":-5.566201,"1":-1.829716,"11":-7.7527223,"12":-10.379072,"15":-4.866212,"35":-8.091223,"36":-8.137203,"42":-7.2846284,"44":-4.7636213,"28":-6.518874,"34":1.9858776,"43":-10.140268,"0":-3.5068736,"17":-2.3913155,"26":-6.1766686,"22":-9.119884,"14":-7.470778,"5":-5.925585,"23":-6.004782,"31":-2.696432,"4":-2.4887466,"37":-5.5321026},{"25":-8.760574,"0":-2.5970187,"9":-4.270929,"11":-0.27550858,"20":-6.7012835,"30":2.3309054,"4":-7.0107384,"31":-7.5239167,"41":-2.337672,"6":-3.4384027,"16":-7.9485044,"37":-7.3155503,"38":-7.4812994,"3":-3.958924,"42":-7.738173,"43":-6.500585,"22":-6.318394,"17":-5.7882595,"45":-8.782414,"49":-8.84129,"23":-10.222613,"26":-6.06804,"32":-6.4851217,"33":-7.3542376,"34":-2.8723297,"27":-7.1350646,"8":-2.7956052,"18":-5.0000043,"10":-1.5138103,"2":0.10560961,"7":-1.4954948,"35":-7.7015786,"36":-8.602789,"47":-8.117584,"28":-9.151132,"39":-8.035833,"13":-6.2601876,"15":-9.050044,"19":-5.465233,"44":-8.494604,"5":-6.9012084,"12":-9.458872,"21":-5.980685,"14":-7.7407913,"46":-0.701484,"24":-9.477325,"29":-6.6444407,"1":-3.4681067,"40":-5.4685316,"48":0.22965483},{"11":-5.7744265,"12":0.10171394,"18":-8.503949,"3":-1.9760166,"17":-7.895561,"20":-8.515409,"45":-1.9184738,"6":-5.6488137,"46":-6.1171823,"49":-7.006673,"29":-3.6479561,"37":-4.025724,"42":-4.1281996,"9":-2.7060657,"33":0.18799233,"15":-7.8216696,"23":-11.02603,"22":-10.132984,"7":-6.432255,"38":-7.2159233,"10":-2.195277,"2":-6.7676725,"27":-1.8040345,"34":-11.214028,"40":-6.1334066,"35":-9.410227,"44":-0.14929143,"47":-7.3865366,"41":-9.200221,"26":-6.1885824,"13":-5.5693216,"31":-8.184256,"39":-8.06583,"24":-11.773471,"25":-15.231514,"14":-5.4468412,"30":-5.494699,"21":-10.619481,"28":-7.322004,"16":-7.4136076,"8":-3.2260292,"32":-8.187313,"19":-5.9347467,"43":-0.112977505,"5":-1.9279568,"48":-3.8396995,"0":-9.317253,"4":-1.8099403,"1":-5.4981036,"36":-3.5487309},{"28":-6.2057357,"40":-6.9324327,"46":-0.5130272,"23":-7.9489794,"47":-7.3411865,"20":-8.930363,"26":-3.238875,"41":-7.376683,"48":-0.83026105,"27":-10.048681,"36":-5.1788163,"30":-8.002236,"9":-7.4656434,"4":-3.8850121,"16":-3.1768656,"11":1.0195583,"44":-8.7163315,"45":-6.7038856,"33":-6.974304,"22":-10.026589,"13":-4.342838,"12":-6.69588,"31":-2.2994905,"14":-7.9772606,"32":-10.55702,"38":-5.668454,"34":-10.026564,"37":-8.128912,"42":-10.7178335,"17":-5.18195,"49":-9.900299,"21":-12.4000635,"8":-1.8514707,"29":-3.365313,"39":-5.588918,"43":-8.482417,"1":-4.390686,"35":-5.604909,"24":-7.1810236,"25":-5.9158974,"19":-4.5733366,"0":-5.68081,"3":-2.8414884,"6":-1.5809858,"7":-9.295659,"5":-3.7936096,"10":-4.088697,"2":-2.3494315,"15":-7.3323736,"18":-7.7137175},{"1":-2.7719336,"37":-6.097855,"39":-4.1296787,"2":-5.4538774,"34":-11.808794,"40":-9.822159,"3":-7.884645,"42":-14.777964,"32":-2.6564443,"16":-5.2442584,"9":-6.2919874,"48":-2.4359574,"25":-11.707236,"33":-5.5483084,"35":-0.3632618,"7":-4.3673687,"27":-8.139543,"12":-9.019396,"17":-0.029791832,"24":-8.63045,"18":-11.925819,"20":-9.040375,"44":-10.296264,"47":-15.95397,"23":-12.38116,"21":0.18342426,"38":-7.695002,"6":-8.710346,"28":-2.8542902,"5":-2.077858,"10":-3.638583,"8":-7.360152,"15":-7.1610765,"29":-4.8372035,"45":-11.499393,"13":-3.8436065,"22":-5.472387,"11":-4.259357,"26":-4.847328,"4":-2.0376666,"36":-7.5392637,"41":-5.3857164,"19":-8.576212,"14":-8.267895,"30":-4.0456495,"31":-3.806975,"43":-7.9901657,"46":-7.181662,"0":-7.502816,"49":-7.3067017},{"17":-9.793276,"27":-2.8843281,"38":-8.737534,"8":-1.5083166,"16":-8.267393,"42":-8.055011,"47":-2.0843022,"14":-3.9945045,"30":-10.208374,"26":-3.2439823,"49":-2.5527742,"25":-10.359426,"9":-4.4744225,"19":-7.2775927,"3":-7.282045,"36":-8.503307,"40":-12.083569,"22":-3.7249084,"18":-7.5065627,"41":-3.3326488,"44":-2.76882,"45":-12.154654,"24":-2.8332536,"5":-5.2674284,"4":-4.105483,"10":-6.930478,"20":-3.7845988,"2":-4.4593267,"28":-0.3003047,"29":-6.5971193,"32":-5.0542274,"33":-9.068264,"43":-7.124672,"46":-8.358111,"23":-5.551978,"11":-7.7810373,"35":-7.4763336,"34":-10.868844,"39":-10.51066,"7":-4.376377,"48":-9.093265,"6":-0.20033613,"1":-6.125786,"12":-8.243349,"0":-7.1646323,"13":-3.7055316,"15":-6.295897,"21":-5.929867,"31":-7.2123885,"37":-2.482071},{"30":-12.467585,"14":-5.1706576,"40":-9.03964,"18":-5.7730474,"41":-9.061858,"20":-2.8577142,"24":-3.3558655,"42":-7.902747,"43":-6.1566644,"21":-5.4271364,"23":-7.1462164,"44":-7.9898252,"11":-2.493559,"31":-4.6718645,"48":-12.774545,"8":-7.252562,"35":-1.6866531,"49":-4.437603,"45":-7.164916,"7":-4.613396,"32":-8.156101,"39":-10.887325,"0":-0.18116185,"47":-4.998584,"10":-8.914183,"13":-0.8690014,"27":-0.3714923,"28":-12.002966,"9":-6.2789965,"26":-0.46416503,"2":-9.865377,"29":-8.443848,"46":-6.3264246,"3":-7.807205,"4":-6.8240366,"5":-6.843891,"12":-5.6381693,"15":-4.6679296,"36":-6.8010025,"16":-8.222928,"25":-10.326822,"34":-6.0182467,"37":-8.713378,"38":-7.549215,"17":-7.247555,"22":-13.296148,"33":-8.542955,"19":-7.254419,"1":-2.8472056,"6":-5.898753},{"7":-3.6624274,"4":-2.9281456,"39":-5.9176188,"13":-8.0644045,"16":-2.0319564,"49":-10.309226,"3":-0.21671781,"37":-8.295551,"44":-16.496105,"46":-6.2466326,"47":-3.5928986,"19":-9.298591,"1":-7.937351,"15":-8.218504,"6":-6.945601,"25":-8.446054,"12":-5.8477135,"14":-3.9165816,"17":-2.4864268,"20":-7.97737,"22":-5.347026,"0":-6.0739775,"32":-6.7568192,"36":-4.730008,"28":-9.923819,"38":-8.677519,"42":-4.668519,"48":0.14014988,"5":-8.3167,"8":-2.5030074,"21":-1.8195568,"27":-6.111103,"45":-12.708131,"35":-8.089076,"11":-6.0151362,"34":-13.688166,"33":-11.375975,"2":-4.1082373,"24":-4.0867376,"10":-4.2828474,"41":-9.174506,"43":-1.1505331,"29":-3.7704785,"18":-4.9493446,"30":-3.727829,"31":-6.490308,"9":-6.0947385,"40":-9.492185,"26":-13.629112,"23":-9.773454},{"12":-1.754871,"41":2.712658,"24":-4.0929146,"18":-4.9418926,"44":-9.325021,"8":-6.4423165,"1":-0.0946085,"5":-3.0156248,"14":-5.29519,"34":-10.763539,"11":-7.304751,"20":-6.8397574,"22":-5.6720686,"23":-7.829904,"7":-3.8627372,"6":-3.1108487,"16":-8.803584,"36":-13.916307,"21":-10.142917,"37":-12.171498,"45":-13.004938,"19":-3.7237267,"47":-6.0189786,"17":-4.612711,"15":-5.3010545,"30":-5.671092,"46":-13.300519,"25":-8.2948,"3":-10.556543,"42":-7.041272,"48":-9.797744,"9":-5.6163936,"26":-6.665021,"27":-7.074666,"4":-1.5992731,"2":-6.4931273,"29":-3.9785416,"31":-12.222026,"10":-2.3970482,"40":-6.204074,"49":-7.025599,"28":-8.562909,"13":-6.2592154,"32":-10.465271,"33":-7.7043953,"35":-6.4584246,"38":-2.9016697,"39":-1.5256255,"43":-10.858711,"0":-4.720929},{"2":-5.1676617,"3":-4.521774,"29":-7.3104324,"23":-6.550776,"26":-10.467587,"18":1.6576093,"33":-2.564094,"20":-3.2697926,"35":-13.577334,"37":-6.0147185,"17":-4.07909,"0":-9.630419,"38":-7.011383,"12":-10.686635,"43":-8.94728,"48":-9.350017,"30":-7.3335466,"13":-7.7690034,"4":-2.3488472,"14":-7.2594194,"21":-9.08367,"34":-7.7497597,"8":-6.2317214,"27":-8.440135,"22":-4.4437346,"32":-2.194015,"28":-6.6919556,"40":-8.840385,"42":-9.781796,"15":-7.3304253,"49":-8.720987,"19":-9.044103,"6":-5.715863,"41":-8.395639,"36":-3.995482,"25":-9.1373005,"5":-7.5690002,"1":-6.0397635,"16":-8.231512,"10":-6.5344634,"44":-7.749376,"7":-9.302668,"31":-10.868391,"39":-2.7578635,"47":-6.964238,"24":-4.033315,"11":-8.211409,"45":-10.472969,"9":-7.1529093,"46":-9.653514},{"44":-10.252684,"45":-12.034803,"28":-10.405336,"24":1.8021276,"4":-9.921194,"47":-7.9789643,"2":-5.7931147,"29":-7.596897,"25":-10.756151,"10":-8.454973,"7":-3.6788304,"18":-9.022151,"20":-8.593645,"26":-5.224861,"14":-0.5604599,"40":-6.2325897,"0":-0.44600934,"9":-8.139498,"16":-4.6218505,"46":-6.273614,"12":-3.8260474,"31":-11.0625305,"39":-12.0089445,"41":-7.3617644,"42":-4.938992,"48":-3.4777954,"30":-2.2662613,"33":-0.7261769,"35":-8.172319,"15":1.0887976,"23":-5.195476,"34":-7.1455193,"17":-12.706947,"13":-3.165891,"3":-9.327427,"21":-8.4359255,"32":-1.9019763,"37":-3.6867127,"38":-8.124417,"5":-7.17052,"49":-3.9712586,"36":-7.623468,"22":-7.0057154,"43":-6.3270884,"27":-8.314371,"6":-4.204889,"8":-8.243958,"11":-5.846903,"19":-2.6325738,"1":-6.2474594},{"39":0.43706903,"44":-6.5642495,"40":-8.924684,"46":-1.797776,"7":-5.478422,"48":-6.087083,"49":-7.956244,"47":-10.078558,"15":-1.2380409,"12":-8.280014,"20":-3.5678017,"33":-16.677906,"27":-9.337598,"26":-5.8644075,"14":-8.427099,"3":-3.3714929,"34":-11.108117,"10":-4.5418024,"21":-9.045767,"18":-8.947485,"8":-7.152398,"43":-9.5986,"11":-6.350339,"41":-1.0246458,"19":-3.1153014,"13":-5.9651165,"38":-2.3405242,"45":-5.2560587,"6":-10.963091,"0":-4.210406,"16":-2.8244996,"37":-8.667362,"25":-10.942785,"22":-4.6812115,"28":-5.555095,"5":-0.59754115,"23":-6.416314,"29":-7.2626123,"36":-12.767626,"17":-7.450956,"31":-6.298139,"1":-5.367549,"32":-4.166111,"9":-8.514038,"2":-5.0412307,"42":-12.814206,"24":-10.961937,"4":-5.677783,"30":-6.514361,"35":-6.4758873},{"3":-1.4171193,"30":-8.201865,"14":-1.0829871,"15":-8.696184,"34":-7.381269,"16":-4.2309136,"35":-7.2169228,"8":0.04883461,"23":-7.0893745,"2":-11.336302,"29":-10.249007,"41":-5.0222054,"49":-4.9262657,"10":-2.5520484,"1":-4.415363,"20":-12.114428,"28":-14.079625,"7":-6.758162,"22":-12.994962,"43":-6.761683,"36":-11.336916,"21":-3.6906323,"0":-8.01961,"17":-5.2390943,"33":-11.575128,"13":-5.7145157,"31":0.16249037,"38":-6.41915,"26":-3.9492679,"24":-5.269326,"45":-8.842736,"46":-7.1800737,"47":-14.274612,"32":-10.240828,"5":-5.5345345,"9":-2.3435318,"11":-8.388118,"25":-5.9386187,"39":-8.01918,"6":-6.938567,"12":-3.0821395,"42":-2.6356082,"44":-1.0947597,"48":-3.987667,"37":-8.242175,"4":-3.000874,"18":-4.3863974,"27":-10.993831,"19":-4.9101877,"40":-9.36493},{"1":-2.5517411,"13":-1.3773863,"48":-5.929145,"40":-6.016658,"35":-8.30968,"39":-8.173601,"43":-9.2206545,"9":-5.413448,"47":-11.219854,"36":-6.522706,"17":-1.9782183,"12":-4.4436707,"3":-5.0848694,"4":-8.382575,"26":-8.983437,"27":-7.5344496,"16":-8.203341,"25":-13.385138,"31":-2.8448677,"33":-7.537777,"8":-9.623908,"10":-5.2933273,"38":-4.203252,"32":-8.949801,"29":-7.71904,"7":-0.59097546,"11":-3.7152443,"18":-7.70849,"24":-8.392967,"30":-8.966001,"14":-3.1536689,"34":-9.573047,"2":-6.939415,"45":-6.952498,"46":-5.5352817,"0":-5.7708282,"28":-7.2594924,"20":-3.7915637,"15":-4.1908197,"49":-6.2850904,"19":-7.4985657,"22":-4.893429,"21":-3.838546,"41":-3.2204995,"37":-6.383028,"42":-7.637233,"5":-3.848416,"44":-9.47161,"6":1.3985821,"23":-6.856476},{"44":-2.861644,"5":-10.645139,"47":-0.74191725,"48":-3.7928212,"41":-3.9138176,"32":-5.723269,"49":-4.4118395,"11":-4.8106203,"45":-5.8674803,"12":-6.1513,"4":-5.2970977,"22":-5.655927,"38":-1.740617,"8":-3.2617576,"30":-9.095524,"7":-12.736498,"10":-2.8470557,"27":-4.006469,"21":-6.1753983,"13":-6.109749,"35":-8.483802,"37":-1.252423,"3":-3.4497364,"46":-9.584096,"33":-3.990739,"43":-5.901332,"20":-9.113932,"24":-8.143213,"0":-14.938147,"2":-7.107156,"9":-1.7288609,"19":-7.172612,"16":-9.64662,"6":-5.6817265,"1":-2.0544078,"28":-12.049086,"17":-4.142796,"23":-16.78327,"14":0.9610933,"29":-9.290024,"26":-9.000046,"31":-12.539607,"15":-4.6688976,"34":-5.9241714,"18":-7.4736533,"36":-9.283766,"39":-9.680832,"40":-15.069257,"42":-5.9029703,"25":-10.804184},{"42":-7.390395,"6":-5.2423105,"20":-9.492358,"30":-10.119262,"8":-8.568228,"17":-5.3969054,"35":-6.3838105,"47":-10.690717,"1":-4.7279353,"3":-6.285371,"25":-4.457618,"15":-3.7073922,"28":-5.813173,"33":-13.824557,"22":-5.7607093,"7":-3.968948,"23":-8.332884,"27":-8.011797,"34":-8.851597,"41":-7.5558634,"44":-12.499257,"45":-5.670898,"0":-7.305059,"11":-4.6200676,"12":-3.284594,"29":-6.23612,"46":-3.97527,"21":-9.3430195,"39":-7.0078306,"26":-2.9919415,"19":-1.8206236,"36":-3.0798898,"40":-9.510875,"43":-4.564447,"48":-13.20809,"10":-8.832611,"2":-3.821116,"38":-3.5049846,"16":-0.5627325,"14":-9.582281,"37":-7.3288584,"18":-2.5249457,"24":-5.016801,"13":-5.3800035,"5":-3.939186,"32":-6.202336,"49":-6.234208,"9":-3.3935227,"31":-7.889515,"4":-5.329782},{"42":-9.893343,"46":-6.007334,"8":-10.236174,"28":-8.647413,"6":-4.17853,"29":-6.938863,"37":-11.130651,"26":-11.733725,"12":-1.986285,"22":-10.029884,"35":-13.32892,"38":-2.4419475,"43":-5.606353,"14":-8.411512,"41":-7.4532537,"17":-3.4157784,"1":-8.006592,"11":-2.6495078,"33":-9.877732,"36":-8.044171,"45":-8.178667,"18":-3.0599625,"20":-3.3062778,"10":-7.8465548,"9":-4.089144,"23":-1.634593,"19":-4.513993,"32":-7.5857177,"47":-13.059774,"0":-9.734381,"24":-7.550769,"30":-0.13144064,"25":-1.6051563,"40":-5.767811,"49":-10.060788,"34":-0.9332817,"31":-10.026274,"39":-4.928876,"5":-5.804112,"44":-6.4794436,"21":0.083639145,"13":-8.63159,"2":-0.3849109,"4":-6.1427803,"48":-8.350489,"16":-4.954889,"27":-12.082092,"3":-9.490924,"15":-4.8369503,"7":-5.296665},{"14":-5.2623034,"32":-10.759203,"17":-6.541181,"0":-1.3925167,"7":-4.83245,"16":-4.1242895,"36":-9.173063,"38":-7.836097,"22":-9.022796,"40":-2.238197,"5":-7.156565,"44":-11.245649,"33":-2.9633536,"20":-7.3342886,"28":-3.333572,"19":-1.4218113,"46":-2.953141,"26":-2.6449294,"8":-3.6259913,"45":-9.9275875,"11":-5.8978066,"23":-5.9765697,"6":-4.419637,"37":-7.7864504,"39":-3.7646708,"47":-8.30409,"43":-3.0333,"49":-8.081027,"25":-8.435537,"18":-6.748767,"30":-8.089577,"15":-8.759301,"29":-9.526569,"1":-5.096554,"3":-5.9022226,"4":-8.257174,"27":-8.606438,"41":-1.497746,"42":-7.427924,"10":-3.2451184,"13":-6.449167,"21":-4.4653535,"24":-7.9258027,"35":-5.5284815,"9":-0.7580929,"34":-7.790374,"48":0.4532753,"31":-9.990816,"12":-4.272876,"2":-6.3599634},{"28":-8.533937,"42":-5.232602,"24":-6.8833175,"36":-6.4969416,"40":-5.8373866,"31":-4.522848,"14":-4.421405,"34":-5.346702,"10":-5.1903396,"20":-6.7530456,"13":-10.935861,"25":-4.5247335,"22":-2.8099911,"37":-8.892298,"46":-9.271803,"5":-5.382541,"9":-2.9776273,"43":-6.9205627,"39":-7.3040075,"44":-9.451643,"6":-2.3566048,"30":-1.4466946,"4":-5.8034754,"18":-3.2191722,"12":-8.764809,"0":-5.13083,"15":-5.9475503,"19":-4.895011,"11":-6.9602995,"16":-7.5741563,"29":-7.5532074,"48":-10.143312,"35":-9.424163,"2":-3.180596,"27":-5.8622227,"17":-1.6907284,"38":-8.533536,"26":-6.4998374,"33":-0.8160969,"7":-3.431047,"23":-5.404256,"3":-1.316479,"21":-10.0209255,"1":-4.8019085,"41":-7.7153654,"32":-5.9212785,"45":-8.015295,"8":-5.7746153,"49":-6.595235,"47":-7.30956},{"3":-5.717492,"38":-2.8490224,"23":-5.651288,"28":-11.385461,"44":-14.559361,"19":-8.516624,"5":-3.4115243,"40":-5.246222,"33":-10.784277,"46":-16.474335,"34":-9.588152,"48":-1.5694482,"7":-6.5084734,"36":-5.6967373,"9":-7.9818993,"0":-5.810563,"12":-4.71036,"18":-6.8263655,"35":-8.9194,"20":-5.9936,"29":-8.087461,"14":-4.6354985,"13":-3.1506,"32":-5.844519,"17":-1.5865021,"6":-8.940691,"39":-11.757639,"37":-1.7090847,"2":-4.0220885,"41":-9.848265,"45":-6.083981,"24":-6.7988753,"27":-4.133395,"42":-11.76692,"49":-9.335624,"1":-8.775384,"43":-6.604913,"22":-5.9234495,"16":-9.831471,"4":-4.2290497,"15":-6.385244,"25":-2.2477531,"31":-8.504316,"11":-6.764846,"30":-9.480185,"26":-1.5271374,"47":-7.4575562,"10":-8.303965,"21":-1.4529445,"8":-1.4026672},{"14":-11.7985735,"16":-7.1402345,"15":-8.83317,"12":-7.4431214,"31":-6.6755295,"34":-9.647256,"37":-8.706275,"39":-3.5989442,"40":-11.197392,"38":-2.7929463,"42":-2.1823444,"3":-11.697901,"43":-9.448908,"44":-6.625801,"46":-9.21576,"47":-7.866433,"18":-4.7899103,"33":-4.5458283,"35":0.34516492,"29":-9.577916,"25":-2.876142,"45":-14.364754,"4":-5.297591,"22":-4.4429545,"7":-1.0755956,"28":-13.072825,"23":-6.8772535,"2":-6.4374185,"10":-5.266856,"36":-7.82693,"48":-9.447512,"0":-4.3712335,"5":0.7012191,"1":-11.748584,"20":-3.3363051,"19":-8.226662,"27":-9.639775,"32":-6.9716234,"49":-9.141571,"26":-9.351931,"6":-5.2754526,"21":-8.384358,"9":-4.23956,"11":-5.3919287,"30":-4.030416,"41":-10.888003,"17":-7.899237,"24":-4.271898,"13":-4.7921405,"8":-2.5590749},{"12":-6.783235,"43":-11.795801,"18":-6.6436944,"25":-7.8932815,"41":-7.526721,"44":-9.238703,"45":-3.6455085,"47":-2.526148,"9":-3.568791,"21":-6.174505,"40":-8.068426,"49":-7.264418,"26":-10.581304,"48":-16.772251,"10":-3.141788,"29":-9.408194,"31":-9.060951,"14":-5.6716475,"11":-2.4118767,"46":-7.8699784,"27":-5.4574385,"35":-7.7864013,"6":-3.1109738,"22":-9.465287,"15":-9.399365,"3":-5.8744564,"23":-4.2338915,"13":-12.697426,"1":-5.2116737,"24":-8.732299,"36":-7.4572754,"28":-10.217757,"0":-5.539334,"33":-9.07178,"30":-2.669991,"32":-5.431058,"19":-5.187009,"2":1.7111435,"4":-8.501895,"17":-9.063622,"20":-7.6048837,"16":-4.023282,"37":-3.852279,"7":-5.007511,"5":-9.595802,"39":-12.846136,"8":-8.102661,"34":-5.6266546,"38":-9.404426,"42":-9.509596},{"6":-9.02064,"9":-7.2843223,"22":-2.310627,"44":-6.465064,"2":-7.846738,"13":-3.6992812,"25":-3.6573105,"7":-6.503886,"29":-18.287153,"39":-7.447867,"41":-9.042229,"14":-5.2810197,"31":-11.863983,"15":-3.4831448,"48":-5.682383,"5":-0.56847966,"26":-1.8970543,"8":-7.3312097,"12":-10.256776,"17":-6.9802933,"23":-2.7267532,"4":-4.750392,"47":-8.777464,"20":-8.523634,"46":-2.8897185,"10":-5.960977,"36":-6.2048035,"30":-8.522104,"1":-0.37618867,"40":-6.4929724,"43":-10.0230465,"16":-6.273286,"33":-4.688217,"3":-5.745288,"45":-2.6610754,"21":-10.674103,"28":0.108149625,"32":-12.529411,"11":-1.6168003,"35":-5.5757422,"42":-8.546778,"34":-6.505348,"18":-4.070351,"37":-3.9847722,"49":-8.392035,"27":-9.88956,"0":-7.0631843,"19":-5.3635316,"24":-11.560919,"38":-8.79977},{"3":-10.595981,"7":-8.839289,"11":-3.5387344,"13":-9.085414,"19":-2.067657,"28":-6.1983695,"12":-2.6320283,"10":-4.7341843,"26":-11.082361,"33":-8.2301235,"14":-8.244513,"9":-6.8764596,"40":-6.0242147,"15":-6.3971987,"31":-4.562212,"17":-9.659184,"36":-5.763261,"41":-4.0493035,"45":-11.246027,"46":-5.2012415,"47":-7.2177415,"2":-7.163291,"27":-11.175449,"0":-2.260675,"4":-1.9289734,"21":-10.65183,"32":-2.5994828,"43":-9.6161375,"18":-7.4306197,"22":-2.2437146,"42":-10.154111,"30":-8.624043,"49":-2.9804494,"25":-7.540709,"37":-10.69308,"24":-5.479112,"35":-12.368281,"38":-9.4130535,"39":-4.365668,"29":-5.6870394,"1":-1.5405269,"20":-6.331662,"8":-7.492573,"16":-3.6112487,"23":-5.005044,"44":-4.4755707,"34":-5.084609,"48":-5.852107,"5":-8.457749,"6":-0.2421112}],"nn_shapes":{"19":[18,22,8],"15":[18,6,31,6,23,29,23,8],"26":[18,16,18,15,10,33,14,24,19,8],"27":[18,30,8],"18":[18,27,27,27,9,12,7,22,8,29,8],"36":[18,5,21,9,11,10,13,27,17,16,8],"45":[18,34,14,28,14,10,33,33,16,8],"46":[18,29,24,16,20,32,30,23,8],"47":[18,31,26,23,6,8],"9":[18,13,19,22,6,26,34,28,21,21,8],"4":[18,23,13,23,6,22,17,12,8],"32":[18,34,21,18,23,20,29,4,8],"42":[18,9,29,31,8],"49":[18,24,12,30,17,33,8],"37":[18,5,20,26,8],"43":[18,8,8,14,34,32,9,8],"38":[18,32,9,28,31,6,8],"40":[18,31,6,20,8],"35":[18,23,18,8,26,8],"7":[18,18,8],"17":[18,22,25,27,18,29,30,27,8],"24":[18,4,19,18,8],"16":[18,17,8,8],"21":[18,20,14,6,18,7,29,6,8],"10":[18,17,18,30,31,3,6,12,25,11,8],"3":[18,20,25,15,18,8],"1":[18,10,8],"23":[18,7,6,7,8],"6":[18,33,34,13,8],"34":[18,15,4,15,25,8],"11":[18,28,11,20,6,13,29,8],"0":[18,14,27,5,6,12,16,17,19,8],"8":[18,18,8],"30":[18,30,23,28,21,27,11,33,21,13,8],"2":[18,28,24,32,16,17,21,8],"39":[18,29,17,25,6,8],"41":[18,34,22,28,13,14,8],"44":[18,18,16,18,11,33,15,15,27,8],"48":[18,19,8,19,26,6,25,18,21,23,8],"25":[18,19,13,31,20,34,30,8,4,17,8],"29":[18,27,21,25,6,8],"14":[18,7,15,31,33,18,8],"31":[18,30,29,12,8],"33":[18,13,24,23,6,8],"5":[18,9,13,26,15,8],"20":[18,7,13,3,13,17,8,8],"28":[18,7,30,30,25,19,20,5,8],"13":[18,34,22,8],"22":[18,21,13,8],"12":[18,7,23,7,5,15,7,8]},"crossbreed_segments":19,"weight_initialization_range":{"start":-0.11584926,"end":1.1683692},"minor_mutation_rate":0.5113807,"major_mutation_rate":0.6868684,"mutation_weight_range":{"start":-0.40629077,"end":0.40629077}},"state":"Finish","generation":25,"max_generations":25,"id":"55eaae5a-e128-4417-ab98-89653f77e382"},"left":null,"right":null}},"right":{"val":{"node":{"id":"8a08002a-b9a3-4eab-b61f-136c46e9b708","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_8a08002a-b9a3-4eab-b61f-136c46e9b708","population_size":50,"generation":29,"scores":[{"30":-6.8903823,"0":-6.366687,"6":-7.251893,"1":-1.5758369,"4":-7.138179,"27":-6.911625,"9":-7.059839,"19":-7.4453316,"39":-6.1214046,"40":-5.0439425,"26":-2.503099,"34":-8.334969,"38":-4.7234306,"23":-5.954438,"24":-7.5706415,"22":-9.881311,"14":-7.163348,"13":-5.449682,"7":-6.5341735,"33":-0.40490597,"47":-7.2984457,"15":-7.3366966,"25":-0.3182812,"28":-8.397097,"42":-7.024077,"36":-7.2214494,"45":-3.998279,"10":-5.410035,"41":-5.0793695,"3":-6.6308517,"20":-4.465564,"11":-2.9854922,"12":-5.696584,"44":-6.940075,"5":-7.403769,"2":-4.707096,"46":-0.1471726,"48":-4.061954,"32":-6.0609922,"31":-7.3291397,"8":-6.904563,"21":-5.6207876,"35":-9.010704,"37":-1.1192181,"17":-6.9232087,"18":-4.5199404,"43":-5.4984875,"16":-7.1221786,"49":-4.7234197,"29":-7.260703},{"4":-2.1173775,"16":-4.6180205,"14":-5.2751327,"8":-5.4661613,"29":-6.643363,"12":-6.6131225,"18":-9.504697,"20":-5.771767,"17":-6.53591,"10":-4.70592,"5":-2.499683,"21":-7.500197,"19":-6.223144,"23":-5.9123497,"27":-4.7125654,"1":0.35022563,"30":-1.443285,"0":-1.3940161,"31":-6.8763785,"36":-5.761595,"33":-5.7728796,"7":-3.611281,"11":-4.433655,"2":-2.2864666,"38":-3.9540055,"40":0.637162,"44":-6.389148,"48":-5.2862444,"49":-7.6244535,"9":-3.7235718,"25":-9.452706,"37":-2.7058406,"3":-2.1251457,"42":-4.557567,"46":-5.72488,"13":-5.7067833,"39":-8.280045,"47":-6.0286193,"22":-9.067814,"35":-5.6872864,"43":-6.526985,"45":-4.815018,"6":-6.872731,"15":-5.8770156,"28":-2.5441356,"26":-2.6909657,"24":-5.8113694,"34":-5.66294,"32":-8.659533,"41":-4.2655635},{"3":-4.707946,"22":-0.9930298,"40":-4.1636987,"44":-6.693418,"46":-7.6929827,"1":0.21600547,"30":-2.6062977,"20":-5.0218782,"39":-7.4659715,"49":-11.782131,"8":-4.8311667,"10":-0.56734496,"29":-0.5500806,"5":-1.3998518,"15":-6.023353,"27":-1.2152869,"7":-5.79787,"23":-4.9278426,"28":-10.597835,"38":1.9843066,"18":-5.2778816,"35":-3.69142,"42":-6.2820597,"26":-4.7657995,"31":-4.387805,"19":-3.300389,"45":-9.194662,"36":-5.7017508,"41":-2.114288,"14":-4.193127,"9":-2.077168,"21":-6.2328887,"47":-5.5189214,"48":-6.670903,"32":-11.53697,"25":-10.070675,"43":-4.0887823,"37":-2.3846292,"2":-2.9606698,"17":-13.399692,"6":-6.354215,"12":-6.405332,"4":-2.3720484,"33":-9.485014,"13":-5.351822,"34":-5.80317,"24":-7.979808,"16":-4.7959824,"0":-1.6341827,"11":-7.36057},{"1":0.37562218,"17":-5.6403093,"40":-7.6453385,"37":-4.011546,"38":1.228275,"41":-0.99011976,"6":-4.998586,"10":-5.460025,"15":-5.44879,"2":1.108212,"24":-10.60242,"43":-5.3095045,"45":-2.5586019,"49":-6.7323723,"3":-8.880538,"33":-6.1697397,"36":-7.454741,"25":-0.17626444,"9":-0.37813005,"20":-4.9976964,"22":-5.93849,"19":-4.7558117,"13":0.14415504,"14":-2.9557383,"5":-1.6969656,"18":-7.075305,"21":-0.7996491,"26":-4.647479,"11":-4.081734,"8":-3.465926,"12":-4.47592,"29":-8.81566,"35":-4.5576224,"42":-5.040536,"46":-5.880772,"16":-1.8863583,"28":-4.099984,"23":-1.858031,"32":-4.4287996,"39":-4.690382,"31":-4.728865,"34":-5.690257,"27":-3.0799367,"48":-2.301661,"47":-2.4755936,"7":-3.3069987,"4":-2.2082336,"44":0.1436568,"0":-2.556739,"30":-4.2639523},{"22":-3.8915043,"7":-1.8629459,"33":-5.9973283,"36":-8.524627,"1":-4.7794275,"17":-6.1454573,"44":-4.6920633,"23":-4.6040993,"9":-2.9316204,"28":-2.2134347,"14":-5.544248,"15":-2.2594807,"12":1.1874464,"29":-4.7877784,"34":-5.1445036,"35":-4.3728113,"42":-9.001171,"45":-6.1106486,"18":-5.804879,"47":-3.8351161,"2":-1.4719741,"48":-2.4615238,"8":-2.775226,"16":-7.6434493,"5":-6.374134,"43":-1.5028708,"25":-0.29187956,"21":-2.7835448,"19":-3.872364,"10":-0.6055382,"32":-9.729819,"37":0.93474656,"41":-1.4799311,"30":-6.5674295,"49":-6.774779,"38":-6.886139,"13":-5.7146444,"31":-6.334458,"27":-8.395178,"39":-1.480377,"0":-0.28676185,"26":-0.67841685,"24":-5.6703634,"40":-8.138655,"46":-5.3050203,"3":-0.9592926,"6":-2.2594695,"20":-4.2805586,"11":-3.8235536,"4":-1.2074919},{"40":-3.0010896,"12":-0.47287354,"6":-1.5899264,"4":-2.4118469,"10":-2.8513339,"32":-2.0976238,"35":-4.667093,"38":1.938852,"9":-0.3600924,"42":-5.872838,"15":-2.1242394,"17":-2.4127755,"23":-0.020880412,"28":-4.6003866,"31":-5.2546453,"22":-1.2632474,"3":-1.201564,"14":-1.238025,"43":-5.890736,"49":-10.754703,"7":0.97774017,"18":-2.219182,"45":-3.3392487,"11":-1.1516854,"36":-3.3555954,"21":-6.0332994,"44":-3.1431673,"20":-2.9144974,"46":-5.557367,"26":-0.092525385,"2":-0.10287504,"24":-1.9347687,"34":3.4260392,"5":-1.8563219,"27":0.6622796,"30":-2.6912205,"8":-1.5823574,"25":-10.679262,"33":-3.7756774,"16":-1.4420292,"47":-2.6836462,"1":0.7574368,"13":-4.5513635,"39":-2.1418312,"29":-3.3399491,"19":-2.334942,"37":2.0126982,"0":-4.288588,"41":-7.9099708,"48":-2.4494293},{"7":-4.9436007,"20":-3.8808124,"25":0.6405264,"37":-2.0668845,"35":-0.1875052,"41":-1.6857042,"1":-4.9752975,"34":-3.8983452,"49":-7.2735953,"14":-5.069049,"47":-2.6037214,"31":-4.398569,"46":-6.8468194,"4":1.1550481,"12":1.928962,"13":-1.924304,"16":-0.15191099,"19":-2.8891091,"24":-5.286527,"32":-5.935781,"33":-0.9248799,"22":-3.785513,"23":-5.1809096,"10":-1.5204986,"27":-4.55277,"39":-7.4145584,"43":-14.647901,"48":-4.265855,"29":-1.3842177,"3":0.07098539,"5":0.82853717,"45":-10.730497,"9":-0.6733018,"21":-2.5182014,"2":-0.83385086,"8":-1.5495563,"36":-0.03221141,"15":-2.072137,"44":-6.384336,"0":-3.0473003,"30":0.7601515,"17":-1.5781072,"18":-0.8381344,"42":-2.582169,"40":-4.614395,"11":-3.8800156,"26":-2.0635307,"6":-4.68423,"28":1.5506709,"38":-2.2673924},{"7":-1.6505486,"2":0.7774186,"47":-6.573714,"37":-4.539879,"13":0.03377179,"38":-7.26351,"16":-1.2384418,"48":-0.21510899,"36":0.1417698,"4":-1.5233848,"28":-5.8403006,"33":-1.5584481,"39":-3.818011,"46":0.11975696,"31":-0.26565015,"11":-1.0494064,"29":-3.681448,"34":-2.017143,"1":-2.9740043,"23":-1.4810946,"12":-0.1327244,"18":-1.9773611,"20":-0.7924546,"14":-3.3341725,"17":-1.4658457,"24":1.7954773,"10":-0.3364888,"15":-3.3656037,"8":-0.6107298,"43":0.7149738,"44":-1.3457949,"3":0.97620183,"45":-1.7013899,"26":-1.5102454,"40":-0.339752,"30":-9.527001,"21":-1.7250589,"41":-0.86631376,"25":-4.9848146,"27":0.7658924,"6":-0.23256321,"5":-4.1068673,"0":3.2152145,"22":-2.8026853,"35":-5.1000853,"19":-2.2166228,"32":-4.892883,"9":-0.43250617,"42":-0.26101598,"49":-5.497836},{"5":0.64945936,"39":-0.05907058,"29":-5.1650662,"44":1.86428,"23":-0.45214185,"24":-1.1747376,"18":3.450955,"46":-3.927073,"16":-2.1201155,"9":-1.0618576,"49":0.9801561,"33":-3.4150214,"38":0.011873191,"19":-1.7743086,"36":-10.073343,"43":-7.3709884,"48":-7.7935066,"15":-0.871607,"1":1.8400133,"7":4.6085854,"37":-1.3199402,"6":-0.5254752,"41":-5.488297,"22":-0.15798141,"8":-0.2689926,"4":0.0070250034,"30":-0.2566538,"32":-5.1819634,"34":0.64409745,"35":0.85777014,"0":2.143561,"21":-0.073742434,"11":-0.43844336,"26":-3.7777207,"28":-2.7066035,"3":0.8786748,"12":-1.3015422,"42":-7.2415023,"47":-0.956086,"25":0.110974,"17":-0.81192,"20":-0.13011317,"13":-0.8048275,"14":-0.2854454,"31":-4.000916,"27":-5.6587157,"10":-0.2606472,"2":1.0424337,"40":-7.7523055,"45":-2.582872},{"36":0.27180222,"23":2.7978485,"20":-1.3364205,"33":-8.464629,"10":0.4004666,"21":-2.7492003,"27":-4.5050507,"44":-4.31029,"37":-1.6940572,"32":-4.7660036,"28":0.3334044,"30":-0.45442843,"19":-0.3445322,"40":-8.239295,"35":0.47767296,"31":-5.8663955,"12":-0.4103446,"15":-1.2449474,"17":0.0257856,"38":-3.4848754,"18":-0.6052362,"1":-0.0063587963,"6":-4.681793,"29":-4.866314,"39":-3.9143035,"9":0.2902396,"48":-0.26042423,"0":2.9007907,"5":1.0276722,"42":-0.7925848,"26":-5.118394,"11":-0.002367808,"25":-3.841985,"45":-0.537751,"34":-7.495551,"47":-1.6614449,"22":-0.55838305,"2":2.2291977,"14":-2.826922,"16":-0.3536974,"24":-1.096124,"46":-2.3482919,"4":-4.404132,"7":0.59371424,"49":-5.7783227,"8":-0.9134123,"13":-0.8282794,"41":-2.8126519,"43":-3.7567513,"3":0.6197542},{"47":-3.466451,"12":0.4526772,"8":-0.887543,"0":0.40766907,"7":0.340661,"14":-1.2155756,"21":-0.52555835,"18":0.16660759,"31":-0.4225104,"1":-2.4355006,"23":0.11080019,"32":0.77408504,"28":-4.6681557,"33":-2.6104722,"37":-6.771203,"46":-4.158824,"3":0.5076564,"6":1.3568645,"13":-2.2395298,"17":-0.00092999934,"30":-2.2732644,"35":-0.8506044,"38":-2.9995084,"40":-4.005641,"10":0.75578177,"39":-7.017123,"2":-3.732964,"41":-3.9281597,"24":0.0051654116,"16":0.7956792,"27":-1.3640368,"42":-0.44231778,"43":-0.27105874,"44":-0.37946758,"11":-0.18678841,"22":-1.4216859,"9":-0.54350585,"49":0.05486574,"15":-0.097495005,"34":-1.4785053,"25":-7.7886686,"4":1.0454166,"26":-1.1955853,"45":-7.542697,"36":-1.206464,"48":-0.4479108,"29":-1.9547632,"20":0.199999,"19":-1.3918304,"5":1.187502},{"7":-0.055990804,"32":0.15957642,"5":0.931877,"37":0.5894412,"44":-5.4633756,"14":0.2276588,"47":-0.3331896,"18":-3.2150345,"40":-0.370715,"43":0.6012882,"28":-6.1086364,"8":-0.52198184,"21":-1.0775121,"49":-1.9851396,"2":1.0139883,"26":-3.5421982,"3":0.38368022,"48":-3.9251645,"6":0.95860064,"24":0.77015275,"31":-4.462737,"1":1.1644658,"20":-0.89435995,"30":1.7337078,"39":-3.0713792,"42":-0.457604,"0":1.0809774,"9":-0.5640028,"25":-1.3178831,"13":0.4953414,"4":1.2208271,"29":-5.3828278,"11":0.6927994,"16":0.6993264,"10":1.0544658,"27":-6.1370287,"34":-0.4300012,"22":-0.36046538,"17":-1.903821,"46":-5.143382,"23":-0.5656086,"35":0.7297146,"38":0.61732376,"45":-3.5132241,"12":-0.39357737,"36":1.4724967,"41":-3.488839,"33":-3.3702226,"19":-0.3116702,"15":0.43473634},{"27":1.181613,"22":0.055693995,"29":-0.3129018,"33":-7.299405,"1":1.5262117,"19":0.5360656,"32":-1.6477096,"13":0.64982116,"40":0.91174144,"41":-2.7509122,"14":-0.167828,"44":-2.822415,"45":1.2327583,"11":-0.011979785,"38":-6.813726,"43":0.11981901,"46":-3.4579914,"15":0.7411006,"16":0.8099394,"7":1.1826488,"35":0.95745754,"37":-3.0358167,"8":1.1782231,"10":1.0557808,"48":-3.045028,"49":-3.8261337,"5":0.7451374,"20":-0.1527806,"39":-6.4743195,"28":-6.5273843,"4":1.220751,"42":-4.5711756,"24":-0.041719608,"31":0.34226742,"18":-0.91081256,"0":1.0743566,"23":-0.17759499,"30":1.4853861,"17":0.46872276,"34":1.2847208,"25":-6.5099783,"3":0.87976015,"47":-0.5755362,"26":1.3066028,"2":1.4612877,"6":1.1884784,"9":-0.8657932,"36":0.09794381,"12":0.3919762,"21":0.29546002},{"30":0.543356,"29":1.4511869,"6":1.5986239,"1":1.7871357,"24":0.44812518,"38":1.2497461,"4":1.3088481,"41":-3.509452,"48":1.1025379,"15":1.1625886,"28":1.0937965,"3":1.3820547,"14":1.3868314,"17":1.2814915,"21":0.2979782,"13":0.8331116,"11":1.481771,"42":-5.8899517,"37":0.9281726,"18":0.466715,"22":0.39471,"23":0.83010465,"12":0.57770675,"33":-5.1343074,"31":-0.051722206,"7":1.1165328,"32":2.0868556,"5":0.8164091,"20":-0.8658458,"35":-1.6920564,"43":1.5389199,"44":-0.69294435,"26":-0.054529577,"0":0.8418752,"16":0.68473107,"34":1.4229796,"36":-5.3850393,"47":-1.431451,"8":1.0309417,"49":1.295428,"19":0.91012347,"40":-5.1053925,"10":1.2443664,"2":0.70411724,"39":1.0932726,"25":-5.872225,"27":-1.4230958,"46":0.6440994,"9":1.1426489,"45":1.0967102},{"41":-0.8937888,"38":-3.716568,"43":0.83768904,"25":-1.06985,"24":1.5895697,"12":1.3031318,"31":-6.981823,"35":-0.617902,"28":1.2498503,"0":1.3810146,"7":1.3701488,"22":0.5957642,"18":0.89235955,"39":0.3732926,"42":0.412604,"15":1.2952098,"6":0.60526836,"29":0.3031598,"32":1.5218765,"45":-6.5065665,"20":1.3963188,"27":1.2640693,"5":0.8349102,"19":0.95996416,"10":1.4788206,"46":1.6185303,"16":0.46717244,"37":-5.133558,"17":1.4022924,"1":1.5865852,"47":-3.2271333,"11":1.3340809,"48":1.1868662,"4":1.5121167,"23":0.60344905,"13":0.84270716,"33":-5.3183193,"26":-0.2449282,"30":1.6124281,"8":1.4946703,"40":1.303579,"3":1.7255867,"21":1.1728556,"36":0.2820046,"2":1.4260871,"9":0.605192,"34":0.041747987,"49":1.4496037,"14":1.1405728,"44":-7.878412},{"3":1.06853,"22":1.5044442,"4":1.5756843,"32":1.5952417,"9":1.586146,"41":0.1423614,"46":-1.7510738,"30":-2.900235,"47":3.6666653,"15":1.203729,"48":-1.3951555,"49":-6.9787354,"43":1.896711,"27":-5.219539,"35":1.390768,"19":1.6316789,"5":1.5311888,"0":1.5764972,"36":0.82220095,"33":-6.8519506,"20":0.8197409,"16":0.7336922,"25":-4.6792617,"12":0.95957357,"44":1.3089871,"34":0.83662015,"2":1.1631157,"38":-0.5674101,"11":0.8625002,"7":1.4115431,"13":1.2713734,"23":1.2255038,"21":1.2702303,"17":1.2648827,"29":-4.59699,"10":1.6951157,"18":0.95991695,"31":0.94614536,"1":1.91795,"42":1.4615324,"39":-0.0689796,"8":1.2110746,"14":0.82729214,"37":-2.262866,"40":-0.321691,"45":-2.861486,"24":0.553185,"28":1.7802076,"6":1.2392688,"26":0.559428},{"4":1.5179548,"27":-6.264064,"49":0.47696343,"0":0.066469386,"47":-5.7816567,"2":1.8454969,"46":0.1787258,"26":-3.4672687,"13":1.5142362,"1":1.7937119,"15":1.4136237,"22":0.98619956,"16":1.4075732,"18":1.2502508,"45":-2.5159807,"14":-0.887997,"9":1.5993698,"5":0.3839901,"6":1.6086,"3":1.457516,"28":-7.4280267,"12":-1.0802304,"48":0.82555276,"32":-0.7044152,"35":1.5340879,"42":0.77651846,"30":0.4361906,"24":1.6176443,"37":-7.6016197,"39":1.6315203,"17":1.2428942,"29":1.209003,"7":1.6030687,"23":1.3045851,"33":-2.9556956,"40":-6.5972023,"20":0.30333602,"25":0.9161374,"36":-6.604985,"41":-0.65913355,"43":1.743408,"31":-7.1618032,"44":-2.6561675,"8":1.6508014,"11":1.1103718,"21":1.2077398,"38":-6.124724,"10":1.5096354,"19":1.9523376,"34":2.6506305},{"2":1.5297358,"17":1.1704702,"26":-6.16945,"39":-2.9829984,"9":1.2676624,"10":0.5051805,"13":1.3597181,"6":1.1794776,"22":1.4321846,"3":1.08179,"5":1.6785371,"25":-1.1475462,"0":-0.34125432,"16":1.2396181,"23":1.1752719,"27":-1.9520136,"28":-1.3069769,"37":-3.381085,"19":1.372544,"38":0.007790795,"12":1.6322134,"40":3.4216495,"35":-8.126452,"42":-2.0214622,"43":0.23487015,"8":-0.01564021,"44":-7.6624045,"29":1.5084686,"11":0.6349971,"47":-2.8156064,"34":-4.1204715,"31":-8.108996,"21":1.2164943,"48":-2.8430297,"49":-2.9457614,"18":0.62638175,"45":1.2671716,"41":-6.0569415,"33":0.39131317,"36":-2.3818052,"4":2.009478,"15":1.6500012,"46":-3.5459144,"32":-7.3374343,"20":1.2811453,"30":-1.09656,"1":1.1212342,"7":-1.7780695,"24":1.0333579,"14":1.61798},{"39":1.1167322,"30":1.4514105,"47":-6.5923767,"48":0.9891836,"43":-4.254728,"5":1.3477652,"10":0.50612104,"22":0.6411706,"32":0.3954892,"16":1.3328848,"27":1.2563778,"7":1.3520648,"45":-0.99833333,"21":0.6445106,"36":1.5755234,"42":-1.4367013,"1":1.5964768,"14":1.126214,"49":-5.693822,"2":1.5589868,"9":1.7812173,"38":-2.8544154,"25":-6.4703674,"18":0.1501132,"34":0.31430298,"4":1.4938426,"8":1.5314653,"28":1.656511,"13":1.3649709,"40":-6.2768335,"26":1.101163,"31":-0.7388144,"41":-2.1115413,"17":0.9507782,"11":0.97697604,"29":0.55346644,"3":1.6671025,"6":1.4095662,"12":-0.11343231,"15":1.7699897,"20":1.5169379,"0":2.771112,"19":1.7405956,"33":-1.298229,"37":-6.6350493,"23":1.7395853,"44":-6.3225856,"24":1.58325,"35":-0.053157162,"46":-9.4540615},{"6":1.2187669,"7":0.8917198,"47":1.8631401,"2":1.1745762,"35":-1.6448038,"49":-2.95982,"32":-2.0818577,"34":-6.179839,"44":-3.6606793,"41":0.5892452,"18":1.7373435,"8":1.6228693,"29":-0.17469399,"26":1.6262884,"27":0.0862808,"11":1.189906,"25":-2.8834498,"10":1.1779134,"45":-2.0754929,"20":1.558006,"23":1.5464153,"16":1.1434767,"3":1.1076599,"4":1.1778538,"5":-0.42678183,"17":1.7063715,"12":1.8446579,"14":-0.3774435,"1":1.5259365,"40":-4.735013,"33":0.99616224,"0":3.964498,"13":1.5515668,"43":-6.9779253,"46":0.3668552,"48":-2.6729264,"22":1.1326334,"31":1.4176149,"15":1.8948257,"24":-1.1004847,"30":1.4272197,"9":1.2269651,"21":1.3684937,"37":-5.4659343,"39":-2.9745486,"42":-1.7469413,"36":1.8223385,"38":0.61339784,"28":-6.5556955,"19":0.92647237},{"20":1.4957073,"47":-7.6401763,"37":1.5045424,"32":-3.0742247,"2":1.3811878,"19":1.6339369,"46":3.273948,"33":0.7683608,"17":1.074304,"14":0.8990938,"26":-1.1447313,"29":1.0419735,"9":1.133498,"13":1.8232332,"22":1.3013092,"48":2.2217846,"49":0.046359204,"3":1.8796438,"18":0.99727887,"16":0.40142736,"0":1.6930526,"15":0.13463955,"21":0.794613,"31":1.9557081,"39":-0.54255676,"1":0.23620617,"23":0.8635624,"5":1.7801396,"27":0.58400375,"44":-5.3096094,"4":1.7721612,"11":1.2898297,"30":3.9104648,"38":-1.2507364,"24":-0.55052215,"34":-2.7482874,"40":0.93363285,"10":1.5955795,"42":-5.272129,"45":-3.926464,"6":0.87878215,"28":-0.13962458,"43":-2.9131722,"8":1.7215147,"25":-4.6805735,"35":-0.4225172,"7":1.8165493,"36":-5.8606043,"41":-1.4097888,"12":1.5908456},{"46":0.74108964,"49":-4.1909633,"20":0.30749458,"19":1.9475998,"14":0.31330737,"27":-4.3371725,"29":2.7149265,"15":1.1186619,"32":-3.4302926,"37":-6.3331,"6":1.7349503,"11":1.7137706,"24":0.1975456,"0":-0.7540966,"16":-1.1393349,"23":1.8451722,"28":1.7192996,"31":-0.44986576,"2":1.2848672,"35":-0.825477,"12":1.4231809,"48":-7.606537,"3":0.501546,"13":1.5034555,"17":1.2041571,"21":1.126523,"33":1.0317538,"39":0.3762208,"43":0.11361933,"5":1.5044609,"18":1.2175517,"25":0.95986015,"26":1.1233764,"40":3.6794903,"8":1.5731483,"7":2.0125415,"1":2.3247137,"41":-9.749472,"42":1.6741203,"44":-1.0102679,"9":1.4840626,"45":-2.4632657,"10":1.6355549,"47":1.5323204,"22":0.21311255,"36":1.5120761,"30":0.2143166,"38":-3.5642304,"4":1.7638651,"34":0.39690977},{"42":-1.2920502,"5":1.3969676,"9":1.554745,"30":-0.10908177,"3":1.2139347,"39":1.0080363,"47":1.200161,"2":2.3049264,"12":2.0657258,"13":1.7098567,"43":-2.527463,"27":-3.8782554,"17":1.2076086,"28":-3.7903857,"29":-9.85261,"46":-4.4447265,"48":1.1618752,"49":0.19929662,"41":2.4686918,"44":-4.0732775,"20":1.5870037,"4":1.8357404,"0":-2.0527384,"8":1.5574868,"10":1.22925,"24":0.45239258,"35":-5.810202,"22":2.246426,"36":0.71208864,"38":-5.465337,"6":1.6349227,"33":-1.3913972,"26":-0.060923196,"31":1.3563511,"1":1.7311414,"25":-7.8462257,"37":1.4671192,"32":1.7025058,"18":1.6255014,"40":0.76987636,"45":-7.1601205,"16":1.321495,"7":2.0029209,"15":1.3958324,"11":1.4291557,"23":1.020146,"14":1.3514392,"34":-3.9700756,"19":0.87691844,"21":1.457301},{"10":1.3900664,"29":1.5545199,"23":0.7173734,"38":-0.035635423,"49":-5.677472,"25":-2.8942063,"5":1.5183672,"43":-9.479878,"47":-2.3738418,"34":-7.6670456,"14":1.1688192,"22":1.448393,"36":2.3566444,"37":-1.7029192,"45":-1.9353702,"21":1.181576,"8":-0.21047358,"39":3.2392578,"7":0.90635955,"9":1.3024013,"26":1.8222473,"24":1.337246,"3":1.5609477,"12":1.8292301,"0":0.62835604,"17":0.8763803,"48":0.7421207,"19":1.4354224,"1":3.1899996,"20":0.5356204,"11":0.32281262,"28":-0.057988644,"31":-2.2201662,"13":1.4153163,"2":2.538074,"32":2.533946,"16":1.75793,"42":0.36935982,"6":3.4213595,"33":-1.1408689,"4":1.6013329,"27":-1.2621657,"30":-2.4088655,"35":-9.271956,"40":-0.23365942,"44":-4.820443,"15":0.98999214,"41":0.779949,"46":-1.000054,"18":0.7356068},{"3":-0.83181953,"28":-2.9239163,"18":1.0110298,"20":1.4475853,"1":-0.087203085,"27":-8.34004,"37":-2.9980285,"35":1.1809899,"11":1.4232423,"36":2.7912552,"38":1.6999662,"16":1.2309988,"32":-1.3243482,"44":-1.1714809,"49":-10.058592,"26":-3.105577,"25":1.4807254,"42":1.2478247,"19":1.3075396,"47":0.63402104,"15":1.838217,"2":1.4958794,"39":-4.058095,"10":1.3976815,"9":0.75132984,"31":-5.109008,"43":1.2327099,"40":0.35012078,"4":2.4473233,"21":0.99680173,"34":-4.42363,"0":0.8248364,"23":0.43658724,"48":-0.48618975,"6":0.71962893,"7":0.613024,"5":-1.0769475,"22":-0.39623517,"30":-4.3155847,"12":1.4463699,"45":0.65694743,"29":-0.9168655,"17":1.0417278,"8":0.9492218,"14":2.2935612,"46":-4.9462934,"24":0.260953,"33":-5.8987875,"13":0.033944797,"41":3.7868094},{"45":0.0684842,"15":0.5276226,"43":3.6445403,"34":-0.51650757,"0":-1.9127007,"1":3.9725666,"27":-0.23630016,"7":1.5093597,"9":1.4424554,"22":-0.2881744,"33":1.7457469,"44":-0.92784184,"28":-7.526722,"41":-3.4292526,"47":-0.8512896,"48":-3.3854833,"30":1.551065,"10":-3.0717058,"5":1.0323613,"2":1.4360434,"21":0.7947602,"11":0.70066816,"31":-2.9565368,"32":1.6050752,"49":-1.2321005,"6":0.259293,"13":2.6843486,"17":1.2362102,"29":0.09348246,"36":-9.459752,"14":0.6812374,"37":-2.0481486,"3":-1.2719615,"24":-0.30212656,"8":-0.09922062,"4":1.6250948,"19":0.31614476,"12":1.5376502,"25":-0.9299536,"38":0.38055125,"39":-7.025481,"42":-3.050082,"26":2.6087768,"35":-0.4830926,"20":1.6740729,"23":2.0530748,"46":-5.4383454,"18":1.8147866,"40":-5.0388107,"16":-3.0236375},{"45":-8.327242,"30":-11.326494,"0":1.050215,"48":2.7887874,"1":2.1725523,"34":1.5555595,"38":-0.9663353,"40":-11.917261,"33":-5.550151,"49":-8.402519,"5":0.22542039,"2":-1.1448904,"23":3.235646,"10":0.19637775,"15":1.4809368,"31":-0.587714,"20":1.6347845,"9":-1.203298,"19":1.5319674,"27":-11.047888,"16":1.8472086,"13":1.4091709,"29":-2.4094012,"35":-1.1548483,"39":-3.7547355,"41":0.86946535,"43":0.72847444,"44":0.5401133,"12":0.84144497,"7":2.6294243,"18":1.5767672,"24":-0.26575905,"21":2.2624278,"26":-0.6386402,"3":5.237002,"28":-7.0032654,"37":-2.321322,"46":-3.3797333,"47":-0.16604157,"14":1.4270293,"6":-2.0011048,"25":1.3073906,"11":1.4543204,"8":0.5777082,"17":3.604412,"32":1.7178024,"36":1.2897886,"4":1.6126816,"42":3.1484401,"22":0.400719},{"3":2.51722,"5":1.6788085,"12":1.6577823,"17":0.73920006,"6":-0.9401892,"0":3.6358628,"20":1.3569462,"11":1.2359018,"28":3.8309493,"36":-7.88842,"14":0.390558,"1":-2.4561696,"7":-3.0893154,"19":-2.8343208,"21":1.230938,"30":1.519681,"18":-0.25336796,"35":-8.591397,"38":4.0539126,"2":1.3142867,"39":-0.41035575,"44":-0.8191618,"23":1.9358387,"46":-0.98267543,"47":1.9498055,"8":1.184375,"31":-3.613992,"24":0.51519924,"29":-3.3397663,"42":-12.80098,"9":0.92517126,"26":-1.6531479,"41":-11.539165,"13":0.555514,"45":0.4131689,"49":-3.2789326,"15":-1.1341568,"27":-6.675209,"32":-0.8474833,"48":-0.6487338,"16":0.6263184,"10":1.2492015,"4":0.38530108,"43":-1.2618519,"34":-1.205608,"25":-1.1481497,"33":-0.5578264,"37":0.107068256,"40":-6.672026,"22":-1.9860134},{"3":1.4870069,"24":0.5922798,"42":-0.90082943,"46":1.1263611,"28":4.0212173,"6":0.847098,"39":1.5401908,"2":3.6113706,"22":-2.1477005,"32":-1.143802,"34":1.7614723,"0":0.2418124,"45":-4.5013013,"40":0.5079947,"23":-2.6009984,"15":4.4212484,"9":1.5904756,"13":3.1554604,"38":-9.773752,"36":-8.051781,"11":0.7847768,"12":-0.500327,"18":2.6738858,"43":0.8890418,"8":-0.11626544,"10":-0.6089754,"30":-6.938854,"33":-0.13215446,"44":-7.1068983,"1":-1.205419,"19":2.5999706,"29":2.7931895,"41":-2.8217766,"25":4.527017,"48":0.22729865,"26":-8.110232,"47":-1.2862024,"49":2.4557524,"17":0.640802,"31":-8.518101,"35":-3.8277054,"20":3.3180757,"21":0.66522586,"7":2.2479103,"37":1.7639908,"4":1.7260542,"14":-0.16935782,"16":1.3788013,"27":1.2699888,"5":-1.1353257},{"16":-0.5070044,"22":0.5732678,"9":-3.311842,"14":1.3319747,"35":-5.4703245,"36":-8.155589,"49":0.37697345,"31":-2.2505832,"6":1.6764879,"24":2.1798148,"40":0.057171058,"5":2.2697942,"48":-10.495432,"2":-0.20947047,"29":-0.546161,"32":-6.696771,"4":2.6180937,"19":0.19290301,"17":1.5652777,"7":-0.49070844,"30":0.2449191,"37":-2.5612454,"38":-1.0849822,"1":0.79552424,"41":1.7466857,"11":2.5568452,"21":-2.0221412,"0":-0.51793313,"8":0.5822776,"25":-7.0668707,"46":-1.6242403,"15":-0.90794677,"23":1.4886081,"28":-5.8837767,"20":1.7212784,"18":-0.6829664,"33":-6.7982726,"12":0.93712425,"10":1.5152016,"34":-2.6526597,"39":0.06669118,"47":-0.4047792,"44":-2.9025836,"13":-0.2605246,"27":-2.9493988,"43":0.9045593,"3":0.60476106,"26":-0.7757357,"42":-0.3835604,"45":-8.929934}],"nn_shapes":{"40":[18,12,12,10,27,23,30,7,8],"13":[18,23,4,8],"41":[18,20,31,28,33,23,8],"36":[18,8,32,27,8,8],"14":[18,5,11,33,33,22,19,8],"16":[18,27,22,29,14,8],"30":[18,10,33,8],"25":[18,11,8],"18":[18,13,8],"42":[18,29,23,7,5,29,9,8],"43":[18,25,21,17,12,8],"28":[18,30,34,32,26,10,15,8],"31":[18,34,26,30,8],"38":[18,33,26,10,26,31,4,8],"7":[18,10,26,28,23,13,8,4,20,14,8],"24":[18,18,31,14,34,23,30,8],"22":[18,8,6,5,21,34,11,25,10,8],"9":[18,27,9,8],"47":[18,8,15,8],"45":[18,17,8],"23":[18,28,30,21,8],"11":[18,23,31,19,31,17,10,3,8],"32":[18,25,17,27,3,18,29,19,4,23,8],"48":[18,31,22,17,12,16,3,33,3,8],"19":[18,11,30,9,13,27,32,8],"3":[18,4,5,20,15,8],"0":[18,30,26,3,13,16,8],"2":[18,19,29,14,29,3,8,8],"8":[18,26,8,9,29,8],"21":[18,5,17,16,12,13,8,14,8],"33":[18,3,23,21,20,21,15,11,8],"12":[18,25,24,23,22,19,8],"37":[18,23,8],"10":[18,34,8],"44":[18,10,11,9,29,14,27,33,6,8],"1":[18,22,24,26,20,26,7,23,4,4,8],"29":[18,9,13,8],"6":[18,10,6,8],"35":[18,9,18,7,14,8],"4":[18,4,22,8],"5":[18,33,27,24,13,7,30,21,19,8],"15":[18,9,29,21,18,30,14,33,14,8],"39":[18,28,5,6,18,31,4,6,13,8],"46":[18,8,23,3,8],"49":[18,15,28,16,12,9,17,13,34,20,8],"34":[18,17,22,11,16,8,15,8],"17":[18,21,9,17,4,23,4,8],"20":[18,4,27,26,8],"26":[18,15,34,22,6,26,8],"27":[18,25,17,22,28,5,16,8]},"crossbreed_segments":19,"weight_initialization_range":{"start":-1.6164029,"end":0.42884523},"minor_mutation_rate":0.1538608,"major_mutation_rate":0.11361027,"mutation_weight_range":{"start":-0.8341105,"end":0.8341105}},"state":"Finish","generation":30,"max_generations":30,"id":"8a08002a-b9a3-4eab-b61f-136c46e9b708"},"left":null,"right":null}},"right":{"val":{"node":{"id":"7ce5d55b-767e-4c6a-9fdc-4c2300c7f0b4","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_7ce5d55b-767e-4c6a-9fdc-4c2300c7f0b4","population_size":50,"generation":34,"scores":[{"13":-1.4799551,"12":-3.3707557,"32":1.2356446,"20":-4.5820093,"21":-3.6551948,"18":1.0499984,"40":-5.7712092,"10":-1.1848409,"5":-2.5973716,"25":-5.2012186,"36":-5.02569,"43":-6.0541677,"6":-3.3707566,"9":-1.796328,"23":-5.670643,"24":-5.2992578,"34":-4.775678,"48":-5.848365,"46":1.8619846,"11":-4.1024766,"29":-11.595612,"45":-2.7100034,"14":-1.2602413,"7":-1.4976543,"22":-2.1690187,"26":-0.57119787,"8":-0.8552567,"4":-0.014923185,"2":-0.40724778,"27":-0.90810204,"1":-5.1352377,"17":-4.1625814,"28":0.17093961,"30":-5.7198286,"0":-2.6610336,"3":-1.5259964,"16":-6.797188,"33":-6.229727,"35":-7.8272476,"37":-0.8351852,"38":0.74034035,"15":-1.0149847,"39":-4.168948,"42":-5.989782,"19":-3.6024654,"44":-3.3829772,"47":-5.1616178,"49":-6.1079907,"31":-5.3034387,"41":-8.023984},{"46":-5.5986238,"34":-4.534239,"23":-2.7403977,"47":-6.284207,"26":-4.6461744,"49":-3.0859594,"32":-3.6064186,"37":-2.8101246,"3":0.2711714,"24":-2.9530277,"38":-2.7212186,"9":-4.177823,"8":-1.4947598,"43":-2.3903615,"7":-1.9176388,"20":-2.6888652,"16":-0.95691025,"41":-4.245606,"6":-0.2821784,"15":-0.23934121,"36":-2.0781984,"25":-6.746737,"35":-3.1192393,"44":0.84309137,"48":-2.6173937,"31":-7.2024155,"0":-0.9995136,"42":-10.776218,"13":-1.3483765,"21":-1.829633,"30":-7.427111,"29":-6.346571,"11":-0.701403,"5":0.75094104,"27":-4.7604833,"2":-0.2158086,"17":-2.952026,"19":-4.709943,"22":-3.5801616,"1":-2.994163,"10":-2.6068876,"33":-7.4807577,"4":0.29855937,"14":-2.051252,"40":-0.92696303,"45":-5.846688,"18":-0.2620568,"12":-0.5900184,"39":-3.320311,"28":-4.803691},{"19":-1.6374098,"32":-7.0608854,"5":-0.115976594,"0":-0.823748,"8":-0.4674174,"1":-4.241291,"16":0.6256918,"18":-2.7909126,"17":-0.90980357,"29":1.124589,"3":0.53907764,"34":-4.1516957,"7":-0.11296322,"31":-8.022696,"11":-1.7543952,"35":-7.2468405,"9":-1.9748577,"27":-6.025627,"30":-6.885924,"37":-6.368652,"39":-1.8914082,"40":-3.897443,"24":-1.9746668,"41":-7.366498,"45":0.25113982,"21":-4.1330175,"44":-5.6694856,"46":-2.799114,"47":-0.72811735,"14":-2.3075433,"42":0.34034058,"49":-4.327436,"6":-0.9277388,"22":-3.248761,"36":-1.5806258,"4":0.79853237,"12":-1.7290052,"10":-2.2808337,"20":-2.4480398,"25":-5.4077253,"2":-0.13549562,"15":0.0587466,"38":-5.9818506,"48":-7.7930098,"26":-1.6008437,"33":-3.0970616,"23":-0.5001246,"43":-8.257734,"13":-0.4739554,"28":-9.101915},{"34":-7.1162095,"5":0.75404155,"8":0.0806644,"36":-0.5598956,"38":-5.283127,"24":-0.5738148,"41":-5.822985,"45":-3.6928368,"19":1.8945599,"26":2.2363143,"33":-5.3702226,"46":-5.6504335,"39":-0.5380732,"12":-3.494994,"21":-0.24514881,"29":-6.4968796,"37":-1.7391956,"9":0.24764648,"44":-6.0050616,"43":-3.1556416,"40":-3.114514,"0":1.1169783,"10":-3.0221496,"4":0.71817005,"2":-0.10828309,"47":-0.8597895,"48":-0.13096179,"49":-4.729623,"30":-1.0527647,"7":-1.2968149,"13":-1.2063895,"18":-0.23004182,"32":-0.4751242,"35":-6.380617,"25":-4.895747,"20":-0.904418,"1":0.3126194,"27":-4.631973,"11":-1.4761704,"15":-0.68164796,"23":-0.89406717,"17":-4.3468423,"16":-3.0963852,"28":-4.2654424,"3":0.292789,"6":-1.2786105,"14":1.09606,"22":-0.32667297,"31":-2.2469232,"42":-5.4418697},{"16":-5.2526503,"32":-6.3634443,"36":-3.2679844,"42":-6.1911187,"17":0.6657694,"28":-2.2287307,"43":-6.0696135,"19":-2.5921116,"21":-1.0395129,"20":3.3325691,"38":-5.403447,"22":-1.0866393,"39":-5.2612906,"13":0.110688806,"35":-9.209028,"6":-1.2982272,"1":-0.5857518,"7":0.3971466,"44":-7.7847915,"8":-0.93844205,"14":2.6282563,"4":0.40624943,"40":-6.5754633,"24":-0.85832083,"33":-0.6104063,"3":2.1158707,"29":-4.8762445,"31":-1.1775994,"37":-6.249992,"9":-1.0541943,"46":-6.47375,"47":-5.1030784,"10":0.5411806,"48":-7.319977,"11":-0.93714416,"15":-1.4099878,"30":-4.102948,"0":2.0518181,"26":-7.4277487,"34":-4.820016,"18":-0.5411726,"12":-0.7130984,"27":-1.1400305,"25":-6.816667,"2":-0.7096395,"5":-0.64454496,"41":-7.3878846,"45":-1.0586528,"49":-4.8044972,"23":0.48288918},{"0":0.6224516,"21":-1.9724243,"8":0.3221472,"23":-1.1445072,"9":-1.8855957,"38":-4.3636928,"39":-2.5525608,"44":-6.022342,"45":-2.6967726,"47":-0.22814481,"42":-4.267274,"49":-14.870151,"35":-4.246401,"20":-0.08219239,"7":0.24648981,"41":-1.4371231,"4":-0.67331505,"3":-0.45806536,"24":-5.4120345,"43":-6.4070425,"12":-3.4360378,"19":-1.5386674,"5":0.8870964,"2":3.1363246,"18":-0.06081761,"1":1.6736473,"32":0.354136,"46":-9.758692,"48":-6.220552,"11":0.2751074,"6":-0.3357262,"16":-0.9532158,"17":-6.4547324,"27":-5.6590633,"36":-7.743505,"28":-5.8381033,"29":-5.6711817,"34":-5.7756267,"31":-6.076832,"13":-1.1353436,"14":-1.3015141,"22":-1.0451987,"25":-4.9334326,"26":-2.2235262,"15":-0.617426,"30":-4.905954,"33":1.0185475,"37":-4.8664293,"40":-1.9478962,"10":-3.680655},{"7":-0.39519322,"31":-5.695974,"29":-3.7772954,"44":-7.6308074,"27":-2.0888543,"40":-5.2389083,"38":-3.2697976,"13":0.014459705,"41":-5.0734143,"43":-2.3007417,"9":-0.273322,"17":-1.2354606,"32":-2.7820916,"11":-0.18107119,"46":-6.8478456,"48":-3.8834,"4":7.362853,"18":0.22247219,"22":-1.4087099,"49":-5.961255,"20":-0.03473518,"39":-6.0345163,"47":-4.124648,"16":-2.4454093,"25":-8.348789,"37":1.779715,"19":-1.1829737,"2":-1.9087565,"8":0.704858,"14":-0.6411097,"28":-10.572978,"34":-3.7115192,"26":-0.9956168,"0":3.9657726,"5":-0.8495882,"45":-3.034346,"23":-1.076154,"42":-5.1411347,"33":-4.69675,"24":-5.2366548,"1":2.8699353,"3":-1.9849409,"6":-0.4074544,"12":-0.886967,"15":1.2337506,"35":-4.029725,"30":-2.2090604,"10":1.0753075,"21":0.90168494,"36":-5.338687},{"22":-2.5521665,"9":-2.1332574,"35":-4.0650635,"5":-0.24811077,"2":4.0341806,"7":-0.8468792,"10":-2.1746728,"14":0.1538938,"15":-1.3318721,"18":-1.6870466,"3":3.7032642,"21":-1.4621934,"32":-5.043085,"33":-7.713305,"36":-8.053912,"37":-8.696161,"39":-4.4493365,"40":-8.144897,"1":-1.2661278,"6":-0.12410784,"8":-2.746985,"43":-5.1592073,"44":2.2333236,"47":-8.900255,"20":-1.095273,"30":-4.513528,"48":-5.3303876,"24":-1.2112929,"34":-5.172039,"19":-0.95324004,"17":-2.070929,"42":-6.461173,"45":-6.7600837,"31":-2.5871289,"26":-3.340777,"11":0.2804512,"16":0.1068594,"23":-4.8734684,"12":-1.5782331,"29":-7.1542053,"28":-3.4927502,"49":-5.3866286,"27":-3.0936756,"38":-2.0778599,"4":-0.9748148,"13":-0.52859306,"25":0.20650482,"41":-5.2895117,"46":-8.941137,"0":-1.1868578},{"3":-0.009137404,"25":-1.5603099,"28":-2.22714,"32":-7.8123603,"44":-9.291084,"11":-3.164494,"0":-1.2419983,"22":-2.3453734,"43":-6.110915,"46":1.2300284,"26":-6.2174125,"42":-6.7060685,"40":0.5960925,"39":-0.55510026,"8":0.34857464,"14":-1.5066582,"20":-1.337916,"9":0.6811942,"24":1.2820337,"37":-8.370699,"45":-9.192501,"38":-1.04816,"36":-4.4000883,"47":-5.744712,"1":3.5028634,"19":-1.1210515,"12":-0.43374163,"27":-3.1297262,"4":-1.7701153,"7":-3.2430923,"35":-6.307065,"16":0.6228256,"48":-5.625827,"10":-2.5743856,"31":-6.1147633,"23":0.11987386,"18":-2.0092087,"21":0.17314282,"6":-0.7134326,"29":-3.5094981,"30":-6.9117966,"13":-0.8778326,"34":-8.84523,"49":1.066749,"17":-0.0647094,"5":0.5607598,"15":-1.9322069,"33":0.3513314,"41":-5.7962723,"2":-2.0310614},{"49":-6.5220246,"4":-1.1306865,"10":-3.8198159,"30":-1.1031879,"48":-1.5790758,"8":2.2603817,"16":-0.79346,"28":2.0295594,"44":-4.851879,"26":-1.8362617,"5":-2.1923168,"2":-0.3212226,"14":1.6364956,"15":-4.142886,"29":2.2276883,"33":-5.144367,"35":-1.0640407,"41":-5.1647105,"42":-2.6379626,"45":-1.6092819,"12":-3.3547428,"0":1.5736263,"27":-5.785195,"21":-1.2146126,"24":-2.0415292,"18":-1.5602193,"32":-7.0221467,"13":-1.3474666,"22":-0.81844556,"40":-3.3200588,"20":-0.6514348,"43":-4.108674,"46":-4.2089496,"11":-0.7390355,"1":1.061663,"6":1.5885234,"7":0.6280818,"3":2.9117198,"9":0.07487325,"23":4.007449,"34":-1.4104478,"19":2.5727477,"36":-3.5412018,"17":-1.2870796,"38":-6.710744,"25":-7.109666,"47":-0.370833,"39":-5.6028166,"31":-8.9709215,"37":-1.790431},{"11":-0.2690058,"14":-3.3913841,"33":-10.581197,"30":-2.976953,"44":-8.972217,"47":-1.6555841,"12":-0.70008695,"31":-0.25022045,"32":-4.7593946,"3":4.0897493,"25":-5.8330374,"28":-0.3651042,"1":-1.3884176,"4":1.4129777,"17":0.060185242,"39":0.2675672,"45":-9.048476,"13":0.4261586,"20":-2.8880363,"5":0.482935,"46":-1.8770514,"35":-6.084609,"37":-4.4077177,"0":0.6917456,"29":0.7991396,"6":1.3436899,"24":-5.914603,"10":0.5822578,"40":-10.1855345,"21":-1.2513664,"26":-3.6266258,"41":-11.609622,"7":-3.9402604,"19":-7.361811,"16":-1.9210724,"18":-2.2022328,"23":-1.5631672,"9":0.24691784,"38":0.1480616,"27":-0.855943,"22":-1.0407014,"36":-7.1688128,"15":1.2310383,"34":-7.753492,"2":-0.61810076,"49":-5.084746,"42":-1.9394081,"43":-2.63866,"48":3.1244411,"8":-0.15182641},{"7":-0.43349022,"49":-10.877606,"31":2.9603925,"6":-5.556307,"5":0.4943432,"8":3.279948,"15":1.1809219,"0":-1.1014942,"21":-1.0453392,"42":-5.4414873,"2":-1.333519,"14":1.0737827,"23":-0.8279856,"36":-5.918971,"43":-5.7445765,"33":-6.0693674,"47":-1.0138916,"18":-2.6747794,"12":-3.0570805,"41":-8.46067,"1":1.6520433,"19":-1.2949446,"3":-0.5493026,"20":-1.2345463,"22":-2.8604836,"30":1.1112442,"4":-4.622977,"38":-8.06308,"17":-1.5339053,"34":-1.6944987,"48":-10.203657,"10":-0.2837516,"9":-2.6424656,"32":-0.2698845,"37":-0.52395785,"27":-2.9062283,"13":-3.9294496,"26":-0.08224879,"40":-4.747753,"45":-5.1964364,"28":1.508776,"16":2.8767316,"11":-0.23955336,"25":-0.7301278,"35":-3.5013275,"24":-1.5041457,"29":1.7090166,"39":3.4003577,"44":-5.3315945,"46":-1.7841461},{"39":-8.850305,"0":-0.31384102,"1":0.33005637,"14":-4.2095695,"30":-4.744254,"24":0.49226952,"34":0.30211696,"5":-2.258627,"16":1.6377497,"35":-8.30131,"36":-1.7115602,"38":1.0439469,"22":4.7798386,"18":-2.6568706,"47":-1.1623662,"48":-6.944415,"12":1.617782,"20":3.180341,"46":1.2265141,"9":1.1028006,"8":-1.625981,"43":4.024492,"19":1.2642254,"27":-6.604052,"7":-0.815612,"15":-3.5696876,"21":-1.0932764,"44":-3.9737823,"28":-7.3513513,"31":-8.281484,"2":3.462738,"29":2.4749138,"40":-4.0741677,"3":-0.35019416,"11":3.2782063,"10":0.8151676,"41":-5.3454633,"23":-0.6134226,"42":-11.818179,"49":-4.002997,"32":-3.4771075,"6":-1.4568881,"13":3.3970814,"37":-2.5683503,"4":-1.2886088,"17":1.3861911,"26":-9.284855,"45":-0.5503845,"33":-1.9150283,"25":-7.9661813},{"41":-2.125005,"45":-2.285364,"48":-4.6482453,"28":0.6604254,"16":-2.4213662,"29":-1.7540013,"37":-2.3518906,"40":0.780832,"22":-2.9027069,"25":0.19567661,"43":-5.648198,"34":-1.4331398,"46":-7.898465,"17":-1.7564275,"20":-1.69505,"10":0.52288496,"5":0.9459981,"2":-1.6511103,"6":-1.2657624,"26":-1.7086216,"38":-7.8005066,"15":6.415408,"27":-1.8759415,"19":-3.2890472,"13":-0.92866564,"8":-0.80353004,"21":-0.846669,"33":-6.3950396,"18":-0.30217385,"0":2.4311461,"47":-4.7150774,"9":-0.7468906,"11":-2.1090035,"31":-6.2131243,"30":-4.3828797,"36":-4.1589737,"42":-1.7141869,"49":1.9700136,"32":1.4449139,"3":-0.23058562,"23":-1.0550442,"1":1.9567564,"7":-1.8053299,"14":0.6537564,"44":-2.2380223,"4":-0.39013156,"35":-8.063723,"12":2.818864,"39":-7.846856,"24":-5.2459273},{"11":-3.651795,"36":-3.6592553,"46":3.5374198,"43":-1.0684702,"28":-4.6952696,"25":-9.738652,"10":2.9154086,"7":0.47379246,"23":0.25744936,"39":-3.3373227,"18":-3.2992806,"13":2.4632888,"22":2.5900362,"24":0.019536471,"40":3.7954628,"4":-3.5070312,"19":-1.2321124,"41":-7.391198,"21":-1.5825781,"44":-9.540948,"1":0.9921595,"47":-3.066313,"5":-1.6908886,"32":3.6888547,"6":-1.893885,"30":-6.961839,"34":-9.805777,"31":-9.293772,"17":-1.1776975,"20":3.8133101,"42":-6.945607,"2":0.515446,"45":-3.4431224,"15":-1.1121713,"48":-4.484528,"12":-0.2619448,"27":-4.018808,"16":-1.0770698,"35":-6.763035,"49":-2.9136088,"29":0.96365297,"33":-12.277365,"0":-1.1318548,"3":-3.742433,"26":-5.55164,"9":-0.8058752,"37":-5.8556137,"38":-4.246865,"8":5.757755,"14":3.0806584},{"9":-0.2057724,"18":0.65480024,"19":-0.5791656,"6":-4.1889286,"1":-1.5216358,"39":-1.0287908,"10":-4.0246553,"32":-2.125645,"41":0.21487436,"0":-4.069114,"2":3.1414428,"47":-0.90623647,"11":1.5333946,"21":-2.0543995,"12":-3.2007298,"22":-1.4497566,"14":-6.1828666,"7":-4.159892,"25":-9.63585,"26":-7.4429374,"37":-7.786339,"43":0.12014313,"20":-1.934631,"4":1.2492312,"40":-2.1454458,"30":0.52946377,"24":2.935751,"34":-6.5771346,"17":-4.5573783,"46":-9.804109,"48":0.22530365,"49":-6.157985,"13":-4.2057967,"28":-6.7769585,"31":0.2229528,"36":-6.825086,"38":-5.032506,"23":-4.5710535,"45":-2.0093617,"8":-0.11515007,"5":-5.807103,"42":1.6801345,"3":-2.5140862,"16":-1.4748932,"33":-8.827309,"35":-2.1972833,"27":2.141932,"15":-2.8333688,"29":-0.17297097,"44":-1.3831823},{"36":-2.8135095,"10":-2.25838,"35":-13.175997,"38":-0.10819602,"14":0.14488819,"32":1.849961,"33":-3.560912,"40":-6.5650406,"43":-7.281546,"9":-1.366451,"45":-8.470662,"41":-2.6826994,"0":-4.7471137,"22":3.7242997,"23":-9.1801,"28":-6.467778,"44":-4.0730352,"47":4.854802,"48":-1.3245436,"8":-4.2908792,"49":-4.777823,"34":-7.4530396,"29":-3.5376484,"15":-1.3363883,"25":0.50829124,"6":-0.8238987,"31":-0.6595589,"42":-5.1152244,"11":-1.041917,"26":-5.4131002,"30":-10.942613,"13":-6.3417645,"5":2.9464679,"46":-1.7785702,"20":-0.87815493,"2":2.3935616,"7":0.35318094,"16":-4.648909,"17":-4.268477,"27":2.2764773,"39":-7.591966,"21":0.62592983,"12":1.6934954,"19":-0.71312016,"3":1.8464531,"37":-4.0512896,"18":1.5132655,"24":-0.075415,"1":2.4190595,"4":0.5512174},{"20":-1.0029869,"32":-7.0028534,"46":-6.3712296,"38":-11.28469,"11":1.4321346,"13":-4.646512,"8":0.82568824,"26":-6.6866937,"34":-6.9743547,"21":-0.15993659,"35":-7.561179,"2":-1.3738582,"33":-5.8647323,"29":-5.79581,"42":-9.010637,"4":3.8576484,"3":1.5597671,"25":-2.0643868,"1":1.403411,"36":-2.9291446,"40":-2.4914355,"49":-7.5185366,"24":0.10178737,"10":-2.319798,"30":-0.38378707,"28":-2.7420464,"41":1.1980788,"12":1.0323683,"23":-1.6790464,"37":-5.815829,"39":-6.986014,"27":-2.016024,"9":1.5110933,"6":3.8653584,"16":-2.3072445,"17":-1.1320391,"18":-1.3100469,"31":-6.7579093,"19":-3.8272312,"5":3.4332893,"15":-1.0270852,"7":-0.21366605,"22":-4.735616,"43":-7.3431945,"44":0.2794382,"45":-5.481395,"48":1.4239324,"0":-2.4516406,"14":2.852354,"47":-1.8403158},{"43":4.712132,"47":-2.8076248,"34":-5.309314,"21":-3.2880757,"35":-7.4554377,"32":-3.1367512,"36":-4.7028556,"37":-3.8426208,"9":-0.674682,"39":-0.5599256,"29":-12.210589,"22":-0.13606283,"41":-6.328129,"18":-1.164909,"49":-3.57017,"1":4.127396,"11":-0.9428345,"3":1.9628041,"4":4.1953306,"7":-3.6845498,"14":0.4822033,"2":3.093894,"20":-1.0495186,"46":-6.5783567,"40":-3.0340357,"45":2.5400724,"27":-5.730103,"24":-3.9002068,"12":0.27995282,"16":-3.8064854,"25":-7.231135,"8":-3.498286,"15":4.724143,"13":1.7394968,"42":-6.487033,"44":-5.5935636,"30":-2.5756748,"19":-0.1506988,"5":-3.5778935,"33":-4.179188,"38":-0.9539401,"0":3.8216126,"28":-1.8977871,"6":-0.37263384,"31":-4.747041,"23":-3.6482468,"17":-0.9581779,"10":-2.6111498,"26":-8.157618,"48":-7.4557214},{"33":-10.314481,"45":0.45036238,"38":-5.734908,"43":2.639183,"2":2.5284503,"41":-4.2538176,"22":-1.7867035,"12":-2.610121,"7":1.3721632,"13":-1.2747895,"36":0.02496109,"14":2.4581943,"5":-0.44087973,"8":-1.2204568,"39":-5.6762896,"16":-0.4870592,"3":0.82786256,"4":0.5342036,"20":-1.6381956,"0":-2.7690248,"31":-6.653174,"29":-2.4528012,"30":-11.768569,"34":1.431767,"46":-4.433118,"42":1.3029988,"24":-1.3565546,"27":-5.373475,"49":-0.84570026,"48":-2.0513895,"6":3.2195282,"18":-0.808298,"40":-2.6703453,"23":-1.254504,"19":-0.95532054,"10":0.28146416,"37":1.9051262,"35":-10.859381,"1":1.3868704,"21":-0.40074697,"26":-7.523516,"9":-0.68576276,"44":-7.2934403,"47":-3.176794,"15":2.6704679,"25":-5.222205,"32":-4.2750063,"17":-6.635766,"28":-3.4479432,"11":-1.3641121},{"2":2.276215,"47":-3.2619267,"42":-8.609499,"12":1.1753048,"25":0.2569335,"22":-1.1133944,"34":-0.46955186,"43":-7.5719423,"46":-8.8374815,"38":-4.504824,"14":-2.7551217,"0":-2.560429,"3":5.1125245,"10":1.7989223,"48":-6.3536506,"15":-2.8420284,"39":-8.642576,"41":-8.690711,"5":-3.0391555,"31":-6.457976,"49":-4.138269,"7":-1.457037,"40":1.0686321,"37":-5.4112587,"44":-5.18201,"35":-7.1456757,"18":-5.5924788,"24":3.118132,"19":0.79015416,"29":-10.938389,"30":-9.238553,"36":-8.172209,"45":2.0533695,"13":1.0999572,"20":0.54769146,"23":-0.7988988,"26":-0.24174762,"16":-0.6107198,"28":-3.4613,"32":0.6095932,"6":3.6477408,"17":-3.6828415,"33":-0.8097604,"4":2.1152596,"21":-1.380294,"27":-6.2883444,"8":0.33570617,"11":2.2367969,"9":-2.6519506,"1":1.980705},{"43":1.0098683,"15":1.5508325,"20":2.5622602,"44":-4.5266366,"47":0.5595596,"49":2.4441323,"40":-4.0074677,"12":-0.048756026,"6":0.71921927,"34":-0.58480847,"48":-0.91496503,"38":-15.602101,"9":2.1975396,"46":-7.5261803,"23":-1.7485645,"25":-9.98234,"3":0.40352917,"2":-1.3000925,"21":0.3793082,"39":-6.092683,"7":-0.03801304,"29":-3.934002,"45":0.6354839,"13":3.6475587,"8":1.8720827,"18":0.017846633,"11":-2.043403,"17":-0.34380132,"22":-5.0894356,"26":-5.601128,"4":2.7117019,"27":-9.118801,"28":-6.380714,"14":2.336324,"31":1.0155867,"32":-2.3734066,"33":-7.9750533,"35":-10.834158,"1":1.1004568,"10":0.0801218,"36":-3.3937993,"37":-3.5098853,"30":1.2499138,"0":-1.912598,"5":0.8430294,"41":-2.8287723,"24":3.875525,"19":4.445234,"16":0.12486223,"42":5.643771},{"35":-4.373653,"39":-3.8339558,"40":-7.6567903,"41":0.69444716,"42":0.028828835,"45":-1.4600407,"47":-2.4417567,"11":-4.7095685,"24":-0.5535633,"32":1.9858036,"37":2.0322814,"12":-2.4162068,"34":-9.590607,"48":-8.917204,"49":-3.570806,"2":2.803176,"5":0.36249524,"25":-10.362577,"1":3.1976237,"6":1.1062518,"9":-0.26095095,"10":-1.7055686,"16":-1.6398824,"22":0.28752896,"7":-1.9602249,"27":1.841807,"30":-3.7835777,"43":-13.37118,"36":-0.991679,"33":-4.332863,"44":-2.2979324,"0":-7.826865,"8":0.6042371,"29":-0.68760395,"38":-0.43278694,"28":-6.7683854,"46":-6.5680857,"31":-2.083424,"19":1.515781,"21":0.58686703,"3":-0.42720348,"13":-1.0984137,"20":-0.82027215,"14":-2.2391453,"26":-11.6260605,"23":-1.8618019,"15":1.1798068,"17":-6.882319,"18":-6.176575,"4":-1.6281652},{"28":-5.8822837,"1":2.2290926,"15":-0.09372015,"17":-2.3746054,"7":0.20270583,"27":0.69065905,"32":0.915246,"40":-5.31744,"46":-9.837294,"2":-0.93895876,"8":-6.085413,"22":-2.7239099,"41":-5.6375093,"48":-8.899936,"29":0.0027605921,"37":-0.8713808,"12":-1.8604918,"6":-1.0182593,"20":-3.73087,"14":1.816541,"44":-0.7164334,"49":2.4857643,"4":-2.1264198,"24":-5.3399286,"9":2.5283256,"5":2.2643538,"33":-7.4025598,"47":2.1582499,"34":-5.583238,"45":-6.126522,"35":-2.115096,"30":-6.706281,"26":-6.4366236,"0":2.2121549,"25":-5.4770045,"38":-4.6497736,"36":-9.086209,"42":-12.190786,"3":1.6297512,"10":0.459166,"19":-4.500603,"16":-6.038791,"23":1.9341301,"39":-2.7983081,"43":-1.0343044,"21":-1.0805318,"31":0.14618544,"11":-3.062262,"18":-0.41792578,"13":-0.090203665},{"32":-8.576912,"3":2.902244,"9":-2.2658901,"38":3.9877076,"47":-5.7354836,"4":1.5600158,"42":1.057154,"0":-0.6629299,"16":-0.018089961,"33":1.3013139,"8":-0.74672085,"31":-8.661469,"6":-0.5772754,"18":1.1205552,"30":-1.0220808,"40":-7.375016,"44":2.6144624,"46":-10.400541,"49":-4.659255,"25":0.7587763,"43":-4.1714654,"34":-0.7735878,"29":-6.635881,"26":-11.739682,"10":0.8690982,"11":-1.6824448,"37":-1.3363206,"12":3.0274022,"19":-1.7937721,"39":-7.3830743,"45":-2.3882127,"20":-5.4004745,"48":-3.9932237,"17":-1.3957766,"7":3.6751428,"13":1.9064058,"14":-3.3826096,"36":-4.5335298,"22":-2.5287876,"15":2.7899702,"1":5.52359,"28":-9.294072,"35":-3.6705127,"2":-2.1291173,"24":-1.5257052,"41":-0.71657294,"5":2.1434064,"21":4.107393,"27":-3.5565886,"23":0.010993642},{"38":0.46182185,"45":-12.038506,"5":2.267443,"17":-0.878061,"1":-0.50349605,"8":0.42654738,"12":0.60158116,"14":-8.053246,"21":-7.054209,"30":-7.0099854,"9":-2.9551797,"35":-0.55309594,"48":-1.035589,"31":-13.417664,"27":1.3648034,"3":-4.7918863,"2":0.7940941,"4":-0.43286604,"37":-13.6914425,"24":3.1593513,"16":-0.6849333,"13":-5.147784,"10":-1.0683151,"6":-0.6961941,"19":1.851626,"26":-5.6383963,"32":-5.3763113,"7":0.23220558,"33":-2.6460223,"42":-6.849289,"46":-4.746596,"23":-7.258671,"28":-7.6737146,"18":2.3403146,"11":-0.013505417,"49":-6.286148,"39":-0.40695518,"0":-3.2664692,"15":-7.00295,"22":-1.4648011,"41":-0.9407824,"47":-10.191892,"43":-3.423345,"29":-0.13835388,"36":-9.666438,"40":-4.6459026,"44":-5.1328597,"25":-5.3466907,"20":-0.236443,"34":-7.8050904},{"36":-1.1021398,"38":2.8363478,"7":-0.25550094,"32":-4.397956,"13":-2.272571,"39":-7.7849946,"14":-0.6163994,"44":-3.109485,"40":-5.039219,"42":-4.409039,"11":2.5792224,"12":-0.73589885,"26":-0.181468,"46":-4.6932697,"4":-4.100952,"9":0.17415304,"10":-0.49939138,"0":1.5686579,"49":-5.457412,"41":-1.1163824,"48":-5.947212,"19":-0.12931958,"16":-1.2785285,"3":3.1339252,"22":0.87162524,"24":-1.804472,"27":-2.787685,"20":0.64999044,"8":-0.6033512,"31":-2.776437,"6":2.0214238,"43":-6.332577,"47":-9.101875,"5":-5.3744264,"18":-1.4351798,"25":1.6444756,"15":-2.0076077,"29":-6.951574,"28":-4.494911,"45":-5.2034936,"30":-3.3045204,"17":1.156345,"37":-7.3774886,"23":1.0976998,"33":-0.89560604,"1":0.5232691,"21":1.0698664,"2":-0.64349777,"35":-1.1121864,"34":-4.6030397},{"9":2.190166,"28":-7.4588118,"41":-3.0742311,"17":2.9307246,"22":-9.923037,"6":-0.16164787,"4":-1.1363175,"39":-6.248021,"12":0.24132304,"1":-2.713612,"27":-0.07187139,"33":-7.763086,"34":-0.41333818,"43":-2.1395404,"19":4.359834,"35":-11.584904,"29":-6.971022,"7":-1.7796974,"31":0.14847927,"40":-5.780584,"42":-1.7180843,"38":-6.7182236,"45":-6.369739,"3":-1.054813,"25":-9.008263,"32":-4.6274695,"14":-1.1536787,"23":-1.4608774,"26":-3.0705976,"20":1.8712232,"5":-1.1226983,"2":4.438132,"0":2.674596,"8":-4.648837,"30":-6.2653766,"15":-1.0775777,"44":1.1995169,"36":-8.327929,"37":-0.2859703,"18":2.7921336,"46":1.1397331,"10":-5.2556334,"11":0.12635288,"13":-1.2765973,"47":-9.403023,"48":-0.38354635,"49":-4.84352,"21":-2.7698321,"16":-0.83886015,"24":-0.15186839},{"28":5.9858217,"41":-2.5946589,"33":-13.326108,"49":-0.1200408,"8":-3.520242,"23":-0.89950657,"29":-2.5363152,"16":4.158094,"44":-7.062139,"0":-0.6842047,"39":-1.0357105,"48":-5.7711987,"1":0.8595847,"24":-0.6513852,"3":1.8261887,"43":-1.865908,"7":-3.6038241,"21":1.4663875,"22":-0.5832413,"13":0.008382213,"9":-0.11109141,"34":-5.511672,"17":0.71482784,"12":1.7165174,"38":-8.569481,"45":-0.40404946,"6":2.7062097,"10":-2.8204112,"18":-1.0395474,"42":-4.379624,"32":-1.5242176,"46":-0.5147158,"47":-7.1876717,"25":-5.6444526,"19":-0.42743522,"2":2.95814,"15":-3.4512162,"20":-3.0797153,"26":-8.033984,"27":-1.3476844,"11":0.70997757,"30":-2.856438,"14":0.80246,"31":-8.738088,"35":-8.800494,"36":-0.73412144,"37":-6.1242657,"40":-4.5236726,"5":-0.049777318,"4":1.0418781},{"45":-2.0878205,"16":-0.92655456,"41":-3.649571,"10":4.049384,"49":-0.5620002,"6":-0.45706034,"37":-5.9845037,"20":-5.267105,"14":1.7209429,"23":0.29273722,"27":2.8883698,"43":-0.39091828,"12":0.15071163,"0":1.6999867,"13":1.1857785,"46":-1.219162,"17":0.42997736,"1":-0.54470813,"48":-1.5372324,"18":-0.060723305,"19":-1.794137,"29":-3.1528285,"30":-6.650927,"15":-3.9812603,"3":-0.19193271,"9":-0.3728759,"31":-3.5608888,"7":1.4619437,"44":-6.973526,"26":-2.5819368,"33":2.5657094,"2":0.5460104,"11":1.6197468,"25":-3.3421376,"40":-4.1599216,"47":-0.41627723,"35":0.88371384,"21":-0.1889462,"42":3.0599132,"36":-1.9014488,"4":2.864126,"5":2.5040603,"8":-0.54301894,"24":-0.17286158,"28":-7.469434,"32":-4.774502,"38":-7.184961,"39":-1.9451778,"22":0.06270179,"34":-3.0252218},{"1":0.92288417,"28":2.7764556,"32":0.7825122,"37":-8.300393,"38":-6.160379,"6":1.199569,"13":1.843347,"26":-0.54914397,"7":0.56303585,"22":-0.3928766,"27":-8.180695,"39":-2.5213783,"48":-9.358813,"15":-0.7177399,"20":0.759079,"12":0.99288845,"9":1.4179152,"17":2.3389351,"35":1.6612186,"41":-1.1579964,"30":-5.2557178,"43":-4.2738295,"44":-3.757796,"4":-0.057965852,"45":-3.4518898,"36":-0.76826334,"46":-2.6758778,"21":3.1219337,"47":0.497543,"18":-1.9290537,"40":-1.2653574,"42":-7.452469,"23":1.2170466,"16":-0.8002862,"11":2.9817648,"29":-4.8019037,"49":-4.8648057,"10":1.394212,"3":0.16004555,"24":-3.3897088,"5":4.5822277,"0":-2.0557404,"25":2.7564936,"33":-7.0469894,"8":1.8904943,"2":1.7194443,"19":1.5301462,"31":-2.5256042,"34":-2.4306138,"14":-0.65857023},{"35":-7.440613,"38":-5.8018556,"45":-3.9852467,"39":-10.731432,"7":2.283769,"44":-4.273898,"41":-0.2424072,"17":1.2359033,"33":1.4908397,"6":1.0062697,"14":4.7816806,"2":2.648913,"16":1.5623152,"18":-1.9043556,"20":2.4425204,"25":0.06304965,"12":3.6959503,"4":1.7667234,"40":4.697076,"46":-7.489553,"49":1.2028736,"22":-1.5187964,"3":0.7459575,"8":4.042272,"9":1.4675652,"13":2.143397,"31":-5.179932,"26":-0.298519,"19":0.8127718,"0":-0.77233094,"29":-7.1337113,"30":-6.8897324,"11":0.47068086,"48":-3.2769763,"23":0.74841917,"34":0.6907096,"15":-0.22754793,"47":-3.2000694,"1":-0.150973,"24":-1.9273676,"32":0.5115291,"37":-8.655817,"42":-0.6817434,"10":-1.4657556,"27":-2.863134,"36":-5.0656424,"43":-7.0269217,"5":-2.4479375,"21":2.1148446,"28":-9.779695},{"3":2.7166603,"21":0.5608846,"33":-4.4162126,"8":0.020828724,"46":-6.2821884,"45":-6.739582,"10":-2.304177,"27":2.0577347,"42":-0.77062494,"1":3.8701484,"0":-0.80404174,"9":3.6465697,"24":-0.6140908,"48":-8.031019,"34":-1.6037617,"4":2.2597942,"13":-0.47515202,"6":0.45057854,"16":-1.9515893,"12":-1.9472828,"17":3.103903,"23":-1.8590027,"28":3.0854974,"31":-2.7784257,"36":-7.423518,"38":3.785356,"39":-0.14416714,"40":-9.468713,"29":-1.2941794,"19":-2.6143894,"15":2.0398443,"18":0.827158,"11":-2.2458482,"7":2.3907075,"25":-3.0863743,"43":-4.634142,"22":-1.2707936,"30":-4.269676,"41":1.9262397,"35":1.7550011,"47":-8.083954,"44":-9.288431,"49":-5.073826,"37":-1.8682301,"26":-12.438062,"14":-7.7163367,"20":0.7501568,"2":2.001269,"5":2.2369907,"32":0.87154025},{"13":4.528148,"34":-7.3217316,"22":-1.0858823,"40":-2.9252558,"30":-6.155912,"29":-7.895255,"41":-9.410375,"43":-1.2027782,"31":-8.15159,"49":-3.664584,"7":2.3541687,"28":-1.44493,"42":-4.3936977,"46":-12.043066,"6":-1.1208286,"37":-1.7790867,"35":-11.96357,"45":-1.0903281,"1":1.1902065,"24":4.6144295,"10":0.11402883,"18":-0.6236558,"48":2.1994855,"44":-4.8308754,"8":-1.0938857,"36":-2.5280573,"38":0.4988204,"19":3.6937184,"12":-0.21491185,"5":-0.94118184,"9":-2.128302,"3":1.3499311,"25":-2.3497412,"21":0.7251157,"11":0.64074576,"39":-6.9730415,"47":-10.566658,"27":-1.7992518,"2":1.8139048,"26":-13.1088,"23":-4.486793,"0":1.0543308,"17":0.8507044,"32":-0.74444467,"16":1.7839527,"20":-3.2473247,"14":-0.22960457,"4":0.64059687,"15":-2.1211636,"33":-5.263303},{"22":-2.3157744,"5":-0.8974422,"2":0.89333487,"3":-2.720667,"26":-5.7018075,"8":5.0277834,"34":-3.7690265,"18":0.4795602,"16":-7.229969,"15":0.4576252,"19":-1.4481394,"1":-1.0378385,"38":-1.8835398,"41":-9.38455,"24":3.7980595,"7":1.2893342,"39":-1.9939642,"10":1.7236315,"14":-2.1268165,"13":2.4808698,"30":-6.2626553,"35":-1.2192616,"37":0.33116633,"31":1.3258712,"46":-3.4694881,"11":0.9214215,"23":2.2248876,"21":0.28852615,"29":-0.9131012,"6":-1.1601796,"40":0.3064028,"32":-10.766546,"4":3.450002,"9":-0.19940262,"0":-3.8103623,"27":-6.823036,"36":-4.575931,"45":1.1442382,"43":-1.949317,"44":-1.1268657,"48":0.19965892,"20":1.9219799,"47":-4.3444314,"42":-3.2612987,"49":0.29744834,"28":-5.1106477,"12":-2.436583,"25":2.4430537,"33":-3.0943086,"17":-3.724048}],"nn_shapes":{"31":[18,29,20,13,4,10,18,3,28,8],"9":[18,25,22,27,18,8],"32":[18,32,29,4,28,10,34,8,8],"33":[18,34,17,28,8],"42":[18,30,33,19,7,16,14,12,10,8],"35":[18,34,22,34,4,8],"4":[18,31,19,8],"46":[18,20,4,16,26,7,30,6,8],"36":[18,32,25,19,8],"20":[18,4,21,19,8],"34":[18,27,10,19,9,20,24,33,15,9,8],"45":[18,11,12,6,8],"8":[18,21,7,8],"24":[18,17,11,11,23,34,8],"1":[18,15,34,8],"5":[18,26,34,8],"11":[18,16,4,3,8,8,34,15,8],"23":[18,25,9,13,26,10,30,28,8],"26":[18,3,4,29,19,26,26,14,8],"6":[18,4,29,16,11,20,12,25,8],"21":[18,19,21,6,32,31,9,10,8],"16":[18,6,11,18,18,8],"19":[18,16,33,6,26,8],"38":[18,17,3,21,4,8],"3":[18,27,28,8],"2":[18,5,15,22,7,8],"40":[18,32,8],"47":[18,9,6,7,29,8],"17":[18,8,9,8],"22":[18,33,8],"0":[18,9,8],"37":[18,24,19,16,19,5,25,3,13,4,8],"41":[18,25,21,15,29,8],"44":[18,32,20,20,33,3,26,27,19,14,8],"49":[18,8,8],"43":[18,22,19,15,32,15,29,27,12,33,8],"12":[18,11,24,26,16,6,32,30,3,8],"7":[18,30,8],"14":[18,10,31,27,8,9,11,8,8],"15":[18,16,24,20,16,8],"48":[18,32,4,6,5,7,6,31,8],"25":[18,3,5,29,21,8],"39":[18,12,29,8],"10":[18,30,27,6,31,6,8],"18":[18,23,8,3,20,8,5,11,28,12,8],"28":[18,29,8],"30":[18,11,22,4,19,8],"27":[18,26,17,8],"29":[18,19,29,28,19,9,23,8],"13":[18,9,28,14,34,19,30,25,8]},"crossbreed_segments":19,"weight_initialization_range":{"start":-1.520325,"end":0.9699532},"minor_mutation_rate":0.7658576,"major_mutation_rate":0.5723158,"mutation_weight_range":{"start":-0.5263189,"end":0.5263189}},"state":"Finish","generation":35,"max_generations":35,"id":"7ce5d55b-767e-4c6a-9fdc-4c2300c7f0b4"},"left":null,"right":null}},"right":{"val":{"node":{"id":"e9ccd9b0-ed91-4472-82f2-4a2eba9ec7cf","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_e9ccd9b0-ed91-4472-82f2-4a2eba9ec7cf","population_size":50,"generation":39,"scores":[{"43":3.9992142,"47":-4.5046477,"2":-5.5795836,"8":-1.3530275,"44":-3.0102296,"14":-3.7390893,"23":-4.242768,"29":-3.8128166,"3":-5.487908,"21":2.6828303,"25":-1.0500784,"26":-3.7741694,"48":-0.42123738,"7":-6.781279,"24":-2.9674377,"9":-7.3867884,"13":-7.6303062,"18":-7.8061724,"4":-1.3329614,"19":-5.2335706,"6":-0.5076838,"28":-5.945432,"41":-5.6895003,"11":-0.73944724,"42":-9.400959,"45":-1.5968401,"27":-0.355994,"46":-7.7236977,"20":-1.1665789,"31":-1.7760379,"0":-2.8921604,"1":-2.114426,"15":-4.2793436,"30":-4.6812367,"35":-1.5566807,"16":-8.6894245,"39":-5.578435,"22":-5.2217,"34":-1.9205135,"40":-3.0051837,"49":0.66726625,"33":-3.3533502,"12":-5.592861,"5":-5.312856,"36":-6.2344356,"17":-5.7748103,"38":0.5411754,"32":-3.046572,"37":-1.5194869,"10":-6.279486},{"30":1.2161078,"41":-4.031306,"9":-1.494943,"1":1.0179576,"12":-2.2582572,"14":-0.0033039986,"27":-3.4722354,"42":0.2308604,"46":-6.825545,"35":-6.606893,"49":-0.3220868,"8":-1.0572344,"16":-1.1804974,"18":-3.7892349,"28":-4.736219,"2":-1.8897156,"31":-3.9525425,"13":0.76869696,"26":-3.3246117,"34":-4.679414,"25":-1.8132522,"3":0.1972888,"0":1.9798477,"11":3.4497924,"19":-5.0132017,"36":-0.5877894,"5":-4.8670454,"10":0.3955422,"38":-3.554502,"39":-6.358167,"40":-7.178646,"22":-3.6489968,"20":-11.086825,"6":-2.3245173,"23":-2.4844785,"48":-3.9294486,"21":0.5636681,"47":-4.993905,"43":-4.0265465,"29":-4.931633,"37":-2.3056412,"44":-2.1918764,"24":-0.804235,"45":1.0559769,"17":-4.9950585,"15":-7.107587,"7":-0.095338166,"32":-5.624285,"33":-11.3650875,"4":-6.0735903},{"33":-0.2672924,"37":-7.0523896,"45":-3.1899393,"4":1.1832592,"46":-7.437276,"9":-1.2738589,"20":-0.8374712,"30":-1.8275493,"44":-5.6137276,"10":0.016293202,"38":-7.447117,"47":0.51455915,"2":-5.1127825,"3":1.1743109,"17":-2.266808,"25":-5.9312935,"13":-3.5899894,"34":-0.5204814,"8":0.71664774,"22":-7.2720046,"42":-2.2801425,"35":-0.54734063,"16":-1.882032,"36":-4.8975015,"48":1.9084457,"49":-6.8638544,"27":-6.180188,"15":-3.1083062,"41":2.9100556,"43":0.72237223,"0":6.4811068,"23":-3.8430195,"18":3.4745827,"32":-0.8023281,"21":-1.9450836,"12":-2.7419124,"24":-3.4513748,"26":-4.294472,"29":-3.973111,"19":-1.660873,"40":-7.069042,"6":-6.3210344,"1":0.6500218,"5":2.709934,"11":0.124825045,"39":-3.5105922,"14":-0.3178838,"28":-2.510563,"7":0.4148386,"31":-6.6896887},{"3":0.70983666,"8":-2.2349143,"15":-1.2513312,"2":3.1698282,"4":-0.8506104,"16":-5.381428,"18":-6.4334326,"19":-2.572496,"21":1.1237977,"22":-1.7872235,"1":-1.9813999,"0":2.2834702,"14":-0.39137167,"17":0.65386784,"25":-2.5975916,"29":-7.5092864,"13":-1.0119712,"31":1.5681539,"6":-1.3966221,"33":-0.81131,"9":-1.8049839,"36":-4.071992,"37":-5.9423456,"12":-1.3135865,"39":-5.1311674,"41":-6.0883174,"43":-2.120512,"44":-2.1180835,"48":-1.4098536,"7":-2.358136,"11":0.40244323,"46":-5.146202,"23":-1.516969,"20":-0.36661258,"10":0.19683321,"28":-3.416018,"30":-1.3816006,"40":-1.0533543,"38":-1.5830915,"34":-7.170728,"45":-1.7942884,"47":-6.9535356,"27":-7.1779795,"49":3.0859761,"24":-3.9915624,"32":-3.174423,"5":3.4098115,"35":-3.2228577,"26":-8.541118,"42":-1.303786},{"41":-6.3701453,"15":-2.0339947,"20":0.4183866,"31":-3.7665467,"35":-6.529357,"5":-1.2160978,"1":1.0422201,"40":-1.9403757,"42":-7.4427004,"12":-0.6386552,"46":-6.137133,"0":-1.8831915,"27":-5.0639377,"2":-0.29547006,"14":-0.15438399,"28":-6.4436235,"3":3.0657654,"16":-0.729885,"18":-0.5947112,"19":-2.33356,"13":-0.20582339,"48":-1.9001707,"23":-0.20848198,"29":-2.633077,"39":-1.1359828,"43":-4.9488792,"8":-2.3867326,"21":-0.1401242,"37":-5.498063,"6":-1.8504162,"22":-1.1518805,"4":-2.0092123,"17":-2.5496225,"33":-3.7804139,"36":-1.7574921,"44":-1.2355787,"26":-9.513443,"49":-2.929417,"47":-2.2842972,"11":-1.5175254,"9":-4.323693,"45":-7.9193735,"30":-0.5015472,"10":-2.2717535,"38":-3.238856,"25":-4.037089,"7":0.11865799,"24":1.5743479,"34":-2.54675,"32":-7.3294044},{"21":0.002967483,"10":-1.6881393,"0":-0.09014299,"15":-1.753298,"44":-12.352557,"45":-1.7466633,"13":-6.0673747,"14":-0.2908476,"8":-1.794873,"6":-1.6077182,"22":-2.0611207,"16":-5.3833337,"33":-3.2568278,"37":-1.7897274,"41":-6.8075967,"39":-7.8766813,"28":-6.0944533,"9":-2.2877007,"25":-1.7429354,"11":-1.460429,"1":-2.8458152,"19":-5.320201,"4":-0.9419524,"20":-0.112826966,"12":-1.8409541,"26":-5.6394415,"34":-5.3140664,"35":-1.4955766,"40":-1.1019347,"5":-0.4805966,"18":-3.5132184,"30":-0.6222774,"23":-1.342622,"7":0.3945582,"24":0.35801253,"17":-0.99341553,"31":-1.3236859,"32":-1.8476651,"2":0.37019396,"36":-3.540688,"38":-4.2795362,"42":-3.8382351,"43":-1.3367411,"29":-5.935337,"46":-1.2821245,"27":-5.479205,"3":0.5341558,"47":-2.9846358,"48":-5.097788,"49":-0.094796374},{"17":-3.9550927,"32":-3.3171592,"40":-2.4567707,"31":-3.6726398,"43":-5.3145113,"45":-3.314258,"15":-1.4313362,"46":-4.794688,"1":1.1092482,"48":-0.5783099,"47":-4.1458673,"20":-0.14032319,"24":-2.865559,"6":-0.1845022,"18":-0.29459697,"27":1.3865328,"7":-2.8306901,"29":-3.8067093,"23":-5.62708,"2":1.9770987,"3":-0.40744215,"39":-2.027505,"30":-3.4561577,"42":-0.49071854,"13":-1.1093819,"44":-1.359725,"49":-4.270898,"16":-1.3099486,"14":-3.1968582,"33":-6.610059,"11":-1.639101,"35":-2.336779,"36":-0.6048788,"38":-0.6002465,"37":-3.7830098,"41":-4.5704784,"8":-0.3513032,"26":-4.4879103,"34":-2.945097,"22":-2.128545,"25":-2.4106598,"21":-1.2951179,"10":1.1755426,"12":-1.1337605,"0":-2.481666,"4":-1.0497648,"5":1.6246659,"28":-2.1384883,"9":-2.84212,"19":0.23993035},{"15":-0.5575965,"8":-1.2797987,"42":-8.086983,"44":-6.320508,"2":1.7739608,"19":-1.1683242,"45":-6.0461717,"13":0.43286723,"46":-6.203097,"48":-2.3888295,"33":-8.6250305,"36":-1.2893306,"17":-1.1821846,"7":-6.0994215,"30":-2.6853046,"20":-2.3868945,"10":0.44522938,"12":-8.017397,"28":-4.1283474,"6":-0.78775585,"29":1.959193,"27":-5.0404935,"34":-3.4925885,"37":-4.3349533,"5":-4.8512006,"16":-1.1228998,"40":-2.2141623,"9":-3.424548,"24":-1.9454339,"39":0.5661337,"22":-0.4741568,"41":-1.8539038,"23":-2.322418,"32":-6.666247,"35":-0.71028095,"43":-4.313234,"14":-2.363731,"18":-4.0629244,"31":-3.703715,"38":-3.891969,"47":-4.446846,"3":1.4331659,"21":-3.304261,"25":-0.2744257,"49":-6.246268,"1":-2.474549,"11":-0.20799717,"4":0.542174,"26":-5.744078,"0":2.9048371},{"39":-1.0083039,"27":-3.3774612,"6":-2.4098399,"40":-2.4348347,"33":0.048642922,"43":-3.37929,"44":-0.3132566,"45":-0.9355472,"20":-2.4439034,"47":-2.7029686,"1":3.3756676,"4":-4.2703366,"24":-1.7563646,"9":0.049877547,"10":-2.1979668,"23":-7.2783213,"37":-7.5914383,"12":-2.8697972,"49":3.4945786,"17":0.2660234,"32":-6.674684,"48":-2.9025922,"19":-6.035722,"46":-0.7289762,"21":-3.212648,"7":-0.33863682,"3":-1.3051996,"28":-3.1396213,"8":0.009596395,"29":-2.2473025,"18":-1.5519928,"36":-5.6627817,"13":-1.8093303,"26":-5.7594147,"2":0.8199009,"0":1.1146716,"14":-3.5745785,"22":-0.6953138,"15":-0.3217592,"25":-1.5685022,"34":-1.4037598,"16":-0.8556398,"11":2.4476075,"30":-0.41936773,"31":-1.4368732,"5":1.1860164,"35":-5.6351204,"38":-1.9837389,"41":-4.495288,"42":0.15063803},{"7":-0.7718784,"30":-3.541531,"27":-4.7723556,"22":-1.4038429,"11":-0.185096,"10":0.68760884,"49":-1.5618917,"2":1.419422,"29":-5.385145,"0":2.755609,"19":1.3312238,"12":-2.159225,"5":-0.42700833,"13":-1.0507237,"23":0.35108203,"24":-2.510683,"47":-0.032363795,"1":3.2318428,"16":0.027117383,"36":-1.3730414,"8":-4.152361,"33":-0.4757672,"38":-12.011841,"20":1.8526627,"9":-3.9146304,"42":0.078292586,"17":-2.0965407,"45":0.708029,"41":-1.2375861,"43":-2.6319194,"31":1.3287512,"34":0.016118193,"4":1.3351895,"15":-0.7380446,"32":-4.3767843,"37":-5.2361054,"28":-5.5365553,"14":0.065305196,"21":2.1623693,"39":-0.80251104,"3":1.0586182,"35":-0.61291254,"48":-4.540633,"25":-2.4006119,"6":-0.17414978,"40":-4.7291884,"18":1.3840742,"26":-2.750651,"44":-5.106943,"46":-0.67965573},{"37":-3.647609,"29":0.53563297,"12":0.182439,"21":-0.37325156,"33":-5.569397,"40":0.4050188,"9":0.6015199,"17":-0.8685428,"27":-6.0469756,"32":-0.17398681,"47":-6.4160223,"48":-0.011684597,"5":0.6881464,"20":0.909681,"45":-1.1848186,"6":2.1076868,"24":-1.043169,"36":-1.1742532,"42":-4.045116,"43":-1.703203,"8":-1.2379206,"39":-3.3361504,"25":0.069199,"2":1.0969979,"34":-6.279511,"13":-0.21740022,"10":-2.1218956,"22":-1.3644476,"1":1.096051,"7":-2.4457722,"28":-1.4240782,"35":-2.0047002,"41":-0.88586825,"19":-0.4632822,"26":-4.4105883,"14":0.23970902,"31":-1.8136575,"46":0.1194268,"16":1.1666701,"44":-0.060142003,"3":0.5819024,"18":-0.15628457,"11":-1.2421626,"15":1.0688509,"0":-0.01766477,"23":-0.52614087,"30":-3.4085166,"38":1.092621,"4":0.051703263,"49":-0.48033255},{"39":-0.8097353,"45":-4.6091747,"49":-2.3429544,"0":4.6909013,"23":-0.33870643,"46":-6.2761345,"36":-2.7613902,"14":-2.8446157,"44":-1.0748715,"18":-2.9685073,"21":-1.8445809,"4":-0.18027519,"8":-0.20233941,"19":3.2557712,"22":0.1839052,"32":-0.52092063,"5":-1.2569103,"47":-7.675683,"48":-6.2399397,"25":-3.5096703,"12":-0.5838994,"6":1.7820994,"37":1.153915,"38":1.4842095,"7":0.60168,"16":3.6185489,"17":0.115030386,"3":0.65398276,"9":0.9398902,"26":-1.1603184,"27":-2.6607437,"33":-0.9219724,"35":-6.264696,"41":-0.6842636,"13":0.90996706,"11":0.39423978,"24":-1.2792782,"28":-4.616446,"29":-1.6313177,"34":-1.893089,"42":-1.6772099,"43":-4.4321504,"1":1.6374744,"30":-1.1260786,"20":-2.3123834,"40":-1.0584848,"15":-0.1136778,"2":1.297457,"10":0.771706,"31":-0.49372903},{"18":1.843343,"48":-2.3260427,"11":1.2345682,"20":-0.44947678,"35":-1.4961929,"0":2.5324197,"39":-5.0516925,"16":-0.24023978,"46":-2.5366795,"8":1.6773808,"3":0.9517206,"22":0.059372794,"34":-1.8604243,"37":-2.1974106,"42":-1.0585811,"15":-0.6423564,"6":0.82989085,"23":-1.0489807,"49":-2.9762394,"4":1.1213424,"25":3.165667,"38":-4.2662935,"21":-0.2642374,"7":1.5180295,"17":-0.68948835,"24":-1.9519389,"14":-0.07390101,"27":0.40589008,"13":1.3306811,"9":-1.7720668,"19":-0.6951172,"30":0.1460456,"1":-0.24095845,"29":-0.5211332,"10":-0.6118816,"12":1.0779173,"33":-5.2865686,"36":-0.6828236,"40":2.691331,"41":-1.8558085,"44":-1.1460923,"45":-3.0736158,"5":1.581803,"2":0.2060998,"47":-4.8148293,"32":0.520277,"28":-3.3647797,"43":0.4097596,"31":-2.5112262,"26":-1.9135878},{"45":-1.828818,"27":-3.9781845,"9":1.0448663,"1":0.5032152,"44":-5.0817337,"12":2.0783315,"2":2.6825695,"16":-0.01902939,"28":-0.14135881,"3":-1.4847008,"49":-3.2476387,"6":1.6506058,"26":2.4984813,"0":1.5982283,"14":-1.8659487,"39":-1.8003147,"4":-0.3222174,"40":-3.874482,"35":-2.7212148,"18":-0.14480382,"36":-4.484176,"17":-4.527171,"32":-5.005798,"33":-0.6439644,"22":-0.87691224,"38":-2.155911,"47":-4.8374915,"46":-4.4317145,"19":-0.4433476,"20":-1.169621,"21":0.86086017,"30":-0.8310798,"43":-1.8577973,"48":-1.7748047,"13":0.70406055,"15":-4.6105113,"10":1.3232958,"25":-6.4578896,"8":3.2943752,"37":-6.259249,"31":1.449472,"41":-1.0283349,"42":-5.8519,"7":0.08401001,"23":-1.32374,"24":0.6111746,"29":-2.395357,"11":0.6146709,"5":1.9058244,"34":-2.7471895},{"41":1.5665799,"14":0.98933697,"48":1.1490715,"17":-0.65369266,"31":4.948372,"13":-0.6165978,"28":-6.0271063,"7":-0.49433655,"9":2.243611,"34":0.65494835,"15":-0.46870703,"23":0.43246946,"37":-4.889591,"12":0.7344483,"24":-0.17955902,"30":0.2612438,"39":0.85301316,"43":-0.4484605,"44":-4.687682,"46":-0.91271675,"22":-1.4422206,"29":-4.8090563,"38":-2.1764765,"40":1.121421,"49":-2.2692378,"45":-2.7204723,"5":0.95555294,"10":-0.8139192,"25":-2.6582286,"2":0.16849199,"6":1.6245015,"27":-0.5177392,"0":2.319653,"4":0.97141457,"8":0.12884225,"35":-7.141038,"47":-0.85919076,"26":1.4384298,"36":-0.85735494,"42":-0.32833806,"21":-1.189548,"33":-1.2861583,"11":0.5186689,"18":-0.0094464,"16":2.2433152,"20":-0.052867997,"3":0.9253257,"1":5.393658,"19":1.5576637,"32":-3.226401},{"20":-0.19593543,"14":0.83755285,"25":-3.3789551,"16":1.7667615,"23":-2.8395653,"0":2.9271774,"45":-2.0184617,"27":-5.159618,"7":0.49175817,"17":-1.4910275,"33":-0.6657322,"31":-5.1403327,"34":-1.7197632,"41":-0.5913552,"18":-0.77919066,"29":3.1252992,"13":1.258585,"3":-0.98743933,"22":0.33892083,"28":-6.335474,"32":0.18858862,"24":-0.3337306,"26":-4.24691,"9":-0.6333006,"2":1.0877444,"11":1.4607445,"42":-1.7089491,"19":-0.4904346,"37":-4.237266,"38":0.4482972,"10":1.4857776,"44":-4.6286287,"47":-2.2293868,"39":-0.6815168,"48":-1.0589371,"49":-0.207094,"21":-0.33392,"30":0.97979534,"46":-0.92402613,"4":-0.9387461,"5":-1.1654049,"15":-3.043011,"35":-0.4398272,"1":0.9374426,"43":-2.230125,"8":-2.0628667,"36":-4.8863635,"40":-0.29670602,"12":-2.1375053,"6":-0.47122374},{"20":-2.0764604,"7":0.6831805,"42":-1.8826067,"28":1.4643631,"9":0.16895945,"47":-2.528984,"19":-0.91754705,"13":-2.2037284,"0":2.3398948,"16":-2.7832704,"17":-0.11946501,"18":1.0754315,"21":-2.4618123,"22":-1.8098357,"12":1.3815764,"24":-0.776282,"30":-4.980134,"6":0.91764337,"41":-1.1241322,"27":-9.4606905,"29":-4.3998313,"46":-2.5967937,"48":-1.9770539,"36":-0.7873017,"44":-1.0231769,"23":-0.3352366,"3":0.048101377,"35":-1.396189,"10":1.4753078,"39":-2.2247286,"4":1.1689899,"11":0.41674337,"14":0.53420657,"31":-0.06436679,"8":0.03502481,"5":1.0379488,"15":0.063165605,"2":0.3548666,"32":-4.513288,"33":-1.2948178,"34":-5.5102587,"26":0.23320284,"38":-2.0078678,"45":-5.1284723,"49":-4.005569,"43":-0.9229042,"25":-0.45239383,"37":-0.6370138,"40":-1.2462422,"1":1.6530983},{"21":-0.38331962,"8":2.026757,"20":0.5151671,"9":-1.1924055,"37":-2.0277083,"38":-0.3017742,"16":1.3640101,"15":0.13279581,"44":0.17723341,"34":-3.5099156,"17":1.050812,"48":-2.6314995,"18":-0.6588848,"3":0.16171512,"28":-1.1441414,"13":-0.67578256,"39":0.8596543,"29":-2.3316154,"4":0.8421298,"24":-2.6958911,"43":-1.0933688,"40":-8.010778,"1":1.0655518,"5":0.8657354,"12":1.7577412,"22":-1.7020146,"30":-1.2490209,"36":-2.9840484,"42":-3.3119438,"33":-1.4082686,"35":-2.4250636,"10":-0.68846077,"14":1.5289061,"31":0.41172418,"46":-2.9917612,"32":-0.2886078,"2":2.022957,"0":0.103600785,"26":-0.559658,"7":0.81756955,"11":-1.0929754,"27":-6.08912,"19":-0.22903097,"45":-1.3501732,"41":0.95710343,"47":-5.5495005,"49":0.534135,"23":1.0897367,"25":-0.5317975,"6":-0.4651228},{"43":-0.912937,"35":-5.619212,"2":1.8388735,"22":-0.9333854,"12":0.8697807,"32":0.110505484,"41":-1.8088824,"18":0.2546326,"47":3.81651,"49":1.6372879,"10":0.6941844,"30":-2.030867,"29":-0.37006766,"48":1.118849,"5":0.950566,"31":-4.9221606,"23":1.8758444,"44":6.254659,"26":-4.2569823,"7":1.2397369,"38":-5.7180223,"19":1.3346251,"6":5.6847343,"36":-0.43315703,"39":-0.8046114,"8":-2.9715362,"24":0.054477204,"40":0.5457916,"42":-7.515415,"17":0.7352798,"28":-1.6180528,"16":-0.655275,"37":-5.824168,"1":1.5037214,"33":0.99957705,"46":1.3663788,"0":1.5710032,"4":-0.71526957,"20":-0.4160846,"21":0.6576546,"45":0.21937521,"9":-0.2137694,"34":0.79329664,"25":-2.0128503,"15":1.2275442,"27":1.4035922,"3":1.2848402,"14":-1.2121488,"13":0.3068056,"11":-0.29952198},{"37":-4.4004545,"41":-2.222691,"23":-0.5789318,"48":-0.3404936,"28":-1.5919569,"11":1.1093464,"33":-4.122744,"10":0.061913647,"3":-0.35628682,"21":1.5737168,"16":0.44581977,"7":1.7618641,"5":-1.1996305,"29":-0.3316942,"34":-2.5683775,"35":-5.105937,"44":-2.6302686,"14":0.31766742,"17":0.23522523,"19":0.18837519,"12":0.05608965,"13":-2.070341,"20":0.6463616,"32":3.5927615,"47":-1.7332737,"25":-5.487849,"2":0.06039362,"1":0.8864252,"15":-2.714026,"36":-1.1382253,"42":-0.71505725,"4":1.4925568,"39":-3.233741,"6":0.58313,"27":1.2401294,"18":1.621157,"30":0.1280034,"38":-3.1176038,"40":-2.0954242,"0":-2.9062622,"45":-8.4225445,"8":-0.6730328,"31":-4.962428,"43":-6.423936,"24":-0.47346577,"46":-3.6372285,"49":-1.8083713,"9":-0.6966462,"26":-6.2507567,"22":3.008004},{"45":-1.3502338,"14":0.13960299,"44":-5.4499006,"16":0.95496666,"6":1.367815,"49":-0.4535522,"37":-2.4270504,"35":-7.0546584,"4":1.002504,"41":0.4838778,"47":-5.666858,"30":0.014100397,"1":-1.9072472,"8":-0.036146164,"0":1.2664982,"10":1.2509449,"11":-2.5789587,"42":-1.4337652,"43":-4.2873507,"48":-0.4941422,"5":1.7776229,"29":-1.0830773,"36":2.0817184,"23":-0.50965863,"40":-2.8496785,"2":0.9281192,"19":1.0012746,"20":0.015172231,"27":-4.8046246,"25":-3.8953362,"13":-0.39926222,"9":-1.3867865,"32":-6.3781114,"12":-0.77974415,"15":0.1178506,"3":-1.3276548,"21":-0.22982259,"26":-1.2269458,"17":1.9777172,"24":1.1893345,"34":-2.942224,"39":-5.1201906,"22":-2.2610478,"18":1.5328476,"46":0.0609849,"28":-2.2523043,"31":-3.73463,"7":1.619745,"33":1.2126552,"38":0.57228535},{"33":0.111007,"0":0.26398602,"46":-5.181652,"39":-2.4558427,"2":1.6909914,"11":0.4194135,"27":-0.14934582,"25":-4.174695,"44":-1.0273359,"8":-0.353817,"17":0.1357848,"45":-1.6964096,"49":-4.6153183,"7":3.1115263,"18":0.13559121,"24":0.39826924,"26":-0.55122554,"30":-0.11509659,"32":0.48896962,"13":1.2921351,"38":-0.1601688,"47":0.59402,"4":0.7718768,"5":0.4986666,"28":0.027459204,"3":1.0086848,"9":0.49581322,"15":0.6072948,"21":4.538417,"35":-2.450342,"31":-3.7499118,"40":-5.7495856,"41":-3.7462673,"48":-1.2489603,"6":0.6865552,"14":0.49282742,"37":-7.440291,"19":-0.6730548,"20":0.8999076,"12":2.2032115,"16":0.7288872,"10":0.018206965,"29":-1.6469771,"36":-0.8425232,"42":-3.6253192,"22":-0.20344476,"1":2.8155265,"34":-7.0441017,"23":1.0008605,"43":-0.1113266},{"22":-0.6586558,"9":1.1314344,"32":-1.5680592,"36":-0.8533578,"31":-3.2067542,"38":-3.6722717,"12":-0.07685864,"14":-0.60288227,"17":-2.8837016,"2":3.3234475,"35":-1.5204799,"37":-3.209972,"42":-2.1913428,"43":-1.8419298,"45":1.2042474,"48":-1.9888375,"25":-3.2159877,"47":2.0907893,"29":6.7774034,"27":-3.1187475,"49":0.71196216,"15":-2.6433995,"7":0.7915288,"46":1.1011006,"39":0.1378324,"21":-0.5441158,"10":0.66657865,"33":1.123852,"3":0.7643628,"19":-1.1355412,"44":-7.3508873,"0":-1.8399782,"6":1.0705874,"24":-1.1593626,"4":1.0258007,"40":-1.1859785,"34":-1.0650387,"26":-0.62917054,"41":-4.253795,"1":0.69640076,"18":-0.5270852,"30":-0.5022922,"13":-1.9747204,"23":-0.078635395,"5":1.4788063,"11":-0.07876179,"16":0.013259391,"20":1.9411991,"28":-3.362252,"8":0.06666659},{"34":-2.7972891,"41":-3.0241163,"17":-0.72019404,"10":0.565201,"5":-0.89857703,"40":-3.9168696,"30":-1.582868,"25":0.9845649,"22":-1.398474,"8":2.672579,"20":0.1820744,"27":0.1380808,"21":-1.4320674,"23":-0.5520258,"15":0.3157118,"35":-4.4062576,"44":1.6953154,"9":1.5416327,"11":-0.58077353,"47":-4.6250205,"32":1.6194327,"42":-0.9026753,"2":1.6730347,"4":1.4957306,"26":-0.91637754,"6":0.4077366,"33":-2.1433148,"24":0.5288726,"19":2.8770792,"14":1.7169793,"13":0.2853578,"37":-7.774788,"38":1.1167654,"0":3.6629982,"16":-2.4991193,"43":-4.6798472,"45":0.02233224,"39":-1.5030068,"7":1.0117346,"46":-2.5338433,"36":-6.143666,"48":-0.8891219,"12":1.5098102,"18":-0.6569006,"28":-1.0404272,"29":-0.03862221,"31":-1.5303066,"49":0.32614166,"3":-3.1282127,"1":1.0279994},{"18":0.6604774,"47":0.8899954,"33":-3.7245018,"36":-1.0225995,"17":-1.1147861,"24":0.5374546,"38":-3.3574524,"3":1.7766193,"14":1.2199423,"21":1.243838,"23":-1.267468,"41":-8.298978,"29":-1.5435592,"31":-0.7093566,"46":-2.9374676,"15":0.2779922,"6":0.9636345,"5":1.4459374,"11":0.579142,"42":-1.097306,"27":-5.7303634,"30":-3.3576713,"0":-1.4678035,"26":-5.9164486,"34":0.45587367,"37":-0.10927023,"48":-1.8899235,"2":-0.18987153,"1":0.6175562,"10":1.7188747,"12":0.10841598,"13":1.3972888,"28":-1.3158249,"49":-1.3877184,"20":-1.2648284,"43":-1.663022,"8":-1.132695,"44":-3.0397713,"22":-1.488466,"25":-2.5360603,"19":-0.064900205,"32":-1.1007107,"35":-3.084701,"4":-0.17310643,"7":-1.6408389,"9":0.30429503,"16":2.8813558,"40":0.1800078,"45":-0.3830676,"39":-0.96258104},{"36":-3.3359516,"9":0.9330746,"49":-1.5461657,"11":-0.5726112,"45":-2.8377793,"15":0.54808795,"7":1.5244305,"10":-0.22020045,"28":-1.7956407,"2":-0.85950917,"0":1.7151455,"1":3.3772361,"30":-1.8674014,"32":-6.0219817,"43":-0.8482424,"44":-2.0775094,"39":-1.0879123,"14":1.617876,"25":-4.747404,"13":-0.36698598,"18":-0.20844777,"38":-5.3396997,"23":-0.6448526,"5":1.2135891,"8":-0.12349222,"37":-4.1154084,"26":1.6531429,"19":-0.19092938,"48":-1.8402226,"22":-1.0843995,"27":-5.412662,"29":0.4311072,"31":-2.2850564,"34":-1.8338486,"35":0.3665412,"12":0.55514324,"21":1.6548359,"40":0.92506677,"33":5.5298824,"6":1.9586585,"42":-0.5972514,"46":-1.542189,"4":0.31508017,"41":-0.90031564,"16":-0.9500306,"24":-6.0560417,"3":3.789816,"47":-3.0269158,"17":0.027847994,"20":-0.6700682},{"28":1.1288234,"40":0.87217796,"0":-0.7586061,"8":0.494485,"44":-2.2150588,"45":-0.386381,"38":-0.2505812,"26":0.5875476,"25":-1.6999061,"48":-2.9775357,"30":-1.5419779,"43":-4.88367,"32":-1.2074382,"15":-1.1331334,"22":-0.48761138,"1":-1.1029061,"19":-2.8695912,"36":-1.385388,"9":-0.5907652,"5":-0.2272294,"17":2.398828,"13":0.51119363,"39":-3.2340355,"31":-6.655467,"6":-0.0746934,"41":-0.62863064,"34":-0.9826952,"10":0.6478238,"16":-0.4877163,"27":-2.4787016,"35":-0.9645256,"3":1.4740309,"47":-0.10555184,"21":1.4830458,"33":-2.2505474,"49":0.3895468,"14":-0.94155276,"11":1.9035084,"18":0.483735,"2":0.3721424,"37":-2.1193151,"42":-3.030218,"23":3.4191055,"7":1.4082838,"12":-1.0382984,"24":-0.75468576,"4":1.7115856,"20":-0.5657972,"29":-2.110145,"46":-1.9120096},{"15":1.944478,"21":-0.2984876,"42":-2.9528816,"39":1.800387,"47":-1.646691,"49":-0.34259042,"22":1.7518902,"25":2.524334,"30":-4.8175273,"34":-5.4654436,"46":0.20593223,"31":-2.050412,"23":-0.11068597,"5":1.2099707,"14":0.4865474,"2":0.68227077,"43":-0.06186118,"24":-0.19395098,"26":2.034871,"1":1.9514068,"0":-2.6643283,"18":0.8866358,"10":1.1588438,"32":-3.457545,"16":0.97437125,"35":-5.945342,"44":-3.396225,"8":-1.2162848,"40":0.19354261,"7":2.798709,"27":-5.3135023,"36":1.8891814,"33":1.1082118,"19":-1.4409006,"12":1.138737,"28":0.17646646,"45":-9.295891,"48":-3.3867753,"3":1.3969146,"38":-5.801274,"4":0.4594646,"37":-1.4969332,"6":1.598965,"17":0.57453,"9":0.38121003,"11":0.3105362,"13":-0.016958024,"29":-1.0688138,"41":-3.969773,"20":-1.6625687},{"9":0.97805023,"16":1.1191095,"10":-1.1945508,"23":0.003369999,"0":-1.5999568,"25":2.8128219,"26":-2.0414555,"18":-0.1778976,"38":1.3205807,"47":-2.7215629,"32":-1.5231652,"49":-0.30859417,"42":-4.472599,"8":1.270441,"12":0.6815638,"36":-1.9087219,"46":2.3731933,"33":-4.327035,"39":-7.8875937,"21":-0.29515883,"20":1.0052465,"45":-3.2687156,"13":-2.9033473,"27":-4.476912,"3":-0.3934346,"14":-0.4230876,"44":-2.2067287,"41":-1.896862,"34":-8.244708,"37":0.901989,"30":-2.5450568,"2":-0.79439163,"6":0.1481972,"22":-0.2921804,"48":0.95617753,"5":1.8698848,"4":-1.7504936,"7":2.5125144,"28":-3.7448273,"19":0.83776075,"29":0.51791394,"31":-2.7646766,"40":-3.016816,"15":-0.7282958,"1":-2.3728797,"11":1.5873438,"17":-3.3093534,"24":0.5696704,"43":-2.8125026,"35":1.3244356},{"15":-0.25488853,"20":-0.48834497,"33":-1.0895845,"14":1.278381,"29":-5.3387976,"24":1.4251068,"21":-0.24026322,"2":-0.42692345,"7":1.3910904,"35":-0.82035637,"37":-1.7306406,"43":-6.320527,"44":-4.8869805,"45":-3.1586444,"19":1.3541219,"31":-6.04781,"6":-1.1386598,"28":-2.3460262,"46":-1.5687416,"48":-1.9219358,"49":0.9321574,"34":-3.0071368,"3":2.6741743,"42":-0.12092724,"47":-2.0108137,"5":-1.6729527,"25":0.4264628,"36":-1.7714208,"18":-0.1819288,"8":0.7693506,"9":1.5816145,"27":-3.9188354,"41":-3.4202054,"26":2.8988082,"12":1.1819172,"4":0.22990718,"38":-3.0631194,"16":0.17362687,"17":1.1310523,"23":-1.602157,"30":-1.034477,"32":2.242922,"0":-0.9159268,"39":-4.006467,"13":1.1536442,"40":-0.73043764,"22":2.8358412,"10":1.3282477,"1":1.5065397,"11":-3.0932324},{"38":-2.5680718,"14":1.4560716,"29":-3.4689415,"30":-2.5148644,"37":-6.302335,"41":-4.8315206,"19":0.3500399,"47":-0.548347,"5":0.3119769,"9":-0.423551,"0":1.6750437,"24":-2.5930886,"42":-0.3687028,"33":0.9139765,"17":-4.8899183,"31":-2.1750543,"36":0.49549618,"44":0.97260416,"11":0.6013934,"12":1.4031398,"40":-0.6134804,"10":1.4210254,"6":0.6365846,"13":0.0067045987,"15":2.107652,"8":-0.02119199,"16":0.36185378,"20":0.9270712,"21":-0.67956376,"1":0.99191236,"28":-3.1786149,"34":-0.160623,"18":4.133351,"35":0.9718288,"39":-2.6729126,"26":-1.9536797,"32":-4.0769935,"43":-2.8834677,"45":-3.72598,"46":-1.6148249,"48":-0.3796658,"7":0.83497447,"22":0.16604762,"23":-3.27048,"27":-0.33879238,"4":-0.033089064,"49":0.311697,"25":-3.1667867,"2":-1.9228675,"3":-0.31504256},{"35":-3.5243175,"40":-1.3703208,"47":-4.34122,"31":-1.9018475,"39":-9.854407,"6":0.34723464,"38":-3.2374332,"12":0.45909357,"49":1.1421105,"19":-0.6246812,"26":-4.347471,"33":-0.2909654,"8":-4.262568,"10":-0.1504522,"13":0.92017806,"36":0.2519614,"2":3.4330597,"42":-0.5678538,"43":0.43201742,"46":-6.489367,"48":-1.277086,"3":1.5350004,"11":1.1993188,"16":-0.7542814,"24":2.4536736,"23":0.7671008,"14":-1.6488779,"37":-6.230839,"44":-0.038455203,"4":1.4139125,"9":0.3742116,"7":-0.4297134,"28":-1.1396847,"45":-5.292748,"0":0.17727824,"17":0.32667857,"21":-0.747707,"27":-4.0839047,"41":-0.8616618,"25":-3.6654048,"32":-2.0008845,"5":1.7872808,"20":-1.6609504,"30":1.9633911,"34":2.1601627,"1":0.8120201,"22":1.5914758,"18":0.8138803,"29":-0.9236167,"15":0.1999308},{"33":-1.1205482,"42":-0.3568408,"44":-5.4842167,"18":-0.4991506,"29":-6.497651,"39":-0.4408508,"0":1.6255801,"5":-0.33866897,"34":-0.8651436,"21":-4.441754,"31":-3.93688,"26":-0.29017657,"14":-1.554593,"35":-0.0065032006,"37":0.9210024,"41":-2.7178075,"11":1.90499,"3":-0.3665378,"24":-0.18292336,"40":-4.4100804,"25":0.10754641,"8":1.6238363,"1":1.7215664,"9":-0.46882373,"10":1.2484143,"46":0.35080737,"20":-0.21955438,"32":-4.011467,"15":-0.3031666,"43":-0.17886767,"23":-2.527999,"4":0.19145255,"19":-1.3317778,"28":-0.7315844,"13":1.2637122,"27":0.067873016,"17":-0.2314182,"38":-4.2398543,"47":-2.0861669,"16":0.19074997,"6":-0.8019079,"36":-3.0603595,"22":0.11332059,"45":-1.9139893,"48":-4.86819,"49":-4.381362,"30":0.19002959,"7":0.92482007,"12":0.7591172,"2":-0.78119516},{"0":0.07573281,"1":-0.51525474,"11":-2.2619138,"27":-0.8619586,"16":-0.91446114,"21":-1.6303629,"30":-2.9187837,"39":-2.561172,"47":-0.7712024,"13":-0.8877617,"28":-5.8734403,"18":-1.3136339,"4":0.99469185,"37":-2.2994409,"45":-0.70429707,"23":0.59479326,"8":1.6799672,"36":-7.0039735,"26":-2.6348667,"35":-0.75352323,"20":-1.9679495,"32":-2.9398806,"9":-0.5643793,"12":-0.14451401,"15":-2.1569707,"5":0.5518644,"22":0.13247797,"40":-3.69274,"44":-2.8314805,"38":0.54267675,"6":1.4411135,"25":-2.5298598,"46":-2.0339694,"49":-2.0023599,"41":-0.31658298,"43":-7.0486283,"24":-0.4602314,"2":1.793318,"19":-1.3801826,"34":1.1256696,"10":0.22839019,"17":-1.8590304,"33":-4.5432596,"48":-6.701822,"14":0.009854799,"3":1.632971,"7":1.0404248,"29":-6.5853806,"31":-2.0828743,"42":-1.8138546},{"49":-6.757707,"40":-1.1292893,"0":0.5713294,"22":-1.0479006,"11":0.9841038,"24":-1.3268901,"27":-5.5573626,"43":-7.4976945,"2":1.9192886,"20":-1.3256521,"8":1.1106948,"29":-2.1459212,"10":1.0210966,"14":-0.277484,"34":-3.349294,"45":-3.5646331,"4":-0.26060295,"3":0.93326557,"44":-4.3833513,"5":0.40772098,"12":0.8045707,"17":-2.2520256,"23":0.12034259,"33":0.37488517,"39":-2.7319741,"15":-0.9907344,"30":-3.9259887,"32":-4.2665873,"35":-4.4173417,"47":-4.703204,"16":-0.6270258,"9":-1.5985333,"28":-1.9594017,"31":-1.7163975,"7":0.84538186,"13":0.046235003,"38":-4.0677934,"46":-1.5943807,"42":2.789826,"18":-0.47530442,"21":-2.5285869,"19":2.0338924,"6":1.6060946,"37":-2.0172658,"25":-0.046380807,"48":-2.2985606,"26":-2.3568342,"41":0.08112361,"36":-6.16838,"1":1.797365},{"37":-1.4304115,"21":-0.52033883,"45":-0.042861998,"36":-2.9040992,"30":-2.2107856,"29":-6.4635572,"3":1.1938145,"6":0.7663746,"24":2.6614363,"25":-4.108682,"31":0.07092409,"42":-1.513715,"17":-1.0665957,"26":-0.45318097,"34":-2.965707,"49":-2.8141303,"2":1.5740372,"40":-2.9386754,"46":-5.3229613,"47":-4.545804,"13":-3.7739463,"35":-3.5969536,"5":-0.43369442,"19":-0.027410597,"20":0.8128456,"7":0.2537802,"23":-0.8441516,"32":-2.9709396,"8":-2.3096359,"18":1.831557,"27":-6.999772,"41":-1.4876883,"43":-0.4713582,"9":0.47276178,"44":-0.6776245,"48":-4.1920123,"11":1.5427032,"12":6.3979793,"39":-3.7826912,"16":-1.2882276,"28":0.268861,"1":-0.22343893,"14":-2.0444407,"38":-4.46025,"15":0.038316537,"33":-1.6677793,"22":-2.8683054,"4":1.0132487,"10":1.5811306,"0":2.0965312},{"39":-1.9916598,"8":0.82139397,"4":-0.074226394,"21":-2.2298713,"35":-2.4928071,"44":-3.204987,"47":-5.725115,"15":-0.1635712,"20":-0.7012008,"28":-2.9107132,"3":-1.820108,"22":-0.338886,"31":0.63103104,"43":-4.008866,"46":-0.57759374,"38":-1.9465355,"10":0.6690342,"45":-2.8019192,"5":1.1833485,"11":1.1237419,"2":1.1560495,"26":-1.3252609,"25":-1.405263,"40":2.4006233,"36":-4.345249,"37":-6.311501,"41":-3.0459294,"48":-1.5928406,"1":-0.172332,"18":-0.9315017,"19":1.0583894,"0":2.823148,"27":-2.0228767,"7":-2.4231975,"32":-1.1156528,"49":-0.42193374,"17":0.1206646,"34":-4.4950075,"42":1.8763359,"13":-0.5032588,"6":2.3546627,"24":-2.7632918,"14":-1.0245461,"9":1.9460214,"12":-2.3633664,"16":-1.7197758,"23":-2.5543134,"29":-1.5893643,"30":-4.188504,"33":-3.0962613},{"36":-0.18478374,"42":-1.5540817,"29":-7.7798424,"47":0.30624396,"13":-0.42620045,"12":0.57017577,"1":-0.6129861,"16":-0.596876,"25":0.12901358,"23":-2.3385625,"31":-5.808885,"5":0.555488,"45":-6.5301576,"17":-0.95508516,"11":0.1731164,"26":-1.1566368,"14":0.32648858,"33":-3.4008038,"0":-0.3367958,"6":-2.8475065,"32":-3.7033966,"37":1.6442347,"7":1.5582616,"38":-3.522852,"40":-1.6138722,"41":-5.496302,"3":-1.2893279,"49":4.1518035,"39":0.33432153,"9":0.14616223,"21":4.864088,"43":0.39905006,"44":-5.1964397,"35":0.31146058,"2":0.5905143,"20":-2.3346632,"28":-0.7481426,"27":-0.93774664,"19":-2.005755,"34":-1.6427485,"30":-6.2885246,"10":0.24368978,"8":-0.06622221,"46":-3.4268932,"15":-4.8229866,"48":0.4666408,"4":-1.9318378,"22":-0.1114092,"24":1.0758584,"18":0.4650938},{"22":-3.5756817,"37":-9.071033,"10":-1.8858116,"34":2.7970462,"7":0.3158346,"12":4.501189,"5":1.2660772,"19":0.5042872,"18":0.48649222,"25":1.5755206,"27":-6.3972588,"17":-0.42274117,"29":-3.2006156,"36":1.1589952,"4":0.3002882,"23":1.5621816,"21":2.6864624,"0":-0.16294077,"26":-0.5033666,"39":-7.6897545,"40":-0.016058827,"24":-0.61354035,"8":-0.51865804,"13":-0.52019703,"38":-0.6581934,"14":0.42200416,"2":-2.3457806,"9":0.33820218,"3":1.3744695,"30":2.2592192,"16":-0.90820324,"32":-0.20193341,"42":-2.347648,"1":0.16103336,"33":-0.4909802,"6":3.1643548,"43":-3.1131873,"35":0.08277939,"41":-2.6790206,"44":1.16269,"11":-6.843913,"45":-4.1776648,"46":0.303608,"49":-0.54469836,"20":-2.58775,"28":3.2526505,"15":1.0949957,"47":0.471136,"48":-2.1694362,"31":-3.8798134},{"30":-3.0976996,"29":-0.11577072,"35":-7.354271,"39":-1.9747374,"43":-3.329221,"12":0.6476982,"1":0.92776096,"16":0.86639655,"22":-0.93201333,"10":-2.6646035,"11":2.5242467,"7":-0.445926,"23":0.39262843,"9":1.7748388,"17":0.5707578,"24":1.9917469,"31":-5.3366585,"42":-1.1999134,"0":0.24666736,"3":0.64872617,"21":1.7236627,"4":-0.9960572,"6":1.8076702,"2":0.65111303,"27":-0.7413453,"18":1.4938546,"34":-9.794947,"15":2.9434776,"26":-3.570221,"36":-2.976345,"37":-0.82631433,"19":0.85954446,"41":-2.9892304,"8":0.260788,"45":-0.795544,"14":-0.20265062,"47":-1.6965628,"48":-7.5674195,"46":1.4020077,"32":-0.7225974,"38":-2.3181195,"44":-3.9726825,"40":0.69793445,"49":4.098015,"25":-0.04452408,"20":0.98498106,"5":-0.24092159,"28":0.18877839,"13":1.3221974,"33":-7.76015}],"nn_shapes":{"34":[18,30,26,4,8],"30":[18,12,9,8],"40":[18,19,12,33,8],"44":[18,16,8],"39":[18,14,34,21,8],"26":[18,25,30,17,12,29,8],"37":[18,24,8],"24":[18,5,7,30,5,14,8],"11":[18,6,7,8],"0":[18,16,20,14,14,8],"2":[18,3,20,14,27,3,23,32,15,16,8],"6":[18,4,8],"1":[18,34,8],"10":[18,21,29,8],"3":[18,8,32,17,16,20,14,8],"22":[18,16,31,8],"28":[18,12,33,31,24,8],"32":[18,6,3,3,9,8],"42":[18,21,12,33,8],"20":[18,16,8],"12":[18,29,18,8],"27":[18,19,8],"17":[18,33,16,11,21,24,8],"45":[18,29,5,10,21,8],"41":[18,15,25,24,8],"48":[18,14,3,32,7,30,18,28,32,8],"38":[18,16,7,19,8],"5":[18,9,10,24,16,16,24,22,8],"18":[18,21,10,15,3,30,14,8,9,8],"29":[18,12,31,18,17,22,11,8],"25":[18,6,30,9,30,13,34,11,31,4,8],"16":[18,27,28,25,25,8],"23":[18,31,27,7,24,24,10,34,17,8],"35":[18,20,22,8],"43":[18,22,17,15,11,33,9,20,8],"13":[18,33,11,9,11,33,12,29,8],"31":[18,20,5,5,29,22,34,17,4,13,8],"49":[18,12,14,8],"19":[18,23,6,34,15,15,19,31,8],"8":[18,23,18,30,9,4,6,25,10,8],"14":[18,5,16,32,4,8],"36":[18,6,11,29,11,26,8],"33":[18,10,3,18,32,11,8],"46":[18,7,17,12,15,23,21,8,14,8],"21":[18,11,8],"47":[18,16,24,18,9,30,8],"7":[18,11,26,32,14,23,25,8],"4":[18,11,30,6,11,27,4,4,25,8],"15":[18,31,18,12,28,8],"9":[18,27,29,27,13,22,23,30,21,8]},"crossbreed_segments":17,"weight_initialization_range":{"start":-0.58231544,"end":0.9471909},"minor_mutation_rate":0.6085979,"major_mutation_rate":0.8314668,"mutation_weight_range":{"start":-0.3715943,"end":0.3715943}},"state":"Finish","generation":40,"max_generations":40,"id":"e9ccd9b0-ed91-4472-82f2-4a2eba9ec7cf"},"left":null,"right":null}},"right":{"val":{"node":{"id":"4927e1b5-284e-483d-8034-f3215653c381","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_4927e1b5-284e-483d-8034-f3215653c381","population_size":50,"generation":31,"scores":[{"12":-2.2077053,"3":0.45991325,"29":-5.7882757,"18":-2.2291424,"34":-3.1415505,"39":-0.7864648,"41":-3.9259582,"31":-8.159813,"30":-9.762412,"44":-1.0203212,"1":-6.7983437,"45":-6.35163,"49":-2.099032,"9":-7.843995,"46":-6.1997557,"16":-5.510003,"25":-2.2139595,"32":-6.1215925,"28":-6.6735907,"17":1.905855,"37":-6.420905,"15":-6.4148955,"26":-7.188308,"33":-5.85794,"7":-6.0783887,"5":-6.417207,"10":0.010562134,"2":-5.9887342,"21":-9.142227,"19":-6.406163,"4":-7.634589,"11":-6.9612417,"8":-5.8950186,"36":-5.302123,"38":-5.260914,"42":-6.4598823,"43":-4.6521482,"47":-9.834925,"35":-3.2873101,"48":-6.4925375,"6":-6.1462326,"27":-11.390952,"13":-4.002927,"23":-6.287218,"20":0.85533315,"0":-2.0039697,"40":1.309963,"14":-7.07207,"24":0.5197624,"22":-6.666387},{"11":-3.1593714,"23":-7.3299093,"1":-6.145644,"6":1.483731,"9":-0.6556816,"2":-4.148638,"25":-5.1679506,"30":-5.348699,"46":-7.037226,"49":-8.848057,"4":-3.0848193,"15":-2.467299,"39":-6.170186,"26":-0.5247086,"21":-10.35053,"14":-5.0187755,"33":-8.753212,"0":-1.8040926,"10":-2.776891,"18":-8.060572,"34":-9.008932,"16":-5.412995,"22":-6.774733,"20":-6.516478,"27":-14.3797865,"35":-6.627566,"36":-7.2936707,"13":-5.982255,"41":-5.6998496,"29":-5.5276694,"12":-4.0255694,"44":0.14863339,"32":-4.6895003,"40":-6.7866983,"47":-9.807707,"3":0.58976936,"8":-1.0932678,"28":-7.4047194,"31":-6.036427,"17":-6.1377964,"42":-4.0090632,"19":-1.7264202,"24":-8.958688,"37":-7.0282373,"43":-6.6490464,"38":-6.3970222,"45":-1.4937651,"7":4.220898,"48":-9.550586,"5":-2.8598814},{"33":-5.6023507,"13":-6.3158717,"39":-2.8065636,"42":-7.7015204,"47":-2.3057332,"16":-3.6309006,"2":0.5464354,"17":-4.1867523,"23":-4.0060606,"28":-8.803106,"41":-9.040792,"18":-9.425822,"48":-4.321704,"36":-11.423918,"34":-2.2816656,"44":-7.009552,"43":-4.4056306,"35":-6.7132483,"15":-6.161241,"10":-3.6888256,"46":-7.8272743,"7":-3.0122342,"6":-1.4529712,"49":-5.3853364,"27":-8.59745,"20":-7.1111917,"8":-1.6422218,"38":-5.9290366,"40":-3.7400718,"12":-1.9083523,"5":-3.9113204,"21":-8.91613,"9":0.6585968,"22":-1.135728,"25":-6.484613,"29":-8.474455,"45":-8.32863,"4":-2.2522066,"11":-7.930439,"14":-2.1474767,"0":-6.758498,"1":1.7913196,"26":-7.3978105,"3":-2.4625714,"19":-7.1945314,"31":-7.0496416,"30":-4.800157,"32":-8.060525,"24":-6.128545,"37":-5.7629156},{"10":-3.046687,"1":0.8607483,"8":-2.0910704,"24":-5.5820785,"42":-2.4317532,"37":-9.662027,"22":-4.7042913,"0":-1.5257607,"41":-8.399315,"48":-5.0573096,"21":-4.6769023,"18":-6.5455866,"4":-0.223496,"36":-6.822048,"35":-6.348737,"29":-5.338405,"7":0.19812974,"17":-1.858531,"26":-5.9917307,"6":-4.5832453,"40":-7.9213243,"38":-7.6650033,"14":-3.6837623,"28":-5.839343,"34":-8.584593,"39":-8.098098,"16":-0.16430959,"20":-4.863111,"43":-9.438566,"46":-2.0096812,"23":-10.821867,"25":-6.103114,"27":-7.6538863,"15":-6.214506,"3":-3.1899228,"2":-2.3405912,"47":-4.1823525,"13":-3.684972,"9":-4.205561,"19":-3.1162148,"45":-2.1388886,"12":-4.7768736,"44":-2.3882852,"11":-4.203885,"49":-11.360749,"33":-3.9829133,"5":-2.353114,"30":0.2969101,"31":-9.7553425,"32":-6.381272},{"35":-6.329638,"8":-8.48383,"3":-2.7779248,"31":-5.3982596,"4":-1.1286305,"10":-1.4911047,"11":-5.4523444,"14":-1.5719903,"34":-7.1248283,"0":-1.5292861,"41":-5.144696,"15":-3.0785363,"12":-4.135915,"2":2.068638,"44":-5.723578,"32":-7.1161447,"46":-5.656232,"1":-7.9339433,"13":-1.6012735,"16":-3.7929688,"17":-1.3662989,"28":-8.468862,"38":-6.283846,"7":-4.1859508,"6":2.258199,"39":-5.7517385,"40":-2.185653,"25":-6.7867517,"43":-5.6956687,"23":-6.5497603,"27":-6.7101393,"18":-5.6598907,"42":-8.010338,"22":-4.293061,"47":-8.410631,"29":-6.0448213,"30":-3.120536,"33":-6.678425,"36":-2.2476978,"9":-3.785134,"20":-6.76407,"21":-0.9748436,"24":-5.464995,"37":-8.838186,"45":-10.511658,"48":-6.5637045,"49":-8.30405,"26":-1.5648729,"19":-6.8301277,"5":-6.780622},{"13":-5.9658566,"36":-6.69612,"44":-4.1755066,"48":-7.27846,"23":-2.788705,"24":-6.7490454,"6":0.7435967,"31":-8.528669,"12":-3.128417,"25":0.61172104,"41":-11.587574,"42":-5.197815,"21":-6.3767424,"26":-2.4461863,"43":-4.697268,"3":-2.897878,"2":-6.833159,"22":-0.6180641,"7":-3.0073612,"28":-4.701036,"39":-2.9259324,"47":-8.755625,"16":-1.4458896,"19":-4.337165,"30":-1.7970674,"46":-6.1650977,"11":-4.2741604,"9":-2.1782713,"14":-5.538568,"10":-0.8069183,"1":-2.7422295,"29":-7.9439964,"33":-4.7999787,"38":-8.359238,"32":-6.887723,"5":-1.2106429,"18":-4.7042913,"37":-5.0650835,"20":-7.0292463,"40":-6.260282,"0":2.7349591,"4":-5.354793,"45":-8.416021,"15":-4.5302224,"49":-6.64499,"27":-7.81325,"8":-3.964267,"34":-11.757513,"17":-7.3064184,"35":-5.698499},{"33":-2.8844151,"0":-5.29483,"12":-3.0051084,"45":-7.5685225,"3":-6.2350845,"14":-6.2239347,"6":-2.8760228,"37":-8.236056,"47":-5.9538865,"5":0.445618,"27":-6.2923574,"1":0.9577133,"8":-7.049463,"9":-2.4823775,"15":-4.167392,"16":-5.8828115,"25":-6.781297,"36":-0.42601576,"41":-5.7433476,"46":-5.007496,"21":-7.180176,"48":-6.344386,"32":-7.031269,"22":-3.5685406,"20":-4.583305,"43":-4.1296554,"2":-4.326234,"26":-6.328514,"44":-10.1752825,"34":-6.8098245,"49":-7.051538,"30":-3.7744975,"31":-2.5379722,"13":-2.7742624,"17":-7.1861777,"24":-4.7720366,"4":-1.2100899,"11":-3.746964,"19":-2.7870185,"28":-8.136251,"10":-4.802005,"35":-7.066821,"38":-7.573728,"23":-6.519259,"42":-5.2036214,"7":-0.7041553,"39":-5.050607,"29":-4.8870606,"18":-15.415439,"40":-5.1404343},{"15":-8.91263,"1":0.21375242,"3":0.49288803,"43":-6.556487,"18":-4.627845,"32":-6.410241,"30":-3.4534926,"40":-9.319895,"23":-5.557311,"44":-7.9583273,"21":-5.3575063,"29":-1.4556402,"31":-8.192297,"49":-2.491073,"35":-3.2211766,"26":-5.3741684,"27":-9.978143,"19":-6.541009,"25":-3.8241258,"8":-4.257202,"38":-5.017821,"39":-6.1002455,"41":-5.269944,"28":-6.237544,"42":0.06850467,"46":-7.923688,"0":-0.084578946,"7":-3.8082633,"4":-4.165742,"14":-6.744937,"17":1.7393742,"5":-3.3279145,"24":-4.778824,"45":-7.3343086,"47":-3.5562463,"48":-7.8751755,"10":-4.039015,"16":-3.9520848,"12":-1.9155678,"13":-4.6556044,"11":-1.266288,"22":-0.11874382,"34":-6.661275,"37":-4.6740875,"9":-3.9724884,"36":-7.0592093,"20":-2.2873082,"2":-8.036842,"6":-0.9284045,"33":-5.0256705},{"16":-5.7060385,"17":-6.00199,"29":-9.993887,"13":-3.0642574,"30":-4.9186707,"4":-0.08997903,"31":-4.3743963,"26":-12.245878,"10":-8.57184,"7":-0.2084132,"34":-6.9803047,"35":-6.4062715,"44":-1.9192054,"39":-5.011348,"49":2.6927643,"9":-0.8585614,"12":-5.466088,"43":-10.287722,"22":-5.9940467,"8":-4.290485,"19":-3.3977306,"1":-0.95906943,"38":-7.672231,"32":-1.7153251,"23":-7.570333,"40":-7.549639,"18":0.73267543,"3":0.6381788,"6":-3.5349364,"20":-4.618871,"36":-12.99025,"11":-7.5268354,"15":-4.7133417,"21":-3.5082684,"5":-1.8957971,"37":-5.7457724,"33":-3.6420357,"41":-10.183071,"14":-3.272628,"25":-9.749201,"42":-0.19926815,"48":-4.6013193,"2":0.1687006,"27":-3.8905106,"28":-4.321185,"45":-6.652732,"46":-5.34919,"47":-4.9052267,"0":-3.8688831,"24":-2.6291459},{"37":-7.2748322,"6":-0.31089884,"23":-6.879588,"25":-7.101007,"32":-7.4528136,"43":-6.1593175,"35":-4.6540337,"41":-2.1827996,"18":-3.2506332,"20":-6.901155,"26":-8.36724,"10":3.0738454,"31":-5.2871537,"48":-12.69541,"34":-7.05717,"36":-2.7232203,"3":0.2693832,"2":-4.658323,"9":-5.308313,"47":-7.8141937,"15":-2.3651512,"49":-1.7504419,"11":-0.40183085,"13":-4.140607,"12":-5.6743426,"24":-6.4019914,"42":-10.019555,"45":-8.312067,"19":-2.8960106,"7":-4.1169744,"21":-5.5204325,"28":-4.099666,"1":-0.2089366,"38":-4.4818516,"0":-5.8983216,"39":-6.707189,"8":-0.7153311,"5":-3.0025651,"16":-5.835742,"30":-5.5074663,"22":-6.1405478,"33":-1.0401447,"4":1.815493,"44":-8.676687,"46":-4.735227,"27":-6.675126,"14":-6.8651285,"17":-0.71239364,"29":0.46844274,"40":-7.5029707},{"33":-5.9125648,"8":0.14899102,"37":0.38451424,"34":-7.3850946,"38":-4.199651,"6":-6.153229,"42":-8.322774,"43":-7.804656,"39":-6.839391,"28":-5.1167727,"44":-8.080609,"29":-6.9762588,"2":-4.595597,"47":-6.9096756,"31":-5.0568776,"14":-3.8473141,"18":-5.3494596,"5":-2.057659,"25":-2.453268,"15":-5.9120173,"12":-2.942243,"36":-10.233141,"19":-1.7612708,"3":0.3510024,"41":-5.2399035,"46":-7.0233965,"49":-9.244016,"48":-8.312361,"40":-3.9163368,"10":-6.2418394,"23":-2.8777976,"45":-4.2259398,"27":-6.2297773,"1":1.0182073,"7":-3.4470134,"4":-3.8573196,"0":-3.9372115,"17":-5.191558,"9":-5.7371383,"26":-3.0888908,"11":-4.635509,"30":-3.2773666,"16":-5.304137,"32":-8.943511,"22":-4.2112503,"35":-9.10764,"20":-1.55245,"21":-5.92438,"24":-8.879614,"13":-3.61291},{"9":-7.4169035,"17":1.3541992,"1":-5.735981,"4":-0.48756522,"2":-1.4803843,"18":-2.3469899,"28":-5.098632,"47":-3.6011894,"48":-3.8222148,"34":-7.241843,"16":-8.341502,"41":-8.041476,"23":1.0413781,"32":-2.9410741,"26":-0.1209062,"30":-5.6943645,"3":-0.5713614,"45":-7.103102,"5":-3.883152,"22":-4.065497,"33":-9.958631,"43":-5.6515026,"10":-7.9155474,"21":-5.0712223,"31":-3.8318057,"27":-7.7358246,"0":-0.15761042,"14":-6.9304824,"7":-6.235607,"36":-5.8479605,"19":-2.0947754,"8":-5.8903217,"15":-2.7406294,"38":-2.6919823,"11":-8.327294,"40":-8.012971,"24":-7.224681,"42":-6.7071037,"46":-2.4638524,"13":-3.7110162,"12":-4.488091,"44":-4.25462,"49":-4.6317053,"6":-2.0671265,"39":-13.899658,"37":-5.7786827,"35":-3.579789,"20":-1.8500891,"29":-3.5330734,"25":-7.45224},{"22":-4.7515526,"21":-3.1519268,"1":-3.0484269,"37":-7.3985634,"5":0.25969338,"16":0.095714174,"28":-2.7611527,"47":-7.3844004,"26":-8.852846,"32":-4.988953,"48":-11.587687,"49":-5.6892476,"35":-7.930368,"40":-9.40737,"2":-0.84102315,"24":4.085591,"43":-3.3806412,"4":-2.5573273,"39":-5.6632476,"3":-0.14983721,"12":-8.579244,"41":-15.314466,"42":-6.535417,"34":-9.491703,"44":-3.834795,"11":-6.328217,"6":-3.179299,"31":-11.512368,"30":-6.9321136,"45":-8.699331,"0":2.6831613,"13":-4.5044584,"20":-4.9255114,"27":-10.761828,"8":-1.9548645,"10":-10.137311,"9":-5.4584427,"19":-5.5328,"33":-6.4684677,"14":-2.0774312,"36":-3.4614434,"46":-5.018688,"29":-4.7117186,"25":-7.821883,"18":-4.051301,"15":-1.6228491,"23":-1.9468492,"7":-6.561249,"38":-4.413884,"17":-9.781391},{"3":-0.77211833,"40":-6.209918,"19":-3.5400372,"43":-9.216475,"7":-5.710351,"27":-7.268997,"25":-8.909723,"49":-3.6221306,"21":-6.638794,"23":-2.8979821,"1":-2.7847931,"6":-3.2586129,"13":-3.2376664,"22":-4.4467936,"18":-3.5210862,"28":-4.4665866,"9":-7.7249703,"30":-3.6593947,"32":-4.5404425,"16":-6.028268,"26":-2.0425534,"12":-7.073365,"34":-10.693955,"37":-10.274292,"42":-0.8407051,"24":-6.834813,"10":-4.782112,"47":-8.874941,"35":-6.6097536,"14":-2.5304096,"44":-4.281205,"17":-6.99986,"8":-0.87549794,"33":-7.5529037,"36":-6.5181284,"29":-8.490213,"45":-8.281681,"46":-3.2461705,"11":-6.6051865,"20":-3.9592204,"4":0.13969398,"0":-1.1905669,"2":0.1986072,"41":-3.5668838,"39":-7.3067665,"48":-7.1473937,"38":-7.6338105,"15":0.7119096,"31":-6.6137266,"5":0.055739008},{"32":-6.3817797,"34":-6.263388,"5":-5.4151177,"17":-7.683957,"44":-9.784686,"7":-2.7387128,"43":-6.668387,"37":-9.53776,"23":-8.877035,"19":-0.9805794,"42":-4.269587,"20":-2.563757,"3":-1.6871903,"45":-7.2974615,"10":-3.2716262,"48":-12.854228,"22":-4.3801355,"15":-4.2943907,"24":-7.2439013,"14":-5.44735,"38":-1.9346743,"13":-1.7601013,"21":-6.8980308,"39":-4.576972,"41":-8.118965,"4":-2.6488667,"31":-6.1606555,"12":-4.790626,"29":-9.496099,"25":-14.426544,"35":-3.1628685,"40":-3.1711364,"1":0.177584,"6":-1.8051761,"27":-7.1199737,"30":-5.4633884,"46":-12.516011,"49":-6.179161,"26":-9.616083,"2":0.8546661,"11":-4.01641,"18":-3.4379616,"16":1.071059,"36":-6.679106,"8":-5.407766,"0":-1.1821814,"9":-0.188424,"28":-3.1310668,"33":-6.7162066,"47":-2.7651396},{"40":-6.3169127,"31":-6.788479,"2":0.11499499,"35":-8.516233,"12":-6.0810227,"3":-2.1721585,"4":2.2971516,"6":-0.9608844,"14":-6.8593993,"39":-5.013154,"44":-5.6186895,"45":-3.0345654,"41":-5.0983863,"22":-4.5058136,"10":-3.3421524,"21":-2.1000023,"5":-1.8317201,"9":-5.251985,"13":-3.929404,"7":-3.594026,"20":-6.4318247,"16":-8.4494505,"28":-11.063459,"15":-2.9159875,"30":-7.4929,"32":-4.423497,"43":-15.614697,"25":-9.994823,"34":-6.2063565,"47":-5.8860264,"48":-2.2612045,"38":-2.8748927,"27":-9.8296585,"36":-6.7027864,"18":-1.9196243,"23":-5.6416984,"46":-8.678217,"29":-1.4928672,"37":-0.7193918,"1":0.70679003,"42":-5.7712545,"24":-4.043242,"8":-0.20630619,"26":1.2309443,"33":-4.078003,"0":-9.763197,"11":0.74552023,"17":1.6316736,"19":-2.982643,"49":-4.6068354},{"47":-5.7185183,"44":-6.57044,"19":-3.6985493,"48":-8.407866,"40":-6.994649,"25":-7.776094,"27":-8.951444,"22":-2.3760169,"4":0.92195046,"42":0.056731403,"2":1.9285071,"43":-8.157738,"6":-1.4142399,"7":-4.5393295,"46":-0.41131386,"5":-1.0734369,"14":-9.053869,"23":-6.9136496,"11":-3.4859402,"13":-1.2687516,"30":-8.026538,"39":-7.9194603,"32":-6.7739296,"17":-3.972007,"28":-8.560347,"26":-7.5500464,"37":-8.96202,"1":3.5935051,"20":0.85310334,"15":-4.5362573,"29":-5.6153116,"35":-3.5876281,"36":-6.90512,"16":-2.7031672,"38":-6.561864,"41":-7.4606094,"34":-7.788598,"21":-4.4619265,"31":-8.414707,"49":-0.711028,"3":-2.6411507,"8":-2.5088086,"0":1.2982078,"9":-5.219794,"18":-3.7880325,"45":-11.461676,"10":-7.058548,"24":-5.789211,"12":-0.19020978,"33":-5.491061},{"15":-3.9012065,"22":-3.449311,"12":-4.8677597,"36":-17.441645,"1":1.7412128,"23":-8.145149,"0":-1.1199046,"25":-11.89994,"39":-6.626439,"21":-4.9122534,"45":-1.5332215,"8":-2.5583434,"26":-2.8571725,"14":-3.7259727,"37":-4.853111,"41":-11.401272,"7":-4.843131,"32":-9.396578,"38":-1.0024046,"20":-4.2007637,"29":-5.2518992,"19":-2.8491623,"44":-2.795422,"9":-0.9532218,"10":-0.016619802,"13":-1.109414,"48":-13.301729,"42":-6.7707214,"11":-3.798603,"43":-3.0779607,"17":-9.384002,"31":-4.7559156,"46":-6.406909,"49":-4.5331526,"6":-1.6094253,"4":-6.000406,"3":0.78815204,"30":-2.39151,"34":-2.7504447,"33":-8.972921,"28":-9.452806,"40":-6.2907686,"16":-1.9519722,"18":-1.2361414,"24":-3.1618586,"47":-1.93146,"35":-2.5651517,"27":-11.535366,"2":-2.827455,"5":-0.89086163},{"28":-6.168729,"23":-5.091327,"36":-7.896076,"4":-0.6808048,"43":-4.0872064,"15":-5.060832,"35":-7.8679466,"16":2.3580716,"11":-6.402948,"22":-0.21221161,"2":-0.45511517,"5":-7.220313,"14":-1.7785864,"21":-5.768071,"12":-5.537334,"18":-2.9126792,"37":-3.1373315,"39":-7.484565,"1":0.83852994,"26":-6.7451324,"19":-5.0899377,"9":-1.2609146,"13":1.1598383,"42":-1.7430694,"44":-7.794197,"41":-5.3883505,"47":-5.5544233,"48":-9.115694,"30":-5.866732,"10":-5.222727,"17":-7.704146,"40":-7.9548197,"29":-2.7511039,"8":-1.2438145,"20":-3.4886322,"25":-2.9154382,"33":-5.1570845,"24":-1.6546043,"27":-6.740902,"0":-0.38604698,"6":-0.3249498,"7":0.011517334,"31":-9.004103,"34":-5.4399896,"38":-1.5282686,"3":-0.023884201,"45":-7.5850234,"32":-10.357863,"46":-9.2418375,"49":-3.7870064},{"35":-4.0093746,"37":-1.2684741,"14":-0.6104725,"0":0.69505835,"6":-1.7735542,"34":-4.3040996,"27":-7.9827676,"25":-8.473538,"41":-6.092523,"18":-4.8728914,"3":-1.9708881,"39":0.16423199,"8":0.1419992,"24":-9.447664,"22":-2.5927353,"29":-5.897491,"19":-2.1336436,"16":-3.8984675,"4":-2.0244925,"1":-3.7751756,"21":-4.7222166,"28":-5.464208,"31":-8.301497,"2":1.7633228,"9":-0.016923595,"32":-11.56977,"11":2.1795933,"36":-5.280381,"15":-1.1858556,"40":0.44014746,"7":0.61527395,"42":-7.37359,"44":-0.9581,"47":-0.77596426,"12":-5.7259784,"20":-2.3802009,"13":-5.6673765,"43":-6.2162867,"45":-5.83232,"48":-0.96138537,"46":-7.8550553,"30":-1.3662884,"49":-4.963779,"26":-6.8248696,"38":-4.165621,"17":-0.33950955,"5":-3.2327838,"10":-2.0035245,"33":-7.269362,"23":-5.149654},{"15":-5.8778105,"31":-7.4263864,"22":-3.0823693,"12":-1.0283643,"3":1.2516215,"24":-4.5884557,"32":-9.882088,"21":-4.9306097,"36":-2.0598521,"41":-6.314498,"49":-6.514525,"16":-0.2281086,"29":-3.9958122,"44":0.04656654,"46":-4.6025896,"6":-1.5386164,"4":3.721232,"48":-2.793364,"39":-3.4709504,"43":-19.224146,"19":-1.3619962,"27":-8.529882,"9":-3.5389438,"5":1.3677812,"23":-1.4420968,"0":1.6338917,"20":-2.5260792,"8":-3.8938897,"34":-6.3907065,"28":-2.9147327,"35":-3.1052494,"2":-1.0358795,"17":-0.9774192,"18":0.70798683,"37":-6.218275,"33":-8.12211,"11":-2.79563,"1":0.8589767,"10":3.093267,"13":-2.6534617,"7":0.18166378,"42":-1.0995693,"14":-2.525447,"45":-8.643217,"30":-14.979738,"47":-8.130171,"26":-2.81726,"38":-4.3409705,"40":-2.6157117,"25":-6.353838},{"44":-8.50006,"6":-2.0926635,"43":-6.241271,"9":-0.8451432,"7":-1.6807859,"12":0.50125086,"18":-1.5310643,"29":-3.816148,"41":-4.897126,"45":-6.16201,"16":-0.020999575,"32":-4.2065463,"8":-6.858557,"46":-7.230836,"2":3.0744202,"19":-1.6173872,"47":-8.711152,"48":0.5274326,"25":-6.9953904,"37":1.6969225,"49":-0.6539704,"14":-1.7808943,"4":1.4781072,"33":4.1129317,"10":-3.3978896,"36":-6.433152,"26":-4.2478914,"22":-3.3536906,"5":-0.050629735,"24":-2.4221032,"39":-2.843961,"38":-10.483934,"11":-2.0277932,"15":-2.5512938,"0":2.7106996,"17":-4.07591,"13":-4.117734,"3":-0.9371809,"20":-0.49810869,"42":3.0832992,"21":-0.86282796,"1":-3.4048321,"28":-8.772857,"30":-3.2485013,"27":0.45775598,"31":-6.5474176,"34":-7.441789,"35":-4.0919113,"40":-5.383832,"23":-4.906007},{"23":2.2807653,"18":-1.9577965,"43":-8.174139,"44":-7.386832,"28":-4.351672,"39":-7.900308,"0":0.5285856,"10":0.37729222,"31":-0.19897583,"33":-4.6669607,"27":-2.6046462,"20":-1.2066267,"14":-1.1362668,"11":1.0077413,"41":-0.478535,"6":-0.4414224,"45":-8.069284,"1":-9.255552,"5":1.3562931,"22":-1.4410591,"25":-3.4098713,"37":-5.671054,"16":-2.4818156,"40":-4.346452,"30":0.6393293,"17":-1.4314704,"35":1.8644912,"4":2.1321304,"7":-0.096214294,"34":-5.767625,"36":-5.2387476,"49":-6.179792,"32":-9.304794,"46":-7.2231016,"47":-5.767058,"8":-1.7443111,"26":-8.583871,"24":-0.46072045,"2":-4.3851767,"21":0.3429398,"48":-9.851889,"29":1.0060906,"42":-5.6073976,"19":-0.9780343,"12":-2.981559,"15":3.8326268,"38":-6.608889,"9":0.299455,"13":-1.1476824,"3":2.5979218},{"19":-0.19572818,"5":0.43866682,"17":-2.1176047,"24":2.0992517,"35":-9.157915,"40":-10.009754,"41":-11.370805,"16":-10.49123,"30":-10.726629,"38":-4.4614334,"43":-1.5596721,"25":-0.35049,"29":-8.028803,"49":-5.2757196,"15":-1.3581061,"1":3.8374572,"21":-0.5448636,"28":-8.341843,"13":2.959203,"34":-7.3304453,"20":-1.969656,"4":-1.0717344,"47":-6.8036637,"48":-0.35139427,"2":-0.6767151,"8":-2.959065,"7":-7.9132996,"6":0.13814719,"11":-2.8848605,"22":-2.424499,"3":-0.5579618,"23":-4.5508432,"27":-0.4200186,"31":-0.25811654,"36":0.88451403,"42":-5.2860374,"44":-3.2424133,"45":-8.194428,"0":1.2812722,"46":-3.5587463,"26":-9.993482,"12":0.2769742,"33":-2.4051697,"14":-0.9771984,"18":-1.098754,"10":0.6583066,"9":-4.469278,"32":-8.635898,"37":-5.788775,"39":-6.285392},{"37":-5.6931424,"45":-4.403555,"15":-3.099538,"39":-0.75549346,"13":2.159759,"8":-1.9632168,"21":-1.3247403,"23":-1.3566242,"36":-3.1095176,"46":-6.386506,"32":-9.831261,"7":-1.5145661,"2":5.472353,"5":1.9507589,"22":-0.053236198,"40":-7.7109632,"30":0.065202594,"6":1.5998818,"17":-0.4691194,"1":-3.1568475,"34":-1.6445547,"27":-7.7280273,"49":-5.792971,"38":-1.2645669,"31":-3.0119731,"0":0.3504137,"14":-5.790829,"26":-4.0739875,"18":0.65096486,"24":-8.489643,"3":0.9707786,"4":0.978186,"48":-3.417251,"12":0.32214004,"20":0.5484868,"47":-3.042462,"9":-1.8674599,"16":-0.42284623,"29":-6.055676,"41":-5.4693413,"25":-9.707097,"28":-1.635248,"33":-1.3564689,"35":-11.695998,"43":-10.47551,"10":-8.123158,"19":-1.3940105,"44":-8.574059,"42":-4.169252,"11":-0.8294384},{"20":-0.16003199,"11":-1.9874451,"40":-10.189647,"41":-3.565167,"22":-0.82479924,"7":0.52353704,"32":-5.0426292,"43":-7.950408,"45":-10.119469,"35":-2.0043066,"47":-4.7557993,"49":-7.0734396,"19":-0.30181038,"13":-1.2902004,"46":-6.726818,"37":-5.9003277,"2":-0.6885988,"5":0.18776599,"36":-6.162833,"34":-6.932719,"31":-0.84623444,"39":-6.634928,"42":-5.322622,"48":-0.59931934,"3":1.7159679,"21":0.25509998,"15":0.9164914,"44":-8.222937,"1":1.8034108,"27":-7.7412057,"17":-3.1398792,"18":-0.525844,"29":-7.2635217,"6":0.54169834,"9":-2.429277,"25":-2.3472564,"38":-1.3054307,"14":-2.864987,"4":-3.8559635,"12":-1.8887336,"33":-6.1447196,"10":-3.1041374,"16":-5.70136,"0":3.6604676,"8":2.5261884,"23":-5.0369844,"28":-5.528515,"26":-6.893248,"24":-0.46608344,"30":-3.9200988},{"0":2.799172,"3":0.7874632,"14":-1.7181822,"24":-3.4304478,"35":-6.7193117,"13":0.41537824,"5":2.7171228,"37":-5.027726,"43":-5.43523,"4":-0.09500298,"46":-8.622667,"48":-7.9176216,"30":-4.2853765,"6":0.0148634,"18":-4.461198,"31":-8.836862,"7":0.3118286,"28":-8.084752,"17":-1.2637011,"34":-4.806041,"36":-5.812302,"38":-4.879042,"25":4.325031,"2":-0.20646039,"29":-4.230074,"39":-5.7430944,"23":-0.4728716,"41":-5.4831743,"40":-4.317229,"45":1.2612218,"49":-7.160132,"26":-1.6063076,"16":-2.7028515,"1":2.9316213,"8":0.36154866,"47":-7.843116,"11":-0.6635078,"33":-2.0000467,"44":-1.480444,"20":-0.8691734,"32":-6.462825,"9":-1.0986978,"15":-3.1273415,"12":-4.778491,"21":-3.4896972,"42":-6.302517,"10":-0.07799481,"22":2.573429,"19":1.4512104,"27":-7.1704316},{"29":-6.2978754,"22":1.962752,"47":-7.610662,"2":-0.04739348,"4":-1.0464453,"34":-2.2990997,"44":-7.0319357,"12":-1.4976182,"48":-4.687726,"13":0.694455,"19":-1.9763286,"5":-1.454668,"20":-0.064576246,"36":-0.31257114,"1":3.1782584,"42":-0.40251812,"0":0.46174207,"37":-6.867858,"21":-5.765976,"27":-1.6544834,"28":-0.519924,"33":-7.456163,"6":-8.124794,"10":-1.316211,"38":-9.13648,"43":-1.5818509,"46":-4.074041,"9":2.019923,"18":-0.10317061,"40":1.1408619,"45":-6.3800116,"23":1.9282601,"15":2.204761,"41":-2.7825036,"14":-0.92068326,"32":-5.854055,"16":-3.6365674,"26":-3.366635,"3":1.507069,"30":-0.49880537,"25":-3.7281852,"35":-5.303521,"8":-4.913729,"17":0.055183005,"11":0.1572492,"49":-7.636659,"7":1.6844584,"24":3.5923011,"31":-4.1475525,"39":-6.168802},{"17":-0.9454006,"48":-4.6368685,"13":-1.3639282,"5":-4.1024647,"12":-1.2815914,"10":-0.95624274,"7":0.11633961,"23":0.31117058,"18":-4.709592,"22":-0.5083992,"25":-7.252188,"26":-3.5911686,"24":-0.7936962,"31":-1.8184278,"42":-6.4817,"28":-2.8908885,"30":-13.66734,"4":-1.0620741,"29":-7.0017653,"44":-10.846217,"11":0.24425921,"35":-7.5436096,"16":-2.0619254,"20":1.7944053,"15":-0.264994,"38":-0.23960027,"33":-6.2392874,"39":-6.2364187,"14":-5.3246827,"21":-1.2647974,"34":-3.8605335,"37":-0.5008541,"49":0.6760356,"45":-7.144435,"0":-8.659049,"32":-7.71543,"2":-1.4090497,"6":0.37938777,"43":-5.59842,"19":-1.180762,"47":-8.232537,"41":-5.0816107,"3":-0.072279006,"46":-5.0089836,"1":-2.5223753,"8":1.2994208,"40":-9.043745,"27":-2.782248,"9":0.49734837,"36":-2.8629117},{"10":-1.1431856,"19":-0.00888381,"21":-0.9491043,"28":-5.6273203,"5":-8.733055,"43":-4.9717584,"4":1.3330718,"3":-1.8273941,"9":-5.1436996,"2":1.0189362,"12":-0.50710356,"32":-2.2215023,"7":-2.288145,"26":-4.1429253,"30":-1.6915871,"35":-8.091185,"34":-5.6282473,"38":-3.1990018,"41":-7.9956346,"22":-3.1670842,"15":-1.9314263,"17":-0.918849,"45":-6.80713,"47":-0.2870374,"1":1.18836,"23":-3.831369,"14":-0.6701314,"13":-1.408299,"18":2.3839207,"42":-4.5490522,"48":-5.086974,"39":-4.9896884,"6":0.5895823,"44":-0.9529087,"20":-0.91898996,"24":4.123849,"31":-7.2199583,"16":1.8607514,"40":-1.249728,"33":-6.3128176,"8":1.8995619,"11":-1.2937064,"49":-7.038812,"36":-1.8019352,"0":1.9573166,"27":-10.852641,"29":-5.5810843,"46":-4.0519304,"37":-7.403051,"25":-3.7937102},{"27":-5.495222,"4":0.6431998,"18":-2.87314,"7":-3.9599311,"49":-0.1454852,"34":-6.70123,"42":-1.1761116,"39":-5.4738607,"23":-0.20574598,"1":0.42282137,"35":-2.1898088,"12":-1.7741026,"38":2.4154522,"2":0.31656614,"19":-0.423431,"29":-3.7359993,"46":-2.1432633,"26":-8.270744,"47":-6.977235,"48":-5.900238,"41":-1.0820382,"25":-8.489737,"20":-2.6018925,"44":-6.538512,"15":-4.125592,"16":-4.223521,"30":1.3424636,"10":-0.5507144,"9":-0.25193802,"17":-1.9685625,"31":-5.058756,"36":-2.2006512,"33":-6.9902534,"0":3.968801,"37":-8.3227625,"14":5.2644496,"11":0.028243404,"3":1.0192555,"5":-0.593826,"8":-0.903951,"40":-1.7970212,"43":-5.4003954,"45":-0.7839618,"13":-2.4410012,"24":-4.9908996,"32":0.188975,"6":-0.4564694,"21":-8.233892,"22":-6.377118,"28":-8.211889},{}],"nn_shapes":{"30":[18,12,8],"45":[18,11,11,6,21,23,14,26,31,18,8],"49":[18,31,14,31,25,16,6,8],"46":[18,27,33,8],"0":[18,10,33,4,4,25,21,8,5,8],"17":[18,16,22,25,19,5,8],"41":[18,7,8],"34":[18,16,23,27,15,34,28,26,6,8],"2":[18,9,15,11,20,27,23,16,13,10,8],"21":[18,20,26,11,13,8],"37":[18,32,14,30,28,13,23,8],"40":[18,14,14,5,8,9,11,30,17,10,8],"47":[18,7,26,19,18,17,33,8],"9":[18,4,7,7,18,8],"12":[18,12,19,20,7,18,8],"16":[18,22,30,31,33,10,26,8],"3":[18,34,24,20,31,3,26,21,21,8],"38":[18,22,17,8],"20":[18,6,7,18,8],"25":[18,14,25,16,20,22,28,10,8],"22":[18,21,25,14,6,21,15,3,23,8],"15":[18,14,33,27,11,8],"18":[18,10,7,16,20,3,18,8],"1":[18,30,8],"35":[18,22,22,6,15,7,6,8],"44":[18,19,32,4,6,8],"28":[18,29,5,27,14,9,3,5,26,8,8],"36":[18,13,8],"42":[18,20,23,32,20,16,3,9,28,12,8],"11":[18,18,26,22,32,7,31,6,17,26,8],"8":[18,21,24,14,9,30,14,33,8],"7":[18,30,31,9,12,14,6,30,8],"19":[18,5,29,19,27,7,32,8],"27":[18,5,5,26,32,16,7,17,20,8],"31":[18,17,33,30,9,7,20,26,8],"24":[18,9,8],"32":[18,15,25,5,34,14,11,14,19,8],"5":[18,9,13,20,26,31,19,8],"13":[18,29,22,31,20,29,6,8],"6":[18,16,7,28,13,27,18,8,33,15,8],"14":[18,19,8],"23":[18,11,25,11,9,32,8],"26":[18,18,24,19,24,8],"29":[18,10,26,24,4,27,27,22,5,4,8],"33":[18,6,11,27,12,8],"39":[18,17,16,4,34,22,8],"43":[18,9,11,8],"4":[18,10,23,12,33,22,8],"48":[18,31,28,8,14,17,17,8],"10":[18,4,26,34,29,8,11,31,7,28,8]},"crossbreed_segments":19,"weight_initialization_range":{"start":-0.57954955,"end":1.4462243},"minor_mutation_rate":0.78990555,"major_mutation_rate":0.9755472,"mutation_weight_range":{"start":-0.52652776,"end":0.52652776}},"state":"Simulate","generation":32,"max_generations":45,"id":"4927e1b5-284e-483d-8034-f3215653c381"},"left":null,"right":null}},{"generations_per_height":5,"overwrite":false},[50,1]] \ No newline at end of file diff --git a/gemla/src/bin/fighter_nn/fighter_context.rs b/gemla/src/bin/fighter_nn/fighter_context.rs index 4b2a90b..56c328f 100644 --- a/gemla/src/bin/fighter_nn/fighter_context.rs +++ b/gemla/src/bin/fighter_nn/fighter_context.rs @@ -45,11 +45,12 @@ impl Serialize for FighterContext { // Custom deserialization to reconstruct the FighterContext from a concurrency limit. impl<'de> Deserialize<'de> for FighterContext { - fn deserialize(_: D) -> Result + fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { // Deserialize the tuple + let (_, _) = <(usize, usize)>::deserialize(deserializer)?; Ok(FighterContext { shared_semaphore: Arc::new(Semaphore::new(SHARED_SEMAPHORE_CONCURRENCY_LIMIT)), visible_simulations: Arc::new(Semaphore::new(VISIBLE_SIMULATIONS_CONCURRENCY_LIMIT)), diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index 9d642a9..b52deec 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -103,7 +103,9 @@ impl GeneticNode for FighterNN { // Create the first generation in this folder for i in 0..POPULATION { // Filenames are stored in the format of "xxxxxx_fighter_nn_0.net", "xxxxxx_fighter_nn_1.net", etc. Where xxxxxx is the folder name - let nn = gen_folder.join(format!("{:06}_fighter_nn_{}.net", context.id, i)); + let nn = gen_folder + .join(format!("{:06}_fighter_nn_{}", context.id, i)) + .with_extension("net"); // Randomly generate a neural network shape based on constants let hidden_layers = thread_rng() @@ -168,7 +170,8 @@ impl GeneticNode for FighterNN { let nn = self_clone .folder .join(format!("{}", self_clone.generation)) - .join(self_clone.get_individual_id(i as u64)); + .join(self_clone.get_individual_id(i as u64)) + .with_extension("net"); let mut simulations = Vec::new(); // Using the same original nn, repeat the simulation with 5 random nn's from the current generation concurrently @@ -181,7 +184,8 @@ impl GeneticNode for FighterNN { let random_nn = folder .join(format!("{}", generation)) - .join(self_clone.get_individual_id(random_nn_index as u64)); + .join(self_clone.get_individual_id(random_nn_index as u64)) + .with_extension("net"); let nn_clone = nn.clone(); // Clone the path to use in the async block let future = async move { @@ -250,6 +254,7 @@ impl GeneticNode for FighterNN { async fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { let survivor_count = (self.population_size as f32 * SURVIVAL_RATE) as usize; + let mut nn_sizes = Vec::new(); // Create the new generation folder let new_gen_folder = self.folder.join(format!("{}", self.generation + 1)); @@ -262,11 +267,10 @@ impl GeneticNode for FighterNN { // Remove the 5 nn's with the lowest scores let mut sorted_scores: Vec<_> = self.scores[self.generation as usize].iter().collect(); - sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); - let to_keep = sorted_scores[survivor_count..] - .iter() - .map(|(k, _)| *k) - .collect::>(); + sorted_scores.sort_by(|a, b| b.1.partial_cmp(a.1).unwrap()); + let scores_to_keep: Vec<&(&u64, &f32)> = + sorted_scores.iter().take(survivor_count).collect(); + let to_keep = scores_to_keep.iter().map(|(k, _)| *k).collect::>(); // Save the remaining 5 nn's to the new generation folder for (i, nn_id) in to_keep.iter().enumerate().take(survivor_count) { @@ -275,58 +279,94 @@ impl GeneticNode for FighterNN { .join(format!("{}", self.generation)) .join(format!("{:06}_fighter_nn_{}.net", self.id, nn_id)); let new_nn = new_gen_folder.join(format!("{:06}_fighter_nn_{}.net", self.id, i)); + debug!("Copying nn from {:?} to {:?}", nn_id, i); fs::copy(&nn, &new_nn)?; + nn_sizes.push(self.nn_shapes.get(nn_id).unwrap().clone()); } - // Take the remaining 5 nn's and create 5 new nn's by the following: + let weights: HashMap = scores_to_keep.iter().map(|(k, v)| (**k, **v)).collect(); + + debug!("scores: {:?}", scores_to_keep); + + let mut tasks = Vec::new(); + + // Take the remaining nn's and create new nn's by the following: for i in 0..survivor_count { - let nn_id = to_keep[i]; - let nn = self + let self_clone = self.clone(); + + // randomly select individual id's sorted scores proportional to their score + let nn_id = weighted_random_selection(&weights); + let nn = self_clone .folder - .join(format!("{}", self.generation)) - .join(format!("{:06}_fighter_nn_{}.net", self.id, nn_id)); - let fann = Fann::from_file(&nn).with_context(|| "Failed to load nn")?; + .join(format!("{}", self_clone.generation)) + .join(self_clone.get_individual_id(nn_id)) + .with_extension("net"); // Load another nn from the current generation and cross breed it with the current nn - let cross_nn = self - .folder - .join(format!("{}", self.generation)) - .join(format!( - "{:06}_fighter_nn_{}.net", - self.id, - to_keep[thread_rng().gen_range(0..survivor_count)] - )); - let cross_fann = - Fann::from_file(&cross_nn).with_context(|| "Failed to load cross nn")?; - - let mut new_fann = crossbreed(self, &fann, &cross_fann, self.crossbreed_segments)?; - - // For each weight in the 5 new nn's there is a 20% chance of a minor mutation (a random number between -0.1 and 0.1 is added to the weight) - // And a 5% chance of a major mutation a new neuron is randomly added to a hidden layer - let mut connections = new_fann.get_connections(); // Vector of connections - for c in &mut connections { - if thread_rng().gen_range(0.0..1.0) < self.minor_mutation_rate { - trace!("Minor mutation on connection {:?}", c); - c.weight += thread_rng().gen_range(self.weight_initialization_range.clone()); - trace!("New weight: {}", c.weight); + let cross_id = loop { + let cross_id = weighted_random_selection(&weights); + if cross_id != nn_id { + break cross_id; } - } + }; - new_fann.set_connections(&connections); + let cross_nn = self_clone + .folder + .join(format!("{}", self_clone.generation)) + .join(self_clone.get_individual_id(cross_id)) + .with_extension("net"); - if thread_rng().gen_range(0.0..1.0) < self.major_mutation_rate { - new_fann = major_mutation(&new_fann, self.weight_initialization_range.clone())?; - } + let new_gen_folder = new_gen_folder.clone(); - // Save the new nn's to the new generation folder - let new_nn = new_gen_folder.join(format!( - "{:06}_fighter_nn_{}.net", - self.id, - i + survivor_count - )); - new_fann - .save(&new_nn) - .with_context(|| "Failed to save nn")?; + let future = tokio::task::spawn_blocking(move || -> Result, Error> { + let fann = Fann::from_file(&nn).with_context(|| "Failed to load nn")?; + let cross_fann = + Fann::from_file(&cross_nn).with_context(|| "Failed to load cross nn")?; + + let mut new_fann = crossbreed( + &self_clone, + &fann, + &cross_fann, + self_clone.crossbreed_segments, + )?; + + // For each weight in the 5 new nn's there is a 20% chance of a minor mutation (a random number between -0.1 and 0.1 is added to the weight) + // And a 5% chance of a major mutation a new neuron is randomly added to a hidden layer + let mut connections = new_fann.get_connections(); // Vector of connections + for c in &mut connections { + if thread_rng().gen_range(0.0..1.0) < self_clone.minor_mutation_rate { + trace!("Minor mutation on connection {:?}", c); + c.weight += + thread_rng().gen_range(self_clone.weight_initialization_range.clone()); + trace!("New weight: {}", c.weight); + } + } + + new_fann.set_connections(&connections); + + if thread_rng().gen_range(0.0..1.0) < self_clone.major_mutation_rate { + new_fann = + major_mutation(&new_fann, self_clone.weight_initialization_range.clone())?; + } + + let new_nn = new_gen_folder + .join(self_clone.get_individual_id((i + survivor_count) as u64)) + .with_extension("net"); + new_fann + .save(&new_nn) + .with_context(|| "Failed to save nn")?; + + Ok::, Error>(new_fann.get_layer_sizes()) + }); + + tasks.push(future); + } + + let results = join_all(tasks).await; + + for result in results.into_iter() { + let new_size = result.with_context(|| "Failed to create new nn")??; + nn_sizes.push(new_size); } self.generation += 1; @@ -351,7 +391,7 @@ impl GeneticNode for FighterNN { let get_highest_scores = |fighter: &FighterNN| -> Vec<(u64, f32)> { let mut sorted_scores: Vec<_> = fighter.scores[fighter.generation as usize].iter().collect(); - sorted_scores.sort_by(|a, b| a.1.partial_cmp(b.1).unwrap()); + sorted_scores.sort_by(|a, b| b.1.partial_cmp(a.1).unwrap()); sorted_scores .iter() .take(fighter.population_size / 2) @@ -367,18 +407,25 @@ impl GeneticNode for FighterNN { let mut simulations = Vec::new(); - for _ in 0..max(left.population_size, right.population_size) * SIMULATION_ROUNDS { - let left_nn_id = left_scores[thread_rng().gen_range(0..left_scores.len())].0; - let right_nn_id = right_scores[thread_rng().gen_range(0..right_scores.len())].0; + let left_weights: HashMap = left_scores.iter().map(|(k, v)| (*k, *v)).collect(); + let right_weights: HashMap = right_scores.iter().map(|(k, v)| (*k, *v)).collect(); + + let num_simulations = max(left.population_size, right.population_size) * SIMULATION_ROUNDS; + + for _ in 0..num_simulations { + let left_nn_id = weighted_random_selection(&left_weights); + let right_nn_id = weighted_random_selection(&right_weights); let left_nn_path = left .folder .join(left.generation.to_string()) - .join(left.get_individual_id(left_nn_id)); + .join(left.get_individual_id(left_nn_id)) + .with_extension("net"); let right_nn_path = right .folder .join(right.generation.to_string()) - .join(right.get_individual_id(right_nn_id)); + .join(right.get_individual_id(right_nn_id)) + .with_extension("net"); let semaphore_clone = gemla_context.shared_semaphore.clone(); let display_simulation_semaphore = gemla_context.visible_simulations.clone(); @@ -414,8 +461,8 @@ impl GeneticNode for FighterNN { join_all(simulations).await.into_iter().collect(); let scores = results?; - let total_left_score = scores.iter().map(|(l, _)| l).sum::(); - let total_right_score = scores.iter().map(|(_, r)| r).sum::(); + let total_left_score = scores.iter().map(|(l, _)| l).sum::() / num_simulations as f32; + let total_right_score = scores.iter().map(|(_, r)| r).sum::() / num_simulations as f32; debug!("Total left score: {}", total_left_score); debug!("Total right score: {}", total_right_score); @@ -545,6 +592,33 @@ impl FighterNN { } } +fn weighted_random_selection(weights: &HashMap) -> T { + let mut rng = thread_rng(); + + // Identify the minimum weight + let min_weight = weights.values().fold(f32::INFINITY, |a, &b| a.min(b)); + + // Adjust all weights to be non-negative + let offset = if min_weight < 0.0 { + (-min_weight) + 0.5 + } else { + 0.0 + }; + let total_weight: f32 = weights.values().map(|w| w + offset).sum(); + + let mut cumulative_weight = 0.0; + let random_weight = rng.gen::() * total_weight; + + for (item, weight) in weights.iter() { + cumulative_weight += *weight + offset; + if cumulative_weight >= random_weight { + return item.clone(); + } + } + + panic!("Weighted random selection failed."); +} + async fn run_1v1_simulation( nn_path_1: &Path, nn_path_2: &Path, @@ -602,7 +676,13 @@ async fn run_1v1_simulation( let config2_arg = format!("-NN2Config=\"{}\"", nn_path_2.to_str().unwrap()); let disable_unreal_rendering_arg = "-nullrhi".to_string(); - // debug!("the following command {} {} {} {}", GAME_EXECUTABLE_PATH, config1_arg, config2_arg, disable_unreal_rendering_arg); + trace!( + "Executing the following command {} {} {} {}", + GAME_EXECUTABLE_PATH, + config1_arg, + config2_arg, + disable_unreal_rendering_arg + ); trace!("Running simulation for {} vs {}", nn_1_id, nn_2_id); @@ -696,3 +776,56 @@ async fn read_score_from_file(file_path: &Path, nn_id: &str) -> Result= NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN) .collect::>(); + // If a layer has more than NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MAX, remove the neurons with the highest id + for layer in 1..layer_counts.len() - 1 { + let new_neurons_clone = new_neurons.clone(); + let layer_neurons = new_neurons_clone + .iter() + .filter(|(_, _, l, _)| l == &layer) + .collect::>(); + if layer_neurons.len() > NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MAX { + let mut sorted_neurons = layer_neurons.clone(); + // Take primary neurons first, order by highest id + sorted_neurons.sort_by(|a, b| a.1.cmp(&b.1).then(a.0.cmp(&b.0))); + let neurons_to_remove = sorted_neurons.len() - NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MAX; + for _ in 0..neurons_to_remove { + let neuron_to_remove = sorted_neurons.pop().unwrap(); + new_neurons.retain(|neuron| neuron != neuron_to_remove); + } + } + } + // Collect and sort unique layer numbers let mut unique_layers = new_neurons .iter() @@ -606,7 +628,7 @@ pub fn major_mutation(fann: &Fann, weight_initialization_range: Range) -> R .collect::>(); // Determine first whether to add or remove a neuron - if thread_rng().gen_range(0..2) == 0 { + if thread_rng().gen_bool(0.5) { // To add a neuron we need to create a new fann object with the new layer sizes, then copy the information and connections over let max_id = mutated_neurons .iter() @@ -616,9 +638,12 @@ pub fn major_mutation(fann: &Fann, weight_initialization_range: Range) -> R // Now we inject the new neuron into mutated_neurons let layer = thread_rng().gen_range(1..fann.get_num_layers() - 1) as usize; - let new_id = max_id + 1; - mutated_neurons.push((new_id, true, layer, new_id)); - mutated_shape[layer] += 1; + // Do not add to layer if it would result in more than NEURALNETWORK_HIDDEN_LAYER_SIZE_MAX neurons + if mutated_shape[layer] < NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MAX as u32 { + let new_id = max_id + 1; + mutated_neurons.push((new_id, true, layer, new_id)); + mutated_shape[layer] += 1; + } } else { // Remove a neuron let layer = thread_rng().gen_range(1..fann.get_num_layers() - 1) as usize; diff --git a/gemla/src/core/mod.rs b/gemla/src/core/mod.rs index 4cf9b7a..766a7d0 100644 --- a/gemla/src/core/mod.rs +++ b/gemla/src/core/mod.rs @@ -260,10 +260,10 @@ where && r.val.state() == GeneticState::Finish => { info!("Merging nodes {} and {}", l.val.id(), r.val.id()); - if let (Some(left_node), Some(right_node)) = (l.val.take(), r.val.take()) { + if let (Some(left_node), Some(right_node)) = (l.val.as_ref(), r.val.as_ref()) { let merged_node = GeneticNode::merge( - &left_node, - &right_node, + left_node, + right_node, &tree.val.id(), gemla_context.clone(), ) @@ -283,9 +283,9 @@ where (Some(l), None) if l.val.state() == GeneticState::Finish => { trace!("Copying node {}", l.val.id()); - if let Some(left_node) = l.val.take() { + if let Some(left_node) = l.val.as_ref() { GeneticNodeWrapper::from( - left_node, + left_node.clone(), tree.val.max_generations(), tree.val.id(), ); @@ -295,9 +295,9 @@ where (None, Some(r)) if r.val.state() == GeneticState::Finish => { trace!("Copying node {}", r.val.id()); - if let Some(right_node) = r.val.take() { + if let Some(right_node) = r.val.as_ref() { tree.val = GeneticNodeWrapper::from( - right_node, + right_node.clone(), tree.val.max_generations(), tree.val.id(), ); From 822df77f62fc50e73e9ce080dbfa1a15d6f78a97 Mon Sep 17 00:00:00 2001 From: vandomej Date: Mon, 8 Apr 2024 23:27:28 -0700 Subject: [PATCH 23/26] Adding self determination of generation length --- analyze_data.py | 163 ++++ gemla/src/bin/bin.rs | 5 +- gemla/src/bin/fighter_nn/mod.rs | 921 ++++++++++++++++++-- gemla/src/bin/test_state/mod.rs | 32 +- gemla/src/core/genetic_node.rs | 89 +- gemla/src/core/mod.rs | 59 +- analysis.py => visualize_simulation_tree.py | 0 7 files changed, 1089 insertions(+), 180 deletions(-) create mode 100644 analyze_data.py rename analysis.py => visualize_simulation_tree.py (100%) diff --git a/analyze_data.py b/analyze_data.py new file mode 100644 index 0000000..0ac2c16 --- /dev/null +++ b/analyze_data.py @@ -0,0 +1,163 @@ +# Re-importing necessary libraries +import json +import matplotlib.pyplot as plt +from collections import defaultdict +import numpy as np + +# Simplified JSON data for demonstration +with open('gemla/round2.json', 'r') as file: + simplified_json_data = json.load(file) + +target_node_id = '0c1e64dc-6ddf-4dbb-bf6e-e8218b925194' + +# Function to traverse the tree to find a node id +def traverse_left_nodes(node): + if node is None: + return [] + + left_node = node.get("left") + if left_node is None: + return [node] + + return [node] + traverse_left_nodes(left_node) + +# Function to traverse the tree to find a node id +def traverse_right_nodes(node): + if node is None: + return [] + + right_node = node.get("right") + left_node = node.get("left") + + if right_node is None and left_node is None: + return [] + elif right_node and left_node: + return [right_node] + traverse_right_nodes(left_node) + + return [] + + +# Getting the left graph +left_nodes = traverse_left_nodes(simplified_json_data[0]) +left_nodes.reverse() +# print(node) +# Print properties available on the first node +node = left_nodes[0] +# print(node["val"].keys()) + +scores = [] +for node in left_nodes: + # print(node) + # print(f'Node ID: {node["val"]["id"]}') + # print(f'Node scores length: {len(node["val"]["node"]["scores"])}') + if node["val"]["node"]: + node_scores = node["val"]["node"]["scores"] + if node_scores: + for score in node_scores: + scores.append(score) + +# print(scores) + +scores_values = [list(score_set.values()) for score_set in scores] + +# Set up the figure for plotting on the same graph +fig, ax = plt.subplots(figsize=(10, 6)) + +# Generate a boxplot for each set of scores on the same graph +boxplots = ax.boxplot(scores_values, vert=False, patch_artist=True, labels=[f'Set {i+1}' for i in range(len(scores_values))]) + +# Set figure name to node id +# fig.canvas.set_window_title('Main node line') + +# Labeling +ax.set_xlabel(f'Scores - Main Line') +ax.set_ylabel('Score Sets') +ax.yaxis.grid(True) # Add horizontal grid lines for clarity + +# Set y-axis labels to be visible +ax.set_yticklabels([f'Set {i+1}' for i in range(len(scores_values))]) + +# Getting most recent right graph +right_nodes = traverse_right_nodes(simplified_json_data[0]) +target_node_id = None +target_node = None +if target_node_id: + for node in right_nodes: + if node["val"]["id"] == target_node_id: + target_node = node + break +else: + target_node = right_nodes[1] +scores = target_node["val"]["node"]["scores"] + +scores_values = [list(score_set.values()) for score_set in scores] + +# Set up the figure for plotting on the same graph +fig, ax = plt.subplots(figsize=(10, 6)) + +# Generate a boxplot for each set of scores on the same graph +boxplots = ax.boxplot(scores_values, vert=False, patch_artist=True, labels=[f'Set {i+1}' for i in range(len(scores_values))]) + + +# Labeling +ax.set_xlabel(f'Scores: {target_node['val']['id']}') +ax.set_ylabel('Score Sets') +ax.yaxis.grid(True) # Add horizontal grid lines for clarity + +# Set y-axis labels to be visible +ax.set_yticklabels([f'Set {i+1}' for i in range(len(scores_values))]) + +# Find the highest scoring sets combining all scores and generations +scores = [] +for node in left_nodes: + if node["val"]["node"]: + node_scores = node["val"]["node"]["scores"] + translated_node_scores = [] + if node_scores: + for i in range(len(node_scores)): + for (individual, score) in node_scores[i].items(): + translated_node_scores.append((node["val"]["id"], i, score)) + + scores.append(translated_node_scores) + +# Add scores from the right nodes +for node in right_nodes: + if node["val"]["node"]: + node_scores = node["val"]["node"]["scores"] + translated_node_scores = [] + if node_scores: + for i in range(len(node_scores)): + for (individual, score) in node_scores[i].items(): + translated_node_scores.append((node["val"]["id"], i, score)) + +# Organize scores by individual and then by generation +individual_generation_scores = defaultdict(lambda: defaultdict(list)) +for sublist in scores: + for id, generation, score in sublist: + individual_generation_scores[id][generation].append(score) + +# Calculate Q3 for each individual's generation +individual_generation_q3 = {} +for id, generations in individual_generation_scores.items(): + for gen, scores in generations.items(): + individual_generation_q3[(id, gen)] = np.percentile(scores, 75) + +# Sort by Q3 value, highest first, and select the top 20 +top_20_individual_generations = sorted(individual_generation_q3, key=individual_generation_q3.get, reverse=True)[:40] + +# Prepare scores for the top 20 for plotting +top_20_scores = [individual_generation_scores[id][gen] for id, gen in top_20_individual_generations] + +# Adjust labels for clarity, indicating both the individual ID and generation +labels = [f'{id[:8]}... Gen {gen}' for id, gen in top_20_individual_generations] + +# Generate box and whisker plots for the top 20 individual generations +fig, ax = plt.subplots(figsize=(12, 10)) +ax.boxplot(top_20_scores, vert=False, patch_artist=True, labels=labels) +ax.set_xlabel('Scores') +ax.set_ylabel('Individual Generation') +ax.set_title('Top 20 Individual Generations by Q3 Value') + +# Display the plot +plt.show() + diff --git a/gemla/src/bin/bin.rs b/gemla/src/bin/bin.rs index e41e417..96248cb 100644 --- a/gemla/src/bin/bin.rs +++ b/gemla/src/bin/bin.rs @@ -47,10 +47,7 @@ fn main() -> Result<()> { let mut gemla = log_error( Gemla::::new( &PathBuf::from(args.file), - GemlaConfig { - generations_per_height: 5, - overwrite: false, - }, + GemlaConfig { overwrite: false }, DataFormat::Json, ) .await, diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index b52deec..820d90b 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -3,7 +3,7 @@ extern crate fann; pub mod fighter_context; pub mod neural_network_utility; -use anyhow::Context; +use anyhow::{anyhow, Context}; use async_trait::async_trait; use fann::{ActivationFunc, Fann}; use futures::future::join_all; @@ -22,7 +22,7 @@ use std::{ ops::Range, path::{Path, PathBuf}, }; -use tokio::process::Command; +use tokio::{process::Command, sync::mpsc::channel}; use uuid::Uuid; use self::neural_network_utility::{crossbreed, major_mutation}; @@ -65,12 +65,15 @@ pub struct FighterNN { // A map of each nn identifier in a generation and their physics score pub scores: Vec>, // A map of the id of the nn in the current generation and their neural network shape - pub nn_shapes: HashMap>, + pub nn_shapes: Vec>>, pub crossbreed_segments: usize, pub weight_initialization_range: Range, pub minor_mutation_rate: f32, pub major_mutation_rate: f32, pub mutation_weight_range: Range, + // Shows how individuals are mapped from one generation to the next + pub id_mapping: Vec>, + pub lerp_amount: f32, } #[async_trait] @@ -146,115 +149,158 @@ impl GeneticNode for FighterNN { population_size: POPULATION, generation: 0, scores: vec![HashMap::new()], - nn_shapes, + nn_shapes: vec![nn_shapes], // we need crossbreed segments to be even crossbreed_segments, weight_initialization_range, minor_mutation_rate: thread_rng().gen_range(0.0..1.0), major_mutation_rate: thread_rng().gen_range(0.0..1.0), mutation_weight_range: -mutation_weight_amplitude..mutation_weight_amplitude, + id_mapping: vec![HashMap::new()], + lerp_amount: 0.0, })) } - async fn simulate(&mut self, context: GeneticNodeContext) -> Result<(), Error> { + async fn simulate( + &mut self, + context: GeneticNodeContext, + ) -> Result { debug!("Context: {:?}", context); + let mut matches = Vec::new(); + let mut allotted_simulations = Vec::new(); + for i in 0..self.population_size { + allotted_simulations.push((i, SIMULATION_ROUNDS)); + } + + while !allotted_simulations.is_empty() { + let primary_id = { + let id = thread_rng().gen_range(0..allotted_simulations.len()); + let (i, _) = allotted_simulations[id]; + // Decrement the number of simulations left for this nn + allotted_simulations[id].1 -= 1; + // Remove the nn from the list if it has no more simulations left + if allotted_simulations[id].1 == 0 { + allotted_simulations.remove(id); + } + i + }; + + let secondary_id = loop { + let id = thread_rng().gen_range(0..allotted_simulations.len()); + let (i, _) = allotted_simulations[id]; + + if i != primary_id { + // Decrement the number of simulations left for this nn + allotted_simulations[id].1 -= 1; + // Remove the nn from the list if it has no more simulations left + if allotted_simulations[id].1 == 0 { + allotted_simulations.remove(id); + } + break i; + } + }; + + matches.push((primary_id, secondary_id)); + } + + trace!("Matches: {:?}", matches); + + // Create a channel to send the scores back to the main thread + let (tx, mut rx) = channel::<(usize, f32)>(self.population_size * SIMULATION_ROUNDS * 20); let mut tasks = Vec::new(); - // For each nn in the current generation: - for i in 0..self.population_size { + for (primary_id, secondary_id) in matches.iter() { let self_clone = self.clone(); let semaphore_clone = context.gemla_context.shared_semaphore.clone(); let display_simulation_semaphore = context.gemla_context.visible_simulations.clone(); + let tx = tx.clone(); let task = async move { - let nn = self_clone + let folder = self_clone.folder.clone(); + let generation = self_clone.generation; + + let primary_nn = self_clone .folder .join(format!("{}", self_clone.generation)) - .join(self_clone.get_individual_id(i as u64)) + .join(self_clone.get_individual_id(*primary_id as u64)) + .with_extension("net"); + let secondary_nn = folder + .join(format!("{}", generation)) + .join(self_clone.get_individual_id(*secondary_id as u64)) .with_extension("net"); - let mut simulations = Vec::new(); - // Using the same original nn, repeat the simulation with 5 random nn's from the current generation concurrently - for _ in 0..SIMULATION_ROUNDS { - let random_nn_index = thread_rng().gen_range(0..self_clone.population_size); - let folder = self_clone.folder.clone(); - let generation = self_clone.generation; - let semaphore_clone = semaphore_clone.clone(); - let display_simulation_semaphore = display_simulation_semaphore.clone(); + let permit = semaphore_clone + .acquire_owned() + .await + .with_context(|| "Failed to acquire semaphore permit")?; - let random_nn = folder - .join(format!("{}", generation)) - .join(self_clone.get_individual_id(random_nn_index as u64)) - .with_extension("net"); - let nn_clone = nn.clone(); // Clone the path to use in the async block + let display_simulation = match display_simulation_semaphore.try_acquire_owned() { + Ok(s) => Some(s), + Err(_) => None, + }; - let future = async move { - let permit = semaphore_clone - .acquire_owned() - .await - .with_context(|| "Failed to acquire semaphore permit")?; - - let display_simulation = - match display_simulation_semaphore.try_acquire_owned() { - Ok(s) => Some(s), - Err(_) => None, - }; - - let (score, _) = if let Some(display_simulation) = display_simulation { - let result = run_1v1_simulation(&nn_clone, &random_nn, true).await?; - drop(display_simulation); - result - } else { - run_1v1_simulation(&nn_clone, &random_nn, false).await? - }; - - drop(permit); - - Ok(score) + let (primary_score, secondary_score) = + if let Some(display_simulation) = display_simulation { + let result = run_1v1_simulation(&primary_nn, &secondary_nn, true).await?; + drop(display_simulation); + result + } else { + run_1v1_simulation(&primary_nn, &secondary_nn, false).await? }; - simulations.push(future); - } + drop(permit); - // Wait for all simulation rounds to complete - let results: Result, Error> = - join_all(simulations).await.into_iter().collect(); + debug!( + "{} vs {} -> {} vs {}", + primary_id, secondary_id, primary_score, secondary_score + ); - let score = match results { - Ok(scores) => scores.into_iter().sum::() / SIMULATION_ROUNDS as f32, - Err(e) => return Err(e), // Return the error if results collection failed - }; - debug!("NN {:06}_fighter_nn_{} scored {}", self_clone.id, i, score); - Ok((i, score)) + // Send score using a channel + tx.send((*primary_id, primary_score)) + .await + .with_context(|| "Failed to send score")?; + tx.send((*secondary_id, secondary_score)) + .await + .with_context(|| "Failed to send score")?; + + Ok(()) }; tasks.push(task); } - let results = join_all(tasks).await; + let results: Vec> = join_all(tasks).await; - for result in results { - match result { - Ok((index, score)) => { - // Update the original `self` object with the score. - self.scores[self.generation as usize].insert(index as u64, score); - } - Err(e) => { - // Handle task panic or execution error - return Err(Error::Other(anyhow::anyhow!(format!( - "Task failed: {:?}", - e - )))); - } + // resolve results for any errors + for result in results.into_iter() { + result.with_context(|| "Failed to run simulation")?; + } + + // Receive the scores from the channel + let mut scores = HashMap::new(); + while let Some((id, score)) = rx.recv().await { + // If score exists, add the new score to the existing score + if let Some(existing_score) = scores.get_mut(&(id as u64)) { + *existing_score += score; + } else { + scores.insert(id as u64, score); } } - Ok(()) + // Average scores for each individual + for (_, score) in scores.iter_mut() { + *score /= SIMULATION_ROUNDS as f32; + } + + self.scores.push(scores); + + Ok(should_continue(&self.scores)?) } async fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { let survivor_count = (self.population_size as f32 * SURVIVAL_RATE) as usize; let mut nn_sizes = Vec::new(); + let mut id_mapping = HashMap::new(); // Create the new generation folder let new_gen_folder = self.folder.join(format!("{}", self.generation + 1)); @@ -280,8 +326,14 @@ impl GeneticNode for FighterNN { .join(format!("{:06}_fighter_nn_{}.net", self.id, nn_id)); let new_nn = new_gen_folder.join(format!("{:06}_fighter_nn_{}.net", self.id, i)); debug!("Copying nn from {:?} to {:?}", nn_id, i); + id_mapping.insert(**nn_id, i as u64); fs::copy(&nn, &new_nn)?; - nn_sizes.push(self.nn_shapes.get(nn_id).unwrap().clone()); + nn_sizes.push( + self.nn_shapes[self.generation as usize] + .get(nn_id) + .unwrap() + .clone(), + ); } let weights: HashMap = scores_to_keep.iter().map(|(k, v)| (**k, **v)).collect(); @@ -352,9 +404,7 @@ impl GeneticNode for FighterNN { let new_nn = new_gen_folder .join(self_clone.get_individual_id((i + survivor_count) as u64)) .with_extension("net"); - new_fann - .save(&new_nn) - .with_context(|| "Failed to save nn")?; + new_fann.save(new_nn).with_context(|| "Failed to save nn")?; Ok::, Error>(new_fann.get_layer_sizes()) }); @@ -369,8 +419,17 @@ impl GeneticNode for FighterNN { nn_sizes.push(new_size); } + // Use the index of nn_sizes to generate the id for the nn_sizes HashMap + let nn_sizes_map = nn_sizes + .into_iter() + .enumerate() + .map(|(i, v)| (i as u64, v)) + .collect::>(); + self.generation += 1; self.scores.push(HashMap::new()); + self.nn_shapes.push(nn_sizes_map); + self.id_mapping.push(id_mapping); Ok(()) } @@ -502,10 +561,11 @@ impl GeneticNode for FighterNN { format!("Failed to copy nn from {:?} to {:?}", nn_path, new_nn_path) })?; - nn_shapes.insert( - (start_idx + i) as u64, - source.nn_shapes.get(nn_id).unwrap().clone(), - ); + let nn_shape = source.nn_shapes[source.generation as usize] + .get(nn_id) + .unwrap(); + + nn_shapes.insert((start_idx + i) as u64, nn_shape.clone()); } Ok(()) @@ -577,11 +637,13 @@ impl GeneticNode for FighterNN { population_size: nn_shapes.len(), scores: vec![HashMap::new()], crossbreed_segments, - nn_shapes, + nn_shapes: vec![nn_shapes], weight_initialization_range, minor_mutation_rate, major_mutation_rate, mutation_weight_range, + id_mapping: vec![HashMap::new()], + lerp_amount, })) } } @@ -592,6 +654,54 @@ impl FighterNN { } } +fn should_continue(scores: &[HashMap]) -> Result { + if scores.len() < 5 { + return Ok(true); + } + + let mut highest_q3_value = f32::MIN; + let mut generation_with_highest_q3 = 0; + + let mut highest_median = f32::MIN; + let mut generation_with_highest_median = 0; + + for (generation_index, generation) in scores.iter().enumerate() { + let mut scores: Vec = generation.values().copied().collect(); + scores.sort_by(|a, b| a.partial_cmp(b).unwrap()); + + let q3_index = (scores.len() as f32 * 0.75).ceil() as usize - 1; + let q3_value = scores + .get(q3_index) + .ok_or(anyhow!("Failed to get Q3 value"))?; + + if *q3_value > highest_q3_value { + highest_q3_value = *q3_value; + generation_with_highest_q3 = generation_index; + } + + let median_index = (scores.len() as f32 * 0.5).ceil() as usize - 1; + let median_value = scores + .get(median_index) + .ok_or(anyhow!("Failed to get median value"))?; + + if *median_value > highest_median { + highest_median = *median_value; + generation_with_highest_median = generation_index; + } + } + + let highest_generation_index = scores.len() - 1; + let result = highest_generation_index - generation_with_highest_q3 < 5 + && highest_generation_index - generation_with_highest_median < 5; + + debug!( + "Highest Q3 value: {} at generation {}, Highest Median value: {} at generation {}, Continuing? {}", + highest_q3_value, generation_with_highest_q3, highest_median, generation_with_highest_median, result + ); + + Ok(result) +} + fn weighted_random_selection(weights: &HashMap) -> T { let mut rng = thread_rng(); @@ -828,4 +938,655 @@ pub mod test { assert_eq!(ids.len(), 0); } + + #[test] + fn test_should_continue() { + let scores = vec![ + // Generation 0 + [ + (37, -7.1222725), + (12, -3.6037624), + (27, -5.202844), + (21, -6.3283415), + (4, -6.0053186), + (8, -4.040202), + (13, -4.0050435), + (17, -5.8206105), + (40, -7.5448103), + (42, -8.027704), + (15, -5.1600137), + (10, -7.9063845), + (1, -6.9830275), + (7, -3.3323112), + (16, -6.1065326), + (23, -6.417853), + (25, -6.410652), + (14, -6.5887403), + (3, -6.3966584), + (19, 0.1242948), + (28, -4.806827), + (18, -6.3310747), + (30, -5.8972425), + (31, -6.398958), + (22, -7.042196), + (29, -5.7098813), + (9, -8.931531), + (33, -5.9806275), + (6, -6.5489874), + (26, -5.892653), + (34, -6.4281516), + (35, -5.5369387), + (38, -5.495344), + (43, 0.9552175), + (44, -6.2549844), + (45, -8.42142), + (24, -7.121878), + (47, -5.373896), + (48, -6.445716), + (39, -6.053849), + (11, -5.8320975), + (49, -10.014197), + (46, -7.0919595), + (20, -6.033137), + (5, -6.3501267), + (32, -4.203919), + (2, -5.743471), + (36, -8.493466), + (41, -7.60419), + (0, -7.388545), + ], + // Generation 1 + [ + (18, -6.048934), + (39, -1.1448132), + (48, -7.921489), + (38, -6.0117235), + (27, -6.30289), + (9, -6.5567093), + (29, -5.905172), + (25, -4.2305975), + (40, -5.1198816), + (24, -7.232001), + (46, -6.5581756), + (20, -6.7987585), + (8, -9.346154), + (2, -7.6944494), + (3, -6.487195), + (16, -8.379641), + (32, -7.292016), + (33, -7.91467), + (41, -7.4449363), + (21, -6.0500197), + (19, -5.357873), + (10, -6.9984064), + (7, -5.6824636), + (13, -8.154273), + (45, -7.8713655), + (47, -5.279138), + (49, -1.915852), + (6, -2.682654), + (30, -5.566201), + (1, -1.829716), + (11, -7.7527223), + (12, -10.379072), + (15, -4.866212), + (35, -8.091223), + (36, -8.137203), + (42, -7.2846284), + (44, -4.7636213), + (28, -6.518874), + (34, 1.9858776), + (43, -10.140268), + (0, -3.5068736), + (17, -2.3913155), + (26, -6.1766686), + (22, -9.119884), + (14, -7.470778), + (5, -5.925585), + (23, -6.004782), + (31, -2.696432), + (4, -2.4887466), + (37, -5.5321026), + ], + // Generation 2 + [ + (25, -8.760574), + (0, -2.5970187), + (9, -4.270929), + (11, -0.27550858), + (20, -6.7012835), + (30, 2.3309054), + (4, -7.0107384), + (31, -7.5239167), + (41, -2.337672), + (6, -3.4384027), + (16, -7.9485044), + (37, -7.3155503), + (38, -7.4812994), + (3, -3.958924), + (42, -7.738173), + (43, -6.500585), + (22, -6.318394), + (17, -5.7882595), + (45, -8.782414), + (49, -8.84129), + (23, -10.222613), + (26, -6.06804), + (32, -6.4851217), + (33, -7.3542376), + (34, -2.8723297), + (27, -7.1350646), + (8, -2.7956052), + (18, -5.0000043), + (10, -1.5138103), + (2, 0.10560961), + (7, -1.4954948), + (35, -7.7015786), + (36, -8.602789), + (47, -8.117584), + (28, -9.151132), + (39, -8.035833), + (13, -6.2601876), + (15, -9.050044), + (19, -5.465233), + (44, -8.494604), + (5, -6.9012084), + (12, -9.458872), + (21, -5.980685), + (14, -7.7407913), + (46, -0.701484), + (24, -9.477325), + (29, -6.6444407), + (1, -3.4681067), + (40, -5.4685316), + (48, 0.22965483), + ], + // Generation 3 + [ + (11, -5.7744265), + (12, 0.10171394), + (18, -8.503949), + (3, -1.9760166), + (17, -7.895561), + (20, -8.515409), + (45, -1.9184738), + (6, -5.6488137), + (46, -6.1171823), + (49, -7.006673), + (29, -3.6479561), + (37, -4.025724), + (42, -4.1281996), + (9, -2.7060657), + (33, 0.18799233), + (15, -7.8216696), + (23, -11.02603), + (22, -10.132984), + (7, -6.432255), + (38, -7.2159233), + (10, -2.195277), + (2, -6.7676725), + (27, -1.8040345), + (34, -11.214028), + (40, -6.1334066), + (35, -9.410227), + (44, -0.14929143), + (47, -7.3865366), + (41, -9.200221), + (26, -6.1885824), + (13, -5.5693216), + (31, -8.184256), + (39, -8.06583), + (24, -11.773471), + (25, -15.231514), + (14, -5.4468412), + (30, -5.494699), + (21, -10.619481), + (28, -7.322004), + (16, -7.4136076), + (8, -3.2260292), + (32, -8.187313), + (19, -5.9347467), + (43, -0.112977505), + (5, -1.9279568), + (48, -3.8396995), + (0, -9.317253), + (4, -1.8099403), + (1, -5.4981036), + (36, -3.5487309), + ], + // Generation 4 + [ + (28, -6.2057357), + (40, -6.9324327), + (46, -0.5130272), + (23, -7.9489794), + (47, -7.3411865), + (20, -8.930363), + (26, -3.238875), + (41, -7.376683), + (48, -0.83026105), + (27, -10.048681), + (36, -5.1788163), + (30, -8.002236), + (9, -7.4656434), + (4, -3.8850121), + (16, -3.1768656), + (11, 1.0195583), + (44, -8.7163315), + (45, -6.7038856), + (33, -6.974304), + (22, -10.026589), + (13, -4.342838), + (12, -6.69588), + (31, -2.2994905), + (14, -7.9772606), + (32, -10.55702), + (38, -5.668454), + (34, -10.026564), + (37, -8.128912), + (42, -10.7178335), + (17, -5.18195), + (49, -9.900299), + (21, -12.4000635), + (8, -1.8514707), + (29, -3.365313), + (39, -5.588918), + (43, -8.482417), + (1, -4.390686), + (35, -5.604909), + (24, -7.1810236), + (25, -5.9158974), + (19, -4.5733366), + (0, -5.68081), + (3, -2.8414884), + (6, -1.5809858), + (7, -9.295659), + (5, -3.7936096), + (10, -4.088697), + (2, -2.3494315), + (15, -7.3323736), + (18, -7.7137175), + ], + // Generation 5 + [ + (1, -2.7719336), + (37, -6.097855), + (39, -4.1296787), + (2, -5.4538774), + (34, -11.808794), + (40, -9.822159), + (3, -7.884645), + (42, -14.777964), + (32, -2.6564443), + (16, -5.2442584), + (9, -6.2919874), + (48, -2.4359574), + (25, -11.707236), + (33, -5.5483084), + (35, -0.3632618), + (7, -4.3673687), + (27, -8.139543), + (12, -9.019396), + (17, -0.029791832), + (24, -8.63045), + (18, -11.925819), + (20, -9.040375), + (44, -10.296264), + (47, -15.95397), + (23, -12.38116), + (21, 0.18342426), + (38, -7.695002), + (6, -8.710346), + (28, -2.8542902), + (5, -2.077858), + (10, -3.638583), + (8, -7.360152), + (15, -7.1610765), + (29, -4.8372035), + (45, -11.499393), + (13, -3.8436065), + (22, -5.472387), + (11, -4.259357), + (26, -4.847328), + (4, -2.0376666), + (36, -7.5392637), + (41, -5.3857164), + (19, -8.576212), + (14, -8.267895), + (30, -4.0456495), + (31, -3.806975), + (43, -7.9901657), + (46, -7.181662), + (0, -7.502816), + (49, -7.3067017), + ], + // Generation 6 + [ + (17, -9.793276), + (27, -2.8843281), + (38, -8.737534), + (8, -1.5083166), + (16, -8.267393), + (42, -8.055011), + (47, -2.0843022), + (14, -3.9945045), + (30, -10.208374), + (26, -3.2439823), + (49, -2.5527742), + (25, -10.359426), + (9, -4.4744225), + (19, -7.2775927), + (3, -7.282045), + (36, -8.503307), + (40, -12.083569), + (22, -3.7249084), + (18, -7.5065627), + (41, -3.3326488), + (44, -2.76882), + (45, -12.154654), + (24, -2.8332536), + (5, -5.2674284), + (4, -4.105483), + (10, -6.930478), + (20, -3.7845988), + (2, -4.4593267), + (28, -0.3003047), + (29, -6.5971193), + (32, -5.0542274), + (33, -9.068264), + (43, -7.124672), + (46, -8.358111), + (23, -5.551978), + (11, -7.7810373), + (35, -7.4763336), + (34, -10.868844), + (39, -10.51066), + (7, -4.376377), + (48, -9.093265), + (6, -0.20033613), + (1, -6.125786), + (12, -8.243349), + (0, -7.1646323), + (13, -3.7055316), + (15, -6.295897), + (21, -5.929867), + (31, -7.2123885), + (37, -2.482071), + ], + // Generation 7 + [ + (30, -12.467585), + (14, -5.1706576), + (40, -9.03964), + (18, -5.7730474), + (41, -9.061858), + (20, -2.8577142), + (24, -3.3558655), + (42, -7.902747), + (43, -6.1566644), + (21, -5.4271364), + (23, -7.1462164), + (44, -7.9898252), + (11, -2.493559), + (31, -4.6718645), + (48, -12.774545), + (8, -7.252562), + (35, -1.6866531), + (49, -4.437603), + (45, -7.164916), + (7, -4.613396), + (32, -8.156101), + (39, -10.887325), + (0, -0.18116185), + (47, -4.998584), + (10, -8.914183), + (13, -0.8690014), + (27, -0.3714923), + (28, -12.002966), + (9, -6.2789965), + (26, -0.46416503), + (2, -9.865377), + (29, -8.443848), + (46, -6.3264246), + (3, -7.807205), + (4, -6.8240366), + (5, -6.843891), + (12, -5.6381693), + (15, -4.6679296), + (36, -6.8010025), + (16, -8.222928), + (25, -10.326822), + (34, -6.0182467), + (37, -8.713378), + (38, -7.549215), + (17, -7.247555), + (22, -13.296148), + (33, -8.542955), + (19, -7.254419), + (1, -2.8472056), + (6, -5.898753), + ], + // Generation 8 + [ + (7, -3.6624274), + (4, -2.9281456), + (39, -5.9176188), + (13, -8.0644045), + (16, -2.0319564), + (49, -10.309226), + (3, -0.21671781), + (37, -8.295551), + (44, -16.496105), + (46, -6.2466326), + (47, -3.5928986), + (19, -9.298591), + (1, -7.937351), + (15, -8.218504), + (6, -6.945601), + (25, -8.446054), + (12, -5.8477135), + (14, -3.9165816), + (17, -2.4864268), + (20, -7.97737), + (22, -5.347026), + (0, -6.0739775), + (32, -6.7568192), + (36, -4.730008), + (28, -9.923819), + (38, -8.677519), + (42, -4.668519), + (48, 0.14014988), + (5, -8.3167), + (8, -2.5030074), + (21, -1.8195568), + (27, -6.111103), + (45, -12.708131), + (35, -8.089076), + (11, -6.0151362), + (34, -13.688166), + (33, -11.375975), + (2, -4.1082373), + (24, -4.0867376), + (10, -4.2828474), + (41, -9.174506), + (43, -1.1505331), + (29, -3.7704785), + (18, -4.9493446), + (30, -3.727829), + (31, -6.490308), + (9, -6.0947385), + (40, -9.492185), + (26, -13.629112), + (23, -9.773454), + ], + // Generation 9 + [ + (12, -1.754871), + (41, 2.712658), + (24, -4.0929146), + (18, -4.9418926), + (44, -9.325021), + (8, -6.4423165), + (1, -0.0946085), + (5, -3.0156248), + (14, -5.29519), + (34, -10.763539), + (11, -7.304751), + (20, -6.8397574), + (22, -5.6720686), + (23, -7.829904), + (7, -3.8627372), + (6, -3.1108487), + (16, -8.803584), + (36, -13.916307), + (21, -10.142917), + (37, -12.171498), + (45, -13.004938), + (19, -3.7237267), + (47, -6.0189786), + (17, -4.612711), + (15, -5.3010545), + (30, -5.671092), + (46, -13.300519), + (25, -8.2948), + (3, -10.556543), + (42, -7.041272), + (48, -9.797744), + (9, -5.6163936), + (26, -6.665021), + (27, -7.074666), + (4, -1.5992731), + (2, -6.4931273), + (29, -3.9785416), + (31, -12.222026), + (10, -2.3970482), + (40, -6.204074), + (49, -7.025599), + (28, -8.562909), + (13, -6.2592154), + (32, -10.465271), + (33, -7.7043953), + (35, -6.4584246), + (38, -2.9016697), + (39, -1.5256255), + (43, -10.858711), + (0, -4.720929), + ], + //Generation 10 + [ + (2, -5.1676617), + (3, -4.521774), + (29, -7.3104324), + (23, -6.550776), + (26, -10.467587), + (18, 1.6576093), + (33, -2.564094), + (20, -3.2697926), + (35, -13.577334), + (37, -6.0147185), + (17, -4.07909), + (0, -9.630419), + (38, -7.011383), + (12, -10.686635), + (43, -8.94728), + (48, -9.350017), + (30, -7.3335466), + (13, -7.7690034), + (4, -2.3488472), + (14, -7.2594194), + (21, -9.08367), + (34, -7.7497597), + (8, -6.2317214), + (27, -8.440135), + (22, -4.4437346), + (32, -2.194015), + (28, -6.6919556), + (40, -8.840385), + (42, -9.781796), + (15, -7.3304253), + (49, -8.720987), + (19, -9.044103), + (6, -5.715863), + (41, -8.395639), + (36, -3.995482), + (25, -9.1373005), + (5, -7.5690002), + (1, -6.0397635), + (16, -8.231512), + (10, -6.5344634), + (44, -7.749376), + (7, -9.302668), + (31, -10.868391), + (39, -2.7578635), + (47, -6.964238), + (24, -4.033315), + (11, -8.211409), + (45, -10.472969), + (9, -7.1529093), + (46, -9.653514), + ], + ]; + + // Transform scores into a vector of hashmaps instead + let scores: Vec> = scores + .iter() + .map(|gen_scores| gen_scores.iter().cloned().collect()) + .collect(); + + assert!( + should_continue(scores[..0].as_ref()) + .expect("Failed to determine if the simulation should continue") + == true + ); + assert!( + should_continue(scores[..1].as_ref()) + .expect("Failed to determine if the simulation should continue") + == true + ); + assert!( + should_continue(scores[..2].as_ref()) + .expect("Failed to determine if the simulation should continue") + == true + ); + assert!( + should_continue(scores[..3].as_ref()) + .expect("Failed to determine if the simulation should continue") + == true + ); + assert!( + should_continue(scores[..4].as_ref()) + .expect("Failed to determine if the simulation should continue") + == true + ); + assert!( + should_continue(scores[..5].as_ref()) + .expect("Failed to determine if the simulation should continue") + == true + ); + assert!( + should_continue(scores[..6].as_ref()) + .expect("Failed to determine if the simulation should continue") + == true + ); + assert!( + should_continue(scores[..7].as_ref()) + .expect("Failed to determine if the simulation should continue") + == true + ); + assert!( + should_continue(scores[..8].as_ref()) + .expect("Failed to determine if the simulation should continue") + == false + ); + assert!( + should_continue(scores[..9].as_ref()) + .expect("Failed to determine if the simulation should continue") + == false + ); + assert!( + should_continue(scores[..10].as_ref()) + .expect("Failed to determine if the simulation should continue") + == false + ); + } } diff --git a/gemla/src/bin/test_state/mod.rs b/gemla/src/bin/test_state/mod.rs index af5b316..5bde119 100644 --- a/gemla/src/bin/test_state/mod.rs +++ b/gemla/src/bin/test_state/mod.rs @@ -13,6 +13,7 @@ const POPULATION_REDUCTION_SIZE: u64 = 3; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct TestState { pub population: Vec, + pub max_generations: u64, } #[async_trait] @@ -26,10 +27,16 @@ impl GeneticNode for TestState { population.push(thread_rng().gen_range(0..100)) } - Ok(Box::new(TestState { population })) + Ok(Box::new(TestState { + population, + max_generations: 10, + })) } - async fn simulate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { + async fn simulate( + &mut self, + context: GeneticNodeContext, + ) -> Result { let mut rng = thread_rng(); self.population = self @@ -38,7 +45,11 @@ impl GeneticNode for TestState { .map(|p| p.saturating_add(rng.gen_range(-1..2))) .collect(); - Ok(()) + if context.generation >= self.max_generations { + Ok(false) + } else { + Ok(true) + } } async fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { @@ -93,13 +104,15 @@ impl GeneticNode for TestState { v = v[..(POPULATION_REDUCTION_SIZE as usize)].to_vec(); - let mut result = TestState { population: v }; + let mut result = TestState { + population: v, + max_generations: 10, + }; result .mutate(GeneticNodeContext { id: *id, generation: 0, - max_generations: 0, gemla_context, }) .await?; @@ -118,7 +131,6 @@ mod tests { let state = TestState::initialize(GeneticNodeContext { id: Uuid::new_v4(), generation: 0, - max_generations: 0, gemla_context: (), }) .await @@ -131,6 +143,7 @@ mod tests { async fn test_simulate() { let mut state = TestState { population: vec![1, 1, 2, 3], + max_generations: 1, }; let original_population = state.population.clone(); @@ -139,7 +152,6 @@ mod tests { .simulate(GeneticNodeContext { id: Uuid::new_v4(), generation: 0, - max_generations: 0, gemla_context: (), }) .await @@ -153,7 +165,6 @@ mod tests { .simulate(GeneticNodeContext { id: Uuid::new_v4(), generation: 0, - max_generations: 0, gemla_context: (), }) .await @@ -162,7 +173,6 @@ mod tests { .simulate(GeneticNodeContext { id: Uuid::new_v4(), generation: 0, - max_generations: 0, gemla_context: (), }) .await @@ -177,13 +187,13 @@ mod tests { async fn test_mutate() { let mut state = TestState { population: vec![4, 3, 3], + max_generations: 1, }; state .mutate(GeneticNodeContext { id: Uuid::new_v4(), generation: 0, - max_generations: 0, gemla_context: (), }) .await @@ -196,10 +206,12 @@ mod tests { async fn test_merge() { let state1 = TestState { population: vec![1, 2, 4, 5], + max_generations: 1, }; let state2 = TestState { population: vec![0, 1, 3, 7], + max_generations: 1, }; let merged_state = TestState::merge(&state1, &state2, &Uuid::new_v4(), ()) diff --git a/gemla/src/core/genetic_node.rs b/gemla/src/core/genetic_node.rs index 020d2c6..b85b775 100644 --- a/gemla/src/core/genetic_node.rs +++ b/gemla/src/core/genetic_node.rs @@ -28,7 +28,6 @@ pub enum GeneticState { #[derive(Clone, Debug)] pub struct GeneticNodeContext { pub generation: u64, - pub max_generations: u64, pub id: Uuid, pub gemla_context: S, } @@ -46,7 +45,8 @@ pub trait GeneticNode: Send { /// TODO async fn initialize(context: GeneticNodeContext) -> Result, Error>; - async fn simulate(&mut self, context: GeneticNodeContext) -> Result<(), Error>; + async fn simulate(&mut self, context: GeneticNodeContext) + -> Result; /// Mutates members in a population and/or crossbreeds them to produce new offspring. /// @@ -72,7 +72,6 @@ where node: Option, state: GeneticState, generation: u64, - max_generations: u64, id: Uuid, } @@ -85,7 +84,6 @@ where node: None, state: GeneticState::Initialize, generation: 1, - max_generations: 1, id: Uuid::new_v4(), } } @@ -96,19 +94,17 @@ where T: GeneticNode + Debug + Send + Clone, T::Context: Send + Sync + Clone + Debug + Serialize + DeserializeOwned + 'static + Default, { - pub fn new(max_generations: u64) -> Self { + pub fn new() -> Self { GeneticNodeWrapper:: { - max_generations, ..Default::default() } } - pub fn from(data: T, max_generations: u64, id: Uuid) -> Self { + pub fn from(data: T, id: Uuid) -> Self { GeneticNodeWrapper { node: Some(data), state: GeneticState::Simulate, generation: 1, - max_generations, id, } } @@ -125,10 +121,6 @@ where self.id } - pub fn max_generations(&self) -> u64 { - self.max_generations - } - pub fn generation(&self) -> u64 { self.generation } @@ -140,7 +132,6 @@ where pub async fn process_node(&mut self, gemla_context: T::Context) -> Result { let context = GeneticNodeContext { generation: self.generation, - max_generations: self.max_generations, id: self.id, gemla_context, }; @@ -151,14 +142,15 @@ where self.state = GeneticState::Simulate; } (GeneticState::Simulate, Some(n)) => { - n.simulate(context.clone()) + let next_generation = n + .simulate(context.clone()) .await .with_context(|| format!("Error simulating node: {:?}", self))?; - self.state = if self.generation >= self.max_generations { - GeneticState::Finish - } else { + self.state = if next_generation { GeneticState::Mutate + } else { + GeneticState::Finish }; } (GeneticState::Mutate, Some(n)) => { @@ -187,6 +179,7 @@ mod tests { #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] struct TestState { pub score: f64, + pub max_generations: u64, } #[async_trait] @@ -195,10 +188,14 @@ mod tests { async fn simulate( &mut self, - _context: GeneticNodeContext, - ) -> Result<(), Error> { + context: GeneticNodeContext, + ) -> Result { self.score += 1.0; - Ok(()) + if context.generation >= self.max_generations { + Ok(false) + } else { + Ok(true) + } } async fn mutate( @@ -211,7 +208,10 @@ mod tests { async fn initialize( _context: GeneticNodeContext, ) -> Result, Error> { - Ok(Box::new(TestState { score: 0.0 })) + Ok(Box::new(TestState { + score: 0.0, + max_generations: 2, + })) } async fn merge( @@ -226,13 +226,12 @@ mod tests { #[test] fn test_new() -> Result<(), Error> { - let genetic_node = GeneticNodeWrapper::::new(10); + let genetic_node = GeneticNodeWrapper::::new(); let other_genetic_node = GeneticNodeWrapper:: { node: None, state: GeneticState::Initialize, generation: 1, - max_generations: 10, id: genetic_node.id(), }; @@ -243,15 +242,17 @@ mod tests { #[test] fn test_from() -> Result<(), Error> { - let val = TestState { score: 0.0 }; + let val = TestState { + score: 0.0, + max_generations: 10, + }; let uuid = Uuid::new_v4(); - let genetic_node = GeneticNodeWrapper::from(val.clone(), 10, uuid); + let genetic_node = GeneticNodeWrapper::from(val.clone(), uuid); let other_genetic_node = GeneticNodeWrapper:: { node: Some(val), state: GeneticState::Simulate, generation: 1, - max_generations: 10, id: genetic_node.id(), }; @@ -262,9 +263,12 @@ mod tests { #[test] fn test_as_ref() -> Result<(), Error> { - let val = TestState { score: 3.0 }; + let val = TestState { + score: 3.0, + max_generations: 10, + }; let uuid = Uuid::new_v4(); - let genetic_node = GeneticNodeWrapper::from(val.clone(), 10, uuid); + let genetic_node = GeneticNodeWrapper::from(val.clone(), uuid); let ref_value = genetic_node.as_ref().unwrap(); @@ -275,9 +279,12 @@ mod tests { #[test] fn test_id() -> Result<(), Error> { - let val = TestState { score: 3.0 }; + let val = TestState { + score: 3.0, + max_generations: 10, + }; let uuid = Uuid::new_v4(); - let genetic_node = GeneticNodeWrapper::from(val.clone(), 10, uuid); + let genetic_node = GeneticNodeWrapper::from(val.clone(), uuid); let id_value = genetic_node.id(); @@ -286,24 +293,14 @@ mod tests { Ok(()) } - #[test] - fn test_max_generations() -> Result<(), Error> { - let val = TestState { score: 3.0 }; - let uuid = Uuid::new_v4(); - let genetic_node = GeneticNodeWrapper::from(val.clone(), 10, uuid); - - let max_generations = genetic_node.max_generations(); - - assert_eq!(max_generations, 10); - - Ok(()) - } - #[test] fn test_state() -> Result<(), Error> { - let val = TestState { score: 3.0 }; + let val = TestState { + score: 3.0, + max_generations: 10, + }; let uuid = Uuid::new_v4(); - let genetic_node = GeneticNodeWrapper::from(val.clone(), 10, uuid); + let genetic_node = GeneticNodeWrapper::from(val.clone(), uuid); let state = genetic_node.state(); @@ -314,7 +311,7 @@ mod tests { #[tokio::test] async fn test_process_node() -> Result<(), Error> { - let mut genetic_node = GeneticNodeWrapper::::new(2); + let mut genetic_node = GeneticNodeWrapper::::new(); assert_eq!(genetic_node.state(), GeneticState::Initialize); assert_eq!(genetic_node.process_node(()).await?, GeneticState::Simulate); diff --git a/gemla/src/core/mod.rs b/gemla/src/core/mod.rs index 766a7d0..d3de3f3 100644 --- a/gemla/src/core/mod.rs +++ b/gemla/src/core/mod.rs @@ -57,7 +57,6 @@ type SimulationTree = Box>>; /// ``` #[derive(Serialize, Deserialize, Copy, Clone)] pub struct GemlaConfig { - pub generations_per_height: u64, pub overwrite: bool, } @@ -126,9 +125,9 @@ where // Before we can process nodes we must create blank nodes in their place to keep track of which nodes have been processed // in the tree and which nodes have not. self.data - .mutate(|(d, c, _)| { + .mutate(|(d, _, _)| { let mut tree: Option> = - Gemla::increase_height(d.take(), c, steps); + Gemla::increase_height(d.take(), steps); mem::swap(d, &mut tree); }) .await?; @@ -268,11 +267,7 @@ where gemla_context.clone(), ) .await?; - tree.val = GeneticNodeWrapper::from( - *merged_node, - tree.val.max_generations(), - tree.val.id(), - ); + tree.val = GeneticNodeWrapper::from(*merged_node, tree.val.id()); } } (Some(l), Some(r)) => { @@ -284,11 +279,7 @@ where trace!("Copying node {}", l.val.id()); if let Some(left_node) = l.val.as_ref() { - GeneticNodeWrapper::from( - left_node.clone(), - tree.val.max_generations(), - tree.val.id(), - ); + GeneticNodeWrapper::from(left_node.clone(), tree.val.id()); } } (Some(l), None) => Gemla::merge_completed_nodes(l, gemla_context.clone()).await?, @@ -296,11 +287,7 @@ where trace!("Copying node {}", r.val.id()); if let Some(right_node) = r.val.as_ref() { - tree.val = GeneticNodeWrapper::from( - right_node.clone(), - tree.val.max_generations(), - tree.val.id(), - ); + tree.val = GeneticNodeWrapper::from(right_node.clone(), tree.val.id()); } } (None, Some(r)) => Gemla::merge_completed_nodes(r, gemla_context.clone()).await?, @@ -353,11 +340,7 @@ where } } - fn increase_height( - tree: Option>, - config: &GemlaConfig, - amount: u64, - ) -> Option> { + fn increase_height(tree: Option>, amount: u64) -> Option> { if amount == 0 { tree } else { @@ -365,13 +348,11 @@ where tree.as_ref().map(|t| t.height() as u64).unwrap_or(0) + amount - 1; Some(Box::new(Tree::new( - GeneticNodeWrapper::new(config.generations_per_height), - Gemla::increase_height(tree, config, amount - 1), + GeneticNodeWrapper::new(), + Gemla::increase_height(tree, amount - 1), // The right branch height has to equal the left branches total height if left_branch_height > 0 { - Some(Box::new(btree!(GeneticNodeWrapper::new( - left_branch_height * config.generations_per_height - )))) + Some(Box::new(btree!(GeneticNodeWrapper::new()))) } else { None }, @@ -446,6 +427,7 @@ mod tests { #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] struct TestState { pub score: f64, + pub max_generations: u64, } #[async_trait] @@ -454,10 +436,10 @@ mod tests { async fn simulate( &mut self, - _context: GeneticNodeContext, - ) -> Result<(), Error> { + context: GeneticNodeContext, + ) -> Result { self.score += 1.0; - Ok(()) + Ok(context.generation < self.max_generations) } async fn mutate( @@ -470,7 +452,10 @@ mod tests { async fn initialize( _context: GeneticNodeContext, ) -> Result, Error> { - Ok(Box::new(TestState { score: 0.0 })) + Ok(Box::new(TestState { + score: 0.0, + max_generations: 10, + })) } async fn merge( @@ -498,10 +483,7 @@ mod tests { assert!(!path.exists()); // Testing initial creation - let mut config = GemlaConfig { - generations_per_height: 1, - overwrite: true, - }; + let mut config = GemlaConfig { overwrite: true }; let mut gemla = Gemla::::new(&p, config, DataFormat::Json).await?; // Now we can use `.await` within the spawned blocking task. @@ -559,10 +541,7 @@ mod tests { CleanUp::new(&path).run(move |p| { rt.block_on(async { // Testing initial creation - let config = GemlaConfig { - generations_per_height: 10, - overwrite: true, - }; + let config = GemlaConfig { overwrite: true }; let mut gemla = Gemla::::new(&p, config, DataFormat::Json).await?; // Now we can use `.await` within the spawned blocking task. diff --git a/analysis.py b/visualize_simulation_tree.py similarity index 100% rename from analysis.py rename to visualize_simulation_tree.py From e2be40c3189ac2d8428c6a914b0fbef4fef1d89c Mon Sep 17 00:00:00 2001 From: vandomej Date: Tue, 9 Apr 2024 15:05:14 -0700 Subject: [PATCH 24/26] Discerning between mainline and offshoot --- analyze_data.py | 1 + gemla/src/bin/fighter_nn/mod.rs | 49 ++++++++++++++++++++++----------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/analyze_data.py b/analyze_data.py index 0ac2c16..703f23d 100644 --- a/analyze_data.py +++ b/analyze_data.py @@ -129,6 +129,7 @@ for node in right_nodes: for i in range(len(node_scores)): for (individual, score) in node_scores[i].items(): translated_node_scores.append((node["val"]["id"], i, score)) + scores.append(translated_node_scores) # Organize scores by individual and then by generation individual_generation_scores = defaultdict(lambda: defaultdict(list)) diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index 820d90b..aa96286 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -40,6 +40,8 @@ const NEURAL_NETWORK_INITIAL_WEIGHT_MIN: f32 = -2.0; const NEURAL_NETWORK_INITIAL_WEIGHT_MAX: f32 = 2.0; const NEURAL_NETWORK_CROSSBREED_SEGMENTS_MIN: usize = 2; const NEURAL_NETWORK_CROSSBREED_SEGMENTS_MAX: usize = 20; +const OFFSHOOT_GENERATIONAL_LENIENCE: u64 = 5; +const MAINLINE_GENERATIONAL_LENIENCE: u64 = 20; const SIMULATION_ROUNDS: usize = 5; const SURVIVAL_RATE: f32 = 0.5; @@ -74,6 +76,7 @@ pub struct FighterNN { // Shows how individuals are mapped from one generation to the next pub id_mapping: Vec>, pub lerp_amount: f32, + pub generational_lenience: u64, } #[async_trait] @@ -158,6 +161,7 @@ impl GeneticNode for FighterNN { mutation_weight_range: -mutation_weight_amplitude..mutation_weight_amplitude, id_mapping: vec![HashMap::new()], lerp_amount: 0.0, + generational_lenience: OFFSHOOT_GENERATIONAL_LENIENCE, })) } @@ -186,6 +190,18 @@ impl GeneticNode for FighterNN { }; let secondary_id = loop { + if allotted_simulations.is_empty() { + // Select a random id + let random_id = loop { + let id = thread_rng().gen_range(0..self.population_size); + if id != primary_id { + break id; + } + }; + + break random_id; + } + let id = thread_rng().gen_range(0..allotted_simulations.len()); let (i, _) = allotted_simulations[id]; @@ -294,7 +310,7 @@ impl GeneticNode for FighterNN { self.scores.push(scores); - Ok(should_continue(&self.scores)?) + Ok(should_continue(&self.scores, self.generational_lenience)?) } async fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { @@ -644,6 +660,7 @@ impl GeneticNode for FighterNN { mutation_weight_range, id_mapping: vec![HashMap::new()], lerp_amount, + generational_lenience: MAINLINE_GENERATIONAL_LENIENCE, })) } } @@ -654,8 +671,8 @@ impl FighterNN { } } -fn should_continue(scores: &[HashMap]) -> Result { - if scores.len() < 5 { +fn should_continue(scores: &[HashMap], lenience: u64) -> Result { + if scores.len() < lenience as usize { return Ok(true); } @@ -691,8 +708,8 @@ fn should_continue(scores: &[HashMap]) -> Result { } let highest_generation_index = scores.len() - 1; - let result = highest_generation_index - generation_with_highest_q3 < 5 - && highest_generation_index - generation_with_highest_median < 5; + let result = highest_generation_index - generation_with_highest_q3 < lenience as usize + && highest_generation_index - generation_with_highest_median < lenience as usize; debug!( "Highest Q3 value: {} at generation {}, Highest Median value: {} at generation {}, Continuing? {}", @@ -1534,57 +1551,57 @@ pub mod test { .collect(); assert!( - should_continue(scores[..0].as_ref()) + should_continue(scores[..0].as_ref(), 5) .expect("Failed to determine if the simulation should continue") == true ); assert!( - should_continue(scores[..1].as_ref()) + should_continue(scores[..1].as_ref(), 5) .expect("Failed to determine if the simulation should continue") == true ); assert!( - should_continue(scores[..2].as_ref()) + should_continue(scores[..2].as_ref(), 5) .expect("Failed to determine if the simulation should continue") == true ); assert!( - should_continue(scores[..3].as_ref()) + should_continue(scores[..3].as_ref(), 5) .expect("Failed to determine if the simulation should continue") == true ); assert!( - should_continue(scores[..4].as_ref()) + should_continue(scores[..4].as_ref(), 5) .expect("Failed to determine if the simulation should continue") == true ); assert!( - should_continue(scores[..5].as_ref()) + should_continue(scores[..5].as_ref(), 5) .expect("Failed to determine if the simulation should continue") == true ); assert!( - should_continue(scores[..6].as_ref()) + should_continue(scores[..6].as_ref(), 5) .expect("Failed to determine if the simulation should continue") == true ); assert!( - should_continue(scores[..7].as_ref()) + should_continue(scores[..7].as_ref(), 5) .expect("Failed to determine if the simulation should continue") == true ); assert!( - should_continue(scores[..8].as_ref()) + should_continue(scores[..8].as_ref(), 5) .expect("Failed to determine if the simulation should continue") == false ); assert!( - should_continue(scores[..9].as_ref()) + should_continue(scores[..9].as_ref(), 5) .expect("Failed to determine if the simulation should continue") == false ); assert!( - should_continue(scores[..10].as_ref()) + should_continue(scores[..10].as_ref(), 5) .expect("Failed to determine if the simulation should continue") == false ); From 4efba94ff477d3b9cf7603888caa44fc681c03e3 Mon Sep 17 00:00:00 2001 From: vandomej Date: Tue, 9 Apr 2024 15:16:21 -0700 Subject: [PATCH 25/26] Preparing for round 3 --- gemla/round2.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 gemla/round2.json diff --git a/gemla/round2.json b/gemla/round2.json deleted file mode 100644 index b2401a8..0000000 --- a/gemla/round2.json +++ /dev/null @@ -1 +0,0 @@ -[{"val":{"node":null,"state":"Initialize","generation":1,"max_generations":5,"id":"5ec4f3dc-8123-409e-825c-662681228ab7"},"left":{"val":{"node":{"id":"50ebdb65-5d8c-4ae0-a287-cf1752ee3e7a","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_50ebdb65-5d8c-4ae0-a287-cf1752ee3e7a","population_size":50,"generation":4,"scores":[{"31":-0.13612905,"33":-1.0776138,"49":1.0980284,"30":-1.9793463,"17":1.7722206,"35":0.5282012,"43":1.0499356,"10":-1.0785636,"40":1.1580324,"21":0.55297184,"19":2.3659606,"8":-1.1596327,"45":1.2928445,"15":-0.38365746,"7":-1.3709673,"18":2.3096547,"22":2.0469928,"28":-0.4978822,"41":4.144234,"5":0.07915659,"26":0.90480375,"37":1.494353,"39":-2.3988552,"47":0.34005648,"6":-7.033618,"27":1.0777633,"16":2.901632,"14":1.9946716,"20":3.7013183,"3":-2.2309659,"36":0.70247716,"4":-6.9653826,"29":0.45382744,"1":-2.6762161,"11":0.036121655,"13":-0.96337116,"34":-0.2231338,"38":-0.55380166,"42":1.7713648,"44":1.4386444,"9":0.028786521,"48":-4.3657136,"12":2.421192,"23":3.9476352,"24":-0.66796416,"25":-3.155373,"32":1.4074609,"0":-0.1740944,"46":0.6339666,"2":-0.53725517},{"4":0.7436782,"3":3.2122047,"5":2.1668441,"20":1.110868,"39":-2.9981642,"13":2.538597,"41":2.149324,"2":4.4572535,"10":1.7422009,"26":-4.81853,"29":-2.5022538,"15":0.740168,"23":5.280815,"32":0.2042454,"24":1.3782952,"8":1.3916748,"16":1.8820988,"27":-1.0135753,"44":4.2513857,"45":-7.277108,"48":-1.9649296,"28":-2.5748737,"36":-2.348684,"38":-4.5012045,"9":2.8673189,"21":0.24331684,"0":0.81623286,"40":-1.0026308,"43":-1.384711,"33":0.810703,"11":1.3250918,"12":1.6499374,"42":-2.7954354,"47":-1.0316321,"18":0.75795716,"37":1.5436422,"35":-2.120893,"49":1.3816634,"14":0.5867568,"17":4.5584574,"19":1.022777,"25":-2.384229,"46":-2.6336,"22":-3.5684505,"6":2.907734,"34":0.3625866,"1":1.4271891,"7":3.1036994,"31":0.25269595,"30":-4.3955135},{"29":-8.933199,"25":0.8984653,"11":0.40797964,"13":2.9981232,"16":1.4885747,"19":2.711595,"30":-1.1489832,"32":-2.470939,"33":-6.3783817,"35":-2.0185058,"37":-2.388184,"12":0.82435036,"9":3.27996,"28":-4.4827237,"39":-2.178865,"41":-1.7175516,"8":0.8589166,"24":1.9250387,"42":-1.1390394,"2":4.301921,"10":0.27182764,"31":-2.491881,"3":4.3458285,"44":-3.2156835,"17":0.30330983,"45":-1.0126257,"48":-7.657384,"49":-2.3429596,"38":-1.107363,"22":0.24029942,"15":3.652279,"26":0.6514605,"34":-3.7715619,"21":-0.17929637,"7":0.930509,"47":-6.146611,"18":-0.08523829,"5":4.084977,"20":0.6663896,"23":2.6454415,"1":0.7503186,"14":-2.896947,"40":-4.771443,"43":-4.230453,"46":-6.056484,"6":2.0080876,"27":-1.3334317,"36":-1.4744998,"4":2.8780289,"0":1.2278525},{"25":-3.211018,"12":0.7650175,"5":1.7488182,"46":4.561215,"39":-0.8045467,"45":0.5278336,"6":1.3769066,"9":1.4209664,"21":-1.5987527,"35":-3.4995422,"34":2.4080498,"15":1.3436749,"20":-0.8802644,"24":0.43879455,"16":1.7340788,"33":-0.36120838,"49":-0.82267416,"17":-0.90683824,"41":-4.4410596,"3":3.8727448,"8":-0.1619916,"0":-1.2115911,"14":0.1691064,"18":0.17399707,"26":-0.35883158,"31":-1.3777262,"30":-1.5434114,"13":1.8553307,"44":-1.6750336,"10":1.5830498,"48":-2.2214565,"32":-1.914746,"1":3.3461983,"28":-0.8201621,"40":-2.3096998,"19":-0.114658594,"43":-3.153609,"38":0.8486921,"7":-0.6367208,"22":2.2888017,"4":1.4505427,"37":-1.3729974,"27":0.19734183,"36":-0.059907578,"42":-0.3139604,"23":3.9043097,"47":1.7215679,"11":1.9946696,"29":-5.348122,"2":3.916179},{"48":0.8495762,"33":-1.5749425,"7":1.8273497,"37":-0.0934452,"9":0.8297514,"32":-1.9380062,"5":2.6225371,"0":2.9893174,"28":1.1498768,"30":-3.0286043,"16":0.7810628,"36":-3.065391,"39":-0.6764022,"29":-2.6871326,"40":-1.1660333,"34":-7.6445327,"3":3.5084794,"38":-2.1503594,"12":-1.3775728,"17":-0.28026563,"2":0.8047453,"26":-0.2800478,"10":1.8954436,"41":-0.9755514,"43":-2.3991253,"44":1.7079166,"45":-3.5458252,"8":1.9785389,"24":1.5254066,"11":0.24450493,"18":1.1810827,"15":2.0651846,"14":2.0105624,"13":3.0678258,"19":1.1483902,"20":0.7748546,"23":1.3448334,"25":0.0561754,"35":-3.5130627,"46":-1.4552925,"27":2.6987588,"49":0.06736002,"4":4.091585,"22":0.54525864,"6":1.2180727,"21":2.4670484,"42":3.6728942,"1":3.7313595,"31":-1.5604084,"47":2.743227}],"nn_shapes":{"44":[18,27,29,27,13,22,23,30,21,8],"32":[18,29,18,8],"12":[18,7,5,6,30,29,31,13,7,8],"7":[18,5,15,22,7,8],"17":[18,3,5,8],"31":[18,33,16,11,21,24,8],"25":[18,12,31,18,17,22,11,8],"18":[18,4,22,8],"34":[18,3,20,14,27,3,23,32,15,16,8],"35":[18,19,12,33,8],"43":[18,11,8],"6":[18,25,22,27,18,8],"36":[18,23,6,34,15,15,19,31,8],"21":[18,4,27,26,8],"0":[18,27,21,25,6,8],"20":[18,29,24,16,20,32,30,23,8],"23":[18,4,30,26,15,30,12,5,21,27,8],"39":[18,16,8],"10":[18,13,8],"9":[18,21,9,17,4,23,4,8],"19":[18,24,6,5,31,8],"29":[18,23,18,30,9,4,6,25,10,8],"24":[18,8,8],"11":[18,19,21,6,32,31,9,10,8],"3":[18,34,21,18,23,20,29,4,8],"8":[18,9,28,14,34,19,30,25,8],"4":[18,24,19,16,19,5,25,3,13,4,8],"5":[18,16,24,20,16,8],"49":[18,12,14,8],"47":[18,6,7,8],"28":[18,16,20,14,14,8],"27":[18,12,33,31,24,8],"46":[18,5,7,30,5,14,8],"13":[18,23,31,19,31,17,10,3,8],"26":[18,6,30,9,30,13,34,11,31,4,8],"14":[18,29,23,7,5,29,9,8],"15":[18,10,6,8],"30":[18,31,27,7,24,24,10,34,17,8],"38":[18,34,8],"41":[18,7,17,12,15,23,21,8,14,8],"45":[18,4,8],"48":[18,31,18,12,28,8],"42":[18,21,10,15,3,30,14,8,9,8],"1":[18,30,8],"16":[18,16,29,18,5,6,22,23,8],"2":[18,11,12,6,8],"40":[18,33,11,9,11,33,12,29,8],"22":[18,10,14,28,29,11,18,20,10,8],"37":[18,27,28,25,25,8],"33":[18,8,32,17,16,20,14,8]},"crossbreed_segments":17,"weight_initialization_range":{"start":-0.8264158,"end":0.878737},"minor_mutation_rate":0.6007006,"major_mutation_rate":0.69530445,"mutation_weight_range":{"start":-0.4734988,"end":0.4734988}},"state":"Finish","generation":5,"max_generations":5,"id":"50ebdb65-5d8c-4ae0-a287-cf1752ee3e7a"},"left":{"val":{"node":{"id":"d3819667-2b18-4b95-9a28-26c6e03dc476","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_d3819667-2b18-4b95-9a28-26c6e03dc476","population_size":50,"generation":4,"scores":[{"36":0.52841055,"15":-1.1991132,"37":-3.2981744,"49":-1.8880517,"13":-1.6512722,"32":1.0124598,"43":2.494171,"26":-1.3937424,"19":3.493512,"21":2.3532522,"17":2.689321,"31":2.2110536,"46":-0.4825391,"6":1.8422686,"10":-0.32022244,"22":-0.3459033,"42":-1.683349,"23":4.053544,"47":-2.2043169,"1":1.863919,"12":2.0911965,"38":0.24497437,"20":1.547824,"18":1.7499046,"5":0.49471003,"14":1.9232328,"30":0.8192028,"9":2.6325006,"4":1.8692623,"11":1.515634,"24":3.58217,"25":-1.2022996,"28":1.0040348,"29":-1.4738817,"41":3.5459945,"2":-3.6696267,"0":0.4801852,"3":-0.09719024,"39":-3.908245,"33":-0.91764957,"40":0.8810172,"44":2.2009876,"45":-1.3817523,"27":-0.19515392,"35":-0.027263165,"34":-3.189738,"16":3.8335247,"48":0.9000354,"7":1.028139,"8":2.6590905},{"10":1.3170006,"17":2.231814,"34":-1.2865344,"45":2.3518786,"41":3.6458764,"21":0.8024928,"16":1.9739323,"22":1.8600712,"25":-9.58074,"1":0.24613556,"32":-3.8310456,"5":2.9235337,"12":3.2730007,"38":0.218226,"42":3.8644855,"13":2.9329138,"27":-6.526806,"40":1.8908739,"49":-5.292937,"44":-1.129288,"28":-6.468558,"3":4.0713334,"36":-1.6670258,"0":3.819156,"23":-1.2354233,"24":3.5202663,"37":-12.305478,"39":-5.751393,"29":-1.6295974,"4":4.000201,"15":1.8389952,"20":2.0815675,"48":0.855487,"26":-1.5036308,"30":-3.9943376,"14":2.4486287,"47":2.6005633,"31":-3.4889805,"43":2.9267437,"46":-0.8074959,"35":-4.0209146,"11":0.9406686,"2":3.3860729,"9":4.1048174,"33":-6.9062905,"8":5.9823117,"19":-0.19810537,"18":-1.2411083,"7":2.8926134,"6":-3.7741578},{"7":0.039615013,"19":2.1138473,"14":-3.9792695,"26":1.5719395,"2":-1.1006941,"11":0.29687914,"31":-1.6652381,"46":-4.654462,"48":1.0881627,"12":0.6574399,"13":2.580459,"47":0.5501457,"30":1.0701891,"1":3.955607,"4":2.6093411,"5":3.8768296,"23":-0.16426,"28":-5.369283,"42":-0.7794517,"39":-4.815303,"25":-11.748198,"27":-6.5224624,"3":3.2421424,"6":1.9797084,"16":0.32828337,"24":2.2681708,"8":3.5486038,"40":-0.5599359,"18":1.7272246,"41":-0.6892798,"49":3.3327858,"35":-6.8564734,"15":2.197027,"43":1.1656187,"9":0.5838804,"10":1.7632539,"44":1.3067775,"38":-4.1817884,"29":-0.010108185,"17":1.4017338,"20":1.1528027,"21":-2.0243993,"0":1.8688208,"37":-3.9298809,"32":1.608084,"34":0.29795903,"22":-1.0562317,"33":1.5627401,"36":1.9605706,"45":-4.1273623},{"22":3.7864814,"48":-5.1172805,"44":-7.1750364,"45":-7.1881766,"27":-3.211064,"18":2.5377452,"21":-0.0047807456,"23":-2.5914617,"38":-2.026207,"13":2.7289197,"14":-0.86196077,"47":2.3228345,"32":-3.6571102,"33":-1.8783233,"6":2.7211597,"26":-1.2817702,"40":-5.2434363,"43":-1.3648752,"29":-3.2677646,"37":-5.4217234,"49":6.32213,"31":0.65605927,"0":4.0934405,"16":-1.9610307,"15":-0.5933281,"35":1.0039356,"42":-5.741083,"41":0.1720238,"3":-0.526399,"7":0.4030818,"36":3.3475738,"17":-1.7582073,"30":-1.572907,"46":-3.8359694,"24":2.9443383,"8":1.5571549,"1":3.898974,"2":4.2115107,"11":0.589545,"12":2.5865626,"25":0.7350615,"34":-3.614159,"10":3.3951313,"19":-0.8030449,"20":0.31260815,"28":1.0681746,"9":1.0806137,"4":3.06236,"39":-2.972804,"5":0.6295648},{"47":-1.8156517,"10":2.70822,"11":0.013542938,"48":-6.912471,"5":-0.35663193,"44":-3.8048778,"49":-1.3085688,"14":1.7900337,"24":-5.8084245,"8":2.2216375,"25":-6.1367373,"7":-0.45275903,"30":-1.7194979,"34":0.13795105,"42":-3.814258,"16":3.908256,"9":2.6561737,"13":-1.8770411,"21":1.3039081,"40":-0.24722286,"17":-2.5117145,"41":-5.126404,"4":-4.3981633,"15":2.1462803,"18":-1.8711113,"12":2.7365608,"6":-3.9147289,"20":1.0436176,"29":0.31122702,"2":4.1983523,"39":-0.13585219,"43":-7.8228645,"45":-1.9057821,"3":3.946886,"1":3.267223,"35":0.2371548,"31":1.5849909,"46":0.8540207,"37":0.3498088,"26":-7.9124327,"0":-0.50944835,"19":2.1885152,"32":4.8325567,"27":-4.7971716,"36":-2.634483,"22":2.0692124,"23":-0.3195792,"38":-8.581514,"28":-3.5876858,"33":-5.4614477}],"nn_shapes":{"49":[18,21,7,8],"19":[18,10,6,8],"15":[18,29,23,7,5,29,9,8],"21":[18,13,8],"46":[18,9,28,14,34,19,30,25,8],"20":[18,21,9,17,4,23,4,8],"29":[18,25,22,27,18,8],"41":[18,29,20,13,4,10,18,3,28,8],"27":[18,19,29,28,19,9,23,8],"31":[18,19,21,6,32,31,9,10,8],"12":[18,24,6,5,31,8],"2":[18,4,30,26,15,30,12,5,21,27,8],"34":[18,24,19,16,19,5,25,3,13,4,8],"45":[18,3,5,29,21,8],"28":[18,26,34,8],"0":[18,22,24,26,20,26,7,23,4,4,8],"13":[18,29,25,31,16,20,9,20,8,8],"7":[18,16,11,12,31,23,8],"11":[18,34,21,18,23,20,29,4,8],"38":[18,16,4,3,8,8,34,15,8],"44":[18,25,9,13,26,10,30,28,8],"16":[18,4,27,26,8],"6":[18,30,29,12,8],"39":[18,11,12,6,8],"18":[18,12,11,13,20,25,8],"5":[18,7,23,7,5,15,7,8],"22":[18,23,31,19,31,17,10,3,8],"9":[18,3,5,8],"23":[18,27,21,25,6,8],"26":[18,15,34,8],"24":[18,23,13,23,6,22,17,12,8],"30":[18,32,4,6,5,7,6,31,8],"32":[18,8,8],"35":[18,16,24,20,16,8],"37":[18,5,15,22,7,8],"40":[18,30,8],"42":[18,30,27,6,31,6,8],"43":[18,4,21,19,8],"10":[18,4,22,8],"47":[18,31,19,8],"3":[18,10,14,28,29,11,18,20,10,8],"25":[18,32,20,20,33,3,26,27,19,14,8],"14":[18,7,5,6,30,29,31,13,7,8],"17":[18,15,4,15,25,8],"33":[18,32,8],"4":[18,12,14,3,22,20,8],"36":[18,23,8,3,20,8,5,11,28,12,8],"8":[18,16,29,18,5,6,22,23,8],"48":[18,17,11,11,23,34,8],"1":[18,29,24,16,20,32,30,23,8]},"crossbreed_segments":17,"weight_initialization_range":{"start":-1.4009845,"end":0.71760863},"minor_mutation_rate":0.58211184,"major_mutation_rate":0.3748024,"mutation_weight_range":{"start":-0.7133639,"end":0.7133639}},"state":"Finish","generation":5,"max_generations":5,"id":"d3819667-2b18-4b95-9a28-26c6e03dc476"},"left":{"val":{"node":{"id":"0c1e64dc-6ddf-4dbb-bf6e-e8218b925194","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_0c1e64dc-6ddf-4dbb-bf6e-e8218b925194","population_size":50,"generation":4,"scores":[{"16":2.7842956,"17":3.2546368,"18":2.834027,"22":3.6374855,"23":0.55781853,"39":1.5122628,"30":-0.0950552,"38":-1.6534564,"42":0.9564872,"45":2.796212,"32":3.1485744,"1":-0.056508802,"26":0.27695063,"27":-0.9529072,"35":-0.33414435,"41":0.9368752,"49":1.2474676,"4":1.9148009,"29":-0.8555522,"19":0.7134926,"20":4.182901,"36":2.0954838,"25":1.1097888,"24":1.1636646,"48":2.0578096,"2":0.13178372,"11":3.930512,"21":2.7500353,"28":-2.7004008,"8":-0.14735499,"0":2.4760363,"12":1.8176826,"33":-0.8801649,"5":1.1445743,"43":1.6762855,"44":1.7246205,"14":0.12700202,"13":-1.0921293,"3":0.8263828,"9":-0.63989145,"40":1.0889086,"15":0.38060492,"6":2.162825,"34":0.8519516,"7":1.5481932,"37":-0.30656368,"47":0.12655163,"31":-0.334538,"46":-0.10322921,"10":2.1048977},{"2":4.0336885,"11":1.9123293,"28":1.1152416,"0":2.6964252,"8":2.643211,"5":1.9824579,"14":1.9350994,"20":-2.117892,"27":-1.8575739,"32":3.808883,"40":-1.701194,"43":1.2392315,"45":-1.0804688,"4":-1.0602609,"41":-2.600569,"47":-3.656672,"48":-1.1821935,"22":3.038847,"18":2.4507322,"17":1.0593803,"26":4.082426,"37":3.3133476,"15":2.7473907,"42":3.6408525,"33":2.8012383,"6":1.2478402,"3":3.200624,"44":2.551259,"16":2.6077023,"34":-3.2371726,"30":-5.005698,"38":-2.7984025,"19":0.5384928,"23":2.693526,"12":0.55265176,"25":-3.2805107,"36":1.1476547,"46":-2.3774133,"1":3.9180245,"29":1.2915124,"7":2.743547,"9":1.824632,"31":-1.9200878,"35":-8.691515,"49":-9.31593,"39":-0.28261098,"10":2.151708,"21":2.0202444,"24":-0.98241556,"13":1.4065293},{"42":-0.1698114,"45":-0.9050466,"12":0.33145928,"20":2.1155891,"48":1.5600955,"0":3.3518238,"36":-3.1458013,"3":-0.07596121,"8":2.8887057,"24":1.2255046,"40":-2.242703,"46":-0.57636845,"37":-6.9247026,"47":-0.058634616,"10":0.91950357,"5":0.44212827,"13":2.6170678,"28":2.0092816,"30":-6.610072,"33":-8.984638,"18":2.6534104,"35":0.76075226,"6":3.1536205,"41":-0.43077993,"27":2.3785758,"19":3.0031989,"4":1.9033867,"7":2.1948285,"17":1.896376,"2":3.6457944,"11":3.13304,"25":3.723556,"29":-3.2704506,"32":1.8960766,"14":0.113289595,"39":-0.10378437,"34":-3.1352968,"15":0.8491783,"38":0.1912586,"49":-1.1872209,"9":2.6683042,"16":5.404914,"22":2.237846,"1":3.7422562,"21":1.3533652,"23":2.1378658,"44":3.8486843,"26":-0.4825582,"43":0.19282512,"31":0.47917405},{"28":-7.894442,"18":-0.5081554,"12":4.472903,"19":2.968141,"29":0.3170418,"30":-2.707025,"49":-0.5579669,"27":-0.058833785,"17":2.36423,"31":1.5799856,"39":-4.114499,"3":0.97037774,"8":2.5219383,"34":-10.829961,"6":3.2782185,"35":-4.850678,"22":1.2201262,"42":-1.1182394,"44":1.4363219,"48":-7.355731,"7":4.1624565,"11":2.2376986,"32":-0.18231583,"1":0.35062942,"15":3.3270416,"20":2.3351607,"26":3.0582035,"40":2.0540967,"5":0.055399038,"47":-3.5113704,"45":-3.669038,"4":3.82812,"2":3.748457,"9":2.0209565,"33":0.15889016,"21":1.0599203,"0":5.94346,"38":0.7334972,"41":2.1650863,"37":4.926632,"10":2.7536874,"14":1.9314429,"36":-2.9116273,"24":2.108854,"43":-6.7626557,"46":-4.723344,"25":-0.56377923,"23":1.9115753,"16":1.0394429,"13":2.78011},{"15":1.4749693,"13":1.7178307,"11":2.5947552,"20":1.5962856,"5":3.9152215,"10":1.9538119,"1":-0.40396795,"29":-2.258254,"37":1.105455,"17":2.5183578,"16":0.46422988,"24":2.2004254,"12":3.1671283,"48":4.001414,"2":1.536825,"21":1.3735349,"27":-1.3144708,"39":-5.0491495,"49":2.1212723,"34":-0.9968918,"40":-0.9705099,"7":3.0003047,"44":2.7472353,"47":-0.22038798,"45":-4.9677887,"26":-7.6167555,"38":-1.8368845,"22":2.5509207,"23":1.6071596,"30":-4.427607,"9":-1.5331998,"6":1.4454396,"18":1.8494036,"19":2.0788915,"31":1.3762347,"32":-1.3371822,"33":-2.4997528,"41":-1.9396175,"43":3.1764946,"28":-3.7262979,"8":-0.8827422,"3":5.203038,"14":1.4151531,"36":1.4748793,"42":3.4031627,"4":4.065404,"0":1.4889172,"25":2.7390487,"46":-0.332081,"35":-1.5676318}],"nn_shapes":{"32":[18,15,28,16,12,9,17,13,34,20,8],"4":[18,27,21,25,6,8],"12":[18,12,11,13,20,25,8],"22":[18,29,25,31,16,20,9,20,8,8],"30":[18,11,30,9,13,27,32,8],"27":[18,19,29,14,29,3,8,8],"24":[18,34,21,18,23,20,29,4,8],"34":[18,26,8,9,29,8],"21":[18,11,3,20,7,8],"1":[18,10,8],"44":[18,4,27,26,8],"9":[18,26,13,16,17,7,3,8],"5":[18,13,8],"48":[18,23,31,19,31,17,10,3,8],"31":[18,10,33,8],"28":[18,12,12,10,27,23,30,7,8],"40":[18,28,30,21,8],"33":[18,8,6,5,21,34,11,25,10,8],"46":[18,18,31,14,34,23,30,8],"29":[18,28,5,6,18,31,4,6,13,8],"13":[18,30,29,12,8],"23":[18,7,23,7,5,15,7,8],"16":[18,13,12,32,16,34,8],"35":[18,4,5,20,15,8],"11":[18,7,5,6,30,29,31,13,7,8],"8":[18,7,9,24,24,24,29,24,8],"14":[18,12,4,27,27,16,8],"19":[18,3,5,8],"18":[18,16,11,12,31,23,8],"38":[18,25,24,23,22,19,8],"42":[18,21,9,17,4,23,4,8],"15":[18,29,24,16,20,32,30,23,8],"37":[18,25,21,17,12,8],"3":[18,23,13,23,6,22,17,12,8],"17":[18,24,6,5,31,8],"20":[18,12,14,3,22,20,8],"6":[18,23,7,18,18,19,6,6,33,33,8],"36":[18,22,24,26,20,26,7,23,4,4,8],"43":[18,10,6,8],"45":[18,20,31,28,33,23,8],"49":[18,4,22,8],"47":[18,33,27,24,13,7,30,21,19,8],"25":[18,29,23,7,5,29,9,8],"2":[18,10,14,28,29,11,18,20,10,8],"10":[18,16,29,18,5,6,22,23,8],"39":[18,5,11,33,33,22,19,8],"41":[18,34,8],"7":[18,15,4,15,25,8],"26":[18,23,4,8],"0":[18,4,30,26,15,30,12,5,21,27,8]},"crossbreed_segments":16,"weight_initialization_range":{"start":-1.3029977,"end":0.51041603},"minor_mutation_rate":0.4312437,"major_mutation_rate":0.2126301,"mutation_weight_range":{"start":-0.86694086,"end":0.86694086}},"state":"Finish","generation":5,"max_generations":5,"id":"0c1e64dc-6ddf-4dbb-bf6e-e8218b925194"},"left":{"val":{"node":{"id":"fd37c853-1504-4464-8897-1daff7a72be5","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_fd37c853-1504-4464-8897-1daff7a72be5","population_size":50,"generation":4,"scores":[{"42":-2.2721553,"22":1.0232317,"40":-5.9998856,"1":-3.994938,"25":-9.930249,"27":-7.8477364,"37":-5.496006,"5":1.1646187,"46":-4.96828,"3":-1.1402376,"13":1.9225012,"26":-1.3158404,"14":2.4945993,"30":-7.240546,"23":3.8293235,"29":-3.8496826,"21":3.7215874,"49":-9.225942,"31":-8.099161,"11":0.88319874,"7":-2.0457823,"2":-0.12721822,"45":-5.7793913,"28":-4.1380286,"15":2.9860673,"20":2.128657,"32":-3.8224225,"9":2.4512286,"24":5.1030297,"19":0.27070022,"41":-4.9243727,"17":-1.272063,"47":-3.7681346,"12":0.56694984,"48":1.2148348,"44":-5.1761513,"34":-7.1951623,"36":-6.7270365,"33":-8.101049,"35":-5.470336,"38":-2.4845133,"39":-7.134122,"43":-8.256643,"4":1.1818645,"6":0.9297161,"8":-0.4258314,"10":-0.3768764,"16":-0.06290223,"18":2.514954,"0":-2.4905317},{"27":0.95811284,"46":2.4985795,"8":-1.7966896,"6":3.3313987,"37":2.4091609,"9":0.89002323,"10":-0.24809322,"28":2.7617157,"15":1.6055084,"30":-1.4701109,"31":0.90125,"33":-1.4433162,"36":-1.8132203,"12":-0.7547363,"3":2.346582,"44":-4.4732213,"45":0.81152236,"48":4.4929247,"40":-4.770377,"11":0.87109774,"16":0.63175476,"35":-5.9830647,"1":3.976232,"39":-2.9760668,"7":1.3261242,"20":0.057714842,"34":-3.805713,"17":2.653192,"43":-2.0348437,"2":3.822759,"21":0.73448956,"29":-6.0006204,"49":-1.1507516,"25":1.983723,"22":0.6264278,"38":2.6556149,"26":-4.3593946,"42":0.35124415,"4":2.6484601,"24":0.44920617,"18":-1.1584501,"0":3.052092,"14":1.2551721,"41":-1.5921788,"13":-0.44852152,"47":-2.7429378,"23":-3.6057796,"19":2.7150357,"5":2.6166,"32":-5.0448375},{"11":0.24148598,"16":1.1744473,"23":0.87205726,"27":0.3939582,"26":-4.8855453,"7":0.73869574,"33":-3.6728272,"3":2.1907496,"37":-7.545512,"44":-1.4448053,"14":-0.97644913,"6":1.1288795,"30":-1.8206075,"35":2.1864667,"40":1.5490462,"8":2.7416787,"45":-5.4727135,"9":2.8946023,"47":1.2556527,"19":1.3244388,"31":-0.78719276,"5":3.572733,"18":1.7603016,"42":0.83883345,"43":-1.3601041,"49":-0.20906155,"46":-0.31403762,"13":2.7593741,"34":-3.5647533,"36":-4.0503144,"25":1.7323751,"28":-3.2712636,"10":1.7972724,"38":1.6502354,"39":-0.40534177,"41":-0.043547057,"0":3.3609338,"48":-2.3421752,"21":1.6295173,"12":-0.112102196,"24":0.1910994,"17":0.79251236,"22":0.5870294,"29":1.0588541,"4":3.7645988,"32":1.7781379,"1":4.139815,"2":3.7459385,"15":2.458321,"20":3.5645466},{"28":-0.3401602,"19":1.3713418,"5":3.5159538,"22":6.9944353,"31":-4.0907526,"36":1.895592,"4":-1.5086001,"15":2.4590595,"39":3.181663,"3":1.6952648,"47":-4.1927,"43":-0.6410026,"1":4.124709,"8":2.1984046,"42":-0.8253414,"25":-0.8266258,"27":-2.1104884,"41":-8.608764,"9":2.3678985,"0":4.0736403,"24":0.827624,"16":1.4439828,"32":0.6308866,"34":-1.3887271,"45":-0.5371846,"49":0.9086536,"12":2.6437063,"7":3.4995968,"13":0.5635818,"17":1.8616883,"23":1.22412,"26":2.1465325,"37":-2.5422812,"33":-2.0116632,"6":3.1111484,"14":2.2081165,"29":-1.4582347,"18":1.7700018,"38":-3.9882941,"2":3.620486,"10":1.5943378,"44":3.4898422,"46":-3.2846375,"40":-5.23321,"48":1.3899428,"21":2.149033,"11":3.0369506,"20":1.2599217,"30":-3.8670013,"35":-11.008142},{"12":2.35251,"13":2.1651278,"37":-7.216863,"20":2.3004658,"32":2.0750155,"7":-1.6450062,"38":-5.5044084,"21":2.023457,"9":2.7243361,"24":1.5391018,"17":-1.7105582,"23":1.9464728,"22":-0.13848563,"1":3.4145145,"29":1.8971004,"6":1.8432477,"2":4.0936923,"14":1.5620744,"28":-1.6894335,"31":2.6148155,"36":-0.6197856,"39":-7.6506586,"35":2.5756009,"8":3.1064951,"40":-3.3752155,"43":7.8085213,"46":-4.395103,"5":3.348603,"16":3.7527301,"47":1.8600842,"15":3.301286,"11":1.1328014,"18":0.058104564,"30":-1.3595872,"19":0.6658308,"10":2.5431685,"49":-1.0252998,"0":1.3744335,"4":2.5909457,"45":-3.7512455,"27":-1.452537,"34":0.92168653,"42":5.8911624,"26":-1.8472269,"44":-0.4115014,"41":-1.6939585,"25":-0.563695,"48":1.5778819,"33":-0.27565208,"3":2.497078}],"nn_shapes":{"42":[18,7,23,7,5,15,7,8],"37":[18,29,17,25,6,8],"13":[18,7,9,24,24,24,29,24,8],"33":[18,7,6,7,8],"45":[18,21,13,8],"3":[18,7,5,6,30,29,31,13,7,8],"0":[18,19,10,34,8],"8":[18,24,6,5,31,8],"23":[18,13,8],"10":[18,12,11,13,20,25,8],"25":[18,7,30,30,25,19,20,5,8],"17":[18,10,15,22,8],"24":[18,34,28,11,11,29,11,12,8],"35":[18,30,29,12,8],"38":[18,34,22,28,13,14,8],"41":[18,24,12,30,17,33,8],"31":[18,29,24,16,20,32,30,23,8],"40":[18,28,11,20,6,13,29,8],"2":[18,29,25,31,16,20,9,20,8,8],"16":[18,11,3,20,7,8],"1":[18,12,14,3,22,20,8],"34":[18,17,18,30,31,3,6,12,25,11,8],"20":[18,26,13,16,17,7,3,8],"39":[18,17,8,8],"27":[18,19,8,19,26,6,25,18,21,23,8],"21":[18,23,7,18,18,19,6,6,33,33,8],"26":[18,31,6,20,8],"28":[18,5,21,9,11,10,13,27,17,16,8],"19":[18,14,10,33,8,11,4,21,28,8],"29":[18,27,21,25,6,8],"46":[18,22,8],"49":[18,33,34,13,8],"6":[18,10,14,28,29,11,18,20,10,8],"47":[18,23,13,23,6,22,17,12,8],"48":[18,10,8],"44":[18,14,27,5,6,12,16,17,19,8],"7":[18,18,3,14,8],"4":[18,12,4,27,27,16,8],"11":[18,19,8],"15":[18,16,11,12,31,23,8],"12":[18,16,29,18,5,6,22,23,8],"14":[18,4,30,26,15,30,12,5,21,27,8],"30":[18,4,19,18,8],"9":[18,13,12,32,16,34,8],"5":[18,3,5,8],"32":[18,15,4,15,25,8],"36":[18,18,16,18,11,33,15,15,27,8],"18":[18,16,16,27,18,8],"22":[18,17,12,14,21,14,6,21,19,8],"43":[18,34,21,18,23,20,29,4,8]},"crossbreed_segments":12,"weight_initialization_range":{"start":-0.7746323,"end":0.64793515},"minor_mutation_rate":0.89887977,"major_mutation_rate":0.37956625,"mutation_weight_range":{"start":-0.9222891,"end":0.9222891}},"state":"Finish","generation":5,"max_generations":5,"id":"fd37c853-1504-4464-8897-1daff7a72be5"},"left":{"val":{"node":{"id":"0ad6c0a9-268e-4f36-b986-a095ce958e87","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_0ad6c0a9-268e-4f36-b986-a095ce958e87","population_size":50,"generation":4,"scores":[{"28":1.4615542,"29":-3.351231,"41":0.11434956,"47":3.353677,"24":-2.3349676,"27":-3.0977936,"43":4.7768126,"39":1.232477,"20":2.131682,"10":0.65271276,"32":3.4047546,"37":0.24620943,"31":1.5759578,"23":-0.32563427,"3":1.1822358,"40":-0.42240685,"19":0.9445168,"2":-1.0627751,"42":3.125601,"33":0.80939466,"46":3.1065967,"11":-4.4918694,"6":0.7574291,"49":-3.4887753,"8":-4.1754675,"0":-9.796589,"5":-0.2148296,"13":1.5807844,"17":-1.4137825,"34":-3.5904312,"9":-4.3053155,"16":-1.9381826,"15":0.35073262,"18":4.652893,"44":3.688664,"35":0.5031668,"1":-1.327118,"45":3.9197388,"48":-2.0562541,"21":0.21452656,"30":1.4445921,"36":0.4763755,"4":-0.6387031,"12":-1.0553591,"38":-1.564517,"25":-1.0317254,"26":0.948751,"7":0.051721383,"14":2.7838516,"22":0.3971446},{"8":-0.4450551,"15":3.0406582,"22":0.1862926,"10":-1.0695591,"42":-4.579817,"11":-0.03542762,"43":-2.354273,"20":3.1427789,"5":3.5550492,"24":-2.7110906,"44":-5.6810613,"1":1.412229,"14":3.0840802,"17":1.0067269,"35":-5.2434473,"46":-1.7983131,"2":3.8090882,"19":1.1941998,"21":1.5723072,"30":-8.034946,"12":2.1055703,"36":-4.125515,"9":0.80242157,"6":3.3065217,"23":1.9119755,"25":1.5494883,"13":5.389744,"37":0.41448742,"40":1.6354656,"26":2.8828807,"28":1.547584,"49":1.1942029,"48":-4.0498896,"7":1.1680777,"45":-3.7617173,"4":-0.83832073,"27":-7.9582443,"32":0.027616406,"33":0.85028744,"38":-3.1612341,"47":-4.9559927,"3":3.7921467,"31":-2.7395563,"29":-5.5815687,"34":0.15367377,"39":1.9094998,"18":1.4039291,"41":6.31805,"0":1.6352074,"16":1.0272758},{"17":1.4303916,"10":2.0075524,"6":-1.0944701,"1":-6.3332605,"45":-1.1300482,"21":1.5855234,"41":-3.5284915,"15":2.2013316,"49":-6.670298,"35":-0.72289884,"46":-3.121521,"13":0.12067218,"4":1.492248,"24":-1.156515,"22":1.3185016,"30":-0.1220412,"20":1.0285561,"33":0.25970158,"38":-2.8305871,"9":-3.185809,"43":-2.4596064,"18":0.10767321,"44":-6.2140107,"12":0.65513146,"8":3.7638183,"27":-2.3283656,"37":-5.2938666,"5":2.8565912,"7":-1.6208298,"11":-0.5699972,"31":-1.8019298,"39":-3.8167872,"42":0.62593377,"40":-3.3886192,"47":0.55262935,"34":4.8451715,"48":-0.4967394,"19":0.8086522,"26":-0.16367832,"25":-1.7693208,"2":3.8625455,"29":-2.7617586,"3":3.8275685,"28":2.4664369,"32":-2.9393113,"23":2.0031307,"0":2.9781694,"14":2.3455732,"16":-1.9647186,"36":-2.0843992},{"5":2.3199508,"17":2.4556768,"6":0.99130905,"2":3.7347991,"21":-0.27123898,"27":-5.349469,"37":0.2067368,"0":4.260925,"10":2.0144703,"45":-1.8468329,"49":0.3703224,"29":-1.9481332,"28":-10.377831,"41":-6.3821745,"20":0.505881,"19":2.3852916,"42":-3.1312802,"46":-1.7531841,"1":3.7902591,"3":0.75912887,"25":-3.2048562,"32":3.359387,"23":0.85752183,"39":-0.91159695,"14":0.66083515,"16":1.6131394,"12":4.2249913,"48":-3.1543155,"36":-3.265039,"26":-0.2300566,"47":-2.798001,"33":-1.2588333,"13":3.555592,"15":1.6225817,"30":1.1084969,"4":-0.80275536,"22":1.080758,"31":-3.770164,"11":3.1434116,"35":-3.1652713,"9":4.3430805,"34":-1.3829889,"18":0.41391096,"7":2.5888634,"43":1.8247061,"40":-1.506657,"44":1.1751028,"8":0.24729638,"38":-0.5430144,"24":0.84149456},{"26":-1.5581883,"17":0.45890158,"13":2.6077938,"20":1.5608526,"6":0.7407518,"14":1.8970188,"21":2.307454,"24":-2.2616897,"31":-1.2380049,"36":-3.3436444,"2":4.3228645,"44":-7.0414248,"45":-4.322558,"37":-2.0368595,"16":-0.7485286,"0":1.4736874,"8":2.7514439,"9":2.7842798,"38":-2.5296543,"29":-2.35339,"33":-2.799434,"42":1.5451206,"28":0.9679454,"34":-5.0157824,"49":0.44937867,"27":-4.0082808,"4":3.7921321,"7":3.8526225,"12":1.0690136,"22":-0.85086995,"19":-2.555062,"47":-2.2651973,"25":-3.2443783,"15":0.97329867,"46":-3.4256077,"41":-7.144478,"1":1.1129873,"10":1.4387215,"32":-1.0147102,"40":-11.496406,"48":-4.2306504,"5":3.3042362,"30":0.15390578,"43":0.9680899,"3":4.003913,"11":3.149682,"23":-2.0422919,"18":2.985143,"35":3.154074,"39":-0.3716902}],"nn_shapes":{"4":[18,23,7,18,18,19,6,6,33,33,8],"6":[18,29,25,31,16,20,9,20,8,8],"45":[18,8,25,8],"36":[18,18,8],"39":[18,17,28,4,8],"9":[18,11,3,20,7,8],"25":[18,30,11,14,24,26,9,29,15,13,8],"30":[18,4,24,19,15,33,10,34,15,8],"27":[18,6,29,26,8],"23":[18,34,12,10,13,28,21,15,28,8],"20":[18,19,8],"28":[18,7,5,6,30,29,31,13,7,8],"11":[18,16,16,27,18,8],"18":[18,10,15,22,8],"37":[18,12,13,12,20,32,8],"41":[18,3,20,20,8,8],"7":[18,17,12,14,21,14,6,21,19,8],"34":[18,25,8],"38":[18,15,31,25,7,30,33,33,8,8],"2":[18,34,28,11,11,29,11,12,8],"35":[18,14,10,33,8,11,4,21,28,8],"43":[18,12,4,27,27,16,8],"48":[18,31,17,8],"32":[18,33,14,8,15,8],"47":[18,30,33,8,24,22,13,28,3,8],"17":[18,12,14,3,22,20,8],"22":[18,5,21,4,7,9,13,28,8],"31":[18,31,18,8,29,8],"19":[18,7,19,18,9,26,13,32,30,8],"33":[18,17,8,23,30,23,8,33,27,8],"44":[18,32,15,3,11,5,11,3,8],"10":[18,24,6,5,31,8],"8":[18,16,11,12,31,23,8],"29":[18,25,8],"0":[18,13,12,32,16,34,8],"3":[18,13,8],"5":[18,26,13,16,17,7,3,8],"1":[18,18,3,14,8],"15":[18,3,5,8],"14":[18,16,29,18,5,6,22,23,8],"12":[18,10,14,28,29,11,18,20,10,8],"13":[18,4,30,26,15,30,12,5,21,27,8],"24":[18,23,8,8],"16":[18,15,30,8],"26":[18,10,29,8],"40":[18,11,8],"42":[18,12,11,13,20,25,8],"46":[18,19,8],"49":[18,19,10,34,8],"21":[18,7,9,24,24,24,29,24,8]},"crossbreed_segments":12,"weight_initialization_range":{"start":-0.79062134,"end":0.6353039},"minor_mutation_rate":0.9082846,"major_mutation_rate":0.37210783,"mutation_weight_range":{"start":-0.9348126,"end":0.9348126}},"state":"Finish","generation":5,"max_generations":5,"id":"0ad6c0a9-268e-4f36-b986-a095ce958e87"},"left":{"val":{"node":{"id":"2230be79-f940-4689-972a-7008e3f4c998","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_2230be79-f940-4689-972a-7008e3f4c998","population_size":50,"generation":4,"scores":[{"1":-2.3773646,"0":-2.1571355,"12":1.4148271,"21":-2.421327,"33":-1.934478,"17":0.138854,"34":-0.4928736,"44":1.4438745,"15":1.8977562,"36":0.9711355,"4":1.9662882,"22":-0.73786503,"3":1.9117216,"11":-1.7609564,"8":4.3882666,"37":-0.9355248,"38":0.67264163,"2":0.89526814,"20":1.2228067,"41":-0.69157594,"35":0.2589668,"42":0.7870628,"9":0.29466486,"26":0.046588603,"39":-2.6555886,"10":-1.7522205,"45":0.22093217,"47":2.8745866,"23":2.3134782,"24":0.2931752,"13":-1.7469364,"31":-0.48889366,"46":-1.4888307,"18":1.6355522,"28":-1.6607144,"29":-0.091461204,"14":2.8278089,"30":-1.5403664,"5":0.28095183,"27":-2.2598128,"40":0.17108059,"43":1.2745006,"25":1.8205446,"48":0.19277701,"19":0.7784854,"7":1.7413013,"49":-0.46817464,"6":0.6888849,"32":-1.3222702,"16":-0.4496932},{"17":-2.3785129,"3":2.9041004,"26":-1.5274507,"29":-4.735349,"48":-1.8411024,"9":-0.77052134,"11":-0.08661283,"14":-1.7952125,"16":2.9026654,"15":-1.8081363,"41":-2.6796186,"12":1.0565262,"49":0.60432214,"19":-1.4364105,"34":-1.241155,"36":-0.7845459,"35":-5.2296076,"28":-4.3732424,"30":-0.015474224,"2":0.133679,"47":-1.1977552,"22":-1.8609911,"44":-4.1988745,"40":-5.811055,"23":-0.8860939,"43":-2.7589722,"0":-1.8361069,"6":0.9826768,"33":-7.5137763,"37":-1.3062632,"39":-1.9096992,"5":-1.5289714,"25":-2.9348187,"7":1.6092517,"8":0.8130643,"20":-2.0613518,"24":-0.57125616,"27":-1.2508152,"32":-3.5545921,"42":0.017004633,"13":1.1759713,"10":4.379163,"18":0.5535432,"1":1.4643844,"4":-0.1658,"46":-0.48931485,"38":-1.1717209,"21":1.2540135,"45":-2.5831802,"31":-1.0906768},{"19":2.758,"23":-0.1547494,"16":-0.24027161,"27":-4.7461,"1":1.9463758,"0":5.49269,"38":-4.5831237,"39":-0.752874,"49":-3.560801,"18":-1.372174,"25":-0.24467158,"2":-1.4239506,"10":-1.7297821,"45":-0.550079,"20":-2.916182,"24":-1.0988299,"3":1.0242682,"9":-0.9811675,"21":-0.55461586,"32":-4.361062,"7":-1.9895757,"44":-0.217258,"46":1.3056983,"48":0.027753597,"12":0.4383232,"13":1.7189554,"36":-2.2416625,"5":0.8830037,"11":-1.4609058,"8":-0.16048487,"22":-3.4026666,"41":-3.1433291,"29":-2.5550942,"6":0.9628898,"30":2.8254886,"4":3.1442847,"31":-3.0069652,"14":-2.2467086,"28":-2.3709545,"17":-5.425764,"26":-0.6892742,"34":0.4522006,"37":2.8570902,"42":-8.46829,"47":-4.117214,"35":-0.49623865,"15":0.57778114,"33":-1.0330775,"40":-2.8532898,"43":-1.4928547},{"40":-0.7036076,"30":-0.5719384,"1":2.25287,"14":-1.288854,"16":1.8005784,"44":1.8507036,"47":-3.295477,"17":0.53820264,"24":-4.5791464,"33":-2.7593288,"15":-1.035099,"18":0.6495812,"35":-7.1353035,"13":-1.0557783,"8":3.2177284,"43":2.1751304,"3":0.6698944,"7":0.34247094,"46":-2.612004,"31":3.3641772,"26":-1.1441598,"29":-2.781163,"2":-0.13234572,"5":1.5759518,"45":-8.701353,"48":-4.505052,"36":0.5430554,"10":0.24432358,"49":-4.2009077,"20":-1.2227812,"19":-0.1535736,"23":-1.3555021,"22":-2.4631474,"34":0.70411575,"12":1.7876114,"27":-5.090214,"11":2.5564446,"32":-3.6049714,"28":-0.011442797,"37":-5.639792,"39":-2.1910725,"25":6.6744385,"0":0.80345714,"4":-0.25791565,"21":-0.5257034,"6":1.4308555,"38":0.21877857,"41":-0.8577938,"42":-3.589934,"9":1.4351888},{"3":0.99701536,"30":-1.2227508,"40":-1.5728966,"47":-0.9126034,"2":1.7522328,"17":-0.95693076,"42":0.54109496,"43":-4.4988546,"46":-2.362868,"23":-0.06628381,"20":-2.6594634,"31":-1.575641,"18":0.302563,"6":0.8362234,"4":3.3849263,"25":-5.9296846,"13":-0.29305857,"26":-2.5544639,"27":-5.2713065,"36":-0.62222356,"49":-3.8993137,"5":-4.794875,"1":4.0791264,"12":2.951697,"15":0.21713142,"22":0.8922955,"24":0.20521124,"41":-7.768069,"48":-2.7011123,"21":-3.9334495,"16":-2.0758946,"34":-7.4594717,"35":-7.6091437,"37":-4.4366155,"9":2.1347003,"38":0.23643506,"33":-7.0710535,"28":-1.8344015,"11":2.30687,"45":-2.57183,"19":2.1262608,"7":1.5562618,"39":-1.2651775,"14":1.1440918,"44":-6.099185,"32":-1.6038958,"10":1.5246786,"0":-1.7383505,"8":1.2577031,"29":-6.3010983}],"nn_shapes":{"6":[18,16,16,27,18,8],"21":[18,30,24,7,23,15,6,8],"0":[18,6,7,18,8],"14":[18,16,29,18,5,6,22,23,8],"27":[18,21,14,31,15,27,26,34,7,8],"40":[18,14,10,4,8],"41":[18,7,10,28,26,16,23,31,28,8],"39":[18,30,8,33,7,25,17,18,26,31,8],"43":[18,15,15,8],"47":[18,34,28,11,11,29,11,12,8],"38":[18,16,11,12,31,23,8],"4":[18,34,12,10,13,28,21,15,28,8],"20":[18,29,30,28,15,21,16,4,7,9,8],"31":[18,15,7,20,8],"45":[18,13,8],"29":[18,13,8],"9":[18,19,8],"28":[18,15,8],"44":[18,28,8],"11":[18,7,9,24,24,24,29,24,8],"24":[18,29,25,31,16,20,9,20,8,8],"36":[18,13,8],"17":[18,18,3,14,8],"15":[18,17,12,14,21,14,6,21,19,8],"10":[18,15,30,8],"32":[18,25,8],"30":[18,13,12,32,16,34,8],"2":[18,10,15,22,8],"25":[18,14,34,25,18,11,27,23,17,8],"5":[18,28,28,22,12,26,16,8],"3":[18,4,30,26,15,30,12,5,21,27,8],"18":[18,11,3,20,7,8],"23":[18,26,13,16,17,7,3,8],"34":[18,19,5,34,8,11,8],"46":[18,5,14,4,10,8],"1":[18,23,8,8],"33":[18,21,17,21,8],"12":[18,5,21,4,7,9,13,28,8],"8":[18,3,5,8],"19":[18,7,19,18,9,26,13,32,30,8],"37":[18,13,11,5,32,8],"48":[18,14,5,4,5,8],"49":[18,20,8],"22":[18,10,14,28,29,11,18,20,10,8],"7":[18,12,14,3,22,20,8],"16":[18,7,18,32,5,14,8],"42":[18,24,6,5,31,8],"35":[18,28,8],"26":[18,30,7,34,8,13,24,26,27,8],"13":[18,23,7,18,18,19,6,6,33,33,8]},"crossbreed_segments":11,"weight_initialization_range":{"start":-1.215388,"end":1.5817409},"minor_mutation_rate":0.77005047,"major_mutation_rate":0.7637305,"mutation_weight_range":{"start":-0.6824868,"end":0.6824868}},"state":"Finish","generation":5,"max_generations":5,"id":"2230be79-f940-4689-972a-7008e3f4c998"},"left":{"val":{"node":{"id":"5830af23-37c2-45fd-bc48-4be19bb47989","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_5830af23-37c2-45fd-bc48-4be19bb47989","population_size":50,"generation":4,"scores":[{"4":-0.4398686,"13":0.6435866,"25":-1.1424577,"7":2.2064023,"17":-1.1729944,"29":-2.188397,"36":-0.7252862,"41":-0.06775464,"27":-3.0315576,"9":-1.8444555,"23":0.47301316,"6":0.5633224,"18":0.9158624,"31":-2.2378566,"24":2.491937,"3":-1.7823699,"37":0.29173023,"5":0.6846366,"20":1.0448742,"32":-3.6604862,"38":-1.1224955,"39":-0.30770817,"8":0.31371704,"42":-0.2333417,"45":-0.7877932,"46":-0.55997026,"47":1.1051524,"19":2.861733,"48":-1.1579436,"30":-0.6199918,"43":-1.3103287,"0":0.073735476,"22":1.867547,"1":0.40812922,"16":0.16757026,"28":-5.616639,"40":3.373998,"11":-2.1214867,"14":-0.3355748,"44":0.52112305,"33":-6.5058165,"26":-2.0824904,"21":1.0021068,"10":0.48167562,"49":-0.87629604,"2":-0.0054346086,"34":-1.2387748,"35":0.28463203,"15":-0.5290295,"12":0.7119624},{"13":0.8524078,"39":-1.6857016,"7":-1.7376686,"11":-2.2416666,"27":-4.389194,"40":-4.86255,"5":0.4651522,"25":1.8499638,"42":-1.521401,"12":-0.790142,"20":0.776415,"30":-0.6416456,"32":-1.8126523,"45":-6.633146,"1":-2.1365325,"21":1.382251,"6":0.81977844,"17":-0.80237997,"2":1.1953188,"15":3.9234138,"46":-3.14258,"19":1.2063272,"31":-1.2679306,"47":-3.2304752,"29":0.713826,"22":-0.07156279,"35":0.57997036,"43":-1.6351938,"44":-2.756394,"0":1.2175782,"8":0.32304603,"18":-1.7216289,"48":-2.5695126,"37":-7.174632,"24":-0.32638264,"23":0.7591497,"16":-0.09428916,"26":-2.6883242,"38":-2.8030906,"34":-0.2926392,"41":-2.8787274,"3":1.6496121,"10":2.4494653,"14":0.769758,"28":-2.195226,"36":-0.8310772,"33":-1.4455681,"49":-0.66453344,"9":0.2630516,"4":0.21751109},{"35":-1.2995249,"39":-4.3697367,"49":-5.5780034,"1":1.9374145,"43":-5.1250587,"3":0.29240862,"11":-1.3891447,"12":0.593122,"17":0.4923257,"28":-0.6408792,"47":-6.84591,"32":-2.3980255,"4":-0.40212068,"2":-0.30574152,"15":1.4105376,"18":3.3082592,"23":0.95887107,"25":2.410621,"26":-7.540658,"31":-0.2849912,"38":-0.06010101,"40":-3.8810935,"6":-1.9693276,"16":-0.35065803,"29":-1.5561249,"42":-9.490059,"27":-1.828867,"22":1.1340528,"45":-2.825584,"48":-0.9286879,"0":-4.842866,"24":0.6481105,"7":2.5768476,"36":-3.6143699,"41":-1.6077397,"8":-0.5062324,"34":-1.9471012,"13":1.0228976,"20":-0.8251265,"14":0.8828863,"5":1.4987826,"10":0.5583974,"37":-1.1287113,"46":-0.537475,"30":0.0993626,"33":-5.52423,"44":-2.2344456,"21":0.2783533,"9":0.026928205,"19":0.14576219},{"17":-2.6396623,"2":1.2169989,"37":-8.008817,"45":2.6028926,"46":-1.5116042,"6":3.4691956,"31":-0.165995,"8":0.32855827,"10":0.15786763,"19":-0.16524422,"27":-1.9287678,"23":-0.1792428,"28":-4.0277762,"22":0.37375817,"36":-2.5048773,"14":-0.28106713,"25":1.5299038,"21":0.91780585,"15":-1.4555721,"40":-4.9625564,"48":-7.442425,"39":-3.0028021,"1":-0.29292637,"13":-0.034496427,"49":-1.8842728,"9":-1.4072593,"41":-0.3804465,"35":-7.516181,"4":-2.062589,"33":-6.622129,"7":1.1507305,"34":-5.826934,"47":-1.2597684,"24":-0.73715174,"18":0.9347435,"26":-0.58290416,"29":1.3509219,"42":-0.2881294,"43":-2.917752,"0":2.1211834,"30":-0.30634144,"20":-3.504623,"38":-2.5137649,"3":1.5459003,"11":-2.5077972,"16":-1.1970947,"32":1.4198091,"12":-0.53543484,"44":-0.65983814,"5":1.3583586},{"27":-4.6737294,"47":-0.9961362,"48":-1.5854882,"11":-1.8830795,"37":-1.1362636,"34":-6.0494013,"7":-0.3092016,"2":1.2706958,"15":-2.2915282,"25":-1.0481479,"29":-4.1591725,"9":-0.04948742,"31":-0.43341333,"40":1.9337126,"49":0.19426656,"13":0.230414,"1":2.1338658,"23":-1.7014267,"3":-0.30542445,"33":4.549133,"35":-4.031805,"17":-1.013982,"32":-7.554162,"36":-4.573109,"46":0.38773063,"20":1.140489,"5":1.2783581,"14":0.28659493,"43":-0.90195465,"8":-0.5335846,"19":0.97964114,"18":0.7644297,"21":2.3947785,"10":0.17983632,"12":-2.02406,"16":-0.095197245,"24":0.14134718,"28":1.012642,"26":-5.5137,"22":-2.4713922,"30":-3.1684103,"4":-1.7671635,"38":0.7613734,"39":-3.4774318,"6":1.4320437,"41":-2.7136345,"42":-3.4777806,"0":-1.8335673,"44":-4.0395794,"45":-5.5930243}],"nn_shapes":{"20":[18,18,3,14,8],"13":[18,15,30,8],"28":[18,7,18,32,5,14,8],"12":[18,25,14,12,8,16,7,8],"17":[18,8,12,8,24,18,8],"3":[18,34,12,10,13,28,21,15,28,8],"24":[18,12,14,3,22,20,8],"19":[18,17,12,14,21,14,6,21,19,8],"40":[18,30,24,7,23,15,6,8],"2":[18,11,3,20,7,8],"41":[18,15,28,8],"42":[18,6,19,16,27,11,21,18,24,8],"44":[18,33,8],"36":[18,8,7,4,15,15,8],"25":[18,3,13,28,33,14,9,11,10,8],"7":[18,4,30,26,15,30,12,5,21,27,8],"11":[18,18,22,6,19,12,24,34,8],"37":[18,30,18,14,24,26,33,25,28,33,8],"6":[18,29,30,28,15,21,16,4,7,9,8],"8":[18,23,8,8],"4":[18,20,18,4,12,8],"33":[18,29,25,31,16,20,9,20,8,8],"39":[18,7,20,20,14,4,17,8],"43":[18,6,7,18,8],"35":[18,26,9,8],"45":[18,4,4,19,8],"10":[18,3,5,8],"14":[18,7,9,24,24,24,29,24,8],"34":[18,6,7,20,8],"22":[18,8,22,29,16,25,8,23,10,21,8],"30":[18,3,4,6,8],"16":[18,28,28,22,12,26,16,8],"15":[18,16,10,13,10,14,3,11,8],"5":[18,7,19,18,9,26,13,32,30,8],"9":[18,16,16,27,18,8],"26":[18,6,13,11,8],"18":[18,16,29,18,5,6,22,23,8],"27":[18,27,11,26,3,17,8,8],"31":[18,10,15,22,8],"38":[18,23,7,18,18,19,6,6,33,33,8],"49":[18,19,8],"29":[18,18,20,9,19,13,4,8],"48":[18,30,32,22,16,10,13,16,27,24,8],"32":[18,5,29,18,16,9,31,8],"0":[18,6,21,24,5,31,27,8],"21":[18,26,13,16,17,7,3,8],"47":[18,16,34,19,20,8],"1":[18,10,14,28,29,11,18,20,10,8],"23":[18,25,13,20,21,12,18,8,17,8],"46":[18,5,21,4,7,9,13,28,8]},"crossbreed_segments":13,"weight_initialization_range":{"start":-1.6086724,"end":1.7038059},"minor_mutation_rate":0.36123097,"major_mutation_rate":0.60697865,"mutation_weight_range":{"start":-0.86246336,"end":0.86246336}},"state":"Finish","generation":5,"max_generations":5,"id":"5830af23-37c2-45fd-bc48-4be19bb47989"},"left":{"val":{"node":{"id":"d7dc25e5-16bd-4f5d-bc97-d6c4cf90012c","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_d7dc25e5-16bd-4f5d-bc97-d6c4cf90012c","population_size":50,"generation":4,"scores":[{"36":-2.443375,"37":1.1562526,"39":-1.4698045,"43":1.9774078,"22":-1.2957172,"11":-3.543326,"45":1.1007092,"0":0.13766284,"18":-1.5081861,"27":-0.3628974,"2":-1.5020592,"15":-1.8695787,"17":-0.80195874,"21":-3.627962,"30":-5.9871097,"47":-0.5287416,"31":0.6174214,"13":-1.3917341,"38":-0.4312748,"16":-1.3465332,"33":0.92242736,"35":-0.14989817,"49":4.272779,"14":-1.5017221,"24":1.4262507,"6":-3.547879,"10":-2.916342,"4":-1.141561,"32":-1.5708299,"41":-0.026479607,"20":-0.16278799,"19":-1.0929224,"12":-1.6322893,"29":-3.2093625,"28":-1.5375618,"44":0.67286664,"48":-1.4393549,"40":2.4525964,"26":-3.0939457,"3":-0.638409,"1":0.39259672,"9":-1.5627674,"46":2.6744673,"34":-0.57457024,"42":-0.70243216,"8":-2.3440309,"25":5.3659525,"5":-3.8967907,"23":-2.4046814,"7":-1.3113697},{"43":-3.5765548,"32":-2.1894574,"40":-0.056246854,"44":-1.1792139,"3":1.122174,"25":-0.17178361,"29":-3.5132928,"7":-0.00574913,"4":1.2176167,"45":1.701143,"1":-0.41889125,"17":-0.2031568,"2":0.7972824,"15":-0.5265984,"23":0.3870982,"20":-0.8553486,"28":-1.6365826,"42":-2.0912087,"26":-1.9612887,"16":-1.4218798,"31":-0.1845134,"47":-2.828169,"49":-2.789091,"34":-4.926751,"19":-1.0707716,"39":-2.5893881,"13":-1.0763242,"12":-1.3562354,"27":-4.985302,"24":-0.39440042,"6":-1.4130237,"35":-3.373304,"37":-2.530553,"48":-1.9251556,"30":-5.641555,"46":-3.7538414,"9":0.46599898,"18":3.1130311,"8":-0.59825116,"33":-3.1443129,"36":-6.31389,"21":-1.6509422,"10":0.79040325,"5":-2.260046,"0":3.8864777,"38":-0.5514248,"41":-2.7165523,"22":-0.4323124,"11":1.0230696,"14":-0.2367413},{"11":1.9855608,"27":-1.4113008,"32":-3.47152,"34":-0.8354224,"35":-3.2586715,"4":1.4771069,"19":-3.441225,"5":-0.73485357,"14":-0.8333043,"23":-0.11942999,"0":4.844884,"25":-0.5912832,"3":0.92040044,"1":-0.76377815,"20":-2.0964956,"33":-0.37407398,"38":0.5312749,"44":-0.2386789,"49":-0.033635713,"12":-1.5284121,"22":0.57188094,"29":1.2233804,"31":-3.2222774,"48":1.4144858,"8":1.0541319,"21":-1.6275711,"40":-1.5101508,"6":-0.439957,"2":0.8432387,"26":-2.0842004,"41":-0.9327258,"15":-4.2353735,"18":-0.5040212,"10":2.760879,"17":2.14171,"24":0.4595502,"28":-2.1864777,"13":0.118149005,"36":-0.7745973,"7":0.67550546,"16":-0.50533116,"9":-2.03656,"37":-2.1948545,"42":-1.2179236,"43":-5.7127614,"45":0.96074045,"30":-2.7779126,"46":1.2078952,"47":-2.6667004,"39":-2.3364167},{"29":-1.49889,"47":-2.1373265,"49":-3.668885,"19":-1.9121835,"43":-4.294961,"42":-2.510038,"5":-6.127954,"18":-0.4878006,"24":-0.50900334,"35":-6.8719015,"37":-3.2887795,"17":-6.090243,"9":0.98944414,"26":1.0927036,"34":-4.9022627,"10":0.84963435,"20":-3.501018,"25":0.13348675,"1":0.02200427,"16":-4.878383,"4":2.2152524,"39":-5.288896,"11":0.9839722,"45":-1.3084223,"31":0.835742,"38":-4.1196938,"6":-0.4101388,"14":-5.849814,"8":1.5794041,"27":-6.5084815,"48":-3.34514,"0":-2.3548312,"33":-1.4235249,"7":3.1076987,"44":-0.27251154,"15":-0.2992934,"32":-5.1047645,"40":-0.7286312,"28":-0.87923414,"41":0.9774147,"3":-1.7796749,"13":1.8801941,"23":-2.5226865,"22":-0.39809638,"21":0.9538826,"2":0.3000278,"12":-0.7506649,"30":-1.2880104,"36":3.7323055,"46":-3.7501194},{"4":1.0086969,"46":-0.08042721,"18":-1.179418,"44":-4.586928,"5":4.1943126,"13":-0.15399918,"0":-1.765187,"40":-5.726017,"45":-7.071813,"32":-0.38681263,"33":-3.899791,"48":-7.4941225,"30":-2.2340627,"28":-4.9254932,"49":-1.9380242,"3":2.641837,"47":0.88087475,"20":-2.7248514,"6":-1.6207275,"10":-0.9453846,"14":-0.588112,"17":0.2461802,"8":-2.4899194,"24":-0.06998464,"25":-0.8650482,"12":1.0286778,"16":-0.3848494,"2":-0.7636143,"22":-0.87525463,"21":-0.65220153,"35":-3.7077782,"38":-0.86415654,"11":-1.1537424,"26":-1.1066633,"41":2.4212365,"39":-8.511892,"34":0.031094,"31":-2.6876805,"19":0.20523944,"36":0.41543216,"9":-0.22608939,"7":-3.7244701,"15":2.012662,"37":-4.712545,"1":6.5607452,"23":0.22806843,"27":0.32851115,"29":-6.4183474,"42":3.8774147,"43":1.1255028}],"nn_shapes":{"3":[18,26,13,16,17,7,3,8],"0":[18,26,8],"35":[18,19,20,30,9,30,18,20,4,21,8],"16":[18,20,18,4,12,8],"31":[18,14,32,33,3,8],"27":[18,15,30,8],"28":[18,22,11,15,13,13,24,5,34,8],"11":[18,23,32,20,18,8],"37":[18,28,24,8],"45":[18,21,8],"7":[18,25,22,14,4,33,9,22,12,9,8],"6":[18,5,34,8],"2":[18,6,21,24,5,31,27,8],"5":[18,25,13,20,21,12,18,8,17,8],"8":[18,12,33,33,12,4,15,34,17,8],"30":[18,24,34,14,6,20,22,15,6,6,8],"32":[18,34,12,10,13,28,21,15,28,8],"23":[18,18,22,6,19,12,24,34,8],"25":[18,12,30,8],"40":[18,31,6,34,27,15,34,33,8],"41":[18,18,3,14,8],"20":[18,20,31,6,17,15,19,25,31,8],"48":[18,7,24,32,29,19,20,8],"13":[18,29,30,28,15,21,16,4,7,9,8],"19":[18,3,5,8],"42":[18,8,22,29,16,25,8,23,10,21,8],"22":[18,28,4,10,11,7,34,19,3,8],"24":[18,23,8,8],"44":[18,25,19,16,19,30,8],"34":[18,16,16,27,18,8],"49":[18,21,3,18,11,8],"29":[18,3,28,30,23,4,8],"26":[18,22,27,15,12,9,8],"43":[18,16,29,18,5,6,22,23,8],"10":[18,31,27,12,3,24,31,8],"12":[18,8,12,8,24,18,8],"18":[18,31,6,29,30,32,32,14,11,7,8],"33":[18,24,21,12,8],"38":[18,6,17,28,12,4,22,30,8],"39":[18,3,22,17,3,28,7,25,16,22,8],"17":[18,25,14,12,8,16,7,8],"46":[18,4,30,26,15,30,12,5,21,27,8],"9":[18,7,19,18,9,26,13,32,30,8],"14":[18,11,3,20,7,8],"4":[18,28,28,22,12,26,16,8],"21":[18,10,14,28,29,11,18,20,10,8],"47":[18,16,10,13,10,14,3,11,8],"15":[18,17,12,14,21,14,6,21,19,8],"36":[18,7,9,24,24,24,29,24,8],"1":[18,12,14,3,22,20,8]},"crossbreed_segments":17,"weight_initialization_range":{"start":-1.7525467,"end":1.6750996},"minor_mutation_rate":0.19365302,"major_mutation_rate":0.94476026,"mutation_weight_range":{"start":-0.84336406,"end":0.84336406}},"state":"Finish","generation":5,"max_generations":5,"id":"d7dc25e5-16bd-4f5d-bc97-d6c4cf90012c"},"left":{"val":{"node":{"id":"206450bc-272b-41f7-ae89-fc4f12be753c","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_206450bc-272b-41f7-ae89-fc4f12be753c","population_size":50,"generation":4,"scores":[{"4":-0.25408143,"41":-1.7904724,"36":2.1737509,"49":0.20309429,"0":-0.66195565,"1":-1.8757442,"25":-3.5965798,"38":-6.863303,"37":0.46780187,"10":-2.0059624,"5":-5.5871625,"15":-2.403483,"18":-0.8859708,"23":-1.2753251,"27":-4.9748425,"6":-1.0913737,"8":-6.0900583,"28":-7.0202684,"9":-4.8177304,"19":-7.19686,"32":-2.7875524,"2":-7.0430055,"16":-0.27839962,"12":-0.2691624,"34":-1.479561,"35":-7.1771135,"39":-1.6123358,"11":-7.47368,"33":-6.8079133,"14":-2.8988595,"22":-4.932962,"42":-3.2319355,"43":-1.6130539,"47":1.0813572,"21":-5.6781116,"30":0.978965,"45":-2.5380788,"26":-2.465695,"17":-5.9036236,"20":-3.9086146,"31":-1.2463293,"3":-3.2792873,"24":-5.369993,"7":-1.4513847,"46":6.1682773,"13":-3.4915504,"29":-6.566017,"44":-8.941375,"40":-5.520095,"48":-1.0354421},{"35":-0.2504428,"2":-2.866654,"38":-2.699653,"22":-3.781758,"27":-1.5028169,"41":-2.1340575,"46":0.68042946,"48":2.0310922,"29":-1.670792,"5":-2.4222636,"8":-3.758992,"13":-2.5876176,"39":-2.672819,"42":-5.368996,"4":-1.3166544,"11":-1.0092744,"14":-3.5402977,"33":-2.136273,"6":-1.0932586,"15":-2.5740986,"37":-7.9974265,"9":-0.7977718,"25":-1.6041262,"1":0.63081247,"16":-2.3773406,"20":-1.7264913,"28":-0.089106,"12":-1.3272784,"36":0.61650956,"40":0.2590232,"43":-1.9761422,"31":-2.0207806,"7":0.1699496,"24":-3.923205,"0":0.35101363,"18":-2.2779722,"19":-3.3019118,"26":-6.8219934,"10":-2.0487385,"30":-5.7969003,"21":-2.0886593,"32":-5.1758175,"3":-0.2843196,"45":-3.3521304,"17":-2.688929,"47":-0.36435205,"44":-2.9112706,"23":-6.120496,"34":-6.7256265,"49":-1.3379397},{"35":-1.9879253,"6":0.4575228,"25":-5.48533,"33":0.63332397,"8":0.315913,"42":-6.9842176,"5":-2.0301785,"13":-0.19631381,"10":-0.439648,"9":-0.047661208,"37":-0.032142986,"16":-1.9775356,"46":-5.1132545,"48":-2.9102721,"14":-1.6374485,"18":-2.2513611,"39":-5.9638968,"40":-3.6864495,"2":1.8884017,"43":-3.5454178,"24":1.7852094,"45":-2.5057929,"17":-1.4369357,"32":-1.0873439,"30":-1.1533614,"44":-1.3637924,"41":0.17533419,"27":-3.9604943,"36":-2.5420253,"7":-1.3091543,"38":-3.7847595,"47":-3.3268592,"0":-2.795135,"1":0.38470575,"12":-1.0349476,"26":-1.6940864,"23":-2.038426,"28":-2.5738132,"49":0.49575263,"31":-1.5538758,"22":-0.2278616,"19":-0.84008443,"11":-3.588379,"4":4.72171,"3":-2.2519011,"15":-1.9365683,"29":-3.695866,"34":-8.262297,"20":-2.5298142,"21":2.4993515},{"4":-2.017178,"41":-3.7915883,"27":-1.1440285,"28":-0.910886,"14":-1.0659852,"26":-5.911423,"20":-2.0808756,"35":-1.6167637,"46":-6.1646094,"9":-3.002094,"25":-2.4201577,"47":-6.4115615,"37":-1.7778126,"8":0.04078598,"1":-1.8136123,"13":-3.4314759,"12":-1.7723328,"38":0.24750924,"31":-4.733556,"16":-1.5186998,"23":-0.7909092,"30":-2.4318392,"32":1.933485,"11":-0.15842238,"21":-2.3946443,"34":-2.4807293,"33":-2.7696655,"45":-7.775463,"40":-2.2423255,"19":2.3957248,"18":-2.4072661,"39":-4.6428156,"42":-0.5141532,"29":-7.422221,"0":4.457864,"3":-1.9035103,"2":-2.3215656,"5":-1.0763216,"36":-1.583903,"6":-1.5136465,"44":-2.0852666,"15":-0.5611115,"7":0.07060242,"24":-0.743223,"48":-2.7856417,"49":-1.4144045,"10":0.5483734,"22":-2.5478017,"43":-2.7007813,"17":-1.5073609},{"30":-3.0501373,"7":-0.8361124,"47":-0.8469876,"5":-1.766114,"36":0.30409366,"48":-1.212121,"1":-1.1675081,"10":-2.3685074,"16":-0.24671106,"24":-2.2479625,"20":-0.7164024,"0":1.5040326,"28":-2.7426717,"11":-1.6075907,"19":-1.1831938,"32":-1.2883797,"41":-0.28320545,"45":-1.947333,"33":0.015564804,"27":1.0732508,"42":-4.9141226,"46":-6.5679398,"49":-2.7856412,"31":-6.4782953,"44":-1.108532,"15":-1.4689306,"25":-6.487939,"2":-2.7836912,"29":-1.2318416,"13":-1.128869,"18":-2.0082943,"4":-3.6522014,"40":-3.9933362,"6":-0.90794164,"35":-1.8104624,"8":-0.654948,"9":-1.4459306,"23":1.4575778,"3":-0.0406546,"14":-0.2458098,"17":-1.2613282,"12":-1.3665266,"37":-9.234129,"43":0.8146294,"22":-0.017080784,"26":-1.0855381,"34":-0.053871382,"21":-0.73696125,"38":-2.8070807,"39":-1.2034365}],"nn_shapes":{"49":[18,18,13,34,33,11,13,25,8],"22":[18,31,6,29,30,32,32,14,11,7,8],"15":[18,23,32,18,30,3,8],"47":[18,12,33,33,12,4,15,34,17,8],"2":[18,34,17,27,31,8],"8":[18,8,12,8,24,18,8],"34":[18,20,18,4,12,8],"38":[18,5,8,26,8],"11":[18,3,9,20,5,21,8,21,20,19,8],"6":[18,25,22,14,4,33,9,22,12,9,8],"21":[18,31,27,12,3,24,31,8],"39":[18,12,14,3,22,20,8],"18":[18,5,17,23,3,21,8,21,23,27,8],"33":[18,3,5,8],"17":[18,29,27,8],"30":[18,25,18,17,6,20,21,33,19,3,8],"42":[18,30,9,34,20,6,24,8],"44":[18,25,13,20,21,12,18,8,17,8],"46":[18,14,20,8,22,5,9,8],"10":[18,30,29,5,12,9,18,8],"3":[18,25,14,12,8,16,7,8],"16":[18,11,3,20,7,8],"23":[18,18,22,6,19,12,24,34,8],"25":[18,24,3,19,6,29,24,19,13,8],"13":[18,28,28,22,12,26,16,8],"14":[18,17,12,14,21,14,6,21,19,8],"27":[18,28,4,10,11,7,34,19,3,8],"24":[18,19,34,19,10,10,13,25,8,8],"26":[18,5,34,8],"0":[18,23,8,8],"4":[18,27,33,33,12,18,8],"5":[18,19,14,3,16,11,28,16,13,34,8],"29":[18,14,28,17,8],"35":[18,3,27,13,8],"36":[18,20,31,6,17,15,19,25,31,8],"43":[18,10,14,28,29,11,18,20,10,8],"1":[18,26,13,16,17,7,3,8],"7":[18,7,19,18,9,26,13,32,30,8],"9":[18,12,25,8],"28":[18,10,7,6,8,20,23,11,12,18,8],"20":[18,23,32,20,18,8],"40":[18,15,21,8],"41":[18,29,30,28,15,21,16,4,7,9,8],"48":[18,26,8],"32":[18,15,10,34,23,21,29,15,8],"31":[18,7,8],"37":[18,15,19,24,14,24,20,20,23,30,8],"12":[18,15,26,32,3,30,19,14,8],"19":[18,6,21,24,5,31,27,8],"45":[18,5,13,6,8]},"crossbreed_segments":19,"weight_initialization_range":{"start":-1.9261076,"end":1.5660605},"minor_mutation_rate":0.81368506,"major_mutation_rate":0.21178806,"mutation_weight_range":{"start":-0.09049201,"end":0.09049201}},"state":"Finish","generation":5,"max_generations":5,"id":"206450bc-272b-41f7-ae89-fc4f12be753c"},"left":null,"right":null},"right":{"val":{"node":{"id":"a21eff6c-ff0d-4f4f-8354-14ac42930850","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_a21eff6c-ff0d-4f4f-8354-14ac42930850","population_size":50,"generation":4,"scores":[{"11":-6.4714723,"21":-2.4387584,"32":0.51196724,"46":-2.8112407,"47":3.9526958,"17":-2.534247,"49":0.6575879,"38":-0.69729745,"15":-5.533148,"2":-6.5250335,"25":-3.6730926,"1":0.1370612,"27":-4.329281,"33":-5.7648,"43":0.31441694,"13":-6.8382897,"41":-5.1048594,"5":-1.4107186,"26":-5.659274,"12":-3.595717,"18":-2.075016,"9":-0.36810058,"19":-5.8094816,"20":-6.8285494,"6":-8.124129,"40":-1.8836892,"42":-4.3344374,"14":-4.4880095,"30":-1.9241444,"0":-0.9877204,"4":-2.1065373,"16":-6.3015647,"22":-4.8700056,"24":-4.32249,"37":-2.2750306,"44":-3.5271213,"48":-0.4754526,"28":-5.6138673,"23":-3.8899643,"39":-5.5279393,"3":-5.776387,"7":-4.5177007,"45":-3.2805505,"10":-3.3764129,"29":-0.6382438,"31":-3.962623,"8":-1.1595395,"34":-2.9952676,"35":-6.2117767,"36":1.2942022},{"0":-0.42869464,"23":-6.562229,"41":-5.1660156,"42":-1.74071,"21":-1.4251636,"4":2.1877246,"44":-7.116065,"47":-2.257065,"18":-3.0274513,"49":2.1844962,"48":-5.5216503,"35":0.2611336,"14":-1.730476,"46":-0.45125613,"7":-0.44024363,"9":-3.730825,"26":-5.3071184,"28":-5.733193,"33":-1.6490171,"31":2.2274585,"38":-0.042269394,"16":-3.5550365,"40":-0.4170471,"10":-3.7582068,"20":-7.434947,"24":-4.267804,"27":1.0609788,"2":0.6224848,"30":-1.226848,"15":-2.1411896,"29":-2.7852437,"39":-3.7156053,"17":-3.113152,"5":0.056865405,"3":0.4255442,"8":-5.3668776,"36":0.56464416,"37":-2.1761982,"19":-3.4220695,"32":-4.987323,"11":-1.0636063,"25":-0.7849502,"13":-3.4652767,"6":-0.0017968237,"34":-4.096925,"43":-4.2301006,"45":-4.2687526,"22":-5.219239,"12":-2.5277166,"1":0.93786526},{"18":-2.842162,"29":-2.5924702,"13":3.463049,"35":-2.066756,"37":-2.5072942,"5":-0.624416,"7":1.4464777,"8":1.3839967,"45":-1.4903508,"46":-5.7676916,"25":-1.5863268,"12":-2.2818654,"26":-5.7843394,"10":-0.81996393,"38":-0.504665,"48":-4.511069,"32":-4.463644,"1":-1.7803532,"6":1.3676453,"27":-0.81607056,"41":-5.172019,"39":-6.267836,"21":-1.0194148,"3":-2.6400383,"24":-3.0220032,"30":-2.9699166,"43":-0.94422615,"17":-1.2235672,"14":-1.1192089,"15":-2.770475,"36":-6.417554,"19":-3.1521173,"9":1.0718931,"47":-4.5135217,"49":-0.22810516,"0":-0.1726802,"4":0.04109261,"23":-1.9920948,"22":-4.0239234,"33":0.08560918,"31":0.64719695,"40":-0.19472441,"2":0.025729995,"34":-2.4242225,"16":-1.0669054,"42":-4.6856117,"11":-0.54649603,"28":1.372547,"20":-6.1615343,"44":-2.9095218},{"8":0.58357877,"15":-0.06683802,"32":0.9100934,"28":-0.27048746,"24":-1.6066669,"10":-0.7108048,"35":-0.62085783,"1":1.0821325,"25":-5.284899,"44":-5.060845,"0":-0.78118956,"17":-0.6072541,"37":-1.7003189,"45":-3.2974827,"47":-4.4146976,"22":0.2507886,"41":-1.6168216,"23":-1.3340887,"4":-2.2199645,"2":0.9136928,"38":-4.916292,"42":-3.5366962,"27":-6.0338736,"34":-2.4532602,"43":-1.3611755,"14":0.5956539,"3":-0.675242,"11":-3.1293192,"9":-0.6097592,"33":0.0664752,"40":-3.3125527,"13":-2.857099,"31":1.6122506,"6":-2.9755213,"36":-4.948716,"16":-1.6437817,"19":-0.82884026,"20":-1.425541,"21":0.71509355,"29":-6.4884863,"5":-0.49564084,"39":-2.5433745,"46":-3.3904083,"26":-0.3979296,"49":-0.5887252,"7":-0.1689198,"30":-3.2371833,"12":-1.3743494,"18":-0.56168216,"48":-6.2913656},{"3":0.1732178,"10":-1.8926271,"28":-1.4953603,"33":0.805235,"36":-3.458831,"41":-2.7929707,"39":1.3030131,"16":-0.117981456,"29":-3.1151898,"35":-2.2739785,"38":-3.272089,"19":1.1487312,"46":-1.2794657,"20":-0.22847109,"7":-0.32170042,"2":0.45393562,"13":-0.024807762,"18":-0.2644976,"42":-5.8328943,"1":-0.3120924,"15":-0.7801078,"6":0.57796335,"9":0.008637002,"34":-0.16548419,"43":-2.8915856,"24":-1.9777126,"49":-4.152351,"45":-1.0009283,"11":-2.4516919,"8":-2.7317948,"17":0.66630775,"30":-2.788486,"26":-0.05352392,"31":-4.2538157,"44":1.4022282,"0":-0.34089082,"47":-3.231812,"12":-1.8993992,"23":-3.3586304,"32":-4.4242287,"22":-0.8754101,"37":-5.9391913,"40":-5.3672366,"48":-2.2119036,"14":-0.8015324,"25":-0.62322587,"27":-2.5147645,"4":-0.23502302,"21":-1.553767,"5":-0.9330942}],"nn_shapes":{"42":[18,33,33,8],"49":[18,3,33,5,27,33,19,14,23,8],"31":[18,26,26,9,16,9,22,22,8],"47":[18,4,16,11,17,3,8],"8":[18,6,29,13,30,24,33,31,20,8],"17":[18,21,8],"16":[18,6,17,28,12,4,22,30,8],"15":[18,3,28,30,23,4,8],"3":[18,8,22,29,16,25,8,23,10,21,8],"2":[18,16,29,18,5,6,22,23,8],"5":[18,22,27,15,12,9,8],"22":[18,15,30,8],"23":[18,10,31,30,26,3,28,30,8],"24":[18,27,26,26,26,33,8],"26":[18,3,22,17,3,28,7,25,16,22,8],"29":[18,26,21,12,14,19,8],"11":[18,24,3,12,17,5,8],"34":[18,28,24,8],"37":[18,23,20,8,18,22,18,27,8],"41":[18,12,18,30,11,3,29,8],"48":[18,11,11,21,8,13,8],"40":[18,10,14,8],"1":[18,24,21,12,8],"14":[18,22,11,15,13,13,24,5,34,8],"19":[18,16,10,13,10,14,3,11,8],"46":[18,18,21,23,19,7,24,8],"36":[18,33,3,21,28,8],"0":[18,14,32,33,3,8],"18":[18,16,16,27,18,8],"10":[18,29,27,13,5,17,26,34,8],"13":[18,31,6,34,27,15,34,33,8],"38":[18,20,23,8],"21":[18,8,13,24,8,9,31,8],"9":[18,18,3,14,8],"12":[18,14,8],"20":[18,7,9,24,24,24,29,24,8],"25":[18,24,34,14,6,20,22,15,6,6,8],"27":[18,23,32,17,33,28,8],"4":[18,19,20,30,9,30,18,20,4,21,8],"28":[18,30,3,9,8],"30":[18,7,31,25,22,34,18,8],"32":[18,25,5,19,7,23,8],"33":[18,4,30,26,15,30,12,5,21,27,8],"39":[18,7,24,32,29,19,20,8],"35":[18,29,15,29,14,12,6,8],"43":[18,5,12,24,28,14,16,8],"7":[18,34,12,10,13,28,21,15,28,8],"44":[18,21,3,18,11,8],"45":[18,12,30,8],"6":[18,25,19,16,19,30,8]},"crossbreed_segments":17,"weight_initialization_range":{"start":-1.7432716,"end":1.6809266},"minor_mutation_rate":0.16051888,"major_mutation_rate":0.9839299,"mutation_weight_range":{"start":-0.88359714,"end":0.88359714}},"state":"Finish","generation":5,"max_generations":5,"id":"a21eff6c-ff0d-4f4f-8354-14ac42930850"},"left":null,"right":null}},"right":{"val":{"node":{"id":"203368ce-5df8-47cc-85a3-8737f2f61d5b","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_203368ce-5df8-47cc-85a3-8737f2f61d5b","population_size":50,"generation":9,"scores":[{"13":-6.0615363,"12":-2.6796837,"4":1.5202897,"15":-1.3500706,"18":-0.5050914,"7":0.6162976,"19":-0.1533648,"21":-1.0203056,"22":-1.3992598,"23":-2.641868,"28":-4.299928,"30":-5.302378,"1":0.94063365,"29":-2.8476875,"17":-2.6706123,"31":-6.235944,"3":-4.7235327,"27":-2.455749,"32":-2.7329144,"34":-3.846684,"45":-5.355481,"48":-5.3916907,"33":-6.618595,"40":-0.61342084,"38":-6.691718,"41":-0.71297365,"42":-2.482652,"47":-4.85316,"8":-5.761569,"36":-3.1418037,"37":-1.5414846,"11":-1.3640214,"0":-3.1706002,"10":-4.0832353,"24":-0.0047788084,"5":-0.06780057,"6":1.4726968,"9":-2.7802906,"46":-5.0855565,"16":-1.5577738,"2":-7.291587,"26":-3.848475,"39":-2.580203,"44":-8.082107,"25":-7.188414,"49":-2.8559015,"20":-4.22116,"43":-3.6626022,"14":-3.942197,"35":-2.3295014},{"18":-0.11432342,"22":-0.9443925,"41":-1.9059979,"48":-3.2894027,"43":-5.4873667,"9":-0.45641842,"29":-3.8709152,"35":-8.257012,"44":-8.091996,"23":-1.6757748,"14":-2.8090603,"12":-0.45822677,"31":-5.4818435,"27":-9.313177,"47":-0.7598516,"6":-0.3413308,"17":-2.7760038,"25":-2.5767207,"33":-1.674531,"40":-3.7255013,"46":-1.3680242,"20":-1.4310821,"3":-4.8296347,"5":-1.0949562,"19":-4.3625565,"37":1.8395226,"26":-0.514341,"42":-0.20106061,"36":-4.6662865,"16":-3.7663884,"11":-1.0167248,"15":-1.8159335,"2":0.73568285,"24":-1.5104856,"28":-2.4822166,"45":-4.814537,"1":-5.2182913,"7":-2.1969981,"49":-5.352608,"4":-1.80509,"13":-2.6319325,"8":-0.20569114,"10":-1.935244,"32":-3.320692,"38":0.6912533,"30":-2.2159657,"0":-3.753378,"21":-2.7289367,"34":-4.2699256,"39":-0.92220813},{"31":-6.2261367,"34":-2.2532945,"19":-2.9685862,"24":-0.36538887,"29":-1.8620571,"1":-1.0877621,"20":0.275602,"39":-3.096109,"9":-1.3850116,"38":-9.6056385,"43":-0.26035717,"44":-4.687214,"32":-2.6568785,"16":-0.41369098,"3":-0.3260142,"7":1.6613563,"15":-1.6330436,"46":-7.197299,"22":-1.6352446,"4":-1.3589034,"49":-5.975999,"10":0.7375352,"30":-6.0976777,"40":0.54419273,"42":-1.4632086,"2":-5.911438,"26":-2.3958082,"5":-5.546512,"6":-1.177063,"23":-0.3758726,"0":-1.5063884,"21":-0.48584405,"25":-5.264371,"33":-3.4972088,"8":-2.2249763,"12":-4.6143556,"13":-2.613673,"14":-0.2013896,"35":-2.59116,"18":-2.9167368,"36":-1.2123888,"37":-4.6822767,"45":-4.273552,"27":-2.7699463,"47":-0.6502439,"48":-1.493132,"17":-1.741444,"11":-0.5439457,"41":-0.808615,"28":-2.9987345},{"5":-1.8213758,"43":-2.7431512,"38":-5.95363,"49":0.035115886,"32":-5.1510925,"12":-2.8091362,"25":-6.496497,"33":-7.949338,"48":-0.99491465,"8":-2.0829463,"2":-0.031106567,"15":-0.372649,"34":-1.6739277,"42":-0.619355,"16":-1.4548671,"37":-3.4394093,"9":-1.5828683,"30":-0.9453615,"45":-1.724397,"21":-1.6225876,"0":0.15423045,"36":-1.7103798,"28":-4.6417975,"19":-2.4957173,"41":-1.5402925,"6":0.33565888,"10":-1.8752396,"24":-2.7322052,"40":-1.9615946,"44":-5.6492515,"17":1.014154,"26":-6.536299,"7":-1.3930643,"13":-2.5884836,"3":-1.262034,"14":-2.5048103,"46":-6.4057937,"1":-0.21534681,"47":-6.278834,"27":-4.356794,"31":-1.4171464,"39":-6.406414,"20":-6.073895,"22":-0.71730816,"29":-0.8216996,"23":-1.5029604,"35":-4.992949,"11":-3.7783446,"4":0.0031358034,"18":-0.67258096},{"10":-0.25090164,"18":-0.858119,"8":-5.777336,"19":-0.7887672,"39":-4.918338,"49":-0.7818918,"7":-0.5166468,"21":0.13674489,"37":-4.7251687,"43":0.14045382,"11":0.17619798,"1":0.021573782,"32":-3.2923455,"29":-7.159108,"14":-1.3452873,"48":-2.3901944,"36":-3.0151825,"0":2.8386433,"6":-1.8094699,"25":-5.2633567,"40":0.6209098,"5":-1.658753,"23":-0.737769,"41":-3.5934289,"47":-4.8464775,"22":-1.9721928,"38":-2.089369,"15":-0.538136,"27":2.3752265,"31":-6.2390184,"44":-5.0705123,"3":-2.6324131,"2":1.2862574,"26":-5.131711,"20":-4.543001,"13":-3.8828483,"16":-0.790923,"9":2.1973586,"33":-1.910883,"17":-1.5043161,"4":0.075564995,"46":-2.2381697,"12":-1.9475548,"30":-5.2913437,"24":1.4310465,"28":-7.7879343,"42":-6.175967,"34":-5.14563,"35":-0.025316978,"45":-5.904042},{"25":-2.8692286,"47":-7.627328,"10":0.17991559,"12":-2.7248616,"31":-6.1483893,"15":-1.9559215,"33":-7.1912904,"49":-1.3946544,"2":2.2620661,"39":-2.7464297,"41":-2.714857,"42":-0.036035158,"6":0.288,"13":-1.0198475,"40":-5.378584,"36":-8.762684,"45":-4.758359,"46":-3.1634154,"48":-5.722167,"22":-1.2965059,"11":-0.07008378,"14":-0.43319377,"16":-0.95561635,"26":-5.4947495,"30":-3.170524,"44":-5.551297,"1":-1.5958624,"4":-0.7176281,"35":-6.031193,"17":-4.579427,"0":-2.0472035,"37":-2.643792,"28":-0.97088355,"38":-3.72789,"27":-3.2043538,"5":0.62878126,"7":-0.8526117,"18":-1.6840101,"43":-4.0878997,"9":-0.6471418,"24":-2.3522565,"20":-5.3582354,"3":0.73508096,"19":-1.8957741,"21":-3.051081,"34":-5.6870265,"8":-2.418698,"32":0.38842565,"23":0.5827245,"29":-3.823841},{"5":-0.220507,"23":-4.410055,"27":-6.706713,"39":-3.3960824,"40":-2.1484895,"49":-1.5460713,"9":-2.1876059,"41":-4.4184256,"31":-5.663493,"14":-1.3139164,"6":-0.5312986,"36":-6.3686433,"37":-5.154157,"45":-2.2323058,"32":-4.058252,"4":-6.0212264,"10":-0.86265737,"46":1.7008975,"18":4.498134,"8":-2.7613544,"42":-4.4026065,"17":0.39508438,"2":-1.616938,"21":1.1348299,"13":-3.777708,"35":0.43933,"26":-0.13403341,"47":-5.7819333,"7":-2.3441997,"30":-8.241878,"44":-5.7271743,"12":-3.0233912,"34":-5.5483613,"25":-6.8587112,"29":-4.544421,"43":-4.879415,"48":-3.8700097,"24":-1.362485,"1":-3.36082,"11":0.27863663,"20":-1.9567833,"22":-2.7686324,"15":-0.5434549,"3":0.9506604,"16":3.0620127,"19":-3.2280064,"28":-0.0035861984,"0":-0.4407722,"33":-3.465736,"38":0.223805},{"32":-4.2679415,"27":-4.617057,"29":0.087170504,"42":-2.9107833,"7":-0.39363545,"8":-2.871464,"9":-1.2108052,"16":-0.97661984,"21":-4.908767,"22":-1.2127631,"38":-0.31704503,"23":-2.939609,"31":-0.25758976,"24":-1.0036132,"14":-0.3476418,"26":-2.7195346,"35":-2.4430473,"36":-5.7113047,"46":-3.8022473,"45":-2.2357075,"3":-2.2639267,"20":-1.5970081,"49":-0.8887558,"47":-0.7757422,"11":0.94219744,"48":-0.28411922,"13":-2.093788,"37":-3.340915,"4":-1.825848,"2":-1.4276224,"0":1.2062461,"39":-11.106636,"41":-1.3248581,"19":-0.17546004,"43":0.76113236,"6":-2.7164958,"40":-5.0853443,"44":-4.2800694,"5":-0.65610963,"18":1.6902949,"30":-4.7505875,"12":-2.4398055,"33":-1.3714097,"15":-0.63636,"10":0.9763905,"1":0.3065812,"25":-5.893094,"28":-2.331335,"17":-1.6994712,"34":-7.236337},{"4":4.092096,"28":-3.767767,"37":-1.5373563,"17":-2.157727,"13":-0.79626095,"2":0.9916604,"30":-2.0188994,"44":-1.2411817,"22":-3.8631191,"48":-3.4246116,"29":-0.97772866,"14":-3.3033214,"18":-1.3941777,"6":-4.4819703,"5":-0.4289755,"32":-3.4806926,"12":-0.33485812,"39":-6.292939,"43":0.92196214,"45":-0.6753839,"33":-0.25819525,"34":-3.035078,"25":0.004158786,"21":-2.043839,"16":-1.2684958,"27":-3.249688,"10":-0.87576705,"11":-1.8128872,"15":-0.4661776,"46":-4.3860717,"38":-5.7667212,"3":-0.5464066,"26":-2.9459498,"35":-0.5777134,"36":-4.628657,"40":-4.2499084,"42":0.51436794,"49":-7.9355116,"31":-6.1434374,"20":-0.3625256,"23":-3.510929,"41":-0.4129098,"7":0.92192286,"8":-1.9173721,"0":-0.23905472,"1":0.26135007,"9":-3.2764,"47":-5.172612,"24":0.75993913,"19":0.6675881},{"30":-6.456954,"16":0.036798805,"33":-3.7803955,"34":-1.9072559,"38":-3.089058,"19":0.58897,"39":-4.736411,"26":-1.6644833,"2":1.2184676,"40":-1.6352108,"41":0.77811116,"44":-2.814549,"36":-4.2947717,"45":-9.383311,"46":0.2331924,"37":-5.1677117,"32":-5.427541,"12":-1.1785867,"47":-1.0297588,"48":-6.8122406,"0":0.9402604,"18":-2.762963,"49":-1.5772594,"24":-0.3033608,"7":-0.3254214,"6":-3.3805523,"42":-2.89198,"5":-0.10723499,"31":-6.986254,"23":-1.6471126,"10":-3.4487329,"29":-8.496641,"11":3.7004852,"35":-7.657138,"17":-1.7617385,"20":-1.5303608,"28":-5.2911096,"22":-3.5617645,"43":-5.972586,"3":0.3634794,"13":-1.9006294,"9":0.3458802,"8":0.38095504,"27":-6.3094463,"25":-3.316194,"1":0.64339125,"14":6.0491147,"4":-0.6245182,"15":-1.1051981,"21":-0.5828692}],"nn_shapes":{"19":[18,6,7,18,8],"17":[18,29,27,20,15,8],"40":[18,27,11,26,3,17,8,8],"11":[18,30,32,22,16,10,13,16,27,24,8],"21":[18,6,7,20,8],"42":[18,28,8],"45":[18,27,26,5,16,26,8],"47":[18,5,29,18,16,9,31,8],"10":[18,31,12,22,25,24,6,31,8],"24":[18,8,7,4,15,15,8],"0":[18,5,21,4,7,9,13,28,8],"48":[18,31,6,29,33,14,30,14,8],"2":[18,16,34,19,20,8],"38":[18,5,31,12,15,34,30,21,8,34,8],"32":[18,29,28,32,22,13,16,25,14,18,8],"1":[18,33,8],"6":[18,29,12,5,18,21,29,8],"9":[18,30,24,7,23,15,6,8],"3":[18,15,28,8],"20":[18,18,20,9,19,13,4,8],"30":[18,23,8],"5":[18,30,18,14,24,26,33,25,28,33,8],"33":[18,20,16,32,28,27,20,14,7,8],"35":[18,33,5,8],"39":[18,19,20,16,8],"13":[18,32,32,15,8],"41":[18,4,4,19,8],"43":[18,20,18,28,17,13,15,8],"23":[18,6,13,11,8],"44":[18,29,8],"46":[18,7,20,20,14,4,17,8],"49":[18,7,18,32,5,14,8],"18":[18,15,29,8,8],"16":[18,23,7,18,18,19,6,6,33,33,8],"7":[18,26,9,8],"12":[18,3,4,6,8],"28":[18,32,12,22,9,19,26,8],"29":[18,11,32,12,8],"15":[18,10,15,22,8],"27":[18,30,22,13,25,16,25,10,4,8],"37":[18,25,24,33,24,33,16,8],"4":[18,29,25,31,16,20,9,20,8,8],"14":[18,19,8],"25":[18,17,16,26,21,13,23,7,34,8],"31":[18,34,7,21,12,34,8],"36":[18,15,24,10,8],"8":[18,6,19,16,27,11,21,18,24,8],"22":[18,30,8],"26":[18,3,13,28,33,14,9,11,10,8],"34":[18,12,8]},"crossbreed_segments":9,"weight_initialization_range":{"start":-1.3771327,"end":1.7500036},"minor_mutation_rate":0.6309173,"major_mutation_rate":0.06338024,"mutation_weight_range":{"start":-0.89320016,"end":0.89320016}},"state":"Finish","generation":10,"max_generations":10,"id":"203368ce-5df8-47cc-85a3-8737f2f61d5b"},"left":null,"right":null}},"right":{"val":{"node":{"id":"63af491b-9b84-4549-bf28-d5c3fcb72ffd","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_63af491b-9b84-4549-bf28-d5c3fcb72ffd","population_size":50,"generation":14,"scores":[{"8":-6.069092,"33":-5.0940948,"4":-4.470458,"42":-4.660769,"25":-4.349419,"47":-8.300748,"20":-1.9045479,"43":-3.161159,"36":-5.716647,"28":-4.0584264,"26":-4.338761,"39":-6.4248137,"23":-1.626778,"40":-8.266317,"49":-4.4791436,"16":-1.9763448,"3":-1.400876,"7":-7.603074,"11":-0.6352552,"21":-1.0142238,"32":-6.2223997,"2":-4.106714,"18":-4.8280454,"17":-4.45882,"12":-2.1958303,"5":-2.794752,"10":-4.4663258,"19":-2.1331758,"44":2.5687604,"48":-3.2384803,"46":-6.0489435,"1":-0.1744744,"35":-0.8893156,"9":-1.0875275,"15":-1.1706532,"27":-2.2872546,"30":-2.0954628,"13":-0.7405074,"14":-4.169658,"34":-4.5048933,"37":1.6277792,"41":-7.645879,"31":-0.7182592,"38":-5.6641545,"24":0.78242075,"22":-6.6202226,"45":-3.6109688,"6":-4.854441,"0":-3.7418995,"29":0.9188182},{"49":-8.088182,"27":-4.4094706,"11":0.47737116,"29":-2.8574414,"31":-6.4447417,"34":-7.420352,"42":-5.0692816,"7":-1.9054571,"28":-3.26824,"17":-2.0616498,"48":-5.1362467,"21":-3.938991,"44":-6.1047955,"36":-3.2295728,"22":-2.1624913,"4":-0.09455097,"33":-0.25262243,"9":0.7189634,"26":-5.600524,"15":0.42617542,"40":-0.0046018003,"46":-4.7301927,"35":-3.7268822,"0":8.1669,"24":-4.9653997,"23":-2.7329252,"41":-10.61446,"6":-1.5024382,"1":-0.6274835,"12":-2.2065892,"10":-3.2536988,"16":-5.0924234,"18":-1.4048216,"2":-4.133996,"37":-10.1552105,"14":-2.3844306,"38":-6.710885,"43":1.4886957,"8":-0.360426,"32":-5.816176,"39":-2.983708,"3":-2.1701016,"13":-1.825918,"20":-5.24152,"25":-5.1760826,"5":-0.58427346,"30":-5.0981026,"45":-5.697856,"47":-5.226863,"19":-1.560044},{"42":-4.790041,"20":-1.1890002,"43":-6.0043163,"28":-6.9555635,"48":-0.510232,"9":-0.4068926,"26":-8.332675,"34":-1.0714524,"37":2.1537495,"30":-3.716394,"46":-2.1185315,"47":-5.9434023,"24":-3.1260734,"11":-2.7178226,"19":-1.5212322,"14":-1.7723862,"13":-0.8461763,"32":-5.6576056,"4":-3.228576,"0":1.411445,"35":-7.955058,"22":-0.82116145,"38":-4.0411196,"49":-3.5768852,"25":2.5552037,"17":-3.8672116,"7":-0.0842596,"27":-3.3282406,"10":2.898662,"39":-5.368108,"40":-1.03172,"6":-1.0901622,"2":0.6594648,"31":-0.2705296,"18":-2.2509701,"29":2.1199079,"12":-0.2960566,"36":-5.92122,"15":-2.254159,"41":-5.7638197,"33":-6.19812,"23":-1.9294733,"21":-3.52288,"5":-0.1741896,"16":-1.6020119,"45":-3.3413723,"8":-1.7656858,"1":-1.4515138,"44":-5.3893876,"3":-0.79005444},{"7":0.16130401,"21":-1.9730005,"15":-0.1315082,"40":-5.3528476,"34":-4.213156,"48":-4.282601,"47":-5.5874953,"25":-1.2648476,"8":-0.22879839,"1":1.4288082,"6":-0.94857055,"26":-7.524634,"39":-6.158347,"35":-4.27282,"43":-8.628385,"13":0.10465145,"44":-3.2539196,"0":-0.11162619,"5":-1.043254,"42":-2.0195737,"24":-1.6932726,"2":3.3416386,"23":-1.5218649,"36":-8.102966,"41":-6.2159843,"9":-1.6141726,"12":-0.74325323,"20":-1.4542454,"27":-7.2206926,"18":4.016916,"30":-0.874025,"29":-7.2742605,"14":-2.3179727,"45":-8.892599,"10":-0.69393337,"46":-6.7053704,"4":2.1962924,"16":-14.738788,"19":-0.21545644,"33":-4.486966,"38":-2.6419265,"49":-1.9328684,"11":-1.293656,"31":-3.5481186,"3":0.45642534,"22":-0.5492472,"37":3.1110644,"32":-3.7383697,"17":-0.85649097,"28":-7.8166184},{"47":-4.3279033,"35":-5.373517,"11":-0.74563074,"40":-5.3293886,"5":0.13447618,"22":-1.7905525,"38":-7.99418,"29":-5.6509647,"33":-1.2474883,"19":-1.3597196,"27":-2.762928,"3":-4.4732842,"17":-0.046969604,"32":-1.0133835,"48":-4.5410213,"23":-1.0213821,"20":-3.5897534,"26":-1.9709444,"18":-0.6774128,"2":-0.13767043,"0":-4.548816,"14":-0.295729,"4":-2.9405982,"36":-4.5952272,"28":-2.512223,"46":-2.4868076,"9":0.3691674,"24":-1.3622794,"25":-6.683936,"34":-5.349573,"1":-0.67742556,"6":-1.951775,"7":-1.3647935,"39":-4.418799,"41":-5.764389,"12":-0.98820055,"13":-0.3580936,"16":-0.54273975,"8":1.8473701,"43":-1.8787056,"42":-5.4131083,"49":-5.41763,"31":-5.7524247,"15":-1.1466728,"30":-7.7752686,"37":0.27733213,"45":-7.140503,"44":-6.461798,"21":-1.8989708,"10":-0.46728197},{"2":-1.56248,"13":-0.7525544,"43":-5.837439,"6":0.47565785,"46":-1.5897272,"39":-5.68252,"45":-2.3996503,"44":-4.9558625,"23":-2.2326112,"41":-9.368122,"9":-1.9876497,"11":-1.8456295,"15":-1.3360106,"29":-3.2400506,"20":-2.602886,"32":-0.4177026,"22":0.64007074,"19":-0.45666522,"1":-0.5352438,"27":-4.078212,"5":-0.3983138,"17":1.3219271,"16":1.6173245,"0":1.1778939,"33":-7.54399,"42":-5.606898,"34":-0.9048826,"47":-3.0472536,"38":-2.98173,"18":-2.141915,"40":-5.1117973,"10":-0.34259522,"7":-0.556374,"36":-1.6564305,"25":-10.329073,"48":-3.1240704,"3":1.0361704,"28":-8.048054,"35":-3.0688634,"24":-0.1821236,"37":1.7575128,"8":-1.3200384,"4":-0.1346158,"49":-6.504126,"26":0.5167214,"12":0.96005666,"14":-1.1138606,"31":-3.9105728,"21":-2.5513926,"30":-9.554128},{"13":-1.4711908,"44":-5.9292803,"32":-1.6712959,"10":-0.11659022,"15":-0.22578481,"20":-0.26050824,"14":-3.9976869,"8":0.5072394,"16":-0.20553902,"45":-6.6763563,"37":-6.550061,"29":-6.1376,"3":0.9823904,"24":-1.8991677,"25":-2.7527473,"40":-6.401937,"28":-5.2939734,"7":-0.048050977,"21":-1.5307443,"1":-1.0779276,"17":-0.76480997,"23":-1.3948135,"6":0.12415314,"9":-0.26064882,"11":-0.9512538,"36":-4.4375963,"41":-3.6213593,"42":-6.099228,"48":0.09729838,"2":1.8621299,"4":-0.72417676,"33":-7.120556,"30":-3.1712918,"18":0.010256794,"35":-6.842204,"19":-0.20973177,"0":-2.6267352,"22":-3.4517379,"27":-1.727238,"34":-4.3673334,"43":-6.9841127,"26":-5.9439487,"12":-1.3332065,"47":-1.0936369,"39":-3.052627,"49":-5.076259,"5":0.1122748,"38":-6.282195,"46":-6.4391375,"31":-1.3574141},{"10":-0.2211164,"35":-6.6669593,"48":-1.0380865,"20":2.0287774,"29":-3.3574173,"9":-0.5559626,"14":0.14803696,"12":0.41750318,"38":-7.112405,"4":0.1335312,"18":-1.0399034,"23":-1.4316481,"36":-3.5204754,"41":-8.614304,"26":1.4097984,"1":4.035802,"30":-6.4118285,"17":0.36632484,"22":3.0402958,"21":-1.0572317,"19":2.5371234,"24":-0.9996996,"15":-1.104537,"0":-0.7000558,"28":-3.8471859,"3":-1.2423786,"5":-0.808416,"32":-8.684097,"33":-4.9276524,"43":-6.9978704,"42":1.5353154,"8":-0.47881657,"45":0.5173014,"46":-0.34014502,"47":-6.6365633,"44":-7.32023,"27":-6.893125,"31":-6.360853,"34":-5.0873036,"40":-2.3171132,"49":-2.8701851,"6":-1.2537096,"7":-4.2261634,"2":-0.70211,"25":-6.5653243,"37":-4.22389,"11":0.2990488,"13":-0.20701341,"39":-4.641257,"16":0.0112473965},{"39":-3.6825485,"44":-2.4370027,"9":-0.1743464,"47":-7.0503654,"13":-0.0332822,"41":-2.5515766,"1":-1.541414,"0":1.541014,"4":0.940028,"27":-2.8493986,"36":-4.726627,"24":-1.6463757,"25":-3.654139,"7":0.4505847,"3":0.89223623,"23":-2.0576584,"29":-3.908837,"30":-4.5101647,"21":-2.445579,"31":-3.649403,"40":-6.0176225,"17":-0.31697503,"34":-4.48388,"12":-0.9229352,"45":-1.9503323,"18":0.9324938,"33":0.15854302,"37":-1.6256497,"35":-7.331717,"48":-6.6231103,"15":-0.5374302,"38":-5.9349833,"32":-4.645023,"42":-5.042142,"43":-3.789404,"46":-1.1764994,"6":5.882359,"2":3.8521378,"10":1.499598,"16":-1.7752663,"19":-0.90521586,"22":-1.8889548,"5":0.6613487,"28":-8.877123,"49":-7.0020742,"8":-0.87334347,"26":-7.4668303,"20":-2.512741,"11":-1.0245552,"14":-0.81175613},{"22":-0.49079657,"33":-4.0775614,"37":-0.62546676,"0":1.7051964,"10":-0.047491405,"40":-4.3915215,"1":2.0448983,"5":-1.378012,"4":0.3188538,"16":-1.7123814,"15":-1.483071,"34":-5.7027273,"41":-3.6069686,"42":-4.998118,"39":-6.598256,"43":-4.106491,"7":-1.4503245,"12":-0.5346658,"17":0.398249,"14":0.2996894,"25":-6.3164377,"31":-7.430293,"38":-5.887355,"44":-5.486763,"21":-3.704354,"2":-0.19979087,"47":-5.257516,"28":0.5822532,"35":0.44862086,"11":0.1961306,"13":-4.0350695,"24":-0.28154978,"26":2.9913385,"32":-5.9540772,"8":-1.8885313,"18":0.0213396,"46":-6.612848,"23":0.1444552,"48":-2.0025697,"9":1.4237278,"20":-0.028476572,"49":2.3796742,"3":0.8991828,"19":-2.0367374,"27":-5.2373004,"30":-5.489272,"45":-1.1280714,"29":2.535732,"6":1.3464735,"36":-5.648479},{"11":0.11987443,"13":-1.3703808,"8":-0.7758378,"35":-1.3983872,"18":-0.27867323,"27":-4.3856654,"44":-1.2755907,"15":-0.32736063,"24":-3.7100956,"32":-0.0764086,"43":0.18626317,"45":-3.5338511,"4":1.0853077,"25":-9.29453,"16":-4.446281,"12":0.1594002,"36":-1.7182869,"20":-2.3400369,"37":-4.4235787,"47":-10.831648,"48":-3.013094,"49":-8.558775,"30":-7.007659,"7":3.540858,"23":-0.28038746,"42":-6.7514243,"22":-0.8520716,"3":0.22084627,"10":-0.72354466,"41":-2.6133766,"34":-7.1538353,"29":-5.3938837,"40":-0.40576425,"9":-4.1087017,"17":0.1366714,"28":-1.7240318,"38":-1.3967516,"46":-4.3899465,"31":-6.127557,"33":2.3046422,"39":-9.954111,"26":-9.14213,"14":-3.0409713,"2":3.2370842,"6":0.40174532,"0":-4.177638,"5":-0.3059289,"19":-1.0527039,"1":0.79302967,"21":-0.58490145},{"12":2.1832337,"49":-7.0401483,"16":-1.6947788,"30":-1.3135207,"18":0.703499,"44":-5.152332,"11":0.570145,"26":-3.0996785,"0":0.070637405,"8":-0.7534913,"20":-1.954236,"27":0.9747921,"7":0.5450776,"29":-5.49309,"40":-5.0619526,"41":-0.27579817,"1":-2.5208938,"6":2.2722094,"37":-5.502732,"13":2.1581044,"22":-2.8321745,"38":-0.6764842,"39":-6.8810205,"42":-4.752784,"21":-1.3444358,"46":-5.083355,"36":-8.058732,"17":-0.6713782,"23":-0.039514005,"3":-1.5181656,"14":0.388066,"15":0.17807738,"10":1.4885564,"4":-1.7827431,"2":1.1435935,"33":0.776235,"43":-4.068568,"25":-4.0459485,"47":-5.918283,"28":-7.9561453,"19":-0.061852597,"48":-4.999099,"31":-6.3655405,"9":-0.084318206,"5":-2.254188,"35":-2.5133655,"45":-0.6381622,"24":1.5963113,"34":-4.9767027,"32":-1.4229188},{"14":-0.26403242,"2":2.1234822,"16":-0.171707,"42":-1.957675,"8":-0.4832286,"27":-4.791644,"46":-5.7846956,"13":6.383906,"10":0.82343644,"32":-6.1985607,"21":-0.2600368,"35":-3.293181,"15":-0.16612342,"39":-5.487646,"17":-0.90918285,"18":-0.4506496,"45":-4.5066237,"28":-2.034595,"36":-3.2557678,"31":-6.3209476,"7":0.9062312,"25":-4.8203287,"23":-1.4385079,"38":2.3662407,"4":1.4349756,"33":-3.0348454,"34":2.8219178,"11":0.971183,"41":-6.6662207,"5":3.1754055,"19":-0.5304278,"47":-4.7532735,"37":-6.768679,"48":-1.6132733,"6":-1.2470607,"30":-1.7141603,"9":0.0972314,"22":-0.8169382,"3":0.66940355,"29":-7.715441,"12":0.0965532,"43":-2.8710086,"20":-0.96138203,"0":2.461893,"40":0.16619243,"26":-1.75643,"24":-1.9928026,"44":-4.8273773,"49":-1.7080085,"1":3.1069047},{"23":-1.0567077,"41":-0.36431417,"15":-0.735537,"46":-2.1280885,"49":-6.228997,"8":1.2514383,"37":-6.034368,"0":3.241253,"38":-5.155979,"11":2.625294,"4":2.5957713,"26":-4.7352057,"30":-5.138303,"1":1.9234915,"40":-5.213203,"47":-5.9502745,"29":-0.72342,"22":-1.8519977,"21":-0.5797606,"5":-0.3990143,"24":-1.5207313,"48":-4.497856,"2":1.6721871,"33":-4.990741,"35":-0.5015704,"9":-0.97921765,"45":-3.2262008,"31":-1.5186932,"14":1.0603493,"13":-0.7976054,"32":4.094207,"34":0.33811378,"44":-7.761998,"16":-0.13641839,"42":1.3870549,"3":0.11979119,"25":-8.261856,"27":-5.808234,"20":-0.6997692,"10":-1.6402384,"19":-1.2573239,"17":0.55972064,"7":0.021158999,"39":-4.507757,"28":-1.2109119,"18":-0.9441657,"12":-0.21774468,"36":-2.7474074,"43":-8.356656,"6":1.5366848},{"46":1.1030022,"13":0.7303882,"48":-4.0814185,"49":-7.3009596,"11":2.099044,"34":-2.6376014,"33":-2.0458329,"15":3.298797,"7":-1.5549805,"6":-0.97469556,"21":-0.19640112,"30":-4.087301,"18":-3.4629655,"41":-3.6512809,"25":-3.2870553,"0":2.9551253,"28":-6.6697874,"37":-6.231577,"39":-3.4252715,"44":-5.2634215,"27":-3.615893,"12":-1.2823393,"17":0.2736004,"22":0.521864,"4":2.4086232,"14":-0.578271,"29":-4.471439,"3":1.3296368,"20":-0.71489245,"35":-0.4940032,"40":-8.11772,"2":1.6850106,"45":-0.2666122,"5":1.3215994,"26":-5.663763,"31":-6.1445756,"47":-3.7819877,"32":-3.4226127,"9":-0.1418186,"19":-0.45779124,"38":-1.9440556,"42":0.12248763,"1":-1.4773883,"10":-1.0335268,"36":-9.659117,"8":1.0191154,"24":0.284077,"43":-4.3620725,"16":-2.0288436,"23":0.27707738}],"nn_shapes":{"15":[18,20,8],"26":[18,32,31,24,22,8],"41":[18,22,25,25,31,23,8],"8":[18,7,10,28,26,16,23,31,28,8],"10":[18,30,7,34,8,13,24,26,27,8],"19":[18,15,7,20,8],"28":[18,29,17,9,34,6,14,13,28,27,8],"34":[18,16,11,21,16,20,18,5,8],"44":[18,28,11,11,19,17,8,31,32,5,8],"13":[18,14,10,4,8],"17":[18,13,8],"1":[18,25,21,8],"20":[18,15,8],"14":[18,13,8],"12":[18,14,34,25,18,11,27,23,17,8],"22":[18,30,8,33,7,25,17,18,26,31,8],"6":[18,21,14,31,15,27,26,34,7,8],"11":[18,5,14,4,10,8],"29":[18,14,17,24,25,10,18,13,8],"36":[18,30,8,19,28,18,30,21,24,8],"42":[18,28,8],"48":[18,10,6,20,8],"35":[18,13,12,32,16,34,8],"23":[18,13,11,5,32,8],"45":[18,25,8],"43":[18,23,8],"9":[18,19,5,34,8,11,8],"33":[18,34,8],"5":[18,15,15,8],"3":[18,28,8],"2":[18,13,8],"27":[18,27,25,7,31,9,3,3,19,10,8],"21":[18,21,17,21,8],"49":[18,13,27,27,32,3,8],"31":[18,15,32,8],"37":[18,10,9,8],"18":[18,24,12,8],"46":[18,24,6,5,31,8],"47":[18,31,3,25,32,24,28,4,25,8],"0":[18,14,5,4,5,8],"40":[18,5,31,8],"7":[18,34,4,10,11,30,8],"24":[18,16,11,12,31,23,8],"30":[18,11,18,16,33,12,9,21,11,15,8],"38":[18,17,33,19,8],"39":[18,26,16,31,22,8],"4":[18,34,28,11,11,29,11,12,8],"16":[18,27,6,14,10,19,10,34,27,8],"25":[18,28,10,10,32,32,12,8],"32":[18,30,7,24,28,8]},"crossbreed_segments":11,"weight_initialization_range":{"start":-1.0402882,"end":1.5273945},"minor_mutation_rate":0.9520668,"major_mutation_rate":0.8335203,"mutation_weight_range":{"start":-0.6023569,"end":0.6023569}},"state":"Finish","generation":15,"max_generations":15,"id":"63af491b-9b84-4549-bf28-d5c3fcb72ffd"},"left":null,"right":null}},"right":{"val":{"node":{"id":"48ec59ed-40e8-4fe0-a929-92cc324873ca","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_48ec59ed-40e8-4fe0-a929-92cc324873ca","population_size":50,"generation":19,"scores":[{"13":-1.4142692,"47":-1.6592379,"49":0.3410964,"22":-6.591704,"1":-3.781786,"24":1.5314912,"23":-0.24905081,"35":0.6497308,"39":-4.099218,"46":0.30892056,"19":0.5735318,"14":-0.015305,"40":2.8135312,"6":-1.1633298,"8":-1.510386,"29":0.176255,"3":1.6047258,"10":-1.4822628,"28":-0.61696815,"2":0.80709714,"26":-1.1791419,"43":-2.7403412,"27":-5.292212,"48":-0.61211836,"20":-3.3156848,"15":2.4606733,"32":-0.427847,"4":-4.128209,"34":2.834388,"17":-0.9344204,"18":0.4177352,"41":-1.2587774,"42":-5.070863,"38":1.3624249,"9":-4.3788576,"37":-1.8078709,"12":-7.796138,"0":1.6523159,"5":-0.6745032,"25":-0.48673376,"30":-2.525943,"21":-3.7513194,"33":-3.6008232,"31":-3.4886208,"36":0.6533136,"11":-5.4849296,"44":1.2480085,"45":-4.5369644,"7":-0.630715,"16":-3.6863465},{"43":-1.4187799,"3":0.5602858,"15":0.28763258,"0":-2.1157553,"29":-4.9932585,"30":-4.8374114,"25":0.70482737,"32":-7.191799,"42":-0.229579,"21":-0.78822863,"28":-1.3480982,"27":-4.935256,"13":2.2695951,"5":1.0831163,"1":3.186741,"23":-0.53452784,"35":-4.816189,"17":-0.5340606,"37":-5.2658195,"46":-5.6004496,"39":-0.5162036,"4":1.5308871,"8":0.8792552,"34":-4.257403,"40":-0.035599984,"47":-2.2445347,"48":-4.9693174,"49":-5.36049,"22":-0.679153,"2":1.9149898,"24":-1.1383171,"41":-2.8188515,"45":-2.6315355,"7":1.0400218,"31":-4.352787,"14":0.30300403,"33":5.4634247,"11":-0.5809352,"36":2.6106982,"44":0.5219575,"6":1.0872599,"19":-0.41813737,"16":-0.3034976,"26":-1.6733954,"9":0.40437204,"20":-0.161498,"38":0.44429082,"18":-0.828228,"12":0.028000206,"10":1.2287343},{"16":-0.110653974,"32":-5.0137234,"6":3.2855735,"35":-4.916234,"33":-2.922092,"3":3.0281954,"4":2.7760167,"38":-3.822181,"0":-2.673564,"15":-0.79444206,"19":-0.10265,"22":-1.8150918,"39":-0.63781416,"45":0.86532545,"31":-3.163527,"26":0.078408144,"12":0.5580987,"7":0.95455945,"18":0.88184404,"2":1.7036226,"21":-2.5584326,"20":-0.4360312,"14":-5.642728,"30":-4.7311277,"40":-1.9300216,"37":1.6188036,"43":0.52707785,"44":-1.8107672,"47":-1.0798619,"48":-2.6738892,"8":0.6588317,"28":-7.9935713,"49":-3.944384,"5":2.0550141,"29":-3.8474987,"42":-1.0989082,"46":-5.9515686,"27":-2.0485811,"9":1.471608,"1":2.47185,"24":0.12452121,"34":-1.3555843,"10":1.3148601,"36":-3.0478368,"41":0.3570898,"11":3.7797947,"13":-0.10295038,"25":-0.8727354,"23":-0.35180458,"17":-0.96149045},{"35":-2.0437932,"5":2.0670333,"15":-0.46692443,"16":0.2679208,"0":-0.7605384,"12":0.59541285,"28":-4.173994,"20":-0.3992396,"45":-1.051142,"10":0.8140138,"46":-2.7517638,"42":-6.171156,"23":-4.831238,"48":-2.677819,"1":1.224031,"4":3.2673473,"30":-2.8519773,"32":-4.4643426,"33":-0.44000491,"37":1.3480432,"40":-2.1318023,"43":-1.1509007,"44":-2.0562027,"24":-1.8844818,"26":-5.3923025,"41":0.20562944,"34":0.51221,"38":-0.11386959,"7":1.5406876,"13":0.7819358,"29":-3.4840672,"2":-0.3486044,"31":-1.7830808,"8":1.2974102,"22":-0.5343738,"17":-1.6286198,"25":-4.005146,"36":-5.415494,"9":0.115413606,"27":-5.5485926,"47":-1.6215239,"49":-4.2820125,"21":-1.1757715,"14":0.635203,"18":1.8389509,"39":-0.49644622,"11":-0.68393385,"6":2.645306,"3":1.2012576,"19":0.08734958},{"6":-0.20685184,"7":0.7482052,"23":-0.116231404,"28":-0.40918478,"25":-3.7985291,"12":1.1636417,"15":-4.469844,"39":0.32581556,"29":-1.8403037,"42":-4.6972175,"9":-2.5952392,"45":-2.2271256,"49":-8.009915,"48":0.097842194,"2":0.9440371,"26":-1.4332052,"35":-3.9221509,"27":0.43948522,"40":-1.195525,"8":2.398372,"10":0.8457554,"41":2.879052,"14":-0.44750994,"46":-5.801083,"43":-5.6808176,"11":-4.294608,"5":-1.3787876,"20":0.21462278,"32":0.9640708,"38":3.164453,"47":-1.9674394,"33":-1.3166887,"17":-0.4287854,"13":0.6798086,"4":1.280604,"19":-0.33062363,"22":1.0917785,"16":-0.44355115,"18":1.1119808,"24":-3.979077,"31":-1.5345764,"36":0.22797139,"34":-5.955845,"37":-3.3212132,"44":-3.5280128,"21":1.5814302,"1":2.6665463,"3":-1.297233,"30":-1.5187546,"0":1.5335789},{"9":2.5269558,"34":-0.31695202,"15":0.358236,"16":1.9090137,"28":-7.2770643,"7":0.827785,"22":0.624541,"24":-0.49286598,"27":-0.047854356,"0":0.014746046,"32":-3.046439,"35":-1.970074,"43":-4.0212536,"12":1.1749474,"8":0.629521,"21":1.0198265,"26":-3.8477929,"47":1.4195983,"5":2.8749852,"17":0.3194898,"45":0.15612099,"46":0.57873833,"33":-3.400888,"18":-0.64871776,"31":0.9258426,"20":-0.2123458,"1":-0.018678535,"49":-2.536954,"44":-2.3766665,"48":-2.7109916,"42":2.4054458,"29":-0.365946,"19":-0.8580963,"4":3.024848,"36":2.353439,"6":0.7704121,"3":1.8559345,"11":0.67827654,"14":0.5944872,"40":-1.1564267,"25":-1.254179,"2":2.2601426,"13":0.11195178,"23":-0.7038632,"41":-4.349866,"30":2.0492432,"38":-1.8046792,"10":0.7969944,"37":-1.6574113,"39":0.6443548},{"38":-5.593236,"29":-2.0681195,"41":-0.22347467,"47":-0.20740981,"10":0.6398827,"43":-4.9407096,"27":-1.6263736,"3":0.618173,"25":-5.5301485,"42":-1.0599917,"1":2.8082507,"11":-0.21492091,"19":-0.210343,"46":0.38218263,"28":-4.883363,"40":-1.1004293,"16":1.3583285,"14":1.1719106,"35":-7.591609,"37":-2.6224945,"7":-1.7977062,"44":-2.5597596,"8":1.5589517,"23":0.6407778,"0":3.2596047,"12":-1.5043256,"31":-4.082364,"32":0.26498643,"9":1.5094626,"18":0.44769382,"24":1.0350058,"26":-3.0634084,"2":1.4039166,"30":-4.710024,"48":-3.0759435,"36":0.31134498,"49":-6.624237,"6":-2.9722295,"4":1.5669414,"15":1.6458206,"45":0.085735396,"21":1.7907193,"33":-1.297011,"39":-0.017494207,"22":-0.11674581,"5":2.5079103,"13":1.0968001,"34":-2.7541168,"20":0.5292344,"17":0.1906384},{"45":0.5252536,"26":0.3974368,"13":0.5588312,"30":-1.8244877,"7":1.4933112,"9":-3.8389251,"25":-1.1505449,"32":-2.7444942,"35":-5.6595144,"41":-3.4394646,"48":-1.361736,"37":2.154315,"23":-0.4231196,"42":-5.1911764,"47":-2.6004348,"34":0.2603728,"49":-3.3825104,"36":3.3940125,"46":-2.8885045,"3":2.6000814,"40":0.288439,"12":0.0005240202,"5":0.5214776,"15":-0.6024442,"18":0.5623234,"33":-2.7948456,"21":1.0384016,"31":-5.3817234,"38":0.6145694,"44":-2.7523022,"0":1.7473558,"4":1.6743968,"22":0.098448396,"27":-6.411023,"1":0.13419405,"8":0.6231524,"29":-1.2262142,"39":0.6235626,"10":1.0708561,"19":0.43949756,"28":-6.0392623,"14":-1.1028479,"16":-0.49937558,"2":2.3430197,"17":-0.55631053,"11":1.1489971,"20":0.100311995,"24":-0.96297103,"6":1.6375496,"43":4.015054},{"2":1.0920179,"14":1.3236622,"28":-4.63235,"34":1.0205047,"1":-5.815806,"4":2.558737,"5":-0.28819573,"35":1.8724258,"43":-3.1752496,"8":1.3220638,"20":-0.47128558,"22":-0.6295472,"16":-1.069982,"24":-0.2182076,"32":-3.0007422,"36":1.177172,"11":-0.2960864,"29":-3.1426556,"18":-0.321199,"39":-0.7286054,"40":-3.574768,"13":-1.7908977,"19":-1.566545,"6":0.10560825,"31":-2.9546175,"21":0.5344242,"46":0.8421229,"48":-5.4846654,"26":-0.16800079,"30":-0.63499004,"12":0.9751353,"17":0.080357805,"37":-5.7761393,"47":0.1455886,"0":-0.5469036,"25":-3.657659,"33":-5.989932,"42":-2.2691529,"49":3.8069737,"3":2.483134,"9":0.4608814,"10":-0.21474338,"23":1.5727831,"41":-1.7126083,"44":-2.4735823,"7":1.8333137,"27":-3.513314,"45":-4.305055,"38":0.33684176,"15":0.3030752},{"41":-2.4863777,"42":-1.4586465,"49":0.809266,"13":-1.2854989,"28":1.3336452,"17":0.2468174,"2":2.4948452,"5":2.9562654,"1":1.420696,"10":2.5979302,"19":1.0988202,"21":1.2759278,"25":-1.7183332,"37":-5.066767,"11":-1.7485008,"47":1.6204903,"24":1.452265,"8":0.07814765,"29":-2.434204,"0":-0.70899695,"9":0.15161927,"39":-4.978727,"46":-2.8493817,"12":-3.0307314,"15":0.14451161,"16":0.30487078,"33":-7.813867,"3":0.26481026,"34":-0.8905366,"48":-3.2962487,"18":1.7813027,"23":2.5804691,"7":0.616895,"14":1.2282082,"26":-1.2754395,"35":-4.9574194,"36":-7.887868,"38":1.2359992,"45":1.3187423,"44":-4.939965,"43":-0.731094,"32":-7.854181,"22":0.2931754,"40":-3.4965339,"27":-8.200976,"4":-1.6270396,"31":-3.2845535,"20":-0.0813784,"6":1.2323984,"30":1.2903303},{"29":0.76983786,"10":1.1695973,"30":2.3881211,"13":-2.1178145,"5":1.032727,"7":0.4232266,"14":1.4746369,"42":-3.0101175,"24":0.6749432,"3":2.5838737,"47":0.3716606,"12":0.034056615,"19":-0.5428704,"43":-2.3864942,"11":0.1684568,"33":2.047521,"36":1.3975251,"4":1.4912655,"49":-1.9071302,"20":0.66135,"31":0.3048658,"9":0.3718818,"39":-4.9778905,"34":-5.4845343,"21":0.264103,"28":-4.6927276,"2":3.9047546,"22":1.7412987,"26":-0.34287968,"0":0.79953676,"8":-2.503515,"18":0.5331046,"45":-3.80481,"40":-3.3307064,"35":-0.66795295,"23":2.5979419,"32":-5.551989,"15":-1.5478857,"37":0.6220144,"38":1.4604373,"41":0.97876513,"46":2.3414655,"48":0.4169334,"27":3.265742,"44":3.1393316,"25":-4.0014124,"16":1.7088041,"6":0.92421687,"1":-2.643936,"17":1.5321608},{"18":0.15271512,"28":0.8231074,"31":-1.9563223,"11":1.4094507,"5":2.5155084,"44":-1.3571329,"46":1.8211524,"47":-4.676179,"10":0.56388783,"48":-5.832213,"8":1.566211,"34":0.5341672,"39":2.360628,"41":-0.81608117,"22":-0.647547,"36":-3.0471568,"6":-0.59395313,"4":1.2839394,"25":-5.5545883,"37":-2.5384088,"20":2.9726455,"12":1.1182439,"13":1.8803022,"30":-3.0120084,"40":-7.0986595,"43":1.8841732,"0":0.6630294,"35":-3.9670842,"45":-3.7290092,"49":-5.0952806,"17":0.2583828,"9":0.7004884,"2":2.3868642,"14":1.1022385,"7":1.5477122,"33":2.143043,"38":0.5190696,"15":0.14373502,"29":-4.386737,"3":-3.0224242,"26":-3.8995755,"32":-0.51439965,"42":-0.383613,"24":0.26088682,"1":3.7029004,"16":1.1914532,"21":1.7543865,"19":2.4447637,"23":2.213075,"27":3.2780387},{"2":-0.65427446,"17":0.90136564,"9":-0.5912026,"45":-3.1104972,"30":3.8257012,"1":-1.1222456,"33":3.2083735,"42":-0.87039006,"48":-2.8021472,"18":1.5804374,"35":-0.44894782,"3":2.8484106,"21":1.9852188,"25":0.368119,"29":1.5137444,"4":3.0808597,"5":0.16939983,"43":-0.07154502,"20":-2.8535187,"46":-10.316307,"22":1.0970856,"41":3.381101,"15":-4.5189314,"47":-4.271408,"32":-0.12736262,"6":1.4997796,"8":0.8192856,"11":-1.1406939,"28":-3.0016181,"26":1.0549641,"19":0.3062792,"24":1.4364198,"38":-7.554895,"34":-5.6036787,"12":1.6562859,"37":0.65752596,"39":-5.1502852,"13":1.6195282,"40":-3.1926622,"49":-5.48314,"31":3.1437995,"16":2.4158304,"7":2.427155,"23":1.5658007,"10":4.544055,"0":3.7469585,"27":-3.7847276,"36":-0.1779578,"44":-2.9680314,"14":2.4946625},{"3":3.298818,"4":-0.100204185,"10":2.4243913,"31":-0.25114003,"18":0.24044621,"36":0.8928568,"7":1.5746257,"14":0.15602557,"2":2.7182584,"12":0.825092,"8":3.3791401,"0":0.8052031,"22":0.8128505,"34":1.0762432,"41":2.5831215,"17":2.047896,"33":-1.9972169,"46":0.7778258,"20":-1.6034877,"1":4.514162,"28":0.11998816,"48":-0.5316952,"35":-1.5996503,"11":0.22015977,"43":1.3659737,"26":-1.7325569,"27":-8.253664,"13":0.1734304,"37":-0.3869438,"29":4.442569,"47":-1.8515161,"21":-1.5478828,"25":0.3130408,"45":-1.9054098,"16":1.3335179,"49":0.8774155,"19":1.4240228,"40":-2.9783475,"42":-8.505626,"44":-0.31775862,"23":1.3930626,"15":1.3329651,"6":2.955215,"32":-3.1671722,"5":-3.956508,"30":-0.19470882,"38":2.264006,"39":-2.97102,"9":3.0682359,"24":-3.2080452},{"5":-0.5408281,"34":1.6181517,"47":1.5908577,"48":1.7172235,"49":-2.4024558,"17":1.9604908,"45":0.4115004,"37":0.3741082,"21":0.6393524,"13":1.7402023,"41":-1.8253998,"12":1.2494247,"23":0.70641744,"9":2.8593893,"10":4.1445603,"28":0.7264148,"20":-0.022489607,"22":-0.05633059,"11":-0.87277544,"32":-1.2561429,"42":1.4251055,"7":2.4510393,"1":-0.94481486,"31":-1.175452,"14":5.030714,"16":0.5764612,"29":-3.5285103,"6":2.1434236,"0":1.7296807,"43":2.7396176,"39":4.023013,"2":2.3528953,"25":1.9261501,"38":-0.88579595,"8":1.4411128,"4":1.5460316,"18":0.282081,"33":2.3182895,"26":-0.9694773,"40":-3.379509,"24":0.13257396,"35":2.2814608,"27":1.6139975,"3":2.6575274,"36":-3.9818718,"44":2.7506993,"30":-8.778745,"15":-2.0349305,"46":-3.470742,"19":-3.8818722},{"0":-0.06885891,"17":0.636088,"22":2.3478549,"24":-0.045837425,"2":-0.7956078,"12":1.9855839,"32":-2.3023667,"30":-0.97484815,"33":-7.4737372,"36":0.2148408,"40":1.4850098,"38":-2.8117013,"35":-3.077861,"41":3.9098945,"21":1.5247008,"42":-7.0531464,"31":0.36817628,"23":1.3593233,"39":1.0669391,"45":-3.938919,"47":1.2769856,"18":6.957845,"26":-5.302268,"44":-4.660885,"9":2.2920365,"7":2.529813,"1":2.1732376,"16":0.5333187,"14":-2.2619777,"3":2.3606544,"4":3.9950047,"6":1.7795098,"15":3.1897218,"5":0.10226059,"19":0.28279838,"10":3.0701108,"20":4.0169177,"27":2.5359845,"8":1.9472278,"29":-4.2149224,"34":-0.7892742,"13":1.2534057,"37":-1.3440574,"43":-6.9592667,"48":1.8630025,"25":0.82092935,"49":-2.3563805,"11":3.297667,"28":-0.329632,"46":3.2459197},{"22":0.50588465,"25":-4.7370954,"19":-0.91318095,"28":0.31480804,"31":0.065649465,"3":0.5063764,"6":5.1110444,"29":-2.489795,"32":-11.229839,"33":2.4709823,"34":2.8695674,"36":0.62174416,"40":0.1279682,"42":0.47100592,"44":-6.3088784,"15":-2.8494515,"24":1.8442934,"9":2.8121595,"46":-1.0751657,"27":-9.398052,"13":1.7741573,"17":4.1550884,"23":-2.0619445,"26":0.017771998,"38":2.193775,"41":-4.4600706,"49":1.5474513,"48":-0.37165743,"0":1.003366,"1":-0.32828838,"11":-1.8789746,"39":-2.3800955,"21":0.25985384,"35":-4.9107614,"4":4.0792146,"43":0.9346365,"47":-3.9471307,"37":0.43831158,"5":3.9157176,"16":2.5956256,"20":-0.17795548,"30":-0.44199666,"14":0.76868117,"45":-1.9965626,"7":-4.6487746,"18":0.8212639,"12":0.53507257,"8":2.3825238,"10":1.4968458,"2":-3.0374155},{"31":-7.249073,"2":5.348681,"32":-3.786815,"49":-8.196339,"4":-0.8796919,"3":3.8961625,"19":2.259184,"28":3.560927,"48":3.826062,"6":4.164523,"38":0.7781402,"46":-4.5676928,"29":-5.356247,"44":0.15922546,"23":-3.1550167,"21":-4.3266816,"45":1.2732226,"30":-1.4273186,"16":1.6312279,"36":0.24064521,"40":8.496379,"39":-2.6050453,"0":3.1275628,"8":-1.777848,"5":2.8627615,"12":-3.394359,"17":2.5028915,"35":-3.440471,"11":1.8028643,"10":0.92121077,"1":2.924457,"22":-3.813434,"24":-0.678315,"25":-0.17476197,"34":-1.3690792,"27":2.1548553,"14":-3.4256318,"20":1.4138949,"42":-4.0063763,"43":-0.44322735,"37":-3.5964768,"47":-3.10027,"13":0.79479057,"15":-1.8431025,"41":-6.513213,"7":-6.554456,"26":-0.3503102,"9":-3.2855153,"33":-6.1897845,"18":-1.7627401},{"0":-1.379682,"9":1.7693253,"24":-1.7425396,"29":-1.3095939,"12":0.3712794,"10":2.8009505,"39":2.0196414,"40":-5.442218,"19":0.07471542,"38":-8.772409,"30":-4.0180197,"11":1.4203637,"41":-4.7962756,"42":-1.1986992,"49":1.1919396,"4":0.32527694,"36":-1.9501377,"20":3.3796432,"43":0.95696867,"45":-1.8301255,"46":-8.758689,"25":-6.2930193,"6":2.598937,"47":-1.7618761,"48":5.545912,"2":-0.18258914,"16":1.6644957,"13":1.4507275,"27":-1.2420224,"44":-1.6392796,"21":-1.834655,"17":4.7461395,"32":1.9383072,"33":-0.09801979,"37":2.1693015,"15":3.7099216,"18":-0.849576,"3":2.982232,"31":1.9015796,"7":0.56306016,"14":1.4726218,"1":4.4150147,"23":-2.4777474,"28":-2.066594,"35":-9.634148,"8":0.754855,"22":0.9184534,"5":0.77764595,"26":3.8850017,"34":-6.85335},{"6":3.9536424,"37":-1.7106135,"3":3.952736,"41":0.7060195,"29":2.3967164,"33":-0.43785763,"12":1.9563553,"32":2.9527333,"34":-1.0106162,"20":0.13315716,"27":-0.92878675,"46":6.8867826,"49":0.93543357,"4":1.1506236,"14":1.0140021,"24":2.2763062,"28":3.0574925,"5":0.7614522,"18":0.8230068,"48":3.324789,"38":-1.2328963,"8":0.42752314,"39":1.5602262,"43":2.0240207,"31":0.43064317,"36":-1.9770912,"13":1.6254408,"15":2.7690034,"19":1.0028356,"22":-0.08353281,"21":5.3307166,"7":1.3423629,"25":1.2356588,"10":0.9194766,"30":-5.161771,"1":2.7103047,"35":-3.3447888,"42":-2.481101,"40":4.1213207,"0":-1.4099376,"11":0.052261163,"23":3.419119,"16":1.7044884,"9":-1.8273771,"17":1.8015019,"44":-7.9374723,"26":2.2453463,"2":4.293378,"47":-2.043562,"45":-0.16055045}],"nn_shapes":{"20":[18,32,11,18,5,8],"8":[18,30,27,26,14,8],"12":[18,17,8,23,30,23,8,33,27,8],"9":[18,7,33,20,15,33,18,8],"25":[18,6,29,26,8],"48":[18,12,11,13,20,25,8],"37":[18,24,24,8],"47":[18,8,24,6,10,16,30,11,8],"19":[18,4,23,8],"44":[18,31,16,7,19,23,25,7,12,6,8],"43":[18,25,8],"31":[18,3,11,15,15,24,24,24,28,23,8],"36":[18,32,7,10,17,9,7,17,11,8],"7":[18,7,5,6,30,29,31,13,7,8],"24":[18,18,8],"26":[18,14,10,33,8,11,4,21,28,8],"18":[18,26,7,6,8],"6":[18,8,25,8],"23":[18,12,4,27,27,16,8],"1":[18,15,31,25,7,30,33,33,8,8],"28":[18,3,20,20,8,8],"2":[18,30,33,8,24,22,13,28,3,8],"17":[18,33,14,8,15,8],"30":[18,8,31,31,20,32,33,8],"27":[18,28,20,20,15,8],"3":[18,32,15,3,11,5,11,3,8],"16":[18,31,18,8,29,8],"22":[18,16,18,34,34,12,18,8],"4":[18,10,29,8],"11":[18,29,29,30,29,8],"32":[18,11,8],"13":[18,4,24,19,15,33,10,34,15,8],"41":[18,4,8,5,23,33,6,5,8],"45":[18,22,20,18,34,7,15,8],"49":[18,24,8],"0":[18,13,19,14,20,15,8],"39":[18,25,8],"5":[18,17,8],"10":[18,30,5,28,4,27,8],"29":[18,12,13,12,20,32,8],"40":[18,19,8],"14":[18,30,11,14,24,26,9,29,15,13,8],"21":[18,31,17,8],"33":[18,24,11,8],"35":[18,29,17,13,20,28,25,19,8],"15":[18,17,28,4,8],"38":[18,34,30,3,8],"34":[18,34,15,23,8],"42":[18,33,9,33,29,5,17,24,8],"46":[18,19,10,34,8]},"crossbreed_segments":13,"weight_initialization_range":{"start":-0.69214654,"end":0.41588885},"minor_mutation_rate":0.9403318,"major_mutation_rate":0.28131688,"mutation_weight_range":{"start":-0.99331,"end":0.99331}},"state":"Finish","generation":20,"max_generations":20,"id":"48ec59ed-40e8-4fe0-a929-92cc324873ca"},"left":null,"right":null}},"right":{"val":{"node":{"id":"55eaae5a-e128-4417-ab98-89653f77e382","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_55eaae5a-e128-4417-ab98-89653f77e382","population_size":50,"generation":24,"scores":[{"37":-7.1222725,"12":-3.6037624,"27":-5.202844,"21":-6.3283415,"4":-6.0053186,"8":-4.040202,"13":-4.0050435,"17":-5.8206105,"40":-7.5448103,"42":-8.027704,"15":-5.1600137,"10":-7.9063845,"1":-6.9830275,"7":-3.3323112,"16":-6.1065326,"23":-6.417853,"25":-6.410652,"14":-6.5887403,"3":-6.3966584,"19":0.1242948,"28":-4.806827,"18":-6.3310747,"30":-5.8972425,"31":-6.398958,"22":-7.042196,"29":-5.7098813,"9":-8.931531,"33":-5.9806275,"6":-6.5489874,"26":-5.892653,"34":-6.4281516,"35":-5.5369387,"38":-5.495344,"43":0.9552175,"44":-6.2549844,"45":-8.42142,"24":-7.121878,"47":-5.373896,"48":-6.445716,"39":-6.053849,"11":-5.8320975,"49":-10.014197,"46":-7.0919595,"20":-6.033137,"5":-6.3501267,"32":-4.203919,"2":-5.743471,"36":-8.493466,"41":-7.60419,"0":-7.388545},{"18":-6.048934,"39":-1.1448132,"48":-7.921489,"38":-6.0117235,"27":-6.30289,"9":-6.5567093,"29":-5.905172,"25":-4.2305975,"40":-5.1198816,"24":-7.232001,"46":-6.5581756,"20":-6.7987585,"8":-9.346154,"2":-7.6944494,"3":-6.487195,"16":-8.379641,"32":-7.292016,"33":-7.91467,"41":-7.4449363,"21":-6.0500197,"19":-5.357873,"10":-6.9984064,"7":-5.6824636,"13":-8.154273,"45":-7.8713655,"47":-5.279138,"49":-1.915852,"6":-2.682654,"30":-5.566201,"1":-1.829716,"11":-7.7527223,"12":-10.379072,"15":-4.866212,"35":-8.091223,"36":-8.137203,"42":-7.2846284,"44":-4.7636213,"28":-6.518874,"34":1.9858776,"43":-10.140268,"0":-3.5068736,"17":-2.3913155,"26":-6.1766686,"22":-9.119884,"14":-7.470778,"5":-5.925585,"23":-6.004782,"31":-2.696432,"4":-2.4887466,"37":-5.5321026},{"25":-8.760574,"0":-2.5970187,"9":-4.270929,"11":-0.27550858,"20":-6.7012835,"30":2.3309054,"4":-7.0107384,"31":-7.5239167,"41":-2.337672,"6":-3.4384027,"16":-7.9485044,"37":-7.3155503,"38":-7.4812994,"3":-3.958924,"42":-7.738173,"43":-6.500585,"22":-6.318394,"17":-5.7882595,"45":-8.782414,"49":-8.84129,"23":-10.222613,"26":-6.06804,"32":-6.4851217,"33":-7.3542376,"34":-2.8723297,"27":-7.1350646,"8":-2.7956052,"18":-5.0000043,"10":-1.5138103,"2":0.10560961,"7":-1.4954948,"35":-7.7015786,"36":-8.602789,"47":-8.117584,"28":-9.151132,"39":-8.035833,"13":-6.2601876,"15":-9.050044,"19":-5.465233,"44":-8.494604,"5":-6.9012084,"12":-9.458872,"21":-5.980685,"14":-7.7407913,"46":-0.701484,"24":-9.477325,"29":-6.6444407,"1":-3.4681067,"40":-5.4685316,"48":0.22965483},{"11":-5.7744265,"12":0.10171394,"18":-8.503949,"3":-1.9760166,"17":-7.895561,"20":-8.515409,"45":-1.9184738,"6":-5.6488137,"46":-6.1171823,"49":-7.006673,"29":-3.6479561,"37":-4.025724,"42":-4.1281996,"9":-2.7060657,"33":0.18799233,"15":-7.8216696,"23":-11.02603,"22":-10.132984,"7":-6.432255,"38":-7.2159233,"10":-2.195277,"2":-6.7676725,"27":-1.8040345,"34":-11.214028,"40":-6.1334066,"35":-9.410227,"44":-0.14929143,"47":-7.3865366,"41":-9.200221,"26":-6.1885824,"13":-5.5693216,"31":-8.184256,"39":-8.06583,"24":-11.773471,"25":-15.231514,"14":-5.4468412,"30":-5.494699,"21":-10.619481,"28":-7.322004,"16":-7.4136076,"8":-3.2260292,"32":-8.187313,"19":-5.9347467,"43":-0.112977505,"5":-1.9279568,"48":-3.8396995,"0":-9.317253,"4":-1.8099403,"1":-5.4981036,"36":-3.5487309},{"28":-6.2057357,"40":-6.9324327,"46":-0.5130272,"23":-7.9489794,"47":-7.3411865,"20":-8.930363,"26":-3.238875,"41":-7.376683,"48":-0.83026105,"27":-10.048681,"36":-5.1788163,"30":-8.002236,"9":-7.4656434,"4":-3.8850121,"16":-3.1768656,"11":1.0195583,"44":-8.7163315,"45":-6.7038856,"33":-6.974304,"22":-10.026589,"13":-4.342838,"12":-6.69588,"31":-2.2994905,"14":-7.9772606,"32":-10.55702,"38":-5.668454,"34":-10.026564,"37":-8.128912,"42":-10.7178335,"17":-5.18195,"49":-9.900299,"21":-12.4000635,"8":-1.8514707,"29":-3.365313,"39":-5.588918,"43":-8.482417,"1":-4.390686,"35":-5.604909,"24":-7.1810236,"25":-5.9158974,"19":-4.5733366,"0":-5.68081,"3":-2.8414884,"6":-1.5809858,"7":-9.295659,"5":-3.7936096,"10":-4.088697,"2":-2.3494315,"15":-7.3323736,"18":-7.7137175},{"1":-2.7719336,"37":-6.097855,"39":-4.1296787,"2":-5.4538774,"34":-11.808794,"40":-9.822159,"3":-7.884645,"42":-14.777964,"32":-2.6564443,"16":-5.2442584,"9":-6.2919874,"48":-2.4359574,"25":-11.707236,"33":-5.5483084,"35":-0.3632618,"7":-4.3673687,"27":-8.139543,"12":-9.019396,"17":-0.029791832,"24":-8.63045,"18":-11.925819,"20":-9.040375,"44":-10.296264,"47":-15.95397,"23":-12.38116,"21":0.18342426,"38":-7.695002,"6":-8.710346,"28":-2.8542902,"5":-2.077858,"10":-3.638583,"8":-7.360152,"15":-7.1610765,"29":-4.8372035,"45":-11.499393,"13":-3.8436065,"22":-5.472387,"11":-4.259357,"26":-4.847328,"4":-2.0376666,"36":-7.5392637,"41":-5.3857164,"19":-8.576212,"14":-8.267895,"30":-4.0456495,"31":-3.806975,"43":-7.9901657,"46":-7.181662,"0":-7.502816,"49":-7.3067017},{"17":-9.793276,"27":-2.8843281,"38":-8.737534,"8":-1.5083166,"16":-8.267393,"42":-8.055011,"47":-2.0843022,"14":-3.9945045,"30":-10.208374,"26":-3.2439823,"49":-2.5527742,"25":-10.359426,"9":-4.4744225,"19":-7.2775927,"3":-7.282045,"36":-8.503307,"40":-12.083569,"22":-3.7249084,"18":-7.5065627,"41":-3.3326488,"44":-2.76882,"45":-12.154654,"24":-2.8332536,"5":-5.2674284,"4":-4.105483,"10":-6.930478,"20":-3.7845988,"2":-4.4593267,"28":-0.3003047,"29":-6.5971193,"32":-5.0542274,"33":-9.068264,"43":-7.124672,"46":-8.358111,"23":-5.551978,"11":-7.7810373,"35":-7.4763336,"34":-10.868844,"39":-10.51066,"7":-4.376377,"48":-9.093265,"6":-0.20033613,"1":-6.125786,"12":-8.243349,"0":-7.1646323,"13":-3.7055316,"15":-6.295897,"21":-5.929867,"31":-7.2123885,"37":-2.482071},{"30":-12.467585,"14":-5.1706576,"40":-9.03964,"18":-5.7730474,"41":-9.061858,"20":-2.8577142,"24":-3.3558655,"42":-7.902747,"43":-6.1566644,"21":-5.4271364,"23":-7.1462164,"44":-7.9898252,"11":-2.493559,"31":-4.6718645,"48":-12.774545,"8":-7.252562,"35":-1.6866531,"49":-4.437603,"45":-7.164916,"7":-4.613396,"32":-8.156101,"39":-10.887325,"0":-0.18116185,"47":-4.998584,"10":-8.914183,"13":-0.8690014,"27":-0.3714923,"28":-12.002966,"9":-6.2789965,"26":-0.46416503,"2":-9.865377,"29":-8.443848,"46":-6.3264246,"3":-7.807205,"4":-6.8240366,"5":-6.843891,"12":-5.6381693,"15":-4.6679296,"36":-6.8010025,"16":-8.222928,"25":-10.326822,"34":-6.0182467,"37":-8.713378,"38":-7.549215,"17":-7.247555,"22":-13.296148,"33":-8.542955,"19":-7.254419,"1":-2.8472056,"6":-5.898753},{"7":-3.6624274,"4":-2.9281456,"39":-5.9176188,"13":-8.0644045,"16":-2.0319564,"49":-10.309226,"3":-0.21671781,"37":-8.295551,"44":-16.496105,"46":-6.2466326,"47":-3.5928986,"19":-9.298591,"1":-7.937351,"15":-8.218504,"6":-6.945601,"25":-8.446054,"12":-5.8477135,"14":-3.9165816,"17":-2.4864268,"20":-7.97737,"22":-5.347026,"0":-6.0739775,"32":-6.7568192,"36":-4.730008,"28":-9.923819,"38":-8.677519,"42":-4.668519,"48":0.14014988,"5":-8.3167,"8":-2.5030074,"21":-1.8195568,"27":-6.111103,"45":-12.708131,"35":-8.089076,"11":-6.0151362,"34":-13.688166,"33":-11.375975,"2":-4.1082373,"24":-4.0867376,"10":-4.2828474,"41":-9.174506,"43":-1.1505331,"29":-3.7704785,"18":-4.9493446,"30":-3.727829,"31":-6.490308,"9":-6.0947385,"40":-9.492185,"26":-13.629112,"23":-9.773454},{"12":-1.754871,"41":2.712658,"24":-4.0929146,"18":-4.9418926,"44":-9.325021,"8":-6.4423165,"1":-0.0946085,"5":-3.0156248,"14":-5.29519,"34":-10.763539,"11":-7.304751,"20":-6.8397574,"22":-5.6720686,"23":-7.829904,"7":-3.8627372,"6":-3.1108487,"16":-8.803584,"36":-13.916307,"21":-10.142917,"37":-12.171498,"45":-13.004938,"19":-3.7237267,"47":-6.0189786,"17":-4.612711,"15":-5.3010545,"30":-5.671092,"46":-13.300519,"25":-8.2948,"3":-10.556543,"42":-7.041272,"48":-9.797744,"9":-5.6163936,"26":-6.665021,"27":-7.074666,"4":-1.5992731,"2":-6.4931273,"29":-3.9785416,"31":-12.222026,"10":-2.3970482,"40":-6.204074,"49":-7.025599,"28":-8.562909,"13":-6.2592154,"32":-10.465271,"33":-7.7043953,"35":-6.4584246,"38":-2.9016697,"39":-1.5256255,"43":-10.858711,"0":-4.720929},{"2":-5.1676617,"3":-4.521774,"29":-7.3104324,"23":-6.550776,"26":-10.467587,"18":1.6576093,"33":-2.564094,"20":-3.2697926,"35":-13.577334,"37":-6.0147185,"17":-4.07909,"0":-9.630419,"38":-7.011383,"12":-10.686635,"43":-8.94728,"48":-9.350017,"30":-7.3335466,"13":-7.7690034,"4":-2.3488472,"14":-7.2594194,"21":-9.08367,"34":-7.7497597,"8":-6.2317214,"27":-8.440135,"22":-4.4437346,"32":-2.194015,"28":-6.6919556,"40":-8.840385,"42":-9.781796,"15":-7.3304253,"49":-8.720987,"19":-9.044103,"6":-5.715863,"41":-8.395639,"36":-3.995482,"25":-9.1373005,"5":-7.5690002,"1":-6.0397635,"16":-8.231512,"10":-6.5344634,"44":-7.749376,"7":-9.302668,"31":-10.868391,"39":-2.7578635,"47":-6.964238,"24":-4.033315,"11":-8.211409,"45":-10.472969,"9":-7.1529093,"46":-9.653514},{"44":-10.252684,"45":-12.034803,"28":-10.405336,"24":1.8021276,"4":-9.921194,"47":-7.9789643,"2":-5.7931147,"29":-7.596897,"25":-10.756151,"10":-8.454973,"7":-3.6788304,"18":-9.022151,"20":-8.593645,"26":-5.224861,"14":-0.5604599,"40":-6.2325897,"0":-0.44600934,"9":-8.139498,"16":-4.6218505,"46":-6.273614,"12":-3.8260474,"31":-11.0625305,"39":-12.0089445,"41":-7.3617644,"42":-4.938992,"48":-3.4777954,"30":-2.2662613,"33":-0.7261769,"35":-8.172319,"15":1.0887976,"23":-5.195476,"34":-7.1455193,"17":-12.706947,"13":-3.165891,"3":-9.327427,"21":-8.4359255,"32":-1.9019763,"37":-3.6867127,"38":-8.124417,"5":-7.17052,"49":-3.9712586,"36":-7.623468,"22":-7.0057154,"43":-6.3270884,"27":-8.314371,"6":-4.204889,"8":-8.243958,"11":-5.846903,"19":-2.6325738,"1":-6.2474594},{"39":0.43706903,"44":-6.5642495,"40":-8.924684,"46":-1.797776,"7":-5.478422,"48":-6.087083,"49":-7.956244,"47":-10.078558,"15":-1.2380409,"12":-8.280014,"20":-3.5678017,"33":-16.677906,"27":-9.337598,"26":-5.8644075,"14":-8.427099,"3":-3.3714929,"34":-11.108117,"10":-4.5418024,"21":-9.045767,"18":-8.947485,"8":-7.152398,"43":-9.5986,"11":-6.350339,"41":-1.0246458,"19":-3.1153014,"13":-5.9651165,"38":-2.3405242,"45":-5.2560587,"6":-10.963091,"0":-4.210406,"16":-2.8244996,"37":-8.667362,"25":-10.942785,"22":-4.6812115,"28":-5.555095,"5":-0.59754115,"23":-6.416314,"29":-7.2626123,"36":-12.767626,"17":-7.450956,"31":-6.298139,"1":-5.367549,"32":-4.166111,"9":-8.514038,"2":-5.0412307,"42":-12.814206,"24":-10.961937,"4":-5.677783,"30":-6.514361,"35":-6.4758873},{"3":-1.4171193,"30":-8.201865,"14":-1.0829871,"15":-8.696184,"34":-7.381269,"16":-4.2309136,"35":-7.2169228,"8":0.04883461,"23":-7.0893745,"2":-11.336302,"29":-10.249007,"41":-5.0222054,"49":-4.9262657,"10":-2.5520484,"1":-4.415363,"20":-12.114428,"28":-14.079625,"7":-6.758162,"22":-12.994962,"43":-6.761683,"36":-11.336916,"21":-3.6906323,"0":-8.01961,"17":-5.2390943,"33":-11.575128,"13":-5.7145157,"31":0.16249037,"38":-6.41915,"26":-3.9492679,"24":-5.269326,"45":-8.842736,"46":-7.1800737,"47":-14.274612,"32":-10.240828,"5":-5.5345345,"9":-2.3435318,"11":-8.388118,"25":-5.9386187,"39":-8.01918,"6":-6.938567,"12":-3.0821395,"42":-2.6356082,"44":-1.0947597,"48":-3.987667,"37":-8.242175,"4":-3.000874,"18":-4.3863974,"27":-10.993831,"19":-4.9101877,"40":-9.36493},{"1":-2.5517411,"13":-1.3773863,"48":-5.929145,"40":-6.016658,"35":-8.30968,"39":-8.173601,"43":-9.2206545,"9":-5.413448,"47":-11.219854,"36":-6.522706,"17":-1.9782183,"12":-4.4436707,"3":-5.0848694,"4":-8.382575,"26":-8.983437,"27":-7.5344496,"16":-8.203341,"25":-13.385138,"31":-2.8448677,"33":-7.537777,"8":-9.623908,"10":-5.2933273,"38":-4.203252,"32":-8.949801,"29":-7.71904,"7":-0.59097546,"11":-3.7152443,"18":-7.70849,"24":-8.392967,"30":-8.966001,"14":-3.1536689,"34":-9.573047,"2":-6.939415,"45":-6.952498,"46":-5.5352817,"0":-5.7708282,"28":-7.2594924,"20":-3.7915637,"15":-4.1908197,"49":-6.2850904,"19":-7.4985657,"22":-4.893429,"21":-3.838546,"41":-3.2204995,"37":-6.383028,"42":-7.637233,"5":-3.848416,"44":-9.47161,"6":1.3985821,"23":-6.856476},{"44":-2.861644,"5":-10.645139,"47":-0.74191725,"48":-3.7928212,"41":-3.9138176,"32":-5.723269,"49":-4.4118395,"11":-4.8106203,"45":-5.8674803,"12":-6.1513,"4":-5.2970977,"22":-5.655927,"38":-1.740617,"8":-3.2617576,"30":-9.095524,"7":-12.736498,"10":-2.8470557,"27":-4.006469,"21":-6.1753983,"13":-6.109749,"35":-8.483802,"37":-1.252423,"3":-3.4497364,"46":-9.584096,"33":-3.990739,"43":-5.901332,"20":-9.113932,"24":-8.143213,"0":-14.938147,"2":-7.107156,"9":-1.7288609,"19":-7.172612,"16":-9.64662,"6":-5.6817265,"1":-2.0544078,"28":-12.049086,"17":-4.142796,"23":-16.78327,"14":0.9610933,"29":-9.290024,"26":-9.000046,"31":-12.539607,"15":-4.6688976,"34":-5.9241714,"18":-7.4736533,"36":-9.283766,"39":-9.680832,"40":-15.069257,"42":-5.9029703,"25":-10.804184},{"42":-7.390395,"6":-5.2423105,"20":-9.492358,"30":-10.119262,"8":-8.568228,"17":-5.3969054,"35":-6.3838105,"47":-10.690717,"1":-4.7279353,"3":-6.285371,"25":-4.457618,"15":-3.7073922,"28":-5.813173,"33":-13.824557,"22":-5.7607093,"7":-3.968948,"23":-8.332884,"27":-8.011797,"34":-8.851597,"41":-7.5558634,"44":-12.499257,"45":-5.670898,"0":-7.305059,"11":-4.6200676,"12":-3.284594,"29":-6.23612,"46":-3.97527,"21":-9.3430195,"39":-7.0078306,"26":-2.9919415,"19":-1.8206236,"36":-3.0798898,"40":-9.510875,"43":-4.564447,"48":-13.20809,"10":-8.832611,"2":-3.821116,"38":-3.5049846,"16":-0.5627325,"14":-9.582281,"37":-7.3288584,"18":-2.5249457,"24":-5.016801,"13":-5.3800035,"5":-3.939186,"32":-6.202336,"49":-6.234208,"9":-3.3935227,"31":-7.889515,"4":-5.329782},{"42":-9.893343,"46":-6.007334,"8":-10.236174,"28":-8.647413,"6":-4.17853,"29":-6.938863,"37":-11.130651,"26":-11.733725,"12":-1.986285,"22":-10.029884,"35":-13.32892,"38":-2.4419475,"43":-5.606353,"14":-8.411512,"41":-7.4532537,"17":-3.4157784,"1":-8.006592,"11":-2.6495078,"33":-9.877732,"36":-8.044171,"45":-8.178667,"18":-3.0599625,"20":-3.3062778,"10":-7.8465548,"9":-4.089144,"23":-1.634593,"19":-4.513993,"32":-7.5857177,"47":-13.059774,"0":-9.734381,"24":-7.550769,"30":-0.13144064,"25":-1.6051563,"40":-5.767811,"49":-10.060788,"34":-0.9332817,"31":-10.026274,"39":-4.928876,"5":-5.804112,"44":-6.4794436,"21":0.083639145,"13":-8.63159,"2":-0.3849109,"4":-6.1427803,"48":-8.350489,"16":-4.954889,"27":-12.082092,"3":-9.490924,"15":-4.8369503,"7":-5.296665},{"14":-5.2623034,"32":-10.759203,"17":-6.541181,"0":-1.3925167,"7":-4.83245,"16":-4.1242895,"36":-9.173063,"38":-7.836097,"22":-9.022796,"40":-2.238197,"5":-7.156565,"44":-11.245649,"33":-2.9633536,"20":-7.3342886,"28":-3.333572,"19":-1.4218113,"46":-2.953141,"26":-2.6449294,"8":-3.6259913,"45":-9.9275875,"11":-5.8978066,"23":-5.9765697,"6":-4.419637,"37":-7.7864504,"39":-3.7646708,"47":-8.30409,"43":-3.0333,"49":-8.081027,"25":-8.435537,"18":-6.748767,"30":-8.089577,"15":-8.759301,"29":-9.526569,"1":-5.096554,"3":-5.9022226,"4":-8.257174,"27":-8.606438,"41":-1.497746,"42":-7.427924,"10":-3.2451184,"13":-6.449167,"21":-4.4653535,"24":-7.9258027,"35":-5.5284815,"9":-0.7580929,"34":-7.790374,"48":0.4532753,"31":-9.990816,"12":-4.272876,"2":-6.3599634},{"28":-8.533937,"42":-5.232602,"24":-6.8833175,"36":-6.4969416,"40":-5.8373866,"31":-4.522848,"14":-4.421405,"34":-5.346702,"10":-5.1903396,"20":-6.7530456,"13":-10.935861,"25":-4.5247335,"22":-2.8099911,"37":-8.892298,"46":-9.271803,"5":-5.382541,"9":-2.9776273,"43":-6.9205627,"39":-7.3040075,"44":-9.451643,"6":-2.3566048,"30":-1.4466946,"4":-5.8034754,"18":-3.2191722,"12":-8.764809,"0":-5.13083,"15":-5.9475503,"19":-4.895011,"11":-6.9602995,"16":-7.5741563,"29":-7.5532074,"48":-10.143312,"35":-9.424163,"2":-3.180596,"27":-5.8622227,"17":-1.6907284,"38":-8.533536,"26":-6.4998374,"33":-0.8160969,"7":-3.431047,"23":-5.404256,"3":-1.316479,"21":-10.0209255,"1":-4.8019085,"41":-7.7153654,"32":-5.9212785,"45":-8.015295,"8":-5.7746153,"49":-6.595235,"47":-7.30956},{"3":-5.717492,"38":-2.8490224,"23":-5.651288,"28":-11.385461,"44":-14.559361,"19":-8.516624,"5":-3.4115243,"40":-5.246222,"33":-10.784277,"46":-16.474335,"34":-9.588152,"48":-1.5694482,"7":-6.5084734,"36":-5.6967373,"9":-7.9818993,"0":-5.810563,"12":-4.71036,"18":-6.8263655,"35":-8.9194,"20":-5.9936,"29":-8.087461,"14":-4.6354985,"13":-3.1506,"32":-5.844519,"17":-1.5865021,"6":-8.940691,"39":-11.757639,"37":-1.7090847,"2":-4.0220885,"41":-9.848265,"45":-6.083981,"24":-6.7988753,"27":-4.133395,"42":-11.76692,"49":-9.335624,"1":-8.775384,"43":-6.604913,"22":-5.9234495,"16":-9.831471,"4":-4.2290497,"15":-6.385244,"25":-2.2477531,"31":-8.504316,"11":-6.764846,"30":-9.480185,"26":-1.5271374,"47":-7.4575562,"10":-8.303965,"21":-1.4529445,"8":-1.4026672},{"14":-11.7985735,"16":-7.1402345,"15":-8.83317,"12":-7.4431214,"31":-6.6755295,"34":-9.647256,"37":-8.706275,"39":-3.5989442,"40":-11.197392,"38":-2.7929463,"42":-2.1823444,"3":-11.697901,"43":-9.448908,"44":-6.625801,"46":-9.21576,"47":-7.866433,"18":-4.7899103,"33":-4.5458283,"35":0.34516492,"29":-9.577916,"25":-2.876142,"45":-14.364754,"4":-5.297591,"22":-4.4429545,"7":-1.0755956,"28":-13.072825,"23":-6.8772535,"2":-6.4374185,"10":-5.266856,"36":-7.82693,"48":-9.447512,"0":-4.3712335,"5":0.7012191,"1":-11.748584,"20":-3.3363051,"19":-8.226662,"27":-9.639775,"32":-6.9716234,"49":-9.141571,"26":-9.351931,"6":-5.2754526,"21":-8.384358,"9":-4.23956,"11":-5.3919287,"30":-4.030416,"41":-10.888003,"17":-7.899237,"24":-4.271898,"13":-4.7921405,"8":-2.5590749},{"12":-6.783235,"43":-11.795801,"18":-6.6436944,"25":-7.8932815,"41":-7.526721,"44":-9.238703,"45":-3.6455085,"47":-2.526148,"9":-3.568791,"21":-6.174505,"40":-8.068426,"49":-7.264418,"26":-10.581304,"48":-16.772251,"10":-3.141788,"29":-9.408194,"31":-9.060951,"14":-5.6716475,"11":-2.4118767,"46":-7.8699784,"27":-5.4574385,"35":-7.7864013,"6":-3.1109738,"22":-9.465287,"15":-9.399365,"3":-5.8744564,"23":-4.2338915,"13":-12.697426,"1":-5.2116737,"24":-8.732299,"36":-7.4572754,"28":-10.217757,"0":-5.539334,"33":-9.07178,"30":-2.669991,"32":-5.431058,"19":-5.187009,"2":1.7111435,"4":-8.501895,"17":-9.063622,"20":-7.6048837,"16":-4.023282,"37":-3.852279,"7":-5.007511,"5":-9.595802,"39":-12.846136,"8":-8.102661,"34":-5.6266546,"38":-9.404426,"42":-9.509596},{"6":-9.02064,"9":-7.2843223,"22":-2.310627,"44":-6.465064,"2":-7.846738,"13":-3.6992812,"25":-3.6573105,"7":-6.503886,"29":-18.287153,"39":-7.447867,"41":-9.042229,"14":-5.2810197,"31":-11.863983,"15":-3.4831448,"48":-5.682383,"5":-0.56847966,"26":-1.8970543,"8":-7.3312097,"12":-10.256776,"17":-6.9802933,"23":-2.7267532,"4":-4.750392,"47":-8.777464,"20":-8.523634,"46":-2.8897185,"10":-5.960977,"36":-6.2048035,"30":-8.522104,"1":-0.37618867,"40":-6.4929724,"43":-10.0230465,"16":-6.273286,"33":-4.688217,"3":-5.745288,"45":-2.6610754,"21":-10.674103,"28":0.108149625,"32":-12.529411,"11":-1.6168003,"35":-5.5757422,"42":-8.546778,"34":-6.505348,"18":-4.070351,"37":-3.9847722,"49":-8.392035,"27":-9.88956,"0":-7.0631843,"19":-5.3635316,"24":-11.560919,"38":-8.79977},{"3":-10.595981,"7":-8.839289,"11":-3.5387344,"13":-9.085414,"19":-2.067657,"28":-6.1983695,"12":-2.6320283,"10":-4.7341843,"26":-11.082361,"33":-8.2301235,"14":-8.244513,"9":-6.8764596,"40":-6.0242147,"15":-6.3971987,"31":-4.562212,"17":-9.659184,"36":-5.763261,"41":-4.0493035,"45":-11.246027,"46":-5.2012415,"47":-7.2177415,"2":-7.163291,"27":-11.175449,"0":-2.260675,"4":-1.9289734,"21":-10.65183,"32":-2.5994828,"43":-9.6161375,"18":-7.4306197,"22":-2.2437146,"42":-10.154111,"30":-8.624043,"49":-2.9804494,"25":-7.540709,"37":-10.69308,"24":-5.479112,"35":-12.368281,"38":-9.4130535,"39":-4.365668,"29":-5.6870394,"1":-1.5405269,"20":-6.331662,"8":-7.492573,"16":-3.6112487,"23":-5.005044,"44":-4.4755707,"34":-5.084609,"48":-5.852107,"5":-8.457749,"6":-0.2421112}],"nn_shapes":{"19":[18,22,8],"15":[18,6,31,6,23,29,23,8],"26":[18,16,18,15,10,33,14,24,19,8],"27":[18,30,8],"18":[18,27,27,27,9,12,7,22,8,29,8],"36":[18,5,21,9,11,10,13,27,17,16,8],"45":[18,34,14,28,14,10,33,33,16,8],"46":[18,29,24,16,20,32,30,23,8],"47":[18,31,26,23,6,8],"9":[18,13,19,22,6,26,34,28,21,21,8],"4":[18,23,13,23,6,22,17,12,8],"32":[18,34,21,18,23,20,29,4,8],"42":[18,9,29,31,8],"49":[18,24,12,30,17,33,8],"37":[18,5,20,26,8],"43":[18,8,8,14,34,32,9,8],"38":[18,32,9,28,31,6,8],"40":[18,31,6,20,8],"35":[18,23,18,8,26,8],"7":[18,18,8],"17":[18,22,25,27,18,29,30,27,8],"24":[18,4,19,18,8],"16":[18,17,8,8],"21":[18,20,14,6,18,7,29,6,8],"10":[18,17,18,30,31,3,6,12,25,11,8],"3":[18,20,25,15,18,8],"1":[18,10,8],"23":[18,7,6,7,8],"6":[18,33,34,13,8],"34":[18,15,4,15,25,8],"11":[18,28,11,20,6,13,29,8],"0":[18,14,27,5,6,12,16,17,19,8],"8":[18,18,8],"30":[18,30,23,28,21,27,11,33,21,13,8],"2":[18,28,24,32,16,17,21,8],"39":[18,29,17,25,6,8],"41":[18,34,22,28,13,14,8],"44":[18,18,16,18,11,33,15,15,27,8],"48":[18,19,8,19,26,6,25,18,21,23,8],"25":[18,19,13,31,20,34,30,8,4,17,8],"29":[18,27,21,25,6,8],"14":[18,7,15,31,33,18,8],"31":[18,30,29,12,8],"33":[18,13,24,23,6,8],"5":[18,9,13,26,15,8],"20":[18,7,13,3,13,17,8,8],"28":[18,7,30,30,25,19,20,5,8],"13":[18,34,22,8],"22":[18,21,13,8],"12":[18,7,23,7,5,15,7,8]},"crossbreed_segments":19,"weight_initialization_range":{"start":-0.11584926,"end":1.1683692},"minor_mutation_rate":0.5113807,"major_mutation_rate":0.6868684,"mutation_weight_range":{"start":-0.40629077,"end":0.40629077}},"state":"Finish","generation":25,"max_generations":25,"id":"55eaae5a-e128-4417-ab98-89653f77e382"},"left":null,"right":null}},"right":{"val":{"node":{"id":"8a08002a-b9a3-4eab-b61f-136c46e9b708","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_8a08002a-b9a3-4eab-b61f-136c46e9b708","population_size":50,"generation":29,"scores":[{"30":-6.8903823,"0":-6.366687,"6":-7.251893,"1":-1.5758369,"4":-7.138179,"27":-6.911625,"9":-7.059839,"19":-7.4453316,"39":-6.1214046,"40":-5.0439425,"26":-2.503099,"34":-8.334969,"38":-4.7234306,"23":-5.954438,"24":-7.5706415,"22":-9.881311,"14":-7.163348,"13":-5.449682,"7":-6.5341735,"33":-0.40490597,"47":-7.2984457,"15":-7.3366966,"25":-0.3182812,"28":-8.397097,"42":-7.024077,"36":-7.2214494,"45":-3.998279,"10":-5.410035,"41":-5.0793695,"3":-6.6308517,"20":-4.465564,"11":-2.9854922,"12":-5.696584,"44":-6.940075,"5":-7.403769,"2":-4.707096,"46":-0.1471726,"48":-4.061954,"32":-6.0609922,"31":-7.3291397,"8":-6.904563,"21":-5.6207876,"35":-9.010704,"37":-1.1192181,"17":-6.9232087,"18":-4.5199404,"43":-5.4984875,"16":-7.1221786,"49":-4.7234197,"29":-7.260703},{"4":-2.1173775,"16":-4.6180205,"14":-5.2751327,"8":-5.4661613,"29":-6.643363,"12":-6.6131225,"18":-9.504697,"20":-5.771767,"17":-6.53591,"10":-4.70592,"5":-2.499683,"21":-7.500197,"19":-6.223144,"23":-5.9123497,"27":-4.7125654,"1":0.35022563,"30":-1.443285,"0":-1.3940161,"31":-6.8763785,"36":-5.761595,"33":-5.7728796,"7":-3.611281,"11":-4.433655,"2":-2.2864666,"38":-3.9540055,"40":0.637162,"44":-6.389148,"48":-5.2862444,"49":-7.6244535,"9":-3.7235718,"25":-9.452706,"37":-2.7058406,"3":-2.1251457,"42":-4.557567,"46":-5.72488,"13":-5.7067833,"39":-8.280045,"47":-6.0286193,"22":-9.067814,"35":-5.6872864,"43":-6.526985,"45":-4.815018,"6":-6.872731,"15":-5.8770156,"28":-2.5441356,"26":-2.6909657,"24":-5.8113694,"34":-5.66294,"32":-8.659533,"41":-4.2655635},{"3":-4.707946,"22":-0.9930298,"40":-4.1636987,"44":-6.693418,"46":-7.6929827,"1":0.21600547,"30":-2.6062977,"20":-5.0218782,"39":-7.4659715,"49":-11.782131,"8":-4.8311667,"10":-0.56734496,"29":-0.5500806,"5":-1.3998518,"15":-6.023353,"27":-1.2152869,"7":-5.79787,"23":-4.9278426,"28":-10.597835,"38":1.9843066,"18":-5.2778816,"35":-3.69142,"42":-6.2820597,"26":-4.7657995,"31":-4.387805,"19":-3.300389,"45":-9.194662,"36":-5.7017508,"41":-2.114288,"14":-4.193127,"9":-2.077168,"21":-6.2328887,"47":-5.5189214,"48":-6.670903,"32":-11.53697,"25":-10.070675,"43":-4.0887823,"37":-2.3846292,"2":-2.9606698,"17":-13.399692,"6":-6.354215,"12":-6.405332,"4":-2.3720484,"33":-9.485014,"13":-5.351822,"34":-5.80317,"24":-7.979808,"16":-4.7959824,"0":-1.6341827,"11":-7.36057},{"1":0.37562218,"17":-5.6403093,"40":-7.6453385,"37":-4.011546,"38":1.228275,"41":-0.99011976,"6":-4.998586,"10":-5.460025,"15":-5.44879,"2":1.108212,"24":-10.60242,"43":-5.3095045,"45":-2.5586019,"49":-6.7323723,"3":-8.880538,"33":-6.1697397,"36":-7.454741,"25":-0.17626444,"9":-0.37813005,"20":-4.9976964,"22":-5.93849,"19":-4.7558117,"13":0.14415504,"14":-2.9557383,"5":-1.6969656,"18":-7.075305,"21":-0.7996491,"26":-4.647479,"11":-4.081734,"8":-3.465926,"12":-4.47592,"29":-8.81566,"35":-4.5576224,"42":-5.040536,"46":-5.880772,"16":-1.8863583,"28":-4.099984,"23":-1.858031,"32":-4.4287996,"39":-4.690382,"31":-4.728865,"34":-5.690257,"27":-3.0799367,"48":-2.301661,"47":-2.4755936,"7":-3.3069987,"4":-2.2082336,"44":0.1436568,"0":-2.556739,"30":-4.2639523},{"22":-3.8915043,"7":-1.8629459,"33":-5.9973283,"36":-8.524627,"1":-4.7794275,"17":-6.1454573,"44":-4.6920633,"23":-4.6040993,"9":-2.9316204,"28":-2.2134347,"14":-5.544248,"15":-2.2594807,"12":1.1874464,"29":-4.7877784,"34":-5.1445036,"35":-4.3728113,"42":-9.001171,"45":-6.1106486,"18":-5.804879,"47":-3.8351161,"2":-1.4719741,"48":-2.4615238,"8":-2.775226,"16":-7.6434493,"5":-6.374134,"43":-1.5028708,"25":-0.29187956,"21":-2.7835448,"19":-3.872364,"10":-0.6055382,"32":-9.729819,"37":0.93474656,"41":-1.4799311,"30":-6.5674295,"49":-6.774779,"38":-6.886139,"13":-5.7146444,"31":-6.334458,"27":-8.395178,"39":-1.480377,"0":-0.28676185,"26":-0.67841685,"24":-5.6703634,"40":-8.138655,"46":-5.3050203,"3":-0.9592926,"6":-2.2594695,"20":-4.2805586,"11":-3.8235536,"4":-1.2074919},{"40":-3.0010896,"12":-0.47287354,"6":-1.5899264,"4":-2.4118469,"10":-2.8513339,"32":-2.0976238,"35":-4.667093,"38":1.938852,"9":-0.3600924,"42":-5.872838,"15":-2.1242394,"17":-2.4127755,"23":-0.020880412,"28":-4.6003866,"31":-5.2546453,"22":-1.2632474,"3":-1.201564,"14":-1.238025,"43":-5.890736,"49":-10.754703,"7":0.97774017,"18":-2.219182,"45":-3.3392487,"11":-1.1516854,"36":-3.3555954,"21":-6.0332994,"44":-3.1431673,"20":-2.9144974,"46":-5.557367,"26":-0.092525385,"2":-0.10287504,"24":-1.9347687,"34":3.4260392,"5":-1.8563219,"27":0.6622796,"30":-2.6912205,"8":-1.5823574,"25":-10.679262,"33":-3.7756774,"16":-1.4420292,"47":-2.6836462,"1":0.7574368,"13":-4.5513635,"39":-2.1418312,"29":-3.3399491,"19":-2.334942,"37":2.0126982,"0":-4.288588,"41":-7.9099708,"48":-2.4494293},{"7":-4.9436007,"20":-3.8808124,"25":0.6405264,"37":-2.0668845,"35":-0.1875052,"41":-1.6857042,"1":-4.9752975,"34":-3.8983452,"49":-7.2735953,"14":-5.069049,"47":-2.6037214,"31":-4.398569,"46":-6.8468194,"4":1.1550481,"12":1.928962,"13":-1.924304,"16":-0.15191099,"19":-2.8891091,"24":-5.286527,"32":-5.935781,"33":-0.9248799,"22":-3.785513,"23":-5.1809096,"10":-1.5204986,"27":-4.55277,"39":-7.4145584,"43":-14.647901,"48":-4.265855,"29":-1.3842177,"3":0.07098539,"5":0.82853717,"45":-10.730497,"9":-0.6733018,"21":-2.5182014,"2":-0.83385086,"8":-1.5495563,"36":-0.03221141,"15":-2.072137,"44":-6.384336,"0":-3.0473003,"30":0.7601515,"17":-1.5781072,"18":-0.8381344,"42":-2.582169,"40":-4.614395,"11":-3.8800156,"26":-2.0635307,"6":-4.68423,"28":1.5506709,"38":-2.2673924},{"7":-1.6505486,"2":0.7774186,"47":-6.573714,"37":-4.539879,"13":0.03377179,"38":-7.26351,"16":-1.2384418,"48":-0.21510899,"36":0.1417698,"4":-1.5233848,"28":-5.8403006,"33":-1.5584481,"39":-3.818011,"46":0.11975696,"31":-0.26565015,"11":-1.0494064,"29":-3.681448,"34":-2.017143,"1":-2.9740043,"23":-1.4810946,"12":-0.1327244,"18":-1.9773611,"20":-0.7924546,"14":-3.3341725,"17":-1.4658457,"24":1.7954773,"10":-0.3364888,"15":-3.3656037,"8":-0.6107298,"43":0.7149738,"44":-1.3457949,"3":0.97620183,"45":-1.7013899,"26":-1.5102454,"40":-0.339752,"30":-9.527001,"21":-1.7250589,"41":-0.86631376,"25":-4.9848146,"27":0.7658924,"6":-0.23256321,"5":-4.1068673,"0":3.2152145,"22":-2.8026853,"35":-5.1000853,"19":-2.2166228,"32":-4.892883,"9":-0.43250617,"42":-0.26101598,"49":-5.497836},{"5":0.64945936,"39":-0.05907058,"29":-5.1650662,"44":1.86428,"23":-0.45214185,"24":-1.1747376,"18":3.450955,"46":-3.927073,"16":-2.1201155,"9":-1.0618576,"49":0.9801561,"33":-3.4150214,"38":0.011873191,"19":-1.7743086,"36":-10.073343,"43":-7.3709884,"48":-7.7935066,"15":-0.871607,"1":1.8400133,"7":4.6085854,"37":-1.3199402,"6":-0.5254752,"41":-5.488297,"22":-0.15798141,"8":-0.2689926,"4":0.0070250034,"30":-0.2566538,"32":-5.1819634,"34":0.64409745,"35":0.85777014,"0":2.143561,"21":-0.073742434,"11":-0.43844336,"26":-3.7777207,"28":-2.7066035,"3":0.8786748,"12":-1.3015422,"42":-7.2415023,"47":-0.956086,"25":0.110974,"17":-0.81192,"20":-0.13011317,"13":-0.8048275,"14":-0.2854454,"31":-4.000916,"27":-5.6587157,"10":-0.2606472,"2":1.0424337,"40":-7.7523055,"45":-2.582872},{"36":0.27180222,"23":2.7978485,"20":-1.3364205,"33":-8.464629,"10":0.4004666,"21":-2.7492003,"27":-4.5050507,"44":-4.31029,"37":-1.6940572,"32":-4.7660036,"28":0.3334044,"30":-0.45442843,"19":-0.3445322,"40":-8.239295,"35":0.47767296,"31":-5.8663955,"12":-0.4103446,"15":-1.2449474,"17":0.0257856,"38":-3.4848754,"18":-0.6052362,"1":-0.0063587963,"6":-4.681793,"29":-4.866314,"39":-3.9143035,"9":0.2902396,"48":-0.26042423,"0":2.9007907,"5":1.0276722,"42":-0.7925848,"26":-5.118394,"11":-0.002367808,"25":-3.841985,"45":-0.537751,"34":-7.495551,"47":-1.6614449,"22":-0.55838305,"2":2.2291977,"14":-2.826922,"16":-0.3536974,"24":-1.096124,"46":-2.3482919,"4":-4.404132,"7":0.59371424,"49":-5.7783227,"8":-0.9134123,"13":-0.8282794,"41":-2.8126519,"43":-3.7567513,"3":0.6197542},{"47":-3.466451,"12":0.4526772,"8":-0.887543,"0":0.40766907,"7":0.340661,"14":-1.2155756,"21":-0.52555835,"18":0.16660759,"31":-0.4225104,"1":-2.4355006,"23":0.11080019,"32":0.77408504,"28":-4.6681557,"33":-2.6104722,"37":-6.771203,"46":-4.158824,"3":0.5076564,"6":1.3568645,"13":-2.2395298,"17":-0.00092999934,"30":-2.2732644,"35":-0.8506044,"38":-2.9995084,"40":-4.005641,"10":0.75578177,"39":-7.017123,"2":-3.732964,"41":-3.9281597,"24":0.0051654116,"16":0.7956792,"27":-1.3640368,"42":-0.44231778,"43":-0.27105874,"44":-0.37946758,"11":-0.18678841,"22":-1.4216859,"9":-0.54350585,"49":0.05486574,"15":-0.097495005,"34":-1.4785053,"25":-7.7886686,"4":1.0454166,"26":-1.1955853,"45":-7.542697,"36":-1.206464,"48":-0.4479108,"29":-1.9547632,"20":0.199999,"19":-1.3918304,"5":1.187502},{"7":-0.055990804,"32":0.15957642,"5":0.931877,"37":0.5894412,"44":-5.4633756,"14":0.2276588,"47":-0.3331896,"18":-3.2150345,"40":-0.370715,"43":0.6012882,"28":-6.1086364,"8":-0.52198184,"21":-1.0775121,"49":-1.9851396,"2":1.0139883,"26":-3.5421982,"3":0.38368022,"48":-3.9251645,"6":0.95860064,"24":0.77015275,"31":-4.462737,"1":1.1644658,"20":-0.89435995,"30":1.7337078,"39":-3.0713792,"42":-0.457604,"0":1.0809774,"9":-0.5640028,"25":-1.3178831,"13":0.4953414,"4":1.2208271,"29":-5.3828278,"11":0.6927994,"16":0.6993264,"10":1.0544658,"27":-6.1370287,"34":-0.4300012,"22":-0.36046538,"17":-1.903821,"46":-5.143382,"23":-0.5656086,"35":0.7297146,"38":0.61732376,"45":-3.5132241,"12":-0.39357737,"36":1.4724967,"41":-3.488839,"33":-3.3702226,"19":-0.3116702,"15":0.43473634},{"27":1.181613,"22":0.055693995,"29":-0.3129018,"33":-7.299405,"1":1.5262117,"19":0.5360656,"32":-1.6477096,"13":0.64982116,"40":0.91174144,"41":-2.7509122,"14":-0.167828,"44":-2.822415,"45":1.2327583,"11":-0.011979785,"38":-6.813726,"43":0.11981901,"46":-3.4579914,"15":0.7411006,"16":0.8099394,"7":1.1826488,"35":0.95745754,"37":-3.0358167,"8":1.1782231,"10":1.0557808,"48":-3.045028,"49":-3.8261337,"5":0.7451374,"20":-0.1527806,"39":-6.4743195,"28":-6.5273843,"4":1.220751,"42":-4.5711756,"24":-0.041719608,"31":0.34226742,"18":-0.91081256,"0":1.0743566,"23":-0.17759499,"30":1.4853861,"17":0.46872276,"34":1.2847208,"25":-6.5099783,"3":0.87976015,"47":-0.5755362,"26":1.3066028,"2":1.4612877,"6":1.1884784,"9":-0.8657932,"36":0.09794381,"12":0.3919762,"21":0.29546002},{"30":0.543356,"29":1.4511869,"6":1.5986239,"1":1.7871357,"24":0.44812518,"38":1.2497461,"4":1.3088481,"41":-3.509452,"48":1.1025379,"15":1.1625886,"28":1.0937965,"3":1.3820547,"14":1.3868314,"17":1.2814915,"21":0.2979782,"13":0.8331116,"11":1.481771,"42":-5.8899517,"37":0.9281726,"18":0.466715,"22":0.39471,"23":0.83010465,"12":0.57770675,"33":-5.1343074,"31":-0.051722206,"7":1.1165328,"32":2.0868556,"5":0.8164091,"20":-0.8658458,"35":-1.6920564,"43":1.5389199,"44":-0.69294435,"26":-0.054529577,"0":0.8418752,"16":0.68473107,"34":1.4229796,"36":-5.3850393,"47":-1.431451,"8":1.0309417,"49":1.295428,"19":0.91012347,"40":-5.1053925,"10":1.2443664,"2":0.70411724,"39":1.0932726,"25":-5.872225,"27":-1.4230958,"46":0.6440994,"9":1.1426489,"45":1.0967102},{"41":-0.8937888,"38":-3.716568,"43":0.83768904,"25":-1.06985,"24":1.5895697,"12":1.3031318,"31":-6.981823,"35":-0.617902,"28":1.2498503,"0":1.3810146,"7":1.3701488,"22":0.5957642,"18":0.89235955,"39":0.3732926,"42":0.412604,"15":1.2952098,"6":0.60526836,"29":0.3031598,"32":1.5218765,"45":-6.5065665,"20":1.3963188,"27":1.2640693,"5":0.8349102,"19":0.95996416,"10":1.4788206,"46":1.6185303,"16":0.46717244,"37":-5.133558,"17":1.4022924,"1":1.5865852,"47":-3.2271333,"11":1.3340809,"48":1.1868662,"4":1.5121167,"23":0.60344905,"13":0.84270716,"33":-5.3183193,"26":-0.2449282,"30":1.6124281,"8":1.4946703,"40":1.303579,"3":1.7255867,"21":1.1728556,"36":0.2820046,"2":1.4260871,"9":0.605192,"34":0.041747987,"49":1.4496037,"14":1.1405728,"44":-7.878412},{"3":1.06853,"22":1.5044442,"4":1.5756843,"32":1.5952417,"9":1.586146,"41":0.1423614,"46":-1.7510738,"30":-2.900235,"47":3.6666653,"15":1.203729,"48":-1.3951555,"49":-6.9787354,"43":1.896711,"27":-5.219539,"35":1.390768,"19":1.6316789,"5":1.5311888,"0":1.5764972,"36":0.82220095,"33":-6.8519506,"20":0.8197409,"16":0.7336922,"25":-4.6792617,"12":0.95957357,"44":1.3089871,"34":0.83662015,"2":1.1631157,"38":-0.5674101,"11":0.8625002,"7":1.4115431,"13":1.2713734,"23":1.2255038,"21":1.2702303,"17":1.2648827,"29":-4.59699,"10":1.6951157,"18":0.95991695,"31":0.94614536,"1":1.91795,"42":1.4615324,"39":-0.0689796,"8":1.2110746,"14":0.82729214,"37":-2.262866,"40":-0.321691,"45":-2.861486,"24":0.553185,"28":1.7802076,"6":1.2392688,"26":0.559428},{"4":1.5179548,"27":-6.264064,"49":0.47696343,"0":0.066469386,"47":-5.7816567,"2":1.8454969,"46":0.1787258,"26":-3.4672687,"13":1.5142362,"1":1.7937119,"15":1.4136237,"22":0.98619956,"16":1.4075732,"18":1.2502508,"45":-2.5159807,"14":-0.887997,"9":1.5993698,"5":0.3839901,"6":1.6086,"3":1.457516,"28":-7.4280267,"12":-1.0802304,"48":0.82555276,"32":-0.7044152,"35":1.5340879,"42":0.77651846,"30":0.4361906,"24":1.6176443,"37":-7.6016197,"39":1.6315203,"17":1.2428942,"29":1.209003,"7":1.6030687,"23":1.3045851,"33":-2.9556956,"40":-6.5972023,"20":0.30333602,"25":0.9161374,"36":-6.604985,"41":-0.65913355,"43":1.743408,"31":-7.1618032,"44":-2.6561675,"8":1.6508014,"11":1.1103718,"21":1.2077398,"38":-6.124724,"10":1.5096354,"19":1.9523376,"34":2.6506305},{"2":1.5297358,"17":1.1704702,"26":-6.16945,"39":-2.9829984,"9":1.2676624,"10":0.5051805,"13":1.3597181,"6":1.1794776,"22":1.4321846,"3":1.08179,"5":1.6785371,"25":-1.1475462,"0":-0.34125432,"16":1.2396181,"23":1.1752719,"27":-1.9520136,"28":-1.3069769,"37":-3.381085,"19":1.372544,"38":0.007790795,"12":1.6322134,"40":3.4216495,"35":-8.126452,"42":-2.0214622,"43":0.23487015,"8":-0.01564021,"44":-7.6624045,"29":1.5084686,"11":0.6349971,"47":-2.8156064,"34":-4.1204715,"31":-8.108996,"21":1.2164943,"48":-2.8430297,"49":-2.9457614,"18":0.62638175,"45":1.2671716,"41":-6.0569415,"33":0.39131317,"36":-2.3818052,"4":2.009478,"15":1.6500012,"46":-3.5459144,"32":-7.3374343,"20":1.2811453,"30":-1.09656,"1":1.1212342,"7":-1.7780695,"24":1.0333579,"14":1.61798},{"39":1.1167322,"30":1.4514105,"47":-6.5923767,"48":0.9891836,"43":-4.254728,"5":1.3477652,"10":0.50612104,"22":0.6411706,"32":0.3954892,"16":1.3328848,"27":1.2563778,"7":1.3520648,"45":-0.99833333,"21":0.6445106,"36":1.5755234,"42":-1.4367013,"1":1.5964768,"14":1.126214,"49":-5.693822,"2":1.5589868,"9":1.7812173,"38":-2.8544154,"25":-6.4703674,"18":0.1501132,"34":0.31430298,"4":1.4938426,"8":1.5314653,"28":1.656511,"13":1.3649709,"40":-6.2768335,"26":1.101163,"31":-0.7388144,"41":-2.1115413,"17":0.9507782,"11":0.97697604,"29":0.55346644,"3":1.6671025,"6":1.4095662,"12":-0.11343231,"15":1.7699897,"20":1.5169379,"0":2.771112,"19":1.7405956,"33":-1.298229,"37":-6.6350493,"23":1.7395853,"44":-6.3225856,"24":1.58325,"35":-0.053157162,"46":-9.4540615},{"6":1.2187669,"7":0.8917198,"47":1.8631401,"2":1.1745762,"35":-1.6448038,"49":-2.95982,"32":-2.0818577,"34":-6.179839,"44":-3.6606793,"41":0.5892452,"18":1.7373435,"8":1.6228693,"29":-0.17469399,"26":1.6262884,"27":0.0862808,"11":1.189906,"25":-2.8834498,"10":1.1779134,"45":-2.0754929,"20":1.558006,"23":1.5464153,"16":1.1434767,"3":1.1076599,"4":1.1778538,"5":-0.42678183,"17":1.7063715,"12":1.8446579,"14":-0.3774435,"1":1.5259365,"40":-4.735013,"33":0.99616224,"0":3.964498,"13":1.5515668,"43":-6.9779253,"46":0.3668552,"48":-2.6729264,"22":1.1326334,"31":1.4176149,"15":1.8948257,"24":-1.1004847,"30":1.4272197,"9":1.2269651,"21":1.3684937,"37":-5.4659343,"39":-2.9745486,"42":-1.7469413,"36":1.8223385,"38":0.61339784,"28":-6.5556955,"19":0.92647237},{"20":1.4957073,"47":-7.6401763,"37":1.5045424,"32":-3.0742247,"2":1.3811878,"19":1.6339369,"46":3.273948,"33":0.7683608,"17":1.074304,"14":0.8990938,"26":-1.1447313,"29":1.0419735,"9":1.133498,"13":1.8232332,"22":1.3013092,"48":2.2217846,"49":0.046359204,"3":1.8796438,"18":0.99727887,"16":0.40142736,"0":1.6930526,"15":0.13463955,"21":0.794613,"31":1.9557081,"39":-0.54255676,"1":0.23620617,"23":0.8635624,"5":1.7801396,"27":0.58400375,"44":-5.3096094,"4":1.7721612,"11":1.2898297,"30":3.9104648,"38":-1.2507364,"24":-0.55052215,"34":-2.7482874,"40":0.93363285,"10":1.5955795,"42":-5.272129,"45":-3.926464,"6":0.87878215,"28":-0.13962458,"43":-2.9131722,"8":1.7215147,"25":-4.6805735,"35":-0.4225172,"7":1.8165493,"36":-5.8606043,"41":-1.4097888,"12":1.5908456},{"46":0.74108964,"49":-4.1909633,"20":0.30749458,"19":1.9475998,"14":0.31330737,"27":-4.3371725,"29":2.7149265,"15":1.1186619,"32":-3.4302926,"37":-6.3331,"6":1.7349503,"11":1.7137706,"24":0.1975456,"0":-0.7540966,"16":-1.1393349,"23":1.8451722,"28":1.7192996,"31":-0.44986576,"2":1.2848672,"35":-0.825477,"12":1.4231809,"48":-7.606537,"3":0.501546,"13":1.5034555,"17":1.2041571,"21":1.126523,"33":1.0317538,"39":0.3762208,"43":0.11361933,"5":1.5044609,"18":1.2175517,"25":0.95986015,"26":1.1233764,"40":3.6794903,"8":1.5731483,"7":2.0125415,"1":2.3247137,"41":-9.749472,"42":1.6741203,"44":-1.0102679,"9":1.4840626,"45":-2.4632657,"10":1.6355549,"47":1.5323204,"22":0.21311255,"36":1.5120761,"30":0.2143166,"38":-3.5642304,"4":1.7638651,"34":0.39690977},{"42":-1.2920502,"5":1.3969676,"9":1.554745,"30":-0.10908177,"3":1.2139347,"39":1.0080363,"47":1.200161,"2":2.3049264,"12":2.0657258,"13":1.7098567,"43":-2.527463,"27":-3.8782554,"17":1.2076086,"28":-3.7903857,"29":-9.85261,"46":-4.4447265,"48":1.1618752,"49":0.19929662,"41":2.4686918,"44":-4.0732775,"20":1.5870037,"4":1.8357404,"0":-2.0527384,"8":1.5574868,"10":1.22925,"24":0.45239258,"35":-5.810202,"22":2.246426,"36":0.71208864,"38":-5.465337,"6":1.6349227,"33":-1.3913972,"26":-0.060923196,"31":1.3563511,"1":1.7311414,"25":-7.8462257,"37":1.4671192,"32":1.7025058,"18":1.6255014,"40":0.76987636,"45":-7.1601205,"16":1.321495,"7":2.0029209,"15":1.3958324,"11":1.4291557,"23":1.020146,"14":1.3514392,"34":-3.9700756,"19":0.87691844,"21":1.457301},{"10":1.3900664,"29":1.5545199,"23":0.7173734,"38":-0.035635423,"49":-5.677472,"25":-2.8942063,"5":1.5183672,"43":-9.479878,"47":-2.3738418,"34":-7.6670456,"14":1.1688192,"22":1.448393,"36":2.3566444,"37":-1.7029192,"45":-1.9353702,"21":1.181576,"8":-0.21047358,"39":3.2392578,"7":0.90635955,"9":1.3024013,"26":1.8222473,"24":1.337246,"3":1.5609477,"12":1.8292301,"0":0.62835604,"17":0.8763803,"48":0.7421207,"19":1.4354224,"1":3.1899996,"20":0.5356204,"11":0.32281262,"28":-0.057988644,"31":-2.2201662,"13":1.4153163,"2":2.538074,"32":2.533946,"16":1.75793,"42":0.36935982,"6":3.4213595,"33":-1.1408689,"4":1.6013329,"27":-1.2621657,"30":-2.4088655,"35":-9.271956,"40":-0.23365942,"44":-4.820443,"15":0.98999214,"41":0.779949,"46":-1.000054,"18":0.7356068},{"3":-0.83181953,"28":-2.9239163,"18":1.0110298,"20":1.4475853,"1":-0.087203085,"27":-8.34004,"37":-2.9980285,"35":1.1809899,"11":1.4232423,"36":2.7912552,"38":1.6999662,"16":1.2309988,"32":-1.3243482,"44":-1.1714809,"49":-10.058592,"26":-3.105577,"25":1.4807254,"42":1.2478247,"19":1.3075396,"47":0.63402104,"15":1.838217,"2":1.4958794,"39":-4.058095,"10":1.3976815,"9":0.75132984,"31":-5.109008,"43":1.2327099,"40":0.35012078,"4":2.4473233,"21":0.99680173,"34":-4.42363,"0":0.8248364,"23":0.43658724,"48":-0.48618975,"6":0.71962893,"7":0.613024,"5":-1.0769475,"22":-0.39623517,"30":-4.3155847,"12":1.4463699,"45":0.65694743,"29":-0.9168655,"17":1.0417278,"8":0.9492218,"14":2.2935612,"46":-4.9462934,"24":0.260953,"33":-5.8987875,"13":0.033944797,"41":3.7868094},{"45":0.0684842,"15":0.5276226,"43":3.6445403,"34":-0.51650757,"0":-1.9127007,"1":3.9725666,"27":-0.23630016,"7":1.5093597,"9":1.4424554,"22":-0.2881744,"33":1.7457469,"44":-0.92784184,"28":-7.526722,"41":-3.4292526,"47":-0.8512896,"48":-3.3854833,"30":1.551065,"10":-3.0717058,"5":1.0323613,"2":1.4360434,"21":0.7947602,"11":0.70066816,"31":-2.9565368,"32":1.6050752,"49":-1.2321005,"6":0.259293,"13":2.6843486,"17":1.2362102,"29":0.09348246,"36":-9.459752,"14":0.6812374,"37":-2.0481486,"3":-1.2719615,"24":-0.30212656,"8":-0.09922062,"4":1.6250948,"19":0.31614476,"12":1.5376502,"25":-0.9299536,"38":0.38055125,"39":-7.025481,"42":-3.050082,"26":2.6087768,"35":-0.4830926,"20":1.6740729,"23":2.0530748,"46":-5.4383454,"18":1.8147866,"40":-5.0388107,"16":-3.0236375},{"45":-8.327242,"30":-11.326494,"0":1.050215,"48":2.7887874,"1":2.1725523,"34":1.5555595,"38":-0.9663353,"40":-11.917261,"33":-5.550151,"49":-8.402519,"5":0.22542039,"2":-1.1448904,"23":3.235646,"10":0.19637775,"15":1.4809368,"31":-0.587714,"20":1.6347845,"9":-1.203298,"19":1.5319674,"27":-11.047888,"16":1.8472086,"13":1.4091709,"29":-2.4094012,"35":-1.1548483,"39":-3.7547355,"41":0.86946535,"43":0.72847444,"44":0.5401133,"12":0.84144497,"7":2.6294243,"18":1.5767672,"24":-0.26575905,"21":2.2624278,"26":-0.6386402,"3":5.237002,"28":-7.0032654,"37":-2.321322,"46":-3.3797333,"47":-0.16604157,"14":1.4270293,"6":-2.0011048,"25":1.3073906,"11":1.4543204,"8":0.5777082,"17":3.604412,"32":1.7178024,"36":1.2897886,"4":1.6126816,"42":3.1484401,"22":0.400719},{"3":2.51722,"5":1.6788085,"12":1.6577823,"17":0.73920006,"6":-0.9401892,"0":3.6358628,"20":1.3569462,"11":1.2359018,"28":3.8309493,"36":-7.88842,"14":0.390558,"1":-2.4561696,"7":-3.0893154,"19":-2.8343208,"21":1.230938,"30":1.519681,"18":-0.25336796,"35":-8.591397,"38":4.0539126,"2":1.3142867,"39":-0.41035575,"44":-0.8191618,"23":1.9358387,"46":-0.98267543,"47":1.9498055,"8":1.184375,"31":-3.613992,"24":0.51519924,"29":-3.3397663,"42":-12.80098,"9":0.92517126,"26":-1.6531479,"41":-11.539165,"13":0.555514,"45":0.4131689,"49":-3.2789326,"15":-1.1341568,"27":-6.675209,"32":-0.8474833,"48":-0.6487338,"16":0.6263184,"10":1.2492015,"4":0.38530108,"43":-1.2618519,"34":-1.205608,"25":-1.1481497,"33":-0.5578264,"37":0.107068256,"40":-6.672026,"22":-1.9860134},{"3":1.4870069,"24":0.5922798,"42":-0.90082943,"46":1.1263611,"28":4.0212173,"6":0.847098,"39":1.5401908,"2":3.6113706,"22":-2.1477005,"32":-1.143802,"34":1.7614723,"0":0.2418124,"45":-4.5013013,"40":0.5079947,"23":-2.6009984,"15":4.4212484,"9":1.5904756,"13":3.1554604,"38":-9.773752,"36":-8.051781,"11":0.7847768,"12":-0.500327,"18":2.6738858,"43":0.8890418,"8":-0.11626544,"10":-0.6089754,"30":-6.938854,"33":-0.13215446,"44":-7.1068983,"1":-1.205419,"19":2.5999706,"29":2.7931895,"41":-2.8217766,"25":4.527017,"48":0.22729865,"26":-8.110232,"47":-1.2862024,"49":2.4557524,"17":0.640802,"31":-8.518101,"35":-3.8277054,"20":3.3180757,"21":0.66522586,"7":2.2479103,"37":1.7639908,"4":1.7260542,"14":-0.16935782,"16":1.3788013,"27":1.2699888,"5":-1.1353257},{"16":-0.5070044,"22":0.5732678,"9":-3.311842,"14":1.3319747,"35":-5.4703245,"36":-8.155589,"49":0.37697345,"31":-2.2505832,"6":1.6764879,"24":2.1798148,"40":0.057171058,"5":2.2697942,"48":-10.495432,"2":-0.20947047,"29":-0.546161,"32":-6.696771,"4":2.6180937,"19":0.19290301,"17":1.5652777,"7":-0.49070844,"30":0.2449191,"37":-2.5612454,"38":-1.0849822,"1":0.79552424,"41":1.7466857,"11":2.5568452,"21":-2.0221412,"0":-0.51793313,"8":0.5822776,"25":-7.0668707,"46":-1.6242403,"15":-0.90794677,"23":1.4886081,"28":-5.8837767,"20":1.7212784,"18":-0.6829664,"33":-6.7982726,"12":0.93712425,"10":1.5152016,"34":-2.6526597,"39":0.06669118,"47":-0.4047792,"44":-2.9025836,"13":-0.2605246,"27":-2.9493988,"43":0.9045593,"3":0.60476106,"26":-0.7757357,"42":-0.3835604,"45":-8.929934}],"nn_shapes":{"40":[18,12,12,10,27,23,30,7,8],"13":[18,23,4,8],"41":[18,20,31,28,33,23,8],"36":[18,8,32,27,8,8],"14":[18,5,11,33,33,22,19,8],"16":[18,27,22,29,14,8],"30":[18,10,33,8],"25":[18,11,8],"18":[18,13,8],"42":[18,29,23,7,5,29,9,8],"43":[18,25,21,17,12,8],"28":[18,30,34,32,26,10,15,8],"31":[18,34,26,30,8],"38":[18,33,26,10,26,31,4,8],"7":[18,10,26,28,23,13,8,4,20,14,8],"24":[18,18,31,14,34,23,30,8],"22":[18,8,6,5,21,34,11,25,10,8],"9":[18,27,9,8],"47":[18,8,15,8],"45":[18,17,8],"23":[18,28,30,21,8],"11":[18,23,31,19,31,17,10,3,8],"32":[18,25,17,27,3,18,29,19,4,23,8],"48":[18,31,22,17,12,16,3,33,3,8],"19":[18,11,30,9,13,27,32,8],"3":[18,4,5,20,15,8],"0":[18,30,26,3,13,16,8],"2":[18,19,29,14,29,3,8,8],"8":[18,26,8,9,29,8],"21":[18,5,17,16,12,13,8,14,8],"33":[18,3,23,21,20,21,15,11,8],"12":[18,25,24,23,22,19,8],"37":[18,23,8],"10":[18,34,8],"44":[18,10,11,9,29,14,27,33,6,8],"1":[18,22,24,26,20,26,7,23,4,4,8],"29":[18,9,13,8],"6":[18,10,6,8],"35":[18,9,18,7,14,8],"4":[18,4,22,8],"5":[18,33,27,24,13,7,30,21,19,8],"15":[18,9,29,21,18,30,14,33,14,8],"39":[18,28,5,6,18,31,4,6,13,8],"46":[18,8,23,3,8],"49":[18,15,28,16,12,9,17,13,34,20,8],"34":[18,17,22,11,16,8,15,8],"17":[18,21,9,17,4,23,4,8],"20":[18,4,27,26,8],"26":[18,15,34,22,6,26,8],"27":[18,25,17,22,28,5,16,8]},"crossbreed_segments":19,"weight_initialization_range":{"start":-1.6164029,"end":0.42884523},"minor_mutation_rate":0.1538608,"major_mutation_rate":0.11361027,"mutation_weight_range":{"start":-0.8341105,"end":0.8341105}},"state":"Finish","generation":30,"max_generations":30,"id":"8a08002a-b9a3-4eab-b61f-136c46e9b708"},"left":null,"right":null}},"right":{"val":{"node":{"id":"7ce5d55b-767e-4c6a-9fdc-4c2300c7f0b4","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_7ce5d55b-767e-4c6a-9fdc-4c2300c7f0b4","population_size":50,"generation":34,"scores":[{"13":-1.4799551,"12":-3.3707557,"32":1.2356446,"20":-4.5820093,"21":-3.6551948,"18":1.0499984,"40":-5.7712092,"10":-1.1848409,"5":-2.5973716,"25":-5.2012186,"36":-5.02569,"43":-6.0541677,"6":-3.3707566,"9":-1.796328,"23":-5.670643,"24":-5.2992578,"34":-4.775678,"48":-5.848365,"46":1.8619846,"11":-4.1024766,"29":-11.595612,"45":-2.7100034,"14":-1.2602413,"7":-1.4976543,"22":-2.1690187,"26":-0.57119787,"8":-0.8552567,"4":-0.014923185,"2":-0.40724778,"27":-0.90810204,"1":-5.1352377,"17":-4.1625814,"28":0.17093961,"30":-5.7198286,"0":-2.6610336,"3":-1.5259964,"16":-6.797188,"33":-6.229727,"35":-7.8272476,"37":-0.8351852,"38":0.74034035,"15":-1.0149847,"39":-4.168948,"42":-5.989782,"19":-3.6024654,"44":-3.3829772,"47":-5.1616178,"49":-6.1079907,"31":-5.3034387,"41":-8.023984},{"46":-5.5986238,"34":-4.534239,"23":-2.7403977,"47":-6.284207,"26":-4.6461744,"49":-3.0859594,"32":-3.6064186,"37":-2.8101246,"3":0.2711714,"24":-2.9530277,"38":-2.7212186,"9":-4.177823,"8":-1.4947598,"43":-2.3903615,"7":-1.9176388,"20":-2.6888652,"16":-0.95691025,"41":-4.245606,"6":-0.2821784,"15":-0.23934121,"36":-2.0781984,"25":-6.746737,"35":-3.1192393,"44":0.84309137,"48":-2.6173937,"31":-7.2024155,"0":-0.9995136,"42":-10.776218,"13":-1.3483765,"21":-1.829633,"30":-7.427111,"29":-6.346571,"11":-0.701403,"5":0.75094104,"27":-4.7604833,"2":-0.2158086,"17":-2.952026,"19":-4.709943,"22":-3.5801616,"1":-2.994163,"10":-2.6068876,"33":-7.4807577,"4":0.29855937,"14":-2.051252,"40":-0.92696303,"45":-5.846688,"18":-0.2620568,"12":-0.5900184,"39":-3.320311,"28":-4.803691},{"19":-1.6374098,"32":-7.0608854,"5":-0.115976594,"0":-0.823748,"8":-0.4674174,"1":-4.241291,"16":0.6256918,"18":-2.7909126,"17":-0.90980357,"29":1.124589,"3":0.53907764,"34":-4.1516957,"7":-0.11296322,"31":-8.022696,"11":-1.7543952,"35":-7.2468405,"9":-1.9748577,"27":-6.025627,"30":-6.885924,"37":-6.368652,"39":-1.8914082,"40":-3.897443,"24":-1.9746668,"41":-7.366498,"45":0.25113982,"21":-4.1330175,"44":-5.6694856,"46":-2.799114,"47":-0.72811735,"14":-2.3075433,"42":0.34034058,"49":-4.327436,"6":-0.9277388,"22":-3.248761,"36":-1.5806258,"4":0.79853237,"12":-1.7290052,"10":-2.2808337,"20":-2.4480398,"25":-5.4077253,"2":-0.13549562,"15":0.0587466,"38":-5.9818506,"48":-7.7930098,"26":-1.6008437,"33":-3.0970616,"23":-0.5001246,"43":-8.257734,"13":-0.4739554,"28":-9.101915},{"34":-7.1162095,"5":0.75404155,"8":0.0806644,"36":-0.5598956,"38":-5.283127,"24":-0.5738148,"41":-5.822985,"45":-3.6928368,"19":1.8945599,"26":2.2363143,"33":-5.3702226,"46":-5.6504335,"39":-0.5380732,"12":-3.494994,"21":-0.24514881,"29":-6.4968796,"37":-1.7391956,"9":0.24764648,"44":-6.0050616,"43":-3.1556416,"40":-3.114514,"0":1.1169783,"10":-3.0221496,"4":0.71817005,"2":-0.10828309,"47":-0.8597895,"48":-0.13096179,"49":-4.729623,"30":-1.0527647,"7":-1.2968149,"13":-1.2063895,"18":-0.23004182,"32":-0.4751242,"35":-6.380617,"25":-4.895747,"20":-0.904418,"1":0.3126194,"27":-4.631973,"11":-1.4761704,"15":-0.68164796,"23":-0.89406717,"17":-4.3468423,"16":-3.0963852,"28":-4.2654424,"3":0.292789,"6":-1.2786105,"14":1.09606,"22":-0.32667297,"31":-2.2469232,"42":-5.4418697},{"16":-5.2526503,"32":-6.3634443,"36":-3.2679844,"42":-6.1911187,"17":0.6657694,"28":-2.2287307,"43":-6.0696135,"19":-2.5921116,"21":-1.0395129,"20":3.3325691,"38":-5.403447,"22":-1.0866393,"39":-5.2612906,"13":0.110688806,"35":-9.209028,"6":-1.2982272,"1":-0.5857518,"7":0.3971466,"44":-7.7847915,"8":-0.93844205,"14":2.6282563,"4":0.40624943,"40":-6.5754633,"24":-0.85832083,"33":-0.6104063,"3":2.1158707,"29":-4.8762445,"31":-1.1775994,"37":-6.249992,"9":-1.0541943,"46":-6.47375,"47":-5.1030784,"10":0.5411806,"48":-7.319977,"11":-0.93714416,"15":-1.4099878,"30":-4.102948,"0":2.0518181,"26":-7.4277487,"34":-4.820016,"18":-0.5411726,"12":-0.7130984,"27":-1.1400305,"25":-6.816667,"2":-0.7096395,"5":-0.64454496,"41":-7.3878846,"45":-1.0586528,"49":-4.8044972,"23":0.48288918},{"0":0.6224516,"21":-1.9724243,"8":0.3221472,"23":-1.1445072,"9":-1.8855957,"38":-4.3636928,"39":-2.5525608,"44":-6.022342,"45":-2.6967726,"47":-0.22814481,"42":-4.267274,"49":-14.870151,"35":-4.246401,"20":-0.08219239,"7":0.24648981,"41":-1.4371231,"4":-0.67331505,"3":-0.45806536,"24":-5.4120345,"43":-6.4070425,"12":-3.4360378,"19":-1.5386674,"5":0.8870964,"2":3.1363246,"18":-0.06081761,"1":1.6736473,"32":0.354136,"46":-9.758692,"48":-6.220552,"11":0.2751074,"6":-0.3357262,"16":-0.9532158,"17":-6.4547324,"27":-5.6590633,"36":-7.743505,"28":-5.8381033,"29":-5.6711817,"34":-5.7756267,"31":-6.076832,"13":-1.1353436,"14":-1.3015141,"22":-1.0451987,"25":-4.9334326,"26":-2.2235262,"15":-0.617426,"30":-4.905954,"33":1.0185475,"37":-4.8664293,"40":-1.9478962,"10":-3.680655},{"7":-0.39519322,"31":-5.695974,"29":-3.7772954,"44":-7.6308074,"27":-2.0888543,"40":-5.2389083,"38":-3.2697976,"13":0.014459705,"41":-5.0734143,"43":-2.3007417,"9":-0.273322,"17":-1.2354606,"32":-2.7820916,"11":-0.18107119,"46":-6.8478456,"48":-3.8834,"4":7.362853,"18":0.22247219,"22":-1.4087099,"49":-5.961255,"20":-0.03473518,"39":-6.0345163,"47":-4.124648,"16":-2.4454093,"25":-8.348789,"37":1.779715,"19":-1.1829737,"2":-1.9087565,"8":0.704858,"14":-0.6411097,"28":-10.572978,"34":-3.7115192,"26":-0.9956168,"0":3.9657726,"5":-0.8495882,"45":-3.034346,"23":-1.076154,"42":-5.1411347,"33":-4.69675,"24":-5.2366548,"1":2.8699353,"3":-1.9849409,"6":-0.4074544,"12":-0.886967,"15":1.2337506,"35":-4.029725,"30":-2.2090604,"10":1.0753075,"21":0.90168494,"36":-5.338687},{"22":-2.5521665,"9":-2.1332574,"35":-4.0650635,"5":-0.24811077,"2":4.0341806,"7":-0.8468792,"10":-2.1746728,"14":0.1538938,"15":-1.3318721,"18":-1.6870466,"3":3.7032642,"21":-1.4621934,"32":-5.043085,"33":-7.713305,"36":-8.053912,"37":-8.696161,"39":-4.4493365,"40":-8.144897,"1":-1.2661278,"6":-0.12410784,"8":-2.746985,"43":-5.1592073,"44":2.2333236,"47":-8.900255,"20":-1.095273,"30":-4.513528,"48":-5.3303876,"24":-1.2112929,"34":-5.172039,"19":-0.95324004,"17":-2.070929,"42":-6.461173,"45":-6.7600837,"31":-2.5871289,"26":-3.340777,"11":0.2804512,"16":0.1068594,"23":-4.8734684,"12":-1.5782331,"29":-7.1542053,"28":-3.4927502,"49":-5.3866286,"27":-3.0936756,"38":-2.0778599,"4":-0.9748148,"13":-0.52859306,"25":0.20650482,"41":-5.2895117,"46":-8.941137,"0":-1.1868578},{"3":-0.009137404,"25":-1.5603099,"28":-2.22714,"32":-7.8123603,"44":-9.291084,"11":-3.164494,"0":-1.2419983,"22":-2.3453734,"43":-6.110915,"46":1.2300284,"26":-6.2174125,"42":-6.7060685,"40":0.5960925,"39":-0.55510026,"8":0.34857464,"14":-1.5066582,"20":-1.337916,"9":0.6811942,"24":1.2820337,"37":-8.370699,"45":-9.192501,"38":-1.04816,"36":-4.4000883,"47":-5.744712,"1":3.5028634,"19":-1.1210515,"12":-0.43374163,"27":-3.1297262,"4":-1.7701153,"7":-3.2430923,"35":-6.307065,"16":0.6228256,"48":-5.625827,"10":-2.5743856,"31":-6.1147633,"23":0.11987386,"18":-2.0092087,"21":0.17314282,"6":-0.7134326,"29":-3.5094981,"30":-6.9117966,"13":-0.8778326,"34":-8.84523,"49":1.066749,"17":-0.0647094,"5":0.5607598,"15":-1.9322069,"33":0.3513314,"41":-5.7962723,"2":-2.0310614},{"49":-6.5220246,"4":-1.1306865,"10":-3.8198159,"30":-1.1031879,"48":-1.5790758,"8":2.2603817,"16":-0.79346,"28":2.0295594,"44":-4.851879,"26":-1.8362617,"5":-2.1923168,"2":-0.3212226,"14":1.6364956,"15":-4.142886,"29":2.2276883,"33":-5.144367,"35":-1.0640407,"41":-5.1647105,"42":-2.6379626,"45":-1.6092819,"12":-3.3547428,"0":1.5736263,"27":-5.785195,"21":-1.2146126,"24":-2.0415292,"18":-1.5602193,"32":-7.0221467,"13":-1.3474666,"22":-0.81844556,"40":-3.3200588,"20":-0.6514348,"43":-4.108674,"46":-4.2089496,"11":-0.7390355,"1":1.061663,"6":1.5885234,"7":0.6280818,"3":2.9117198,"9":0.07487325,"23":4.007449,"34":-1.4104478,"19":2.5727477,"36":-3.5412018,"17":-1.2870796,"38":-6.710744,"25":-7.109666,"47":-0.370833,"39":-5.6028166,"31":-8.9709215,"37":-1.790431},{"11":-0.2690058,"14":-3.3913841,"33":-10.581197,"30":-2.976953,"44":-8.972217,"47":-1.6555841,"12":-0.70008695,"31":-0.25022045,"32":-4.7593946,"3":4.0897493,"25":-5.8330374,"28":-0.3651042,"1":-1.3884176,"4":1.4129777,"17":0.060185242,"39":0.2675672,"45":-9.048476,"13":0.4261586,"20":-2.8880363,"5":0.482935,"46":-1.8770514,"35":-6.084609,"37":-4.4077177,"0":0.6917456,"29":0.7991396,"6":1.3436899,"24":-5.914603,"10":0.5822578,"40":-10.1855345,"21":-1.2513664,"26":-3.6266258,"41":-11.609622,"7":-3.9402604,"19":-7.361811,"16":-1.9210724,"18":-2.2022328,"23":-1.5631672,"9":0.24691784,"38":0.1480616,"27":-0.855943,"22":-1.0407014,"36":-7.1688128,"15":1.2310383,"34":-7.753492,"2":-0.61810076,"49":-5.084746,"42":-1.9394081,"43":-2.63866,"48":3.1244411,"8":-0.15182641},{"7":-0.43349022,"49":-10.877606,"31":2.9603925,"6":-5.556307,"5":0.4943432,"8":3.279948,"15":1.1809219,"0":-1.1014942,"21":-1.0453392,"42":-5.4414873,"2":-1.333519,"14":1.0737827,"23":-0.8279856,"36":-5.918971,"43":-5.7445765,"33":-6.0693674,"47":-1.0138916,"18":-2.6747794,"12":-3.0570805,"41":-8.46067,"1":1.6520433,"19":-1.2949446,"3":-0.5493026,"20":-1.2345463,"22":-2.8604836,"30":1.1112442,"4":-4.622977,"38":-8.06308,"17":-1.5339053,"34":-1.6944987,"48":-10.203657,"10":-0.2837516,"9":-2.6424656,"32":-0.2698845,"37":-0.52395785,"27":-2.9062283,"13":-3.9294496,"26":-0.08224879,"40":-4.747753,"45":-5.1964364,"28":1.508776,"16":2.8767316,"11":-0.23955336,"25":-0.7301278,"35":-3.5013275,"24":-1.5041457,"29":1.7090166,"39":3.4003577,"44":-5.3315945,"46":-1.7841461},{"39":-8.850305,"0":-0.31384102,"1":0.33005637,"14":-4.2095695,"30":-4.744254,"24":0.49226952,"34":0.30211696,"5":-2.258627,"16":1.6377497,"35":-8.30131,"36":-1.7115602,"38":1.0439469,"22":4.7798386,"18":-2.6568706,"47":-1.1623662,"48":-6.944415,"12":1.617782,"20":3.180341,"46":1.2265141,"9":1.1028006,"8":-1.625981,"43":4.024492,"19":1.2642254,"27":-6.604052,"7":-0.815612,"15":-3.5696876,"21":-1.0932764,"44":-3.9737823,"28":-7.3513513,"31":-8.281484,"2":3.462738,"29":2.4749138,"40":-4.0741677,"3":-0.35019416,"11":3.2782063,"10":0.8151676,"41":-5.3454633,"23":-0.6134226,"42":-11.818179,"49":-4.002997,"32":-3.4771075,"6":-1.4568881,"13":3.3970814,"37":-2.5683503,"4":-1.2886088,"17":1.3861911,"26":-9.284855,"45":-0.5503845,"33":-1.9150283,"25":-7.9661813},{"41":-2.125005,"45":-2.285364,"48":-4.6482453,"28":0.6604254,"16":-2.4213662,"29":-1.7540013,"37":-2.3518906,"40":0.780832,"22":-2.9027069,"25":0.19567661,"43":-5.648198,"34":-1.4331398,"46":-7.898465,"17":-1.7564275,"20":-1.69505,"10":0.52288496,"5":0.9459981,"2":-1.6511103,"6":-1.2657624,"26":-1.7086216,"38":-7.8005066,"15":6.415408,"27":-1.8759415,"19":-3.2890472,"13":-0.92866564,"8":-0.80353004,"21":-0.846669,"33":-6.3950396,"18":-0.30217385,"0":2.4311461,"47":-4.7150774,"9":-0.7468906,"11":-2.1090035,"31":-6.2131243,"30":-4.3828797,"36":-4.1589737,"42":-1.7141869,"49":1.9700136,"32":1.4449139,"3":-0.23058562,"23":-1.0550442,"1":1.9567564,"7":-1.8053299,"14":0.6537564,"44":-2.2380223,"4":-0.39013156,"35":-8.063723,"12":2.818864,"39":-7.846856,"24":-5.2459273},{"11":-3.651795,"36":-3.6592553,"46":3.5374198,"43":-1.0684702,"28":-4.6952696,"25":-9.738652,"10":2.9154086,"7":0.47379246,"23":0.25744936,"39":-3.3373227,"18":-3.2992806,"13":2.4632888,"22":2.5900362,"24":0.019536471,"40":3.7954628,"4":-3.5070312,"19":-1.2321124,"41":-7.391198,"21":-1.5825781,"44":-9.540948,"1":0.9921595,"47":-3.066313,"5":-1.6908886,"32":3.6888547,"6":-1.893885,"30":-6.961839,"34":-9.805777,"31":-9.293772,"17":-1.1776975,"20":3.8133101,"42":-6.945607,"2":0.515446,"45":-3.4431224,"15":-1.1121713,"48":-4.484528,"12":-0.2619448,"27":-4.018808,"16":-1.0770698,"35":-6.763035,"49":-2.9136088,"29":0.96365297,"33":-12.277365,"0":-1.1318548,"3":-3.742433,"26":-5.55164,"9":-0.8058752,"37":-5.8556137,"38":-4.246865,"8":5.757755,"14":3.0806584},{"9":-0.2057724,"18":0.65480024,"19":-0.5791656,"6":-4.1889286,"1":-1.5216358,"39":-1.0287908,"10":-4.0246553,"32":-2.125645,"41":0.21487436,"0":-4.069114,"2":3.1414428,"47":-0.90623647,"11":1.5333946,"21":-2.0543995,"12":-3.2007298,"22":-1.4497566,"14":-6.1828666,"7":-4.159892,"25":-9.63585,"26":-7.4429374,"37":-7.786339,"43":0.12014313,"20":-1.934631,"4":1.2492312,"40":-2.1454458,"30":0.52946377,"24":2.935751,"34":-6.5771346,"17":-4.5573783,"46":-9.804109,"48":0.22530365,"49":-6.157985,"13":-4.2057967,"28":-6.7769585,"31":0.2229528,"36":-6.825086,"38":-5.032506,"23":-4.5710535,"45":-2.0093617,"8":-0.11515007,"5":-5.807103,"42":1.6801345,"3":-2.5140862,"16":-1.4748932,"33":-8.827309,"35":-2.1972833,"27":2.141932,"15":-2.8333688,"29":-0.17297097,"44":-1.3831823},{"36":-2.8135095,"10":-2.25838,"35":-13.175997,"38":-0.10819602,"14":0.14488819,"32":1.849961,"33":-3.560912,"40":-6.5650406,"43":-7.281546,"9":-1.366451,"45":-8.470662,"41":-2.6826994,"0":-4.7471137,"22":3.7242997,"23":-9.1801,"28":-6.467778,"44":-4.0730352,"47":4.854802,"48":-1.3245436,"8":-4.2908792,"49":-4.777823,"34":-7.4530396,"29":-3.5376484,"15":-1.3363883,"25":0.50829124,"6":-0.8238987,"31":-0.6595589,"42":-5.1152244,"11":-1.041917,"26":-5.4131002,"30":-10.942613,"13":-6.3417645,"5":2.9464679,"46":-1.7785702,"20":-0.87815493,"2":2.3935616,"7":0.35318094,"16":-4.648909,"17":-4.268477,"27":2.2764773,"39":-7.591966,"21":0.62592983,"12":1.6934954,"19":-0.71312016,"3":1.8464531,"37":-4.0512896,"18":1.5132655,"24":-0.075415,"1":2.4190595,"4":0.5512174},{"20":-1.0029869,"32":-7.0028534,"46":-6.3712296,"38":-11.28469,"11":1.4321346,"13":-4.646512,"8":0.82568824,"26":-6.6866937,"34":-6.9743547,"21":-0.15993659,"35":-7.561179,"2":-1.3738582,"33":-5.8647323,"29":-5.79581,"42":-9.010637,"4":3.8576484,"3":1.5597671,"25":-2.0643868,"1":1.403411,"36":-2.9291446,"40":-2.4914355,"49":-7.5185366,"24":0.10178737,"10":-2.319798,"30":-0.38378707,"28":-2.7420464,"41":1.1980788,"12":1.0323683,"23":-1.6790464,"37":-5.815829,"39":-6.986014,"27":-2.016024,"9":1.5110933,"6":3.8653584,"16":-2.3072445,"17":-1.1320391,"18":-1.3100469,"31":-6.7579093,"19":-3.8272312,"5":3.4332893,"15":-1.0270852,"7":-0.21366605,"22":-4.735616,"43":-7.3431945,"44":0.2794382,"45":-5.481395,"48":1.4239324,"0":-2.4516406,"14":2.852354,"47":-1.8403158},{"43":4.712132,"47":-2.8076248,"34":-5.309314,"21":-3.2880757,"35":-7.4554377,"32":-3.1367512,"36":-4.7028556,"37":-3.8426208,"9":-0.674682,"39":-0.5599256,"29":-12.210589,"22":-0.13606283,"41":-6.328129,"18":-1.164909,"49":-3.57017,"1":4.127396,"11":-0.9428345,"3":1.9628041,"4":4.1953306,"7":-3.6845498,"14":0.4822033,"2":3.093894,"20":-1.0495186,"46":-6.5783567,"40":-3.0340357,"45":2.5400724,"27":-5.730103,"24":-3.9002068,"12":0.27995282,"16":-3.8064854,"25":-7.231135,"8":-3.498286,"15":4.724143,"13":1.7394968,"42":-6.487033,"44":-5.5935636,"30":-2.5756748,"19":-0.1506988,"5":-3.5778935,"33":-4.179188,"38":-0.9539401,"0":3.8216126,"28":-1.8977871,"6":-0.37263384,"31":-4.747041,"23":-3.6482468,"17":-0.9581779,"10":-2.6111498,"26":-8.157618,"48":-7.4557214},{"33":-10.314481,"45":0.45036238,"38":-5.734908,"43":2.639183,"2":2.5284503,"41":-4.2538176,"22":-1.7867035,"12":-2.610121,"7":1.3721632,"13":-1.2747895,"36":0.02496109,"14":2.4581943,"5":-0.44087973,"8":-1.2204568,"39":-5.6762896,"16":-0.4870592,"3":0.82786256,"4":0.5342036,"20":-1.6381956,"0":-2.7690248,"31":-6.653174,"29":-2.4528012,"30":-11.768569,"34":1.431767,"46":-4.433118,"42":1.3029988,"24":-1.3565546,"27":-5.373475,"49":-0.84570026,"48":-2.0513895,"6":3.2195282,"18":-0.808298,"40":-2.6703453,"23":-1.254504,"19":-0.95532054,"10":0.28146416,"37":1.9051262,"35":-10.859381,"1":1.3868704,"21":-0.40074697,"26":-7.523516,"9":-0.68576276,"44":-7.2934403,"47":-3.176794,"15":2.6704679,"25":-5.222205,"32":-4.2750063,"17":-6.635766,"28":-3.4479432,"11":-1.3641121},{"2":2.276215,"47":-3.2619267,"42":-8.609499,"12":1.1753048,"25":0.2569335,"22":-1.1133944,"34":-0.46955186,"43":-7.5719423,"46":-8.8374815,"38":-4.504824,"14":-2.7551217,"0":-2.560429,"3":5.1125245,"10":1.7989223,"48":-6.3536506,"15":-2.8420284,"39":-8.642576,"41":-8.690711,"5":-3.0391555,"31":-6.457976,"49":-4.138269,"7":-1.457037,"40":1.0686321,"37":-5.4112587,"44":-5.18201,"35":-7.1456757,"18":-5.5924788,"24":3.118132,"19":0.79015416,"29":-10.938389,"30":-9.238553,"36":-8.172209,"45":2.0533695,"13":1.0999572,"20":0.54769146,"23":-0.7988988,"26":-0.24174762,"16":-0.6107198,"28":-3.4613,"32":0.6095932,"6":3.6477408,"17":-3.6828415,"33":-0.8097604,"4":2.1152596,"21":-1.380294,"27":-6.2883444,"8":0.33570617,"11":2.2367969,"9":-2.6519506,"1":1.980705},{"43":1.0098683,"15":1.5508325,"20":2.5622602,"44":-4.5266366,"47":0.5595596,"49":2.4441323,"40":-4.0074677,"12":-0.048756026,"6":0.71921927,"34":-0.58480847,"48":-0.91496503,"38":-15.602101,"9":2.1975396,"46":-7.5261803,"23":-1.7485645,"25":-9.98234,"3":0.40352917,"2":-1.3000925,"21":0.3793082,"39":-6.092683,"7":-0.03801304,"29":-3.934002,"45":0.6354839,"13":3.6475587,"8":1.8720827,"18":0.017846633,"11":-2.043403,"17":-0.34380132,"22":-5.0894356,"26":-5.601128,"4":2.7117019,"27":-9.118801,"28":-6.380714,"14":2.336324,"31":1.0155867,"32":-2.3734066,"33":-7.9750533,"35":-10.834158,"1":1.1004568,"10":0.0801218,"36":-3.3937993,"37":-3.5098853,"30":1.2499138,"0":-1.912598,"5":0.8430294,"41":-2.8287723,"24":3.875525,"19":4.445234,"16":0.12486223,"42":5.643771},{"35":-4.373653,"39":-3.8339558,"40":-7.6567903,"41":0.69444716,"42":0.028828835,"45":-1.4600407,"47":-2.4417567,"11":-4.7095685,"24":-0.5535633,"32":1.9858036,"37":2.0322814,"12":-2.4162068,"34":-9.590607,"48":-8.917204,"49":-3.570806,"2":2.803176,"5":0.36249524,"25":-10.362577,"1":3.1976237,"6":1.1062518,"9":-0.26095095,"10":-1.7055686,"16":-1.6398824,"22":0.28752896,"7":-1.9602249,"27":1.841807,"30":-3.7835777,"43":-13.37118,"36":-0.991679,"33":-4.332863,"44":-2.2979324,"0":-7.826865,"8":0.6042371,"29":-0.68760395,"38":-0.43278694,"28":-6.7683854,"46":-6.5680857,"31":-2.083424,"19":1.515781,"21":0.58686703,"3":-0.42720348,"13":-1.0984137,"20":-0.82027215,"14":-2.2391453,"26":-11.6260605,"23":-1.8618019,"15":1.1798068,"17":-6.882319,"18":-6.176575,"4":-1.6281652},{"28":-5.8822837,"1":2.2290926,"15":-0.09372015,"17":-2.3746054,"7":0.20270583,"27":0.69065905,"32":0.915246,"40":-5.31744,"46":-9.837294,"2":-0.93895876,"8":-6.085413,"22":-2.7239099,"41":-5.6375093,"48":-8.899936,"29":0.0027605921,"37":-0.8713808,"12":-1.8604918,"6":-1.0182593,"20":-3.73087,"14":1.816541,"44":-0.7164334,"49":2.4857643,"4":-2.1264198,"24":-5.3399286,"9":2.5283256,"5":2.2643538,"33":-7.4025598,"47":2.1582499,"34":-5.583238,"45":-6.126522,"35":-2.115096,"30":-6.706281,"26":-6.4366236,"0":2.2121549,"25":-5.4770045,"38":-4.6497736,"36":-9.086209,"42":-12.190786,"3":1.6297512,"10":0.459166,"19":-4.500603,"16":-6.038791,"23":1.9341301,"39":-2.7983081,"43":-1.0343044,"21":-1.0805318,"31":0.14618544,"11":-3.062262,"18":-0.41792578,"13":-0.090203665},{"32":-8.576912,"3":2.902244,"9":-2.2658901,"38":3.9877076,"47":-5.7354836,"4":1.5600158,"42":1.057154,"0":-0.6629299,"16":-0.018089961,"33":1.3013139,"8":-0.74672085,"31":-8.661469,"6":-0.5772754,"18":1.1205552,"30":-1.0220808,"40":-7.375016,"44":2.6144624,"46":-10.400541,"49":-4.659255,"25":0.7587763,"43":-4.1714654,"34":-0.7735878,"29":-6.635881,"26":-11.739682,"10":0.8690982,"11":-1.6824448,"37":-1.3363206,"12":3.0274022,"19":-1.7937721,"39":-7.3830743,"45":-2.3882127,"20":-5.4004745,"48":-3.9932237,"17":-1.3957766,"7":3.6751428,"13":1.9064058,"14":-3.3826096,"36":-4.5335298,"22":-2.5287876,"15":2.7899702,"1":5.52359,"28":-9.294072,"35":-3.6705127,"2":-2.1291173,"24":-1.5257052,"41":-0.71657294,"5":2.1434064,"21":4.107393,"27":-3.5565886,"23":0.010993642},{"38":0.46182185,"45":-12.038506,"5":2.267443,"17":-0.878061,"1":-0.50349605,"8":0.42654738,"12":0.60158116,"14":-8.053246,"21":-7.054209,"30":-7.0099854,"9":-2.9551797,"35":-0.55309594,"48":-1.035589,"31":-13.417664,"27":1.3648034,"3":-4.7918863,"2":0.7940941,"4":-0.43286604,"37":-13.6914425,"24":3.1593513,"16":-0.6849333,"13":-5.147784,"10":-1.0683151,"6":-0.6961941,"19":1.851626,"26":-5.6383963,"32":-5.3763113,"7":0.23220558,"33":-2.6460223,"42":-6.849289,"46":-4.746596,"23":-7.258671,"28":-7.6737146,"18":2.3403146,"11":-0.013505417,"49":-6.286148,"39":-0.40695518,"0":-3.2664692,"15":-7.00295,"22":-1.4648011,"41":-0.9407824,"47":-10.191892,"43":-3.423345,"29":-0.13835388,"36":-9.666438,"40":-4.6459026,"44":-5.1328597,"25":-5.3466907,"20":-0.236443,"34":-7.8050904},{"36":-1.1021398,"38":2.8363478,"7":-0.25550094,"32":-4.397956,"13":-2.272571,"39":-7.7849946,"14":-0.6163994,"44":-3.109485,"40":-5.039219,"42":-4.409039,"11":2.5792224,"12":-0.73589885,"26":-0.181468,"46":-4.6932697,"4":-4.100952,"9":0.17415304,"10":-0.49939138,"0":1.5686579,"49":-5.457412,"41":-1.1163824,"48":-5.947212,"19":-0.12931958,"16":-1.2785285,"3":3.1339252,"22":0.87162524,"24":-1.804472,"27":-2.787685,"20":0.64999044,"8":-0.6033512,"31":-2.776437,"6":2.0214238,"43":-6.332577,"47":-9.101875,"5":-5.3744264,"18":-1.4351798,"25":1.6444756,"15":-2.0076077,"29":-6.951574,"28":-4.494911,"45":-5.2034936,"30":-3.3045204,"17":1.156345,"37":-7.3774886,"23":1.0976998,"33":-0.89560604,"1":0.5232691,"21":1.0698664,"2":-0.64349777,"35":-1.1121864,"34":-4.6030397},{"9":2.190166,"28":-7.4588118,"41":-3.0742311,"17":2.9307246,"22":-9.923037,"6":-0.16164787,"4":-1.1363175,"39":-6.248021,"12":0.24132304,"1":-2.713612,"27":-0.07187139,"33":-7.763086,"34":-0.41333818,"43":-2.1395404,"19":4.359834,"35":-11.584904,"29":-6.971022,"7":-1.7796974,"31":0.14847927,"40":-5.780584,"42":-1.7180843,"38":-6.7182236,"45":-6.369739,"3":-1.054813,"25":-9.008263,"32":-4.6274695,"14":-1.1536787,"23":-1.4608774,"26":-3.0705976,"20":1.8712232,"5":-1.1226983,"2":4.438132,"0":2.674596,"8":-4.648837,"30":-6.2653766,"15":-1.0775777,"44":1.1995169,"36":-8.327929,"37":-0.2859703,"18":2.7921336,"46":1.1397331,"10":-5.2556334,"11":0.12635288,"13":-1.2765973,"47":-9.403023,"48":-0.38354635,"49":-4.84352,"21":-2.7698321,"16":-0.83886015,"24":-0.15186839},{"28":5.9858217,"41":-2.5946589,"33":-13.326108,"49":-0.1200408,"8":-3.520242,"23":-0.89950657,"29":-2.5363152,"16":4.158094,"44":-7.062139,"0":-0.6842047,"39":-1.0357105,"48":-5.7711987,"1":0.8595847,"24":-0.6513852,"3":1.8261887,"43":-1.865908,"7":-3.6038241,"21":1.4663875,"22":-0.5832413,"13":0.008382213,"9":-0.11109141,"34":-5.511672,"17":0.71482784,"12":1.7165174,"38":-8.569481,"45":-0.40404946,"6":2.7062097,"10":-2.8204112,"18":-1.0395474,"42":-4.379624,"32":-1.5242176,"46":-0.5147158,"47":-7.1876717,"25":-5.6444526,"19":-0.42743522,"2":2.95814,"15":-3.4512162,"20":-3.0797153,"26":-8.033984,"27":-1.3476844,"11":0.70997757,"30":-2.856438,"14":0.80246,"31":-8.738088,"35":-8.800494,"36":-0.73412144,"37":-6.1242657,"40":-4.5236726,"5":-0.049777318,"4":1.0418781},{"45":-2.0878205,"16":-0.92655456,"41":-3.649571,"10":4.049384,"49":-0.5620002,"6":-0.45706034,"37":-5.9845037,"20":-5.267105,"14":1.7209429,"23":0.29273722,"27":2.8883698,"43":-0.39091828,"12":0.15071163,"0":1.6999867,"13":1.1857785,"46":-1.219162,"17":0.42997736,"1":-0.54470813,"48":-1.5372324,"18":-0.060723305,"19":-1.794137,"29":-3.1528285,"30":-6.650927,"15":-3.9812603,"3":-0.19193271,"9":-0.3728759,"31":-3.5608888,"7":1.4619437,"44":-6.973526,"26":-2.5819368,"33":2.5657094,"2":0.5460104,"11":1.6197468,"25":-3.3421376,"40":-4.1599216,"47":-0.41627723,"35":0.88371384,"21":-0.1889462,"42":3.0599132,"36":-1.9014488,"4":2.864126,"5":2.5040603,"8":-0.54301894,"24":-0.17286158,"28":-7.469434,"32":-4.774502,"38":-7.184961,"39":-1.9451778,"22":0.06270179,"34":-3.0252218},{"1":0.92288417,"28":2.7764556,"32":0.7825122,"37":-8.300393,"38":-6.160379,"6":1.199569,"13":1.843347,"26":-0.54914397,"7":0.56303585,"22":-0.3928766,"27":-8.180695,"39":-2.5213783,"48":-9.358813,"15":-0.7177399,"20":0.759079,"12":0.99288845,"9":1.4179152,"17":2.3389351,"35":1.6612186,"41":-1.1579964,"30":-5.2557178,"43":-4.2738295,"44":-3.757796,"4":-0.057965852,"45":-3.4518898,"36":-0.76826334,"46":-2.6758778,"21":3.1219337,"47":0.497543,"18":-1.9290537,"40":-1.2653574,"42":-7.452469,"23":1.2170466,"16":-0.8002862,"11":2.9817648,"29":-4.8019037,"49":-4.8648057,"10":1.394212,"3":0.16004555,"24":-3.3897088,"5":4.5822277,"0":-2.0557404,"25":2.7564936,"33":-7.0469894,"8":1.8904943,"2":1.7194443,"19":1.5301462,"31":-2.5256042,"34":-2.4306138,"14":-0.65857023},{"35":-7.440613,"38":-5.8018556,"45":-3.9852467,"39":-10.731432,"7":2.283769,"44":-4.273898,"41":-0.2424072,"17":1.2359033,"33":1.4908397,"6":1.0062697,"14":4.7816806,"2":2.648913,"16":1.5623152,"18":-1.9043556,"20":2.4425204,"25":0.06304965,"12":3.6959503,"4":1.7667234,"40":4.697076,"46":-7.489553,"49":1.2028736,"22":-1.5187964,"3":0.7459575,"8":4.042272,"9":1.4675652,"13":2.143397,"31":-5.179932,"26":-0.298519,"19":0.8127718,"0":-0.77233094,"29":-7.1337113,"30":-6.8897324,"11":0.47068086,"48":-3.2769763,"23":0.74841917,"34":0.6907096,"15":-0.22754793,"47":-3.2000694,"1":-0.150973,"24":-1.9273676,"32":0.5115291,"37":-8.655817,"42":-0.6817434,"10":-1.4657556,"27":-2.863134,"36":-5.0656424,"43":-7.0269217,"5":-2.4479375,"21":2.1148446,"28":-9.779695},{"3":2.7166603,"21":0.5608846,"33":-4.4162126,"8":0.020828724,"46":-6.2821884,"45":-6.739582,"10":-2.304177,"27":2.0577347,"42":-0.77062494,"1":3.8701484,"0":-0.80404174,"9":3.6465697,"24":-0.6140908,"48":-8.031019,"34":-1.6037617,"4":2.2597942,"13":-0.47515202,"6":0.45057854,"16":-1.9515893,"12":-1.9472828,"17":3.103903,"23":-1.8590027,"28":3.0854974,"31":-2.7784257,"36":-7.423518,"38":3.785356,"39":-0.14416714,"40":-9.468713,"29":-1.2941794,"19":-2.6143894,"15":2.0398443,"18":0.827158,"11":-2.2458482,"7":2.3907075,"25":-3.0863743,"43":-4.634142,"22":-1.2707936,"30":-4.269676,"41":1.9262397,"35":1.7550011,"47":-8.083954,"44":-9.288431,"49":-5.073826,"37":-1.8682301,"26":-12.438062,"14":-7.7163367,"20":0.7501568,"2":2.001269,"5":2.2369907,"32":0.87154025},{"13":4.528148,"34":-7.3217316,"22":-1.0858823,"40":-2.9252558,"30":-6.155912,"29":-7.895255,"41":-9.410375,"43":-1.2027782,"31":-8.15159,"49":-3.664584,"7":2.3541687,"28":-1.44493,"42":-4.3936977,"46":-12.043066,"6":-1.1208286,"37":-1.7790867,"35":-11.96357,"45":-1.0903281,"1":1.1902065,"24":4.6144295,"10":0.11402883,"18":-0.6236558,"48":2.1994855,"44":-4.8308754,"8":-1.0938857,"36":-2.5280573,"38":0.4988204,"19":3.6937184,"12":-0.21491185,"5":-0.94118184,"9":-2.128302,"3":1.3499311,"25":-2.3497412,"21":0.7251157,"11":0.64074576,"39":-6.9730415,"47":-10.566658,"27":-1.7992518,"2":1.8139048,"26":-13.1088,"23":-4.486793,"0":1.0543308,"17":0.8507044,"32":-0.74444467,"16":1.7839527,"20":-3.2473247,"14":-0.22960457,"4":0.64059687,"15":-2.1211636,"33":-5.263303},{"22":-2.3157744,"5":-0.8974422,"2":0.89333487,"3":-2.720667,"26":-5.7018075,"8":5.0277834,"34":-3.7690265,"18":0.4795602,"16":-7.229969,"15":0.4576252,"19":-1.4481394,"1":-1.0378385,"38":-1.8835398,"41":-9.38455,"24":3.7980595,"7":1.2893342,"39":-1.9939642,"10":1.7236315,"14":-2.1268165,"13":2.4808698,"30":-6.2626553,"35":-1.2192616,"37":0.33116633,"31":1.3258712,"46":-3.4694881,"11":0.9214215,"23":2.2248876,"21":0.28852615,"29":-0.9131012,"6":-1.1601796,"40":0.3064028,"32":-10.766546,"4":3.450002,"9":-0.19940262,"0":-3.8103623,"27":-6.823036,"36":-4.575931,"45":1.1442382,"43":-1.949317,"44":-1.1268657,"48":0.19965892,"20":1.9219799,"47":-4.3444314,"42":-3.2612987,"49":0.29744834,"28":-5.1106477,"12":-2.436583,"25":2.4430537,"33":-3.0943086,"17":-3.724048}],"nn_shapes":{"31":[18,29,20,13,4,10,18,3,28,8],"9":[18,25,22,27,18,8],"32":[18,32,29,4,28,10,34,8,8],"33":[18,34,17,28,8],"42":[18,30,33,19,7,16,14,12,10,8],"35":[18,34,22,34,4,8],"4":[18,31,19,8],"46":[18,20,4,16,26,7,30,6,8],"36":[18,32,25,19,8],"20":[18,4,21,19,8],"34":[18,27,10,19,9,20,24,33,15,9,8],"45":[18,11,12,6,8],"8":[18,21,7,8],"24":[18,17,11,11,23,34,8],"1":[18,15,34,8],"5":[18,26,34,8],"11":[18,16,4,3,8,8,34,15,8],"23":[18,25,9,13,26,10,30,28,8],"26":[18,3,4,29,19,26,26,14,8],"6":[18,4,29,16,11,20,12,25,8],"21":[18,19,21,6,32,31,9,10,8],"16":[18,6,11,18,18,8],"19":[18,16,33,6,26,8],"38":[18,17,3,21,4,8],"3":[18,27,28,8],"2":[18,5,15,22,7,8],"40":[18,32,8],"47":[18,9,6,7,29,8],"17":[18,8,9,8],"22":[18,33,8],"0":[18,9,8],"37":[18,24,19,16,19,5,25,3,13,4,8],"41":[18,25,21,15,29,8],"44":[18,32,20,20,33,3,26,27,19,14,8],"49":[18,8,8],"43":[18,22,19,15,32,15,29,27,12,33,8],"12":[18,11,24,26,16,6,32,30,3,8],"7":[18,30,8],"14":[18,10,31,27,8,9,11,8,8],"15":[18,16,24,20,16,8],"48":[18,32,4,6,5,7,6,31,8],"25":[18,3,5,29,21,8],"39":[18,12,29,8],"10":[18,30,27,6,31,6,8],"18":[18,23,8,3,20,8,5,11,28,12,8],"28":[18,29,8],"30":[18,11,22,4,19,8],"27":[18,26,17,8],"29":[18,19,29,28,19,9,23,8],"13":[18,9,28,14,34,19,30,25,8]},"crossbreed_segments":19,"weight_initialization_range":{"start":-1.520325,"end":0.9699532},"minor_mutation_rate":0.7658576,"major_mutation_rate":0.5723158,"mutation_weight_range":{"start":-0.5263189,"end":0.5263189}},"state":"Finish","generation":35,"max_generations":35,"id":"7ce5d55b-767e-4c6a-9fdc-4c2300c7f0b4"},"left":null,"right":null}},"right":{"val":{"node":{"id":"e9ccd9b0-ed91-4472-82f2-4a2eba9ec7cf","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_e9ccd9b0-ed91-4472-82f2-4a2eba9ec7cf","population_size":50,"generation":39,"scores":[{"43":3.9992142,"47":-4.5046477,"2":-5.5795836,"8":-1.3530275,"44":-3.0102296,"14":-3.7390893,"23":-4.242768,"29":-3.8128166,"3":-5.487908,"21":2.6828303,"25":-1.0500784,"26":-3.7741694,"48":-0.42123738,"7":-6.781279,"24":-2.9674377,"9":-7.3867884,"13":-7.6303062,"18":-7.8061724,"4":-1.3329614,"19":-5.2335706,"6":-0.5076838,"28":-5.945432,"41":-5.6895003,"11":-0.73944724,"42":-9.400959,"45":-1.5968401,"27":-0.355994,"46":-7.7236977,"20":-1.1665789,"31":-1.7760379,"0":-2.8921604,"1":-2.114426,"15":-4.2793436,"30":-4.6812367,"35":-1.5566807,"16":-8.6894245,"39":-5.578435,"22":-5.2217,"34":-1.9205135,"40":-3.0051837,"49":0.66726625,"33":-3.3533502,"12":-5.592861,"5":-5.312856,"36":-6.2344356,"17":-5.7748103,"38":0.5411754,"32":-3.046572,"37":-1.5194869,"10":-6.279486},{"30":1.2161078,"41":-4.031306,"9":-1.494943,"1":1.0179576,"12":-2.2582572,"14":-0.0033039986,"27":-3.4722354,"42":0.2308604,"46":-6.825545,"35":-6.606893,"49":-0.3220868,"8":-1.0572344,"16":-1.1804974,"18":-3.7892349,"28":-4.736219,"2":-1.8897156,"31":-3.9525425,"13":0.76869696,"26":-3.3246117,"34":-4.679414,"25":-1.8132522,"3":0.1972888,"0":1.9798477,"11":3.4497924,"19":-5.0132017,"36":-0.5877894,"5":-4.8670454,"10":0.3955422,"38":-3.554502,"39":-6.358167,"40":-7.178646,"22":-3.6489968,"20":-11.086825,"6":-2.3245173,"23":-2.4844785,"48":-3.9294486,"21":0.5636681,"47":-4.993905,"43":-4.0265465,"29":-4.931633,"37":-2.3056412,"44":-2.1918764,"24":-0.804235,"45":1.0559769,"17":-4.9950585,"15":-7.107587,"7":-0.095338166,"32":-5.624285,"33":-11.3650875,"4":-6.0735903},{"33":-0.2672924,"37":-7.0523896,"45":-3.1899393,"4":1.1832592,"46":-7.437276,"9":-1.2738589,"20":-0.8374712,"30":-1.8275493,"44":-5.6137276,"10":0.016293202,"38":-7.447117,"47":0.51455915,"2":-5.1127825,"3":1.1743109,"17":-2.266808,"25":-5.9312935,"13":-3.5899894,"34":-0.5204814,"8":0.71664774,"22":-7.2720046,"42":-2.2801425,"35":-0.54734063,"16":-1.882032,"36":-4.8975015,"48":1.9084457,"49":-6.8638544,"27":-6.180188,"15":-3.1083062,"41":2.9100556,"43":0.72237223,"0":6.4811068,"23":-3.8430195,"18":3.4745827,"32":-0.8023281,"21":-1.9450836,"12":-2.7419124,"24":-3.4513748,"26":-4.294472,"29":-3.973111,"19":-1.660873,"40":-7.069042,"6":-6.3210344,"1":0.6500218,"5":2.709934,"11":0.124825045,"39":-3.5105922,"14":-0.3178838,"28":-2.510563,"7":0.4148386,"31":-6.6896887},{"3":0.70983666,"8":-2.2349143,"15":-1.2513312,"2":3.1698282,"4":-0.8506104,"16":-5.381428,"18":-6.4334326,"19":-2.572496,"21":1.1237977,"22":-1.7872235,"1":-1.9813999,"0":2.2834702,"14":-0.39137167,"17":0.65386784,"25":-2.5975916,"29":-7.5092864,"13":-1.0119712,"31":1.5681539,"6":-1.3966221,"33":-0.81131,"9":-1.8049839,"36":-4.071992,"37":-5.9423456,"12":-1.3135865,"39":-5.1311674,"41":-6.0883174,"43":-2.120512,"44":-2.1180835,"48":-1.4098536,"7":-2.358136,"11":0.40244323,"46":-5.146202,"23":-1.516969,"20":-0.36661258,"10":0.19683321,"28":-3.416018,"30":-1.3816006,"40":-1.0533543,"38":-1.5830915,"34":-7.170728,"45":-1.7942884,"47":-6.9535356,"27":-7.1779795,"49":3.0859761,"24":-3.9915624,"32":-3.174423,"5":3.4098115,"35":-3.2228577,"26":-8.541118,"42":-1.303786},{"41":-6.3701453,"15":-2.0339947,"20":0.4183866,"31":-3.7665467,"35":-6.529357,"5":-1.2160978,"1":1.0422201,"40":-1.9403757,"42":-7.4427004,"12":-0.6386552,"46":-6.137133,"0":-1.8831915,"27":-5.0639377,"2":-0.29547006,"14":-0.15438399,"28":-6.4436235,"3":3.0657654,"16":-0.729885,"18":-0.5947112,"19":-2.33356,"13":-0.20582339,"48":-1.9001707,"23":-0.20848198,"29":-2.633077,"39":-1.1359828,"43":-4.9488792,"8":-2.3867326,"21":-0.1401242,"37":-5.498063,"6":-1.8504162,"22":-1.1518805,"4":-2.0092123,"17":-2.5496225,"33":-3.7804139,"36":-1.7574921,"44":-1.2355787,"26":-9.513443,"49":-2.929417,"47":-2.2842972,"11":-1.5175254,"9":-4.323693,"45":-7.9193735,"30":-0.5015472,"10":-2.2717535,"38":-3.238856,"25":-4.037089,"7":0.11865799,"24":1.5743479,"34":-2.54675,"32":-7.3294044},{"21":0.002967483,"10":-1.6881393,"0":-0.09014299,"15":-1.753298,"44":-12.352557,"45":-1.7466633,"13":-6.0673747,"14":-0.2908476,"8":-1.794873,"6":-1.6077182,"22":-2.0611207,"16":-5.3833337,"33":-3.2568278,"37":-1.7897274,"41":-6.8075967,"39":-7.8766813,"28":-6.0944533,"9":-2.2877007,"25":-1.7429354,"11":-1.460429,"1":-2.8458152,"19":-5.320201,"4":-0.9419524,"20":-0.112826966,"12":-1.8409541,"26":-5.6394415,"34":-5.3140664,"35":-1.4955766,"40":-1.1019347,"5":-0.4805966,"18":-3.5132184,"30":-0.6222774,"23":-1.342622,"7":0.3945582,"24":0.35801253,"17":-0.99341553,"31":-1.3236859,"32":-1.8476651,"2":0.37019396,"36":-3.540688,"38":-4.2795362,"42":-3.8382351,"43":-1.3367411,"29":-5.935337,"46":-1.2821245,"27":-5.479205,"3":0.5341558,"47":-2.9846358,"48":-5.097788,"49":-0.094796374},{"17":-3.9550927,"32":-3.3171592,"40":-2.4567707,"31":-3.6726398,"43":-5.3145113,"45":-3.314258,"15":-1.4313362,"46":-4.794688,"1":1.1092482,"48":-0.5783099,"47":-4.1458673,"20":-0.14032319,"24":-2.865559,"6":-0.1845022,"18":-0.29459697,"27":1.3865328,"7":-2.8306901,"29":-3.8067093,"23":-5.62708,"2":1.9770987,"3":-0.40744215,"39":-2.027505,"30":-3.4561577,"42":-0.49071854,"13":-1.1093819,"44":-1.359725,"49":-4.270898,"16":-1.3099486,"14":-3.1968582,"33":-6.610059,"11":-1.639101,"35":-2.336779,"36":-0.6048788,"38":-0.6002465,"37":-3.7830098,"41":-4.5704784,"8":-0.3513032,"26":-4.4879103,"34":-2.945097,"22":-2.128545,"25":-2.4106598,"21":-1.2951179,"10":1.1755426,"12":-1.1337605,"0":-2.481666,"4":-1.0497648,"5":1.6246659,"28":-2.1384883,"9":-2.84212,"19":0.23993035},{"15":-0.5575965,"8":-1.2797987,"42":-8.086983,"44":-6.320508,"2":1.7739608,"19":-1.1683242,"45":-6.0461717,"13":0.43286723,"46":-6.203097,"48":-2.3888295,"33":-8.6250305,"36":-1.2893306,"17":-1.1821846,"7":-6.0994215,"30":-2.6853046,"20":-2.3868945,"10":0.44522938,"12":-8.017397,"28":-4.1283474,"6":-0.78775585,"29":1.959193,"27":-5.0404935,"34":-3.4925885,"37":-4.3349533,"5":-4.8512006,"16":-1.1228998,"40":-2.2141623,"9":-3.424548,"24":-1.9454339,"39":0.5661337,"22":-0.4741568,"41":-1.8539038,"23":-2.322418,"32":-6.666247,"35":-0.71028095,"43":-4.313234,"14":-2.363731,"18":-4.0629244,"31":-3.703715,"38":-3.891969,"47":-4.446846,"3":1.4331659,"21":-3.304261,"25":-0.2744257,"49":-6.246268,"1":-2.474549,"11":-0.20799717,"4":0.542174,"26":-5.744078,"0":2.9048371},{"39":-1.0083039,"27":-3.3774612,"6":-2.4098399,"40":-2.4348347,"33":0.048642922,"43":-3.37929,"44":-0.3132566,"45":-0.9355472,"20":-2.4439034,"47":-2.7029686,"1":3.3756676,"4":-4.2703366,"24":-1.7563646,"9":0.049877547,"10":-2.1979668,"23":-7.2783213,"37":-7.5914383,"12":-2.8697972,"49":3.4945786,"17":0.2660234,"32":-6.674684,"48":-2.9025922,"19":-6.035722,"46":-0.7289762,"21":-3.212648,"7":-0.33863682,"3":-1.3051996,"28":-3.1396213,"8":0.009596395,"29":-2.2473025,"18":-1.5519928,"36":-5.6627817,"13":-1.8093303,"26":-5.7594147,"2":0.8199009,"0":1.1146716,"14":-3.5745785,"22":-0.6953138,"15":-0.3217592,"25":-1.5685022,"34":-1.4037598,"16":-0.8556398,"11":2.4476075,"30":-0.41936773,"31":-1.4368732,"5":1.1860164,"35":-5.6351204,"38":-1.9837389,"41":-4.495288,"42":0.15063803},{"7":-0.7718784,"30":-3.541531,"27":-4.7723556,"22":-1.4038429,"11":-0.185096,"10":0.68760884,"49":-1.5618917,"2":1.419422,"29":-5.385145,"0":2.755609,"19":1.3312238,"12":-2.159225,"5":-0.42700833,"13":-1.0507237,"23":0.35108203,"24":-2.510683,"47":-0.032363795,"1":3.2318428,"16":0.027117383,"36":-1.3730414,"8":-4.152361,"33":-0.4757672,"38":-12.011841,"20":1.8526627,"9":-3.9146304,"42":0.078292586,"17":-2.0965407,"45":0.708029,"41":-1.2375861,"43":-2.6319194,"31":1.3287512,"34":0.016118193,"4":1.3351895,"15":-0.7380446,"32":-4.3767843,"37":-5.2361054,"28":-5.5365553,"14":0.065305196,"21":2.1623693,"39":-0.80251104,"3":1.0586182,"35":-0.61291254,"48":-4.540633,"25":-2.4006119,"6":-0.17414978,"40":-4.7291884,"18":1.3840742,"26":-2.750651,"44":-5.106943,"46":-0.67965573},{"37":-3.647609,"29":0.53563297,"12":0.182439,"21":-0.37325156,"33":-5.569397,"40":0.4050188,"9":0.6015199,"17":-0.8685428,"27":-6.0469756,"32":-0.17398681,"47":-6.4160223,"48":-0.011684597,"5":0.6881464,"20":0.909681,"45":-1.1848186,"6":2.1076868,"24":-1.043169,"36":-1.1742532,"42":-4.045116,"43":-1.703203,"8":-1.2379206,"39":-3.3361504,"25":0.069199,"2":1.0969979,"34":-6.279511,"13":-0.21740022,"10":-2.1218956,"22":-1.3644476,"1":1.096051,"7":-2.4457722,"28":-1.4240782,"35":-2.0047002,"41":-0.88586825,"19":-0.4632822,"26":-4.4105883,"14":0.23970902,"31":-1.8136575,"46":0.1194268,"16":1.1666701,"44":-0.060142003,"3":0.5819024,"18":-0.15628457,"11":-1.2421626,"15":1.0688509,"0":-0.01766477,"23":-0.52614087,"30":-3.4085166,"38":1.092621,"4":0.051703263,"49":-0.48033255},{"39":-0.8097353,"45":-4.6091747,"49":-2.3429544,"0":4.6909013,"23":-0.33870643,"46":-6.2761345,"36":-2.7613902,"14":-2.8446157,"44":-1.0748715,"18":-2.9685073,"21":-1.8445809,"4":-0.18027519,"8":-0.20233941,"19":3.2557712,"22":0.1839052,"32":-0.52092063,"5":-1.2569103,"47":-7.675683,"48":-6.2399397,"25":-3.5096703,"12":-0.5838994,"6":1.7820994,"37":1.153915,"38":1.4842095,"7":0.60168,"16":3.6185489,"17":0.115030386,"3":0.65398276,"9":0.9398902,"26":-1.1603184,"27":-2.6607437,"33":-0.9219724,"35":-6.264696,"41":-0.6842636,"13":0.90996706,"11":0.39423978,"24":-1.2792782,"28":-4.616446,"29":-1.6313177,"34":-1.893089,"42":-1.6772099,"43":-4.4321504,"1":1.6374744,"30":-1.1260786,"20":-2.3123834,"40":-1.0584848,"15":-0.1136778,"2":1.297457,"10":0.771706,"31":-0.49372903},{"18":1.843343,"48":-2.3260427,"11":1.2345682,"20":-0.44947678,"35":-1.4961929,"0":2.5324197,"39":-5.0516925,"16":-0.24023978,"46":-2.5366795,"8":1.6773808,"3":0.9517206,"22":0.059372794,"34":-1.8604243,"37":-2.1974106,"42":-1.0585811,"15":-0.6423564,"6":0.82989085,"23":-1.0489807,"49":-2.9762394,"4":1.1213424,"25":3.165667,"38":-4.2662935,"21":-0.2642374,"7":1.5180295,"17":-0.68948835,"24":-1.9519389,"14":-0.07390101,"27":0.40589008,"13":1.3306811,"9":-1.7720668,"19":-0.6951172,"30":0.1460456,"1":-0.24095845,"29":-0.5211332,"10":-0.6118816,"12":1.0779173,"33":-5.2865686,"36":-0.6828236,"40":2.691331,"41":-1.8558085,"44":-1.1460923,"45":-3.0736158,"5":1.581803,"2":0.2060998,"47":-4.8148293,"32":0.520277,"28":-3.3647797,"43":0.4097596,"31":-2.5112262,"26":-1.9135878},{"45":-1.828818,"27":-3.9781845,"9":1.0448663,"1":0.5032152,"44":-5.0817337,"12":2.0783315,"2":2.6825695,"16":-0.01902939,"28":-0.14135881,"3":-1.4847008,"49":-3.2476387,"6":1.6506058,"26":2.4984813,"0":1.5982283,"14":-1.8659487,"39":-1.8003147,"4":-0.3222174,"40":-3.874482,"35":-2.7212148,"18":-0.14480382,"36":-4.484176,"17":-4.527171,"32":-5.005798,"33":-0.6439644,"22":-0.87691224,"38":-2.155911,"47":-4.8374915,"46":-4.4317145,"19":-0.4433476,"20":-1.169621,"21":0.86086017,"30":-0.8310798,"43":-1.8577973,"48":-1.7748047,"13":0.70406055,"15":-4.6105113,"10":1.3232958,"25":-6.4578896,"8":3.2943752,"37":-6.259249,"31":1.449472,"41":-1.0283349,"42":-5.8519,"7":0.08401001,"23":-1.32374,"24":0.6111746,"29":-2.395357,"11":0.6146709,"5":1.9058244,"34":-2.7471895},{"41":1.5665799,"14":0.98933697,"48":1.1490715,"17":-0.65369266,"31":4.948372,"13":-0.6165978,"28":-6.0271063,"7":-0.49433655,"9":2.243611,"34":0.65494835,"15":-0.46870703,"23":0.43246946,"37":-4.889591,"12":0.7344483,"24":-0.17955902,"30":0.2612438,"39":0.85301316,"43":-0.4484605,"44":-4.687682,"46":-0.91271675,"22":-1.4422206,"29":-4.8090563,"38":-2.1764765,"40":1.121421,"49":-2.2692378,"45":-2.7204723,"5":0.95555294,"10":-0.8139192,"25":-2.6582286,"2":0.16849199,"6":1.6245015,"27":-0.5177392,"0":2.319653,"4":0.97141457,"8":0.12884225,"35":-7.141038,"47":-0.85919076,"26":1.4384298,"36":-0.85735494,"42":-0.32833806,"21":-1.189548,"33":-1.2861583,"11":0.5186689,"18":-0.0094464,"16":2.2433152,"20":-0.052867997,"3":0.9253257,"1":5.393658,"19":1.5576637,"32":-3.226401},{"20":-0.19593543,"14":0.83755285,"25":-3.3789551,"16":1.7667615,"23":-2.8395653,"0":2.9271774,"45":-2.0184617,"27":-5.159618,"7":0.49175817,"17":-1.4910275,"33":-0.6657322,"31":-5.1403327,"34":-1.7197632,"41":-0.5913552,"18":-0.77919066,"29":3.1252992,"13":1.258585,"3":-0.98743933,"22":0.33892083,"28":-6.335474,"32":0.18858862,"24":-0.3337306,"26":-4.24691,"9":-0.6333006,"2":1.0877444,"11":1.4607445,"42":-1.7089491,"19":-0.4904346,"37":-4.237266,"38":0.4482972,"10":1.4857776,"44":-4.6286287,"47":-2.2293868,"39":-0.6815168,"48":-1.0589371,"49":-0.207094,"21":-0.33392,"30":0.97979534,"46":-0.92402613,"4":-0.9387461,"5":-1.1654049,"15":-3.043011,"35":-0.4398272,"1":0.9374426,"43":-2.230125,"8":-2.0628667,"36":-4.8863635,"40":-0.29670602,"12":-2.1375053,"6":-0.47122374},{"20":-2.0764604,"7":0.6831805,"42":-1.8826067,"28":1.4643631,"9":0.16895945,"47":-2.528984,"19":-0.91754705,"13":-2.2037284,"0":2.3398948,"16":-2.7832704,"17":-0.11946501,"18":1.0754315,"21":-2.4618123,"22":-1.8098357,"12":1.3815764,"24":-0.776282,"30":-4.980134,"6":0.91764337,"41":-1.1241322,"27":-9.4606905,"29":-4.3998313,"46":-2.5967937,"48":-1.9770539,"36":-0.7873017,"44":-1.0231769,"23":-0.3352366,"3":0.048101377,"35":-1.396189,"10":1.4753078,"39":-2.2247286,"4":1.1689899,"11":0.41674337,"14":0.53420657,"31":-0.06436679,"8":0.03502481,"5":1.0379488,"15":0.063165605,"2":0.3548666,"32":-4.513288,"33":-1.2948178,"34":-5.5102587,"26":0.23320284,"38":-2.0078678,"45":-5.1284723,"49":-4.005569,"43":-0.9229042,"25":-0.45239383,"37":-0.6370138,"40":-1.2462422,"1":1.6530983},{"21":-0.38331962,"8":2.026757,"20":0.5151671,"9":-1.1924055,"37":-2.0277083,"38":-0.3017742,"16":1.3640101,"15":0.13279581,"44":0.17723341,"34":-3.5099156,"17":1.050812,"48":-2.6314995,"18":-0.6588848,"3":0.16171512,"28":-1.1441414,"13":-0.67578256,"39":0.8596543,"29":-2.3316154,"4":0.8421298,"24":-2.6958911,"43":-1.0933688,"40":-8.010778,"1":1.0655518,"5":0.8657354,"12":1.7577412,"22":-1.7020146,"30":-1.2490209,"36":-2.9840484,"42":-3.3119438,"33":-1.4082686,"35":-2.4250636,"10":-0.68846077,"14":1.5289061,"31":0.41172418,"46":-2.9917612,"32":-0.2886078,"2":2.022957,"0":0.103600785,"26":-0.559658,"7":0.81756955,"11":-1.0929754,"27":-6.08912,"19":-0.22903097,"45":-1.3501732,"41":0.95710343,"47":-5.5495005,"49":0.534135,"23":1.0897367,"25":-0.5317975,"6":-0.4651228},{"43":-0.912937,"35":-5.619212,"2":1.8388735,"22":-0.9333854,"12":0.8697807,"32":0.110505484,"41":-1.8088824,"18":0.2546326,"47":3.81651,"49":1.6372879,"10":0.6941844,"30":-2.030867,"29":-0.37006766,"48":1.118849,"5":0.950566,"31":-4.9221606,"23":1.8758444,"44":6.254659,"26":-4.2569823,"7":1.2397369,"38":-5.7180223,"19":1.3346251,"6":5.6847343,"36":-0.43315703,"39":-0.8046114,"8":-2.9715362,"24":0.054477204,"40":0.5457916,"42":-7.515415,"17":0.7352798,"28":-1.6180528,"16":-0.655275,"37":-5.824168,"1":1.5037214,"33":0.99957705,"46":1.3663788,"0":1.5710032,"4":-0.71526957,"20":-0.4160846,"21":0.6576546,"45":0.21937521,"9":-0.2137694,"34":0.79329664,"25":-2.0128503,"15":1.2275442,"27":1.4035922,"3":1.2848402,"14":-1.2121488,"13":0.3068056,"11":-0.29952198},{"37":-4.4004545,"41":-2.222691,"23":-0.5789318,"48":-0.3404936,"28":-1.5919569,"11":1.1093464,"33":-4.122744,"10":0.061913647,"3":-0.35628682,"21":1.5737168,"16":0.44581977,"7":1.7618641,"5":-1.1996305,"29":-0.3316942,"34":-2.5683775,"35":-5.105937,"44":-2.6302686,"14":0.31766742,"17":0.23522523,"19":0.18837519,"12":0.05608965,"13":-2.070341,"20":0.6463616,"32":3.5927615,"47":-1.7332737,"25":-5.487849,"2":0.06039362,"1":0.8864252,"15":-2.714026,"36":-1.1382253,"42":-0.71505725,"4":1.4925568,"39":-3.233741,"6":0.58313,"27":1.2401294,"18":1.621157,"30":0.1280034,"38":-3.1176038,"40":-2.0954242,"0":-2.9062622,"45":-8.4225445,"8":-0.6730328,"31":-4.962428,"43":-6.423936,"24":-0.47346577,"46":-3.6372285,"49":-1.8083713,"9":-0.6966462,"26":-6.2507567,"22":3.008004},{"45":-1.3502338,"14":0.13960299,"44":-5.4499006,"16":0.95496666,"6":1.367815,"49":-0.4535522,"37":-2.4270504,"35":-7.0546584,"4":1.002504,"41":0.4838778,"47":-5.666858,"30":0.014100397,"1":-1.9072472,"8":-0.036146164,"0":1.2664982,"10":1.2509449,"11":-2.5789587,"42":-1.4337652,"43":-4.2873507,"48":-0.4941422,"5":1.7776229,"29":-1.0830773,"36":2.0817184,"23":-0.50965863,"40":-2.8496785,"2":0.9281192,"19":1.0012746,"20":0.015172231,"27":-4.8046246,"25":-3.8953362,"13":-0.39926222,"9":-1.3867865,"32":-6.3781114,"12":-0.77974415,"15":0.1178506,"3":-1.3276548,"21":-0.22982259,"26":-1.2269458,"17":1.9777172,"24":1.1893345,"34":-2.942224,"39":-5.1201906,"22":-2.2610478,"18":1.5328476,"46":0.0609849,"28":-2.2523043,"31":-3.73463,"7":1.619745,"33":1.2126552,"38":0.57228535},{"33":0.111007,"0":0.26398602,"46":-5.181652,"39":-2.4558427,"2":1.6909914,"11":0.4194135,"27":-0.14934582,"25":-4.174695,"44":-1.0273359,"8":-0.353817,"17":0.1357848,"45":-1.6964096,"49":-4.6153183,"7":3.1115263,"18":0.13559121,"24":0.39826924,"26":-0.55122554,"30":-0.11509659,"32":0.48896962,"13":1.2921351,"38":-0.1601688,"47":0.59402,"4":0.7718768,"5":0.4986666,"28":0.027459204,"3":1.0086848,"9":0.49581322,"15":0.6072948,"21":4.538417,"35":-2.450342,"31":-3.7499118,"40":-5.7495856,"41":-3.7462673,"48":-1.2489603,"6":0.6865552,"14":0.49282742,"37":-7.440291,"19":-0.6730548,"20":0.8999076,"12":2.2032115,"16":0.7288872,"10":0.018206965,"29":-1.6469771,"36":-0.8425232,"42":-3.6253192,"22":-0.20344476,"1":2.8155265,"34":-7.0441017,"23":1.0008605,"43":-0.1113266},{"22":-0.6586558,"9":1.1314344,"32":-1.5680592,"36":-0.8533578,"31":-3.2067542,"38":-3.6722717,"12":-0.07685864,"14":-0.60288227,"17":-2.8837016,"2":3.3234475,"35":-1.5204799,"37":-3.209972,"42":-2.1913428,"43":-1.8419298,"45":1.2042474,"48":-1.9888375,"25":-3.2159877,"47":2.0907893,"29":6.7774034,"27":-3.1187475,"49":0.71196216,"15":-2.6433995,"7":0.7915288,"46":1.1011006,"39":0.1378324,"21":-0.5441158,"10":0.66657865,"33":1.123852,"3":0.7643628,"19":-1.1355412,"44":-7.3508873,"0":-1.8399782,"6":1.0705874,"24":-1.1593626,"4":1.0258007,"40":-1.1859785,"34":-1.0650387,"26":-0.62917054,"41":-4.253795,"1":0.69640076,"18":-0.5270852,"30":-0.5022922,"13":-1.9747204,"23":-0.078635395,"5":1.4788063,"11":-0.07876179,"16":0.013259391,"20":1.9411991,"28":-3.362252,"8":0.06666659},{"34":-2.7972891,"41":-3.0241163,"17":-0.72019404,"10":0.565201,"5":-0.89857703,"40":-3.9168696,"30":-1.582868,"25":0.9845649,"22":-1.398474,"8":2.672579,"20":0.1820744,"27":0.1380808,"21":-1.4320674,"23":-0.5520258,"15":0.3157118,"35":-4.4062576,"44":1.6953154,"9":1.5416327,"11":-0.58077353,"47":-4.6250205,"32":1.6194327,"42":-0.9026753,"2":1.6730347,"4":1.4957306,"26":-0.91637754,"6":0.4077366,"33":-2.1433148,"24":0.5288726,"19":2.8770792,"14":1.7169793,"13":0.2853578,"37":-7.774788,"38":1.1167654,"0":3.6629982,"16":-2.4991193,"43":-4.6798472,"45":0.02233224,"39":-1.5030068,"7":1.0117346,"46":-2.5338433,"36":-6.143666,"48":-0.8891219,"12":1.5098102,"18":-0.6569006,"28":-1.0404272,"29":-0.03862221,"31":-1.5303066,"49":0.32614166,"3":-3.1282127,"1":1.0279994},{"18":0.6604774,"47":0.8899954,"33":-3.7245018,"36":-1.0225995,"17":-1.1147861,"24":0.5374546,"38":-3.3574524,"3":1.7766193,"14":1.2199423,"21":1.243838,"23":-1.267468,"41":-8.298978,"29":-1.5435592,"31":-0.7093566,"46":-2.9374676,"15":0.2779922,"6":0.9636345,"5":1.4459374,"11":0.579142,"42":-1.097306,"27":-5.7303634,"30":-3.3576713,"0":-1.4678035,"26":-5.9164486,"34":0.45587367,"37":-0.10927023,"48":-1.8899235,"2":-0.18987153,"1":0.6175562,"10":1.7188747,"12":0.10841598,"13":1.3972888,"28":-1.3158249,"49":-1.3877184,"20":-1.2648284,"43":-1.663022,"8":-1.132695,"44":-3.0397713,"22":-1.488466,"25":-2.5360603,"19":-0.064900205,"32":-1.1007107,"35":-3.084701,"4":-0.17310643,"7":-1.6408389,"9":0.30429503,"16":2.8813558,"40":0.1800078,"45":-0.3830676,"39":-0.96258104},{"36":-3.3359516,"9":0.9330746,"49":-1.5461657,"11":-0.5726112,"45":-2.8377793,"15":0.54808795,"7":1.5244305,"10":-0.22020045,"28":-1.7956407,"2":-0.85950917,"0":1.7151455,"1":3.3772361,"30":-1.8674014,"32":-6.0219817,"43":-0.8482424,"44":-2.0775094,"39":-1.0879123,"14":1.617876,"25":-4.747404,"13":-0.36698598,"18":-0.20844777,"38":-5.3396997,"23":-0.6448526,"5":1.2135891,"8":-0.12349222,"37":-4.1154084,"26":1.6531429,"19":-0.19092938,"48":-1.8402226,"22":-1.0843995,"27":-5.412662,"29":0.4311072,"31":-2.2850564,"34":-1.8338486,"35":0.3665412,"12":0.55514324,"21":1.6548359,"40":0.92506677,"33":5.5298824,"6":1.9586585,"42":-0.5972514,"46":-1.542189,"4":0.31508017,"41":-0.90031564,"16":-0.9500306,"24":-6.0560417,"3":3.789816,"47":-3.0269158,"17":0.027847994,"20":-0.6700682},{"28":1.1288234,"40":0.87217796,"0":-0.7586061,"8":0.494485,"44":-2.2150588,"45":-0.386381,"38":-0.2505812,"26":0.5875476,"25":-1.6999061,"48":-2.9775357,"30":-1.5419779,"43":-4.88367,"32":-1.2074382,"15":-1.1331334,"22":-0.48761138,"1":-1.1029061,"19":-2.8695912,"36":-1.385388,"9":-0.5907652,"5":-0.2272294,"17":2.398828,"13":0.51119363,"39":-3.2340355,"31":-6.655467,"6":-0.0746934,"41":-0.62863064,"34":-0.9826952,"10":0.6478238,"16":-0.4877163,"27":-2.4787016,"35":-0.9645256,"3":1.4740309,"47":-0.10555184,"21":1.4830458,"33":-2.2505474,"49":0.3895468,"14":-0.94155276,"11":1.9035084,"18":0.483735,"2":0.3721424,"37":-2.1193151,"42":-3.030218,"23":3.4191055,"7":1.4082838,"12":-1.0382984,"24":-0.75468576,"4":1.7115856,"20":-0.5657972,"29":-2.110145,"46":-1.9120096},{"15":1.944478,"21":-0.2984876,"42":-2.9528816,"39":1.800387,"47":-1.646691,"49":-0.34259042,"22":1.7518902,"25":2.524334,"30":-4.8175273,"34":-5.4654436,"46":0.20593223,"31":-2.050412,"23":-0.11068597,"5":1.2099707,"14":0.4865474,"2":0.68227077,"43":-0.06186118,"24":-0.19395098,"26":2.034871,"1":1.9514068,"0":-2.6643283,"18":0.8866358,"10":1.1588438,"32":-3.457545,"16":0.97437125,"35":-5.945342,"44":-3.396225,"8":-1.2162848,"40":0.19354261,"7":2.798709,"27":-5.3135023,"36":1.8891814,"33":1.1082118,"19":-1.4409006,"12":1.138737,"28":0.17646646,"45":-9.295891,"48":-3.3867753,"3":1.3969146,"38":-5.801274,"4":0.4594646,"37":-1.4969332,"6":1.598965,"17":0.57453,"9":0.38121003,"11":0.3105362,"13":-0.016958024,"29":-1.0688138,"41":-3.969773,"20":-1.6625687},{"9":0.97805023,"16":1.1191095,"10":-1.1945508,"23":0.003369999,"0":-1.5999568,"25":2.8128219,"26":-2.0414555,"18":-0.1778976,"38":1.3205807,"47":-2.7215629,"32":-1.5231652,"49":-0.30859417,"42":-4.472599,"8":1.270441,"12":0.6815638,"36":-1.9087219,"46":2.3731933,"33":-4.327035,"39":-7.8875937,"21":-0.29515883,"20":1.0052465,"45":-3.2687156,"13":-2.9033473,"27":-4.476912,"3":-0.3934346,"14":-0.4230876,"44":-2.2067287,"41":-1.896862,"34":-8.244708,"37":0.901989,"30":-2.5450568,"2":-0.79439163,"6":0.1481972,"22":-0.2921804,"48":0.95617753,"5":1.8698848,"4":-1.7504936,"7":2.5125144,"28":-3.7448273,"19":0.83776075,"29":0.51791394,"31":-2.7646766,"40":-3.016816,"15":-0.7282958,"1":-2.3728797,"11":1.5873438,"17":-3.3093534,"24":0.5696704,"43":-2.8125026,"35":1.3244356},{"15":-0.25488853,"20":-0.48834497,"33":-1.0895845,"14":1.278381,"29":-5.3387976,"24":1.4251068,"21":-0.24026322,"2":-0.42692345,"7":1.3910904,"35":-0.82035637,"37":-1.7306406,"43":-6.320527,"44":-4.8869805,"45":-3.1586444,"19":1.3541219,"31":-6.04781,"6":-1.1386598,"28":-2.3460262,"46":-1.5687416,"48":-1.9219358,"49":0.9321574,"34":-3.0071368,"3":2.6741743,"42":-0.12092724,"47":-2.0108137,"5":-1.6729527,"25":0.4264628,"36":-1.7714208,"18":-0.1819288,"8":0.7693506,"9":1.5816145,"27":-3.9188354,"41":-3.4202054,"26":2.8988082,"12":1.1819172,"4":0.22990718,"38":-3.0631194,"16":0.17362687,"17":1.1310523,"23":-1.602157,"30":-1.034477,"32":2.242922,"0":-0.9159268,"39":-4.006467,"13":1.1536442,"40":-0.73043764,"22":2.8358412,"10":1.3282477,"1":1.5065397,"11":-3.0932324},{"38":-2.5680718,"14":1.4560716,"29":-3.4689415,"30":-2.5148644,"37":-6.302335,"41":-4.8315206,"19":0.3500399,"47":-0.548347,"5":0.3119769,"9":-0.423551,"0":1.6750437,"24":-2.5930886,"42":-0.3687028,"33":0.9139765,"17":-4.8899183,"31":-2.1750543,"36":0.49549618,"44":0.97260416,"11":0.6013934,"12":1.4031398,"40":-0.6134804,"10":1.4210254,"6":0.6365846,"13":0.0067045987,"15":2.107652,"8":-0.02119199,"16":0.36185378,"20":0.9270712,"21":-0.67956376,"1":0.99191236,"28":-3.1786149,"34":-0.160623,"18":4.133351,"35":0.9718288,"39":-2.6729126,"26":-1.9536797,"32":-4.0769935,"43":-2.8834677,"45":-3.72598,"46":-1.6148249,"48":-0.3796658,"7":0.83497447,"22":0.16604762,"23":-3.27048,"27":-0.33879238,"4":-0.033089064,"49":0.311697,"25":-3.1667867,"2":-1.9228675,"3":-0.31504256},{"35":-3.5243175,"40":-1.3703208,"47":-4.34122,"31":-1.9018475,"39":-9.854407,"6":0.34723464,"38":-3.2374332,"12":0.45909357,"49":1.1421105,"19":-0.6246812,"26":-4.347471,"33":-0.2909654,"8":-4.262568,"10":-0.1504522,"13":0.92017806,"36":0.2519614,"2":3.4330597,"42":-0.5678538,"43":0.43201742,"46":-6.489367,"48":-1.277086,"3":1.5350004,"11":1.1993188,"16":-0.7542814,"24":2.4536736,"23":0.7671008,"14":-1.6488779,"37":-6.230839,"44":-0.038455203,"4":1.4139125,"9":0.3742116,"7":-0.4297134,"28":-1.1396847,"45":-5.292748,"0":0.17727824,"17":0.32667857,"21":-0.747707,"27":-4.0839047,"41":-0.8616618,"25":-3.6654048,"32":-2.0008845,"5":1.7872808,"20":-1.6609504,"30":1.9633911,"34":2.1601627,"1":0.8120201,"22":1.5914758,"18":0.8138803,"29":-0.9236167,"15":0.1999308},{"33":-1.1205482,"42":-0.3568408,"44":-5.4842167,"18":-0.4991506,"29":-6.497651,"39":-0.4408508,"0":1.6255801,"5":-0.33866897,"34":-0.8651436,"21":-4.441754,"31":-3.93688,"26":-0.29017657,"14":-1.554593,"35":-0.0065032006,"37":0.9210024,"41":-2.7178075,"11":1.90499,"3":-0.3665378,"24":-0.18292336,"40":-4.4100804,"25":0.10754641,"8":1.6238363,"1":1.7215664,"9":-0.46882373,"10":1.2484143,"46":0.35080737,"20":-0.21955438,"32":-4.011467,"15":-0.3031666,"43":-0.17886767,"23":-2.527999,"4":0.19145255,"19":-1.3317778,"28":-0.7315844,"13":1.2637122,"27":0.067873016,"17":-0.2314182,"38":-4.2398543,"47":-2.0861669,"16":0.19074997,"6":-0.8019079,"36":-3.0603595,"22":0.11332059,"45":-1.9139893,"48":-4.86819,"49":-4.381362,"30":0.19002959,"7":0.92482007,"12":0.7591172,"2":-0.78119516},{"0":0.07573281,"1":-0.51525474,"11":-2.2619138,"27":-0.8619586,"16":-0.91446114,"21":-1.6303629,"30":-2.9187837,"39":-2.561172,"47":-0.7712024,"13":-0.8877617,"28":-5.8734403,"18":-1.3136339,"4":0.99469185,"37":-2.2994409,"45":-0.70429707,"23":0.59479326,"8":1.6799672,"36":-7.0039735,"26":-2.6348667,"35":-0.75352323,"20":-1.9679495,"32":-2.9398806,"9":-0.5643793,"12":-0.14451401,"15":-2.1569707,"5":0.5518644,"22":0.13247797,"40":-3.69274,"44":-2.8314805,"38":0.54267675,"6":1.4411135,"25":-2.5298598,"46":-2.0339694,"49":-2.0023599,"41":-0.31658298,"43":-7.0486283,"24":-0.4602314,"2":1.793318,"19":-1.3801826,"34":1.1256696,"10":0.22839019,"17":-1.8590304,"33":-4.5432596,"48":-6.701822,"14":0.009854799,"3":1.632971,"7":1.0404248,"29":-6.5853806,"31":-2.0828743,"42":-1.8138546},{"49":-6.757707,"40":-1.1292893,"0":0.5713294,"22":-1.0479006,"11":0.9841038,"24":-1.3268901,"27":-5.5573626,"43":-7.4976945,"2":1.9192886,"20":-1.3256521,"8":1.1106948,"29":-2.1459212,"10":1.0210966,"14":-0.277484,"34":-3.349294,"45":-3.5646331,"4":-0.26060295,"3":0.93326557,"44":-4.3833513,"5":0.40772098,"12":0.8045707,"17":-2.2520256,"23":0.12034259,"33":0.37488517,"39":-2.7319741,"15":-0.9907344,"30":-3.9259887,"32":-4.2665873,"35":-4.4173417,"47":-4.703204,"16":-0.6270258,"9":-1.5985333,"28":-1.9594017,"31":-1.7163975,"7":0.84538186,"13":0.046235003,"38":-4.0677934,"46":-1.5943807,"42":2.789826,"18":-0.47530442,"21":-2.5285869,"19":2.0338924,"6":1.6060946,"37":-2.0172658,"25":-0.046380807,"48":-2.2985606,"26":-2.3568342,"41":0.08112361,"36":-6.16838,"1":1.797365},{"37":-1.4304115,"21":-0.52033883,"45":-0.042861998,"36":-2.9040992,"30":-2.2107856,"29":-6.4635572,"3":1.1938145,"6":0.7663746,"24":2.6614363,"25":-4.108682,"31":0.07092409,"42":-1.513715,"17":-1.0665957,"26":-0.45318097,"34":-2.965707,"49":-2.8141303,"2":1.5740372,"40":-2.9386754,"46":-5.3229613,"47":-4.545804,"13":-3.7739463,"35":-3.5969536,"5":-0.43369442,"19":-0.027410597,"20":0.8128456,"7":0.2537802,"23":-0.8441516,"32":-2.9709396,"8":-2.3096359,"18":1.831557,"27":-6.999772,"41":-1.4876883,"43":-0.4713582,"9":0.47276178,"44":-0.6776245,"48":-4.1920123,"11":1.5427032,"12":6.3979793,"39":-3.7826912,"16":-1.2882276,"28":0.268861,"1":-0.22343893,"14":-2.0444407,"38":-4.46025,"15":0.038316537,"33":-1.6677793,"22":-2.8683054,"4":1.0132487,"10":1.5811306,"0":2.0965312},{"39":-1.9916598,"8":0.82139397,"4":-0.074226394,"21":-2.2298713,"35":-2.4928071,"44":-3.204987,"47":-5.725115,"15":-0.1635712,"20":-0.7012008,"28":-2.9107132,"3":-1.820108,"22":-0.338886,"31":0.63103104,"43":-4.008866,"46":-0.57759374,"38":-1.9465355,"10":0.6690342,"45":-2.8019192,"5":1.1833485,"11":1.1237419,"2":1.1560495,"26":-1.3252609,"25":-1.405263,"40":2.4006233,"36":-4.345249,"37":-6.311501,"41":-3.0459294,"48":-1.5928406,"1":-0.172332,"18":-0.9315017,"19":1.0583894,"0":2.823148,"27":-2.0228767,"7":-2.4231975,"32":-1.1156528,"49":-0.42193374,"17":0.1206646,"34":-4.4950075,"42":1.8763359,"13":-0.5032588,"6":2.3546627,"24":-2.7632918,"14":-1.0245461,"9":1.9460214,"12":-2.3633664,"16":-1.7197758,"23":-2.5543134,"29":-1.5893643,"30":-4.188504,"33":-3.0962613},{"36":-0.18478374,"42":-1.5540817,"29":-7.7798424,"47":0.30624396,"13":-0.42620045,"12":0.57017577,"1":-0.6129861,"16":-0.596876,"25":0.12901358,"23":-2.3385625,"31":-5.808885,"5":0.555488,"45":-6.5301576,"17":-0.95508516,"11":0.1731164,"26":-1.1566368,"14":0.32648858,"33":-3.4008038,"0":-0.3367958,"6":-2.8475065,"32":-3.7033966,"37":1.6442347,"7":1.5582616,"38":-3.522852,"40":-1.6138722,"41":-5.496302,"3":-1.2893279,"49":4.1518035,"39":0.33432153,"9":0.14616223,"21":4.864088,"43":0.39905006,"44":-5.1964397,"35":0.31146058,"2":0.5905143,"20":-2.3346632,"28":-0.7481426,"27":-0.93774664,"19":-2.005755,"34":-1.6427485,"30":-6.2885246,"10":0.24368978,"8":-0.06622221,"46":-3.4268932,"15":-4.8229866,"48":0.4666408,"4":-1.9318378,"22":-0.1114092,"24":1.0758584,"18":0.4650938},{"22":-3.5756817,"37":-9.071033,"10":-1.8858116,"34":2.7970462,"7":0.3158346,"12":4.501189,"5":1.2660772,"19":0.5042872,"18":0.48649222,"25":1.5755206,"27":-6.3972588,"17":-0.42274117,"29":-3.2006156,"36":1.1589952,"4":0.3002882,"23":1.5621816,"21":2.6864624,"0":-0.16294077,"26":-0.5033666,"39":-7.6897545,"40":-0.016058827,"24":-0.61354035,"8":-0.51865804,"13":-0.52019703,"38":-0.6581934,"14":0.42200416,"2":-2.3457806,"9":0.33820218,"3":1.3744695,"30":2.2592192,"16":-0.90820324,"32":-0.20193341,"42":-2.347648,"1":0.16103336,"33":-0.4909802,"6":3.1643548,"43":-3.1131873,"35":0.08277939,"41":-2.6790206,"44":1.16269,"11":-6.843913,"45":-4.1776648,"46":0.303608,"49":-0.54469836,"20":-2.58775,"28":3.2526505,"15":1.0949957,"47":0.471136,"48":-2.1694362,"31":-3.8798134},{"30":-3.0976996,"29":-0.11577072,"35":-7.354271,"39":-1.9747374,"43":-3.329221,"12":0.6476982,"1":0.92776096,"16":0.86639655,"22":-0.93201333,"10":-2.6646035,"11":2.5242467,"7":-0.445926,"23":0.39262843,"9":1.7748388,"17":0.5707578,"24":1.9917469,"31":-5.3366585,"42":-1.1999134,"0":0.24666736,"3":0.64872617,"21":1.7236627,"4":-0.9960572,"6":1.8076702,"2":0.65111303,"27":-0.7413453,"18":1.4938546,"34":-9.794947,"15":2.9434776,"26":-3.570221,"36":-2.976345,"37":-0.82631433,"19":0.85954446,"41":-2.9892304,"8":0.260788,"45":-0.795544,"14":-0.20265062,"47":-1.6965628,"48":-7.5674195,"46":1.4020077,"32":-0.7225974,"38":-2.3181195,"44":-3.9726825,"40":0.69793445,"49":4.098015,"25":-0.04452408,"20":0.98498106,"5":-0.24092159,"28":0.18877839,"13":1.3221974,"33":-7.76015}],"nn_shapes":{"34":[18,30,26,4,8],"30":[18,12,9,8],"40":[18,19,12,33,8],"44":[18,16,8],"39":[18,14,34,21,8],"26":[18,25,30,17,12,29,8],"37":[18,24,8],"24":[18,5,7,30,5,14,8],"11":[18,6,7,8],"0":[18,16,20,14,14,8],"2":[18,3,20,14,27,3,23,32,15,16,8],"6":[18,4,8],"1":[18,34,8],"10":[18,21,29,8],"3":[18,8,32,17,16,20,14,8],"22":[18,16,31,8],"28":[18,12,33,31,24,8],"32":[18,6,3,3,9,8],"42":[18,21,12,33,8],"20":[18,16,8],"12":[18,29,18,8],"27":[18,19,8],"17":[18,33,16,11,21,24,8],"45":[18,29,5,10,21,8],"41":[18,15,25,24,8],"48":[18,14,3,32,7,30,18,28,32,8],"38":[18,16,7,19,8],"5":[18,9,10,24,16,16,24,22,8],"18":[18,21,10,15,3,30,14,8,9,8],"29":[18,12,31,18,17,22,11,8],"25":[18,6,30,9,30,13,34,11,31,4,8],"16":[18,27,28,25,25,8],"23":[18,31,27,7,24,24,10,34,17,8],"35":[18,20,22,8],"43":[18,22,17,15,11,33,9,20,8],"13":[18,33,11,9,11,33,12,29,8],"31":[18,20,5,5,29,22,34,17,4,13,8],"49":[18,12,14,8],"19":[18,23,6,34,15,15,19,31,8],"8":[18,23,18,30,9,4,6,25,10,8],"14":[18,5,16,32,4,8],"36":[18,6,11,29,11,26,8],"33":[18,10,3,18,32,11,8],"46":[18,7,17,12,15,23,21,8,14,8],"21":[18,11,8],"47":[18,16,24,18,9,30,8],"7":[18,11,26,32,14,23,25,8],"4":[18,11,30,6,11,27,4,4,25,8],"15":[18,31,18,12,28,8],"9":[18,27,29,27,13,22,23,30,21,8]},"crossbreed_segments":17,"weight_initialization_range":{"start":-0.58231544,"end":0.9471909},"minor_mutation_rate":0.6085979,"major_mutation_rate":0.8314668,"mutation_weight_range":{"start":-0.3715943,"end":0.3715943}},"state":"Finish","generation":40,"max_generations":40,"id":"e9ccd9b0-ed91-4472-82f2-4a2eba9ec7cf"},"left":null,"right":null}},"right":{"val":{"node":{"id":"4927e1b5-284e-483d-8034-f3215653c381","folder":"F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_4927e1b5-284e-483d-8034-f3215653c381","population_size":50,"generation":31,"scores":[{"12":-2.2077053,"3":0.45991325,"29":-5.7882757,"18":-2.2291424,"34":-3.1415505,"39":-0.7864648,"41":-3.9259582,"31":-8.159813,"30":-9.762412,"44":-1.0203212,"1":-6.7983437,"45":-6.35163,"49":-2.099032,"9":-7.843995,"46":-6.1997557,"16":-5.510003,"25":-2.2139595,"32":-6.1215925,"28":-6.6735907,"17":1.905855,"37":-6.420905,"15":-6.4148955,"26":-7.188308,"33":-5.85794,"7":-6.0783887,"5":-6.417207,"10":0.010562134,"2":-5.9887342,"21":-9.142227,"19":-6.406163,"4":-7.634589,"11":-6.9612417,"8":-5.8950186,"36":-5.302123,"38":-5.260914,"42":-6.4598823,"43":-4.6521482,"47":-9.834925,"35":-3.2873101,"48":-6.4925375,"6":-6.1462326,"27":-11.390952,"13":-4.002927,"23":-6.287218,"20":0.85533315,"0":-2.0039697,"40":1.309963,"14":-7.07207,"24":0.5197624,"22":-6.666387},{"11":-3.1593714,"23":-7.3299093,"1":-6.145644,"6":1.483731,"9":-0.6556816,"2":-4.148638,"25":-5.1679506,"30":-5.348699,"46":-7.037226,"49":-8.848057,"4":-3.0848193,"15":-2.467299,"39":-6.170186,"26":-0.5247086,"21":-10.35053,"14":-5.0187755,"33":-8.753212,"0":-1.8040926,"10":-2.776891,"18":-8.060572,"34":-9.008932,"16":-5.412995,"22":-6.774733,"20":-6.516478,"27":-14.3797865,"35":-6.627566,"36":-7.2936707,"13":-5.982255,"41":-5.6998496,"29":-5.5276694,"12":-4.0255694,"44":0.14863339,"32":-4.6895003,"40":-6.7866983,"47":-9.807707,"3":0.58976936,"8":-1.0932678,"28":-7.4047194,"31":-6.036427,"17":-6.1377964,"42":-4.0090632,"19":-1.7264202,"24":-8.958688,"37":-7.0282373,"43":-6.6490464,"38":-6.3970222,"45":-1.4937651,"7":4.220898,"48":-9.550586,"5":-2.8598814},{"33":-5.6023507,"13":-6.3158717,"39":-2.8065636,"42":-7.7015204,"47":-2.3057332,"16":-3.6309006,"2":0.5464354,"17":-4.1867523,"23":-4.0060606,"28":-8.803106,"41":-9.040792,"18":-9.425822,"48":-4.321704,"36":-11.423918,"34":-2.2816656,"44":-7.009552,"43":-4.4056306,"35":-6.7132483,"15":-6.161241,"10":-3.6888256,"46":-7.8272743,"7":-3.0122342,"6":-1.4529712,"49":-5.3853364,"27":-8.59745,"20":-7.1111917,"8":-1.6422218,"38":-5.9290366,"40":-3.7400718,"12":-1.9083523,"5":-3.9113204,"21":-8.91613,"9":0.6585968,"22":-1.135728,"25":-6.484613,"29":-8.474455,"45":-8.32863,"4":-2.2522066,"11":-7.930439,"14":-2.1474767,"0":-6.758498,"1":1.7913196,"26":-7.3978105,"3":-2.4625714,"19":-7.1945314,"31":-7.0496416,"30":-4.800157,"32":-8.060525,"24":-6.128545,"37":-5.7629156},{"10":-3.046687,"1":0.8607483,"8":-2.0910704,"24":-5.5820785,"42":-2.4317532,"37":-9.662027,"22":-4.7042913,"0":-1.5257607,"41":-8.399315,"48":-5.0573096,"21":-4.6769023,"18":-6.5455866,"4":-0.223496,"36":-6.822048,"35":-6.348737,"29":-5.338405,"7":0.19812974,"17":-1.858531,"26":-5.9917307,"6":-4.5832453,"40":-7.9213243,"38":-7.6650033,"14":-3.6837623,"28":-5.839343,"34":-8.584593,"39":-8.098098,"16":-0.16430959,"20":-4.863111,"43":-9.438566,"46":-2.0096812,"23":-10.821867,"25":-6.103114,"27":-7.6538863,"15":-6.214506,"3":-3.1899228,"2":-2.3405912,"47":-4.1823525,"13":-3.684972,"9":-4.205561,"19":-3.1162148,"45":-2.1388886,"12":-4.7768736,"44":-2.3882852,"11":-4.203885,"49":-11.360749,"33":-3.9829133,"5":-2.353114,"30":0.2969101,"31":-9.7553425,"32":-6.381272},{"35":-6.329638,"8":-8.48383,"3":-2.7779248,"31":-5.3982596,"4":-1.1286305,"10":-1.4911047,"11":-5.4523444,"14":-1.5719903,"34":-7.1248283,"0":-1.5292861,"41":-5.144696,"15":-3.0785363,"12":-4.135915,"2":2.068638,"44":-5.723578,"32":-7.1161447,"46":-5.656232,"1":-7.9339433,"13":-1.6012735,"16":-3.7929688,"17":-1.3662989,"28":-8.468862,"38":-6.283846,"7":-4.1859508,"6":2.258199,"39":-5.7517385,"40":-2.185653,"25":-6.7867517,"43":-5.6956687,"23":-6.5497603,"27":-6.7101393,"18":-5.6598907,"42":-8.010338,"22":-4.293061,"47":-8.410631,"29":-6.0448213,"30":-3.120536,"33":-6.678425,"36":-2.2476978,"9":-3.785134,"20":-6.76407,"21":-0.9748436,"24":-5.464995,"37":-8.838186,"45":-10.511658,"48":-6.5637045,"49":-8.30405,"26":-1.5648729,"19":-6.8301277,"5":-6.780622},{"13":-5.9658566,"36":-6.69612,"44":-4.1755066,"48":-7.27846,"23":-2.788705,"24":-6.7490454,"6":0.7435967,"31":-8.528669,"12":-3.128417,"25":0.61172104,"41":-11.587574,"42":-5.197815,"21":-6.3767424,"26":-2.4461863,"43":-4.697268,"3":-2.897878,"2":-6.833159,"22":-0.6180641,"7":-3.0073612,"28":-4.701036,"39":-2.9259324,"47":-8.755625,"16":-1.4458896,"19":-4.337165,"30":-1.7970674,"46":-6.1650977,"11":-4.2741604,"9":-2.1782713,"14":-5.538568,"10":-0.8069183,"1":-2.7422295,"29":-7.9439964,"33":-4.7999787,"38":-8.359238,"32":-6.887723,"5":-1.2106429,"18":-4.7042913,"37":-5.0650835,"20":-7.0292463,"40":-6.260282,"0":2.7349591,"4":-5.354793,"45":-8.416021,"15":-4.5302224,"49":-6.64499,"27":-7.81325,"8":-3.964267,"34":-11.757513,"17":-7.3064184,"35":-5.698499},{"33":-2.8844151,"0":-5.29483,"12":-3.0051084,"45":-7.5685225,"3":-6.2350845,"14":-6.2239347,"6":-2.8760228,"37":-8.236056,"47":-5.9538865,"5":0.445618,"27":-6.2923574,"1":0.9577133,"8":-7.049463,"9":-2.4823775,"15":-4.167392,"16":-5.8828115,"25":-6.781297,"36":-0.42601576,"41":-5.7433476,"46":-5.007496,"21":-7.180176,"48":-6.344386,"32":-7.031269,"22":-3.5685406,"20":-4.583305,"43":-4.1296554,"2":-4.326234,"26":-6.328514,"44":-10.1752825,"34":-6.8098245,"49":-7.051538,"30":-3.7744975,"31":-2.5379722,"13":-2.7742624,"17":-7.1861777,"24":-4.7720366,"4":-1.2100899,"11":-3.746964,"19":-2.7870185,"28":-8.136251,"10":-4.802005,"35":-7.066821,"38":-7.573728,"23":-6.519259,"42":-5.2036214,"7":-0.7041553,"39":-5.050607,"29":-4.8870606,"18":-15.415439,"40":-5.1404343},{"15":-8.91263,"1":0.21375242,"3":0.49288803,"43":-6.556487,"18":-4.627845,"32":-6.410241,"30":-3.4534926,"40":-9.319895,"23":-5.557311,"44":-7.9583273,"21":-5.3575063,"29":-1.4556402,"31":-8.192297,"49":-2.491073,"35":-3.2211766,"26":-5.3741684,"27":-9.978143,"19":-6.541009,"25":-3.8241258,"8":-4.257202,"38":-5.017821,"39":-6.1002455,"41":-5.269944,"28":-6.237544,"42":0.06850467,"46":-7.923688,"0":-0.084578946,"7":-3.8082633,"4":-4.165742,"14":-6.744937,"17":1.7393742,"5":-3.3279145,"24":-4.778824,"45":-7.3343086,"47":-3.5562463,"48":-7.8751755,"10":-4.039015,"16":-3.9520848,"12":-1.9155678,"13":-4.6556044,"11":-1.266288,"22":-0.11874382,"34":-6.661275,"37":-4.6740875,"9":-3.9724884,"36":-7.0592093,"20":-2.2873082,"2":-8.036842,"6":-0.9284045,"33":-5.0256705},{"16":-5.7060385,"17":-6.00199,"29":-9.993887,"13":-3.0642574,"30":-4.9186707,"4":-0.08997903,"31":-4.3743963,"26":-12.245878,"10":-8.57184,"7":-0.2084132,"34":-6.9803047,"35":-6.4062715,"44":-1.9192054,"39":-5.011348,"49":2.6927643,"9":-0.8585614,"12":-5.466088,"43":-10.287722,"22":-5.9940467,"8":-4.290485,"19":-3.3977306,"1":-0.95906943,"38":-7.672231,"32":-1.7153251,"23":-7.570333,"40":-7.549639,"18":0.73267543,"3":0.6381788,"6":-3.5349364,"20":-4.618871,"36":-12.99025,"11":-7.5268354,"15":-4.7133417,"21":-3.5082684,"5":-1.8957971,"37":-5.7457724,"33":-3.6420357,"41":-10.183071,"14":-3.272628,"25":-9.749201,"42":-0.19926815,"48":-4.6013193,"2":0.1687006,"27":-3.8905106,"28":-4.321185,"45":-6.652732,"46":-5.34919,"47":-4.9052267,"0":-3.8688831,"24":-2.6291459},{"37":-7.2748322,"6":-0.31089884,"23":-6.879588,"25":-7.101007,"32":-7.4528136,"43":-6.1593175,"35":-4.6540337,"41":-2.1827996,"18":-3.2506332,"20":-6.901155,"26":-8.36724,"10":3.0738454,"31":-5.2871537,"48":-12.69541,"34":-7.05717,"36":-2.7232203,"3":0.2693832,"2":-4.658323,"9":-5.308313,"47":-7.8141937,"15":-2.3651512,"49":-1.7504419,"11":-0.40183085,"13":-4.140607,"12":-5.6743426,"24":-6.4019914,"42":-10.019555,"45":-8.312067,"19":-2.8960106,"7":-4.1169744,"21":-5.5204325,"28":-4.099666,"1":-0.2089366,"38":-4.4818516,"0":-5.8983216,"39":-6.707189,"8":-0.7153311,"5":-3.0025651,"16":-5.835742,"30":-5.5074663,"22":-6.1405478,"33":-1.0401447,"4":1.815493,"44":-8.676687,"46":-4.735227,"27":-6.675126,"14":-6.8651285,"17":-0.71239364,"29":0.46844274,"40":-7.5029707},{"33":-5.9125648,"8":0.14899102,"37":0.38451424,"34":-7.3850946,"38":-4.199651,"6":-6.153229,"42":-8.322774,"43":-7.804656,"39":-6.839391,"28":-5.1167727,"44":-8.080609,"29":-6.9762588,"2":-4.595597,"47":-6.9096756,"31":-5.0568776,"14":-3.8473141,"18":-5.3494596,"5":-2.057659,"25":-2.453268,"15":-5.9120173,"12":-2.942243,"36":-10.233141,"19":-1.7612708,"3":0.3510024,"41":-5.2399035,"46":-7.0233965,"49":-9.244016,"48":-8.312361,"40":-3.9163368,"10":-6.2418394,"23":-2.8777976,"45":-4.2259398,"27":-6.2297773,"1":1.0182073,"7":-3.4470134,"4":-3.8573196,"0":-3.9372115,"17":-5.191558,"9":-5.7371383,"26":-3.0888908,"11":-4.635509,"30":-3.2773666,"16":-5.304137,"32":-8.943511,"22":-4.2112503,"35":-9.10764,"20":-1.55245,"21":-5.92438,"24":-8.879614,"13":-3.61291},{"9":-7.4169035,"17":1.3541992,"1":-5.735981,"4":-0.48756522,"2":-1.4803843,"18":-2.3469899,"28":-5.098632,"47":-3.6011894,"48":-3.8222148,"34":-7.241843,"16":-8.341502,"41":-8.041476,"23":1.0413781,"32":-2.9410741,"26":-0.1209062,"30":-5.6943645,"3":-0.5713614,"45":-7.103102,"5":-3.883152,"22":-4.065497,"33":-9.958631,"43":-5.6515026,"10":-7.9155474,"21":-5.0712223,"31":-3.8318057,"27":-7.7358246,"0":-0.15761042,"14":-6.9304824,"7":-6.235607,"36":-5.8479605,"19":-2.0947754,"8":-5.8903217,"15":-2.7406294,"38":-2.6919823,"11":-8.327294,"40":-8.012971,"24":-7.224681,"42":-6.7071037,"46":-2.4638524,"13":-3.7110162,"12":-4.488091,"44":-4.25462,"49":-4.6317053,"6":-2.0671265,"39":-13.899658,"37":-5.7786827,"35":-3.579789,"20":-1.8500891,"29":-3.5330734,"25":-7.45224},{"22":-4.7515526,"21":-3.1519268,"1":-3.0484269,"37":-7.3985634,"5":0.25969338,"16":0.095714174,"28":-2.7611527,"47":-7.3844004,"26":-8.852846,"32":-4.988953,"48":-11.587687,"49":-5.6892476,"35":-7.930368,"40":-9.40737,"2":-0.84102315,"24":4.085591,"43":-3.3806412,"4":-2.5573273,"39":-5.6632476,"3":-0.14983721,"12":-8.579244,"41":-15.314466,"42":-6.535417,"34":-9.491703,"44":-3.834795,"11":-6.328217,"6":-3.179299,"31":-11.512368,"30":-6.9321136,"45":-8.699331,"0":2.6831613,"13":-4.5044584,"20":-4.9255114,"27":-10.761828,"8":-1.9548645,"10":-10.137311,"9":-5.4584427,"19":-5.5328,"33":-6.4684677,"14":-2.0774312,"36":-3.4614434,"46":-5.018688,"29":-4.7117186,"25":-7.821883,"18":-4.051301,"15":-1.6228491,"23":-1.9468492,"7":-6.561249,"38":-4.413884,"17":-9.781391},{"3":-0.77211833,"40":-6.209918,"19":-3.5400372,"43":-9.216475,"7":-5.710351,"27":-7.268997,"25":-8.909723,"49":-3.6221306,"21":-6.638794,"23":-2.8979821,"1":-2.7847931,"6":-3.2586129,"13":-3.2376664,"22":-4.4467936,"18":-3.5210862,"28":-4.4665866,"9":-7.7249703,"30":-3.6593947,"32":-4.5404425,"16":-6.028268,"26":-2.0425534,"12":-7.073365,"34":-10.693955,"37":-10.274292,"42":-0.8407051,"24":-6.834813,"10":-4.782112,"47":-8.874941,"35":-6.6097536,"14":-2.5304096,"44":-4.281205,"17":-6.99986,"8":-0.87549794,"33":-7.5529037,"36":-6.5181284,"29":-8.490213,"45":-8.281681,"46":-3.2461705,"11":-6.6051865,"20":-3.9592204,"4":0.13969398,"0":-1.1905669,"2":0.1986072,"41":-3.5668838,"39":-7.3067665,"48":-7.1473937,"38":-7.6338105,"15":0.7119096,"31":-6.6137266,"5":0.055739008},{"32":-6.3817797,"34":-6.263388,"5":-5.4151177,"17":-7.683957,"44":-9.784686,"7":-2.7387128,"43":-6.668387,"37":-9.53776,"23":-8.877035,"19":-0.9805794,"42":-4.269587,"20":-2.563757,"3":-1.6871903,"45":-7.2974615,"10":-3.2716262,"48":-12.854228,"22":-4.3801355,"15":-4.2943907,"24":-7.2439013,"14":-5.44735,"38":-1.9346743,"13":-1.7601013,"21":-6.8980308,"39":-4.576972,"41":-8.118965,"4":-2.6488667,"31":-6.1606555,"12":-4.790626,"29":-9.496099,"25":-14.426544,"35":-3.1628685,"40":-3.1711364,"1":0.177584,"6":-1.8051761,"27":-7.1199737,"30":-5.4633884,"46":-12.516011,"49":-6.179161,"26":-9.616083,"2":0.8546661,"11":-4.01641,"18":-3.4379616,"16":1.071059,"36":-6.679106,"8":-5.407766,"0":-1.1821814,"9":-0.188424,"28":-3.1310668,"33":-6.7162066,"47":-2.7651396},{"40":-6.3169127,"31":-6.788479,"2":0.11499499,"35":-8.516233,"12":-6.0810227,"3":-2.1721585,"4":2.2971516,"6":-0.9608844,"14":-6.8593993,"39":-5.013154,"44":-5.6186895,"45":-3.0345654,"41":-5.0983863,"22":-4.5058136,"10":-3.3421524,"21":-2.1000023,"5":-1.8317201,"9":-5.251985,"13":-3.929404,"7":-3.594026,"20":-6.4318247,"16":-8.4494505,"28":-11.063459,"15":-2.9159875,"30":-7.4929,"32":-4.423497,"43":-15.614697,"25":-9.994823,"34":-6.2063565,"47":-5.8860264,"48":-2.2612045,"38":-2.8748927,"27":-9.8296585,"36":-6.7027864,"18":-1.9196243,"23":-5.6416984,"46":-8.678217,"29":-1.4928672,"37":-0.7193918,"1":0.70679003,"42":-5.7712545,"24":-4.043242,"8":-0.20630619,"26":1.2309443,"33":-4.078003,"0":-9.763197,"11":0.74552023,"17":1.6316736,"19":-2.982643,"49":-4.6068354},{"47":-5.7185183,"44":-6.57044,"19":-3.6985493,"48":-8.407866,"40":-6.994649,"25":-7.776094,"27":-8.951444,"22":-2.3760169,"4":0.92195046,"42":0.056731403,"2":1.9285071,"43":-8.157738,"6":-1.4142399,"7":-4.5393295,"46":-0.41131386,"5":-1.0734369,"14":-9.053869,"23":-6.9136496,"11":-3.4859402,"13":-1.2687516,"30":-8.026538,"39":-7.9194603,"32":-6.7739296,"17":-3.972007,"28":-8.560347,"26":-7.5500464,"37":-8.96202,"1":3.5935051,"20":0.85310334,"15":-4.5362573,"29":-5.6153116,"35":-3.5876281,"36":-6.90512,"16":-2.7031672,"38":-6.561864,"41":-7.4606094,"34":-7.788598,"21":-4.4619265,"31":-8.414707,"49":-0.711028,"3":-2.6411507,"8":-2.5088086,"0":1.2982078,"9":-5.219794,"18":-3.7880325,"45":-11.461676,"10":-7.058548,"24":-5.789211,"12":-0.19020978,"33":-5.491061},{"15":-3.9012065,"22":-3.449311,"12":-4.8677597,"36":-17.441645,"1":1.7412128,"23":-8.145149,"0":-1.1199046,"25":-11.89994,"39":-6.626439,"21":-4.9122534,"45":-1.5332215,"8":-2.5583434,"26":-2.8571725,"14":-3.7259727,"37":-4.853111,"41":-11.401272,"7":-4.843131,"32":-9.396578,"38":-1.0024046,"20":-4.2007637,"29":-5.2518992,"19":-2.8491623,"44":-2.795422,"9":-0.9532218,"10":-0.016619802,"13":-1.109414,"48":-13.301729,"42":-6.7707214,"11":-3.798603,"43":-3.0779607,"17":-9.384002,"31":-4.7559156,"46":-6.406909,"49":-4.5331526,"6":-1.6094253,"4":-6.000406,"3":0.78815204,"30":-2.39151,"34":-2.7504447,"33":-8.972921,"28":-9.452806,"40":-6.2907686,"16":-1.9519722,"18":-1.2361414,"24":-3.1618586,"47":-1.93146,"35":-2.5651517,"27":-11.535366,"2":-2.827455,"5":-0.89086163},{"28":-6.168729,"23":-5.091327,"36":-7.896076,"4":-0.6808048,"43":-4.0872064,"15":-5.060832,"35":-7.8679466,"16":2.3580716,"11":-6.402948,"22":-0.21221161,"2":-0.45511517,"5":-7.220313,"14":-1.7785864,"21":-5.768071,"12":-5.537334,"18":-2.9126792,"37":-3.1373315,"39":-7.484565,"1":0.83852994,"26":-6.7451324,"19":-5.0899377,"9":-1.2609146,"13":1.1598383,"42":-1.7430694,"44":-7.794197,"41":-5.3883505,"47":-5.5544233,"48":-9.115694,"30":-5.866732,"10":-5.222727,"17":-7.704146,"40":-7.9548197,"29":-2.7511039,"8":-1.2438145,"20":-3.4886322,"25":-2.9154382,"33":-5.1570845,"24":-1.6546043,"27":-6.740902,"0":-0.38604698,"6":-0.3249498,"7":0.011517334,"31":-9.004103,"34":-5.4399896,"38":-1.5282686,"3":-0.023884201,"45":-7.5850234,"32":-10.357863,"46":-9.2418375,"49":-3.7870064},{"35":-4.0093746,"37":-1.2684741,"14":-0.6104725,"0":0.69505835,"6":-1.7735542,"34":-4.3040996,"27":-7.9827676,"25":-8.473538,"41":-6.092523,"18":-4.8728914,"3":-1.9708881,"39":0.16423199,"8":0.1419992,"24":-9.447664,"22":-2.5927353,"29":-5.897491,"19":-2.1336436,"16":-3.8984675,"4":-2.0244925,"1":-3.7751756,"21":-4.7222166,"28":-5.464208,"31":-8.301497,"2":1.7633228,"9":-0.016923595,"32":-11.56977,"11":2.1795933,"36":-5.280381,"15":-1.1858556,"40":0.44014746,"7":0.61527395,"42":-7.37359,"44":-0.9581,"47":-0.77596426,"12":-5.7259784,"20":-2.3802009,"13":-5.6673765,"43":-6.2162867,"45":-5.83232,"48":-0.96138537,"46":-7.8550553,"30":-1.3662884,"49":-4.963779,"26":-6.8248696,"38":-4.165621,"17":-0.33950955,"5":-3.2327838,"10":-2.0035245,"33":-7.269362,"23":-5.149654},{"15":-5.8778105,"31":-7.4263864,"22":-3.0823693,"12":-1.0283643,"3":1.2516215,"24":-4.5884557,"32":-9.882088,"21":-4.9306097,"36":-2.0598521,"41":-6.314498,"49":-6.514525,"16":-0.2281086,"29":-3.9958122,"44":0.04656654,"46":-4.6025896,"6":-1.5386164,"4":3.721232,"48":-2.793364,"39":-3.4709504,"43":-19.224146,"19":-1.3619962,"27":-8.529882,"9":-3.5389438,"5":1.3677812,"23":-1.4420968,"0":1.6338917,"20":-2.5260792,"8":-3.8938897,"34":-6.3907065,"28":-2.9147327,"35":-3.1052494,"2":-1.0358795,"17":-0.9774192,"18":0.70798683,"37":-6.218275,"33":-8.12211,"11":-2.79563,"1":0.8589767,"10":3.093267,"13":-2.6534617,"7":0.18166378,"42":-1.0995693,"14":-2.525447,"45":-8.643217,"30":-14.979738,"47":-8.130171,"26":-2.81726,"38":-4.3409705,"40":-2.6157117,"25":-6.353838},{"44":-8.50006,"6":-2.0926635,"43":-6.241271,"9":-0.8451432,"7":-1.6807859,"12":0.50125086,"18":-1.5310643,"29":-3.816148,"41":-4.897126,"45":-6.16201,"16":-0.020999575,"32":-4.2065463,"8":-6.858557,"46":-7.230836,"2":3.0744202,"19":-1.6173872,"47":-8.711152,"48":0.5274326,"25":-6.9953904,"37":1.6969225,"49":-0.6539704,"14":-1.7808943,"4":1.4781072,"33":4.1129317,"10":-3.3978896,"36":-6.433152,"26":-4.2478914,"22":-3.3536906,"5":-0.050629735,"24":-2.4221032,"39":-2.843961,"38":-10.483934,"11":-2.0277932,"15":-2.5512938,"0":2.7106996,"17":-4.07591,"13":-4.117734,"3":-0.9371809,"20":-0.49810869,"42":3.0832992,"21":-0.86282796,"1":-3.4048321,"28":-8.772857,"30":-3.2485013,"27":0.45775598,"31":-6.5474176,"34":-7.441789,"35":-4.0919113,"40":-5.383832,"23":-4.906007},{"23":2.2807653,"18":-1.9577965,"43":-8.174139,"44":-7.386832,"28":-4.351672,"39":-7.900308,"0":0.5285856,"10":0.37729222,"31":-0.19897583,"33":-4.6669607,"27":-2.6046462,"20":-1.2066267,"14":-1.1362668,"11":1.0077413,"41":-0.478535,"6":-0.4414224,"45":-8.069284,"1":-9.255552,"5":1.3562931,"22":-1.4410591,"25":-3.4098713,"37":-5.671054,"16":-2.4818156,"40":-4.346452,"30":0.6393293,"17":-1.4314704,"35":1.8644912,"4":2.1321304,"7":-0.096214294,"34":-5.767625,"36":-5.2387476,"49":-6.179792,"32":-9.304794,"46":-7.2231016,"47":-5.767058,"8":-1.7443111,"26":-8.583871,"24":-0.46072045,"2":-4.3851767,"21":0.3429398,"48":-9.851889,"29":1.0060906,"42":-5.6073976,"19":-0.9780343,"12":-2.981559,"15":3.8326268,"38":-6.608889,"9":0.299455,"13":-1.1476824,"3":2.5979218},{"19":-0.19572818,"5":0.43866682,"17":-2.1176047,"24":2.0992517,"35":-9.157915,"40":-10.009754,"41":-11.370805,"16":-10.49123,"30":-10.726629,"38":-4.4614334,"43":-1.5596721,"25":-0.35049,"29":-8.028803,"49":-5.2757196,"15":-1.3581061,"1":3.8374572,"21":-0.5448636,"28":-8.341843,"13":2.959203,"34":-7.3304453,"20":-1.969656,"4":-1.0717344,"47":-6.8036637,"48":-0.35139427,"2":-0.6767151,"8":-2.959065,"7":-7.9132996,"6":0.13814719,"11":-2.8848605,"22":-2.424499,"3":-0.5579618,"23":-4.5508432,"27":-0.4200186,"31":-0.25811654,"36":0.88451403,"42":-5.2860374,"44":-3.2424133,"45":-8.194428,"0":1.2812722,"46":-3.5587463,"26":-9.993482,"12":0.2769742,"33":-2.4051697,"14":-0.9771984,"18":-1.098754,"10":0.6583066,"9":-4.469278,"32":-8.635898,"37":-5.788775,"39":-6.285392},{"37":-5.6931424,"45":-4.403555,"15":-3.099538,"39":-0.75549346,"13":2.159759,"8":-1.9632168,"21":-1.3247403,"23":-1.3566242,"36":-3.1095176,"46":-6.386506,"32":-9.831261,"7":-1.5145661,"2":5.472353,"5":1.9507589,"22":-0.053236198,"40":-7.7109632,"30":0.065202594,"6":1.5998818,"17":-0.4691194,"1":-3.1568475,"34":-1.6445547,"27":-7.7280273,"49":-5.792971,"38":-1.2645669,"31":-3.0119731,"0":0.3504137,"14":-5.790829,"26":-4.0739875,"18":0.65096486,"24":-8.489643,"3":0.9707786,"4":0.978186,"48":-3.417251,"12":0.32214004,"20":0.5484868,"47":-3.042462,"9":-1.8674599,"16":-0.42284623,"29":-6.055676,"41":-5.4693413,"25":-9.707097,"28":-1.635248,"33":-1.3564689,"35":-11.695998,"43":-10.47551,"10":-8.123158,"19":-1.3940105,"44":-8.574059,"42":-4.169252,"11":-0.8294384},{"20":-0.16003199,"11":-1.9874451,"40":-10.189647,"41":-3.565167,"22":-0.82479924,"7":0.52353704,"32":-5.0426292,"43":-7.950408,"45":-10.119469,"35":-2.0043066,"47":-4.7557993,"49":-7.0734396,"19":-0.30181038,"13":-1.2902004,"46":-6.726818,"37":-5.9003277,"2":-0.6885988,"5":0.18776599,"36":-6.162833,"34":-6.932719,"31":-0.84623444,"39":-6.634928,"42":-5.322622,"48":-0.59931934,"3":1.7159679,"21":0.25509998,"15":0.9164914,"44":-8.222937,"1":1.8034108,"27":-7.7412057,"17":-3.1398792,"18":-0.525844,"29":-7.2635217,"6":0.54169834,"9":-2.429277,"25":-2.3472564,"38":-1.3054307,"14":-2.864987,"4":-3.8559635,"12":-1.8887336,"33":-6.1447196,"10":-3.1041374,"16":-5.70136,"0":3.6604676,"8":2.5261884,"23":-5.0369844,"28":-5.528515,"26":-6.893248,"24":-0.46608344,"30":-3.9200988},{"0":2.799172,"3":0.7874632,"14":-1.7181822,"24":-3.4304478,"35":-6.7193117,"13":0.41537824,"5":2.7171228,"37":-5.027726,"43":-5.43523,"4":-0.09500298,"46":-8.622667,"48":-7.9176216,"30":-4.2853765,"6":0.0148634,"18":-4.461198,"31":-8.836862,"7":0.3118286,"28":-8.084752,"17":-1.2637011,"34":-4.806041,"36":-5.812302,"38":-4.879042,"25":4.325031,"2":-0.20646039,"29":-4.230074,"39":-5.7430944,"23":-0.4728716,"41":-5.4831743,"40":-4.317229,"45":1.2612218,"49":-7.160132,"26":-1.6063076,"16":-2.7028515,"1":2.9316213,"8":0.36154866,"47":-7.843116,"11":-0.6635078,"33":-2.0000467,"44":-1.480444,"20":-0.8691734,"32":-6.462825,"9":-1.0986978,"15":-3.1273415,"12":-4.778491,"21":-3.4896972,"42":-6.302517,"10":-0.07799481,"22":2.573429,"19":1.4512104,"27":-7.1704316},{"29":-6.2978754,"22":1.962752,"47":-7.610662,"2":-0.04739348,"4":-1.0464453,"34":-2.2990997,"44":-7.0319357,"12":-1.4976182,"48":-4.687726,"13":0.694455,"19":-1.9763286,"5":-1.454668,"20":-0.064576246,"36":-0.31257114,"1":3.1782584,"42":-0.40251812,"0":0.46174207,"37":-6.867858,"21":-5.765976,"27":-1.6544834,"28":-0.519924,"33":-7.456163,"6":-8.124794,"10":-1.316211,"38":-9.13648,"43":-1.5818509,"46":-4.074041,"9":2.019923,"18":-0.10317061,"40":1.1408619,"45":-6.3800116,"23":1.9282601,"15":2.204761,"41":-2.7825036,"14":-0.92068326,"32":-5.854055,"16":-3.6365674,"26":-3.366635,"3":1.507069,"30":-0.49880537,"25":-3.7281852,"35":-5.303521,"8":-4.913729,"17":0.055183005,"11":0.1572492,"49":-7.636659,"7":1.6844584,"24":3.5923011,"31":-4.1475525,"39":-6.168802},{"17":-0.9454006,"48":-4.6368685,"13":-1.3639282,"5":-4.1024647,"12":-1.2815914,"10":-0.95624274,"7":0.11633961,"23":0.31117058,"18":-4.709592,"22":-0.5083992,"25":-7.252188,"26":-3.5911686,"24":-0.7936962,"31":-1.8184278,"42":-6.4817,"28":-2.8908885,"30":-13.66734,"4":-1.0620741,"29":-7.0017653,"44":-10.846217,"11":0.24425921,"35":-7.5436096,"16":-2.0619254,"20":1.7944053,"15":-0.264994,"38":-0.23960027,"33":-6.2392874,"39":-6.2364187,"14":-5.3246827,"21":-1.2647974,"34":-3.8605335,"37":-0.5008541,"49":0.6760356,"45":-7.144435,"0":-8.659049,"32":-7.71543,"2":-1.4090497,"6":0.37938777,"43":-5.59842,"19":-1.180762,"47":-8.232537,"41":-5.0816107,"3":-0.072279006,"46":-5.0089836,"1":-2.5223753,"8":1.2994208,"40":-9.043745,"27":-2.782248,"9":0.49734837,"36":-2.8629117},{"10":-1.1431856,"19":-0.00888381,"21":-0.9491043,"28":-5.6273203,"5":-8.733055,"43":-4.9717584,"4":1.3330718,"3":-1.8273941,"9":-5.1436996,"2":1.0189362,"12":-0.50710356,"32":-2.2215023,"7":-2.288145,"26":-4.1429253,"30":-1.6915871,"35":-8.091185,"34":-5.6282473,"38":-3.1990018,"41":-7.9956346,"22":-3.1670842,"15":-1.9314263,"17":-0.918849,"45":-6.80713,"47":-0.2870374,"1":1.18836,"23":-3.831369,"14":-0.6701314,"13":-1.408299,"18":2.3839207,"42":-4.5490522,"48":-5.086974,"39":-4.9896884,"6":0.5895823,"44":-0.9529087,"20":-0.91898996,"24":4.123849,"31":-7.2199583,"16":1.8607514,"40":-1.249728,"33":-6.3128176,"8":1.8995619,"11":-1.2937064,"49":-7.038812,"36":-1.8019352,"0":1.9573166,"27":-10.852641,"29":-5.5810843,"46":-4.0519304,"37":-7.403051,"25":-3.7937102},{"27":-5.495222,"4":0.6431998,"18":-2.87314,"7":-3.9599311,"49":-0.1454852,"34":-6.70123,"42":-1.1761116,"39":-5.4738607,"23":-0.20574598,"1":0.42282137,"35":-2.1898088,"12":-1.7741026,"38":2.4154522,"2":0.31656614,"19":-0.423431,"29":-3.7359993,"46":-2.1432633,"26":-8.270744,"47":-6.977235,"48":-5.900238,"41":-1.0820382,"25":-8.489737,"20":-2.6018925,"44":-6.538512,"15":-4.125592,"16":-4.223521,"30":1.3424636,"10":-0.5507144,"9":-0.25193802,"17":-1.9685625,"31":-5.058756,"36":-2.2006512,"33":-6.9902534,"0":3.968801,"37":-8.3227625,"14":5.2644496,"11":0.028243404,"3":1.0192555,"5":-0.593826,"8":-0.903951,"40":-1.7970212,"43":-5.4003954,"45":-0.7839618,"13":-2.4410012,"24":-4.9908996,"32":0.188975,"6":-0.4564694,"21":-8.233892,"22":-6.377118,"28":-8.211889},{}],"nn_shapes":{"30":[18,12,8],"45":[18,11,11,6,21,23,14,26,31,18,8],"49":[18,31,14,31,25,16,6,8],"46":[18,27,33,8],"0":[18,10,33,4,4,25,21,8,5,8],"17":[18,16,22,25,19,5,8],"41":[18,7,8],"34":[18,16,23,27,15,34,28,26,6,8],"2":[18,9,15,11,20,27,23,16,13,10,8],"21":[18,20,26,11,13,8],"37":[18,32,14,30,28,13,23,8],"40":[18,14,14,5,8,9,11,30,17,10,8],"47":[18,7,26,19,18,17,33,8],"9":[18,4,7,7,18,8],"12":[18,12,19,20,7,18,8],"16":[18,22,30,31,33,10,26,8],"3":[18,34,24,20,31,3,26,21,21,8],"38":[18,22,17,8],"20":[18,6,7,18,8],"25":[18,14,25,16,20,22,28,10,8],"22":[18,21,25,14,6,21,15,3,23,8],"15":[18,14,33,27,11,8],"18":[18,10,7,16,20,3,18,8],"1":[18,30,8],"35":[18,22,22,6,15,7,6,8],"44":[18,19,32,4,6,8],"28":[18,29,5,27,14,9,3,5,26,8,8],"36":[18,13,8],"42":[18,20,23,32,20,16,3,9,28,12,8],"11":[18,18,26,22,32,7,31,6,17,26,8],"8":[18,21,24,14,9,30,14,33,8],"7":[18,30,31,9,12,14,6,30,8],"19":[18,5,29,19,27,7,32,8],"27":[18,5,5,26,32,16,7,17,20,8],"31":[18,17,33,30,9,7,20,26,8],"24":[18,9,8],"32":[18,15,25,5,34,14,11,14,19,8],"5":[18,9,13,20,26,31,19,8],"13":[18,29,22,31,20,29,6,8],"6":[18,16,7,28,13,27,18,8,33,15,8],"14":[18,19,8],"23":[18,11,25,11,9,32,8],"26":[18,18,24,19,24,8],"29":[18,10,26,24,4,27,27,22,5,4,8],"33":[18,6,11,27,12,8],"39":[18,17,16,4,34,22,8],"43":[18,9,11,8],"4":[18,10,23,12,33,22,8],"48":[18,31,28,8,14,17,17,8],"10":[18,4,26,34,29,8,11,31,7,28,8]},"crossbreed_segments":19,"weight_initialization_range":{"start":-0.57954955,"end":1.4462243},"minor_mutation_rate":0.78990555,"major_mutation_rate":0.9755472,"mutation_weight_range":{"start":-0.52652776,"end":0.52652776}},"state":"Simulate","generation":32,"max_generations":45,"id":"4927e1b5-284e-483d-8034-f3215653c381"},"left":null,"right":null}},{"generations_per_height":5,"overwrite":false},[50,1]] \ No newline at end of file From 7156d6d733ad553a0a0ba1c7537cd1ea7ed81c35 Mon Sep 17 00:00:00 2001 From: vandomej Date: Fri, 5 Sep 2025 09:24:53 -0700 Subject: [PATCH 26/26] Additional visualization for round 4 --- .gitignore | 6 +- analyze_data.py | 73 ++-- extract_fann_data/Cargo.toml | 9 + extract_fann_data/build.rs | 11 + extract_fann_data/src/main.rs | 38 ++ gemla/src/bin/fighter_nn/mod.rs | 202 +++++----- .../bin/fighter_nn/neural_network_utility.rs | 6 +- gemla/src/core/genetic_node.rs | 3 + parameter_analysis.py | 380 ++++++++++++++++++ visualize_networks.py | 118 ++++++ visualize_simulation_tree.py | 4 +- 11 files changed, 721 insertions(+), 129 deletions(-) create mode 100644 extract_fann_data/Cargo.toml create mode 100644 extract_fann_data/build.rs create mode 100644 extract_fann_data/src/main.rs create mode 100644 parameter_analysis.py create mode 100644 visualize_networks.py diff --git a/.gitignore b/.gitignore index 3014a3b..68359b1 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,8 @@ settings.json .DS_Store -.vscode/alive \ No newline at end of file +.vscode/alive + +# Added by cargo + +/target diff --git a/analyze_data.py b/analyze_data.py index 703f23d..2eba59d 100644 --- a/analyze_data.py +++ b/analyze_data.py @@ -5,10 +5,10 @@ from collections import defaultdict import numpy as np # Simplified JSON data for demonstration -with open('gemla/round2.json', 'r') as file: +with open('gemla/round4.json', 'r') as file: simplified_json_data = json.load(file) -target_node_id = '0c1e64dc-6ddf-4dbb-bf6e-e8218b925194' +target_node_id = '523f8250-3101-4586-90a1-127ffa6d73d9' # Function to traverse the tree to find a node id def traverse_left_nodes(node): @@ -67,7 +67,7 @@ fig, ax = plt.subplots(figsize=(10, 6)) boxplots = ax.boxplot(scores_values, vert=False, patch_artist=True, labels=[f'Set {i+1}' for i in range(len(scores_values))]) # Set figure name to node id -# fig.canvas.set_window_title('Main node line') +ax.set_xscale('symlog', linthresh=1.0) # Labeling ax.set_xlabel(f'Scores - Main Line') @@ -79,33 +79,35 @@ ax.set_yticklabels([f'Set {i+1}' for i in range(len(scores_values))]) # Getting most recent right graph right_nodes = traverse_right_nodes(simplified_json_data[0]) -target_node_id = None -target_node = None -if target_node_id: - for node in right_nodes: - if node["val"]["id"] == target_node_id: - target_node = node - break -else: - target_node = right_nodes[1] -scores = target_node["val"]["node"]["scores"] +if len(right_nodes) != 0: + target_node_id = None + target_node = None + if target_node_id: + for node in right_nodes: + if node["val"]["id"] == target_node_id: + target_node = node + break + else: + target_node = right_nodes[0] + scores = target_node["val"]["node"]["scores"] -scores_values = [list(score_set.values()) for score_set in scores] + scores_values = [list(score_set.values()) for score_set in scores] -# Set up the figure for plotting on the same graph -fig, ax = plt.subplots(figsize=(10, 6)) + # Set up the figure for plotting on the same graph + fig, ax = plt.subplots(figsize=(10, 6)) -# Generate a boxplot for each set of scores on the same graph -boxplots = ax.boxplot(scores_values, vert=False, patch_artist=True, labels=[f'Set {i+1}' for i in range(len(scores_values))]) + # Generate a boxplot for each set of scores on the same graph + boxplots = ax.boxplot(scores_values, vert=False, patch_artist=True, labels=[f'Set {i+1}' for i in range(len(scores_values))]) + ax.set_xscale('symlog', linthresh=1.0) -# Labeling -ax.set_xlabel(f'Scores: {target_node['val']['id']}') -ax.set_ylabel('Score Sets') -ax.yaxis.grid(True) # Add horizontal grid lines for clarity + # Labeling + ax.set_xlabel(f'Scores: {target_node['val']['id']}') + ax.set_ylabel('Score Sets') + ax.yaxis.grid(True) # Add horizontal grid lines for clarity -# Set y-axis labels to be visible -ax.set_yticklabels([f'Set {i+1}' for i in range(len(scores_values))]) + # Set y-axis labels to be visible + ax.set_yticklabels([f'Set {i+1}' for i in range(len(scores_values))]) # Find the highest scoring sets combining all scores and generations scores = [] @@ -121,15 +123,16 @@ for node in left_nodes: scores.append(translated_node_scores) # Add scores from the right nodes -for node in right_nodes: - if node["val"]["node"]: - node_scores = node["val"]["node"]["scores"] - translated_node_scores = [] - if node_scores: - for i in range(len(node_scores)): - for (individual, score) in node_scores[i].items(): - translated_node_scores.append((node["val"]["id"], i, score)) - scores.append(translated_node_scores) +if len(right_nodes) != 0: + for node in right_nodes: + if node["val"]["node"]: + node_scores = node["val"]["node"]["scores"] + translated_node_scores = [] + if node_scores: + for i in range(len(node_scores)): + for (individual, score) in node_scores[i].items(): + translated_node_scores.append((node["val"]["id"], i, score)) + scores.append(translated_node_scores) # Organize scores by individual and then by generation individual_generation_scores = defaultdict(lambda: defaultdict(list)) @@ -155,9 +158,13 @@ labels = [f'{id[:8]}... Gen {gen}' for id, gen in top_20_individual_generations] # Generate box and whisker plots for the top 20 individual generations fig, ax = plt.subplots(figsize=(12, 10)) ax.boxplot(top_20_scores, vert=False, patch_artist=True, labels=labels) + +ax.set_xscale('symlog', linthresh=1.0) + ax.set_xlabel('Scores') ax.set_ylabel('Individual Generation') ax.set_title('Top 20 Individual Generations by Q3 Value') +ax.yaxis.grid(True) # Add horizontal grid lines for clarity # Display the plot plt.show() diff --git a/extract_fann_data/Cargo.toml b/extract_fann_data/Cargo.toml new file mode 100644 index 0000000..c324aaf --- /dev/null +++ b/extract_fann_data/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "extract_fann_data" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +fann = "0.1.8" diff --git a/extract_fann_data/build.rs b/extract_fann_data/build.rs new file mode 100644 index 0000000..e6b8ca6 --- /dev/null +++ b/extract_fann_data/build.rs @@ -0,0 +1,11 @@ +fn main() { + // Replace this with the path to the directory containing `fann.lib` + let lib_dir = "F://vandomej/Downloads/vcpkg/packages/fann_x64-windows/lib"; + + println!("cargo:rustc-link-search=native={}", lib_dir); + println!("cargo:rustc-link-lib=static=fann"); + // Use `dylib=fann` instead of `static=fann` if you're linking dynamically + + // If there are any additional directories where the compiler can find header files, you can specify them like this: + // println!("cargo:include={}", path_to_include_directory); +} diff --git a/extract_fann_data/src/main.rs b/extract_fann_data/src/main.rs new file mode 100644 index 0000000..8d6f03b --- /dev/null +++ b/extract_fann_data/src/main.rs @@ -0,0 +1,38 @@ +extern crate fann; + +use fann::Fann; +use std::os::raw::c_uint; + +fn main() { + let args: Vec = std::env::args().collect(); + if args.len() < 2 { + eprintln!("Usage: {} ", args[0]); + std::process::exit(1); + } + + let network_file = &args[1]; + match Fann::from_file(network_file) { + Ok(ann) => { + // Output layer sizes + let layer_sizes = ann.get_layer_sizes(); + let bias_counts = ann.get_bias_counts(); + + println!("Layers:"); + for (layer_size, bias_count) in layer_sizes.iter().zip(bias_counts.iter()) { + println!("{} {}", layer_size, bias_count); + } + + // Output connections + println!("Connections:"); + let connections = ann.get_connections(); + + for connection in connections { + println!("{} {} {}", connection.from_neuron, connection.to_neuron, connection.weight); + } + }, + Err(err) => { + eprintln!("Error loading network from file {}: {}", network_file, err); + std::process::exit(1); + } + } +} diff --git a/gemla/src/bin/fighter_nn/mod.rs b/gemla/src/bin/fighter_nn/mod.rs index aa96286..4c4bd98 100644 --- a/gemla/src/bin/fighter_nn/mod.rs +++ b/gemla/src/bin/fighter_nn/mod.rs @@ -22,29 +22,39 @@ use std::{ ops::Range, path::{Path, PathBuf}, }; -use tokio::{process::Command, sync::mpsc::channel}; +use tokio::process::Command; use uuid::Uuid; use self::neural_network_utility::{crossbreed, major_mutation}; const BASE_DIR: &str = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations"; -const POPULATION: usize = 50; +const POPULATION: usize = 200; -const NEURAL_NETWORK_INPUTS: usize = 18; +const NEURAL_NETWORK_INPUTS: usize = 22; const NEURAL_NETWORK_OUTPUTS: usize = 8; + const NEURAL_NETWORK_HIDDEN_LAYERS_MIN: usize = 1; -const NEURAL_NETWORK_HIDDEN_LAYERS_MAX: usize = 10; +const NEURAL_NETWORK_HIDDEN_LAYERS_MAX: usize = 2; + const NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN: usize = 3; -const NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MAX: usize = 35; -const NEURAL_NETWORK_INITIAL_WEIGHT_MIN: f32 = -2.0; -const NEURAL_NETWORK_INITIAL_WEIGHT_MAX: f32 = 2.0; +const NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MAX: usize = 50; + +const NEURAL_NETWORK_INITIAL_WEIGHT_MAX: f32 = 0.5; + +const NEURAL_NETWORK_MINOR_MUTATION_RATE_MAX: f32 = 0.3; +const NEURAL_NETWORK_MUTATION_WEIGHT_MAX: f32 = 1.0; + +const NEURAL_NETWORK_MAJOR_MUTATION_RATE_MAX: f32 = 1.0; + const NEURAL_NETWORK_CROSSBREED_SEGMENTS_MIN: usize = 2; -const NEURAL_NETWORK_CROSSBREED_SEGMENTS_MAX: usize = 20; -const OFFSHOOT_GENERATIONAL_LENIENCE: u64 = 5; +const NEURAL_NETWORK_CROSSBREED_SEGMENTS_MAX: usize = 6; + +const OFFSHOOT_GENERATIONAL_LENIENCE: u64 = 10; const MAINLINE_GENERATIONAL_LENIENCE: u64 = 20; const SIMULATION_ROUNDS: usize = 5; -const SURVIVAL_RATE: f32 = 0.5; +const SURVIVAL_RATE_MIN: f32 = 0.1; +const SURVIVAL_RATE_MAX: f32 = 0.9; const GAME_EXECUTABLE_PATH: &str = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Package\\Windows\\AI_Fight_Sim.exe"; @@ -77,6 +87,7 @@ pub struct FighterNN { pub id_mapping: Vec>, pub lerp_amount: f32, pub generational_lenience: u64, + pub survival_rate: f32, } #[async_trait] @@ -102,9 +113,8 @@ impl GeneticNode for FighterNN { })?; let mut nn_shapes = HashMap::new(); - let weight_initialization_range = thread_rng() - .gen_range(NEURAL_NETWORK_INITIAL_WEIGHT_MIN..0.0) - ..thread_rng().gen_range(0.0..=NEURAL_NETWORK_INITIAL_WEIGHT_MAX); + let weight_initialization_amplitude = thread_rng().gen_range(0.0..NEURAL_NETWORK_INITIAL_WEIGHT_MAX); + let weight_initialization_range = -weight_initialization_amplitude..weight_initialization_amplitude; // Create the first generation in this folder for i in 0..POPULATION { @@ -115,11 +125,11 @@ impl GeneticNode for FighterNN { // Randomly generate a neural network shape based on constants let hidden_layers = thread_rng() - .gen_range(NEURAL_NETWORK_HIDDEN_LAYERS_MIN..NEURAL_NETWORK_HIDDEN_LAYERS_MAX); + .gen_range(NEURAL_NETWORK_HIDDEN_LAYERS_MIN..=NEURAL_NETWORK_HIDDEN_LAYERS_MAX); let mut nn_shape = vec![NEURAL_NETWORK_INPUTS as u32]; for _ in 0..hidden_layers { nn_shape.push(thread_rng().gen_range( - NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN..NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MAX, + NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MIN..=NEURAL_NETWORK_HIDDEN_LAYER_SIZE_MAX, ) as u32); } nn_shape.push(NEURAL_NETWORK_OUTPUTS as u32); @@ -138,30 +148,31 @@ impl GeneticNode for FighterNN { } let mut crossbreed_segments = thread_rng().gen_range( - NEURAL_NETWORK_CROSSBREED_SEGMENTS_MIN..NEURAL_NETWORK_CROSSBREED_SEGMENTS_MAX, + NEURAL_NETWORK_CROSSBREED_SEGMENTS_MIN..=NEURAL_NETWORK_CROSSBREED_SEGMENTS_MAX, ); if crossbreed_segments % 2 == 0 { crossbreed_segments += 1; } - let mutation_weight_amplitude = thread_rng().gen_range(0.0..1.0); + let mutation_weight_amplitude = thread_rng().gen_range(0.0..NEURAL_NETWORK_MUTATION_WEIGHT_MAX); Ok(Box::new(FighterNN { id: context.id, folder, population_size: POPULATION, generation: 0, - scores: vec![HashMap::new()], + scores: vec![], nn_shapes: vec![nn_shapes], // we need crossbreed segments to be even crossbreed_segments, weight_initialization_range, - minor_mutation_rate: thread_rng().gen_range(0.0..1.0), - major_mutation_rate: thread_rng().gen_range(0.0..1.0), + minor_mutation_rate: thread_rng().gen_range(0.0..NEURAL_NETWORK_MINOR_MUTATION_RATE_MAX), + major_mutation_rate: thread_rng().gen_range(0.0..NEURAL_NETWORK_MAJOR_MUTATION_RATE_MAX), mutation_weight_range: -mutation_weight_amplitude..mutation_weight_amplitude, - id_mapping: vec![HashMap::new()], + id_mapping: vec![], lerp_amount: 0.0, generational_lenience: OFFSHOOT_GENERATIONAL_LENIENCE, + survival_rate: thread_rng().gen_range(SURVIVAL_RATE_MIN..SURVIVAL_RATE_MAX), })) } @@ -189,12 +200,14 @@ impl GeneticNode for FighterNN { i }; + let secondary_id = loop { - if allotted_simulations.is_empty() { + if allotted_simulations.is_empty() || allotted_simulations.len() == 1 { // Select a random id let random_id = loop { let id = thread_rng().gen_range(0..self.population_size); if id != primary_id { + allotted_simulations.clear(); break id; } }; @@ -219,22 +232,21 @@ impl GeneticNode for FighterNN { matches.push((primary_id, secondary_id)); } + debug!("Matches determined"); trace!("Matches: {:?}", matches); // Create a channel to send the scores back to the main thread - let (tx, mut rx) = channel::<(usize, f32)>(self.population_size * SIMULATION_ROUNDS * 20); let mut tasks = Vec::new(); for (primary_id, secondary_id) in matches.iter() { - let self_clone = self.clone(); - let semaphore_clone = context.gemla_context.shared_semaphore.clone(); - let display_simulation_semaphore = context.gemla_context.visible_simulations.clone(); - let tx = tx.clone(); - - let task = async move { + let task = { + let self_clone = self.clone(); + let semaphore_clone = context.gemla_context.shared_semaphore.clone(); + let display_simulation_semaphore = context.gemla_context.visible_simulations.clone(); + let folder = self_clone.folder.clone(); let generation = self_clone.generation; - + let primary_nn = self_clone .folder .join(format!("{}", self_clone.generation)) @@ -244,77 +256,84 @@ impl GeneticNode for FighterNN { .join(format!("{}", generation)) .join(self_clone.get_individual_id(*secondary_id as u64)) .with_extension("net"); - - let permit = semaphore_clone - .acquire_owned() - .await - .with_context(|| "Failed to acquire semaphore permit")?; - - let display_simulation = match display_simulation_semaphore.try_acquire_owned() { - Ok(s) => Some(s), - Err(_) => None, - }; - - let (primary_score, secondary_score) = - if let Some(display_simulation) = display_simulation { + + // Introducing a new scope for acquiring permits and running simulations + let simulation_result = async move { + let permit = semaphore_clone.acquire_owned().await + .with_context(|| "Failed to acquire semaphore permit")?; + + let display_simulation = match display_simulation_semaphore.try_acquire_owned() { + Ok(s) => Some(s), + Err(_) => None, + }; + + let (primary_score, secondary_score) = if let Some(display_simulation) = display_simulation { let result = run_1v1_simulation(&primary_nn, &secondary_nn, true).await?; - drop(display_simulation); + drop(display_simulation); // Explicitly dropping resources no longer needed result } else { run_1v1_simulation(&primary_nn, &secondary_nn, false).await? }; + + drop(permit); // Explicitly dropping resources no longer needed - drop(permit); - - debug!( - "{} vs {} -> {} vs {}", - primary_id, secondary_id, primary_score, secondary_score - ); - - // Send score using a channel - tx.send((*primary_id, primary_score)) - .await - .with_context(|| "Failed to send score")?; - tx.send((*secondary_id, secondary_score)) - .await - .with_context(|| "Failed to send score")?; - - Ok(()) + debug!( + "{} vs {} -> {} vs {}", + primary_id, secondary_id, primary_score, secondary_score + ); + + Ok((*primary_id, primary_score, *secondary_id, secondary_score)) + }; // Await the scoped async block immediately + + // The result of the simulation, whether Ok or Err, is returned here. + // This ensures tx is dropped when the block exits, regardless of success or failure. + simulation_result }; tasks.push(task); } - let results: Vec> = join_all(tasks).await; + debug!("Tasks created"); + + let results: Vec> = join_all(tasks).await; + + debug!("Tasks completed"); // resolve results for any errors - for result in results.into_iter() { - result.with_context(|| "Failed to run simulation")?; - } - - // Receive the scores from the channel let mut scores = HashMap::new(); - while let Some((id, score)) = rx.recv().await { + for result in results.into_iter() { + let (primary_id, primary_score, secondary_id, secondary_score) = result.with_context(|| "Failed to run simulation")?; + // If score exists, add the new score to the existing score - if let Some(existing_score) = scores.get_mut(&(id as u64)) { - *existing_score += score; + if let Some((existing_score, count)) = scores.get_mut(&(primary_id as u64)) { + *existing_score += primary_score; + *count += 1; } else { - scores.insert(id as u64, score); + scores.insert(primary_id as u64, (primary_score, 1)); + } + + // If score exists, add the new score to the existing score + if let Some((existing_score, count)) = scores.get_mut(&(secondary_id as u64)) { + *existing_score += secondary_score; + *count += 1; + } else { + scores.insert(secondary_id as u64, (secondary_score, 1)); } } // Average scores for each individual - for (_, score) in scores.iter_mut() { - *score /= SIMULATION_ROUNDS as f32; + let mut final_scores = HashMap::new(); + for (i, (score, count)) in scores.iter() { + final_scores.insert(*i, *score / *count as f32); } - self.scores.push(scores); + self.scores.push(final_scores); Ok(should_continue(&self.scores, self.generational_lenience)?) } async fn mutate(&mut self, _context: GeneticNodeContext) -> Result<(), Error> { - let survivor_count = (self.population_size as f32 * SURVIVAL_RATE) as usize; + let survivor_count = (self.population_size as f32 * self.survival_rate) as usize; let mut nn_sizes = Vec::new(); let mut id_mapping = HashMap::new(); @@ -359,7 +378,7 @@ impl GeneticNode for FighterNN { let mut tasks = Vec::new(); // Take the remaining nn's and create new nn's by the following: - for i in 0..survivor_count { + for i in 0..(self.population_size - survivor_count) { let self_clone = self.clone(); // randomly select individual id's sorted scores proportional to their score @@ -443,7 +462,6 @@ impl GeneticNode for FighterNN { .collect::>(); self.generation += 1; - self.scores.push(HashMap::new()); self.nn_shapes.push(nn_sizes_map); self.id_mapping.push(id_mapping); @@ -524,6 +542,8 @@ impl GeneticNode for FighterNN { run_1v1_simulation(&left_nn_path, &right_nn_path, false).await? }; + debug!("{} vs {} -> {} vs {}", left_nn_id, right_nn_id, left_score, right_score); + drop(permit); Ok::<(f32, f32), Error>((left_score, right_score)) @@ -646,21 +666,27 @@ impl GeneticNode for FighterNN { debug!("mutation_weight_range: {:?}", mutation_weight_range); + let survival_rate = left.survival_rate.lerp(right.survival_rate, lerp_amount); + + debug!("survival_rate: {}", survival_rate); + Ok(Box::new(FighterNN { id: *id, folder, generation: 0, population_size: nn_shapes.len(), - scores: vec![HashMap::new()], + scores: vec![], crossbreed_segments, nn_shapes: vec![nn_shapes], weight_initialization_range, minor_mutation_rate, major_mutation_rate, mutation_weight_range, - id_mapping: vec![HashMap::new()], + id_mapping: vec![], lerp_amount, + // generational_lenience: left.generational_lenience + MAINLINE_GENERATIONAL_LENIENCE, generational_lenience: MAINLINE_GENERATIONAL_LENIENCE, + survival_rate, })) } } @@ -713,7 +739,7 @@ fn should_continue(scores: &[HashMap], lenience: u64) -> Result Result + Err(_) => { - if attempts >= 5 { + if attempts >= 2 { // Attempt 5 times before giving up. - return Err(e); + return Ok(-100.0); } attempts += 1; // wait 1 second to ensure the file is written - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + tokio::time::sleep(tokio::time::Duration::from_secs(10)).await; } - Err(e) => return Err(e), } } } diff --git a/gemla/src/bin/fighter_nn/neural_network_utility.rs b/gemla/src/bin/fighter_nn/neural_network_utility.rs index dba26af..7fe922e 100644 --- a/gemla/src/bin/fighter_nn/neural_network_utility.rs +++ b/gemla/src/bin/fighter_nn/neural_network_utility.rs @@ -109,9 +109,9 @@ pub fn consolidate_old_connections( let primary_shape = primary.get_layer_sizes(); let secondary_shape = secondary.get_layer_sizes(); - debug!("Primary shape: {:?}", primary_shape); - debug!("Secondary shape: {:?}", secondary_shape); - debug!("New shape: {:?}", new_shape); + trace!("Primary shape: {:?}", primary_shape); + trace!("Secondary shape: {:?}", secondary_shape); + trace!("New shape: {:?}", new_shape); // Start by iterating layer by later let primary_connections = primary.get_connections(); diff --git a/gemla/src/core/genetic_node.rs b/gemla/src/core/genetic_node.rs index b85b775..abcb15f 100644 --- a/gemla/src/core/genetic_node.rs +++ b/gemla/src/core/genetic_node.rs @@ -6,6 +6,7 @@ use crate::error::Error; use anyhow::Context; use async_trait::async_trait; +use log::info; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::fmt::Debug; use uuid::Uuid; @@ -147,6 +148,8 @@ where .await .with_context(|| format!("Error simulating node: {:?}", self))?; + info!("Simulation complete and continuing: {:?}", next_generation); + self.state = if next_generation { GeneticState::Mutate } else { diff --git a/parameter_analysis.py b/parameter_analysis.py new file mode 100644 index 0000000..f3993fb --- /dev/null +++ b/parameter_analysis.py @@ -0,0 +1,380 @@ +# Re-importing necessary libraries +import json +import matplotlib.pyplot as plt +from collections import defaultdict +import numpy as np +import pandas as pd +import seaborn as sns +import matplotlib.colors as mcolors +import matplotlib.cm as cm +import matplotlib.ticker as ticker + +# Simplified JSON data for demonstration +with open('gemla/round4.json', 'r') as file: + simplified_json_data = json.load(file) + +# Function to traverse the tree to find a node id +def traverse_right_nodes(node): + if node is None: + return [] + + right_node = node.get("right") + left_node = node.get("left") + + if right_node is None and left_node is None: + return [] + elif right_node and left_node: + return [right_node] + traverse_right_nodes(left_node) + + return [] + +# Getting most recent right graph +right_nodes = traverse_right_nodes(simplified_json_data[0]) + +# Heatmaps +# Data structure to store mutation rates, generations, and scores +mutation_rate_data = defaultdict(lambda: defaultdict(list)) + +# Populate the dictionary with scores indexed by mutation rate and generation +for node in right_nodes: + node_val = node["val"]["node"] + if node_val: + scores = node_val["scores"] + minor_mutation_rate = node_val["minor_mutation_rate"] + generation = node_val["generation"] + # Ensure each score is associated with the correct generation + for gen_index, score_list in enumerate(scores): + for score in score_list.values(): + mutation_rate_data[minor_mutation_rate][gen_index].append(score) + +# Prepare data for heatmap +max_generation = max(max(gens.keys()) for gens in mutation_rate_data.values()) +heatmap_data = np.full((len(mutation_rate_data), max_generation + 1), np.nan) + +# Populate the heatmap data with average scores +mutation_rates = sorted(mutation_rate_data.keys()) +for i, mutation_rate in enumerate(mutation_rates): + for generation in range(max_generation + 1): + scores = mutation_rate_data[mutation_rate][generation] + if scores: # Check if there are scores for this generation + heatmap_data[i, generation] = np.mean(scores) + +# Creating a DataFrame for the heatmap +df_heatmap = pd.DataFrame( + data=heatmap_data, + index=mutation_rates, + columns=range(max_generation + 1) +) + +# Data structure to store major mutation rates, generations, and scores +major_mutation_rate_data = defaultdict(lambda: defaultdict(list)) + +# Populate the dictionary with scores indexed by major mutation rate and generation +# This is assuming the structure to retrieve major_mutation_rate is similar to minor_mutation_rate +for node in right_nodes: + node_val = node["val"]["node"] + if node_val: + scores = node_val["scores"] + major_mutation_rate = node_val["major_mutation_rate"] + generation = node_val["generation"] + for gen_index, score_list in enumerate(scores): + for score in score_list.values(): + major_mutation_rate_data[major_mutation_rate][gen_index].append(score) + +# Prepare the heatmap data for major_mutation_rate similar to minor_mutation_rate +major_heatmap_data = np.full((len(major_mutation_rate_data), max_generation + 1), np.nan) +major_mutation_rates = sorted(major_mutation_rate_data.keys()) + +for i, major_rate in enumerate(major_mutation_rates): + for generation in range(max_generation + 1): + scores = major_mutation_rate_data[major_rate][generation] + if scores: # Check if there are scores for this generation + major_heatmap_data[i, generation] = np.mean(scores) + +# Creating a DataFrame for the major mutation rate heatmap +df_major_heatmap = pd.DataFrame( + data=major_heatmap_data, + index=major_mutation_rates, + columns=range(max_generation + 1) +) + +# crossbreed_segments +# Data structure to store major mutation rates, generations, and scores +crossbreed_segments_data = defaultdict(lambda: defaultdict(list)) + +# Populate the dictionary with scores indexed by major mutation rate and generation +# This is assuming the structure to retrieve major_mutation_rate is similar to minor_mutation_rate +for node in right_nodes: + node_val = node["val"]["node"] + if node_val: + scores = node_val["scores"] + crossbreed_segments = node_val["crossbreed_segments"] + generation = node_val["generation"] + for gen_index, score_list in enumerate(scores): + for score in score_list.values(): + crossbreed_segments_data[crossbreed_segments][gen_index].append(score) + +# Prepare the heatmap data for crossbreed_segments similar to minor_mutation_rate +crossbreed_heatmap_data = np.full((len(crossbreed_segments_data), max_generation + 1), np.nan) +crossbreed_segments = sorted(crossbreed_segments_data.keys()) + +for i, crossbreed_segment in enumerate(crossbreed_segments): + for generation in range(max_generation + 1): + scores = crossbreed_segments_data[crossbreed_segment][generation] + if scores: # Check if there are scores for this generation + crossbreed_heatmap_data[i, generation] = np.mean(scores) + +# Creating a DataFrame for the major mutation rate heatmap +df_crossbreed_heatmap = pd.DataFrame( + data=crossbreed_heatmap_data, + index=crossbreed_segments, + columns=range(max_generation + 1) +) + +# mutation_weight_range +# Data structure to store major mutation rates, generations, and scores +mutation_weight_range_data = defaultdict(lambda: defaultdict(list)) + +# Populate the dictionary with scores indexed by major mutation rate and generation +# This is assuming the structure to retrieve major_mutation_rate is similar to minor_mutation_rate +for node in right_nodes: + node_val = node["val"]["node"] + if node_val: + scores = node_val["scores"] + mutation_weight_range = node_val["mutation_weight_range"] + positive_extent = mutation_weight_range["end"] + negative_extent = -mutation_weight_range["start"] + mutation_weight_range = (positive_extent + negative_extent) / 2 + generation = node_val["generation"] + for gen_index, score_list in enumerate(scores): + for score in score_list.values(): + mutation_weight_range_data[mutation_weight_range][gen_index].append(score) + +# Prepare the heatmap data for crossbreed_segments similar to minor_mutation_rate +mutation_weight_range_heatmap_data = np.full((len(mutation_weight_range_data), max_generation + 1), np.nan) +mutation_weight_ranges = sorted(mutation_weight_range_data.keys()) + +for i, mutation_weight_range in enumerate(mutation_weight_ranges): + for generation in range(max_generation + 1): + scores = mutation_weight_range_data[mutation_weight_range][generation] + if scores: # Check if there are scores for this generation + mutation_weight_range_heatmap_data[i, generation] = np.mean(scores) + +# Creating a DataFrame for the major mutation rate heatmap +df_mutation_weight_range_heatmap = pd.DataFrame( + data=mutation_weight_range_heatmap_data, + index=mutation_weight_ranges, + columns=range(max_generation + 1) +) + +# weight_initialization_range +# Data structure to store major mutation rates, generations, and scores +weight_initialization_range_data = defaultdict(lambda: defaultdict(list)) + +# Populate the dictionary with scores indexed by major mutation rate and generation +# This is assuming the structure to retrieve major_mutation_rate is similar to minor_mutation_rate +for node in right_nodes: + node_val = node["val"]["node"] + if node_val: + scores = node_val["scores"] + weight_initialization_range = node_val["weight_initialization_range"] + positive_extent = weight_initialization_range["end"] + negative_extent = -weight_initialization_range["start"] + weight_initialization_range = (positive_extent + negative_extent) / 2 + generation = node_val["generation"] + for gen_index, score_list in enumerate(scores): + for score in score_list.values(): + weight_initialization_range_data[weight_initialization_range][gen_index].append(score) + +# Prepare the heatmap data for crossbreed_segments similar to minor_mutation_rate +weight_initialization_range_heatmap_data = np.full((len(weight_initialization_range_data), max_generation + 1), np.nan) +weight_initialization_ranges = sorted(weight_initialization_range_data.keys()) + +for i, weight_initialization_range in enumerate(weight_initialization_ranges): + for generation in range(max_generation + 1): + scores = weight_initialization_range_data[weight_initialization_range][generation] + if scores: # Check if there are scores for this generation + weight_initialization_range_heatmap_data[i, generation] = np.mean(scores) + +# Creating a DataFrame for the major mutation rate heatmap +df_weight_initialization_range_heatmap = pd.DataFrame( + data=weight_initialization_range_heatmap_data, + index=weight_initialization_ranges, + columns=range(max_generation + 1) +) + +# weight_initialization_range_skew +# Data structure to store major mutation rates, generations, and scores +weight_initialization_range_skew_data = defaultdict(lambda: defaultdict(list)) + +# Populate the dictionary with scores indexed by major mutation rate and generation +# This is assuming the structure to retrieve major_mutation_rate is similar to minor_mutation_rate +for node in right_nodes: + node_val = node["val"]["node"] + if node_val: + scores = node_val["scores"] + weight_initialization_range = node_val["weight_initialization_range"] + positive_extent = weight_initialization_range["end"] + negative_extent = -weight_initialization_range["start"] + weight_initialization_range_skew = (positive_extent - negative_extent) / 2 + generation = node_val["generation"] + for gen_index, score_list in enumerate(scores): + for score in score_list.values(): + weight_initialization_range_skew_data[weight_initialization_range_skew][gen_index].append(score) + +# Prepare the heatmap data for crossbreed_segments similar to minor_mutation_rate +weight_initialization_range_skew_heatmap_data = np.full((len(weight_initialization_range_skew_data), max_generation + 1), np.nan) +weight_initialization_range_skews = sorted(weight_initialization_range_skew_data.keys()) + +for i, weight_initialization_range_skew in enumerate(weight_initialization_range_skews): + for generation in range(max_generation + 1): + scores = weight_initialization_range_skew_data[weight_initialization_range_skew][generation] + if scores: # Check if there are scores for this generation + weight_initialization_range_skew_heatmap_data[i, generation] = np.mean(scores) + +# Creating a DataFrame for the major mutation rate heatmap +df_weight_initialization_range_skew_heatmap = pd.DataFrame( + data=weight_initialization_range_skew_heatmap_data, + index=weight_initialization_range_skews, + columns=range(max_generation + 1) +) + +# Analyze number of neurons correlation to score +# We can get the number of neurons via node_val["nn_shapes"] which contains an array of maps +# Each map has a key for the individual id and a value which is an array of integers representing the number of neurons in each layer +# We can use the individual id to get the score from the scores array +# We then generate a density map of the number of neurons vs the score +neuron_number_score_data = defaultdict(lambda: defaultdict(list)) + +for node in right_nodes: + node_val = node["val"]["node"] + if node_val: + scores = node_val["scores"] + nn_shapes = node_val["nn_shapes"] + # Both scores and nn_shapes are arrays where score is 1 less in length than nn_shapes (each index corresponds to a generation) + for gen_index, score in enumerate(scores): + for individual_id, nn_shape in nn_shapes[gen_index].items(): + neuron_number = sum(nn_shape) + # check if score has a value for the individual id + if individual_id not in score: + continue + neuron_number_score_data[neuron_number][gen_index].append(score[individual_id]) + +# prepare the density map data +neuron_number_score_heatmap_data = np.full((len(neuron_number_score_data), max_generation + 1), np.nan) +neuron_numbers = sorted(neuron_number_score_data.keys()) + +for i, neuron_number in enumerate(neuron_numbers): + for generation in range(max_generation + 1): + scores = neuron_number_score_data[neuron_number][generation] + if scores: # Check if there are scores for this generation + neuron_number_score_heatmap_data[i, generation] = np.mean(scores) + +# Creating a DataFrame for the major mutation rate heatmap +df_neuron_number_score_heatmap = pd.DataFrame( + data=neuron_number_score_heatmap_data, + index=neuron_numbers, + columns=range(max_generation + 1) +) + +# Analyze number of layers correlation to score +nn_layers_score_data = defaultdict(lambda: defaultdict(list)) + +for node in right_nodes: + node_val = node["val"]["node"] + if node_val: + scores = node_val["scores"] + nn_shapes = node_val["nn_shapes"] + # Both scores and nn_shapes are arrays where score is 1 less in length than nn_shapes (each index corresponds to a generation) + for gen_index, score in enumerate(scores): + for individual_id, nn_shape in nn_shapes[gen_index].items(): + layer_number = len(nn_shape) + # check if score has a value for the individual id + if individual_id not in score: + continue + nn_layers_score_data[layer_number][gen_index].append(score[individual_id]) + +# prepare the density map data +nn_layers_score_heatmap_data = np.full((len(nn_layers_score_data), max_generation + 1), np.nan) +nn_layers = sorted(nn_layers_score_data.keys()) + +for i, nn_layer in enumerate(nn_layers): + for generation in range(max_generation + 1): + scores = nn_layers_score_data[nn_layer][generation] + if scores: # Check if there are scores for this generation + nn_layers_score_heatmap_data[i, generation] = np.mean(scores) + +# Creating a DataFrame for the major mutation rate heatmap +df_nn_layers_score_heatmap = pd.DataFrame( + data=nn_layers_score_heatmap_data, + index=nn_layers, + columns=range(max_generation + 1) +) + +# print("Format: ", custom_formatter(0.123498761234, 0)) + +# Creating subplots +fig, axs = plt.subplots(2, 2, figsize=(20, 14)) # Creates a 3x2 grid of subplots + +# Plotting the minor mutation rate heatmap +sns.heatmap(df_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs[0, 0]) +# axs[0, 0].set_title('Minor Mutation Rate') +axs[0, 0].set_xlabel('Minor Mutation Rate') +axs[0, 0].set_ylabel('Generation') +axs[0, 0].invert_yaxis() + +# Plotting the major mutation rate heatmap +sns.heatmap(df_major_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs[0, 1]) +# axs[0, 1].set_title('Major Mutation Rate') +axs[0, 1].set_xlabel('Major Mutation Rate') +axs[0, 1].invert_yaxis() + +# Plotting the crossbreed_segments heatmap +sns.heatmap(df_crossbreed_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs[1, 0]) +# axs[1, 0].set_title('Crossbreed Segments') +axs[1, 0].set_xlabel('Crossbreed Segments') +axs[1, 0].set_ylabel('Generation') +axs[1, 0].invert_yaxis() + +# Plotting the mutation_weight_range heatmap +sns.heatmap(df_mutation_weight_range_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs[1, 1]) +# axs[1, 1].set_title('Mutation Weight Range') +axs[1, 1].set_xlabel('Mutation Weight Range') +axs[1, 1].invert_yaxis() + +fig3, axs3 = plt.subplots(1, 2, figsize=(20, 14)) # Creates a 3x2 grid of subplots + +# Plotting the weight_initialization_range heatmap +sns.heatmap(df_weight_initialization_range_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs3[0]) +# axs[2, 0].set_title('Weight Initialization Range') +axs3[0].set_xlabel('Weight Initialization Range') +axs3[0].set_ylabel('Generation') +axs3[0].invert_yaxis() + +# Plotting the weight_initialization_range_skew heatmap +sns.heatmap(df_weight_initialization_range_skew_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs3[1]) +# axs[2, 1].set_title('Weight Initialization Range Skew') +axs3[1].set_xlabel('Weight Initialization Range Skew') +axs3[1].set_ylabel('Generation') +axs3[1].invert_yaxis() + +# Creating a new window for the scatter plots +fig2, axs2 = plt.subplots(2, 1, figsize=(20, 14)) # Creates a 2x1 grid of subplots + +# Plotting the neuron number vs score heatmap +sns.heatmap(df_neuron_number_score_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs2[1]) +# axs[3, 1].set_title('Neuron Number vs. Score') +axs2[1].set_xlabel('Neuron Number') +axs2[1].set_ylabel('Generation') +axs2[1].invert_yaxis() + +# Plotting the number of layers vs score heatmap +sns.heatmap(df_nn_layers_score_heatmap.T, cmap='viridis', fmt=".4g", cbar_kws={'label': 'Mean Score'}, ax=axs2[0]) +# axs[3, 1].set_title('Number of Layers vs. Score') +axs2[0].set_xlabel('Number of Layers') +axs2[0].set_ylabel('Generation') +axs2[0].invert_yaxis() + +# Display the plot +plt.tight_layout() # Adjusts the subplots to fit into the figure area. +plt.show() \ No newline at end of file diff --git a/visualize_networks.py b/visualize_networks.py new file mode 100644 index 0000000..280011f --- /dev/null +++ b/visualize_networks.py @@ -0,0 +1,118 @@ +import matplotlib.pyplot as plt +import networkx as nx +import subprocess +import tkinter as tk +from tkinter import filedialog + +def select_file(): + root = tk.Tk() + root.withdraw() # Hide the main window + file_path = filedialog.askopenfilename( + initialdir="/", # Set the initial directory to search for files + title="Select file", + filetypes=(("Net files", "*.net"), ("All files", "*.*")) + ) + return file_path + +def get_fann_data(network_file): + # Adjust the path to the Rust executable as needed + result = subprocess.run(['./extract_fann_data/target/debug/extract_fann_data.exe', network_file], capture_output=True, text=True) + if result.returncode != 0: + print("Error:", result.stderr) + return None, None + + layer_sizes = [] + connections = [] + parsing_connections = False + + for line in result.stdout.splitlines(): + if line.startswith("Layers:"): + continue + elif line.startswith("Connections:"): + parsing_connections = True + continue + + if parsing_connections: + from_neuron, to_neuron, weight = map(float, line.split()) + connections.append((int(from_neuron), int(to_neuron), weight)) + else: + layer_size, bias_count = map(int, line.split()) + layer_sizes.append((layer_size, bias_count)) + + return layer_sizes, connections + +def visualize_fann_network(network_file): + # Get network data + layer_sizes, connections = get_fann_data(network_file) + if layer_sizes is None or connections is None: + return # Error handling in get_fann_data should provide error output + + # Create a directed graph + G = nx.DiGraph() + + # Positions dictionary to hold the position of each neuron + pos = {} + node_count = 0 + x_spacing = 1.0 + y_spacing = 1.0 + + # Calculate the maximum layer size for proper spacing + max_layer_size = max(size for size, bias in layer_sizes) + + # Build nodes and position them layer by layer from left to right + for layer_index, (layer_size, bias_count) in enumerate(layer_sizes): + y_positions = list(range(-layer_size-bias_count+1, 1, 1)) # Center-align vertically + y_positions = [y * (max_layer_size / (layer_size + bias_count)) * y_spacing for y in y_positions] # Adjust spacing + for neuron_index in range(layer_size + bias_count): # Include bias neurons + node_label = f"L{layer_index}N{neuron_index}" + G.add_node(node_count, label=node_label) + pos[node_count] = (layer_index * x_spacing, y_positions[neuron_index % len(y_positions)]) + node_count += 1 + + # Add connections to the graph + for from_neuron, to_neuron, weight in connections: + G.add_edge(from_neuron, to_neuron, weight=weight) + + max_weight = max(abs(weight) for _, _, weight in connections) + print(f"Max weight: {max_weight}") + + # Draw nodes + nx.draw_networkx_nodes(G, pos, node_color='skyblue', node_size=200) + nx.draw_networkx_labels(G, pos, font_size=7) + + # Custom function for edge properties + def adjust_properties(weight): + # if weight > 0: + # print("Weight:", weight) + color = 'green' if weight > 0 else 'red' + alpha = min((abs(weight) / max_weight) ** 3, 1) + # print(f"Color: {color}, Alpha: {alpha}") + return color, alpha + + # Draw edges with custom properties + for u, v, d in G.edges(data=True): + color, alpha = adjust_properties(d['weight']) + nx.draw_networkx_edges(G, pos, edgelist=[(u, v)], edge_color=color, alpha=alpha, width=1.5, arrows=False) + + # Show plot + plt.title('FANN Network Visualization') + plt.axis('off') # Turn off the axis + plt.show() + +# Path to the FANN network file +fann_path = 'F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_4f2be613-ab26-4384-9a65-450e043984ea\\6\\4f2be613-ab26-4384-9a65-450e043984ea_fighter_nn_0.net' +# fann_path = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_fc294503-7b2a-40f8-be59-ccc486eb3f79\\0\\fc294503-7b2a-40f8-be59-ccc486eb3f79_fighter_nn_0.net" +# fann_path = 'F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_99c30a7f-40ab-4faf-b16a-b44703fdb6cd\\0\\99c30a7f-40ab-4faf-b16a-b44703fdb6cd_fighter_nn_0.net' +# Has a 4 layer network +# # Generation 1 +# fann_path = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98\\1\\16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98_fighter_nn_0.net" +# # Generation 5 +# fann_path = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98\\5\\16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98_fighter_nn_0.net" +# # Generation 10 +# fann_path = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98\\10\\16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98_fighter_nn_0.net" +# # Generation 20 +# fann_path = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98\\20\\16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98_fighter_nn_0.net" +# # Generation 32 +# fann_path = "F:\\\\vandomej\\Projects\\dootcamp-AI-Simulation\\Simulations\\fighter_nn_16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98\\32\\16dfa1b4-03c7-45a6-84b4-22fe3c8e2d98_fighter_nn_0.net" +fann_path = select_file() +visualize_fann_network(fann_path) \ No newline at end of file diff --git a/visualize_simulation_tree.py b/visualize_simulation_tree.py index 419d0e1..7d91343 100644 --- a/visualize_simulation_tree.py +++ b/visualize_simulation_tree.py @@ -36,7 +36,7 @@ def hierarchy_pos(G, root=None, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5) return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter) # Simplified JSON data for demonstration -with open('gemla/round2.json', 'r') as file: +with open('gemla/round4.json', 'r') as file: simplified_json_data = json.load(file) # Function to traverse the tree and create a graph @@ -68,7 +68,7 @@ def traverse(node, graph, parent=None): # print debug statement # print(f"Node {node_id}: Max score: {overall_max_score:.6f} (Individual {overall_max_score_individual} in Gen {overall_max_score_gen})") # print(f"Left: {node.get('left')}, Right: {node.get('right')}") - label = f"{node_id}\nGenerations: {generations}, Population: {population_size}\nMax score: {overall_max_score:.6f} (Individual {overall_max_score_individual} in Gen {overall_max_score_gen})" + label = f"{node_id}\nGenerations: {generations}, Population: {population_size}\nMax score: {overall_max_score:.6f} (Individual {overall_max_score_individual} in Gen {overall_max_score_gen + 1 if overall_max_score_gen is not None else 'N/A'})" else: label = node_id