Skip to content

Latest commit

 

History

History
178 lines (147 loc) · 12.2 KB

combat.md

File metadata and controls

178 lines (147 loc) · 12.2 KB

Combat Shared Kernel

The implemented combat foundation currently owns exact arithmetic, deterministic random streams, temperament/runtime identities and structural species/lane/team/deck record decoding. It does not yet claim a content catalog or either battle engine.

Temperament and runtime identities

NewIdentity / identity admit the six Temperaments paired with exactly container, vm, bare_metal, serverless, edge or mainframe. Unknown, empty, case-adjusted, aliased or whitespace-adjusted values refuse instead of being repaired. TypeScript also refuses non-string axes and returns fresh frozen identity storage; Go returns a value and no partial identity on refusal. These are internal pairs, not a new serialized envelope, species catalog or ownership rule.

ChartIdentities / chartIdentities revalidate both caller-built pairs and delegate to the existing temperament chart. Runtime is a future catalog tag carrier, not a second Phase-0 type chart: all 1,296 directed matchups across the 36 identities match the existing shared literal chart rows. Every identity has 12 advantaged, 12 disadvantaged and 12 neutral opponents. Both runtimes consume testdata/combat/identity-vectors.json plus the existing arithmetic vectors. No stats, tags, sprites, effects, Trust/Soul tables, live engine, replay or HTTP consumer is added. As with the reference library, authoritative integration must register combat's runtime paths and perform the genuine kernel version change before activation.

Team and deck references

DecodeTeamReferences / decodeTeamReferences and the corresponding deck functions implement Combat C2's exact {v: 1, ids: [...]} envelope. Teams have three ordered species references; decks have eight ordered card references. Callers must supply declarations from their validated, pinned catalog. Unknown references, wrong slot counts, missing/extra/duplicate keys, noninteger version tokens and non-string entries refuse. Escaped keys/references, whitespace and valid Unicode remain valid; malformed surrogate escapes cannot become a declared replacement-character ID.

Slots are preserved, not sorted or deduplicated. This is structural reference validation, not authorization, pet-instance selection, duplicate-selection policy or battle/cycle legality. Those checks belong to the complete catalog/engine/integration contracts. The result owns its storage (a Go array; a frozen TypeScript tuple), with no mutation of source or declarations and no partial references returned on refusal. Both runtimes consume testdata/combat/reference-vectors.json; the tests use declarations, not a fabricated combat catalog. No live/replay/HTTP consumer imports this new layer yet. Combat's authoritative integration must register its runtime paths with the kernel identity guard and perform the genuine semantic version change; this standalone library is not a replay activation or mint.

Species records

DecodeSpeciesRecord / decodeSpeciesRecord implement C2's exact species row: {id, temperament, runtime, stats: {hp, atk, def, spd, stamina_max}, moves, tags}. Both identity axes use the existing C1 authority; all five stat fields require integer tokens in signed-int32 range. Every field is present/non-null, object keys are exact and unique, and the four ordered moves must occur in the caller's declared move set. Tags are an ordered string array. Duplicates in moves/tags are preserved: this reader does not invent a selection rule or synergy vocabulary. Valid escaping/Unicode and empty tag arrays survive; missing/extra/case- aliased/duplicate keys, malformed surrogate text, wrong types and undeclared moves refuse. Go additionally rejects malformed UTF-8 before its JSON decoder can replace bytes.

This is structural row decoding, not the complete catalog loader. Its signed-int32 representation does not decide battle-legal stat minima, ID namespace, effect compatibility, species ownership, runtime tag inheritance or balance values. Those remain the complete catalog/engine contracts. The decoder returns no partial Go record on refusal and does not rewrite source bytes. Go results own their four slots and tag slice; TypeScript freezes the record, stats, moves and tags.

Both runtimes consume testdata/combat/species-vectors.json, with eight accepted row forms and 67 refusals, plus every existing temperament/runtime pair and storage/declaration controls. The ordinary row values are synthetic test data, not a production species fixture. No live engine, save/replay or HTTP consumer imports this layer. Its eventual authoritative integration must register combat's runtime prefixes and make the genuine kernel version change before activation. Full C2's versioned catalog, effect union, cross-row policies and Phase-0 population, C5's Trust/Soul tables and both battle engines remain unimplemented.

Lane records

DecodeLaneRecord / decodeLaneRecord decode C2's common lane unit/building/spell row: {id, temperament, cost, hp, atk, spd, range, targets, tags} with an optional boolean bypass marker. The existing temperament authority is reused; targets are exactly units, buildings or any. All five numeric fields require signed-int32 integer tokens. Every required field is non-null; duplicate, extra, case-aliased, missing or wrong-type fields refuse. Ordered string tags and valid Unicode survive; malformed surrogate text refuses, and Go also rejects malformed UTF-8 bytes. Absence, explicit false and true bypass markers retain their distinct serialized forms. Go returns no partial record and owns its tag slice/marker; TypeScript returns fresh frozen records and tags. Source bytes remain untouched.

This is structural representation, not battle-legal stat/cost minima, a cheap-cycle threshold, effect execution, a cross-row bypass-coverage check or a complete catalog. The shared testdata/combat/lane-record-vectors.json supplies eight accepted forms and92 refusals; tests also cover all54 temperament/target/marker combinations and independent storage. These synthetic values are not production balance or content. No live/replay/HTTP consumer imports the decoder. Full catalog construction still needs the effect/table contracts and complete Phase-0 population; authoritative integration must register Combat's prefixes and make the genuine kernel version change.

