thoemo/poker (1.8.4)

Published 2026-10-25 15:24:05 +00:00 by elnebuloso

Installation

{
	"repositories": [{
			"type": "composer",
			"url": ""
		}
	]
}
composer config repositories.thoemo composer 
composer require thoemo/poker:1.8.4

About this package

Poker Game Library

thoemo/poker

Poker hand evaluation and a Texas Hold'em round simulation for PHP, built on thoemo/cards. It ranks five-card hands, compares them, and deals and evaluates a complete Hold'em round from the hole cards to the showdown. There are no chips and no betting.

Requirements

  • PHP >= 8.2
  • thoemo/cards ^1.3.1 (installed automatically, brings thoemo/calc)

Installation

The package is served by the Forgejo Composer registry. Add the repository to your composer.json:

"repositories": {
    "packages.link.thoemo.net/thoemo": {
        "type": "composer",
        "url": "https://packages.link.thoemo.net/api/packages/thoemo/composer"
    }
}

Then require the package:

composer require thoemo/poker

The PHP namespace is elnebuloso\Poker; cards come from elnebuloso\Cards (see the thoemo/cards README).

Overview

Class Description
Hand A CardCollection plus the Ranking the ranker attached to it.
Ranking Hand category (ROYAL_FLUSH = 10 … HIGH_CARD = 1), high card and up to four kickers; beats() and ties() compare two rankings.
RankerFactory create(): Ranker with all ten ranking handlers in the right order.
Ranker evaluateHand(Hand $hand): Ranking for a hand of exactly five cards.
RankingHandlerInterface One handler per hand category in RankingHandler\, chained from Royal Flush down to High Card.
RankerException Thrown for a hand without five cards or a misconfigured handler chain.
Game\TexasHoldEmFactory create(string $id, array $players): TexasHoldEm with a new shuffled StandardDeck.
Game\TexasHoldEm Deals and evaluates one complete round for 2 to 10 players in its constructor.
Player A player of a round: ID, seat position, start, flop, turn and final hand, and rank.

Usage

Evaluate a hand

use elnebuloso\Cards\Card;
use elnebuloso\Cards\CardCollection;
use elnebuloso\Poker\Hand;
use elnebuloso\Poker\RankerFactory;

$ranker = (new RankerFactory())->create();

$hand = new Hand();
$hand->setCards(new CardCollection(array_map([Card::class, "createByType"], ["KS", "KH", "KD", "3C", "3D"])));

$ranking = $ranker->evaluateHand($hand); // also stored in $hand->getRanking()
$ranking->getName();  // "Full House"
$ranking->getValue(); // 7 (Ranking::FULL_HOUSE)
$ranking->getDesc();  // "07/13/03/00/00/00": category/high card/kicker 1-4, two digits each

A hand with more or fewer than five cards throws a RankerException (Every hand must contain exactly 5 cards!). Suits never decide between two hands.

Compare hands

$a = new Hand();
$a->setCards(new CardCollection(array_map([Card::class, "createByType"], ["AS", "AD", "KC", "KH", "2D"])));

$b = new Hand();
$b->setCards(new CardCollection(array_map([Card::class, "createByType"], ["AC", "AH", "QS", "QD", "9C"])));

$rankingA = $ranker->evaluateHand($a); // Two Pairs, "03/14/13/02/00/00"
$rankingB = $ranker->evaluateHand($b); // Two Pairs, "03/14/12/09/00/00"

$rankingA->beats($rankingB); // true: kings beat queens as the second pair
$rankingA->ties($rankingB);  // false

To find the best five of more cards, rank every combination and keep the best one:

$best = null;

foreach ($sevenCards->combinations(5) as $fiveCards) {
    $hand = new Hand();
    $hand->setCards($fiveCards);
    $ranker->evaluateHand($hand);

    if ($best === null || $hand->getRanking()->beats($best->getRanking())) {
        $best = $hand;
    }
}

Play a Texas Hold'em round

The whole round happens in the constructor: each player in turn gets two cards from the top of the deck, then one card is burned before each of flop (3 cards), turn and river. Every player's best hand is evaluated for the flop (start hand plus flop), the turn (best five of six) and the river (best five of seven), and players are ranked by their final hand.

use elnebuloso\Poker\Game\TexasHoldEmFactory;

$game = (new TexasHoldEmFactory())->create("game-1", ["Alice", "Bob", "Carol"]);

echo $game->getFlop(), " | ", $game->getTurn(), " | ", $game->getRiver(); // e.g. "3H,5C,KH | 3C | JH"
echo $game->getBoard();                                                   // "3H,5C,KH,3C,JH"

foreach ($game->getPlayersByRank() as $player) {
    printf(
        "%d %s %s -> %s %s\n",
        $player->getRank(),
        $player->getId(),
        $player->getStartHand()->getCards(),
        $player->getFinalHand()->getCards(),
        $player->getFinalHand()->getRanking()->getName(),
    );
}
// e.g. 0 Bob 7S,3D -> 3D,3H,KH,3C,JH Three of a Kind
  • Rank 0 is the winner. Players with equal final hands share a rank and the next rank is skipped (0, 0, 2), so a split pot is every player with rank 0.
  • Player names are used as IDs and as array keys: getPlayers() returns the players by seat position, getPlayersByRank() by rank, getPlayerById("Bob") one player or null.
  • getCardsBurnedBeforeFlop(), getCardsBurnedBeforeTurn() and getCardsBurnedBeforeRiver() return the burned cards.

new TexasHoldEm($id, $deck, $players) takes a StandardDeck of your own. The deck must be shuffled and there must be 2 to 10 players, otherwise an InvalidArgumentException is thrown. To replay a fixed card order (e.g. in a test), mark an empty collection as shuffled, then add a complete deck in dealing order:

use elnebuloso\Cards\StandardDeck;
use elnebuloso\Poker\Game\TexasHoldEm;

$order = "AC,KD,AS,QH,2C,3H,5D,8C,2D,9S,2H,JH,2S,3C,3D,3S,4C,4D,4H,4S,5C,5H,5S,6C,6D,6H,"
    . "6S,7C,7D,7H,7S,8D,8H,8S,9C,9D,9H,10C,10D,10H,10S,JC,JD,JS,KC,KH,KS,QC,QD,QS,AD,AH";

$cards = new CardCollection();
$cards->shuffle();
$cards->addCards(array_map([Card::class, "createByType"], explode(",", $order)));

$game = new TexasHoldEm("game-2", new StandardDeck($cards), ["Alice", "Bob"]);
// Alice: AC,KD  Bob: AS,QH  board: 3H,5D,8C,9S,JH
$game->getPlayerById("Alice")->getFinalHand()->getRanking()->getDesc(); // "01/14/13/11/09/08"
$game->getPlayerById("Alice")->getRank();                               // 0, wins on the king

Background on the hand-ranking rules and the round flow, in German: features/001-overview/PLAN.md and features/003-regelkonformitaet/PLAN.md in the repository.

Development

Tests and tools run through redo, the Docker-based CI wrapper; build.yml holds the project's redo properties (default PHP version 8.2).

redo composer:install phpunit                     # PHPUnit with the PHP version from build.yml
redo composer:install phpunit -Dphp_version=8.4   # PHPUnit with another PHP version
redo phplint -Dphp_version=8.4

The coverage report is written to build/reports/phpunit/all/coverage/html.

Releases

Every push to main runs the GitLab pipeline (phplint, prettier, composer:validate:require and PHPUnit on PHP 8.2 to 8.5), raises the patch version in VERSION and uploads that version to the Forgejo registry. The released archive contains only README.md, composer.json and src/.

Dependencies

Dependencies

ID Version
php >=8.2
thoemo/cards ^1.3.1
Details
Composer
2026-10-25 15:24:05 +00:00
2
2026-10-25 15:26:12 +00:00
proprietary
18 KiB
Assets (1)
Versions (5) View all
1.9.1 2026-10-25
1.8.5 2026-10-25
1.8.4 2026-10-25
1.8.3 2026-10-25
1.8.2 2026-10-25