diff --git a/src/main/kotlin/backend/config/MapConfig.kt b/src/main/kotlin/backend/config/MapConfig.kt new file mode 100644 index 0000000..e5ebab3 --- /dev/null +++ b/src/main/kotlin/backend/config/MapConfig.kt @@ -0,0 +1,250 @@ +package backend.config + +import backend.config.ConfigField.Companion.default +import tornadofx.* +import kotlin.reflect.full.companionObjectInstance +import kotlin.reflect.full.createInstance +import kotlin.random.Random + +data class MapConfig( + val mapWidth: MapWidth = default(), + val mapHeight: MapHeight = default(), + val initialPlants: InitialPlants = default(), + val nutritionScore: NutritionScore = default(), + val plantsPerDay: PlantsPerDay = default(), + val plantGrowthVariant: PlantGrowthVariantField = default(), + val initialAnimals: InitialAnimals = default(), + val initialAnimalEnergy: InitialAnimalEnergy = default(), + val satietyEnergy: SatietyEnergy = default(), + val reproductionEnergyRatio: ReproductionEnergyRatio = default(), + val minMutations: MinMutations = default(), + val maxMutations: MaxMutations = default(), + val mutationVariant: MutationVariant = default(), + val genomeLength: GenomeLength = default(), +) + +abstract class ConfigFieldInfo { + abstract val label: String + abstract val description: String + abstract val errorMessage: String + + abstract fun isValid(it: String): Boolean + fun validate(it: String) = require(isValid(it)) { errorMessage } +} + +sealed class ConfigField( + val value: T, +) { + companion object { + inline fun > find() = ConfigField::class.sealedSubclasses.first { it == U::class } + + inline fun > default(): U = find().createInstance() as U + + inline fun > label() = (find().companionObjectInstance as ConfigFieldInfo<*>).label + + inline fun > description() = + (find().companionObjectInstance as ConfigFieldInfo<*>).description + + inline fun > errorMessage() = + (find().companionObjectInstance as ConfigFieldInfo<*>).errorMessage + + inline fun > validate(value: String) = + (find().companionObjectInstance as ConfigFieldInfo<*>).isValid(value) + + } +} + +class MapWidth( + mapWidth: Int = 50, +) : ConfigField( + mapWidth, +) { + companion object : ConfigFieldInfo() { + override val label = "Map width" + override val description = "Width of the map" + override val errorMessage = "Must be between 1 and 1000" + override fun isValid(it: String): Boolean = it.isInt() && it.toInt() in 1..1000 + } +} + +class MapHeight( + mapHeight: Int = 50, +) : ConfigField( + mapHeight, +) { + companion object : ConfigFieldInfo() { + override val label = "Map height" + override val description = "Height of the map" + override val errorMessage = "Must be between 1 and 1000" + override fun isValid(it: String): Boolean = it.isInt() && it.toInt() in 1..1000 + } +} + +class Seed( + seed: Int = Random.nextInt(), +) : ConfigField( + seed, +) { + companion object : ConfigFieldInfo() { + override val label = "Seed" + override val description = "Seed for random generator" + override val errorMessage = "Must be Int type" + override fun isValid(it: String): Boolean = it.isInt() + } +} + +class InitialPlants( + initialPlants: Int = 300, +) : ConfigField( + initialPlants, +) { + companion object : ConfigFieldInfo() { + override val label = "Initial plants" + override val description = "Number of plants at the beginning of the simulation" + override val errorMessage: String = "Must be greater or equal than 0" + override fun isValid(it: String): Boolean = it.isInt() && it.toInt() >= 0 + } +} + +class NutritionScore( + nutritionScore: Int = 50, +) : ConfigField( + nutritionScore, +) { + companion object : ConfigFieldInfo() { + override val label = "Nutrition score" + override val description = "Energy provided by eating one plant" + override val errorMessage: String = "Must be greater or equal than 0" + override fun isValid(it: String): Boolean = it.isInt() && it.toInt() >= 0 + } +} + +class PlantsPerDay( + plantsPerDay: Int = 200, +) : ConfigField( + plantsPerDay, +) { + companion object : ConfigFieldInfo() { + override val label = "Plants per day" + override val description = "Number of plants growing each day" + override val errorMessage: String = "Must be greater or equal than 0" + override fun isValid(it: String): Boolean = it.isInt() && it.toInt() >= 0 + } +} + +class PlantGrowthVariantField( + plantGrowthVariant: PlantGrowthVariant = PlantGrowthVariant.EQUATOR, +) : ConfigField( + plantGrowthVariant, +) { + companion object : ConfigFieldInfo() { + override val label = "Plant growth variant" + override val description = "Variant of plant growth" + override val errorMessage: String = "Must be one of ${PlantGrowthVariant.entries.map { it.name }}" + override fun isValid(it: String) = PlantGrowthVariant.entries.map { it.name }.contains(it) + } +} + +class InitialAnimals( + initialAnimals: Int = 100, +) : ConfigField( + initialAnimals, +) { + companion object : ConfigFieldInfo() { + override val label = "Initial animals" + override val description = "Number of animals at the beginning of the simulation" + override val errorMessage: String = "Must be greater or equal than 0" + override fun isValid(it: String) = it.isInt() && it.toInt() >= 0 + } +} + +class InitialAnimalEnergy( + initialAnimalEnergy: Int = 100, +) : ConfigField( + initialAnimalEnergy, +) { + companion object : ConfigFieldInfo() { + override val label = "Initial animal energy" + override val description = "Initial energy of animals" + override val errorMessage: String = "Must be greater than 0" + override fun isValid(it: String) = it.isInt() && it.toInt() > 0 + } +} + +class SatietyEnergy( + satietyEnergy: Int = 50, +) : ConfigField( + satietyEnergy, +) { + companion object : ConfigFieldInfo() { + override val label = "Satiety energy" + override val description = "Energy required to consider an animal full (and ready to reproduce)" + override val errorMessage: String = "Must be greater than 0" + override fun isValid(it: String) = it.isInt() && it.toInt() > 0 + } +} + +class ReproductionEnergyRatio( + reproductionEnergyRatio: Double = .5, +) : ConfigField( + reproductionEnergyRatio, +) { + companion object : ConfigFieldInfo() { + override val label = "Reproduction energy ratio" + override val description = "Proportion of energy used by parents to create offspring" + override val errorMessage: String = "Must be between 0 and 1" + override fun isValid(it: String) = it.isDouble() && it.toDouble() in 0.0..1.0 + } +} + +class MinMutations( + minMutations: Int = 0, +) : ConfigField( + minMutations, +) { + companion object : ConfigFieldInfo() { + override val label = "Min mutations" + override val description = "Minimum number of mutations in offspring (can be 0)" + override val errorMessage: String = "Must be greater or equal than 0" + override fun isValid(it: String) = it.isInt() && it.toInt() >= 0 + } +} + +class MaxMutations( + maxMutations: Int = 4, +) : ConfigField( + maxMutations, +) { + companion object : ConfigFieldInfo() { + override val label = "Max mutations" + override val description = "Maximum number of mutations in offspring (can be 0)" + override val errorMessage: String = "Must be greater or equal than 0" + override fun isValid(it: String) = it.isInt() && it.toInt() >= 0 + } +} + +class MutationVariant( + mutationVariant: Double = 0.0, +) : ConfigField( + mutationVariant, +) { + companion object : ConfigFieldInfo() { + override val label = "Mutation variant" + override val description = "Chance that genes will change, when 0 then default variant" + override val errorMessage: String = "Must be between 0 and 1" + override fun isValid(it: String) = it.isDouble() && it.toDouble() in 0.0..1.0 + } +} + +class GenomeLength( + genomeLength: Int = 5, +) : ConfigField( + genomeLength, +) { + companion object : ConfigFieldInfo() { + override val label = "Genome length" + override val description = "Length of the genome" + override val errorMessage: String = "Must be greater than 0" + override fun isValid(it: String) = it.isInt() && it.toInt() > 0 + } +}