Diamond Mine
The Tetgame answer to the Gold Room.
Read-only starter
The Diamond Mine is intended to expose the deeper machinery of Tetgame Rabbots: behavior files, scoring nudges, strategy filters, voice loaders, backups, and eventually safe editing tools.
Current pass: this page only lists and displays local Tetgame JS/JSON files from
js/rabbots, js/personalities, and js/voices. It does not save changes yet.Behavior / voice file viewer
js/personalities/grim_pharaoh.js
/*
* Tetgame Rabbot: Grim Pharaoh
*
* Grim v5 — tomb architect with private-chamber tie-breaker discipline.
* Tetgame supplies legal moves in ctx.tokenMoves and ctx.tetMoves;
* this file only chooses among those legal moves.
* Voice lines live separately in js/voices/grim_pharaoh.json.
*/
(function(global){
'use strict';
const registry = global.TetgameRabbotBrains = global.TetgameRabbotBrains || {};
function cloneMove(move, reason, scoreBoost){
if (!move) return null;
const copy = Object.assign({}, move);
if (reason) copy.reason = reason;
if (typeof scoreBoost === 'number') copy.score = Number(copy.score || 0) + scoreBoost;
return copy;
}
function pass(reason){
return {type:'pass', score:-1, reason:reason || 'no legal action found'};
}
function scoreOf(move){
return Number(move && move.score || 0);
}
function socketIndex(move){
return Number(move && move.socket && move.socket.metadata ? move.socket.metadata.socketIndex : -1);
}
function tetId(move){
return Number(move && move.socket && move.socket.metadata ? move.socket.metadata.tetId : -1);
}
function isCenterSocket(move){
return socketIndex(move) === 10;
}
function isCornerSocket(move){
const idx = socketIndex(move);
return idx >= 0 && idx <= 3;
}
function isEdgeSocket(move){
const idx = socketIndex(move);
return idx >= 4 && idx <= 9;
}
function isRootSocket(move){
return tetId(move) === 1;
}
function socketReason(move){
if (!move || !move.socket || !move.socket.metadata) return 'the open point';
return 'socket ' + move.socket.metadata.tetId + ':' + move.socket.metadata.socketIndex;
}
function captureEstimate(move){
// Tetgame's token scores are based heavily on captured paths. A one-path
// capture usually pushes the score above roughly 8; two-path and three-path
// captures climb sharply from there. This stays approximate so it remains
// compatible with future engine scoring tweaks.
const s = scoreOf(move);
if (s >= 23) return 3;
if (s >= 15) return 2;
if (s >= 7.6) return 1;
return 0;
}
function tokenValue(move, ctx){
if (!move) return -999;
const idx = socketIndex(move);
const captures = captureEstimate(move);
const boardLocked = !!(ctx && ctx.boardLocked);
const secondAction = Number(ctx && ctx.turnActionCount || 0) > 0;
const activeCount = Math.max(2, Number(ctx && ctx.activeCount || 2));
const tokenCount = Number(ctx && ctx.tokenCount || 0);
let v = scoreOf(move) * 1.34;
// v3 correction: Morrigan's hard-corner shadow beat Grim because his
// old tomb style still valued decorative edge-mid seals too early.
// Corners create or deny the automatic edge tokens, so Grim now treats
// root corners as the stones that actually close the tomb.
v += captures * 2.15;
if (captures >= 2) v += 3.60;
if (captures >= 3) v += 4.80;
if (idx === 10) v += 2.25; // throne / center
if (idx >= 0 && idx <= 3) v += 4.95; // corner anchors win the duel
if (idx >= 4 && idx <= 9) {
v -= 1.65; // edge mids are often bait
if (scoreOf(move) >= 10.5) v += 2.10;
if (captures >= 2) v += 1.40;
}
// In the first few moves of a two-player duel, root corners are worth
// more than their immediate path score because they deny the enemy's
// auto-token patterns.
if (ctx && Number(ctx.activeCount || 2) <= 2 && tetId(move) === 1 && tokenCount <= activeCount * 3) {
if (idx >= 0 && idx <= 3) v += 4.20;
if (idx >= 4 && idx <= 9 && scoreOf(move) < 12.0) v -= 3.80;
}
// Later turns and locked boards are where a tomb closes.
if (secondAction) v += 2.45;
if (boardLocked) v += 2.20;
if (tokenCount >= activeCount * 2) v += 0.75;
if (tokenCount >= activeCount * 3) v += 1.05;
// Quiet endgame socket choices were costing him matches. Prefer them only
// when there is no tribute available, and prefer sealed edges over empty
// ornamental corners.
if (captures === 0 && tokenCount >= activeCount * 2) {
if (idx >= 4 && idx <= 9) v += 0.65;
if (idx >= 0 && idx <= 3) v -= 0.25;
}
return v;
}
function bestToken(ctx, limit){
const list = Array.isArray(ctx && ctx.tokenMoves) ? ctx.tokenMoves : [];
let best = null;
let bestScore = -999;
list.slice(0, Math.max(1, limit || 16)).forEach(move => {
const v = tokenValue(move, ctx);
if (v > bestScore) {
bestScore = v;
best = move;
}
});
return best;
}
function bestTokenAtLeast(ctx, threshold, limit){
const move = bestToken(ctx, limit || 16);
return move && scoreOf(move) >= Number(threshold || 0) ? move : null;
}
function firstSealValue(move, ctx){
if (!move || !isRootSocket(move)) return -999;
const idx = socketIndex(move);
let v = scoreOf(move) * 1.05;
// Opening discipline: take a root mark before offering the opponent
// another face to harvest. Center is regal; corners are lethal.
if (idx === 10) v += 9.20;
if (idx >= 0 && idx <= 3) v += 7.20;
if (idx >= 4 && idx <= 9) v -= 5.50;
if (ctx && Number(ctx.turnActionCount || 0) > 0) v += 0.65;
return v;
}
function bestFirstSealToken(ctx){
const list = Array.isArray(ctx && ctx.tokenMoves) ? ctx.tokenMoves : [];
let best = null;
let bestScore = -999;
list.forEach(move => {
const v = firstSealValue(move, ctx);
if (v > bestScore) {
bestScore = v;
best = move;
}
});
return best;
}
function earlyDuelValue(move, ctx){
if (!move || !isRootSocket(move)) return -999;
const idx = socketIndex(move);
const raw = scoreOf(move);
const captures = captureEstimate(move);
let v = raw * 1.10;
// Anti-Morrigan rule: during the root fight, spend moves on center/corners
// first. Edge mids should be won automatically from corners unless the
// immediate payoff is overwhelming.
if (idx === 10) v += 6.50;
if (idx >= 0 && idx <= 3) v += 9.25;
if (idx >= 4 && idx <= 9) {
v -= 6.75;
if (raw >= 12.0) v += 3.20;
if (captures >= 2) v += 2.20;
}
if (ctx && Number(ctx.turnActionCount || 0) > 0) v += 1.10;
if (ctx && ctx.boardLocked) v += 0.80;
return v;
}
function bestEarlyDuelToken(ctx){
if (!ctx || Number(ctx.activeCount || 2) > 2) return null;
const tetCount = Number(ctx.tetCount || 0);
const tokenCount = Number(ctx.tokenCount || 0);
const activeCount = Math.max(2, Number(ctx.activeCount || 2));
// This is intentionally narrow: it only handles the opening root fight,
// where Morrigan was winning before Grim's tomb had a chance to close.
if (tetCount > 2) return null;
if (tokenCount > activeCount * 4) return null;
const list = Array.isArray(ctx.tokenMoves) ? ctx.tokenMoves : [];
let bestPrimary = null;
let bestPrimaryScore = -999;
let bestAny = null;
let bestAnyScore = -999;
list.forEach(move => {
if (!isRootSocket(move)) return;
const v = earlyDuelValue(move, ctx);
if (v > bestAnyScore) {
bestAnyScore = v;
bestAny = move;
}
if (isCenterSocket(move) || isCornerSocket(move)) {
if (v > bestPrimaryScore) {
bestPrimaryScore = v;
bestPrimary = move;
}
}
});
if (bestPrimary && bestPrimaryScore >= 6.0) return bestPrimary;
return bestAny && bestAnyScore >= 12.5 ? bestAny : null;
}
function tetValue(move, ctx){
if (!move) return -999;
const firstAction = Number(ctx && ctx.turnActionCount || 0) === 0;
const secondAction = !firstAction;
const tetCount = Number(ctx && ctx.tetCount || 0);
const activeCount = Math.max(2, Number(ctx && ctx.activeCount || 2));
const tokenCount = Number(ctx && ctx.tokenCount || 0);
let v = scoreOf(move);
// Grim may still build, but not as charity for Scuttle and Echo. New tets
// must happen early, and preferably before the board already has enough
// tokens to make immediate tribute available.
if (firstAction) v += 0.45;
if (secondAction) v -= 6.25;
if (tetCount <= 1) v += 0.85;
if (tetCount === 2) v += 0.35;
if (tetCount >= 3) v -= 1.85;
if (tetCount >= Math.min(5, activeCount)) v -= 3.25;
if (tokenCount >= activeCount) v -= 1.55;
if (tokenCount >= activeCount * 2) v -= 2.20;
return v;
}
function bestTet(ctx, limit){
const list = Array.isArray(ctx && ctx.tetMoves) ? ctx.tetMoves : [];
let best = null;
let bestScore = -999;
list.slice(0, Math.max(1, limit || 8)).forEach(move => {
const v = tetValue(move, ctx);
if (v > bestScore) {
bestScore = v;
best = move;
}
});
return best;
}
function newestTetId(ctx){
const list = Array.isArray(ctx && ctx.tokenMoves) ? ctx.tokenMoves : [];
let max = 1;
list.forEach(move => {
const id = tetId(move);
if (id > max) max = id;
});
return max;
}
function privateChamberSealValue(move, ctx){
if (!move) return -999;
const id = tetId(move);
const idx = socketIndex(move);
const newest = newestTetId(ctx);
if (id < 2 || id !== newest) return -999;
let v = scoreOf(move) * 1.12;
// v5 personality shift: when Grim is answering Morrigan's opening shadow,
// he should not merely place a counter-token on her home tet and then give
// her first access to his new chamber. If he raises a private chamber,
// he immediately seals it with a throne/corner so later tribute belongs
// to him instead of becoming a Morrigan harvest field.
if (idx === 10) v += 12.50;
if (idx >= 0 && idx <= 3) v += 8.75;
if (idx >= 4 && idx <= 9) v -= 4.25;
const captures = captureEstimate(move);
v += captures * 1.60;
if (ctx && Number(ctx.turnActionCount || 0) > 0) v += 1.40;
return v;
}
function bestPrivateChamberSeal(ctx){
const list = Array.isArray(ctx && ctx.tokenMoves) ? ctx.tokenMoves : [];
let best = null;
let bestScore = -999;
list.forEach(move => {
const v = privateChamberSealValue(move, ctx);
if (v > bestScore) {
bestScore = v;
best = move;
}
});
return best && bestScore > -100 ? best : null;
}
function shouldRaisePrivateChamberFirst(ctx, tet){
if (!ctx || !tet || ctx.boardLocked) return false;
if (Number(ctx.activeCount || 2) > 2) return false;
const firstAction = Number(ctx.turnActionCount || 0) === 0;
const tetCount = Number(ctx.tetCount || 0);
const tokenCount = Number(ctx.tokenCount || 0);
// Morrigan's first-shadow mark sits on the starter's home stone.
// Counter-sealing that stone simply repeats the old starter-wins mirror.
// Grim's tie-breaker temperament is to raise his own tomb first, then
// claim its throne on action two.
return firstAction && tetCount === 1 && tokenCount === 1;
}
function shouldSealPrivateChamberAfterRaise(ctx){
if (!ctx || ctx.boardLocked) return false;
if (Number(ctx.activeCount || 2) > 2) return false;
const secondAction = Number(ctx.turnActionCount || 0) > 0;
const tetCount = Number(ctx.tetCount || 0);
const tokenCount = Number(ctx.tokenCount || 0);
return secondAction && tetCount === 2 && tokenCount === 1;
}
function shouldAvoidSecondCounterLock(ctx, tet){
if (!ctx || !tet || ctx.boardLocked) return false;
if (Number(ctx.activeCount || 2) > 2) return false;
const secondAction = Number(ctx.turnActionCount || 0) > 0;
const tetCount = Number(ctx.tetCount || 0);
const tokenCount = Number(ctx.tokenCount || 0);
// v4 fallback rule: when Morrigan opens with the first mark, Grim's
// two-corner counter was reducing the game to one tet. That made the
// starter advantage repeat forever: Morrigan won every odd match, Grim
// won every even match. After Grim answers the first mark with one root
// seal, he now opens a second chamber instead of dropping the second
// lock-token that freezes the board too soon.
return secondAction && tetCount === 1 && tokenCount === 2;
}
function openingLimit(ctx){
const active = Number(ctx && ctx.activeCount || 2);
return Math.max(2, Math.min(4, active - 1));
}
function reasonForToken(move, prefix){
const captures = captureEstimate(move);
const socket = socketReason(move);
if (captures >= 3) return 'close the tomb at ' + socket;
if (captures >= 2) return 'take heavy tribute from ' + socket;
if (captures >= 1) return 'collect tribute from ' + socket;
return (prefix || 'seal') + ' ' + socket;
}
registry.grim_pharaoh = {
key:'grim_pharaoh',
id:'grim_pharaoh',
name:'Grim Pharaoh',
gender:'male',
level:9,
style:'tomb architect / private-chamber tie-breaker discipline',
temperament:'patient, territorial, and now willing to build his own tomb before answering a shadow mark',
chooseMove(ctx){
if (!ctx || ctx.matchEnded || ctx.tournamentEnded) return null;
if (ctx.tetCount <= 0) return {type:'start_tet', score:1000, reason:'raise the first monument'};
const boardLocked = !!ctx.boardLocked;
const firstAction = Number(ctx.turnActionCount || 0) === 0;
const secondAction = !firstAction;
const tetCount = Number(ctx.tetCount || 0);
const tokenCount = Number(ctx.tokenCount || 0);
const activeCount = Math.max(2, Number(ctx.activeCount || 2));
const royalToken = bestTokenAtLeast(ctx, 22.0, 16); // usually 3-path tribute
const heavyToken = bestTokenAtLeast(ctx, 14.4, 16); // usually 2-path tribute
const tributeToken = bestTokenAtLeast(ctx, 7.35, 16); // usually 1-path tribute
const usefulToken = bestTokenAtLeast(ctx, 3.15, 16);
const quietToken = bestToken(ctx, 18);
const tet = !boardLocked ? bestTet(ctx, 8) : null;
const firstSealToken = bestFirstSealToken(ctx);
const earlyDuelToken = bestEarlyDuelToken(ctx);
const privateChamberSeal = bestPrivateChamberSeal(ctx);
// v5 tie-breaker: when answering a lone opening mark, Grim now raises
// his own private chamber first and immediately claims it. The v4
// counter-token-then-tet line let Morrigan move first into the new
// chamber and steal a sub every time she started.
if (shouldRaisePrivateChamberFirst(ctx, tet)) {
return cloneMove(tet, 'raise a private tomb before answering the shadow', 0.70);
}
if (shouldSealPrivateChamberAfterRaise(ctx) && privateChamberSeal) {
return cloneMove(privateChamberSeal, 'claim the private tomb throne at ' + socketReason(privateChamberSeal), 1.35);
}
// v4: in a two-player duel, do not let Morrigan own the root-corner
// tempo, but also do not freeze the entire board when Grim is moving
// second. The previous build tied because each starter won every time.
if (activeCount <= 2 && secondAction && tetCount === 1 && tokenCount === 0 && firstSealToken) {
return cloneMove(firstSealToken, 'plant the first royal seal ' + socketReason(firstSealToken), 1.10);
}
if (shouldAvoidSecondCounterLock(ctx, tet)) {
return cloneMove(tet, 'open a burial chamber instead of locking the first stone', 0.55);
}
if (earlyDuelToken) {
return cloneMove(earlyDuelToken, reasonForToken(earlyDuelToken, 'counter-seal'), 1.20);
}
// Second actions must close value. v1 still wandered too often; v2 only
// builds on a second action if there is literally no socket to claim.
if (secondAction) {
if (royalToken) return cloneMove(royalToken, reasonForToken(royalToken), 1.35);
if (heavyToken) return cloneMove(heavyToken, reasonForToken(heavyToken), 1.05);
if (tributeToken) return cloneMove(tributeToken, reasonForToken(tributeToken), 0.80);
if (usefulToken) return cloneMove(usefulToken, reasonForToken(usefulToken, 'seal'), 0.45);
if (quietToken) return cloneMove(quietToken, 'place a final seal', 0.25);
if (tet) return cloneMove(tet, 'raise a last wall', -0.35);
}
// Captures outrank construction. This is the main v2 performance shove.
if (royalToken) return cloneMove(royalToken, reasonForToken(royalToken), 1.35);
if (heavyToken) return cloneMove(heavyToken, reasonForToken(heavyToken), 1.05);
if (boardLocked && tributeToken) return cloneMove(tributeToken, reasonForToken(tributeToken), 0.90);
// Early: do not help everyone else by overbuilding. Expand only if the
// current sockets are too quiet and the board is still truly small.
if (firstAction && !boardLocked && tet && tetCount < openingLimit(ctx) && tokenCount < activeCount && (!usefulToken || scoreOf(usefulToken) < 3.8)) {
return cloneMove(tet, 'shape a sealed chamber', 0.30);
}
if (tributeToken) return cloneMove(tributeToken, reasonForToken(tributeToken), 0.80);
// If there is any respectable seal, claim it before allowing another
// builder to convert Grim's monument into their victory.
if (usefulToken) return cloneMove(usefulToken, reasonForToken(usefulToken, 'seal'), 0.45);
if (firstAction && !boardLocked && tet && tetCount <= 2 && (!quietToken || scoreOf(quietToken) < 2.6)) {
return cloneMove(tet, 'set a guarded wall', 0.10);
}
if (quietToken) return cloneMove(quietToken, 'mark the old ground', 0.15);
if (!boardLocked && tet) return cloneMove(tet, 'extend the kingdom only because no seal remains', -0.10);
return pass('no decree is needed yet');
}
};
})(window);
Future safe editor shape
Borrowing the Gold Room idea without mixing Trigame and Tetgame logic.
Read original
Keep an original snapshot before the first edit.
Edit safe sections
Expose selected behavior sections without letting the page damage wrapper code.
Test in Tetgame
Save, then jump back to the Blue Room or board to test the Rabbot live.
