mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-29 01:35:42 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5d636a94c |
+22
-17
@@ -8,24 +8,27 @@ import net.eagle0.eagle.api.available_command.{
|
||||
RecommendedBattleConfig
|
||||
}
|
||||
import net.eagle0.eagle.api.command.util.battalion_with_food_cost.BattalionWithFoodCost
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.internal.game_state.GameState as GameStateProto
|
||||
import net.eagle0.eagle.library.settings.{MaxCombatUnitCountPerSide, MinVigorForMarch}
|
||||
import net.eagle0.eagle.library.util.{BattalionSuitability, LegacyBattalionUtils, ShardokMapInfo}
|
||||
import net.eagle0.eagle.library.util.{BattalionSuitability, BattalionUtils, ShardokMapInfo}
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.{CombatUnitSelector, MarchSuppliesHelpers}
|
||||
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
import net.eagle0.eagle.library.util.hero.HeroUtils
|
||||
import net.eagle0.eagle.library.util.province.ProvinceUtils
|
||||
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
|
||||
import net.eagle0.eagle.model.proto_converters.BattalionTypeConverter
|
||||
import net.eagle0.eagle.model.state.game_state.GameState
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
|
||||
object AvailableMarchCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
private def oneOriginProvinceCommand(
|
||||
gameState: GameState,
|
||||
originProvince: Province
|
||||
originProvince: ProvinceT
|
||||
): Option[MarchCommandFromOneProvince] = {
|
||||
val availableDestinations = originProvince.neighbors
|
||||
.map(_.provinceId)
|
||||
.flatMap(gameState.provinces.get)
|
||||
.filterNot(LegacyProvinceUtils.hasBlizzard)
|
||||
.map(gameState.provinces)
|
||||
.filterNot(ProvinceUtils.hasBlizzard)
|
||||
.map { destinationProvince =>
|
||||
val startingPositionIndex = destinationProvince.neighbors
|
||||
.find(n => n.provinceId == originProvince.id)
|
||||
@@ -49,14 +52,14 @@ object AvailableMarchCommandFactory extends AvailableCommandsFactoryForType {
|
||||
.map(heroId => gameState.heroes(heroId))
|
||||
.filter(_.vigor >= MinVigorForMarch.doubleValue)
|
||||
|
||||
if LegacyProvinceUtils.hasBlizzard(originProvince) then return None
|
||||
if ProvinceUtils.hasBlizzard(originProvince) then return None
|
||||
|
||||
if availableDestinations.isEmpty then return None
|
||||
if availableHeroes.isEmpty then return None
|
||||
|
||||
val availableBattalions = originProvince.battalionIds
|
||||
.map(battId => gameState.battalions(battId))
|
||||
.sortBy(_.`type`.index)
|
||||
.sortBy(_.typeId.value)
|
||||
|
||||
val recommendedSendCount =
|
||||
availableHeroes.size
|
||||
@@ -74,7 +77,7 @@ object AvailableMarchCommandFactory extends AvailableCommandsFactoryForType {
|
||||
RecommendedBattleConfig(
|
||||
units = units,
|
||||
food = MarchSuppliesHelpers.foodToTakeOnAttack(
|
||||
units.flatMap(_.battalionId).map(gameState.battalions),
|
||||
units.flatMap(_.battalionId).size,
|
||||
originProvince.food
|
||||
),
|
||||
gold = MarchSuppliesHelpers.goldToTakeOnAttack(
|
||||
@@ -90,9 +93,9 @@ object AvailableMarchCommandFactory extends AvailableCommandsFactoryForType {
|
||||
availableBattalions = availableBattalions.map { batt =>
|
||||
BattalionWithFoodCost(
|
||||
battalionId = batt.id,
|
||||
monthlyFood = LegacyBattalionUtils.monthlyConsumedFood(
|
||||
monthlyFood = BattalionUtils.monthlyConsumedFood(
|
||||
battalion = batt,
|
||||
bts = gameState.battalionTypes.toVector
|
||||
bts = gameState.battalionTypes
|
||||
)
|
||||
)
|
||||
}.toVector,
|
||||
@@ -112,11 +115,12 @@ object AvailableMarchCommandFactory extends AvailableCommandsFactoryForType {
|
||||
}
|
||||
|
||||
override def availableCommand(
|
||||
gameState: GameState,
|
||||
gameStateProto: GameStateProto,
|
||||
factionId: FactionId,
|
||||
provinceId: ProvinceId
|
||||
): Option[MarchAvailableCommand] =
|
||||
LegacyHeroUtils
|
||||
): Option[MarchAvailableCommand] = {
|
||||
val gameState = GameStateConverter.fromProto(gameStateProto)
|
||||
HeroUtils
|
||||
.consideredProvinces(gameState, factionId, provinceId)
|
||||
.flatMap(p => oneOriginProvinceCommand(gameState = gameState, originProvince = p)) match {
|
||||
case items if items.isEmpty => None
|
||||
@@ -125,8 +129,9 @@ object AvailableMarchCommandFactory extends AvailableCommandsFactoryForType {
|
||||
MarchAvailableCommand(
|
||||
actingProvinceId = provinceId,
|
||||
oneProvinceCommands = opcs,
|
||||
availableTypes = gameState.battalionTypes
|
||||
availableTypes = gameState.battalionTypes.map(BattalionTypeConverter.toProto)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,12 +447,16 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:max_combat_unit_count_per_side",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:min_vigor_for_march",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:battalion_suitability",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:legacy_battalion_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:battalion_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:shardok_map_info",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:combat_unit_selector",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:march_supplies_helpers",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:legacy_hero_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/province:legacy_province_utils",
|
||||
"//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/proto_converters:battalion_type_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -45,11 +45,10 @@ scala_library(
|
||||
],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/api/command/util:appropriate_battalions_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:battalion_type_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:profession_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:battalion_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//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/hero",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -64,10 +63,12 @@ scala_library(
|
||||
],
|
||||
exports = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:battalion_type_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type_id",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:battalion_type_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type_id",
|
||||
],
|
||||
)
|
||||
@@ -97,6 +98,7 @@ scala_library(
|
||||
deps = [
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type_id",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -10,25 +10,25 @@ import net.eagle0.eagle.api.command.util.appropriate_battalions.SuitableBattalio
|
||||
RESTRICTIVE,
|
||||
SUBOPTIMAL
|
||||
}
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionType
|
||||
import net.eagle0.eagle.common.profession.Profession.{ENGINEER, MAGE, NECROMANCER, RANGER}
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.hero.Profession.{Engineer, Mage, Necromancer, Ranger}
|
||||
import net.eagle0.eagle.model.state.BattalionType
|
||||
import net.eagle0.eagle.HeroId
|
||||
|
||||
object BattalionSuitability {
|
||||
def suitabilityLevel(h: Hero, bt: BattalionType): SuitabilityLevel =
|
||||
def suitabilityLevel(h: HeroT, bt: BattalionType): SuitabilityLevel =
|
||||
h.profession match {
|
||||
case MAGE | NECROMANCER =>
|
||||
case Mage | Necromancer =>
|
||||
if !bt.allowsCasting then RESTRICTIVE
|
||||
else if bt.alwaysArcheryCapable then SUBOPTIMAL
|
||||
else OPTIMAL
|
||||
|
||||
case ENGINEER =>
|
||||
case Engineer =>
|
||||
if bt.alwaysArcheryCapable then SUBOPTIMAL
|
||||
else OPTIMAL
|
||||
|
||||
case RANGER =>
|
||||
case Ranger =>
|
||||
if !bt.allowsStealth then RESTRICTIVE
|
||||
else OPTIMAL
|
||||
|
||||
@@ -36,8 +36,8 @@ object BattalionSuitability {
|
||||
}
|
||||
|
||||
def suitableBattalions(
|
||||
h: Hero,
|
||||
bs: Vector[Battalion],
|
||||
h: HeroT,
|
||||
bs: Vector[BattalionT],
|
||||
bts: Vector[BattalionType]
|
||||
): SuitableBattalions =
|
||||
SuitableBattalions(
|
||||
@@ -45,14 +45,14 @@ object BattalionSuitability {
|
||||
BattalionIdWithSuitability(
|
||||
battalionId = b.id,
|
||||
level = BattalionSuitability
|
||||
.suitabilityLevel(h, bts.find(_.typeId == b.`type`).get)
|
||||
.suitabilityLevel(h, bts.find(_.typeId == b.typeId).get)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def suitableBattalionsForHeroes(
|
||||
hs: Vector[Hero],
|
||||
bs: Vector[Battalion],
|
||||
hs: Vector[HeroT],
|
||||
bs: Vector[BattalionT],
|
||||
bts: Vector[BattalionType]
|
||||
): Map[HeroId, SuitableBattalions] =
|
||||
hs.map(h => h.id -> suitableBattalions(h, bs, bts)).toMap
|
||||
|
||||
@@ -4,7 +4,7 @@ import net.eagle0.eagle.common.battalion_type.{
|
||||
BattalionType as BattalionTypeProto,
|
||||
BattalionTypeId as BattalionTypeIdProto
|
||||
}
|
||||
import net.eagle0.eagle.model.state.BattalionTypeId
|
||||
import net.eagle0.eagle.model.state.{BattalionType, BattalionTypeId}
|
||||
|
||||
object BattalionTypeFinder {
|
||||
def battalionType(
|
||||
@@ -18,4 +18,10 @@ object BattalionTypeFinder {
|
||||
battalionTypes: Vector[BattalionTypeProto]
|
||||
): BattalionTypeProto =
|
||||
battalionTypes.find(_.typeId == battalionTypeId).get
|
||||
|
||||
def findBattalionType(
|
||||
battalionTypeId: BattalionTypeId,
|
||||
battalionTypes: Vector[BattalionType]
|
||||
): BattalionType =
|
||||
battalionTypes.find(_.typeId == battalionTypeId).get
|
||||
}
|
||||
|
||||
@@ -1,9 +1,24 @@
|
||||
package net.eagle0.eagle.library.util
|
||||
|
||||
import net.eagle0.eagle.model.state.{BattalionType, BattalionTypeId}
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.BattalionType
|
||||
import net.eagle0.eagle.model.state.BattalionTypeId.{
|
||||
HeavyCavalry,
|
||||
HeavyInfantry,
|
||||
LightCavalry,
|
||||
LightInfantry,
|
||||
Longbowmen
|
||||
}
|
||||
|
||||
object BattalionUtils {
|
||||
val powerMultiplier: Map[BattalionTypeId, Double] = Map(
|
||||
LightInfantry -> 1.0,
|
||||
HeavyInfantry -> 2.0,
|
||||
LightCavalry -> 2.0,
|
||||
HeavyCavalry -> 4.0,
|
||||
Longbowmen -> 1.5
|
||||
)
|
||||
|
||||
def monthlyConsumedFood(
|
||||
battalions: Iterable[BattalionT],
|
||||
bts: Vector[BattalionType]
|
||||
@@ -21,4 +36,9 @@ object BattalionUtils {
|
||||
val battalionType = bts.find(_.typeId == battalion.typeId).get
|
||||
battalion.size.toDouble * battalionType.monthlyFoodCost
|
||||
}
|
||||
|
||||
def power(batt: BattalionT): Double =
|
||||
powerMultiplier(
|
||||
batt.typeId
|
||||
) * batt.size * (1.0 + batt.armament / 100.0) + (1.0 + batt.training / 100.0)
|
||||
}
|
||||
|
||||
+65
-18
@@ -3,41 +3,42 @@ 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.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.internal.game_state.GameState as GameStateProto
|
||||
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.command_choice_helpers.FoodConsumptionUtils.foodConsumptionMonthsToHold
|
||||
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
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.model.proto_converters.hero.HeroConverter
|
||||
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
|
||||
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
|
||||
|
||||
object AlmsCommandSelector {
|
||||
|
||||
private def bestHeroWithSupport(
|
||||
availableHeroes: Vector[Hero]
|
||||
): (Hero, Double) =
|
||||
availableHeroes: Vector[HeroT]
|
||||
): (HeroT, Double) =
|
||||
availableHeroes
|
||||
.map(h => (h, AlmsCommand.supportIncreasePerFood(HeroConverter.fromProto(h))))
|
||||
.map(h => (h, AlmsCommand.supportIncreasePerFood(h)))
|
||||
.maxBy {
|
||||
case (hero, inc) =>
|
||||
(inc, -LegacyHeroUtils.fatigue(hero))
|
||||
(inc, -HeroUtils.fatigue(hero))
|
||||
}
|
||||
|
||||
private def neededSupport(province: Province): Double =
|
||||
private def neededSupport(province: ProvinceT): Double =
|
||||
MinSupportForTaxes.doubleValue - province.support
|
||||
|
||||
def surplusFood(province: Province, gameState: GameState): Int =
|
||||
def surplusFood(province: ProvinceT, gameState: GameState): Int =
|
||||
Math.max(
|
||||
0,
|
||||
province.food - foodConsumptionMonthsToHold(
|
||||
province.food.toInt - foodConsumptionMonthsToHold(
|
||||
provinceId = province.id,
|
||||
gameState = gameState
|
||||
) * LegacyProvinceUtils
|
||||
.monthlyFoodConsumption(province.id, gameState)
|
||||
) * ProvinceUtils
|
||||
.monthlyFoodConsumption(province, gameState)
|
||||
)
|
||||
|
||||
def chosenAlmsForSupportCommand(
|
||||
@@ -97,7 +98,7 @@ object AlmsCommandSelector {
|
||||
gameState: GameState,
|
||||
availableCommand: AlmsAvailableCommand,
|
||||
minimumFood: Int,
|
||||
province: Province
|
||||
province: ProvinceT
|
||||
): Option[CommandSelection] = {
|
||||
val foodAfterFeedingTroops = surplusFood(province, gameState)
|
||||
val effectiveMinimumFood = minimumFood.min(foodAfterFeedingTroops)
|
||||
@@ -109,7 +110,7 @@ object AlmsCommandSelector {
|
||||
)
|
||||
|
||||
val neededSupportPerMonth =
|
||||
neededSupport(province) / (13 - gameState.currentDate.get.month)
|
||||
neededSupport(province) / (13 - gameState.currentDate.get.month.value)
|
||||
val neededFoodForSupport =
|
||||
neededSupport(province) / supportPerFood
|
||||
val neededFoodPerMonth =
|
||||
@@ -123,7 +124,7 @@ object AlmsCommandSelector {
|
||||
|
||||
val isPartialGift =
|
||||
neededFoodForSupport > availableCommand.foodAvailable
|
||||
val isDecember = gameState.currentDate.get.month == 12
|
||||
val isDecember = gameState.currentDate.get.month.value == 12
|
||||
val haveExcessToQuellRiot =
|
||||
(foodAfterFeedingTroops - neededFoodForSupport > 1000) || province.gold > 700
|
||||
|
||||
@@ -152,4 +153,50 @@ object AlmsCommandSelector {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Proto-accepting overloads for backward compatibility
|
||||
def surplusFood(province: net.eagle0.eagle.internal.province.Province, gameState: GameStateProto): Int =
|
||||
surplusFood(
|
||||
net.eagle0.eagle.model.proto_converters.province.ProvinceConverter.fromProto(province),
|
||||
GameStateConverter.fromProto(gameState)
|
||||
)
|
||||
|
||||
def chosenAlmsForSupportCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
chosenAlmsForSupportCommand(
|
||||
actingFactionId,
|
||||
GameStateConverter.fromProto(gameState),
|
||||
availableCommands
|
||||
)
|
||||
|
||||
def chosenAlmsForSupportWithMinimumCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
minimumFood: Int
|
||||
): Option[CommandSelection] =
|
||||
chosenAlmsForSupportWithMinimumCommand(
|
||||
actingFactionId,
|
||||
GameStateConverter.fromProto(gameState),
|
||||
availableCommands,
|
||||
minimumFood
|
||||
)
|
||||
|
||||
def chosenAlmsForSupportWithMinimumCommandForProvince(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
minimumFood: Int,
|
||||
provinceId: ProvinceId
|
||||
): Option[CommandSelection] =
|
||||
chosenAlmsForSupportWithMinimumCommandForProvince(
|
||||
actingFactionId,
|
||||
GameStateConverter.fromProto(gameState),
|
||||
availableCommands,
|
||||
minimumFood,
|
||||
provinceId
|
||||
)
|
||||
}
|
||||
|
||||
+51
-20
@@ -4,22 +4,23 @@ 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.common.profession.Profession.{ENGINEER, MAGE}
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.internal.game_state.GameState as GameStateProto
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
MinVigorForMarch,
|
||||
RequiredRatioToAttack,
|
||||
RequiredRatioToAttackWithoutTakingCastles
|
||||
}
|
||||
import net.eagle0.eagle.library.util.{CommandSelection, LegacyBattalionUtils, ShardokMapInfo}
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.library.util.{BattalionUtils, CommandSelection, LegacyBattalionUtils, ShardokMapInfo}
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
|
||||
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}
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
import net.eagle0.eagle.views.battalion_view.BattalionView
|
||||
|
||||
object AttackCommandChooser {
|
||||
|
||||
import net.eagle0.eagle.library.util.DateProtoUtils._Date
|
||||
import AvailableCommandSelector.flatSelectionForType
|
||||
|
||||
private val winterMonths = Set(12, 1, 2)
|
||||
@@ -27,7 +28,7 @@ object AttackCommandChooser {
|
||||
private def heroesWithSufficientVigorForAttack(
|
||||
provinceId: ProvinceId,
|
||||
gs: GameState
|
||||
): Vector[Hero] =
|
||||
): Vector[HeroT] =
|
||||
gs.provinces(provinceId)
|
||||
.rulingFactionHeroIds
|
||||
.map(gs.heroes)
|
||||
@@ -41,10 +42,13 @@ object AttackCommandChooser {
|
||||
gameState: GameState
|
||||
): Boolean =
|
||||
destinationFactionId.exists(dfid =>
|
||||
dfid != actingFactionId && !LegacyFactionUtils
|
||||
.hasAlliance(actingFactionId, dfid, gameState) && !LegacyFactionUtils
|
||||
.truceExpirationDate(dfid, actingFactionId, gameState)
|
||||
.exists(_ > gameState.currentDate.get)
|
||||
dfid != actingFactionId && !FactionUtils
|
||||
.hasAlliance(actingFactionId, dfid, gameState.factions.values.toVector) && !FactionUtils
|
||||
.factionRelationship(dfid, actingFactionId, gameState.factions.values)
|
||||
.resetDate
|
||||
.exists(d =>
|
||||
d.year > gameState.currentDate.get.year || (d.year == gameState.currentDate.get.year && d.month.value > gameState.currentDate.get.month.value)
|
||||
)
|
||||
)
|
||||
|
||||
// Calculates the best attack for a particular destination, not taking into account whether it's a good idea --
|
||||
@@ -56,7 +60,7 @@ object AttackCommandChooser {
|
||||
goldAvailable: Int,
|
||||
foodAvailable: Int,
|
||||
originProvinceId: ProvinceId,
|
||||
destinationProvince: Province,
|
||||
destinationProvince: ProvinceT,
|
||||
gameState: GameState
|
||||
): MarchSelectedCommand = MarchSelectedCommand(
|
||||
marchingUnits = selectedUnits,
|
||||
@@ -65,7 +69,7 @@ object AttackCommandChooser {
|
||||
gold = MarchSuppliesHelpers
|
||||
.goldToTakeOnAttack(selectedUnits.size, goldAvailable),
|
||||
food = MarchSuppliesHelpers.foodToTakeOnAttack(
|
||||
selectedUnits.flatMap(_.battalionId).map(gameState.battalions),
|
||||
selectedUnits.flatMap(_.battalionId).size,
|
||||
foodAvailable
|
||||
)
|
||||
)
|
||||
@@ -81,7 +85,7 @@ object AttackCommandChooser {
|
||||
goldAvailable: Int,
|
||||
foodAvailable: Int,
|
||||
originProvinceId: ProvinceId,
|
||||
destinationProvince: Province,
|
||||
destinationProvince: ProvinceT,
|
||||
requiresRiverCrossing: Boolean,
|
||||
totalBattalionPower: Double
|
||||
)
|
||||
@@ -124,7 +128,7 @@ object AttackCommandChooser {
|
||||
desiredCount = unitCount,
|
||||
heroes = heroes,
|
||||
battalions = battalions.toVector,
|
||||
battalionTypes = gameState.battalionTypes.toVector
|
||||
battalionTypes = gameState.battalionTypes
|
||||
)
|
||||
|
||||
// Get river crossing requirement from the available destination
|
||||
@@ -140,7 +144,7 @@ object AttackCommandChooser {
|
||||
totalBattalionPower = selectedUnits
|
||||
.flatMap(_.battalionId)
|
||||
.map(gameState.battalions)
|
||||
.map(LegacyBattalionUtils.power)
|
||||
.map(BattalionUtils.power)
|
||||
.sum
|
||||
)
|
||||
}).toVector
|
||||
@@ -162,7 +166,7 @@ object AttackCommandChooser {
|
||||
.filter {
|
||||
_.destinationProvince.incomingArmies
|
||||
.exists(incomingArmy =>
|
||||
incomingArmy.getArmy.factionId == actingFactionId && incomingArmy.arrivalRound > gameState.currentRoundId
|
||||
incomingArmy.army.factionId == actingFactionId && incomingArmy.arrivalRound > gameState.currentRoundId
|
||||
)
|
||||
}
|
||||
.map {
|
||||
@@ -258,12 +262,12 @@ object AttackCommandChooser {
|
||||
.map(u => u.heroId)
|
||||
.map(gameState.heroes)
|
||||
.map(_.profession)
|
||||
.exists(prof => prof == ENGINEER || prof == MAGE)
|
||||
.exists(prof => prof == Engineer || prof == Mage)
|
||||
|
||||
// If it's winter or we have a river crossing profession, take everything.
|
||||
// Otherwise, only take those that don't require a river crossing.
|
||||
val isWinter =
|
||||
winterMonths.contains(gameState.currentDate.get.month)
|
||||
winterMonths.contains(gameState.currentDate.get.month.value)
|
||||
|
||||
val allMarchingCIs =
|
||||
if isWinter || hasRiverCrossingProfession then commandInfos
|
||||
@@ -316,4 +320,31 @@ object AttackCommandChooser {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Proto-accepting overloads for backward compatibility
|
||||
def possibleAttackCommands(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameStateProto,
|
||||
ac: MarchAvailableCommand
|
||||
): Vector[PossibleAttackCommandInfo] =
|
||||
possibleAttackCommands(
|
||||
actingFactionId,
|
||||
GameStateConverter.fromProto(gameState),
|
||||
ac
|
||||
)
|
||||
|
||||
def chosenAttackCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameStateProto,
|
||||
acs: Vector[AvailableCommand],
|
||||
knownEnemyHeroCount: ProvinceId => Int,
|
||||
knownEnemyBattalions: ProvinceId => Vector[BattalionView]
|
||||
): Option[CommandSelection] =
|
||||
chosenAttackCommand(
|
||||
actingFactionId,
|
||||
GameStateConverter.fromProto(gameState),
|
||||
acs,
|
||||
knownEnemyHeroCount,
|
||||
knownEnemyBattalions
|
||||
)
|
||||
}
|
||||
|
||||
+25
-3
@@ -11,11 +11,14 @@ import net.eagle0.eagle.api.command.util.attack_decision_type.{
|
||||
}
|
||||
import net.eagle0.eagle.api.selected_command.AttackDecisionSelectedCommand
|
||||
import net.eagle0.eagle.common.tribute_amount.TributeAmount
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.game_state.GameState as GameStateProto
|
||||
import net.eagle0.eagle.library.settings.MinimumPercentageOfEnemyToAttack
|
||||
import net.eagle0.eagle.library.util.{BattalionPower, CommandSelection, IncomingArmyUtils}
|
||||
import net.eagle0.eagle.library.util.{BattalionPower, CommandSelection}
|
||||
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.proto_converters.game_state.GameStateConverter
|
||||
import net.eagle0.eagle.model.state.game_state.GameState
|
||||
import net.eagle0.eagle.model.state.Army
|
||||
import net.eagle0.eagle.FactionId
|
||||
|
||||
object AttackDecisionCommandChooser {
|
||||
@@ -127,6 +130,11 @@ object AttackDecisionCommandChooser {
|
||||
}.flatten
|
||||
}
|
||||
|
||||
private def armyPower(army: Army, gs: GameState): Double = {
|
||||
val battalions = army.units.flatMap(_.battalionId).map(gs.battalions)
|
||||
battalions.map(BattalionPower.power).sum
|
||||
}
|
||||
|
||||
private def okToAttack(
|
||||
availableCommand: AttackDecisionAvailableCommand,
|
||||
gameState: GameState
|
||||
@@ -143,7 +151,7 @@ object AttackDecisionCommandChooser {
|
||||
.find(hag => hag.factionId == armyStats.factionId)
|
||||
.map(_.armies)
|
||||
.getOrElse(Vector.empty)
|
||||
.map(ia => IncomingArmyUtils.armyPower(ia.getArmy, gameState))
|
||||
.map(ia => armyPower(ia.army, gameState))
|
||||
.sum
|
||||
)
|
||||
.sum
|
||||
@@ -219,4 +227,18 @@ object AttackDecisionCommandChooser {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Proto-accepting overload for backward compatibility
|
||||
def attackDecisionSelectedCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
attackDecisionSelectedCommand(
|
||||
actingFactionId,
|
||||
GameStateConverter.fromProto(gameState),
|
||||
availableCommands,
|
||||
functionalRandom
|
||||
)
|
||||
}
|
||||
|
||||
@@ -26,19 +26,26 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers/quest_command_selectors:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
|
||||
],
|
||||
exports = [
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
],
|
||||
deps = [
|
||||
":available_command_selector",
|
||||
":food_consumption_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/protobuf/net/eagle0/eagle/internal:game_state_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",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:legacy_hero_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/province:legacy_province_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero",
|
||||
"//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/proto_converters/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/province",
|
||||
"//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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -49,25 +56,30 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/ai:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
|
||||
],
|
||||
exports = [
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
],
|
||||
deps = [
|
||||
":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/internal:battalion_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:province_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/views:battalion_view_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",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:required_ratio_to_attack_without_taking_castles",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:battalion_type_finder",
|
||||
"//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:date_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:legacy_battalion_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:shardok_map_info",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state",
|
||||
"//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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -78,6 +90,9 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/ai:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
|
||||
],
|
||||
exports = [
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
],
|
||||
deps = [
|
||||
":available_command_selector",
|
||||
":command_chooser",
|
||||
@@ -89,7 +104,9 @@ scala_library(
|
||||
"//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/proto_converters/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:army",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -118,12 +135,13 @@ scala_library(
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/api/command/util:appropriate_battalions_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:combat_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:battalion_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:battalion_suitability",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:battalion_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:eagle_require",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:legacy_battalion_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:legacy_hero_utils",
|
||||
"//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/hero",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -143,6 +161,8 @@ scala_library(
|
||||
"//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/proto_converters/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -198,13 +218,22 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:vassal_minimum_food_to_send_supplies",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:vassal_minimum_gold_to_send_supplies",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:battalion_type_finder",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:battalion_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:beast_utils",
|
||||
"//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/library/util:province_distances",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:hero_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:legacy_hero_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/province",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/province:legacy_province_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -233,6 +262,10 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/ai:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
|
||||
],
|
||||
exports = [
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
],
|
||||
deps = [
|
||||
":available_command_selector",
|
||||
":food_consumption_utils",
|
||||
@@ -242,12 +275,15 @@ scala_library(
|
||||
"//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",
|
||||
"//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:legacy_battalion_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:province_gold_surplus_calculator",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/province:legacy_province_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -258,10 +294,18 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/ai:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
|
||||
],
|
||||
exports = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:min_support_for_taxes",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:round_phase",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -323,9 +367,9 @@ scala_library(
|
||||
"//src/test/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:legacy_hero_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:hero_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -337,16 +381,21 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/ai:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
|
||||
],
|
||||
exports = [
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
],
|
||||
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/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:hero_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:legacy_hero_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:hero_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -358,9 +407,7 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:battalion_scala_proto",
|
||||
],
|
||||
deps = [],
|
||||
)
|
||||
|
||||
scala_library(
|
||||
@@ -380,8 +427,13 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:battalion_type_finder",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:province_gold_surplus_calculator",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:legacy_hero_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/province:legacy_province_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_type_id_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type_id",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -396,6 +448,8 @@ scala_library(
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:gold_per_hero_held_back",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
+15
-15
@@ -4,19 +4,19 @@ import scala.annotation.tailrec
|
||||
|
||||
import net.eagle0.eagle.api.command.util.appropriate_battalions.SuitableBattalions.SuitabilityLevel
|
||||
import net.eagle0.eagle.api.command.util.appropriate_battalions.SuitableBattalions.SuitabilityLevel.RESTRICTIVE
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionType
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.library.util.{BattalionSuitability, LegacyBattalionUtils}
|
||||
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||
import net.eagle0.eagle.library.util.{BattalionSuitability, BattalionUtils}
|
||||
import net.eagle0.eagle.library.util.hero.HeroUtils
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.BattalionType
|
||||
|
||||
object CombatUnitSelector {
|
||||
def selectedCombatBattalions(
|
||||
desiredCount: Int,
|
||||
heroes: Vector[Hero],
|
||||
battalions: Vector[Battalion],
|
||||
heroes: Vector[HeroT],
|
||||
battalions: Vector[BattalionT],
|
||||
battalionTypes: Vector[BattalionType]
|
||||
): Vector[CombatUnit] = {
|
||||
val suitableBattalionsMap =
|
||||
@@ -26,18 +26,18 @@ object CombatUnitSelector {
|
||||
bts = battalionTypes
|
||||
)
|
||||
|
||||
def suitability(h: Hero, b: Battalion): SuitabilityLevel =
|
||||
def suitability(h: HeroT, b: BattalionT): SuitabilityLevel =
|
||||
suitableBattalionsMap(h.id).battalionIdsWithSuitability
|
||||
.find(_.battalionId == b.id)
|
||||
.get
|
||||
.level
|
||||
|
||||
def chooseOne(hs: Vector[Hero], bs: Vector[Battalion]): CombatUnit = {
|
||||
def chooseOne(hs: Vector[HeroT], bs: Vector[BattalionT]): CombatUnit = {
|
||||
val bestBattalionOpt = bs
|
||||
.filter(b => hs.exists(h => suitability(h, b) != RESTRICTIVE))
|
||||
.maxByOption(LegacyBattalionUtils.power)
|
||||
.maxByOption(BattalionUtils.power)
|
||||
bestBattalionOpt.map { bestBattalion =>
|
||||
val bestHero = hs.minBy(h => (suitability(h, bestBattalion).index, -LegacyHeroUtils.power(h)))
|
||||
val bestHero = hs.minBy(h => (suitability(h, bestBattalion).index, -HeroUtils.power(h)))
|
||||
CombatUnit(
|
||||
heroId = bestHero.id,
|
||||
battalionId = Some(bestBattalion.id),
|
||||
@@ -46,8 +46,8 @@ object CombatUnitSelector {
|
||||
}
|
||||
.getOrElse(
|
||||
CombatUnit(
|
||||
heroId = hs.maxBy(LegacyHeroUtils.power).id,
|
||||
battalionId = bs.maxByOption(LegacyBattalionUtils.power).map(_.id),
|
||||
heroId = hs.maxBy(HeroUtils.power).id,
|
||||
battalionId = bs.maxByOption(BattalionUtils.power).map(_.id),
|
||||
factionId = hs.head.factionId.get
|
||||
)
|
||||
)
|
||||
@@ -56,8 +56,8 @@ object CombatUnitSelector {
|
||||
@tailrec
|
||||
def go(
|
||||
c: Int,
|
||||
hs: Vector[Hero],
|
||||
bs: Vector[Battalion],
|
||||
hs: Vector[HeroT],
|
||||
bs: Vector[BattalionT],
|
||||
acc: Vector[CombatUnit]
|
||||
): Vector[CombatUnit] =
|
||||
if c == 0 then acc
|
||||
|
||||
+97
-86
@@ -24,7 +24,7 @@ import net.eagle0.eagle.common.recruitment_info.RecruitmentStatus.{
|
||||
import net.eagle0.eagle.common.tribute_amount.TributeAmount
|
||||
import net.eagle0.eagle.common.unaffiliated_hero_type.UnaffiliatedHeroType.UNAFFILIATED_HERO_PRISONER
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.game_state.GameState as GameStateProto
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.internal.unaffiliated_hero.UnaffiliatedHero
|
||||
@@ -41,24 +41,32 @@ import net.eagle0.eagle.library.settings.{
|
||||
VassalMinimumFoodToSendSupplies,
|
||||
VassalMinimumGoldToSendSupplies
|
||||
}
|
||||
import net.eagle0.eagle.library.util.{BattalionTypeFinder, CommandSelection, IncomingArmyUtils, ProvinceDistances}
|
||||
import net.eagle0.eagle.library.util.{
|
||||
BattalionTypeFinder,
|
||||
BattalionUtils,
|
||||
CommandSelection,
|
||||
IncomingArmyUtils,
|
||||
ProvinceDistances
|
||||
}
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.randomSelectionForType
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChooserImplicits.*
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChooserProtoImplicits.*
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.FoodConsumptionUtils.foodConsumptionMonthsToHold
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.ProvinceGoldSurplusCalculator.{
|
||||
goldToHoldBack,
|
||||
provinceGoldSurplus
|
||||
}
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
import net.eagle0.eagle.library.util.faction_utils.{FactionUtils, LegacyFactionUtils}
|
||||
import net.eagle0.eagle.library.util.hero.{HeroUtils, LegacyHeroUtils}
|
||||
import net.eagle0.eagle.library.util.province.{LegacyProvinceUtils, ProvinceUtils}
|
||||
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
|
||||
import net.eagle0.eagle.model.state.hero.{HeroT, Profession}
|
||||
|
||||
object CommandChoiceHelpers {
|
||||
import AvailableCommandSelector.{flatSelectionForType, selectionForType}
|
||||
|
||||
def chosenUnderAttackCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
flatSelectionForType[OrganizeTroopsAvailableCommand](availableCommands) { organizeCommand =>
|
||||
@@ -80,7 +88,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def excessBeyondMaximumSupplies(
|
||||
actingProvinceId: ProvinceId,
|
||||
gameState: GameState
|
||||
gameState: GameStateProto
|
||||
): Option[Supplies] = {
|
||||
def battalionType(battalionTypeId: BattalionTypeId): BattalionType =
|
||||
BattalionTypeFinder.battalionType(
|
||||
@@ -149,7 +157,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def chosenShipExcessSuppliesCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
flatSelectionForType[SendSuppliesAvailableCommand](availableCommands) { ac =>
|
||||
@@ -166,7 +174,7 @@ object CommandChoiceHelpers {
|
||||
// Commands that should be done first regardless of the province's orders.
|
||||
def chosenUniversalCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -174,7 +182,7 @@ object CommandChoiceHelpers {
|
||||
actingFactionId = actingFactionId,
|
||||
gameState = gameState,
|
||||
availableCommands = availableCommands,
|
||||
rankedChoosers = Vector[CommandChooser](
|
||||
rankedChoosers = Vector[CommandChooserProto](
|
||||
chosenCommandWhileTraveling,
|
||||
AttackDecisionCommandChooser.attackDecisionSelectedCommand,
|
||||
(actingFactionId, gameState, availableCommands, functionalRandom) =>
|
||||
@@ -231,7 +239,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def chosenDevelopCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -239,7 +247,7 @@ object CommandChoiceHelpers {
|
||||
actingFactionId = actingFactionId,
|
||||
gameState = gameState,
|
||||
availableCommands = availableCommands,
|
||||
rankedChoosers = Vector[CommandChooser](
|
||||
rankedChoosers = Vector[CommandChooserProto](
|
||||
chosenUniversalCommand,
|
||||
(actingFactionId, gameState, availableCommands, functionalRandom) =>
|
||||
RandomState(
|
||||
@@ -275,7 +283,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def chosenMobilizeCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -283,7 +291,7 @@ object CommandChoiceHelpers {
|
||||
actingFactionId = actingFactionId,
|
||||
gameState = gameState,
|
||||
availableCommands = availableCommands,
|
||||
rankedChoosers = Vector[CommandChooser](
|
||||
rankedChoosers = Vector[CommandChooserProto](
|
||||
chosenUniversalCommand,
|
||||
(actingFactionId, gameState, availableCommands, functionalRandom) =>
|
||||
RandomState(
|
||||
@@ -352,7 +360,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def chosenEntrustCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
actingProvinceId: ProvinceId,
|
||||
functionalRandom: FunctionalRandom
|
||||
@@ -361,11 +369,11 @@ object CommandChoiceHelpers {
|
||||
actingFactionId = actingFactionId,
|
||||
gameState = gameState,
|
||||
availableCommands = availableCommands,
|
||||
rankedChoosers = Vector[CommandChooser](
|
||||
rankedChoosers = Vector[CommandChooserProto](
|
||||
chosenUniversalCommand,
|
||||
(
|
||||
fid: FactionId,
|
||||
gs: GameState,
|
||||
gs: GameStateProto,
|
||||
acs: Vector[AvailableCommand],
|
||||
fr: FunctionalRandom
|
||||
) =>
|
||||
@@ -381,14 +389,14 @@ object CommandChoiceHelpers {
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
|
||||
private def suppressBeastsValue(hero: Hero): Double =
|
||||
(hero.strength + hero.agility) / 2.0 + (if hero.profession.isNoProfession
|
||||
private def suppressBeastsValue(hero: HeroT): Double =
|
||||
(hero.strength + hero.agility) / 2.0 + (if hero.profession == Profession.NoProfession
|
||||
then 0.0
|
||||
else 50.0)
|
||||
|
||||
def chosenSuppressBeastsCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
flatSelectionForType[SuppressBeastsAvailableCommand](availableCommands) { suppressAc =>
|
||||
@@ -405,10 +413,11 @@ object CommandChoiceHelpers {
|
||||
if province.battalionIds
|
||||
.exists(b => gameState.battalions(b).size >= beastPower)
|
||||
then {
|
||||
val scalaGs = GameStateConverter.fromProto(gameState)
|
||||
val leaderId = HeroSelector
|
||||
.minimallyFatiguedHeroes(
|
||||
suppressAc.availableHeroIds
|
||||
.map(gameState.heroes)
|
||||
.map(scalaGs.heroes)
|
||||
.toVector
|
||||
)
|
||||
.maxBy(suppressBeastsValue(_))
|
||||
@@ -441,7 +450,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def chosenMobilizeIfAdjacentEnemyCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
actingProvinceId: ProvinceId,
|
||||
functionalRandom: FunctionalRandom
|
||||
@@ -460,7 +469,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def chosenExpandCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -468,12 +477,12 @@ object CommandChoiceHelpers {
|
||||
actingFactionId = actingFactionId,
|
||||
gameState = gameState,
|
||||
availableCommands = availableCommands,
|
||||
rankedChoosers = Vector[CommandChooser](
|
||||
rankedChoosers = Vector[CommandChooserProto](
|
||||
chosenUniversalCommand,
|
||||
ExpandCommandSelector.maybeChosenExpandCommand,
|
||||
(
|
||||
fid: FactionId,
|
||||
gs: GameState,
|
||||
gs: GameStateProto,
|
||||
acs: Vector[AvailableCommand],
|
||||
fr: FunctionalRandom
|
||||
) =>
|
||||
@@ -512,7 +521,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def chosenShouldRestCommand(
|
||||
actingFactionId: FactionId,
|
||||
gs: GameState,
|
||||
gs: GameStateProto,
|
||||
acs: Vector[AvailableCommand],
|
||||
reason: String
|
||||
): Option[CommandSelection] =
|
||||
@@ -540,7 +549,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def chosenRestCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
reason: String
|
||||
): Option[CommandSelection] =
|
||||
@@ -556,7 +565,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def handleCapturedHeroesSelectedCommand(
|
||||
actingFactionId: FactionId,
|
||||
gs: GameState,
|
||||
gs: GameStateProto,
|
||||
acs: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -599,7 +608,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def resolvePleaseRecruitMeSelectedCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
selectionForType[PleaseRecruitMeAvailableCommand](availableCommands) { pleaseRecruitMeAc =>
|
||||
@@ -618,7 +627,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def handleRiotGiveSelectedCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
selectionForType[HandleRiotGiveAvailableCommand](availableCommands) { availableCommand =>
|
||||
@@ -643,7 +652,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def handleRiotCrackDownSelectedCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
selectionForType[HandleRiotCrackDownAvailableCommand](availableCommands) { availableCommand =>
|
||||
@@ -663,7 +672,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def handleRiotDoNothingSelectedCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
selectionForType[HandleRiotDoNothingAvailableCommand](availableCommands) { availableCommand =>
|
||||
@@ -678,7 +687,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def handleRiotSelectedCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -686,7 +695,7 @@ object CommandChoiceHelpers {
|
||||
actingFactionId = actingFactionId,
|
||||
gameState = gameState,
|
||||
availableCommands = availableCommands,
|
||||
rankedChoosers = Vector[CommandChooser](
|
||||
rankedChoosers = Vector[CommandChooserProto](
|
||||
(actingFactionId, gameState, availableCommands, functionalRandom) =>
|
||||
RandomState(
|
||||
handleRiotGiveSelectedCommand(
|
||||
@@ -720,7 +729,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def freeForAllDecisionSelectedCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -728,7 +737,7 @@ object CommandChoiceHelpers {
|
||||
actingFactionId = actingFactionId,
|
||||
gameState = gameState,
|
||||
availableCommands = availableCommands,
|
||||
rankedChoosers = Vector[CommandChooser](
|
||||
rankedChoosers = Vector[CommandChooserProto](
|
||||
(actingFactionId, gameState, availableCommands, functionalRandom) =>
|
||||
RandomState(
|
||||
advanceFFASelectedCommand(
|
||||
@@ -753,7 +762,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def withdrawFFASelectedCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
flatSelectionForType[FreeForAllDecisionAvailableCommand](
|
||||
@@ -777,7 +786,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def advanceFFASelectedCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
flatSelectionForType[FreeForAllDecisionAvailableCommand](
|
||||
@@ -801,7 +810,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def resolveTributeSelectedCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -836,7 +845,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def defendSelectedCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -873,23 +882,25 @@ object CommandChoiceHelpers {
|
||||
}
|
||||
|
||||
def defendingUnits(
|
||||
gs: GameState,
|
||||
gs: GameStateProto,
|
||||
availableCommand: DefendAvailableCommand
|
||||
): Vector[CombatUnit] =
|
||||
): Vector[CombatUnit] = {
|
||||
val scalaGs = GameStateConverter.fromProto(gs)
|
||||
CombatUnitSelector.selectedCombatBattalions(
|
||||
desiredCount = availableCommand.availableHeroIds.size
|
||||
.min(MaxCombatUnitCountPerSide.intValue),
|
||||
heroes = availableCommand.availableHeroIds.map(gs.heroes).toVector,
|
||||
heroes = availableCommand.availableHeroIds.map(scalaGs.heroes).toVector,
|
||||
battalions = availableCommand.availableBattalions
|
||||
.map(_.battalionId)
|
||||
.map(gs.battalions)
|
||||
.map(scalaGs.battalions)
|
||||
.toVector,
|
||||
battalionTypes = gs.battalionTypes.toVector
|
||||
battalionTypes = scalaGs.battalionTypes
|
||||
)
|
||||
}
|
||||
|
||||
def foodAmountToBuy(
|
||||
province: Province,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
goldAvailable: Int,
|
||||
foodBuyPrice: Double
|
||||
): Option[Int] = {
|
||||
@@ -910,7 +921,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def maybeChosenTravelToBuyFoodCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
flatSelectionForType[TravelAvailableCommand](availableCommands) { availableCommand =>
|
||||
@@ -929,7 +940,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def chosenBuyFoodCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
flatSelectionForType[TradeAvailableCommand](availableCommands) { availableCommand =>
|
||||
@@ -957,7 +968,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def chosenImproveInfrastructureIfBelowTrainingCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
flatSelectionForType[ImproveAvailableCommand](availableCommands) { availableCommand =>
|
||||
@@ -988,7 +999,7 @@ object CommandChoiceHelpers {
|
||||
}
|
||||
}
|
||||
|
||||
def suppliesToSend(provinceId: ProvinceId, gameState: GameState): Supplies =
|
||||
def suppliesToSend(provinceId: ProvinceId, gameState: GameStateProto): Supplies =
|
||||
Supplies(
|
||||
food = provinceFoodSurplus(provinceId, gameState),
|
||||
gold = provinceGoldSurplus(provinceId, gameState)
|
||||
@@ -996,7 +1007,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def closestLeader(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
actingProvinceId: ProvinceId
|
||||
): Option[(HeroId, ProvinceId)] =
|
||||
gameState
|
||||
@@ -1027,7 +1038,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def preferredSuppliesDestination(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
ac: SendSuppliesAvailableCommand
|
||||
): Option[ProvinceId] =
|
||||
gameState
|
||||
@@ -1052,7 +1063,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def chosenSendSuppliesCommandWithSuppliesAndDestination(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
ac: SendSuppliesAvailableCommand,
|
||||
supplies: Supplies,
|
||||
destination: ProvinceId
|
||||
@@ -1073,7 +1084,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def chosenSendSuppliesCommandWithSupplies(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
ac: SendSuppliesAvailableCommand,
|
||||
supplies: Supplies
|
||||
): Option[CommandSelection] =
|
||||
@@ -1104,7 +1115,7 @@ object CommandChoiceHelpers {
|
||||
private def lowLoyaltyHeroes(
|
||||
provinceId: ProvinceId,
|
||||
heroIds: Vector[HeroId],
|
||||
gameState: GameState
|
||||
gameState: GameStateProto
|
||||
): Vector[Hero] =
|
||||
heroIds
|
||||
.filter(hid =>
|
||||
@@ -1118,7 +1129,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def chosenFeastCommandIfAvailable(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
reason: String
|
||||
): Option[CommandSelection] =
|
||||
@@ -1145,7 +1156,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def maybeChosenFeastCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
reason: String
|
||||
): Option[CommandSelection] =
|
||||
@@ -1163,7 +1174,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def maybeChosenGiftCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
reason: String
|
||||
): Option[CommandSelection] =
|
||||
@@ -1188,7 +1199,7 @@ object CommandChoiceHelpers {
|
||||
private def maybeSpreadIntoOwnedProvince(
|
||||
opmc: MarchCommandFromOneProvince,
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
ac: MarchAvailableCommand
|
||||
): Option[CommandSelection] = {
|
||||
val originProvince = gameState.provinces(opmc.originProvinceId)
|
||||
@@ -1242,7 +1253,7 @@ object CommandChoiceHelpers {
|
||||
private def maybeSpreadIntoEmptyProvince(
|
||||
opmc: MarchCommandFromOneProvince,
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
ac: MarchAvailableCommand,
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] = {
|
||||
@@ -1308,7 +1319,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def maybeChosenGetUnderHeroCapCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -1347,13 +1358,13 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def maybeChosenAttackToSaveLeaderCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] = None
|
||||
|
||||
def maybeRansomLeaderCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] = {
|
||||
@@ -1419,7 +1430,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def chosenRescueLeaderIfAllPrisonersCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -1445,7 +1456,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def chosenRescueLeaderCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -1454,7 +1465,7 @@ object CommandChoiceHelpers {
|
||||
actingFactionId = actingFactionId,
|
||||
gameState = gameState,
|
||||
availableCommands = availableCommands,
|
||||
rankedChoosers = Vector[CommandChooser](
|
||||
rankedChoosers = Vector[CommandChooserProto](
|
||||
(actingFactionId, gameState, availableCommands, functionalRandom) =>
|
||||
RandomState(
|
||||
maybeChosenAttackToSaveLeaderCommand(
|
||||
@@ -1472,7 +1483,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def chosenLoyaltyManagementCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -1481,7 +1492,7 @@ object CommandChoiceHelpers {
|
||||
actingFactionId = actingFactionId,
|
||||
gameState = gameState,
|
||||
availableCommands = availableCommands,
|
||||
rankedChoosers = Vector[CommandChooser](
|
||||
rankedChoosers = Vector[CommandChooserProto](
|
||||
maybeChosenGetUnderHeroCapCommand,
|
||||
(actingFactionId, gameState, availableCommands, functionalRandom) =>
|
||||
RandomState(
|
||||
@@ -1510,7 +1521,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def maybeChosenSendSuppliesCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
flatSelectionForType[SendSuppliesAvailableCommand](availableCommands) { ac =>
|
||||
@@ -1527,7 +1538,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def chosenCommandWhileTraveling(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -1536,7 +1547,7 @@ object CommandChoiceHelpers {
|
||||
actingFactionId = actingFactionId,
|
||||
gameState = gameState,
|
||||
availableCommands = availableCommands,
|
||||
rankedChoosers = Vector[CommandChooser](
|
||||
rankedChoosers = Vector[CommandChooserProto](
|
||||
(actingFactionId, gameState, availableCommands, functionalRandom) =>
|
||||
RandomState(
|
||||
maybeChosenRecruitCommand(
|
||||
@@ -1590,7 +1601,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def maybeChosenRecruitCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
flatSelectionForType[RecruitHeroesAvailableCommand](availableCommands) { availableCommand =>
|
||||
@@ -1610,7 +1621,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def maybeChosenDivineCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
flatSelectionForType[DivineAvailableCommand](availableCommands) { availableCommand =>
|
||||
@@ -1637,7 +1648,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def chosenReturnCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
selectionForType[ReturnAvailableCommand](availableCommands) { availableCommand =>
|
||||
@@ -1652,7 +1663,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def maybeChosenTravelToArmTroopsCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
flatSelectionForType[TravelAvailableCommand](availableCommands) { availableCommand =>
|
||||
@@ -1681,7 +1692,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def maybeMoveToRecruitCommand(
|
||||
actingFactionId: FactionId,
|
||||
gs: GameState,
|
||||
gs: GameStateProto,
|
||||
acs: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
@@ -1768,14 +1779,14 @@ object CommandChoiceHelpers {
|
||||
private def isInterestingUH(uh: UnaffiliatedHero): Boolean =
|
||||
uh.recruitmentInfo.get.status == RECRUITMENT_STATUS_WOULD_JOIN || uh.recruitmentInfo.get.status == RECRUITMENT_STATUS_NOT_DIVINED
|
||||
|
||||
private def hasInterestingUH(provinceId: ProvinceId, gs: GameState): Boolean =
|
||||
private def hasInterestingUH(provinceId: ProvinceId, gs: GameStateProto): Boolean =
|
||||
gs.provinces(provinceId)
|
||||
.unaffiliatedHeroes
|
||||
.exists(isInterestingUH)
|
||||
|
||||
def maybeChosenTravelToRecruitCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
flatSelectionForType[TravelAvailableCommand](availableCommands) { availableCommand =>
|
||||
@@ -1810,7 +1821,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
def chosenTrainCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
selectionForType[TrainAvailableCommand](availableCommands) { availableCommand =>
|
||||
@@ -1827,7 +1838,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def organizeForFightingBeastsCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
beastPower: Int
|
||||
): Option[CommandSelection] =
|
||||
@@ -1863,7 +1874,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def maybeChosenArmTroopsCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
reason: String
|
||||
): Option[CommandSelection] =
|
||||
@@ -1913,7 +1924,7 @@ object CommandChoiceHelpers {
|
||||
end if
|
||||
}
|
||||
|
||||
private def provinceFoodSurplus(provinceId: ProvinceId, gs: GameState): Int =
|
||||
private def provinceFoodSurplus(provinceId: ProvinceId, gs: GameStateProto): Int =
|
||||
(LegacyProvinceUtils.absoluteFoodSurplus(
|
||||
provinceId,
|
||||
gs
|
||||
@@ -1922,7 +1933,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def destinationCloserTo(
|
||||
actingFactionId: FactionId,
|
||||
gs: GameState,
|
||||
gs: GameStateProto,
|
||||
actingProvinceId: ProvinceId,
|
||||
availableProvinceIds: Vector[ProvinceId],
|
||||
pid: ProvinceId
|
||||
@@ -1943,7 +1954,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def topUpBattalionToFightBeastsCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
organizeCommand: OrganizeTroopsAvailableCommand,
|
||||
province: Province,
|
||||
costs: Map[BattalionTypeId, Double],
|
||||
@@ -2001,7 +2012,7 @@ object CommandChoiceHelpers {
|
||||
|
||||
private def newBattalionToFightBeastsCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState,
|
||||
gameState: GameStateProto,
|
||||
organizeCommand: AvailableCommand,
|
||||
province: Province,
|
||||
costs: Map[BattalionTypeId, Double],
|
||||
|
||||
+69
-1
@@ -4,8 +4,10 @@ import scala.annotation.tailrec
|
||||
|
||||
import net.eagle0.common.{FunctionalRandom, RandomState}
|
||||
import net.eagle0.eagle.api.available_command.AvailableCommand
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.game_state.GameState as GameStateProto
|
||||
import net.eagle0.eagle.library.util.CommandSelection
|
||||
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
|
||||
import net.eagle0.eagle.model.state.game_state.GameState
|
||||
import net.eagle0.eagle.FactionId
|
||||
|
||||
@FunctionalInterface
|
||||
@@ -35,6 +37,34 @@ object CommandChooser {
|
||||
}
|
||||
case _ => RandomState(None, functionalRandom)
|
||||
}
|
||||
|
||||
// Proto-accepting overload for backward compatibility with files still using proto GameState
|
||||
@tailrec
|
||||
def choose(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
rankedChoosers: Vector[CommandChooserProto],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] = rankedChoosers match {
|
||||
case h +: t =>
|
||||
h(actingFactionId, gameState, availableCommands, functionalRandom) match {
|
||||
case rss @ RandomState(Some(_), _) => rss
|
||||
case RandomState(None, nextFr) =>
|
||||
choose(actingFactionId, gameState, availableCommands, t, nextFr)
|
||||
}
|
||||
case _ => RandomState(None, functionalRandom)
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
trait CommandChooserProto {
|
||||
def apply(
|
||||
factionId: FactionId,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]]
|
||||
}
|
||||
|
||||
object CommandChooserImplicits {
|
||||
@@ -74,3 +104,41 @@ object CommandChooserImplicits {
|
||||
randomChooser(factionId, gameState, availableCommands, functionalRandom)
|
||||
}
|
||||
}
|
||||
|
||||
object CommandChooserProtoImplicits {
|
||||
implicit class DeterministicCommandChooserProto(
|
||||
deterministicChooser: (
|
||||
FactionId,
|
||||
GameStateProto,
|
||||
Vector[AvailableCommand]
|
||||
) => Option[CommandSelection]
|
||||
) extends CommandChooserProto {
|
||||
override def apply(
|
||||
factionId: FactionId,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
RandomState(
|
||||
deterministicChooser(factionId, gameState, availableCommands),
|
||||
functionalRandom
|
||||
)
|
||||
}
|
||||
|
||||
implicit class RandomCommandChooserProto(
|
||||
randomChooser: (
|
||||
FactionId,
|
||||
GameStateProto,
|
||||
Vector[AvailableCommand],
|
||||
FunctionalRandom
|
||||
) => RandomState[Option[CommandSelection]]
|
||||
) extends CommandChooserProto {
|
||||
override def apply(
|
||||
factionId: FactionId,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
randomChooser(factionId, gameState, availableCommands, functionalRandom)
|
||||
}
|
||||
}
|
||||
|
||||
+56
-34
@@ -7,15 +7,17 @@ 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.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.internal.game_state.GameState as GameStateProto
|
||||
import net.eagle0.eagle.library.settings.MaxCombatUnitCountPerSide
|
||||
import net.eagle0.eagle.library.util.{CommandSelection, LegacyBattalionUtils}
|
||||
import net.eagle0.eagle.library.util.{BattalionUtils, CommandSelection}
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.randomSelectionForType
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.ProvinceGoldSurplusCalculator.provinceGoldSurplus
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.library.util.province.ProvinceUtils
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
|
||||
import net.eagle0.eagle.model.state.game_state.GameState
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
|
||||
object ExpandCommandSelector {
|
||||
private case class MarchUnitsWithFood(units: Vector[CombatUnit], food: Int)
|
||||
@@ -37,7 +39,7 @@ object ExpandCommandSelector {
|
||||
|
||||
if (rulingFactionHeroIds.size < 2) // Don't abandon this province
|
||||
|| (rulingFactionHeroIds.size - rulingFactionHeroIds.count(
|
||||
LegacyFactionUtils.isFactionLeader(_, gameState)
|
||||
FactionUtils.isFactionLeader(_, gameState.factions.values)
|
||||
) < 2) // Don't leave a faction leader alone
|
||||
then randomNone
|
||||
else
|
||||
@@ -52,7 +54,7 @@ object ExpandCommandSelector {
|
||||
.map(_.provinceId)
|
||||
.toVector
|
||||
).partition(p =>
|
||||
LegacyProvinceUtils
|
||||
ProvinceUtils
|
||||
.isAdjacentEnemy(p.id, actingFactionId, gameState)
|
||||
)
|
||||
|
||||
@@ -90,20 +92,16 @@ object ExpandCommandSelector {
|
||||
gameState: GameState,
|
||||
fromProvince: ProvinceId,
|
||||
eligibleDestinations: Vector[ProvinceId]
|
||||
): Vector[Province] =
|
||||
): Vector[ProvinceT] =
|
||||
eligibleDestinations
|
||||
.map(gameState.provinces)
|
||||
.filterNot(_.rulingFactionId.isDefined)
|
||||
.filterNot(
|
||||
_.incomingArmies.exists(ma =>
|
||||
ma.army
|
||||
.map(_.factionId)
|
||||
.contains(
|
||||
gameState
|
||||
.provinces(fromProvince)
|
||||
.rulingFactionId
|
||||
.get
|
||||
)
|
||||
gameState
|
||||
.provinces(fromProvince)
|
||||
.rulingFactionId
|
||||
.contains(ma.army.factionId)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -113,7 +111,7 @@ object ExpandCommandSelector {
|
||||
gameState: GameState,
|
||||
availableCommand: MarchAvailableCommand,
|
||||
oneProvinceCommand: MarchCommandFromOneProvince,
|
||||
safeExpansionCandidateProvinces: Vector[Province],
|
||||
safeExpansionCandidateProvinces: Vector[ProvinceT],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] = {
|
||||
val province = gameState.provinces(availableCommand.actingProvinceId)
|
||||
@@ -157,7 +155,7 @@ object ExpandCommandSelector {
|
||||
gameState: GameState,
|
||||
availableCommand: MarchAvailableCommand,
|
||||
oneProvinceCommand: MarchCommandFromOneProvince,
|
||||
adjacentEnemyCandidateProvinces: Vector[Province],
|
||||
adjacentEnemyCandidateProvinces: Vector[ProvinceT],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] = {
|
||||
val province = gameState.provinces(availableCommand.actingProvinceId)
|
||||
@@ -175,7 +173,7 @@ object ExpandCommandSelector {
|
||||
else {
|
||||
// If the originating province is safe, only leave two heroes behind
|
||||
val heroCountToSend = (
|
||||
if LegacyProvinceUtils.isAdjacentEnemy(
|
||||
if ProvinceUtils.isAdjacentEnemy(
|
||||
oneProvinceCommand.originProvinceId,
|
||||
actingFactionId,
|
||||
gameState
|
||||
@@ -251,8 +249,8 @@ object ExpandCommandSelector {
|
||||
actingFactionId: FactionId,
|
||||
heroCountToSend: Int,
|
||||
goldToSend: Int,
|
||||
originatingProvince: Province,
|
||||
candidateDestinationProvinces: Vector[Province],
|
||||
originatingProvince: ProvinceT,
|
||||
candidateDestinationProvinces: Vector[ProvinceT],
|
||||
oneProvinceCommand: MarchCommandFromOneProvince,
|
||||
availableCommand: MarchAvailableCommand,
|
||||
gameState: GameState,
|
||||
@@ -268,8 +266,8 @@ object ExpandCommandSelector {
|
||||
rulingFactionHeroIds.diff(oneProvinceCommand.availableHeroIds)
|
||||
|
||||
// Don't leave a faction leader alone in the origin province
|
||||
if heroCountLeftBehind == 1 && notAvailableHeroes.size == 1 && LegacyFactionUtils
|
||||
.isFactionLeader(notAvailableHeroes.head, gameState)
|
||||
if heroCountLeftBehind == 1 && notAvailableHeroes.size == 1 && FactionUtils
|
||||
.isFactionLeader(notAvailableHeroes.head, gameState.factions.values)
|
||||
then {
|
||||
return randomNone
|
||||
}
|
||||
@@ -297,9 +295,8 @@ object ExpandCommandSelector {
|
||||
fid = actingFactionId,
|
||||
factionLeaders = gameState
|
||||
.factions(actingFactionId)
|
||||
.leaders
|
||||
.intersect(originatingProvince.rulingFactionHeroIds)
|
||||
.toVector,
|
||||
.leaderIds
|
||||
.intersect(originatingProvince.rulingFactionHeroIds),
|
||||
otherCountLeftBehind =
|
||||
originatingProvince.rulingFactionHeroIds.length - oneProvinceCommand.availableHeroIds.length,
|
||||
functionalRandom = innerFr
|
||||
@@ -310,19 +307,19 @@ object ExpandCommandSelector {
|
||||
rulingFactionHeroIds.filterNot(takenHeroes.contains)
|
||||
|
||||
internalRequire(
|
||||
takenHeroes.length > 1 || !LegacyFactionUtils
|
||||
takenHeroes.length > 1 || !FactionUtils
|
||||
.isFactionLeader(
|
||||
heroId = takenHeroes.head,
|
||||
gameState = gameState
|
||||
factions = gameState.factions.values
|
||||
),
|
||||
"trying to take only a faction leader!"
|
||||
)
|
||||
|
||||
internalRequire(
|
||||
leftHeroes.length > 1 || !LegacyFactionUtils
|
||||
leftHeroes.length > 1 || !FactionUtils
|
||||
.isFactionLeader(
|
||||
heroId = leftHeroes.head,
|
||||
gameState = gameState
|
||||
factions = gameState.factions.values
|
||||
),
|
||||
"trying to leave a faction leader alone!"
|
||||
)
|
||||
@@ -335,9 +332,9 @@ object ExpandCommandSelector {
|
||||
)
|
||||
.map(gameState.battalions)
|
||||
val monthlyFoodConsumptionInOrigin =
|
||||
LegacyBattalionUtils.monthlyConsumedFood(
|
||||
BattalionUtils.monthlyConsumedFood(
|
||||
battalionsHeldBack,
|
||||
gameState.battalionTypes.toVector
|
||||
gameState.battalionTypes
|
||||
)
|
||||
val foodToHoldBack = monthlyFoodConsumptionInOrigin *
|
||||
FoodConsumptionUtils.foodConsumptionMonthsToHold(
|
||||
@@ -424,7 +421,7 @@ object ExpandCommandSelector {
|
||||
heroCount = heroCountToSend,
|
||||
battalionCount = battalionCountToSend,
|
||||
fid = actingFactionId,
|
||||
factionLeaders = gameState.factions(actingFactionId).leaders.toVector,
|
||||
factionLeaders = gameState.factions(actingFactionId).leaderIds,
|
||||
otherCountLeftBehind = originProvince.rulingFactionHeroIds.length - opmc.availableHeroIds.length,
|
||||
functionalRandom = functionalRandom
|
||||
).map { marchingUnitsWithFood =>
|
||||
@@ -451,7 +448,7 @@ object ExpandCommandSelector {
|
||||
gameState: GameState,
|
||||
fromProvince: ProvinceId,
|
||||
eligibleDestinations: Vector[ProvinceId]
|
||||
): Vector[Province] = {
|
||||
): Vector[ProvinceT] = {
|
||||
val p = gameState.provinces(fromProvince)
|
||||
eligibleDestinations
|
||||
.map(gameState.provinces)
|
||||
@@ -533,4 +530,29 @@ object ExpandCommandSelector {
|
||||
end if
|
||||
}
|
||||
}
|
||||
|
||||
// Proto-accepting overloads for backward compatibility
|
||||
def expansionCandidateProvinces(
|
||||
gameState: GameStateProto,
|
||||
fromProvince: ProvinceId,
|
||||
eligibleDestinations: Vector[ProvinceId]
|
||||
): Vector[ProvinceT] =
|
||||
expansionCandidateProvinces(
|
||||
GameStateConverter.fromProto(gameState),
|
||||
fromProvince,
|
||||
eligibleDestinations
|
||||
)
|
||||
|
||||
def maybeChosenExpandCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Option[CommandSelection]] =
|
||||
maybeChosenExpandCommand(
|
||||
actingFactionId,
|
||||
GameStateConverter.fromProto(gameState),
|
||||
availableCommands,
|
||||
functionalRandom
|
||||
)
|
||||
}
|
||||
|
||||
+13
-6
@@ -1,10 +1,11 @@
|
||||
package net.eagle0.eagle.library.util.command_choice_helpers
|
||||
|
||||
import net.eagle0.eagle.common.date.Date
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase.FOOD_CONSUMPTION
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.game_state.GameState as GameStateProto
|
||||
import net.eagle0.eagle.library.settings.MinSupportForTaxes
|
||||
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
|
||||
import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.model.state.game_state.GameState
|
||||
import net.eagle0.eagle.model.state.RoundPhase
|
||||
import net.eagle0.eagle.ProvinceId
|
||||
|
||||
object FoodConsumptionUtils {
|
||||
@@ -12,8 +13,8 @@ object FoodConsumptionUtils {
|
||||
date: Date,
|
||||
roundPhase: RoundPhase
|
||||
): Int =
|
||||
if roundPhase.value < FOOD_CONSUMPTION.value then 13 - date.month
|
||||
else 12 - date.month
|
||||
if roundPhase.value < RoundPhase.FoodConsumption.value then 13 - date.month.value
|
||||
else 12 - date.month.value
|
||||
|
||||
private def foodConsumptionMonthsRemainingInYear(gameState: GameState): Int =
|
||||
foodConsumptionMonthsRemainingInYear(
|
||||
@@ -28,4 +29,10 @@ object FoodConsumptionUtils {
|
||||
if gameState.provinces(provinceId).support >= MinSupportForTaxes.doubleValue
|
||||
then foodConsumptionMonthsRemainingInYear(gameState)
|
||||
else foodConsumptionMonthsRemainingInYear(gameState).max(6)
|
||||
|
||||
def foodConsumptionMonthsToHold(
|
||||
provinceId: ProvinceId,
|
||||
gameState: GameStateProto
|
||||
): Int =
|
||||
foodConsumptionMonthsToHold(provinceId, GameStateConverter.fromProto(gameState))
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,12 +1,12 @@
|
||||
package net.eagle0.eagle.library.util.command_choice_helpers
|
||||
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||
import net.eagle0.eagle.library.util.hero.HeroUtils
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
|
||||
object HeroSelector {
|
||||
def minimallyFatiguedHeroes(heroes: Vector[Hero]): Vector[Hero] = {
|
||||
val minimumFatigue = heroes.map(LegacyHeroUtils.fatigue).min
|
||||
def minimallyFatiguedHeroes(heroes: Vector[HeroT]): Vector[HeroT] = {
|
||||
val minimumFatigue = heroes.map(HeroUtils.fatigue).min
|
||||
heroes
|
||||
.filter(LegacyHeroUtils.fatigue(_) == minimumFatigue)
|
||||
.filter(HeroUtils.fatigue(_) == minimumFatigue)
|
||||
}
|
||||
}
|
||||
|
||||
+34
-7
@@ -5,21 +5,23 @@ import net.eagle0.eagle.api.available_command.{AvailableCommand, ImproveAvailabl
|
||||
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.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.internal.game_state.GameState as GameStateProto
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.{
|
||||
flatSelectionForType,
|
||||
selectionForType
|
||||
}
|
||||
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||
import net.eagle0.eagle.library.util.hero.HeroUtils
|
||||
import net.eagle0.eagle.library.util.CommandSelection
|
||||
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
|
||||
import net.eagle0.eagle.model.state.game_state.GameState
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
|
||||
object ImproveCommandSelector {
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.HeroSelector.minimallyFatiguedHeroes
|
||||
|
||||
private def provinceValueForType(
|
||||
improvementType: ImprovementType,
|
||||
province: Province
|
||||
province: ProvinceT
|
||||
): Double = improvementType match {
|
||||
case ImprovementType.ECONOMY => province.economy
|
||||
case ImprovementType.AGRICULTURE => province.agriculture
|
||||
@@ -31,7 +33,7 @@ object ImproveCommandSelector {
|
||||
|
||||
private def chosenType(
|
||||
availableTypes: Vector[ImprovementType],
|
||||
province: Province
|
||||
province: ProvinceT
|
||||
): ImprovementType =
|
||||
if availableTypes.contains(DEVASTATION) then DEVASTATION
|
||||
else availableTypes.minBy(provinceValueForType(_, province))
|
||||
@@ -80,7 +82,7 @@ object ImproveCommandSelector {
|
||||
improveCommand.availableHeroIds.toVector.map(
|
||||
gameState.heroes(_)
|
||||
)
|
||||
).maxBy(LegacyHeroUtils.power(_)).id
|
||||
).maxBy(HeroUtils.power(_)).id
|
||||
),
|
||||
reason = "chosenSpecificImprovementCommand"
|
||||
)
|
||||
@@ -105,7 +107,7 @@ object ImproveCommandSelector {
|
||||
improveAvailableCommand.availableHeroIds.toVector.map(
|
||||
gameState.heroes(_)
|
||||
)
|
||||
).maxBy(LegacyHeroUtils.power(_)).id
|
||||
).maxBy(HeroUtils.power(_)).id
|
||||
),
|
||||
reason = "ImproveCommandSelector.commandSelection"
|
||||
)
|
||||
@@ -118,4 +120,29 @@ object ImproveCommandSelector {
|
||||
selectionForType[ImproveAvailableCommand](availableCommands) { improveCommand =>
|
||||
commandSelection(gameState, actingFactionId, improveCommand)
|
||||
}
|
||||
|
||||
// Proto-accepting overloads for backward compatibility
|
||||
def chosenImproveCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand]
|
||||
): Option[CommandSelection] =
|
||||
chosenImproveCommand(
|
||||
actingFactionId,
|
||||
GameStateConverter.fromProto(gameState),
|
||||
availableCommands
|
||||
)
|
||||
|
||||
def chosenSpecificImprovementCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
improvementType: ImprovementType
|
||||
): Option[CommandSelection] =
|
||||
chosenSpecificImprovementCommand(
|
||||
actingFactionId,
|
||||
GameStateConverter.fromProto(gameState),
|
||||
availableCommands,
|
||||
improvementType
|
||||
)
|
||||
}
|
||||
|
||||
+2
-4
@@ -1,14 +1,12 @@
|
||||
package net.eagle0.eagle.library.util.command_choice_helpers
|
||||
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
|
||||
object MarchSuppliesHelpers {
|
||||
def goldToTakeOnAttack(unitCount: Int, goldAvailable: Int): Int =
|
||||
goldAvailable.min(200 * unitCount)
|
||||
|
||||
def foodToTakeOnAttack(
|
||||
battalions: Vector[Battalion],
|
||||
battalionCount: Int,
|
||||
foodAvailable: Int
|
||||
): Int =
|
||||
foodAvailable.min(300 * battalions.size)
|
||||
foodAvailable.min(300 * battalionCount)
|
||||
}
|
||||
|
||||
+64
-30
@@ -5,14 +5,17 @@ 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.{BattalionType, BattalionTypeId}
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.game_state.GameState as GameStateProto
|
||||
import net.eagle0.eagle.library.util.{BattalionTypeFinder, CommandSelection}
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.AvailableCommandSelector.flatSelectionForType
|
||||
import net.eagle0.eagle.library.util.command_choice_helpers.ProvinceGoldSurplusCalculator.provinceGoldSurplus
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
import net.eagle0.eagle.library.util.province.ProvinceUtils
|
||||
import net.eagle0.eagle.library.util.BattalionTypeFinder.battalionType
|
||||
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
|
||||
import net.eagle0.eagle.model.proto_converters.BattalionTypeIdConverter
|
||||
import net.eagle0.eagle.model.state.{BattalionType, BattalionTypeId}
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.game_state.GameState
|
||||
import net.eagle0.eagle.FactionId
|
||||
|
||||
object OrganizeCommandSelector {
|
||||
@@ -32,7 +35,7 @@ object OrganizeCommandSelector {
|
||||
gold: Int,
|
||||
foodSurplus: Int,
|
||||
costs: Map[BattalionTypeId, Double],
|
||||
existingBattalions: Vector[Battalion],
|
||||
existingBattalions: Vector[BattalionT],
|
||||
battalionTypes: Vector[BattalionType],
|
||||
eligibleBattalionTypeIds: Vector[BattalionTypeId]
|
||||
): ChangedBattalionsResult = {
|
||||
@@ -40,17 +43,19 @@ object OrganizeCommandSelector {
|
||||
def go(
|
||||
gold: Int,
|
||||
foodSurplus: Int,
|
||||
battalionsInNeed: Vector[Battalion],
|
||||
battalionsInNeed: Vector[BattalionT],
|
||||
acc: Vector[ChangedBattalion]
|
||||
): ChangedBattalionsResult = battalionsInNeed match {
|
||||
case b +: bs =>
|
||||
val missing = battalionType(b.`type`, battalionTypes).capacity - b.size
|
||||
val goldCost = (missing * costs(b.`type`)).ceil.toInt
|
||||
val missing = BattalionTypeFinder.findBattalionType(b.typeId, battalionTypes).capacity - b.size
|
||||
val goldCost = (missing * costs(b.typeId)).ceil.toInt
|
||||
val foodCost =
|
||||
(missing * battalionType(
|
||||
b.`type`,
|
||||
battalionTypes
|
||||
).monthlyFoodCost).ceil.toInt
|
||||
(missing * BattalionTypeFinder
|
||||
.findBattalionType(
|
||||
b.typeId,
|
||||
battalionTypes
|
||||
)
|
||||
.monthlyFoodCost).ceil.toInt
|
||||
if goldCost <= gold && foodCost <= foodSurplus then
|
||||
go(
|
||||
gold - goldCost,
|
||||
@@ -77,9 +82,9 @@ object OrganizeCommandSelector {
|
||||
gold = gold,
|
||||
foodSurplus = foodSurplus,
|
||||
battalionsInNeed = existingBattalions
|
||||
.filter(b => eligibleBattalionTypeIds.contains(b.`type`))
|
||||
.filter(b => costs.contains(b.`type`))
|
||||
.filter(b => b.size < battalionType(b.`type`, battalionTypes).capacity),
|
||||
.filter(b => eligibleBattalionTypeIds.contains(b.typeId))
|
||||
.filter(b => costs.contains(b.typeId))
|
||||
.filter(b => b.size < BattalionTypeFinder.findBattalionType(b.typeId, battalionTypes).capacity),
|
||||
acc = Vector()
|
||||
)
|
||||
}
|
||||
@@ -90,7 +95,7 @@ object OrganizeCommandSelector {
|
||||
costs: Map[BattalionTypeId, Double],
|
||||
allBattalionTypes: Vector[BattalionType],
|
||||
eligibleBattalionTypeIds: Vector[BattalionTypeId],
|
||||
existingBattalions: Vector[Battalion],
|
||||
existingBattalions: Vector[BattalionT],
|
||||
maxCount: Int = 100
|
||||
): NewBattalionsResult = {
|
||||
val eligibleCosts = costs.filter {
|
||||
@@ -120,13 +125,13 @@ object OrganizeCommandSelector {
|
||||
|
||||
eligibleCosts.toVector.map {
|
||||
case (tid, costPerTroop) =>
|
||||
val battalionType =
|
||||
BattalionTypeFinder.battalionType(tid, allBattalionTypes)
|
||||
val cap = battalionType.capacity
|
||||
val bt =
|
||||
BattalionTypeFinder.findBattalionType(tid, allBattalionTypes)
|
||||
val cap = bt.capacity
|
||||
FoodAndGoldCosts(
|
||||
battalionTypeId = tid,
|
||||
goldCost = (costPerTroop * cap).ceil.toInt,
|
||||
monthlyFoodCost = (cap * battalionType.monthlyFoodCost).ceil.toInt
|
||||
monthlyFoodCost = (cap * bt.monthlyFoodCost).ceil.toInt
|
||||
)
|
||||
}
|
||||
.filter(_.goldCost <= gold)
|
||||
@@ -142,8 +147,8 @@ object OrganizeCommandSelector {
|
||||
.headOption match {
|
||||
case Some(x) =>
|
||||
val newBattalion = NewBattalion(
|
||||
`type` = x.battalionTypeId,
|
||||
newTroops = battalionType(x.battalionTypeId, allBattalionTypes).capacity
|
||||
`type` = BattalionTypeIdConverter.toProto(x.battalionTypeId),
|
||||
newTroops = BattalionTypeFinder.findBattalionType(x.battalionTypeId, allBattalionTypes).capacity
|
||||
)
|
||||
go(
|
||||
gold = gold - x.goldCost,
|
||||
@@ -166,7 +171,7 @@ object OrganizeCommandSelector {
|
||||
gold = gold,
|
||||
foodSurplus = foodSurplus,
|
||||
acc = Vector(),
|
||||
existingBattalionTypeCounts = existingBattalions.groupBy(_.`type`).map { case (k, v) => k -> v.size }
|
||||
existingBattalionTypeCounts = existingBattalions.groupBy(_.typeId).map { case (k, v) => k -> v.size }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -178,7 +183,7 @@ object OrganizeCommandSelector {
|
||||
reason: String
|
||||
): Option[CommandSelection] = {
|
||||
val foodSurplus =
|
||||
LegacyProvinceUtils.monthlyFoodSurplus(
|
||||
ProvinceUtils.monthlyFoodSurplus(
|
||||
gameState = gameState,
|
||||
provinceId = availableCommand.actingProvinceId
|
||||
) + extraFoodSurplus
|
||||
@@ -187,7 +192,7 @@ object OrganizeCommandSelector {
|
||||
if foodSurplus <= 0 || goldSurplus <= 0 then None
|
||||
else {
|
||||
val costs = availableCommand.troopCosts
|
||||
.map(tc => tc.`type` -> tc.costPerTroop)
|
||||
.map(tc => BattalionTypeId.fromInt(tc.`type`.value) -> tc.costPerTroop)
|
||||
.toMap
|
||||
|
||||
val toppedBattalionResults = changedBattalions(
|
||||
@@ -199,11 +204,11 @@ object OrganizeCommandSelector {
|
||||
.battalionIds
|
||||
.toVector
|
||||
.map(gameState.battalions(_)),
|
||||
battalionTypes = gameState.battalionTypes.toVector,
|
||||
battalionTypes = gameState.battalionTypes,
|
||||
eligibleBattalionTypeIds = availableCommand.availableBattalionTypes
|
||||
.filter(_.meetsRequirements)
|
||||
.toVector
|
||||
.map(_.typeId)
|
||||
.map(tid => BattalionTypeId.fromInt(tid.typeId.value))
|
||||
)
|
||||
|
||||
val newBattalionResults = newBattalions(
|
||||
@@ -217,14 +222,14 @@ object OrganizeCommandSelector {
|
||||
.provinces(availableCommand.actingProvinceId)
|
||||
.battalionIds
|
||||
.size,
|
||||
allBattalionTypes = gameState.battalionTypes.toVector.filter(bt =>
|
||||
allBattalionTypes = gameState.battalionTypes.filter(bt =>
|
||||
availableCommand.availableBattalionTypes
|
||||
.exists(tp => tp.typeId == bt.typeId && tp.meetsRequirements)
|
||||
.exists(tp => tp.typeId.value == bt.typeId.value && tp.meetsRequirements)
|
||||
),
|
||||
eligibleBattalionTypeIds = availableCommand.availableBattalionTypes
|
||||
.filter(_.meetsRequirements)
|
||||
.toVector
|
||||
.map(_.typeId),
|
||||
.map(tid => BattalionTypeId.fromInt(tid.typeId.value)),
|
||||
existingBattalions = gameState
|
||||
.provinces(availableCommand.actingProvinceId)
|
||||
.battalionIds
|
||||
@@ -279,4 +284,33 @@ object OrganizeCommandSelector {
|
||||
extraFoodSurplus = 0,
|
||||
reason = reason
|
||||
)
|
||||
|
||||
// Proto-accepting overloads for backward compatibility
|
||||
def selectedOrganizeCommand(
|
||||
actingFactionId: FactionId,
|
||||
availableCommand: OrganizeTroopsAvailableCommand,
|
||||
gameState: GameStateProto,
|
||||
extraFoodSurplus: Int,
|
||||
reason: String
|
||||
): Option[CommandSelection] =
|
||||
selectedOrganizeCommand(
|
||||
actingFactionId,
|
||||
availableCommand,
|
||||
GameStateConverter.fromProto(gameState),
|
||||
extraFoodSurplus,
|
||||
reason
|
||||
)
|
||||
|
||||
def chosenOrganizeCommand(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameStateProto,
|
||||
availableCommands: Vector[AvailableCommand],
|
||||
reason: String
|
||||
): Option[CommandSelection] =
|
||||
chosenOrganizeCommand(
|
||||
actingFactionId,
|
||||
GameStateConverter.fromProto(gameState),
|
||||
availableCommands,
|
||||
reason
|
||||
)
|
||||
}
|
||||
|
||||
+15
-1
@@ -1,7 +1,9 @@
|
||||
package net.eagle0.eagle.library.util.command_choice_helpers
|
||||
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.game_state.GameState as GameStateProto
|
||||
import net.eagle0.eagle.library.settings.GoldPerHeroHeldBack
|
||||
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
|
||||
import net.eagle0.eagle.model.state.game_state.GameState
|
||||
import net.eagle0.eagle.ProvinceId
|
||||
|
||||
object ProvinceGoldSurplusCalculator {
|
||||
@@ -15,6 +17,12 @@ object ProvinceGoldSurplusCalculator {
|
||||
.rulingFactionHeroIds
|
||||
.length).ceil.toInt
|
||||
|
||||
def goldToHoldBack(
|
||||
provinceId: ProvinceId,
|
||||
gameState: GameStateProto
|
||||
): Int =
|
||||
goldToHoldBack(provinceId, GameStateConverter.fromProto(gameState))
|
||||
|
||||
def provinceGoldSurplus(
|
||||
provinceId: ProvinceId,
|
||||
gameState: GameState
|
||||
@@ -23,4 +31,10 @@ object ProvinceGoldSurplusCalculator {
|
||||
provinceId,
|
||||
gameState
|
||||
)).max(0)
|
||||
|
||||
def provinceGoldSurplus(
|
||||
provinceId: ProvinceId,
|
||||
gameState: GameStateProto
|
||||
): Int =
|
||||
provinceGoldSurplus(provinceId, GameStateConverter.fromProto(gameState))
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package net.eagle0.eagle.library.util.faction_utils
|
||||
|
||||
import net.eagle0.eagle.{FactionId, HeroId}
|
||||
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
|
||||
import net.eagle0.eagle.library.settings.{MinSupportForTaxes, PrestigePerSupportedProvince}
|
||||
import net.eagle0.eagle.model.state.faction.{FactionRelationship, FactionT}
|
||||
import net.eagle0.eagle.model.state.faction.FactionRelationship.RelationshipLevel.Ally
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
|
||||
object FactionUtils {
|
||||
def isFactionLeader(heroId: HeroId, factions: Vector[FactionT]): Boolean =
|
||||
factions.flatMap(_.leaderIds).contains(heroId)
|
||||
def isFactionLeader(heroId: HeroId, factions: Iterable[FactionT]): Boolean =
|
||||
factions.flatMap(_.leaderIds).exists(_ == heroId)
|
||||
|
||||
def factionRelationship(
|
||||
by: FactionId,
|
||||
@@ -143,4 +143,38 @@ object FactionUtils {
|
||||
allProvinces
|
||||
.filter(_.rulingFactionId.contains(factionId))
|
||||
.count(_.support >= MinSupportForTaxes.doubleValue)
|
||||
|
||||
def isFactionHead(heroId: HeroId, factions: Iterable[FactionT]): Boolean =
|
||||
factions.exists(_.factionHeadId == heroId)
|
||||
|
||||
def leadersBesidesHead(factionId: FactionId, factions: Iterable[FactionT]): Vector[HeroId] =
|
||||
factions
|
||||
.find(_.id == factionId)
|
||||
.map(f => f.leaderIds.filterNot(_ == f.factionHeadId))
|
||||
.getOrElse(Vector.empty)
|
||||
|
||||
def factionHeadId(factionId: FactionId, factions: Iterable[FactionT]): Option[HeroId] =
|
||||
factions.find(_.id == factionId).map(_.factionHeadId)
|
||||
|
||||
def ownedNeighbors(
|
||||
provinces: ProvinceId => ProvinceT,
|
||||
fid: FactionId
|
||||
): ProvinceId => Vector[ProvinceId] =
|
||||
pid =>
|
||||
provinces(pid).neighbors
|
||||
.map(_.provinceId)
|
||||
.filter(n => provinces(n).rulingFactionId.contains(fid))
|
||||
|
||||
def hostileNeighbors(
|
||||
province: ProvinceT,
|
||||
fid: FactionId,
|
||||
factions: Vector[FactionT],
|
||||
provinces: ProvinceId => ProvinceT
|
||||
): Vector[ProvinceT] =
|
||||
province.neighbors
|
||||
.map(_.provinceId)
|
||||
.map(provinces)
|
||||
.filter(_.rulingFactionId.isDefined)
|
||||
.filterNot(_.rulingFactionId.contains(fid))
|
||||
.filterNot(neighbor => hasTruceOrAlliance(fid, neighbor.rulingFactionId.get, factions))
|
||||
}
|
||||
|
||||
@@ -14,8 +14,10 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//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/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero:gender",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@ package net.eagle0.eagle.library.util.hero
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.model.state.faction.FactionT
|
||||
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.NoProfession
|
||||
import net.eagle0.eagle.HeroId
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
|
||||
object HeroUtils {
|
||||
|
||||
@@ -81,4 +83,39 @@ object HeroUtils {
|
||||
|
||||
def sortOrdering(factionLeaderIds: Vector[HeroId]): Ordering[HeroT] =
|
||||
Ordering.fromLessThan(sortOrderer(factionLeaderIds))
|
||||
|
||||
def fatigue(hero: HeroT): Double = hero.constitution - hero.vigor
|
||||
|
||||
def seniorityOrder(faction: FactionT, heroId: HeroId): Int =
|
||||
faction.leaderIds.indexOf(heroId) match {
|
||||
case -1 => Int.MaxValue
|
||||
case idx => idx
|
||||
}
|
||||
|
||||
// For commands where the faction leader can act on other provinces; returns the list of provinces to consider
|
||||
// for this hero. For a vassal, considers just the one province, but for faction leader returns all owned provinces
|
||||
// that did not contain other faction leaders. Always returns the provinceId as the first element.
|
||||
def consideredProvinces(
|
||||
gameState: GameState,
|
||||
factionId: FactionId,
|
||||
provinceId: ProvinceId
|
||||
): Vector[ProvinceT] =
|
||||
if gameState
|
||||
.provinces(provinceId)
|
||||
.rulingHeroId
|
||||
.exists(hid => FactionUtils.isFactionHead(hid, gameState.factions.values))
|
||||
then
|
||||
gameState.provinces.values
|
||||
.filter(_.rulingFactionId.contains(factionId))
|
||||
.filterNot(p =>
|
||||
p.rulingFactionHeroIds.exists(
|
||||
FactionUtils.leadersBesidesHead(factionId, gameState.factions.values).contains
|
||||
) && !p.rulingFactionHeroIds
|
||||
.exists(
|
||||
FactionUtils.factionHeadId(factionId, gameState.factions.values).contains
|
||||
)
|
||||
)
|
||||
.toVector
|
||||
.sortBy(p => if p.id == provinceId then 0 else p.id)
|
||||
else Vector(gameState.provinces(provinceId))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package net.eagle0.eagle.library.util.province
|
||||
|
||||
import net.eagle0.eagle.{FactionId, HeroId, RoundId}
|
||||
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId, RoundId}
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionType as BattalionTypeProto
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
BaseResourceLimit,
|
||||
@@ -258,6 +258,72 @@ object ProvinceUtils {
|
||||
province.rulingFactionId.nonEmpty && province.rulingFactionHeroIds.isEmpty
|
||||
)(abandonedProvinceCP(province))
|
||||
|
||||
def isAdjacentEnemy(
|
||||
provinceId: ProvinceId,
|
||||
factionId: FactionId,
|
||||
gameState: GameState
|
||||
): Boolean =
|
||||
gameState
|
||||
.provinces(provinceId)
|
||||
.neighbors
|
||||
.map(_.provinceId)
|
||||
.map(gameState.provinces)
|
||||
.exists(
|
||||
_.rulingFactionId.exists(_ != factionId)
|
||||
)
|
||||
|
||||
def adjacentHostiles(
|
||||
provinceId: ProvinceId,
|
||||
gameState: GameState
|
||||
): Vector[ProvinceT] = {
|
||||
val baseProvince = gameState.provinces(provinceId)
|
||||
baseProvince.neighbors
|
||||
.map(_.provinceId)
|
||||
.map(gameState.provinces)
|
||||
.filter(_.rulingFactionId.isDefined)
|
||||
.filter(_.rulingFactionId != baseProvince.rulingFactionId)
|
||||
}
|
||||
|
||||
def locationOf(
|
||||
heroId: HeroId,
|
||||
provinces: Iterable[ProvinceT]
|
||||
): Option[ProvinceId] =
|
||||
provinces
|
||||
.find(p =>
|
||||
(p.rulingFactionHeroIds ++ p.unaffiliatedHeroes.map(_.heroId))
|
||||
.contains(heroId)
|
||||
)
|
||||
.map(_.id)
|
||||
|
||||
def absoluteFoodSurplus(provinceId: ProvinceId, gameState: GameState): Int = {
|
||||
val province = gameState.provinces(provinceId)
|
||||
val monthlyConsumption = monthlyFoodConsumption(province, gameState)
|
||||
val annualProduction = annualFoodProduction(province).getOrElse(0)
|
||||
if annualProduction / 12 < monthlyConsumption then 0
|
||||
else {
|
||||
val currentMonth = gameState.currentDate.map(_.month.value).getOrElse(1)
|
||||
(province.food - monthlyConsumption * (12 - currentMonth)).max(0).toInt
|
||||
}
|
||||
}
|
||||
|
||||
def containsFactionLeader(pid: ProvinceId, gs: GameState): Boolean =
|
||||
gs.provinces(pid).rulingFactionId.exists { fid =>
|
||||
gs.provinces(pid)
|
||||
.rulingFactionHeroIds
|
||||
.contains(gs.factions(fid).factionHeadId)
|
||||
}
|
||||
|
||||
def monthlyFoodSurplus(gameState: GameState, provinceId: ProvinceId): Int = {
|
||||
val province = gameState.provinces(provinceId)
|
||||
val consumption = monthlyFoodConsumption(province, gameState)
|
||||
val monthsRemaining = 12 - gameState.currentDate.get.month.value
|
||||
val monthlyAvailable =
|
||||
if monthsRemaining == 0 then Int.MaxValue
|
||||
else (province.food / monthsRemaining).toInt
|
||||
val monthlyProduction = annualFoodProduction(province).getOrElse(0) / 12
|
||||
monthlyProduction.min(monthlyAvailable) - consumption
|
||||
}
|
||||
|
||||
def afterHeroDeparture(
|
||||
cp: ChangedProvinceT,
|
||||
province: ProvinceT
|
||||
|
||||
@@ -106,6 +106,7 @@ scala_library(
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle/library:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/view_filters:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/province:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/view/battalion:__pkg__",
|
||||
@@ -127,6 +128,7 @@ scala_library(
|
||||
srcs = ["BattalionTypeConverter.scala"],
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle/library:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
|
||||
@@ -9,6 +9,7 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/applier:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/service:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/library:__pkg__",
|
||||
|
||||
@@ -7,6 +7,7 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
|
||||
@@ -7,6 +7,7 @@ scala_library(
|
||||
":__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/province:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/validations:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/view_filters:__pkg__",
|
||||
|
||||
@@ -54,6 +54,7 @@ scala_library(
|
||||
"//src/test/scala/net/eagle0/eagle/library/util:__subpackages__",
|
||||
],
|
||||
exports = [
|
||||
":gender",
|
||||
":profession",
|
||||
],
|
||||
deps = [
|
||||
|
||||
+29
-10
@@ -6,6 +6,7 @@ import net.eagle0.eagle.common.battalion_type.BattalionTypeId
|
||||
import net.eagle0.eagle.common.date.Date
|
||||
import net.eagle0.eagle.common.gender.Gender.GENDER_OTHER
|
||||
import net.eagle0.eagle.common.profession.Profession.{NO_PROFESSION, PALADIN}
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
@@ -52,7 +53,8 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
pronounGender = GENDER_OTHER
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 12))
|
||||
currentDate = Some(Date(year = 353, month = 12)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS
|
||||
)
|
||||
|
||||
val ac = AlmsAvailableCommand(
|
||||
@@ -101,7 +103,8 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
pronounGender = GENDER_OTHER
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 12))
|
||||
currentDate = Some(Date(year = 353, month = 12)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS
|
||||
)
|
||||
|
||||
val ac = AlmsAvailableCommand(
|
||||
@@ -150,7 +153,8 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
pronounGender = GENDER_OTHER
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 12))
|
||||
currentDate = Some(Date(year = 353, month = 12)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS
|
||||
)
|
||||
|
||||
val ac = AlmsAvailableCommand(
|
||||
@@ -191,7 +195,8 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
pronounGender = GENDER_OTHER
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 12))
|
||||
currentDate = Some(Date(year = 353, month = 12)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS
|
||||
)
|
||||
|
||||
val ac = AlmsAvailableCommand(
|
||||
@@ -232,7 +237,8 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
pronounGender = GENDER_OTHER
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 12))
|
||||
currentDate = Some(Date(year = 353, month = 12)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS
|
||||
)
|
||||
|
||||
val ac = AlmsAvailableCommand(
|
||||
@@ -281,7 +287,8 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
pronounGender = GENDER_OTHER
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 11))
|
||||
currentDate = Some(Date(year = 353, month = 11)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS
|
||||
)
|
||||
|
||||
val ac = AlmsAvailableCommand(
|
||||
@@ -330,7 +337,8 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
pronounGender = GENDER_OTHER
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 11))
|
||||
currentDate = Some(Date(year = 353, month = 11)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS
|
||||
)
|
||||
|
||||
val ac = AlmsAvailableCommand(
|
||||
@@ -379,7 +387,8 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
pronounGender = GENDER_OTHER
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 11))
|
||||
currentDate = Some(Date(year = 353, month = 11)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS
|
||||
)
|
||||
|
||||
val ac = AlmsAvailableCommand(
|
||||
@@ -428,7 +437,8 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
pronounGender = GENDER_OTHER
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 11))
|
||||
currentDate = Some(Date(year = 353, month = 11)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS
|
||||
)
|
||||
|
||||
val ac = AlmsAvailableCommand(
|
||||
@@ -474,6 +484,7 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
Battalion(id = 85, `type` = BattalionTypeId.HEAVY_CAVALRY, size = 600)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 11)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -516,6 +527,7 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -558,6 +570,7 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -609,6 +622,7 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -659,6 +673,7 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 12)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -709,6 +724,7 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -758,7 +774,8 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
pronounGender = GENDER_OTHER
|
||||
)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 11))
|
||||
currentDate = Some(Date(year = 353, month = 11)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS
|
||||
)
|
||||
|
||||
val ac = AlmsAvailableCommand(
|
||||
@@ -813,6 +830,7 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
Battalion(id = 85, `type` = BattalionTypeId.HEAVY_CAVALRY, size = 600)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 11)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -871,6 +889,7 @@ class AlmsCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAf
|
||||
Battalion(id = 85, `type` = BattalionTypeId.HEAVY_CAVALRY, size = 600)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 11)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
|
||||
+2
@@ -6,6 +6,7 @@ import net.eagle0.eagle.api.selected_command.MarchSelectedCommand
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionTypeId.LIGHT_INFANTRY
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.common.date.Date
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.faction.Faction
|
||||
import net.eagle0.eagle.internal.faction_relationship.FactionRelationship
|
||||
@@ -96,6 +97,7 @@ class AttackCommandChooserTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
|
||||
private val gameState = GameState(
|
||||
currentDate = Some(Date(year = 501, month = 2)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes,
|
||||
provinces = mapifyProvinces(
|
||||
Province(
|
||||
|
||||
+5
@@ -9,6 +9,7 @@ import net.eagle0.eagle.api.selected_command.AttackDecisionSelectedCommand
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionTypeId.LIGHT_INFANTRY
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.common.date.Date
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase
|
||||
import net.eagle0.eagle.internal.army.{Army, AwaitingDecision, HostileArmyGroup, MovingArmy}
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.faction.Faction
|
||||
@@ -111,6 +112,7 @@ class AttackDecisionCommandChooserTest extends AnyFlatSpec with Matchers with Be
|
||||
// Create GameState
|
||||
val gameState = GameState(
|
||||
currentDate = Some(Date(year = 501, month = 1)),
|
||||
currentPhase = RoundPhase.ATTACK_DECISION,
|
||||
battalionTypes = battalionTypes,
|
||||
provinces = mapifyProvinces(province),
|
||||
battalions = mapifyBattalions(battalion1, battalion2, battalion3),
|
||||
@@ -224,6 +226,7 @@ class AttackDecisionCommandChooserTest extends AnyFlatSpec with Matchers with Be
|
||||
// Create GameState
|
||||
val gameState = GameState(
|
||||
currentDate = Some(Date(year = 501, month = 1)),
|
||||
currentPhase = RoundPhase.ATTACK_DECISION,
|
||||
battalionTypes = battalionTypes,
|
||||
provinces = mapifyProvinces(province),
|
||||
battalions = mapifyBattalions(weakBattalion, strongBattalion),
|
||||
@@ -354,6 +357,7 @@ class AttackDecisionCommandChooserTest extends AnyFlatSpec with Matchers with Be
|
||||
|
||||
val gameState = GameState(
|
||||
currentDate = Some(Date(year = 501, month = 1)),
|
||||
currentPhase = RoundPhase.ATTACK_DECISION,
|
||||
battalionTypes = battalionTypes,
|
||||
provinces = mapifyProvinces(province),
|
||||
battalions = mapifyBattalions(battalion1, battalion2, battalion3, enemyBattalion),
|
||||
@@ -484,6 +488,7 @@ class AttackDecisionCommandChooserTest extends AnyFlatSpec with Matchers with Be
|
||||
|
||||
val gameState = GameState(
|
||||
currentDate = Some(Date(year = 501, month = 1)),
|
||||
currentPhase = RoundPhase.ATTACK_DECISION,
|
||||
battalionTypes = battalionTypes,
|
||||
provinces = mapifyProvinces(province),
|
||||
battalions = mapifyBattalions(
|
||||
|
||||
@@ -19,6 +19,7 @@ scala_test(
|
||||
srcs = ["AlmsCommandSelectorTest.scala"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:round_phase_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:alms_paladin_support_multiplier",
|
||||
@@ -36,6 +37,7 @@ scala_test(
|
||||
srcs = ["AttackCommandChooserTest.scala"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:round_phase_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:alms_paladin_support_multiplier",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:alms_support_increase_per_food",
|
||||
@@ -71,6 +73,7 @@ scala_test(
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:battalion_type_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:combat_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:date_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:round_phase_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:army_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:battalion_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:faction_scala_proto",
|
||||
@@ -115,6 +118,7 @@ scala_test(
|
||||
deps = [
|
||||
"//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/common:round_phase_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/common:functional_random",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:alms_paladin_support_multiplier",
|
||||
@@ -137,14 +141,14 @@ scala_test(
|
||||
name = "combat_unit_selector_test",
|
||||
srcs = ["CombatUnitSelectorTest.scala"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:battalion_type_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:combat_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:profession_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:battalion_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:idable",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:combat_unit_selector",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util:battalion_types_test_data",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type_id",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -168,6 +172,7 @@ scala_test(
|
||||
deps = [
|
||||
"//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/common:round_phase_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/common:functional_random",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
|
||||
@@ -181,8 +186,6 @@ scala_test(
|
||||
name = "food_consumption_utils_test",
|
||||
srcs = ["FoodConsumptionUtilsTest.scala"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:alms_paladin_support_multiplier",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:alms_support_increase_per_food",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:base_food_buy_price",
|
||||
@@ -192,8 +195,9 @@ scala_test(
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:prestige_per_supported_province",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:vassal_minimum_food_to_send_supplies",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:vassal_minimum_gold_to_send_supplies",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:idable",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:food_consumption_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:round_phase",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/date",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -217,6 +221,7 @@ scala_test(
|
||||
deps = [
|
||||
":selected_command_matcher",
|
||||
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:round_phase_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:command_selection",
|
||||
@@ -229,9 +234,13 @@ scala_test(
|
||||
name = "organize_command_selector_test",
|
||||
srcs = ["OrganizeCommandSelectorTest.scala"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:idable",
|
||||
"//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_choice_helpers:organize_command_selector",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type_id",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion/concrete",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
+99
-34
@@ -1,63 +1,128 @@
|
||||
package net.eagle0.eagle.library.util.command_choice_helpers
|
||||
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionTypeId
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.common.profession.Profession.{ENGINEER, MAGE, NO_PROFESSION}
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.library.util.BattalionTypesTestData
|
||||
import net.eagle0.eagle.model.state.battalion.concrete.BattalionC
|
||||
import net.eagle0.eagle.model.state.hero.concrete.HeroC
|
||||
import net.eagle0.eagle.model.state.hero.Profession
|
||||
import net.eagle0.eagle.model.state.BattalionType
|
||||
import net.eagle0.eagle.model.state.BattalionTypeId
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
|
||||
class CombatUnitSelectorTest extends AnyFlatSpec with Matchers with BeforeAndAfterEach {
|
||||
|
||||
private val battalionTypes = BattalionTypesTestData.battalionTypes
|
||||
private val battalionTypes = Vector(
|
||||
BattalionType(
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
name = "Heavy Cavalry",
|
||||
capacity = 600,
|
||||
allowsCasting = false,
|
||||
allowsStealth = false,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 3.0,
|
||||
monthlyFoodCost = 0.15,
|
||||
baseArmamentCostPerTroop = 0.05,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
),
|
||||
BattalionType(
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
name = "Light Infantry",
|
||||
capacity = 1000,
|
||||
allowsCasting = true,
|
||||
allowsStealth = true,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 1.0,
|
||||
monthlyFoodCost = 0.065,
|
||||
baseArmamentCostPerTroop = 0.02,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
),
|
||||
BattalionType(
|
||||
typeId = BattalionTypeId.HeavyInfantry,
|
||||
name = "Heavy Infantry",
|
||||
capacity = 800,
|
||||
allowsCasting = false,
|
||||
allowsStealth = false,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 2.0,
|
||||
monthlyFoodCost = 0.08,
|
||||
baseArmamentCostPerTroop = 0.035,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
),
|
||||
BattalionType(
|
||||
typeId = BattalionTypeId.Longbowmen,
|
||||
name = "Longbowmen",
|
||||
capacity = 800,
|
||||
allowsCasting = true,
|
||||
allowsStealth = true,
|
||||
alwaysArcheryCapable = true,
|
||||
baseCost = 1.5,
|
||||
monthlyFoodCost = 0.08,
|
||||
baseArmamentCostPerTroop = 0.03,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
)
|
||||
)
|
||||
|
||||
behavior of "CombatUnitSelectorTest"
|
||||
|
||||
"selectedCombatBattalions" should "pair a mage with an optimal battalion if possible" in {
|
||||
val battalions = Vector(
|
||||
Battalion(id = 1, size = 800, `type` = BattalionTypeId.HEAVY_INFANTRY),
|
||||
Battalion(id = 2, size = 1000, `type` = BattalionTypeId.LIGHT_INFANTRY),
|
||||
Battalion(id = 3, size = 800, `type` = BattalionTypeId.LONGBOWMEN)
|
||||
BattalionC(id = 1, size = 800, typeId = BattalionTypeId.HeavyInfantry, training = 0, armament = 0, name = ""),
|
||||
BattalionC(id = 2, size = 1000, typeId = BattalionTypeId.LightInfantry, training = 0, armament = 0, name = ""),
|
||||
BattalionC(id = 3, size = 800, typeId = BattalionTypeId.Longbowmen, training = 0, armament = 0, name = "")
|
||||
)
|
||||
|
||||
val heroes = Vector(
|
||||
Hero(id = 6, factionId = Some(1), profession = MAGE),
|
||||
Hero(id = 7, factionId = Some(1), profession = ENGINEER),
|
||||
Hero(id = 8, factionId = Some(1), profession = NO_PROFESSION)
|
||||
HeroC(id = 6, factionId = Some(1), profession = Profession.Mage),
|
||||
HeroC(id = 7, factionId = Some(1), profession = Profession.Engineer),
|
||||
HeroC(id = 8, factionId = Some(1), profession = Profession.NoProfession)
|
||||
)
|
||||
|
||||
CombatUnitSelector.selectedCombatBattalions(
|
||||
desiredCount = 3,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
battalionTypes = battalionTypes
|
||||
) should contain(
|
||||
CombatUnit(factionId = 1, heroId = 6, battalionId = Some(2))
|
||||
)
|
||||
CombatUnitSelector
|
||||
.selectedCombatBattalions(
|
||||
desiredCount = 3,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
.should(
|
||||
contain(
|
||||
CombatUnit(factionId = 1, heroId = 6, battalionId = Some(2))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
it should "pair a mage with an longbowmen if no other option" in {
|
||||
val battalions = Vector(
|
||||
Battalion(id = 1, size = 800, `type` = BattalionTypeId.HEAVY_INFANTRY),
|
||||
Battalion(id = 2, size = 600, `type` = BattalionTypeId.HEAVY_CAVALRY),
|
||||
Battalion(id = 3, size = 800, `type` = BattalionTypeId.LONGBOWMEN)
|
||||
BattalionC(id = 1, size = 800, typeId = BattalionTypeId.HeavyInfantry, training = 0, armament = 0, name = ""),
|
||||
BattalionC(id = 2, size = 600, typeId = BattalionTypeId.HeavyCavalry, training = 0, armament = 0, name = ""),
|
||||
BattalionC(id = 3, size = 800, typeId = BattalionTypeId.Longbowmen, training = 0, armament = 0, name = "")
|
||||
)
|
||||
|
||||
val heroes = Vector(
|
||||
Hero(id = 6, factionId = Some(1), profession = MAGE),
|
||||
Hero(id = 7, factionId = Some(1), profession = ENGINEER),
|
||||
Hero(id = 8, factionId = Some(1), profession = NO_PROFESSION)
|
||||
)
|
||||
CombatUnitSelector.selectedCombatBattalions(
|
||||
desiredCount = 3,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
battalionTypes = battalionTypes
|
||||
) should contain(
|
||||
CombatUnit(factionId = 1, heroId = 6, battalionId = Some(3))
|
||||
HeroC(id = 6, factionId = Some(1), profession = Profession.Mage),
|
||||
HeroC(id = 7, factionId = Some(1), profession = Profession.Engineer),
|
||||
HeroC(id = 8, factionId = Some(1), profession = Profession.NoProfession)
|
||||
)
|
||||
CombatUnitSelector
|
||||
.selectedCombatBattalions(
|
||||
desiredCount = 3,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
.should(
|
||||
contain(
|
||||
CombatUnit(factionId = 1, heroId = 6, battalionId = Some(3))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+31
-2
@@ -21,6 +21,7 @@ import net.eagle0.eagle.common.battalion_type.BattalionTypeId.{HEAVY_CAVALRY, LI
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.common.date.Date
|
||||
import net.eagle0.eagle.common.diplomacy_offer.{PrisonerToBeRansomed, RansomOfferDetails}
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase
|
||||
import net.eagle0.eagle.internal.army.{Army, MovingArmy}
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.faction.Faction
|
||||
@@ -69,7 +70,8 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
"chosenUnderAttackCommand" should "organize troops if under attack" in {
|
||||
val actingFid = 3
|
||||
val gs = GameState(
|
||||
currentDate = Some(Date()),
|
||||
currentDate = Some(Date(year = 500, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
provinces = Map(
|
||||
7 -> Province(
|
||||
id = 7,
|
||||
@@ -166,6 +168,7 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
Battalion(id = 85, `type` = BattalionTypeId.HEAVY_CAVALRY, size = 600)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 10)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -217,6 +220,7 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
Battalion(id = 85, `type` = BattalionTypeId.HEAVY_CAVALRY, size = 600)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 10)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -268,6 +272,7 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
Battalion(id = 85, `type` = BattalionTypeId.HEAVY_CAVALRY, size = 600)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 10)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -308,6 +313,7 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
Battalion(id = 85, `type` = BattalionTypeId.HEAVY_CAVALRY, size = 600)
|
||||
),
|
||||
currentDate = Some(Date(year = 353, month = 10)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -341,6 +347,8 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
),
|
||||
Battalion(id = 12, `type` = LONGBOWMEN, size = 800, armament = 100)
|
||||
),
|
||||
currentDate = Some(Date(year = 500, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -371,6 +379,8 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
),
|
||||
Battalion(id = 12, `type` = LONGBOWMEN, size = 800, armament = 100)
|
||||
),
|
||||
currentDate = Some(Date(year = 500, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -396,6 +406,8 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
Battalion(id = 11, `type` = LIGHT_INFANTRY, size = 950, armament = 100),
|
||||
Battalion(id = 12, `type` = LONGBOWMEN, size = 800, armament = 100)
|
||||
),
|
||||
currentDate = Some(Date(year = 500, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -426,6 +438,8 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
),
|
||||
Battalion(id = 12, `type` = LONGBOWMEN, size = 750, armament = 100)
|
||||
),
|
||||
currentDate = Some(Date(year = 500, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -456,6 +470,8 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
),
|
||||
Battalion(id = 12, `type` = LONGBOWMEN, size = 800, armament = 100)
|
||||
),
|
||||
currentDate = Some(Date(year = 500, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -486,6 +502,8 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
),
|
||||
Battalion(id = 12, `type` = LONGBOWMEN, size = 800, armament = 100)
|
||||
),
|
||||
currentDate = Some(Date(year = 500, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -516,6 +534,8 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
),
|
||||
Battalion(id = 12, `type` = LONGBOWMEN, size = 225, armament = 100)
|
||||
),
|
||||
currentDate = Some(Date(year = 500, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -546,6 +566,8 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
),
|
||||
Battalion(id = 12, `type` = LONGBOWMEN, size = 800, armament = 100)
|
||||
),
|
||||
currentDate = Some(Date(year = 500, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -577,6 +599,8 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
),
|
||||
Battalion(id = 12, `type` = LONGBOWMEN, size = 800, armament = 100)
|
||||
),
|
||||
currentDate = Some(Date(year = 500, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -608,6 +632,8 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
),
|
||||
Battalion(id = 12, `type` = LONGBOWMEN, size = 800, armament = 100)
|
||||
),
|
||||
currentDate = Some(Date(year = 500, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
@@ -618,7 +644,8 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
}
|
||||
|
||||
private val getUnderHeroCapGameState = GameState(
|
||||
currentDate = Some(Date()),
|
||||
currentDate = Some(Date(year = 500, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
heroes = mapifyHeroes((1 to 100).map(hid => Hero(id = hid)).toVector*),
|
||||
provinces = mapifyProvinces(
|
||||
Province(
|
||||
@@ -972,6 +999,7 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
"foodAmountToBuy" should "buy in December if we're out of food" in {
|
||||
val gameState = GameState(
|
||||
currentDate = Some(Date(year = 323, month = 12)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
factions = mapifyFactions(Faction(id = 17, leaders = Vector(1))),
|
||||
provinces = mapifyProvinces(
|
||||
Province(
|
||||
@@ -1003,6 +1031,7 @@ class CommandChoiceHelpersTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
it should "buy more if support is not up" in {
|
||||
val gameState = GameState(
|
||||
currentDate = Some(Date(year = 323, month = 12)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
factions = mapifyFactions(Faction(id = 17, leaders = Vector(1))),
|
||||
provinces = mapifyProvinces(
|
||||
Province(
|
||||
|
||||
+39
-27
@@ -17,6 +17,7 @@ import net.eagle0.eagle.common.battalion_type.BattalionTypeId.{HEAVY_CAVALRY, HE
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.common.date.Date
|
||||
import net.eagle0.eagle.common.improvement_type.ImprovementType.{AGRICULTURE, ECONOMY, INFRASTRUCTURE}
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase
|
||||
import net.eagle0.eagle.internal.army.{Army, MovingArmy}
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.faction.Faction
|
||||
@@ -105,6 +106,7 @@ class ExpandCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAnd
|
||||
"expandSelectedCommand" should "return a march command if conditions are right" in {
|
||||
val gameState = GameState(
|
||||
currentDate = Some(Date(year = 1501, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
randomSeed = 1L,
|
||||
provinces = mapifyProvinces(
|
||||
Province(
|
||||
@@ -172,6 +174,7 @@ class ExpandCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAnd
|
||||
it should "return None if there are 3 heroes and 2 are faction leaders" in {
|
||||
val gameState = GameState(
|
||||
currentDate = Some(Date(year = 1501, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
randomSeed = 1L,
|
||||
provinces = mapifyProvinces(
|
||||
Province(
|
||||
@@ -352,6 +355,8 @@ class ExpandCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAnd
|
||||
)
|
||||
|
||||
private val marchGameState = GameState(
|
||||
currentDate = Some(Date(year = 2983, month = 3)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
randomSeed = 1L,
|
||||
provinces = mapifyProvinces(
|
||||
actingProvince,
|
||||
@@ -378,47 +383,54 @@ class ExpandCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAnd
|
||||
Hero(id = 10, vigor = 90)
|
||||
),
|
||||
battalions = battalions.map(b => b.id -> b).toMap,
|
||||
currentDate = Some(Date(year = 2983, month = 3)),
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
"expansionCandidates" should "return neighbors that are unowned" in {
|
||||
ExpandCommandSelector.expansionCandidateProvinces(
|
||||
marchGameState,
|
||||
actingProvince.id,
|
||||
actingProvince.neighbors.map(_.provinceId).toVector
|
||||
) should contain.allOf(
|
||||
unownedProvince1,
|
||||
unownedProvince2,
|
||||
unownedProvince3
|
||||
ExpandCommandSelector
|
||||
.expansionCandidateProvinces(
|
||||
marchGameState,
|
||||
actingProvince.id,
|
||||
actingProvince.neighbors.map(_.provinceId).toVector
|
||||
)
|
||||
.map(_.id) should contain.allOf(
|
||||
unownedProvince1.id,
|
||||
unownedProvince2.id,
|
||||
unownedProvince3.id
|
||||
)
|
||||
}
|
||||
|
||||
it should "not return provinces that you already own" in {
|
||||
ExpandCommandSelector.expansionCandidateProvinces(
|
||||
marchGameState,
|
||||
actingProvince.id,
|
||||
actingProvince.neighbors.map(_.provinceId).toVector
|
||||
) should not contain
|
||||
ownedProvince
|
||||
ExpandCommandSelector
|
||||
.expansionCandidateProvinces(
|
||||
marchGameState,
|
||||
actingProvince.id,
|
||||
actingProvince.neighbors.map(_.provinceId).toVector
|
||||
)
|
||||
.map(_.id) should not contain
|
||||
ownedProvince.id
|
||||
}
|
||||
|
||||
it should "not return provinces that you already have an army heading to" in {
|
||||
ExpandCommandSelector.expansionCandidateProvinces(
|
||||
marchGameState,
|
||||
actingProvince.id,
|
||||
actingProvince.neighbors.map(_.provinceId).toVector
|
||||
) should not contain
|
||||
enRouteProvince
|
||||
ExpandCommandSelector
|
||||
.expansionCandidateProvinces(
|
||||
marchGameState,
|
||||
actingProvince.id,
|
||||
actingProvince.neighbors.map(_.provinceId).toVector
|
||||
)
|
||||
.map(_.id) should not contain
|
||||
enRouteProvince.id
|
||||
}
|
||||
|
||||
it should "not return hostile provinces" in {
|
||||
ExpandCommandSelector.expansionCandidateProvinces(
|
||||
marchGameState,
|
||||
actingProvince.id,
|
||||
actingProvince.neighbors.map(_.provinceId).toVector
|
||||
) should not contain
|
||||
hostileProvince
|
||||
ExpandCommandSelector
|
||||
.expansionCandidateProvinces(
|
||||
marchGameState,
|
||||
actingProvince.id,
|
||||
actingProvince.neighbors.map(_.provinceId).toVector
|
||||
)
|
||||
.map(_.id) should not contain
|
||||
hostileProvince.id
|
||||
}
|
||||
|
||||
private val improveCommand = ImproveAvailableCommand(
|
||||
|
||||
+30
-27
@@ -1,12 +1,5 @@
|
||||
package net.eagle0.eagle.library.util.command_choice_helpers
|
||||
|
||||
import net.eagle0.eagle.common.date.Date
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase.{
|
||||
BATTLE_RESOLUTION,
|
||||
PLAYER_COMMANDS,
|
||||
RECON_RESOLUTION,
|
||||
VASSAL_COMMANDS
|
||||
}
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
AlmsPaladinSupportMultiplier,
|
||||
AlmsSupportIncreasePerFood,
|
||||
@@ -18,6 +11,8 @@ import net.eagle0.eagle.library.settings.{
|
||||
VassalMinimumFoodToSendSupplies,
|
||||
VassalMinimumGoldToSendSupplies
|
||||
}
|
||||
import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.model.state.RoundPhase
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
@@ -37,38 +32,46 @@ class FoodConsumptionUtilsTest extends AnyFlatSpec with Matchers with BeforeAndA
|
||||
}
|
||||
|
||||
"foodConsumptionMonthsRemainingInYear" should "be 12 in January before FOOD_CONSUMPTION" in {
|
||||
val date = Date(year = 323, month = 1)
|
||||
val date = Date(year = 323, month = Date.Month.January)
|
||||
|
||||
FoodConsumptionUtils.foodConsumptionMonthsRemainingInYear(
|
||||
date,
|
||||
PLAYER_COMMANDS
|
||||
) shouldBe 12
|
||||
FoodConsumptionUtils
|
||||
.foodConsumptionMonthsRemainingInYear(
|
||||
date,
|
||||
RoundPhase.PlayerCommands
|
||||
)
|
||||
.shouldBe(12)
|
||||
}
|
||||
|
||||
it should "be 11 in January after FOOD_CONSUMPTION" in {
|
||||
val date = Date(year = 323, month = 1)
|
||||
val date = Date(year = 323, month = Date.Month.January)
|
||||
|
||||
FoodConsumptionUtils.foodConsumptionMonthsRemainingInYear(
|
||||
date,
|
||||
BATTLE_RESOLUTION
|
||||
) shouldBe 11
|
||||
FoodConsumptionUtils
|
||||
.foodConsumptionMonthsRemainingInYear(
|
||||
date,
|
||||
RoundPhase.BattleResolution
|
||||
)
|
||||
.shouldBe(11)
|
||||
}
|
||||
|
||||
it should "be 1 in December before FOOD_CONSUMPTION" in {
|
||||
val date = Date(year = 323, month = 12)
|
||||
val date = Date(year = 323, month = Date.Month.December)
|
||||
|
||||
FoodConsumptionUtils.foodConsumptionMonthsRemainingInYear(
|
||||
date,
|
||||
VASSAL_COMMANDS
|
||||
) shouldBe 1
|
||||
FoodConsumptionUtils
|
||||
.foodConsumptionMonthsRemainingInYear(
|
||||
date,
|
||||
RoundPhase.VassalCommands
|
||||
)
|
||||
.shouldBe(1)
|
||||
}
|
||||
|
||||
it should "be 0 in December after FOOD_CONSUMPTION" in {
|
||||
val date = Date(year = 323, month = 12)
|
||||
val date = Date(year = 323, month = Date.Month.December)
|
||||
|
||||
FoodConsumptionUtils.foodConsumptionMonthsRemainingInYear(
|
||||
date,
|
||||
RECON_RESOLUTION
|
||||
) shouldBe 0
|
||||
FoodConsumptionUtils
|
||||
.foodConsumptionMonthsRemainingInYear(
|
||||
date,
|
||||
RoundPhase.ReconResolution
|
||||
)
|
||||
.shouldBe(0)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -4,6 +4,7 @@ import net.eagle0.eagle.api.available_command.{ImproveAvailableCommand, RestAvai
|
||||
import net.eagle0.eagle.api.selected_command.ImproveSelectedCommand
|
||||
import net.eagle0.eagle.common.date.Date
|
||||
import net.eagle0.eagle.common.improvement_type.ImprovementType.{AGRICULTURE, ECONOMY, INFRASTRUCTURE}
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
@@ -83,7 +84,8 @@ class ImproveCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeAn
|
||||
veryTiredHero2
|
||||
),
|
||||
provinces = Map(7 -> actingProvince),
|
||||
currentDate = Some(Date(year = 2983, month = 3))
|
||||
currentDate = Some(Date(year = 2983, month = 3)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS
|
||||
)
|
||||
|
||||
it should "choose Economy if that is lower than agriculture and infrastructure" in {
|
||||
|
||||
+255
-86
@@ -1,9 +1,10 @@
|
||||
package net.eagle0.eagle.library.util.command_choice_helpers
|
||||
|
||||
import net.eagle0.eagle.api.selected_command.OrganizeTroopsSelectedCommand.{ChangedBattalion, NewBattalion}
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionType
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionTypeId.{HEAVY_CAVALRY, LIGHT_INFANTRY}
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionTypeId as ProtoBattalionTypeId
|
||||
import net.eagle0.eagle.model.state.battalion.concrete.BattalionC
|
||||
import net.eagle0.eagle.model.state.BattalionType
|
||||
import net.eagle0.eagle.model.state.BattalionTypeId
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
@@ -14,278 +15,446 @@ class OrganizeCommandSelectorTest extends AnyFlatSpec with Matchers with BeforeA
|
||||
val battalionTypes =
|
||||
Vector(
|
||||
BattalionType(
|
||||
typeId = LIGHT_INFANTRY,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
name = "Light Infantry",
|
||||
capacity = 1000,
|
||||
allowsCasting = true,
|
||||
allowsStealth = true,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 1.0,
|
||||
monthlyFoodCost = 0.15,
|
||||
capacity = 1000
|
||||
baseArmamentCostPerTroop = 0.02,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
),
|
||||
BattalionType(
|
||||
typeId = HEAVY_CAVALRY,
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
name = "Heavy Cavalry",
|
||||
capacity = 600,
|
||||
allowsCasting = false,
|
||||
allowsStealth = false,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 3.0,
|
||||
monthlyFoodCost = 0.25,
|
||||
capacity = 600
|
||||
baseArmamentCostPerTroop = 0.05,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
)
|
||||
)
|
||||
|
||||
val cbResult = OrganizeCommandSelector.changedBattalions(
|
||||
gold = 10000,
|
||||
foodSurplus = 500,
|
||||
costs = Map(LIGHT_INFANTRY -> 2.0, HEAVY_CAVALRY -> 3.0),
|
||||
costs = Map(BattalionTypeId.LightInfantry -> 2.0, BattalionTypeId.HeavyCavalry -> 3.0),
|
||||
existingBattalions = Vector(
|
||||
Battalion(id = 1, `type` = LIGHT_INFANTRY, size = 950),
|
||||
Battalion(id = 2, `type` = HEAVY_CAVALRY, size = 560)
|
||||
BattalionC(id = 1, typeId = BattalionTypeId.LightInfantry, size = 950, training = 0, armament = 0, name = ""),
|
||||
BattalionC(id = 2, typeId = BattalionTypeId.HeavyCavalry, size = 560, training = 0, armament = 0, name = "")
|
||||
),
|
||||
battalionTypes = battalionTypes,
|
||||
eligibleBattalionTypeIds = battalionTypes.map(_.typeId)
|
||||
)
|
||||
|
||||
cbResult.changedBattalions should have size 2
|
||||
cbResult.changedBattalions.should(have).size(2)
|
||||
|
||||
val expectedRemainingGold = 9780 // 10000 - 50 * 2.0 - 40 * 3.0
|
||||
cbResult.remainingGold shouldBe expectedRemainingGold
|
||||
cbResult.remainingGold.shouldBe(expectedRemainingGold)
|
||||
val expectedRemainingFoodSurplus = 482 // 500 - 50 * 0.15 - 40 * 0.25
|
||||
cbResult.remainingFoodSurplus shouldBe expectedRemainingFoodSurplus
|
||||
cbResult.remainingFoodSurplus.shouldBe(expectedRemainingFoodSurplus)
|
||||
|
||||
cbResult.changedBattalions should contain.allOf(
|
||||
ChangedBattalion(id = 1, newTroops = 50),
|
||||
ChangedBattalion(id = 2, newTroops = 40)
|
||||
)
|
||||
cbResult.changedBattalions
|
||||
.should(contain)
|
||||
.allOf(
|
||||
ChangedBattalion(id = 1, newTroops = 50),
|
||||
ChangedBattalion(id = 2, newTroops = 40)
|
||||
)
|
||||
}
|
||||
|
||||
it should "top up only one battalion if there's only food for one" in {
|
||||
val battalionTypes =
|
||||
Vector(
|
||||
BattalionType(
|
||||
typeId = LIGHT_INFANTRY,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
name = "Light Infantry",
|
||||
capacity = 1000,
|
||||
allowsCasting = true,
|
||||
allowsStealth = true,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 1.0,
|
||||
monthlyFoodCost = 0.15,
|
||||
capacity = 1000
|
||||
baseArmamentCostPerTroop = 0.02,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
),
|
||||
BattalionType(
|
||||
typeId = HEAVY_CAVALRY,
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
name = "Heavy Cavalry",
|
||||
capacity = 600,
|
||||
allowsCasting = false,
|
||||
allowsStealth = false,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 3.0,
|
||||
monthlyFoodCost = 0.25,
|
||||
capacity = 600
|
||||
baseArmamentCostPerTroop = 0.05,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
)
|
||||
)
|
||||
|
||||
val cbResult = OrganizeCommandSelector.changedBattalions(
|
||||
gold = 10000,
|
||||
foodSurplus = 11,
|
||||
costs = Map(LIGHT_INFANTRY -> 2.0, HEAVY_CAVALRY -> 3.0),
|
||||
costs = Map(BattalionTypeId.LightInfantry -> 2.0, BattalionTypeId.HeavyCavalry -> 3.0),
|
||||
existingBattalions = Vector(
|
||||
Battalion(id = 1, `type` = LIGHT_INFANTRY, size = 950),
|
||||
Battalion(id = 2, `type` = HEAVY_CAVALRY, size = 560)
|
||||
BattalionC(id = 1, typeId = BattalionTypeId.LightInfantry, size = 950, training = 0, armament = 0, name = ""),
|
||||
BattalionC(id = 2, typeId = BattalionTypeId.HeavyCavalry, size = 560, training = 0, armament = 0, name = "")
|
||||
),
|
||||
battalionTypes = battalionTypes,
|
||||
eligibleBattalionTypeIds = battalionTypes.map(_.typeId)
|
||||
)
|
||||
|
||||
cbResult.changedBattalions should have size 1
|
||||
cbResult.changedBattalions.should(have).size(1)
|
||||
}
|
||||
|
||||
it should "top up only one battalion if there's only gold for one" in {
|
||||
val battalionTypes =
|
||||
Vector(
|
||||
BattalionType(
|
||||
typeId = LIGHT_INFANTRY,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
name = "Light Infantry",
|
||||
capacity = 1000,
|
||||
allowsCasting = true,
|
||||
allowsStealth = true,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 1.0,
|
||||
monthlyFoodCost = 0.15,
|
||||
capacity = 1000
|
||||
baseArmamentCostPerTroop = 0.02,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
),
|
||||
BattalionType(
|
||||
typeId = HEAVY_CAVALRY,
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
name = "Heavy Cavalry",
|
||||
capacity = 600,
|
||||
allowsCasting = false,
|
||||
allowsStealth = false,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 3.0,
|
||||
monthlyFoodCost = 0.25,
|
||||
capacity = 600
|
||||
baseArmamentCostPerTroop = 0.05,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
)
|
||||
)
|
||||
|
||||
val cbResult = OrganizeCommandSelector.changedBattalions(
|
||||
gold = 150,
|
||||
foodSurplus = 500,
|
||||
costs = Map(LIGHT_INFANTRY -> 2.0, HEAVY_CAVALRY -> 3.0),
|
||||
costs = Map(BattalionTypeId.LightInfantry -> 2.0, BattalionTypeId.HeavyCavalry -> 3.0),
|
||||
existingBattalions = Vector(
|
||||
Battalion(id = 1, `type` = LIGHT_INFANTRY, size = 950),
|
||||
Battalion(id = 2, `type` = HEAVY_CAVALRY, size = 560)
|
||||
BattalionC(id = 1, typeId = BattalionTypeId.LightInfantry, size = 950, training = 0, armament = 0, name = ""),
|
||||
BattalionC(id = 2, typeId = BattalionTypeId.HeavyCavalry, size = 560, training = 0, armament = 0, name = "")
|
||||
),
|
||||
battalionTypes = battalionTypes,
|
||||
eligibleBattalionTypeIds = battalionTypes.map(_.typeId)
|
||||
)
|
||||
|
||||
cbResult.changedBattalions should have size 1
|
||||
cbResult.changedBattalions.should(have).size(1)
|
||||
}
|
||||
|
||||
"newBattalions" should "buy a battalion if there's sufficient gold and food" in {
|
||||
val battalionTypes =
|
||||
Vector(
|
||||
BattalionType(
|
||||
typeId = LIGHT_INFANTRY,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
name = "Light Infantry",
|
||||
capacity = 1000,
|
||||
allowsCasting = true,
|
||||
allowsStealth = true,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 1.0,
|
||||
monthlyFoodCost = 0.15,
|
||||
capacity = 1000
|
||||
baseArmamentCostPerTroop = 0.02,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
),
|
||||
BattalionType(
|
||||
typeId = HEAVY_CAVALRY,
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
name = "Heavy Cavalry",
|
||||
capacity = 600,
|
||||
allowsCasting = false,
|
||||
allowsStealth = false,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 3.0,
|
||||
monthlyFoodCost = 0.25,
|
||||
capacity = 600
|
||||
baseArmamentCostPerTroop = 0.05,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
)
|
||||
)
|
||||
|
||||
val nbResult = OrganizeCommandSelector.newBattalions(
|
||||
gold = 10000,
|
||||
foodSurplus = 200,
|
||||
costs = Map(LIGHT_INFANTRY -> 2.0, HEAVY_CAVALRY -> 3.0),
|
||||
costs = Map(BattalionTypeId.LightInfantry -> 2.0, BattalionTypeId.HeavyCavalry -> 3.0),
|
||||
allBattalionTypes = battalionTypes,
|
||||
eligibleBattalionTypeIds = battalionTypes.map(_.typeId),
|
||||
existingBattalions = Vector()
|
||||
)
|
||||
|
||||
nbResult.newBattalions should have size 1
|
||||
nbResult.newBattalions.should(have).size(1)
|
||||
}
|
||||
|
||||
it should "buy the battalion we have least of" in {
|
||||
val battalionTypes =
|
||||
Vector(
|
||||
BattalionType(
|
||||
typeId = LIGHT_INFANTRY,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
name = "Light Infantry",
|
||||
capacity = 1000,
|
||||
allowsCasting = true,
|
||||
allowsStealth = true,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 1.0,
|
||||
monthlyFoodCost = 0.15,
|
||||
capacity = 1000
|
||||
baseArmamentCostPerTroop = 0.02,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
),
|
||||
BattalionType(
|
||||
typeId = HEAVY_CAVALRY,
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
name = "Heavy Cavalry",
|
||||
capacity = 600,
|
||||
allowsCasting = false,
|
||||
allowsStealth = false,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 3.0,
|
||||
monthlyFoodCost = 0.25,
|
||||
capacity = 600
|
||||
baseArmamentCostPerTroop = 0.05,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
)
|
||||
)
|
||||
|
||||
val nbResult = OrganizeCommandSelector.newBattalions(
|
||||
gold = 10000,
|
||||
foodSurplus = 200,
|
||||
costs = Map(LIGHT_INFANTRY -> 2.0, HEAVY_CAVALRY -> 4.0),
|
||||
costs = Map(BattalionTypeId.LightInfantry -> 2.0, BattalionTypeId.HeavyCavalry -> 4.0),
|
||||
allBattalionTypes = battalionTypes,
|
||||
eligibleBattalionTypeIds = battalionTypes.map(_.typeId),
|
||||
existingBattalions = Vector(Battalion(`type` = HEAVY_CAVALRY))
|
||||
existingBattalions = Vector(
|
||||
BattalionC(id = 1, typeId = BattalionTypeId.HeavyCavalry, size = 0, training = 0, armament = 0, name = "")
|
||||
)
|
||||
)
|
||||
|
||||
nbResult.newBattalions should have size 1
|
||||
nbResult.newBattalions.head.`type` shouldBe LIGHT_INFANTRY
|
||||
nbResult.newBattalions.head.newTroops shouldBe 1000
|
||||
nbResult.newBattalions.should(have).size(1)
|
||||
nbResult.newBattalions.head.`type`.shouldBe(ProtoBattalionTypeId.LIGHT_INFANTRY)
|
||||
nbResult.newBattalions.head.newTroops.shouldBe(1000)
|
||||
}
|
||||
|
||||
it should "buy the most expensive battalion if there's a tie" in {
|
||||
val battalionTypes =
|
||||
Vector(
|
||||
BattalionType(
|
||||
typeId = LIGHT_INFANTRY,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
name = "Light Infantry",
|
||||
capacity = 1000,
|
||||
allowsCasting = true,
|
||||
allowsStealth = true,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 1.0,
|
||||
monthlyFoodCost = 0.15,
|
||||
capacity = 1000
|
||||
baseArmamentCostPerTroop = 0.02,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
),
|
||||
BattalionType(
|
||||
typeId = HEAVY_CAVALRY,
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
name = "Heavy Cavalry",
|
||||
capacity = 600,
|
||||
allowsCasting = false,
|
||||
allowsStealth = false,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 3.0,
|
||||
monthlyFoodCost = 0.25,
|
||||
capacity = 600
|
||||
baseArmamentCostPerTroop = 0.05,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
)
|
||||
)
|
||||
|
||||
val nbResult = OrganizeCommandSelector.newBattalions(
|
||||
gold = 10000,
|
||||
foodSurplus = 200,
|
||||
costs = Map(LIGHT_INFANTRY -> 2.0, HEAVY_CAVALRY -> 4.0),
|
||||
costs = Map(BattalionTypeId.LightInfantry -> 2.0, BattalionTypeId.HeavyCavalry -> 4.0),
|
||||
allBattalionTypes = battalionTypes,
|
||||
eligibleBattalionTypeIds = battalionTypes.map(_.typeId),
|
||||
existingBattalions = Vector(
|
||||
Battalion(`type` = HEAVY_CAVALRY),
|
||||
Battalion(`type` = LIGHT_INFANTRY)
|
||||
BattalionC(id = 1, typeId = BattalionTypeId.HeavyCavalry, size = 0, training = 0, armament = 0, name = ""),
|
||||
BattalionC(id = 2, typeId = BattalionTypeId.LightInfantry, size = 0, training = 0, armament = 0, name = "")
|
||||
)
|
||||
)
|
||||
|
||||
nbResult.newBattalions should have size 1
|
||||
nbResult.newBattalions.head.`type` shouldBe HEAVY_CAVALRY
|
||||
nbResult.newBattalions.head.newTroops shouldBe 600
|
||||
nbResult.newBattalions.should(have).size(1)
|
||||
nbResult.newBattalions.head.`type`.shouldBe(ProtoBattalionTypeId.HEAVY_CAVALRY)
|
||||
nbResult.newBattalions.head.newTroops.shouldBe(600)
|
||||
}
|
||||
|
||||
it should "buy multiple battalions if there is sufficient gold and food" in {
|
||||
val battalionTypes =
|
||||
Vector(
|
||||
BattalionType(
|
||||
typeId = LIGHT_INFANTRY,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
name = "Light Infantry",
|
||||
capacity = 1000,
|
||||
allowsCasting = true,
|
||||
allowsStealth = true,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 1.0,
|
||||
monthlyFoodCost = 0.15,
|
||||
capacity = 1000
|
||||
baseArmamentCostPerTroop = 0.02,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
),
|
||||
BattalionType(
|
||||
typeId = HEAVY_CAVALRY,
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
name = "Heavy Cavalry",
|
||||
capacity = 600,
|
||||
allowsCasting = false,
|
||||
allowsStealth = false,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 3.0,
|
||||
monthlyFoodCost = 0.3,
|
||||
capacity = 600
|
||||
baseArmamentCostPerTroop = 0.05,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
)
|
||||
)
|
||||
|
||||
val nbResult = OrganizeCommandSelector.newBattalions(
|
||||
gold = 10000,
|
||||
foodSurplus = 330,
|
||||
costs = Map(LIGHT_INFANTRY -> 2.0, HEAVY_CAVALRY -> 4.0),
|
||||
costs = Map(BattalionTypeId.LightInfantry -> 2.0, BattalionTypeId.HeavyCavalry -> 4.0),
|
||||
allBattalionTypes = battalionTypes,
|
||||
eligibleBattalionTypeIds = battalionTypes.map(_.typeId),
|
||||
existingBattalions = Vector()
|
||||
)
|
||||
|
||||
nbResult.newBattalions should have size 2
|
||||
nbResult.newBattalions should contain.allOf(
|
||||
NewBattalion(`type` = HEAVY_CAVALRY, newTroops = 600),
|
||||
NewBattalion(`type` = LIGHT_INFANTRY, newTroops = 1000)
|
||||
)
|
||||
nbResult.remainingFoodSurplus shouldBe 0
|
||||
nbResult.remainingGold shouldBe 5600
|
||||
nbResult.newBattalions.should(have).size(2)
|
||||
nbResult.newBattalions
|
||||
.should(contain)
|
||||
.allOf(
|
||||
NewBattalion(`type` = ProtoBattalionTypeId.HEAVY_CAVALRY, newTroops = 600),
|
||||
NewBattalion(`type` = ProtoBattalionTypeId.LIGHT_INFANTRY, newTroops = 1000)
|
||||
)
|
||||
nbResult.remainingFoodSurplus.shouldBe(0)
|
||||
nbResult.remainingGold.shouldBe(5600)
|
||||
}
|
||||
|
||||
it should "not buy anything if there is not enough gold" in {
|
||||
val battalionTypes =
|
||||
Vector(
|
||||
BattalionType(
|
||||
typeId = LIGHT_INFANTRY,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
name = "Light Infantry",
|
||||
capacity = 1000,
|
||||
allowsCasting = true,
|
||||
allowsStealth = true,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 1.0,
|
||||
monthlyFoodCost = 0.15,
|
||||
capacity = 1000
|
||||
baseArmamentCostPerTroop = 0.02,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
),
|
||||
BattalionType(
|
||||
typeId = HEAVY_CAVALRY,
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
name = "Heavy Cavalry",
|
||||
capacity = 600,
|
||||
allowsCasting = false,
|
||||
allowsStealth = false,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 3.0,
|
||||
monthlyFoodCost = 0.3,
|
||||
capacity = 600
|
||||
baseArmamentCostPerTroop = 0.05,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
)
|
||||
)
|
||||
|
||||
val nbResult = OrganizeCommandSelector.newBattalions(
|
||||
gold = 1900,
|
||||
foodSurplus = 330,
|
||||
costs = Map(LIGHT_INFANTRY -> 2.0, HEAVY_CAVALRY -> 4.0),
|
||||
costs = Map(BattalionTypeId.LightInfantry -> 2.0, BattalionTypeId.HeavyCavalry -> 4.0),
|
||||
allBattalionTypes = battalionTypes,
|
||||
eligibleBattalionTypeIds = battalionTypes.map(_.typeId),
|
||||
existingBattalions = Vector()
|
||||
)
|
||||
|
||||
nbResult.newBattalions shouldBe empty
|
||||
nbResult.remainingGold shouldBe 1900
|
||||
nbResult.remainingFoodSurplus shouldBe 330
|
||||
nbResult.newBattalions.shouldBe(empty)
|
||||
nbResult.remainingGold.shouldBe(1900)
|
||||
nbResult.remainingFoodSurplus.shouldBe(330)
|
||||
}
|
||||
|
||||
it should "not buy anything if there is not enough food" in {
|
||||
val battalionTypes =
|
||||
Vector(
|
||||
BattalionType(
|
||||
typeId = LIGHT_INFANTRY,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
name = "Light Infantry",
|
||||
capacity = 1000,
|
||||
allowsCasting = true,
|
||||
allowsStealth = true,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 1.0,
|
||||
monthlyFoodCost = 0.15,
|
||||
capacity = 1000
|
||||
baseArmamentCostPerTroop = 0.02,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
),
|
||||
BattalionType(
|
||||
typeId = HEAVY_CAVALRY,
|
||||
typeId = BattalionTypeId.HeavyCavalry,
|
||||
name = "Heavy Cavalry",
|
||||
capacity = 600,
|
||||
allowsCasting = false,
|
||||
allowsStealth = false,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 3.0,
|
||||
monthlyFoodCost = 0.3,
|
||||
capacity = 600
|
||||
baseArmamentCostPerTroop = 0.05,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
)
|
||||
)
|
||||
|
||||
val nbResult = OrganizeCommandSelector.newBattalions(
|
||||
gold = 10000,
|
||||
foodSurplus = 140,
|
||||
costs = Map(LIGHT_INFANTRY -> 2.0, HEAVY_CAVALRY -> 4.0),
|
||||
costs = Map(BattalionTypeId.LightInfantry -> 2.0, BattalionTypeId.HeavyCavalry -> 4.0),
|
||||
allBattalionTypes = battalionTypes,
|
||||
eligibleBattalionTypeIds = battalionTypes.map(_.typeId),
|
||||
existingBattalions = Vector()
|
||||
)
|
||||
|
||||
nbResult.newBattalions shouldBe empty
|
||||
nbResult.remainingGold shouldBe 10000
|
||||
nbResult.remainingFoodSurplus shouldBe 140
|
||||
nbResult.newBattalions.shouldBe(empty)
|
||||
nbResult.remainingGold.shouldBe(10000)
|
||||
nbResult.remainingFoodSurplus.shouldBe(140)
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -6,6 +6,7 @@ import net.eagle0.eagle.common.date.Date
|
||||
import net.eagle0.eagle.common.gender.Gender.GENDER_OTHER
|
||||
import net.eagle0.eagle.common.profession.Profession.NO_PROFESSION
|
||||
import net.eagle0.eagle.common.recruitment_info.RecruitmentInfo
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase
|
||||
import net.eagle0.eagle.common.unaffiliated_hero_quest.{AlmsAcrossRealmQuest, ImproveAgricultureQuest, Quest}
|
||||
import net.eagle0.eagle.internal.faction.Faction
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
@@ -80,6 +81,7 @@ class AlmsAcrossRealmQuestCommandChooserTest extends AnyFlatSpec with BeforeAndA
|
||||
|
||||
private val gameState = GameState(
|
||||
currentDate = Some(Date(year = 1501, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
randomSeed = 1L,
|
||||
heroes = mapifyHeroes(uh1, uh2, factionLeader, vassal),
|
||||
provinces = mapifyProvinces(
|
||||
|
||||
+2
@@ -6,6 +6,7 @@ import net.eagle0.eagle.common.date.Date
|
||||
import net.eagle0.eagle.common.gender.Gender.GENDER_OTHER
|
||||
import net.eagle0.eagle.common.profession.Profession.NO_PROFESSION
|
||||
import net.eagle0.eagle.common.recruitment_info.RecruitmentInfo
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase
|
||||
import net.eagle0.eagle.common.unaffiliated_hero_quest.{AlmsToProvinceQuest, ImproveAgricultureQuest, Quest}
|
||||
import net.eagle0.eagle.internal.faction.Faction
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
@@ -56,6 +57,7 @@ class AlmsToProvinceQuestCommandChooserTest extends AnyFlatSpec with BeforeAndAf
|
||||
|
||||
private val gameState = GameState(
|
||||
currentDate = Some(Date(year = 1501, month = 6)),
|
||||
currentPhase = RoundPhase.PLAYER_COMMANDS,
|
||||
randomSeed = 1L,
|
||||
heroes = mapifyHeroes(hero, factionLeader),
|
||||
provinces = mapifyProvinces(
|
||||
|
||||
+2
@@ -23,6 +23,7 @@ scala_test(
|
||||
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:round_phase_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
@@ -40,6 +41,7 @@ scala_test(
|
||||
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:round_phase_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
|
||||
Reference in New Issue
Block a user