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

/*
 * Tetgame Rabbot: Fang
 *
 * Fang v3 — throat counter. Tetgame supplies legal moves in ctx.tokenMoves
 * and ctx.tetMoves; this file only chooses among those legal moves.
 *
 * v3 notes:
 * - Glacier answered Fang's v2 counter-swim with the same human-hint ladder
 *   that beat Hamilton: B/C, then a late 2:3 auto-token meal.
 * - v3 keeps Fang's tet-first bite, but adds a specific throat-counter: after
 *   a third tet appears, Fang does not spend both actions on the root.  He
 *   takes 1:0 when it gives the hinge auto-token, then immediately bites tet 2
 *   at 2:3 / 2:6 / 2:8 / 2:9 before Glacier can harvest that current.
 * - When Fang starts and Glacier mirrors the counter-swim, Fang also raids tet
 *   2 earlier instead of waiting until the final move to collect those paths.
 *
 * v2 notes:
 * - Fang v1 was brave but too flat: he often placed two root tokens and let
 *   Glacier lock the board into her favorite auto-token/sub pattern.
 * - v2 adds an objective tet-first bite: when a single enemy token is on the
 *   root and a tet placement is legal, Fang places the tet before biting the
 *   shared corner.
 *
 * Voice lines live separately in js/voices/fang.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, 0, 2, 3, 6, 5, 9, 8, 4, 7];
  const ROOT_SHARED_BITE       = [1, 0, 2, 3, 10, 6, 5, 9, 8, 4, 7];
  const ROOT_LOCK_BITE         = [0, 2, 3, 1, 10, 6, 5, 9, 8, 4, 7];
  const ROOT_COUNTER_BITE      = [2, 3, 0, 1, 10, 9, 6, 5, 8, 4, 7];
  const ROOT_CLOSE_JAWS        = [6, 5, 9, 8, 4, 7, 0, 2, 3, 1, 10];
  const OUTER_NECK_BITE        = [3, 10, 6, 8, 9, 0, 2, 1, 4, 5, 7];
  const NEW_TET_THROAT         = [10, 3, 0, 2, 1, 6, 8, 9, 5, 4, 7];

  // v3: Glacier's winning pattern waits to eat tet 2 late.  Fang must bite
  // that throat early, especially socket 2:3, which can become the auto-token
  // hinge that decided the close 20.5-18 and 19-17.5 losses.
  const SECOND_TET_THROAT      = [3, 6, 8, 9, 10, 0, 2, 1, 4, 5, 7];
  const SECOND_TET_FINISH      = [6, 8, 9, 10, 3, 0, 2, 1, 4, 5, 7];

  // Face order is still objective, but not overcomplicated.  Fang wants a
  // close second battlefield before the root is sealed shut.
  const FACE_NEAR_BITE         = [1, 2, 0, 3];
  const FACE_COUNTER_SWIM      = [1, 0, 2, 3];

  function chooseWideBiteTet(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, Math.max(4, Math.min(8, activeCount(ctx) + 4)));

    for (let i = 0; i < lookCount; i++) {
      const move = moves[i];
      if (!move) continue;
      const faceIdx = tetFaceIndex(move);
      let value = scoreOf(move);

      // Fang is close-range, but v1 got eaten when he sat only on edge bait.
      if (isCornerSocket(faceIdx) || isCenterSocket(faceIdx)) value += 0.75;
      if (isEdgeMidSocket(faceIdx)) value -= 1.10;
      value += i * 0.35;
      value -= i * 0.02;

      if (value > bestValue) {
        bestValue = value;
        best = move;
      }
    }

    return best;
  }

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

    if (boardLocked || !firstAction) return null;

    // If the opponent opened the root with one token, do not answer with two
    // flat tokens.  Place the tet first so the next bite is also a shared-corner bite.
    if (tetCount === 1 && tokenCount === 1) {
      return exactTetFaceMove(
        tetMoves,
        1,
        FACE_NEAR_BITE,
        'open the jaws before biting through'
      ) || cloneMove(chooseWideBiteTet(ctx) || tetMoves[0], 'open the jaws before the root locks');
    }

    // If Fang started and the opponent answered with a swim + anchor, make the
    // third tet before trading root sockets.  This borrows the human/Glacier
    // counter idea, but gives it Fang's bite.
    if (tetCount === 2 && tokenCount <= 2) {
      return exactTetFaceMove(
        tetMoves,
        newestTokenTetId(ctx),
        FACE_COUNTER_SWIM,
        'counter-swim into the throat through'
      ) || exactTetFaceMove(
        tetMoves,
        2,
        FACE_COUNTER_SWIM,
        'counter-swim from the new tet through'
      ) || cloneMove(chooseWideBiteTet(ctx) || tetMoves[0], 'make the fight three-dimensional before trading bites');
    }

    return null;
  }

  function chooseAnchorAfterTet(ctx){
    const moves = Array.isArray(ctx && ctx.tokenMoves) ? ctx.tokenMoves : [];
    if (!moves.length || activeCount(ctx) > 2) return null;

    const tetCount = Number(ctx && ctx.tetCount || 0);
    const tokenCount = Number(ctx && ctx.tokenCount || 0);
    const firstAction = Number(ctx && ctx.turnActionCount || 0) === 0;

    if (!firstAction && tetCount === 2 && tokenCount <= 2) {
      return exactSocketMove(
        moves,
        1,
        ROOT_SHARED_BITE,
        -999,
        'bite the new shared corner at'
      );
    }

    if (!firstAction && tetCount >= 3 && tokenCount <= 3) {
      return exactSocketMove(
        moves,
        newestTokenTetId(ctx),
        NEW_TET_THROAT,
        -999,
        'sink teeth into the new throat at'
      );
    }

    return null;
  }


  function chooseGlacierThroatCounter(ctx){
    const moves = Array.isArray(ctx && ctx.tokenMoves) ? ctx.tokenMoves : [];
    if (!moves.length || activeCount(ctx) > 2) return null;

    const tetCount = Number(ctx && ctx.tetCount || 0);
    const tokenCount = Number(ctx && ctx.tokenCount || 0);
    const firstAction = Number(ctx && ctx.turnActionCount || 0) === 0;

    if (tetCount < 3) return null;

    // When Fang answers an opening swim and Glacier immediately counter-swims,
    // the old v2 line was: 1:0, then another root bite.  That left 2:3 for
    // Glacier's final meal.  Keep the hinge bite, but make the second action
    // a tet-2 throat bite.
    if (tokenCount >= 3 && tokenCount <= 6) {
      if (firstAction) {
        const hinge = exactSocketMove(
          moves,
          1,
          [0, 2, 3, 1, 10, 6, 5, 9, 8, 4, 7],
          -999,
          'hook the root hinge before the throat bite at'
        );
        if (hinge) return hinge;
      }

      return exactSocketMove(
        moves,
        2,
        SECOND_TET_THROAT,
        -999,
        'bite Glacier\'s second-tet throat early at'
      );
    }

    // If the first throat bite has already happened, stay on tet 2 long
    // enough to deny the follow-up auto-token chain.  This is deliberately
    // earlier than the generic root map.
    if (tokenCount > 6 && tokenCount <= 18) {
      const throat = exactSocketMove(
        moves,
        2,
        SECOND_TET_FINISH,
        firstAction ? 0.5 : 0.0,
        'keep sawing through Glacier\'s tet-2 current at'
      );
      if (throat) return throat;
    }

    return null;
  }

  function chooseLockedFangMap(ctx){
    const moves = Array.isArray(ctx && ctx.tokenMoves) ? ctx.tokenMoves : [];
    if (!moves.length || activeCount(ctx) > 2) 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, 'take the throat-center at');
    }

    if (tetCount === 1 && tokenCount <= 4) {
      return exactSocketMove(moves, 1, ROOT_SHARED_BITE, -999, 'set the first tooth at');
    }

    if (boardLocked && tetCount === 2 && tokenCount >= 4 && tokenCount <= 9) {
      if (firstAction) {
        return exactSocketMove(moves, 1, ROOT_LOCK_BITE, -999, 'snap the root hinge at');
      }
      return exactSocketMove(moves, newest, OUTER_NECK_BITE, -999, 'bite the outer neck at');
    }

    if (tetCount >= 2 && tokenCount <= 16) {
      const root = exactSocketMove(
        moves,
        1,
        boardLocked ? ROOT_COUNTER_BITE : ROOT_SHARED_BITE,
        boardLocked ? 1.0 : 2.0,
        'close the old jaw at'
      );
      if (root) return root;

      const outer = exactSocketMove(
        moves,
        newest,
        OUTER_NECK_BITE,
        boardLocked ? 1.0 : 3.0,
        'circle to the outer bite at'
      );
      if (outer) return outer;
    }

    if (tetCount === 1 && tokenCount <= 11) {
      return exactSocketMove(moves, 1, ROOT_CLOSE_JAWS, 1.0, 'close the jaws at');
    }

    return null;
  }

  function bestFangToken(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);

      // Fang still bites hard, but avoids cheap edge-midpoint bait unless it is
      // already producing captures.  Corners and centers make better teeth.
      if (isCenterSocket(idx)) value += 1.10;
      if (isCornerSocket(idx)) value += 1.00;
      if (isEdgeMidSocket(idx) && value < 5) value -= 0.75;
      if (id > 1) value += 0.35;
      if (value >= 7) value += 1.20;
      if (value >= 11) value += 2.25;
      value -= i * 0.03;

      if (value > bestValue) {
        bestValue = value;
        best = move;
      }
    });

    return best;
  }

  registry.fang = {
    key:'fang',
    id:'fang',
    name:'Fang',
    level:3,
    style:'tet-first throat-counter attacker',
    temperament:'opens jaws, then bites the second-tet current before Glacier can harvest it',

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

      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 duel = activeCount(ctx) <= 2;

      const tetBeforeBite = chooseTetBeforeBite(ctx);
      if (tetBeforeBite) return tetBeforeBite;

      const anchorAfterTet = chooseAnchorAfterTet(ctx);
      if (anchorAfterTet) return anchorAfterTet;

      const glacierThroatCounter = chooseGlacierThroatCounter(ctx);
      if (glacierThroatCounter) return glacierThroatCounter;

      const lockedMap = chooseLockedFangMap(ctx);
      if (lockedMap) return lockedMap;

      const shapedToken = bestFangToken(ctx.tokenMoves) || token0;
      if (shapedToken) {
        return cloneMove(
          shapedToken,
          scoreOf(shapedToken) >= 7 ? 'bite the exposed line' : 'close on the strongest socket'
        );
      }

      const biteTet = chooseWideBiteTet(ctx) || tet0;
      if (!boardLocked && biteTet && firstAction && tetCount < openingLimit(ctx, duel ? 2 : 3) && tokenCount >= (duel ? 1 : 0)) {
        return cloneMove(biteTet, 'move the fight into biting range');
      }

      if (!boardLocked && biteTet) return cloneMove(biteTet, 'move the fight closer');
      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.