Sunteți pe pagina 1din 13

1 package Games::Poker::TexasHold'em;

2 use strict;
3 our $VERSION = '1.4';
4 use Carp;
5
6 my @stages = qw( preflop flop turn river showdown);
7
8 =head1 NAME
9
10 Games::Poker::TexasHold'em - Abstract state in a Hold'em game
11
12 =head1 SYNOPSIS
13
14 use Games::Poker::TexasHold'em;
15 my $game = Games::Poker::TexasHold'em->new(
16 players => [
17 { name => "lathos", bankroll => 500 },
18 { name => "MarcBeth", bankroll => 500 },
19 { name => "Hectate", bankroll => 500 },
20 { name => "RichardIII", bankroll => 500 },
21 ],
22 button => "Hectate",
23 bet => 10,
24 limit => 50
25 );
26 $game->blinds; # Puts in both small and large blinds
27 print $game->pot; # 15
28
29 $game->call; # Hecate puts in 10
30 $game->bet_raise(15) # RichardIII sees the 10, raises another 5
31 ...
32
33 =head1 DESCRIPTION
34
35 This represents a game of Texas Hold'em poker. It maintains the state of
36 the pot, who's in to what amount, who's folded, what the bankrolls look
37 like, and so on. It's meant to be used in conjunction with
38 L<Games::Poker::OPP>, but can be used stand-alone as well for analysis.
39
40 =head1 METHODS
41
42 =head2 new
43
44 Starts a new game.
45
46 =cut
47
48 sub new {
49 my ($self, %args) = @_;
50 my @players = @{$args{players}};
51 $args{seats} = { map { $players[$_]->{name} => $_ } 0..$#players };
52 $args{button} = $args{next} = $args{seats}->{$args{button}};
53 for (@players) { $_->{in} = $_->{in_this_round} = 0 }
54 $args{unfolded} = @players;
55 $args{board} = [];
56 $args{hole} = [];
57 $args{stage} = 0;
58 bless \%args, $self;
59 }
60
61 =head2 General information about the game
62
63 =head3 seat2name
64
65 Returns the name of the player at the specified seat. Seats are numbered
66 from zero.
67
68 =cut
69
70 sub seat2name {
71 my ($self, $seat) = @_;
72 $self->{players}->[$seat]->{name};
73 }
74
75 =head3 players
76
77 Returns the names of all players in the game.
78
79 =cut
80
81 sub players {
82 my $self = shift;
83 map { $self->seat2name($_) } 0...$#{$self->{players}};
84 }
85
86 =head3 bet
87
88 Returns the initial bet.
89
90 =head2 limit
91
92 Returns the raise limit or 0 for unlimited.
93
94 =cut
95
96 sub bet { $_[0]->{bet} }
97 sub limit { $_[0]->{limit} || 0 }
98
99
10 =head2 Information about the current state of play
0
10 =head3 next_to_play
1
10 Returns the name of the player who's next to act in the game.
2
10 =cut
3
10 sub next_to_play {
4 my $self = shift;
10 return $self->seat2name($self->{next});
5 }
10
6 =head3 stage
10
7 Returns the stage of play. (preflop, flop, turn, river, showdown)
10
8 =cut
10
9 sub stage {
11 my $self = shift;
0 return $stages[$self->{stage}];
11 }
1
11 =head3 bankroll
2
11 Returns the bankroll of a given player.
3
11 =cut
4
11 sub bankroll {
5 my ($self, $player) = @_;
11 $player = $self->_rationalise_player($player);
6 $self->{players}->[$player]->{bankroll};
11 }
7
11 =head3 in
8
11 Returns the investment in the pot of a given player.
9
12 =cut
0
12 sub in {
1 my ($self, $player) = @_;
12 $player = $self->_rationalise_player($player);
2 $self->{players}->[$player]->{in}
12 }
3
12 =head2 folded
4
12 Returns whether or not the given player has folded. Players may be
5 specified by name or seat number.
12
6 =cut
12
7 sub folded {
12 my $self = shift;
8 my $player = shift;
12 $player = $self->_rationalise_player($player);
9 return $self->{players}->[$player]->{folded};
13 }
0
13 sub _rationalise_player {
1 my ($self, $player) = @_;
13 return $player if $player =~ /^-?\d+$/ and $self->{players}->[$player];
2 my $seat = $self->{seats}{$player};
13 return $seat if defined $seat;
3 croak "Couldn't find player $player";
13 }
4
13 sub _advance {
5 my $self = shift;
13 do {
6 return if $self->{unfolded} < 2; # Shouldn't be here anyway.
13 $self->{next}++;
7 $self->{next} = 0 if $self->{next} > $#{$self->{players}};
13 } until ! $self->folded($self->{next});
8 }
13
9 =head3 pot
14
0 Returns the current amount of cash in the pot
14
1 =cut
14
2 sub pot {
14 # The total of each player's interest in the game.
3 my $self = shift;
14 my $pot = 0;
4 for (@{$self->{players}}) { $pot += $_->{in} };
14 return $pot;
5 }
14
6 =head3 pot_square
14
7 Returns whether or not the pot is square and the current stage should be
14 ended.
8
14 =cut
9
15 sub pot_square {
0 # Everyone (who is still in) is in to the tune of the current bet.
15 my $self = shift;
1 for (@{$self->{players}}) {
15 next if $_->{folded};
2 return 0 if $_->{in_this_round} != $self->{current_bet}
15 }
3 return 1;
15 }
4
15 =head3 board
5
15 Returns the current board, if you're using one; this is the set of
6 things which have been passed in to L</next_stage>.
15
7 =cut
15
8 sub board {
15 return @{$_[0]->{board}};
9 }
16
0 =head3 hole
16
1 Similar to C<board>, this is an opaque area where you can store your
16 hole cards in whatever format you want to, if you want to.
2
16 =cut
3
16 sub hole {
4 my $self = shift;
16 if (@_) { $self->{hole} = [@_] }
5 @{$self->{hole}}
16 }
6
16 =head3 status
7
16 Returns a nice table summarizing what's going on in the game.
8
16 =cut
9
17 sub status {
0 my $self = shift;
17 my @players = @{$self->{players}};
1 my $output = "Pot: ".$self->pot." Stage: ".$self->stage."\n";
17 $output .= "? ". sprintf("%20s %6s %6s", qw[Name Bankroll InPot])."\n";
2 for (0..$#players) {
17 my $p = $players[$_];
3 my $status;
17 if ($p->{folded}) { $status = "F" }
4 elsif ($self->{next} != $_) { $status = " " }
17 elsif ($self->pot_square) { $status="." }
5 else { $status = "*" }
17 $output .= "$status ";
6 $output .= sprintf("%20s \$% 5d \$% 5d", $p->{name}, $p->{bankroll},
17 $p->{in});
7 $output .="\n";
17 }
8 return $output;
17 }
9
18 =head2 Actions
0
18 These actions all apply to he current person who is next to act. No
1 playing out of turn! After an action, play is advanced to the next
18 player except in the case of C<blinds>.
2
18 =head3 blinds
3
18 Puts in both small and large blinds. Play is not advanced, because
4 blinds are taken from the left of the button. It's all so confusing.
18
5 =cut
18
6 sub blinds {
18 my $self = shift;
7 my $big = $self->{bet};
18 my $small = $big / 2;
8 $self->{current_bet} = $big;
18 # Play *is* advanced, but it goes backwards two first
9 $self->{next}-=2;
19 $self->_put_in($small);
0 $self->_advance;
19 $self->_put_in($big);
1 $self->_advance;
19 }
2
19 sub _put_in {
3 my ($self, $amount) = @_;
19 my $who = $self->{players}->[$self->{next}];
4 $who->{bankroll} -= $amount;
19 $who->{in} += $amount;
5 $who->{in_this_round} += $amount;
19 }
6
19 =head3 fold
7
19 Folds, taking the player out of the game.
8
19 =cut
9
20 sub fold {
0 my $self = shift;
20 $self->{players}->[$self->{next}]->{folded}++;
1 $self->{unfolded}--;
20 $self->_advance;
2 }
20
3 =head3 check_call
20
4 Either checks, putting nothing in the pot, or calls, putting in however
20 much the current player is short. Returns the amount put into the pot.
5
20 You can call the C<check> or C<call> methods if you feel happier with
6 that, but they're identical.
20
7 =cut
20
8 sub check_call {
20 my $self = shift;
9 my $player = $self->{players}->[$self->{next}];
21 my $short = $self->{current_bet} - $player->{in_this_round};
0 $self->_put_in($short);
21 $self->_advance;
1 return $short;
21 }
2
21 *check = *check_call;
3 *call = *check_call;
21
4 =head3 bet_raise ($amount)
21
5 Bets, if there's currently no bet, or raises an amount up, up to the
21 limit if there is one. The amount must include the call; that is, if
6 you're short 10, you call
21
7 $self->bet_raise(20);
21
8 to see the 10 and raise another 10. (You'll get an error if it's less
21 than you're short.) If you don't say how much to raise, it'll be raised
9 by the intial bet.
22
0 As with C<check> and C<call>, you can call this either as C<bet> or
22 C<raise> if you prefer.
1
22 =cut
2
22 sub bet_raise {
3 my ($self, $amount) = @_;
22 if (!$amount or $amount < $self->{bet}) { $amount = $self->{bet} };
4 my $player = $self->{players}->[$self->{next}];
22 my $short = $self->{current_bet} - $player->{in_this_round};
5 if ($self->{limit} and $amount > $self->{limit}) {
22 $amount = $self->{limit}
6 }
22 if ($amount < $short) { croak "You need to raise more than the call!" }
7 $self->{current_bet} += $amount-$short;
22 $self->_put_in($amount);
8 $self->_advance;
22 return $amount;
9 }
23
0 *bet = *bet_raise;
23 *raise = *bet_raise;
1
23 =head2 "Control" actions
2
23 These are actions taken by the server or "dealer" rather than an
3 individidual player.
23
4 =head3 next_stage (@cards)
23
5 Checks that the pot is square, and if so, advances to the next stage.
23 Returns 1 if an advance was made, otherwise 0. The optional "cards"
6 argument is treated as an opaque array which is added to the current
23 board.
7
23 =cut
8
23 sub next_stage {
9 my $self = shift;
24 return 0 unless $self->pot_square;
0 $self->{current_bet} = 0;
24 for (@{$self->{players}}) { $_->{in_this_round} = 0 }
1 $self->{stage}++;
24 my @cards = @_ unless $self->stage eq "showdown"; # No more cards now!
2 push @{$self->{board}}, @cards;
24 return 1;
3 }
24
4 =head1 AUTHOR
24
5 Simon Cozens, E<lt>simon@kasei.comE<gt>
24
6 =head1 COPYRIGHT AND LICENSE
24
7 Copyright 2003 by Simon Cozens
24
8 This library is free software; you can redistribute it and/or modify
24 it under the same terms as Perl itself.
9
25 =cut
0
25
1
25
2
25
3
25
4
25
5
25
6
25
7
25
8
25
9
26
0
26
1
26
2
26
3
26
4
26
5
26
6
26
7
26
8
26
9
27
0
27
1
27
2
27
3
27
4
27
5
27
6
27
7
27
8
27
9
28
0
28
1
28
2
28
3
28
4
28
5
28
6
28
7
28
8
28
9
29
0
29
1
29
2
29
3
29
4
29
5
29
6
29
7
29
8
29
9
30
0
30
1
30
2
30
3
30
4
30
5
30
6
30
7
30
8
30
9
31
0
31
1
31
2
31
3
31
4
31
5
31
6
31
7
31
8
31
9
32
0
32
1
32
2
32
3
32
4
32
5
32
6
32
7
32
8
32
9
33
0
33
1
33
2
33
3
33
4
33
5
33
6
33
7
33
8
33
9
34
0
34
1
34
2
34
3
34
4
34
5
34
6
34
7
34
8
34
9
35
0
35
1
35
2
35
3
35
4
35
5
35
6
35
7
35
8
35
9
36
0
36
1
36
2
36
3
36
4
36
5
36
6
36
7
36
8
36
9
37
0
37
1
37
2
37
3
37
4
37
5
37
6
37
7
37
8
37
9
38
0
38
1
38
2
38
3
38
4
38
5
38
6
38
7
38
8
38
9
39
0
39
1
39
2
39
3
39
4
39
5
39
6
39
7
39
8
39
9

S-ar putea să vă placă și