nmarshall commited on
Commit
eba06e4
·
1 Parent(s): 7bc6d15

gameplay: port bot breeding callback policy

Browse files
crates/urt-gameplay/src/bin/urt-native-policy.rs CHANGED
@@ -46,6 +46,30 @@ fn run() -> Result<String, String> {
46
  .unwrap_or_default();
47
  Ok(format!("{{\"calls\":{}}}", nested(&calls)))
48
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  _ => Err("unknown command".into()),
50
  }
51
  }
@@ -58,3 +82,18 @@ fn main() {
58
  }
59
  }
60
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  .unwrap_or_default();
47
  Ok(format!("{{\"calls\":{}}}", nested(&calls)))
48
  }
49
+ "breed" => {
50
+ if w.len() != 261 {
51
+ return Err("invalid breed input".into());
52
+ }
53
+ let mut b = bots(&w[5..])?;
54
+ let selection = if w[0] != 0 {
55
+ Some([w[1] as usize, w[2] as usize, w[3] as usize])
56
+ } else {
57
+ None
58
+ };
59
+ let activity = if w[4] != 0 { vec![(63, 1)] } else { vec![] };
60
+ let out = breed_bots(&mut b, selection, &activity).map_err(|e| format!("{e:?}"))?;
61
+ let calls = breed_calls(&out.effects);
62
+ let end = b
63
+ .iter()
64
+ .map(|b| vec![b.active as u32, b.wins as u32, b.losses as u32, b.handle])
65
+ .collect::<Vec<_>>();
66
+ Ok(format!(
67
+ "{{\"scores\":{},\"calls\":{},\"after\":{}}}",
68
+ list(&out.scores),
69
+ calls,
70
+ nested(&end)
71
+ ))
72
+ }
73
  _ => Err("unknown command".into()),
74
  }
75
  }
 
82
  }
83
  }
84
  }
85
+
86
+ fn breed_calls(effects: &[BreedEffect]) -> String {
87
+ format!(
88
+ "[{}]",
89
+ effects
90
+ .iter()
91
+ .map(|e| match e {
92
+ BreedEffect::Select => "[-565,64]".into(),
93
+ BreedEffect::Interbreed(h) => format!("[-566,{},{},{}]", h[0], h[1], h[2]),
94
+ BreedEffect::Mutate(h) => format!("[-567,{h},1065353216]"),
95
+ })
96
+ .collect::<Vec<String>>()
97
+ .join(",")
98
+ )
99
+ }
crates/urt-gameplay/src/native_policy.rs CHANGED
@@ -60,3 +60,81 @@ mod tests {
60
  assert_eq!(best_bot(&[]), Err(Error::InvalidCount));
61
  }
62
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  assert_eq!(best_bot(&[]), Err(Error::InvalidCount));
61
  }
62
  }
63
+
64
+ #[derive(Clone, Debug, PartialEq, Eq)]
65
+ pub enum BreedEffect {
66
+ Select,
67
+ Interbreed([u32; 3]),
68
+ Mutate(u32),
69
+ }
70
+ #[derive(Clone, Debug, PartialEq, Eq)]
71
+ pub struct Breed {
72
+ pub scores: Vec<u32>,
73
+ pub effects: Vec<BreedEffect>,
74
+ }
75
+ /// Selection is an explicit native answer. Reset observes supplied callback
76
+ /// activity changes, while the selection fitness snapshot precedes them.
77
+ pub fn breed_bots(
78
+ bots: &mut [Bot],
79
+ selection: Option<[usize; 3]>,
80
+ activity: &[(usize, i32)],
81
+ ) -> Result<Breed, Error> {
82
+ if bots.len() != 64 {
83
+ return Err(Error::InvalidCount);
84
+ }
85
+ if selection.is_some_and(|s| s.iter().any(|j| *j >= 64))
86
+ || activity.iter().any(|(j, _)| *j >= 64)
87
+ {
88
+ return Err(Error::InvalidIndex);
89
+ }
90
+ let scores = bots.iter().map(|b| fitness(*b).to_bits()).collect();
91
+ let mut effects = vec![BreedEffect::Select];
92
+ for (j, value) in activity {
93
+ bots[*j].active = *value;
94
+ }
95
+ if let Some(s) = selection {
96
+ let h = s.map(|j| bots[j].handle);
97
+ effects.push(BreedEffect::Interbreed(h));
98
+ effects.push(BreedEffect::Mutate(h[2]));
99
+ }
100
+ for b in bots {
101
+ if b.active != 0 {
102
+ b.wins = 0;
103
+ b.losses = 0;
104
+ }
105
+ }
106
+ Ok(Breed { scores, effects })
107
+ }
108
+ #[cfg(test)]
109
+ mod breed_tests {
110
+ use super::*;
111
+ #[test]
112
+ fn reset_after_supplied_activity() {
113
+ let mut b = [Bot::default(); 64];
114
+ b[63].wins = 99;
115
+ let out = breed_bots(&mut b, None, &[(63, 1)]).unwrap();
116
+ assert_eq!(out.scores[63], (-1f32).to_bits());
117
+ assert_eq!(b[63].wins, 0);
118
+ assert_eq!(out.effects, [BreedEffect::Select]);
119
+ }
120
+ #[test]
121
+ fn invalid_selection_is_atomic() {
122
+ let mut b = [Bot {
123
+ active: 1,
124
+ wins: 2,
125
+ losses: 3,
126
+ handle: 4,
127
+ }; 64];
128
+ let before = b;
129
+ assert_eq!(
130
+ breed_bots(&mut b, Some([0, 1, 64]), &[(0, 0)]),
131
+ Err(Error::InvalidIndex)
132
+ );
133
+ assert_eq!(b, before);
134
+ assert_eq!(
135
+ breed_bots(&mut b, None, &[(64, 0)]),
136
+ Err(Error::InvalidIndex)
137
+ );
138
+ assert_eq!(b, before);
139
+ }
140
+ }
docs/FINDINGS.md CHANGED
@@ -1628,3 +1628,6 @@ RE-813–822 replaces the scoreboard decimal assumption with actual37914 executi
1628
 
1629
 
1630
  **RE-876:** 64 actual121987 cases recover all64 native fitness words, supplied native -565 parent/child selection, ordered -566 interbreed and -567 mutation(handle,1.0), then zeroing both counters of currently active slots. A supplied selection callback activates slot63 to prove reset rereads live state after native calls. Selection algorithm and native genetic/file behavior remain open; invalid native indices are excluded. [Evidence](research/QVM_BOT_BREED.md).
 
 
 
 
1628
 
1629
 
1630
  **RE-876:** 64 actual121987 cases recover all64 native fitness words, supplied native -565 parent/child selection, ordered -566 interbreed and -567 mutation(handle,1.0), then zeroing both counters of currently active slots. A supplied selection callback activates slot63 to prove reset rereads live state after native calls. Selection algorithm and native genetic/file behavior remain open; invalid native indices are excluded. [Evidence](research/QVM_BOT_BREED.md).
1631
+
1632
+
1633
+ **RE-877:** Rust matches all64 actual121987 fitness/selection/interbreed/mutation/reset transcripts, including callback activation before the counter reset. API validates supplied indices before mutation and retains inactive counters. Native bot selection and genetic mutation are explicit answer/effect boundaries, not Rust implementations of botlib. [Evidence](research/RUST_BOT_BREED.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -1656,3 +1656,6 @@ RE-875: Rust matches all64 original bot winner cases, with wrapping fitness arit
1656
 
1657
 
1658
  RE-876: 64 actual121987 cases recover all64 native fitness words, supplied native -565 parent/child selection, ordered -566 interbreed and -567 mutation(handle,1.0), then zeroing both counters of currently active slots. A supplied selection callback activates slot63 to prove reset rereads live state after native calls. Selection algorithm and native genetic/file behavior remain open; invalid native indices are excluded. Full runtime completion remains open. [Evidence](research/QVM_BOT_BREED.md).
 
 
 
 
1656
 
1657
 
1658
  RE-876: 64 actual121987 cases recover all64 native fitness words, supplied native -565 parent/child selection, ordered -566 interbreed and -567 mutation(handle,1.0), then zeroing both counters of currently active slots. A supplied selection callback activates slot63 to prove reset rereads live state after native calls. Selection algorithm and native genetic/file behavior remain open; invalid native indices are excluded. Full runtime completion remains open. [Evidence](research/QVM_BOT_BREED.md).
1659
+
1660
+
1661
+ RE-877: Rust matches all64 actual121987 fitness/selection/interbreed/mutation/reset transcripts, including callback activation before the counter reset. API validates supplied indices before mutation and retains inactive counters. Native bot selection and genetic mutation are explicit answer/effect boundaries, not Rust implementations of botlib. Full runtime completion remains open. [Evidence](research/RUST_BOT_BREED.md).
docs/RESEARCH_LEDGER.md CHANGED
@@ -2085,3 +2085,8 @@ Rust matches all64 original bot winner cases, with wrapping fitness arithmetic b
2085
  ## 2026-10-28 — RE-876 Original bot breeding callback and counter reset
2086
 
2087
  64 actual121987 cases recover all64 native fitness words, supplied native -565 parent/child selection, ordered -566 interbreed and -567 mutation(handle,1.0), then zeroing both counters of currently active slots. A supplied selection callback activates slot63 to prove reset rereads live state after native calls. Selection algorithm and native genetic/file behavior remain open; invalid native indices are excluded. Focused `cargo xtask test-group qvm-bot-breed` passes (4 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.
 
 
 
 
 
 
2085
  ## 2026-10-28 — RE-876 Original bot breeding callback and counter reset
2086
 
2087
  64 actual121987 cases recover all64 native fitness words, supplied native -565 parent/child selection, ordered -566 interbreed and -567 mutation(handle,1.0), then zeroing both counters of currently active slots. A supplied selection callback activates slot63 to prove reset rereads live state after native calls. Selection algorithm and native genetic/file behavior remain open; invalid native indices are excluded. Focused `cargo xtask test-group qvm-bot-breed` passes (4 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.
2088
+
2089
+
2090
+ ## 2026-10-28 — RE-877 Rust bot breeding and post-callback reset
2091
+
2092
+ Rust matches all64 actual121987 fitness/selection/interbreed/mutation/reset transcripts, including callback activation before the counter reset. API validates supplied indices before mutation and retains inactive counters. Native bot selection and genetic mutation are explicit answer/effect boundaries, not Rust implementations of botlib. Focused `cargo xtask test-group rust-bot-breed` 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
@@ -1693,3 +1693,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
1693
 
1694
 
1695
  - [x] RE-876 Original bot breeding callback and counter reset ([evidence](research/QVM_BOT_BREED.md)).
 
 
 
 
1693
 
1694
 
1695
  - [x] RE-876 Original bot breeding callback and counter reset ([evidence](research/QVM_BOT_BREED.md)).
1696
+
1697
+
1698
+ - [x] RE-877 Rust bot breeding and post-callback reset ([evidence](research/RUST_BOT_BREED.md)).
docs/research/README.md CHANGED
@@ -1228,3 +1228,6 @@ The extension collector adds five focused tests for native tail calls, command-r
1228
 
1229
 
1230
  [RE-876: Original bot breeding callback and counter reset](QVM_BOT_BREED.md).
 
 
 
 
1228
 
1229
 
1230
  [RE-876: Original bot breeding callback and counter reset](QVM_BOT_BREED.md).
1231
+
1232
+
1233
+ [RE-877: Rust bot breeding and post-callback reset](RUST_BOT_BREED.md).
docs/research/RUST_BOT_BREED.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Rust bot breeding and post-callback reset
2
+
3
+ RE-877. Reproduce with `python3 scripts/analyze_rust_bot_breed.py`; focused validation: `cargo xtask test-group rust-bot-breed`. [Machine report](rust-bot-breed.json).
4
+
5
+ Rust matches all64 actual121987 fitness/selection/interbreed/mutation/reset transcripts, including callback activation before the counter reset. API validates supplied indices before mutation and retains inactive counters. Native bot selection and genetic mutation are explicit answer/effect boundaries, not Rust implementations of botlib.
6
+
7
+ Original executable/native runtime and complete Linux/macOS Rust/Bevy/Solari acceptance remain unverified.
docs/research/rust-bot-breed.json ADDED
The diff for this file is too large to render. See raw diff
 
docs/research/rust-health-impulse.json CHANGED
@@ -2985,7 +2985,7 @@
2985
  "exit_code": 2
2986
  }
2987
  ],
2988
- "rust_unit_tests": 131,
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": 133,
2989
  "source_report": "docs/research/qvm-health-impulse.json",
2990
  "source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
2991
  }
scripts/analyze_rust_bot_breed.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import hashlib,json
3
+ from pathlib import Path
4
+ from rust_native_policy_support import binary,result,invoke
5
+ from analyze_qvm import write_json
6
+ def encode(s):
7
+ a=s['input'];return [a['response'],*a['selection'],int(a['activate']),*[v for row in a['records'] for v in row]]
8
+ def project(s):return {k:s[k] for k in ['scores','calls','after']}
9
+
10
+ def main():
11
+ b=binary();p=Path('docs/research/qvm-bot-breed.json');data=p.read_bytes();rows=[]
12
+ for s in json.loads(data)['probes']:rows.append({'source':project(s),'rust':result(b,'breed',encode(s))})
13
+ write_json(Path('docs/research/rust-bot-breed.json'),{'source_report':str(p),'source_report_sha256':hashlib.sha256(data).hexdigest(),'probes':rows,'limits':'Rust matches all64 actual121987 fitness/selection/interbreed/mutation/reset transcripts, including callback activation before the counter reset. API validates supplied indices before mutation and retains inactive counters. Native bot selection and genetic mutation are explicit answer/effect boundaries, not Rust implementations of botlib.'})
14
+ if __name__=='__main__':main()
scripts/test_rust_bot_breed.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import hashlib,json,unittest
2
+ from pathlib import Path
3
+ class PortTests(unittest.TestCase):
4
+ def test_all_original_cases(self):
5
+ r=json.loads(Path('docs/research/rust-bot-breed.json').read_text());src=json.loads(Path(r['source_report']).read_text());self.assertEqual(len(r['probes']),len(src['probes']));self.assertGreater(len(r['probes']),20)
6
+ for p in r['probes']:self.assertEqual(p['rust'],p['source'])
7
+ def test_source_identity(self):
8
+ r=json.loads(Path('docs/research/rust-bot-breed.json').read_text());self.assertEqual(hashlib.sha256(Path(r['source_report']).read_bytes()).hexdigest(),r['source_report_sha256'])
9
+ if __name__=='__main__':unittest.main()
xtask/checks.tsv CHANGED
@@ -2539,3 +2539,16 @@ report scripts/analyze_qvm_bot_breed.py docs/research/qvm-bot-breed.json
2539
  edge docs/research/qvm-bot-breed.json scripts/test_qvm_bot_breed.py
2540
  group qvm-bot-breed scripts/analyze_qvm_bot_breed.py
2541
  edge scripts/analyze_qvm_bot_breed.py scripts/analyze_qvm_resumer_audit.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2539
  edge docs/research/qvm-bot-breed.json scripts/test_qvm_bot_breed.py
2540
  group qvm-bot-breed scripts/analyze_qvm_bot_breed.py
2541
  edge scripts/analyze_qvm_bot_breed.py scripts/analyze_qvm_resumer_audit.py
2542
+ edge Cargo.toml scripts/analyze_rust_bot_breed.py
2543
+ edge Cargo.lock scripts/analyze_rust_bot_breed.py
2544
+ edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_bot_breed.py
2545
+ edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_bot_breed.py
2546
+ edge crates/urt-gameplay/src/native_policy.rs scripts/analyze_rust_bot_breed.py
2547
+ edge crates/urt-gameplay/src/bin/urt-native-policy.rs scripts/analyze_rust_bot_breed.py
2548
+ edge docs/research/qvm-bot-breed.json scripts/analyze_rust_bot_breed.py
2549
+ group rust-bot-breed scripts/analyze_rust_health_impulse.py
2550
+ group gameplay scripts/analyze_rust_bot_breed.py
2551
+ report scripts/analyze_rust_bot_breed.py docs/research/rust-bot-breed.json
2552
+ edge docs/research/rust-bot-breed.json scripts/test_rust_bot_breed.py
2553
+ group rust-bot-breed scripts/analyze_rust_bot_breed.py
2554
+ edge scripts/analyze_rust_bot_breed.py scripts/analyze_qvm_resumer_audit.py