Arithmetic and deterministic streams

Lane placement order

CompareLaneOrder / compareLaneOrder compare C4's (tick, player_slot, placement_seq) keys lexicographically, returning exactly −1, 0 or 1. Earlier fields always outrank later ones; equal coordinates compare equal. Comparisons do not subtract signed-int32 components, so representation-edge differences cannot wrap. Go keys have int32 fields; TypeScript validates all three signed-int32 components in both keys before comparing, refusing missing/non-numeric/coercible inputs. The helper does not serialize a command, mutate keys or inspect unrelated payload fields.

This is an ordering projection, not battle-input admission: legal ticks, slots, sequences, ownership and duplicate-placement handling remain the engine's contract. Signed edge fixtures exercise representation, not playable slot/tick values. Both runtimes consume testdata/combat/lane-order-vectors.json; independently enumerated ranks and reversed rotations also exercise the actual standard sort consumers. There is no live engine/replay consumer, complete battle log or C4 acceptance claim.

Exact arithmetic and random streams

The six Temperaments are ordered lazy, playful, curious, sassy, shy, chaotic. An attacker has advantage over the next two entries, disadvantage against the previous two, and a neutral result against itself and the opposite entry. The chart result is an enum, never a floating multiplier.

Damage is calculated identically in Go and TypeScript using integer stages:

  1. base power multiplied by attacker ATK and floor-divided by 64;
  2. advantage multiplied by 13/10 or disadvantage by 10/13, multiply before floor-divide;
  3. a critical result multiplied by 3/2 and floor-divided;
  4. positive base power receives minimum damage 1;
  5. storage saturates to signed int32.

Every intermediate is int64/BigInt. HP and stamina use explicit integer clamp operations.

TypeScript integer-store inputs must be primitive BigInts, and the damage critical flag must be a primitive boolean, even for zero base power. Runtime numbers (including fractions/NaN), numeric strings, boxed primitives and caller coercion cannot pass those boundaries. Damage's base power/ATK and clamp bounds remain signed-int32 numbers; valid results are unchanged. These are standalone library checks, not a new wire grammar or a battle consumer.

Native division is absent from server/combat and client/src/combat; the shared Go integer.DivideFloor and TypeScript idiv helpers live outside those directories. Both accept nonnegative dividends and positive divisors, with invalid inputs rejected. The Go helper leaves the existing staged damage calculation and int32 saturation unchanged.

make verify-combat-go-boundary recursively parses all Go combat sources, including tests and future engine subdirectories, with Go's AST. It rejects / and /=, malformed sources, symlinks, missing trees and empty source populations. Its tests exercise those refusals alongside safe comments, strings and helper calls. It runs in both server verification targets, including the server CI lane and local verify-push.

make verify-combat-boundary recursively parses every .js, .jsx, .ts, .tsx, .mts, and .cts combat module with TypeScript's AST. On every verification run it proves direct /, /=, division inside or after a template interpolation, and TSX division are rejected; strings, comments, and regular expressions cannot create blind spots or false positives; and a seeded violation in a nested future engine directory is discovered.

SplitMix64 and unbiased rejection sampling have one Go authority in server/determinism; the balance harness aliases it rather than maintaining a copy. A battle seed is the first SplitMix64 draw from the match seed. Independent consumers initialize from battle_seed XOR fnv1a64(label), so adding a random consumer cannot shift crit, obedience, or bot-policy draws. TypeScript implements the same uint64 wrap semantics with BigInt. Shared vectors cover damage ordering, minimum damage, saturation, non-identity ATK scaling, an advantage half-point that distinguishes floor from round, the order-sensitive disadvantage-plus- critical path, battle seed, all three registered Phase-0 substream labels, and bounded draws. The bounded corpus records the number of draws consumed and includes a case that rejects four successive draws before accepting, so a modulo-biased implementation cannot pass accidentally.

testdata/combat/substream-sequences.json additionally pins eight draws for each of the three fixture labels at match seeds 0, 42 and uint64 maximum. The retained Python reference prints these values using direct nth-state arithmetic without importing production code; its seed-42 first draws agree with the original vectors. Go/TS keep all stream instances alive together and compare complete sequences under round-robin, reversed and chunked consumption, including a new consumer making bounded draws. A controlled shared/resettable factory passes the earlier first-draw/recreation pattern but fails the sequence comparison. This tests factory/state ownership, not a demonstrated production alias, threaded battle, full catalog label population or golden battle log. Production RNG code is unchanged.

The same shared fixture also pins all 36 directed temperament pairs, nine signed-int32 saturation boundaries, thirteen clamp results and invalid clamp/damage domains in both runtimes. Its high-ATK disadvantage case exceeds int32 between stages but finishes below it, so saturating before the final store produces the wrong result. These are pure-kernel checks, not evidence of an integrated battle, populated combat catalog or live HP/stamina consumer.

The strict combat catalog and Trust/Soul input tables remain unimplemented because their active RFC does not yet enumerate the promised closed effect union or literal piecewise table points. Those are recorded as DESIGN-GAPs in the active planning log rather than represented here as shipped behavior.