NAME
Game::Xiangqi::Error - a flagged refusal, returned and never thrown
SYNOPSIS
my $refusal = $game->play('c4e6');
if ($refusal) {
say $refusal->code; # 'elephant_crosses_river'
say $refusal->message; # 'the elephant never crosses the river'
say 1 if $refusal->elephant_crosses_river;
}
DESCRIPTION
A refused move comes back as one of these. It is not thrown, and that is a decision rather than an oversight: a refused move is an ordinary thing for a player to do, and unwinding the stack for it would make every caller wrap every move in an eval. die is kept for programmer error: a flag that does not exist.
Sixteen flags, one of them set. Six are structural (bad_move, game_over, not_your_turn, no_piece, own_piece, not_legal), two are about the general (in_check, generals_face), and nine name one rule of the game each. The nine exist so a refusal tells a player which rule they broke rather than "no": somebody told their elephant cannot cross the river does not come back and argue.
Game::Xiangqi::Engine is the layer underneath and does not use this class: its own play returns the flag as a plain string, because the engine is a skin over the C ABI and knows nothing about the Perl layers above it.
ATTRIBUTES
bad_move
game_over
not_your_turn
no_piece
own_piece
not_legal
in_check
generals_face
general_leaves_palace
advisor_leaves_palace
elephant_crosses_river
elephant_eye_blocked
horse_leg_blocked
cannon_needs_one_screen
soldier_no_retreat
soldier_no_sideways
One accessor per reason, true on the one that applies and false on the rest.
not_legal is the answer of last resort and the nine rule flags exist to keep it rare: it means the move is not in the legal list and no rule of the piece explains why. in_check and generals_face are told apart because they are different mistakes, one of them the flying general, and a player told the wrong one learns the wrong rule.
error
Always 1. It is there so that a caller holding something which may be a move or may be a refusal can ask one question of either.
message
The sentence for the flag, as a player should be shown it.
legal
The legal alternatives where offering them helps. An arrayref even when empty, because the normal case is a caller dereferencing it without checking.
METHODS
throw
Game::Xiangqi::Error->throw('in_check');
Game::Xiangqi::Error->throw('not_legal', legal => \@iccs);
Returns the refusal. It does not die. The name is the house's.
Dies only if the flag does not exist, which is programmer error.
code
The flag that is set, as a string, or undef. This is what an adapter maps to its own vocabulary.
stringify
The message. There is deliberately no "" overload: a refusal that quietly turned into a sentence when it was used as a hash key would be a worse bug than one that looks like a reference.
flags
my @names = Game::Xiangqi::Error->flags;
A class method: the list of every refusal name, in @FLAGS order. An adapter mapping these to its own codes should assert against this list rather than against a copy of it.
message_for
Game::Xiangqi::Error->message_for('in_check');
# 'that would leave your general in check'
A class method: the sentence for one flag without building a refusal, or undef for a name that is not one. The instance's own sentence is message.
known
Game::Xiangqi::Error->known($flag); # 1 or 0
A class method: whether that name is a refusal this distribution can return.
SEE ALSO
Game::Xiangqi, which returns these; Game::Xiangqi::Engine, which does not.