- Java 100%
| build/libs | ||
| gradle/wrapper | ||
| src | ||
| .gitignore | ||
| build.gradle | ||
| CLAUDE.md | ||
| gradlew | ||
| gradlew.bat | ||
| Opening1M.lin | ||
| README.md | ||
| settings.gradle | ||
| test.lin | ||
BridgeAnalyzer
A Java double-dummy analyzer for the card game of bridge. Given a complete deal (all 52 cards distributed among North, East, South and West), it computes the optimal number of tricks each side can take for every trump suit and every declarer — assuming perfect play by all four hands.
The result is the classic double-dummy table: for each trump strain (S, H, D, C, NT) and each declarer (N, E, S, W) it reports how many tricks the declaring side can take. When North and South get the same result the row is effectively the same, but East and West may differ depending on who is declarer — e.g. East can take 8 tricks in no-trump while West only takes 7.
Requirements
- JDK 17 or later (the code uses records, switch expressions and
Duration.toXxxPart()). It builds and runs fine on modern JDKs such as 21 or 25. - No other dependencies — everything is fetched by the Gradle wrapper.
Building
The project uses Gradle 9.2.1 via the included wrapper, so you do not need a local Gradle install.
# Compile and run the tests
./gradlew build
# Compile only
./gradlew compileJava
# Run the test suite
./gradlew test
# Run a single test
./gradlew test --tests "com.hoddmimes.bridgeanalyzer.SolverTest.testMethodName"
# Remove build output
./gradlew clean
On Windows use gradlew.bat instead of ./gradlew.
Running
The main class is com.hoddmimes.bridgeanalyzer.cli.Main.
Via the Gradle application plugin
Pass program arguments with --args:
# Analyze a freshly generated random deal
./gradlew run
# Analyze board 1 from a LIN file, NT only, with West as declarer
./gradlew run --args="--file=test.lin --board=1 --trump=NT --declarer=W"
Via a runnable JAR
The jar task produces an executable JAR (the manifest's Main-Class is set):
./gradlew jar
java -jar build/libs/BridgeAnalyzer-1.0-SNAPSHOT.jar --file=test.lin
Via a self-contained "fat" JAR
The fatJar task bundles the compiled classes together with everything on the
runtime classpath into a single executable JAR (classifier -all). The project
currently has no runtime dependencies, so today this contains the same classes
as the plain jar, but it will automatically include any dependency you add later:
./gradlew fatJar
java -jar build/libs/BridgeAnalyzer-1.0-SNAPSHOT-all.jar --file=test.lin
Program parameters
All options are passed as --name=value flags. Unknown flags are ignored; any
flag given without =value is treated as a boolean true. If no input file is
given, a single random deal is generated and analyzed.
| Option | Values | Default | Description |
|---|---|---|---|
--file=PATH |
path to a .lin file |
(none) | Read one or more deals from a LIN file. If omitted, a random deal is generated. |
--board=N |
integer | (all boards) | Only analyze the board with this number. Requires --file. Exits with an error if the board is not found. |
--trump=STRAIN |
S, H, D, C, NT |
(all strains) | Restrict analysis to a single trump strain instead of all five. |
--declarer=DIR |
N, E, S, W |
(all four) | Restrict analysis to a single declarer instead of all four. |
--solver=NAME |
alphabeta, bruteforce |
alphabeta |
Choose the search engine. alphabeta uses alpha-beta pruning (fast); bruteforce searches exhaustively (slow, mainly for verification). |
Examples
# Full double-dummy table for a random deal (all strains, all declarers)
./gradlew run
# Every board in a file, all strains/declarers, alpha-beta solver
./gradlew run --args="--file=test.lin"
# Just board 3, hearts as trump
./gradlew run --args="--file=test.lin --board=3 --trump=H"
# Verify the fast solver against the brute-force solver on one board
./gradlew run --args="--file=test.lin --board=1 --solver=bruteforce"
Input format (LIN)
Deals are read from LIN files, the format used by sites such as Bridge Base
Online. Each non-empty line is a pipe-separated list of tag|value pairs. The
parser understands the following tags:
qx— board identifier (e.g.o1,c3); the trailing number sets the board numbermd— the deal itself: a leading dealer digit (1=S,2=W,3=N,4=E) followed by three comma-separated hands (South, West, North); East is inferred as the remaining cardsah— board name / titlesv— vulnerability
Other tags are ignored. A sample file, test.lin, is included in the repository:
qx|o1|md|3SA74HKJ975DT8C842,ST853HT4DJ9CAKQJ5,SKQJ9HQ862DQ54C97|rh||ah|Bricka 1|sv|0|pg||
Output
For each analyzed deal the program prints:
- The board number and name.
- The four hands.
- A table with one column per trump strain and one row per declarer, showing the number of tricks the declaring side can take with optimal play.
- The wall-clock solve time for that board.
Project layout
src/main/java/com/hoddmimes/bridgeanalyzer/
model/ Card, Rank, Suit, Direction, Trump
game/ Hand, Deal, Trick, GameState
solver/ Solver, SolverFactory, AlphaBetaSolver, BruteForceSolver, TranspositionTable
cli/ Main, LinParser, DealGenerator
src/test/java/... JUnit 5 tests