Tetgame mark
Tetgame deep-code room

The Diamond Mine

Inspect Tetgame Rabbot behavior files and prepare the future safe editor for strategy tuning.

Welcome guest — sign in through the Trigame front door when you are ready.
Log in via Trigame

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/spectrum_pistolero.js

/*
 * Tetgame Rabbot: Spectrum Pistolero
 *
 * Spectrum v2 — deadeye anti-tentacle counter.
 * Tetgame supplies legal moves in ctx.tokenMoves and ctx.tetMoves; this file
 * only chooses among those legal moves.
 *
 * v2 notes:
 * - v1 was already a good marksman, but it sometimes spent unlocked-board
 *   turns on small midpoint nibbles while Octavius was growing a long arm.
 * - v2 treats Octavius-style long lattices as a shooting gallery: when the
 *   board is still open, Spectrum looks at the newest/recent tets and shoots
 *   center/corner sockets first, especially sockets that can poison whole-tet
 *   captures later.
 * - It still takes obvious high-value shots, but quiet one-path mid-edge moves
 *   are delayed unless there is no better target.
 * - Once the board is locked, Spectrum goes back to harvest mode and trusts the
 *   engine's socket score more heavily.
 *
 * Voice lines live separately in js/voices/spectrum_pistolero.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(3, Math.min(7, active + Number(extra || 2)));
  }

  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;
  }

  // Raw score scale from the generic socket scorer.  A one-path bite is around
  // 8.5-9 raw points, so do not treat score 4 as a meaningful tactical shot.
  const RAW_ONE_PATH    = 8.5;
  const RAW_TWO_PATHS   = 16.0;
  const RAW_THREE_PATHS = 23.0;
  const RAW_FOUR_PATHS  = 30.0;
  const RAW_HUGE_SHOT   = 44.0;

  function estimatedPathBites(move){
    const raw = scoreOf(move);
    if (raw >= RAW_HUGE_SHOT) return 6;
    if (raw >= RAW_FOUR_PATHS) return 4;
    if (raw >= RAW_THREE_PATHS) return 3;
    if (raw >= RAW_TWO_PATHS) return 2;
    if (raw >= RAW_ONE_PATH) return 1;
    return 0;
  }

  function isSmallMidpointNibble(move){
    return isEdgeMidSocket(socketIndex(move)) && estimatedPathBites(move) <= 1;
  }

  function bestByValue(moves, valueFn, minValue){
    if (!Array.isArray(moves) || !moves.length) return null;
    let best = null;
    let bestValue = -999999;

    moves.forEach(move => {
      if (!move) return;
      const value = valueFn(move);
      if (typeof minValue === 'number' && value < minValue) return;
      if (!best || value > bestValue) {
        best = move;
        bestValue = value;
      }
    });

    return best;
  }

  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.
    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;
  }

  function newestLegalTetId(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 chooseCoverTet(ctx){
    const tetMoves = Array.isArray(ctx && ctx.tetMoves) ? ctx.tetMoves : [];
    if (!tetMoves.length) return null;

    const tetCount = Number(ctx && ctx.tetCount || 0);
    const sourceTet = Math.max(1, tetCount);

    // Pistolero does not want Octavius' exact long rail, but he does want a
    // side angle for later crossfire.  Face order favors a counter-diagonal.
    return exactTetFaceMove(
      tetMoves,
      sourceTet,
      [2, 1, 3, 0],
      'step sideways into cover'
    ) || cloneMove(tetMoves[0], 'step sideways into cover');
  }

  function chooseHighShot(ctx, minRaw, reason){
    const tokenMoves = Array.isArray(ctx && ctx.tokenMoves) ? ctx.tokenMoves : [];
    const move = tokenMoves.find(m => scoreOf(m) >= minRaw);
    return cloneMove(move, reason);
  }

  function chooseCleanShot(ctx, minRaw, reason){
    const tokenMoves = Array.isArray(ctx && ctx.tokenMoves) ? ctx.tokenMoves : [];
    const move = bestByValue(tokenMoves, move => {
      const idx = socketIndex(move);
      let value = scoreOf(move);
      if (isCenterSocket(idx)) value += 3.5;
      if (isCornerSocket(idx)) value += 2.0;
      if (isSmallMidpointNibble(move)) value -= 6.0;
      return value;
    }, minRaw);
    return cloneMove(move, reason);
  }

  function chooseRecentArmShot(ctx, opts){
    const tokenMoves = Array.isArray(ctx && ctx.tokenMoves) ? ctx.tokenMoves : [];
    if (!tokenMoves.length) return null;

    const tetCount = Number(ctx && ctx.tetCount || 0);
    const newest = newestLegalTetId(ctx);
    const minRaw = Number(opts && opts.minRaw || 0);
    const allowQuietCenter = !!(opts && opts.allowQuietCenter);
    const windowBack = Number(opts && opts.windowBack || 4);
    const minTet = Math.max(1, newest - windowBack);

    const move = bestByValue(tokenMoves, m => {
      const tid = moveTetId(m);
      const idx = socketIndex(m);
      const raw = scoreOf(m);

      if (tid < minTet || tid > newest) return -999999;
      if (!isCenterSocket(idx) && !isCornerSocket(idx)) return -999999;
      if (raw < minRaw && !(allowQuietCenter && isCenterSocket(idx))) return -999999;

      let value = raw;

      // Center poisons whole-tet dreams.  Corners are next because two corners
      // can ruin the opponent's lock/capture line.
      if (isCenterSocket(idx)) value += 12.0;
      if (isCornerSocket(idx)) value += 6.0;

      // The most dangerous arm is often not the newest tip, but the joint one
      // or two tets behind it: those are the sockets Octavius harvests after
      // the board locks.
      const behind = newest - tid;
      if (behind === 1) value += 4.5;
      else if (behind === 2) value += 3.0;
      else if (behind === 0) value += 2.0;
      else value += Math.max(0, 2.0 - behind * 0.25);

      // When the board is long, a quiet center shot is still worth considering
      // because it can deny an entire future tet.
      if (allowQuietCenter && isCenterSocket(idx) && raw < RAW_ONE_PATH && tetCount >= 9) value += 5.0;

      return value;
    });

    if (!move) return null;
    const idx = socketIndex(move);
    const tid = moveTetId(move);
    const label = isCenterSocket(idx) ? 'center' : 'corner';
    return cloneMove(move, 'deadeye ' + label + ' shot at arm ' + tid + ':' + idx);
  }

  function chooseAntiLockShot(ctx){
    const tetCount = Number(ctx && ctx.tetCount || 0);
    const top = moveAt(ctx.tokenMoves, 0);

    // Late open board: do not let Octavius save all of the newest tets for a
    // lock-and-harvest finish.  Shoot a recent center/corner even if it is not
    // the top generic score.
    if (tetCount >= 8) {
      return chooseRecentArmShot(ctx, {
        minRaw: RAW_ONE_PATH,
        allowQuietCenter: true,
        windowBack: 5
      });
    }

    // Midgame: only override the top score if it is merely a quiet midpoint.
    if (top && isSmallMidpointNibble(top)) {
      return chooseRecentArmShot(ctx, {
        minRaw: RAW_ONE_PATH,
        allowQuietCenter: false,
        windowBack: 4
      });
    }

    return null;
  }

  registry.spectrum_pistolero = {
    key:'spectrum_pistolero',
    id:'spectrum_pistolero',
    name:'Spectrum Pistolero',
    level:2,
    style:'deadeye anti-tentacle tactician',
    temperament:'waits for the clean shot, then ruins the plan',

    chooseMove(ctx){
      if (!ctx || ctx.matchEnded || ctx.tournamentEnded) return null;
      if (canOpen(ctx)) return {type:'start_tet', score:1000, reason:'open the first position'};

      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);

      // If the mark is enormous, draw immediately.  Do not overthink a sure
      // kill-shot.
      const hugeShot = chooseHighShot(ctx, RAW_HUGE_SHOT, 'take the sure kill-shot');
      if (hugeShot) return hugeShot;

      if (!boardLocked) {
        // Early: take a side angle if the board has not produced a real shot.
        if (firstAction && tet0 && tetCount < openingLimit(ctx, 1) && (!token0 || scoreOf(token0) < RAW_TWO_PATHS)) {
          return chooseCoverTet(ctx) || cloneMove(tet0, 'open a counter-angle');
        }

        // Main anti-Octavius rule: shoot recent arm centers/corners instead of
        // nibbling loose midpoints while the board is still growing.
        const antiLockShot = chooseAntiLockShot(ctx);
        if (antiLockShot) return antiLockShot;

        // Take strong clean shots, but downgrade single-path midpoint nibbles.
        const cleanThree = chooseCleanShot(ctx, RAW_THREE_PATHS, 'draw on the best exposed target');
        if (cleanThree) return cleanThree;

        if (token0 && scoreOf(token0) >= RAW_TWO_PATHS && !isSmallMidpointNibble(token0)) {
          return cloneMove(token0, 'take the clean two-path shot');
        }

        // If all token choices are low-value nibbles, add one more cover tet
        // rather than helping Octavius turn the position finite too early.
        if (firstAction && tet0 && tetCount < openingLimit(ctx, 3) && (!token0 || isSmallMidpointNibble(token0) || scoreOf(token0) < RAW_TWO_PATHS)) {
          return chooseCoverTet(ctx) || cloneMove(tet0, 'move to a better firing lane');
        }

        if (token0 && !isSmallMidpointNibble(token0)) return cloneMove(token0, 'take the fastest clean shot');
        if (tet0 && firstAction && tetCount < openingLimit(ctx, 4)) return chooseCoverTet(ctx) || cloneMove(tet0, 'refuse the weak midpoint shot');
        return cloneMove(token0, 'take the only remaining shot') || cloneMove(tet0, 'shift to a new angle') || pass();
      }

      // Locked board: now the game is finite.  Go back to harvest mode, but
      // still prefer center/corner shots when they are close to the top score.
      const lockedClean = chooseCleanShot(ctx, token0 ? Math.max(0, scoreOf(token0) - 3.0) : 0, 'harvest the cleanest locked-board target');
      if (lockedClean) return lockedClean;

      return cloneMove(token0, 'finish the best visible target') || 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.