package Games::BeyondMath24;
use 5.008007;
use strict;
use warnings;

require Exporter;
#use AutoLoader qw(AUTOLOAD);

our @ISA = qw(Exporter);
our %EXPORT_TAGS = ( 'all' => [ qw(
) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw(beyondMath24);
our $VERSION = '0.01';
#=================================================

my $TARGET=24; 
my $OPERATORS=['+','-','*','/'];
my $MAX_OFFSET = 0.00001;

sub beyondMath24 {
	my ($cards,$target,$operators) = @_;
	$target = (defined $target) ? $target : $TARGET;
	$operators = (defined $operators) ? $operators : $OPERATORS;

	my @solutions;
	packExpr($cards,$operators,sub {
		my $value=eval($_[0]);
		return if ($@);
		push(@solutions,$_[0]) if (abs($target-$value)<$MAX_OFFSET); 
	});
	
	return distinct(@solutions); 
}

sub distinct {
	my @arr=@_;
	my %h=map {$_=>1} @arr;
	return keys %h;	
}

sub packCombined {
	my ($combined,$operators,$doWhatIfOK,@newX) = @_;

	push @newX, $combined; 
	packExpr(\@newX,$operators,$doWhatIfOK);
}

sub packExpr {
	my ($x,$operators,$doWhatIfOK)=@_;

	my $N=scalar @$x;
	return if (1>$N);
	return &$doWhatIfOK($x->[0]) if (1==$N);

	for (my $i=0; $i<$N; $i++) {
		for (my $j=$i+1;$j<$N;$j++) {
			next if ($j==$i);

			my @newX;
			for (my $n=0;$n<$N;$n++) {
				next if ($n==$i || $n==$j);
				push @newX, $x->[$n];
			}

			foreach my $opr (@$operators) {
				packCombined('('.$x->[$i].$opr.$x->[$j].')',
						$operators,$doWhatIfOK,@newX);
				next if (($x->[$i] eq $x->[$j]) || ($opr=~/\+\*/));
				packCombined('('.$x->[$j].$opr.$x->[$i].')',
						$operators,$doWhatIfOK,@newX);
			}
		}
	}
}

sub printArray {
	my ($arr) = @_;
	print join("\t",@$arr),"\n";
}

=pod

=head1 NAME

Games::BeyondMath24 - A 24 game implementation with enhancements of more cards, more operators and customizable target which by default is 24.

=head1 SYNOPSIS

	use Games::BeyondMath24;
	my @solutions=beyondMath24([3 3 8 8]);

	perl -MGames::BeyondMath24 -e "print join(qq{\t},beyondMath24(\@ARGV))" 3 3 8 8

	#The following example uses 5 cards to compute 29 dot with 6 kinds of operators 
	#	(including the empty string ''	)
	my @solutions=beyondMath24([3 3 8 8 3], 29, ['+','-','*','/','^','']);

=head1 DESCRIPTION

BeyondMath24 is an extended version of mathmatical card game "24 game" with enhancements of more cards, more operators and customizable target which by default is 24.

The following description of "24 game" is taken from Wikipedia  (http://en.wikipedia.org/wiki/24_Game):

The 24 Game is a mathematical card game in which the object is to find a way to manipulate four integers so that the end result is 24. Addition, subtraction, multiplication, or division, and sometimes other operations, may be used to make four digits from one to nine equal 24. For an example card with the numbers 4,7,8,8, a possible solution is the following: 8/8=1, 7-1=6, 6×4=24.

The game has been played in Shanghai since the 1960s[1], using ordinary playing cards. Robert Sun commercialised the game in 1988, introducing dedicated game cards bearing four numbers each, and sold it through his company, Suntex International Inc. There are nine official variations of the 24 Game. The tournament-style competition 24 Challenge is based on the game.

=head1 SEE ALSO


=head1 AUTHORS

Chen Yirong E<lt> cyr.master@gmail.com E<gt>, July 9, 2009.
	
=head1 KUDOS


=head1 COPYRIGHT

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

See L<http://www.perl.com/perl/misc/Artistic.html>

=cut

