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.