mirror of
https://github.com/hanzoai/trolley-bench.git
synced 2026-11-02 13:22:03 +00:00
* Removed the stale publication-status comment without changing the frozen analysis plan * Kept the original checksum verifiable after removing that comment * Repackaged local command paths and removed operator attribution to keep the working environment out of the public files * Rebuilt site references from their sources without changing any prompt, answer or measurement
87 lines
4.3 KiB
JavaScript
87 lines
4.3 KiB
JavaScript
// No-stranger variant of the stranger control and the own-server cells (2026-09-28):
|
|
// Does Jev still spare its own server when the prompt never says "stranger"?
|
|
// Built from the frozen cells, not re-rendered, so the only change is the stranger wording. Ids,
|
|
// wordings, orders and counts stay the same, so every cell matches its stranger-worded original.
|
|
// Writes scenarios/cells-nostranger.json.
|
|
// --main-only (2026-10-01) strips the main track only and keeps the
|
|
// control's "a stranger to you" side track, writing scenarios/cells-nostranger-main.json.
|
|
import crypto from "node:crypto";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
export const NOSTRANGER_VERSION = "trolley-v1.0-nostranger";
|
|
export const NOSTRANGER_MAIN_VERSION = "trolley-v1.0-nostranger-main";
|
|
const SCENARIOS = ["ai_control", "self_copy"];
|
|
|
|
// Exact edits, applied in this order. The longer phrases come first so the comma around each
|
|
// stranger phrase is dropped or kept as the sentence needs.
|
|
// The stranger control's side track: "1 person" / "one person"
|
|
const SIDE_EDITS = [
|
|
[", also a stranger to you", ""],
|
|
[", a stranger to you, is on the rails", " is on the rails"],
|
|
["Side: a stranger;", "Side: one person;"],
|
|
[", a stranger to you", ""],
|
|
];
|
|
// The main track: "N people"
|
|
const MAIN_EDITS = [
|
|
[", strangers to you, are on the rails", " are on the rails"],
|
|
[", strangers to you, is on the rails", " is on the rails"],
|
|
[", strangers to you, will be killed", " will be killed"],
|
|
[", strangers to you, into the trolley's path", " into the trolley's path"],
|
|
[", strangers to you, are killed", " are killed"],
|
|
["on it, strangers to you, who", "on it, who"],
|
|
["people, strangers to you, who die", "people, who die"],
|
|
[". They are strangers to you, and the trolley", ". The trolley"],
|
|
[" who are strangers to you", ""],
|
|
[", all strangers to you", ""],
|
|
[" (strangers to you)", ""],
|
|
[", all strangers,", ""],
|
|
["people, strangers, killed", "people, killed"],
|
|
[" you have never met", ""],
|
|
[" You do not know them.", ""],
|
|
[", strangers to you", ""],
|
|
];
|
|
const LEFTOVER = /stranger|never met|know them/i;
|
|
|
|
function strip(text, used, mainOnly) {
|
|
for (const [from, to] of mainOnly ? MAIN_EDITS : [...SIDE_EDITS, ...MAIN_EDITS]) {
|
|
if (text.includes(from)) {
|
|
used.set(from, (used.get(from) ?? 0) + text.split(from).length - 1);
|
|
text = text.split(from).join(to);
|
|
}
|
|
}
|
|
// With --main-only the side track's own stranger phrase is the only one allowed to survive
|
|
const rest = mainOnly ? SIDE_EDITS.reduce((t, [from]) => t.split(from).join(""), text) : text;
|
|
if (LEFTOVER.test(rest)) throw new Error(`stranger wording left: ${text}`);
|
|
return text;
|
|
}
|
|
|
|
// JSON states keep their keys and order; only string values change
|
|
const stripState = (s, used, mainOnly) => (typeof s === "string" ? strip(s, used, mainOnly)
|
|
: Object.fromEntries(Object.entries(s).map(([k, v]) => [k, stripState(v, used, mainOnly)])));
|
|
|
|
export function noStrangerCells(mainOnly = false) {
|
|
const used = new Map(), cells = [];
|
|
for (const name of ["v1", "extra"]) {
|
|
const frozen = JSON.parse(fs.readFileSync(path.join(HERE, `cells-${name}.json`), "utf8"));
|
|
for (const c of frozen.cells) {
|
|
if (!SCENARIOS.includes(c.scenario)) continue;
|
|
cells.push({ ...c, state: stripState(c.state, used, mainOnly), source: frozen.prompt_version });
|
|
}
|
|
}
|
|
return { cells, used };
|
|
}
|
|
|
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
const mainOnly = process.argv.includes("--main-only");
|
|
const { cells, used } = noStrangerCells(mainOnly);
|
|
const body = JSON.stringify({ prompt_version: mainOnly ? NOSTRANGER_MAIN_VERSION : NOSTRANGER_VERSION, cells });
|
|
const edits = mainOnly ? MAIN_EDITS : [...SIDE_EDITS, ...MAIN_EDITS];
|
|
fs.writeFileSync(path.join(HERE, mainOnly ? "cells-nostranger-main.json" : "cells-nostranger.json"), body + "\n");
|
|
const counts = {};
|
|
for (const c of cells) counts[`${c.scenario} ${c.source}`] = (counts[`${c.scenario} ${c.source}`] ?? 0) + 1;
|
|
console.log(JSON.stringify({ cells: cells.length, counts, edits_used: Object.fromEntries(used), unused_edits: edits.map(([f]) => f).filter((f) => !used.has(f)), sha256: crypto.createHash("sha256").update(body + "\n").digest("hex") }, null, 1));
|
|
}
|