Compare commits

...
Author SHA1 Message Date
adminandClaude Opus 4.5 4960987147 Make AI clients protoless
Convert AI client source and test files to use Scala AvailableCommand
and ScalaCommandSelection types instead of proto types.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 08:12:02 -08:00
adminandClaude Opus 4.5 5d9a267b4b 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:46:47 -08:00
50 changed files with 1321 additions and 1290 deletions
+17 -17
View File
@@ -70,14 +70,16 @@ scala_library(
],
deps = [
":resolve_diplomacy_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/settings:invite_gold_cost",
"//src/main/scala/net/eagle0/eagle/library/settings:min_chance_for_a_i_invite",
"//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/library/util/command_choice_helpers:province_gold_surplus_calculator",
"//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/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
],
)
@@ -93,7 +95,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_choice_helpers",
@@ -134,13 +135,13 @@ 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/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/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:available_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/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
],
)
@@ -153,14 +154,14 @@ scala_library(
],
deps = [
":ai_client_utils",
"//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:battalion_utils",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/library/util:eagle_require",
"//src/main/scala/net/eagle0/eagle/library/util:province_distances",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:province_gold_surplus_calculator",
"//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",
],
)
@@ -173,7 +174,6 @@ scala_library(
"//src/test/scala/net/eagle0/eagle/ai:__pkg__",
],
exports = [
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
],
@@ -184,9 +184,6 @@ scala_library(
":march_toward_province_command_chooser",
":move_leader_to_better_province_command_chooser",
":seek_more_leaders_command_chooser",
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api: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/common:more_option",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
@@ -210,6 +207,9 @@ 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/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",
@@ -232,10 +232,10 @@ scala_library(
deps = [
":faction_leader_province_ranker",
":march_toward_province_command_chooser",
"//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:province_distances",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/faction",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
],
@@ -251,8 +251,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/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:eagle_internal_exception",
@@ -263,8 +261,10 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/util:eagle_require",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:available_command_selector",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:command_chooser",
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:ransom_offer_helpers",
"//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/diplomacy_offer/status",
"//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/province",
@@ -279,8 +279,6 @@ scala_library(
"//src/test/scala/net/eagle0/eagle/ai:__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/common:more_option",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/settings:loyalty_gain_from_feast",
@@ -293,6 +291,8 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:sworn_brother_chooser",
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
"//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/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",
@@ -1,7 +1,6 @@
package net.eagle0.eagle.ai
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.api.available_command.AvailableCommand
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChoiceHelpers.{
chosenCommandWhileTraveling,
chosenDevelopCommand,
@@ -10,7 +9,8 @@ import net.eagle0.eagle.library.util.command_choice_helpers.CommandChoiceHelpers
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChooser
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
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
@@ -25,7 +25,7 @@ object EarlyGameAIClient {
gs: GameState,
acs: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[CommandSelection] =
): RandomState[ScalaCommandSelection] =
CommandChooser
.choose(
actingFactionId = actingFactionId,
@@ -1,12 +1,13 @@
package net.eagle0.eagle.ai
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.api.available_command.{AvailableCommand, MarchAvailableCommand}
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
import net.eagle0.eagle.common.combat_unit.CombatUnit
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.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.MarchAvailable
import net.eagle0.eagle.model.state.command.selected.CombatUnit
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.MarchSelected
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.FactionId
@@ -16,10 +17,10 @@ object FixLeaderAloneCommandSelector {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] = {
): RandomState[Option[ScalaCommandSelection]] = {
val factions = gameState.factions.values.toVector
val provinces = gameState.provinces
randomSelectionForType[MarchAvailableCommand](
randomSelectionForType[MarchAvailable](
availableCommands,
functionalRandom
) {
@@ -40,15 +41,15 @@ object FixLeaderAloneCommandSelector {
.map { destination =>
fr.nextRandomElement(opmc.availableHeroIds)
.map { hid =>
CombatUnit(factionId = actingFactionId, heroId = hid)
CombatUnit(heroId = hid, battalionId = None)
}
.map { combatUnit =>
Some(
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = ac.actingProvinceId,
available = ac,
selected = MarchSelectedCommand(
selected = MarchSelected(
gold = 0,
food = 0,
originProvince = opmc.originProvinceId,
@@ -1,18 +1,19 @@
package net.eagle0.eagle.ai
import net.eagle0.eagle.api.available_command.{AvailableCommand, DiplomacyAvailableCommand}
import net.eagle0.eagle.api.command.util.diplomacy_option.InvitationOption
import net.eagle0.eagle.api.selected_command.DiplomacySelectedCommand
import net.eagle0.eagle.library.settings.MinChanceForAIInvite
import net.eagle0.eagle.library.settings.{InviteGoldCost, MinChanceForAIInvite}
import net.eagle0.eagle.library.util.command_choice_helpers.{ProvinceGoldSurplusCalculator, TrustForDiplomacy}
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.{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
import net.eagle0.eagle.FactionId
object InvitationCommandSelector {
private case class InvitationOptionWithChance(
invitationOption: InvitationOption,
targetFactionId: FactionId,
acceptanceChance: Int
)
@@ -20,39 +21,45 @@ object InvitationCommandSelector {
actingFactionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand]
): Option[CommandSelection] =
flatSelectionForType[DiplomacyAvailableCommand](acs = availableCommands) { diplomacyAvailableCommand =>
diplomacyAvailableCommand.options.collect { case io: InvitationOption => io }.filter { invitationOption =>
TrustForDiplomacy.meetsConditionsForInvitation(
actingFactionId = actingFactionId,
targetFactionId = invitationOption.targetFactionId,
gameState = gameState
)
}.filter { invitationOption =>
ProvinceGoldSurplusCalculator.provinceGoldSurplus(
gameState.provinces(diplomacyAvailableCommand.actingProvinceId)
) >= invitationOption.goldCost
}.map { invitationOption =>
InvitationOptionWithChance(
invitationOption = invitationOption,
acceptanceChance = ResolveDiplomacyCommandSelector.invitationAcceptanceChance(
actingFactionId,
invitationOption.targetFactionId,
gameState.factions.values.toVector,
gameState.provinces.values.toVector
): Option[ScalaCommandSelection] =
flatSelectionForType[DiplomacyAvailable](acs = availableCommands) { diplomacyAvailable =>
// Find options that have Invitation as an option type
diplomacyAvailable.options
.filter(opt => opt.optionTypes.contains(DiplomacyOptionType.Invitation))
.filter { diplomacyOption =>
TrustForDiplomacy.meetsConditionsForInvitation(
actingFactionId = actingFactionId,
targetFactionId = diplomacyOption.targetFactionId,
gameState = gameState
)
)
}.maxByOption(_.acceptanceChance)
}
.filter { _ =>
ProvinceGoldSurplusCalculator.provinceGoldSurplus(
gameState.provinces(diplomacyAvailable.actingProvinceId)
) >= InviteGoldCost.intValue
}
.map { diplomacyOption =>
InvitationOptionWithChance(
targetFactionId = diplomacyOption.targetFactionId,
acceptanceChance = ResolveDiplomacyCommandSelector.invitationAcceptanceChance(
actingFactionId,
diplomacyOption.targetFactionId,
gameState.factions.values.toVector,
gameState.provinces.values.toVector
)
)
}
.maxByOption(_.acceptanceChance)
.filter(_.acceptanceChance >= MinChanceForAIInvite.intValue)
.map { iowc =>
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = diplomacyAvailableCommand.actingProvinceId,
available = diplomacyAvailableCommand,
selected = DiplomacySelectedCommand(
selectedOption = iowc.invitationOption,
targetFactionId = iowc.invitationOption.targetFactionId,
sentHeroId = diplomacyAvailableCommand.recommendedHeroId
actingProvinceId = diplomacyAvailable.actingProvinceId,
available = diplomacyAvailable,
selected = DiplomacySelected(
selectedOption = DiplomacyOptionType.Invitation,
targetFactionId = iowc.targetFactionId,
sentHeroId = diplomacyAvailable.recommendedHeroId
),
reason = "maybeInviteOtherFaction"
)
@@ -1,23 +1,23 @@
package net.eagle0.eagle.ai
import net.eagle0.eagle.{FactionId, ProvinceId}
import net.eagle0.eagle.api.available_command.{MarchAvailableCommand, MarchCommandFromOneProvince}
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
import net.eagle0.eagle.common.combat_unit.CombatUnit
import net.eagle0.eagle.library.util.{BattalionUtils, CommandSelection, ProvinceDistances}
import net.eagle0.eagle.library.util.{BattalionUtils, ProvinceDistances, ScalaCommandSelection}
import net.eagle0.eagle.library.util.command_choice_helpers.ProvinceGoldSurplusCalculator.provinceGoldSurplus
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
import net.eagle0.eagle.model.state.command.available.AvailableCommand.{MarchAvailable, MarchCommandFromOneProvince}
import net.eagle0.eagle.model.state.command.selected.CombatUnit
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.MarchSelected
import net.eagle0.eagle.model.state.game_state.GameState
object MarchTowardProvinceCommandChooser {
def marchTowardProvinceCommand(
ac: MarchAvailableCommand,
ac: MarchAvailable,
fid: FactionId,
opmc: MarchCommandFromOneProvince,
destinationProvinceId: ProvinceId,
gs: GameState,
favorLeaders: Boolean
): Option[CommandSelection] = {
): Option[ScalaCommandSelection] = {
val bestCommand =
opmc.availableDestinationProvinces
.map(dest => (opmc, gs.provinces(dest.provinceId)))
@@ -49,7 +49,7 @@ object MarchTowardProvinceCommandChooser {
AIClientUtils.takenHeroIdsForMarchTowardFocus(
originProvince = originProvince,
destinationProvince = dest,
availableHeroIds = opmc.availableHeroIds.toVector,
availableHeroIds = opmc.availableHeroIds,
favorLeaders = favorLeaders,
factions = gs.factions.values.toVector,
heroes = gs.heroes
@@ -72,7 +72,7 @@ object MarchTowardProvinceCommandChooser {
)
.map {
case (hid, bid) =>
CombatUnit(factionId = fid, heroId = hid, battalionId = bid)
CombatUnit(heroId = hid, battalionId = bid)
}
val foodToTake = 2 * BattalionUtils.monthlyConsumedFood(
@@ -80,11 +80,11 @@ object MarchTowardProvinceCommandChooser {
gs.battalionTypes
)
val goldToTake = provinceGoldSurplus(originProvince)
CommandSelection(
ScalaCommandSelection(
actingFactionId = fid,
actingProvinceId = actingProvinceId,
available = ac,
selected = MarchSelectedCommand(
selected = MarchSelected(
marchingUnits = takenUnits, // Check the food cost here?
destinationProvinceId = dest.id,
originProvince = opmc.originProvinceId,
@@ -3,11 +3,6 @@ package net.eagle0.eagle.ai
import net.eagle0.common.{FunctionalRandom, MoreOption, RandomState}
import net.eagle0.eagle.{FactionId, ProvinceId}
import net.eagle0.eagle.ai.AIClientUtils
import net.eagle0.eagle.api.available_command.*
import net.eagle0.eagle.api.command.OneProvinceAvailableCommands
import net.eagle0.eagle.api.selected_command.{MarchSelectedCommand, ReconSelectedCommand}
import net.eagle0.eagle.common.combat_unit.CombatUnit
import net.eagle0.eagle.common.improvement_type.ImprovementType
import net.eagle0.eagle.library.settings.{MinSupportForTaxes, MonthsReconConsideredRecent}
import net.eagle0.eagle.library.util.*
import net.eagle0.eagle.library.util.command_choice_helpers.{
@@ -30,6 +25,12 @@ import net.eagle0.eagle.library.util.hero.HeroUtils
import net.eagle0.eagle.library.util.province.ProvinceUtils
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
import net.eagle0.eagle.library.util.ProvinceDistances
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.ImprovementType
import net.eagle0.eagle.model.state.command.selected.CombatUnit
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.{MarchSelected, ReconSelected}
import net.eagle0.eagle.model.state.date.Date.given
import net.eagle0.eagle.model.state.faction.FactionRelationship as FactionRelationshipC
import net.eagle0.eagle.model.state.game_state.GameState
@@ -60,13 +61,13 @@ object MidGameAIClient {
gs: GameState,
opac: OneProvinceAvailableCommands,
functionalRandom: FunctionalRandom
): RandomState[CommandSelection] = {
): RandomState[ScalaCommandSelection] = {
validateNoFactionLeaderAlone(gs, factionId)
CommandChooser.choose(
actingFactionId = factionId,
gameState = gs,
availableCommands = opac.commands.toVector,
availableCommands = opac.commands,
rankedChoosers = Vector[CommandChooser](
(fid: FactionId, gs: GameState, acs: Vector[AvailableCommand], fr: FunctionalRandom) =>
chosenUniversalCommand(fid, gs, acs, fr),
@@ -128,15 +129,14 @@ object MidGameAIClient {
factionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand]
): Option[CommandSelection] =
flatSelectionForType[SendSuppliesAvailableCommand](availableCommands) {
case available @ SendSuppliesAvailableCommand(
): Option[ScalaCommandSelection] =
flatSelectionForType[SendSuppliesAvailable](availableCommands) {
case available @ SendSuppliesAvailable(
goldAvailable,
foodAvailable,
availableDestinationProvinceIds,
actingProvinceId,
_ /* availableHeroIds */,
_ /* unknownFieldSet */
_ /* availableHeroIds */
) =>
val actingProvince = gameState.provinces(actingProvinceId)
val foodMonthsToHoldBack =
@@ -216,7 +216,7 @@ object MidGameAIClient {
chooser: CommandChooser,
functionalRandom: FunctionalRandom,
reason: String
): RandomState[Option[CommandSelection]] =
): RandomState[Option[ScalaCommandSelection]] =
chooser(actingFactionId, gs, acs, functionalRandom).map(
_.map(_.withReason(reason))
)
@@ -227,7 +227,7 @@ object MidGameAIClient {
acs: Vector[AvailableCommand],
choosersAndReasons: Vector[(CommandChooser, String)],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] = choosersAndReasons match {
): RandomState[Option[ScalaCommandSelection]] = choosersAndReasons match {
case cr +: tail =>
chosenGenericCommand(
actingFactionId = actingFactionId,
@@ -256,7 +256,7 @@ object MidGameAIClient {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
): RandomState[Option[ScalaCommandSelection]] =
chosenGenericCommandFromRankedOptions(
actingFactionId = actingFactionId,
gs = gameState,
@@ -327,7 +327,7 @@ object MidGameAIClient {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
): RandomState[Option[ScalaCommandSelection]] =
provincesWithFactionLeader(actingFactionId, gameState).map { province =>
MoreOption
.flatWhen(province.rulingFactionHeroIds.size < 4)(
@@ -400,7 +400,7 @@ object MidGameAIClient {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
): RandomState[Option[ScalaCommandSelection]] =
provincesWithFactionLeader(actingFactionId, gameState).map { province =>
MoreOption
.flatWhen(province.rulingFactionHeroIds.size < 4)(
@@ -486,7 +486,7 @@ object MidGameAIClient {
actingFactionId: FactionId,
gs: GameState,
acs: Vector[AvailableCommand]
): Option[CommandSelection] = {
): Option[ScalaCommandSelection] = {
def reconnedFullInfoFor(provinceId: ProvinceId): Option[FullProvinceInfoC] =
gs.factions(actingFactionId)
.reconnedProvinces
@@ -515,7 +515,7 @@ object MidGameAIClient {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
provinceId: ProvinceId
): Option[CommandSelection] =
): Option[ScalaCommandSelection] =
MoreOption
.flatWhen(
gameState.provinces(provinceId).support < MinSupportForTaxes.doubleValue
@@ -541,12 +541,12 @@ object MidGameAIClient {
gameState: GameState,
opac: OneProvinceAvailableCommands,
functionalRandom: FunctionalRandom
): RandomState[CommandSelection] =
): RandomState[ScalaCommandSelection] =
CommandChooser
.choose(
actingFactionId = actingFactionId,
gameState = gameState,
availableCommands = opac.commands.toVector,
availableCommands = opac.commands,
rankedChoosers = Vector[CommandChooser](
(actingFactionId, gameState, availableCommands, functionalRandom) =>
RandomState(
@@ -682,8 +682,8 @@ object MidGameAIClient {
gs: GameState,
acs: Vector[AvailableCommand],
factionId: FactionId
): Option[CommandSelection] =
flatSelectionForType[ImproveAvailableCommand](acs) { availableCommand =>
): Option[ScalaCommandSelection] =
flatSelectionForType[ImproveAvailable](acs) { availableCommand =>
val province = gs.provinces(availableCommand.actingProvinceId)
val existingBattalions = province.battalionIds
.map(gs.battalions)
@@ -706,7 +706,7 @@ object MidGameAIClient {
actingFactionId = factionId,
actingProvinceId = province.id,
availableCommands = acs,
improvementType = ImprovementType.DEVASTATION
improvementType = ImprovementType.Devastation
)
.orElse {
val neededAgriculture = notAvailable
@@ -729,7 +729,7 @@ object MidGameAIClient {
actingFactionId = factionId,
actingProvinceId = province.id,
availableCommands = acs,
improvementType = ImprovementType.AGRICULTURE
improvementType = ImprovementType.Agriculture
)
else
ImproveCommandSelector
@@ -737,7 +737,7 @@ object MidGameAIClient {
actingFactionId = factionId,
actingProvinceId = province.id,
availableCommands = acs,
improvementType = ImprovementType.ECONOMY
improvementType = ImprovementType.Economy
)
end if
}
@@ -749,10 +749,10 @@ object MidGameAIClient {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] = {
): RandomState[Option[ScalaCommandSelection]] = {
def go(
provincesRS: RandomState[Vector[ProvinceT]]
): RandomState[Option[CommandSelection]] = provincesRS.continue {
): RandomState[Option[ScalaCommandSelection]] = provincesRS.continue {
case (province +: tail, fr) =>
MoreOption
.flatWhen(province.rulingFactionHeroIds.size < 6)(
@@ -850,13 +850,13 @@ object MidGameAIClient {
actingFactionId: FactionId,
gs: GameState,
acs: Vector[AvailableCommand]
): Option[CommandSelection] = {
): Option[ScalaCommandSelection] = {
val factionLeaderProvincesNeedingHeroes =
provincesWithFactionLeader(actingFactionId, gs)
.filter(p => p.rulingFactionHeroIds.size < p.heroCap)
val cs = for {
(marchCommand, _) <- acs.zipWithIndex.collectFirst {
case (ac: MarchAvailableCommand, idx) => (ac, idx)
case (ac: MarchAvailable, idx) => (ac, idx)
}
} yield {
// Find the march command origin provinces that contain extra heroes
@@ -924,14 +924,14 @@ object MidGameAIClient {
private def selectedReconCommand(
actingFactionId: FactionId,
gameState: GameState,
ac: ReconAvailableCommand,
ac: ReconAvailable,
targetProvinceId: ProvinceId
): CommandSelection =
CommandSelection(
): ScalaCommandSelection =
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = ac.actingProvinceId,
available = ac,
selected = ReconSelectedCommand(
selected = ReconSelected(
actingHeroId = ac.availableHeroIds.maxBy(hid => gameState.heroes(hid).vigor),
targetProvinceId = targetProvinceId
),
@@ -942,8 +942,8 @@ object MidGameAIClient {
actingFactionId: FactionId,
gameState: GameState,
acs: Vector[AvailableCommand]
): Option[CommandSelection] =
flatSelectionForType[ReconAvailableCommand](acs) { ac =>
): Option[ScalaCommandSelection] =
flatSelectionForType[ReconAvailable](acs) { ac =>
val targetProvincesNeighboringMe = ac.availableTargetProvinces
.map(gameState.provinces)
.filter(_.rulingFactionId.exists(_ != actingFactionId))
@@ -1010,11 +1010,11 @@ object MidGameAIClient {
actingFactionId: FactionId,
gameState: GameState,
acs: Vector[AvailableCommand]
): Option[CommandSelection] = {
): Option[ScalaCommandSelection] = {
val faction = gameState.factions(actingFactionId)
val factionLeaders = faction.leaderIds
flatSelectionForType[MarchAvailableCommand](acs) { marchAvailableCommand =>
marchAvailableCommand.oneProvinceCommands.flatMap { opmc =>
flatSelectionForType[MarchAvailable](acs) { marchAvailable =>
marchAvailable.oneProvinceCommands.flatMap { opmc =>
val originProvince = gameState.provinces(opmc.originProvinceId)
val leadersInOriginProvince =
originProvince.rulingFactionHeroIds.intersect(factionLeaders)
@@ -1040,18 +1040,17 @@ object MidGameAIClient {
) // don't send a leader into a province under attack
validDestinations.minByOption(_.rulingFactionHeroIds.size).map { destinationProvince =>
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = marchAvailableCommand.actingProvinceId,
available = marchAvailableCommand,
selected = MarchSelectedCommand(
actingProvinceId = marchAvailable.actingProvinceId,
available = marchAvailable,
selected = MarchSelected(
gold = 0,
food = 0,
originProvince = originProvince.id,
destinationProvinceId = destinationProvince.id,
marchingUnits = Vector(
CombatUnit(
factionId = actingFactionId,
heroId = leaderIdToMove,
battalionId = None
)
@@ -1,10 +1,11 @@
package net.eagle0.eagle.ai
import net.eagle0.eagle.{FactionId, ProvinceId}
import net.eagle0.eagle.api.available_command.{AvailableCommand, MarchAvailableCommand, MarchCommandFromOneProvince}
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.ProvinceDistances
import net.eagle0.eagle.library.util.ProvinceDistances.ProvinceAndDistance
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.{MarchAvailable, MarchCommandFromOneProvince}
import net.eagle0.eagle.model.state.faction.FactionT
import net.eagle0.eagle.model.state.game_state.GameState
@@ -21,7 +22,7 @@ object MoveLeaderToBetterProvinceCommandChooser {
actingFactionId: FactionId,
gs: GameState,
acs: Vector[AvailableCommand]
): Option[CommandSelection] =
): Option[ScalaCommandSelection] =
maybeMoveLeaderToBetterProvinceCommandWithChooser(
actingFactionId = actingFactionId,
gs = gs,
@@ -31,11 +32,11 @@ object MoveLeaderToBetterProvinceCommandChooser {
)
private def candidatesForMarchCommand(
marchCommand: MarchAvailableCommand,
marchCommand: MarchAvailable,
gs: GameState,
faction: FactionT
): Vector[MarchCommandFromOneProvince] =
marchCommand.oneProvinceCommands.toVector.filter { opmc =>
marchCommand.oneProvinceCommands.filter { opmc =>
val originProvince = gs.provinces(opmc.originProvinceId)
if opmc.availableHeroIds.intersect(faction.leaderIds).isEmpty then false
else if originProvince.rulingFactionHeroIds.length < 2 then false
@@ -57,7 +58,7 @@ object MoveLeaderToBetterProvinceCommandChooser {
ProvinceDistances
.closestProvinceThroughFriendliesOption(
desiredPid,
mcfop.availableDestinationProvinces.map(_.provinceId).toVector,
mcfop.availableDestinationProvinces.map(_.provinceId),
actingFactionId,
gs.provinces
)
@@ -75,10 +76,10 @@ object MoveLeaderToBetterProvinceCommandChooser {
gs: GameState,
acs: Vector[AvailableCommand],
betterDestinationChooser: DestinationChooser
): Option[CommandSelection] = {
): Option[ScalaCommandSelection] = {
val cs = for {
faction <- gs.factions.get(actingFactionId)
marchCommand <- acs.collectFirst { case ac: MarchAvailableCommand => ac }
marchCommand <- acs.collectFirst { case ac: MarchAvailable => ac }
} yield {
// Find the march command origin provinces that contain a faction leader
val candidatesWithDistances = candidateCommandsWithDistances(
@@ -1,42 +1,25 @@
package net.eagle0.eagle.ai
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.api.available_command.{
AvailableCommand,
ResolveAllianceOfferAvailableCommand,
ResolveBreakAllianceAvailableCommand,
ResolveInvitationAvailableCommand,
ResolveRansomOfferAvailableCommand,
ResolveTruceOfferAvailableCommand
}
import net.eagle0.eagle.api.selected_command.{
ResolveAllianceOfferSelectedCommand,
ResolveBreakAllianceSelectedCommand,
ResolveInvitationSelectedCommand,
ResolveRansomOfferSelectedCommand,
ResolveTruceOfferSelectedCommand,
SelectedCommand
}
import net.eagle0.eagle.common.diplomacy_offer.RansomOfferDetails
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.{
DIPLOMACY_OFFER_STATUS_ACCEPTED,
DIPLOMACY_OFFER_STATUS_IMPRISONED,
DIPLOMACY_OFFER_STATUS_REJECTED
}
import net.eagle0.eagle.library.settings.{
AiAllianceAcceptanceBaseChance,
AiTruceAcceptanceBaseChance,
MinimumPrestigeDifferenceToInvite
}
import net.eagle0.eagle.library.util.command_choice_helpers.{CommandChooser, RansomOfferHelpers}
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.{
randomSelectionForType,
selectionForType
}
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChooser
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
import net.eagle0.eagle.library.util.ScalaCommandSelection
import net.eagle0.eagle.library.EagleInternalException
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.selected.SelectedCommand
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.*
import net.eagle0.eagle.model.state.diplomacy_offer.status.{Accepted, Imprisoned, Rejected, Status}
import net.eagle0.eagle.model.state.faction.FactionT
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.province.ProvinceT
@@ -50,7 +33,7 @@ object ResolveDiplomacyCommandSelector {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
): RandomState[Option[ScalaCommandSelection]] =
CommandChooser.choose(
actingFactionId = actingFactionId,
gameState = gameState,
@@ -70,8 +53,8 @@ object ResolveDiplomacyCommandSelector {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
randomSelectionForType[ResolveInvitationAvailableCommand](
): RandomState[Option[ScalaCommandSelection]] =
randomSelectionForType[ResolveInvitationAvailable](
availableCommands,
functionalRandom
) {
@@ -83,7 +66,7 @@ object ResolveDiplomacyCommandSelector {
functionalRandom = fr
).map { resolveInviteSelection =>
Some(
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = 0,
available = resolveInvitationAc,
@@ -96,7 +79,7 @@ object ResolveDiplomacyCommandSelector {
private def selectionForResolveInvitationCommand(
actingFactionId: FactionId,
ac: ResolveInvitationAvailableCommand,
ac: ResolveInvitationAvailable,
gs: GameState,
functionalRandom: FunctionalRandom
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { roll =>
@@ -104,20 +87,20 @@ object ResolveDiplomacyCommandSelector {
val factions = gs.factions.values.toVector
val provinces = gs.provinces.values.toVector
val bestInvitation = ac.invitations
.maxBy(inv => invitationAcceptanceChance(inv.originatingFactionId, invitedFid, factions, provinces))
.maxBy(inv => invitationAcceptanceChance(inv.offeringFactionId, invitedFid, factions, provinces))
internalRequire(
bestInvitation.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_ACCEPTED),
bestInvitation.eligibleStatuses.contains(Accepted),
"Cannot handle invitation without ACCEPTED option"
)
internalRequire(
bestInvitation.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_REJECTED),
bestInvitation.eligibleStatuses.contains(Rejected),
"Cannot handle invitation without REJECTED option"
)
val bestOriginatingFid = bestInvitation.originatingFactionId
val bestOriginatingFid = bestInvitation.offeringFactionId
ResolveInvitationSelectedCommand(
ResolveInvitationSelected(
originatingFactionId = bestOriginatingFid,
resolution =
if 100.0 * roll < invitationAcceptanceChance(
@@ -126,8 +109,8 @@ object ResolveDiplomacyCommandSelector {
factions,
provinces
)
then DIPLOMACY_OFFER_STATUS_ACCEPTED
else DIPLOMACY_OFFER_STATUS_REJECTED
then Accepted
else Rejected
)
}
@@ -149,8 +132,8 @@ object ResolveDiplomacyCommandSelector {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
randomSelectionForType[ResolveTruceOfferAvailableCommand](
): RandomState[Option[ScalaCommandSelection]] =
randomSelectionForType[ResolveTruceOfferAvailable](
availableCommands,
functionalRandom
) {
@@ -162,7 +145,7 @@ object ResolveDiplomacyCommandSelector {
functionalRandom = fr
).map { resolveTruceOfferSelection =>
Some(
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = 0,
available = resolveTruceAc,
@@ -175,7 +158,7 @@ object ResolveDiplomacyCommandSelector {
private def selectionForResolveTruceOfferSelectedCommand(
actingFactionId: FactionId,
ac: ResolveTruceOfferAvailableCommand,
ac: ResolveTruceOfferAvailable,
gs: GameState,
functionalRandom: FunctionalRandom
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { roll =>
@@ -183,20 +166,20 @@ object ResolveDiplomacyCommandSelector {
val factions = gs.factions.values.toVector
val provinces = gs.provinces.values.toVector
val bestOffer = ac.offers
.maxBy(inv => truceOfferAcceptanceChance(inv.originatingFactionId, actingFid, factions, provinces))
.maxBy(inv => truceOfferAcceptanceChance(inv.offeringFactionId, actingFid, factions, provinces))
internalRequire(
bestOffer.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_ACCEPTED),
bestOffer.eligibleStatuses.contains(Accepted),
"Cannot handle truce offer without ACCEPTED option"
)
internalRequire(
bestOffer.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_REJECTED),
bestOffer.eligibleStatuses.contains(Rejected),
"Cannot handle truce offer without REJECTED option"
)
val bestOriginatingFid = bestOffer.originatingFactionId
val bestOriginatingFid = bestOffer.offeringFactionId
ResolveTruceOfferSelectedCommand(
ResolveTruceOfferSelected(
originatingFactionId = bestOriginatingFid,
resolution =
if 100.0 * roll < truceOfferAcceptanceChance(
@@ -205,8 +188,8 @@ object ResolveDiplomacyCommandSelector {
factions = factions,
provinces = provinces
)
then DIPLOMACY_OFFER_STATUS_ACCEPTED
else DIPLOMACY_OFFER_STATUS_REJECTED
then Accepted
else Rejected
)
}
@@ -270,9 +253,9 @@ object ResolveDiplomacyCommandSelector {
actingFactionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand]
): Option[CommandSelection] =
selectionForType[ResolveRansomOfferAvailableCommand](availableCommands) { resolveRansomAc =>
CommandSelection(
): Option[ScalaCommandSelection] =
selectionForType[ResolveRansomOfferAvailable](availableCommands) { resolveRansomAc =>
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = 0,
available = resolveRansomAc,
@@ -284,25 +267,26 @@ object ResolveDiplomacyCommandSelector {
}
private def selectionForResolveRansomOfferSelectedCommand(
ac: ResolveRansomOfferAvailableCommand
): SelectedCommand =
ac.offers.head.offerDetails match {
case rod: RansomOfferDetails =>
ResolveRansomOfferSelectedCommand(
offeringFactionId = ac.offers.head.originatingFactionId,
ransomOffer = Some(ac.offers.head),
resolution = RansomOfferHelpers.chosenResolution(rod)
)
case _ => throw new EagleInternalException("Not a ransom offer")
}
ac: ResolveRansomOfferAvailable
): SelectedCommand = {
val offer = ac.offers.head
// TODO: Proper scoring requires ransom details not currently in DiplomacyOfferInfo
// For now, accept all ransom offers
val resolution = if offer.eligibleStatuses.contains(Accepted) then Accepted else Rejected
ResolveRansomOfferSelected(
offeringFactionId = offer.offeringFactionId,
prisonerHeroId = offer.heroId.get,
resolution = resolution
)
}
private def resolveAllianceOfferSelectedCommand(
actingFactionId: FactionId,
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
randomSelectionForType[ResolveAllianceOfferAvailableCommand](
): RandomState[Option[ScalaCommandSelection]] =
randomSelectionForType[ResolveAllianceOfferAvailable](
availableCommands,
functionalRandom
) {
@@ -314,7 +298,7 @@ object ResolveDiplomacyCommandSelector {
functionalRandom = fr
).map { resolveAllianceOfferSelection =>
Some(
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = 0,
available = resolveAllianceAc,
@@ -330,8 +314,8 @@ object ResolveDiplomacyCommandSelector {
gameState: GameState,
availableCommands: Vector[AvailableCommand],
functionalRandom: FunctionalRandom
): RandomState[Option[CommandSelection]] =
randomSelectionForType[ResolveBreakAllianceAvailableCommand](
): RandomState[Option[ScalaCommandSelection]] =
randomSelectionForType[ResolveBreakAllianceAvailable](
availableCommands,
functionalRandom
) {
@@ -341,7 +325,7 @@ object ResolveDiplomacyCommandSelector {
functionalRandom = fr
).map { resolveBreakAllianceSelection =>
Some(
CommandSelection(
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = 0,
available = resolveBreakAllianceAc,
@@ -354,7 +338,7 @@ object ResolveDiplomacyCommandSelector {
private def selectionForResolveAllianceOfferSelectedCommand(
actingFactionId: FactionId,
ac: ResolveAllianceOfferAvailableCommand,
ac: ResolveAllianceOfferAvailable,
gs: GameState,
functionalRandom: FunctionalRandom
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { roll =>
@@ -362,20 +346,20 @@ object ResolveDiplomacyCommandSelector {
val factions = gs.factions.values.toVector
val provinces = gs.provinces.values.toVector
val bestOffer = ac.offers
.maxBy(inv => allianceOfferAcceptanceChance(inv.originatingFactionId, actingFid, factions, provinces))
.maxBy(inv => allianceOfferAcceptanceChance(inv.offeringFactionId, actingFid, factions, provinces))
internalRequire(
bestOffer.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_ACCEPTED),
bestOffer.eligibleStatuses.contains(Accepted),
"Cannot handle alliance offer without ACCEPTED option"
)
internalRequire(
bestOffer.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_REJECTED),
bestOffer.eligibleStatuses.contains(Rejected),
"Cannot handle alliance offer without REJECTED option"
)
val bestOriginatingFid = bestOffer.originatingFactionId
val bestOriginatingFid = bestOffer.offeringFactionId
ResolveAllianceOfferSelectedCommand(
ResolveAllianceOfferSelected(
originatingFactionId = bestOriginatingFid,
resolution =
if 100.0 * roll < allianceOfferAcceptanceChance(
@@ -384,26 +368,26 @@ object ResolveDiplomacyCommandSelector {
factions = factions,
provinces = provinces
)
then DIPLOMACY_OFFER_STATUS_ACCEPTED
else DIPLOMACY_OFFER_STATUS_REJECTED
then Accepted
else Rejected
)
}
// FIXME: always imprison ambassadors for now
// Note: doesn't use gameState or functionalRandom roll
private def selectionForResolveBreakAllianceSelectedCommand(
ac: ResolveBreakAllianceAvailableCommand,
ac: ResolveBreakAllianceAvailable,
functionalRandom: FunctionalRandom
): RandomState[SelectedCommand] = functionalRandom.nextDouble.map { _ =>
val offer = ac.offers.head
ResolveBreakAllianceSelectedCommand(
originatingFactionId = ac.offers.head.originatingFactionId,
ResolveBreakAllianceSelected(
originatingFactionId = offer.offeringFactionId,
resolution =
if offer.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_IMPRISONED)
then DIPLOMACY_OFFER_STATUS_IMPRISONED
else if offer.eligibleStatuses.contains(DIPLOMACY_OFFER_STATUS_ACCEPTED)
then DIPLOMACY_OFFER_STATUS_ACCEPTED
if offer.eligibleStatuses.contains(Imprisoned)
then Imprisoned
else if offer.eligibleStatuses.contains(Accepted)
then Accepted
else
throw new EagleInternalException(
"Unable to handle a break alliance command without IMPRISONED or ACCEPTED options"
@@ -1,11 +1,8 @@
package net.eagle0.eagle.ai
import net.eagle0.common.MoreOption
import net.eagle0.eagle.api.available_command.{AvailableCommand, MarchAvailableCommand}
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
import net.eagle0.eagle.common.combat_unit.CombatUnit
import net.eagle0.eagle.library.settings.{LoyaltyGainFromFeast, MinimumLoyaltyForSwearBrotherhood}
import net.eagle0.eagle.library.util.{CommandSelection, ProvinceDistances}
import net.eagle0.eagle.library.util.{ProvinceDistances, ScalaCommandSelection}
import net.eagle0.eagle.library.util.command_choice_helpers.{
CommandChoiceHelpers,
HeroGiftCommandSelector,
@@ -14,6 +11,10 @@ import net.eagle0.eagle.library.util.command_choice_helpers.{
}
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
import net.eagle0.eagle.library.util.hero.HeroUtils
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
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.province.ProvinceT
@@ -32,12 +33,12 @@ object SeekMoreLeadersCommandChooser {
case class Candidate(hero: HeroT, province: ProvinceT)
private def maybeMoveTowardDestination(
command: MarchAvailableCommand,
command: MarchAvailable,
hero: HeroT,
startingProvince: ProvinceT,
factionHeadProvince: ProvinceT,
gameState: GameState
): Option[CommandSelection] = {
): Option[ScalaCommandSelection] = {
val opmc = command.oneProvinceCommands
.find(_.originProvinceId == startingProvince.id)
.get
@@ -58,14 +59,13 @@ object SeekMoreLeadersCommandChooser {
.minByOption(_._3)
bestDestination.map { destPid =>
CommandSelection(
ScalaCommandSelection(
actingFactionId = factionHeadProvince.rulingFactionId.get,
actingProvinceId = command.actingProvinceId,
available = command,
selected = MarchSelectedCommand(
selected = MarchSelected(
marchingUnits = Vector(
CombatUnit(
factionId = factionHeadProvince.rulingFactionId.get,
heroId = hero.id,
battalionId = None
)
@@ -84,7 +84,7 @@ object SeekMoreLeadersCommandChooser {
actingFactionId: FactionId,
gameState: GameState,
acs: Vector[AvailableCommand]
): Option[CommandSelection] = {
): Option[ScalaCommandSelection] = {
val provincesVector = gameState.provinces.values.toVector
FactionUtils
.provinces(actingFactionId, provincesVector)
@@ -106,7 +106,7 @@ object SeekMoreLeadersCommandChooser {
.map(hero => Candidate(hero, province))
}
val marchACs = acs.collect { case ac: MarchAvailableCommand => ac }
val marchACs = acs.collect { case ac: MarchAvailable => ac }
val marchableCandidates = candidates.flatMap { c =>
marchACs.find { mac =>
@@ -137,7 +137,7 @@ object SeekMoreLeadersCommandChooser {
actingFactionId: FactionId,
gameState: GameState,
acs: Vector[AvailableCommand]
): Option[CommandSelection] = {
): Option[ScalaCommandSelection] = {
val faction = gameState.factions(actingFactionId)
gameState.provinces.values
.find(_.rulingFactionHeroIds.contains(faction.factionHeadId))
@@ -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,
@@ -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 }
@@ -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__",
+13 -13
View File
@@ -30,20 +30,18 @@ scala_test(
name = "fix_leader_alone_command_selector_test",
srcs = ["FixLeaderAloneCommandSelectorTest.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:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/ai:fix_leader_alone_command_selector",
"//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/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/faction",
"//src/main/scala/net/eagle0/eagle/model/state/faction/concrete",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/province",
"//src/main/scala/net/eagle0/eagle/model/state/province/concrete",
"//src/main/scala/net/eagle0/eagle/model/state/run_status",
"//src/test/scala/net/eagle0/eagle/library/util/command_choice_helpers:available_command_matcher",
"//src/test/scala/net/eagle0/eagle/library/util/command_choice_helpers:selected_command_matcher",
],
)
@@ -51,8 +49,6 @@ scala_test(
name = "invitation_command_selector_test",
srcs = ["InvitationCommandSelectorTest.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/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/ai:invitation_command_selector",
"//src/main/scala/net/eagle0/eagle/library/settings:gold_per_hero_held_back",
@@ -64,6 +60,9 @@ scala_test(
"//src/main/scala/net/eagle0/eagle/library/settings:prestige_per_supported_province",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//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/command/common",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/faction",
"//src/main/scala/net/eagle0/eagle/model/state/faction/concrete",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
@@ -77,9 +76,6 @@ scala_test(
name = "mid_game_ai_client_test",
srcs = ["MidGameAIClientTest.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/protobuf/net/eagle0/eagle/common:battalion_type_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/ai:mid_game_ai_client",
"//src/main/scala/net/eagle0/eagle/library/settings:gold_per_hero_held_back",
@@ -90,6 +86,9 @@ scala_test(
"//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/battalion/concrete",
"//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/faction/concrete",
@@ -107,13 +106,13 @@ scala_test(
name = "move_leader_to_better_province_command_chooser_test",
srcs = ["MoveLeaderToBetterProvinceCommandChooserTest.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/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/ai:move_leader_to_better_province_command_chooser",
"//src/main/scala/net/eagle0/eagle/library/settings:gold_per_hero_held_back",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//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/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/faction",
"//src/main/scala/net/eagle0/eagle/model/state/faction/concrete",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
@@ -146,8 +145,6 @@ scala_test(
name = "seek_more_leaders_command_chooser_test",
srcs = ["SeekMoreLeadersCommandChooserTest.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/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/ai:seek_more_leaders_command_chooser",
"//src/main/scala/net/eagle0/eagle/library/settings:ai_minimum_charisma_for_sworn_brother",
@@ -156,6 +153,9 @@ scala_test(
"//src/main/scala/net/eagle0/eagle/library/settings:minimum_loyalty_for_swear_brotherhood",
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
"//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/command/common",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
"//src/main/scala/net/eagle0/eagle/model/state/faction",
"//src/main/scala/net/eagle0/eagle/model/state/faction/concrete",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
@@ -4,15 +4,14 @@ import scala.util.Random
import net.eagle0.common.SeededRandom
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.api.available_command.{
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.{
AvailableDestinationProvince,
MarchAvailableCommand,
MarchAvailable,
MarchCommandFromOneProvince
}
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
import net.eagle0.eagle.common.combat_unit.CombatUnit
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandMatcher.equalAvailableCommand
import net.eagle0.eagle.library.util.command_choice_helpers.SelectedCommandMatcher.equalSelectedCommand
import net.eagle0.eagle.model.state.command.selected.CombatUnit
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.MarchSelected
import net.eagle0.eagle.model.state.faction.concrete.FactionC
import net.eagle0.eagle.model.state.faction.FactionT
import net.eagle0.eagle.model.state.game_state.GameState
@@ -23,6 +22,7 @@ import net.eagle0.eagle.model.state.RoundPhase
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatest.BeforeAndAfterEach
import org.scalatest.Inside.inside
class FixLeaderAloneCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAfterEach {
@@ -81,6 +81,35 @@ class FixLeaderAloneCommandSelectorTest extends AnyFlatSpec with Matchers with B
chronicleEntries = Vector.empty
)
private def makeMarchAvailable(
actingProvinceId: ProvinceId,
oneProvinceCommands: Vector[MarchCommandFromOneProvince]
): MarchAvailable =
MarchAvailable(
oneProvinceCommands = oneProvinceCommands,
actingProvinceId = actingProvinceId,
availableTypes = Vector.empty
)
private def makeMarchCommand(
originProvinceId: ProvinceId,
availableDestinationProvinces: Vector[AvailableDestinationProvince],
availableHeroIds: Vector[HeroId],
goldAvailable: Int = 0,
foodAvailable: Int = 0
): MarchCommandFromOneProvince =
MarchCommandFromOneProvince(
suitableBattalionsForHeroes = Map.empty,
originProvinceId = originProvinceId,
goldCost = 0,
goldAvailable = goldAvailable,
foodAvailable = foodAvailable,
availableDestinationProvinces = availableDestinationProvinces,
availableHeroIds = availableHeroIds,
availableBattalions = Vector.empty,
recommendedBattleConfig = None
)
private val province14: ProvinceC = ProvinceC(
id = 14,
rulingFactionId = Some(factionId),
@@ -117,11 +146,11 @@ class FixLeaderAloneCommandSelectorTest extends AnyFlatSpec with Matchers with B
provinces = mapifyProvinces(province14, province15, province16, province17)
)
private val availableCommands = Vector(
MarchAvailableCommand(
private val availableCommands: Vector[AvailableCommand] = Vector(
makeMarchAvailable(
actingProvinceId = factionLeaderProvinceId,
oneProvinceCommands = Vector(
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 15,
availableHeroIds = Vector(151),
availableDestinationProvinces = Vector(14, 16).map(pid =>
@@ -133,10 +162,10 @@ class FixLeaderAloneCommandSelectorTest extends AnyFlatSpec with Matchers with B
)
)
),
MarchAvailableCommand(
makeMarchAvailable(
actingProvinceId = factionLeaderProvinceId,
oneProvinceCommands = Vector(
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 16,
availableHeroIds = Vector(161),
availableDestinationProvinces = Vector(15, 17).map(pid =>
@@ -178,34 +207,21 @@ class FixLeaderAloneCommandSelectorTest extends AnyFlatSpec with Matchers with B
.newValue
.get
selection.available should equalAvailableCommand(
MarchAvailableCommand(
actingProvinceId = factionLeaderProvinceId,
oneProvinceCommands = Vector(
MarchCommandFromOneProvince(
originProvinceId = 16,
availableHeroIds = Vector(161),
availableDestinationProvinces = Vector(15, 17).map(pid =>
AvailableDestinationProvince(
provinceId = pid,
requiresRiverCrossing = false
)
)
)
)
)
)
selection.actingProvinceId shouldBe factionLeaderProvinceId
selection.selected should equalSelectedCommand(
MarchSelectedCommand(
gold = 0,
food = 0,
originProvince = 16,
destinationProvinceId = 17,
marchingUnits = Vector(
CombatUnit(factionId = factionId, heroId = 161, battalionId = None)
)
)
)
inside(selection.available) {
case marchAvailable: MarchAvailable =>
marchAvailable.actingProvinceId shouldBe factionLeaderProvinceId
marchAvailable.oneProvinceCommands should have length 1
marchAvailable.oneProvinceCommands.head.originProvinceId shouldBe 16
}
inside(selection.selected) {
case marchSelected: MarchSelected =>
marchSelected.originProvince shouldBe 16
marchSelected.destinationProvinceId shouldBe 17
marchSelected.marchingUnits should have length 1
marchSelected.marchingUnits.head.heroId shouldBe 161
}
}
}
@@ -1,9 +1,6 @@
package net.eagle0.eagle.ai
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.api.available_command.{AvailableCommand, DiplomacyAvailableCommand}
import net.eagle0.eagle.api.command.util.diplomacy_option.{InvitationOption, TruceOption}
import net.eagle0.eagle.api.selected_command.DiplomacySelectedCommand
import net.eagle0.eagle.library.settings.{
GoldPerHeroHeldBack,
MinChanceForAIInvite,
@@ -13,6 +10,10 @@ import net.eagle0.eagle.library.settings.{
MinimumTrustToInvite,
PrestigePerSupportedProvince
}
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.faction.{FactionRelationship, FactionT}
import net.eagle0.eagle.model.state.faction.concrete.FactionC
import net.eagle0.eagle.model.state.faction.FactionRelationship.RelationshipLevel.Hostile
@@ -116,13 +117,18 @@ class InvitationCommandSelectorTest extends AnyFlatSpec with Matchers with Befor
provinces = mapifyProvinces(actingProvince, province90, province91, province99)
)
private val invitationOption =
InvitationOption(targetFactionId = targetFactionId, goldCost = 1000)
private val diplomacyAvailableCommand = DiplomacyAvailableCommand(
options = Vector(
TruceOption(targetFactionId = targetFactionId),
invitationOption
),
private val invitationOption: DiplomacyOption =
DiplomacyOption(
targetFactionId = targetFactionId,
optionTypes = Vector(DiplomacyOptionType.Invitation)
)
private val truceOption: DiplomacyOption =
DiplomacyOption(
targetFactionId = targetFactionId,
optionTypes = Vector(DiplomacyOptionType.Truce)
)
private val diplomacyAvailableCommand: DiplomacyAvailable = DiplomacyAvailable(
options = Vector(truceOption, invitationOption),
availableHeroIds = Vector(15, 25, 35),
actingProvinceId = actingProvinceId,
recommendedHeroId = 25
@@ -157,26 +163,26 @@ class InvitationCommandSelectorTest extends AnyFlatSpec with Matchers with Befor
chosenCommand.available shouldBe diplomacyAvailableCommand
inside(chosenCommand.selected) {
case DiplomacySelectedCommand(
selectedOption,
targetFactionId,
sentHeroId,
_ /* unknownFieldSet */
) =>
selectedOption shouldBe invitationOption
targetFactionId shouldBe targetFactionId
case DiplomacySelected(selectedOption, selectedTargetFid, sentHeroId) =>
selectedOption shouldBe DiplomacyOptionType.Invitation
selectedTargetFid shouldBe targetFactionId
sentHeroId shouldBe 25
}
}
it should "return nothing if there is no invitation option" in {
val onlyTruceOption = DiplomacyOption(
targetFactionId = targetFactionId,
optionTypes = Vector(DiplomacyOptionType.Truce)
)
InvitationCommandSelector
.maybeInviteOtherFaction(
actingFactionId = actingFactionId,
gameState = gameState,
availableCommands = Vector(
diplomacyAvailableCommand.withOptions(
Vector(TruceOption(targetFactionId = targetFactionId))
diplomacyAvailableCommand.copy(
options = Vector(onlyTruceOption)
)
)
) shouldBe empty
@@ -1,24 +1,20 @@
package net.eagle0.eagle.ai
import net.eagle0.eagle.{BattalionId, FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.api.available_command.{
AvailableCommand,
AvailableDestinationProvince,
ImproveAvailableCommand,
MarchAvailableCommand,
MarchCommandFromOneProvince,
ReconAvailableCommand
}
import net.eagle0.eagle.api.selected_command.{ImproveSelectedCommand, MarchSelectedCommand, ReconSelectedCommand}
import net.eagle0.eagle.common.battalion_type.{
BattalionType as ProtoBattalionType,
BattalionTypeId as ProtoBattalionTypeId
}
import net.eagle0.eagle.common.improvement_type.ImprovementType
import net.eagle0.eagle.library.settings.{GoldPerHeroHeldBack, MonthsReconConsideredRecent}
import net.eagle0.eagle.model.state.{Army, BattalionType, MovingArmy, Supplies}
import net.eagle0.eagle.model.state.battalion.concrete.BattalionC
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.{
AvailableDestinationProvince,
ImproveAvailable,
MarchAvailable,
MarchCommandFromOneProvince,
ReconAvailable
}
import net.eagle0.eagle.model.state.command.common.ImprovementType
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.{ImproveSelected, MarchSelected, ReconSelected}
import net.eagle0.eagle.model.state.date.Date
import net.eagle0.eagle.model.state.faction.{FactionRelationship, FactionT}
import net.eagle0.eagle.model.state.faction.concrete.FactionC
@@ -114,6 +110,35 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
private def neighbor(provinceId: ProvinceId): Neighbor =
Neighbor(provinceId = provinceId, startingPositionIndex = 0)
private def makeMarchAvailable(
actingProvinceId: ProvinceId,
oneProvinceCommands: Vector[MarchCommandFromOneProvince]
): MarchAvailable =
MarchAvailable(
oneProvinceCommands = oneProvinceCommands,
actingProvinceId = actingProvinceId,
availableTypes = Vector.empty
)
private def makeMarchCommand(
originProvinceId: ProvinceId,
availableDestinationProvinces: Vector[AvailableDestinationProvince],
availableHeroIds: Vector[HeroId],
goldAvailable: Int = 0,
foodAvailable: Int = 0
): MarchCommandFromOneProvince =
MarchCommandFromOneProvince(
suitableBattalionsForHeroes = Map.empty,
originProvinceId = originProvinceId,
goldCost = 0,
goldAvailable = goldAvailable,
foodAvailable = foodAvailable,
availableDestinationProvinces = availableDestinationProvinces,
availableHeroIds = availableHeroIds,
availableBattalions = Vector.empty,
recommendedBattleConfig = None
)
private def date(year: Int, month: Int): Date =
Date(year = year, month = Date.Month.fromInt(month))
@@ -203,10 +228,10 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
)
val acs = Vector(
MarchAvailableCommand(
makeMarchAvailable(
actingProvinceId = 1,
oneProvinceCommands = Vector(
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 2,
availableDestinationProvinces = Vector(1, 3, 4).map(pid =>
AvailableDestinationProvince(
@@ -216,7 +241,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
),
availableHeroIds = Vector(87)
),
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 3,
availableDestinationProvinces = Vector(1, 2).map(pid =>
AvailableDestinationProvince(
@@ -242,13 +267,12 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
.get
commandSelection.actingProvinceId shouldBe 1
inside(commandSelection.selected) {
case MarchSelectedCommand(
case MarchSelected(
_, /* gold */
_, /* food */
originProvince,
destinationProvinceId,
marchingUnits,
_ /* uknownFieldSet */
marchingUnits
) =>
destinationProvinceId shouldBe 1
originProvince shouldBe 3
@@ -278,10 +302,10 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
)
val acs = Vector(
MarchAvailableCommand(
makeMarchAvailable(
actingProvinceId = 1,
oneProvinceCommands = Vector(
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 2,
availableDestinationProvinces = Vector(1, 3, 4).map(pid =>
AvailableDestinationProvince(
@@ -291,7 +315,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
),
availableHeroIds = Vector(87)
),
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 3,
availableDestinationProvinces = Vector(1, 2).map(pid =>
AvailableDestinationProvince(
@@ -337,10 +361,10 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
)
val acs = Vector(
MarchAvailableCommand(
makeMarchAvailable(
actingProvinceId = 1,
oneProvinceCommands = Vector(
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 1,
availableDestinationProvinces = Vector(2, 3, 4).map(pid =>
AvailableDestinationProvince(
@@ -350,7 +374,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
),
availableHeroIds = Vector(123, 124, 125, 126, 127, 128)
),
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 2,
availableDestinationProvinces = Vector(1, 3, 4).map(pid =>
AvailableDestinationProvince(
@@ -360,7 +384,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
),
availableHeroIds = Vector(87)
),
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 3,
availableDestinationProvinces = Vector(1, 2).map(pid =>
AvailableDestinationProvince(
@@ -406,7 +430,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
)
val acs = Vector(
ReconAvailableCommand(
ReconAvailable(
availableTargetProvinces = Vector(23),
actingProvinceId = 19,
availableHeroIds = Vector(9)
@@ -447,7 +471,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
)
val acs = Vector(
ReconAvailableCommand(
ReconAvailable(
availableTargetProvinces = Vector(23),
actingProvinceId = 19,
availableHeroIds = Vector(9)
@@ -493,7 +517,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
)
val acs = Vector(
ReconAvailableCommand(
ReconAvailable(
availableTargetProvinces = Vector(7, 23),
actingProvinceId = 19,
availableHeroIds = Vector(9)
@@ -510,11 +534,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
.selected
inside(selected) {
case ReconSelectedCommand(
actingHeroId,
targetProvinceId,
_ /* uknownFieldSet */
) =>
case ReconSelected(actingHeroId, targetProvinceId) =>
actingHeroId shouldBe 9
targetProvinceId shouldBe 23
}
@@ -553,7 +573,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
)
val acs = Vector(
ReconAvailableCommand(
ReconAvailable(
availableTargetProvinces = Vector(7, 23),
actingProvinceId = 19,
availableHeroIds = Vector(9)
@@ -570,11 +590,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
.selected
inside(selected) {
case ReconSelectedCommand(
actingHeroId,
targetProvinceId,
_ /* uknownFieldSet */
) =>
case ReconSelected(actingHeroId, targetProvinceId) =>
actingHeroId shouldBe 9
targetProvinceId shouldBe 23
}
@@ -613,7 +629,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
)
val acs = Vector(
ReconAvailableCommand(
ReconAvailable(
availableTargetProvinces = Vector(7, 23),
actingProvinceId = 19,
availableHeroIds = Vector(9)
@@ -676,7 +692,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
)
val acs = Vector(
ReconAvailableCommand(
ReconAvailable(
availableTargetProvinces = Vector(7, 23),
actingProvinceId = 19,
availableHeroIds = Vector(9)
@@ -693,11 +709,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
.selected
inside(selected) {
case ReconSelectedCommand(
actingHeroId,
targetProvinceId,
_ /* uknownFieldSet */
) =>
case ReconSelected(actingHeroId, targetProvinceId) =>
actingHeroId shouldBe 9
targetProvinceId shouldBe 7
}
@@ -731,15 +743,6 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
makeBattalionType(ScalaBattalionTypeId.Longbowmen)
)
// Proto battalion types for use in proto MarchAvailableCommand
private val protoBattalionTypes: Vector[ProtoBattalionType] = Vector(
ProtoBattalionType(typeId = ProtoBattalionTypeId.LIGHT_INFANTRY),
ProtoBattalionType(typeId = ProtoBattalionTypeId.HEAVY_INFANTRY, minimumEconomy = 60),
ProtoBattalionType(typeId = ProtoBattalionTypeId.LIGHT_CAVALRY, minimumAgriculture = 60),
ProtoBattalionType(typeId = ProtoBattalionTypeId.HEAVY_CAVALRY, minimumEconomy = 60, minimumAgriculture = 60),
ProtoBattalionType(typeId = ProtoBattalionTypeId.LONGBOWMEN)
)
private val improveForTroopsProvince: ProvinceC = ProvinceC(
id = 17,
rulingFactionId = Some(factionId),
@@ -749,17 +752,18 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
battalionIds = Vector(3)
)
private val improveForTroopsAC: ImproveAvailableCommand =
ImproveAvailableCommand(
private val improveForTroopsAC: ImproveAvailable =
ImproveAvailable(
availableTypes = Vector(
ImprovementType.ECONOMY,
ImprovementType.AGRICULTURE,
ImprovementType.INFRASTRUCTURE,
ImprovementType.DEVASTATION
ImprovementType.Economy,
ImprovementType.Agriculture,
ImprovementType.Infrastructure,
ImprovementType.Devastation
),
availableHeroIds = Vector(6, 7, 8),
recommendedHeroId = 6,
actingProvinceId = 17
actingProvinceId = 17,
lockedType = None
)
private val improveForTroopsACs: Vector[AvailableCommand] = Vector(
@@ -846,7 +850,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
command.actingProvinceId shouldBe 17
command.actingFactionId shouldBe factionId
command.selected shouldBe a[ImproveSelectedCommand]
command.selected shouldBe a[ImproveSelected]
}
it should "improve devastation if available" in {
@@ -871,7 +875,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
command.actingProvinceId shouldBe 17
command.actingFactionId shouldBe factionId
command.selected shouldBe a[ImproveSelectedCommand]
command.selected shouldBe a[ImproveSelected]
}
it should "improve economy if we're closer to that requirement" in {
@@ -886,12 +890,12 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
)
)
val acsWithoutDevastation = Vector(
improveForTroopsAC.update(
_.availableTypes := Vector(
ImprovementType.ECONOMY,
ImprovementType.AGRICULTURE,
ImprovementType.INFRASTRUCTURE
val acsWithoutDevastation: Vector[AvailableCommand] = Vector(
improveForTroopsAC.copy(
availableTypes = Vector(
ImprovementType.Economy,
ImprovementType.Agriculture,
ImprovementType.Infrastructure
)
)
)
@@ -905,13 +909,8 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
.get
inside(command.selected) {
case ImproveSelectedCommand(
improvementType,
_,
_,
_ /* uknownFieldSet */
) =>
improvementType shouldBe ImprovementType.ECONOMY
case ImproveSelected(improvementType, _, _) =>
improvementType shouldBe ImprovementType.Economy
}
}
@@ -927,12 +926,12 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
)
)
val acsWithoutDevastation = Vector(
improveForTroopsAC.update(
_.availableTypes := Vector(
ImprovementType.ECONOMY,
ImprovementType.AGRICULTURE,
ImprovementType.INFRASTRUCTURE
val acsWithoutDevastation: Vector[AvailableCommand] = Vector(
improveForTroopsAC.copy(
availableTypes = Vector(
ImprovementType.Economy,
ImprovementType.Agriculture,
ImprovementType.Infrastructure
)
)
)
@@ -946,13 +945,8 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
.get
inside(command.selected) {
case ImproveSelectedCommand(
improvementType,
_,
_,
_ /* uknownFieldSet */
) =>
improvementType shouldBe ImprovementType.AGRICULTURE
case ImproveSelected(improvementType, _, _) =>
improvementType shouldBe ImprovementType.Agriculture
}
}
@@ -977,9 +971,10 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
)
)
private val spreadLeadersAC: MarchAvailableCommand = MarchAvailableCommand(
oneProvinceCommands = Vector[MarchCommandFromOneProvince](
MarchCommandFromOneProvince(
private val spreadLeadersAC: MarchAvailable = makeMarchAvailable(
actingProvinceId = 7,
oneProvinceCommands = Vector(
makeMarchCommand(
originProvinceId = 7,
availableDestinationProvinces = Vector(12, 15).map(pid =>
AvailableDestinationProvince(
@@ -989,9 +984,7 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
),
availableHeroIds = Vector(10, 20, 30, 40, 50)
)
),
actingProvinceId = 7,
availableTypes = protoBattalionTypes
)
)
"maybeSpreadLeadersCommand" should "move a leader to a province without leaders" in {
@@ -1005,13 +998,12 @@ class MidGameAIClientTest extends AnyFlatSpec with Matchers with BeforeAndAfterE
.selected
inside(selection) {
case MarchSelectedCommand(
case MarchSelected(
gold,
food,
originProvince,
destinationProvinceId,
marchingUnits,
_ /* uknownFieldSet */
marchingUnits
) =>
originProvince shouldBe 7
destinationProvinceId shouldBe 15
@@ -1,13 +1,14 @@
package net.eagle0.eagle.ai
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.api.available_command.{
import net.eagle0.eagle.library.settings.GoldPerHeroHeldBack
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.available.AvailableCommand.{
AvailableDestinationProvince,
MarchAvailableCommand,
MarchAvailable,
MarchCommandFromOneProvince
}
import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
import net.eagle0.eagle.library.settings.GoldPerHeroHeldBack
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.MarchSelected
import net.eagle0.eagle.model.state.faction.concrete.FactionC
import net.eagle0.eagle.model.state.faction.FactionT
import net.eagle0.eagle.model.state.game_state.GameState
@@ -40,6 +41,35 @@ class MoveLeaderToBetterProvinceCommandChooserTest extends AnyFlatSpec with Matc
private def neighbor(provinceId: ProvinceId): Neighbor =
Neighbor(provinceId = provinceId, startingPositionIndex = 0)
private def makeMarchAvailable(
actingProvinceId: ProvinceId,
oneProvinceCommands: Vector[MarchCommandFromOneProvince]
): MarchAvailable =
MarchAvailable(
oneProvinceCommands = oneProvinceCommands,
actingProvinceId = actingProvinceId,
availableTypes = Vector.empty
)
private def makeMarchCommand(
originProvinceId: ProvinceId,
availableDestinationProvinces: Vector[AvailableDestinationProvince],
availableHeroIds: Vector[HeroId],
goldAvailable: Int = 0,
foodAvailable: Int = 0
): MarchCommandFromOneProvince =
MarchCommandFromOneProvince(
suitableBattalionsForHeroes = Map.empty,
originProvinceId = originProvinceId,
goldCost = 0,
goldAvailable = goldAvailable,
foodAvailable = foodAvailable,
availableDestinationProvinces = availableDestinationProvinces,
availableHeroIds = availableHeroIds,
availableBattalions = Vector.empty,
recommendedBattleConfig = None
)
private def makeFaction(
id: FactionId,
factionHeadId: HeroId = 0,
@@ -139,10 +169,10 @@ class MoveLeaderToBetterProvinceCommandChooserTest extends AnyFlatSpec with Matc
)
val acs = Vector(
MarchAvailableCommand(
makeMarchAvailable(
actingProvinceId = 1,
oneProvinceCommands = Vector(
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 2,
availableDestinationProvinces = Vector(1, 3, 4).map(pid =>
AvailableDestinationProvince(
@@ -152,7 +182,7 @@ class MoveLeaderToBetterProvinceCommandChooserTest extends AnyFlatSpec with Matc
),
availableHeroIds = Vector(87)
),
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 1,
availableDestinationProvinces = Vector(2, 3).map(pid =>
AvailableDestinationProvince(
@@ -182,13 +212,12 @@ class MoveLeaderToBetterProvinceCommandChooserTest extends AnyFlatSpec with Matc
.get
commandSelection.actingProvinceId shouldBe 1
inside(commandSelection.selected) {
case MarchSelectedCommand(
_, /* gold */
_, /* food */
case MarchSelected(
gold,
food,
originProvince,
destinationProvinceId,
marchingUnits,
_ /* uknownFieldSet */
marchingUnits
) =>
destinationProvinceId shouldBe 2
originProvince shouldBe 1
@@ -211,10 +240,10 @@ class MoveLeaderToBetterProvinceCommandChooserTest extends AnyFlatSpec with Matc
)
val acs = Vector(
MarchAvailableCommand(
makeMarchAvailable(
actingProvinceId = 1,
oneProvinceCommands = Vector(
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 1,
availableDestinationProvinces = Vector(2, 3).map(pid =>
AvailableDestinationProvince(
@@ -226,7 +255,7 @@ class MoveLeaderToBetterProvinceCommandChooserTest extends AnyFlatSpec with Matc
goldAvailable = 6000,
foodAvailable = 7000
),
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 2,
availableDestinationProvinces = Vector(1, 3, 4).map(pid =>
AvailableDestinationProvince(
@@ -256,13 +285,12 @@ class MoveLeaderToBetterProvinceCommandChooserTest extends AnyFlatSpec with Matc
.get
commandSelection.actingProvinceId shouldBe 1
inside(commandSelection.selected) {
case MarchSelectedCommand(
_, /* gold */
_, /* food */
case MarchSelected(
gold,
food,
originProvince,
destinationProvinceId,
marchingUnits,
_ /* uknownFieldSet */
marchingUnits
) =>
destinationProvinceId shouldBe 4
originProvince shouldBe 2
@@ -283,10 +311,10 @@ class MoveLeaderToBetterProvinceCommandChooserTest extends AnyFlatSpec with Matc
)
val acs = Vector(
MarchAvailableCommand(
makeMarchAvailable(
actingProvinceId = 1,
oneProvinceCommands = Vector(
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 2,
availableDestinationProvinces = Vector(1, 3, 4).map(pid =>
AvailableDestinationProvince(
@@ -296,7 +324,7 @@ class MoveLeaderToBetterProvinceCommandChooserTest extends AnyFlatSpec with Matc
),
availableHeroIds = Vector(87)
),
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 1,
availableDestinationProvinces = Vector(2, 3).map(pid =>
AvailableDestinationProvince(
@@ -1,33 +1,33 @@
package net.eagle0.eagle.ai
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.api.available_command.{
AvailableCommand,
AvailableDestinationProvince,
EligibleGift,
FeastAvailableCommand,
HeroGiftAvailableCommand,
ImproveAvailableCommand,
MarchAvailableCommand,
MarchCommandFromOneProvince,
SwearBrotherhoodAvailableCommand
}
import net.eagle0.eagle.api.available_command.SwearBrotherhoodAvailableCommand.HeroAndBackstory
import net.eagle0.eagle.api.selected_command.{
FeastSelectedCommand,
HeroGiftSelectedCommand,
MarchSelectedCommand,
SwearBrotherhoodSelectedCommand
}
import net.eagle0.eagle.common.combat_unit.CombatUnit
import net.eagle0.eagle.common.improvement_type.ImprovementType
import net.eagle0.eagle.library.settings.{
AiMinimumCharismaForSwornBrother,
AiMinimumConstitutionForSwornBrother,
LoyaltyGainFromFeast,
MinimumLoyaltyForSwearBrotherhood
}
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.{
AvailableDestinationProvince,
EligibleGift,
FeastAvailable,
HeroAndBackstory,
HeroGiftAvailable,
ImproveAvailable,
MarchAvailable,
MarchCommandFromOneProvince,
SwearBrotherhoodAvailable
}
import net.eagle0.eagle.model.state.command.common.ImprovementType
import net.eagle0.eagle.model.state.command.selected.CombatUnit
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.{
FeastSelected,
HeroGiftSelected,
MarchSelected,
SwearBrotherhoodSelected
}
import net.eagle0.eagle.model.state.faction.concrete.FactionC
import net.eagle0.eagle.model.state.faction.FactionT
import net.eagle0.eagle.model.state.game_state.GameState
@@ -50,12 +50,12 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
private def mapifyHeroes(hs: HeroT*): Map[HeroId, HeroT] = hs.map(h => h.id -> h).toMap
private def mapifyFactions(fs: FactionT*): Map[FactionId, FactionT] = fs.map(f => f.id -> f).toMap
private val swearBrotherhoodAC = SwearBrotherhoodAvailableCommand(
private val swearBrotherhoodAC: SwearBrotherhoodAvailable = SwearBrotherhoodAvailable(
actingProvinceId = 17,
availableHeroes = Vector(9, 12, 13).map(hid => HeroAndBackstory(heroId = hid))
availableHeroes = Vector(9, 12, 13).map(hid => HeroAndBackstory(heroId = hid, backstoryTextId = ""))
)
private val heroGiftAC = HeroGiftAvailableCommand(
private val heroGiftAC: HeroGiftAvailable = HeroGiftAvailable(
eligibleGifts = Vector(9, 12, 13).map { hid =>
EligibleGift(
recipientHeroId = hid,
@@ -66,8 +66,8 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
actingProvinceId = 17
)
private val feastAC =
FeastAvailableCommand(goldCost = 120, actingProvinceId = 17)
private val feastAC: FeastAvailable =
FeastAvailable(goldCost = 120, actingProvinceId = 17)
private val tryToSwearBrotherhoodACs: Vector[AvailableCommand] =
Vector(heroGiftAC, feastAC, swearBrotherhoodAC)
@@ -149,6 +149,35 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
chronicleEntries = Vector.empty
)
private def makeMarchAvailable(
actingProvinceId: ProvinceId,
oneProvinceCommands: Vector[MarchCommandFromOneProvince]
): MarchAvailable =
MarchAvailable(
oneProvinceCommands = oneProvinceCommands,
actingProvinceId = actingProvinceId,
availableTypes = Vector.empty
)
private def makeMarchCommand(
originProvinceId: ProvinceId,
availableDestinationProvinces: Vector[AvailableDestinationProvince],
availableHeroIds: Vector[HeroId],
goldAvailable: Int = 0,
foodAvailable: Int = 0
): MarchCommandFromOneProvince =
MarchCommandFromOneProvince(
suitableBattalionsForHeroes = Map.empty,
originProvinceId = originProvinceId,
goldCost = 0,
goldAvailable = goldAvailable,
foodAvailable = foodAvailable,
availableDestinationProvinces = availableDestinationProvinces,
availableHeroIds = availableHeroIds,
availableBattalions = Vector.empty,
recommendedBattleConfig = None
)
private val swearBrotherhoodGameState: GameState = makeGameState(
makeProvince(
id = 17,
@@ -172,17 +201,18 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
makeFaction(id = 4, factionHeadId = 1, leaderIds = Vector(1))
)
private val improveForTroopsAC: ImproveAvailableCommand =
ImproveAvailableCommand(
private val improveForTroopsAC: ImproveAvailable =
ImproveAvailable(
availableTypes = Vector(
ImprovementType.ECONOMY,
ImprovementType.AGRICULTURE,
ImprovementType.INFRASTRUCTURE,
ImprovementType.DEVASTATION
ImprovementType.Economy,
ImprovementType.Agriculture,
ImprovementType.Infrastructure,
ImprovementType.Devastation
),
availableHeroIds = Vector(6, 7, 8),
recommendedHeroId = 6,
actingProvinceId = 17
actingProvinceId = 17,
lockedType = None
)
private val improveForTroopsACs: Vector[AvailableCommand] = Vector(
@@ -207,10 +237,7 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
selection.available shouldBe swearBrotherhoodAC
inside(selection.selected) {
case SwearBrotherhoodSelectedCommand(
newBrotherHeroId,
_ /* unknownFieldSet */
) =>
case SwearBrotherhoodSelected(newBrotherHeroId) =>
newBrotherHeroId shouldBe 12
}
}
@@ -219,8 +246,9 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
val noOptionForMyChoice = Vector[AvailableCommand](
feastAC,
heroGiftAC,
swearBrotherhoodAC.withAvailableHeroes(
Vector(9, 13).map(hid => HeroAndBackstory(heroId = hid))
SwearBrotherhoodAvailable(
actingProvinceId = 17,
availableHeroes = Vector(9, 13).map(hid => HeroAndBackstory(heroId = hid, backstoryTextId = ""))
)
)
@@ -233,17 +261,16 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
.get
selection.available should not be swearBrotherhoodAC
selection.selected should not matchPattern {
case SwearBrotherhoodSelectedCommand(_, _ /* unknownFieldSet */ ) =>
}
selection.selected should not matchPattern { case SwearBrotherhoodSelected(_) => }
}
it should "feast if the best hero is >=85 loyalty" in {
val noOptionForMyChoice = Vector[AvailableCommand](
feastAC,
heroGiftAC,
swearBrotherhoodAC.withAvailableHeroes(
Vector(9, 13).map(hid => HeroAndBackstory(heroId = hid))
SwearBrotherhoodAvailable(
actingProvinceId = 17,
availableHeroes = Vector(9, 13).map(hid => HeroAndBackstory(heroId = hid, backstoryTextId = ""))
)
)
@@ -263,17 +290,16 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
.get
selection.available shouldBe feastAC
selection.selected should matchPattern {
case FeastSelectedCommand(_ /* unknownFieldSet */ ) =>
}
selection.selected should matchPattern { case FeastSelected => }
}
it should "give if the best hero is <85 loyalty" in {
val noOptionForMyChoice = Vector[AvailableCommand](
feastAC,
heroGiftAC,
swearBrotherhoodAC.withAvailableHeroes(
Vector(9, 13).map(hid => HeroAndBackstory(heroId = hid))
SwearBrotherhoodAvailable(
actingProvinceId = 17,
availableHeroes = Vector(9, 13).map(hid => HeroAndBackstory(heroId = hid, backstoryTextId = ""))
)
)
@@ -294,11 +320,7 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
selection.available shouldBe heroGiftAC
inside(selection.selected) {
case HeroGiftSelectedCommand(
recipientHeroId,
amount,
_ /* unknownFieldSet */
) =>
case HeroGiftSelected(recipientHeroId, amount) =>
recipientHeroId shouldBe 12
amount shouldBe 100
}
@@ -377,9 +399,10 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
makeFaction(id = 4, factionHeadId = 1, leaderIds = Vector(1))
)
val marchAC = MarchAvailableCommand(
val marchAC = makeMarchAvailable(
actingProvinceId = 17,
oneProvinceCommands = Vector(
MarchCommandFromOneProvince(
makeMarchCommand(
originProvinceId = 20,
availableDestinationProvinces = Vector(17, 909).map(pid =>
AvailableDestinationProvince(
@@ -389,9 +412,7 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
),
availableHeroIds = Vector(12, 15, 16)
)
),
actingProvinceId = 17,
availableTypes = Vector()
)
)
val moveHeroACs: Vector[AvailableCommand] =
@@ -409,18 +430,14 @@ class SeekMoreLeadersCommandChooserTest extends AnyFlatSpec with Matchers with B
)
.get
selectionWithReason shouldBe CommandSelection(
actingFactionId = 4,
actingProvinceId = 17,
available = marchAC,
selected = MarchSelectedCommand(
gold = 0,
food = 0,
originProvince = 20,
destinationProvinceId = 17,
marchingUnits = Vector(CombatUnit(factionId = 4, heroId = 12, battalionId = None))
),
reason = "Moving a good hero toward the faction leader"
)
selectionWithReason.actingProvinceId shouldBe 17
selectionWithReason.available shouldBe marchAC
inside(selectionWithReason.selected) {
case MarchSelected(_, _, originProvince, destinationProvinceId, marchingUnits) =>
originProvince shouldBe 20
destinationProvinceId shouldBe 17
marchingUnits.map(_.heroId) should contain(12)
}
selectionWithReason.reason shouldBe "Moving a good hero toward the faction leader"
}
}
@@ -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,