Skip to content

Playing

This page covers what you can play and how each option behaves, as implemented in src/core/formats.js, src/core/match.js, src/server/lobby.js, src/server/actor.js and the client in public/.

Formats and match lengths

The server and the browser import one list, so they cannot disagree about what is on offer (MATCH_LENGTHS in src/core/formats.js):

lengthlabelspeed pool
11 pointbullet
33 pointsblitz
55 pointsblitz
77 pointsrapid
99 pointsrapid
11, 13, 15, 21n pointslong
0money gamemoney

The lobby selects 3 points by default. If a client asks for a length that is not in the list, the server uses 3.

In match play the first player to reach the match length wins. In a money game (length 0) there is no target score. Points accumulate game by game until someone leaves or forfeits.

Clocks

Backgammon clocks use a reserve plus a per-move delay, not the chess-style increment. Each time you have to act, the first few seconds are free. Only the time beyond the delay comes off your reserve. That is what lets you play routine moves without rushing, especially on a phone. The delay is deliberately generous for that reason.

There are three modes (timeControl() in src/core/formats.js). relaxed doubles both numbers. casual has no clock at all.

speed poolstandardrelaxed
bullet (1 point)90 s reserve + 8 s delay180 s + 16 s
blitz (3, 5)150 s + 10 s300 s + 20 s
rapid (7, 9)240 s + 12 s480 s + 24 s
long (11–21)420 s + 12 s840 s + 24 s
money240 s + 12 s480 s + 24 s

The lobby shows these values rounded to whole minutes (for example "2m + 8s delay" for bullet), because clockLabel() rounds the reserve.

How the clock runs (src/server/actor.js):

  • The clock runs against whoever the match is waiting on. Normally that is the player on turn. After a double, it is the player who has to take or pass. After a resignation offer, it is the player who has to answer.
  • When you act, the server charges max(0, elapsed − delay) to your reserve.
  • A timer fires at reserve + delay. If it fires, you forfeit the match. This is recorded as a forfeit event with reason time in the signed log, not handled off to the side.
  • Bots are never on the clock.
  • No clock runs between games, while the two browsers exchange the reveals that seed the next game.

Opponents

Online: open seeks

"Find an opponent" posts a seek to the public list. The lobby works like Lichess's seek list, not a hidden matchmaking queue. Everything on offer is visible, and anyone can take anything. The code comments explain why: with ten people online, a rating-banded queue finds nobody, but a visible list finds a game.

If someone has already posted a public seek with the same match length and the same clock mode, posting yours pairs you with them straight away. Otherwise your seek waits in the list until someone joins it, you cancel it, or your connection closes.

Online: private challenges

"Private link" posts a seek that does not appear in the public list. The client shows a link of the form https://<host>/#c=<id>. Whoever opens the link joins that exact challenge.

Whoever accepts a challenge accepts its terms

Match length, clock, and the money-play agreements (beavers, automatic doubles) all come from the player who posted the challenge.

Bots

There are three difficulties (DIFFICULTIES in src/core/formats.js, behaviour in src/server/opponent.js). They differ in how deeply they search, not just in how much noise they add:

tiersearchdeliberate mistakescube
easy0-ply heuristicplays a worse move 55% of the time, picked from up to 8 places down the rankingloose: doubles too early and too often, takes too much
mediumheuristic ranking, then 1-ply lookahead over the top 812% of the time, up to 3 places downbasic
hardthe strongest engine installed: BGBlitz if configured, otherwise gnubg, otherwise the built-in evaluatornoneasks the engine

Details that are easy to miss:

  • Bot games are never rated. A fixed-strength opponent is a training partner, and a ladder that people can farm against one stops meaning anything.
  • The bot's seat is chosen at random.
  • When a bot faces a double, it compares its winning chances with the take point from the match equity table at the current score, not a flat 25%. In money play it uses 25%.
  • The bot answers resignation offers. It turns down a single-game resignation when a gammon looks likely, and a gammon resignation when a backgammon looks likely.
  • Bots never beaver.
  • If the hard bot's engine fails mid-game, the bot falls back to the heuristic rather than stalling the match.

/healthzbotEngine shows which engine hard actually got. Check it after deploying, or you may be serving a "hard" bot that is really the built-in heuristic.

Two players, one device

This is a real match on the real server, not an offline mode. The page opens a second WebSocket with a throwaway key and its own reveal chain (public/hotseat.js). That second connection accepts a private challenge posted by the first. The match uses the same dice protocol and produces the same signed record as any other.

Local matches are always unrated, because you would be playing yourself, and always use the casual clock (no clock).

By default the board does not turn round between turns. You sit across the device from each other and each read it from your own side, the way a real board works. If you really are passing a phone back and forth, turn on the "turn the board between turns" setting.

Leaving the page ends a local match

The second player's connection only exists inside the page. When the page is hidden (pagehide), the client abandons the match, because a reload would otherwise leave you in a match whose opponent can never come back.

The doubling cube

