- Теперь можно убивать клетки за пределами поля
- И эксперименты теперь более глубокие и отражают более реальные значения за счёт кол-ва ранов 100 на каждую плотность (шаг 0.05)
This commit is contained in:
@@ -9,7 +9,8 @@ data class AppConfig(
|
|||||||
val gridHeight: Int = 50,
|
val gridHeight: Int = 50,
|
||||||
val simulationSpeed: Int = 10,
|
val simulationSpeed: Int = 10,
|
||||||
val probability: Double = 0.5,
|
val probability: Double = 0.5,
|
||||||
val oneDimensionalRule: OneDimensionalRule = OneDimensionalRule.RULE_30
|
val oneDimensionalRule: OneDimensionalRule = OneDimensionalRule.RULE_30,
|
||||||
|
val killAtBoundary: Boolean = true
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ package model
|
|||||||
|
|
||||||
class GameOfLife(
|
class GameOfLife(
|
||||||
val width: Int,
|
val width: Int,
|
||||||
val height: Int
|
val height: Int,
|
||||||
|
val killAtBoundary: Boolean = true
|
||||||
) {
|
) {
|
||||||
private var grid: Array<Array<Boolean>> = Array(height) { Array(width) { false } }
|
private var grid: Array<Array<Boolean>> = Array(height) { Array(width) { false } }
|
||||||
private var generation: Int = 0
|
private var generation: Int = 0
|
||||||
@@ -56,9 +57,18 @@ class GameOfLife(
|
|||||||
for (dy in -1..1) {
|
for (dy in -1..1) {
|
||||||
for (dx in -1..1) {
|
for (dx in -1..1) {
|
||||||
if (dx == 0 && dy == 0) continue
|
if (dx == 0 && dy == 0) continue
|
||||||
val nx = (x + dx + width) % width
|
|
||||||
val ny = (y + dy + height) % height
|
if (killAtBoundary) {
|
||||||
if (grid[ny][nx]) count++
|
val nx = x + dx
|
||||||
|
val ny = y + dy
|
||||||
|
if (nx in 0 until width && ny in 0 until height && grid[ny][nx]) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val nx = (x + dx + width) % width
|
||||||
|
val ny = (y + dy + height) % height
|
||||||
|
if (grid[ny][nx]) count++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count
|
return count
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import androidx.compose.material3.*
|
|||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -22,7 +23,7 @@ fun Sidebar(
|
|||||||
config: AppConfig,
|
config: AppConfig,
|
||||||
onConfigChange: (AppConfig) -> Unit,
|
onConfigChange: (AppConfig) -> Unit,
|
||||||
onRandomize: () -> Unit = {},
|
onRandomize: () -> Unit = {},
|
||||||
onClear: () -> Unit = {}
|
onClear: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
var isRunning by remember { mutableStateOf(false) }
|
var isRunning by remember { mutableStateOf(false) }
|
||||||
var showExperimentResults by remember { mutableStateOf(false) }
|
var showExperimentResults by remember { mutableStateOf(false) }
|
||||||
@@ -31,17 +32,18 @@ fun Sidebar(
|
|||||||
val primaryColor = MaterialTheme.colorScheme.primary
|
val primaryColor = MaterialTheme.colorScheme.primary
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier =
|
||||||
.width(280.dp)
|
Modifier
|
||||||
.fillMaxHeight()
|
.width(280.dp)
|
||||||
.padding(16.dp)
|
.fillMaxHeight()
|
||||||
.verticalScroll(rememberScrollState()),
|
.padding(16.dp)
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
.verticalScroll(rememberScrollState()),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
"Настройки",
|
"Настройки",
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
color = primaryColor
|
color = primaryColor,
|
||||||
)
|
)
|
||||||
|
|
||||||
HorizontalDivider(color = primaryColor.copy(alpha = 0.3f))
|
HorizontalDivider(color = primaryColor.copy(alpha = 0.3f))
|
||||||
@@ -49,22 +51,22 @@ fun Sidebar(
|
|||||||
Text(
|
Text(
|
||||||
"Режим автомата",
|
"Режим автомата",
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
color = primaryColor
|
color = primaryColor,
|
||||||
)
|
)
|
||||||
SelectAutomatonMode(
|
SelectAutomatonMode(
|
||||||
mode = config.mode,
|
mode = config.mode,
|
||||||
onModeChange = { onConfigChange(config.copy(mode = it)) }
|
onModeChange = { onConfigChange(config.copy(mode = it)) },
|
||||||
)
|
)
|
||||||
|
|
||||||
if (config.mode == AutomatonMode.ONE_DIMENSIONAL) {
|
if (config.mode == AutomatonMode.ONE_DIMENSIONAL) {
|
||||||
Text(
|
Text(
|
||||||
"Правило",
|
"Правило",
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
color = primaryColor
|
color = primaryColor,
|
||||||
)
|
)
|
||||||
SelectRule(
|
SelectRule(
|
||||||
rule = config.oneDimensionalRule,
|
rule = config.oneDimensionalRule,
|
||||||
onRuleChange = { onConfigChange(config.copy(oneDimensionalRule = it)) }
|
onRuleChange = { onConfigChange(config.copy(oneDimensionalRule = it)) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,13 +75,13 @@ fun Sidebar(
|
|||||||
Text(
|
Text(
|
||||||
"Размер поля",
|
"Размер поля",
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
color = primaryColor
|
color = primaryColor,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (config.mode == AutomatonMode.GAME_OF_LIFE) {
|
if (config.mode == AutomatonMode.GAME_OF_LIFE) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = config.gridWidth.toString(),
|
value = config.gridWidth.toString(),
|
||||||
@@ -90,13 +92,14 @@ fun Sidebar(
|
|||||||
label = { Text("Ширина", color = primaryColor) },
|
label = { Text("Ширина", color = primaryColor) },
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
colors = OutlinedTextFieldDefaults.colors(
|
colors =
|
||||||
focusedTextColor = primaryColor,
|
OutlinedTextFieldDefaults.colors(
|
||||||
unfocusedTextColor = primaryColor,
|
focusedTextColor = primaryColor,
|
||||||
focusedBorderColor = primaryColor,
|
unfocusedTextColor = primaryColor,
|
||||||
unfocusedBorderColor = primaryColor.copy(alpha = 0.5f),
|
focusedBorderColor = primaryColor,
|
||||||
cursorColor = primaryColor
|
unfocusedBorderColor = primaryColor.copy(alpha = 0.5f),
|
||||||
)
|
cursorColor = primaryColor,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = config.gridHeight.toString(),
|
value = config.gridHeight.toString(),
|
||||||
@@ -107,13 +110,14 @@ fun Sidebar(
|
|||||||
label = { Text("Высота", color = primaryColor) },
|
label = { Text("Высота", color = primaryColor) },
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
colors = OutlinedTextFieldDefaults.colors(
|
colors =
|
||||||
focusedTextColor = primaryColor,
|
OutlinedTextFieldDefaults.colors(
|
||||||
unfocusedTextColor = primaryColor,
|
focusedTextColor = primaryColor,
|
||||||
focusedBorderColor = primaryColor,
|
unfocusedTextColor = primaryColor,
|
||||||
unfocusedBorderColor = primaryColor.copy(alpha = 0.5f),
|
focusedBorderColor = primaryColor,
|
||||||
cursorColor = primaryColor
|
unfocusedBorderColor = primaryColor.copy(alpha = 0.5f),
|
||||||
)
|
cursorColor = primaryColor,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -126,13 +130,14 @@ fun Sidebar(
|
|||||||
label = { Text("Ширина (ячеек)", color = primaryColor) },
|
label = { Text("Ширина (ячеек)", color = primaryColor) },
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
colors = OutlinedTextFieldDefaults.colors(
|
colors =
|
||||||
focusedTextColor = primaryColor,
|
OutlinedTextFieldDefaults.colors(
|
||||||
unfocusedTextColor = primaryColor,
|
focusedTextColor = primaryColor,
|
||||||
focusedBorderColor = primaryColor,
|
unfocusedTextColor = primaryColor,
|
||||||
unfocusedBorderColor = primaryColor.copy(alpha = 0.5f),
|
focusedBorderColor = primaryColor,
|
||||||
cursorColor = primaryColor
|
unfocusedBorderColor = primaryColor.copy(alpha = 0.5f),
|
||||||
)
|
cursorColor = primaryColor,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +146,7 @@ fun Sidebar(
|
|||||||
Text(
|
Text(
|
||||||
"Скорость симуляции",
|
"Скорость симуляции",
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
color = primaryColor
|
color = primaryColor,
|
||||||
)
|
)
|
||||||
Slider(
|
Slider(
|
||||||
value = config.simulationSpeed.toFloat(),
|
value = config.simulationSpeed.toFloat(),
|
||||||
@@ -149,16 +154,17 @@ fun Sidebar(
|
|||||||
valueRange = 1f..60f,
|
valueRange = 1f..60f,
|
||||||
steps = 58,
|
steps = 58,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
colors = SliderDefaults.colors(
|
colors =
|
||||||
thumbColor = primaryColor,
|
SliderDefaults.colors(
|
||||||
activeTrackColor = primaryColor,
|
thumbColor = primaryColor,
|
||||||
inactiveTrackColor = primaryColor.copy(alpha = 0.3f)
|
activeTrackColor = primaryColor,
|
||||||
)
|
inactiveTrackColor = primaryColor.copy(alpha = 0.3f),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
"${config.simulationSpeed} FPS",
|
"${config.simulationSpeed} FPS",
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = primaryColor
|
color = primaryColor,
|
||||||
)
|
)
|
||||||
|
|
||||||
HorizontalDivider(color = primaryColor.copy(alpha = 0.3f))
|
HorizontalDivider(color = primaryColor.copy(alpha = 0.3f))
|
||||||
@@ -167,23 +173,55 @@ fun Sidebar(
|
|||||||
Text(
|
Text(
|
||||||
"Вероятность заполнения",
|
"Вероятность заполнения",
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
color = primaryColor
|
color = primaryColor,
|
||||||
)
|
)
|
||||||
Slider(
|
Slider(
|
||||||
value = config.probability.toFloat(),
|
value = config.probability.toFloat(),
|
||||||
onValueChange = { onConfigChange(config.copy(probability = it.toDouble())) },
|
onValueChange = { onConfigChange(config.copy(probability = it.toDouble())) },
|
||||||
valueRange = 0.0f..1.0f,
|
valueRange = 0.0f..1.0f,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
colors = SliderDefaults.colors(
|
colors =
|
||||||
thumbColor = primaryColor,
|
SliderDefaults.colors(
|
||||||
activeTrackColor = primaryColor,
|
thumbColor = primaryColor,
|
||||||
inactiveTrackColor = primaryColor.copy(alpha = 0.3f)
|
activeTrackColor = primaryColor,
|
||||||
)
|
inactiveTrackColor = primaryColor.copy(alpha = 0.3f),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
"${(config.probability * 100).toInt()}%",
|
"${(config.probability * 100).toInt()}%",
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = primaryColor
|
color = primaryColor,
|
||||||
|
)
|
||||||
|
|
||||||
|
HorizontalDivider(color = primaryColor.copy(alpha = 0.3f))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
"Граничные условия",
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
color = primaryColor,
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Checkbox(
|
||||||
|
checked = config.killAtBoundary,
|
||||||
|
onCheckedChange = { onConfigChange(config.copy(killAtBoundary = it)) },
|
||||||
|
colors = CheckboxDefaults.colors(
|
||||||
|
checkedColor = primaryColor,
|
||||||
|
uncheckedColor = primaryColor,
|
||||||
|
checkmarkColor = Color.Black
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
"Убивать клетки на границе",
|
||||||
|
color = primaryColor,
|
||||||
|
style = MaterialTheme.typography.bodySmall
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
if (config.killAtBoundary) "Клетки за границей мертвы" else "Поле замыкается (тороид)",
|
||||||
|
color = primaryColor.copy(alpha = 0.7f),
|
||||||
|
style = MaterialTheme.typography.bodySmall
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,15 +229,16 @@ fun Sidebar(
|
|||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
) {
|
) {
|
||||||
Button(
|
Button(
|
||||||
onClick = { isRunning = !isRunning },
|
onClick = { isRunning = !isRunning },
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
colors = ButtonDefaults.buttonColors(
|
colors =
|
||||||
containerColor = MaterialTheme.colorScheme.primary,
|
ButtonDefaults.buttonColors(
|
||||||
contentColor = MaterialTheme.colorScheme.onPrimary
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
)
|
contentColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
Text(if (isRunning) "Стоп" else "Старт")
|
Text(if (isRunning) "Стоп" else "Старт")
|
||||||
}
|
}
|
||||||
@@ -207,9 +246,10 @@ fun Sidebar(
|
|||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
onClick = onClear,
|
onClick = onClear,
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
colors = ButtonDefaults.outlinedButtonColors(
|
colors =
|
||||||
contentColor = primaryColor
|
ButtonDefaults.outlinedButtonColors(
|
||||||
)
|
contentColor = primaryColor,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
Text("Сброс")
|
Text("Сброс")
|
||||||
}
|
}
|
||||||
@@ -219,10 +259,11 @@ fun Sidebar(
|
|||||||
Button(
|
Button(
|
||||||
onClick = onRandomize,
|
onClick = onRandomize,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
colors = ButtonDefaults.buttonColors(
|
colors =
|
||||||
containerColor = MaterialTheme.colorScheme.primary,
|
ButtonDefaults.buttonColors(
|
||||||
contentColor = MaterialTheme.colorScheme.onPrimary
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
)
|
contentColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
Text("Случайное заполнение")
|
Text("Случайное заполнение")
|
||||||
}
|
}
|
||||||
@@ -234,7 +275,7 @@ fun Sidebar(
|
|||||||
Text(
|
Text(
|
||||||
"Эксперименты",
|
"Эксперименты",
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
color = primaryColor
|
color = primaryColor,
|
||||||
)
|
)
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
@@ -251,10 +292,11 @@ fun Sidebar(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
colors = ButtonDefaults.buttonColors(
|
colors =
|
||||||
containerColor = MaterialTheme.colorScheme.primary,
|
ButtonDefaults.buttonColors(
|
||||||
contentColor = MaterialTheme.colorScheme.onPrimary
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
)
|
contentColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
Text("Запустить эксперименты")
|
Text("Запустить эксперименты")
|
||||||
}
|
}
|
||||||
@@ -263,7 +305,7 @@ fun Sidebar(
|
|||||||
Text(
|
Text(
|
||||||
experimentProgress,
|
experimentProgress,
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = primaryColor
|
color = primaryColor,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,9 +315,10 @@ fun Sidebar(
|
|||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
enabled = false,
|
enabled = false,
|
||||||
colors = ButtonDefaults.outlinedButtonColors(
|
colors =
|
||||||
contentColor = primaryColor.copy(alpha = 0.5f)
|
ButtonDefaults.outlinedButtonColors(
|
||||||
)
|
contentColor = primaryColor.copy(alpha = 0.5f),
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
Text("Загрузить паттерн")
|
Text("Загрузить паттерн")
|
||||||
}
|
}
|
||||||
@@ -286,9 +329,10 @@ fun Sidebar(
|
|||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
enabled = false,
|
enabled = false,
|
||||||
colors = ButtonDefaults.outlinedButtonColors(
|
colors =
|
||||||
contentColor = primaryColor.copy(alpha = 0.5f)
|
ButtonDefaults.outlinedButtonColors(
|
||||||
)
|
contentColor = primaryColor.copy(alpha = 0.5f),
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
Text("Сохранить паттерн")
|
Text("Сохранить паттерн")
|
||||||
}
|
}
|
||||||
@@ -300,55 +344,58 @@ fun Sidebar(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun SelectAutomatonMode(
|
private fun SelectAutomatonMode(
|
||||||
mode: AutomatonMode,
|
mode: AutomatonMode,
|
||||||
onModeChange: (AutomatonMode) -> Unit
|
onModeChange: (AutomatonMode) -> Unit,
|
||||||
) {
|
) {
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
val primaryColor = MaterialTheme.colorScheme.primary
|
val primaryColor = MaterialTheme.colorScheme.primary
|
||||||
|
|
||||||
ExposedDropdownMenuBox(
|
ExposedDropdownMenuBox(
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
onExpandedChange = { expanded = it }
|
onExpandedChange = { expanded = it },
|
||||||
) {
|
) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = when (mode) {
|
value =
|
||||||
AutomatonMode.GAME_OF_LIFE -> "Game of Life (2D)"
|
when (mode) {
|
||||||
AutomatonMode.ONE_DIMENSIONAL -> "1D Cellular Automaton"
|
AutomatonMode.GAME_OF_LIFE -> "Game of Life (2D)"
|
||||||
},
|
AutomatonMode.ONE_DIMENSIONAL -> "1D Cellular Automaton"
|
||||||
|
},
|
||||||
onValueChange = {},
|
onValueChange = {},
|
||||||
readOnly = true,
|
readOnly = true,
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
|
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier =
|
||||||
.menuAnchor(MenuAnchorType.PrimaryNotEditable)
|
Modifier
|
||||||
.fillMaxWidth(),
|
.menuAnchor(MenuAnchorType.PrimaryNotEditable)
|
||||||
colors = OutlinedTextFieldDefaults.colors(
|
.fillMaxWidth(),
|
||||||
focusedTextColor = primaryColor,
|
colors =
|
||||||
unfocusedTextColor = primaryColor,
|
OutlinedTextFieldDefaults.colors(
|
||||||
focusedBorderColor = primaryColor,
|
focusedTextColor = primaryColor,
|
||||||
unfocusedBorderColor = primaryColor.copy(alpha = 0.5f),
|
unfocusedTextColor = primaryColor,
|
||||||
focusedTrailingIconColor = primaryColor,
|
focusedBorderColor = primaryColor,
|
||||||
unfocusedTrailingIconColor = primaryColor
|
unfocusedBorderColor = primaryColor.copy(alpha = 0.5f),
|
||||||
)
|
focusedTrailingIconColor = primaryColor,
|
||||||
|
unfocusedTrailingIconColor = primaryColor,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
ExposedDropdownMenu(
|
ExposedDropdownMenu(
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
onDismissRequest = { expanded = false },
|
onDismissRequest = { expanded = false },
|
||||||
containerColor = MaterialTheme.colorScheme.surface
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
) {
|
) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("Game of Life (2D)", color = primaryColor) },
|
text = { Text("Game of Life (2D)", color = primaryColor) },
|
||||||
onClick = {
|
onClick = {
|
||||||
onModeChange(AutomatonMode.GAME_OF_LIFE)
|
onModeChange(AutomatonMode.GAME_OF_LIFE)
|
||||||
expanded = false
|
expanded = false
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("1D Cellular Automaton", color = primaryColor) },
|
text = { Text("1D Cellular Automaton", color = primaryColor) },
|
||||||
onClick = {
|
onClick = {
|
||||||
onModeChange(AutomatonMode.ONE_DIMENSIONAL)
|
onModeChange(AutomatonMode.ONE_DIMENSIONAL)
|
||||||
expanded = false
|
expanded = false
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -358,14 +405,14 @@ private fun SelectAutomatonMode(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun SelectRule(
|
private fun SelectRule(
|
||||||
rule: OneDimensionalRule,
|
rule: OneDimensionalRule,
|
||||||
onRuleChange: (OneDimensionalRule) -> Unit
|
onRuleChange: (OneDimensionalRule) -> Unit,
|
||||||
) {
|
) {
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
val primaryColor = MaterialTheme.colorScheme.primary
|
val primaryColor = MaterialTheme.colorScheme.primary
|
||||||
|
|
||||||
ExposedDropdownMenuBox(
|
ExposedDropdownMenuBox(
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
onExpandedChange = { expanded = it }
|
onExpandedChange = { expanded = it },
|
||||||
) {
|
) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = "Rule ${rule.value}",
|
value = "Rule ${rule.value}",
|
||||||
@@ -374,22 +421,24 @@ private fun SelectRule(
|
|||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
|
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier =
|
||||||
.menuAnchor(MenuAnchorType.PrimaryNotEditable)
|
Modifier
|
||||||
.fillMaxWidth(),
|
.menuAnchor(MenuAnchorType.PrimaryNotEditable)
|
||||||
colors = OutlinedTextFieldDefaults.colors(
|
.fillMaxWidth(),
|
||||||
focusedTextColor = primaryColor,
|
colors =
|
||||||
unfocusedTextColor = primaryColor,
|
OutlinedTextFieldDefaults.colors(
|
||||||
focusedBorderColor = primaryColor,
|
focusedTextColor = primaryColor,
|
||||||
unfocusedBorderColor = primaryColor.copy(alpha = 0.5f),
|
unfocusedTextColor = primaryColor,
|
||||||
focusedTrailingIconColor = primaryColor,
|
focusedBorderColor = primaryColor,
|
||||||
unfocusedTrailingIconColor = primaryColor
|
unfocusedBorderColor = primaryColor.copy(alpha = 0.5f),
|
||||||
)
|
focusedTrailingIconColor = primaryColor,
|
||||||
|
unfocusedTrailingIconColor = primaryColor,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
ExposedDropdownMenu(
|
ExposedDropdownMenu(
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
onDismissRequest = { expanded = false },
|
onDismissRequest = { expanded = false },
|
||||||
containerColor = MaterialTheme.colorScheme.surface
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
) {
|
) {
|
||||||
OneDimensionalRule.entries.forEach { r ->
|
OneDimensionalRule.entries.forEach { r ->
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
@@ -397,9 +446,9 @@ private fun SelectRule(
|
|||||||
onClick = {
|
onClick = {
|
||||||
onRuleChange(r)
|
onRuleChange(r)
|
||||||
expanded = false
|
expanded = false
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ fun GameOfLifeView(
|
|||||||
onActionConsumed: () -> Unit,
|
onActionConsumed: () -> Unit,
|
||||||
onConfigChange: (AppConfig) -> Unit
|
onConfigChange: (AppConfig) -> Unit
|
||||||
) {
|
) {
|
||||||
var game by remember(config.gridWidth, config.gridHeight) {
|
var game by remember(config.gridWidth, config.gridHeight, config.killAtBoundary) {
|
||||||
mutableStateOf(GameOfLife(config.gridWidth, config.gridHeight))
|
mutableStateOf(GameOfLife(config.gridWidth, config.gridHeight, config.killAtBoundary))
|
||||||
}
|
}
|
||||||
var isRunning by remember { mutableStateOf(false) }
|
var isRunning by remember { mutableStateOf(false) }
|
||||||
var generation by remember { mutableIntStateOf(0) }
|
var generation by remember { mutableIntStateOf(0) }
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ class ExperimentRunner {
|
|||||||
width: Int,
|
width: Int,
|
||||||
height: Int,
|
height: Int,
|
||||||
): List<ExperimentResult> {
|
): List<ExperimentResult> {
|
||||||
val densities = listOf(0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5)
|
val densities = generateSequence(0.1) { it + 0.05 }.takeWhile { it <= 0.5 }.toList()
|
||||||
val results = mutableListOf<ExperimentResult>()
|
val results = mutableListOf<ExperimentResult>()
|
||||||
|
|
||||||
for (density in densities) {
|
for (density in densities) {
|
||||||
repeat(10) { run ->
|
repeat(100) { run ->
|
||||||
val result = runSingleExperiment(width, height, density)
|
val result = runSingleExperiment(width, height, density)
|
||||||
results.add(result)
|
results.add(result)
|
||||||
println("Density: $density, Run: ${run + 1}, Stabilization: ${result.stabilizationTime}")
|
println("Density: $density, Run: ${run + 1}, Stabilization: ${result.stabilizationTime}")
|
||||||
|
|||||||
Reference in New Issue
Block a user