This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/crates/sylpheed-formats/tests/zz_scratch_measure.rs
Sylpheed RE agent 49a09a9496 re: the "sibling default" rules are a dedup artefact — WITHDRAWN
The corpus recorded that some unit fields the disc leaves defaulted inherit
from a sibling: Size_Y from Size_X, FCSRange from RadarRange, DefencePoint
from AttackVesselPoint. Size_Y was marked the one to trust, on 9/9 support
across 7 independent ships, and it is restated in INDEX.md.

The premise is false. These fields are not defaulted -- they are on disc for
113-114 of 114 unit tables -- and Size_Y DIFFERS from Size_X in 90 of them.

The mechanism, cross-tabulating "legacy reader missed it" against "equal on
disc":

  pair                          seen+differ  seen+equal  miss+differ  miss+equal
  Size_Y / Size_X                    90          0            0           24
  FCSRange / RadarRange              54          0            1           58
  DefencePoint / AttackVesselPoint   51          0            1           61

seen+equal is 0 for all three: a value shared with a sibling is ALWAYS
invisible to the string-pool reader, because the pool stores each distinct
string once. And the reader almost never misses a value that differs. So
"the missing value equals the sibling's" was true BY CONSTRUCTION -- the rule
re-derived the very condition that made the field go missing. That is why the
support looked perfect: it could not fail on the cases it was fitted to.

The two miss+differ cells are its real wrong predictions, both named:
UN_e104_ADAN_Carrier DefencePoint is 0.2 (rule says 0.003), and
UN_e011_ADAN_Attacker_B_HF_Wayne FCSRange is 3000.0 (rule says 6000.0).

Retracted in unit-struct-runtime.md (original reasoning kept below the
correction), live-unit-definitions.md and INDEX.md. Pinned by a disc test
that asserts the seen+equal cells stay zero, so the mechanism itself is
guarded, not just the counts. Artifact: examples/sibling_rule_check.rs.

This one was found by my own check after the subagent assigned to it stalled.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-25 23:38:55 +00:00

89 lines
5.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! TEMPORARY measurement scratch — not for commit.
use std::collections::{BTreeMap, BTreeSet};
use sylpheed_formats::idxd::IdxdObject;
use sylpheed_formats::game_data::schema;
use sylpheed_formats::PakArchive;
fn pak(name: &str) -> Option<PakArchive> {
let disc = std::env::var("SYLPHEED_DISC").ok()?;
PakArchive::open(format!("{disc}/dat/{name}")).ok()
}
fn as_f32(v: &str) -> Option<f32> {
let v = v.strip_suffix(['f','F']).unwrap_or(v);
v.parse().ok()
}
/// field lists per loader
fn keys_for(schema_id: u32) -> Vec<&'static str> {
match schema_id {
schema::WEAPON => vec!["Power","Velocity","MinimumVelocity","MaximumVelocity","MinimumRange","MaximumRange","LoadingCount","Interval","TriggerShotCount","Mass","Heating","Cooling","LifeTime","ID","Name","TargetType"],
schema::UNIT => vec!["HP","IsDestructible","Size_X","Size_Y","Size_Z","Size_Radius","RadarRange","FCSRange","CruisingVelocity","MaximumVelocity","Acceleration","Deceleration","ShieldRatio","TurretCount","ScorePoint","ID","Name"],
schema::VESSEL => vec!["HP","Size_X","Size_Y","Size_Z","RadarRange","FCSRange","MaximumVelocity","ShieldRatio","ScorePoint","TurretCount","BridgeCount","HatchCount","ShieldGeneratorCount","ThrusterCount","ID","Name","Model"],
schema::PLAYER => vec!["AirDragFactor","GravityFactor","BulletLimit","LaserLimit","HomingLimit","SpaceSize","SupplyRange","MainMissionBonus","RankScore_S","RankScore_A","RankScore_B","RankScore_C","RankScore_D"],
schema::CHARACTER => vec!["ID","Name","SideID","Unique"],
schema::STAGE => vec!["BackGroundID"],
_ => vec![],
}
}
#[test]
fn measure() {
let Some(pak) = pak("GP_MAIN_GAME_E.pak") else { eprintln!("SKIP"); return };
let schemas = [
("WEAPON", schema::WEAPON), ("UNIT", schema::UNIT), ("VESSEL", schema::VESSEL),
("PLAYER", schema::PLAYER), ("CHARACTER", schema::CHARACTER), ("STAGE", schema::STAGE),
];
let mut tot = [0usize;5]; // reads, ok, miss, flat, wrong
for (label, sid) in schemas {
let mut objs = 0usize;
let mut c = [0usize;5];
let mut recnames: BTreeMap<String, usize> = BTreeMap::new();
let mut fieldspread: BTreeMap<&str, BTreeMap<usize,usize>> = BTreeMap::new();
let mut examples: Vec<String> = Vec::new();
for e in pak.entries() {
let Ok(b) = pak.read(e) else { continue };
let Ok(o) = IdxdObject::parse(&b) else { continue };
if o.schema_hash != sid { continue }
objs += 1;
let Some(recs) = o.records() else { eprintln!("{label}: NO RECORD TABLE"); continue };
for r in recs { *recnames.entry(r.name.clone()).or_default() += 1; }
// flat map, as game_data builds it
let fmap: BTreeMap<String,String> = o.resolved_fields().into_iter().map(|(k,v)|(k.to_string(),v.to_string())).collect();
for key in keys_for(sid) {
let hits: Vec<(&str,&str)> = recs.iter().filter_map(|r| r.get(key).map(|v| (r.name.as_str(), v))).collect();
let flat: Option<&str> = if ["ID","Name","Model","TargetType","SideID","BackGroundID","Unique","IsDestructible"].contains(&key) {
o.get_raw(key)
} else {
fmap.get(key).map(String::as_str)
};
if hits.is_empty() { continue }
c[0]+=1; tot[0]+=1;
let distinct: BTreeSet<&str> = hits.iter().map(|(_,v)| *v).collect();
*fieldspread.entry(key).or_default().entry(hits.len()).or_default() += 1;
if hits.len() > 1 && distinct.len() > 1 {
c[3]+=1; tot[3]+=1;
if examples.len() < 6 { examples.push(format!("FLAT {key}: {} records, {} distinct vals, flat={:?}, e.g. {:?}", hits.len(), distinct.len(), flat, &hits[..hits.len().min(4)])); }
} else {
let truth = hits[0].1;
match flat {
None => { c[2]+=1; tot[2]+=1;
if examples.len()<6 && hits.len()==1 { examples.push(format!("MISS {key} = {truth:?} in record {:?}", hits[0].0)); } }
Some(f) if f == truth || as_f32(f).is_some() && as_f32(f)==as_f32(truth) => { c[1]+=1; tot[1]+=1; }
Some(f) => { c[4]+=1; tot[4]+=1; examples.push(format!("WRONG {key}: flat={f:?} truth={truth:?} rec={:?}", hits[0].0)); }
}
}
}
}
println!("\n=== {label} {sid:#010x}: {objs} objects; reads {} ok {} miss {} flat {} wrong {}", c[0],c[1],c[2],c[3],c[4]);
let mut rn: Vec<_> = recnames.iter().collect();
rn.sort_by_key(|(_,n)| std::cmp::Reverse(**n));
println!(" records ({} distinct): {:?}", recnames.len(), rn.iter().take(25).map(|(k,n)|format!("{k}×{n}")).collect::<Vec<_>>());
for (k, spread) in &fieldspread {
println!(" field {k}: recs-per-object {:?}", spread);
}
for e in &examples { println!(" {e}"); }
}
println!("\n=== TOTAL reads {} ok {} miss {} flat {} wrong {}", tot[0],tot[1],tot[2],tot[3],tot[4]);
}