File Coverage

lib/Games/Checkers/Game.pm
Criterion Covered Total %
statement 24 148 16.2
branch 0 78 0.0
condition 0 42 0.0
subroutine 8 27 29.6
pod 0 18 0.0
total 32 313 10.2


line stmt bran cond sub pod time code
1             # Games::Checkers, Copyright (C) 1996-2012 Mikhael Goikhman, migo@cpan.org
2             #
3             # This program is free software: you can redistribute it and/or modify
4             # it under the terms of the GNU General Public License as published by
5             # the Free Software Foundation, either version 3 of the License, or
6             # (at your option) any later version.
7             #
8             # This program is distributed in the hope that it will be useful,
9             # but WITHOUT ANY WARRANTY; without even the implied warranty of
10             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11             # GNU General Public License for more details.
12             #
13             # You should have received a copy of the GNU General Public License
14             # along with this program. If not, see .
15              
16 1     1   1237 use strict;
  1         2  
  1         24  
17 1     1   4 use warnings;
  1         1  
  1         27  
18              
19             package Games::Checkers::Game;
20              
21 1     1   402 use Games::Checkers::Rules;
  1         3  
  1         25  
22 1     1   5 use Games::Checkers::Board;
  1         2  
  1         16  
23 1     1   4 use Games::Checkers::Constants;
  1         1  
  1         5  
24 1     1   5 use Games::Checkers::BoardTree;
  1         2  
  1         43  
25 1     1   7 use Games::Checkers::CreateMoveList;
  1         1  
  1         22  
26 1     1   9 use Games::Checkers::MoveConstants;
  1         2  
  1         4  
