Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add seed to make the simulation repeatable #7

Merged
merged 1 commit into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions src/main/kotlin/backend/GenMutator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,31 @@ import backend.model.Genome
import kotlin.random.Random

class GenMutator(private val config: Config) {
fun combine(genome1: Genome, genome2: Genome, ratio: Double): Genome {
val numberOfGenes = (ratio * config.genomeLength).toInt()
private val random = Random(config.seed)
fun combine(genome1: Genome, genome2: Genome, ratio: Double): Genome = with(config) {
val numberOfGenes = (ratio * genomeLength).toInt()

val newGenes = (
if (Random.nextBoolean()) genome1.take(numberOfGenes) + genome2.drop(numberOfGenes)
else genome2.dropLast(numberOfGenes) + genome1.takeLast(numberOfGenes)
).toMutableList()
if (random.nextBoolean()) genome1.take(numberOfGenes) + genome2.drop(numberOfGenes)
else genome2.dropLast(numberOfGenes) + genome1.takeLast(numberOfGenes)
).toMutableList()

val numberOfMutations = Random.nextInt(config.minMutations, config.maxMutations + 1)
val switchMutations = (numberOfMutations * config.mutationVariant).toInt()
val numberOfMutations = random.nextInt(minMutations, maxMutations + 1)
val switchMutations = (numberOfMutations * mutationVariant).toInt()

generateSequence { Random.nextInt(config.genomeLength) }
generateSequence { random.nextInt(genomeLength) }
.distinct()
.take(2 * switchMutations)
.windowed(2)
.forEach { indicators ->
newGenes[indicators[0]] = newGenes[indicators[1]].also { newGenes[indicators[1]] = newGenes[indicators[0]] }
}

generateSequence { Random.nextInt(config.genomeLength) }
generateSequence { random.nextInt(genomeLength) }
.distinct()
.take(numberOfMutations - switchMutations)
.forEach { newGenes[it] = Gen.random() }
.forEach { newGenes[it] = Gen.random(random) }

return Genome(newGenes)
return Genome(newGenes, random.nextInt(genomeLength))
}
}
45 changes: 34 additions & 11 deletions src/main/kotlin/backend/config/Config.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package backend.config

import backend.config.ConfigField.Companion.default
import backend.config.GenomeGroup.*
import backend.config.MapGroup.*
import backend.config.PlantGroup.*
import backend.config.PlantGrowthVariant.EQUATOR
import backend.config.StatisticsGroup.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import tornadofx.*
import java.io.File
import kotlin.random.Random
import kotlin.reflect.full.companionObjectInstance
import kotlin.reflect.full.createInstance

Expand All @@ -26,6 +31,7 @@ data class Config(
val maxMutations: Int,
val mutationVariant: Double,
val genomeLength: Int,
val seed: Long,

val births: Boolean,
val deaths: Boolean,
Expand Down Expand Up @@ -73,6 +79,7 @@ data class Config(
genomes = false,
csvExportEnabled = false,
filename = "",
seed = 2137,
)
val debug = Config(
mapWidth = 5,
Expand All @@ -99,22 +106,23 @@ data class Config(
genomes = false,
csvExportEnabled = false,
filename = "stat.csv",
seed = 2137,
)
val default = Config(
mapWidth = default<MapGroup.MapWidth>().value,
mapHeight = default<MapGroup.MapHeight>().value,
initialPlants = default<PlantGroup.InitialPlants>().value,
nutritionScore = default<PlantGroup.NutritionScore>().value,
plantsPerDay = default<PlantGroup.PlantsPerDay>().value,
plantGrowthVariant = default<PlantGroup.PlantGrowthVariantField>().value,
mapWidth = default<MapWidth>().value,
mapHeight = default<MapHeight>().value,
initialPlants = default<InitialPlants>().value,
nutritionScore = default<NutritionScore>().value,
plantsPerDay = default<PlantsPerDay>().value,
plantGrowthVariant = default<PlantGrowthVariantField>().value,
initialAnimals = default<AnimalGroup.InitialAnimals>().value,
initialAnimalEnergy = default<AnimalGroup.InitialAnimalEnergy>().value,
satietyEnergy = default<AnimalGroup.SatietyEnergy>().value,
reproductionEnergyRatio = default<GenomeGroup.ReproductionEnergyRatio>().value,
minMutations = default<GenomeGroup.MinMutations>().value,
maxMutations = default<GenomeGroup.MaxMutations>().value,
mutationVariant = default<GenomeGroup.MutationVariant>().value,
genomeLength = default<GenomeGroup.GenomeLength>().value,
reproductionEnergyRatio = default<ReproductionEnergyRatio>().value,
minMutations = default<MinMutations>().value,
maxMutations = default<MaxMutations>().value,
mutationVariant = default<MutationVariant>().value,
genomeLength = default<GenomeLength>().value,
births = default<Births>().value,
deaths = default<Deaths>().value,
population = default<Population>().value,
Expand All @@ -125,6 +133,7 @@ data class Config(
genomes = default<Genomes>().value,
csvExportEnabled = default<CsvExportEnabled>().value,
filename = default<Filename>().value,
seed = default<Seed>().value,
)

fun fromFile(file: File) = Json.decodeFromString<Config>(file.readText()) // catch exceptions
Expand Down Expand Up @@ -166,6 +175,7 @@ sealed class ConfigField<out T : Any>(
class MapGroup(
val mapWidth: MapWidth = default(),
val mapHeight: MapHeight = default(),
val seed: Seed = default(),
) {

class MapWidth(
Expand Down Expand Up @@ -193,6 +203,19 @@ class MapGroup(
override fun isValid(it: String): Boolean = it.isInt() && it.toInt() in 0..1000
}
}

class Seed(
seed: Long = Random.nextLong(),
) : ConfigField<Long>(
seed,
) {
companion object : ConfigFieldInfo<Long>() {
override val label = "Seed"
override val description = "Seed for random generator"
override val errorMessage = "Must be Long type"
override fun isValid(it: String): Boolean = it.isLong()
}
}
}

class PlantGroup(
Expand Down
119 changes: 0 additions & 119 deletions src/main/kotlin/backend/config/StatisticsConfig.kt

This file was deleted.

120 changes: 120 additions & 0 deletions src/main/kotlin/backend/config/StatisticsGroup.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package backend.config

import backend.config.ConfigField.Companion.default

data class StatisticsGroup(
val births: Births = default(),
val deaths: Deaths = default(),
val population: Population = default(),
val plantDensity: PlantDensity = default(),
val dailyAverageEnergy: DailyAverageEnergy = default(),
val dailyAverageAge: DailyAverageAge = default(),
val gens: Gens = default(),
val genomes: Genomes = default(),
val csvExportEnabled: CsvExportEnabled = default(),
val filename: Filename = default(),
) {
init {
require(!csvExportEnabled.value || Filename.isValid(filename.value)) {
"Filename must be valid when csv export is enabled"
}
}


abstract class BooleanConfigFieldInfo : ConfigFieldInfo<Boolean>() {
override val errorMessage: String = ""
override fun isValid(it: String) = true
}

class Births(
births: Boolean = true,
) : ConfigField<Boolean>(births) {
companion object : BooleanConfigFieldInfo() {
override val label = "Births"
override val description = "Count births dailt"
}
}

class Deaths(
deaths: Boolean = true,
) : ConfigField<Boolean>(deaths) {
companion object : BooleanConfigFieldInfo() {
override val label = "Deaths"
override val description = "Count deaths daily"
}
}

class Population(
population: Boolean = true,
) : ConfigField<Boolean>(population) {
companion object : BooleanConfigFieldInfo() {
override val label = "Population"
override val description = "Count population daily"
}
}

class PlantDensity(
plantDensity: Boolean = true,
) : ConfigField<Boolean>(plantDensity) {
companion object : BooleanConfigFieldInfo() {
override val label = "Plant Density"
override val description = "Count plant density daily"
}
}

class DailyAverageEnergy(
dailyAverageEnergy: Boolean = true,
) : ConfigField<Boolean>(dailyAverageEnergy) {
companion object : BooleanConfigFieldInfo() {
override val label = "Daily Average Energy"
override val description = "Count daily average energy"
}
}

class DailyAverageAge(
dailyAverageAge: Boolean = true,
) : ConfigField<Boolean>(dailyAverageAge) {
companion object : BooleanConfigFieldInfo() {
override val label = "Daily Average Age"
override val description = "Count daily average age"
}
}

class Gens(
gens: Boolean = true,
) : ConfigField<Boolean>(gens) {
companion object : BooleanConfigFieldInfo() {
override val label = "Gens"
override val description = "Count gens daily"
}
}

class Genomes(
genomes: Boolean = true,
) : ConfigField<Boolean>(genomes) {
companion object : BooleanConfigFieldInfo() {
override val label = "Genomes"
override val description = "Count genomes daily"
}
}

class CsvExportEnabled(
csvExportEnabled: Boolean = false,
) : ConfigField<Boolean>(csvExportEnabled) {
companion object : BooleanConfigFieldInfo() {
override val label = "CSV Export Enabled"
override val description = "Export simulation statistics to CSV file"
}
}

class Filename(
filename: String = "simulation.csv",
) : ConfigField<String>(filename) {
companion object : ConfigFieldInfo<String>() {
override val label = "Filename"
override val description = "Filename to save simulation statistics"
override val errorMessage = "Must be valid csv file"
override fun isValid(it: String) = it.endsWith(".csv") && it.trim().length > 4
}
}
}
Loading
Loading