Compare commits

...
Author SHA1 Message Date
adminandClaude Opus 4.5 e7b92ae927 Add toScala conversion to CommandSelection
Adds a toScala() method to CommandSelection that converts proto-based
command selections to ScalaCommandSelection with proper Scala types.
This encapsulates the proto→Scala conversion logic that was previously
duplicated in each caller.

Updates the three action classes in library/ that use CommandSelection:
- EndHandleRiotsPhaseAction
- PerformVassalCommandsPhaseAction
- PerformVassalDefenseDecisionsAction

These now use cs.toScala(opac.commands) instead of manually converting
the selected command and finding the matching available command.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 07:35:31 -08:00
6 changed files with 46 additions and 48 deletions
@@ -2,7 +2,7 @@ package net.eagle0.eagle.library.actions.impl.action
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.library.actions.applier.ActionResultApplier
import net.eagle0.eagle.library.actions.impl.command.{AvailableCommandTypeMap, HandleRiotUtils, TCommandFactory}
import net.eagle0.eagle.library.actions.impl.command.{HandleRiotUtils, TCommandFactory}
import net.eagle0.eagle.library.actions.impl.common.{ProtolessRandomSequentialResultsAction, TCommand}
import net.eagle0.eagle.library.actions.random_state_sequencer.RandomStateSequencer
import net.eagle0.eagle.library.util.command_choice_helpers.CommandChoiceHelpers
@@ -12,7 +12,6 @@ import net.eagle0.eagle.model.action_result.concrete.ActionResultC
import net.eagle0.eagle.model.action_result.types.ActionResultType.EndHandleRiotsPhase
import net.eagle0.eagle.model.action_result.ActionResultT
import net.eagle0.eagle.model.proto_converters.command.available.AvailableCommandConverter
import net.eagle0.eagle.model.proto_converters.command.selected.SelectedCommandConverter
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
import net.eagle0.eagle.model.state.command.available.OneProvinceAvailableCommands
import net.eagle0.eagle.model.state.game_state.GameState
@@ -53,21 +52,12 @@ case class EndHandleRiotsPhaseAction(
)
.continue {
case (Some(cs), nextFr) =>
// Convert proto types to Scala types
val scalaSelectedCommand = SelectedCommandConverter.fromProto(cs.selected)
val scalaAvailableCommand = AvailableCommandTypeMap
.matchingAvailableCommand(opac.commands, scalaSelectedCommand)
.getOrElse(
throw new IllegalStateException(
s"No matching Scala available command for selected command: ${scalaSelectedCommand.commandType}"
)
)
val cmd = commandFactory.makeTCommand(
actingFactionId = province.rulingFactionId.get,
val scalaCs = cs.toScala(opac.commands)
val cmd = commandFactory.makeTCommand(
actingFactionId = scalaCs.actingFactionId,
gameState = gs,
availableCommand = scalaAvailableCommand,
selectedCommand = scalaSelectedCommand
availableCommand = scalaCs.available,
selectedCommand = scalaCs.selected
)
cmd match {
case TCommand.Simple(action) =>
@@ -4,7 +4,6 @@ import net.eagle0.common.{FunctionalRandom, MoreOption, RandomState}
import net.eagle0.eagle.{FactionId, ProvinceId}
import net.eagle0.eagle.api.available_command.{AvailableCommand as ProtoAvailableCommand, RestAvailableCommand}
import net.eagle0.eagle.library.actions.applier.ActionResultApplier
import net.eagle0.eagle.library.actions.impl.command.AvailableCommandTypeMap
import net.eagle0.eagle.library.actions.impl.command.TCommandFactory
import net.eagle0.eagle.library.actions.impl.common.{ProtolessRandomSequentialResultsAction, TCommand}
import net.eagle0.eagle.library.actions.random_state_sequencer.RandomStateSequencer
@@ -13,7 +12,6 @@ import net.eagle0.eagle.library.util.province.ProvinceUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.model.action_result.ActionResultT
import net.eagle0.eagle.model.proto_converters.command.available.AvailableCommandConverter
import net.eagle0.eagle.model.proto_converters.command.selected.SelectedCommandConverter
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
import net.eagle0.eagle.model.state.command.available.OneProvinceAvailableCommands
import net.eagle0.eagle.model.state.game_state.GameState
@@ -52,21 +50,12 @@ case class PerformVassalCommandsPhaseAction(
sequencer.withRandomActionResults { (gs, fr) =>
chooseCommand(gs, province.id, fr).continue {
case (Some(cs), nextFr) =>
// Convert proto types to Scala types
val scalaSelectedCommand = SelectedCommandConverter.fromProto(cs.selected)
val scalaAvailableCommand = AvailableCommandTypeMap
.matchingAvailableCommand(opac.commands, scalaSelectedCommand)
.getOrElse(
throw new IllegalStateException(
s"No matching Scala available command for selected command: ${scalaSelectedCommand.commandType}"
)
)
val cmd = commandFactory.makeTCommand(
actingFactionId = cs.actingFactionId,
val scalaCs = cs.toScala(opac.commands)
val cmd = commandFactory.makeTCommand(
actingFactionId = scalaCs.actingFactionId,
gameState = gs,
availableCommand = scalaAvailableCommand,
selectedCommand = scalaSelectedCommand
availableCommand = scalaCs.available,
selectedCommand = scalaCs.selected
)
cmd match {
case TCommand.Simple(action) =>
@@ -3,7 +3,6 @@ package net.eagle0.eagle.library.actions.impl.action
import net.eagle0.common.{FunctionalRandom, RandomState}
import net.eagle0.eagle.api.available_command.AvailableCommand as ProtoAvailableCommand
import net.eagle0.eagle.library.actions.applier.ActionResultApplier
import net.eagle0.eagle.library.actions.impl.command.AvailableCommandTypeMap
import net.eagle0.eagle.library.actions.impl.command.TCommandFactory
import net.eagle0.eagle.library.actions.impl.common.{ProtolessRandomSequentialResultsAction, TCommand}
import net.eagle0.eagle.library.actions.random_state_sequencer.RandomStateSequencer
@@ -12,7 +11,6 @@ import net.eagle0.eagle.library.util.province.ProvinceUtils
import net.eagle0.eagle.library.util.CommandSelection
import net.eagle0.eagle.model.action_result.ActionResultT
import net.eagle0.eagle.model.proto_converters.command.available.AvailableCommandConverter
import net.eagle0.eagle.model.proto_converters.command.selected.SelectedCommandConverter
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
import net.eagle0.eagle.model.state.command.available.OneProvinceAvailableCommands
import net.eagle0.eagle.model.state.game_state.GameState
@@ -52,21 +50,12 @@ case class PerformVassalDefenseDecisionsAction(
}
chooseCommand(gs, province.id, protoCommands, fr).continue {
case (Some(cs), nextFr) =>
// Convert proto types to Scala types
val scalaSelectedCommand = SelectedCommandConverter.fromProto(cs.selected)
val scalaAvailableCommand = AvailableCommandTypeMap
.matchingAvailableCommand(opac.commands, scalaSelectedCommand)
.getOrElse(
throw new IllegalStateException(
s"No matching Scala available command for selected command: ${scalaSelectedCommand.commandType}"
)
)
val cmd = commandFactory.makeTCommand(
actingFactionId = cs.actingFactionId,
val scalaCs = cs.toScala(opac.commands)
val cmd = commandFactory.makeTCommand(
actingFactionId = scalaCs.actingFactionId,
gameState = gs,
availableCommand = scalaAvailableCommand,
selectedCommand = scalaSelectedCommand
availableCommand = scalaCs.available,
selectedCommand = scalaCs.selected
)
cmd match {
case TCommand.Simple(action) =>
@@ -127,6 +127,8 @@ scala_library(
"//src/main/protobuf/net/eagle0/eagle/api:available_command_scala_proto",
"//src/main/protobuf/net/eagle0/eagle/api:selected_command_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/actions/impl/command:available_command_type_map",
"//src/main/scala/net/eagle0/eagle/model/proto_converters/command/selected:selected_command_converter",
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/selected",
],
@@ -3,6 +3,8 @@ package net.eagle0.eagle.library.util
import net.eagle0.eagle.{FactionId, ProvinceId}
import net.eagle0.eagle.api.available_command.AvailableCommand as ProtoAvailableCommand
import net.eagle0.eagle.api.selected_command.SelectedCommand as ProtoSelectedCommand
import net.eagle0.eagle.library.actions.impl.command.AvailableCommandTypeMap
import net.eagle0.eagle.model.proto_converters.command.selected.SelectedCommandConverter
import net.eagle0.eagle.model.state.command.available.AvailableCommand
import net.eagle0.eagle.model.state.command.selected.SelectedCommand
@@ -16,6 +18,31 @@ case class CommandSelection(
) {
def withReason(reason: String): CommandSelection =
this.copy(reason = reason)
/**
* Converts this proto-based CommandSelection to a ScalaCommandSelection.
* @param scalaCommands
* The Scala available commands to match against
* @return
* ScalaCommandSelection with Scala types, or throws if no matching available command found
*/
def toScala(scalaCommands: Vector[AvailableCommand]): ScalaCommandSelection = {
val scalaSelected = SelectedCommandConverter.fromProto(selected)
val scalaAvailable = AvailableCommandTypeMap
.matchingAvailableCommand(scalaCommands, scalaSelected)
.getOrElse(
throw new IllegalStateException(
s"No matching Scala available command for selected command: ${scalaSelected.commandType}"
)
)
ScalaCommandSelection(
actingFactionId = actingFactionId,
actingProvinceId = actingProvinceId,
available = scalaAvailable,
selected = scalaSelected,
reason = reason
)
}
}
// Scala-based command selection
@@ -6,6 +6,7 @@ scala_library(
visibility = [
"//src/main/scala/net/eagle0/eagle/library:__pkg__",
"//src/main/scala/net/eagle0/eagle/library/actions:__subpackages__",
"//src/main/scala/net/eagle0/eagle/library/util:__pkg__",
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__pkg__",
"//src/main/scala/net/eagle0/eagle/service:__subpackages__",
"//src/test/scala/net/eagle0/eagle/model/proto_converters:__subpackages__",