| Games-Euchre documentation | Contained in the Games-Euchre distribution. |
Games::Euchre - Euchre card game for humans and computers
Simply run my game wrapper:
% euchre.pl
or write your own:
use Games::Euchre;
use Games::Euchre::AI::Simple;
use Games::Euchre::AI::Human;
my $game = Games::Euchre->new();
foreach my $i (1..3) {
$game->setAI($i, Games::Euchre::AI::Simple->new());
}
$game->setAI(4, Games::Euchre::AI::Human->new());
$game->playGame();
my @scores = sort {$b <=> $a} $game->getScores();
print("The winner is " . $game->getWinner()->getName() . " with a score of " .
"$scores[0] to $scores[1]\n");
This software implements the card game of Euchre. The game is played with four players composing two teams. Any of the four players can be human or computer players, but more than one human is not well supported yet.
The Games::Euchre::AI module implements a simple framework for adding new classes of human interfaces or computer opponents. I recomment that AI writers use Games::Euchre::AI::Simple (a REALLY dumb computer opponent) as starting point.
Aside from the ::AI class and its descendents, this package also implements the following classes: Games::Euchre::Team, Games::Euchre::Player and Games::Euchre::Trick.
Create and initialize a new Euchre game.
Turns on the hang-the-dealer game option. It is off by default. If on, this means that the dealer may not pass in the second bidding round. Otherwise, the deal passes to the next player and bidding begins anew.
Turns on the no-trump game option. It is off by default. If on, this means that in the second round of bidding, players may declare "No trump".
Tells the game to use the specified AI instance to control the player of the given index. The index must be an integer between 1 and 4. The AI instance must inherit from Games::Euchre::AI.
Clear all of the state for the current game and get ready for the next one.
Clear all of the state for the current hand and get ready for the next one.
Start a game.
Start a hand. Called from playGame().
Called from playHand().
Called from playHand().
Records the trump suit for this hand. Also computes the suit of the left jack for convenience. No-trump is handled correctly. Called from getBid().
Instantiate and return a new Games::Euchre::Trick object. Called from playHand().
At the end of a hand, update the scores for the teams.
At the end of a hand, compute how many points each team deserves to gain for the tricks they won. Returns an array of these score increments. This method does not record any changes at all. Called by scoreHand().
At the end of a hand, compute what type of result each team deserves to gain for the tricks they won: one of 'win', 'all', 'alone', or 'euchre'. Returns an array of these win types increments. This method does not record any changes at all. Called by computeHandScores().
Tell AIs the results of the bidding.
Tell AIs the results of the trick.
Tell AIs the results of the hand.
Tell AIs the results of the game.
Returns the Team object who has won the game, or undef if nobody has won yet.
Returns an array of two Team objects.
Returns an array of four Player objects.
Returns a hash relating player numbers to player names for all four players.
Returns an array of current scores for the two teams. The order of the returned scores is the same as the order of the returned teams in the getTeams() method.
Returns the suit of the given card. The left jack is reported to be of the trump suit, if a trump has been declared. [This latter convenience is the whole point of having this function at all and not just calling CARD->suit().]
Games::Cards by Amir Karger
GNU Public License, version 2
Chris Dolan, chrisdolan@users.sourceforge.net
| Games-Euchre documentation | Contained in the Games-Euchre distribution. |
package Games::Euchre;
require 5.005_62; use strict; use warnings; use Games::Cards; use Games::Euchre::Team; use Games::Euchre::Player; use Games::Euchre::Trick; our $VERSION = '1.02';
sub new { my $pkg = shift; my %values = ( 9 => 9, 10 => 10, "J" => 11, "Q" => 12, "K" => 13, "A" => 14, ); my $self = bless({ game => Games::Cards::Game->new({cards_in_suit => \%values}), notrump => undef, hangdealer => undef, winningScore => 10, trump => undef, deck => undef, blind => undef, dealer => undef, bidder => undef, players => [], teams => [], }, $pkg); $self->resetGame(); return $self; }
sub enableHangDealer { my $self = shift; $self->{hangdealer} = 1; return $self; }
sub enableNoTrump { my $self = shift; $self->{notrump} = 1; return $self; }
sub setAI { my $self = shift; my $index = shift; # one-based my $ai = shift; die "Bad index" unless ($index && $index =~ /^[1-4]$/); die "Invalid AI instance" unless ($ai && ref($ai) && $ai->isa("Games::Euchre::AI")); $self->{players}->[$index-1]->setAI($ai); return $self; }
sub resetGame { my $self = shift; $self->{dealer} = 0; $self->{players} ||= []; $self->{teams} ||= []; foreach my $i (1 .. 4) { $self->{players}->[$i-1] ||= Games::Euchre::Player->new($self, $i, "Player $i"); # Refresh the old AI, if any my $player = $self->{players}->[$i-1]; my $ai = $player->getAI(); if ($ai) { if ($ai->persist()) { $ai->reset(); } else { my $pkg = ref($ai); $player->setAI($pkg->new()); } } } foreach my $i (1 .. 2) { if (!$self->{teams}->[$i-1]) { my $team = Games::Euchre::Team->new($self, $i, "Team $i", $self->{players}->[$i-1], $self->{players}->[$i+1]); foreach my $player ($team->getPlayers()) { $player->setTeam($team); } $self->{teams}->[$i-1] = $team; } } foreach my $player ($self->getPlayers()) { $player->resetGame(); } foreach my $team ($self->getTeams()) { $team->resetGame(); } return $self->resetHand(); }
sub resetHand { my $self = shift; $self->{bidder} = undef; $self->{trump} = undef; $self->{othertrump} = undef; $self->{deck} = Games::Cards::Deck->new($self->{game}, "Deck"); $self->{deck}->shuffle(); $self->{blind} = Games::Cards::Stack->new($self->{game}, "blind"); $self->{deck}->give_cards($self->{blind}, 4); foreach my $team ($self->getTeams()) { $team->resetHand(); } foreach my $player ($self->getPlayers()) { $player->resetHand(); $self->{deck}->give_cards($player->getHand(), 5); $player->getHand()->sort_by_value(); } }
sub playGame { my $self = shift; $self->resetGame(); while (!$self->getWinner()) { $self->playHand(); # Announce the end of the hand to AI players $self->announceEndOfHand() } # Announce the end of the game to AI players $self->announceEndOfGame(); }
sub playHand { my $self = shift; $self->resetHand(); if (!$self->getBid()) { $self->announceEndOfBidding(); $self->nextDealer(); return $self; } $self->announceEndOfBidding(); my $lead = ($self->getPlayers())[$self->{dealer} + 1 % 4]; foreach my $trickNum (1 .. 5) { my $trick = $self->getNewTrick($lead, $trickNum); for (1 .. $self->getPlayers()) { $trick->play(); } $trick->recordTrick(); my $winner = $trick->getWinner(); $self->announceEndOfTrick($trick); $lead = $winner; } $self->scoreHand(); $self->nextDealer(); return $self; }
sub getBid { my $self = shift; #print "Blind shows " . $self->{blind}->top_card()->truename() . "\n" # if ($self->{debug}); my @players = $self->getPlayers(); my $lastbid = 2*@players; for (my $turn = 1; $turn <= $lastbid; $turn++) { my $index = ($self->{dealer} + $turn) % @players; my $trump = $players[$index]->bid($turn); if (defined $trump) { $self->{bidder} = $index; $players[$index]->setBid(); if ($trump =~ s/A//) { $players[$index]->setAlone(); } $self->setTrump($trump); if ($turn <= @players) { # First round, dealer trades for turned card $players[$self->{dealer}]->pickItUp(); } #print($players[$index]->getName() . " called $trump for trump" . # ($players[$index]->wentAlone() ? ", alone" : "") . "\n") # if ($self->{debug}); last; } } if (!defined $self->{trump}) { return undef; # signal that all players passed } return $self; }
sub nextDealer { my $self = shift; $self->{dealer}++; $self->{dealer} %= $self->getPlayers(); return $self; }
sub setTrump { my $self = shift; my $trump = shift; $self->{trump} = $trump; my %othertrump = ( D => "H", C => "S", H => "D", S => "C", N => "", ); $self->{othertrump} = $othertrump{$trump}; return $self; }
sub getNewTrick { my $self = shift; my $lead = shift; my $number = shift; # 1-based return Games::Euchre::Trick->new($self, $lead, "Trick $number", $number); }
sub scoreHand { my $self = shift; my @scores = $self->computeHandScores(); foreach my $team ($self->getTeams()) { $team->addScore(shift @scores); } return $self; }
sub computeHandScores { my $self = shift; my %scoreMap = ('win' => 1, 'euchre' => 2, 'all' => 2, 'alone' => 4); return map {$scoreMap{$_} || 0} $self->computeWinTypes(); }
sub computeWinTypes { my $self = shift; my @winTypes = (); foreach my $team ($self->getTeams()) { my $tricks = $team->getTricks(); my $win = ""; if ($tricks >= 3) { $win = "win"; if (!$team->isBidder()) { $win = "euchre"; } elsif ($tricks >= 5) { $win = "all"; if ($team->wentAlone()) { $win = "alone"; } } } push @winTypes, $win; } return @winTypes; }
sub announceEndOfBidding { my $self = shift; foreach my $player ($self->getPlayers()) { if ($player->getAI()) { my $state = { name => $player->getName(), names => {$self->getPlayerNames()}, number => $player->getNumber(), trump => $self->{trump}, dealer => $self->{dealer}, bidder => defined $self->{bidder} ? $self->{bidder}+1 : undef, weBid => $player->getTeam()->isBidder(), usAlone => $player->getTeam()->wentAlone(), themAlone => $player->getTeam()->getOtherTeam()->wentAlone(), debug => $self->{debug}, }; $player->getAI()->endOfBidding($state); } } }
sub announceEndOfTrick { my $self = shift; my $trick = shift; my $winner = $trick->getWinner(); my $winCard = $trick->getPlayerIndex($winner); foreach my $player ($self->getPlayers()) { if ($player->getAI()) { my $state = { name => $player->getName(), names => {$self->getPlayerNames()}, number => $player->getNumber(), played => [map {$_->truename()} $trick->getCards()], playedBy => [map {$_->getNumber()} $trick->getPlayers()], myCard => $trick->getPlayerIndex($player), winCard => $winCard, winner => $winner->getNumber(), debug => $self->{debug}, }; $player->getAI()->endOfTrick($state); } } }
sub announceEndOfHand { my $self = shift; # Retrieve the wintype of the winning team my ($winType) = grep {$_} $self->computeWinTypes(); foreach my $player ($self->getPlayers()) { if ($player->getAI()) { my $state = { name => $player->getName(), names => {$self->getPlayerNames()}, number => $player->getNumber(), ourTricks => $player->getTeam()->getTricks(), theirTricks => $player->getTeam()->getOtherTeam()->getTricks(), winType => $winType, ourScore => $player->getTeam()->getScore(), theirScore => $player->getTeam()->getOtherTeam()->getScore(), winScore => $self->{winningScore}, debug => $self->{debug}, }; $player->getAI()->endOfHand($state); } } }
sub announceEndOfGame { my $self = shift; foreach my $player ($self->getPlayers()) { if ($player->getAI()) { my $state = { name => $player->getName(), names => {$self->getPlayerNames()}, number => $player->getNumber(), ourScore => $player->getTeam()->getScore(), theirScore => $player->getTeam()->getOtherTeam()->getScore(), debug => $self->{debug}, }; $player->getAI()->endOfHand($state); } } }
sub getWinner { my $self = shift; foreach my $team ($self->getTeams()) { return $team if ($team->getScore() >= $self->{winningScore}); } return undef; }
sub getTeams { my $self = shift; return @{$self->{teams}}; }
sub getPlayers { my $self = shift; return @{$self->{players}}; }
sub getPlayerNames { my $self = shift; return map {$_->getNumber(), $_->getName()} $self->getPlayers(); }
sub getScores { my $self = shift; return map {$_->getScore()} $self->getTeams(); }
sub getCardSuit { my $self = shift; my $card = shift; my $cardsuit = $card->suit(); if ($self->{trump} && $self->{othertrump} && $cardsuit eq $self->{othertrump} && $card->name() eq "J") { $cardsuit = $self->{trump}; } return $cardsuit; } 1; __END__