Formatting code
This commit is contained in:
parent
5ab3c2382e
commit
95fdad1034
4 changed files with 103 additions and 60 deletions
|
@ -3,7 +3,7 @@ extern crate fann;
|
|||
pub mod fighter_context;
|
||||
pub mod neural_network_utility;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use anyhow::{Context, anyhow};
|
||||
use async_trait::async_trait;
|
||||
use fann::{ActivationFunc, Fann};
|
||||
use futures::future::join_all;
|
||||
|
@ -114,8 +114,10 @@ impl GeneticNode for FighterNN {
|
|||
})?;
|
||||
|
||||
let mut nn_shapes = HashMap::new();
|
||||
let weight_initialization_amplitude = rng().random_range(0.0..NEURAL_NETWORK_INITIAL_WEIGHT_MAX);
|
||||
let weight_initialization_range = -weight_initialization_amplitude..weight_initialization_amplitude;
|
||||
let weight_initialization_amplitude =
|
||||
rng().random_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 {
|
||||
|
@ -201,7 +203,6 @@ impl GeneticNode for FighterNN {
|
|||
i
|
||||
};
|
||||
|
||||
|
||||
let secondary_id = loop {
|
||||
if allotted_simulations.is_empty() || allotted_simulations.len() == 1 {
|
||||
// Select a random id
|
||||
|
@ -243,11 +244,12 @@ impl GeneticNode for FighterNN {
|
|||
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 display_simulation_semaphore =
|
||||
context.gemla_context.visible_simulations.clone();
|
||||
|
||||
let folder = self_clone.folder.clone();
|
||||
let generation = self_clone.r#generation;
|
||||
|
||||
|
||||
let primary_nn = self_clone
|
||||
.folder
|
||||
.join(format!("{}", self_clone.r#generation))
|
||||
|
@ -257,32 +259,36 @@ impl GeneticNode for FighterNN {
|
|||
.join(format!("{}", generation))
|
||||
.join(self_clone.get_individual_id(*secondary_id as u64))
|
||||
.with_extension("net");
|
||||
|
||||
|
||||
// Introducing a new scope for acquiring permits and running simulations
|
||||
let simulation_result = async move {
|
||||
let permit = semaphore_clone.acquire_owned().await
|
||||
let permit = semaphore_clone
|
||||
.acquire_owned()
|
||||
.await
|
||||
.with_context(|| "Failed to acquire semaphore permit")?;
|
||||
|
||||
|
||||
let display_simulation = display_simulation_semaphore.try_acquire_owned().ok();
|
||||
|
||||
let (primary_score, secondary_score) = if let Some(display_simulation) = display_simulation {
|
||||
|
||||
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); // 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
|
||||
|
||||
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
|
||||
|
@ -300,7 +306,8 @@ impl GeneticNode for FighterNN {
|
|||
// resolve results for any errors
|
||||
let mut scores = HashMap::new();
|
||||
for result in results.into_iter() {
|
||||
let (primary_id, primary_score, secondary_id, secondary_score) = result.with_context(|| "Failed to run simulation")?;
|
||||
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, count)) = scores.get_mut(&(primary_id as u64)) {
|
||||
|
@ -480,8 +487,9 @@ impl GeneticNode for FighterNN {
|
|||
.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.r#generation as usize].iter().collect();
|
||||
let mut sorted_scores: Vec<_> = fighter.scores[fighter.r#generation as usize]
|
||||
.iter()
|
||||
.collect();
|
||||
sorted_scores.sort_by(|a, b| b.1.partial_cmp(a.1).unwrap());
|
||||
sorted_scores
|
||||
.iter()
|
||||
|
@ -537,7 +545,10 @@ 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);
|
||||
debug!(
|
||||
"{} vs {} -> {} vs {}",
|
||||
left_nn_id, right_nn_id, left_score, right_score
|
||||
);
|
||||
|
||||
drop(permit);
|
||||
|
||||
|
@ -734,7 +745,11 @@ fn should_continue(scores: &[HashMap<u64, f32>], lenience: u64) -> Result<bool,
|
|||
|
||||
debug!(
|
||||
"Highest Q3 value: {} at generation {}, Highest Median value: {} at generation {}, Continuing? {}",
|
||||
highest_q3_value, generation_with_highest_q3 + 1, highest_median, generation_with_highest_median + 1, result
|
||||
highest_q3_value,
|
||||
generation_with_highest_q3 + 1,
|
||||
highest_median,
|
||||
generation_with_highest_median + 1,
|
||||
result
|
||||
);
|
||||
|
||||
Ok(result)
|
||||
|
@ -826,10 +841,7 @@ async fn run_1v1_simulation(
|
|||
|
||||
trace!(
|
||||
"Executing the following command {} {} {} {}",
|
||||
GAME_EXECUTABLE_PATH,
|
||||
config1_arg,
|
||||
config2_arg,
|
||||
disable_unreal_rendering_arg
|
||||
GAME_EXECUTABLE_PATH, config1_arg, config2_arg, disable_unreal_rendering_arg
|
||||
);
|
||||
|
||||
trace!("Running simulation for {} vs {}", nn_1_id, nn_2_id);
|
||||
|
@ -906,8 +918,7 @@ async fn read_score_from_file(file_path: &Path, nn_id: &str) -> Result<f32, io::
|
|||
"NN ID not found in scores file",
|
||||
));
|
||||
}
|
||||
Err(_) =>
|
||||
{
|
||||
Err(_) => {
|
||||
if attempts >= 2 {
|
||||
// Attempt 5 times before giving up.
|
||||
return Ok(-100.0);
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use std::{cmp::min, cmp::Ordering, collections::HashMap, ops::Range};
|
||||
use std::{cmp::Ordering, cmp::min, collections::HashMap, ops::Range};
|
||||
|
||||
use anyhow::Context;
|
||||
use fann::{ActivationFunc, Fann};
|
||||
use gemla::error::Error;
|
||||
use rand::{
|
||||
Rng,
|
||||
distr::{Distribution, Uniform},
|
||||
rng,
|
||||
seq::IteratorRandom,
|
||||
rng, Rng,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -208,13 +209,37 @@ pub fn consolidate_old_connections(
|
|||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
|
@ -222,10 +247,7 @@ pub fn consolidate_old_connections(
|
|||
} else {
|
||||
trace!(
|
||||
"Connection not found for ({}, {}) -> ({}, {})",
|
||||
previous_new_id,
|
||||
new_id,
|
||||
previous_neuron_id,
|
||||
neuron_id
|
||||
previous_new_id, new_id, previous_neuron_id, neuron_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -317,23 +339,43 @@ pub fn consolidate_old_connections(
|
|||
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);
|
||||
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);
|
||||
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);
|
||||
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
|
||||
bias_neuron, neuron_id, bias_neuron, translated_neuron_id, is_primary
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -388,8 +430,7 @@ pub fn crossbreed_neuron_arrays(
|
|||
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
|
||||
|
||||
match layer.cmp(¤t_layer)
|
||||
{
|
||||
match layer.cmp(¤t_layer) {
|
||||
Ordering::Equal => {
|
||||
new_neurons.push((*neuron_id, is_primary, current_layer, 0));
|
||||
|
||||
|
@ -399,7 +440,7 @@ pub fn crossbreed_neuron_arrays(
|
|||
secondary_last_layer = current_layer;
|
||||
}
|
||||
}
|
||||
Ordering:: Less => {
|
||||
Ordering::Less => {
|
||||
// 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
|
||||
|
@ -455,7 +496,12 @@ pub fn crossbreed_neuron_arrays(
|
|||
.filter(|(id, l)| id > &highest_id.0 && *l == layer - 1)
|
||||
.collect::<Vec<_>>();
|
||||
for (neuron_id, _) in neurons_to_add {
|
||||
new_neurons.push((*neuron_id, is_primary, current_layer, 0));
|
||||
new_neurons.push((
|
||||
*neuron_id,
|
||||
is_primary,
|
||||
current_layer,
|
||||
0,
|
||||
));
|
||||
|
||||
if is_primary {
|
||||
primary_last_layer = current_layer;
|
||||
|
|
|
@ -68,4 +68,4 @@ fn main() -> Result<()> {
|
|||
|
||||
info!("Finished in {:?}", now.elapsed());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,19 +100,5 @@ def visualize_fann_network(network_file):
|
|||
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)
|
Loading…
Add table
Reference in a new issue