Add limited brute force algorithm

This commit is contained in:
Lucas Lima 2020-08-06 22:11:05 -03:00
parent 818b05f5d9
commit 42ed255f3c
No known key found for this signature in database
GPG key ID: C5EEF4C30BFBF8D7
6 changed files with 94 additions and 18 deletions

View file

@ -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)
}

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

@ -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())
}
}
}