NAME
Game::Mahjong::Decompose - every way a hand is complete, and what it waits for
VERSION
Version 0.01
SYNOPSIS
my $hand = Game::Mahjong::Hand->from_notation('11122233344455m');
my @splits = Game::Mahjong::Decompose::decompose($hand, $winning_kind);
# four of them: three pungs and a chow... see t/06-decompose.t
Game::Mahjong::Decompose::is_complete($hand); # 1
my @waits = Game::Mahjong::Decompose::waits($thirteen); # the kinds that complete it
DESCRIPTION
The decomposer is C (mahjong_decompose.c, its interface published in include/mahjong_decompose.h), because the bot asks it for every candidate discard and the scorer for every winning hand. This module is its Perl face: it hands the C a hand's concealed counts and meld count, and puts the hand's melds back beside the concealed sets the C found.
Every split, not the first
A hand can be complete more than one way. 111222333m is three pungs or three chows, and the scoring rules let the winner take the higher (3.9.1.5, "the High-versus-Low Principle"). So decompose returns every split and the scorer chooses.
A split
{
form => 'standard' | 'seven_pairs' | 'thirteen_orphans'
| 'honours_knitted' | 'knitted_straight',
sets => [ { kind => 'chow' | 'pung' | 'kong' | 'knit',
tiles => [kinds], concealed => 0 | 1, melded => 0 | 1 }, ... ],
pair => kind | undef,
singles => [kinds],
placement => { in => 'set' | 'pair' | 'single' | undef, index => n,
wait => 'edge' | 'closed' | 'two_sided' | 'pair' | 'pung'
| 'single' | 'knit' | undef },
}
The concealed sets come first, in the order the walk found them, then the melds in the order they were made; placement.index counts from the first concealed set. A kong on the table is a set of kind kong with four tiles. Seven pairs lists its pairs in singles, one entry per pair; thirteen orphans and the honours-and-knitted forms list every tile; a knitted straight's three sequences are sets of kind knit.
The placement is per split
With a winning kind given, a split is returned once for every place that kind can sit in it: a chow, a pung, the pair, a single. The wait named is what that placement alone says (the 3 of a 1-2-3 is an edge wait). Whether the hand was waiting on that tile ALONE, which fans 77 to 79 require, is a question about the thirteen tiles before it came, and waits answers it.
FUNCTIONS
decompose
my @splits = decompose($hand, $winning_kind);
Every split of a complete fourteen-tile hand (the concealed tiles plus three a meld). An incomplete hand gives an empty list. $winning_kind may be omitted for the splits without placements.
is_complete
Whether the hand is complete in any form.
waits
my @kinds = waits($hand);
The kinds whose addition completes a thirteen-tile hand, ascending. Dies on a hand that is not thirteen tiles' worth.
SEE ALSO
Game::Mahjong::Hand, Game::Mahjong::Meld
AUTHOR
LNATION <email@lnation.org>
LICENSE AND COPYRIGHT
This software is Copyright (c) 2026 by LNATION <email@lnation.org>.
This is free software, licensed under the Artistic License 2.0.