gameplay: compose eligible ranking
Browse files- crates/urt-gameplay/src/bin/urt-lifecycle.rs +24 -1
- crates/urt-gameplay/src/ranking.rs +31 -1
- 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_COMPLETE.md +7 -0
- docs/research/rust-health-impulse.json +1 -1
- docs/research/rust-ranking-complete.json +0 -0
- scripts/analyze_rust_ranking_complete.py +16 -0
- scripts/test_rust_ranking_complete.py +12 -0
- xtask/checks.tsv +13 -0
crates/urt-gameplay/src/bin/urt-lifecycle.rs
CHANGED
|
@@ -328,6 +328,29 @@ fn run() -> Result<(), String> {
|
|
| 328 |
second.cached_ping as u32
|
| 329 |
);
|
| 330 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
"rank-sort" if w.len() >= 2 && w[1] <= 64 => {
|
| 332 |
let n = w[1] as usize;
|
| 333 |
if w.len() != 2 + 11 * n {
|
|
@@ -363,7 +386,7 @@ fn run() -> Result<(), String> {
|
|
| 363 |
urt_gameplay::ranking::compare(w[0] as i32, client(&w[1..11]), client(&w[11..]))
|
| 364 |
);
|
| 365 |
}
|
| 366 |
-
"rank-words" if w.len() >= 4 && w[3] <=
|
| 367 |
let n = w[3] as usize;
|
| 368 |
if w.len() != 4 + 11 * n {
|
| 369 |
return Err("invalid ranking words".into());
|
|
|
|
| 328 |
second.cached_ping as u32
|
| 329 |
);
|
| 330 |
}
|
| 331 |
+
"rank-compose" if w.len() >= 4 && w[3] <= 64 => {
|
| 332 |
+
let n = w[3] as usize;
|
| 333 |
+
if w.len() != 4 + 10 * n {
|
| 334 |
+
return Err("invalid composed ranking words".into());
|
| 335 |
+
}
|
| 336 |
+
let clients: Vec<_> = w[4..].chunks_exact(10).map(ranking_client).collect();
|
| 337 |
+
let (order, ranks) =
|
| 338 |
+
urt_gameplay::ranking::ranked(w[0] as i32, &clients, [w[1] as i32, w[2] as i32])
|
| 339 |
+
.map_err(|e| format!("unsupported input: {e:?}"))?;
|
| 340 |
+
println!(
|
| 341 |
+
"{{\"order\":[{}],\"ranks\":[{}]}}",
|
| 342 |
+
order
|
| 343 |
+
.iter()
|
| 344 |
+
.map(|v| v.to_string())
|
| 345 |
+
.collect::<Vec<_>>()
|
| 346 |
+
.join(","),
|
| 347 |
+
ranks
|
| 348 |
+
.iter()
|
| 349 |
+
.map(|v| v.to_string())
|
| 350 |
+
.collect::<Vec<_>>()
|
| 351 |
+
.join(",")
|
| 352 |
+
);
|
| 353 |
+
}
|
| 354 |
"rank-sort" if w.len() >= 2 && w[1] <= 64 => {
|
| 355 |
let n = w[1] as usize;
|
| 356 |
if w.len() != 2 + 11 * n {
|
|
|
|
| 386 |
urt_gameplay::ranking::compare(w[0] as i32, client(&w[1..11]), client(&w[11..]))
|
| 387 |
);
|
| 388 |
}
|
| 389 |
+
"rank-words" if w.len() >= 4 && w[3] <= 64 => {
|
| 390 |
let n = w[3] as usize;
|
| 391 |
if w.len() != 4 + 11 * n {
|
| 392 |
return Err("invalid ranking words".into());
|
crates/urt-gameplay/src/ranking.rs
CHANGED
|
@@ -74,7 +74,7 @@ pub enum RankError {
|
|
| 74 |
InvalidOrder,
|
| 75 |
}
|
| 76 |
/// Caller supplies a complete ordering. This primitive does not execute a sort
|
| 77 |
-
/// or publication callbacks. Source parity fixtures
|
| 78 |
pub fn rank_words(
|
| 79 |
mode: i32,
|
| 80 |
clients: &[Client],
|
|
@@ -132,6 +132,18 @@ pub fn rank_words(
|
|
| 132 |
}
|
| 133 |
Ok(result)
|
| 134 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
#[cfg(test)]
|
| 136 |
mod tests {
|
| 137 |
use super::*;
|
|
@@ -150,6 +162,24 @@ mod tests {
|
|
| 150 |
}
|
| 151 |
}
|
| 152 |
#[test]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
fn raw_prefilter_quirks_remain_nonordering() {
|
| 154 |
let c = Client {
|
| 155 |
spectator_state: 3,
|
|
|
|
| 74 |
InvalidOrder,
|
| 75 |
}
|
| 76 |
/// Caller supplies a complete ordering. This primitive does not execute a sort
|
| 77 |
+
/// or publication callbacks. Source parity fixtures include eligible populations through64 clients.
|
| 78 |
pub fn rank_words(
|
| 79 |
mode: i32,
|
| 80 |
clients: &[Client],
|
|
|
|
| 132 |
}
|
| 133 |
Ok(result)
|
| 134 |
}
|
| 135 |
+
/// Complete eligible-client sorting and rank-word composition. Publication
|
| 136 |
+
/// callbacks and runtime/global ownership remain the caller's responsibility.
|
| 137 |
+
pub fn ranked(
|
| 138 |
+
mode: i32,
|
| 139 |
+
clients: &[Client],
|
| 140 |
+
scores: [i32; 2],
|
| 141 |
+
) -> Result<(Vec<usize>, Vec<u32>), RankError> {
|
| 142 |
+
let mut order: Vec<_> = (0..clients.len()).collect();
|
| 143 |
+
crate::rank_sort::sort(mode, clients, &mut order)?;
|
| 144 |
+
let ranks = rank_words(mode, clients, &order, scores)?;
|
| 145 |
+
Ok((order, ranks))
|
| 146 |
+
}
|
| 147 |
#[cfg(test)]
|
| 148 |
mod tests {
|
| 149 |
use super::*;
|
|
|
|
| 162 |
}
|
| 163 |
}
|
| 164 |
#[test]
|
| 165 |
+
fn composed_rank_sort_handles64_and_global_team_result() {
|
| 166 |
+
let c: Vec<_> = (0..64)
|
| 167 |
+
.map(|j| Client {
|
| 168 |
+
score: j % 2,
|
| 169 |
+
..client()
|
| 170 |
+
})
|
| 171 |
+
.collect();
|
| 172 |
+
let (order, ranks) = ranked(0, &c, [0, 0]).unwrap();
|
| 173 |
+
assert!(order[..32].iter().all(|j| j % 2 == 1));
|
| 174 |
+
assert!(
|
| 175 |
+
ranks
|
| 176 |
+
.iter()
|
| 177 |
+
.enumerate()
|
| 178 |
+
.all(|(j, &v)| v == if j % 2 == 1 { 16384 } else { 16384 | 32 })
|
| 179 |
+
);
|
| 180 |
+
assert_eq!(ranked(3, &c, [1, 2]).unwrap().1, vec![1; 64]);
|
| 181 |
+
}
|
| 182 |
+
#[test]
|
| 183 |
fn raw_prefilter_quirks_remain_nonordering() {
|
| 184 |
let c = Client {
|
| 185 |
spectator_state: 3,
|
docs/FINDINGS.md
CHANGED
|
@@ -1382,3 +1382,6 @@ RE-780–789 exposes and bounds legacy resumer timing/byte-state flaws, migrates
|
|
| 1382 |
|
| 1383 |
|
| 1384 |
**RE-794:** Rust now executes the recovered insertion/partition/median/ninther sort without assuming comparator Ord; source parity includes unstable tie permutations. [Evidence](research/RUST_RANK_SORT.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 1382 |
|
| 1383 |
|
| 1384 |
**RE-794:** Rust now executes the recovered insertion/partition/median/ninther sort without assuming comparator Ord; source parity includes unstable tie permutations. [Evidence](research/RUST_RANK_SORT.md).
|
| 1385 |
+
|
| 1386 |
+
|
| 1387 |
+
**RE-795:** Rust composes recovered source sorting and rank writes, matching exact order and tied/team rank words for128 full caller fixtures. [Evidence](research/RUST_RANKING_COMPLETE.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -1386,3 +1386,6 @@ RE-793: Ranking count globals follow connection/team/bot gates, but deferred rec
|
|
| 1386 |
|
| 1387 |
|
| 1388 |
RE-794: Rust now executes the recovered insertion/partition/median/ninther sort without assuming comparator Ord; source parity includes unstable tie permutations. Full runtime completion remains open. [Evidence](research/RUST_RANK_SORT.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 1386 |
|
| 1387 |
|
| 1388 |
RE-794: Rust now executes the recovered insertion/partition/median/ninther sort without assuming comparator Ord; source parity includes unstable tie permutations. Full runtime completion remains open. [Evidence](research/RUST_RANK_SORT.md).
|
| 1389 |
+
|
| 1390 |
+
|
| 1391 |
+
RE-795: Rust composes recovered source sorting and rank writes, matching exact order and tied/team rank words for128 full caller fixtures. Full runtime completion remains open. [Evidence](research/RUST_RANKING_COMPLETE.md).
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -1634,3 +1634,8 @@ Ranking count globals follow connection/team/bot gates, but deferred records can
|
|
| 1634 |
## 2026-10-26 — RE-794 Rust source-shaped aligned ranking sort with exact tie order
|
| 1635 |
|
| 1636 |
Rust now executes the recovered insertion/partition/median/ninther sort without assuming comparator Ord; source parity includes unstable tie permutations. Focused `cargo xtask test-group rust-rank-sort` passes (9 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1634 |
## 2026-10-26 — RE-794 Rust source-shaped aligned ranking sort with exact tie order
|
| 1635 |
|
| 1636 |
Rust now executes the recovered insertion/partition/median/ninther sort without assuming comparator Ord; source parity includes unstable tie permutations. Focused `cargo xtask test-group rust-rank-sort` passes (9 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
|
| 1637 |
+
|
| 1638 |
+
|
| 1639 |
+
## 2026-10-26 — RE-795 Rust eligible ranking composition through64 clients
|
| 1640 |
+
|
| 1641 |
+
Rust composes recovered source sorting and rank writes, matching exact order and tied/team rank words for128 full caller fixtures. Focused `cargo xtask test-group rust-ranking-complete` passes (9 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
|
docs/REVERSE_ENGINEERING_TODO.md
CHANGED
|
@@ -1432,3 +1432,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 1432 |
|
| 1433 |
|
| 1434 |
- [x] RE-794 Rust source-shaped aligned ranking sort with exact tie order ([evidence](research/RUST_RANK_SORT.md)).
|
|
|
|
|
|
|
|
|
|
|
|
| 1432 |
|
| 1433 |
|
| 1434 |
- [x] RE-794 Rust source-shaped aligned ranking sort with exact tie order ([evidence](research/RUST_RANK_SORT.md)).
|
| 1435 |
+
|
| 1436 |
+
|
| 1437 |
+
- [x] RE-795 Rust eligible ranking composition through64 clients ([evidence](research/RUST_RANKING_COMPLETE.md)).
|
docs/research/README.md
CHANGED
|
@@ -972,3 +972,6 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 972 |
|
| 973 |
|
| 974 |
[RE-794: Rust source-shaped aligned ranking sort with exact tie order](RUST_RANK_SORT.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 972 |
|
| 973 |
|
| 974 |
[RE-794: Rust source-shaped aligned ranking sort with exact tie order](RUST_RANK_SORT.md).
|
| 975 |
+
|
| 976 |
+
|
| 977 |
+
[RE-795: Rust eligible ranking composition through64 clients](RUST_RANKING_COMPLETE.md).
|
docs/research/RUST_RANKING_COMPLETE.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Rust eligible ranking composition through64 clients
|
| 2 |
+
|
| 3 |
+
RE-795. Reproduce with `python3 scripts/analyze_rust_ranking_complete.py`; focused validation: `cargo xtask test-group rust-ranking-complete`. [Machine report](rust-ranking-complete.json).
|
| 4 |
+
|
| 5 |
+
The public ranked function owns sorting rather than requiring a precomputed order. Source parity covers both earlier48 small and80 larger caller fixtures, including zero participants, partial ties, all team-score outcomes and mode-specific keys. A64-client unit test independently checks tied ranks and team results. The CLI rank-word primitive accepts the now-verified larger eligible domain. Mixed-population collection and publication/native side effects remain separate contracts.
|
| 6 |
+
|
| 7 |
+
Original executable/native runtime and complete Linux/macOS Rust/Bevy/Solari acceptance remain unverified.
|
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": 58,
|
| 2989 |
"source_report": "docs/research/qvm-health-impulse.json",
|
| 2990 |
"source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
|
| 2991 |
}
|
docs/research/rust-ranking-complete.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_rust_ranking_complete.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
import hashlib,json
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
from analyze_rust_ranking import words
|
| 5 |
+
from rust_lifecycle_support import binary,result,invoke
|
| 6 |
+
from analyze_qvm import write_json
|
| 7 |
+
|
| 8 |
+
def main():
|
| 9 |
+
b=binary();rows=[];sources={}
|
| 10 |
+
for name in ['qvm-ranking-complete','qvm-ranking-large-caller']:
|
| 11 |
+
p=Path('docs/research/'+name+'.json');data=p.read_bytes();sources[str(p)]=hashlib.sha256(data).hexdigest()
|
| 12 |
+
for row in json.loads(data)['probes']:
|
| 13 |
+
a=row['input'];c=a['clients'];rows.append({'input':a,'source_order':row['order'],'source_ranks':row['ranks'],'rust':result(b,'rank-compose',[a['mode'],*a['team_scores'],len(c),*[v for client in c for v in words(client)]])})
|
| 14 |
+
bad=invoke(b,'rank-compose',[0,0,0,1,*words({'3096':3})],check=False);assert bad.returncode
|
| 15 |
+
write_json(Path('docs/research/rust-ranking-complete.json'),{'source_reports':sources,'probes':rows,'rejections':[bad.returncode],'limits':'Rust source sort plus rank words matches128 complete5864 eligible compositions through64 clients, exact order and rank flags. Publication, collection of mixed clients and native/runtime ownership excluded.'})
|
| 16 |
+
if __name__=='__main__':main()
|
scripts/test_rust_ranking_complete.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import hashlib,json,unittest
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from test_qvm_ranking_large_caller import ranks
|
| 4 |
+
class RustRankingCompleteTests(unittest.TestCase):
|
| 5 |
+
def test_source_order_rank_and_independent_tie_oracle(self):
|
| 6 |
+
ps=json.loads(Path('docs/research/rust-ranking-complete.json').read_text())['probes'];self.assertEqual(len(ps),128)
|
| 7 |
+
for p in ps:a=p['input'];self.assertEqual(p['rust']['order'],p['source_order']);self.assertEqual(p['rust']['ranks'],p['source_ranks']);self.assertEqual(p['rust']['ranks'],ranks(a['mode'],a['clients'],p['source_order'],a['team_scores']))
|
| 8 |
+
def test_identity_and_unsupported_domain(self):
|
| 9 |
+
r=json.loads(Path('docs/research/rust-ranking-complete.json').read_text())
|
| 10 |
+
for p,h in r['source_reports'].items():self.assertEqual(hashlib.sha256(Path(p).read_bytes()).hexdigest(),h)
|
| 11 |
+
self.assertTrue(all(r['rejections']))
|
| 12 |
+
if __name__=='__main__':unittest.main()
|
xtask/checks.tsv
CHANGED
|
@@ -1807,3 +1807,16 @@ report scripts/analyze_rust_rank_sort.py docs/research/rust-rank-sort.json
|
|
| 1807 |
edge docs/research/rust-rank-sort.json scripts/test_rust_rank_sort.py
|
| 1808 |
group rust-rank-sort scripts/analyze_rust_rank_sort.py
|
| 1809 |
edge scripts/analyze_rust_rank_sort.py scripts/analyze_qvm_resumer_audit.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1807 |
edge docs/research/rust-rank-sort.json scripts/test_rust_rank_sort.py
|
| 1808 |
group rust-rank-sort scripts/analyze_rust_rank_sort.py
|
| 1809 |
edge scripts/analyze_rust_rank_sort.py scripts/analyze_qvm_resumer_audit.py
|
| 1810 |
+
edge Cargo.toml scripts/analyze_rust_ranking_complete.py
|
| 1811 |
+
edge Cargo.lock scripts/analyze_rust_ranking_complete.py
|
| 1812 |
+
edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_ranking_complete.py
|
| 1813 |
+
edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_ranking_complete.py
|
| 1814 |
+
edge crates/urt-gameplay/src/bin/urt-lifecycle.rs scripts/analyze_rust_ranking_complete.py
|
| 1815 |
+
edge crates/urt-gameplay/src/rank_sort.rs scripts/analyze_rust_ranking_complete.py
|
| 1816 |
+
edge crates/urt-gameplay/src/ranking.rs scripts/analyze_rust_ranking_complete.py
|
| 1817 |
+
group gameplay scripts/analyze_rust_ranking_complete.py
|
| 1818 |
+
group rust-ranking-complete scripts/analyze_rust_health_impulse.py
|
| 1819 |
+
report scripts/analyze_rust_ranking_complete.py docs/research/rust-ranking-complete.json
|
| 1820 |
+
edge docs/research/rust-ranking-complete.json scripts/test_rust_ranking_complete.py
|
| 1821 |
+
group rust-ranking-complete scripts/analyze_rust_ranking_complete.py
|
| 1822 |
+
edge scripts/analyze_rust_ranking_complete.py scripts/analyze_qvm_resumer_audit.py
|