Add timer
This commit is contained in:
@@ -1 +1 @@
|
||||
java temurin-17.0.12+7
|
||||
java temurin-21
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user