feat(battle-node): client-shaped handshake builders for the scripted bot
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -127,6 +127,37 @@ public static class ScriptedLifecycle
|
|||||||
IdxChangeSeed: ScriptedProfiles.ReadyIdxChangeSeed,
|
IdxChangeSeed: ScriptedProfiles.ReadyIdxChangeSeed,
|
||||||
Spin: ScriptedProfiles.ReadySpin));
|
Spin: ScriptedProfiles.ReadySpin));
|
||||||
|
|
||||||
|
// --- Client-shaped emissions used by ScriptedBotParticipant so the session brokers
|
||||||
|
// the bot through the same handshake arms as a human. Bodies for the parameterless
|
||||||
|
// handshake frames are ignored by the session (it reads from.Context / phase); only
|
||||||
|
// Swap's idxList is consumed (empty = keep the dealt hand).
|
||||||
|
|
||||||
|
public static MsgEnvelope BuildClientInitNetwork() => ClientFrame(NetworkBattleUri.InitNetwork, EmitCategory.General);
|
||||||
|
public static MsgEnvelope BuildClientInitBattle() => ClientFrame(NetworkBattleUri.InitBattle, EmitCategory.General);
|
||||||
|
public static MsgEnvelope BuildClientLoaded() => ClientFrame(NetworkBattleUri.Loaded, EmitCategory.General);
|
||||||
|
|
||||||
|
public static MsgEnvelope BuildClientSwap() =>
|
||||||
|
new(NetworkBattleUri.Swap,
|
||||||
|
ViewerId: FakeOpponentViewerId,
|
||||||
|
Uuid: WireConstants.ServerUuid,
|
||||||
|
Bid: null,
|
||||||
|
Try: 0,
|
||||||
|
Cat: EmitCategory.Battle,
|
||||||
|
PubSeq: null,
|
||||||
|
PlaySeq: null,
|
||||||
|
Body: new RawBody(new Dictionary<string, object?> { ["idxList"] = new List<object?>() }));
|
||||||
|
|
||||||
|
private static MsgEnvelope ClientFrame(NetworkBattleUri uri, EmitCategory cat) =>
|
||||||
|
new(uri,
|
||||||
|
ViewerId: FakeOpponentViewerId,
|
||||||
|
Uuid: WireConstants.ServerUuid,
|
||||||
|
Bid: null,
|
||||||
|
Try: 0,
|
||||||
|
Cat: cat,
|
||||||
|
PubSeq: null,
|
||||||
|
PlaySeq: null,
|
||||||
|
Body: new ResultCodeOnlyBody());
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// First half of the v1.1 scripted opponent turn cycle: pushed after the player's
|
/// First half of the v1.1 scripted opponent turn cycle: pushed after the player's
|
||||||
/// TurnEnd, transitions the client into "Opponent's turn…" state. Paired with
|
/// TurnEnd, transitions the client into "Opponent's turn…" state. Paired with
|
||||||
|
|||||||
@@ -166,6 +166,27 @@ public class ScriptedLifecycleTests
|
|||||||
"single-arg overload (non-interactive opponent) keeps the placeholder hand.");
|
"single-arg overload (non-interactive opponent) keeps the placeholder hand.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BuildClientSwap_has_empty_idxList_in_RawBody()
|
||||||
|
{
|
||||||
|
var env = ScriptedLifecycle.BuildClientSwap();
|
||||||
|
Assert.That(env.Uri, Is.EqualTo(NetworkBattleUri.Swap));
|
||||||
|
Assert.That(env.ViewerId, Is.EqualTo(ScriptedLifecycle.FakeOpponentViewerId));
|
||||||
|
|
||||||
|
var raw = (RawBody)env.Body;
|
||||||
|
Assert.That(raw.Entries.ContainsKey("idxList"), Is.True);
|
||||||
|
var idx = (System.Collections.IEnumerable)raw.Entries["idxList"]!;
|
||||||
|
Assert.That(idx.Cast<object>().Count(), Is.EqualTo(0), "bot keeps its dealt hand (empty mulligan).");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BuildClientHandshakeFrames_carry_expected_uris()
|
||||||
|
{
|
||||||
|
Assert.That(ScriptedLifecycle.BuildClientInitNetwork().Uri, Is.EqualTo(NetworkBattleUri.InitNetwork));
|
||||||
|
Assert.That(ScriptedLifecycle.BuildClientInitBattle().Uri, Is.EqualTo(NetworkBattleUri.InitBattle));
|
||||||
|
Assert.That(ScriptedLifecycle.BuildClientLoaded().Uri, Is.EqualTo(NetworkBattleUri.Loaded));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void BuildOpponentTurnStart_HasUriTurnStartAndSpin()
|
public void BuildOpponentTurnStart_HasUriTurnStartAndSpin()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user