A collection of tools and study materials for the card game of contract bridge. The projects here span analysis (working out the best line in a deal), study (managing and sharing flashcards), and the day-to-day logistics of playing at a local club.
Each project lives in its own top-level directory. Performance-sensitive analysis is written in Rust; most tooling and automation in Python, with browser-side userscripts in JavaScript.
Note that most of the projects are currently only dreamed, and not yet started. This is a personal, hobby repository. Projects arrive over time and graduate from "planned" to "working" as they're built.
suit_combinations/ — Rust
Given a single suit held between two hands, work out the line of play that maximizes the expected number of tricks (or the chance of a target number of tricks) against best defense. This is the classic "how do I play this suit" question, answered exhaustively rather than from memory.
The aim is both a library and a small command-line tool: describe the cards held in each hand, and get back the optimal line together with its trick expectation. Unlike existing tools such as https://bridge.esmarkkappel.dk/main/main.html, the goal is to provide a brief intuitive explanation alongside the best line of play.
double_dummy/ — Rust
A double-dummy solver evaluates a complete deal — all four hands visible — to determine, with perfect play by everyone, exactly how many tricks each side can take in each strain. It's the ground truth against which real-world bidding and play are measured.
The goal is a correct, fast solver usable as a library by the other projects here (notably session analysis below), plus a command-line entry point for one-off deals.
Related work: https://mirgo2.co.uk/bridgesolver/ -> https://dds.bridgewebs.com/bridgesolver/upload.htm
anki/ — Python
Utilities for managing a personal bridge study deck in Anki, treating the flashcards as version-controlled source rather than as opaque state inside the Anki app. The intent is to author, organize, and review cards from plain-text sources, and to keep the deck reproducible.
TBD: Some flashcards cannot be published (see below). How exactly is the source of truth synced?
flashcards/ — content
The subset of the study deck that is safe to share publicly: original cards authored from scratch, free of any third-party copyrighted material. The full personal deck includes cards built around others' books and lessons for private self-study; those are deliberately kept out of this repository, and only the clean, original subset is published here.
See licensing below — this content carries a different license from the code.
club_sites/ — JavaScript (userscripts)
Per-club userscripts that add personal conveniences to a club's website — the kind of repetitive logistics that are tedious by hand. Each club lives in its own subdirectory.
club_sites/palo_alto/— a Tampermonkey userscript for the Palo Alto Bridge Center reservations page: a remembered identity (prefilled name, email, and playing direction), the game list expanded by default, and a guard against accidentally booking a limited game such as EZ Bridge.
convention_cards/ — Python
Merges a convention card PDF (as downloaded from BridgeWinners) with a fold-over strip of personal bidding reminders, producing a single print-ready PDF: the card on the bottom 8.5", the reminders on the top 2.5" so they fold behind the card and tuck away in a card holder.
The card content and reminders themselves are personal/partnership data and kept in a private companion repository — only the generation tool lives here.
A Streamlit webapp wrapping the tool is deployed on Render at
https://ruffdraft.onrender.com. Render deploys from
convention_cards/requirements.txt, a uv export snapshot rather than the
workspace's own uv.lock (Streamlit Community Cloud was the original hosting
choice, but its GitHub OAuth integration requires write access to every public
repo on the account; Render's GitHub App can be scoped to this repo alone).
Regenerate that file after any dependency change:
uv export --format requirements.txt --package convention-cards --no-dev \
--no-hashes -o convention_cards/requirements.txt
system_notes/ — Python
Renders a partnership's system notes into three outputs from one source: a
self-contained HTML page for the screen, a two-column US-letter PDF whose table
of contents and cross-references carry page numbers, and a hard-wrapped
plain-text rendering for pasting into email. The notes are a long, deeply nested
bidding and carding agreement written in Pandoc Markdown. See
system_notes/spec.md for the design.
The notes themselves are partnership agreements and live in the private
companion repository; only the renderer and an AI-drafted sample document
(system_notes/fixture/notes.md, unreviewed and not a workable system) live
here.
Prerequisites beyond uv are listed in system_notes/Brewfile; install them
with brew bundle --file system_notes/Brewfile. Run that as the account that
renders, since a font cask installs into the running account's
~/Library/Fonts. Render, from the repo root, with
uv run --project . python -m system_notes.render_notes path/to/notes.md
which writes notes.html, notes.pdf, and notes.txt beside the input. A
render fails if the PDF embeds a font the stylesheet never asked for, which
means some text fell back to whatever the machine happened to have.
The Markdown is ordinary Pandoc Markdown plus a few conventions:
- Bids are written plainly —
4S,2NT,3C— and the renderer draws the suit symbol. Notrump is alwaysNT; a bareNafter a level fails the render. !S !H !D !Cask for a suit symbol outside a bid, as ina !H lead.- A shorthand compound ending in the major-suit placeholder
M—OM,W2M,4cM— gets thatMbolded automatically; ordinary words (IMP,BAM) and the minor's lowercasemstay plain. Write compounds apart, as4cM & 5+m, so each one ends its own word. - A link to a heading id —
[Stayman](#stayman)— carries that heading's page number in print, as "Stayman (p. 4)". Left empty,[](#stayman)takes the heading's own title for its text. Give headings explicit ids (## Stayman {#stayman}) so references survive retitling. - Card-count ranges such as
15–17and slashed shorthand such asP/Cnever break across lines; other hyphenated and slashed words wrap normally. - The YAML block needs a
title. Heading levels must not skip (no###directly under a#), and every internal link must point at a heading that exists.
After an intentional change to the rendering, run
uv run --project . python -m system_notes.update_goldens and review the golden
diff.
session_analysis/ — Python
A longer-term, still-speculative idea: turn a night of bridge into something to learn from, automatically. The envisioned pipeline scans a handwritten scoresheet, fetches the official hand records for the session, and compares the contracts and results reached against the double-dummy analysis — surfacing the deals where the table diverged most from optimal play.
This depends on the double-dummy solver and is the least defined of the projects; treat it as a direction, not a commitment.
practice/squeezes/ — Python + TypeScript
A BridgeMaster-style declarer-play trainer for deep-diving one technique at a
time, starting with squeeze play. The robot defenders' hands are deliberately
not fixed: the engine tracks every layout consistent with the play so far, so
only a line that handles all of them succeeds — the correct technique, not a
memorized layout. Currently a prototype under practice/squeezes/scratch/; see
practice/squeezes/spec.md for the design.
This repository is dual-licensed to reflect the difference between code and educational content:
- Code is licensed under the MIT License.
- The publishable flashcard content in
flashcards/is licensed under Creative Commons Attribution 4.0 International (CC-BY-4.0) — free to use, share, and adapt, including commercially, as long as you give credit.
Copyright 2026 Ilya Sherman (ishermandom@).