feat(ai): ✨ improve move vs combat parity tracking
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
88bb753876
commit
c94faa3a11
1 changed files with 36 additions and 3 deletions
|
|
@ -291,16 +291,30 @@ static func _replay_learned_actions(action_log: Array, player: RefCounted) -> in
|
|||
"to_hex": [ax.x, ax.y],
|
||||
}
|
||||
})
|
||||
# Capture pre-dispatch position + whether an enemy occupies the
|
||||
# target, so we can distinguish a true move (position == target)
|
||||
# from a move-as-attack (position unchanged, combat fired via
|
||||
# CombatResolver — `dispatch_move` routes to resolve_move_as_attack
|
||||
# which does NOT move the attacker).
|
||||
var u_pre: RefCounted = (index_maps.get("units", {}) as Dictionary).get(uid)
|
||||
var pre_pos: Vector2i = u_pre.position if u_pre != null else Vector2i(-999, -999)
|
||||
var was_attack: bool = (kind == "attack") or _enemy_or_city_at_axial(ax, player)
|
||||
if DispatchScript.dispatch_action(move_json, player, index_maps, city_name):
|
||||
applied += 1
|
||||
# Position-parity proof: read the unit's resulting GDScript
|
||||
# position and compare to the intended axial target.
|
||||
# position and label move vs combat.
|
||||
if parity_samples.size() < PARITY_SAMPLE_CAP:
|
||||
var u: RefCounted = (index_maps.get("units", {}) as Dictionary).get(uid)
|
||||
if u != null:
|
||||
var match_str: String = "OK" if u.position == ax else "MISMATCH"
|
||||
var label: String
|
||||
if u.position == ax:
|
||||
label = "MOVE_OK"
|
||||
elif was_attack and u.position == pre_pos:
|
||||
label = "COMBAT(stayed, CombatResolver fired)"
|
||||
else:
|
||||
label = "MISMATCH"
|
||||
parity_samples.append(
|
||||
"u%d→intended%s got%s [%s]" % [uid, str(ax), str(u.position), match_str]
|
||||
"u%d→tgt%s got%s [%s]" % [uid, str(ax), str(u.position), label]
|
||||
)
|
||||
"queue":
|
||||
var queue_json: String = JSON.stringify({
|
||||
|
|
@ -332,6 +346,25 @@ static func _replay_learned_actions(action_log: Array, player: RefCounted) -> in
|
|||
return applied
|
||||
|
||||
|
||||
## True if an ENEMY unit (or enemy city) occupies axial hex `at` from
|
||||
## `player`'s perspective — used only to LABEL the parity sample as a
|
||||
## move-as-attack (combat) vs a true move. Read-only.
|
||||
static func _enemy_or_city_at_axial(at: Vector2i, player: RefCounted) -> bool:
|
||||
var my_owner: int = int(player.index)
|
||||
for p: RefCounted in GameState.players:
|
||||
if p == null:
|
||||
continue
|
||||
if int(p.index) == my_owner:
|
||||
continue
|
||||
for u: RefCounted in p.units:
|
||||
if u != null and u.is_alive() and u.position == at:
|
||||
return true
|
||||
for c: RefCounted in p.cities:
|
||||
if c != null and c.position == at:
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
## Resolve the Rust faithful-state city_id (`"<pi>_<ci>"`) to the GDScript
|
||||
## index-maps city key (`pi*ID_STRIDE+ci`) that `dispatch_set_production`
|
||||
## consumes via `resolve_city`. The Rust CityView id is `"{p_idx}_{c_idx}"`.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue