feat: integrate room framework and isolated subgame bundles

Complete room UI and protocol integration, move game definitions and resources behind bundle entries, publish authoritative version XML, and document single-game builds. Include all current resource changes and experiment artifacts.
This commit is contained in:
2026-09-09 03:13:18 +08:00
parent a7ec8aa6a7
commit 35216f75e7
1028 changed files with 835556 additions and 21415 deletions
@@ -21,10 +21,26 @@ test('all 32 UI combinations match actual server config, round-trip and fees',()
});
test('required groups have defaults and malformed saved configuration is rejected',()=>{
const r=new ErqiwangRoomRules();r.open(null);assert.equal(r.build(),'00000000000');
for(const invalid of ['00000','00000000001','2'.repeat(11),[],{},123])assert.throws(()=>r.open(invalid));
for(const invalid of ['', '0000','2'.repeat(11),[],{},123])assert.throws(()=>r.open(invalid));
assert.throws(()=>r.select(5,0));assert.throws(()=>r.select(0,2));
});
test('legacy and extended room configuration reads match server while new requests stay 11 bits',()=>{
for(const code of ['11011','110110','11011000001','11011future-extension']){
const r=new ErqiwangRoomRules();r.open(code);
const cfg=server.parse(code);
assert.deepEqual(r.selection.map(Boolean),[cfg.aset12,cfg.deductAA,cfg.nocheck,cfg.bangwang,cfg.climb]);
assert.equal(r.seatCount(code),3);
assert.equal(r.build(),'11011000000');
}
});
test('invalid room configuration reports actual wire type and value',()=>{
const r=new ErqiwangRoomRules();
assert.throws(()=>r.seatCount([0,1,0]),/received array: \[0,1,0\]/);
assert.throws(()=>r.seatCount('001'),/received string: "001"/);
});
test('platform preserves erqiwang string through canonical state and entry responses',()=>{
const code='10100000000';
assert.equal(cloneAndFreezeRoomtype(code as never),code);