1
0
Fork
You've already forked cat
0
No description
  • Go 79.9%
  • HTML 19.7%
  • Dockerfile 0.4%
Nils Israel 1065976403
All checks were successful
CI / test (push) Successful in 43s
CI / docker (push) Successful in 51s
Update fill-wait logic to support filler-only idle lobbies with abort-on-join behavior and countdown resets.
2026年06月12日 19:36:45 +02:00
.forgejo/workflows Add needs: test dependency to Docker workflow in CI pipeline 2026年06月12日 18:14:11 +02:00
cmd Add fill-wait configuration to enable lobby countdown and auto-fill empty seats with bots 2026年06月12日 18:51:44 +02:00
internal Update fill-wait logic to support filler-only idle lobbies with abort-on-join behavior and countdown resets. 2026年06月12日 19:36:45 +02:00
LICENSES Initial commit. 2026年06月12日 11:59:13 +02:00
.dockerignore Add Docker support with multi-stage build, workflow, push to codeberg using Buildah for rootless container builds 2026年06月12日 18:11:45 +02:00
.gitignore Add minimal server support for rules and protocol documentation rendering, player authentication, and high scores tracking. 2026年06月12日 16:11:07 +02:00
CLAUDE.md Add fill-wait configuration to enable lobby countdown and auto-fill empty seats with bots 2026年06月12日 18:51:44 +02:00
Dockerfile Add Docker support with multi-stage build, workflow, push to codeberg using Buildah for rootless container builds 2026年06月12日 18:11:45 +02:00
docs.go Add minimal server support for rules and protocol documentation rendering, player authentication, and high scores tracking. 2026年06月12日 16:11:07 +02:00
go.mod Initial commit. 2026年06月12日 11:59:13 +02:00
mise.toml Initial commit. 2026年06月12日 11:59:13 +02:00
PROTOCOL.md Extend fill-wait behavior to allow filler-only games for idle lobbies, abortable by joining players, with countdown reset. Update tests, protocol, and UI. 2026年06月12日 19:11:59 +02:00
README.md Extend fill-wait behavior to allow filler-only games for idle lobbies, abortable by joining players, with countdown reset. Update tests, protocol, and UI. 2026年06月12日 19:11:59 +02:00
RULES.md Introduce 6 player mode. 2026年06月12日 17:15:39 +02:00

CAT — the bot arena

The last one is cuddled by kittens.

CAT is a card-driven race game for bot-programming events: humans write bots, bots play, everyone watches on the beamer. Four or six players (-players) move their pieces around a board using cards — no dice, no drawing mid-round, and the nasty cards (swaps, splits, backward moves) make it tactical.

The twist of CAT: opposite seats are teams (0&2 vs 1&3 at a 4-player table; 0&3, 1&4 and 2&5 at a 6-player one), but partners may not communicate — and neither may your bots. The only legal signal is the one card you pass your partner each round.

┌────────────┐ TCP :5555, JSON lines ┌────────────┐
│ your bot │ ◄────────────────────────► │ │
└────────────┘ │ cat-server │ HTTP :8080
┌────────────┐ │ │ ◄──────────── beamer
│ any lang │ ◄────────────────────────► │ │ (SSE live board)
└────────────┘ └────────────┘

Quick start

mise install # installs Go (see mise.toml)
go build ./...
# terminal 1: the server (waits for 4 players)
go run ./cmd/cat-server
# terminal 2-5: four bots (the bundled example bot)
go run ./cmd/cat-bot -name alice
go run ./cmd/cat-bot -name bob
go run ./cmd/cat-bot -name carol
go run ./cmd/cat-bot -name dan
# beamer: open http://localhost:8080/

Too few friends around? Fill seats with built-in random bots:

go run ./cmd/cat-server -bots 3 # you provide only 1 bot

Or let the table fill itself when players are slow to arrive: with -fill-wait 1m the lobby waits — each joining player resets the clock — and once a minute passes without a new player, built-in bots take the remaining seats so a game always gets going — even an empty table runs builtin-only games, so the board never sits idle. Unlike -bots, these fillers leave after every game: queued players take the freed chairs first, and only if the table is still short after another wait do the bots return. A game played only by fillers is just a placeholder — the first player to connect aborts it, sits down immediately, and the wait starts over.

Six at the table (three teams of two, 96-space track):

go run ./cmd/cat-server -players 6

Writing a bot

