<commit_before>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::marker::PhantomData;

use sfml::graphics::Color;
use sfml::system::Vector2f;

use specs::{
    Entities,
    LazyUpdate,
    Read,
    Write,
};
use rand::{
    Rng,
    distributions::{
        Bernoulli,
        Distribution,
        Exp,
        Normal,
        Uniform,
    },
};

use xsecurelock_saver::engine::{
    resources::scene::SceneLoader,
    components::{
        draw::{
            DrawColor,
            DrawShape,
            ShapeType,
        },
        scene::InScene,
        physics::{
            ForceAccumulator,
            Mass,
            Position,
            Rotation,
            Vector,
            Velocity,
        },
    },
};

use circle_collision::CircleCollider;
use gravity::{GravitySource, GravityTarget};

use collision;

use model::{Planet, Scenario, World};
use statustracker::ActiveWorld;
use storage::Storage;
use config::{
    generator::{
        GeneratorConfig,
        MutationParameters,
        NewPlanetParameters,
        NewWorldParameters,
        PlanetMutationParameters,
    },
    util::{
        Distribution as ConfDist,
        ExponentialDistribution,
        NormalDistribution,
        UniformDistribution,
    },
};
use self::per_thread_rng::PerThreadRng;

pub mod per_thread_rng;

pub struct WorldGenerator<T, R = PerThreadRng> {
    rng: R,
    _phantom_storage_type: PhantomData<T>,
}

impl<T, R> WorldGenerator<T, R> {
    /// Constructs a new WorldGenerator with the given RNG.
    #[allow(dead_code)]
    fn new(rng: R) -> Self {
        WorldGenerator {
            rng,
            _phantom_storage_type: PhantomData,
        }
    }
}

impl<T> Default for WorldGenerator<T, PerThreadRng> {
    fn default() -> Self {
        WorldGenerator {
            rng: PerThreadRng,
            _phantom_storage_type: PhantomData,
        }
    }
}

impl<'a, T> SceneLoader<'a> for WorldGenerator<T>
where T: Storage + Default + Send + Sync + 'static 
{
    type SystemData = (
        Entities<'a>,
        Read<'a, GeneratorConfig>,
        Read<'a, LazyUpdate>,
        Write<'a, T>,
        Write<'a, ActiveWorld>,
    );

    fn load(&mut self, (entities, config, lazy, mut storage, mut status,): Self::SystemData) {
        let parent = self.pick_scenario(&mut *storage, &*config);
        let new_world = match parent {
            Some(ref parent) => self.generate_child_world(
                &parent.world, &config.mutation_parameters),
            None => self.generate_new_world(&config.new_world_parameters),
        };

        for planet in new_world.planets.iter() {
            let radius = planet.radius();
            let color = self.generate_random_color();
            lazy.create_entity(&entities)
                .with(InScene)
                // Drawing
                .with(DrawColor {
                    fill_color: color,
                    outline_color: color,
                    outline_thickness: 0.,
                })
                .with(DrawShape {
                    shape_type: ShapeType::Circle {
                        radius,
                        point_count: radius_to_point_count(radius),
                    },
                    origin: Vector2f::new(radius, radius),
                })
                // Physics
                .with(Position::new(planet.position, Rotation::from_angle(0.)))
                .with(Velocity {
                    linear: planet.velocity,
                    angular: Rotation::from_angle(0.),
                })
                .with(Mass {
                    linear: planet.mass,
                    angular: 1.,
                })
                .with(ForceAccumulator::default())
                .with(GravitySource)
                .with(GravityTarget)
                .with(CircleCollider::new_in_layer(radius, collision::planet()))
                .build();
        }

        status.world = new_world;
        status.parent = parent;
        status.cumulative_score = 0.;
        status.ticks_completed = 0;
    }
}

