garak / bridge
A PHP library to manage bridge card games
v0.6.0
2026-04-28 07:20 UTC
Requires
- php: ^8.2
- doctrine/collections: ^2.0 || ^3.0
- garak/card: ^0.8
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^11.5 || ^12.5 || ^13.1
This package is auto-updated.
Last update: 2026-05-19 07:24:39 UTC
README
Introduction
This library offers some PHP classes useful for creating a Bridge card game:
- Game (needs to be extended)
- Player (needs to be extended)
- Table
- Turn
- Hand
- Side
- Wins
Installation
Run composer require garak/bridge.
Usage
Here is an example of the first turn of a game:
<?php require 'vendor/autoload.php'; use App\Game; // this is your Game class, extending \Garak\Bridge\Game use App\Player; // this is your Player class, extending \Garak\Bridge\Player use Garak\Bridge\Hand; use Garak\Bridge\Side; use Garak\Bridge\Table; use Garak\Bridge\Turn; $north = Hand::createFromString('6s,4h,3s,Td,6c,3d,3h,Kc,Qc,Tc,7d,2c,6d'); $east = Hand::createFromString('9d,Jh,5s,8c,Ks,4s,5h,4d,8s,Jc,2d,2s,Qs'); $south = Hand::createFromString('7h,Kd,Js,2h,Th,Qh,7s,Ac,3c,Ad,7c,9s,6h'); $west = Hand::createFromString('9h,Ts,5c,Jd,9c,As,8h,Ah,Kh,8d,4c,Qd,5d'); $table = new Table($north, $east, $south, $west); $game = new Game($table, startingSide: new Side('N')); $game->join(new Player('John Doe'), new Side('N')); $game->join(new Player('Will Riker'), new Side('E')); $game->join(new Player('Yoda'), new Side('S')); $game->join(new Player('Peter Venkman'), new Side('W')); $game->addTurn(new Turn($game, 1, Card::fromRankSuit('6s'))); $game->addTurn(new Turn($game, 2, Card::fromRankSuit('4s'))); $game->addTurn(new Turn($game, 3, Card::fromRankSuit('7s'))); $game->addTurn(new Turn($game, 4, Card::fromRankSuit('Ts'))); echo $game->getWins()->getEastWest(); // will output "1", since West won the first turn
Auction
The library exposes an abstract Garak\Bridge\Auction class that represents one auction entry (a bid or a pass).
Key points
- The
Auctionconstructor takes theGame, the auctionorder, an optional numericvalue(bid level), and an optionalGarak\Card\Suitfor trump. - A
nullvalue is treated as a pass. - When an
Auctionis created, it records the bidding side, advances to the next side, and callsGame::addAuction().
Implement a concrete auction class
Auction is abstract, so your application must provide a concrete implementation:
<?php namespace App; use Garak\Bridge\Auction; final class Contract extends Auction { public function __toString(): string { return (string) $this->getValue() . ($this->getTrump() ? (string) $this->getTrump() : ''); } }
Use auctions in a game
<?php use App\Contract; use App\Game; // your concrete class extending Garak\Bridge\Game use App\Player; // your concrete class extending Garak\Bridge\Player use Garak\Bridge\Side; use Garak\Card\Suit; $table = /* create Table as in the Usage example above */; $game = new Game($table, startingSide: new Side('N')); $game->join(new Player('North'), new Side('N')); $game->join(new Player('East'), new Side('E')); $game->join(new Player('South'), new Side('S')); $game->join(new Player('West'), new Side('W')); new Contract($game, 1, 1, new Suit('d')); // 1d from North new Contract($game, 2, null, null); // pass from East new Contract($game, 3, null, null); // pass from South new Contract($game, 4, null, null); // pass from West (auction ends) $contract = $game->getAuction(); // last valid auction, or null $trump = $game->getTrump(); // Suit|null $dummy = $game->getDummySide(); // Side|null if (null !== $contract) { echo (string) $contract; // e.g. "1d" }
Auction ordering rules (summary)
- Same trump suit: higher
valuewins. - Different suits: suit ranking is used (club < diamond < heart < spade < no-trump).
Game::addAuction()throwsDomainExceptionwhen a bid is not greater than the previous valid one.- The auction is considered ended after three consecutive passes after at least one non-pass bid. If everyone passes from the start, there is no winning contract.