feat: strategy engine tournaments for paperclips study (#5)
This commit is contained in:
117
tests/strategy_engine.test.cjs
Normal file
117
tests/strategy_engine.test.cjs
Normal file
@@ -0,0 +1,117 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const vm = require('node:vm');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
|
||||
function loadStrategy() {
|
||||
const dataSrc = fs.readFileSync(path.join(ROOT, 'js/data.js'), 'utf8');
|
||||
const utilsSrc = fs.readFileSync(path.join(ROOT, 'js/utils.js'), 'utf8');
|
||||
const renderSrc = fs.readFileSync(path.join(ROOT, 'js/render.js'), 'utf8');
|
||||
const strategySrc = fs.readFileSync(path.join(ROOT, 'js/strategy.js'), 'utf8');
|
||||
const context = {
|
||||
console,
|
||||
Math,
|
||||
Date,
|
||||
window: {},
|
||||
document: {
|
||||
getElementById() { return null; },
|
||||
body: { appendChild() {} },
|
||||
createElement() { return { style: {}, appendChild() {}, remove() {}, setAttribute() {}, innerHTML: '', textContent: '' }; },
|
||||
querySelector() { return null; },
|
||||
querySelectorAll() { return []; },
|
||||
},
|
||||
showToast() {},
|
||||
log() {},
|
||||
canAffordBuilding() { return false; },
|
||||
localStorage: {
|
||||
_raw: null,
|
||||
getItem() { return this._raw; },
|
||||
setItem(_k, v) { this._raw = v; },
|
||||
removeItem() { this._raw = null; },
|
||||
},
|
||||
EVENTS: [],
|
||||
updateRates() {},
|
||||
showOfflinePopup() {},
|
||||
Combat: { renderCombatPanel() {} },
|
||||
};
|
||||
vm.createContext(context);
|
||||
vm.runInContext(`${dataSrc}\n${utilsSrc}\n${renderSrc}\n${strategySrc}\nthis.__exports = { G, StrategyEngine, STRATEGY_LIBRARY, saveGame: typeof saveGame === 'function' ? saveGame : null, loadGame: typeof loadGame === 'function' ? loadGame : null };`, context);
|
||||
return context.__exports;
|
||||
}
|
||||
|
||||
test('strategy engine exposes eight game theory strategies', () => {
|
||||
const { STRATEGY_LIBRARY } = loadStrategy();
|
||||
assert.deepEqual(Object.keys(STRATEGY_LIBRARY).sort(), [
|
||||
'cooperate', 'defect', 'generous', 'greedy', 'grim', 'minimax', 'random', 'tit_for_tat'
|
||||
]);
|
||||
});
|
||||
|
||||
test('strategy engine unlocks strategies progressively by knowledge', () => {
|
||||
const { G, StrategyEngine } = loadStrategy();
|
||||
G.strategicFlag = 1;
|
||||
G.totalKnowledge = 16000;
|
||||
const sse = new StrategyEngine();
|
||||
assert.equal(sse.getUnlockedStrategies().length, 4);
|
||||
G.totalKnowledge = 25000;
|
||||
assert.equal(sse.getUnlockedStrategies().length, 6);
|
||||
G.totalKnowledge = 60000;
|
||||
assert.equal(sse.getUnlockedStrategies().length, 8);
|
||||
});
|
||||
|
||||
test('round-robin tournament produces a sorted leaderboard', () => {
|
||||
const { G, StrategyEngine } = loadStrategy();
|
||||
G.strategicFlag = 1;
|
||||
G.totalKnowledge = 60000;
|
||||
const sse = new StrategyEngine();
|
||||
const board = sse.runTournament(6);
|
||||
assert.ok(board.length >= 8);
|
||||
for (let i = 1; i < board.length; i++) {
|
||||
assert.ok(board[i - 1].score >= board[i].score);
|
||||
}
|
||||
});
|
||||
|
||||
test('auto-tournament mode awards Yomi as knowledge over time', () => {
|
||||
const { G, StrategyEngine } = loadStrategy();
|
||||
G.strategicFlag = 1;
|
||||
G.totalKnowledge = 60000;
|
||||
G.autoTournamentUnlocked = true;
|
||||
G.autoTournamentEnabled = true;
|
||||
G.strategyPoints = 0;
|
||||
const sse = new StrategyEngine();
|
||||
sse.tick(31);
|
||||
assert.ok(G.strategyPoints > 0);
|
||||
assert.ok(G.totalKnowledge > 60000);
|
||||
assert.ok(Array.isArray(G.strategyLeaderboard));
|
||||
assert.ok(G.strategyLeaderboard.length > 0);
|
||||
});
|
||||
|
||||
test('strategy state persists through save/load payload fields', () => {
|
||||
const { G, saveGame, loadGame } = loadStrategy();
|
||||
assert.equal(typeof saveGame, 'function');
|
||||
assert.equal(typeof loadGame, 'function');
|
||||
|
||||
G.startedAt = Date.now();
|
||||
G.strategyPoints = 42;
|
||||
G.autoTournamentUnlocked = true;
|
||||
G.autoTournamentEnabled = true;
|
||||
G.strategyLastRunAt = 123456;
|
||||
G.strategyLeaderboard = [{ id: 'tit_for_tat', score: 99 }];
|
||||
|
||||
saveGame();
|
||||
|
||||
G.strategyPoints = 0;
|
||||
G.autoTournamentUnlocked = false;
|
||||
G.autoTournamentEnabled = false;
|
||||
G.strategyLastRunAt = 0;
|
||||
G.strategyLeaderboard = [];
|
||||
|
||||
assert.equal(loadGame(), true);
|
||||
assert.equal(G.strategyPoints, 42);
|
||||
assert.equal(G.autoTournamentUnlocked, true);
|
||||
assert.equal(G.autoTournamentEnabled, true);
|
||||
assert.equal(G.strategyLastRunAt, 123456);
|
||||
assert.equal(G.strategyLeaderboard[0].id, 'tit_for_tat');
|
||||
});
|
||||
Reference in New Issue
Block a user