27              
28             sub new ($%) {
29 0     0 0   my $class = shift;
30 0           my %params = @_;
31              
32 0           my $variant = $params{variant};
33 0           Games::Checkers::Rules::set_variant($variant);
34              
35 0 0         $ENV{DUMB_CHARS} = 1 if $params{dumb_chars};
36              
37 0   0       my $title = $params{title} || "Unknown White - Unknown Black";
38 0           my $board = Games::Checkers::Board->new($params{board}, $params{size});
39             my $color = defined $params{color} ? $params{color} :
40             (defined $ENV{_WHITE_STARTS} ? $ENV{_WHITE_STARTS} :
41 0 0 0       $::RULES{WHITE_STARTS}) ^ ($params{black} || 0) ? White : Black;
    0          
    0          
42              
43             # probe and use if available
44 0   0       my $frontend = !($params{use_term} || $ENV{USE_TERM}) && eval q{
45             use Games::Checkers::SDL;
46             Games::Checkers::SDL->new($title, $board, fullscreen => $params{fullscreen});
47             };
48              
49             my $self = {
50             variant => $variant,
51             title => $title,
52             board => $board,
53             color => $color,
54             frontend => $frontend,
55             dumb_term => $params{dumb_term},
56             level => $params{level} || 3,
57             random => $params{random} || 0,
58 0   0       max_move_num => $params{max_move_num} || 1000,
      0        
      0        
59             plies => [],
60             initial => {
61             board => $board->clone,
62             color => $color,
63             },
64             };
65              
66 0           bless $self, $class;
67              
68             $SIG{__DIE__} = sub {
69 0     0     $self->show_result(shift);
70 0           };
71              
72 0 0         $self->edit_board if $params{edit_board};
73              
74 0 0         $self->show_menu if $params{show_menu};
75              
76 0           $self->init;
77             }
78              
79             sub show_menu ($) {
80 0     0 0   my $self = shift;
81              
82 0 0         if ($self->{frontend}) {
83 0           $self->{board} = $self->{frontend}->show_menu($self->{board});
84             }
85              
86 0 0         $self->quit unless $self->{board};
87             }
88              
89             sub init ($) {
90 0     0 0   my $self = shift;
91              
92 0           $self->{plies} = [];
93              
94 0 0         if ($self->{frontend}) {
95 0           $self->{frontend}->init;
96             } else {
97 0           $| = 1;
98 0 0         print "\e[2J" unless $self->{dumb_term};
99             }
100              
101 0           return $self;
102             }
103              
104             sub restart ($) {
105 0     0 0   my $self = shift;
106              
107 0           $self->{board} = $self->{initial}{board}->clone;
108 0           $self->{color} = $self->{initial}{color};
109              
110 0 0         if ($self->{frontend}) {
111 0           $self->{frontend}->restart($self->{board});
112             }
113              
114 0           $self->init;
115 0           $self->show_board;
116 0           $self->sleep(1);
117             }
118              
119             sub quit ($) {
120 0     0 0   my $self = shift;
121              
122 0 0         if ($self->{frontend}) {
123 0 0         $self->{frontend}->quit or return;
124             }
125              
126 0           exit(0);
127             }
128              
129             sub call_frontend ($$@) {
130 0     0 0   my $self = shift;
131 0   0       my $method = shift || die;
132              
133 0           my $rv = $self->{frontend}->$method(@_);
134              
135 0 0         if ($rv == -1) {
136 0           $self->restart;
137             }
138 0 0         if ($rv == -2) {
139 0           $self->quit;
140             }
141              
142 0           return $rv;
143             }
144              
145             sub sleep ($$) {
146 0     0 0   my $self = shift;
147 0   0       my $secs = shift || 0;
148              
149 0 0         if ($self->{frontend}) {
150 0           $self->call_frontend('sleep', $secs);
151             } else {
152 0           sleep($secs);
153             }
154             }
155              
156             sub hold ($;$) {
157 0     0 0   my $self = shift;
158 0           my $break = shift;
159              
160 0 0         if ($self->{frontend}) {
161 0           $self->call_frontend('hold', $break);
162             } else {
163 0 0         $self->sleep($break) if $break;
164             }
165             }
166              
167             sub show_board ($) {
168 0     0 0   my $self = shift;
169              
170 0 0         if ($self->{frontend}) {
171 0           $self->call_frontend('show_board');
172             } else {
173 0           my $title = $self->{title};
174 0           my $indent = int((37 - length($title)) / 2);
175 0 0         print "\e[1;1H\e[?25l" unless $self->{dumb_term};
176 0           print " " x $indent, $title, "\n";
177 0           print $self->{board}->dump;
178 0 0         print "\e[?25h" unless $self->{dumb_term};
179             }
180             }
181              
182             sub can_move ($) {
183 0     0 0   my $self = shift;
184              
185 0           $self->{board}->can_color_move($self->{color});
186             }
187              
188             sub is_max_move_num_reached ($) {
189 0     0 0   my $self = shift;
190              
191 0           return @{$self->{plies}} >= $self->{max_move_num} * 2;
  0            
192             }
193              
194             sub choose_move ($) {
195 0     0 0   my $self = shift;
196              
197             my ($board, $color, $level, $random) = map {
198 0           $self->{$_}
  0            
199             } qw(board color level random);
200              
201 0           my $board_tree = Games::Checkers::BoardTree->new($board, $color, $level);
202              
203 0 0 0       my $move = $random eq 1 || $random eq ($color == White ? 'w' : 'b')
204             ? $board_tree->choose_random_move
205             : $board_tree->choose_best_move;
206              
207 0           return $move;
208             }
209              
210             sub create_move ($$$@) {
211 0     0 0   my $self = shift;
212 0           my $is_beat = shift;
213 0           my $src = shift;
214 0           my @dsts = @_;
215              
216             my $creating_move = Games::Checkers::CreateVergeMove->new(
217 0           $self->{board}, $self->{color}, $is_beat, $src, @dsts
218             );
219 0 0         die "Internal problem" unless $creating_move->status == Ok;
220 0           my $move = $creating_move->get_move;
221              
222 0 0         return $move == NO_MOVE ? undef : $move;
223             }
224              
225             sub show_move ($$) {
226 0     0 0   my $self = shift;
227 0           my $move = shift;
228              
229             my ($board, $color, $plies) = map {
230 0           $self->{$_}
  0            
231             } qw(board color plies);
232              
233 0           my $move_str = $move->dump($board);
234 0           my $is_second = ($color eq White) != $::RULES{WHITE_STARTS};
235              
236 0 0         if ($self->{frontend}) {
237 0 0         $self->call_frontend('show_move', $move, $move_str, $is_second, $plies)
238             && return; # return on "restart" or unconfirmed "quit"
239             } else {
240 0 0         printf " %02d. %s", 1 + @$plies / 2, $is_second ? "... " : "";
241 0           print "$move_str \n";
242             }
243              
244 0           $board->apply_move($move);
245              
246 0 0         $self->{color} = $color == White ? Black : White;
247 0           push @$plies, $move;
248             }
249              
250             sub color_name ($;$$) {
251 0     0 0   my $self = shift;
252 0   0       my $opposite = shift || 0;
253 0   0       my $starting = shift || 0;
254              
255             # does it ever make sense to use $self->{initial}{color} instead?
256 0 0         my $starting_color = $::RULES{WHITE_STARTS} ? White : Black;
257              
258             return (($starting ? $starting_color : $self->{color})
259 0 0 0       == White xor $opposite) ? 'White' : 'Black';
    0          
260             }
261              
262             sub show_result ($;$$) {
263 0     0 0   my $self = shift;
264             my $message = shift || ($self->is_max_move_num_reached
265             ? "Automatic draw after $self->{max_move_num} moves"
266 0   0       : $self->color_name(!$::RULES{GIVE_AWAY}) . " won"
267             );
268 0           my $break = shift;
269              
270 0 0         if ($self->{frontend}) {
271 0 0         $self->call_frontend('show_result', $message)
272             && return; # return on "restart" or unconfirmed "quit"
273             } else {
274 0           print "\n$message.\e[0K\n";
275             }
276              
277 0           $self->hold($break);
278             }
279              
280             sub show_result_code ($;$$) {
281 0     0 0   my $self = shift;
282 0   0       my $code = shift || '';
283 0           my $break = shift;
284              
285 0 0 0       my $message =
    0 0        
    0 0        
    0          
286             $code eq '1-0' || $code eq '2-0' ? '%C %l' :
287             $code eq '0-1' || $code eq '0-2' ? '%c %l' :
288             $code eq '1/2-1/2' || $code eq '1-1' ? 'Draw is agreed' :
289             $code eq '*' ? 'Unfinished game' : "Unknown result ($code)";
290              
291 0           $message =~ s/%([cC])/$self->color_name($1 ne 'c', 1)/e;
  0            
292 0 0         $message =~ s/%l/$self->can_move ? 'resigned' : 'lost'/e;
  0            
293              
294 0           $self->show_result($message, $break);
295             }
296              
297             sub edit_board ($) {
298 0     0 0   my $self = shift;
299              
300 0           my $board = $self->{board};
301              
302 0 0         if ($self->{frontend}) {
303 0           $self->{frontend}->edit_board($board);
304             }
305              
306 0           $self->{initial}{board}->copy($board);
307             }
308              
309             1;