Fluffle Personality Companion Pages

This set gives you one plain HTML page per rabbit. The first group reflects the personalities you have actively tested and tuned. The later pages are lightweight worksheets so you can fill in observations as you study their behavior.

Best use:
Open one page beside the JS file for that rabbit. Edit a little, test a little, and write down what changed.

Quick reminder

Your current Trigame builds load twelve individual personality files, one per rabbit, through the separate JS includes in the game page. That is why a one-page-per-rabbit companion makes sense.

On this page:
Companion Index JS Guide

Trigame Fluffle Personality JS Guide

This page is a plain-English guide to the personality JavaScript files used by the Trigame fluffle. It is meant as a practical “master key” so you can tweak rabbits yourself without having to rediscover what every setting does.

Big idea:
A personality file does not usually contain the whole AI. It supplies a flavor layer that nudges move choice. The engine generates legal moves first, then the personality:
  1. optionally filters which legal moves are even considered
  2. scores each remaining move
  3. the engine chooses the best-scoring move

1. Basic file shape

(function(){
  const register = window.RustyPersonalities && window.RustyPersonalities.register;
  if(!register) return;

  register({
    level: 5,
    id: "hare_brained",
    label: "Hare-Brained",

    filterActions(ctx){
      ...
    },

    scoreAction(ctx){
      let score = 0;
      ...
      return score;
    }
  });
})();

2. Top-level fields

FieldWhat it doesTypical value / rangeAdvice
level A numeric slot or identity level used by the game. Usually a positive integer such as 1, 2, 5, etc. Do not reuse the same level for two different intended personalities unless you mean to replace one.
id Internal machine name. Lowercase string with underscores, such as newb_bble. Keep it unique and stable. Good for filenames and debugging.
label Human-readable display name. Any short string. This is what players see. Changing this is safe.
filterActions(ctx) Optional hard preference stage. Lets the rabbit throw away many legal moves before scoring. Returns an array of actions. Use carefully. This is powerful and can make a rabbit feel stubborn or “forced.”
scoreAction(ctx) Main personality scoring function. Returns one number. This is the safest place to tune personality.

3. The two main tuning methods

3a. filterActions(ctx)

Use this when you want a rabbit to behave in a very strong, rule-like way.

filterActions(ctx){
  const tileActions = ctx.actions.filter(a => a.type === 'tile');
  if(!tileActions.length) return ctx.actions;

  if(ctx.phase === 0 && ctx.tileCount <= 10) return tileActions;
  if(ctx.phase === 1 && ctx.tileCount <= 6) return tileActions;

  return ctx.actions;
}

That example means: “if tiles exist, and we are early enough in the turn / game, consider only tile moves.”

Warning:
filterActions() can overpower everything else. If you make it too strict, a rabbit may stop looking smart and start looking blind.

3b. scoreAction(ctx)

This adds or subtracts points from candidate moves.

scoreAction(ctx){
  let score = 0;

  if(ctx.action.type === 'tile'){
    score += 140;
  }

  if(ctx.action.type === 'token'){
    score -= 10;
  }

  return score;
}

The engine compares scores across moves. Higher usually wins.

4. The most important ctx values

NameMeaningExpected valuesNotes
ctx.actions All currently legal candidate moves. Array Used mostly by filterActions().
ctx.action The one move currently being scored. Object Used mostly by scoreAction().
ctx.action.type What kind of move this is. 'tile', 'token', 'eraseTile', 'eraseToken' The most important branch point.
ctx.action.move The move payload. Object For tiles, this often holds cell info. For tokens, socket/key info.
ctx.phase Current turn phase. Usually 0 or 1 Phase 0 = first action, Phase 1 = second action. Bonus token actions are handled separately by the engine.
ctx.seat Which player seat this rabbit is. Integer, usually starting at 0 Seat 0 is player 1, seat 1 is player 2, and so on.
ctx.level The rabbit level/slot currently being used. Positive integer Often matches the file’s level.
ctx.tileCount How many tiles are currently on the board. 0 and up Most personalities use low thresholds like 1, 2, 3, 5, 8, 10.
ctx.tokenCount How many tokens are on the board. 0 and up Often less important than tile count, but still useful.
ctx.boardLocked Whether the board is locked. true / false Good for defensive checks if needed.
ctx.bonusCount How many bonus token actions are currently available. 0 and up Mostly relevant in advanced tuning.

5. Common helper functions you can call from ctx

