Every token on the board is a bluff until it isn’t. You deploy six of them face-down behind your edge — strengths 0, 1, 2, 2, 3, 4 — and from then on the only way to learn what an opponent is holding is to attack it, which is also the only way to lose it.
Built on the same board and seating as Hexagonal Checkers: a radius-5 hexagon, six faces, the two rows in front of your face as your home. The geometry is shared; nothing else is. Checkers is about compulsory captures on open information. This is about what you can work out from a board where the numbers are turned over.
Rules
Deployment. Your home is the two rows in front of your edge, minus the corner cells two seats claim equally — seven cells for six tokens, so the gap you leave is your first decision. Place them in any arrangement; the strengths are hidden from everyone else until a Battle.
Moves. You bank +1 Move at the end of every turn, capped at 2. A Move steps one token to an adjacent cell, or commits it to a Battle. So a turn is two steps of one token, one step each of two tokens, one two-token charge — or nothing, and you bank. Except at the cap: sitting on two Moves you must spend at least one, because the Move you are about to bank would be thrown away. That single clause is most of the game’s tempo. Bursting two Moves drops you to zero and you climb back one turn at a time; the sustainable rate is one action a turn with a Move always in hand.
Friendly tokens can pass through each other — that is what the two-Move step through a friend is — but two tokens never share a cell.
Battle. Move one or two tokens into an occupied cell and you have committed to a Battle. Both sides reveal, and reveal permanently:
- Attack = the attackers’ strengths, plus a Flanking bonus of +1 per spoke edge of the defending hex lying between them. Two attackers on neighbouring cells: +1. With a whole hex between them: +2. From opposite sides: +3, the most the geometry allows.
- Defence = the defender’s strength, +1 if any friendly token stands beside them.
Higher total wins. Ties go to the defender. The losing side loses everything it committed — so a two-token flank that misreads the defender costs two tokens, not one. The winner keeps theirs, and one of them walks into the vacated cell (with two survivors, the attacker chooses which).
Retreat. Before any of that, the defender may spend a Move to slip out to an empty adjacent cell. No Moves, or nowhere to go, and they cannot. A Retreat costs the attacker nothing back — the Moves are spent on the charge either way, and an attacker walks into the ground that was given up. But nothing is revealed. Retreating is how a 0 stays a rumour.
Winning. Be the last player with tokens on the board, or hold the center hex and two of its six neighbours at the start of your turn — which gives the table exactly one round to break the hold.
What the numbers do
Two things are worth noticing before your first game.
Both battle modifiers are public. The flanking bonus and the defender’s +1 are pure geometry — anyone looking at the board computes them exactly. Only the base strengths are secret. That is why a token parked on a forward cell is a real threat regardless of what it is: you are buying the shape, and a 0 in a flanking position reads identically to a 4.
So is the pool. Every token that dies dies in a Battle, and every Battle
turns its participants face-up — so the strengths a player has been forced to
show, subtracted from 0 1 2 2 3 4, are exactly what their face-down tokens can
still be. The demo prints that remainder next to each seat (?01223). Once
somebody’s 4 is spent, the entire table knows nothing they own can beat a 3 head
on. The two 2s exist to blunt that: showing one tells you less than showing any
other number does.
And the Move economy is the real defence. A defender with a Move in hand can
buy their way out of any Battle, however lopsided — so charging one is not a
fight, it is a costed advance that also drains them. A defender with none cannot
refuse. The strong line is therefore to attack the player who just spent, and
the reason to sit at one action a turn is to never be that player. That fell out
of the rules rather than being designed in; a soak run of bot-vs-bot games is
where it showed up, and the bot in src/feint-bot.js now plays it deliberately.
Playing it
solo puts you at seat 1 against bots. hot-seat passes one screen around: a curtain drops between turns so the next player can look without seeing what the last one was holding — including before a defence, since deciding whether to Retreat means looking at your own token. watch is bots only, all cards face up, which is the quickest way to see what the shape of a game looks like.
Click a token to pick it up; marked cells are one Move, ringed cells are the two-Move step through a friend. Click an enemy you are in contact with to open a charge: pick the second attacker and the panel shows the flank, their defence bonus, and what it costs before you commit. rules keeps a summary on screen.
The bot never looks at a face-down strength. It reasons from the pool, from the geometry, and from the fact that a player who pays a Move to charge you usually believes they are winning — which is why it will stand against a bluff and run from a 4.
Reuse
src/feint.js is a headless, framework-free ES module — no DOM, no rendering,
just the law:
createGame({ players, radius, quietLimit })→ live state (board,seats,phase,current,battle,log) plus commands. Every command returns an event ornull, and an illegal one never touches the board, so an untrusted caller can ask and simply be refused.- Setup:
place(hex, value),unplace(hex),autoPlace(rnd),commitSetup(),tray(seat). - Play:
stepsFrom(hex)(one-Move steps and two-Move transits),step(from, to),attackersFor(target),declareAttack(target, attackers), then the defender’sretreat(to)orstand(), thenoccupy(hex)when two attackers survive.endTurn()enforces the use-it-or-lose-it clause;canEndTurn()reports it. - Public reasoning aids, all derivable from the board:
flankFor(target, attackers),defenseBonus(target),canRetreatTo(target),hiddenPool(seat). snapshot()/restore()are a string round-trip.toAct()is the one to render against: during a Battle the player who must act is the defender, not whoever’s turn it is.
src/feint-bot.js is a separate module on top of that public surface only —
createBot(seatId) gives you setup, next, defend and pick, each
returning a command, and applyBotCommand(game, cmd) runs it. It is a worked
example of playing the game from public information, and a fair opponent because
it structurally cannot cheat.
Geometry comes from the hex-grid mechanic;
FACES, homeCells(faceIndex, radius) and seatFaces(players) expose the board
sectioning if you want different seating or a different home shape. radius is
a parameter — but note that a radius-5 board is what makes home exactly seven
cells for six tokens, and radius 4 gives only five.
Gotchas
- “The losing side loses their token” needed a reading. Taken as: the losing side loses every token it committed. That makes a flank a genuine commitment rather than a free +3, and it is the only reading symmetric with “winning side keeps their tokens”.
- A Retreat does not refund the attacker. Moves are spent when the charge is declared, and the attacker takes the vacated cell. Otherwise attacking would be a free way to drain an opponent’s Moves, and the game would be nothing else.
- A two-Move transit lands on an empty cell only. Passing through a friend and straight into a Battle is not offered: it is a strictly worse single-token attack that costs both Moves, and supporting it doubles the size of the charge UI for nothing.
- The thirteen rules have no draw in them, and two cautious players can
circle indefinitely — a Retreat is repeatable as long as you can pay for it. A
table settles that by agreement; a program needs a number, so
quietLimit(default 50 turns with nobody committing to a Battle) calls it a stalemate. PassquietLimit: Infinityfor the rules exactly as written. - Two players is a race, not a brawl. Across 160 bot games, duels were decided by the center hold almost every time and rarely saw more than two Battles; six-seat games went the other way, ending in elimination with thirty Battles behind them. If you want the knife fight, add seats.
src/bundles its own copy ofhex-grid.jsanddemo/bundles a copy of every module it loads — self-contained by contract (ADR-0002). Re-copy after editing any of them.