Read PROTOCOL.md — it is a newline-delimited JSON protocol over TCP, usable from any language. The server sends you your hand and a list of all legal moves; your bot just picks one. Minimal viable bot: answer exchange_request with any hand card and move_request with any element of legal.

cmd/cat-bot is a commented Go template; copy it and replace chooseMove/chooseExchange.

Rules of the event:

  • The game is tick-based: you have -turn-timeout (default 3s) per decision. Too slow / invalid ⇒ the server plays a random legal move for you (flagged orange on the beamer — the audience will notice).
  • Crashed bots may reconnect with the same name and resume their seat.
  • Names are registered on first join: the server issues a secret that your bot must store and use to answer the auth challenge on every later join (see PROTOCOL.md "Authentication & highscores"). This protects your seat and your highscores from impersonation.
  • Bots of the same team must not exchange information outside the game.

Server flags

flag default
-tcp :5555 bot/spectator TCP address
-http :8080 visualization address
-turn-timeout 3s thinking time per move
-exchange-timeout 3s thinking time for the partner exchange
-pace 400ms minimum delay between plays, so spectators can follow
-restart-delay 10s pause before the next game starts
-players 4 players at the table: 4 (two teams) or 6 (three teams)
-track-len 16/player track spaces: 64 for 4 players, 96 for 6
-bots 0 seats filled with built-in random bots
-fill-wait 0 lobby wait: when no further player joined for this long, the empty seats are filled with built-in random bots for one game (0 = wait forever)
-reveal-hands off spectators: web view + spectators see hands, seated bots don't (honor system — anyone can open a spectator connection); all: bots too (debugging; spoils the game)
-seed random deterministic games for debugging
-data cat-data.json player registry + persistent highscores (empty = in-memory)
-public-tcp (empty) public host:port bots should connect to, shown on the board (empty = hidden)

Every flag can also be set through the environment: CAT_ plus the flag name uppercased with dashes as underscores (-turn-timeout$CAT_TURN_TIMEOUT, -public-tcp$CAT_PUBLIC_TCP, ...). A flag given on the command line takes precedence over its environment variable.

The server runs a continuous series: when a game ends its players are put at the end of the waiting queue in random order and the seats are refilled from the front of the queue, so any number of bots can rotate through the seats; the score across games is shown on the board. Players connecting while a game is running (or while the table is full) spectate and are queued; each name can be logged in only once.

Across games the server keeps two persistent leaderboards — per player and per team (pair of names) — stored in the -data file, shown on the board page and served at /api/highscores. Built-in random bots are not tracked. If a participant loses their secret, delete their entry from the data file and let them re-register.

Running with Docker

The image is published as codeberg.org/nisrael/cat:latest. All server options are set through the CAT_* environment variables described above (don't quote the values — the quotes would become part of them):

services:cat:image:codeberg.org/nisrael/cat:latestrestart:unless-stoppedports:- "5555:5555"# bots/spectators (TCP, JSON lines)- "8080:8080"# board visualization (HTTP)volumes:- ./data:/data# player registry + highscoresenvironment:# host:port bots should connect to, shown on the board- CAT_PUBLIC_TCP=cat.example.org:5555- CAT_PLAYERS=6- CAT_PACE=200ms

The image's entrypoint is cat-server, so flags work too: a command: list (or arguments after the image name with docker run) is passed straight to the server and overrides the environment. Note that setting command: replaces the image default -data /data/cat-data.json, so keep -data in the list if you want persistent highscores.

Repository layout

  • internal/game — pure rules engine (no I/O), exhaustively tested, including random self-play games (go test ./...)
  • internal/server — TCP protocol, game hub with tick deadlines, HTTP/SSE, embedded visualization (internal/server/web/index.html)
  • cmd/cat-server — the server binary
  • cmd/cat-bot — example bot / template

Rule interpretations

The full rules are in RULES.md, summarized in PROTOCOL.md — and encoded in the legal move list the server hands your bot. The running server renders both at /rules and /protocol. Decisions made where the rules are ambiguous:

  • Each piece may be part of a 7-split at most once.
  • A 7 that wins the game mid-card may leave the remainder unused.
  • A joker may emulate a J even when the J would have no effect (discard).
  • Entering the arrival area is optional when passing the start space (both moves are offered).

License

MIT — see LICENSES/MIT.txt. The project is REUSE compliant: every file carries SPDX copyright and license information.


This project was built with the help of Claude Code.