Add limited brute force algorithm
This commit is contained in:
parent
818b05f5d9
commit
42ed255f3c
6 changed files with 94 additions and 18 deletions
|
@ -15,6 +15,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
|
||||
|
@ -69,12 +70,14 @@ class GameController {
|
|||
|
||||
private fun plantMinesExcept(safeId: Int) {
|
||||
if (useSolverAlgorithms) {
|
||||
val solver = LimitedBruteForceSolver()
|
||||
do {
|
||||
field = minefieldCreator.create(safeId, false)
|
||||
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 (!BruteForceSolver(minefieldHandler.result().toMutableList()).isSolvable())
|
||||
} while (solver.keepTrying() && !solver.trySolve(minefieldHandler.result().toMutableList()))
|
||||
} else {
|
||||
field = minefieldCreator.create(safeId, true)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue