mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-29 01:55:42 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
daeb3907bb | ||
|
|
1e83f3687a | ||
|
|
52c2e4b7f1 | ||
|
|
245572a5e5 | ||
|
|
d8f3e64934 | ||
|
|
9798aa23ab |
@@ -9,10 +9,10 @@ This document analyzes all actions and commands in `src/main/scala/net/eagle0/ea
|
||||
|
||||
## Summary
|
||||
|
||||
Based on BUILD.bazel dependency analysis (2025-09-04):
|
||||
Based on BUILD.bazel dependency analysis (2025-09-05):
|
||||
- **Total Commands Analyzed:** 40
|
||||
- **Commands Fully Migrated (No Protobuf):** 36 (90%)
|
||||
- **Commands Still Using Protobuf:** 4 (10%)
|
||||
- **Commands Fully Migrated (No Protobuf):** 37 (92.5%)
|
||||
- **Commands Still Using Protobuf:** 3 (7.5%)
|
||||
- **Actions:** Most still have protobuf dependencies
|
||||
- **Base Classes:** 8 protoless variants available, 6 still use protobuf
|
||||
|
||||
@@ -184,8 +184,9 @@ These commands have **NO protobuf dependencies** in their BUILD.bazel targets:
|
||||
| ResolveInvitationCommand.scala | `resolve_invitation_command` | **NEWLY MIGRATED** - Uses ProtolessSimpleAction, Invitation domain model, LLM integration |
|
||||
| ResolveRansomOfferCommand.scala | `resolve_ransom_offer_command` | **NEWLY MIGRATED** - Uses ProtolessSimpleAction, RansomOffer domain model, LLM integration |
|
||||
| ResolveTributeCommand.scala | `resolve_tribute_command` | **NEWLY MIGRATED** - Uses ProtolessSimpleAction, TributeAmount domain model |
|
||||
| ResolveAllianceOfferCommand.scala | `resolve_alliance_offer_command` | **NEWLY MIGRATED** - Uses ProtolessSimpleAction, AllianceOffer domain model, simplified interface |
|
||||
|
||||
**Total: 36 commands (90% of all commands)**
|
||||
**Total: 37 commands (92.5% of all commands)**
|
||||
|
||||
### ⚠️ Commands Still Using Protobuf Dependencies
|
||||
|
||||
@@ -194,12 +195,13 @@ These commands have protobuf dependencies in their BUILD.bazel files:
|
||||
| File | Build Target | Key Protobuf Dependencies | Migration Complexity |
|
||||
|------|--------------|---------------------------|---------------------|
|
||||
| DiplomacyCommand.scala | `diplomacy_command` | `diplomacy_option`, `diplomacy_offer`, `available_command` | High - Complex diplomacy |
|
||||
| ResolveAllianceOfferCommand.scala | `resolve_alliance_offer_command` | `diplomacy_offer`, `available_command`, `action_result` | High - Diplomacy |
|
||||
| ResolveBreakAllianceCommand.scala | `resolve_break_alliance_command` | `diplomacy_offer`, `available_command`, `action_result` | High - Diplomacy |
|
||||
| FreeForAllDecisionCommand.scala | `free_for_all_decision_command` | `available_command`, `selected_command` | Medium - API layer |
|
||||
| ~~ResolveAllianceOfferCommand.scala~~ | ~~`resolve_alliance_offer_command`~~ | ~~**MIGRATED**~~ | ~~High - Diplomacy~~ |
|
||||
| ~~ResolveRansomOfferCommand.scala~~ | ~~`resolve_ransom_offer_command`~~ | ~~**MIGRATED**~~ | ~~High - Diplomacy~~ |
|
||||
| ~~ResolveTributeCommand.scala~~ | ~~`resolve_tribute_command`~~ | ~~**MIGRATED**~~ | ~~High - Diplomacy~~ |
|
||||
|
||||
**Total: 4 commands (10% of all commands)**
|
||||
**Total: 3 commands (7.5% of all commands)**
|
||||
|
||||
### Migration Complexity Analysis
|
||||
|
||||
@@ -209,9 +211,12 @@ These commands have protobuf dependencies in their BUILD.bazel files:
|
||||
**Medium Complexity (0 commands)**: API layer or simple state dependencies
|
||||
- ~~All medium complexity commands have been migrated~~
|
||||
|
||||
**High Complexity (6 commands)**: Complex state or diplomacy
|
||||
- All diplomacy resolution commands (5) - need complete diplomacy model migration
|
||||
- Commands with complex `game_state` dependencies (1)
|
||||
**Medium Complexity (1 command)**: API layer dependencies
|
||||
- `FreeForAllDecisionCommand` - Uses `available_command`, `selected_command`
|
||||
|
||||
**High Complexity (2 commands)**: Complex state or diplomacy
|
||||
- Remaining diplomacy resolution commands (2) - need complete diplomacy model migration
|
||||
- Commands with complex `game_state` dependencies
|
||||
|
||||
---
|
||||
|
||||
@@ -268,16 +273,17 @@ Based on the BUILD.bazel dependency analysis, here are the key findings and reco
|
||||
### 🏆 Success Metrics
|
||||
|
||||
**Current Status:**
|
||||
- ✅ **82.5% of commands fully migrated** (33/40)
|
||||
- ✅ **92.5% of commands fully migrated** (37/40)
|
||||
- ✅ **All diplomacy helpers use Scala models**
|
||||
- ✅ **All protoless base classes available**
|
||||
- ✅ **One diplomacy command completed** (ResolveTruceOfferCommand)
|
||||
- ✅ **Five diplomacy commands completed** (ResolveTruceOfferCommand, ResolveInvitationCommand, ResolveRansomOfferCommand, ResolveTributeCommand, ResolveAllianceOfferCommand)
|
||||
|
||||
**Next Milestones:**
|
||||
- ✅ **70% target:** Migrate Tier 1 + some Tier 2 commands **COMPLETED**
|
||||
- ✅ **80% target:** Continue with remaining non-diplomacy commands **COMPLETED**
|
||||
- **85% target:** Complete API layer migration
|
||||
- **95% target:** Complete diplomacy migration
|
||||
- ✅ **90% target:** Complete most diplomacy migrations **COMPLETED**
|
||||
- **95% target:** Complete API layer migration (FreeForAllDecisionCommand)
|
||||
- **100% target:** Complete all remaining commands (DiplomacyCommand, ResolveBreakAllianceCommand)
|
||||
|
||||
### 🔄 Conversion Strategy Updates
|
||||
|
||||
@@ -300,4 +306,4 @@ Based on the BUILD.bazel dependency analysis, here are the key findings and reco
|
||||
|
||||
---
|
||||
|
||||
*Updated on 2025-09-04 - Analysis based on BUILD.bazel dependencies*
|
||||
*Updated on 2025-09-05 - Analysis based on BUILD.bazel dependencies. Latest update: ResolveAllianceOfferCommand migrated, 92.5% completion achieved.*
|
||||
@@ -1224,17 +1224,22 @@ scala_library(
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions:__subpackages__",
|
||||
],
|
||||
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:action_result_type_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:diplomacy_offer_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:action_result_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library:eagle_client_exception",
|
||||
"//src/main/scala/net/eagle0/eagle/library:eagle_internal_exception",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:simple_action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/llm_request_generators/diplomacy_llm_helpers:diplomacy_resolution_llm_request_generator",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_simple_action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:eagle_require",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:action_result_trait",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:changed_battalion_trait",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:changed_hero_trait",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:notification_trait",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/changed_province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:action_result_concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:changed_faction_concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:notification_concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:offer_resolved_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/diplomacy_offer",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/diplomacy_offer/status",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -60,7 +60,11 @@ import net.eagle0.eagle.model.proto_converters.{
|
||||
UnaffiliatedHeroConverter
|
||||
}
|
||||
import net.eagle0.eagle.model.proto_converters.BattalionTypeIdConverter
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.{Invitation, TruceOffer}
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.{
|
||||
BreakAlliance,
|
||||
Invitation,
|
||||
TruceOffer
|
||||
}
|
||||
import net.eagle0.eagle.model.state.BattalionTypeId
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.faction.FactionT
|
||||
@@ -667,10 +671,31 @@ class CommandFactory {
|
||||
ac: ResolveBreakAllianceAvailableCommand,
|
||||
sc: ResolveBreakAllianceSelectedCommand
|
||||
) =>
|
||||
// TODO: after the Availability is migrated off protobuf, move these checks into the "make" method
|
||||
val originatingFid = sc.originatingFactionId
|
||||
val selectedOffer = ac.offers
|
||||
.find(_.originatingFactionId == originatingFid)
|
||||
.getOrElse(
|
||||
throw new EagleCommandException(
|
||||
s"Selected faction ID $originatingFid was not among options ${ac.offers.map(_.originatingFactionId)}"
|
||||
)
|
||||
)
|
||||
|
||||
val breakAllianceOffer =
|
||||
DiplomacyOfferConverter.fromProto(selectedOffer) match {
|
||||
case breakAlliance: BreakAlliance => breakAlliance
|
||||
case other =>
|
||||
throw new EagleInternalException(
|
||||
s"Expected BreakAlliance offer, got $other"
|
||||
)
|
||||
}
|
||||
|
||||
val resolution = StatusConverter.fromProto(sc.resolution)
|
||||
|
||||
ResolveBreakAllianceCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
availableCommand = ac,
|
||||
selectedCommand = sc,
|
||||
breakAllianceOffer = breakAllianceOffer,
|
||||
resolution = resolution,
|
||||
gameId = gameState.gameId,
|
||||
currentRoundId = gameState.currentRoundId
|
||||
)
|
||||
|
||||
+124
-101
@@ -1,130 +1,153 @@
|
||||
package net.eagle0.eagle.library.actions.impl.command
|
||||
import net.eagle0.eagle.api.available_command.ResolveBreakAllianceAvailableCommand
|
||||
import net.eagle0.eagle.api.selected_command.ResolveBreakAllianceSelectedCommand
|
||||
import net.eagle0.eagle.common.action_result_notification_details.Notification.Llm
|
||||
import net.eagle0.eagle.common.action_result_notification_details.{
|
||||
BreakAllianceAcceptedDetails,
|
||||
BreakAllianceAmbassadorImprisonedDetails,
|
||||
Notification
|
||||
}
|
||||
import net.eagle0.eagle.common.action_result_type.ActionResultType
|
||||
import net.eagle0.eagle.common.diplomacy_offer.DiplomacyOffer
|
||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus
|
||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.{
|
||||
DIPLOMACY_OFFER_STATUS_ACCEPTED,
|
||||
DIPLOMACY_OFFER_STATUS_IMPRISONED,
|
||||
DIPLOMACY_OFFER_STATUS_INVALIDATED,
|
||||
DIPLOMACY_OFFER_STATUS_REJECTED,
|
||||
DIPLOMACY_OFFER_STATUS_UNKNOWN,
|
||||
DIPLOMACY_OFFER_STATUS_UNRESOLVED,
|
||||
Unrecognized
|
||||
}
|
||||
import net.eagle0.eagle.internal.action_result.ActionResult
|
||||
import net.eagle0.eagle.internal.changed_faction.ChangedFaction
|
||||
import net.eagle0.eagle.library.actions.impl.common.SimpleAction
|
||||
import net.eagle0.eagle.library.actions.generated_text_request_generators.diplomacy_llm_helpers.DiplomacyResolutionLlmRequestGenerator
|
||||
|
||||
import net.eagle0.eagle.library.actions.impl.common.ProtolessSimpleAction
|
||||
import net.eagle0.eagle.library.util.EagleRequire.commandRequire
|
||||
import net.eagle0.eagle.library.{EagleCommandException, EagleInternalException}
|
||||
import net.eagle0.eagle.{FactionId, GameId, RoundId}
|
||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.action_result.concrete.{
|
||||
ActionResultC,
|
||||
ChangedFactionC,
|
||||
NotificationC
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.generated_text_request.LlmRequestT.BreakAllianceResolutionMessage
|
||||
import net.eagle0.eagle.model.action_result.{NotificationDetails, NotificationT}
|
||||
import net.eagle0.eagle.model.action_result.types.OfferResolvedResultType
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.status.{
|
||||
Accepted,
|
||||
Imprisoned,
|
||||
Rejected,
|
||||
Status
|
||||
}
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.{
|
||||
BreakAlliance,
|
||||
DiplomacyOffer
|
||||
}
|
||||
import net.eagle0.eagle.library.EagleCommandException
|
||||
import net.eagle0.eagle.{FactionId, GameId, HeroId, ProvinceId, RoundId}
|
||||
|
||||
object ResolveBreakAllianceCommand {
|
||||
private case class ResolveBreakAllianceCommand(
|
||||
sc: ResolveBreakAllianceSelectedCommand,
|
||||
breakAllianceOffer: DiplomacyOffer,
|
||||
resolution: DiplomacyOfferStatus,
|
||||
actingFactionId: FactionId,
|
||||
breakAllianceOffer: BreakAlliance,
|
||||
resolution: Status,
|
||||
gameId: GameId,
|
||||
currentRoundId: RoundId
|
||||
) extends SimpleAction {
|
||||
override def immediateExecute: ActionResult = {
|
||||
val llmRequest =
|
||||
DiplomacyResolutionLlmRequestGenerator.breakAllianceResolutionRequest(
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId,
|
||||
originatingFactionId = breakAllianceOffer.originatingFactionId,
|
||||
targetFactionId = breakAllianceOffer.targetFactionId,
|
||||
messengerHeroId = breakAllianceOffer.messengerHeroId,
|
||||
resolution = resolution
|
||||
)
|
||||
) extends ProtolessSimpleAction {
|
||||
private val originatingFactionId = breakAllianceOffer.originatingFactionId
|
||||
private val messengerHeroId = breakAllianceOffer.messengerHeroId
|
||||
private val messengerOriginProvinceId =
|
||||
breakAllianceOffer.messengerOriginProvinceId
|
||||
|
||||
ActionResult(
|
||||
`type` = ActionResultType.OFFER_RESOLVED,
|
||||
player = Some(breakAllianceOffer.targetFactionId),
|
||||
override def immediateExecute: ActionResultT = {
|
||||
val llmRequestId =
|
||||
s"$currentRoundId resolve break alliance $messengerHeroId"
|
||||
|
||||
resolution match {
|
||||
case Accepted =>
|
||||
acceptBreakAlliance(llmRequestId)
|
||||
case Imprisoned =>
|
||||
imprisonAmbassador(llmRequestId)
|
||||
case Rejected =>
|
||||
throw new EagleCommandException(
|
||||
"REJECTED not valid for break alliance"
|
||||
)
|
||||
case _ =>
|
||||
throw new EagleCommandException(
|
||||
s"Invalid diplomacy offer status for command: $resolution"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def acceptBreakAlliance(llmRequestId: String): ActionResultT = {
|
||||
ActionResultC(
|
||||
actionResultType = OfferResolvedResultType,
|
||||
actingFactionId = Some(actingFactionId),
|
||||
changedFactions = Vector(
|
||||
ChangedFaction(
|
||||
id = breakAllianceOffer.targetFactionId,
|
||||
ChangedFactionC(
|
||||
factionId = actingFactionId,
|
||||
removedIncomingDiplomacyOfferFactionIds =
|
||||
Vector(breakAllianceOffer.originatingFactionId),
|
||||
addedIncomingDiplomacyOffers =
|
||||
Vector(breakAllianceOffer.withStatus(resolution))
|
||||
Vector(originatingFactionId),
|
||||
newIncomingDiplomacyOffers =
|
||||
Vector(breakAllianceOffer.copy(status = Accepted))
|
||||
)
|
||||
),
|
||||
newDeferredNotification = Some(
|
||||
Notification(
|
||||
details = resolution match {
|
||||
case DIPLOMACY_OFFER_STATUS_ACCEPTED =>
|
||||
BreakAllianceAcceptedDetails(
|
||||
offeringFactionId = breakAllianceOffer.originatingFactionId,
|
||||
targetFactionId = breakAllianceOffer.targetFactionId,
|
||||
ambassadorHeroId = breakAllianceOffer.messengerHeroId
|
||||
)
|
||||
|
||||
case DIPLOMACY_OFFER_STATUS_REJECTED =>
|
||||
throw new EagleCommandException(
|
||||
"REJECTED not valid for break alliance"
|
||||
)
|
||||
|
||||
case DIPLOMACY_OFFER_STATUS_IMPRISONED =>
|
||||
BreakAllianceAmbassadorImprisonedDetails(
|
||||
offeringFactionId = breakAllianceOffer.originatingFactionId,
|
||||
targetFactionId = breakAllianceOffer.targetFactionId,
|
||||
ambassadorHeroId = breakAllianceOffer.messengerHeroId
|
||||
)
|
||||
|
||||
case DIPLOMACY_OFFER_STATUS_INVALIDATED =>
|
||||
throw new EagleCommandException(
|
||||
"Invalid diplomacy offers status for command"
|
||||
)
|
||||
|
||||
case DIPLOMACY_OFFER_STATUS_UNKNOWN |
|
||||
DIPLOMACY_OFFER_STATUS_UNRESOLVED | Unrecognized(_) =>
|
||||
throw new EagleInternalException(
|
||||
s"Unknown diplomacy offer status"
|
||||
)
|
||||
},
|
||||
llm = Llm.LlmId(llmRequest.id)
|
||||
newNotifications = Vector(
|
||||
NotificationC(
|
||||
details = NotificationDetails.BreakAllianceAccepted(
|
||||
offeringFactionId = originatingFactionId,
|
||||
targetFactionId = actingFactionId,
|
||||
ambassadorHeroId = messengerHeroId
|
||||
),
|
||||
affectedHeroIds = Vector(messengerHeroId),
|
||||
deferred = true
|
||||
)
|
||||
),
|
||||
newGeneratedTextRequests = Vector(llmRequest)
|
||||
newGeneratedTextRequests =
|
||||
Vector(createLlmRequest(llmRequestId, Accepted))
|
||||
)
|
||||
}
|
||||
|
||||
private def imprisonAmbassador(llmRequestId: String): ActionResultT = {
|
||||
ActionResultC(
|
||||
actionResultType = OfferResolvedResultType,
|
||||
actingFactionId = Some(actingFactionId),
|
||||
changedFactions = Vector(
|
||||
ChangedFactionC(
|
||||
factionId = actingFactionId,
|
||||
removedIncomingDiplomacyOfferFactionIds =
|
||||
Vector(originatingFactionId),
|
||||
newIncomingDiplomacyOffers =
|
||||
Vector(breakAllianceOffer.copy(status = Imprisoned))
|
||||
)
|
||||
),
|
||||
newNotifications = Vector(
|
||||
NotificationC(
|
||||
details = NotificationDetails.BreakAllianceAmbassadorImprisoned(
|
||||
offeringFactionId = originatingFactionId,
|
||||
targetFactionId = actingFactionId,
|
||||
ambassadorHeroId = messengerHeroId
|
||||
),
|
||||
affectedHeroIds = Vector(messengerHeroId),
|
||||
deferred = true
|
||||
)
|
||||
),
|
||||
newGeneratedTextRequests =
|
||||
Vector(createLlmRequest(llmRequestId, Imprisoned))
|
||||
)
|
||||
}
|
||||
|
||||
private def createLlmRequest(
|
||||
llmRequestId: String,
|
||||
status: Status
|
||||
): BreakAllianceResolutionMessage = {
|
||||
BreakAllianceResolutionMessage(
|
||||
requestId = llmRequestId,
|
||||
eagleGameId = gameId,
|
||||
recipientFactionIds = Vector.empty,
|
||||
alwaysGenerate = false,
|
||||
breakingFactionId = originatingFactionId,
|
||||
targetFactionId = actingFactionId,
|
||||
messengerHeroId = messengerHeroId,
|
||||
resolution = status
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def make(
|
||||
actingFactionId: FactionId,
|
||||
availableCommand: ResolveBreakAllianceAvailableCommand,
|
||||
selectedCommand: ResolveBreakAllianceSelectedCommand,
|
||||
breakAllianceOffer: BreakAlliance,
|
||||
resolution: Status,
|
||||
gameId: GameId,
|
||||
currentRoundId: RoundId
|
||||
): SimpleAction = {
|
||||
// check requirements
|
||||
val breakingFid = selectedCommand.originatingFactionId
|
||||
val offers = availableCommand.offers
|
||||
val selectedOffer = offers.find(_.originatingFactionId == breakingFid)
|
||||
|
||||
): ProtolessSimpleAction = {
|
||||
commandRequire(
|
||||
selectedOffer.nonEmpty,
|
||||
s"Selected faction ID $breakingFid was not among options ${offers.map(_.originatingFactionId)}"
|
||||
)
|
||||
|
||||
commandRequire(
|
||||
selectedOffer.get.eligibleStatuses.contains(selectedCommand.resolution),
|
||||
s"Selected break alliance resolution ${selectedCommand.resolution} was not among options ${selectedOffer.get.eligibleStatuses}"
|
||||
breakAllianceOffer.eligibleStatuses.contains(resolution),
|
||||
s"Selected break alliance resolution $resolution was not among options ${breakAllianceOffer.eligibleStatuses}"
|
||||
)
|
||||
|
||||
ResolveBreakAllianceCommand(
|
||||
sc = selectedCommand,
|
||||
breakAllianceOffer = selectedOffer.get,
|
||||
resolution = selectedCommand.resolution,
|
||||
actingFactionId = actingFactionId,
|
||||
breakAllianceOffer = breakAllianceOffer,
|
||||
resolution = resolution,
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId
|
||||
)
|
||||
|
||||
@@ -829,21 +829,21 @@ scala_test(
|
||||
name = "resolve_break_alliance_command_test",
|
||||
srcs = ["ResolveBreakAllianceCommandTest.scala"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:action_result_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:faction_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library:eagle_client_exception",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/command:resolve_break_alliance_command",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action_with_resulting_state",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:simple_action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:break_alliance_charisma_xp",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:break_alliance_wisdom_xp",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:faction_bias_from_imprisonment",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:trust_delta_for_imprisoning_other_hero",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:trust_delta_for_imprisoning_own_hero",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:idable",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_simple_action",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:action_result_trait",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:changed_battalion_trait",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:changed_hero_trait",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:notification_trait",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/changed_province/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:action_result_concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:changed_faction_concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:offer_resolved_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/diplomacy_offer",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/diplomacy_offer/status",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:action_impl_pkg",
|
||||
],
|
||||
)
|
||||
|
||||
+164
-149
@@ -1,29 +1,24 @@
|
||||
package net.eagle0.eagle.library.actions.impl.command
|
||||
|
||||
import net.eagle0.eagle.api.available_command.ResolveBreakAllianceAvailableCommand
|
||||
import net.eagle0.eagle.api.selected_command.ResolveBreakAllianceSelectedCommand
|
||||
import net.eagle0.eagle.common.action_result_type.ActionResultType.OFFER_RESOLVED
|
||||
import net.eagle0.eagle.common.diplomacy_offer.{
|
||||
BreakAllianceDetails,
|
||||
DiplomacyOffer
|
||||
}
|
||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.{
|
||||
DIPLOMACY_OFFER_STATUS_ACCEPTED,
|
||||
DIPLOMACY_OFFER_STATUS_IMPRISONED,
|
||||
DIPLOMACY_OFFER_STATUS_REJECTED,
|
||||
DIPLOMACY_OFFER_STATUS_UNRESOLVED
|
||||
}
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
BreakAllianceCharismaXp,
|
||||
BreakAllianceWisdomXp,
|
||||
FactionBiasFromImprisonment,
|
||||
TrustDeltaForImprisoningOtherHero,
|
||||
TrustDeltaForImprisoningOwnHero
|
||||
}
|
||||
import net.eagle0.eagle.library.{EagleClientException, EagleCommandException}
|
||||
import net.eagle0.eagle.model.action_result.concrete.{
|
||||
ActionResultC,
|
||||
ChangedFactionC
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.generated_text_request.LlmRequestT.BreakAllianceResolutionMessage
|
||||
import net.eagle0.eagle.model.action_result.NotificationDetails
|
||||
import net.eagle0.eagle.model.action_result.types.OfferResolvedResultType
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.BreakAlliance
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.status.{
|
||||
Accepted,
|
||||
Imprisoned,
|
||||
Rejected
|
||||
}
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.Inside.inside
|
||||
import org.scalatest.LoneElement.convertToCollectionLoneElementWrapper
|
||||
|
||||
class ResolveBreakAllianceCommandTest
|
||||
extends AnyFlatSpec
|
||||
@@ -31,112 +26,118 @@ class ResolveBreakAllianceCommandTest
|
||||
with BeforeAndAfterEach {
|
||||
|
||||
val actingFactionId = 4
|
||||
val gameId = 0xfeed
|
||||
val currentRoundId = 873
|
||||
|
||||
private val firstOffer = DiplomacyOffer(
|
||||
private val firstBreakAllianceOffer = BreakAlliance(
|
||||
originatingFactionId = 8,
|
||||
targetFactionId = actingFactionId,
|
||||
offerDetails = BreakAllianceDetails(),
|
||||
messengerHeroId = 29,
|
||||
messengerOriginProvinceId = 2,
|
||||
status = DIPLOMACY_OFFER_STATUS_UNRESOLVED,
|
||||
eligibleStatuses = Vector(
|
||||
DIPLOMACY_OFFER_STATUS_ACCEPTED,
|
||||
DIPLOMACY_OFFER_STATUS_REJECTED,
|
||||
DIPLOMACY_OFFER_STATUS_IMPRISONED
|
||||
)
|
||||
status = net.eagle0.eagle.model.state.diplomacy_offer.status.Unresolved,
|
||||
eligibleStatuses = Vector(Accepted, Rejected, Imprisoned),
|
||||
offerTextId = "break-alliance-text-1"
|
||||
)
|
||||
|
||||
private val secondOffer = DiplomacyOffer(
|
||||
originatingFactionId = 3,
|
||||
targetFactionId = actingFactionId,
|
||||
offerDetails = BreakAllianceDetails(),
|
||||
messengerHeroId = 19,
|
||||
messengerOriginProvinceId = 1,
|
||||
status = DIPLOMACY_OFFER_STATUS_UNRESOLVED,
|
||||
eligibleStatuses = Vector(
|
||||
DIPLOMACY_OFFER_STATUS_ACCEPTED,
|
||||
DIPLOMACY_OFFER_STATUS_REJECTED,
|
||||
DIPLOMACY_OFFER_STATUS_IMPRISONED
|
||||
)
|
||||
)
|
||||
|
||||
private val currentRoundId = 873
|
||||
private val gameId = 0xfeed
|
||||
|
||||
private val availableCommand = ResolveBreakAllianceAvailableCommand(
|
||||
offers = Vector(
|
||||
secondOffer,
|
||||
firstOffer
|
||||
)
|
||||
)
|
||||
|
||||
override def beforeEach(): Unit = {
|
||||
BreakAllianceCharismaXp.setIntValue(25)
|
||||
BreakAllianceWisdomXp.setIntValue(20)
|
||||
FactionBiasFromImprisonment.setDoubleValue(-100)
|
||||
TrustDeltaForImprisoningOwnHero.setIntValue(-150)
|
||||
TrustDeltaForImprisoningOtherHero.setIntValue(-40)
|
||||
}
|
||||
private val availableOriginatingFactionIds = Vector(3, 8)
|
||||
private val availableResolutions = Vector(Accepted, Rejected, Imprisoned)
|
||||
|
||||
"execute with resolution ACCEPT" should "return a result of type OFFER_RESOLVED" in {
|
||||
val sc = ResolveBreakAllianceSelectedCommand(
|
||||
originatingFactionId = 8,
|
||||
resolution = DIPLOMACY_OFFER_STATUS_ACCEPTED
|
||||
)
|
||||
|
||||
val result = ResolveBreakAllianceCommand
|
||||
.make(
|
||||
actingFactionId = actingFactionId,
|
||||
availableCommand = availableCommand,
|
||||
selectedCommand = sc,
|
||||
breakAllianceOffer = firstBreakAllianceOffer,
|
||||
resolution = Accepted,
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId
|
||||
)
|
||||
.immediateExecute
|
||||
|
||||
result.`type` shouldBe OFFER_RESOLVED
|
||||
result.actionResultType shouldBe OfferResolvedResultType
|
||||
}
|
||||
|
||||
it should "set the ACCEPTED resolution on the alliance offer on the acting faction" in {
|
||||
val sc = ResolveBreakAllianceSelectedCommand(
|
||||
originatingFactionId = 8,
|
||||
resolution = DIPLOMACY_OFFER_STATUS_ACCEPTED
|
||||
)
|
||||
|
||||
val result = ResolveBreakAllianceCommand
|
||||
val ar = ResolveBreakAllianceCommand
|
||||
.make(
|
||||
actingFactionId = actingFactionId,
|
||||
availableCommand = availableCommand,
|
||||
selectedCommand = sc,
|
||||
breakAllianceOffer = firstBreakAllianceOffer,
|
||||
resolution = Accepted,
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId
|
||||
)
|
||||
.immediateExecute
|
||||
|
||||
val actingFactionAfter = result.changedFactions
|
||||
.find(_.id == actingFactionId)
|
||||
.get
|
||||
inside(ar) { case result: ActionResultC =>
|
||||
val actingFactionAfter = result.changedFactions
|
||||
.find(_.factionId == actingFactionId)
|
||||
.get
|
||||
|
||||
actingFactionAfter.removedIncomingDiplomacyOfferFactionIds shouldBe Vector(
|
||||
firstOffer.originatingFactionId
|
||||
)
|
||||
actingFactionAfter.addedIncomingDiplomacyOffers shouldBe Vector(
|
||||
firstOffer.withStatus(DIPLOMACY_OFFER_STATUS_ACCEPTED)
|
||||
)
|
||||
inside(actingFactionAfter) { case cf: ChangedFactionC =>
|
||||
cf.removedIncomingDiplomacyOfferFactionIds shouldBe Vector(8)
|
||||
cf.newIncomingDiplomacyOffers shouldBe Vector(
|
||||
firstBreakAllianceOffer.copy(status = Accepted)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it should "create a notification for break alliance acceptance" in {
|
||||
val ar = ResolveBreakAllianceCommand
|
||||
.make(
|
||||
actingFactionId = actingFactionId,
|
||||
breakAllianceOffer = firstBreakAllianceOffer,
|
||||
resolution = Accepted,
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId
|
||||
)
|
||||
.immediateExecute
|
||||
|
||||
inside(ar) { case result: ActionResultC =>
|
||||
result.newNotifications should have size 1
|
||||
val notification = result.newNotifications.head
|
||||
notification.details shouldBe NotificationDetails.BreakAllianceAccepted(
|
||||
offeringFactionId = 8,
|
||||
targetFactionId = actingFactionId,
|
||||
ambassadorHeroId = 29
|
||||
)
|
||||
notification.affectedHeroIds shouldBe Vector(29)
|
||||
notification.deferred shouldBe true
|
||||
}
|
||||
}
|
||||
|
||||
it should "create an LLM request for break alliance acceptance" in {
|
||||
val ar = ResolveBreakAllianceCommand
|
||||
.make(
|
||||
actingFactionId = actingFactionId,
|
||||
breakAllianceOffer = firstBreakAllianceOffer,
|
||||
resolution = Accepted,
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId
|
||||
)
|
||||
.immediateExecute
|
||||
|
||||
inside(ar) { case result: ActionResultC =>
|
||||
result.newGeneratedTextRequests should have size 1
|
||||
|
||||
inside(result.newGeneratedTextRequests.head) {
|
||||
case llmRequest: BreakAllianceResolutionMessage =>
|
||||
llmRequest.requestId shouldBe s"$currentRoundId resolve break alliance 29"
|
||||
llmRequest.eagleGameId shouldBe gameId
|
||||
llmRequest.breakingFactionId shouldBe 8
|
||||
llmRequest.targetFactionId shouldBe actingFactionId
|
||||
llmRequest.messengerHeroId shouldBe 29
|
||||
llmRequest.resolution shouldBe Accepted
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"execute with resolution REJECT" should "throw an exception" in {
|
||||
val sc = ResolveBreakAllianceSelectedCommand(
|
||||
originatingFactionId = 8,
|
||||
resolution = DIPLOMACY_OFFER_STATUS_REJECTED
|
||||
)
|
||||
|
||||
val ex = the[EagleCommandException] thrownBy {
|
||||
ResolveBreakAllianceCommand
|
||||
.make(
|
||||
actingFactionId = actingFactionId,
|
||||
availableCommand = availableCommand,
|
||||
selectedCommand = sc,
|
||||
breakAllianceOffer = firstBreakAllianceOffer,
|
||||
resolution = Rejected,
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId
|
||||
)
|
||||
@@ -146,95 +147,109 @@ class ResolveBreakAllianceCommandTest
|
||||
ex.getMessage shouldBe "REJECTED not valid for break alliance"
|
||||
}
|
||||
|
||||
"make" should "throw if the selected fid was not an option" in {
|
||||
val sc = ResolveBreakAllianceSelectedCommand(
|
||||
originatingFactionId = 4
|
||||
)
|
||||
|
||||
val ex = the[EagleClientException] thrownBy {
|
||||
ResolveBreakAllianceCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
availableCommand = availableCommand,
|
||||
selectedCommand = sc,
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId
|
||||
)
|
||||
}
|
||||
|
||||
ex.getMessage shouldBe "requirement failed: Selected faction ID 4 was not among options Vector(3, 8)"
|
||||
}
|
||||
|
||||
it should "throw if the selected resolution was not an option" in {
|
||||
val sc = ResolveBreakAllianceSelectedCommand(
|
||||
originatingFactionId = 8,
|
||||
resolution = DIPLOMACY_OFFER_STATUS_IMPRISONED
|
||||
)
|
||||
val limitedResolutions = Vector(Accepted, Rejected) // No Imprisoned
|
||||
|
||||
val ex = the[EagleClientException] thrownBy {
|
||||
ResolveBreakAllianceCommand.make(
|
||||
actingFactionId = actingFactionId,
|
||||
availableCommand = availableCommand.withOffers(
|
||||
Vector(
|
||||
firstOffer.withEligibleStatuses(
|
||||
Vector(
|
||||
DIPLOMACY_OFFER_STATUS_ACCEPTED,
|
||||
DIPLOMACY_OFFER_STATUS_REJECTED
|
||||
)
|
||||
)
|
||||
)
|
||||
breakAllianceOffer = firstBreakAllianceOffer.copy(
|
||||
eligibleStatuses = limitedResolutions
|
||||
),
|
||||
selectedCommand = sc,
|
||||
resolution = Imprisoned, // Not in limitedResolutions
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId
|
||||
)
|
||||
}
|
||||
|
||||
ex.getMessage shouldBe "requirement failed: Selected break alliance resolution DIPLOMACY_OFFER_STATUS_IMPRISONED was not among options Vector(DIPLOMACY_OFFER_STATUS_ACCEPTED, DIPLOMACY_OFFER_STATUS_REJECTED)"
|
||||
ex.getMessage shouldBe "requirement failed: Selected break alliance resolution Imprisoned was not among options Vector(Accepted, Rejected)"
|
||||
}
|
||||
|
||||
"execute with resolution IMPRISON" should "return a result of type OFFER_RESOLVED" in {
|
||||
val sc = ResolveBreakAllianceSelectedCommand(
|
||||
originatingFactionId = 8,
|
||||
resolution = DIPLOMACY_OFFER_STATUS_IMPRISONED
|
||||
)
|
||||
|
||||
val result = ResolveBreakAllianceCommand
|
||||
.make(
|
||||
actingFactionId = actingFactionId,
|
||||
availableCommand = availableCommand,
|
||||
selectedCommand = sc,
|
||||
breakAllianceOffer = firstBreakAllianceOffer,
|
||||
resolution = Imprisoned,
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId
|
||||
)
|
||||
.immediateExecute
|
||||
|
||||
result.`type` shouldBe OFFER_RESOLVED
|
||||
result.actionResultType shouldBe OfferResolvedResultType
|
||||
}
|
||||
|
||||
it should "set the IMPRISONED resolution on the alliance offer on the acting faction" in {
|
||||
val sc = ResolveBreakAllianceSelectedCommand(
|
||||
originatingFactionId = 8,
|
||||
resolution = DIPLOMACY_OFFER_STATUS_IMPRISONED
|
||||
)
|
||||
|
||||
val result = ResolveBreakAllianceCommand
|
||||
val ar = ResolveBreakAllianceCommand
|
||||
.make(
|
||||
actingFactionId = actingFactionId,
|
||||
availableCommand = availableCommand,
|
||||
selectedCommand = sc,
|
||||
breakAllianceOffer = firstBreakAllianceOffer,
|
||||
resolution = Imprisoned,
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId
|
||||
)
|
||||
.immediateExecute
|
||||
|
||||
val actingFactionAfter = result.changedFactions
|
||||
.find(_.id == actingFactionId)
|
||||
.get
|
||||
actingFactionAfter.removedIncomingDiplomacyOfferFactionIds shouldBe Vector(
|
||||
firstOffer.originatingFactionId
|
||||
)
|
||||
actingFactionAfter.addedIncomingDiplomacyOffers shouldBe Vector(
|
||||
firstOffer.withStatus(DIPLOMACY_OFFER_STATUS_IMPRISONED)
|
||||
)
|
||||
inside(ar) { case result: ActionResultC =>
|
||||
val actingFactionAfter = result.changedFactions
|
||||
.find(_.factionId == actingFactionId)
|
||||
.get
|
||||
|
||||
inside(actingFactionAfter) { case cf: ChangedFactionC =>
|
||||
cf.removedIncomingDiplomacyOfferFactionIds shouldBe Vector(8)
|
||||
cf.newIncomingDiplomacyOffers shouldBe Vector(
|
||||
firstBreakAllianceOffer.copy(status = Imprisoned)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
it should "create a notification for ambassador imprisonment" in {
|
||||
val ar = ResolveBreakAllianceCommand
|
||||
.make(
|
||||
actingFactionId = actingFactionId,
|
||||
breakAllianceOffer = firstBreakAllianceOffer,
|
||||
resolution = Imprisoned,
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId
|
||||
)
|
||||
.immediateExecute
|
||||
|
||||
inside(ar) { case result: ActionResultC =>
|
||||
val notification = result.newNotifications.loneElement
|
||||
notification.details shouldBe NotificationDetails
|
||||
.BreakAllianceAmbassadorImprisoned(
|
||||
offeringFactionId = 8,
|
||||
targetFactionId = actingFactionId,
|
||||
ambassadorHeroId = 29
|
||||
)
|
||||
notification.affectedHeroIds shouldBe Vector(29)
|
||||
notification.deferred shouldBe true
|
||||
}
|
||||
}
|
||||
|
||||
it should "create an LLM request for ambassador imprisonment" in {
|
||||
val ar = ResolveBreakAllianceCommand
|
||||
.make(
|
||||
actingFactionId = actingFactionId,
|
||||
breakAllianceOffer = firstBreakAllianceOffer,
|
||||
resolution = Imprisoned,
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId
|
||||
)
|
||||
.immediateExecute
|
||||
|
||||
inside(ar) { case result: ActionResultC =>
|
||||
inside(result.newGeneratedTextRequests.loneElement) {
|
||||
case llmRequest: BreakAllianceResolutionMessage =>
|
||||
llmRequest.requestId shouldBe s"$currentRoundId resolve break alliance 29"
|
||||
llmRequest.eagleGameId shouldBe gameId
|
||||
llmRequest.breakingFactionId shouldBe 8
|
||||
llmRequest.targetFactionId shouldBe actingFactionId
|
||||
llmRequest.messengerHeroId shouldBe 29
|
||||
llmRequest.resolution shouldBe Imprisoned
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user