NAME
Games::Dukedom - The classic big-iron game
SYNOPSIS
use Games::Dukedom;
my $game = Games::Dukedom->new();
DESCRIPTION
This is an implementation of the classic game of "Dukedom". It is intended to be display agnostic so that it can be used not only by command line scripts such as the one included but also by graphical UIs such as Tk or web sites.
It has been implemented as an "interrupt driven" state-machine. The actual executable application need only concern itself with displaying messages and collecting appropriate input as requested.
Here is a minimal script that implements a fully functional game:
#!/usr/local/bin/perl
$| = 1;
use strict;
use warnings;
use Scalar::Util qw( blessed );
use Try::Tiny;
use Games::Dukedom;
my $input_yn = sub {
my $default = shift || '';
my $ans = <>;
chomp($ans);
$ans ||= $default;
return ( $ans =~ /^(?:q|quit)\s*$/i || $ans =~ /^(?:y|n)$/i )
? lc($ans)
: undef;
};
my $input_value = sub {
my $default = shift || 0;
my $ans = <>;
chomp($ans);
$ans = $default unless length($ans);
return ( $ans =~ /^(?:q|quit)\s*$/i || $ans !~ /\D/ ) ? $ans : undef;
};
my %actions = (
get_yn => $input_yn,
get_value => $input_value,
);
play_game();
exit;
sub play_game {
my $game = Games::Dukedom->new;
do {
try {
$game->play_one_year;
}
catch {
if ( blessed($_) && $_->isa('Games::Dukedom::Signal') ) {
print $_->msg if $_->msg;
return unless defined( $_->action );
my $action = $_->action;
$game->input( &{ $actions{$action} }( $_->default ) );
}
else {
die $_;
}
};
} until ( $game->game_over );
return;
}
__END__
The important thing to take away from this is how play_one_year
is wrapped in a try/catch construct and how the script displays messages and requests input as needed. This is the heart of the state-machine design.
All of the logic for the game is provided by the module itself and any given implementation framework need only handle the I/O as needed.
CONSTRUCTOR
One begins the game by calling the expected new
method like so:
my $game = Games::Dukedom->new();
It currently does not take any parameters.
ATTRIBUTES
All attributes, except for input
, have read-only accessors.
It should be noted that the values in the attributes will probably not be of much use to a game implementation other than to provide specialized reports if so desired, hence the reason for being read-only (except for the obvious case of input
).
On the other hand, they do provide the current environment for a given year of play and must be preserved at all times. It is anticipated that a stateless environment such as a CGI script will need to save state in some fashion when requesting input and then restore it prior to applying the input and re-entering the state-machine.
- input (read-write)
-
This attribute should hold the latest value requested by the state-machine. It will recognize the values 'q' and 'quit' (case-insensitive) and set the game status to
QUIT_GAME
if either of those are submitted. - grain
-
The current amount of grain on hand.
- king_unrest
-
Used to indicate the level of the King's mistrust.
- land
-
The current amount of land on hand.
- land_fertility
-
A hash containing "buckets" that indicate how much land is in what condition of productivity at any given time. The game assumes that land that is planted will lose 20% of it's full productivity each each it is used without being allowed to lie fallow.
Basically this means that you should have twice as much total land available as what is needed to plant to ensure 100% productivity each year.
- population
-
The current number of peasants in the Dukedom.
- status
-
Indicates that the game is either
RUNNING
or in one of the conditions that indicate that the end of the game has been reached.A "win" is indicated by a positive value, a "loss" by a negative one.
- unrest
-
Holds the cummulative population unrest factor. There is also an annual unrest factor that gets reset at the start of each game year. The two are relatively independent in that an excess of either one can cause you to be deposed and end the game.
- tax_paid
-
Total amount of taxes paid to the King since the beginnig of the game.
- year
-
The current game year. The will automatically end with you being forced into retirement at the end of 45 years unless some other cause occurs first.
NOTE: This will be ignored if a state of war currently exists between you and the King that must be resolved.
- yield
-
The amound of grain produced in the prior yield expressed as HL/HA.
METHODS
play_one_year
This method begins a new year of play. It initializes the temporary structures and factors and resets the state-machine.
Note: The caller should trap any errors thrown by this method to determine the correct course of action to take based on the value of the exception's msg
and action
attributes.
game_over
Boolean that indicates that current game is over and further play is not possible. Check status
for reason if desired.
input_is_yn
Returns a boolean indicating that the current content of $game->input
is either "Y" or "N" (case insensitive).
input_is_value
Returns a boolean indicating that the current content of $game->input
is "0" or a positive integer.
SEE ALSO
This package is based on the logic found in this C code, which appears to have been derived from an older source written in Basic:
https://github.com/caryo/Dukedom/blob/master/imports/dukedom.c
BUGS
Seriously? Look at the version number.
AUTHOR
Jim Bacon, <jim@nortx.com>
COPYRIGHT AND LICENSE
Copyright (C) 2014 by Jim Bacon
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8 or, at your option, any later version of Perl 5 you may have available.