Compare commits

...
Author SHA1 Message Date
adminandClaude Opus 4.5 e82f28e678 Make CommandChoiceHelpers protoless
Convert all command choice helper files to use Scala types instead of
proto types. This eliminates proto dependencies from the command
selection logic in the library.

Key changes:
- Update AvailableCommandSelector, CommandChoiceHelpers, and all
  command selector files to use Scala AvailableCommand/SelectedCommand
- Convert CommandSelection to ScalaCommandSelection throughout
- Update action files (EndHandleRiotsPhaseAction,
  PerformVassalCommandsPhaseAction, PerformVassalDefenseDecisionsAction)
  to work with Scala commands directly
- Update all quest command choosers to use Scala types
- Fix ArmedBattalion Scala definition (battalionTypeId -> newArmament)
- Add exports to combat_unit_selector BUILD.bazel

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 22:37:27 -08:00
adminandClaude Opus 4.5 eb37b9a734 Fix ArmedBattalion field name: battalionTypeId -> newArmament
The ArmedBattalion.battalionTypeId field was misnamed - it represents
the armament level to raise troops to, not a battalion type ID.
Renamed to newArmament to match the proto field name and the
semantic meaning.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 22:36:39 -08:00
39 changed files with 786 additions and 806 deletions
@@ -460,7 +460,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/model/action_result/changed_province/concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:action_result_concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/types",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/command/available:available_command_converter",
"//src/main/scala/net/eagle0/eagle/model/state:round_phase",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
@@ -1304,7 +1303,6 @@ scala_library(
],
deps = [
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/common:province_order_type_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/common:more_option",
@@ -1319,7 +1317,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:command_chooser",
"//src/main/scala/net/eagle0/eagle/library/util/province",
"//src/main/scala/net/eagle0/eagle/model/action_result:action_result_trait",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/command/available:available_command_converter",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/province",
@@ -1342,7 +1339,6 @@ scala_library(
],
deps = [
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/actions/applier:action_result_applier",
@@ -1354,7 +1350,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers",
"//src/main/scala/net/eagle0/eagle/library/util/province",
"//src/main/scala/net/eagle0/eagle/model/action_result:action_result_trait",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/command/available:available_command_converter",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/province",
@@ -11,7 +11,6 @@ import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedPro
import net.eagle0.eagle.model.action_result.concrete.ActionResultC
import net.eagle0.eagle.model.action_result.types.ActionResultType.EndHandleRiotsPhase
import net.eagle0.eagle.model.action_result.ActionResultT
import net.eagle0.eagle.model.proto_converters.command.available.AvailableCommandConverter
import net.eagle0.eagle.model.state.command.available.OneProvinceAvailableCommands
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.province.ProvinceT
@@ -39,19 +38,15 @@ case class EndHandleRiotsPhaseAction(
commandsForProvince(province.id).map { opac =>
seq.withRandomActionResults { (gs, fr) =>
val actingFactionId = province.rulingFactionId.get
val protoCommands = opac.commands.map { cmd =>
AvailableCommandConverter.toProto(cmd, gs, actingFactionId)
}
CommandChoiceHelpers
.handleRiotSelectedCommand(
actingFactionId = actingFactionId,
gameState = gs,
availableCommands = protoCommands,
availableCommands = opac.commands,
functionalRandom = fr
)
.continue {
case (Some(cs), nextFr) =>
val scs = cs.toScala(opac.commands)
case (Some(scs), nextFr) =>
val cmd = commandFactory.makeTCommand(
actingFactionId = province.rulingFactionId.get,
gameState = gs,
@@ -66,7 +61,7 @@ case class EndHandleRiotsPhaseAction(
case TCommand.Sequential(action) =>
RandomState(action.results, nextFr)
}
case (None, nextFr) =>
case (None, nextFr) =>
RandomState(Vector.empty[ActionResultT], nextFr)
}
}
@@ -2,17 +2,16 @@ package net.eagle0.eagle.library.actions.impl.action
import net.eagle0.common.{FunctionalRandom, MoreOption, RandomState}
import net.eagle0.eagle.{FactionId, ProvinceId}
import net.eagle0.eagle.api.available_command.{AvailableCommand as ProtoAvailableCommand, RestAvailableCommand}
import net.eagle0.eagle.library.actions.applier.ActionResultApplier
import net.eagle0.eagle.library.actions.impl.command.TCommandFactory
import net.eagle0.eagle.library.actions.impl.common.{ProtolessRandomSequentialResultsAction, TCommand}
import net.eagle0.eagle.library.actions.random_state_sequencer.RandomStateSequencer
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChooser
import net.eagle0.eagle.library.util.province.ProvinceUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.action_result.ActionResultT
import net.eagle0.eagle.model.proto_converters.command.available.AvailableCommandConverter
import net.eagle0.eagle.model.state.command.available.OneProvinceAvailableCommands
import net.eagle0.eagle.model.state.command.available.{AvailableCommand, OneProvinceAvailableCommands}
import net.eagle0.eagle.model.state.command.available.AvailableCommand.RestAvailable
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.province.ProvinceOrderType.*
import net.eagle0.eagle.model.state.province.ProvinceT
@@ -47,11 +46,10 @@ case class PerformVassalCommandsPhaseAction(
case (sequencer, province) =>
commandsForProvince(province.id).map { opac =>
sequencer.withRandomActionResults { (gs, fr) =>
chooseCommand(gs, province.id, fr).continue {
case (Some(cs), nextFr) =>
val scs = cs.toScala(opac.commands)
chooseCommand(gs, province.id, opac.commands, fr).continue {
case (Some(scs), nextFr) =>
val cmd = commandFactory.makeTCommand(
actingFactionId = cs.actingFactionId,
actingFactionId = scs.actingFactionId,
gameState = gs,
availableCommand = scs.available,
selectedCommand = scs.selected
@@ -64,7 +62,7 @@ case class PerformVassalCommandsPhaseAction(
case TCommand.Sequential(action) =>
RandomState(action.results, nextFr)
}
case (None, nextFr) =>
case (None, nextFr) =>
RandomState(Vector.empty[ActionResultT], nextFr)
}
}
@@ -76,15 +74,15 @@ case class PerformVassalCommandsPhaseAction(
private def maybeRestCommand(
actingFactionId: FactionId,
gs: GameState,
commandOptions: Vector[ProtoAvailableCommand],
commandOptions: Vector[AvailableCommand],
provinceId: ProvinceId,
reason: String
): Option[CommandSelection] = {
): Option[ScalaCommandSelection] = {
val heroes =
gs.provinces(provinceId).rulingFactionHeroIds.map(gs.heroes)
MoreOption.flatWhen(
commandOptions.collectFirst {
case ac: RestAvailableCommand =>
case ac: RestAvailable =>
ac
}.isDefined && shouldRest(heroes)
) {
@@ -95,10 +93,10 @@ case class PerformVassalCommandsPhaseAction(
private def selectedCommandFromOrders(
actingFactionId: FactionId,
gs: GameState,
commandOptions: Vector[ProtoAvailableCommand],
commandOptions: Vector[AvailableCommand],
functionalRandom: FunctionalRandom,
provinceId: ProvinceId
): RandomState[Option[CommandSelection]] =
): RandomState[Option[ScalaCommandSelection]] =
gs.provinces(provinceId).provinceOrders match {
case Entrust =>
chosenEntrustCommand(
@@ -145,45 +143,42 @@ case class PerformVassalCommandsPhaseAction(
def chooseCommand(
gs: GameState,
provinceId: ProvinceId,
commands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
commandsForProvince(provinceId).map { oneProvinceAvailableCommands =>
val actingFactionId = gs.provinces(provinceId).rulingFactionId.get
val protoCommands = oneProvinceAvailableCommands.commands.map { cmd =>
AvailableCommandConverter.toProto(cmd, gs, actingFactionId)
}
CommandChooser.choose(
actingFactionId,
gs,
protoCommands,
Vector[CommandChooser](
resolveTributeSelectedCommand,
defendSelectedCommand,
handleRiotSelectedCommand,
(
fid: FactionId,
gsC: GameState,
acs: Vector[ProtoAvailableCommand],
fr: FunctionalRandom
) =>
RandomState(
maybeRestCommand(
fid,
gsC,
acs,
provinceId,
"chosen vassal command: rest"
),
fr
): RandomState[Option[ScalaCommandSelection]] = {
val actingFactionId = gs.provinces(provinceId).rulingFactionId.get
CommandChooser.choose(
actingFactionId,
gs,
commands,
Vector[CommandChooser](
resolveTributeSelectedCommand,
defendSelectedCommand,
handleRiotSelectedCommand,
(
fid: FactionId,
gsC: GameState,
acs: Vector[AvailableCommand],
fr: FunctionalRandom
) =>
RandomState(
maybeRestCommand(
fid,
gsC,
acs,
provinceId,
"chosen vassal command: rest"
),
(
fid: FactionId,
gsC: GameState,
acs: Vector[ProtoAvailableCommand],
fr: FunctionalRandom
) => selectedCommandFromOrders(fid, gsC, acs, fr, provinceId)
),
functionalRandom
)
}.get
fr
),
(
fid: FactionId,
gsC: GameState,
acs: Vector[AvailableCommand],
fr: FunctionalRandom
) => selectedCommandFromOrders(fid, gsC, acs, fr, provinceId)
),
functionalRandom
)
}
}
@@ -1,17 +1,15 @@
package net.eagle0.eagle.library.actions.impl.action
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.api.available_command.AvailableCommand as ProtoAvailableCommand
import net.eagle0.eagle.library.actions.applier.ActionResultApplier
import net.eagle0.eagle.library.actions.impl.command.TCommandFactory
import net.eagle0.eagle.library.actions.impl.common.{ProtolessRandomSequentialResultsAction, TCommand}
import net.eagle0.eagle.library.actions.random_state_sequencer.RandomStateSequencer
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChoiceHelpers
import net.eagle0.eagle.library.util.province.ProvinceUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.action_result.ActionResultT
import net.eagle0.eagle.model.proto_converters.command.available.AvailableCommandConverter
import net.eagle0.eagle.model.state.command.available.OneProvinceAvailableCommands
import net.eagle0.eagle.model.state.command.available.{AvailableCommand, OneProvinceAvailableCommands}
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.province.ProvinceT
import net.eagle0.eagle.ProvinceId
@@ -43,15 +41,10 @@ case class PerformVassalDefenseDecisionsAction(
case (sequencer, province) =>
commandsForProvince(province.id).map { opac =>
sequencer.withRandomActionResults { (gs, fr) =>
val actingFactionId = province.rulingFactionId.get
val protoCommands = opac.commands.map { cmd =>
AvailableCommandConverter.toProto(cmd, gs, actingFactionId)
}
chooseCommand(gs, province.id, protoCommands, fr).continue {
case (Some(cs), nextFr) =>
val scs = cs.toScala(opac.commands)
chooseCommand(gs, province.id, opac.commands, fr).continue {
case (Some(scs), nextFr) =>
val cmd = commandFactory.makeTCommand(
actingFactionId = cs.actingFactionId,
actingFactionId = scs.actingFactionId,
gameState = gs,
availableCommand = scs.available,
selectedCommand = scs.selected
@@ -64,7 +57,7 @@ case class PerformVassalDefenseDecisionsAction(
case TCommand.Sequential(action) =>
RandomState(action.results, nextFr)
}
case (None, nextFr) =>
case (None, nextFr) =>
RandomState(Vector.empty[ActionResultT], nextFr)
}
}
@@ -76,9 +69,9 @@ case class PerformVassalDefenseDecisionsAction(
private def chooseCommand(
gs: GameState,
provinceId: ProvinceId,
commandOptions: Vector[ProtoAvailableCommand],
commandOptions: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
): RandomState[Option[ScalaCommandSelection]] =
CommandChoiceHelpers.resolveTributeSelectedCommand(
actingFactionId = gs.provinces(provinceId).rulingFactionId.get,
gameState = gs,
@@ -120,7 +120,7 @@ class CommandFactory extends TCommandFactory {
armedBattalions = sc.armedBattalions.map { ab =>
ArmTroopsCommand.ArmedBattalion(
id = ab.battalionId,
newArmament = ab.battalionTypeId.toDouble
newArmament = ab.newArmament.toDouble
)
},
actingProvince = gameState.provinces(ac.actingProvinceId),
@@ -1,12 +1,13 @@
package net.eagle0.eagle.library.util.command_choice_helpers
import net.eagle0.eagle.{FactionId, HeroId}
import net.eagle0.eagle.api.available_command.{AvailableCommand, DiplomacyAvailableCommand}
import net.eagle0.eagle.api.command.util.diplomacy_option.{AllianceOption, DiplomacyOption}
import net.eagle0.eagle.api.selected_command.DiplomacySelectedCommand
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.ofType
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.{DiplomacyAvailable, DiplomacyOption}
import net.eagle0.eagle.model.state.command.common.DiplomacyOptionType
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.DiplomacySelected
import net.eagle0.eagle.model.state.game_state.GameState
object AllianceOfferCommandSelector {
@@ -16,13 +17,13 @@ object AllianceOfferCommandSelector {
): Vector[HeroId] =
hids.filterNot(FactionUtils.isFactionLeader(_, gameState.factions.values.toVector))
private def allianceOptionWithTarget(
private def hasAllianceOptionWithTarget(
options: Vector[DiplomacyOption],
targetFactionId: FactionId
): Option[AllianceOption] =
options.collectFirst {
case x @ AllianceOption(tfid, _, _ /* unknownFieldSet */ ) if tfid == targetFactionId =>
x
): Boolean =
options.exists { opt =>
opt.targetFactionId == targetFactionId &&
opt.optionTypes.contains(DiplomacyOptionType.Alliance)
}
def chosenAllianceWithFactionCommand(
@@ -30,27 +31,19 @@ object AllianceOfferCommandSelector {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
targetFactionId: FactionId
): Option[CommandSelection] =
ofType[DiplomacyAvailableCommand](availableCommands).find { ac =>
allianceOptionWithTarget(
ac.options.toVector,
targetFactionId
).isDefined && usableHeroIds(
ac.availableHeroIds.toVector,
gameState
).nonEmpty
): Option[ScalaCommandSelection] =
ofType[DiplomacyAvailable](availableCommands).find { ac =>
hasAllianceOptionWithTarget(ac.options, targetFactionId) &&
usableHeroIds(ac.availableHeroIds, gameState).nonEmpty
}.map { ac =>
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = ac.actingProvinceId,
available = ac,
selected = DiplomacySelectedCommand(
selectedOption = allianceOptionWithTarget(
ac.options.toVector,
targetFactionId
).get,
selected = DiplomacySelected(
selectedOption = DiplomacyOptionType.Alliance,
targetFactionId = targetFactionId,
sentHeroId = usableHeroIds(ac.availableHeroIds.toVector, gameState).maxBy { hid =>
sentHeroId = usableHeroIds(ac.availableHeroIds, gameState).maxBy { hid =>
gameState.heroes(hid).vigor
}
),
@@ -1,15 +1,16 @@
package net.eagle0.eagle.library.util.command_choice_helpers
import net.eagle0.eagle.{FactionId, ProvinceId}
import net.eagle0.eagle.api.available_command.{AlmsAvailableCommand, AvailableCommand}
import net.eagle0.eagle.api.selected_command.AlmsSelectedCommand
import net.eagle0.eagle.library.actions.impl.command.AlmsCommand
import net.eagle0.eagle.library.settings.MinSupportForTaxes
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.flatSelectionForType
import net.eagle0.eagle.library.util.food_consumption.FoodConsumptionUtils.foodConsumptionMonthsToHold
import net.eagle0.eagle.library.util.hero.HeroUtils
import net.eagle0.eagle.library.util.province.ProvinceUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.AlmsAvailable
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.AlmsSelected
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.hero.HeroT
import net.eagle0.eagle.model.state.province.ProvinceT
@@ -43,7 +44,7 @@ object AlmsCommandSelector {
actingFactionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand]
): Option[CommandSelection] = chosenAlmsForSupportWithMinimumCommand(
): Option[ScalaCommandSelection] = chosenAlmsForSupportWithMinimumCommand(
actingFactionId = actingFactionId,
gameState = gameState,
availableCommands = availableCommands,
@@ -56,8 +57,8 @@ object AlmsCommandSelector {
availableCommands: Vector[AvailableCommand],
minimumFood: Int,
provinceId: ProvinceId
): Option[CommandSelection] =
flatSelectionForType[AlmsAvailableCommand](availableCommands) { availableCommand =>
): Option[ScalaCommandSelection] =
flatSelectionForType[AlmsAvailable](availableCommands) { availableCommand =>
val province = gameState
.provinces(availableCommand.actingProvinceId)
@@ -77,8 +78,8 @@ object AlmsCommandSelector {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
minimumFood: Int
): Option[CommandSelection] =
flatSelectionForType[AlmsAvailableCommand](availableCommands) { availableCommand =>
): Option[ScalaCommandSelection] =
flatSelectionForType[AlmsAvailable](availableCommands) { availableCommand =>
val province = gameState
.provinces(availableCommand.actingProvinceId)
@@ -94,10 +95,10 @@ object AlmsCommandSelector {
private def maybeAlmsCommandForProvince(
actingFactionId: FactionId,
gameState: GameState,
availableCommand: AlmsAvailableCommand,
availableCommand: AlmsAvailable,
minimumFood: Int,
province: ProvinceT
): Option[CommandSelection] = {
): Option[ScalaCommandSelection] = {
val foodAfterFeedingTroops = surplusFood(province, gameState)
val effectiveMinimumFood = minimumFood.min(foodAfterFeedingTroops)
@@ -139,11 +140,11 @@ object AlmsCommandSelector {
.min(desiredFood, availableCommand.foodAvailable)
.ceil
.toInt
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = availableCommand.actingProvinceId,
available = availableCommand,
selected = AlmsSelectedCommand(
selected = AlmsSelected(
amount = foodToUse,
actingHeroId = bestHero.id
),
@@ -1,16 +1,17 @@
package net.eagle0.eagle.library.util.command_choice_helpers
import net.eagle0.eagle.{FactionId, ProvinceId}
import net.eagle0.eagle.api.available_command.*
import net.eagle0.eagle.api.selected_command.*
import net.eagle0.eagle.common.combat_unit.CombatUnit
import net.eagle0.eagle.library.settings.{
MinVigorForMarch,
RequiredRatioToAttack,
RequiredRatioToAttackWithoutTakingCastles
}
import net.eagle0.eagle.library.util.{BattalionPower, CommandSelection, ShardokMapInfo}
import net.eagle0.eagle.library.util.{BattalionPower, ScalaCommandSelection, ShardokMapInfo}
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.MarchAvailable
import net.eagle0.eagle.model.state.command.selected.{CombatUnit, SelectedCommand}
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.MarchSelected
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.hero.HeroT
import net.eagle0.eagle.model.state.hero.Profession.{Engineer, Mage}
@@ -53,14 +54,14 @@ object AttackCommandChooser {
// this might be a supporting attack
private def chosenAttackOnProvinceCommand(
actingFactionId: FactionId,
ac: MarchAvailableCommand,
ac: MarchAvailable,
selectedUnits: Vector[CombatUnit],
goldAvailable: Int,
foodAvailable: Int,
originProvinceId: ProvinceId,
destinationProvince: ProvinceT,
gameState: GameState
): MarchSelectedCommand = MarchSelectedCommand(
): MarchSelected = MarchSelected(
marchingUnits = selectedUnits,
originProvince = originProvinceId,
destinationProvinceId = destinationProvince.id,
@@ -73,7 +74,7 @@ object AttackCommandChooser {
)
case class CommandInfo(
sc: MarchSelectedCommand,
sc: MarchSelected,
requiresRiverCrossing: Boolean,
totalBattalionPower: Double
)
@@ -91,7 +92,7 @@ object AttackCommandChooser {
def possibleAttackCommands(
actingFactionId: FactionId,
gameState: GameState,
ac: MarchAvailableCommand
ac: MarchAvailable
): Vector[PossibleAttackCommandInfo] =
(for {
opmc <- ac.oneProvinceCommands
@@ -152,9 +153,9 @@ object AttackCommandChooser {
actingFactionId: FactionId,
gameState: GameState,
acs: Vector[AvailableCommand]
): Option[CommandSelection] =
): Option[ScalaCommandSelection] =
// find march commands to an enemy-occupied territory
flatSelectionForType[MarchAvailableCommand](acs) { ac =>
flatSelectionForType[MarchAvailable](acs) { ac =>
possibleAttackCommands(
actingFactionId,
gameState,
@@ -196,7 +197,7 @@ object AttackCommandChooser {
.sum
)
.map { sc =>
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = ac.actingProvinceId,
available = ac,
@@ -212,9 +213,9 @@ object AttackCommandChooser {
acs: Vector[AvailableCommand],
knownEnemyHeroCount: ProvinceId => Int,
knownEnemyBattalions: ProvinceId => Vector[BattalionViewC]
): Option[CommandSelection] =
): Option[ScalaCommandSelection] =
// find march commands to an enemy-occupied territory
flatSelectionForType[MarchAvailableCommand](acs) { ac =>
flatSelectionForType[MarchAvailable](acs) { ac =>
val attackCommands =
possibleAttackCommands(
actingFactionId = actingFactionId,
@@ -309,7 +310,7 @@ object AttackCommandChooser {
}.flatMap { commandInfos =>
commandInfos.maxByOption(_.totalBattalionPower)
}.map { commandInfo =>
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = ac.actingProvinceId,
available = ac,
@@ -2,19 +2,15 @@ package net.eagle0.eagle.library.util.command_choice_helpers
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.common.hostility.Hostility
import net.eagle0.eagle.api.available_command.{AttackDecisionAvailableCommand, AvailableCommand}
import net.eagle0.eagle.api.command.util.attack_decision_type.{
AdvanceDecision,
DemandTributeDecision,
SafePassageDecision,
WithdrawDecision
}
import net.eagle0.eagle.api.selected_command.AttackDecisionSelectedCommand
import net.eagle0.eagle.common.tribute_amount.TributeAmount
import net.eagle0.eagle.library.settings.MinimumPercentageOfEnemyToAttack
import net.eagle0.eagle.library.util.{BattalionPower, CommandSelection, IncomingArmyUtils}
import net.eagle0.eagle.library.util.{BattalionPower, IncomingArmyUtils, ScalaCommandSelection}
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.flatSelectionForType
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChooserImplicits.*
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.AttackDecisionAvailable
import net.eagle0.eagle.model.state.command.common.AttackDecisionType
import net.eagle0.eagle.model.state.command.common.AttackDecisionType.{Advance, DemandTribute, SafePassage, Withdraw}
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.AttackDecisionSelected
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.FactionId
@@ -24,7 +20,7 @@ object AttackDecisionCommandChooser {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
): RandomState[Option[ScalaCommandSelection]] =
CommandChooser.choose(
actingFactionId = actingFactionId,
gameState = gameState,
@@ -74,22 +70,22 @@ object AttackDecisionCommandChooser {
actingFactionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand]
): Option[CommandSelection] =
flatSelectionForType[AttackDecisionAvailableCommand](availableCommands) { withdrawAc =>
): Option[ScalaCommandSelection] =
flatSelectionForType[AttackDecisionAvailable](availableCommands) { withdrawAc =>
Option.when(
withdrawAc.availableDecisions
.contains(WithdrawDecision()) &&
.contains(Withdraw) &&
(!okToAttack(
withdrawAc,
gameState
) || withdrawAc.availableDecisions.size == 1)
)(
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = withdrawAc.actingProvinceId,
available = withdrawAc,
selected = AttackDecisionSelectedCommand(
decision = WithdrawDecision()
selected = AttackDecisionSelected(
decision = Withdraw
),
reason = "withdrawArmySelectedCommand"
)
@@ -100,35 +96,38 @@ object AttackDecisionCommandChooser {
actingFactionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand]
): Option[CommandSelection] =
flatSelectionForType[AttackDecisionAvailableCommand](availableCommands) { availableCommand =>
): Option[ScalaCommandSelection] =
flatSelectionForType[AttackDecisionAvailable](availableCommands) { availableCommand =>
availableCommand.availableDecisions.collectFirst {
case DemandTributeDecision(maxTribute, _ /* unknownFieldSet */ ) =>
tributeToDemand(maxTribute.get).flatMap { tribute =>
Option.when(
(okToAttack(
availableCommand,
gameState
) || availableCommand.availableDecisions.size == 1) &&
availableCommand.availableDecisions
.contains(DemandTributeDecision())
)(
CommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = availableCommand.actingProvinceId,
available = availableCommand,
selected = AttackDecisionSelectedCommand(
decision = DemandTributeDecision(Some(tribute))
),
reason = "demandTributeSelectedCommand"
case dt @ DemandTribute(maxGold, maxFood) =>
tributeToDemand(maxGold, maxFood).flatMap {
case (demandedGold, demandedFood) =>
val hasDemandTributeOption = availableCommand.availableDecisions.exists {
case DemandTribute(_, _) => true
case _ => false
}
Option.when(
(okToAttack(
availableCommand,
gameState
) || availableCommand.availableDecisions.size == 1) && hasDemandTributeOption
)(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = availableCommand.actingProvinceId,
available = availableCommand,
selected = AttackDecisionSelected(
decision = DemandTribute(demandedGold, demandedFood)
),
reason = "demandTributeSelectedCommand"
)
)
)
}
}.flatten
}
private def okToAttack(
availableCommand: AttackDecisionAvailableCommand,
availableCommand: AttackDecisionAvailable,
gameState: GameState
): Boolean = {
val armies = availableCommand.armies
@@ -158,29 +157,26 @@ object AttackDecisionCommandChooser {
}
private def tributeToDemand(
maxTribute: TributeAmount
): Option[TributeAmount] =
Option.when(2 * maxTribute.gold + maxTribute.food > 4000) {
val goldDemanded = 0.max(maxTribute.gold - 200)
val foodDemanded = 0.max(maxTribute.food - 400)
maxGold: Int,
maxFood: Int
): Option[(Int, Int)] =
Option.when(2 * maxGold + maxFood > 4000) {
val goldDemanded = 0.max(maxGold - 200)
val foodDemanded = 0.max(maxFood - 400)
if goldDemanded == 0 && foodDemanded == 0 then tributeToDemand(maxTribute).get
else
TributeAmount(
gold = goldDemanded,
food = foodDemanded
)
if goldDemanded == 0 && foodDemanded == 0 then tributeToDemand(maxGold, maxFood).get
else (goldDemanded, foodDemanded)
}
private def advanceArmySelectedCommand(
actingFactionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand]
): Option[CommandSelection] =
flatSelectionForType[AttackDecisionAvailableCommand](availableCommands) { availableCommand =>
): Option[ScalaCommandSelection] =
flatSelectionForType[AttackDecisionAvailable](availableCommands) { availableCommand =>
Option.when(
availableCommand.availableDecisions
.contains(AdvanceDecision()) && (
.contains(Advance) && (
availableCommand.availableDecisions.size == 1 ||
okToAttack(
availableCommand,
@@ -188,12 +184,12 @@ object AttackDecisionCommandChooser {
)
)
) {
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
available = availableCommand,
actingProvinceId = availableCommand.actingProvinceId,
selected = AttackDecisionSelectedCommand(
decision = AdvanceDecision()
selected = AttackDecisionSelected(
decision = Advance
),
reason = "advanceArmySelectedCommand"
)
@@ -204,15 +200,15 @@ object AttackDecisionCommandChooser {
actingFactionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand]
): Option[CommandSelection] =
flatSelectionForType[AttackDecisionAvailableCommand](availableCommands) { availableCommand =>
): Option[ScalaCommandSelection] =
flatSelectionForType[AttackDecisionAvailable](availableCommands) { availableCommand =>
availableCommand.availableDecisions.collectFirst {
case spd: SafePassageDecision =>
CommandSelection(
case spd @ SafePassage(_) =>
ScalaCommandSelection(
actingFactionId = actingFactionId,
available = availableCommand,
actingProvinceId = availableCommand.actingProvinceId,
selected = AttackDecisionSelectedCommand(
selected = AttackDecisionSelected(
decision = spd
),
reason = "safePassageSelectedCommand"
@@ -3,8 +3,8 @@ package net.eagle0.eagle.library.util.command_choice_helpers
import scala.reflect.ClassTag
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.api.available_command.AvailableCommand
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
object AvailableCommandSelector {
def ofType[T <: AvailableCommand: ClassTag](
@@ -17,20 +17,20 @@ object AvailableCommandSelector {
def selectionForType[T <: AvailableCommand: ClassTag](
acs: Vector[AvailableCommand]
)(f: T => CommandSelection): Option[CommandSelection] =
)(f: T => ScalaCommandSelection): Option[ScalaCommandSelection] =
acs.collectFirst { case ac: T => f(ac) }
def flatSelectionForType[T <: AvailableCommand: ClassTag](
acs: Vector[AvailableCommand]
)(f: T => Option[CommandSelection]): Option[CommandSelection] =
)(f: T => Option[ScalaCommandSelection]): Option[ScalaCommandSelection] =
ofType(acs).flatMap(f).headOption
def randomSelectionForType[T <: AvailableCommand: ClassTag](
acs: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
)(
f: (T, FunctionalRandom) => RandomState[Option[CommandSelection]]
): RandomState[Option[CommandSelection]] =
f: (T, FunctionalRandom) => RandomState[Option[ScalaCommandSelection]]
): RandomState[Option[ScalaCommandSelection]] =
functionalRandom.nextFlatCollectFirst(acs) {
case ac: T =>
fr => f(ac, fr)
@@ -9,11 +9,12 @@ scala_library(
],
deps = [
":available_command_selector",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/common",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
],
)
@@ -32,8 +33,6 @@ scala_library(
],
deps = [
":available_command_selector",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/actions/impl/command:alms_command",
"//src/main/scala/net/eagle0/eagle/library/settings:min_support_for_taxes",
@@ -41,6 +40,8 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/util/food_consumption",
"//src/main/scala/net/eagle0/eagle/library/util/hero:hero_utils",
"//src/main/scala/net/eagle0/eagle/library/util/province",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/hero",
"//src/main/scala/net/eagle0/eagle/model/state/province",
@@ -61,8 +62,6 @@ scala_library(
":available_command_selector",
":combat_unit_selector",
":march_supplies_helpers",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/settings:min_vigor_for_march",
"//src/main/scala/net/eagle0/eagle/library/settings:required_ratio_to_attack",
@@ -71,6 +70,8 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util:shardok_map_info",
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/hero",
"//src/main/scala/net/eagle0/eagle/model/state/province",
@@ -91,14 +92,15 @@ scala_library(
deps = [
":available_command_selector",
":command_chooser",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/settings:minimum_percentage_of_enemy_to_attack",
"//src/main/scala/net/eagle0/eagle/library/util:battalion_power",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util:incoming_army_utils",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/common",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
],
)
@@ -111,9 +113,9 @@ scala_library(
"//src/test/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
],
deps = [
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
],
)
@@ -125,14 +127,17 @@ scala_library(
"//src/main/scala/net/eagle0/util:__pkg__",
"//src/test/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
],
exports = [
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
],
deps = [
"//src/main/protobuf/net/eagle0/eagle/common:combat_unit_scala_proto",
"//src/main/scala/net/eagle0/eagle/library/util:battalion_power",
"//src/main/scala/net/eagle0/eagle/library/util:eagle_require",
"//src/main/scala/net/eagle0/eagle/library/util/battalion_suitability",
"//src/main/scala/net/eagle0/eagle/library/util/hero:hero_utils",
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/hero",
"//src/main/scala/net/eagle0/eagle/model/state/hero:gender",
],
@@ -150,10 +155,10 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
],
deps = [
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
],
)
@@ -168,9 +173,10 @@ scala_library(
"//src/test/scala/net/eagle0/eagle:__subpackages__",
],
exports = [
"//src/main/protobuf/net/eagle0/eagle/common:combat_unit_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
],
deps = [
":alms_command_selector",
@@ -186,11 +192,6 @@ scala_library(
":organize_command_selector",
":province_gold_surplus_calculator",
":ransom_offer_helpers",
"//src/main/protobuf/net/eagle0/common:hostility_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api/command/util:armed_battalion_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api/command/util:battalion_with_food_cost_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/common:more_option",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
@@ -219,6 +220,9 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type_id",
"//src/main/scala/net/eagle0/eagle/model/state:round_phase",
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/common",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/date",
"//src/main/scala/net/eagle0/eagle/model/state/faction",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
@@ -238,10 +242,10 @@ scala_library(
],
deps = [
":available_command_selector",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
],
)
@@ -259,8 +263,6 @@ scala_library(
deps = [
":available_command_selector",
":province_gold_surplus_calculator",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/settings:max_combat_unit_count_per_side",
@@ -270,6 +272,8 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
"//src/main/scala/net/eagle0/eagle/library/util/food_consumption",
"//src/main/scala/net/eagle0/eagle/library/util/province",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/province",
],
@@ -288,7 +292,6 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
],
deps = [
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
@@ -304,6 +307,7 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers/quest_command_selectors:truce_with_faction_quest_command_chooser",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers/quest_command_selectors:unaffiliated_hero_with_quest",
"//src/main/scala/net/eagle0/eagle/library/util/province",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/unaffiliated_hero",
],
@@ -319,10 +323,10 @@ scala_library(
],
deps = [
":available_command_selector",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
],
)
@@ -354,11 +358,12 @@ scala_library(
deps = [
":available_command_selector",
":hero_selector",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/hero:hero_utils",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/common",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/province",
],
@@ -392,14 +397,13 @@ scala_library(
deps = [
":available_command_selector",
":province_gold_surplus_calculator",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/common:battalion_type_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/battalion_type_finder",
"//src/main/scala/net/eagle0/eagle/library/util/province",
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
],
)
@@ -451,11 +455,11 @@ scala_library(
"//src/test/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
],
deps = [
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:available_command_selector",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
],
)
@@ -484,11 +488,12 @@ scala_library(
],
deps = [
":available_command_selector",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/common",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
],
)
@@ -2,12 +2,12 @@ package net.eagle0.eagle.library.util.command_choice_helpers
import scala.annotation.tailrec
import net.eagle0.eagle.common.combat_unit.CombatUnit
import net.eagle0.eagle.library.util.battalion_suitability.{BattalionSuitability, SuitabilityLevel}
import net.eagle0.eagle.library.util.hero.HeroUtils
import net.eagle0.eagle.library.util.BattalionPower
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
import net.eagle0.eagle.model.state.battalion.BattalionT
import net.eagle0.eagle.model.state.command.selected.CombatUnit
import net.eagle0.eagle.model.state.hero.HeroT
import net.eagle0.eagle.model.state.BattalionType
@@ -39,15 +39,13 @@ object CombatUnitSelector {
val bestHero = hs.minBy(h => (suitability(h, bestBattalion).ordinal, -HeroUtils.power(h)))
CombatUnit(
heroId = bestHero.id,
battalionId = Some(bestBattalion.id),
factionId = bestHero.factionId.get
battalionId = Some(bestBattalion.id)
)
}
.getOrElse(
CombatUnit(
heroId = hs.maxBy(HeroUtils.power).id,
battalionId = bs.maxByOption(BattalionPower.power).map(_.id),
factionId = hs.head.factionId.get
battalionId = bs.maxByOption(BattalionPower.power).map(_.id)
)
)
}
@@ -3,8 +3,8 @@ package net.eagle0.eagle.library.util.command_choice_helpers
import scala.annotation.tailrec
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.api.available_command.AvailableCommand
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.FactionId
@@ -15,7 +15,7 @@ trait CommandChooser {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]]
): RandomState[Option[ScalaCommandSelection]]
}
object CommandChooser {
@@ -26,7 +26,7 @@ object CommandChooser {
availableCommands: Vector[AvailableCommand],
rankedChoosers: Vector[CommandChooser],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] = rankedChoosers match {
): RandomState[Option[ScalaCommandSelection]] = rankedChoosers match {
case h +: t =>
h(actingFactionId, gameState, availableCommands, functionalRandom) match {
case rss @ RandomState(Some(_), _) => rss
@@ -43,14 +43,14 @@ object CommandChooserImplicits {
FactionId,
GameState,
Vector[AvailableCommand]
) => Option[CommandSelection]
) => Option[ScalaCommandSelection]
) extends CommandChooser {
override def apply(
factionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
): RandomState[Option[ScalaCommandSelection]] =
RandomState(
deterministicChooser(factionId, gameState, availableCommands),
functionalRandom
@@ -63,14 +63,14 @@ object CommandChooserImplicits {
GameState,
Vector[AvailableCommand],
FunctionalRandom
) => RandomState[Option[CommandSelection]]
) => RandomState[Option[ScalaCommandSelection]]
) extends CommandChooser {
override def apply(
factionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
): RandomState[Option[ScalaCommandSelection]] =
randomChooser(factionId, gameState, availableCommands, functionalRandom)
}
}
@@ -1,10 +1,11 @@
package net.eagle0.eagle.library.util.command_choice_helpers
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.api.available_command.{AvailableCommand, ExileVassalAvailableCommand}
import net.eagle0.eagle.api.selected_command.ExileVassalSelectedCommand
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.ofType
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.ExileVassalAvailable
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.ExileVassalSelected
object ExileVassalCommandSelector {
def chosenSpecificDismissVassalCommandForProvince(
@@ -13,20 +14,20 @@ object ExileVassalCommandSelector {
availableCommands: Vector[AvailableCommand],
dismissedHeroId: HeroId,
reason: String
): Option[CommandSelection] =
ofType[ExileVassalAvailableCommand](availableCommands)
.find(exileVassalCommand =>
exileVassalCommand.exilableHeroIds
): Option[ScalaCommandSelection] =
ofType[ExileVassalAvailable](availableCommands)
.find(exileVassalAvailable =>
exileVassalAvailable.exilableHeroIds
.contains(
dismissedHeroId
) && exileVassalCommand.actingProvinceId == actingProvinceId
) && exileVassalAvailable.actingProvinceId == actingProvinceId
)
.map { exileVassalCommand =>
CommandSelection(
.map { exileVassalAvailable =>
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = actingProvinceId,
available = exileVassalCommand,
selected = ExileVassalSelectedCommand(exiledHeroId = dismissedHeroId),
available = exileVassalAvailable,
selected = ExileVassalSelected(exiledHeroId = dismissedHeroId),
reason = reason
)
}
@@ -4,16 +4,17 @@ import scala.annotation.tailrec
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.api.available_command.{AvailableCommand, MarchAvailableCommand, MarchCommandFromOneProvince}
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
import net.eagle0.eagle.common.combat_unit.CombatUnit
import net.eagle0.eagle.library.settings.MaxCombatUnitCountPerSide
import net.eagle0.eagle.library.util.{BattalionUtils, CommandSelection}
import net.eagle0.eagle.library.util.{BattalionUtils, ScalaCommandSelection}
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.randomSelectionForType
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
import net.eagle0.eagle.library.util.food_consumption.FoodConsumptionUtils.foodConsumptionMonthsToHold
import net.eagle0.eagle.library.util.province.ProvinceUtils
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.{MarchAvailable, MarchCommandFromOneProvince}
import net.eagle0.eagle.model.state.command.selected.{CombatUnit, SelectedCommand}
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.MarchSelected
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.province.ProvinceT
@@ -25,8 +26,8 @@ object ExpandCommandSelector {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
randomSelectionForType[MarchAvailableCommand](
): RandomState[Option[ScalaCommandSelection]] =
randomSelectionForType[MarchAvailable](
availableCommands,
functionalRandom
) {
@@ -107,11 +108,11 @@ object ExpandCommandSelector {
private def safeExpansionSelectedCommand(
actingFactionId: FactionId,
gameState: GameState,
availableCommand: MarchAvailableCommand,
availableCommand: MarchAvailable,
oneProvinceCommand: MarchCommandFromOneProvince,
safeExpansionCandidateProvinces: Vector[ProvinceT],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] = {
): RandomState[Option[ScalaCommandSelection]] = {
val province = gameState.provinces(availableCommand.actingProvinceId)
val rulingFactionHeroIds = province.rulingFactionHeroIds
@@ -151,11 +152,11 @@ object ExpandCommandSelector {
private def expansionAdjacentToEnemySelectedCommand(
actingFactionId: FactionId,
gameState: GameState,
availableCommand: MarchAvailableCommand,
availableCommand: MarchAvailable,
oneProvinceCommand: MarchCommandFromOneProvince,
adjacentEnemyCandidateProvinces: Vector[ProvinceT],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] = {
): RandomState[Option[ScalaCommandSelection]] = {
val province = gameState.provinces(availableCommand.actingProvinceId)
val rulingFactionHeroIds = province.rulingFactionHeroIds
@@ -201,7 +202,6 @@ object ExpandCommandSelector {
opmc: MarchCommandFromOneProvince,
heroCount: Int,
battalionCount: Int,
fid: FactionId,
factionLeaders: Vector[HeroId],
otherCountLeftBehind: Int,
functionalRandom: FunctionalRandom
@@ -223,7 +223,7 @@ object ExpandCommandSelector {
.map(_.take(battalionCount))
.map { battalionIdsWithFoodCosts =>
val food =
battalionIdsWithFoodCosts.map(_.monthlyFood).sum.ceil.toInt * 2
battalionIdsWithFoodCosts.map(_.foodCost).sum * 2
val battalionIds =
battalionIdsWithFoodCosts.map(_.battalionId).sorted.map(Option(_))
@@ -233,8 +233,7 @@ object ExpandCommandSelector {
.map { pair =>
CombatUnit(
heroId = pair._1,
battalionId = pair._2,
factionId = fid
battalionId = pair._2
)
}
@@ -250,10 +249,10 @@ object ExpandCommandSelector {
originatingProvince: ProvinceT,
candidateDestinationProvinces: Vector[ProvinceT],
oneProvinceCommand: MarchCommandFromOneProvince,
availableCommand: MarchAvailableCommand,
availableCommand: MarchAvailable,
gameState: GameState,
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] = {
): RandomState[Option[ScalaCommandSelection]] = {
val rulingFactionHeroIds = originatingProvince.rulingFactionHeroIds
val randomNone = RandomState(None, functionalRandom)
@@ -290,7 +289,6 @@ object ExpandCommandSelector {
opmc = oneProvinceCommand,
heroCount = heroCountToSend,
battalionCount = battalionCountToSend.min(heroCountToSend),
fid = actingFactionId,
factionLeaders = gameState
.factions(actingFactionId)
.leaderIds
@@ -353,11 +351,11 @@ object ExpandCommandSelector {
)
Option.when(minFoodToSend <= foodAvailableToSend) {
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = availableCommand.actingProvinceId,
available = availableCommand,
selected = MarchSelectedCommand(
selected = MarchSelected(
marchingUnits = marchingUnitsWithFood.units,
destinationProvinceId = chosenCandidateProvince.id,
originProvince = oneProvinceCommand.originProvinceId,
@@ -376,9 +374,9 @@ object ExpandCommandSelector {
private def spreadHeroesCommand(
actingFactionId: FactionId,
gameState: GameState,
availableCommand: MarchAvailableCommand,
availableCommand: MarchAvailable,
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
): RandomState[Option[ScalaCommandSelection]] =
availableCommand.oneProvinceCommands.flatMap { opmc =>
spreadHeroesMarchCandidates(
gameState,
@@ -415,17 +413,16 @@ object ExpandCommandSelector {
opmc = opmc,
heroCount = heroCountToSend,
battalionCount = battalionCountToSend,
fid = actingFactionId,
factionLeaders = gameState.factions(actingFactionId).leaderIds,
otherCountLeftBehind = originProvince.rulingFactionHeroIds.length - opmc.availableHeroIds.length,
functionalRandom = functionalRandom
).map { marchingUnitsWithFood =>
Some(
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = availableCommand.actingProvinceId,
available = availableCommand,
selected = MarchSelectedCommand(
selected = MarchSelected(
marchingUnits = marchingUnitsWithFood.units,
destinationProvinceId = destinationProvince.id,
originProvince = opmc.originProvinceId,
@@ -1,7 +1,6 @@
package net.eagle0.eagle.library.util.command_choice_helpers
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.api.available_command.AvailableCommand
import net.eagle0.eagle.library.util.command_choice_helpers.quest_command_selectors.{
AllianceQuestCommandChooser,
AlmsAcrossRealmQuestCommandChooser,
@@ -16,7 +15,8 @@ import net.eagle0.eagle.library.util.command_choice_helpers.quest_command_select
UnaffiliatedHeroWithQuest
}
import net.eagle0.eagle.library.util.province.ProvinceUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.FactionId
@@ -40,7 +40,7 @@ object FulfillQuestsCommandSelector {
availableCommands: Vector[AvailableCommand],
uhsWithQuests: Vector[UnaffiliatedHeroWithQuest],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
): RandomState[Option[ScalaCommandSelection]] =
functionalRandom.nextFlatCollectFirst(choosers) {
case chooser =>
fr =>
@@ -60,7 +60,7 @@ object FulfillQuestsCommandSelector {
availableCommands: Vector[AvailableCommand],
uhsWithQuests: Vector[UnaffiliatedHeroWithQuest],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
): RandomState[Option[ScalaCommandSelection]] =
choiceFrom(
choosers = choosers,
actingFactionId = actingFactionId,
@@ -76,7 +76,7 @@ object FulfillQuestsCommandSelector {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] = {
): RandomState[Option[ScalaCommandSelection]] = {
val myProvinces = gameState.provinces.values
.filter(_.rulingFactionId.contains(actingFactionId))
.filter(province => ProvinceUtils.ruledByFactionLeader(province, gameState.factions.values.toVector))
@@ -1,10 +1,11 @@
package net.eagle0.eagle.library.util.command_choice_helpers
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.api.available_command.{AvailableCommand, HeroGiftAvailableCommand}
import net.eagle0.eagle.api.selected_command.HeroGiftSelectedCommand
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.flatSelectionForType
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.HeroGiftAvailable
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.HeroGiftSelected
import net.eagle0.eagle.model.state.game_state.GameState
object HeroGiftCommandSelector {
@@ -14,17 +15,17 @@ object HeroGiftCommandSelector {
availableCommands: Vector[AvailableCommand],
provinceId: ProvinceId,
maxAmount: Int
): Option[CommandSelection] =
flatSelectionForType[HeroGiftAvailableCommand](availableCommands) { heroGiftAvailableCommand =>
): Option[ScalaCommandSelection] =
flatSelectionForType[HeroGiftAvailable](availableCommands) { heroGiftAvailableCommand =>
heroGiftAvailableCommand.eligibleGifts
.filter(_.recipientProvinceId == provinceId)
.minByOption(eligibleGift => gameState.heroes(eligibleGift.recipientHeroId).loyalty)
.map { eligibleGift =>
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
available = heroGiftAvailableCommand,
actingProvinceId = heroGiftAvailableCommand.actingProvinceId,
selected = HeroGiftSelectedCommand(
selected = HeroGiftSelected(
recipientHeroId = eligibleGift.recipientHeroId,
amount = eligibleGift.goldAvailable.min(maxAmount)
),
@@ -38,16 +39,16 @@ object HeroGiftCommandSelector {
availableCommands: Vector[AvailableCommand],
heroId: HeroId,
reason: String
): Option[CommandSelection] =
flatSelectionForType[HeroGiftAvailableCommand](availableCommands) { heroGiftAvailableCommand =>
): Option[ScalaCommandSelection] =
flatSelectionForType[HeroGiftAvailable](availableCommands) { heroGiftAvailableCommand =>
heroGiftAvailableCommand.eligibleGifts
.find(_.recipientHeroId == heroId)
.map { eligibleGift =>
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
available = heroGiftAvailableCommand,
actingProvinceId = heroGiftAvailableCommand.actingProvinceId,
selected = HeroGiftSelectedCommand(
selected = HeroGiftSelected(
recipientHeroId = heroId,
amount = eligibleGift.goldAvailable
),
@@ -1,16 +1,17 @@
package net.eagle0.eagle.library.util.command_choice_helpers
import net.eagle0.eagle.{FactionId, ProvinceId}
import net.eagle0.eagle.api.available_command.{AvailableCommand, ImproveAvailableCommand}
import net.eagle0.eagle.api.selected_command.ImproveSelectedCommand
import net.eagle0.eagle.common.improvement_type.ImprovementType
import net.eagle0.eagle.common.improvement_type.ImprovementType.DEVASTATION
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.{
flatSelectionForType,
selectionForType
}
import net.eagle0.eagle.library.util.hero.HeroUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.ImproveAvailable
import net.eagle0.eagle.model.state.command.common.ImprovementType
import net.eagle0.eagle.model.state.command.common.ImprovementType.Devastation
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.ImproveSelected
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.province.ProvinceT
@@ -21,19 +22,18 @@ object ImproveCommandSelector {
improvementType: ImprovementType,
province: ProvinceT
): Double = improvementType match {
case ImprovementType.ECONOMY => province.economy
case ImprovementType.AGRICULTURE => province.agriculture
case ImprovementType.INFRASTRUCTURE => province.infrastructure
case ImprovementType.DEVASTATION =>
case ImprovementType.Economy => province.economy
case ImprovementType.Agriculture => province.agriculture
case ImprovementType.Infrastructure => province.infrastructure
case ImprovementType.Devastation =>
province.economyDevastation + province.agricultureDevastation + province.infrastructureDevastation
case _ => throw new IllegalArgumentException("Illegal improvement type")
}
private def chosenType(
availableTypes: Vector[ImprovementType],
province: ProvinceT
): ImprovementType =
if availableTypes.contains(DEVASTATION) then DEVASTATION
if availableTypes.contains(Devastation) then Devastation
else availableTypes.minBy(provinceValueForType(_, province))
def chosenSpecificImprovementCommandForProvince(
@@ -41,20 +41,21 @@ object ImproveCommandSelector {
actingProvinceId: ProvinceId,
availableCommands: Vector[AvailableCommand],
improvementType: ImprovementType
): Option[CommandSelection] =
flatSelectionForType[ImproveAvailableCommand](availableCommands) { improveCommand =>
): Option[ScalaCommandSelection] =
flatSelectionForType[ImproveAvailable](availableCommands) { improveCommand =>
val actingPid = improveCommand.actingProvinceId
Option.when(
actingPid == actingProvinceId && improveCommand.availableTypes
.contains(improvementType)
) {
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = actingPid,
available = improveCommand,
selected = ImproveSelectedCommand(
selected = ImproveSelected(
improvementType = improvementType,
actingHeroId = improveCommand.recommendedHeroId
actingHeroId = improveCommand.recommendedHeroId,
lockType = false
),
reason = "chosenSpecificImprovementCommandForProvince"
)
@@ -66,21 +67,22 @@ object ImproveCommandSelector {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
improvementType: ImprovementType
): Option[CommandSelection] =
flatSelectionForType[ImproveAvailableCommand](availableCommands) { improveCommand =>
): Option[ScalaCommandSelection] =
flatSelectionForType[ImproveAvailable](availableCommands) { improveCommand =>
val actingPid = improveCommand.actingProvinceId
Option.when(improveCommand.availableTypes.contains(improvementType)) {
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = actingPid,
available = improveCommand,
selected = ImproveSelectedCommand(
selected = ImproveSelected(
improvementType = improvementType,
actingHeroId = minimallyFatiguedHeroes(
improveCommand.availableHeroIds.toVector.map(
gameState.heroes(_)
)
).maxBy(HeroUtils.power(_)).id
).maxBy(HeroUtils.power(_)).id,
lockType = false
),
reason = "chosenSpecificImprovementCommand"
)
@@ -90,13 +92,13 @@ object ImproveCommandSelector {
def commandSelection(
gameState: GameState,
actingFactionId: FactionId,
improveAvailableCommand: ImproveAvailableCommand
): CommandSelection =
CommandSelection(
improveAvailableCommand: ImproveAvailable
): ScalaCommandSelection =
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = improveAvailableCommand.actingProvinceId,
available = improveAvailableCommand,
selected = ImproveSelectedCommand(
selected = ImproveSelected(
improvementType = chosenType(
improveAvailableCommand.availableTypes.toVector,
gameState.provinces(improveAvailableCommand.actingProvinceId)
@@ -105,7 +107,8 @@ object ImproveCommandSelector {
improveAvailableCommand.availableHeroIds.toVector.map(
gameState.heroes(_)
)
).maxBy(HeroUtils.power(_)).id
).maxBy(HeroUtils.power(_)).id,
lockType = false
),
reason = "ImproveCommandSelector.commandSelection"
)
@@ -114,8 +117,8 @@ object ImproveCommandSelector {
actingFactionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand]
): Option[CommandSelection] =
selectionForType[ImproveAvailableCommand](availableCommands) { improveCommand =>
): Option[ScalaCommandSelection] =
selectionForType[ImproveAvailable](availableCommands) { improveCommand =>
commandSelection(gameState, actingFactionId, improveCommand)
}
}
@@ -2,25 +2,22 @@ package net.eagle0.eagle.library.util.command_choice_helpers
import scala.annotation.tailrec
import net.eagle0.eagle.api.available_command.{AvailableCommand, OrganizeTroopsAvailableCommand}
import net.eagle0.eagle.api.selected_command.OrganizeTroopsSelectedCommand
import net.eagle0.eagle.api.selected_command.OrganizeTroopsSelectedCommand.{ChangedBattalion, NewBattalion}
import net.eagle0.eagle.common.battalion_type.BattalionTypeId as ProtoBattalionTypeId
import net.eagle0.eagle.library.util.battalion_type_finder.BattalionTypeFinder
import net.eagle0.eagle.library.util.battalion_type_finder.BattalionTypeFinder.battalionType
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.flatSelectionForType
import net.eagle0.eagle.library.util.command_choice_helpers.ProvinceGoldSurplusCalculator
import net.eagle0.eagle.library.util.province.ProvinceUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.{BattalionType, BattalionTypeId}
import net.eagle0.eagle.model.state.battalion.BattalionT
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.OrganizeTroopsAvailable
import net.eagle0.eagle.model.state.command.selected.{ChangedBattalion, NewBattalion}
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.OrganizeTroopsSelected
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.FactionId
object OrganizeCommandSelector {
// Helper to convert Scala BattalionTypeId to proto BattalionTypeId
private def toProtoBattalionTypeId(typeId: BattalionTypeId): ProtoBattalionTypeId =
ProtoBattalionTypeId.fromValue(typeId.value)
case class ChangedBattalionsResult(
changedBattalions: Vector[ChangedBattalion],
@@ -62,7 +59,12 @@ object OrganizeCommandSelector {
gold - goldCost,
foodSurplus - foodCost,
bs,
ChangedBattalion(id = b.id, newTroops = missing) +: acc
ChangedBattalion(
id = b.id,
troopsFromOtherBattalion = Vector(),
newTroops = missing,
dismissedTroops = 0
) +: acc
)
else
ChangedBattalionsResult(
@@ -70,6 +72,7 @@ object OrganizeCommandSelector {
remainingGold = gold,
remainingFoodSurplus = foodSurplus
)
end if
case _ =>
ChangedBattalionsResult(
@@ -148,7 +151,8 @@ object OrganizeCommandSelector {
.headOption match {
case Some(x) =>
val newBattalion = NewBattalion(
`type` = toProtoBattalionTypeId(x.battalionTypeId),
typeId = x.battalionTypeId.value,
troopsFromOtherBattalion = Vector(),
newTroops = battalionType(x.battalionTypeId, allBattalionTypes).capacity
)
go(
@@ -178,25 +182,24 @@ object OrganizeCommandSelector {
def selectedOrganizeCommand(
actingFactionId: FactionId,
availableCommand: OrganizeTroopsAvailableCommand,
availableCommand: OrganizeTroopsAvailable,
gameState: GameState,
extraFoodSurplus: Int,
reason: String
): Option[CommandSelection] = {
): Option[ScalaCommandSelection] = {
val province = gameState.provinces(availableCommand.actingProvinceId)
val foodSurplus =
ProvinceUtils.monthlyFoodSurplus(province, gameState) + extraFoodSurplus
val goldSurplus = ProvinceGoldSurplusCalculator.provinceGoldSurplus(province)
if foodSurplus <= 0 || goldSurplus <= 0 then None
else {
// Convert proto BattalionTypeId to Scala BattalionTypeId
val costs = availableCommand.troopCosts
.map(tc => BattalionTypeId.fromInt(tc.`type`.value) -> tc.costPerTroop)
.map(tc => BattalionTypeId.fromInt(tc.battalionTypeId) -> tc.costPerTroop)
.toMap
val eligibleTypeIds = availableCommand.availableBattalionTypes
.filter(_.meetsRequirements)
.toVector
.map(abt => BattalionTypeId.fromInt(abt.typeId.value))
.map(abt => BattalionTypeId.fromInt(abt.typeId))
val toppedBattalionResults = changedBattalions(
gold = goldSurplus,
@@ -222,11 +225,11 @@ object OrganizeCommandSelector {
Option.when(
toppedBattalionResults.changedBattalions.nonEmpty || newBattalionResults.newBattalions.nonEmpty
) {
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = availableCommand.actingProvinceId,
available = availableCommand,
selected = OrganizeTroopsSelectedCommand(
selected = OrganizeTroopsSelected(
changedBattalions = toppedBattalionResults.changedBattalions,
newBattalions = newBattalionResults.newBattalions
),
@@ -243,8 +246,8 @@ object OrganizeCommandSelector {
availableCommands: Vector[AvailableCommand],
extraFoodSurplus: Int,
reason: String
): Option[CommandSelection] =
flatSelectionForType[OrganizeTroopsAvailableCommand](availableCommands) { availableCommand =>
): Option[ScalaCommandSelection] =
flatSelectionForType[OrganizeTroopsAvailable](availableCommands) { availableCommand =>
selectedOrganizeCommand(
actingFactionId = actingFactionId,
availableCommand = availableCommand,
@@ -259,7 +262,7 @@ object OrganizeCommandSelector {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
reason: String
): Option[CommandSelection] = chosenOrganizeCommandWithFoodSurplus(
): Option[ScalaCommandSelection] = chosenOrganizeCommandWithFoodSurplus(
actingFactionId = actingFactionId,
gameState = gameState,
availableCommands = availableCommands,
@@ -1,28 +1,29 @@
package net.eagle0.eagle.library.util.command_choice_helpers
import net.eagle0.eagle.{FactionId, HeroId}
import net.eagle0.eagle.api.available_command.{AvailableCommand, SwearBrotherhoodAvailableCommand}
import net.eagle0.eagle.api.selected_command.SwearBrotherhoodSelectedCommand
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.flatSelectionForType
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.SwearBrotherhoodAvailable
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.SwearBrotherhoodSelected
object SwearBrotherhoodCommandSelector {
def chosenCommandForSpecificHero(
actingFactionId: FactionId,
availableCommands: Vector[AvailableCommand],
desiredHeroId: HeroId
): Option[CommandSelection] =
flatSelectionForType[SwearBrotherhoodAvailableCommand](availableCommands) { swearBrotherhoodAvailableCommand =>
): Option[ScalaCommandSelection] =
flatSelectionForType[SwearBrotherhoodAvailable](availableCommands) { swearBrotherhoodAvailable =>
Option.when(
swearBrotherhoodAvailableCommand.availableHeroes
swearBrotherhoodAvailable.availableHeroes
.map(_.heroId)
.contains(desiredHeroId)
) {
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = swearBrotherhoodAvailableCommand.actingProvinceId,
available = swearBrotherhoodAvailableCommand,
selected = SwearBrotherhoodSelectedCommand(newBrotherHeroId = desiredHeroId),
actingProvinceId = swearBrotherhoodAvailable.actingProvinceId,
available = swearBrotherhoodAvailable,
selected = SwearBrotherhoodSelected(newBrotherHeroId = desiredHeroId),
reason = "chose swear brotherhood"
)
}
@@ -1,12 +1,13 @@
package net.eagle0.eagle.library.util.command_choice_helpers
import net.eagle0.eagle.{FactionId, HeroId}
import net.eagle0.eagle.api.available_command.{AvailableCommand, DiplomacyAvailableCommand}
import net.eagle0.eagle.api.command.util.diplomacy_option.{DiplomacyOption, TruceOption}
import net.eagle0.eagle.api.selected_command.DiplomacySelectedCommand
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.ofType
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.{DiplomacyAvailable, DiplomacyOption}
import net.eagle0.eagle.model.state.command.common.DiplomacyOptionType
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.DiplomacySelected
import net.eagle0.eagle.model.state.game_state.GameState
object TruceOfferCommandSelector {
@@ -16,13 +17,13 @@ object TruceOfferCommandSelector {
): Vector[HeroId] =
hids.filterNot(FactionUtils.isFactionLeader(_, gameState.factions.values.toVector))
private def truceOptionWithTarget(
private def hasTruceOptionWithTarget(
options: Vector[DiplomacyOption],
targetFactionId: FactionId
): Option[TruceOption] =
options.collectFirst {
case x @ TruceOption(tfid, _, _ /* unknownFieldSet */ ) if tfid == targetFactionId =>
x
): Boolean =
options.exists { opt =>
opt.targetFactionId == targetFactionId &&
opt.optionTypes.contains(DiplomacyOptionType.Truce)
}
def chosenTruceWithFactionCommand(
@@ -30,24 +31,19 @@ object TruceOfferCommandSelector {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
targetFactionId: FactionId
): Option[CommandSelection] =
ofType[DiplomacyAvailableCommand](availableCommands).find { ac =>
truceOptionWithTarget(
ac.options.toVector,
targetFactionId
).isDefined && usableHeroIds(
ac.availableHeroIds.toVector,
gameState
).nonEmpty
): Option[ScalaCommandSelection] =
ofType[DiplomacyAvailable](availableCommands).find { ac =>
hasTruceOptionWithTarget(ac.options, targetFactionId) &&
usableHeroIds(ac.availableHeroIds, gameState).nonEmpty
}.map { ac =>
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = ac.actingProvinceId,
available = ac,
selected = DiplomacySelectedCommand(
selectedOption = truceOptionWithTarget(ac.options.toVector, targetFactionId).get,
selected = DiplomacySelected(
selectedOption = DiplomacyOptionType.Truce,
targetFactionId = targetFactionId,
sentHeroId = usableHeroIds(ac.availableHeroIds.toVector, gameState).maxBy { hid =>
sentHeroId = usableHeroIds(ac.availableHeroIds, gameState).maxBy { hid =>
gameState.heroes(hid).vigor
}
),
@@ -1,10 +1,11 @@
package net.eagle0.eagle.library.util.command_choice_helpers.quest_command_selectors
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.api.available_command.{AvailableCommand, DiplomacyAvailableCommand}
import net.eagle0.eagle.api.command.util.diplomacy_option.AllianceOption
import net.eagle0.eagle.library.util.command_choice_helpers.{AllianceOfferCommandSelector, TrustForDiplomacy}
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.DiplomacyAvailable
import net.eagle0.eagle.model.state.command.common.DiplomacyOptionType
import net.eagle0.eagle.model.state.faction.FactionRelationship.RelationshipLevel.Ally
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.quest.concrete.AllianceQuest
@@ -17,13 +18,12 @@ object AllianceQuestCommandChooser extends QuestCommandChooser {
availableCommands: Vector[AvailableCommand],
uhsWithQuests: Vector[UnaffiliatedHeroWithQuest],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] = {
): RandomState[Option[ScalaCommandSelection]] = {
val allianceTargets: Vector[FactionId] = availableCommands.collect {
case cmd: DiplomacyAvailableCommand =>
cmd.options.collect {
case alliance: AllianceOption =>
alliance.targetFactionId
}
case cmd: DiplomacyAvailable =>
cmd.options.filter { opt =>
opt.optionTypes.contains(DiplomacyOptionType.Alliance)
}.map(_.targetFactionId)
}.flatten
functionalRandom
@@ -1,8 +1,8 @@
package net.eagle0.eagle.library.util.command_choice_helpers.quest_command_selectors
import net.eagle0.eagle.api.available_command.AvailableCommand
import net.eagle0.eagle.library.util.command_choice_helpers.AlmsCommandSelector
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.quest.concrete.AlmsAcrossRealmQuest
import net.eagle0.eagle.FactionId
@@ -23,7 +23,7 @@ object AlmsAcrossRealmQuestCommandChooser extends DeterministicQuestCommandChoos
actingFactionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand]
): Option[CommandSelection] =
): Option[ScalaCommandSelection] =
FactionUtils
.provinces(actingFactionId, gameState.provinces.values.toVector)
.flatMap { p =>
@@ -49,7 +49,7 @@ object AlmsAcrossRealmQuestCommandChooser extends DeterministicQuestCommandChoos
gameState: GameState,
availableCommands: Vector[AvailableCommand],
uhsWithQuests: Vector[UnaffiliatedHeroWithQuest]
): Option[CommandSelection] =
): Option[ScalaCommandSelection] =
maxRequestedAmount(uhsWithQuests).flatMap { amount =>
bestCommand(
desiredAmount = amount,
@@ -1,8 +1,8 @@
package net.eagle0.eagle.library.util.command_choice_helpers.quest_command_selectors
import net.eagle0.eagle.{FactionId, ProvinceId}
import net.eagle0.eagle.api.available_command.AvailableCommand
import net.eagle0.eagle.library.util.command_choice_helpers.AlmsCommandSelector
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.quest.concrete.AlmsToProvinceQuest
@@ -12,7 +12,7 @@ object AlmsToProvinceQuestCommandChooser extends DeterministicQuestCommandChoose
gameState: GameState,
availableCommands: Vector[AvailableCommand],
uhsWithQuests: Vector[UnaffiliatedHeroWithQuest]
): Option[CommandSelection] =
): Option[ScalaCommandSelection] =
uhsWithQuests
.map(uhwq => (uhwq.pid, uhwq.quest))
.collect {
@@ -13,12 +13,13 @@ scala_library(
deps = [
":quest_command_chooser",
":unaffiliated_hero_with_quest",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:alliance_offer_command_selector",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:trust_for_diplomacy",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/common",
"//src/main/scala/net/eagle0/eagle/model/state/faction",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/quest/concrete",
@@ -39,11 +40,11 @@ scala_library(
deps = [
":quest_command_chooser",
":unaffiliated_hero_with_quest",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:alms_command_selector",
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/quest/concrete",
],
@@ -63,10 +64,10 @@ scala_library(
deps = [
":quest_command_chooser",
":unaffiliated_hero_with_quest",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:alms_command_selector",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/quest/concrete",
],
@@ -86,7 +87,6 @@ scala_library(
deps = [
":quest_command_chooser",
":unaffiliated_hero_with_quest",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/scala/net/eagle0/common:more_option",
"//src/main/scala/net/eagle0/common:more_seq",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
@@ -94,6 +94,7 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:exile_vassal_command_selector",
"//src/main/scala/net/eagle0/eagle/library/util/hero:hero_utils",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/quest/concrete",
],
@@ -113,11 +114,11 @@ scala_library(
deps = [
":quest_command_chooser",
":unaffiliated_hero_with_quest",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:hero_gift_command_selector",
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/quest/concrete",
],
@@ -137,10 +138,10 @@ scala_library(
deps = [
":quest_command_chooser",
":unaffiliated_hero_with_quest",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:hero_gift_command_selector",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/quest/concrete",
],
@@ -159,11 +160,11 @@ scala_library(
deps = [
":quest_command_chooser",
":unaffiliated_hero_with_quest",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/common:improvement_type_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:improve_command_selector",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/common",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/quest/concrete",
],
@@ -182,10 +183,10 @@ scala_library(
],
deps = [
":unaffiliated_hero_with_quest",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
],
)
@@ -203,13 +204,13 @@ scala_library(
deps = [
":quest_command_chooser",
":unaffiliated_hero_with_quest",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:truce_offer_command_selector",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:trust_for_diplomacy",
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/quest/concrete",
],
@@ -228,11 +229,11 @@ scala_library(
deps = [
":quest_command_chooser",
":unaffiliated_hero_with_quest",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:truce_offer_command_selector",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:trust_for_diplomacy",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/quest/concrete",
],
@@ -2,11 +2,11 @@ package net.eagle0.eagle.library.util.command_choice_helpers.quest_command_selec
import net.eagle0.common.MoreOption
import net.eagle0.common.MoreSeq.flatCollectFirst
import net.eagle0.eagle.api.available_command.AvailableCommand
import net.eagle0.eagle.library.settings.RequiredPowerMultiplierForDismiss
import net.eagle0.eagle.library.util.command_choice_helpers.ExileVassalCommandSelector
import net.eagle0.eagle.library.util.hero.HeroUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.quest.concrete.DismissSpecificVassalQuest
import net.eagle0.eagle.FactionId
@@ -18,7 +18,7 @@ object DismissSpecificVassalCommandChooser extends DeterministicQuestCommandChoo
gameState: GameState,
availableCommands: Vector[AvailableCommand],
uhsWithQuests: Vector[UnaffiliatedHeroWithQuest]
): Option[CommandSelection] =
): Option[ScalaCommandSelection] =
uhsWithQuests.flatCollectFirst {
case UnaffiliatedHeroWithQuest(
pid,
@@ -1,8 +1,8 @@
package net.eagle0.eagle.library.util.command_choice_helpers.quest_command_selectors
import net.eagle0.eagle.api.available_command.AvailableCommand
import net.eagle0.eagle.library.util.command_choice_helpers.HeroGiftCommandSelector
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.quest.concrete.GiveToHeroesAcrossRealmQuest
import net.eagle0.eagle.FactionId
@@ -23,7 +23,7 @@ object GiveToHeroesAcrossRealmQuestCommandChooser extends DeterministicQuestComm
actingFactionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand]
): Option[CommandSelection] =
): Option[ScalaCommandSelection] =
FactionUtils
.provinces(actingFactionId, gameState.provinces.values.toVector)
.flatMap { p =>
@@ -45,7 +45,7 @@ object GiveToHeroesAcrossRealmQuestCommandChooser extends DeterministicQuestComm
gameState: GameState,
availableCommands: Vector[AvailableCommand],
uhsWithQuests: Vector[UnaffiliatedHeroWithQuest]
): Option[CommandSelection] =
): Option[ScalaCommandSelection] =
maxRequestedAmount(uhsWithQuests).flatMap { amount =>
bestCommand(
desiredAmount = amount,
@@ -1,9 +1,9 @@
package net.eagle0.eagle.library.util.command_choice_helpers.quest_command_selectors
import net.eagle0.eagle.{FactionId, ProvinceId}
import net.eagle0.eagle.api.available_command.AvailableCommand
import net.eagle0.eagle.library.util.command_choice_helpers.HeroGiftCommandSelector
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.quest.concrete.GiveToHeroesInProvinceQuest
@@ -13,7 +13,7 @@ object GiveToHeroesInProvinceQuestCommandChooser extends DeterministicQuestComma
gameState: GameState,
availableCommands: Vector[AvailableCommand],
uhsWithQuests: Vector[UnaffiliatedHeroWithQuest]
): Option[CommandSelection] =
): Option[ScalaCommandSelection] =
uhsWithQuests
.map(uhwq => (uhwq.pid, uhwq.quest))
.collect {
@@ -1,9 +1,9 @@
package net.eagle0.eagle.library.util.command_choice_helpers.quest_command_selectors
import net.eagle0.eagle.api.available_command.AvailableCommand
import net.eagle0.eagle.common.improvement_type.ImprovementType.{AGRICULTURE, ECONOMY, INFRASTRUCTURE}
import net.eagle0.eagle.library.util.command_choice_helpers.ImproveCommandSelector
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.common.ImprovementType.{Agriculture, Economy, Infrastructure}
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.quest.concrete.{
ImproveAgricultureQuest,
@@ -18,13 +18,13 @@ object ImproveQuestCommandChooser extends DeterministicQuestCommandChooser {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
uhsWithQuests: Vector[UnaffiliatedHeroWithQuest]
): Option[CommandSelection] =
): Option[ScalaCommandSelection] =
uhsWithQuests
.map(uhwq => (uhwq.pid, uhwq.quest))
.collect {
case (pid, _: ImproveEconomyQuest) => (pid, ECONOMY)
case (pid, _: ImproveAgricultureQuest) => (pid, AGRICULTURE)
case (pid, _: ImproveInfrastructureQuest) => (pid, INFRASTRUCTURE)
case (pid, _: ImproveEconomyQuest) => (pid, Economy)
case (pid, _: ImproveAgricultureQuest) => (pid, Agriculture)
case (pid, _: ImproveInfrastructureQuest) => (pid, Infrastructure)
}
.flatMap {
case (pid, it) =>
@@ -1,8 +1,8 @@
package net.eagle0.eagle.library.util.command_choice_helpers.quest_command_selectors
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.api.available_command.AvailableCommand
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.FactionId
@@ -13,7 +13,7 @@ trait QuestCommandChooser {
availableCommands: Vector[AvailableCommand],
uhsWithQuests: Vector[UnaffiliatedHeroWithQuest],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]]
): RandomState[Option[ScalaCommandSelection]]
}
abstract class DeterministicQuestCommandChooser extends QuestCommandChooser {
@@ -23,7 +23,7 @@ abstract class DeterministicQuestCommandChooser extends QuestCommandChooser {
availableCommands: Vector[AvailableCommand],
uhsWithQuests: Vector[UnaffiliatedHeroWithQuest],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] = RandomState(
): RandomState[Option[ScalaCommandSelection]] = RandomState(
chosenDeterministicCommand(
actingFactionId,
gameState,
@@ -38,5 +38,5 @@ abstract class DeterministicQuestCommandChooser extends QuestCommandChooser {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
uhsWithQuests: Vector[UnaffiliatedHeroWithQuest]
): Option[CommandSelection]
): Option[ScalaCommandSelection]
}
@@ -1,10 +1,10 @@
package net.eagle0.eagle.library.util.command_choice_helpers.quest_command_selectors
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.api.available_command.AvailableCommand
import net.eagle0.eagle.library.util.command_choice_helpers.{TruceOfferCommandSelector, TrustForDiplomacy}
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.quest.concrete.TruceCountQuest
import net.eagle0.eagle.FactionId
@@ -16,7 +16,7 @@ object TruceCountQuestCommandChooser extends QuestCommandChooser {
availableCommands: Vector[AvailableCommand],
uhsWithQuests: Vector[UnaffiliatedHeroWithQuest],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
): RandomState[Option[ScalaCommandSelection]] =
functionalRandom
.nextFlatCollectFirst(uhsWithQuests.map(_.quest)) {
case _: TruceCountQuest =>
@@ -1,8 +1,8 @@
package net.eagle0.eagle.library.util.command_choice_helpers.quest_command_selectors
import net.eagle0.eagle.api.available_command.AvailableCommand
import net.eagle0.eagle.library.util.command_choice_helpers.{TruceOfferCommandSelector, TrustForDiplomacy}
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.quest.concrete.TruceWithFactionQuest
import net.eagle0.eagle.FactionId
@@ -13,7 +13,7 @@ object TruceWithFactionQuestCommandChooser extends DeterministicQuestCommandChoo
gameState: GameState,
availableCommands: Vector[AvailableCommand],
uhsWithQuests: Vector[UnaffiliatedHeroWithQuest]
): Option[CommandSelection] =
): Option[ScalaCommandSelection] =
uhsWithQuests
.map(_.quest)
.collect { case twfq: TruceWithFactionQuest => twfq }
@@ -288,7 +288,7 @@ object SelectedCommandConverter {
): ArmedBattalion =
ArmedBattalion(
battalionId = ab.id,
battalionTypeId = ab.newArmament
newArmament = ab.newArmament
)
private def combatUnitFromProto(cu: net.eagle0.eagle.common.combat_unit.CombatUnit): CombatUnit =
@@ -4,7 +4,7 @@ scala_library(
name = "common",
srcs = ["CommandTypes.scala"],
visibility = [
"//src/main/scala/net/eagle0/eagle/library/actions:__subpackages__",
"//src/main/scala/net/eagle0/eagle/library:__subpackages__",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/command:__subpackages__",
"//src/main/scala/net/eagle0/eagle/model/state/command:__subpackages__",
"//src/test/scala/net/eagle0/eagle:__subpackages__",
@@ -113,7 +113,7 @@ enum TradeType:
case class ArmedBattalion(
battalionId: BattalionId,
battalionTypeId: Int
newArmament: Int
)
case class CombatUnit(
@@ -699,7 +699,6 @@ scala_test(
srcs = ["PerformVassalCommandsPhaseActionTest.scala"],
deps = [
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/eagle/library/actions/applier:action_result_applier",
"//src/main/scala/net/eagle0/eagle/library/actions/impl/action:perform_vassal_commands_phase_action",
@@ -725,7 +724,6 @@ scala_test(
"//src/main/scala/net/eagle0/eagle/library/settings:vassal_minimum_gold_to_send_supplies",
"//src/main/scala/net/eagle0/eagle/library/settings:vigor_gain_from_feast",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/command/available:available_command_converter",
"//src/main/scala/net/eagle0/eagle/model/state:army",
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type_id",
@@ -3,13 +3,6 @@ package net.eagle0.eagle.library.actions.impl.action
import scala.util.Random
import net.eagle0.common.SeededRandom
import net.eagle0.eagle.api.available_command.{
FeastAvailableCommand as ProtoFeastAvailableCommand,
HeroGiftAvailableCommand as ProtoHeroGiftAvailableCommand,
MarchAvailableCommand as ProtoMarchAvailableCommand,
SendSuppliesAvailableCommand as ProtoSendSuppliesAvailableCommand
}
import net.eagle0.eagle.api.selected_command.{HeroGiftSelectedCommand, RestSelectedCommand, SendSuppliesSelectedCommand}
import net.eagle0.eagle.library.actions.applier.ActionResultApplier
import net.eagle0.eagle.library.actions.impl.command.TCommandFactory
import net.eagle0.eagle.library.settings.{
@@ -35,21 +28,23 @@ import net.eagle0.eagle.library.settings.{
VigorGainFromFeast
}
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChoiceHelpers
import net.eagle0.eagle.library.util.command_choice_helpers.SelectedCommandMatcher.*
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.model.proto_converters.command.available.AvailableCommandConverter
import net.eagle0.eagle.model.state.{Army, BattalionType, BattalionTypeId, MovingArmy, RoundPhase, Supplies}
import net.eagle0.eagle.model.state.{Army, BattalionType, MovingArmy, RoundPhase, Supplies}
import net.eagle0.eagle.model.state.battalion.concrete.BattalionC
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.*
import net.eagle0.eagle.model.state.command.available.OneProvinceAvailableCommands
import net.eagle0.eagle.model.state.command.common.*
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.{
HeroGiftSelected,
RestSelected,
SendSuppliesSelected
}
import net.eagle0.eagle.model.state.date.Date
import net.eagle0.eagle.model.state.faction.concrete.FactionC
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.hero.concrete.HeroC
import net.eagle0.eagle.model.state.province.{Neighbor, ProvinceOrderType}
import net.eagle0.eagle.model.state.province.concrete.ProvinceC
import net.eagle0.eagle.model.state.province.Neighbor
import net.eagle0.eagle.model.state.province.ProvinceOrderType.{Develop, Expand}
import net.eagle0.eagle.model.state.run_status.RunStatus
import net.eagle0.eagle.model.state.BattalionTypeId.{HeavyCavalry, HeavyInfantry, LightInfantry}
@@ -61,8 +56,6 @@ import org.scalatest.Inside.inside
class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers with BeforeAndAfterEach with MockFactory {
import net.eagle0.common.ProtoMatchers.*
private val functionalRandom = SeededRandom(new Random().nextLong())
private val commandFactory: TCommandFactory = mock[TCommandFactory]
@@ -382,13 +375,6 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
private val foodPerProvinceHeldBack: Int = 300
/** Helper to convert Scala commands to proto for CommandChoiceHelpers tests */
private def toProtoCommands(
commands: Vector[AvailableCommand],
gs: GameState
): Vector[net.eagle0.eagle.api.available_command.AvailableCommand] =
commands.map(cmd => AvailableCommandConverter.toProto(cmd, gs, actingFactionId))
override def beforeEach(): Unit = {
MinSupportForTaxes.setDoubleValue(45.0)
FoodIncreasePerAgriculture.setDoubleValue(50.0)
@@ -439,28 +425,30 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
val developingGS =
gameState.copy(provinces = gameState.provinces.updated(developingProvince.id, developingProvince))
val commands = Vector(
feastCommand,
giftCommand,
restCommand,
improveCommand,
marchCommand
)
val selection = PerformVassalCommandsPhaseAction(
gameState = developingGS,
commandsForProvince = Map(
developingProvince.id -> Some(
OneProvinceAvailableCommands(
provinceId = developingProvince.id,
commands = Vector(
feastCommand,
giftCommand,
restCommand,
improveCommand,
marchCommand
)
commands = commands
)
)
),
commandFactory = commandFactory,
actionResultApplier = actionResultApplier
).chooseCommand(developingGS, developingProvince.id, functionalRandom).newValue.get
).chooseCommand(developingGS, developingProvince.id, commands, functionalRandom).newValue.get
inside(selection.available) {
case fac: ProtoFeastAvailableCommand =>
case fac: FeastAvailable =>
fac.actingProvinceId shouldBe developingProvince.id
}
}
@@ -478,32 +466,34 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
.updated(5, veryTiredHero2.copy(loyalty = 70))
)
val commands = Vector(
feastCommand,
giftCommand,
restCommand,
improveCommand,
marchCommand
)
val selection = PerformVassalCommandsPhaseAction(
gameState = developingGS,
commandsForProvince = Map(
developingProvince.id -> Some(
OneProvinceAvailableCommands(
provinceId = developingProvince.id,
commands = Vector(
feastCommand,
giftCommand,
restCommand,
improveCommand,
marchCommand
)
commands = commands
)
)
),
commandFactory = commandFactory,
actionResultApplier = actionResultApplier
).chooseCommand(developingGS, developingProvince.id, functionalRandom).newValue.get
).chooseCommand(developingGS, developingProvince.id, commands, functionalRandom).newValue.get
inside(selection.available) {
case hgac: ProtoHeroGiftAvailableCommand =>
case hgac: HeroGiftAvailable =>
hgac.actingProvinceId shouldBe developingProvince.id
}
inside(selection.selected) {
case HeroGiftSelectedCommand(recipientHeroId, _, _) =>
case HeroGiftSelected(recipientHeroId, _) =>
recipientHeroId shouldBe 2
}
}
@@ -521,25 +511,27 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
.updated(5, veryTiredHero2.copy(loyalty = 70))
)
val commands = Vector(
feastCommand,
giftCommand,
restCommand,
improveCommand,
marchCommand
)
val selection = PerformVassalCommandsPhaseAction(
gameState = developingGS,
commandsForProvince = Map(
developingProvince.id -> Some(
OneProvinceAvailableCommands(
provinceId = developingProvince.id,
commands = Vector(
feastCommand,
giftCommand,
restCommand,
improveCommand,
marchCommand
)
commands = commands
)
)
),
commandFactory = commandFactory,
actionResultApplier = actionResultApplier
).chooseCommand(developingGS, developingProvince.id, functionalRandom).newValue.get
).chooseCommand(developingGS, developingProvince.id, commands, functionalRandom).newValue.get
selection.available should not matchPattern {
case _: HeroGiftAvailable =>
@@ -548,25 +540,28 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
}
"chosen rest command" should "return the rest command" in {
val protoCommands = toProtoCommands(Vector(restCommand, improveCommand), gameState)
val selection = CommandChoiceHelpers
val commands = Vector(restCommand, improveCommand)
val selection = CommandChoiceHelpers
.chosenRestCommand(
actingFactionId = actingFactionId,
gameState = gameState,
availableCommands = protoCommands,
availableCommands = commands,
reason = "dummy reason"
)
.get
selection.available shouldBe protoCommands.head
selection.selected should equalSelectedCommand(RestSelectedCommand())
selection.available shouldBe restCommand
inside(selection.selected) {
case RestSelected =>
succeed
}
}
"chooseRestCommand" should "return nothing if there is no rest command" in {
CommandChoiceHelpers.chosenRestCommand(
actingFactionId = actingFactionId,
gameState = gameState,
availableCommands = toProtoCommands(Vector(improveCommand), gameState),
availableCommands = Vector(improveCommand),
reason = "dummy reason"
) shouldBe empty
}
@@ -576,21 +571,23 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
val developingGS =
gameState.copy(provinces = gameState.provinces.updated(developingProvince.id, developingProvince))
val commands = Vector(restCommand, improveCommand, marchCommand)
val selection = PerformVassalCommandsPhaseAction(
gameState = developingGS,
commandsForProvince = Map(
developingProvince.id -> Some(
OneProvinceAvailableCommands(
provinceId = developingProvince.id,
commands = Vector(restCommand, improveCommand, marchCommand)
commands = commands
)
)
),
commandFactory = commandFactory,
actionResultApplier = actionResultApplier
).chooseCommand(developingGS, developingProvince.id, functionalRandom).newValue.get
).chooseCommand(developingGS, developingProvince.id, commands, functionalRandom).newValue.get
selection.available shouldBe toProtoCommands(Vector(improveCommand), developingGS).head
selection.available shouldBe improveCommand
}
"choose on a Developing province with no improvement available" should "return an successful Rest" in {
@@ -598,21 +595,23 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
val developingGS =
gameState.copy(provinces = gameState.provinces.updated(developingProvince.id, developingProvince))
val commands = Vector(restCommand, marchCommand)
val selection = PerformVassalCommandsPhaseAction(
gameState = developingGS,
commandsForProvince = Map(
developingProvince.id -> Some(
OneProvinceAvailableCommands(
provinceId = developingProvince.id,
commands = Vector(restCommand, marchCommand)
commands = commands
)
)
),
commandFactory = commandFactory,
actionResultApplier = actionResultApplier
).chooseCommand(developingGS, developingProvince.id, functionalRandom).newValue.get
).chooseCommand(developingGS, developingProvince.id, commands, functionalRandom).newValue.get
selection.available shouldBe toProtoCommands(Vector(restCommand), developingGS).head
selection.available shouldBe restCommand
}
"choose on an Expanding province" should "return a successful March" in {
@@ -621,15 +620,17 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
provinces = marchGameState.provinces.updated(marchingProvince.id, marchingProvince)
)
val commands = Vector(
restCommand,
improveCommand,
marchCommand
)
val commandsMap = Map(
marchingProvince.id -> Some(
OneProvinceAvailableCommands(
provinceId = marchingProvince.id,
commands = Vector(
restCommand,
improveCommand,
marchCommand
)
commands = commands
)
)
)
@@ -639,12 +640,12 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
commandsForProvince = commandsMap,
commandFactory = commandFactory,
actionResultApplier = actionResultApplier
).chooseCommand(marchingGS, actingProvince.id, functionalRandom).newValue.get
).chooseCommand(marchingGS, actingProvince.id, commands, functionalRandom).newValue.get
// Verify the selected command is a march command matching our expected march
inside(selection.available) {
case _: ProtoMarchAvailableCommand =>
selection.available shouldBe toProtoCommands(Vector(marchCommand), marchingGS).head
case mac: MarchAvailable =>
mac shouldBe marchCommand
}
}
@@ -654,11 +655,13 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
provinces = marchGameState.provinces.updated(marchingProvince.id, marchingProvince)
)
val commands = Vector(restCommand, improveCommand)
val commandsMap = Map(
marchingProvince.id -> Some(
OneProvinceAvailableCommands(
provinceId = marchingProvince.id,
commands = Vector(restCommand, improveCommand)
commands = commands
)
)
)
@@ -668,9 +671,9 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
commandsForProvince = commandsMap,
commandFactory = commandFactory,
actionResultApplier = actionResultApplier
).chooseCommand(marchingGS, actingProvince.id, functionalRandom).newValue.get
).chooseCommand(marchingGS, actingProvince.id, commands, functionalRandom).newValue.get
selection.available shouldBe toProtoCommands(Vector(improveCommand), marchingGS).head
selection.available shouldBe improveCommand
}
"suppliesToSend" should "return all available gold even if there is no food" in {
@@ -761,8 +764,8 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
heroes = marchGameState.heroes.updated(leader.id, leader)
)
private val availableSendSuppliesCommand: ProtoSendSuppliesAvailableCommand =
ProtoSendSuppliesAvailableCommand(
private val availableSendSuppliesCommand: SendSuppliesAvailable =
SendSuppliesAvailable(
actingProvinceId = actingProvince.id,
availableHeroIds = actingProvince.rulingFactionHeroIds,
goldAvailable = 2500,
@@ -798,8 +801,9 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
.updated(actingProvinceWithIntermediateNeighbor.id, actingProvinceWithIntermediateNeighbor)
.updated(leaderProvinceWithIntermediateNeighbor.id, leaderProvinceWithIntermediateNeighbor)
)
val ac = availableSendSuppliesCommand.update(
_.availableDestinationProvinceIds :+= intermediateProvince.id
val ac = availableSendSuppliesCommand.copy(
availableDestinationProvinceIds =
availableSendSuppliesCommand.availableDestinationProvinceIds :+ intermediateProvince.id
)
CommandChoiceHelpers.preferredSuppliesDestination(
@@ -837,9 +841,9 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
.updated(actingProvinceWithIntermediateNeighbor.id, actingProvinceWithIntermediateNeighbor)
.updated(leaderProvinceWithIntermediateNeighbor.id, leaderProvinceWithIntermediateNeighbor)
)
val ac = availableSendSuppliesCommand.update(
_.availableDestinationProvinceIds :+= intermediateProvince.id,
_.availableDestinationProvinceIds :+= leaderProvince.id
val ac = availableSendSuppliesCommand.copy(
availableDestinationProvinceIds =
availableSendSuppliesCommand.availableDestinationProvinceIds :+ intermediateProvince.id :+ leaderProvince.id
)
CommandChoiceHelpers.preferredSuppliesDestination(
@@ -894,9 +898,9 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
).map(p => p.id -> p).toMap,
factions = Map(factionWithBrother.id -> factionWithBrother)
)
val ac = availableSendSuppliesCommand.update(
_.actingProvinceId := 15,
_.availableDestinationProvinceIds := Vector(16, 17)
val ac = availableSendSuppliesCommand.copy(
actingProvinceId = 15,
availableDestinationProvinceIds = Vector(16, 17)
)
CommandChoiceHelpers.preferredSuppliesDestination(
@@ -951,9 +955,9 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
).map(p => p.id -> p).toMap,
factions = Map(factionWithBrother.id -> factionWithBrother)
)
val ac = availableSendSuppliesCommand.update(
_.actingProvinceId := 15,
_.availableDestinationProvinceIds := Vector(16, 17)
val ac = availableSendSuppliesCommand.copy(
actingProvinceId = 15,
availableDestinationProvinceIds = Vector(16, 17)
)
CommandChoiceHelpers.preferredSuppliesDestination(
@@ -987,8 +991,9 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
.updated(actingProvinceWithIntermediateNeighbor.id, actingProvinceWithIntermediateNeighbor)
.updated(leaderProvinceWithIntermediateNeighbor.id, leaderProvinceWithIntermediateNeighbor)
)
val ac = availableSendSuppliesCommand.update(
_.availableDestinationProvinceIds :+= intermediateProvince.id
val ac = availableSendSuppliesCommand.copy(
availableDestinationProvinceIds =
availableSendSuppliesCommand.availableDestinationProvinceIds :+ intermediateProvince.id
)
CommandChoiceHelpers.preferredSuppliesDestination(
@@ -1023,8 +1028,9 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
.updated(actingProvinceWithIntermediateNeighbor.id, actingProvinceWithIntermediateNeighbor)
.updated(leaderProvinceWithIntermediateNeighbor.id, leaderProvinceWithIntermediateNeighbor)
)
val ac = availableSendSuppliesCommand.update(
_.availableDestinationProvinceIds :+= intermediateProvince.id
val ac = availableSendSuppliesCommand.copy(
availableDestinationProvinceIds =
availableSendSuppliesCommand.availableDestinationProvinceIds :+ intermediateProvince.id
)
val selection = CommandChoiceHelpers
@@ -1032,14 +1038,13 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
.get
selection.available shouldBe ac
selection.selected should equalSelectedCommand(
SendSuppliesSelectedCommand(
gold = 1900,
food = 3200,
actingHeroId = fullyRestedHero1.id,
destinationProvinceId = intermediateProvince.id
)
)
inside(selection.selected) {
case SendSuppliesSelected(gold, food, actingHeroId, destinationProvinceId) =>
gold shouldBe 1900
food shouldBe 3200
actingHeroId shouldBe fullyRestedHero1.id
destinationProvinceId shouldBe intermediateProvince.id
}
}
it should "return nothing if the supplies are empty" in {
@@ -1067,8 +1072,9 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
.updated(actingProvinceWithIntermediateNeighbor.id, actingProvinceWithIntermediateNeighbor)
.updated(leaderProvinceWithIntermediateNeighbor.id, leaderProvinceWithIntermediateNeighbor)
)
val ac = availableSendSuppliesCommand.update(
_.availableDestinationProvinceIds :+= intermediateProvince.id
val ac = availableSendSuppliesCommand.copy(
availableDestinationProvinceIds =
availableSendSuppliesCommand.availableDestinationProvinceIds :+ intermediateProvince.id
)
CommandChoiceHelpers.maybeChosenSendSuppliesCommand(
@@ -1100,8 +1106,9 @@ class PerformVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers wit
.updated(actingProvinceWithIntermediateNeighbor.id, actingProvinceWithIntermediateNeighbor)
.updated(leaderProvinceWithIntermediateNeighbor.id, leaderProvinceWithIntermediateNeighbor)
)
val ac = availableSendSuppliesCommand.update(
_.availableDestinationProvinceIds :+= intermediateProvince.id
val ac = availableSendSuppliesCommand.copy(
availableDestinationProvinceIds =
availableSendSuppliesCommand.availableDestinationProvinceIds :+ intermediateProvince.id
)
CommandChoiceHelpers.maybeChosenSendSuppliesCommand(
actingFactionId,
@@ -117,7 +117,7 @@ class SelectedCommandConverterTest extends AnyFlatSpec with Matchers {
case cmd: ArmTroopsSelected =>
cmd.armedBattalions should have size 1
cmd.armedBattalions.head.battalionId shouldBe battalionId
cmd.armedBattalions.head.battalionTypeId shouldBe 50
cmd.armedBattalions.head.newArmament shouldBe 50
}
}