The cube follows the standard rules, with the options listed below (src/core/match.js).

  • Who may double: the player on roll, before rolling, if the cube is in the centre or they own it (mayDouble).
  • Take: the cube value doubles and ownership passes to the player who took.
  • Pass: the doubler wins the game at the stake before the double.
  • There is no cap on the cube value in match play.

Crawford

Crawford is always on in match play. The first game played when either player is one point from winning the match is the Crawford game. The cube is dead for exactly that one game. After it (post-Crawford) the cube is live again.

Jacoby

The Jacoby rule is always on in money games and never applies in match play. Until someone turns the cube, a gammon or backgammon counts as a single game. This also applies to an accepted resignation: resigning a gammon with the cube still in the centre scores one point.

Beavers and raccoons (money play, by agreement)

Beavers are only available in money games, and only when the player who posted the challenge ticked "beavers". The option is hidden for match lengths other than money.

  • When doubled, you may beaver: take and immediately redouble, keeping the cube on your side. Now the original doubler has to take or pass.
  • The original doubler may redouble again (a raccoon). The code allows two redoubles on a single offer (beaverCount < 2).
  • Whoever drops loses the stake as it stood before the last redouble, not the redoubled value. So after a beaver, the original doubler passing loses the value of the cube they offered, not the beavered value.

Automatic doubles (money play, by agreement)

If the poster ticked "auto doubles", every tied opening roll turns the cube once, the cube stays in the centre, and the opening is re-rolled. The client sends a cap of 2 automatic doubles when the box is ticked. The server accepts any cap from 0 to 3.

The dice derivation already skips ties to produce a valid opening roll. The number of skipped derivations is the number of ties, so the verifier can re-derive it.

Resigning is an offer

Under the rules, a resignation is an offer that your opponent may refuse. gammonchain implements it that way (_resign, _resignAccept, _resignDecline).

  • You can offer to resign a single game, a gammon or a backgammon, worth 1, 2 or 3 points times the cube. You can offer while it is your turn to roll or move, and so can your opponent during your turn.
  • Your opponent accepts (the game ends at that value) or declines. Declining costs nothing and play resumes in exactly the phase that was interrupted.

This matters more than it sounds. Offering a single game when you are about to be gammoned is a way to save a point, and your opponent is entitled to say no.

Resigning a game is not leaving the match

"Resign" offers to end the current game. Giving up the whole match is a separate action, abandon. It forfeits the match immediately and still produces a complete, verifiable record with reason resigned.

Forfeits, reconnects and abandonment

situationwhat happens
you reload, lock your phone, or change networksyou come back into the same match: same board, clock and reveal chain. Only the holder of your key can resume your seat
your connection drops and you do not returnthe opponent is told, and you forfeit after a 90-second grace period (DEFAULT_GRACE_MS)
a player never signs the match headerthe match is called off after 60 seconds (HANDSHAKE_LIMIT_MS)
nobody does anything for 30 minutesthe match is shut down as abandoned by both players (IDLE_LIMIT_MS)

A forfeit awards the winner exactly the points still needed to win the match, or the current cube value in a money game. It is a real event in the signed log, so a replay reproduces the final score and the record shows that the win came from a forfeit.

Some endings produce no record

If a match is called off before any event was played (handshake timeout, or a disconnect before the first move), or if it expires after 30 minutes of total inactivity, the actor shuts down with an aborted message. No record is written and nothing is rated. A server restart does the same to every live match.

Speed features

  • Forced moves play themselves on the server. When a roll leaves exactly one legal play (including no play at all), the server plays it without waiting for you.
  • The client applies your moves optimistically. Every action carries the event index it was decided on (expect). If the state has already moved on, for example because the server auto-played a forced move, the stale action is dropped silently instead of producing an error.
  • Keyboard shortcuts on a laptop: r roll, d double, t take, p pass, u undo.
  • Click the dice to swap which die a tap plays first, for that move only.

Settings

Settings are stored per browser in localStorage (key gammonchain.settings.v1) and apply immediately. This table lists every setting defined in public/settings.js, with its default:

groupsettingdefaultnotes
boardskinwalnutwalnut, slate or ivory. A skin themes the whole app, not only the board
boardhome board on the rightonwhich direction your checkers travel
boardpoint numberson
boardpip countson
help while playinghighlight checkers you can moveonoff is how a physical board plays
help while playinghighlight where a checker can goon
help while playingone tap moves when there is no choiceoffoff: every tap shows where the checker can go. On: a tap moves at once when there is only one destination, which changes with the position
help while playingplay out a forced remainderonwhen only one way to finish the roll is left, take it
two players, one deviceturn the board between turnsoffsee Two players, one device
paceroll automatically on my turnoffthe cube is still yours to turn first
paceroll delay400 msrange 0–2000 ms in steps of 100. Only shown when auto-roll is on
paceconfirm before a play is sentona completed play waits for a second tap
pacewhen either die reaches a point, playthe higher die firstor the lower die first. The in-game swap flips this for one move

"Reset to defaults" in the settings panel clears the stored values.

Rules engine, fairness protocol, verifier, analysis and worker: MIT. Server and client: AGPL-3.0-or-later.