Expose recommended Shardok placements (#8559)

This commit is contained in:
2026-07-14 14:19:44 -07:00
committed by GitHub
parent 66ecc25075
commit f438c4bbe2
3 changed files with 69 additions and 3 deletions
+14
View File
@@ -133,6 +133,20 @@ Shardok update with no available commands clears the previous command list.
Use `shardok-commands-json` or `shardok-units-json` when the concise output omits a needed field.
During battle setup, `shardok-commands` prints any server-recommended unit placements in order and identifies the
ordinary current command index for the next recommendation:
```text
recommended-starting-positions (ordered):
[0] unit=12 target=(3,4)
[1] unit=15 target=(4,4)
use next: post-shardok <shardok-game-id> 17
```
The list is advice, not an automatic placement batch. Review it against `shardok-map`, choose whether to use the first
recommendation, and submit that one placement with the printed `post-shardok` command. After every placement, wait for
the updated state and run `shardok-commands` again; the legal command indices and remaining recommendations may change.
The client synchronizes Shardok map definitions when it connects. To inspect the
base terrain together with current tile modifiers and occupying units, use:
@@ -1164,21 +1164,39 @@ final class ClientState {
s"battle ${battle.shardokGameId} token=${battle.token} status=${battle.status.map(_.status).getOrElse("-")}"
battle.availableCommands match {
case Some(commands) =>
val currentLines =
val currentLines =
if commands.currentCommand.nonEmpty then
"current:" +: commands.currentCommand.zipWithIndex.map {
case (cmd, index) =>
s" [$index] ${shardokCommandSummary(cmd, rawJson)}"
}
else Vector("current: none")
val previewLines =
val previewLines =
if commands.previewCommand.nonEmpty then
"preview:" +: commands.previewCommand.zipWithIndex.map {
case (cmd, index) =>
s" [$index] ${shardokCommandSummary(cmd, rawJson)}"
}
else Vector.empty
Vector(header) ++ currentLines ++ previewLines
val recommendedLines =
if commands.recommendedStartingPositions.nonEmpty then
val next = commands.recommendedStartingPositions.head
val commandIndex = commands.currentCommand.indexWhere { command =>
command.actor.contains(next.unitId) &&
command.target.exists(target => target.row == next.row && target.column == next.column)
}
val placements = commands.recommendedStartingPositions.zipWithIndex.map {
case (placement, index) =>
val summary =
s" [$index] unit=${placement.unitId} target=(${placement.row},${placement.column})"
if rawJson then s"$summary ${JsonFormat.toJsonString(placement)}" else summary
}
val useNext =
if commandIndex >= 0 then s"use next: post-shardok ${battle.shardokGameId} $commandIndex"
else "next recommended placement is not present in the current command list"
Vector("recommended-starting-positions (ordered):") ++ placements ++ Vector(useNext)
else Vector.empty
Vector(header) ++ currentLines ++ previewLines ++ recommendedLines
case None =>
Vector(header, "current: no command payload")
}
@@ -25,6 +25,7 @@ import net.eagle0.eagle.views.tutorial_dialogue.{TutorialDialogue, TutorialDialo
import net.eagle0.shardok.api.action_result_view.ActionResultView as ShardokActionResultView
import net.eagle0.shardok.api.command_descriptor.{AvailableCommands, CommandDescriptor}
import net.eagle0.shardok.api.game_state_view.GameStateViewDiff as ShardokGameStateViewDiff
import net.eagle0.shardok.api.placement_command.PlacementCommand
import net.eagle0.shardok.api.unit_view.{BattalionView, HeroView as ShardokHeroView, UnitView}
import net.eagle0.shardok.common.coords.Coords
import net.eagle0.shardok.common.game_status.GameStatus
@@ -378,6 +379,39 @@ class ClientStateTest extends AnyFlatSpec with Matchers {
state.shardokCommandPost(Some("battle-1"), index = 0) shouldBe None
}
it should "describe recommended starting positions and identify the next ordinary command" in {
val state = new ClientState
val placements = Vector(
PlacementCommand(unitId = 12, row = 3, column = 4),
PlacementCommand(unitId = 15, row = 4, column = 4)
)
state.handle(startingState("battle-1"))
state.handle(
shardokUpdate(
"battle-1",
Some(
AvailableCommands(
currentCommand = Vector(
CommandDescriptor(player = 3),
CommandDescriptor(
player = 3,
actor = Some(12),
target = Some(Coords(row = 3, column = 4))
)
),
recommendedStartingPositions = placements
)
)
)
)
val output = state.describeShardokCommands(rawJson = false)
output should include("recommended-starting-positions (ordered):")
output should include("[0] unit=12 target=(3,4)")
output should include("[1] unit=15 target=(4,4)")
output should include("use next: post-shardok battle-1 1")
}
it should "describe tactical conditions, unit health, and temporary effects" in {
val state = new ClientState
val diff = ShardokGameStateViewDiff(