Compare commits

...
Author SHA1 Message Date
admin 5c66c6d4e4 Remove Scala asInstanceOf usage 2026-06-19 22:23:05 -07:00
adminandGitHub 719323d3c7 Restore stale text request invariant (#7336) 2026-06-19 14:20:23 -07:00
adminandGitHub 47fd3733b2 Prune generated text after rewind (#7335)
* Prune text store entries after rewind

* Fix rewind controller test text store expectation
2026-06-19 14:12:12 -07:00
48 changed files with 972 additions and 587 deletions
+9
View File
@@ -47,3 +47,12 @@ sh_test(
"no-sandbox",
],
)
sh_test(
name = "no_scala_asinstanceof_test",
srcs = ["scripts/check_no_scala_asinstanceof.sh"],
tags = [
"local",
"no-sandbox",
],
)
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
matches="$(
grep -R -n "asInstanceOf" \
"${REPO_ROOT}/src/main/scala" \
"${REPO_ROOT}/src/test/scala" || true
)"
if [[ -n "${matches}" ]]; then
echo "Scala asInstanceOf usage is not allowed:" >&2
echo "${matches}" >&2
exit 1
fi
@@ -163,7 +163,7 @@ abstract class FunctionalRandom {
@nowarn def foldGen(rsU: RandomState[S[U]], t: T): RandomState[S[U]] =
rsU.continue {
case (us: S[U], fr: FunctionalRandom) =>
f(t, fr).map(x => (us ++ x).asInstanceOf[S[U]])
f(t, fr).map(x => xFactory.from(us ++ x))
}
FunctionalRandom.nextFoldLeft(
seq,
@@ -189,7 +189,7 @@ abstract class FunctionalRandom {
): RandomState[S[U]] =
rsU.continue {
case (us: S[U], fr: FunctionalRandom) =>
f(t, fr).map(x => (us :+ x).asInstanceOf[S[U]])
f(t, fr).map(x => xFactory.from(us :+ x))
}
FunctionalRandom.nextFoldLeft(seq, RandomState(xFactory.empty[U], this))(
@@ -131,7 +131,11 @@ class GeminiServiceImpl(
if candidates.isEmpty then return
val firstCandidate = candidates.head.asInstanceOf[JObject]
val firstCandidate = candidates.head match {
case obj: JObject => obj
case other =>
throw new IllegalArgumentException(s"Expected Gemini candidate object, got $other")
}
// Check finish reason
val finishReason = (firstCandidate \ "finishReason") match {
@@ -143,17 +147,20 @@ class GeminiServiceImpl(
// Extract text content
val textContent = for {
content <- (firstCandidate \ "content").toOption
parts <- (content \ "parts") match {
case JArray(arr) => Some(arr)
case _ => None
}
content <- (firstCandidate \ "content").toOption
parts <- (content \ "parts") match {
case JArray(arr) => Some(arr)
case _ => None
}
if parts.nonEmpty
firstPart = parts.head.asInstanceOf[JObject]
text <- (firstPart \ "text") match {
case JString(s) => Some(s)
case _ => None
}
firstPart <- parts.head match {
case obj: JObject => Some(obj)
case _ => None
}
text <- (firstPart \ "text") match {
case JString(s) => Some(s)
case _ => None
}
} yield text
val tokenUsage =
@@ -120,7 +120,11 @@ class OpenAIChatCompletionsServiceImpl(
val streamId = (parsedJson \ "id").extract[String]
// The final usage chunk has empty choices array and contains usage info
val choicesArray = (parsedJson \ "choices").asInstanceOf[JArray].arr
val choicesArray = (parsedJson \ "choices") match {
case JArray(arr) => arr
case other =>
throw new IllegalArgumentException(s"Expected choices array in OpenAI response, got $other")
}
if choicesArray.isEmpty then {
// This is the usage-only chunk (when stream_options.include_usage is true)
val promptTokens = (parsedJson \ "usage" \ "prompt_tokens").extractOpt[Int].getOrElse(0)
@@ -134,34 +138,37 @@ class OpenAIChatCompletionsServiceImpl(
)
)
} else {
val choice = choicesArray.head.asInstanceOf[JObject]
choicesArray.head match {
case choice: JObject =>
// FIXME: this will mark completed if there is any finish_reason; we should probably handle other possibilities
val completed = (choice \ "finish_reason") match {
case JString("stop") => true
case JString("length") => true
case JString("content_filter") => true
case JString("tool_calls") => true
case JString("function_call") => true
case JString(str) =>
println(s"Unexpected finish reason $str")
true
case _ => false
}
// FIXME: this will mark completed if there is any finish_reason; we should probably handle other possibilities
val completed = (choice \ "finish_reason") match {
case JString("stop") => true
case JString("length") => true
case JString("content_filter") => true
case JString("tool_calls") => true
case JString("function_call") => true
case JString(str) =>
println(s"Unexpected finish reason $str")
true
case _ => false
}
(choice \ "delta" \ "content") match {
case JString(contentString) =>
streamingConsumer.accept(
StreamingTextResults(
streamId = streamId,
value = contentString,
completed = false // Don't mark completed here; wait for usage chunk
)
)
case _ =>
if completed then
// Content is done, but don't send final completion yet - wait for usage chunk
()
(choice \ "delta" \ "content") match {
case JString(contentString) =>
streamingConsumer.accept(
StreamingTextResults(
streamId = streamId,
value = contentString,
completed = false // Don't mark completed here; wait for usage chunk
)
)
case _ =>
if completed then
// Content is done, but don't send final completion yet - wait for usage chunk
()
}
case other =>
throw new IllegalArgumentException(s"Expected OpenAI choice object, got $other")
}
}
}
@@ -145,6 +145,10 @@ object JwtService {
val keyBytes = Base64.getDecoder.decode(base64)
val keySpec = new X509EncodedKeySpec(keyBytes)
val keyFactory = KeyFactory.getInstance("RSA")
keyFactory.generatePublic(keySpec).asInstanceOf[RSAPublicKey]
keyFactory.generatePublic(keySpec) match {
case rsaPublicKey: RSAPublicKey => rsaPublicKey
case other =>
throw new IllegalArgumentException(s"Expected RSA public key, got ${other.getClass.getName}")
}
}
}
@@ -339,7 +339,11 @@ class OAuthServiceImpl(serverBaseUrl: String) extends OAuthService {
val keyBytes = Base64.getDecoder.decode(keyLines)
val keySpec = new PKCS8EncodedKeySpec(keyBytes)
val keyFactory = KeyFactory.getInstance("EC")
val ecPrivateKey = keyFactory.generatePrivate(keySpec).asInstanceOf[ECPrivateKey]
val ecPrivateKey = keyFactory.generatePrivate(keySpec) match {
case privateKey: ECPrivateKey => privateKey
case other =>
throw new IllegalArgumentException(s"Expected EC private key, got ${other.getClass.getName}")
}
val now = Instant.now()
val expiresAt = now.plusSeconds(15777000) // ~6 months (Apple max)
@@ -71,6 +71,12 @@ trait ClientTextStore {
def withMovedBackToUnrequested(id: ClientTextId): ClientTextStore
/**
* Removes generated text rows created after a rewind target. Anything past the retained action count belongs to
* history that no longer exists, and deterministic request IDs may be reused if the game re-advances.
*/
def withoutTextsAfter(maxHistoryCount: Int): ClientTextStore
/** Adds a pre-existing complete text directly (for pregenerated texts like hero names). */
def withAddedCompleteText(
id: ClientTextId,
@@ -219,6 +219,26 @@ case class ClientTextStoreImpl(
this
}
override def withoutTextsAfter(maxHistoryCount: Int): ClientTextStore = {
val removedIds =
completeTexts.values.filter(_.requestedAfterHistoryCount > maxHistoryCount).map(_.id).toSet ++
incompleteTexts.values.filter(_.requestedAfterHistoryCount > maxHistoryCount).map(_.id).toSet ++
unrequestedTexts.values.filter(_.requestedAfterHistoryCount > maxHistoryCount).map(_.id).toSet
if removedIds.isEmpty then this
else
copy(
completeTexts = completeTexts -- removedIds,
incompleteTexts = incompleteTexts -- removedIds,
unrequestedTexts = unrequestedTexts -- removedIds,
accessibleTo = accessibleTo -- removedIds,
hiddenFromClients = hiddenFromClients -- removedIds,
incompleteTextsAreSaved = false,
accessibleToIsSaved = false,
hiddenFromClientsIsSaved = false
)
}
def withAddedCompleteText(
id: ClientTextId,
text: String,
@@ -426,6 +426,80 @@ class PostgresClientTextStore private[client_text] (
this
}
override def withoutTextsAfter(maxHistoryCount: Int): ClientTextStore = {
val selectSql =
"""SELECT id FROM client_texts
|WHERE requested_after_history_count > ?""".stripMargin
val selectStmt = connection.prepareStatement(selectSql)
val ids = JfrEvents.postgresQuery(
databaseName,
"PostgresClientTextStore.withoutTextsAfter.selectIds",
selectSql
) { event =>
try {
selectStmt.setInt(1, maxHistoryCount)
val rs = selectStmt.executeQuery()
val result = scala.collection.mutable.ArrayBuffer[ClientTextId]()
while rs.next() do result += rs.getString("id")
event.rows = result.size
result.toVector
} finally selectStmt.close()
}
if ids.isEmpty then return this
println(
s"Removing ${ids.size} generated text row(s) after rewound history count $maxHistoryCount: ${ids.mkString(", ")}"
)
var idsArray: java.sql.Array = null
try {
idsArray = connection.createArrayOf("text", ids.toArray)
val deleteVisibilitySql = "DELETE FROM client_text_visibility WHERE text_id = ANY(?)"
val deleteVisibilityStmt = connection.prepareStatement(deleteVisibilitySql)
JfrEvents.postgresQuery(
databaseName,
"PostgresClientTextStore.withoutTextsAfter.deleteVisibility",
deleteVisibilitySql
) { event =>
try {
deleteVisibilityStmt.setArray(1, idsArray)
event.rows = deleteVisibilityStmt.executeUpdate()
} finally deleteVisibilityStmt.close()
}
val deleteHiddenSql = "DELETE FROM client_text_hidden WHERE text_id = ANY(?)"
val deleteHiddenStmt = connection.prepareStatement(deleteHiddenSql)
JfrEvents.postgresQuery(
databaseName,
"PostgresClientTextStore.withoutTextsAfter.deleteHidden",
deleteHiddenSql
) { event =>
try {
deleteHiddenStmt.setArray(1, idsArray)
event.rows = deleteHiddenStmt.executeUpdate()
} finally deleteHiddenStmt.close()
}
val deleteTextsSql = "DELETE FROM client_texts WHERE id = ANY(?)"
val deleteTextsStmt = connection.prepareStatement(deleteTextsSql)
JfrEvents.postgresQuery(
databaseName,
"PostgresClientTextStore.withoutTextsAfter.deleteTexts",
deleteTextsSql
) { event =>
try {
deleteTextsStmt.setArray(1, idsArray)
event.rows = deleteTextsStmt.executeUpdate()
} finally deleteTextsStmt.close()
}
} finally if idsArray != null then idsArray.free()
end try
this
}
override def withAddedCompleteText(
id: ClientTextId,
text: String,
@@ -506,6 +506,49 @@ class SqliteClientTextStore private[client_text] (
this
}
override def withoutTextsAfter(maxHistoryCount: Int): ClientTextStore = {
val idsStmt = connection.prepareStatement(
"""SELECT id FROM texts
|WHERE requested_after_history_count > ?""".stripMargin
)
val ids =
try {
idsStmt.setInt(1, maxHistoryCount)
val rs = idsStmt.executeQuery()
val result = scala.collection.mutable.ArrayBuffer[ClientTextId]()
while rs.next() do result += rs.getString("id")
result.toVector
} finally idsStmt.close()
if ids.isEmpty then return this
println(
s"Removing ${ids.size} generated text row(s) after rewound history count $maxHistoryCount: ${ids.mkString(", ")}"
)
ids.foreach { id =>
val visStmt = connection.prepareStatement("DELETE FROM visibility WHERE text_id = ?")
try {
visStmt.setString(1, id)
visStmt.executeUpdate()
} finally visStmt.close()
val hiddenStmt = connection.prepareStatement("DELETE FROM hidden_texts WHERE text_id = ?")
try {
hiddenStmt.setString(1, id)
hiddenStmt.executeUpdate()
} finally hiddenStmt.close()
val textStmt = connection.prepareStatement("DELETE FROM texts WHERE id = ?")
try {
textStmt.setString(1, id)
textStmt.executeUpdate()
} finally textStmt.close()
}
this
}
override def withAddedCompleteText(
id: ClientTextId,
text: String,
@@ -67,7 +67,11 @@ case class PerformReconResolutionAction(
val targetProvince = gameState.provinces(provinceId)
// Extract heroId from the recon action details
val heroId = incomingEndTurnAction.details.asInstanceOf[IncomingRecon].heroId
val heroId = incomingEndTurnAction.details match {
case recon: IncomingRecon => recon.heroId
case null =>
throw new IllegalArgumentException("Expected incoming recon action details, got null")
}
val factionProvinces = gameState.provinces.values.toVector
.filter(_.rulingFactionId.contains(fromFactionId))
@@ -505,10 +505,15 @@ object CommandChoiceHelpers {
reason = reason
).filter {
case CommandSelection(_, _, ac, _, _) =>
val restAvailable = ac match {
case rest: RestAvailable => rest
case other =>
throw new IllegalArgumentException(s"Expected RestAvailable command, got $other")
}
shouldRest(
gs
.provinces(
ac.asInstanceOf[RestAvailable].actingProvinceId
restAvailable.actingProvinceId
)
.rulingFactionHeroIds
.map(gs.heroes)
@@ -1,5 +1,7 @@
package net.eagle0
import scala.reflect.ClassTag
package object eagle {
type GameId = Long
type ShardokGameId = String
@@ -12,4 +14,10 @@ package object eagle {
type MovingSuppliesId = Int
type ClientTextId = String
private[eagle0] def checkedAs[T](value: Any)(using classTag: ClassTag[T]): T =
classTag.unapply(value).getOrElse {
val actualType = Option(value).map(_.getClass.getName).getOrElse("null")
throw new ClassCastException(s"Expected ${classTag.runtimeClass.getName}, got $actualType")
}
}
@@ -8,7 +8,12 @@ class SyncResponseObserver(
responseObserver: StreamObserver[UpdateStreamResponse]
) {
private val serverCallObserver =
responseObserver.asInstanceOf[ServerCallStreamObserver[UpdateStreamResponse]]
responseObserver match {
case null => null
case observer: ServerCallStreamObserver[?] => observer
case other =>
throw new IllegalArgumentException(s"Expected ServerCallStreamObserver, got ${other.getClass.getName}")
}
def isCancelled: Boolean = serverCallObserver.isCancelled
@@ -26,6 +26,18 @@ class UnrequestedTextHandler(llmResolver: LlmResolver, heroNameCache: HeroNameCa
// 3 minutes in milliseconds
private val StalledThresholdMillis: Long = 3 * 60 * 1000
private def requireTextRequestStillValid(
id: String,
requestedAfterHistoryCount: Int,
currentHistoryCount: Int
): Unit =
if requestedAfterHistoryCount > currentHistoryCount then
throw new EagleInternalException(
s"Stale pending text request $id: requestedAfterHistoryCount=$requestedAfterHistoryCount " +
s"is after current history count $currentHistoryCount. This usually means the game was rewound " +
s"without pruning its text store."
)
private def withTransaction[T](store: ClientTextStore)(f: => T): T = {
store.beginTransaction()
try {
@@ -56,8 +68,10 @@ class UnrequestedTextHandler(llmResolver: LlmResolver, heroNameCache: HeroNameCa
stuckEntries: Vector[StuckUnrequested]
)
val currentHistoryCount = gameHistory.count
val (generationRequests, fixedNameRequests) =
clientTextStore.unrequestedTexts.values.partition { ut =>
requireTextRequestStillValid(ut.id, ut.requestedAfterHistoryCount, currentHistoryCount)
ut.llmRequest match {
case _: FixedHeroName => false
case _ => true
@@ -71,13 +85,16 @@ class UnrequestedTextHandler(llmResolver: LlmResolver, heroNameCache: HeroNameCa
withTransaction(clientTextStore) {
fixedNameRequests.foldLeft((clientTextStore, Vector.empty[ClientText])) {
case ((cts, completed), ut) =>
val fixedName = ut.llmRequest match {
case request: FixedHeroName => request.fixedName
case other =>
throw new EagleInternalException(s"Expected FixedHeroName request for ${ut.id}, got $other")
}
val withUpdate = cts
.withMarkedRequested(ut.id)
.withAppendedText(
id = ut.id,
newText = ut.llmRequest
.asInstanceOf[FixedHeroName]
.fixedName,
newText = fixedName,
complete = true
)
(withUpdate.clientTextStore, completed :+ withUpdate.updatedText)
@@ -100,7 +117,11 @@ class UnrequestedTextHandler(llmResolver: LlmResolver, heroNameCache: HeroNameCa
heroNameCache
.takeNames(
genderStrings = generatedNameRequests.map { ut =>
ut.llmRequest.asInstanceOf[GeneratedHeroName].gender
ut.llmRequest match {
case request: GeneratedHeroName => request.gender
case other =>
throw new EagleInternalException(s"Expected GeneratedHeroName request for ${ut.id}, got $other")
}
}.toVector.map {
case Gender.Female => "female"
case Gender.Male => "male"
@@ -262,28 +283,31 @@ class UnrequestedTextHandler(llmResolver: LlmResolver, heroNameCache: HeroNameCa
}
// Load game states, using cached versions when available
val currentHistoryCount = gameHistory.count
val (llmRequests, ctsWithCachedStates) =
clientTextStore.incompleteTexts.values.foldLeft(
(Vector.empty[LlmResolver.LlmRequestWithGameState], clientTextStore)
) {
case ((requests, cts), ict) =>
val (gameState, updatedCts) = ict.cachedGameState match {
case Some(cached) =>
(cached, cts)
case None =>
val loaded = gameHistory.stateAfter(ict.requestedAfterHistoryCount)
val updatedEntry = ict.withCachedGameState(loaded)
val newCts = cts.withCachedIncompleteGameState(ict.id, updatedEntry)
(loaded, newCts)
}
clientTextStore.incompleteTexts.values
.foldLeft(
(Vector.empty[LlmResolver.LlmRequestWithGameState], clientTextStore)
) {
case ((requests, cts), ict) =>
requireTextRequestStillValid(ict.id, ict.requestedAfterHistoryCount, currentHistoryCount)
val (gameState, updatedCts) = ict.cachedGameState match {
case Some(cached) =>
(cached, cts)
case None =>
val loaded = gameHistory.stateAfter(ict.requestedAfterHistoryCount)
val updatedEntry = ict.withCachedGameState(loaded)
val newCts = cts.withCachedIncompleteGameState(ict.id, updatedEntry)
(loaded, newCts)
}
val request = LlmResolver.LlmRequestWithGameState(
llmRequest = ict.llmRequest,
gameState = gameState,
partialCompletion = Some(ict.partialText)
)
(requests :+ request, updatedCts)
}
val request = LlmResolver.LlmRequestWithGameState(
llmRequest = ict.llmRequest,
gameState = gameState,
partialCompletion = Some(ict.partialText)
)
(requests :+ request, updatedCts)
}
val results = llmResolver
.resolveLlmRequests(
@@ -1249,7 +1249,8 @@ final case class GameController(
val resyncedCount = humanClients.length
// Rewind the engine
val rewoundEngine = engine.rewindTo(targetActionCount)
val rewoundEngine = engine.rewindTo(targetActionCount)
val rewoundClientTextStore = clientTextStore.withoutTextsAfter(targetActionCount)
// Resync all connected human clients with the new state
val resyncedClients = humanClients.map { client =>
@@ -1259,7 +1260,7 @@ final case class GameController(
engine = rewoundEngine,
factionId = client.factionId,
otherFactionIds = rewoundEngine.factionIds.filterNot(_ == client.factionId),
clientTextStore = clientTextStore
clientTextStore = rewoundClientTextStore
)
client.resyncAfterRewind(
history = rewoundEngine.history,
@@ -1281,7 +1282,7 @@ final case class GameController(
rsClient.nextRandom
)
},
clientTextStore = this.clientTextStore // Keep text store as-is for now
clientTextStore = rewoundClientTextStore
).copyWithAtomicSave
(newController, resyncedCount)
@@ -248,7 +248,11 @@ object TutorialGameCreation {
val battalionData = attackingArmy.battalions.toVector.map { battalion =>
val battalionId = nextBattalionId
nextBattalionId += 1
val b = BattalionConverter.fromProto(battalion).asInstanceOf[BattalionC]
val b = BattalionConverter.fromProto(battalion) match {
case battalionC: BattalionC => battalionC
case other =>
throw new IllegalArgumentException(s"Expected BattalionC from tutorial battalion proto, got $other")
}
(battalionId, b.copy(id = battalionId))
}
@@ -75,8 +75,11 @@ object HexMapJsonUtils {
.map(terrain),
defenderStartingPositions = Some(
startingPositions(
(obj \ "defenderStartingPositions")
.asInstanceOf[JObject]
(obj \ "defenderStartingPositions") match {
case defenderPositions: JObject => defenderPositions
case other =>
throw new IllegalArgumentException(s"Expected defenderStartingPositions object, got $other")
}
)
),
attackerStartingPositions = (obj \ "attackerStartingPositions").extract[List[JObject]].map { v =>
@@ -86,7 +89,10 @@ object HexMapJsonUtils {
)
def fromJsonStream(stream: InputStream): HexMap = {
val v = parseJson(stringFromStream(stream)).asInstanceOf[JObject]
val v = parseJson(stringFromStream(stream)) match {
case obj: JObject => obj
case other => throw new IllegalArgumentException(s"Expected hex map JSON object, got $other")
}
mapFromJObject(v)
}
@@ -1,6 +1,6 @@
package net.eagle0.eagle.ai
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.{checkedAs, FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.library.settings.{
AiMinimumCharismaForSwornBrother,
AiMinimumConstitutionForSwornBrother,
@@ -276,7 +276,7 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
val gsAlmostEnoughLoyalty = swearBrotherhoodGameState.copy(
heroes = swearBrotherhoodGameState.heroes.updated(
12,
swearBrotherhoodGameState.heroes(12).asInstanceOf[HeroC].copy(loyalty = 86)
checkedAs[HeroC](swearBrotherhoodGameState.heroes(12)).copy(loyalty = 86)
)
)
@@ -305,7 +305,7 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
val gsFeastWontGiveEnough = swearBrotherhoodGameState.copy(
heroes = swearBrotherhoodGameState.heroes.updated(
12,
swearBrotherhoodGameState.heroes(12).asInstanceOf[HeroC].copy(loyalty = 84)
checkedAs[HeroC](swearBrotherhoodGameState.heroes(12)).copy(loyalty = 84)
)
)
@@ -328,9 +328,9 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
it should "do nothing if no available hero meets the minimums" in {
val crappyHeroesGS = swearBrotherhoodGameState.copy(
heroes = swearBrotherhoodGameState.heroes
.updated(12, swearBrotherhoodGameState.heroes(12).asInstanceOf[HeroC].copy(charisma = 64))
.updated(9, swearBrotherhoodGameState.heroes(9).asInstanceOf[HeroC].copy(constitution = 63))
.updated(13, swearBrotherhoodGameState.heroes(13).asInstanceOf[HeroC].copy(charisma = 60))
.updated(12, checkedAs[HeroC](swearBrotherhoodGameState.heroes(12)).copy(charisma = 64))
.updated(9, checkedAs[HeroC](swearBrotherhoodGameState.heroes(9)).copy(constitution = 63))
.updated(13, checkedAs[HeroC](swearBrotherhoodGameState.heroes(13)).copy(charisma = 60))
)
SeekMoreLeadersCommandChooser
@@ -354,7 +354,7 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
val gsWithFewerProvinces = swearBrotherhoodGameState.copy(
provinces = swearBrotherhoodGameState.provinces.updated(
20,
swearBrotherhoodGameState.provinces(20).asInstanceOf[ProvinceC].copy(rulingFactionId = None)
checkedAs[ProvinceC](swearBrotherhoodGameState.provinces(20)).copy(rulingFactionId = None)
)
)
@@ -764,4 +764,44 @@ class ClientTextStoreImplTest extends AnyFlatSpec with MockFactory with Matchers
CompleteClientText("unknown", "unknown text", requestedAfterHistoryCount)
)
}
"withoutTextsAfter" should "remove generated text rows after the rewind target" in {
val store = ClientTextStoreImpl(
pregenerated = pregeneratedClientTextStore,
persister = mockPersister,
completeTexts = Map(
"complete" -> CompleteClientText("complete", "already done", 400),
"staleComplete" -> CompleteClientText("staleComplete", "future text", 500)
),
incompleteTexts = Map(
"oldIncomplete" -> IncompleteClientText("oldIncomplete", "", 300, genericLlmRequest, 1L, 1L),
"staleIncomplete" -> IncompleteClientText("staleIncomplete", "", 500, genericLlmRequest, 1L, 1L)
),
unrequestedTexts = Map(
"oldUnrequested" -> UnrequestedClientText("oldUnrequested", 300, genericLlmRequest),
"staleUnrequested" -> UnrequestedClientText("staleUnrequested", 500, genericLlmRequest)
),
accessibleTo = Map(
"complete" -> Vector(1),
"staleComplete" -> Vector(1),
"oldIncomplete" -> Vector(1),
"staleIncomplete" -> Vector(1),
"oldUnrequested" -> Vector(1),
"staleUnrequested" -> Vector(1)
),
savedCompleteCount = 0,
incompleteTextsAreSaved = true,
accessibleToIsSaved = true,
hiddenFromClients = Set("staleComplete", "staleIncomplete", "complete"),
hiddenFromClientsIsSaved = true
)
val updated = store.withoutTextsAfter(400)
updated.completeTexts.keySet shouldBe Set("complete")
updated.incompleteTexts.keySet shouldBe Set("oldIncomplete")
updated.unrequestedTexts.keySet shouldBe Set("oldUnrequested")
updated.accessibleTo.keySet shouldBe Set("complete", "oldIncomplete", "oldUnrequested")
updated.hiddenFromClients shouldBe Set("complete")
}
}
@@ -1,6 +1,6 @@
package net.eagle0.eagle.library.actions.availability
import net.eagle0.eagle.{FactionId, RoundId}
import net.eagle0.eagle.{checkedAs, FactionId, RoundId}
import net.eagle0.eagle.library.settings.{
MinimumAgilityForArchery,
MinimumAgilityForStartFire,
@@ -186,18 +186,18 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
)
private def getAttackDecisionCommand(gs: GameState): AttackDecisionAvailable =
AvailableAttackDecisionCommandFactory
.availableCommand(gs, attackerFid, defenderProvinceId)
.get
.asInstanceOf[AttackDecisionAvailable]
checkedAs[AttackDecisionAvailable](
AvailableAttackDecisionCommandFactory
.availableCommand(gs, attackerFid, defenderProvinceId)
.get
)
private def withTruce(gs: GameState): GameState =
gs.copy(
factions = gs.factions
.updated(
attackerFid,
gs.factions(attackerFid)
.asInstanceOf[FactionC]
checkedAs[FactionC](gs.factions(attackerFid))
.copy(
factionRelationships = Vector(
FactionRelationship(
@@ -210,8 +210,7 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
)
.updated(
defenderFid,
gs.factions(defenderFid)
.asInstanceOf[FactionC]
checkedAs[FactionC](gs.factions(defenderFid))
.copy(
factionRelationships = Vector(
FactionRelationship(
@@ -228,8 +227,7 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
gs.copy(
provinces = gs.provinces.updated(
defenderProvinceId,
gs.provinces(defenderProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](gs.provinces(defenderProvinceId))
.copy(
hostileArmies = Vector(
gs.provinces(defenderProvinceId)
@@ -265,9 +263,10 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
val gs = baseGameState.copy(
provinces = baseGameState.provinces.updated(
defenderProvinceId,
baseGameState
.provinces(defenderProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(defenderProvinceId)
)
.copy(
hostileArmies = Vector(
baseGameState.provinces(defenderProvinceId).hostileArmies.head.copy(factionId = 5)
@@ -283,9 +282,10 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
val gs = baseGameState.copy(
provinces = baseGameState.provinces.updated(
defenderProvinceId,
baseGameState
.provinces(defenderProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(defenderProvinceId)
)
.copy(
hostileArmies = Vector(
baseGameState
@@ -335,9 +335,10 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
val gs = baseGameState.copy(
provinces = baseGameState.provinces.updated(
defenderProvinceId,
baseGameState
.provinces(defenderProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(defenderProvinceId)
)
.copy(
battalionIds = Vector(50, 60, 70, 100, 110, 120)
)
@@ -388,9 +389,10 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
val gs = baseGameState.copy(
provinces = baseGameState.provinces.updated(
defenderProvinceId,
baseGameState
.provinces(defenderProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(defenderProvinceId)
)
.copy(
battalionIds = Vector(50, 60)
)
@@ -464,9 +466,10 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
heroes = baseGameState.heroes + (9382 -> HeroC(id = 9382, factionId = Some(anotherAttackerFid))),
provinces = baseGameState.provinces.updated(
defenderProvinceId,
baseGameState
.provinces(defenderProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(defenderProvinceId)
)
.copy(
hostileArmies = baseGameState.provinces(defenderProvinceId).hostileArmies :+ HostileArmyGroup(
factionId = anotherAttackerFid,
@@ -486,9 +489,10 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
factions = baseGameState.factions
.updated(
attackerFid,
baseGameState
.factions(attackerFid)
.asInstanceOf[FactionC]
checkedAs[FactionC](
baseGameState
.factions(attackerFid)
)
.copy(
factionRelationships = Vector(
FactionRelationship(
@@ -532,9 +536,10 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
heroes = baseGameState.heroes + (9382 -> HeroC(id = 9382, factionId = Some(anotherAttackerFid))),
provinces = baseGameState.provinces.updated(
defenderProvinceId,
baseGameState
.provinces(defenderProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(defenderProvinceId)
)
.copy(
hostileArmies = baseGameState.provinces(defenderProvinceId).hostileArmies :+ HostileArmyGroup(
factionId = anotherAttackerFid,
@@ -577,9 +582,10 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
heroes = baseGameState.heroes + (9382 -> HeroC(id = 9382, factionId = Some(hostileFid))),
provinces = baseGameState.provinces.updated(
defenderProvinceId,
baseGameState
.provinces(defenderProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(defenderProvinceId)
)
.copy(
hostileArmies = Vector(
baseGameState.provinces(defenderProvinceId).hostileArmies.head,
@@ -625,9 +631,10 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
heroes = baseGameState.heroes + (9382 -> HeroC(id = 9382, factionId = Some(hostileFid))),
provinces = baseGameState.provinces.updated(
defenderProvinceId,
baseGameState
.provinces(defenderProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(defenderProvinceId)
)
.copy(
hostileArmies = Vector(
baseGameState
@@ -673,9 +680,10 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
heroes = baseGameState.heroes + (9382 -> HeroC(id = 9382, factionId = Some(alliedFid))),
provinces = baseGameState.provinces.updated(
defenderProvinceId,
baseGameState
.provinces(defenderProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(defenderProvinceId)
)
.copy(
hostileArmies = Vector(
baseGameState
@@ -704,9 +712,10 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
factions = baseGameState.factions
.updated(
attackerFid,
baseGameState
.factions(attackerFid)
.asInstanceOf[FactionC]
checkedAs[FactionC](
baseGameState
.factions(attackerFid)
)
.copy(
factionRelationships = Vector(
FactionRelationship(
@@ -748,9 +757,10 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
+ (9384 -> HeroC(id = 9384, factionId = Some(joinerFid))),
provinces = baseGameState.provinces.updated(
defenderProvinceId,
baseGameState
.provinces(defenderProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(defenderProvinceId)
)
.copy(
hostileArmies = Vector(
HostileArmyGroup(
@@ -840,7 +850,7 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
val gs = baseGameState.copy(
provinces = baseGameState.provinces.updated(
defenderProvinceId,
baseGameState.provinces(defenderProvinceId).asInstanceOf[ProvinceC].copy(food = 0, gold = 0)
checkedAs[ProvinceC](baseGameState.provinces(defenderProvinceId)).copy(food = 0, gold = 0)
)
)
@@ -884,9 +894,10 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
provinces = withTruce(withNoFleeProvince(baseGameState)).provinces
.updated(
defenderProvinceId,
withTruce(withNoFleeProvince(baseGameState))
.provinces(defenderProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
withTruce(withNoFleeProvince(baseGameState))
.provinces(defenderProvinceId)
)
.copy(
neighbors = Vector(
Neighbor(provinceId = 18, startingPositionIndex = 0),
@@ -917,9 +928,10 @@ class AvailableAttackDecisionCommandFactoryTest extends AnyFlatSpec with Matcher
provinces = withTruce(withNoFleeProvince(baseGameState)).provinces
.updated(
defenderProvinceId,
withTruce(withNoFleeProvince(baseGameState))
.provinces(defenderProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
withTruce(withNoFleeProvince(baseGameState))
.provinces(defenderProvinceId)
)
.copy(
neighbors = Vector(
Neighbor(provinceId = 18, startingPositionIndex = 0),
@@ -1,6 +1,6 @@
package net.eagle0.eagle.library.actions.availability
import net.eagle0.eagle.{FactionId, ProvinceId, RoundId}
import net.eagle0.eagle.{checkedAs, FactionId, ProvinceId, RoundId}
import net.eagle0.eagle.library.settings.{MaxCombatUnitCountPerSide, MinVigorForDefend}
import net.eagle0.eagle.model.state.{
Army,
@@ -194,7 +194,7 @@ class AvailableDefendCommandsFactoryTest extends AnyFlatSpec with BeforeAndAfter
)
command.get shouldBe a[DefendAvailable]
command.get.asInstanceOf[DefendAvailable].actingProvinceId shouldBe defendingProvince.id
checkedAs[DefendAvailable](command.get).actingProvinceId shouldBe defendingProvince.id
}
it should "include heroes with sufficient vigor" in {
@@ -205,7 +205,7 @@ class AvailableDefendCommandsFactoryTest extends AnyFlatSpec with BeforeAndAfter
7
)
command.get.asInstanceOf[DefendAvailable].availableHeroIds should contain theSameElementsAs sufficientVigorHeroes
checkedAs[DefendAvailable](command.get).availableHeroIds should contain theSameElementsAs sufficientVigorHeroes
.map(_.id)
}
@@ -217,7 +217,7 @@ class AvailableDefendCommandsFactoryTest extends AnyFlatSpec with BeforeAndAfter
7
)
command.get.asInstanceOf[DefendAvailable].availableHeroIds should contain.noneOf(3, 4)
checkedAs[DefendAvailable](command.get).availableHeroIds should contain.noneOf(3, 4)
}
it should "include all battalions" in {
@@ -228,9 +228,7 @@ class AvailableDefendCommandsFactoryTest extends AnyFlatSpec with BeforeAndAfter
7
)
command.get
.asInstanceOf[DefendAvailable]
.availableBattalions
checkedAs[DefendAvailable](command.get).availableBattalions
.map(_.battalionId) should contain theSameElementsAs (validBattalions ++ invalidBattalions)
.map(_.id)
}
@@ -272,7 +270,7 @@ class AvailableDefendCommandsFactoryTest extends AnyFlatSpec with BeforeAndAfter
7
)
command.get.asInstanceOf[DefendAvailable].availableFleeProvinceIds shouldBe Vector(
checkedAs[DefendAvailable](command.get).availableFleeProvinceIds shouldBe Vector(
9, // own province
8, // empty province
4, // attacker
@@ -299,19 +297,20 @@ class AvailableDefendCommandsFactoryTest extends AnyFlatSpec with BeforeAndAfter
7
)
command.get.asInstanceOf[DefendAvailable].availableFleeProvinceIds shouldBe empty
checkedAs[DefendAvailable](command.get).availableFleeProvinceIds shouldBe empty
}
it should "include the info about the attacking armies" in {
val gameState = makeGameState(Map(7 -> defendingProvince.copy(hostileArmies = Vector(attackingArmy))))
val command = AvailableDefendCommandsFactory
.availableCommand(
gameState,
actingFactionId,
7
)
.get
.asInstanceOf[DefendAvailable]
val command = checkedAs[DefendAvailable](
AvailableDefendCommandsFactory
.availableCommand(
gameState,
actingFactionId,
7
)
.get
)
command.hostileFactionIds shouldBe Vector(attackingFid)
command.hostileHeroCount shouldBe 3
@@ -1,6 +1,6 @@
package net.eagle0.eagle.library.actions.availability
import net.eagle0.eagle.{FactionId, ProvinceId}
import net.eagle0.eagle.{checkedAs, FactionId, ProvinceId}
import net.eagle0.eagle.library.settings.{
MinimumAgilityForArchery,
MinimumAgilityForStartFire,
@@ -102,9 +102,10 @@ class AvailableHandleCapturedHeroCommandFactoryTest
private val gameStateWithOne: GameState = gameStateWithTwo.copy(
provinces = gameStateWithTwo.provinces.updated(
provinceId,
gameStateWithTwo
.provinces(provinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
gameStateWithTwo
.provinces(provinceId)
)
.copy(
capturedHeroes = gameStateWithTwo.provinces(provinceId).capturedHeroes.drop(1)
)
@@ -112,10 +113,11 @@ class AvailableHandleCapturedHeroCommandFactoryTest
)
private def getHandleCapturedHeroCommand(gs: GameState): HandleCapturedHeroAvailable =
AvailableHandleCapturedHeroCommandFactory
.availableCommand(gs, factionId = rulingFactionId, provinceId = provinceId)
.get
.asInstanceOf[HandleCapturedHeroAvailable]
checkedAs[HandleCapturedHeroAvailable](
AvailableHandleCapturedHeroCommandFactory
.availableCommand(gs, factionId = rulingFactionId, provinceId = provinceId)
.get
)
"availableCommand" should "set the basics" in {
val cmd = getHandleCapturedHeroCommand(gameStateWithTwo)
@@ -240,9 +242,10 @@ class AvailableHandleCapturedHeroCommandFactoryTest
),
provinces = gameStateWithOne.provinces.updated(
5,
gameStateWithOne
.provinces(5)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
gameStateWithOne
.provinces(5)
)
.copy(
rulingFactionId = None
)
@@ -1,6 +1,6 @@
package net.eagle0.eagle.library.actions.availability
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.{checkedAs, FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.library.settings.{MinimumAgilityForArchery, MinimumAgilityForStartFire}
import net.eagle0.eagle.model.state.{GameType, RoundPhase}
import net.eagle0.eagle.model.state.command.available.AvailableCommand.ManagePrisonersAvailable
@@ -115,10 +115,11 @@ class AvailableManagePrisonerCommandsFactoryTest extends AnyFlatSpec with Before
)
private def getManagePrisonersCommand(gs: GameState): ManagePrisonersAvailable =
AvailableManagePrisonersCommandFactory
.availableCommand(gs, actingFactionId, actingProvinceId)
.get
.asInstanceOf[ManagePrisonersAvailable]
checkedAs[ManagePrisonersAvailable](
AvailableManagePrisonersCommandFactory
.availableCommand(gs, actingFactionId, actingProvinceId)
.get
)
"availableCommand" should "return a command with all the prisoners" in {
val ac = getManagePrisonersCommand(baseGameState)
@@ -195,9 +196,10 @@ class AvailableManagePrisonerCommandsFactoryTest extends AnyFlatSpec with Before
val gs = baseGameState.copy(
provinces = baseGameState.provinces.updated(
neighbor1ProvinceId,
baseGameState
.provinces(neighbor1ProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(neighbor1ProvinceId)
)
.copy(
rulingFactionId = Some(actingFactionId)
)
@@ -218,18 +220,20 @@ class AvailableManagePrisonerCommandsFactoryTest extends AnyFlatSpec with Before
provinces = baseGameState.provinces
.updated(
neighbor1ProvinceId,
baseGameState
.provinces(neighbor1ProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(neighbor1ProvinceId)
)
.copy(
rulingFactionId = Some(actingFactionId)
)
)
.updated(
neighbor2ProvinceId,
baseGameState
.provinces(neighbor2ProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(neighbor2ProvinceId)
)
.copy(
rulingFactionId = Some(actingFactionId)
)
@@ -250,9 +254,10 @@ class AvailableManagePrisonerCommandsFactoryTest extends AnyFlatSpec with Before
val gsWithNoPrisoners = baseGameState.copy(
provinces = baseGameState.provinces.updated(
actingProvinceId,
baseGameState
.provinces(actingProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(actingProvinceId)
)
.copy(
unaffiliatedHeroes = baseGameState
.provinces(actingProvinceId)
@@ -270,9 +275,10 @@ class AvailableManagePrisonerCommandsFactoryTest extends AnyFlatSpec with Before
val gsNotTraveling = baseGameState.copy(
provinces = baseGameState.provinces.updated(
actingProvinceId,
baseGameState
.provinces(actingProvinceId)
.asInstanceOf[ProvinceC]
checkedAs[ProvinceC](
baseGameState
.provinces(actingProvinceId)
)
.copy(
rulerIsTraveling = false
)
@@ -94,53 +94,58 @@ class AvailableResolveBreakAllianceCommandFactoryTest extends AnyFlatSpec with M
inside(cmd) {
case AvailableCommand.ResolveBreakAllianceAvailable(offers) =>
offers should have size 2
offers should contain(
DiplomacyOfferInfo(
offeringFactionId = secondOfferingFid,
targetFactionId = actingFid,
heroId = Some(54),
eligibleStatuses = Vector(Accepted, Imprisoned)
offers.should(have size 2)
offers.should(
contain(
DiplomacyOfferInfo(
offeringFactionId = secondOfferingFid,
targetFactionId = actingFid,
heroId = Some(54),
eligibleStatuses = Vector(Accepted, Imprisoned)
)
)
)
offers should contain(
DiplomacyOfferInfo(
offeringFactionId = firstOfferingFid,
targetFactionId = actingFid,
heroId = Some(53),
eligibleStatuses = Vector(Accepted, Imprisoned)
offers.should(
contain(
DiplomacyOfferInfo(
offeringFactionId = firstOfferingFid,
targetFactionId = actingFid,
heroId = Some(53),
eligibleStatuses = Vector(Accepted, Imprisoned)
)
)
)
}
}
it should "not include alliances that have already been resolved" in {
val actingFaction = gameState.factions(actingFid) match {
case faction: FactionC => faction
}
val gsWithOneResolved = gameState.copy(
factions = gameState.factions.updated(
actingFid,
gameState
.factions(actingFid)
.asInstanceOf[FactionC]
.copy(
incomingDiplomacyOffers = Vector(
BreakAlliance(
originatingFactionId = firstOfferingFid,
targetFactionId = actingFid,
messengerHeroId = 53,
messengerOriginProvinceId = 19,
status = Rejected,
offerTextId = "break alliance offer"
),
BreakAlliance(
originatingFactionId = secondOfferingFid,
targetFactionId = actingFid,
messengerHeroId = 54,
messengerOriginProvinceId = 20,
status = Unresolved,
offerTextId = "break alliance offer 2"
)
actingFaction.copy(
incomingDiplomacyOffers = Vector(
BreakAlliance(
originatingFactionId = firstOfferingFid,
targetFactionId = actingFid,
messengerHeroId = 53,
messengerOriginProvinceId = 19,
status = Rejected,
offerTextId = "break alliance offer"
),
BreakAlliance(
originatingFactionId = secondOfferingFid,
targetFactionId = actingFid,
messengerHeroId = 54,
messengerOriginProvinceId = 20,
status = Unresolved,
offerTextId = "break alliance offer 2"
)
)
)
)
)
@@ -150,12 +155,14 @@ class AvailableResolveBreakAllianceCommandFactoryTest extends AnyFlatSpec with M
inside(cmd) {
case AvailableCommand.ResolveBreakAllianceAvailable(offers) =>
offers should have size 1
offers.head shouldBe DiplomacyOfferInfo(
offeringFactionId = secondOfferingFid,
targetFactionId = actingFid,
heroId = Some(54),
eligibleStatuses = Vector(Accepted, Imprisoned)
offers.should(have size 1)
offers.head.shouldBe(
DiplomacyOfferInfo(
offeringFactionId = secondOfferingFid,
targetFactionId = actingFid,
heroId = Some(54),
eligibleStatuses = Vector(Accepted, Imprisoned)
)
)
}
}
@@ -174,17 +181,18 @@ class AvailableResolveBreakAllianceCommandFactoryTest extends AnyFlatSpec with M
inside(cmd) {
case AvailableCommand.ResolveBreakAllianceAvailable(offers) =>
offers should have size 2
offers.should(have size 2)
offers.foreach { offer =>
offer.eligibleStatuses shouldBe Vector(Accepted)
offer.eligibleStatuses.shouldBe(Vector(Accepted))
}
}
}
it should "return nothing if there are no break alliance offers" in {
AvailableResolveBreakAllianceCommandFactory.availableCommand(
gameState,
secondOfferingFid
) shouldBe empty
}
it should "return nothing if there are no break alliance offers" in
AvailableResolveBreakAllianceCommandFactory
.availableCommand(
gameState,
secondOfferingFid
)
.shouldBe(empty)
}
@@ -20,6 +20,7 @@ import net.eagle0.eagle.model.state.GameType
import net.eagle0.eagle.model.state.HostileArmyGroupStatus.TributeDemanded
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatest.Inside.inside
class AvailableResolveTributeCommandsFactoryTest extends AnyFlatSpec with Matchers {
val actingFactionId: FactionId = 3
@@ -138,29 +139,34 @@ class AvailableResolveTributeCommandsFactoryTest extends AnyFlatSpec with Matche
)
.get
resolveCommand shouldBe a[ResolveTributeAvailable]
val tributeCmd = resolveCommand.asInstanceOf[ResolveTributeAvailable]
tributeCmd.actingProvinceId shouldBe actingProvinceId
tributeCmd.availableFood shouldBe 9898
tributeCmd.availableGold shouldBe 3232
tributeCmd.demands should have size 2
tributeCmd.demands should contain(
TributeAndFaction(
demandingFactionId = hostileFaction1Id,
goldDemanded = 123,
foodDemanded = 456,
heroCount = 1,
troopCount = 150
)
)
tributeCmd.demands should contain(
TributeAndFaction(
demandingFactionId = hostileFaction2Id,
goldDemanded = 333,
foodDemanded = 999,
heroCount = 1,
troopCount = 2555
)
)
inside(resolveCommand) {
case tributeCmd: ResolveTributeAvailable =>
tributeCmd.actingProvinceId.shouldBe(actingProvinceId)
tributeCmd.availableFood.shouldBe(9898)
tributeCmd.availableGold.shouldBe(3232)
tributeCmd.demands.size.shouldBe(2)
tributeCmd.demands.should(
contain(
TributeAndFaction(
demandingFactionId = hostileFaction1Id,
goldDemanded = 123,
foodDemanded = 456,
heroCount = 1,
troopCount = 150
)
)
)
tributeCmd.demands.should(
contain(
TributeAndFaction(
demandingFactionId = hostileFaction2Id,
goldDemanded = 333,
foodDemanded = 999,
heroCount = 1,
troopCount = 2555
)
)
)
}
}
}
@@ -1,6 +1,6 @@
package net.eagle0.eagle.library.actions.impl.action
import net.eagle0.eagle.{FactionId, GameId, RoundId}
import net.eagle0.eagle.{checkedAs, FactionId, GameId, RoundId}
import net.eagle0.eagle.model.action_result.changed_province.concrete.{ChangedProvinceC, HostileArmyStatusChange}
import net.eagle0.eagle.model.action_result.concrete.NotificationC
import net.eagle0.eagle.model.action_result.types.ActionResultType.{ArmyShattered, EndAttackDecisionPhase, QuestFailed}
@@ -347,9 +347,7 @@ class EndAttackDecisionPhaseActionTest extends AnyFlatSpec with Matchers {
).results
val bounceResults = results.filter { r =>
r.changedProvinces.exists { cp =>
cp.asInstanceOf[ChangedProvinceC].hostileArmyStatusChanges.nonEmpty
}
r.changedProvinces.exists(cp => checkedAs[ChangedProvinceC](cp).hostileArmyStatusChanges.nonEmpty)
}
bounceResults should have size 1
inside(bounceResults.head.changedProvinces.head) {
@@ -441,9 +439,7 @@ class EndAttackDecisionPhaseActionTest extends AnyFlatSpec with Matchers {
).results
val bounceResults = results.filter { r =>
r.changedProvinces.exists { cp =>
cp.asInstanceOf[ChangedProvinceC].hostileArmyStatusChanges.nonEmpty
}
r.changedProvinces.exists(cp => checkedAs[ChangedProvinceC](cp).hostileArmyStatusChanges.nonEmpty)
}
bounceResults shouldBe empty
}
@@ -513,9 +509,7 @@ class EndAttackDecisionPhaseActionTest extends AnyFlatSpec with Matchers {
).results
val bounceResults = results.filter { r =>
r.changedProvinces.exists { cp =>
cp.asInstanceOf[ChangedProvinceC].hostileArmyStatusChanges.nonEmpty
}
r.changedProvinces.exists(cp => checkedAs[ChangedProvinceC](cp).hostileArmyStatusChanges.nonEmpty)
}
bounceResults should have size 1
inside(bounceResults.head.changedProvinces.head) {
@@ -608,9 +602,7 @@ class EndAttackDecisionPhaseActionTest extends AnyFlatSpec with Matchers {
).results
val bounceResults = results.filter { r =>
r.changedProvinces.exists { cp =>
cp.asInstanceOf[ChangedProvinceC].hostileArmyStatusChanges.nonEmpty
}
r.changedProvinces.exists(cp => checkedAs[ChangedProvinceC](cp).hostileArmyStatusChanges.nonEmpty)
}
bounceResults shouldBe empty
}
@@ -130,42 +130,43 @@ class EndDefenseDecisionPhaseActionTest extends AnyFlatSpec with Matchers {
it should "remove tribute paid armies from the province" in {
val actionResult = EndDefenseDecisionPhaseAction(gameState).immediateExecute
val changedDefendingProvince = actionResult.changedProvinces
.find(_.provinceId == defendingProvinceId)
.map(_.asInstanceOf[ChangedProvinceC])
changedDefendingProvince should not be empty
changedDefendingProvince.get.removedHostileArmyFactionIds should contain allOf (attacker1Fid, attacker2Fid)
val changedDefendingProvince = actionResult.changedProvinces.collectFirst {
case cp: ChangedProvinceC if cp.provinceId == defendingProvinceId => cp
}
changedDefendingProvince.should(not be empty)
changedDefendingProvince.get.removedHostileArmyFactionIds.should(contain(attacker1Fid))
changedDefendingProvince.get.removedHostileArmyFactionIds.should(contain(attacker2Fid))
}
it should "add returning armies to their origin provinces" in {
val actionResult = EndDefenseDecisionPhaseAction(gameState).immediateExecute
// Province 7 should have incoming army returning
val province7Changes = actionResult.changedProvinces
.find(_.provinceId == 7)
.map(_.asInstanceOf[ChangedProvinceC])
province7Changes should not be empty
province7Changes.get.newIncomingArmies should have size 1
province7Changes.get.newIncomingArmies.head.originProvinceId shouldBe defendingProvinceId
province7Changes.get.newIncomingArmies.head.destinationProvinceId shouldBe 7
province7Changes.get.newIncomingArmies.head.arrivalRound shouldBe 20
val province7Changes = actionResult.changedProvinces.collectFirst {
case cp: ChangedProvinceC if cp.provinceId == 7 => cp
}
province7Changes.should(not be empty)
province7Changes.get.newIncomingArmies.size.shouldBe(1)
province7Changes.get.newIncomingArmies.head.originProvinceId.shouldBe(defendingProvinceId)
province7Changes.get.newIncomingArmies.head.destinationProvinceId.shouldBe(7)
province7Changes.get.newIncomingArmies.head.arrivalRound.shouldBe(20)
// Province 27 should have incoming army returning
val province27Changes = actionResult.changedProvinces
.find(_.provinceId == 27)
.map(_.asInstanceOf[ChangedProvinceC])
province27Changes should not be empty
province27Changes.get.newIncomingArmies should have size 1
province27Changes.get.newIncomingArmies.head.originProvinceId shouldBe defendingProvinceId
province27Changes.get.newIncomingArmies.head.destinationProvinceId shouldBe 27
val province27Changes = actionResult.changedProvinces.collectFirst {
case cp: ChangedProvinceC if cp.provinceId == 27 => cp
}
province27Changes.should(not be empty)
province27Changes.get.newIncomingArmies.size.shouldBe(1)
province27Changes.get.newIncomingArmies.head.originProvinceId.shouldBe(defendingProvinceId)
province27Changes.get.newIncomingArmies.head.destinationProvinceId.shouldBe(27)
// Province 8 should have incoming army returning
val province8Changes = actionResult.changedProvinces
.find(_.provinceId == 8)
.map(_.asInstanceOf[ChangedProvinceC])
province8Changes should not be empty
province8Changes.get.newIncomingArmies should have size 1
province8Changes.get.newIncomingArmies.head.originProvinceId shouldBe defendingProvinceId
province8Changes.get.newIncomingArmies.head.destinationProvinceId shouldBe 8
val province8Changes = actionResult.changedProvinces.collectFirst {
case cp: ChangedProvinceC if cp.provinceId == 8 => cp
}
province8Changes.should(not be empty)
province8Changes.get.newIncomingArmies.size.shouldBe(1)
province8Changes.get.newIncomingArmies.head.originProvinceId.shouldBe(defendingProvinceId)
province8Changes.get.newIncomingArmies.head.destinationProvinceId.shouldBe(8)
}
}
@@ -1,6 +1,7 @@
package net.eagle0.eagle.library.actions.impl.action
import net.eagle0.common.SeededRandom
import net.eagle0.eagle.checkedAs
import net.eagle0.eagle.library.actions.applier.ActionResultApplierImpl
import net.eagle0.eagle.library.actions.impl.command.{HandleRiotDoNothingCommand, TCommandFactory}
import net.eagle0.eagle.library.actions.impl.common.TCommand
@@ -133,7 +134,7 @@ class EndHandleRiotsPhaseActionTest extends AnyFlatSpec with BeforeAndAfterEach
result.provinceId shouldBe Some(pid)
result.actingFactionId shouldBe Some(fid)
result.changedProvinces.should(have.size(1))
val changedProvince = result.changedProvinces.head.asInstanceOf[ChangedProvinceC]
val changedProvince = checkedAs[ChangedProvinceC](result.changedProvinces.head)
changedProvince.setHasActed shouldBe Some(true)
result.actionResultType should (be(RiotAversionFailed) or be(RiotOccurred))
}
@@ -1,6 +1,6 @@
package net.eagle0.eagle.library.actions.impl.action
import net.eagle0.eagle.{HeroId, ProvinceId}
import net.eagle0.eagle.{checkedAs, HeroId, ProvinceId}
import net.eagle0.eagle.library.settings.{
AmbitionFactorForLoyaltyDegradation,
FoodIncreasePerAgriculture,
@@ -135,10 +135,10 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
private def gs: GameState = baseGameState
private def updateProvince(gs: GameState, provinceId: ProvinceId)(f: ProvinceC => ProvinceC): GameState =
gs.copy(provinces = gs.provinces.updated(provinceId, f(gs.provinces(provinceId).asInstanceOf[ProvinceC])))
gs.copy(provinces = gs.provinces.updated(provinceId, f(checkedAs[ProvinceC](gs.provinces(provinceId)))))
private def updateHero(gs: GameState, heroId: HeroId)(f: HeroC => HeroC): GameState =
gs.copy(heroes = gs.heroes.updated(heroId, f(gs.heroes(heroId).asInstanceOf[HeroC])))
gs.copy(heroes = gs.heroes.updated(heroId, f(checkedAs[HeroC](gs.heroes(heroId)))))
override def beforeEach(): Unit = {
MinSupportForTaxes.setDoubleValue(45)
@@ -161,7 +161,7 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val result = NewYearAction(gs).immediateExecute
val lowSupportAfter =
result.changedProvinces.find(_.provinceId == lowSupportProvince.id).get.asInstanceOf[ChangedProvinceC]
checkedAs[ChangedProvinceC](result.changedProvinces.find(_.provinceId == lowSupportProvince.id).get)
lowSupportAfter.foodDelta shouldBe None
lowSupportAfter.goldDelta shouldBe None
}
@@ -170,7 +170,7 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val result = NewYearAction(gs).immediateExecute
val highSupportAfter =
result.changedProvinces.find(_.provinceId == highSupportProvince.id).get.asInstanceOf[ChangedProvinceC]
checkedAs[ChangedProvinceC](result.changedProvinces.find(_.provinceId == highSupportProvince.id).get)
highSupportAfter.goldDelta should contain((40 * highSupportProvince.economy).toInt)
}
@@ -178,7 +178,7 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val result = NewYearAction(gs).immediateExecute
val highSupportAfter =
result.changedProvinces.find(_.provinceId == highSupportProvince.id).get.asInstanceOf[ChangedProvinceC]
checkedAs[ChangedProvinceC](result.changedProvinces.find(_.provinceId == highSupportProvince.id).get)
highSupportAfter.foodDelta should contain((60 * highSupportProvince.agriculture).toInt)
}
@@ -186,7 +186,7 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val result = NewYearAction(gs).immediateExecute
val oldAndNew = provincesMap.values.map(p =>
(p, result.changedProvinces.find(_.provinceId == p.id).get.asInstanceOf[ChangedProvinceC])
(p, checkedAs[ChangedProvinceC](result.changedProvinces.find(_.provinceId == p.id).get))
)
for (oldProv, newProv) <- oldAndNew do {
@@ -199,7 +199,7 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val result = NewYearAction(gs).immediateExecute
val oldAndNew = provincesMap.values.map(p =>
(p, result.changedProvinces.find(_.provinceId == p.id).get.asInstanceOf[ChangedProvinceC])
(p, checkedAs[ChangedProvinceC](result.changedProvinces.find(_.provinceId == p.id).get))
)
for (oldProv, newProv) <- oldAndNew do {
@@ -214,7 +214,7 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val result = NewYearAction(gs).immediateExecute
val oldAndNew = provincesMap.values.map(p =>
(p, result.changedProvinces.find(_.provinceId == p.id).get.asInstanceOf[ChangedProvinceC])
(p, checkedAs[ChangedProvinceC](result.changedProvinces.find(_.provinceId == p.id).get))
)
for (oldProv, newProv) <- oldAndNew do {
@@ -229,7 +229,7 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val result = NewYearAction(gs).immediateExecute
val oldAndNew = provincesMap.values.map(p =>
(p, result.changedProvinces.find(_.provinceId == p.id).get.asInstanceOf[ChangedProvinceC])
(p, checkedAs[ChangedProvinceC](result.changedProvinces.find(_.provinceId == p.id).get))
)
for (oldProv, newProv) <- oldAndNew do newProv.supportDelta should contain(-0.15 * oldProv.support)
@@ -239,7 +239,7 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val result = NewYearAction(gs).immediateExecute
val oldAndNew =
battalions.map(b => (b, result.changedBattalions.find(_.battalionId == b.id).get.asInstanceOf[ChangedBattalionC]))
battalions.map(b => (b, checkedAs[ChangedBattalionC](result.changedBattalions.find(_.battalionId == b.id).get)))
for (oldB, newB) <- oldAndNew do newB.to.armament shouldBe (0.85 * oldB.armament)
}
@@ -248,7 +248,7 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val result = NewYearAction(gs).immediateExecute
val oldAndNew =
battalions.map(b => (b, result.changedBattalions.find(_.battalionId == b.id).get.asInstanceOf[ChangedBattalionC]))
battalions.map(b => (b, checkedAs[ChangedBattalionC](result.changedBattalions.find(_.battalionId == b.id).get)))
for (oldB, newB) <- oldAndNew do newB.to.training shouldBe (0.85 * oldB.training)
}
@@ -268,7 +268,7 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val result = NewYearAction(modState).immediateExecute
result.changedHeroes should have size 1
val ch = result.changedHeroes.head.asInstanceOf[ChangedHeroC]
val ch = checkedAs[ChangedHeroC](result.changedHeroes.head)
loyaltyDelta(ch) should be < 0.0
}
@@ -316,9 +316,9 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val lowAmbitionResult = NewYearAction(lowAmbitionGameState).immediateExecute
val highAmbitionHeroAfter =
highAmbitionResult.changedHeroes.find(_.heroId == highDiscordHero.id).get.asInstanceOf[ChangedHeroC]
checkedAs[ChangedHeroC](highAmbitionResult.changedHeroes.find(_.heroId == highDiscordHero.id).get)
val lowAmbitionHeroAfter =
lowAmbitionResult.changedHeroes.find(_.heroId == lowDiscordHero.id).get.asInstanceOf[ChangedHeroC]
checkedAs[ChangedHeroC](lowAmbitionResult.changedHeroes.find(_.heroId == lowDiscordHero.id).get)
loyaltyDelta(highAmbitionHeroAfter) should be < loyaltyDelta(lowAmbitionHeroAfter)
}
@@ -340,9 +340,9 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val lowAmbitionResult = NewYearAction(lowAmbitionGameState).immediateExecute
val highAmbitionHeroAfter =
highAmbitionResult.changedHeroes.find(_.heroId == highDiscordHero.id).get.asInstanceOf[ChangedHeroC]
checkedAs[ChangedHeroC](highAmbitionResult.changedHeroes.find(_.heroId == highDiscordHero.id).get)
val lowAmbitionHeroAfter =
lowAmbitionResult.changedHeroes.find(_.heroId == highDiscordHero.id).get.asInstanceOf[ChangedHeroC]
checkedAs[ChangedHeroC](lowAmbitionResult.changedHeroes.find(_.heroId == highDiscordHero.id).get)
loyaltyDelta(highAmbitionHeroAfter) shouldBe loyaltyDelta(lowAmbitionHeroAfter)
}
@@ -361,9 +361,9 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val result = NewYearAction(modGameState).immediateExecute
val highDiscordHeroAfter =
result.changedHeroes.find(_.heroId == highDiscordHero.id).get.asInstanceOf[ChangedHeroC]
checkedAs[ChangedHeroC](result.changedHeroes.find(_.heroId == highDiscordHero.id).get)
val lowDiscordHeroAfter =
result.changedHeroes.find(_.heroId == lowDiscordHero.id).get.asInstanceOf[ChangedHeroC]
checkedAs[ChangedHeroC](result.changedHeroes.find(_.heroId == lowDiscordHero.id).get)
loyaltyDelta(highDiscordHeroAfter) should be < loyaltyDelta(lowDiscordHeroAfter)
}
@@ -387,7 +387,7 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val result = NewYearAction(modGameState).immediateExecute
val noDiscordHeroAfter =
result.changedHeroes.find(_.heroId == noDiscordHero.id).get.asInstanceOf[ChangedHeroC]
checkedAs[ChangedHeroC](result.changedHeroes.find(_.heroId == noDiscordHero.id).get)
loyaltyDelta(noDiscordHeroAfter) shouldBe 1
}
@@ -409,7 +409,7 @@ class NewYearActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEac
val result = NewYearAction(modGameState).immediateExecute
val highDiscordHeroAfter =
result.changedHeroes.find(_.heroId == highDiscordHero.id).get.asInstanceOf[ChangedHeroC]
checkedAs[ChangedHeroC](result.changedHeroes.find(_.heroId == highDiscordHero.id).get)
loyaltyDelta(highDiscordHeroAfter) shouldBe -3
}
@@ -1,5 +1,6 @@
package net.eagle0.eagle.library.actions.impl.action
import net.eagle0.eagle.checkedAs
import net.eagle0.eagle.library.settings.{MaxBattalionReductionForFoodShortage, MinSupportForTaxes}
import net.eagle0.eagle.library.EagleInternalException
import net.eagle0.eagle.model.action_result.{ActionResultT, NotificationDetails}
@@ -108,7 +109,7 @@ class PerformFoodConsumptionPhaseActionTest extends AnyFlatSpec with Matchers wi
val result: ActionResultT = PerformFoodConsumptionPhaseAction(gameState).immediateExecute
val cp: ChangedProvinceC = result.changedProvinces.head.asInstanceOf[ChangedProvinceC]
val cp: ChangedProvinceC = checkedAs[ChangedProvinceC](result.changedProvinces.head)
cp.foodDelta shouldBe Some(-258)
result.changedBattalions shouldBe empty
}
@@ -128,10 +129,10 @@ class PerformFoodConsumptionPhaseActionTest extends AnyFlatSpec with Matchers wi
val result: ActionResultT = PerformFoodConsumptionPhaseAction(gameState).immediateExecute
val cp: ChangedProvinceC = result.changedProvinces.head.asInstanceOf[ChangedProvinceC]
val cp: ChangedProvinceC = checkedAs[ChangedProvinceC](result.changedProvinces.head)
cp.foodDelta should contain(-196)
val changedBattalions: Vector[ChangedBattalionC] =
result.changedBattalions.map(cb => cb.asInstanceOf[ChangedBattalionC])
result.changedBattalions.map(cb => checkedAs[ChangedBattalionC](cb))
val battalionSizes: Set[Int] = changedBattalions.map(cb => cb.to.size).toSet
battalionSizes shouldBe Set(189, 457)
}
@@ -151,10 +152,10 @@ class PerformFoodConsumptionPhaseActionTest extends AnyFlatSpec with Matchers wi
val result: ActionResultT = PerformFoodConsumptionPhaseAction(gameState).immediateExecute
val cp: ChangedProvinceC = result.changedProvinces.head.asInstanceOf[ChangedProvinceC]
val cp: ChangedProvinceC = checkedAs[ChangedProvinceC](result.changedProvinces.head)
cp.foodDelta should contain(-196)
val changedBattalions: Vector[ChangedBattalionC] =
result.changedBattalions.map(cb => cb.asInstanceOf[ChangedBattalionC])
result.changedBattalions.map(cb => checkedAs[ChangedBattalionC](cb))
val battalionSizes: Set[Int] = changedBattalions.map(cb => cb.to.size).toSet
battalionSizes shouldBe Set(200, 481)
}
@@ -205,7 +206,7 @@ class PerformFoodConsumptionPhaseActionTest extends AnyFlatSpec with Matchers wi
val result: ActionResultT = PerformFoodConsumptionPhaseAction(gameState).immediateExecute
val cp: ChangedProvinceC = result.changedProvinces.head.asInstanceOf[ChangedProvinceC]
val cp: ChangedProvinceC = checkedAs[ChangedProvinceC](result.changedProvinces.head)
cp.removedIncomingArmyIds shouldBe Vector(17)
// Verify the full incoming army structure is preserved (not just supplies)
@@ -1,5 +1,6 @@
package net.eagle0.eagle.library.actions.impl.action
import net.eagle0.eagle.checkedAs
import net.eagle0.eagle.library.settings.WinterSuppliesLoss
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
import net.eagle0.eagle.model.action_result.types.ActionResultType.{
@@ -125,7 +126,7 @@ class PerformForcedTurnBackActionTest extends AnyFlatSpec with Matchers with Bef
first.affectedFactionIds shouldBe Vector(54)
first.changedProvinces should have size 2
first.changedProvinces.map(_.asInstanceOf[ChangedProvinceC]) should contain theSameElementsAs Vector(
first.changedProvinces.map(checkedAs[ChangedProvinceC](_)) should contain theSameElementsAs Vector(
ChangedProvinceC(
provinceId = destinationPid,
removedIncomingArmyIds = Vector(99)
@@ -188,7 +189,7 @@ class PerformForcedTurnBackActionTest extends AnyFlatSpec with Matchers with Bef
first.destroyedBattalionIds shouldBe Vector(22)
first.changedProvinces should have size 1
val changedProvince = first.changedProvinces.head.asInstanceOf[ChangedProvinceC]
val changedProvince = checkedAs[ChangedProvinceC](first.changedProvinces.head)
changedProvince.provinceId shouldBe destinationPid
changedProvince.removedIncomingArmyIds shouldBe Vector(99)
changedProvince.removedHostileArmyFactionIds shouldBe Vector(54)
@@ -264,7 +265,7 @@ class PerformForcedTurnBackActionTest extends AnyFlatSpec with Matchers with Bef
first.actingFactionId should contain(54)
first.provinceId should contain(destinationPid)
first.changedProvinces.map(_.asInstanceOf[ChangedProvinceC]) should contain theSameElementsAs Vector(
first.changedProvinces.map(checkedAs[ChangedProvinceC](_)) should contain theSameElementsAs Vector(
ChangedProvinceC(
provinceId = destinationPid,
removedIncomingShipmentIds = Vector(12)
@@ -315,7 +316,7 @@ class PerformForcedTurnBackActionTest extends AnyFlatSpec with Matchers with Bef
first.actingFactionId should contain(54)
first.provinceId should contain(destinationPid)
first.changedProvinces.map(_.asInstanceOf[ChangedProvinceC]) should contain theSameElementsAs Vector(
first.changedProvinces.map(checkedAs[ChangedProvinceC](_)) should contain theSameElementsAs Vector(
ChangedProvinceC(
provinceId = destinationPid,
removedIncomingShipmentIds = Vector(12)
@@ -1,6 +1,7 @@
package net.eagle0.eagle.library.actions.impl.action
import net.eagle0.common.SeededRandom
import net.eagle0.eagle.checkedAs
import net.eagle0.eagle.library.settings.{
BaseBeastsCount,
BeastsDurationMonths,
@@ -271,7 +272,7 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
val newEvents = cp.newProvinceEvents.get
newEvents.should(have).size(1)
val beastsEvent = newEvents.head.asInstanceOf[BeastsEventT]
val beastsEvent = checkedAs[BeastsEventT](newEvents.head)
beastsEvent.count.shouldBe(100)
beastsEvent.startDate.shouldBe(tDate(1501, 8))
beastsEvent.endDate.shouldBe(tDate(1501, 12))
@@ -534,9 +535,10 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
it should "not add an imminent riot if there is a festival" in {
val gsWithFestival = imminentRiotGS.copy(
provinces = Map(
7 -> imminentRiotGS
.provinces(7)
.asInstanceOf[ProvinceC]
7 -> checkedAs[ProvinceC](
imminentRiotGS
.provinces(7)
)
.copy(
activeEvents = Vector(
FestivalEventT(
@@ -560,9 +562,10 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
it should "not add an imminent riot if it hasn't been long enough based on support" in {
val gsWithLastRiot = imminentRiotGS.copy(
provinces = Map(
7 -> imminentRiotGS
.provinces(7)
.asInstanceOf[ProvinceC]
7 -> checkedAs[ProvinceC](
imminentRiotGS
.provinces(7)
)
.copy(
lastRiotDate = Some(tDate(1501, 3))
)
@@ -582,9 +585,10 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
it should "add a riot if it *has* been long enough based on support" in {
val gsWithOldRiot = imminentRiotGS.copy(
provinces = Map(
7 -> imminentRiotGS
.provinces(7)
.asInstanceOf[ProvinceC]
7 -> checkedAs[ProvinceC](
imminentRiotGS
.provinces(7)
)
.copy(
lastRiotDate = Some(tDate(1500, 3))
)
@@ -692,9 +696,10 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
it should "not add an imminent riot if the province is under attack" in {
val gsWithIncoming = imminentRiotGS.copy(
provinces = Map(
7 -> imminentRiotGS
.provinces(7)
.asInstanceOf[ProvinceC]
7 -> checkedAs[ProvinceC](
imminentRiotGS
.provinces(7)
)
.copy(
incomingArmies = Vector(
MovingArmy(
@@ -724,9 +729,10 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
it should "still add an imminent riot if the province has a friendly incoming" in {
val gsWithFriendlyIncoming = imminentRiotGS.copy(
provinces = Map(
7 -> imminentRiotGS
.provinces(7)
.asInstanceOf[ProvinceC]
7 -> checkedAs[ProvinceC](
imminentRiotGS
.provinces(7)
)
.copy(
incomingArmies = Vector(
MovingArmy(
@@ -757,9 +763,10 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
it should "not add an imminent riot if the support is under tax level" in {
val gsWithLowSupport = imminentRiotGS.copy(
provinces = Map(
7 -> imminentRiotGS
.provinces(7)
.asInstanceOf[ProvinceC]
7 -> checkedAs[ProvinceC](
imminentRiotGS
.provinces(7)
)
.copy(
support = 38
)
@@ -778,9 +785,10 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
it should "still add an imminent riot if the support is over 55" in {
val gsWithHighSupport = imminentRiotGS.copy(
provinces = Map(
7 -> imminentRiotGS
.provinces(7)
.asInstanceOf[ProvinceC]
7 -> checkedAs[ProvinceC](
imminentRiotGS
.provinces(7)
)
.copy(
support = 55
)
@@ -821,7 +829,7 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
cp.get.provinceId.shouldBe(7)
val newEvents = cp.get.newProvinceEvents.get
newEvents.should(have).size(1)
val blizzardEvent = newEvents.head.asInstanceOf[BlizzardEventT]
val blizzardEvent = checkedAs[BlizzardEventT](newEvents.head)
blizzardEvent.startDate.shouldBe(tDate(1501, 12))
blizzardEvent.endDate.shouldBe(tDate(1502, 3))
}
@@ -840,7 +848,7 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
cp.get.provinceId.shouldBe(7)
val newEvents = cp.get.newProvinceEvents.get
newEvents.should(have).size(1)
val blizzardEvent = newEvents.head.asInstanceOf[BlizzardEventT]
val blizzardEvent = checkedAs[BlizzardEventT](newEvents.head)
blizzardEvent.startDate.shouldBe(tDate(1502, 2))
blizzardEvent.endDate.shouldBe(tDate(1502, 3))
}
@@ -871,9 +879,10 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
it should "not add a blizzard if there is already a blizzard" in {
val alreadyBlizzardGS = blizzardGS.copy(
provinces = Map(
7 -> blizzardGS
.provinces(7)
.asInstanceOf[ProvinceC]
7 -> checkedAs[ProvinceC](
blizzardGS
.provinces(7)
)
.copy(
activeEvents = Vector(
BlizzardEventT(
@@ -919,7 +928,7 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
cp.get.provinceId.shouldBe(7)
val newEvents = cp.get.newProvinceEvents.get
newEvents.should(have).size(1)
val floodEvent = newEvents.head.asInstanceOf[FloodEventT]
val floodEvent = checkedAs[FloodEventT](newEvents.head)
floodEvent.startDate.shouldBe(tDate(1501, 7))
floodEvent.endDate.shouldBe(tDate(1501, 9))
}
@@ -927,9 +936,10 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
it should "not add a flood if there's a drought" in {
val alreadyDroughtGS = floodGS.copy(
provinces = Map(
7 -> floodGS
.provinces(7)
.asInstanceOf[ProvinceC]
7 -> checkedAs[ProvinceC](
floodGS
.provinces(7)
)
.copy(
activeEvents = Vector(
DroughtEventT(
@@ -975,7 +985,7 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
cp.get.provinceId.shouldBe(7)
val newEvents = cp.get.newProvinceEvents.get
newEvents.should(have).size(1)
val festivalEvent = newEvents.head.asInstanceOf[FestivalEventT]
val festivalEvent = checkedAs[FestivalEventT](newEvents.head)
festivalEvent.startDate.shouldBe(tDate(1501, 3))
festivalEvent.endDate.shouldBe(tDate(1501, 6))
}
@@ -993,7 +1003,7 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
cp.get.provinceId.shouldBe(7)
val newEvents = cp.get.newProvinceEvents.get
newEvents.should(have).size(1)
val festivalEvent = newEvents.head.asInstanceOf[FestivalEventT]
val festivalEvent = checkedAs[FestivalEventT](newEvents.head)
festivalEvent.startDate.shouldBe(tDate(1501, 9))
festivalEvent.endDate.shouldBe(tDate(1501, 12))
}
@@ -1024,9 +1034,10 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
it should "not add a festival if there is already a festival" in {
val gsWithFestival = festivalGS.copy(
provinces = Map(
7 -> festivalGS
.provinces(7)
.asInstanceOf[ProvinceC]
7 -> checkedAs[ProvinceC](
festivalGS
.provinces(7)
)
.copy(
activeEvents = Vector(
FestivalEventT(
@@ -1050,9 +1061,10 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
it should "not add a festival if there is a drought" in {
val gsWithDrought = festivalGS.copy(
provinces = Map(
7 -> festivalGS
.provinces(7)
.asInstanceOf[ProvinceC]
7 -> checkedAs[ProvinceC](
festivalGS
.provinces(7)
)
.copy(
activeEvents = Vector(
DroughtEventT(
@@ -1130,11 +1142,11 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
)
)
val results = PerformProvinceEventsAction(tGameState).results(SeededRandom(1L)).asInstanceOf[Vector[ActionResultC]]
val results = PerformProvinceEventsAction(tGameState).results(SeededRandom(1L)).map(checkedAs[ActionResultC](_))
val changedFactions = results.head.changedFactions
changedFactions.should(have).size(1)
val cf = changedFactions.head.asInstanceOf[ChangedFactionC]
val cf = checkedAs[ChangedFactionC](changedFactions.head)
cf.factionId.shouldBe(4)
cf.newPrestigeModifiers.should(have).size(1)
cf.newPrestigeModifiers.head.prestigeModifierType.shouldBe(PrestigeModifier.Type.Festival)
@@ -1291,7 +1303,7 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
val newEvents = cp.newProvinceEvents.get
newEvents.should(have).size(1)
val epidemicEvent = newEvents.head.asInstanceOf[EpidemicEventT]
val epidemicEvent = checkedAs[EpidemicEventT](newEvents.head)
epidemicEvent.startDate.shouldBe(tDate(1501, 7))
}
@@ -1440,7 +1452,7 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
val newEvents = cp.newProvinceEvents.get
newEvents.should(have).size(1)
val epidemicEvent = newEvents.head.asInstanceOf[EpidemicEventT]
val epidemicEvent = checkedAs[EpidemicEventT](newEvents.head)
epidemicEvent.startDate.shouldBe(tDate(1501, 7))
}
@@ -1492,11 +1504,11 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
)
)
val results = PerformProvinceEventsAction(tGameState).results(SeededRandom(1L)).asInstanceOf[Vector[ActionResultC]]
val results = PerformProvinceEventsAction(tGameState).results(SeededRandom(1L)).map(checkedAs[ActionResultC](_))
val changedBattalions = results.head.changedBattalions
changedBattalions.should(have).size(2)
changedBattalions
.map(_.asInstanceOf[ChangedBattalionC])
.map(checkedAs[ChangedBattalionC](_))
.map(cb => (cb.to.id, cb.to.size))
.toSet
.shouldBe(Set((19, 212), (25, 850)))
@@ -1535,11 +1547,11 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
)
)
val results = PerformProvinceEventsAction(tGameState).results(SeededRandom(1L)).asInstanceOf[Vector[ActionResultC]]
val results = PerformProvinceEventsAction(tGameState).results(SeededRandom(1L)).map(checkedAs[ActionResultC](_))
val changedHeroes = results.head.changedHeroes
changedHeroes.should(have).size(3)
changedHeroes
.map(_.asInstanceOf[ChangedHeroC])
.map(checkedAs[ChangedHeroC](_))
.map(ch => (ch.heroId, ch.vigorChange))
.toSet
.shouldBe(
@@ -1575,7 +1587,7 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
cp.get.provinceId.shouldBe(7)
val newEvents = cp.get.newProvinceEvents.get
newEvents.should(have).size(1)
val droughtEvent = newEvents.head.asInstanceOf[DroughtEventT]
val droughtEvent = checkedAs[DroughtEventT](newEvents.head)
droughtEvent.startDate.shouldBe(tDate(1501, 7))
droughtEvent.endDate.shouldBe(tDate(1501, 10))
}
@@ -1606,9 +1618,10 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
it should "not add a drought if there is already a drought" in {
val alreadyDroughtGS = droughtGS.copy(
provinces = Map(
7 -> droughtGS
.provinces(7)
.asInstanceOf[ProvinceC]
7 -> checkedAs[ProvinceC](
droughtGS
.provinces(7)
)
.copy(
activeEvents = Vector(
DroughtEventT(
@@ -1632,9 +1645,10 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
it should "not add a drought if there is a flood" in {
val alreadyFloodGS = droughtGS.copy(
provinces = Map(
7 -> droughtGS
.provinces(7)
.asInstanceOf[ProvinceC]
7 -> checkedAs[ProvinceC](
droughtGS
.provinces(7)
)
.copy(
activeEvents = Vector(
FloodEventT(
@@ -1659,7 +1673,7 @@ class PerformProvinceEventsActionTest extends AnyFlatSpec with BeforeAndAfterEac
val earlyGS = droughtGS.copy(currentRoundId = 5)
val tGameState = earlyGS
val results =
PerformProvinceEventsAction(tGameState).results(SeededRandom(1L)).asInstanceOf[Vector[ActionResultC]].head
PerformProvinceEventsAction(tGameState).results(SeededRandom(1L)).map(checkedAs[ActionResultC](_)).head
results.actionResultType.shouldBe(ProvinceEventsChanged)
results.newRoundPhase.shouldBe(Some(RoundPhase.ForcedTurnBack))
@@ -1,6 +1,7 @@
package net.eagle0.eagle.library.actions.impl.action
import net.eagle0.common.SeededRandom
import net.eagle0.eagle.checkedAs
import net.eagle0.eagle.library.actions.applier.ActionResultApplierImpl
import net.eagle0.eagle.library.EagleInternalException
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
@@ -138,11 +139,11 @@ class PerformProvinceMoveResolutionActionTest extends AnyFlatSpec with Matchers
"a single province with a single incoming army" should "produce the friendly move event for that army" in {
val results = getResults(startingState)
val firstResult = results.head.asInstanceOf[ActionResultC]
val firstResult = checkedAs[ActionResultC](results.head)
firstResult.actionResultType shouldBe FriendlyMove
firstResult.provinceId should contain(province1.id)
val changedProvince = firstResult.changedProvinces.head.asInstanceOf[ChangedProvinceC]
val changedProvince = checkedAs[ChangedProvinceC](firstResult.changedProvinces.head)
changedProvince.newRulingFactionHeroIds shouldBe Vector(1)
changedProvince.newBattalionIds shouldBe Vector(1)
}
@@ -161,12 +162,10 @@ class PerformProvinceMoveResolutionActionTest extends AnyFlatSpec with Matchers
val moveResults = results.filter(_.actionResultType == FriendlyMove)
moveResults.should(have.size(2))
forAll(moveResults) { r =>
r.asInstanceOf[ActionResultC].provinceId should contain(p.id)
}
forAll(moveResults)(r => checkedAs[ActionResultC](r).provinceId should contain(p.id))
val lastResult = moveResults.last.asInstanceOf[ActionResultC]
val lastChangedProvince = lastResult.changedProvinces.head.asInstanceOf[ChangedProvinceC]
val lastResult = checkedAs[ActionResultC](moveResults.last)
val lastChangedProvince = checkedAs[ChangedProvinceC](lastResult.changedProvinces.head)
lastChangedProvince.newRulingFactionHeroIds should contain theSameElementsAs Vector(4)
lastChangedProvince.newBattalionIds should contain theSameElementsAs Vector(4)
}
@@ -203,7 +202,7 @@ class PerformProvinceMoveResolutionActionTest extends AnyFlatSpec with Matchers
destinationProvinceId = 300,
army = movingArmy.army.copy(fleeProvinceId = None)
)
val hostileProvince = startingState.provinces(300).asInstanceOf[ProvinceC]
val hostileProvince = checkedAs[ProvinceC](startingState.provinces(300))
val hostileWithArrival = hostileProvince.copy(
incomingArmies = Vector(fleeingArmy)
)
@@ -248,15 +247,15 @@ class PerformProvinceMoveResolutionActionTest extends AnyFlatSpec with Matchers
forAll(moveResults)(_.actionResultType shouldBe FriendlyMove)
forExactly(1, moveResults) { result =>
val ar = result.asInstanceOf[ActionResultC]
val ar = checkedAs[ActionResultC](result)
ar.provinceId should contain(province1.id)
ar.changedProvinces.map(_.asInstanceOf[ChangedProvinceC].provinceId) shouldBe Vector(province1.id)
ar.changedProvinces.map(checkedAs[ChangedProvinceC](_).provinceId) shouldBe Vector(province1.id)
}
forExactly(1, moveResults) { result =>
val ar = result.asInstanceOf[ActionResultC]
val ar = checkedAs[ActionResultC](result)
ar.provinceId should contain(province2.id)
ar.changedProvinces.map(_.asInstanceOf[ChangedProvinceC].provinceId) shouldBe Vector(province2.id)
ar.changedProvinces.map(checkedAs[ChangedProvinceC](_).provinceId) shouldBe Vector(province2.id)
}
}
@@ -3,6 +3,7 @@ package net.eagle0.eagle.library.actions.impl.action
import scala.util.Random
import net.eagle0.common.SeededRandom
import net.eagle0.eagle.checkedAs
import net.eagle0.eagle.library.actions.applier.ActionResultApplierImpl
import net.eagle0.eagle.library.settings.{BaseResourceLimit, PerDevelopmentResourceLimit}
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
@@ -130,38 +131,38 @@ class PerformReconResolutionActionTest extends AnyFlatSpec with BeforeAndAfterEa
}
"perform one" should "remove the incoming recon" in {
val oneResult = PerformReconResolutionAction(gameState, actionResultApplier)
.oneResult(
provinceId = 3,
incomingEndTurnAction = incomingRecon,
functionalRandom = functionalRandom
)
.newValue
.asInstanceOf[ActionResultC]
val oneResult = checkedAs[ActionResultC](
PerformReconResolutionAction(gameState, actionResultApplier)
.oneResult(
provinceId = 3,
incomingEndTurnAction = incomingRecon,
functionalRandom = functionalRandom
)
.newValue
)
oneResult.actionResultType shouldBe ReconSucceeded
val targetProvinceCP = oneResult.changedProvinces
.find(_.asInstanceOf[ChangedProvinceC].provinceId == 3)
.get
.asInstanceOf[ChangedProvinceC]
val targetProvinceCP = oneResult.changedProvinces.collectFirst {
case cp: ChangedProvinceC if cp.provinceId == 3 => cp
}.get
targetProvinceCP.removedIncomingEndTurnActions shouldBe Vector(incomingRecon)
}
it should "return the hero to the origin province if it's still owned" in {
val oneResult = PerformReconResolutionAction(gameState, actionResultApplier)
.oneResult(
provinceId = 3,
incomingEndTurnAction = incomingRecon,
functionalRandom = functionalRandom
)
.newValue
.asInstanceOf[ActionResultC]
val oneResult = checkedAs[ActionResultC](
PerformReconResolutionAction(gameState, actionResultApplier)
.oneResult(
provinceId = 3,
incomingEndTurnAction = incomingRecon,
functionalRandom = functionalRandom
)
.newValue
)
oneResult.actionResultType shouldBe ReconSucceeded
val originProvinceCP = oneResult.changedProvinces
.find(_.asInstanceOf[ChangedProvinceC].provinceId == 9)
.get
.asInstanceOf[ChangedProvinceC]
val originProvinceCP = oneResult.changedProvinces.collectFirst {
case cp: ChangedProvinceC if cp.provinceId == 9 => cp
}.get
originProvinceCP.newRulingFactionHeroIds shouldBe Vector(19)
}
@@ -170,20 +171,20 @@ class PerformReconResolutionActionTest extends AnyFlatSpec with BeforeAndAfterEa
val gsWithLostProvince = makeGameState(provincesWithLostProvince, baseHeroes, baseFactions)
val oneResult =
PerformReconResolutionAction(gsWithLostProvince, actionResultApplier)
.oneResult(
provinceId = 3,
incomingEndTurnAction = incomingRecon,
functionalRandom = functionalRandom
)
.newValue
.asInstanceOf[ActionResultC]
checkedAs[ActionResultC](
PerformReconResolutionAction(gsWithLostProvince, actionResultApplier)
.oneResult(
provinceId = 3,
incomingEndTurnAction = incomingRecon,
functionalRandom = functionalRandom
)
.newValue
)
oneResult.actionResultType shouldBe ReconSucceeded
val returnProvinceCP = oneResult.changedProvinces
.find(_.asInstanceOf[ChangedProvinceC].provinceId == 10)
.get
.asInstanceOf[ChangedProvinceC]
val returnProvinceCP = oneResult.changedProvinces.collectFirst {
case cp: ChangedProvinceC if cp.provinceId == 10 => cp
}.get
returnProvinceCP.newRulingFactionHeroIds shouldBe Vector(19)
}
@@ -194,37 +195,38 @@ class PerformReconResolutionActionTest extends AnyFlatSpec with BeforeAndAfterEa
val gsWithLostProvinces = makeGameState(provincesWithLostProvinces, baseHeroes, baseFactions)
val oneResult =
PerformReconResolutionAction(gsWithLostProvinces, actionResultApplier)
checkedAs[ActionResultC](
PerformReconResolutionAction(gsWithLostProvinces, actionResultApplier)
.oneResult(
provinceId = 3,
incomingEndTurnAction = incomingRecon,
functionalRandom = functionalRandom
)
.newValue
)
oneResult.actionResultType shouldBe ReconSucceeded
val originProvinceCP = oneResult.changedProvinces.collectFirst {
case cp: ChangedProvinceC if cp.provinceId == 9 => cp
}.get
originProvinceCP.newUnaffiliatedHeroes.map(_.heroId) shouldBe Vector(19)
}
it should "update the faction with the info about the province" in {
val oneResult = checkedAs[ActionResultC](
PerformReconResolutionAction(gameState, actionResultApplier)
.oneResult(
provinceId = 3,
incomingEndTurnAction = incomingRecon,
functionalRandom = functionalRandom
)
.newValue
.asInstanceOf[ActionResultC]
oneResult.actionResultType shouldBe ReconSucceeded
val originProvinceCP = oneResult.changedProvinces
.find(_.asInstanceOf[ChangedProvinceC].provinceId == 9)
.get
.asInstanceOf[ChangedProvinceC]
originProvinceCP.newUnaffiliatedHeroes.map(_.heroId) shouldBe Vector(19)
}
it should "update the faction with the info about the province" in {
val oneResult = PerformReconResolutionAction(gameState, actionResultApplier)
.oneResult(
provinceId = 3,
incomingEndTurnAction = incomingRecon,
functionalRandom = functionalRandom
)
.newValue
.asInstanceOf[ActionResultC]
)
oneResult.actionResultType shouldBe ReconSucceeded
oneResult.changedFactions should have size 1
val cf = oneResult.changedFactions.head.asInstanceOf[ChangedFactionC]
val cf = checkedAs[ChangedFactionC](oneResult.changedFactions.head)
cf.updatedReconnedProvinces should have size 1
val rp = cf.updatedReconnedProvinces.head
@@ -233,14 +235,15 @@ class PerformReconResolutionActionTest extends AnyFlatSpec with BeforeAndAfterEa
}
it should "extend client text visibility for the heroes in the reconned province" in {
val oneResult = PerformReconResolutionAction(gameState, actionResultApplier)
.oneResult(
provinceId = 3,
incomingEndTurnAction = incomingRecon,
functionalRandom = functionalRandom
)
.newValue
.asInstanceOf[ActionResultC]
val oneResult = checkedAs[ActionResultC](
PerformReconResolutionAction(gameState, actionResultApplier)
.oneResult(
provinceId = 3,
incomingEndTurnAction = incomingRecon,
functionalRandom = functionalRandom
)
.newValue
)
oneResult.clientTextVisibilityExtensions should contain theSameElementsAs Vector(
ClientTextVisibilityExtensionC(
@@ -1,5 +1,6 @@
package net.eagle0.eagle.library.actions.impl.action
import net.eagle0.eagle.checkedAs
import net.eagle0.eagle.library.actions.impl.action.PrisonerExchangeAction.{ExchangeMatch, ExchangeableHeroInfo}
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
import net.eagle0.eagle.model.action_result.concrete.ChangedHeroC
@@ -275,7 +276,7 @@ class PrisonerExchangeActionTest extends AnyFlatSpec with Matchers {
val headResult = results.head
headResult.actionResultType shouldBe PrisonersExchanged
headResult.changedProvinces.map(_.asInstanceOf[ChangedProvinceC]) should contain theSameElementsAs Vector(
headResult.changedProvinces.map(checkedAs[ChangedProvinceC](_)) should contain theSameElementsAs Vector(
ChangedProvinceC(
provinceId = 5,
removedUnaffiliatedHeroIds = Vector(31),
@@ -1,5 +1,6 @@
package net.eagle0.eagle.library.actions.impl.action
import net.eagle0.eagle.checkedAs
import net.eagle0.eagle.library.actions.applier.ActionResultApplierImpl
import net.eagle0.eagle.library.settings.{
BattleAgricultureDevastationDelta,
@@ -785,11 +786,9 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
val battleEndedResult = results.find(x => x.actionResultType == BattleEnded)
battleEndedResult should not be empty
val defenderProvinceCP = battleEndedResult.get.changedProvinces.find {
case cpc: ChangedProvinceC => cpc.provinceId == defenderProvinceId
case _ => false
val defenderProvinceCP = battleEndedResult.get.changedProvinces.collectFirst {
case cpc: ChangedProvinceC if cpc.provinceId == defenderProvinceId => cpc
}.get
.asInstanceOf[ChangedProvinceC]
// Outlawed defender should be added as an unaffiliated hero (outlaw)
defenderProvinceCP.newUnaffiliatedHeroes.map(_.heroId) should contain(7)
@@ -854,11 +853,9 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
battleEndedResult.get.destroyedBattalionIds should contain(10)
// Battalion should also be removed from the province
val defenderProvinceCP = battleEndedResult.get.changedProvinces.find {
case cpc: ChangedProvinceC => cpc.provinceId == defenderProvinceId
case _ => false
val defenderProvinceCP = battleEndedResult.get.changedProvinces.collectFirst {
case cpc: ChangedProvinceC if cpc.provinceId == defenderProvinceId => cpc
}.get
.asInstanceOf[ChangedProvinceC]
defenderProvinceCP.removedBattalionIds should contain(10)
}
@@ -1120,11 +1117,9 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
battleEndedResult should not be empty
val cp = battleEndedResult.get.changedProvinces.find {
case cpc: ChangedProvinceC => cpc.provinceId == defenderProvinceId
case _ => false
val cp = battleEndedResult.get.changedProvinces.collectFirst {
case cpc: ChangedProvinceC if cpc.provinceId == defenderProvinceId => cpc
}.get
.asInstanceOf[ChangedProvinceC]
cp.newCapturedHeroes should contain(
CapturedHero(
heroId = 7,
@@ -1178,11 +1173,9 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
battleEndedResult should not be empty
val cp = battleEndedResult.get.changedProvinces.find {
case cpc: ChangedProvinceC => cpc.provinceId == defenderProvinceId
case _ => false
val cp = battleEndedResult.get.changedProvinces.collectFirst {
case cpc: ChangedProvinceC if cpc.provinceId == defenderProvinceId => cpc
}.get
.asInstanceOf[ChangedProvinceC]
cp.newCapturedHeroes should contain(
CapturedHero(
heroId = 7,
@@ -1227,11 +1220,9 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
battleEndedResult should not be empty
val changedDefenderProvince = battleEndedResult.get.changedProvinces.find {
case cpc: ChangedProvinceC => cpc.provinceId == defenderProvinceId
case _ => false
val changedDefenderProvince = battleEndedResult.get.changedProvinces.collectFirst {
case cpc: ChangedProvinceC if cpc.provinceId == defenderProvinceId => cpc
}.get
.asInstanceOf[ChangedProvinceC]
changedDefenderProvince.goldDelta should contain(500)
changedDefenderProvince.foodDelta should contain(700)
@@ -1254,11 +1245,9 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
battleEndedResult should not be empty
val changedDefenderProvince = battleEndedResult.get.changedProvinces.find {
case cpc: ChangedProvinceC => cpc.provinceId == defenderProvinceId
case _ => false
val changedDefenderProvince = battleEndedResult.get.changedProvinces.collectFirst {
case cpc: ChangedProvinceC if cpc.provinceId == defenderProvinceId => cpc
}.get
.asInstanceOf[ChangedProvinceC]
changedDefenderProvince.economyDevastationDelta should contain(15)
changedDefenderProvince.agricultureDevastationDelta should contain(12)
@@ -1282,11 +1271,9 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
battleEndedResult should not be empty
val changedDefenderProvince = battleEndedResult.get.changedProvinces.find {
case cpc: ChangedProvinceC => cpc.provinceId == defenderProvinceId
case _ => false
val changedDefenderProvince = battleEndedResult.get.changedProvinces.collectFirst {
case cpc: ChangedProvinceC if cpc.provinceId == defenderProvinceId => cpc
}.get
.asInstanceOf[ChangedProvinceC]
changedDefenderProvince.newBattleRevelations should have size 1
changedDefenderProvince.newBattleRevelations.head shouldBe BattleRevelation(
provinceId = defenderProvinceId,
@@ -1338,11 +1325,9 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
battleEndedResult should not be empty
val fleeProvinceCP = battleEndedResult.get.changedProvinces.find {
case cpc: ChangedProvinceC => cpc.provinceId == 39
case _ => false
val fleeProvinceCP = battleEndedResult.get.changedProvinces.collectFirst {
case cpc: ChangedProvinceC if cpc.provinceId == 39 => cpc
}.get
.asInstanceOf[ChangedProvinceC]
fleeProvinceCP.provinceId shouldBe 39
fleeProvinceCP.newIncomingArmies should have size 1
@@ -1399,11 +1384,9 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
battleEndedResult should not be empty
val attackerFleeProvinceCP = battleEndedResult.get.changedProvinces.find {
case cpc: ChangedProvinceC => cpc.provinceId == attackerProvinceId
case _ => false
val attackerFleeProvinceCP = battleEndedResult.get.changedProvinces.collectFirst {
case cpc: ChangedProvinceC if cpc.provinceId == attackerProvinceId => cpc
}.get
.asInstanceOf[ChangedProvinceC]
attackerFleeProvinceCP.provinceId shouldBe attackerProvinceId
attackerFleeProvinceCP.newIncomingArmies should have size 1
@@ -1548,11 +1531,9 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
battleEndedResult should not be empty
val cp = battleEndedResult.get.changedProvinces.find {
case cpc: ChangedProvinceC => cpc.provinceId == defenderProvinceId
case _ => false
val cp = battleEndedResult.get.changedProvinces.collectFirst {
case cpc: ChangedProvinceC if cpc.provinceId == defenderProvinceId => cpc
}.get
.asInstanceOf[ChangedProvinceC]
cp.newCapturedHeroes should contain(
CapturedHero(
heroId = 1,
@@ -1606,11 +1587,9 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
battleEndedResult should not be empty
val cp = battleEndedResult.get.changedProvinces.find {
case cpc: ChangedProvinceC => cpc.provinceId == defenderProvinceId
case _ => false
val cp = battleEndedResult.get.changedProvinces.collectFirst {
case cpc: ChangedProvinceC if cpc.provinceId == defenderProvinceId => cpc
}.get
.asInstanceOf[ChangedProvinceC]
cp.newCapturedHeroes should contain(
CapturedHero(
heroId = 8,
@@ -1709,7 +1688,7 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
val battleEndedResult = results.find(x => x.actionResultType == BattleEnded).get
battleEndedResult.changedProvinces.exists { cp =>
val cpc = cp.asInstanceOf[ChangedProvinceC]
val cpc = checkedAs[ChangedProvinceC](cp)
cpc.provinceId == defenderFleeProvince.id && cpc.changedUnaffiliatedHeroes.nonEmpty
} shouldBe false
}
@@ -1755,9 +1734,7 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
forAll(results) { result =>
result.actionResultType should not be QuestFulfilled
forAll(result.changedProvinces) { cp =>
cp.asInstanceOf[ChangedProvinceC].changedUnaffiliatedHeroes shouldBe empty
}
forAll(result.changedProvinces)(cp => checkedAs[ChangedProvinceC](cp).changedUnaffiliatedHeroes shouldBe empty)
}
}
@@ -1904,7 +1881,7 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
}
fleeProvinceChange should be(defined)
val fleeProvinceCP = fleeProvinceChange.get.asInstanceOf[ChangedProvinceC]
val fleeProvinceCP = checkedAs[ChangedProvinceC](fleeProvinceChange.get)
fleeProvinceCP.newIncomingArmies should have size 1
val incomingArmy = fleeProvinceCP.newIncomingArmies.head
@@ -1,6 +1,6 @@
package net.eagle0.eagle.library.actions.impl.command
import net.eagle0.eagle.{BattalionId, FactionId, HeroId, ProvinceId, RoundId}
import net.eagle0.eagle.{checkedAs, BattalionId, FactionId, HeroId, ProvinceId, RoundId}
import net.eagle0.eagle.library.settings.{ActionVigorCost, MarchWisdomXp, TurnsToResolveConquest}
import net.eagle0.eagle.library.EagleCommandException
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
@@ -122,7 +122,7 @@ class MarchCommandTest extends AnyFlatSpec with Matchers with BeforeAndAfterEach
val result = action.immediateExecute
result.shouldBe(a[ActionResultC])
val actionResult = result.asInstanceOf[ActionResultC]
val actionResult = checkedAs[ActionResultC](result)
actionResult.actionResultType.shouldBe(MarchAction)
actionResult.actingFactionId.should(contain(actingFactionId))
actionResult.provinceId.should(contain(originProvinceId))
@@ -148,7 +148,7 @@ class MarchCommandTest extends AnyFlatSpec with Matchers with BeforeAndAfterEach
val result = action.immediateExecute
result.shouldBe(a[ActionResultC])
val actionResult = result.asInstanceOf[ActionResultC]
val actionResult = checkedAs[ActionResultC](result)
actionResult.changedHeroes.shouldBe(
Vector(
@@ -185,12 +185,13 @@ class MarchCommandTest extends AnyFlatSpec with Matchers with BeforeAndAfterEach
val result = action.immediateExecute
result.shouldBe(a[ActionResultC])
val actionResult = result.asInstanceOf[ActionResultC]
val actionResult = checkedAs[ActionResultC](result)
val originProvinceChange = actionResult.changedProvinces
.find(_.provinceId == originProvinceId)
.get
.asInstanceOf[ChangedProvinceC]
val originProvinceChange = checkedAs[ChangedProvinceC](
actionResult.changedProvinces
.find(_.provinceId == originProvinceId)
.get
)
originProvinceChange.removedRulingFactionHeroIds.shouldBe(Vector(1, 27))
originProvinceChange.removedBattalionIds.shouldBe(Vector(99, 98))
originProvinceChange.goldDelta.shouldBe(Some(-3212))
@@ -222,11 +223,11 @@ class MarchCommandTest extends AnyFlatSpec with Matchers with BeforeAndAfterEach
val result = action.immediateExecute
result.shouldBe(a[ActionResultC])
val actionResult = result.asInstanceOf[ActionResultC]
val actionResult = checkedAs[ActionResultC](result)
val actingProvinceChange = actionResult.changedProvinces
.find(_.provinceId == actingProvince)
.map(_.asInstanceOf[ChangedProvinceC])
.map(checkedAs[ChangedProvinceC](_))
actingProvinceChange.flatMap(_.setHasActed).shouldBe(Some(true))
}
@@ -250,12 +251,13 @@ class MarchCommandTest extends AnyFlatSpec with Matchers with BeforeAndAfterEach
val result = action.immediateExecute
result.shouldBe(a[ActionResultC])
val actionResult = result.asInstanceOf[ActionResultC]
val actionResult = checkedAs[ActionResultC](result)
val destinationProvinceChange = actionResult.changedProvinces
.find(_.provinceId == destinationProvinceId)
.get
.asInstanceOf[ChangedProvinceC]
val destinationProvinceChange = checkedAs[ChangedProvinceC](
actionResult.changedProvinces
.find(_.provinceId == destinationProvinceId)
.get
)
val incomingArmy = destinationProvinceChange.newIncomingArmies.head
incomingArmy.army.factionId.shouldBe(actingFactionId)
@@ -1,5 +1,6 @@
package net.eagle0.eagle.library.actions.impl.command
import net.eagle0.eagle.checkedAs
import net.eagle0.eagle.library.settings.{
ActionVigorCost,
SendSuppliesWisdomXp,
@@ -208,7 +209,7 @@ class SendSuppliesCommandTest extends AnyFlatSpec with Matchers with BeforeAndAf
// Cast to concrete type to access newIncomingShipments
val concreteChange =
destinationProvinceChange.get.asInstanceOf[ChangedProvinceC]
checkedAs[ChangedProvinceC](destinationProvinceChange.get)
val incomingShipments = concreteChange.newIncomingShipments
incomingShipments.should(have).size(1)
@@ -1,6 +1,6 @@
package net.eagle0.eagle.library.actions.impl.command
import net.eagle0.eagle.{FactionId, GameId, HeroId, ProvinceId, RoundId}
import net.eagle0.eagle.{checkedAs, FactionId, GameId, HeroId, ProvinceId, RoundId}
import net.eagle0.eagle.library.actions.impl.common.ProtolessSequentialResultsAction
import net.eagle0.eagle.library.settings.{AcceptBrotherhoodCharismaXp, SwearBrotherhoodCharismaXp}
import net.eagle0.eagle.library.util.quest_fulfillment.QuestFulfillmentChecker
@@ -68,7 +68,7 @@ class SwearBrotherhoodCommandTest extends AnyFlatSpec with Matchers with BeforeA
val result = action.results.head
result.shouldBe(a[ActionResultC])
val actionResult = result.asInstanceOf[ActionResultC]
val actionResult = checkedAs[ActionResultC](result)
actionResult.actionResultType.shouldBe(SwearBrotherhood)
actionResult.actingFactionId.should(contain(actingFactionId))
actionResult.actingHeroId.should(contain(newBrotherHeroId))
@@ -92,7 +92,7 @@ class SwearBrotherhoodCommandTest extends AnyFlatSpec with Matchers with BeforeA
val result = action.results.head
result.shouldBe(a[ActionResultC])
val actionResult = result.asInstanceOf[ActionResultC]
val actionResult = checkedAs[ActionResultC](result)
actionResult.changedFactions.shouldBe(
Vector(
@@ -140,7 +140,7 @@ class SwearBrotherhoodCommandTest extends AnyFlatSpec with Matchers with BeforeA
val result = action.results.head
result.shouldBe(a[ActionResultC])
val actionResult = result.asInstanceOf[ActionResultC]
val actionResult = checkedAs[ActionResultC](result)
val expectedLlmId =
s"$currentRoundId swear brotherhood $newBrotherHeroId"
@@ -1,5 +1,6 @@
package net.eagle0.eagle.library.util.command_choice_helpers
import net.eagle0.eagle.checkedAs
import net.eagle0.eagle.library.settings.{
AlmsPaladinSupportMultiplier,
AlmsSupportIncreasePerFood,
@@ -253,7 +254,7 @@ class AttackCommandChooserTest extends AnyFlatSpec with Matchers with BeforeAndA
val gsWithoutDestinationRuler = gameState.copy(
provinces = gameState.provinces.updated(
destinationProvinceId,
gameState.provinces(destinationProvinceId).asInstanceOf[ProvinceC].copy(rulingFactionId = None)
checkedAs[ProvinceC](gameState.provinces(destinationProvinceId)).copy(rulingFactionId = None)
)
)
@@ -271,9 +272,10 @@ class AttackCommandChooserTest extends AnyFlatSpec with Matchers with BeforeAndA
factions = gameState.factions
.updated(
destinationFactionId,
gameState
.factions(destinationFactionId)
.asInstanceOf[FactionC]
checkedAs[FactionC](
gameState
.factions(destinationFactionId)
)
.copy(
factionRelationships = Vector(
FactionRelationship(
@@ -286,9 +288,10 @@ class AttackCommandChooserTest extends AnyFlatSpec with Matchers with BeforeAndA
)
.updated(
actingFactionId,
gameState
.factions(actingFactionId)
.asInstanceOf[FactionC]
checkedAs[FactionC](
gameState
.factions(actingFactionId)
)
.copy(
factionRelationships = Vector(
FactionRelationship(
@@ -315,9 +318,10 @@ class AttackCommandChooserTest extends AnyFlatSpec with Matchers with BeforeAndA
factions = gameState.factions
.updated(
destinationFactionId,
gameState
.factions(destinationFactionId)
.asInstanceOf[FactionC]
checkedAs[FactionC](
gameState
.factions(destinationFactionId)
)
.copy(
factionRelationships = Vector(
FactionRelationship(
@@ -330,9 +334,10 @@ class AttackCommandChooserTest extends AnyFlatSpec with Matchers with BeforeAndA
)
.updated(
actingFactionId,
gameState
.factions(actingFactionId)
.asInstanceOf[FactionC]
checkedAs[FactionC](
gameState
.factions(actingFactionId)
)
.copy(
factionRelationships = Vector(
FactionRelationship(
@@ -3,7 +3,7 @@ package net.eagle0.eagle.library.util.command_choice_helpers
import scala.util.Random
import net.eagle0.common.SeededRandom
import net.eagle0.eagle.{BattalionId, FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.{checkedAs, BattalionId, FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.library.settings.{
AlmsPaladinSupportMultiplier,
AlmsSupportIncreasePerFood,
@@ -246,8 +246,7 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
chosenCommand.get.actingFactionId shouldBe actingFid
chosenCommand.get.actingProvinceId shouldBe 7
chosenCommand.get.available shouldBe acs.head
chosenCommand.get.selected
.asInstanceOf[OrganizeTroopsSelected] shouldBe
checkedAs[OrganizeTroopsSelected](chosenCommand.get.selected) shouldBe
OrganizeTroopsSelected(
changedBattalions = Vector.empty,
newBattalions = Vector(
@@ -886,7 +885,7 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
choice.actingProvinceId shouldBe 5
val marchSelectedCommand =
choice.selected.asInstanceOf[MarchSelected]
checkedAs[MarchSelected](choice.selected)
marchSelectedCommand.marchingUnits should have size 25 // have 30, cap is 5
(1 to 30) should contain allElementsOf (marchSelectedCommand.marchingUnits
.map(_.heroId))
@@ -912,7 +911,7 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
choice.actingProvinceId shouldBe 5
val marchSelectedCommand =
choice.selected.asInstanceOf[MarchSelected]
checkedAs[MarchSelected](choice.selected)
marchSelectedCommand.marchingUnits.map(
_.heroId
) shouldBe (15 to 17).toVector
@@ -941,7 +940,7 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
choice.actingProvinceId shouldBe 5
val marchSelectedCommand =
choice.selected.asInstanceOf[MarchSelected]
checkedAs[MarchSelected](choice.selected)
marchSelectedCommand.destinationProvinceId shouldBe 6
}
@@ -1,7 +1,7 @@
package net.eagle0.eagle.library.util.command_choice_helpers
import net.eagle0.common.SeededRandom
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.{checkedAs, FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.library.settings.MinimumTrustToOfferTruce
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.model.state.{GameType, RoundPhase}
@@ -283,9 +283,10 @@ class FulfillQuestsCommandSelectorTest extends AnyFlatSpec with Matchers with Be
val lowTrustGameState = gameState.copy(
factions = gameState.factions.updated(
actingFactionId,
gameState
.factions(actingFactionId)
.asInstanceOf[FactionC]
checkedAs[FactionC](
gameState
.factions(actingFactionId)
)
.copy(
factionRelationships = Vector(
FactionRelationship(
@@ -380,9 +381,10 @@ class FulfillQuestsCommandSelectorTest extends AnyFlatSpec with Matchers with Be
),
factions = gameState.factions.updated(
actingFactionId,
gameState
.factions(actingFactionId)
.asInstanceOf[FactionC]
checkedAs[FactionC](
gameState
.factions(actingFactionId)
)
.copy(
factionRelationships = Vector(
FactionRelationship(
@@ -458,9 +460,10 @@ class FulfillQuestsCommandSelectorTest extends AnyFlatSpec with Matchers with Be
),
factions = gameState.factions.updated(
actingFactionId,
gameState
.factions(actingFactionId)
.asInstanceOf[FactionC]
checkedAs[FactionC](
gameState
.factions(actingFactionId)
)
.copy(
factionRelationships = Vector(
FactionRelationship(
@@ -525,9 +528,10 @@ class FulfillQuestsCommandSelectorTest extends AnyFlatSpec with Matchers with Be
factions = gameState.factions
.updated(
actingFactionId,
gameState
.factions(actingFactionId)
.asInstanceOf[FactionC]
checkedAs[FactionC](
gameState
.factions(actingFactionId)
)
.copy(
factionRelationships = Vector(
FactionRelationship(
@@ -550,9 +554,10 @@ class FulfillQuestsCommandSelectorTest extends AnyFlatSpec with Matchers with Be
)
.updated(
105,
gameState
.factions(105)
.asInstanceOf[FactionC]
checkedAs[FactionC](
gameState
.factions(105)
)
.copy(
factionRelationships = Vector(
FactionRelationship(
@@ -16,6 +16,7 @@ import net.eagle0.eagle.model.state.quest.{DevelopProvincesQuest, ImproveAgricul
import net.eagle0.eagle.model.state.run_status.RunStatus
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatest.Inside.inside
class DevelopProvincesQuestCommandChooserTest extends AnyFlatSpec with Matchers {
@@ -177,12 +178,14 @@ class DevelopProvincesQuestCommandChooserTest extends AnyFlatSpec with Matchers
)
result shouldBe defined
val selected = result.get.selected.asInstanceOf[IssueOrdersSelected]
// province1 and province2 should be switched (non-hostile first), province3 kept as Mobilize
val developIds = selected.newOrders.filter(_.orderType == ProvinceOrderType.Develop).map(_.provinceId).toSet
developIds should contain(province1Id)
developIds should contain(province2Id)
developIds should not contain enemyProvinceId
inside(result.get.selected) {
case selected: IssueOrdersSelected =>
// province1 and province2 should be switched (non-hostile first), province3 kept as Mobilize
val developIds = selected.newOrders.filter(_.orderType == ProvinceOrderType.Develop).map(_.provinceId).toSet
developIds.should(contain(province1Id))
developIds.should(contain(province2Id))
developIds.contains(enemyProvinceId).shouldBe(false)
}
}
it should "include hostile-neighbor provinces when needed" in {
@@ -214,8 +217,10 @@ class DevelopProvincesQuestCommandChooserTest extends AnyFlatSpec with Matchers
)
result shouldBe defined
val selected = result.get.selected.asInstanceOf[IssueOrdersSelected]
selected.newOrders.count(_.orderType == ProvinceOrderType.Develop) shouldBe 2
inside(result.get.selected) {
case selected: IssueOrdersSelected =>
selected.newOrders.count(_.orderType == ProvinceOrderType.Develop).shouldBe(2)
}
}
it should "return None when IssueOrders is not available" in {
@@ -16,6 +16,7 @@ import net.eagle0.eagle.model.state.quest.{DevelopProvincesQuest, MobilizeProvin
import net.eagle0.eagle.model.state.run_status.RunStatus
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatest.Inside.inside
class MobilizeProvincesQuestCommandChooserTest extends AnyFlatSpec with Matchers {
@@ -158,10 +159,12 @@ class MobilizeProvincesQuestCommandChooserTest extends AnyFlatSpec with Matchers
)
result shouldBe defined
val selected = result.get.selected.asInstanceOf[IssueOrdersSelected]
val mobilizeIds = selected.newOrders.filter(_.orderType == ProvinceOrderType.Mobilize).map(_.provinceId).toSet
// province2 (hostile neighbor) should be picked first
mobilizeIds should contain(province2Id)
inside(result.get.selected) {
case selected: IssueOrdersSelected =>
val mobilizeIds = selected.newOrders.filter(_.orderType == ProvinceOrderType.Mobilize).map(_.provinceId).toSet
// province2 (hostile neighbor) should be picked first
mobilizeIds.should(contain(province2Id))
}
}
it should "switch provinces to Mobilize when quest conditions are met" in {
@@ -175,11 +178,13 @@ class MobilizeProvincesQuestCommandChooserTest extends AnyFlatSpec with Matchers
)
result shouldBe defined
val selected = result.get.selected.asInstanceOf[IssueOrdersSelected]
selected.newOrders.count(_.orderType == ProvinceOrderType.Mobilize) shouldBe 2
// The Develop province should not be switched
selected.newOrders.find(_.provinceId == province3Id).get.orderType shouldBe ProvinceOrderType.Develop
result.get.reason shouldBe "fulfill MobilizeProvinces quest"
inside(result.get.selected) {
case selected: IssueOrdersSelected =>
selected.newOrders.count(_.orderType == ProvinceOrderType.Mobilize).shouldBe(2)
// The Develop province should not be switched
selected.newOrders.find(_.provinceId == province3Id).get.orderType.shouldBe(ProvinceOrderType.Develop)
}
result.get.reason.shouldBe("fulfill MobilizeProvinces quest")
}
it should "return None when already enough provinces are mobilizing" in {
@@ -88,6 +88,7 @@ class UnrequestedTextHandlerTest extends AnyFlatSpec with BeforeAndAfterEach wit
(() => mockCts.beginTransaction()).expects().anyNumberOfTimes(): Unit
(() => mockCts.commitTransaction()).expects().anyNumberOfTimes(): Unit
(() => mockCts.rollbackTransaction()).expects().anyNumberOfTimes(): Unit
(() => mockHistory.count).expects().returning(100000).anyNumberOfTimes(): Unit
}
behavior of "UnrequestedTextHandlerTest"
@@ -175,6 +176,44 @@ class UnrequestedTextHandlerTest extends AnyFlatSpec with BeforeAndAfterEach wit
result.newlyCompletedTexts shouldBe Vector.empty
}
it should "throw on stale unrequested text requests left behind by a rewind" in {
(() => mockCts.beginTransaction()).expects().anyNumberOfTimes(): Unit
(() => mockCts.commitTransaction()).expects().anyNumberOfTimes(): Unit
(() => mockCts.rollbackTransaction()).expects().anyNumberOfTimes(): Unit
val staleRequest = UnrequestedClientText(
id = "staleRequest",
requestedAfterHistoryCount = 8550,
llmRequest = llmRequest1
)
(() => mockHistory.count).expects().returning(8545).anyNumberOfTimes(): Unit
(() => mockCts.unrequestedTexts)
.expects()
.returning(Map("staleRequest" -> staleRequest)): Unit
mockHistory.stateAfter.expects(8550).never(): Unit
(mockLlmResolver
.resolveLlmRequests(
_: Vector[LlmRequestWithGameState],
_: ClientTextStore,
_: FunctionalRandom
))
.expects(*, *, *)
.never(): Unit
val thrown = intercept[EagleInternalException] {
handler.clientTextStoreWithHandledUnrequestedTexts(
clientTextStore = mockCts,
gameHistory = mockHistory,
randomLong = 1
)
}
thrown.getMessage shouldBe
"Stale pending text request staleRequest: requestedAfterHistoryCount=8550 is after current history count 8545. " +
"This usually means the game was rewound without pruning its text store."
}
it should "throw an exception if there's a dependency circle" in {
expectTransactionCalls()
val unrequested1 = UnrequestedClientText(
@@ -455,7 +455,8 @@ class GameControllerTest extends AnyFlatSpec with MockFactory with Matchers with
logWriter = mockLogWriter
)
val mockRewoundEngine = mock[Engine]
val mockRewoundEngine = mock[Engine]
val mockPrunedClientTextStore = mock[ClientTextStore]
// Setup mocks for the rewound engine
(() => mockRewoundEngine.factionIds)
@@ -544,9 +545,19 @@ class GameControllerTest extends AnyFlatSpec with MockFactory with Matchers with
.returns(mockRewoundEngine)
.once(): Unit
(() => mockClientTextStore.saved)
mockClientTextStore.withoutTextsAfter
.expects(3)
.returns(mockPrunedClientTextStore)
.once(): Unit
mockPrunedClientTextStore.hasIncompleteTextsAccessibleTo
.expects(*)
.returning(false)
.anyNumberOfTimes(): Unit
(() => mockPrunedClientTextStore.saved)
.expects()
.returns(mockClientTextStore)
.returns(mockPrunedClientTextStore)
.anyNumberOfTimes(): Unit
val controller = GameController(
@@ -567,5 +578,6 @@ class GameControllerTest extends AnyFlatSpec with MockFactory with Matchers with
newController.humanClients.foreach { client =>
client.unfilteredKnownHistoryCount shouldBe 1 // Rewound history has 1 result
}
newController.clientTextStore shouldBe mockPrunedClientTextStore
}
}