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/octavius_indigo002.js
/*
* Tetgame Rabbot: Octavius Indigo
*
* Octavius v2 — radial arms.
* Tetgame supplies legal moves in ctx.tokenMoves and ctx.tetMoves; this file
* only chooses among those legal moves.
*
* v2 notes:
* - v1 was too token-flat. Against Fang it often dropped two root tokens, let
* the board lock, and then fought Fang inside Fang's mouth.
* - v2 acts more like an octopus: when a tet can be placed before a denial
* token, Octavius places the tet first, then anchors the shared corner.
* - When a second branch exists, he tries to make the third tet immediately,
* creating a many-armed board instead of a one-tet knife fight.
* - After the board locks, he alternates between root, tet 2, and the newest
* tet so he does not abandon the outer suckers to Fang's teeth.
*
* Voice lines live separately in js/voices/octavius_indigo.json.
*/
(function(global){
'use strict';
const registry = global.TetgameRabbotBrains = global.TetgameRabbotBrains || {};
function cloneMove(move, fallbackReason){
if (!move) return null;
const copy = Object.assign({}, move);
if (!copy.reason && fallbackReason) copy.reason = fallbackReason;
return copy;
}
function moveAt(list, index){
return Array.isArray(list) && list.length > index ? list[index] : null;
}
function pass(reason){
return {type:'pass', score:-1, reason:reason || 'no legal action found'};
}
function scoreOf(move){
return Number(move && move.score || 0);
}
function canOpen(ctx){
return ctx && Number(ctx.tetCount || 0) <= 0;
}
function activeCount(ctx){
return Math.max(2, Number(ctx && ctx.activeCount || 2));
}
function openingLimit(ctx, extra){
const active = activeCount(ctx);
return Math.max(2, Math.min(5, active + Number(extra || 1)));
}
function moveTetId(move){
return Number(move && move.socket && move.socket.metadata ? move.socket.metadata.tetId : -1);
}
function socketIndex(move){
return Number(move && move.socket && move.socket.metadata ? move.socket.metadata.socketIndex : -1);
}
function tetFaceIndex(move){
return Number(move && move.face && move.face.metadata ? move.face.metadata.faceIndex : -1);
}
function tetFaceTetId(move){
return Number(move && move.face && move.face.metadata ? move.face.metadata.tetId : -1);
}
function isCornerSocket(idx){
return idx >= 0 && idx <= 3;
}
function isEdgeMidSocket(idx){
return idx >= 4 && idx <= 9;
}
function isCenterSocket(idx){
return idx === 10;
}
function newestTokenTetId(ctx){
const moves = Array.isArray(ctx && ctx.tokenMoves) ? ctx.tokenMoves : [];
let newest = Math.max(1, Number(ctx && ctx.tetCount || 1));
moves.forEach(move => {
const id = moveTetId(move);
if (id > newest) newest = id;
});
return newest;
}
function exactSocketMove(moves, tetId, socketOrder, minRawScore, reasonPrefix){
if (!Array.isArray(moves) || tetId < 1) return null;
const bySocket = Object.create(null);
moves.forEach(move => {
if (moveTetId(move) !== tetId) return;
const idx = socketIndex(move);
if (idx < 0) return;
if (!bySocket[idx] || scoreOf(move) > scoreOf(bySocket[idx])) bySocket[idx] = move;
});
for (let i = 0; i < socketOrder.length; i++) {
const idx = socketOrder[i];
const move = bySocket[idx];
if (!move) continue;
if (typeof minRawScore === 'number' && scoreOf(move) < minRawScore) continue;
return cloneMove(move, reasonPrefix + ' socket ' + tetId + ':' + idx);
}
return null;
}
function exactTetFaceMove(moves, sourceTetId, faceOrder, reasonPrefix){
if (!Array.isArray(moves) || !moves.length) return null;
const byFace = Object.create(null);
moves.forEach(move => {
const faceIdx = tetFaceIndex(move);
if (faceIdx < 0) return;
const faceTet = tetFaceTetId(move);
if (sourceTetId >= 1 && faceTet >= 1 && faceTet !== sourceTetId) return;
if (!byFace[faceIdx] || scoreOf(move) > scoreOf(byFace[faceIdx])) byFace[faceIdx] = move;
});
for (let i = 0; i < faceOrder.length; i++) {
const faceIdx = faceOrder[i];
const move = byFace[faceIdx];
if (move) return cloneMove(move, reasonPrefix + ' face ' + sourceTetId + ':' + faceIdx);
}
// Older tetgame builds may not expose the source tet id on face metadata.
// Fall back to face index only.
for (let i = 0; i < faceOrder.length; i++) {
const faceIdx = faceOrder[i];
const move = moves.find(m => tetFaceIndex(m) === faceIdx);
if (move) return cloneMove(move, reasonPrefix + ' face ' + sourceTetId + ':' + faceIdx);
}
return null;
}
// Socket 10 is center. Sockets 0-3 are corners. Sockets 4-9 are edge midpoints.
const ROOT_OPEN_CENTER = [10, 1, 3, 0, 2, 8, 6, 4, 5, 7, 9];
const ROOT_SHARED_SUCKER = [1, 0, 2, 3, 10, 6, 5, 9, 8, 4, 7];
const ROOT_RADIAL_DENIAL = [0, 2, 3, 1, 10, 6, 5, 9, 8, 4, 7];
const ROOT_INK_CLOUD = [2, 3, 0, 1, 10, 9, 6, 5, 8, 4, 7];
const ROOT_RING_CLOSE = [6, 5, 9, 8, 4, 7, 0, 2, 3, 1, 10];
const OUTER_ARM_ANCHOR = [10, 3, 6, 8, 9, 0, 2, 1, 4, 5, 7];
const OUTER_ARM_SWEEP = [3, 6, 8, 9, 10, 0, 2, 1, 4, 5, 7];
const OUTER_ARM_FINISH = [6, 8, 9, 10, 3, 0, 2, 1, 4, 5, 7];
const NEW_ARM_ANCHOR = [10, 3, 0, 2, 1, 6, 8, 9, 5, 4, 7];
// Face maps. These are deliberately not just "first legal face". Octavius
// wants the board to sprout arms instead of becoming Fang's one-tet bite pit.
const FACE_FIRST_ARM = [1, 2, 0, 3];
const FACE_COUNTER_ARM = [1, 0, 2, 3];
const FACE_SIDE_ARM = [2, 3, 1, 0];
const FACE_INK_SPIRAL = [3, 1, 0, 2];
function chooseEightArmTet(ctx){
const moves = Array.isArray(ctx && ctx.tetMoves) ? ctx.tetMoves : [];
if (!moves.length) return null;
let best = moves[0];
let bestValue = -999999;
const lookCount = Math.min(moves.length, 8); // eight arms: look wider than the first easy branch.
const tokenCount = Number(ctx && ctx.tokenCount || 0);
for (let i = 0; i < lookCount; i++) {
const move = moves[i];
if (!move) continue;
const faceIdx = tetFaceIndex(move);
const sourceTet = tetFaceTetId(move);
let value = scoreOf(move);
// Prefer branching away from the root once a second tet exists.
if (sourceTet > 1) value += 1.25;
if (isCornerSocket(faceIdx) || isCenterSocket(faceIdx)) value += 0.90;
if (isEdgeMidSocket(faceIdx)) value -= 1.35;
// Make the choice feel less linear: as the token count changes, a
// different legal branch can become the favored arm.
value += ((i + tokenCount) % 4) * 0.22;
if (i >= 2) value += 0.55;
if (i >= 5) value += 0.30;
value -= i * 0.025;
if (value > bestValue) {
bestValue = value;
best = move;
}
}
return best;
}
function bestIndigoToken(moves){
if (!Array.isArray(moves) || !moves.length) return null;
let best = moves[0];
let bestValue = -999999;
moves.forEach((move, i) => {
const idx = socketIndex(move);
const id = moveTetId(move);
let value = scoreOf(move);
// Octavius values stable suckers. Edge-midpoints are fine once they are
// already biting, but he should not drift into cheap edge bait too early.
if (isCenterSocket(idx)) value += 1.35;
if (isCornerSocket(idx)) value += 1.15;
if (isEdgeMidSocket(idx) && value < 5) value -= 0.75;
if (id > 1) value += 0.70;
if (id >= 3) value += 0.35;
if (value >= 7) value += 1.10;
if (value >= 11) value += 2.10;
value -= i * 0.025;
if (value > bestValue) {
bestValue = value;
best = move;
}
});
return best;
}
function chooseTetFirstArm(ctx){
const tetMoves = Array.isArray(ctx && ctx.tetMoves) ? ctx.tetMoves : [];
if (!tetMoves.length || !!(ctx && ctx.boardLocked)) return null;
const firstAction = Number(ctx && ctx.turnActionCount || 0) === 0;
const tetCount = Number(ctx && ctx.tetCount || 0);
const tokenCount = Number(ctx && ctx.tokenCount || 0);
const limit = openingLimit(ctx, 3);
// The key fix: when the opponent has only one root token, place a tet
// first so the follow-up token can be a shared-corner advantage.
if (firstAction && tetCount === 1 && tokenCount === 1) {
return exactTetFaceMove(tetMoves, 1, FACE_FIRST_ARM, 'unfold the first arm before touching') ||
cloneMove(chooseEightArmTet(ctx), 'unfold the first arm before touching');
}
// Octavius should not stop at two tets if a third branch is legal. This is
// the "eight arms" behavior: branch, anchor, branch again.
if (firstAction && tetCount === 2 && tokenCount <= 3 && tetCount < limit) {
return exactTetFaceMove(tetMoves, newestTokenTetId(ctx), FACE_COUNTER_ARM, 'split the second arm into a third') ||
exactTetFaceMove(tetMoves, 2, FACE_COUNTER_ARM, 'split the second arm into a third') ||
cloneMove(chooseEightArmTet(ctx), 'split the second arm into a third');
}
// If the board is still loose, look wider than the obvious first branch.
// This gives Octavius a more alien, radial opening against non-Fang styles.
if (firstAction && tetCount >= 2 && tetCount < limit && tokenCount <= 6) {
return exactTetFaceMove(tetMoves, newestTokenTetId(ctx), FACE_SIDE_ARM, 'throw another arm sideways') ||
exactTetFaceMove(tetMoves, 1, FACE_INK_SPIRAL, 'throw another arm through the ink') ||
cloneMove(chooseEightArmTet(ctx), 'throw another arm where the current opens');
}
return null;
}
function chooseAnchorAfterArm(ctx){
const moves = Array.isArray(ctx && ctx.tokenMoves) ? ctx.tokenMoves : [];
if (!moves.length) return null;
const firstAction = Number(ctx && ctx.turnActionCount || 0) === 0;
const tetCount = Number(ctx && ctx.tetCount || 0);
const tokenCount = Number(ctx && ctx.tokenCount || 0);
const newest = newestTokenTetId(ctx);
// After placing the first branch, grab the root common corner rather than
// an isolated edge. This is the objective correction Frank spotted.
if (!firstAction && tetCount === 2 && tokenCount <= 2) {
return exactSocketMove(moves, 1, ROOT_SHARED_SUCKER, -999, 'set a sucker on the shared corner at');
}
// After creating a third tet, anchor it immediately so the branch becomes
// useful instead of decorative.
if (!firstAction && tetCount >= 3 && tokenCount <= 4) {
return exactSocketMove(moves, newest, NEW_ARM_ANCHOR, -999, 'anchor the new arm at') ||
exactSocketMove(moves, 3, NEW_ARM_ANCHOR, -999, 'anchor the new arm at');
}
return null;
}
function chooseRadialTokenMap(ctx){
const moves = Array.isArray(ctx && ctx.tokenMoves) ? ctx.tokenMoves : [];
if (!moves.length) return null;
const tetCount = Number(ctx && ctx.tetCount || 0);
const tokenCount = Number(ctx && ctx.tokenCount || 0);
const boardLocked = !!(ctx && ctx.boardLocked);
const firstAction = Number(ctx && ctx.turnActionCount || 0) === 0;
const newest = newestTokenTetId(ctx);
if (tetCount === 1 && tokenCount <= 0) {
return exactSocketMove(moves, 1, ROOT_OPEN_CENTER, -999, 'open the central eye at');
}
if (tetCount === 1 && tokenCount <= 4) {
return exactSocketMove(moves, 1, ROOT_SHARED_SUCKER, -999, 'place the first sucker at');
}
// Fang wins by turning tet 2 into a throat. Octavius now contests tet 2
// early instead of leaving it as prey.
if (tetCount >= 2 && tokenCount >= 3 && tokenCount <= 13) {
const tet2First = (tokenCount % 2 === 0);
const outerA = exactSocketMove(moves, tet2First ? 2 : newest, OUTER_ARM_SWEEP, boardLocked ? 0.0 : 1.0, 'wrap an outer arm around');
if (outerA) return outerA;
const root = exactSocketMove(moves, 1, boardLocked ? ROOT_INK_CLOUD : ROOT_RADIAL_DENIAL, boardLocked ? 0.0 : 1.0, 'cloud the root line at');
if (root) return root;
const outerB = exactSocketMove(moves, tet2First ? newest : 2, OUTER_ARM_FINISH, 0.0, 'finish the outer suction at');
if (outerB) return outerB;
}
if (tetCount >= 3 && tokenCount <= 21) {
const newArm = exactSocketMove(moves, newest, OUTER_ARM_ANCHOR, 0.0, 'keep pressure on the far arm at');
if (newArm) return newArm;
const tet2 = exactSocketMove(moves, 2, OUTER_ARM_FINISH, 0.0, 'do not release tet two at');
if (tet2) return tet2;
}
if (tetCount === 1 && tokenCount <= 11) {
return exactSocketMove(moves, 1, ROOT_RING_CLOSE, 0.0, 'close the ink ring at');
}
return null;
}
registry.octavius_indigo = {
key:'octavius_indigo',
id:'octavius_indigo',
name:'Octavius Indigo',
level:2,
style:'radial eight-arm brancher',
temperament:'unfolds the board into branching ink before closing the suckers',
chooseMove(ctx){
if (!ctx || ctx.matchEnded || ctx.tournamentEnded) return null;
if (canOpen(ctx)) return {type:'start_tet', score:1000, reason:'open the first ink pool'};
const token0 = moveAt(ctx.tokenMoves, 0);
const tet0 = moveAt(ctx.tetMoves, 0);
const boardLocked = !!ctx.boardLocked;
const firstAction = Number(ctx.turnActionCount || 0) === 0;
const tetCount = Number(ctx.tetCount || 0);
const tokenCount = Number(ctx.tokenCount || 0);
const tetFirstArm = chooseTetFirstArm(ctx);
if (tetFirstArm) return tetFirstArm;
const anchorAfterArm = chooseAnchorAfterArm(ctx);
if (anchorAfterArm) return anchorAfterArm;
const radialMap = chooseRadialTokenMap(ctx);
if (radialMap) return radialMap;
const shapedToken = bestIndigoToken(ctx.tokenMoves) || token0;
if (shapedToken) {
return cloneMove(
shapedToken,
scoreOf(shapedToken) >= 8 ? 'pull the scoring arm tight' : 'place the quiet sucker'
);
}
const wideTet = chooseEightArmTet(ctx) || tet0;
if (!boardLocked && wideTet && firstAction && tetCount < openingLimit(ctx, 3) && tokenCount >= 1) {
return cloneMove(wideTet, 'unfold one more arm');
}
if (!boardLocked && wideTet) return cloneMove(wideTet, 'unfold through the ink');
return pass();
}
};
})(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.