impl<T: Storage, R: Rng> WorldGenerator<T, R> {
    /// Picks a scenario to mutate or None if a new scenario should be generated.
    fn pick_scenario(&mut self, storage: &mut T, config: &GeneratorConfig) -> Option<Scenario> {
        let num_scenarios = match storage.num_scenarios() {
            Ok(ns) if ns < 0 => {
                error!("Unexpected negative number of scenarios: {}", ns);
                return None;
            },
            Ok(ns) if ns == 0 => {
                info!("No existing scenarios to mutate, generating new one by default");
                return None;
            }
            Ok(ns) => ns,
            Err(err) => {
                error!("Error getting number of scenarios: {}", err);
                return None;
            },
        };
        let picked_scenario = self.select_index(num_scenarios, config);
        match storage.get_nth_scenario_by_score(picked_scenario) {
            Ok(Some(scenario)) => {
                info!(
                    "Mutating Scenario {} (parent: {:?}, family: {}, generation: {})",
                    scenario.id,
                    scenario.parent,
                    scenario.family,
                    scenario.generation,
                );
                Some(scenario)
            },
            Ok(None) => {
                info!("Generating new Scenario");
                None
            },
            Err(err) => {
                error!(
                    "Generating new Scenario because of error fetching scenario {}: {}",
                    picked_scenario,<commit_msg>log a little more informaton about scenarios being modified<commit_after>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::marker::PhantomData;

use sfml::graphics::Color;
use sfml::system::Vector2f;

use specs::{
    Entities,
    LazyUpdate,
    Read,
    Write,
};
use rand::{
    Rng,
    distributions::{
        Bernoulli,
        Distribution,
        Exp,
        Normal,
        Uniform,
    },
};

use xsecurelock_saver::engine::{
    resources::scene::SceneLoader,
    components::{
        draw::{
            DrawColor,
            DrawShape,
            ShapeType,
        },
        scene::InScene,
        physics::{
            ForceAccumulator,
            Mass,
            Position,
            Rotation,
            Vector,
            Velocity,
        },
    },
};

use circle_collision::CircleCollider;
use gravity::{GravitySource, GravityTarget};

use collision;

use model::{Planet, Scenario, World};
use statustracker::ActiveWorld;
use storage::Storage;
use config::{
    generator::{
        GeneratorConfig,
        MutationParameters,
        NewPlanetParameters,
        NewWorldParameters,
        PlanetMutationParameters,
    },
    util::{
        Distribution as ConfDist,
        ExponentialDistribution,
        NormalDistribution,
        UniformDistribution,
    },
};
use self::per_thread_rng::PerThreadRng;

pub mod per_thread_rng;

pub struct WorldGenerator<T, R = PerThreadRng> {
    rng: R,
    _phantom_storage_type: PhantomData<T>,
}

impl<T, R> WorldGenerator<T, R> {
    /// Constructs a new WorldGenerator with the given RNG.
    #[allow(dead_code)]
    fn new(rng: R) -> Self {
        WorldGenerator {
            rng,
            _phantom_storage_type: PhantomData,
        }
    }
}

impl<T> Default for WorldGenerator<T, PerThreadRng> {
    fn default() -> Self {
        WorldGenerator {
            rng: PerThreadRng,
            _phantom_storage_type: PhantomData,
        }
    }
}

impl<'a, T> SceneLoader<'a> for WorldGenerator<T>
where T: Storage + Default + Send + Sync + 'static 
{
    type SystemData = (
        Entities<'a>,
        Read<'a, GeneratorConfig>,
        Read<'a, LazyUpdate>,
        Write<'a, T>,
        Write<'a, ActiveWorld>,
    );

    fn load(&mut self, (entities, config, lazy, mut storage, mut status,): Self::SystemData) {
        let parent = self.pick_scenario(&mut *storage, &*config);
        let new_world = match parent {
            Some(ref parent) => self.generate_child_world(
                &parent.world, &config.mutation_parameters),
            None => self.generate_new_world(&config.new_world_parameters),
        };

        for planet in new_world.planets.iter() {
            let radius = planet.radius();
            let color = self.generate_random_color();
            lazy.create_entity(&entities)
                .with(InScene)
                // Drawing
                .with(DrawColor {
                    fill_color: color,
                    outline_color: color,
                    outline_thickness: 0.,
                })
                .with(DrawShape {
                    shape_type: ShapeType::Circle {
                        radius,
                        point_count: radius_to_point_count(radius),
                    },
                    origin: Vector2f::new(radius, radius),
                })
                // Physics
                .with(Position::new(planet.position, Rotation::from_angle(0.)))
                .with(Velocity {
                    linear: planet.velocity,
                    angular: Rotation::from_angle(0.),
                })
                .with(Mass {
                    linear: planet.mass,
                    angular: 1.,
                })
                .with(ForceAccumulator::default())
                .with(GravitySource)
                .with(GravityTarget)
                .with(CircleCollider::new_in_layer(radius, collision::planet()))
                .build();
        }

        status.world = new_world;
        status.parent = parent;
        status.cumulative_score = 0.;
        status.ticks_completed = 0;
    }
}

impl<T: Storage, R: Rng> WorldGenerator<T, R> {
    /// Picks a scenario to mutate or None if a new scenario should be generated.
    fn pick_scenario(&mut self, storage: &mut T, config: &GeneratorConfig) -> Option<Scenario> {
        let num_scenarios = match storage.num_scenarios() {
            Ok(ns) if ns < 0 => {
                error!("Unexpected negative number of scenarios: {}", ns);
                return None;
            },
            Ok(ns) if ns == 0 => {
                info!("No existing scenarios to mutate, generating new one by default");
                return None;
            }
            Ok(ns) => ns,
            Err(err) => {
                error!("Error getting number of scenarios: {}", err);
                return None;
            },
        };
        let picked_scenario = self.select_index(num_scenarios, config);
        match storage.get_nth_scenario_by_score(picked_scenario) {
            Ok(Some(scenario)) => {
                info!(
                    "Mutating Scenario {} (parent: {:?}, family: {}, generation: {}, score: {}, \
                    planets: {})",
                    scenario.id,
                    scenario.parent,
                    scenario.family,
                    scenario.generation,
                    scenario.score,
                    scenario.world.planets.len(),
                );
                Some(scenario)
            },
            Ok(None) => {
                info!("Generating new Scenario");
                None
            },
            Err(err) => {
                error!(
                    "Generating new Scenario because of error fetching scenario {}: {}",
                    picked_scenario,
{'fraction_non_alphanumeric': 0.1064932992980217, 'fraction_numerical': 0.002871729419272495, 'mean_word_length': 1.669150521609538, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2550618', 'n_tokens_mistral': 3442, 'n_tokens_neox': 3344, 'n_words': 831}