Merge pull request #142 from lucasnlm/small-improvements

Small improvements
This commit is contained in:
Lucas Nunes 2020-08-07 01:57:42 +00:00 committed by GitHub
commit 40fd494962
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 159 additions and 161 deletions

View file

@ -32,7 +32,7 @@ import dev.lucasnlm.antimine.common.level.models.Status
import dev.lucasnlm.antimine.common.level.repository.ISavesRepository
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel
import dev.lucasnlm.antimine.control.ControlDialogFragment
import dev.lucasnlm.antimine.core.analytics.AnalyticsManager
import dev.lucasnlm.antimine.core.analytics.IAnalyticsManager
import dev.lucasnlm.antimine.core.analytics.models.Analytics
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
import dev.lucasnlm.antimine.history.HistoryActivity
@ -57,7 +57,7 @@ class GameActivity : AppCompatActivity(R.layout.activity_game), DialogInterface.
lateinit var preferencesRepository: IPreferencesRepository
@Inject
lateinit var analyticsManager: AnalyticsManager
lateinit var analyticsManager: IAnalyticsManager
@Inject
lateinit var instantAppManager: InstantAppManager
@ -117,7 +117,7 @@ class GameActivity : AppCompatActivity(R.layout.activity_game), DialogInterface.
retryObserver.observe(
this@GameActivity,
Observer {
GlobalScope.launch {
lifecycleScope.launch {
viewModel.retryGame(currentSaveId.toInt())
}
}

View file

@ -2,14 +2,14 @@ package dev.lucasnlm.antimine
import androidx.multidex.MultiDexApplication
import dagger.hilt.android.HiltAndroidApp
import dev.lucasnlm.antimine.core.analytics.AnalyticsManager
import dev.lucasnlm.antimine.core.analytics.IAnalyticsManager
import dev.lucasnlm.antimine.core.analytics.models.Analytics
import javax.inject.Inject
@HiltAndroidApp
open class MainApplication : MultiDexApplication() {
@Inject
lateinit var analyticsManager: AnalyticsManager
lateinit var analyticsManager: IAnalyticsManager
override fun onCreate() {
super.onCreate()

View file

@ -12,7 +12,7 @@ import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.about.models.AboutEvent
import dev.lucasnlm.antimine.about.viewmodel.AboutViewModel
import dev.lucasnlm.antimine.about.views.AboutInfoFragment
import dev.lucasnlm.antimine.about.views.thirds.ThirdPartiesFragment
import dev.lucasnlm.antimine.about.views.thirds.ThirdPartyFragment
import dev.lucasnlm.antimine.about.views.translators.TranslatorsFragment
class AboutActivity : AppCompatActivity(R.layout.activity_empty) {
@ -25,7 +25,7 @@ class AboutActivity : AppCompatActivity(R.layout.activity_empty) {
Observer { event ->
when (event) {
AboutEvent.ThirdPartyLicenses -> {
replaceFragment(ThirdPartiesFragment(), ThirdPartiesFragment.TAG)
replaceFragment(ThirdPartyFragment(), ThirdPartyFragment.TAG)
}
AboutEvent.SourceCode -> {
openSourceCode()

View file

@ -10,6 +10,10 @@ import dev.lucasnlm.antimine.about.models.TranslationInfo
class AboutViewModel : ViewModel() {
val eventObserver = MutableLiveData<AboutEvent>()
fun sendEvent(event: AboutEvent) {
eventObserver.postValue(event)
}
fun getTranslatorsList() = mapOf(
"Arabic" to sequenceOf("Ahmad Alkurbi"),
"Bulgarian" to sequenceOf("Georgi Eftimov"),

View file

@ -24,15 +24,15 @@ class AboutInfoFragment : Fragment(R.layout.fragment_about_info) {
version.text = getString(R.string.version_s, BuildConfig.VERSION_NAME)
thirdsParties.setOnClickListener {
aboutViewModel.eventObserver.postValue(AboutEvent.ThirdPartyLicenses)
aboutViewModel.sendEvent(AboutEvent.ThirdPartyLicenses)
}
sourceCode.setOnClickListener {
aboutViewModel.eventObserver.postValue(AboutEvent.SourceCode)
aboutViewModel.sendEvent(AboutEvent.SourceCode)
}
translation.setOnClickListener {
aboutViewModel.eventObserver.postValue(AboutEvent.Translators)
aboutViewModel.sendEvent(AboutEvent.Translators)
}
}
}

View file

@ -2,13 +2,16 @@ package dev.lucasnlm.antimine.about.views.thirds
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.text.TextActivity
import dev.lucasnlm.antimine.about.models.ThirdParty
import kotlinx.android.synthetic.main.view_third_party.view.*
class ThirdPartyAdapter(
internal class ThirdPartyAdapter(
private val thirdParties: List<ThirdParty>
) : RecyclerView.Adapter<ThirdPartyViewHolder>() {
@ -32,3 +35,7 @@ class ThirdPartyAdapter(
}
}
}
class ThirdPartyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val title: TextView = view.third_name
}

View file

@ -10,7 +10,7 @@ import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.about.viewmodel.AboutViewModel
import kotlinx.android.synthetic.main.fragment_third_party.*
class ThirdPartiesFragment : Fragment(R.layout.fragment_third_party) {
class ThirdPartyFragment : Fragment(R.layout.fragment_third_party) {
private val aboutViewModel: AboutViewModel by activityViewModels()
override fun onResume() {
@ -30,6 +30,6 @@ class ThirdPartiesFragment : Fragment(R.layout.fragment_third_party) {
}
companion object {
val TAG = ThirdPartiesFragment::class.simpleName!!
val TAG = ThirdPartyFragment::class.simpleName!!
}
}

View file

@ -1,11 +0,0 @@
package dev.lucasnlm.antimine.about.views.thirds
import androidx.recyclerview.widget.RecyclerView
import android.view.View
import android.widget.TextView
import kotlinx.android.synthetic.main.view_third_party.view.*
class ThirdPartyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val title: TextView = view.third_name
}

View file

@ -2,15 +2,18 @@ package dev.lucasnlm.antimine.about.views.translators
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.about.viewmodel.AboutViewModel
import kotlinx.android.synthetic.main.fragment_translators.*
import kotlinx.android.synthetic.main.view_translator.view.*
class TranslatorsFragment : Fragment(R.layout.fragment_translators) {
internal class TranslatorsFragment : Fragment(R.layout.fragment_translators) {
private val aboutViewModel: AboutViewModel by activityViewModels()
override fun onResume() {
@ -32,3 +35,8 @@ class TranslatorsFragment : Fragment(R.layout.fragment_translators) {
val TAG = TranslatorsFragment::class.simpleName!!
}
}
class TranslatorsViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val language: TextView = view.language
val translators: TextView = view.translators
}

View file

@ -1,11 +0,0 @@
package dev.lucasnlm.antimine.about.views.translators
import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.view_translator.view.*
class TranslatorsViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val language: TextView = view.language
val translators: TextView = view.translators
}

View file

@ -9,7 +9,9 @@ import androidx.appcompat.app.AppCompatDialogFragment
import androidx.fragment.app.activityViewModels
import dagger.hilt.android.AndroidEntryPoint
import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.common.level.models.Difficulty
import dev.lucasnlm.antimine.common.level.models.Minefield
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
import dev.lucasnlm.antimine.custom.viewmodel.CreateGameViewModel
import javax.inject.Inject
@ -19,6 +21,7 @@ class CustomLevelDialogFragment : AppCompatDialogFragment() {
@Inject
lateinit var preferencesRepository: IPreferencesRepository
private val viewModel by activityViewModels<GameViewModel>()
private val createGameViewModel by activityViewModels<CreateGameViewModel>()
private fun getSelectedMinefield(): Minefield {
@ -49,10 +52,8 @@ class CustomLevelDialogFragment : AppCompatDialogFragment() {
setNegativeButton(R.string.cancel, null)
setPositiveButton(R.string.start) { _, _ ->
val minefield = getSelectedMinefield()
createGameViewModel.run {
updateCustomGameMode(minefield)
startCustomGame()
}
createGameViewModel.updateCustomGameMode(minefield)
viewModel.startNewGame(Difficulty.Custom)
}
}.create()
}

View file

@ -1,32 +1,14 @@
package dev.lucasnlm.antimine.custom.viewmodel
import android.app.Application
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION
import android.net.Uri
import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.AndroidViewModel
import dev.lucasnlm.antimine.DeepLink
import androidx.lifecycle.ViewModel
import dev.lucasnlm.antimine.common.level.models.Minefield
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
class CreateGameViewModel @ViewModelInject constructor(
application: Application,
private val preferencesRepository: IPreferencesRepository
) : AndroidViewModel(application) {
) : ViewModel() {
fun updateCustomGameMode(minefield: Minefield) {
preferencesRepository.updateCustomGameMode(minefield)
}
fun startCustomGame() {
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse(DeepLink.CUSTOM_NEW_GAME)
addFlags(FLAG_ACTIVITY_NEW_TASK)
addFlags(FLAG_ACTIVITY_CLEAR_TASK)
addFlags(FLAG_ACTIVITY_NO_ANIMATION)
}
getApplication<Application>().startActivity(intent)
}
}

View file

@ -6,7 +6,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false"
tools:context="dev.lucasnlm.antimine.about.views.thirds.ThirdPartiesFragment">
tools:context="dev.lucasnlm.antimine.about.views.thirds.ThirdPartyFragment">
<TextView
android:id="@+id/description"

View file

@ -4,7 +4,6 @@ import dev.lucasnlm.antimine.common.level.database.models.FirstOpen
import dev.lucasnlm.antimine.common.level.database.models.Save
import dev.lucasnlm.antimine.common.level.database.models.SaveStatus
import dev.lucasnlm.antimine.common.level.database.models.Stats
import dev.lucasnlm.antimine.common.level.solver.BruteForceSolver
import dev.lucasnlm.antimine.common.level.logic.FlagAssistant
import dev.lucasnlm.antimine.common.level.logic.MinefieldCreator
import dev.lucasnlm.antimine.common.level.logic.MinefieldHandler
@ -15,6 +14,7 @@ import dev.lucasnlm.antimine.common.level.models.Mark
import dev.lucasnlm.antimine.common.level.models.Minefield
import dev.lucasnlm.antimine.common.level.models.Score
import dev.lucasnlm.antimine.common.level.models.StateUpdate
import dev.lucasnlm.antimine.common.level.solver.LimitedBruteForceSolver
import dev.lucasnlm.antimine.core.control.ActionResponse
import dev.lucasnlm.antimine.core.control.GameControl
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -22,9 +22,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlin.random.Random
/**
* Controls a minesweeper logic.
*/
class GameController {
private val minefield: Minefield
private val startTime = System.currentTimeMillis()
@ -33,7 +30,6 @@ class GameController {
private var gameControl: GameControl = GameControl.Standard
private var mines: Sequence<Area> = emptySequence()
private var useQuestionMark = true
private var useSolverAlgorithms = false
var hasMines = false
private set
@ -68,16 +64,14 @@ class GameController {
private fun getArea(id: Int) = field.first { it.id == id }
private fun plantMinesExcept(safeId: Int) {
if (useSolverAlgorithms) {
do {
field = minefieldCreator.create(safeId, false)
val fieldCopy = field.map { it.copy() }.toMutableList()
val minefieldHandler = MinefieldHandler(fieldCopy, false)
minefieldHandler.openAt(safeId)
} while (!BruteForceSolver(minefieldHandler.result().toMutableList()).isSolvable())
} else {
field = minefieldCreator.create(safeId, true)
}
val solver = LimitedBruteForceSolver()
do {
val useSafeZone = minefield.width > 9 && minefield.height > 9
field = minefieldCreator.create(safeId, useSafeZone)
val fieldCopy = field.map { it.copy() }.toMutableList()
val minefieldHandler = MinefieldHandler(fieldCopy, false)
minefieldHandler.openAt(safeId)
} while (solver.keepTrying() && !solver.trySolve(minefieldHandler.result().toMutableList()))
mines = field.filter { it.hasMine }.asSequence()
firstOpen = FirstOpen.Position(safeId)
@ -269,8 +263,4 @@ class GameController {
fun useQuestionMark(useQuestionMark: Boolean) {
this.useQuestionMark = useQuestionMark
}
fun useSolverAlgorithms(useSolverAlgorithms: Boolean) {
this.useSolverAlgorithms = useSolverAlgorithms
}
}

View file

@ -3,13 +3,10 @@ package dev.lucasnlm.antimine.common.level.solver
import dev.lucasnlm.antimine.common.level.logic.MinefieldHandler
import dev.lucasnlm.antimine.common.level.models.Area
class BruteForceSolver(
minefield: MutableList<Area>
) : GameSolver(minefield) {
private val minefieldHandler =
MinefieldHandler(minefield, false)
open class BruteForceSolver : GameSolver() {
override fun trySolve(minefield: MutableList<Area>): Boolean {
val minefieldHandler = MinefieldHandler(minefield, false)
override fun isSolvable(): Boolean {
do {
val initialMap = minefield.filter { !it.isCovered && it.minesAround != 0 }
initialMap.forEach {

View file

@ -2,8 +2,15 @@ package dev.lucasnlm.antimine.common.level.solver
import dev.lucasnlm.antimine.common.level.models.Area
abstract class GameSolver(
protected val minefield: MutableList<Area>
) {
abstract fun isSolvable(): Boolean
abstract class GameSolver {
/**
* If true it may keep iterating on this algorithm.
*/
open fun keepTrying() = true
/**
* Try solve the given [minefield].
* Returns true if it's solvable or false otherwise.
*/
abstract fun trySolve(minefield: MutableList<Area>): Boolean
}

View file

@ -0,0 +1,16 @@
package dev.lucasnlm.antimine.common.level.solver
class LimitedBruteForceSolver(
private val maxAttemptTime: Long = DEFAULT_BRUTE_FORCE_TIMEOUT
) : BruteForceSolver() {
private val initialTime = System.currentTimeMillis()
override fun keepTrying(): Boolean {
return (System.currentTimeMillis() - initialTime) <= maxAttemptTime
}
companion object {
const val DEFAULT_BRUTE_FORCE_TIMEOUT = 1000L
}
}

View file

@ -3,6 +3,7 @@ package dev.lucasnlm.antimine.common.level.view
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import androidx.core.content.ContextCompat
import dev.lucasnlm.antimine.common.R
@ -122,7 +123,7 @@ fun Area.paintOnCanvas(
7 -> areaPalette.minesAround7
else -> areaPalette.minesAround8
}
drawText(canvas, painter, minesAround.toString(), paintSettings)
canvas.drawText(minesAround.toString(), paintSettings, painter)
}
if (highlighted) {
@ -170,15 +171,15 @@ fun Area.paintOnCanvas(
}
}
private fun drawText(canvas: Canvas, paint: Paint, text: String, paintSettings: AreaPaintSettings) {
private fun Canvas.drawText(text: String, paintSettings: AreaPaintSettings, paint: Paint) {
paintSettings.run {
val bounds = RectF(rectF).apply {
right = paint.measureText(text, 0, text.length)
bottom = paint.descent() - paint.ascent()
left += (rectF.width() - right) / 2.0f
top += (rectF.height() - bottom) / 2.0f
}
val bounds = Rect()
paint.getTextBounds(text.toCharArray(), 0, 1, bounds)
paint.textSize = rectF.height() * 0.45f
canvas.drawText(text, rectF.width() * 0.5f, bounds.top - paint.ascent(), paint)
val xPos = rectF.width() * 0.5f
val yPos = (bounds.height() + rectF.height()) * 0.5f
drawText(text, xPos, yPos, paint)
}
}

View file

@ -18,7 +18,7 @@ import dev.lucasnlm.antimine.common.level.repository.ISavesRepository
import dev.lucasnlm.antimine.common.level.repository.IStatsRepository
import dev.lucasnlm.antimine.common.level.utils.Clock
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackManager
import dev.lucasnlm.antimine.core.analytics.AnalyticsManager
import dev.lucasnlm.antimine.core.analytics.IAnalyticsManager
import dev.lucasnlm.antimine.core.analytics.models.Analytics
import dev.lucasnlm.antimine.core.control.ActionResponse
import dev.lucasnlm.antimine.core.control.GameControl
@ -42,7 +42,7 @@ class GameViewModel @ViewModelInject constructor(
private val hapticFeedbackManager: IHapticFeedbackManager,
private val soundManager: ISoundManager,
private val minefieldRepository: IMinefieldRepository,
private val analyticsManager: AnalyticsManager,
private val analyticsManager: IAnalyticsManager,
private val clock: Clock
) : ViewModel() {
val eventObserver = MutableLiveData<Event>()
@ -346,7 +346,6 @@ class GameViewModel @ViewModelInject constructor(
updateGameControl(gameControl)
useQuestionMark(preferencesRepository.useQuestionMark())
useSolverAlgorithms(preferencesRepository.useSolverAlgorithms())
}
}
}

View file

@ -4,7 +4,7 @@ import android.content.Context
import android.util.Log
import dev.lucasnlm.antimine.core.analytics.models.Analytics
class DebugAnalyticsManager : AnalyticsManager {
class DebugAnalyticsManager : IAnalyticsManager {
override fun setup(context: Context, userProperties: Map<String, String>) {
Log.d(TAG, "Setup Analytics using $userProperties")
}

View file

@ -3,7 +3,7 @@ package dev.lucasnlm.antimine.core.analytics
import android.content.Context
import dev.lucasnlm.antimine.core.analytics.models.Analytics
interface AnalyticsManager {
interface IAnalyticsManager {
fun setup(context: Context, userProperties: Map<String, String>)
fun sentEvent(event: Analytics)
}

View file

@ -6,7 +6,7 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ApplicationComponent
import dagger.hilt.android.qualifiers.ApplicationContext
import dev.lucasnlm.antimine.core.analytics.AnalyticsManager
import dev.lucasnlm.antimine.core.analytics.IAnalyticsManager
import dev.lucasnlm.antimine.core.analytics.DebugAnalyticsManager
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
import dev.lucasnlm.antimine.core.preferences.PreferencesManager
@ -28,7 +28,7 @@ class CommonModule {
): PreferencesManager = PreferencesManager(context)
@Provides
fun provideAnalyticsManager(): AnalyticsManager = DebugAnalyticsManager()
fun provideAnalyticsManager(): IAnalyticsManager = DebugAnalyticsManager()
@Provides
fun provideSoundManager(

View file

@ -24,7 +24,6 @@ interface IPreferencesRepository {
fun useAnimations(): Boolean
fun useQuestionMark(): Boolean
fun isSoundEffectsEnabled(): Boolean
fun useSolverAlgorithms(): Boolean
}
class PreferencesRepository(
@ -78,9 +77,6 @@ class PreferencesRepository(
override fun isSoundEffectsEnabled(): Boolean =
getBoolean(PREFERENCE_SOUND_EFFECTS, false)
override fun useSolverAlgorithms(): Boolean =
getBoolean(PREFERENCE_USE_SOLVER_ALGORITHMS, false)
override fun controlStyle(): ControlStyle {
val index = getInt(PREFERENCE_CONTROL_STYLE, -1)
return ControlStyle.values().getOrNull(index) ?: ControlStyle.Standard
@ -120,6 +116,5 @@ class PreferencesRepository(
private const val PREFERENCE_CUSTOM_GAME_MINES = "preference_custom_game_mines"
private const val PREFERENCE_SOUND_EFFECTS = "preference_sound"
private const val PREFERENCE_STATS_BASE = "preference_stats_base"
private const val PREFERENCE_USE_SOLVER_ALGORITHMS = "preference_use_solver_algorithms"
}
}

View file

@ -48,7 +48,6 @@
<string name="quit">Verlaat</string>
<string name="are_you_sure">Is jy seker?</string>
<string name="enable_automatic_flags">Aktiveer outomatiese plasing van vlaggies</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Open Areas</string>
<string name="total_time">Total Time</string>
<string name="average_time">Average Time</string>

View file

@ -48,7 +48,6 @@
<string name="quit">خروج</string>
<string name="are_you_sure">هل أنت متأكد؟</string>
<string name="enable_automatic_flags">Enable automatic placing of flags</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">فتح المناطق</string>
<string name="total_time">مجموع الوقت</string>
<string name="average_time">متوسط الوقت</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Изход</string>
<string name="are_you_sure">Сигурни ли сте?</string>
<string name="enable_automatic_flags">Позволява автоматично слагане на флагове</string>
<string name="enable_no_guessing_maps">Намери решими карти</string>
<string name="open_areas">Отворени площи</string>
<string name="total_time">Общо време</string>
<string name="average_time">Средно време</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Surt</string>
<string name="are_you_sure">Esteu segurs?</string>
<string name="enable_automatic_flags">Activa la col·locació automàtica de banderes</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Zones obertes</string>
<string name="total_time">Temps total</string>
<string name="average_time">Durada mitjana</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Ukončit</string>
<string name="are_you_sure">Jste si jisti?</string>
<string name="enable_automatic_flags">Povolit automatické umísťování vlajek</string>
<string name="enable_no_guessing_maps">Najít řešitelná minová pole</string>
<string name="open_areas">Otevřené oblasti</string>
<string name="total_time">Celkový čas</string>
<string name="average_time">Průměrný čas</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Quit</string>
<string name="are_you_sure">Are you sure?</string>
<string name="enable_automatic_flags">Enable automatic placing of flags</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Open Areas</string>
<string name="total_time">Total Time</string>
<string name="average_time">Average Time</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Beenden</string>
<string name="are_you_sure">Bist du sicher?</string>
<string name="enable_automatic_flags">Automatische Markierungen aktivieren</string>
<string name="enable_no_guessing_maps">Lösbare Karten finden</string>
<string name="open_areas">Offene Flächen</string>
<string name="total_time">Gesamtzeit</string>
<string name="average_time">Durchschnittliche Zeit</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Έξοδος</string>
<string name="are_you_sure">Είσαι σίγουρος;</string>
<string name="enable_automatic_flags">Ενεργοποίηση αυτόματης τοποθέτησης σημαιών</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Ανοιχτές περιοχές</string>
<string name="total_time">Συνολικός Χρόνος</string>
<string name="average_time">Μέσος χρόνος</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Quit</string>
<string name="are_you_sure">Are you sure?</string>
<string name="enable_automatic_flags">Enable automatic placing of flags</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Open Areas</string>
<string name="total_time">Total Time</string>
<string name="average_time">Average Time</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Salir</string>
<string name="are_you_sure">¿Estás seguro?</string>
<string name="enable_automatic_flags">Activar situación automática de banderas</string>
<string name="enable_no_guessing_maps">Encontrar mapas solubles</string>
<string name="open_areas">Abrir Áreas</string>
<string name="total_time">Tiempo total</string>
<string name="average_time">Tiempo promedio</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Lopeta</string>
<string name="are_you_sure">Oletko varma?</string>
<string name="enable_automatic_flags">Kytke lippujen automaattinen asettaminen päälle</string>
<string name="enable_no_guessing_maps">Etsi ratkaistavia karttoja</string>
<string name="open_areas">Avaa alueita</string>
<string name="total_time">Aika yhteensä</string>
<string name="average_time">Keskimääräinen aika</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Quitter</string>
<string name="are_you_sure">Vraiment ?</string>
<string name="enable_automatic_flags">Activer le placement automatique de drapeaux</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Zones ouvertes</string>
<string name="total_time">Temps de jeu total</string>
<string name="average_time">Temps de jeu moyen</string>

View file

@ -48,7 +48,6 @@
<string name="quit">समाप्त करें</string>
<string name="are_you_sure">क्या आपको यकीन है?</string>
<string name="enable_automatic_flags">फ्लैग के स्वचालित रखे जाने को सक्रिय क</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Open Areas</string>
<string name="total_time">कुल समय</string>
<string name="average_time">औसत समय</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Kilépés</string>
<string name="are_you_sure">Biztos vagy benne?</string>
<string name="enable_automatic_flags">A zászlók automatikus elhelyezésének engedélyezése</string>
<string name="enable_no_guessing_maps">Megoldható térképek keresése</string>
<string name="open_areas">Nyílt területek</string>
<string name="total_time">Teljes játékidő</string>
<string name="average_time">Átlagos játékidő</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Keluar</string>
<string name="are_you_sure">Anda yakin?</string>
<string name="enable_automatic_flags">Aktifkan penempatan bendera otomatis</string>
<string name="enable_no_guessing_maps">Cari bidang ranjau yang dapat dipecahkan</string>
<string name="open_areas">Buka</string>
<string name="total_time">Total waktu</string>
<string name="average_time">Waktu rata-rata</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Esci</string>
<string name="are_you_sure">Sei sicuro?</string>
<string name="enable_automatic_flags">Abilita il posizionamento automatico delle bandiere</string>
<string name="enable_no_guessing_maps">Trova le mappe risolvibili</string>
<string name="open_areas">Aree Aperte</string>
<string name="total_time">Tempo Totale</string>
<string name="average_time">Durata Media</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Quit</string>
<string name="are_you_sure">Are you sure?</string>
<string name="enable_automatic_flags">Enable automatic placing of flags</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Open Areas</string>
<string name="total_time">Total Time</string>
<string name="average_time">Average Time</string>

View file

@ -48,7 +48,6 @@
<string name="quit">終了</string>
<string name="are_you_sure">本当によろしいですか?</string>
<string name="enable_automatic_flags">旗を自動で立てる</string>
<string name="enable_no_guessing_maps">解決可能なマップを検索</string>
<string name="open_areas">開いたエリアの数</string>
<string name="total_time">合計時間</string>
<string name="average_time">平均時間</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Quit</string>
<string name="are_you_sure">Are you sure?</string>
<string name="enable_automatic_flags">Enable automatic placing of flags</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Open Areas</string>
<string name="total_time">Total Time</string>
<string name="average_time">Average Time</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Stoppen</string>
<string name="are_you_sure">Weet je het zeker?</string>
<string name="enable_automatic_flags">Schakel automatisch plaatsen van vlaggen in</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Open gebieden</string>
<string name="total_time">Totale Tijd</string>
<string name="average_time">Gemiddelde Tijd</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Avslutt</string>
<string name="are_you_sure">Er du sikker?</string>
<string name="enable_automatic_flags">Aktiver automatisk plassering av flagg</string>
<string name="enable_no_guessing_maps">Finn løsbare kart</string>
<string name="open_areas">Åpne områder</string>
<string name="total_time">Total Tid</string>
<string name="average_time">Gjennomsnittlig Tid</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Wyjdź</string>
<string name="are_you_sure">Na pewno?</string>
<string name="enable_automatic_flags">Włącz automatyczne umieszczanie flag</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Odkryte pola</string>
<string name="total_time">Całkowity czas gry</string>
<string name="average_time">Średni czas gry</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Sair</string>
<string name="are_you_sure">Tem certeza?</string>
<string name="enable_automatic_flags">Ativar colocação automática de bandeiras</string>
<string name="enable_no_guessing_maps">Calcular mapas solucionáveis</string>
<string name="open_areas">Áreas abertas</string>
<string name="total_time">Tempo total</string>
<string name="average_time">Tempo médio</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Quit</string>
<string name="are_you_sure">Are you sure?</string>
<string name="enable_automatic_flags">Activar colocação automática de bandeiras</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Open Areas</string>
<string name="total_time">Total Time</string>
<string name="average_time">Average Time</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Quit</string>
<string name="are_you_sure">Are you sure?</string>
<string name="enable_automatic_flags">Enable automatic placing of flags</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Open Areas</string>
<string name="total_time">Total Time</string>
<string name="average_time">Average Time</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Выход</string>
<string name="are_you_sure">Вы уверены?</string>
<string name="enable_automatic_flags">Включить авторазмещение флагов</string>
<string name="enable_no_guessing_maps">Найти беспроигрышные поля</string>
<string name="open_areas">Открытые зоны</string>
<string name="total_time">Общее время</string>
<string name="average_time">Среднее время</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Quit</string>
<string name="are_you_sure">Are you sure?</string>
<string name="enable_automatic_flags">Enable automatic placing of flags</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Open Areas</string>
<string name="total_time">Total Time</string>
<string name="average_time">Average Time</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Quit</string>
<string name="are_you_sure">Are you sure?</string>
<string name="enable_automatic_flags">Enable automatic placing of flags</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Open Areas</string>
<string name="total_time">Total Time</string>
<string name="average_time">Average Time</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Çık</string>
<string name="are_you_sure">Emin misiniz?</string>
<string name="enable_automatic_flags">Bayrakları Otomatik Yerleştir</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">ık Alanlar</string>
<string name="total_time">Toplam Süre</string>
<string name="average_time">Ortalama Süre</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Вийти</string>
<string name="are_you_sure">Ви впевнені?</string>
<string name="enable_automatic_flags">Увімкнути автоматичне розміщення прапорців</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Відкриті області</string>
<string name="total_time">Час перегляду</string>
<string name="average_time">Середній час</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Thoát</string>
<string name="are_you_sure">Bạn chắc chứ?</string>
<string name="enable_automatic_flags">Bật tự động để cờ</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Khu vực mở</string>
<string name="total_time">Tổng thời gian</string>
<string name="average_time">Thời gian bình quân</string>

View file

@ -48,7 +48,6 @@
<string name="quit">退出</string>
<string name="are_you_sure">你确定吗?</string>
<string name="enable_automatic_flags">启用旗标自动放置</string>
<string name="enable_no_guessing_maps">查找可解决的地图</string>
<string name="open_areas">开放区域</string>
<string name="total_time">总用时</string>
<string name="average_time">平均用时</string>

View file

@ -48,7 +48,6 @@
<string name="quit">Quit</string>
<string name="are_you_sure">Are you sure?</string>
<string name="enable_automatic_flags">Enable automatic placing of flags</string>
<string name="enable_no_guessing_maps">Find solvable maps</string>
<string name="open_areas">Open Areas</string>
<string name="total_time">Total Time</string>
<string name="average_time">Average Time</string>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
@ -21,13 +21,6 @@
android:title="@string/enable_automatic_flags"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:checked="false"
android:defaultValue="false"
android:key="preference_use_solver_algorithms"
android:title="@string/enable_no_guessing_maps"
app:iconSpaceReserved="false" />
</PreferenceCategory>
<PreferenceCategory
@ -69,4 +62,4 @@
</PreferenceCategory>
</androidx.preference.PreferenceScreen>
</PreferenceScreen>

View file

@ -89,7 +89,7 @@ class GameControllerTest {
withGameController { controller ->
val lastMine = controller.field.last { it.hasMine }
assertEquals(
listOf(92, 91, 81, 70, 86, 41, 87, 67, 33, 36, 13, 59, 38, 27, 49, 4, 39, 5, 29, 19),
listOf(95, 85, 74, 73, 65, 88, 55, 91, 45, 52, 90, 47, 59, 42, 36, 32, 39, 28, 4, 3),
controller.takeExplosionRadius(lastMine).map { it.id }.toList()
)

View file

@ -83,7 +83,7 @@ class MinefieldFactoryTest {
preferencesRepository
).run {
assertEquals(49, width)
assertEquals(97, height)
assertEquals(96, height)
assertEquals(950, mines)
}
}

View file

@ -26,16 +26,14 @@ class BruteForceSolverTest {
fun isSolvable() {
handleMinefield { handler, minefield ->
handler.openAt(40)
val bruteForceSolver =
BruteForceSolver(minefield.toMutableList())
assertTrue(bruteForceSolver.isSolvable())
val bruteForceSolver = BruteForceSolver()
assertTrue(bruteForceSolver.trySolve(minefield.toMutableList()))
}
handleMinefield { handler, minefield ->
handler.openAt(0)
val bruteForceSolver =
BruteForceSolver(minefield.toMutableList())
assertFalse(bruteForceSolver.isSolvable())
val bruteForceSolver = BruteForceSolver()
assertFalse(bruteForceSolver.trySolve(minefield.toMutableList()))
}
}
}

View file

@ -0,0 +1,55 @@
package dev.lucasnlm.antimine.common.level.solver
import dev.lucasnlm.antimine.common.level.logic.MinefieldCreator
import dev.lucasnlm.antimine.common.level.logic.MinefieldHandler
import dev.lucasnlm.antimine.common.level.models.Area
import dev.lucasnlm.antimine.common.level.models.Minefield
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import java.lang.Thread.sleep
import kotlin.random.Random
class LimitedBruteForceSolverTest {
private fun handleMinefield(block: (MinefieldHandler, MutableList<Area>) -> Unit) {
val creator = MinefieldCreator(
Minefield(9, 9, 12),
Random(200)
)
val minefield = creator.create(40, true).toMutableList()
val minefieldHandler =
MinefieldHandler(minefield, false)
block(minefieldHandler, minefield)
}
@Test
fun isSolvable() {
handleMinefield { handler, minefield ->
handler.openAt(40)
val bruteForceSolver = LimitedBruteForceSolver()
assertTrue(bruteForceSolver.trySolve(minefield.toMutableList()))
}
handleMinefield { handler, minefield ->
handler.openAt(0)
val bruteForceSolver = LimitedBruteForceSolver()
assertFalse(bruteForceSolver.trySolve(minefield.toMutableList()))
}
}
@Test
fun shouldntKeepTryingAfterTimout() {
handleMinefield { handler, _ ->
handler.openAt(40)
val bruteForceSolver = LimitedBruteForceSolver(1000L)
assertTrue(bruteForceSolver.keepTrying())
}
handleMinefield { handler, _ ->
handler.openAt(0)
val bruteForceSolver = LimitedBruteForceSolver(50)
sleep(100)
assertFalse(bruteForceSolver.keepTrying())
}
}
}