Cubo: persistir la partida en curso — reanuda el tablero al reabrir
El servicio ya no hace reset() ciego: restaura el tablero guardado (celdas + score + won) si encaja con el tamaño del cubo, guarda tras cada jugada y borra al perder (el récord ya queda sellado aparte). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
5c80ec5d46
commit
76f95ed72e
@@ -35,8 +35,18 @@ class CuboViewModel(app: Application) : AndroidViewModel(app) {
|
||||
init {
|
||||
viewModelScope.launch { progreso.mejorScore.collect { mejorScore = it } }
|
||||
viewModelScope.launch { progreso.mejorBaldosa.collect { mejorTile = it } }
|
||||
juego.reset()
|
||||
sync()
|
||||
// Reanuda la partida a medias si la hay (y encaja con el tamaño del
|
||||
// cubo); si no, empieza una nueva. El tablero arranca en blanco y esta
|
||||
// carga es asíncrona, así que la UI verá el tablero apenas resuelva.
|
||||
viewModelScope.launch {
|
||||
val saved = progreso.cargarPartida()
|
||||
if (saved != null && saved.cells.size == juego.cells.size) {
|
||||
juego.restore(saved.cells, saved.score, saved.won)
|
||||
} else {
|
||||
juego.reset()
|
||||
}
|
||||
sync()
|
||||
}
|
||||
}
|
||||
|
||||
/** Instantánea barata del tablero para el renderer (copia defensiva). */
|
||||
@@ -61,8 +71,16 @@ class CuboViewModel(app: Application) : AndroidViewModel(app) {
|
||||
won = juego.won
|
||||
over = juego.over
|
||||
version++
|
||||
if (over || won) {
|
||||
viewModelScope.launch { progreso.registrar(juego.score, juego.maxValue) }
|
||||
// Captura el estado para persistir fuera del hilo (la partida es mutable).
|
||||
val cells = juego.cells.copyOf()
|
||||
val s = juego.score
|
||||
val mt = juego.maxValue
|
||||
val w = juego.won
|
||||
val finished = juego.over
|
||||
viewModelScope.launch {
|
||||
if (finished || w) progreso.registrar(s, mt)
|
||||
// Guarda mientras se juega; al terminar, borra (nada que reanudar).
|
||||
if (finished) progreso.limpiarPartida() else progreso.guardarPartida(cells, s, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,34 @@ package com.rizoma.cubo.data
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "cubo")
|
||||
|
||||
/** Récords del cubo, persistidos en DataStore: mejor puntuación y mejor baldosa. */
|
||||
/** Una partida a medias: el tablero tal cual, con su puntuación y si ya ganó. */
|
||||
data class PartidaCubo(val cells: IntArray, val score: Int, val won: Boolean) {
|
||||
// data class con IntArray: equals/hashCode a mano para comparar por contenido.
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is PartidaCubo && score == other.score && won == other.won &&
|
||||
cells.contentEquals(other.cells)
|
||||
|
||||
override fun hashCode(): Int =
|
||||
(cells.contentHashCode() * 31 + score) * 31 + won.hashCode()
|
||||
}
|
||||
|
||||
/**
|
||||
* Persistencia del cubo en DataStore. Dos cosas: los **récords** (mejor
|
||||
* puntuación y mejor baldosa, sobreviven a cualquier partida) y la **partida
|
||||
* en curso** (el tablero a medias, para reanudar al reabrir la app). La
|
||||
* partida se borra al terminar (game over): ya no hay nada que reanudar.
|
||||
*/
|
||||
class Progreso(context: Context) {
|
||||
private val store = context.applicationContext.dataStore
|
||||
|
||||
@@ -25,8 +44,39 @@ class Progreso(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Lee la partida a medias guardada, o null si no hay ninguna. */
|
||||
suspend fun cargarPartida(): PartidaCubo? {
|
||||
val p = store.data.first()
|
||||
val csv = p[KEY_CELLS] ?: return null
|
||||
val cells = csv.split(',').mapNotNull { it.toIntOrNull() }.toIntArray()
|
||||
if (cells.isEmpty()) return null
|
||||
return PartidaCubo(cells, p[KEY_PSCORE] ?: 0, p[KEY_PWON] ?: false)
|
||||
}
|
||||
|
||||
/** Guarda el tablero en curso (se llama tras cada jugada). */
|
||||
suspend fun guardarPartida(cells: IntArray, score: Int, won: Boolean) {
|
||||
store.edit { p ->
|
||||
p[KEY_CELLS] = cells.joinToString(",")
|
||||
p[KEY_PSCORE] = score
|
||||
p[KEY_PWON] = won
|
||||
}
|
||||
}
|
||||
|
||||
/** Borra la partida en curso (game over: ya no hay nada que reanudar). */
|
||||
suspend fun limpiarPartida() {
|
||||
store.edit { p ->
|
||||
p.remove(KEY_CELLS)
|
||||
p.remove(KEY_PSCORE)
|
||||
p.remove(KEY_PWON)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val KEY_SCORE = intPreferencesKey("mejor_score")
|
||||
val KEY_TILE = intPreferencesKey("mejor_baldosa")
|
||||
// Partida en curso.
|
||||
val KEY_CELLS = stringPreferencesKey("partida_cells")
|
||||
val KEY_PSCORE = intPreferencesKey("partida_score")
|
||||
val KEY_PWON = booleanPreferencesKey("partida_won")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,18 @@ class Cubo(val n: Int = 3, seed: Long = 0x43_55_42_4FL /* "CUBO" */) {
|
||||
spawn()
|
||||
}
|
||||
|
||||
/**
|
||||
* Restaura una partida a medias guardada. [saved] debe tener el mismo
|
||||
* tamaño que el tablero (n³); el llamador lo valida. No reaparece nada: el
|
||||
* tablero queda exactamente como estaba al guardarse.
|
||||
*/
|
||||
fun restore(saved: IntArray, score: Int, won: Boolean) {
|
||||
saved.copyInto(cells)
|
||||
this.score = score
|
||||
this.won = won
|
||||
lastSpawn = -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Deja caer todo hacia la cara dada y, si algo se movió, aparece una baldosa
|
||||
* nueva. Devuelve si el tablero cambió (un movimiento nulo no cuenta ni hace
|
||||
|
||||
Reference in New Issue
Block a user