Make selected flee province optional, and must not be set if no flee provinces are available (#2985)

* make it optional

* make the flee province optional

* fix test to allow for optional flee province id

* add tests for incorrect flee province settings
This commit is contained in:
2023-10-16 06:58:45 -07:00
committed by GitHub
parent bbd62a09b3
commit be120d7cdf
5 changed files with 116 additions and 16 deletions
@@ -96,7 +96,7 @@ message ControlWeatherSelectedCommand {
}
message DefendSelectedCommand {
int32 flee_province_id = 1;
.google.protobuf.Int32Value flee_province_id = 1;
repeated .net.eagle0.eagle.common.CombatUnit defending_units = 2;
}
@@ -19,7 +19,7 @@ case class DefendCommand(
defendingPlayer: FactionId,
provinceId: ProvinceId,
defendingUnits: Seq[CombatUnit],
fleeProvinceId: ProvinceId
fleeProvinceId: Option[ProvinceId]
) extends DeterministicSingleResultCommand(gameState, sc) {
override def immediateExecute: ActionResult = {
commandRequire(
@@ -52,7 +52,7 @@ case class DefendCommand(
Army(
factionId = defendingPlayer,
units = defendingUnits.toVector,
fleeProvinceId = Some(fleeProvinceId)
fleeProvinceId = fleeProvinceId
)
)
)
@@ -76,12 +76,26 @@ object DefendCommand
s"Tried to defend with ${defendingUnits.length} units (max is $maxUnitCount)"
)
commandRequire(
availableCommand.availableFleeProvinceIds.contains(
selectedCommand.fleeProvinceId
),
s"Selected flee province ${selectedCommand.fleeProvinceId} is not in available ${availableCommand.availableFleeProvinceIds}"
)
// If there are any eligible flee provinces, one must be chosen; if there are none, the flee province must be empty
if (availableCommand.availableFleeProvinceIds.isEmpty)
commandRequire(
selectedCommand.fleeProvinceId.isEmpty,
s"Flee province ${selectedCommand.fleeProvinceId.get} was selected, but none were available"
)
else {
commandRequire(
selectedCommand.fleeProvinceId.nonEmpty,
s"Flee provinces ${availableCommand.availableFleeProvinceIds} are available, but none was selected"
)
commandRequire(
availableCommand.availableFleeProvinceIds
.contains(
selectedCommand.fleeProvinceId.get
),
s"Selected flee province ${selectedCommand.fleeProvinceId.get} is not in available ${availableCommand.availableFleeProvinceIds}"
)
}
DefendCommand(
sc = selectedCommand,
@@ -1167,8 +1167,8 @@ object CommandChoiceHelpers {
val fleeProvinceId = provinces
.find(_.rulingFactionId.contains(actingFactionId))
.orElse(provinces.find(_.rulingFactionId.isEmpty))
.getOrElse(provinces.head)
.id
.orElse(provinces.headOption)
.map(_.id)
DefendSelectedCommand(
defendingUnits = defendingUnits(gameState, defendCommand),
@@ -104,6 +104,7 @@ scala_test(
name = "defend_command_test",
srcs = ["DefendCommandTest.scala"],
deps = [
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/common:action_result_type_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/common:combat_unit_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/common:unaffiliated_hero_scala_proto",
@@ -120,6 +121,7 @@ scala_test(
"//src/main/scala/net/eagle0/eagle/library/actions/impl/command:deterministic_single_result_command",
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action",
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:deterministic_single_result_action",
"//src/main/scala/net/eagle0/eagle/library/settings",
"//src/test/scala/net/eagle0/eagle/library/actions/impl:action_impl_pkg",
],
)
@@ -1,5 +1,7 @@
package net.eagle0.eagle.library.actions.impl.command
import net.eagle0.eagle.SettingsKeys
import net.eagle0.eagle.api.available_command.DefendAvailableCommand
import net.eagle0.eagle.api.selected_command.DefendSelectedCommand
import net.eagle0.eagle.common.action_result_type.ActionResultType.DEFEND
import net.eagle0.eagle.common.combat_unit.CombatUnit
@@ -15,10 +17,15 @@ import net.eagle0.eagle.internal.game_state.GameState
import net.eagle0.eagle.internal.hero.Hero
import net.eagle0.eagle.internal.province.Province
import net.eagle0.eagle.library.EagleCommandException
import net.eagle0.eagle.library.settings.Settings
import org.scalatest.BeforeAndAfterEach
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class DefendCommandTest extends AnyFlatSpec with Matchers {
class DefendCommandTest
extends AnyFlatSpec
with Matchers
with BeforeAndAfterEach {
import net.eagle0.eagle.library.actions.impl._
val fleeProvinceId = 19
@@ -66,6 +73,83 @@ class DefendCommandTest extends AnyFlatSpec with Matchers {
private val selectedCommand = DefendSelectedCommand()
override def beforeEach(): Unit = {
Settings.addSettings(
Vector(
SettingsKeys.maxCombatUnitCountPerSide -> "10"
)
)
}
"make" should "throw if a flee province is selected when none were available" in {
val availableCommand = DefendAvailableCommand(
suitableBattalionsForHeroes = Map(),
availableFleeProvinceIds = Vector(),
availableBattalions = Vector(),
availableHeroIds = Vector(),
recommendedUnits = Vector(),
actingProvinceId = 7
)
val gs = GameState()
val sc = DefendSelectedCommand(fleeProvinceId = Some(fleeProvinceId))
the[EagleCommandException] thrownBy {
DefendCommand.make(
actingFactionId = 1,
gameState = gs,
availableCommand = availableCommand,
selectedCommand = sc
)
} should have message "requirement failed: Flee province 19 was selected, but none were available"
}
it should "throw if flee provinces are available and none is selected" in {
val availableCommand = DefendAvailableCommand(
suitableBattalionsForHeroes = Map(),
availableFleeProvinceIds = Vector(5, 6),
availableBattalions = Vector(),
availableHeroIds = Vector(),
recommendedUnits = Vector(),
actingProvinceId = 7
)
val gs = GameState()
val sc = DefendSelectedCommand(fleeProvinceId = None)
the[EagleCommandException] thrownBy {
DefendCommand.make(
actingFactionId = 1,
gameState = gs,
availableCommand = availableCommand,
selectedCommand = sc
)
} should have message "requirement failed: Flee provinces Vector(5, 6) are available, but none was selected"
}
it should "throw if the selected flee province is not among the available ones" in {
val availableCommand = DefendAvailableCommand(
suitableBattalionsForHeroes = Map(),
availableFleeProvinceIds = Vector(5, 6),
availableBattalions = Vector(),
availableHeroIds = Vector(),
recommendedUnits = Vector(),
actingProvinceId = 7
)
val gs = GameState()
val sc = DefendSelectedCommand(fleeProvinceId = Some(fleeProvinceId))
the[EagleCommandException] thrownBy {
DefendCommand.make(
actingFactionId = 1,
gameState = gs,
availableCommand = availableCommand,
selectedCommand = sc
)
} should have message "requirement failed: Selected flee province 19 is not in available Vector(5, 6)"
}
"Execute" should "throw if there is no hostile army in specified province" in {
val startingState = GameState(
heroes = HeroMap(heroes),
@@ -81,7 +165,7 @@ class DefendCommandTest extends AnyFlatSpec with Matchers {
defendingPlayer = 4,
provinceId = 7,
defendingUnits = defenders,
fleeProvinceId = fleeProvinceId
fleeProvinceId = Some(fleeProvinceId)
)
the[EagleCommandException] thrownBy {
@@ -101,7 +185,7 @@ class DefendCommandTest extends AnyFlatSpec with Matchers {
defendingPlayer = 4,
provinceId = 7,
defendingUnits = defenders,
fleeProvinceId = fleeProvinceId
fleeProvinceId = Some(fleeProvinceId)
)
the[EagleCommandException] thrownBy {
@@ -122,7 +206,7 @@ class DefendCommandTest extends AnyFlatSpec with Matchers {
defendingPlayer = 4,
provinceId = 7,
defendingUnits = defenders,
fleeProvinceId = fleeProvinceId
fleeProvinceId = Some(fleeProvinceId)
)
val results = command.resultsOfExecute
@@ -143,7 +227,7 @@ class DefendCommandTest extends AnyFlatSpec with Matchers {
defendingPlayer = 4,
provinceId = 7,
defendingUnits = defenders,
fleeProvinceId = fleeProvinceId
fleeProvinceId = Some(fleeProvinceId)
)
val results = command.resultsOfExecute