HelperPurposeTypical returnHow to think of it
ctx.adjacentPlacedCount(move) Counts neighboring placed tiles touching the candidate tile. Usually 0 to a few Bigger = more connected growth.
ctx.withTempTileForSeat(move, seat, fn) Temporarily imagines the tile being placed, runs a function, then restores the board. Whatever fn returns Excellent for “if I place this tile, what does it open up?”
ctx.openSocketMoves(true) Looks at currently open token sockets. Array Good for valuing expansion potential.
ctx.estimateBestTokenGainForSeat(seat, N) Roughly estimates token payoff for the seat. Number Use as a bonus multiplier, not as gospel.
ctx.ownTokenCountNearCell(seat, cell) Counts this rabbit’s own tokens near a location. 0 and up Good for local-cluster personalities.

6. What score numbers mean

There is no universal magic number, but these are good rough meanings:

Score amountTypical meaning
1 to 6Tiny nudge
8 to 20Noticeable preference
20 to 50Strong bias
60 to 120Very strong bias
140+Almost a rule, unless another move is boosted similarly
Important:
The score is only meaningful relative to other move scores in the same moment. A +20 can be huge in one rabbit and tiny in another.

7. Practical tuning patterns

Make a rabbit more tile-happy

if(ctx.action.type === 'tile'){
  score += ctx.tileCount <= 2 ? 120 : 60;
}
if(ctx.action.type === 'token'){
  if(ctx.tileCount <= 4) score -= 20;
}

Make a rabbit like connected growth

if(ctx.action.type === 'tile'){
  score += (ctx.adjacentPlacedCount(ctx.action.move) || 0) * 20;
}

Make a rabbit love local clusters

if(ctx.action.type === 'tile'){
  score += ctx.ownTokenCountNearCell(ctx.seat, ctx.action.move) * 26;
}
if(ctx.action.type === 'token'){
  const cell = ctx.action.move.cell || null;
  if(cell) score += ctx.ownTokenCountNearCell(ctx.seat, cell) * 18;
}

Make a rabbit prefer future options

if(ctx.action.type === 'tile'){
  score += ctx.withTempTileForSeat(
    ctx.action.move,
    ctx.seat,
    ()=> Math.min(24, (ctx.openSocketMoves(true).length || 0) * 0.24)
  ) || 0;
}

Make a rabbit delay tokening

if(ctx.action.type === 'token'){
  if(ctx.tileCount <= 5) score -= 10;
}

8. Safe value ranges for common knobs

KnobSafe starter rangeAggressive rangeWarning
Flat tile bonus +20 to +60 +80 to +180 Too high can force tiny-minded rabbits to become expansion drones.
Flat token penalty early -4 to -12 -18 to -60 Too negative can make a rabbit ignore easy wins.
Neighbor multiplier 8 to 18 20 to 30 Too high can make rabbits hug existing tiles and stop exploring.
tileCount thresholds 1,2,3,4 5,6,8,10 Big thresholds can keep a rabbit in “opening mode” too long.
filterActions() hard tile cutoff Phase 0 only, up to tileCount <= 3 Phase 0 and 1, up to tileCount <= 8 or more This is one of the most dangerous knobs.

9. Warnings and gotchas

  • Do not tune from one match. Use batches and tournaments.
  • Multi-player results are messy. Player B can easily determine whether A or C wins.
  • Duplicated personalities skew tests. Two Warrens in one match do not tell the same story as one Warren.
  • Small-board habits are hard to break. Sometimes a rabbit needs hard filtering, not just score nudges.
  • Replace bonuses are touchy. Over-rewarding replacement tiles can create odd behavior.
  • Temporary-board helpers are powerful. They are great, but piling too many of them together can make a rabbit overfit to one kind of move.

10. Suggested workflow for editing rabbits

  1. Change only one rabbit at a time.
  2. Change only one main idea at a time:
    • more tiles
    • more local clustering
    • better token cash-in
    • more blocking
  3. Run a small tournament.
  4. Read both:
    • who won
    • how the board actually behaved
  5. If needed, rename or reorder rabbits only after behavior is stable.

11. Simple personality recipes

Personality styleHow to build it
Token-happy rabbit Boost token, reduce tile, especially when tileCount is small.
Expansion rabbit Boost tile, delay token, reward connected growth.
Cluster rabbit Use ownTokenCountNearCell() for both tile and token scoring.
Lane-builder Use withTempTileForSeat() plus openSocketMoves().
Punisher / closer Reward token gains and capture-related follow-up more than raw tile count.

12. Final advice

The easiest way to make a rabbit stronger fast:
Make it place more tiles, then give it just enough sense to cash in on the growth.

But the most interesting fluffle is not one where every rabbit uses the same trick. Let some rabbits cause the mess, and let others exploit it.