No description
Find a file
2026-05-06 14:30:39 +02:00
bot refact: remove python & c++ bindings 2026-04-04 20:53:09 +02:00
clients fix(web client): normalize cards style 2026-05-06 14:30:39 +02:00
doc fix(doc): rules: opponent's big jan != return jan 2026-04-18 16:21:38 +02:00
server feat: add email verification & password reset link 2026-05-03 21:39:07 +02:00
spiel_bot refact: remove python & c++ bindings 2026-04-04 20:53:09 +02:00
store fix(store): check color on opponent corner hit 2026-05-02 21:31:23 +02:00
.envrc devenv with rust 2022-11-30 16:15:03 +01:00
.gitignore fix: integrate multiplayer 2026-04-23 20:54:52 +02:00
Cargo.lock feat: add email verification & password reset link 2026-05-03 21:39:07 +02:00
Cargo.toml chore: remove old web-game & web-user-portal crates 2026-05-02 11:11:39 +02:00
devenv.lock refact: migrate sqlx + sqlite to tokio-postgresql 2026-04-22 21:52:29 +02:00
devenv.nix feat: add email verification & password reset link 2026-05-03 21:39:07 +02:00
devenv.yaml chore(devenv): pin cmake package (for burn-rl > sdl2) 2026-02-13 20:58:24 +01:00
flake.nix dev env via flake.nix 2022-12-09 21:19:18 +01:00
justfile fix(web client): exit dice used 2026-05-02 21:31:23 +02:00
LICENSE Initial commit 2022-11-30 15:37:20 +01:00
README.md chore: remove old web-game & web-user-portal crates 2026-05-02 11:11:39 +02:00

Trictrac

This is a game of Trictrac rust implementation.

The project is still on its early stages.

Usage

Install devenv, start a devenv shell devenv shell, and run the following commands.

# Run the relay server
just build-relay
just run-relay  # listens on :8080

# Run the game (separate terminal)
just dev

Open two browser windows at http://127.0.0.1:9091. In one, create a room; in the other, join with the same room name.

Playing with the cli against the 'random' bot: cargo run --bin=client_cli -- --bot random

Roadmap

  • rules
  • command line interface
  • basic bot (random play)
  • web client (in progress)
  • network game (in progress)
  • AI bot

Code structure

  • game rules and game state are implemented in the store/ folder.
  • the command-line application is implemented in clients/cli/; it allows you to play against a bot, or to have two bots play against each other
  • the bots algorithms and the training of their models are implemented in the bot/ and spiel_bot folders.

store package

The game state is defined by the GameState struct in store/src/game.rs. The to_string_id() method allows this state to be encoded compactly in a string (without the played moves history). For a more readable textual representation, the fmt::Display trait is implemented.

clients/cli package

clients/cli/src/game_runner.rs contains the logic to make two bots play against each other.

bot package

  • bot/src/strategy/default.rs contains the code for a basic bot strategy: it determines the list of valid moves (using the get_possible_moves_sequences method of store::MoveRules) and simply executes the first move in the list.
  • bot/src/strategy/dqnburn.rs is another bot strategy that uses a reinforcement learning trained model with the DQN algorithm via the burn library (https://burn.dev/).
  • bot/scripts/trains.sh allows you to train agents using different algorithms (DQN, PPO, SAC).

multiplayer game

Packages "clients/backbone-lib", "clients/web/game", "server/protocol", "server/relay-server" are a Leptos-optimized adaptation of the macroquad-based Carbonfreezer/multiplayer project. It is a multiplayer game system in Rust targeting browser-based board games compiled as WASM. The original project used Macroquad with a polling-based transport layer; this version replaces that with an async session API built for Leptos.

The system consists of:

  • A relay server (Axum/Tokio) that routes messages between players and manages rooms, without knowing anything about game rules.
  • A backbone library that handles WebSocket connection, handshake, and message routing, exposing an async API to the game frontend.
  • Game-specific backend logic implementing the BackEndArchitecture trait, which runs only on the hosting client.
  • A Leptos frontend that connects to a session and reacts to state updates.

There is no dedicated game server. One of the players acts as the host: their browser runs the game backend locally. The relay server only forwards messages — it never touches game state.

┌─────────────────────────────────────────────────────────────┐
│                        Host Client                          │
│  ┌─────────────┐    ┌──────────────────┐    ┌────────────┐  │
│  │  Leptos UI  │◄──►│  GameSession     │◄──►│  Backend   │  │
│  └─────────────┘    └────────┬─────────┘    └────────────┘  │
└───────────────────────────── │ ────────────────────────────┘
                                │  WebSocket
                         ┌──────▼──────┐
                         │ Relay Server│
                         └──────┬──────┘
                                │  WebSocket
┌───────────────────────────────│────────────────────────────┐
│  ┌─────────────┐    ┌─────────▼────────┐                   │
│  │  Leptos UI  │◄──►│  GameSession     │  (no backend)     │
│  └─────────────┘    └──────────────────┘                   │
│                        Remote Client                        │
└─────────────────────────────────────────────────────────────┘

Data flow

  • Actions (e.g. "place stone at B3") flow from the UI to the host backend via GameSession::send_action().
  • State updates flow back as ViewStateUpdate::Full (full snapshot, on join or reset) or ViewStateUpdate::Incremental (delta, for animations).
  • Timers are managed by the host's background task (wall-clock, no polling required from the game).

backbone-lib session API

The key design choice: backbone-lib owns a background async task per session. The Leptos app never drives a loop — it just awaits on events.

Workspace

server/protocol

Shared message-type constants and the JoinRequest struct used during the WebSocket handshake.

server/relay-server

Listens on port 8080. Loads GameConfig.json on startup to know which games exist and their player limits:

[{ "name": "trictrac", "max_players": 10 }]

Games can be added at runtime via the /reload endpoint. /enlist lists active rooms. A watchdog cleans up inactive rooms every 20 minutes.

For production, put it behind a reverse proxy with SSL (the browser requires wss:// on HTTPS pages). Example Caddy config:

your-domain.com {
    handle_path /api/* {
        reverse_proxy localhost:8080
    }
    file_server
}

clients/backbone-lib

Modules:

Module Purpose
session GameSession, connect(), SessionEvent, RoomConfig
host Background async task for the hosting client (drives BackEndArchitecture, manages timers)
client Background async task for non-hosting clients
protocol Wire encoding/decoding helpers (postcard + message-type bytes)
platform spawn_task / sleep_ms abstractions (WASM: spawn_local + gloo-timers; native: thread + thread::sleep)
traits BackEndArchitecture, BackendCommand, ViewStateUpdate, SerializationCap