PLAY
Legacy prototype (2002) - modern Canvas versions (2026)
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.
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.
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.
(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;
}
});
})();
| Field | What it does | Typical value / range | Advice |
|---|---|---|---|
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. |
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.”
filterActions() can overpower everything else. If you make it too strict, a rabbit may stop looking smart and start looking blind.
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.
ctx values| Name | Meaning | Expected values | Notes |
|---|---|---|---|
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. |
ctx| Helper | Purpose | Typical return | How 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. |
There is no universal magic number, but these are good rough meanings:
| Score amount | Typical meaning |
|---|---|
1 to 6 | Tiny nudge |
8 to 20 | Noticeable preference |
20 to 50 | Strong bias |
60 to 120 | Very strong bias |
140+ | Almost a rule, unless another move is boosted similarly |
+20 can be huge in one rabbit and tiny in another.
if(ctx.action.type === 'tile'){
score += ctx.tileCount <= 2 ? 120 : 60;
}
if(ctx.action.type === 'token'){
if(ctx.tileCount <= 4) score -= 20;
}
if(ctx.action.type === 'tile'){
score += (ctx.adjacentPlacedCount(ctx.action.move) || 0) * 20;
}
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;
}
if(ctx.action.type === 'tile'){
score += ctx.withTempTileForSeat(
ctx.action.move,
ctx.seat,
()=> Math.min(24, (ctx.openSocketMoves(true).length || 0) * 0.24)
) || 0;
}
if(ctx.action.type === 'token'){
if(ctx.tileCount <= 5) score -= 10;
}
| Knob | Safe starter range | Aggressive range | Warning |
|---|---|---|---|
| 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. |
| Personality style | How 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. |
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.