vadugwi / compute.js
deucebucket's picture
All-neutral projective pool (multi-model consensus) + per-dimension scoring + reworded notes
4fcf639 verified
Raw
History Blame Contribute Delete
3.92 kB
/* compute.js — VADUGWI "Read the Room" scoring.
Pure, deterministic, no DOM. Sets window.RTR_COMPUTE in browser;
exports for Node when run under a module loader / test harness. */
(function (root) {
'use strict';
// Maps a raw slider answer (-6..+6) to an internal 0..255 value (128 = neutral).
// Ported from rtr_logic.js: extremes saturate, the center band is compressed so
// a "0" reads as mild-positive-of-neutral (153), matching the original instrument.
var SCALE_MAP = {
'-6': 0, '-5': 0, '-4': 51, '-3': 102, '-2': 128, '-1': 140,
'0': 153, '1': 166, '2': 179, '3': 204, '4': 230, '5': 255, '6': 255
};
var CATS = ['V', 'A', 'D', 'U', 'G', 'W', 'I'];
function clamp(v) {
return Math.round(Math.max(0, Math.min(255, v)));
}
function meanOf(arr) {
var s = 0;
for (var i = 0; i < arr.length; i++) s += arr[i];
return s / arr.length;
}
// All-neutral pool: every probe is a projective scene with an expected-neutral
// center. There are no NEG/POS tiers anymore, so a dimension is simply how the
// user COLORS those neutral scenes — the per-cat mean of their internal values.
// Arousal is the exception: it reads how much neutral scenes MOVE the user
// (reactivity = mean absolute deviation of raw answers from "doesn't affect me").
function computeProfile(probes, answers) {
var byCat = {};
var i;
for (i = 0; i < CATS.length; i++) {
byCat[CATS[i]] = { raw: [], internal: [] };
}
var deviations = [];
for (i = 0; i < probes.length; i++) {
var p = probes[i];
var raw = answers[i];
if (raw === null || raw === undefined) continue; // unanswered: skip
if (!byCat[p.cat]) continue; // unknown cat: skip
var internal = SCALE_MAP[String(raw)];
byCat[p.cat].raw.push(raw);
byCat[p.cat].internal.push(internal);
// Every probe is neutral (expected center), so the deviation IS the rating:
// how strongly / which way the user colored a scene that was meant to be blank.
deviations.push({
i: i,
id: p.id,
cat: p.cat,
tier: p.tier,
text: p.text,
answer: raw,
internal: internal,
dev: raw // signed projective reaction
});
}
deviations.sort(function (a, b) { return Math.abs(b.dev) - Math.abs(a.dev); });
// Per-dimension projective bias: mean of internal values over that cat's
// answered probes. Default 128 (true neutral) when none answered.
function catMean(cat, dflt) {
var bucket = byCat[cat].internal;
if (!bucket.length) return dflt;
return clamp(meanOf(bucket));
}
var V = catMean('V', 128);
var D = catMean('D', 128);
var G = catMean('G', 128);
var W = catMean('W', 128);
var I = catMean('I', 128);
// U keeps its 0-based axis meaning (Urgency starts at 0/neutral), but is now
// driven purely by the per-cat mean of the answered U probes; 0 when none.
var U = catMean('U', 0);
// A (Arousal) = reactivity = mean ABS deviation of raw answers, mapped 0..255.
// How much do neutral scenes move you, regardless of which way.
var aVals = byCat.A.raw, absSum = 0;
for (i = 0; i < aVals.length; i++) absSum += Math.abs(aVals[i]);
var avgAbs = aVals.length ? absSum / aVals.length : 0;
var A = clamp((avgAbs / 6) * 255);
return {
V: V, A: A, D: D, U: U, G: G, W: W, I: I,
byCat: byCat,
deviations: deviations
};
}
var api = { SCALE_MAP: SCALE_MAP, computeProfile: computeProfile };
if (typeof window !== 'undefined') window.RTR_COMPUTE = api;
if (typeof module !== 'undefined' && module.exports) module.exports = api;
if (typeof root !== 'undefined' && root && !root.RTR_COMPUTE) root.RTR_COMPUTE = api;
})(typeof globalThis !== 'undefined' ? globalThis : this);