From 685810606764f064f4e377fcd71126c52d4859f3 Mon Sep 17 00:00:00 2001 From: Lucas Lima Date: Sat, 11 Nov 2023 20:00:48 -0300 Subject: [PATCH 1/6] Fix lint --- .../dev/lucasnlm/antimine/common/level/logic/MinefieldHandler.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/MinefieldHandler.kt b/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/MinefieldHandler.kt index 45272d6b..75b5dc08 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/MinefieldHandler.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/MinefieldHandler.kt @@ -61,7 +61,6 @@ class MinefieldHandler( null } - return when { nearestTarget != null -> { field[nearestTarget.id] = nearestTarget.copy(revealed = true) From b907fb7fe5467c5e4f23ead40e5f224d62b53eaa Mon Sep 17 00:00:00 2001 From: Lucas Lima Date: Sat, 11 Nov 2023 20:31:40 -0300 Subject: [PATCH 2/6] Remove loading indicator from game activity --- .../antimine/wear/game/WearGameActivity.kt | 12 ------------ wear/src/main/res/layout/activity_game.xml | 17 ++--------------- 2 files changed, 2 insertions(+), 27 deletions(-) diff --git a/wear/src/main/java/dev/lucasnlm/antimine/wear/game/WearGameActivity.kt b/wear/src/main/java/dev/lucasnlm/antimine/wear/game/WearGameActivity.kt index 1321e764..d862d37a 100644 --- a/wear/src/main/java/dev/lucasnlm/antimine/wear/game/WearGameActivity.kt +++ b/wear/src/main/java/dev/lucasnlm/antimine/wear/game/WearGameActivity.kt @@ -194,18 +194,6 @@ class WearGameActivity : ThemedActivity(), AndroidFragmentApplication.Callbacks binding.tapToBegin.isVisible = false } - if (it.isCreatingGame) { - launch { - // Show loading indicator only when it takes more than: - delay(500) - if (singleState().isCreatingGame) { - binding.loadingGame.show() - } - } - } else if (binding.loadingGame.isVisible) { - binding.loadingGame.hide() - } - if (it.duration % 10 > 2) { binding.timer.apply { isVisible = preferencesRepository.showTimer() diff --git a/wear/src/main/res/layout/activity_game.xml b/wear/src/main/res/layout/activity_game.xml index c7a737fa..868564b7 100644 --- a/wear/src/main/res/layout/activity_game.xml +++ b/wear/src/main/res/layout/activity_game.xml @@ -3,7 +3,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + tools:context=".game.WearGameActivity"> - - Date: Sat, 11 Nov 2023 23:00:40 -0300 Subject: [PATCH 3/6] Fix GdxExt --- .../dev/lucasnlm/antimine/gdx/GameContext.kt | 3 + .../java/dev/lucasnlm/antimine/gdx/GdxExt.kt | 128 +++++++++--------- .../lucasnlm/antimine/gdx/actors/AreaActor.kt | 12 +- .../antimine/wear/game/WearGameActivity.kt | 1 - 4 files changed, 74 insertions(+), 70 deletions(-) diff --git a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GameContext.kt b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GameContext.kt index e88db619..0a3e7ae8 100644 --- a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GameContext.kt +++ b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GameContext.kt @@ -2,6 +2,9 @@ package dev.lucasnlm.antimine.gdx import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.g2d.TextureAtlas +import dev.lucasnlm.antimine.gdx.GdxExt.dim +import dev.lucasnlm.antimine.gdx.GdxExt.toGdxColor +import dev.lucasnlm.antimine.gdx.GdxExt.toInverseBackOrWhite import dev.lucasnlm.antimine.gdx.models.GameTextures import dev.lucasnlm.antimine.ui.model.AppTheme diff --git a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GdxExt.kt b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GdxExt.kt index c12f12d1..10b40f20 100644 --- a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GdxExt.kt +++ b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GdxExt.kt @@ -7,78 +7,80 @@ import com.badlogic.gdx.graphics.g2d.Batch import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.scenes.scene2d.Actor -fun Batch.drawRegion( - texture: TextureRegion, - x: Float, - y: Float, - width: Float, - height: Float, - blend: Boolean, - color: Color? = null, -) { - if (blend && !isBlendingEnabled) { - enableBlending() +object GdxExt { + fun Batch.drawRegion( + texture: TextureRegion, + x: Float, + y: Float, + width: Float, + height: Float, + blend: Boolean, + color: Color? = null, + ) { + if (blend && !isBlendingEnabled) { + enableBlending() + } + + setColor(color ?: WHITE) + draw(texture, x, y, width, height) } - setColor(color ?: WHITE) - draw(texture, x, y, width, height) -} + fun Actor.drawAsset( + batch: Batch, + texture: TextureRegion, + color: Color? = null, + blend: Boolean = true, + scale: Float = 1.0f, + ) { + if (blend && !batch.isBlendingEnabled) { + batch.enableBlending() + } -fun Actor.drawAsset( - batch: Batch, - texture: TextureRegion, - color: Color? = null, - blend: Boolean = true, - scale: Float = 1.0f, -) { - if (blend && !batch.isBlendingEnabled) { - batch.enableBlending() + batch.run { + setColor(color ?: WHITE) + draw( + texture, + x - width * (scale - 1.0f) * 0.5f, + y - height * (scale - 1.0f) * 0.5f, + width * scale, + height * scale, + ) + } } - batch.run { - setColor(color ?: WHITE) - draw( - texture, - x - width * (scale - 1.0f) * 0.5f, - y - height * (scale - 1.0f) * 0.5f, - width * scale, - height * scale, - ) + fun Int.toGdxColor(alpha: Float? = 1.0f): Color { + val color = Color() + argb8888ToColor(color, this) + color.a = alpha ?: 1.0f + return color } -} -fun Int.toGdxColor(alpha: Float? = 1.0f): Color { - val color = Color() - argb8888ToColor(color, this) - color.a = alpha ?: 1.0f - return color -} + fun Int.toInverseBackOrWhite(alpha: Float? = 1.0f): Color { + val sumRgb = ( + android.graphics.Color.red(this) + + android.graphics.Color.green(this) + + android.graphics.Color.blue(this) + ) -fun Int.toInverseBackOrWhite(alpha: Float? = 1.0f): Color { - val sumRgb = ( - android.graphics.Color.red(this) + - android.graphics.Color.green(this) + - android.graphics.Color.blue(this) - ) + val value = + if (sumRgb > (160 * 3)) { + 0.15f + } else { + 1.0f + } - val value = - if (sumRgb > (160 * 3)) { - 0.15f - } else { - 1.0f - } - - return Color(value, value, value, alpha ?: 1.0f) -} + return Color(value, value, value, alpha ?: 1.0f) + } -fun Color.alpha(newAlpha: Float): Color { - a = newAlpha - return this -} + fun Color.alpha(newAlpha: Float): Color { + a = newAlpha + return this + } -fun Color.dim(value: Float): Color { - r *= value - g *= value - b *= value - return this + fun Color.dim(value: Float): Color { + r *= value + g *= value + b *= value + return this + } } diff --git a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/actors/AreaActor.kt b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/actors/AreaActor.kt index fba57a63..5ed94ec8 100644 --- a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/actors/AreaActor.kt +++ b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/actors/AreaActor.kt @@ -10,17 +10,17 @@ import dev.lucasnlm.antimine.core.getNeighborIdAtPos import dev.lucasnlm.antimine.core.models.Area import dev.lucasnlm.antimine.gdx.AtlasNames import dev.lucasnlm.antimine.gdx.GameContext +import dev.lucasnlm.antimine.gdx.GdxExt.alpha +import dev.lucasnlm.antimine.gdx.GdxExt.dim +import dev.lucasnlm.antimine.gdx.GdxExt.drawAsset +import dev.lucasnlm.antimine.gdx.GdxExt.drawRegion +import dev.lucasnlm.antimine.gdx.GdxExt.toGdxColor +import dev.lucasnlm.antimine.gdx.GdxExt.toInverseBackOrWhite import dev.lucasnlm.antimine.gdx.actors.AreaForm.AREA_FULL_FORM import dev.lucasnlm.antimine.gdx.actors.AreaForm.AREA_NO_FORM import dev.lucasnlm.antimine.gdx.actors.AreaForm.areaFormOf import dev.lucasnlm.antimine.gdx.actors.AreaForm.toAtlasNames -import dev.lucasnlm.antimine.gdx.alpha -import dev.lucasnlm.antimine.gdx.dim -import dev.lucasnlm.antimine.gdx.drawAsset -import dev.lucasnlm.antimine.gdx.drawRegion import dev.lucasnlm.antimine.gdx.models.GameRenderingContext -import dev.lucasnlm.antimine.gdx.toGdxColor -import dev.lucasnlm.antimine.gdx.toInverseBackOrWhite import dev.lucasnlm.antimine.ui.model.minesAround import dev.lucasnlm.antimine.ui.repository.Themes diff --git a/wear/src/main/java/dev/lucasnlm/antimine/wear/game/WearGameActivity.kt b/wear/src/main/java/dev/lucasnlm/antimine/wear/game/WearGameActivity.kt index d862d37a..233816c1 100644 --- a/wear/src/main/java/dev/lucasnlm/antimine/wear/game/WearGameActivity.kt +++ b/wear/src/main/java/dev/lucasnlm/antimine/wear/game/WearGameActivity.kt @@ -25,7 +25,6 @@ import dev.lucasnlm.antimine.ui.ext.ThemedActivity import dev.lucasnlm.antimine.wear.databinding.ActivityGameBinding import dev.lucasnlm.antimine.wear.message.GameOverActivity import dev.lucasnlm.antimine.wear.message.VictoryActivity -import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel From ed9b96815dd5375f19d2635a73f1cd66da2b3388 Mon Sep 17 00:00:00 2001 From: Lucas Lima Date: Sun, 12 Nov 2023 10:05:04 -0300 Subject: [PATCH 4/6] Prioritize visible mines --- .../dev/lucasnlm/antimine/GameActivity.kt | 26 +++++++++---- .../lucasnlm/antimine/di/ViewModelModule.kt | 2 +- .../antimine/common/level/di/LevelModule.kt | 5 +++ .../common/level/logic/GameController.kt | 15 +++++++- .../common/level/logic/MinefieldHandler.kt | 28 ++++++++++---- .../common/level/logic/VisibleMineStream.kt | 32 ++++++++++++++++ .../common/level/view/GameRenderFragment.kt | 10 +++++ .../common/level/viewmodel/GameViewModel.kt | 38 +++++++++++++------ .../antimine/gdx/GameApplicationListener.kt | 4 ++ .../antimine/gdx/stages/MinefieldStage.kt | 8 ++++ 10 files changed, 139 insertions(+), 29 deletions(-) create mode 100644 common/src/main/java/dev/lucasnlm/antimine/common/level/logic/VisibleMineStream.kt diff --git a/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt b/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt index 0c74cdec..14651a86 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt @@ -54,6 +54,7 @@ import dev.lucasnlm.external.ReviewWrapperImpl import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @@ -617,7 +618,7 @@ class GameActivity : if (wasPlaying) { gameAudioManager.resumeMusic() } - gameViewModel.revealRandomMine(false) + revealRandomMineShowWarning(false) gameViewModel.sendEvent(GameEvent.GiveMoreTip) }, onFail = { @@ -690,22 +691,31 @@ class GameActivity : } } - private fun revealRandomMine() { + private suspend fun revealRandomMine() { analyticsManager.sentEvent(Analytics.UseHint) val hintAmount = gameViewModel.getTips() if (hintAmount > 0) { - val revealedId = gameViewModel.revealRandomMine() - if (revealedId == null) { - showGameWarning(i18n.string.cant_do_it_now) - } else { - showGameWarning(i18n.string.mine_revealed) - } + revealRandomMineShowWarning() } else { showGameWarning(i18n.string.help_win_a_game) } } + private fun revealRandomMineShowWarning(consume: Boolean = true) { + lifecycleScope.launch { + gameViewModel + .revealRandomMine(consume) + .collect { revealedId -> + if (revealedId == null) { + showGameWarning(i18n.string.cant_do_it_now) + } else { + showGameWarning(i18n.string.mine_revealed) + } + } + } + } + private fun startNewGameWithAds() { if (!preferencesRepository.isPremiumEnabled()) { if (featureFlagManager.useInterstitialAd) { diff --git a/app/src/main/java/dev/lucasnlm/antimine/di/ViewModelModule.kt b/app/src/main/java/dev/lucasnlm/antimine/di/ViewModelModule.kt index 20ada6d5..430a16cd 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/di/ViewModelModule.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/di/ViewModelModule.kt @@ -30,7 +30,7 @@ val ViewModelModule = viewModel { LocalizationViewModel(get(), get()) } viewModel { GameViewModel( - get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), + get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), ) } } diff --git a/common/src/main/java/dev/lucasnlm/antimine/common/level/di/LevelModule.kt b/common/src/main/java/dev/lucasnlm/antimine/common/level/di/LevelModule.kt index 913db524..01a214bb 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/common/level/di/LevelModule.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/common/level/di/LevelModule.kt @@ -1,5 +1,6 @@ package dev.lucasnlm.antimine.common.level.di +import dev.lucasnlm.antimine.common.level.logic.VisibleMineStream import dev.lucasnlm.antimine.common.level.repository.MinefieldRepository import dev.lucasnlm.antimine.common.level.repository.MinefieldRepositoryImpl import dev.lucasnlm.antimine.common.level.repository.SavesRepository @@ -33,4 +34,8 @@ val LevelModule = single { TipRepositoryImpl(get(), get()) } bind TipRepository::class + + single { + VisibleMineStream() + } } diff --git a/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/GameController.kt b/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/GameController.kt index 7a3ae21b..c00799da 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/GameController.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/GameController.kt @@ -409,7 +409,13 @@ class GameController { } } - fun revealRandomMine(): Int? { + /** + * Reveal a random mine near an uncovered area. + * + * @param visibleMines It will prioritize mines that are in the visible area. + * @return The id of the revealed mine. + */ + fun revealRandomMine(visibleMines: Set): Int? { val resultId: Int? field = MinefieldHandler( @@ -417,7 +423,12 @@ class GameController { useQuestionMark = false, individualActions = useIndividualActions(), ).run { - resultId = revealRandomMineNearUncoveredArea(lastIdInteractionX, lastIdInteractionY) + resultId = + revealRandomMineNearUncoveredArea( + visibleMines = visibleMines, + lastX = lastIdInteractionX, + lastY = lastIdInteractionY, + ) result() } return resultId diff --git a/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/MinefieldHandler.kt b/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/MinefieldHandler.kt index 75b5dc08..0fddb3a5 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/MinefieldHandler.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/MinefieldHandler.kt @@ -40,20 +40,34 @@ class MinefieldHandler( .forEach { field[it.id] = it.copy(mistake = false) } } - private fun hasUncoveredNeighbor(area: Area): Boolean { - return area.neighborsIds.map { field[it] }.count { !it.isCovered } > 0 + private fun Area.hasUncoveredNeighbor(): Boolean { + return neighborsIds.map { field[it] }.any { !it.isCovered } + } + + private fun Area.potentialMineReveal(): Boolean { + return hasMine && mark.isNone() && !revealed && isCovered } fun revealRandomMineNearUncoveredArea( + visibleMines: Set, lastX: Int? = null, lastY: Int? = null, ): Int? { - val unrevealedMines = field.filter { it.hasMine && it.mark.isNone() && !it.revealed && it.isCovered } - val unrevealedMinesWithUncoveredNeighbors = unrevealedMines.filter(::hasUncoveredNeighbor) - val potentialTargets = unrevealedMinesWithUncoveredNeighbors.ifEmpty { unrevealedMines } + // / Prioritized mines are mines that are visible and have a potential to be revealed. + // / If there are no prioritized mines, then we get all mines that have a potential to be revealed. + val prioritizedMines = + visibleMines + .map { field[it] } + .filter { it.potentialMineReveal() && it.hasUncoveredNeighbor() } + + val unrevealedMines = + prioritizedMines.ifEmpty { + field.filter { it.potentialMineReveal() && it.hasUncoveredNeighbor() } + } + val nearestTarget = if (lastX != null && lastY != null) { - potentialTargets.filter { + unrevealedMines.filter { (lastX - it.posX).absoluteValue < NEAR_MINE_THRESHOLD && (lastY - it.posY).absoluteValue < NEAR_MINE_THRESHOLD }.shuffled().firstOrNull() @@ -67,7 +81,7 @@ class MinefieldHandler( nearestTarget.id } else -> { - potentialTargets.shuffled().firstOrNull()?.run { + unrevealedMines.shuffled().firstOrNull()?.run { field[this.id] = this.copy(revealed = true) this.id } diff --git a/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/VisibleMineStream.kt b/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/VisibleMineStream.kt new file mode 100644 index 00000000..60f97d1e --- /dev/null +++ b/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/VisibleMineStream.kt @@ -0,0 +1,32 @@ +package dev.lucasnlm.antimine.common.level.logic + +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow + +class VisibleMineStream { + private val visibleMinesFlow = + MutableSharedFlow>( + extraBufferCapacity = 1, + ) + private val requestVisibleMinesFlow = + MutableSharedFlow( + extraBufferCapacity = 1, + ) + + /** Returns a [SharedFlow] that emits the visible mines. */ + fun observeVisibleMines() = visibleMinesFlow.asSharedFlow() + + /** Updates the visible mines. */ + fun update(visibleMines: Set) { + visibleMinesFlow.tryEmit(visibleMines) + } + + /** Requests the visible mines. */ + suspend fun requestVisibleMines() { + requestVisibleMinesFlow.emit(Unit) + } + + /** Returns a [SharedFlow] that emits when the visible mines are requested. */ + fun observeRequestVisibleMines() = requestVisibleMinesFlow.asSharedFlow() +} diff --git a/common/src/main/java/dev/lucasnlm/antimine/common/level/view/GameRenderFragment.kt b/common/src/main/java/dev/lucasnlm/antimine/common/level/view/GameRenderFragment.kt index 697438b9..1c0ab654 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/common/level/view/GameRenderFragment.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/common/level/view/GameRenderFragment.kt @@ -19,6 +19,7 @@ import androidx.lifecycle.lifecycleScope import com.badlogic.gdx.Gdx import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration import com.badlogic.gdx.backends.android.AndroidFragmentApplication +import dev.lucasnlm.antimine.common.level.logic.VisibleMineStream import dev.lucasnlm.antimine.common.level.viewmodel.GameEvent import dev.lucasnlm.antimine.common.level.viewmodel.GameState import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel @@ -37,6 +38,7 @@ import dev.lucasnlm.antimine.preferences.models.Action import dev.lucasnlm.antimine.preferences.models.ControlStyle import dev.lucasnlm.antimine.ui.repository.ThemeRepository import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.map @@ -52,6 +54,7 @@ open class GameRenderFragment : AndroidFragmentApplication() { private val preferencesRepository: PreferencesRepository by inject() private val appVersionManager: AppVersionManager by inject() private val gameAudioManager: GameAudioManager by inject() + private val visibleMineStream: VisibleMineStream by inject() private val layoutParent: FrameLayout? by lazy { view?.parent as? FrameLayout @@ -181,6 +184,13 @@ open class GameRenderFragment : AndroidFragmentApplication() { Gdx.graphics.requestRendering() + lifecycleScope.launch { + visibleMineStream + .observeRequestVisibleMines() + .map { levelApplicationListener.getVisibleMineActors() } + .collect(visibleMineStream::update) + } + lifecycleScope.launch { gameViewModel .singleState() diff --git a/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt b/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt index 0754c487..df08ef6e 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt @@ -10,6 +10,7 @@ import androidx.lifecycle.viewModelScope import dev.lucasnlm.antimine.common.io.models.FirstOpen import dev.lucasnlm.antimine.common.io.models.Save import dev.lucasnlm.antimine.common.level.logic.GameController +import dev.lucasnlm.antimine.common.level.logic.VisibleMineStream import dev.lucasnlm.antimine.common.level.models.ActionCompleted import dev.lucasnlm.antimine.common.level.repository.MinefieldRepository import dev.lucasnlm.antimine.common.level.repository.SavesRepository @@ -33,8 +34,12 @@ import dev.lucasnlm.external.Leaderboard import dev.lucasnlm.external.PlayGamesManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.take import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.util.Locale @@ -52,6 +57,7 @@ open class GameViewModel( private val playGamesManager: PlayGamesManager, private val tipRepository: TipRepository, private val clockManager: ClockManager, + private val visibleMineStream: VisibleMineStream, ) : IntentViewModel() { private lateinit var gameController: GameController private var initialized = false @@ -649,20 +655,30 @@ open class GameViewModel( gameController.revealAllEmptyAreas() } - fun revealRandomMine(consume: Boolean = true): Int? { + suspend fun revealRandomMine(consume: Boolean = true): Flow { return if (initialized) { - val result = gameController.revealRandomMine() - - if (result != null) { - soundManager.playRevealBomb() - - if (consume) { - sendEvent(GameEvent.ConsumeTip) + visibleMineStream + .observeVisibleMines() + .take(1) + .map { + gameController + .revealRandomMine(it) + .also { result -> + if (result != null) { + soundManager.playRevealBomb() + + if (consume) { + sendEvent(GameEvent.ConsumeTip) + } + } + } + } + .also { + visibleMineStream + .requestVisibleMines() } - } - result } else { - null + flowOf(null) } } diff --git a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GameApplicationListener.kt b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GameApplicationListener.kt index 8bed67a5..7b87aa30 100644 --- a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GameApplicationListener.kt +++ b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/GameApplicationListener.kt @@ -195,6 +195,10 @@ class GameApplicationListener( minefieldStage.scaleZoom(delta) } + fun getVisibleMineActors(): Set { + return minefieldStage.getVisibleMineActors() + } + fun refreshSettings() { actionSettings = with(preferencesRepository) { diff --git a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/stages/MinefieldStage.kt b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/stages/MinefieldStage.kt index d55879d3..3f24b35f 100644 --- a/gdx/src/main/java/dev/lucasnlm/antimine/gdx/stages/MinefieldStage.kt +++ b/gdx/src/main/java/dev/lucasnlm/antimine/gdx/stages/MinefieldStage.kt @@ -326,6 +326,14 @@ class MinefieldStage( this.actionSettings = actionSettings } + fun getVisibleMineActors(): Set { + return actors.filter { + it.isVisible && (it as? AreaActor)?.area?.hasMine == true + }.mapNotNull { + (it as? AreaActor)?.area?.id + }.toSet() + } + companion object { const val MAX_ZOOM_OUT = 0.35f const val MAX_ZOOM_IN = 3.0f From b6e1e02bc53b6a9c36eb9b662199c6df635567e7 Mon Sep 17 00:00:00 2001 From: Lucas Lima Date: Sun, 12 Nov 2023 10:09:31 -0300 Subject: [PATCH 5/6] Add helper --- .../common/level/logic/MinefieldHandler.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/MinefieldHandler.kt b/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/MinefieldHandler.kt index 0fddb3a5..4573e2c9 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/MinefieldHandler.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/common/level/logic/MinefieldHandler.kt @@ -41,7 +41,7 @@ class MinefieldHandler( } private fun Area.hasUncoveredNeighbor(): Boolean { - return neighborsIds.map { field[it] }.any { !it.isCovered } + return neighborsIds.toArea().any { !it.isCovered } } private fun Area.potentialMineReveal(): Boolean { @@ -57,7 +57,7 @@ class MinefieldHandler( // / If there are no prioritized mines, then we get all mines that have a potential to be revealed. val prioritizedMines = visibleMines - .map { field[it] } + .toArea() .filter { it.potentialMineReveal() && it.hasUncoveredNeighbor() } val unrevealedMines = @@ -141,7 +141,7 @@ class MinefieldHandler( if (!hasMine && minesAround == 0 && openNeighbors) { neighborsIds - .map { field[it] } + .toArea() .filter { it.isCovered } .onEach { openAt(it.id, openNeighbors = true, passive = true) @@ -154,7 +154,7 @@ class MinefieldHandler( fun openOrFlagNeighborsOf(index: Int) { field.getOrNull(index)?.run { if (!isCovered) { - val neighbors = neighborsIds.map { field[it] } + val neighbors = neighborsIds.toArea() val flaggedCount = neighbors.count { it.mark.isFlag() || (!it.isCovered && it.hasMine) } if (flaggedCount >= minesAround) { neighbors @@ -178,8 +178,8 @@ class MinefieldHandler( fun openNeighborsOf(index: Int) { field.getOrNull(index)?.run { if (!isCovered) { - val neighbors = neighborsIds.map { field[it] } - neighbors + neighborsIds + .toArea() .filter { it.isCovered && it.mark.isNone() } .forEach { openAt(it.id, passive = false, openNeighbors = true) } } @@ -188,6 +188,8 @@ class MinefieldHandler( fun result(): List = field.toList() + private fun Collection.toArea(): Collection = map { field[it] } + companion object { const val NEAR_MINE_THRESHOLD = 5 } From 0a757c09d22c993d1a6fd69c5ee91beaac7bb67e Mon Sep 17 00:00:00 2001 From: Lucas Lima Date: Sun, 12 Nov 2023 10:10:40 -0300 Subject: [PATCH 6/6] Bump app version --- app/build.gradle | 6 +++--- wear/build.gradle | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d4b79646..501475c7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { defaultConfig { // versionCode and versionName must be hardcoded to support F-droid - versionCode 1705091 - versionName '17.5.9' + versionCode 1705101 + versionName '17.5.10' minSdk 21 targetSdk 34 compileSdk 34 @@ -77,7 +77,7 @@ android { } googleInstant { - versionCode 162 + versionCode 163 dimension 'version' applicationId 'dev.lucasnlm.antimine' versionNameSuffix ' I' diff --git a/wear/build.gradle b/wear/build.gradle index 4717ded9..fc88356d 100644 --- a/wear/build.gradle +++ b/wear/build.gradle @@ -9,8 +9,8 @@ android { defaultConfig { // versionCode and versionName must be hardcoded to support F-droid applicationId 'dev.lucasnlm.antimine' - versionCode 1705090 - versionName '17.5.9 W' + versionCode 1705100 + versionName '17.5.10 W' minSdk 25 targetSdk 33 compileSdk 34