Add timer

This commit is contained in:
Christian Basler
2025-02-27 20:15:28 +01:00
committed by Christian Basler
parent 7b8d5cb244
commit f9dc62d148
8 changed files with 128 additions and 20 deletions

View File

@@ -1 +1 @@
java temurin-17.0.12+7
java temurin-21

View File

@@ -19,7 +19,7 @@ class MainActivity : ComponentActivity() {
topBar = {
}
) {insets ->
) { insets ->
App(modifier = Modifier.padding(insets))
}
}
@@ -30,4 +30,4 @@ class MainActivity : ComponentActivity() {
@Composable
fun AppAndroidPreview() {
App()
}
}

View File

@@ -18,6 +18,7 @@ import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -58,12 +59,26 @@ class DisplayClue<C : Clue>(val clue: C) {
@Composable
fun App(modifier: Modifier = Modifier, game: Game = remember { generateGame() }) {
val horizontalClues = remember { game.horizontalClues.map { DisplayClue(it) } }
val verticalClues = remember { game.verticalClues.map { DisplayClue(it) } }
val time = "00:00:00" // TODO
val horizontalClues = remember(game) { game.horizontalClues.map { DisplayClue(it) } }
val verticalClues = remember(game) { game.verticalClues.map { DisplayClue(it) } }
val timer = remember(game) { GameTimer() }
val time by timer.elapsedTime.collectAsState("00:00")
LaunchedEffect(game) {
game.onStart {
timer.start()
}
game.onSolved {
timer.stop()
}
}
Row(modifier = modifier) {
PuzzleGrid(
modifier = Modifier.aspectRatio(1f).weight(0.6f).fillMaxHeight(),
modifier = Modifier
.aspectRatio(1f)
.weight(0.6f)
.fillMaxHeight(),
grid = game.grid,
onUpdate = {
horizontalClues.forEach { it.update(game.grid) }

View File

@@ -0,0 +1,57 @@
package ch.dissem.yaep.ui.common
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
class GameTimer {
private var startTime: Long = 0L
private var stopTime: Long = 0L
val isRunning: Boolean
get() = startTime != 0L && stopTime == 0L
val elapsedSeconds: Flow<Long> = flow {
emit(0L)
while (startTime == 0L) {
delay(100)
}
var previousSeconds = 0L
while (stopTime == 0L) {
val elapsedSeconds = System.currentTimeMillis() / 1000 - startTime
if (elapsedSeconds != previousSeconds) {
emit(elapsedSeconds)
}
delay(100)
previousSeconds = elapsedSeconds
}
emit(stopTime - startTime)
}
val elapsedTime: Flow<String> = elapsedSeconds.map { seconds ->
val minutes = seconds / 60
val hours = minutes / 60
val remainingMinutes = minutes % 60
val remainingSeconds = seconds % 60
if (hours == 0L) {
"%02d:%02d".format(minutes, remainingSeconds)
} else {
"%02d:%02d:%02d".format(hours, remainingMinutes, remainingSeconds)
}
}
fun start() {
if (startTime == 0L) {
startTime = System.currentTimeMillis() / 1000
}
}
fun stop() {
if (isRunning) {
stopTime = System.currentTimeMillis() / 1000
}
}
}

View File

@@ -7,6 +7,9 @@ class Game(
val horizontalClues = clues.filterIsInstance<HorizontalClue>()
val verticalClues = clues.filterIsInstance<SameColumnClue<ItemClass<*>, ItemClass<*>>>()
private val onStartListeners = mutableListOf<() -> Unit>()
private val onSolvedListeners = mutableListOf<() -> Unit>()
init {
for (position in clues.filterIsInstance<PositionClue<ItemClass<*>>>()) {
val row = grid[position.item.itemType.companion]
@@ -18,6 +21,29 @@ class Game(
}
}
}
grid.forEach { row ->
row.forEach { cell ->
cell.optionsRemovedListeners.add {
if (onStartListeners.isNotEmpty()) {
onStartListeners.forEach { it() }
onStartListeners.clear()
}
}
}
row.onSolved {
if (onSolvedListeners.isNotEmpty() && isSolved) {
onSolvedListeners.forEach { it() }
}
}
}
}
fun onStart(listener: () -> Unit) {
onStartListeners.add(listener)
}
fun onSolved(listener: () -> Unit) {
onSolvedListeners.add(listener)
}
/**

View File

@@ -6,13 +6,22 @@ class GameRow<C : ItemClass<C>>(
val cells: List<GameCell<C>>
) : List<GameCell<C>> by cells {
var isSolved = false
private set
private set(value) {
field = value
if (value) {
onSolvedListener()
}
}
var onSolvedListener = {}
fun onSolved(listener: () -> Unit) {
onSolvedListener = listener
}
fun indexOf(element: C) = indexOfFirst { it.selection?.itemType == element }
fun cleanupOptions() {
if (
isSolved && all {
if (isSolved && all {
it.solution != null
&& it.options.size == 1
&& it.options.single() == it.selection

View File

@@ -1,14 +1,14 @@
[versions]
agp = "8.5.2"
jdk = "11"
android-compileSdk = "34"
agp = "8.8.2"
jdk = "21"
android-compileSdk = "35"
android-minSdk = "24"
android-targetSdk = "34"
androidx-activityCompose = "1.9.1"
androidx-compose = "1.6.8"
compose-plugin = "1.6.11"
kotlin = "2.0.20"
coreKtx = "1.13.1"
android-targetSdk = "35"
androidx-activityCompose = "1.10.1"
androidx-compose = "1.7.8"
compose-plugin = "1.7.0"
kotlin = "2.1.0"
coreKtx = "1.15.0"
atrium = "1.2.0"
[libraries]
@@ -18,6 +18,7 @@ androidx-compose-foundation = { module = "androidx.compose.foundation:foundation
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
atrium = { module = "ch.tutteli.atrium:atrium-fluent", version.ref = "atrium" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version = "1.10.1" }
logging = { module = "io.github.oshai:kotlin-logging", version = "7.0.0" }
[plugins]

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME