gameplay: port raw ranking contracts
Browse files- crates/urt-gameplay/src/bin/urt-lifecycle.rs +47 -0
- crates/urt-gameplay/src/lib.rs +1 -0
- crates/urt-gameplay/src/ranking.rs +236 -0
- docs/ASSET_PIPELINE_TODO.md +3 -0
- docs/FINDINGS.md +3 -0
- docs/PROVISIONAL_TODO.md +3 -0
- docs/RESEARCH_LEDGER.md +5 -0
- docs/REVERSE_ENGINEERING_TODO.md +3 -0
- docs/research/README.md +3 -0
- docs/research/RUST_RANKING.md +7 -0
- docs/research/rust-health-impulse.json +1 -1
- docs/research/rust-ranking.json +0 -0
- scripts/analyze_rust_ranking.py +18 -0
- scripts/test_rust_ranking.py +15 -0
- xtask/checks.tsv +10 -0
crates/urt-gameplay/src/bin/urt-lifecycle.rs
CHANGED
|
@@ -314,6 +314,53 @@ fn run() -> Result<(), String> {
|
|
| 314 |
second.cached_ping as u32
|
| 315 |
);
|
| 316 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
_ => return Err("unsupported contract or arguments".into()),
|
| 318 |
}
|
| 319 |
Ok(())
|
|
|
|
| 314 |
second.cached_ping as u32
|
| 315 |
);
|
| 316 |
}
|
| 317 |
+
"rank-compare" if w.len() == 21 => {
|
| 318 |
+
let client = |v: &[u32]| urt_gameplay::ranking::Client {
|
| 319 |
+
spectator_state: v[0] as i32,
|
| 320 |
+
follow_index: v[1] as i32,
|
| 321 |
+
connection: v[2] as i32,
|
| 322 |
+
team: v[3] as i32,
|
| 323 |
+
spectator_time: v[4] as i32,
|
| 324 |
+
time: v[5] as i32,
|
| 325 |
+
class: v[6] as i32,
|
| 326 |
+
score: v[7] as i32,
|
| 327 |
+
deaths: v[8] as i32,
|
| 328 |
+
awards: v[9] as i32,
|
| 329 |
+
};
|
| 330 |
+
println!(
|
| 331 |
+
"{{\"result\":{}}}",
|
| 332 |
+
urt_gameplay::ranking::compare(w[0] as i32, client(&w[1..11]), client(&w[11..]))
|
| 333 |
+
);
|
| 334 |
+
}
|
| 335 |
+
"rank-words" if w.len() >= 4 && w[3] <= 6 => {
|
| 336 |
+
let n = w[3] as usize;
|
| 337 |
+
if w.len() != 4 + 11 * n {
|
| 338 |
+
return Err("invalid ranking words".into());
|
| 339 |
+
}
|
| 340 |
+
let client = |v: &[u32]| urt_gameplay::ranking::Client {
|
| 341 |
+
spectator_state: v[0] as i32,
|
| 342 |
+
follow_index: v[1] as i32,
|
| 343 |
+
connection: v[2] as i32,
|
| 344 |
+
team: v[3] as i32,
|
| 345 |
+
spectator_time: v[4] as i32,
|
| 346 |
+
time: v[5] as i32,
|
| 347 |
+
class: v[6] as i32,
|
| 348 |
+
score: v[7] as i32,
|
| 349 |
+
deaths: v[8] as i32,
|
| 350 |
+
awards: v[9] as i32,
|
| 351 |
+
};
|
| 352 |
+
let clients: Vec<_> = w[4 + n..].chunks_exact(10).map(client).collect();
|
| 353 |
+
let order: Vec<_> = w[4..4 + n].iter().map(|v| *v as usize).collect();
|
| 354 |
+
let ranks = urt_gameplay::ranking::rank_words(
|
| 355 |
+
w[0] as i32,
|
| 356 |
+
&clients,
|
| 357 |
+
&order,
|
| 358 |
+
[w[1] as i32, w[2] as i32],
|
| 359 |
+
)
|
| 360 |
+
.map_err(|e| format!("unsupported input: {e:?}"))?;
|
| 361 |
+
let ranks: Vec<_> = ranks.iter().map(|v| v.to_string()).collect();
|
| 362 |
+
println!("{{\"ranks\":[{}]}}", ranks.join(","));
|
| 363 |
+
}
|
| 364 |
_ => return Err("unsupported contract or arguments".into()),
|
| 365 |
}
|
| 366 |
Ok(())
|
crates/urt-gameplay/src/lib.rs
CHANGED
|
@@ -149,6 +149,7 @@ pub mod intercept_animation;
|
|
| 149 |
pub mod method_stats;
|
| 150 |
pub mod mode10;
|
| 151 |
pub mod protected;
|
|
|
|
| 152 |
pub mod recent_awards;
|
| 153 |
pub mod recipient_payload;
|
| 154 |
pub mod record_clear;
|
|
|
|
| 149 |
pub mod method_stats;
|
| 150 |
pub mod mode10;
|
| 151 |
pub mod protected;
|
| 152 |
+
pub mod ranking;
|
| 153 |
pub mod recent_awards;
|
| 154 |
pub mod recipient_payload;
|
| 155 |
pub mod record_clear;
|
crates/urt-gameplay/src/ranking.rs
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//! Source5441 raw comparator and5864 finite eligible-client rank publication.
|
| 2 |
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
| 3 |
+
pub struct Client {
|
| 4 |
+
pub spectator_state: i32,
|
| 5 |
+
pub follow_index: i32,
|
| 6 |
+
pub connection: i32,
|
| 7 |
+
pub team: i32,
|
| 8 |
+
pub spectator_time: i32,
|
| 9 |
+
pub time: i32,
|
| 10 |
+
pub class: i32,
|
| 11 |
+
pub score: i32,
|
| 12 |
+
pub deaths: i32,
|
| 13 |
+
pub awards: i32,
|
| 14 |
+
}
|
| 15 |
+
/// Raw source result deliberately does not implement Ord: two deferred or
|
| 16 |
+
/// connecting clients can compare+1 in both directions, including self.
|
| 17 |
+
pub fn compare(mode: i32, a: Client, b: Client) -> i32 {
|
| 18 |
+
if a.spectator_state == 3 || a.follow_index < 0 {
|
| 19 |
+
return 1;
|
| 20 |
+
}
|
| 21 |
+
if b.spectator_state == 3 || b.follow_index < 0 {
|
| 22 |
+
return -1;
|
| 23 |
+
}
|
| 24 |
+
if a.connection == 1 {
|
| 25 |
+
return 1;
|
| 26 |
+
}
|
| 27 |
+
if b.connection == 1 {
|
| 28 |
+
return -1;
|
| 29 |
+
}
|
| 30 |
+
let cmp = |x: i32, y: i32| {
|
| 31 |
+
if x < y {
|
| 32 |
+
-1
|
| 33 |
+
} else if x > y {
|
| 34 |
+
1
|
| 35 |
+
} else {
|
| 36 |
+
0
|
| 37 |
+
}
|
| 38 |
+
};
|
| 39 |
+
if a.team == 3 && b.team == 3 {
|
| 40 |
+
return cmp(a.spectator_time, b.spectator_time);
|
| 41 |
+
}
|
| 42 |
+
if a.team == 3 {
|
| 43 |
+
return 1;
|
| 44 |
+
}
|
| 45 |
+
if b.team == 3 {
|
| 46 |
+
return -1;
|
| 47 |
+
}
|
| 48 |
+
if mode == 9 {
|
| 49 |
+
return if a.time != 0 && b.time == 0 {
|
| 50 |
+
-1
|
| 51 |
+
} else if a.time == 0 && b.time != 0 {
|
| 52 |
+
1
|
| 53 |
+
} else {
|
| 54 |
+
cmp(a.time, b.time)
|
| 55 |
+
};
|
| 56 |
+
}
|
| 57 |
+
if mode == 11 && a.class != b.class {
|
| 58 |
+
return -cmp(a.class, b.class);
|
| 59 |
+
}
|
| 60 |
+
if a.score != b.score {
|
| 61 |
+
return -cmp(a.score, b.score);
|
| 62 |
+
}
|
| 63 |
+
if a.deaths != b.deaths {
|
| 64 |
+
return cmp(a.deaths, b.deaths);
|
| 65 |
+
}
|
| 66 |
+
if mode >= 3 && mode != 9 && mode != 11 {
|
| 67 |
+
return -cmp(a.awards, b.awards);
|
| 68 |
+
}
|
| 69 |
+
0
|
| 70 |
+
}
|
| 71 |
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
| 72 |
+
pub enum RankError {
|
| 73 |
+
UnsupportedParticipant,
|
| 74 |
+
InvalidOrder,
|
| 75 |
+
}
|
| 76 |
+
/// Caller supplies a complete ordering. This primitive does not execute a sort
|
| 77 |
+
/// or publication callbacks. Source parity fixtures use eligible n<7 clients.
|
| 78 |
+
pub fn rank_words(
|
| 79 |
+
mode: i32,
|
| 80 |
+
clients: &[Client],
|
| 81 |
+
order: &[usize],
|
| 82 |
+
scores: [i32; 2],
|
| 83 |
+
) -> Result<Vec<u32>, RankError> {
|
| 84 |
+
if clients
|
| 85 |
+
.iter()
|
| 86 |
+
.any(|c| c.connection != 2 || c.team == 3 || c.spectator_state == 3 || c.follow_index < 0)
|
| 87 |
+
{
|
| 88 |
+
return Err(RankError::UnsupportedParticipant);
|
| 89 |
+
}
|
| 90 |
+
let mut seen = vec![false; clients.len()];
|
| 91 |
+
if order.len() != clients.len() {
|
| 92 |
+
return Err(RankError::InvalidOrder);
|
| 93 |
+
}
|
| 94 |
+
for &id in order {
|
| 95 |
+
if id >= clients.len() || seen[id] {
|
| 96 |
+
return Err(RankError::InvalidOrder);
|
| 97 |
+
}
|
| 98 |
+
seen[id] = true;
|
| 99 |
+
}
|
| 100 |
+
if mode >= 3 && mode != 9 && mode != 11 {
|
| 101 |
+
return Ok(vec![
|
| 102 |
+
if scores[0] == scores[1] {
|
| 103 |
+
2
|
| 104 |
+
} else if scores[0] > scores[1] {
|
| 105 |
+
0
|
| 106 |
+
} else {
|
| 107 |
+
1
|
| 108 |
+
};
|
| 109 |
+
clients.len()
|
| 110 |
+
]);
|
| 111 |
+
}
|
| 112 |
+
let mut result = vec![0; clients.len()];
|
| 113 |
+
let mut base = 0;
|
| 114 |
+
let mut previous = None;
|
| 115 |
+
for (j, &id) in order.iter().enumerate() {
|
| 116 |
+
let c = clients[id];
|
| 117 |
+
let key = if mode == 9 {
|
| 118 |
+
c.time
|
| 119 |
+
} else if mode == 11 {
|
| 120 |
+
c.class
|
| 121 |
+
} else {
|
| 122 |
+
c.score
|
| 123 |
+
};
|
| 124 |
+
if j == 0 || Some(key) != previous {
|
| 125 |
+
base = j as u32;
|
| 126 |
+
result[id] = base;
|
| 127 |
+
} else {
|
| 128 |
+
result[order[j - 1]] = base | 16384;
|
| 129 |
+
result[id] = base | 16384;
|
| 130 |
+
}
|
| 131 |
+
previous = Some(key);
|
| 132 |
+
}
|
| 133 |
+
Ok(result)
|
| 134 |
+
}
|
| 135 |
+
#[cfg(test)]
|
| 136 |
+
mod tests {
|
| 137 |
+
use super::*;
|
| 138 |
+
fn client() -> Client {
|
| 139 |
+
Client {
|
| 140 |
+
spectator_state: 0,
|
| 141 |
+
follow_index: 0,
|
| 142 |
+
connection: 2,
|
| 143 |
+
team: 1,
|
| 144 |
+
spectator_time: 0,
|
| 145 |
+
time: 0,
|
| 146 |
+
class: 0,
|
| 147 |
+
score: 0,
|
| 148 |
+
deaths: 0,
|
| 149 |
+
awards: 0,
|
| 150 |
+
}
|
| 151 |
+
}
|
| 152 |
+
#[test]
|
| 153 |
+
fn raw_prefilter_quirks_remain_nonordering() {
|
| 154 |
+
let c = Client {
|
| 155 |
+
spectator_state: 3,
|
| 156 |
+
..client()
|
| 157 |
+
};
|
| 158 |
+
assert_eq!(compare(0, c, c), 1);
|
| 159 |
+
let c = Client {
|
| 160 |
+
connection: 1,
|
| 161 |
+
..client()
|
| 162 |
+
};
|
| 163 |
+
assert_eq!(compare(0, c, c), 1);
|
| 164 |
+
}
|
| 165 |
+
#[test]
|
| 166 |
+
fn special_modes_and_signed_priorities() {
|
| 167 |
+
let a = Client {
|
| 168 |
+
time: -1,
|
| 169 |
+
class: 2,
|
| 170 |
+
score: i32::MIN,
|
| 171 |
+
awards: i32::MAX,
|
| 172 |
+
..client()
|
| 173 |
+
};
|
| 174 |
+
let b = Client {
|
| 175 |
+
score: i32::MAX,
|
| 176 |
+
..client()
|
| 177 |
+
};
|
| 178 |
+
assert_eq!(compare(9, a, b), -1);
|
| 179 |
+
assert_eq!(compare(11, a, b), -1);
|
| 180 |
+
assert_eq!(compare(0, a, b), 1);
|
| 181 |
+
assert_eq!(
|
| 182 |
+
compare(
|
| 183 |
+
3,
|
| 184 |
+
a,
|
| 185 |
+
Client {
|
| 186 |
+
score: i32::MIN,
|
| 187 |
+
..b
|
| 188 |
+
}
|
| 189 |
+
),
|
| 190 |
+
-1
|
| 191 |
+
);
|
| 192 |
+
}
|
| 193 |
+
#[test]
|
| 194 |
+
fn ties_and_global_team_result_words() {
|
| 195 |
+
let c = [
|
| 196 |
+
Client {
|
| 197 |
+
score: 5,
|
| 198 |
+
..client()
|
| 199 |
+
},
|
| 200 |
+
Client {
|
| 201 |
+
score: 5,
|
| 202 |
+
..client()
|
| 203 |
+
},
|
| 204 |
+
client(),
|
| 205 |
+
];
|
| 206 |
+
assert_eq!(
|
| 207 |
+
rank_words(0, &c, &[0, 1, 2], [0, 0]).unwrap(),
|
| 208 |
+
[16384, 16384, 2]
|
| 209 |
+
);
|
| 210 |
+
assert_eq!(rank_words(3, &c, &[0, 1, 2], [1, 1]).unwrap(), [2, 2, 2]);
|
| 211 |
+
}
|
| 212 |
+
#[test]
|
| 213 |
+
fn invalid_permutation_and_participants_reject() {
|
| 214 |
+
let c = [client(), client()];
|
| 215 |
+
assert_eq!(
|
| 216 |
+
rank_words(0, &c, &[0, 0], [0, 0]),
|
| 217 |
+
Err(RankError::InvalidOrder)
|
| 218 |
+
);
|
| 219 |
+
assert_eq!(
|
| 220 |
+
rank_words(0, &c, &[0, 2], [0, 0]),
|
| 221 |
+
Err(RankError::InvalidOrder)
|
| 222 |
+
);
|
| 223 |
+
assert_eq!(
|
| 224 |
+
rank_words(
|
| 225 |
+
0,
|
| 226 |
+
&[Client {
|
| 227 |
+
team: 3,
|
| 228 |
+
..client()
|
| 229 |
+
}],
|
| 230 |
+
&[0],
|
| 231 |
+
[0, 0]
|
| 232 |
+
),
|
| 233 |
+
Err(RankError::UnsupportedParticipant)
|
| 234 |
+
);
|
| 235 |
+
}
|
| 236 |
+
}
|
docs/ASSET_PIPELINE_TODO.md
CHANGED
|
@@ -982,3 +982,6 @@ Research progress: RE-774 [report](research/QVM_LEADERBOARD_CATEGORIES.md); orig
|
|
| 982 |
|
| 983 |
|
| 984 |
Research progress: RE-775 [report](research/QVM_RECIPIENT_WIRE.md); original assets unchanged.
|
|
|
|
|
|
|
|
|
|
|
|
| 982 |
|
| 983 |
|
| 984 |
Research progress: RE-775 [report](research/QVM_RECIPIENT_WIRE.md); original assets unchanged.
|
| 985 |
+
|
| 986 |
+
|
| 987 |
+
Research progress: RE-776 [report](research/RUST_RANKING.md); original assets unchanged.
|
docs/FINDINGS.md
CHANGED
|
@@ -1328,3 +1328,6 @@ RE-725–734 batch: [health, contact and Rust impulse findings](research/QVM_HEA
|
|
| 1328 |
|
| 1329 |
|
| 1330 |
**RE-775 — Original recipient command templates and bounded byte serialization model:** Original scoress/scoresd templates carry12 numeric fields plus a name per participant;69338 requests31-byte strncpy then forces the32nd byte to zero. [Evidence](research/QVM_RECIPIENT_WIRE.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 1328 |
|
| 1329 |
|
| 1330 |
**RE-775 — Original recipient command templates and bounded byte serialization model:** Original scoress/scoresd templates carry12 numeric fields plus a name per participant;69338 requests31-byte strncpy then forces the32nd byte to zero. [Evidence](research/QVM_RECIPIENT_WIRE.md).
|
| 1331 |
+
|
| 1332 |
+
|
| 1333 |
+
**RE-776 — Rust raw comparator and eligible rank publication primitives:** Rust raw comparator and rank words match232 comparator and48 eligible complete-ranking source fixtures without imposing Ord on non-ordering prefilters. [Evidence](research/RUST_RANKING.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -1326,3 +1326,6 @@ Research progress: RE-774 [report](research/QVM_LEADERBOARD_CATEGORIES.md); full
|
|
| 1326 |
|
| 1327 |
|
| 1328 |
Research progress: RE-775 [report](research/QVM_RECIPIENT_WIRE.md); full native/platform/game scope remains open.
|
|
|
|
|
|
|
|
|
|
|
|
| 1326 |
|
| 1327 |
|
| 1328 |
Research progress: RE-775 [report](research/QVM_RECIPIENT_WIRE.md); full native/platform/game scope remains open.
|
| 1329 |
+
|
| 1330 |
+
|
| 1331 |
+
Research progress: RE-776 [report](research/RUST_RANKING.md); full native/platform/game scope remains open.
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -1533,3 +1533,8 @@ Exactly ten task commits follow baseline `27a7f96f`. Every completed task passed
|
|
| 1533 |
## 2026-10-21 — RE-775 Original recipient command templates and bounded byte serialization model
|
| 1534 |
|
| 1535 |
Original scoress/scoresd templates carry12 numeric fields plus a name per participant;69338 requests31-byte strncpy then forces the32nd byte to zero. Focused `cargo xtask test-group recipient-wire` passes (3 Python tests); selected reports regenerate twice byte-identically, with storage, links and whitespace guards. New report: 120583 bytes. Full-suite validation is deferred to the tenth batch commit. No original assets, Rust runtime or shared VM evaluator changed.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1533 |
## 2026-10-21 — RE-775 Original recipient command templates and bounded byte serialization model
|
| 1534 |
|
| 1535 |
Original scoress/scoresd templates carry12 numeric fields plus a name per participant;69338 requests31-byte strncpy then forces the32nd byte to zero. Focused `cargo xtask test-group recipient-wire` passes (3 Python tests); selected reports regenerate twice byte-identically, with storage, links and whitespace guards. New report: 120583 bytes. Full-suite validation is deferred to the tenth batch commit. No original assets, Rust runtime or shared VM evaluator changed.
|
| 1536 |
+
|
| 1537 |
+
|
| 1538 |
+
## 2026-10-21 — RE-776 Rust raw comparator and eligible rank publication primitives
|
| 1539 |
+
|
| 1540 |
+
Rust raw comparator and rank words match232 comparator and48 eligible complete-ranking source fixtures without imposing Ord on non-ordering prefilters. Focused `cargo xtask test-group gameplay` passes (34 Python tests); selected reports regenerate twice byte-identically, with storage, links and whitespace guards. New report: 97961 bytes. Full-suite validation is deferred to the tenth batch commit. Rust gameplay crate changed; focused gameplay regenerates eleven parity contracts and scoped package tests. Full suite remains deferred until task ten.
|
docs/REVERSE_ENGINEERING_TODO.md
CHANGED
|
@@ -1372,3 +1372,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 1372 |
|
| 1373 |
|
| 1374 |
- [x] RE-775 Original recipient command templates and bounded byte serialization model ([report](research/QVM_RECIPIENT_WIRE.md)).
|
|
|
|
|
|
|
|
|
|
|
|
| 1372 |
|
| 1373 |
|
| 1374 |
- [x] RE-775 Original recipient command templates and bounded byte serialization model ([report](research/QVM_RECIPIENT_WIRE.md)).
|
| 1375 |
+
|
| 1376 |
+
|
| 1377 |
+
- [x] RE-776 Rust raw comparator and eligible rank publication primitives ([report](research/RUST_RANKING.md)).
|
docs/research/README.md
CHANGED
|
@@ -918,3 +918,6 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 918 |
|
| 919 |
|
| 920 |
[RE-775: Original recipient command templates and bounded byte serialization model](QVM_RECIPIENT_WIRE.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 918 |
|
| 919 |
|
| 920 |
[RE-775: Original recipient command templates and bounded byte serialization model](QVM_RECIPIENT_WIRE.md).
|
| 921 |
+
|
| 922 |
+
|
| 923 |
+
[RE-776: Rust raw comparator and eligible rank publication primitives](RUST_RANKING.md).
|
docs/research/RUST_RANKING.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Rust raw comparator and eligible rank publication primitives
|
| 2 |
+
|
| 3 |
+
RE-776. Reproduce with `python3 scripts/analyze_rust_ranking.py`; validate with `cargo xtask test-group gameplay`. See the [machine report](rust-ranking.json).
|
| 4 |
+
|
| 5 |
+
Added typed ranking clients, raw source comparator and caller-ordered rank publication with permutation/participant validation. Four Rust unit tests cover prefilter quirks, signed and special-mode priorities, ties/team words and invalid domains. Actual aligned small sorting remains in source evidence; Rust sorting, publication callbacks, larger client domains and native/platform parity remain open.
|
| 6 |
+
|
| 7 |
+
No original executable/full VM/native world collision or rendering runs. Supplied callback/native responses and fixture domains remain explicit; complete macOS/Linux Rust/Bevy/Solari gameplay remains open.
|
docs/research/rust-health-impulse.json
CHANGED
|
@@ -2985,7 +2985,7 @@
|
|
| 2985 |
"exit_code": 2
|
| 2986 |
}
|
| 2987 |
],
|
| 2988 |
-
"rust_unit_tests":
|
| 2989 |
"source_report": "docs/research/qvm-health-impulse.json",
|
| 2990 |
"source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
|
| 2991 |
}
|
|
|
|
| 2985 |
"exit_code": 2
|
| 2986 |
}
|
| 2987 |
],
|
| 2988 |
+
"rust_unit_tests": 46,
|
| 2989 |
"source_report": "docs/research/qvm-health-impulse.json",
|
| 2990 |
"source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
|
| 2991 |
}
|
docs/research/rust-ranking.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_rust_ranking.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Rust raw comparator and eligible rank words against source fixtures."""
|
| 3 |
+
import hashlib,json
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from qvm_rank_compare_support import FIELDS,DEFAULT
|
| 6 |
+
from rust_lifecycle_support import binary,result
|
| 7 |
+
from analyze_qvm import write_json
|
| 8 |
+
|
| 9 |
+
def words(c):return [c.get(str(o),DEFAULT[o]) for o in FIELDS]
|
| 10 |
+
|
| 11 |
+
def main():
|
| 12 |
+
b=binary();path=Path('docs/research/qvm-rank-comparator.json');raw=path.read_bytes();rank=Path('docs/research/qvm-ranking-complete.json');data=rank.read_bytes();rows=[];ranks=[]
|
| 13 |
+
for p in json.loads(raw)['probes']:
|
| 14 |
+
a=p['input'];rows.append({'input':a,'source':p['result'],'rust':result(b,'rank-compare',[a['mode'],*words(a['first']),*words(a['second'])])})
|
| 15 |
+
for p in json.loads(data)['probes']:
|
| 16 |
+
a=p['input'];args=[a['mode'],*a['team_scores'],len(a['clients']),*p['order'],*[v for c in a['clients'] for v in words(c)]];ranks.append({'input':a,'order':p['order'],'source':p['ranks'],'rust':result(b,'rank-words',args)})
|
| 17 |
+
write_json(Path('docs/research/rust-ranking.json'),{'source_reports':{str(path):hashlib.sha256(raw).hexdigest(),str(rank):hashlib.sha256(data).hexdigest()},'probes':rows,'rank_probes':ranks,'limits':'Rust raw comparator matches232 source cases; rank words match48 eligible n<7 source compositions. No Rust Ord or native sort is claimed. Caller owns order; invalid permutation/unsupported participant rejects. Ranking callbacks, large sort and native/platform/runtime parity remain open.'})
|
| 18 |
+
if __name__=='__main__':main()
|
scripts/test_rust_ranking.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import hashlib,json,unittest
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from test_qvm_rank_comparator import compare
|
| 4 |
+
class RustRankingTests(unittest.TestCase):
|
| 5 |
+
@classmethod
|
| 6 |
+
def setUpClass(cls):cls.r=json.loads(Path('docs/research/rust-ranking.json').read_text())
|
| 7 |
+
def test_source_raw_compare_and_independent_oracle(self):
|
| 8 |
+
self.assertEqual(len(self.r['probes']),232)
|
| 9 |
+
for p in self.r['probes']:a=p['input'];self.assertEqual(p['rust']['result'],p['source']);self.assertEqual(p['source'],compare(a['mode'],a['first'],a['second']))
|
| 10 |
+
def test_source_rank_words_and_tie_flags(self):
|
| 11 |
+
self.assertEqual(len(self.r['rank_probes']),48)
|
| 12 |
+
for p in self.r['rank_probes']:self.assertEqual(p['rust']['ranks'],p['source'])
|
| 13 |
+
def test_source_identity(self):
|
| 14 |
+
for path,sha in self.r['source_reports'].items():self.assertEqual(hashlib.sha256(Path(path).read_bytes()).hexdigest(),sha)
|
| 15 |
+
if __name__=='__main__':unittest.main()
|
xtask/checks.tsv
CHANGED
|
@@ -1344,3 +1344,13 @@ group leaderboard-categories scripts/analyze_qvm_leaderboard_categories.py
|
|
| 1344 |
report scripts/analyze_qvm_recipient_wire.py docs/research/qvm-recipient-wire.json
|
| 1345 |
edge docs/research/qvm-recipient-wire.json scripts/test_qvm_recipient_wire.py
|
| 1346 |
group recipient-wire scripts/analyze_qvm_recipient_wire.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1344 |
report scripts/analyze_qvm_recipient_wire.py docs/research/qvm-recipient-wire.json
|
| 1345 |
edge docs/research/qvm-recipient-wire.json scripts/test_qvm_recipient_wire.py
|
| 1346 |
group recipient-wire scripts/analyze_qvm_recipient_wire.py
|
| 1347 |
+
edge Cargo.toml scripts/analyze_rust_ranking.py
|
| 1348 |
+
edge Cargo.lock scripts/analyze_rust_ranking.py
|
| 1349 |
+
edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_ranking.py
|
| 1350 |
+
edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_ranking.py
|
| 1351 |
+
edge crates/urt-gameplay/src/ranking.rs scripts/analyze_rust_ranking.py
|
| 1352 |
+
edge crates/urt-gameplay/src/bin/urt-lifecycle.rs scripts/analyze_rust_ranking.py
|
| 1353 |
+
edge crates/urt-gameplay/src/ranking.rs scripts/analyze_rust_health_impulse.py
|
| 1354 |
+
group gameplay scripts/analyze_rust_ranking.py
|
| 1355 |
+
report scripts/analyze_rust_ranking.py docs/research/rust-ranking.json
|
| 1356 |
+
edge docs/research/rust-ranking.json scripts/test_rust_ranking.py
|