File Coverage

lib/Games/Checkers/BoardTree.pm
Criterion Covered Total %
statement 21 87 24.1
branch 0 24 0.0
condition 0 5 0.0
subroutine 7 15 46.6
pod 0 3 0.0
total 28 134 20.9


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   802 use strict;
  1         2  
  1         22  
17 1     1   4 use warnings;
  1         2  
  1         25  
18              
19             package Games::Checkers::BoardTreeNode;
20              
21 1     1   5 use Games::Checkers::Constants;
  1         1  
  1         4  
22 1     1   319 use Games::Checkers::MoveConstants;
  1         3  
  1         5  
23 1     1   340 use Games::Checkers::CreateMoveList;
  1         3  
  1         398  
24              
25             my $stopped = No;
26             sub check_user_interaction () {
27             # no user interaction yet
28 0     0     return Ok;
29             }
30              
31             sub new ($$$) {
32 0     0     my $class = shift;
33 0           my $board = shift;
34 0           my $move = shift;
35              
36 0           my $self = {
37             board => $board,
38             move => $move,
39             sons => [],
40             expanded => 0,
41             };
42              
43 0           return bless $self, $class;
44             }
45              
46             # o 3 0
47             # | white max
48             # o-----------------------o 2 1
49             # | | black min
50             # o-------o-------o o-------o-------o 1 2
51             # | | | | | | white max
52             # o-o-o o-o-o o-o-o o-o-o o-o-o o-o-o 0 3
53              
54             sub expand ($$) {
55 0     0     my $self = shift;
56 0           my $color = shift;
57              
58 0           my $board = $self->{board};
59 0           my $builder = Games::Checkers::CreateMoveList->new($board, $color);
60              
61 0           @{$self->{sons}} = map {
62 0           Games::Checkers::BoardTreeNode->new($_->[1], $_->[0])
63 0           } @{$builder->get_move_boards};
  0            
64 0           $self->{expanded} = 1;
65              
66 0           return $builder->{status};
67             }
68              
69             sub unexpand ($) {
70 0     0     my $self = shift;
71              
72 0           $_->unexpand foreach @{$self->{sons}};
  0            
73 0           @{$self->{sons}} = ();
  0            
74 0           $self->{expanded} = 0;
75             }
76              
77             sub choose_best_son ($$$;$$) {
78 0     0     my $self = shift;
79 0           my $color = shift;
80 0           my $level = shift;
81 0 0         my $min = shift; $min = MIN_SCORE unless defined $min;
  0            
82 0 0         my $max = shift; $max = MAX_SCORE unless defined $max;
  0            
83              
84             # return if $stopped || check_user_interaction() != Ok;
85              
86 0 0         my $is_maximizing = $color == ($::RULES{GIVE_AWAY} ? Black : White);
87 0           my $best_node;
88              
89 0 0         if ($level <= 0) {
90 0           $min = $max = $self->{board}->get_score($color);
91             } else {
92 0 0         $self->expand($color) unless $self->{expanded};
93              
94 0           foreach my $son (@{$self->{sons}}) {
  0            
95 0           my ($score) = $son->choose_best_son(!$color, $level - 1, $min, $max);
96              
97 0 0         if ($is_maximizing) {
98 0 0         if ($score > $min) {
99 0           $min = $score;
100 0           $best_node = $son;
101             }
102             }
103             else {
104 0 0         if ($score < $max) {
105 0           $max = $score;
106 0           $best_node = $son;
107             }
108             }
109             # alpha-beta pruning
110 0 0         $is_maximizing ^= 1, last if $min >= $max;
111             }
112              
113             # all moves if any lead to losage, choose a random one
114 0   0       $best_node ||= $self->{sons}[rand(@{$self->{sons}})];
  0            
115              
116 0           $self->unexpand;
117             }
118              
119 0 0         return wantarray ? ($is_maximizing ? $min : $max) : $best_node;
    0          
120             }
121              
122             package Games::Checkers::BoardTree;
123              
124 1     1   6 use Games::Checkers::MoveConstants;
  1         2  
  1         4  
125              
126 1     1   6 use constant DEFAULT_LEVEL => 5;
  1         1  
  1         229  
127              
128             sub new ($$$;$) {
129 0     0 0   my $class = shift;
130 0           my $board = shift;
131 0           my $color = shift;
132 0   0       my $level = shift || DEFAULT_LEVEL;
133              
134 0           my $self = {
135             head => new Games::Checkers::BoardTreeNode($board, NO_MOVE),
136             max_level => $level,
137             real_level => undef,
138             color => $color,
139             };
140              
141 0           return bless $self, $class;
142             }
143              
144             sub choose_best_move ($) {
145 0     0 0   my $self = shift;
146              
147 0           my $max_level = $self->{max_level};
148 0           my $son = $self->{head}->choose_best_son($self->{color}, $max_level);
149             # foreach my $son0 (@{$self->{sons}}) {
150             # next if defined $son && $son == $son0;
151             # $son0->unexpand;
152             # }
153 0 0         return NO_MOVE unless $son;
154 0           return $son->{move};
155             }
156              
157             sub choose_random_move ($) {
158 0     0 0   my $self = shift;
159              
160 0           $self->{head}->expand($self->{color});
161 0           my $sons = $self->{head}->{sons};
162 0           my $move = $sons->[int(rand(@$sons))]->{move};
163 0           $self->{head}->unexpand;
164 0           return $move;
165             }
166              
167             1;