Add custom control support
This commit is contained in:
parent
abcf8d24bf
commit
e91cbf285e
7 changed files with 155 additions and 162 deletions
|
@ -3,7 +3,7 @@ package dev.lucasnlm.antimine.mocks
|
||||||
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackInteractor
|
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackInteractor
|
||||||
|
|
||||||
class DisabledHapticFeedbackInteractor : IHapticFeedbackInteractor {
|
class DisabledHapticFeedbackInteractor : IHapticFeedbackInteractor {
|
||||||
override fun toggleFlagFeedback() { }
|
override fun longPressFeedback() { }
|
||||||
|
|
||||||
override fun explosionFeedback() { }
|
override fun explosionFeedback() { }
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,9 @@ import dev.lucasnlm.antimine.common.level.models.Difficulty
|
||||||
import dev.lucasnlm.antimine.common.level.models.Mark
|
import dev.lucasnlm.antimine.common.level.models.Mark
|
||||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||||
import dev.lucasnlm.antimine.common.level.models.Score
|
import dev.lucasnlm.antimine.common.level.models.Score
|
||||||
|
import dev.lucasnlm.antimine.core.control.Action
|
||||||
|
import dev.lucasnlm.antimine.core.control.ActionFeedback
|
||||||
|
import dev.lucasnlm.antimine.core.control.GameControl
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
@ -18,6 +21,7 @@ class LevelFacade {
|
||||||
private val startTime = System.currentTimeMillis()
|
private val startTime = System.currentTimeMillis()
|
||||||
private var saveId = 0
|
private var saveId = 0
|
||||||
private var firstOpen: FirstOpen = FirstOpen.Unknown
|
private var firstOpen: FirstOpen = FirstOpen.Unknown
|
||||||
|
private val gameControl: GameControl = GameControl.Standard
|
||||||
|
|
||||||
var hasMines = false
|
var hasMines = false
|
||||||
private set
|
private set
|
||||||
|
@ -66,8 +70,43 @@ class LevelFacade {
|
||||||
|
|
||||||
fun getArea(id: Int) = field.first { it.id == id }
|
fun getArea(id: Int) = field.first { it.id == id }
|
||||||
|
|
||||||
fun switchMarkAt(index: Int): Area =
|
/**
|
||||||
getArea(index).apply {
|
* Run a game [action] on a given tile.
|
||||||
|
* @return The number of changed tiles.
|
||||||
|
*/
|
||||||
|
private fun Area.runActionOn(action: Action?): ActionFeedback {
|
||||||
|
val highlightedChanged = turnOffAllHighlighted()
|
||||||
|
|
||||||
|
val changed = when (action) {
|
||||||
|
Action.OpenTile -> {
|
||||||
|
if (!hasMines) {
|
||||||
|
plantMinesExcept(id, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mark.isNotNone()) {
|
||||||
|
mark = Mark.PurposefulNone
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
openTile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Action.SwitchMark -> {
|
||||||
|
if (isCovered) switchMark()
|
||||||
|
1
|
||||||
|
}
|
||||||
|
Action.HighlightNeighbors -> {
|
||||||
|
if (minesAround != 0) highlight() else 0
|
||||||
|
}
|
||||||
|
Action.OpenNeighbors -> {
|
||||||
|
if (!isCovered) { openNeighbors() } else { 0 }
|
||||||
|
}
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionFeedback(action, id, (changed + highlightedChanged) > 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Area.switchMark(): Area = apply {
|
||||||
if (isCovered) {
|
if (isCovered) {
|
||||||
mark = when (mark) {
|
mark = when (mark) {
|
||||||
Mark.PurposefulNone, Mark.None -> Mark.Flag
|
Mark.PurposefulNone, Mark.None -> Mark.Flag
|
||||||
|
@ -82,14 +121,6 @@ class LevelFacade {
|
||||||
mark = Mark.PurposefulNone
|
mark = Mark.PurposefulNone
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasCoverOn(index: Int): Boolean = getArea(index).isCovered
|
|
||||||
|
|
||||||
fun hasMarkOn(index: Int): Boolean = getArea(index).mark.isNotNone()
|
|
||||||
|
|
||||||
fun hasNoneOn(index: Int): Boolean = getArea(index).mark.isNone()
|
|
||||||
|
|
||||||
fun isHighlighted(index: Int): Boolean = getArea(index).highlighted
|
|
||||||
|
|
||||||
fun plantMinesExcept(index: Int, includeSafeArea: Boolean = false) {
|
fun plantMinesExcept(index: Int, includeSafeArea: Boolean = false) {
|
||||||
plantRandomMines(index, includeSafeArea)
|
plantRandomMines(index, includeSafeArea)
|
||||||
putMinesTips()
|
putMinesTips()
|
||||||
|
@ -136,9 +167,9 @@ class LevelFacade {
|
||||||
/**
|
/**
|
||||||
* Run "Flood Fill algorithm" to open all empty neighbors of a target area.
|
* Run "Flood Fill algorithm" to open all empty neighbors of a target area.
|
||||||
*/
|
*/
|
||||||
fun openField(target: Area): Int {
|
fun Area.openTile(): Int {
|
||||||
var changes = 0
|
var changes = 0
|
||||||
target.run {
|
run {
|
||||||
if (isCovered) {
|
if (isCovered) {
|
||||||
changes += 1
|
changes += 1
|
||||||
isCovered = false
|
isCovered = false
|
||||||
|
@ -152,7 +183,7 @@ class LevelFacade {
|
||||||
.also {
|
.also {
|
||||||
changes += it.count()
|
changes += it.count()
|
||||||
}
|
}
|
||||||
.forEach { openField(it) }
|
.forEach { it.openTile() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,16 +192,13 @@ class LevelFacade {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable all highlighted areas.
|
* Disable all highlighted areas.
|
||||||
*
|
* @return the number of changed tiles.
|
||||||
* @return true if any area was changed.
|
|
||||||
*/
|
*/
|
||||||
fun turnOffAllHighlighted(): Boolean {
|
fun turnOffAllHighlighted(): Int {
|
||||||
var changed: Boolean
|
return field.filter { it.highlighted }.run {
|
||||||
field
|
forEach { it.highlighted = false }
|
||||||
.filter { it.highlighted }
|
count()
|
||||||
.also { changed = it.count() != 0 }
|
}
|
||||||
.forEach { it.highlighted = false }
|
|
||||||
return changed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toggleHighlight(target: Area): Int {
|
private fun toggleHighlight(target: Area): Int {
|
||||||
|
@ -185,34 +213,8 @@ class LevelFacade {
|
||||||
return changed
|
return changed
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a given area by its index.
|
|
||||||
*
|
|
||||||
* @param index the target index
|
|
||||||
* @return true if multiple areas were open
|
|
||||||
*/
|
|
||||||
fun singleClick(index: Int): Int = getArea(index).run {
|
|
||||||
return when {
|
|
||||||
isCovered -> {
|
|
||||||
openField(getArea(index))
|
|
||||||
}
|
|
||||||
minesAround != 0 -> {
|
|
||||||
toggleHighlight(this)
|
|
||||||
}
|
|
||||||
else -> 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun doubleClick(index: Int): Int = getArea(index).run {
|
private fun Area.highlight(): Int = run {
|
||||||
return when {
|
|
||||||
isCovered -> {
|
|
||||||
openField(getArea(index))
|
|
||||||
}
|
|
||||||
else -> 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun highlight(index: Int): Int = getArea(index).run {
|
|
||||||
return when {
|
return when {
|
||||||
minesAround != 0 -> {
|
minesAround != 0 -> {
|
||||||
toggleHighlight(this)
|
toggleHighlight(this)
|
||||||
|
@ -221,41 +223,42 @@ class LevelFacade {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
fun singleClick(index: Int): ActionFeedback = getArea(index).run {
|
||||||
fun longPressOpenArea(index: Int): Sequence<Area> {
|
return if (isCovered) {
|
||||||
val neighbors = getArea(index).findNeighbors().filter {
|
runActionOn(gameControl.onCovered.singleClick)
|
||||||
it.mark.isNone() && it.isCovered
|
|
||||||
}
|
|
||||||
|
|
||||||
val neighborsCount = neighbors.count()
|
|
||||||
val minesCount = neighbors.count { it.hasMine }
|
|
||||||
|
|
||||||
if (neighborsCount == minesCount) {
|
|
||||||
neighbors.forEach { area -> area.mark = Mark.Flag }
|
|
||||||
} else {
|
} else {
|
||||||
neighbors.forEach { area -> openField(area) }
|
runActionOn(gameControl.onOpen.singleClick)
|
||||||
}
|
}
|
||||||
|
|
||||||
return neighbors
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fun doubleClick(index: Int): ActionFeedback = getArea(index).run {
|
||||||
* Open all tiles neighbors of [index].
|
return if (isCovered) {
|
||||||
* If the number of flagged neighbors is less than the number of neighbors
|
runActionOn(gameControl.onCovered.doubleClick)
|
||||||
* it won't open any of them to avoid miss clicks.
|
} else {
|
||||||
*/
|
runActionOn(gameControl.onOpen.doubleClick)
|
||||||
fun openNeighbors(index: Int): Sequence<Area> {
|
}
|
||||||
val neighbors = getArea(index).findNeighbors()
|
}
|
||||||
|
|
||||||
|
fun longPress(index: Int): ActionFeedback = getArea(index).run {
|
||||||
|
return if (isCovered) {
|
||||||
|
runActionOn(gameControl.onCovered.longPress)
|
||||||
|
} else {
|
||||||
|
runActionOn(gameControl.onOpen.longPress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Area.openNeighbors(): Int {
|
||||||
|
val neighbors = findNeighbors()
|
||||||
val flaggedCount = neighbors.count { it.mark.isFlag() }
|
val flaggedCount = neighbors.count { it.mark.isFlag() }
|
||||||
return if (flaggedCount >= getArea(index).minesAround) {
|
return if (flaggedCount >= minesAround) {
|
||||||
neighbors
|
neighbors
|
||||||
.filter {
|
.filter {
|
||||||
it.mark.isNone() && it.isCovered
|
it.mark.isNone() && it.isCovered
|
||||||
}.also {
|
}.also {
|
||||||
it.forEach { area -> openField(area) }
|
it.forEach { area -> area.openTile() }
|
||||||
}
|
}.count()
|
||||||
} else {
|
} else {
|
||||||
sequenceOf()
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import android.os.Vibrator
|
||||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||||
|
|
||||||
interface IHapticFeedbackInteractor {
|
interface IHapticFeedbackInteractor {
|
||||||
fun toggleFlagFeedback()
|
fun longPressFeedback()
|
||||||
fun explosionFeedback()
|
fun explosionFeedback()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ class HapticFeedbackInteractor(
|
||||||
) : IHapticFeedbackInteractor {
|
) : IHapticFeedbackInteractor {
|
||||||
private val vibrator: Vibrator = context.getSystemService(VIBRATOR_SERVICE) as Vibrator
|
private val vibrator: Vibrator = context.getSystemService(VIBRATOR_SERVICE) as Vibrator
|
||||||
|
|
||||||
override fun toggleFlagFeedback() {
|
override fun longPressFeedback() {
|
||||||
if (preferencesRepository.useHapticFeedback()) {
|
if (preferencesRepository.useHapticFeedback()) {
|
||||||
vibrateTo(70, 240)
|
vibrateTo(70, 240)
|
||||||
vibrateTo(10, 100)
|
vibrateTo(10, 100)
|
||||||
|
@ -44,7 +44,7 @@ class HapticFeedbackInteractor(
|
||||||
}
|
}
|
||||||
|
|
||||||
class DisabledIHapticFeedbackInteractor : IHapticFeedbackInteractor {
|
class DisabledIHapticFeedbackInteractor : IHapticFeedbackInteractor {
|
||||||
override fun toggleFlagFeedback() { }
|
override fun longPressFeedback() { }
|
||||||
|
|
||||||
override fun explosionFeedback() { }
|
override fun explosionFeedback() { }
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ import dev.lucasnlm.antimine.common.level.utils.Clock
|
||||||
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackInteractor
|
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackInteractor
|
||||||
import dev.lucasnlm.antimine.core.analytics.AnalyticsManager
|
import dev.lucasnlm.antimine.core.analytics.AnalyticsManager
|
||||||
import dev.lucasnlm.antimine.core.analytics.models.Analytics
|
import dev.lucasnlm.antimine.core.analytics.models.Analytics
|
||||||
|
import dev.lucasnlm.antimine.core.control.Action
|
||||||
|
import dev.lucasnlm.antimine.core.control.ActionFeedback
|
||||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
@ -40,7 +42,6 @@ class GameViewModel @ViewModelInject constructor(
|
||||||
private lateinit var levelFacade: LevelFacade
|
private lateinit var levelFacade: LevelFacade
|
||||||
private var currentDifficulty: Difficulty = Difficulty.Standard
|
private var currentDifficulty: Difficulty = Difficulty.Standard
|
||||||
private var initialized = false
|
private var initialized = false
|
||||||
private var oldGame = false
|
|
||||||
|
|
||||||
val field = MutableLiveData<Sequence<Area>>()
|
val field = MutableLiveData<Sequence<Area>>()
|
||||||
val fieldRefresh = MutableLiveData<Int>()
|
val fieldRefresh = MutableLiveData<Int>()
|
||||||
|
@ -147,7 +148,6 @@ class GameViewModel @ViewModelInject constructor(
|
||||||
startNewGame()
|
startNewGame()
|
||||||
}.also {
|
}.also {
|
||||||
initialized = true
|
initialized = true
|
||||||
oldGame = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +163,6 @@ class GameViewModel @ViewModelInject constructor(
|
||||||
startNewGame()
|
startNewGame()
|
||||||
}.also {
|
}.also {
|
||||||
initialized = true
|
initialized = true
|
||||||
oldGame = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +178,6 @@ class GameViewModel @ViewModelInject constructor(
|
||||||
startNewGame()
|
startNewGame()
|
||||||
}.also {
|
}.also {
|
||||||
initialized = true
|
initialized = true
|
||||||
oldGame = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,44 +216,29 @@ class GameViewModel @ViewModelInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onLongClick(index: Int) {
|
fun onLongClick(index: Int) {
|
||||||
val isHighlighted = levelFacade.isHighlighted(index)
|
val feedback = levelFacade.longPress(index)
|
||||||
levelFacade.turnOffAllHighlighted()
|
refreshIndex(index, feedback.multipleChanges)
|
||||||
refreshAll()
|
onFeedbackAnalytics(feedback)
|
||||||
|
onPostAction()
|
||||||
if (levelFacade.hasCoverOn(index)) {
|
|
||||||
levelFacade.switchMarkAt(index).run {
|
|
||||||
refreshIndex(id)
|
|
||||||
hapticFeedbackInteractor.toggleFlagFeedback()
|
|
||||||
}
|
|
||||||
|
|
||||||
analyticsManager.sentEvent(Analytics.LongPressArea(index))
|
|
||||||
} else if (!preferencesRepository.useDoubleClickToOpen() || isHighlighted) {
|
|
||||||
levelFacade.openNeighbors(index).forEach { refreshIndex(it.id) }
|
|
||||||
analyticsManager.sentEvent(Analytics.LongPressMultipleArea(index))
|
|
||||||
} else {
|
|
||||||
levelFacade.highlight(index).run {
|
|
||||||
refreshIndex(index, this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateGameState()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onDoubleClickArea(index: Int) {
|
fun onDoubleClickArea(index: Int) {
|
||||||
if (levelFacade.turnOffAllHighlighted()) {
|
val feedback = levelFacade.doubleClick(index)
|
||||||
refreshAll()
|
refreshIndex(index, feedback.multipleChanges)
|
||||||
}
|
onFeedbackAnalytics(feedback)
|
||||||
|
onPostAction()
|
||||||
|
|
||||||
if (preferencesRepository.useDoubleClickToOpen()) {
|
hapticFeedbackInteractor.longPressFeedback()
|
||||||
if (!levelFacade.hasMines) {
|
}
|
||||||
levelFacade.plantMinesExcept(index, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
levelFacade.doubleClick(index).run {
|
fun onSingleClick(index: Int) {
|
||||||
refreshIndex(index, this)
|
val feedback = levelFacade.singleClick(index)
|
||||||
}
|
refreshIndex(index, feedback.multipleChanges)
|
||||||
}
|
onFeedbackAnalytics(feedback)
|
||||||
|
onPostAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onPostAction() {
|
||||||
if (preferencesRepository.useFlagAssistant() && !levelFacade.hasAnyMineExploded()) {
|
if (preferencesRepository.useFlagAssistant() && !levelFacade.hasAnyMineExploded()) {
|
||||||
levelFacade.runFlagAssistant().forEach {
|
levelFacade.runFlagAssistant().forEach {
|
||||||
Handler().post {
|
Handler().post {
|
||||||
|
@ -265,51 +248,19 @@ class GameViewModel @ViewModelInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGameState()
|
updateGameState()
|
||||||
analyticsManager.sentEvent(Analytics.PressArea(index))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSingleClick(index: Int) {
|
private fun onFeedbackAnalytics(feedback: ActionFeedback) {
|
||||||
var openAnyArea = false
|
when (feedback.action) {
|
||||||
|
Action.OpenTile -> { analyticsManager.sentEvent(Analytics.OpenTile(feedback.index)) }
|
||||||
if (levelFacade.turnOffAllHighlighted()) {
|
Action.SwitchMark -> { analyticsManager.sentEvent(Analytics.SwitchMark(feedback.index)) }
|
||||||
refreshAll()
|
Action.HighlightNeighbors -> { analyticsManager.sentEvent(Analytics.HighlightNeighbors(feedback.index)) }
|
||||||
}
|
Action.OpenNeighbors -> { analyticsManager.sentEvent(Analytics.OpenNeighbors(feedback.index)) }
|
||||||
|
|
||||||
if (levelFacade.hasMarkOn(index)) {
|
|
||||||
levelFacade.removeMark(index).run {
|
|
||||||
refreshIndex(id)
|
|
||||||
}
|
|
||||||
hapticFeedbackInteractor.toggleFlagFeedback()
|
|
||||||
} else if (!preferencesRepository.useDoubleClickToOpen() || !levelFacade.hasMines) {
|
|
||||||
if (!levelFacade.hasMines) {
|
|
||||||
levelFacade.plantMinesExcept(index, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
levelFacade.singleClick(index).run {
|
|
||||||
refreshIndex(index, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
openAnyArea = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (openAnyArea) {
|
|
||||||
if (preferencesRepository.useFlagAssistant() && !levelFacade.hasAnyMineExploded()) {
|
|
||||||
levelFacade.runFlagAssistant().forEach {
|
|
||||||
Handler().post {
|
|
||||||
refreshIndex(it.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateGameState()
|
|
||||||
analyticsManager.sentEvent(Analytics.PressArea(index))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshMineCount() = mineCount.postValue(levelFacade.remainingMines())
|
private fun refreshMineCount() = mineCount.postValue(levelFacade.remainingMines())
|
||||||
|
|
||||||
fun isCurrentGame() = !oldGame
|
|
||||||
|
|
||||||
private fun updateGameState() {
|
private fun updateGameState() {
|
||||||
when {
|
when {
|
||||||
levelFacade.hasAnyMineExploded() -> {
|
levelFacade.hasAnyMineExploded() -> {
|
||||||
|
@ -393,8 +344,8 @@ class GameViewModel @ViewModelInject constructor(
|
||||||
|
|
||||||
fun useAccessibilityMode() = preferencesRepository.useLargeAreas()
|
fun useAccessibilityMode() = preferencesRepository.useLargeAreas()
|
||||||
|
|
||||||
private fun refreshIndex(targetIndex: Int, changes: Int = 1) {
|
private fun refreshIndex(targetIndex: Int, multipleChanges: Boolean = false) {
|
||||||
if (!preferencesRepository.useAnimations() || changes > 1) {
|
if (!preferencesRepository.useAnimations() || multipleChanges) {
|
||||||
field.postValue(levelFacade.field)
|
field.postValue(levelFacade.field)
|
||||||
} else {
|
} else {
|
||||||
fieldRefresh.postValue(targetIndex)
|
fieldRefresh.postValue(targetIndex)
|
||||||
|
|
|
@ -48,12 +48,13 @@ sealed class Analytics(
|
||||||
|
|
||||||
class ResumePreviousGame : Analytics("Resume previous game")
|
class ResumePreviousGame : Analytics("Resume previous game")
|
||||||
|
|
||||||
class LongPressArea(index: Int) : Analytics("Long press area", mapOf("Index" to index.toString()))
|
class OpenTile(index: Int) : Analytics("Open Tile", mapOf("Index" to index.toString()))
|
||||||
|
|
||||||
class LongPressMultipleArea(index: Int) :
|
class SwitchMark(index: Int) : Analytics("Switch Mark", mapOf("Index" to index.toString()))
|
||||||
Analytics("Long press to open multiple", mapOf("Index" to index.toString()))
|
|
||||||
|
|
||||||
class PressArea(index: Int) : Analytics("Press area", mapOf("Index" to index.toString()))
|
class HighlightNeighbors(index: Int) : Analytics("Highlight Neighbors", mapOf("Index" to index.toString()))
|
||||||
|
|
||||||
|
class OpenNeighbors(index: Int) : Analytics("Open Neighbors", mapOf("Index" to index.toString()))
|
||||||
|
|
||||||
class GameOver(time: Long, score: Score) : Analytics(
|
class GameOver(time: Long, score: Score) : Analytics(
|
||||||
"Game Over",
|
"Game Over",
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package dev.lucasnlm.antimine.core.control
|
||||||
|
|
||||||
|
enum class Action {
|
||||||
|
OpenTile,
|
||||||
|
SwitchMark,
|
||||||
|
HighlightNeighbors,
|
||||||
|
OpenNeighbors,
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Actions(
|
||||||
|
val singleClick: Action?,
|
||||||
|
val doubleClick: Action?,
|
||||||
|
val longPress: Action?
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed class GameControl(
|
||||||
|
val onCovered: Actions,
|
||||||
|
val onOpen: Actions
|
||||||
|
) {
|
||||||
|
object Standard : GameControl(
|
||||||
|
onCovered = Actions(
|
||||||
|
singleClick = Action.OpenTile,
|
||||||
|
longPress = Action.SwitchMark,
|
||||||
|
doubleClick = null
|
||||||
|
),
|
||||||
|
onOpen = Actions(
|
||||||
|
singleClick = Action.HighlightNeighbors,
|
||||||
|
longPress = Action.OpenNeighbors,
|
||||||
|
doubleClick = null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ActionFeedback(
|
||||||
|
val action: Action?,
|
||||||
|
val index: Int,
|
||||||
|
val multipleChanges: Boolean
|
||||||
|
)
|
|
@ -197,21 +197,21 @@ class LevelFacadeTest {
|
||||||
fun testFlagAssistant() {
|
fun testFlagAssistant() {
|
||||||
levelFacadeOf(3, 3, 1, 200L).run {
|
levelFacadeOf(3, 3, 1, 200L).run {
|
||||||
plantMinesExcept(3)
|
plantMinesExcept(3)
|
||||||
field.filterNot { it.hasMine }.forEach { openField(it) }
|
field.filterNot { it.hasMine }.forEach { openTile(it) }
|
||||||
runFlagAssistant()
|
runFlagAssistant()
|
||||||
field.filter { it.hasMine }.map { it.mark.isFlag() }.forEach(::assertTrue)
|
field.filter { it.hasMine }.map { it.mark.isFlag() }.forEach(::assertTrue)
|
||||||
}
|
}
|
||||||
|
|
||||||
levelFacadeOf(3, 3, 2, 200L).run {
|
levelFacadeOf(3, 3, 2, 200L).run {
|
||||||
plantMinesExcept(3)
|
plantMinesExcept(3)
|
||||||
field.filterNot { it.hasMine }.forEach { openField(it) }
|
field.filterNot { it.hasMine }.forEach { openTile(it) }
|
||||||
runFlagAssistant()
|
runFlagAssistant()
|
||||||
field.filter { it.hasMine }.map { it.mark.isFlag() }.forEach(::assertTrue)
|
field.filter { it.hasMine }.map { it.mark.isFlag() }.forEach(::assertTrue)
|
||||||
}
|
}
|
||||||
|
|
||||||
levelFacadeOf(3, 3, 8, 200L).run {
|
levelFacadeOf(3, 3, 8, 200L).run {
|
||||||
plantMinesExcept(3)
|
plantMinesExcept(3)
|
||||||
field.filterNot { it.hasMine }.forEach { openField(it) }
|
field.filterNot { it.hasMine }.forEach { openTile(it) }
|
||||||
runFlagAssistant()
|
runFlagAssistant()
|
||||||
field.filter { it.hasMine }.map { it.mark.isFlag() }.forEach(::assertFalse)
|
field.filter { it.hasMine }.map { it.mark.isFlag() }.forEach(::assertFalse)
|
||||||
}
|
}
|
||||||
|
@ -419,7 +419,7 @@ class LevelFacadeTest {
|
||||||
plantMinesExcept(3)
|
plantMinesExcept(3)
|
||||||
val mine = field.first { it.hasMine }
|
val mine = field.first { it.hasMine }
|
||||||
assertEquals(findExplodedMine(), null)
|
assertEquals(findExplodedMine(), null)
|
||||||
openField(mine)
|
openTile(mine)
|
||||||
assertEquals(findExplodedMine(), mine)
|
assertEquals(findExplodedMine(), mine)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue