File Coverage

lib/Games/Checkers/BoardTree.pm
Criterion Covered Total %
statement 30 94 31.9
branch 0 24 0.0
condition 0 8 0.0
subroutine 10 19 52.6
pod 0 3 0.0
total 40 148 27.0


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   942 use strict;
  1         2  
  1         35  
17 1     1   6 use warnings;
  1         1  
  1         38  
18              
19             package Games::Checkers::BoardTreeNode;
20              
21 1     1   5 use base 'Games::Checkers::Board';
  1         2  
  1         92  
22              
23 1     1   6 use Games::Checkers::Constants;
  1         4  
  1         7  
24 1     1   477 use Games::Checkers::MoveConstants;
  1         3  
  1         5  
25 1     1   526 use Games::Checkers::CreateMoveList;
  1         3  
  1         34  
26              
27 1     1   5 use constant NO_COST => 1e9;
  1         2  
  1         73  
28 1     1   5 use constant EqualCostDeterminism => $ENV{EQUAL_COST_DETERMINISM};
  1         2  
  1         583  
29              
30             my $stopped = No;
31             sub check_user_interaction () {
32             # no user interaction yet
33 0     0     return Ok;
34             }
35              
36             sub new ($$$) {
37 0     0     my $class = shift;
38 0           my $board = shift;
39 0           my $move = shift;
40              
41 0           my $self = $class->SUPER::new($board);
42 0           $self->{move} = $move;
43 0           $self->{sons} = [];
44 0           $self->{expanded} = 0;
45 0           return $self;
46             }
47              
48             # o 3 0
49             # | white max
50             # o-----------------------o 2 1
51             # | | black min
52             # o-------o-------o o-------o-------o 1 2
53             # | | | | | | white max
54             # o-o-o o-o-o o-o-o o-o-o o-o-o o-o-o 0 3
55              
56             sub expand ($$) {
57 0     0     my $self = shift;
58 0           my $color = shift;
59              
60 0           my $creating_moves = Games::Checkers::CreateMoveList->new($self, $color);
61 0           $self->{expanded} = 1;
62 0           return $creating_moves->{status};
63             }
64              
65             sub unexpand ($) {
66 0     0     my $self = shift;
67 0           $_->unexpand foreach @{$self->{sons}};
  0            
68 0           @{$self->{sons}} = ();
  0            
69 0           $self->{expanded} = 0;
70             }
71              
72             sub is_better_cost ($$$$) {
73 0     0     my $self = shift;
74 0           my $color = shift;
75 0           my $cost1 = shift;
76 0           my $cost2 = shift;
77              
78 0 0 0       return int(rand(2)) unless $cost1 != $cost2 || EqualCostDeterminism;
79              
80 0 0         my $max = ($cost1 > $cost2) ? $cost1 : $cost2;
81 0 0         my $min = ($cost1 < $cost2) ? $cost1 : $cost2;
82 0 0         my $best = ($color == ($Games::Checkers::give_away ? Black : White)) ? $max : $min;
    0          
83 0           return $best == $cost1;
84             }
85              
86             sub choose_best_son ($$$$$) {
87 0     0     my $self = shift;
88 0           my $color = shift;
89 0           my $level = shift;
90 0           my $max_level = shift;
91              
92             # return undef if $stopped || check_user_interaction() != Ok;
93              
94 0           my $best_node = undef;
95 0           my $best_cost = NO_COST;
96              
97 0 0         if ($level != 0) {
98             # should use return value to determine actual thinking level
99 0 0         $self->expand($color) unless $self->{expanded};
100              
101 0           foreach my $son (@{$self->{sons}}) {
  0            
102 0           my ($deep_node, $deep_cost) = $son->choose_best_son(!$color, $level-1, $max_level);
103 0 0 0       ($best_node, $best_cost) = ($deep_node, $deep_cost)
104             if $best_cost == NO_COST || $self->is_better_cost($color, $deep_cost, $best_cost);
105             }
106              
107 0           $self->unexpand;
108             }
109              
110 0 0         if (!defined $best_node) {
    0          
111 0           $best_node = $self;
112 0           $best_cost = $self->get_cost($color);
113             } elsif ($level == $max_level - 1) {
114 0           $best_node = $self;
115             }
116              
117 0 0         return wantarray ? ($best_node, $best_cost) : $best_node;
118             }
119              
120             package Games::Checkers::BoardTree;
121              
122 1     1   6 use Games::Checkers::MoveConstants;
  1         1  
  1         4  
123 1     1   5 use Games::Checkers::BoardConstants;
  1         2  
  1         5  
124              
125             sub new ($$$;$) {
126 0     0 0   my $class = shift;
127 0           my $board = shift;
128 0           my $color = shift;
129 0   0       my $level = shift || DEFAULT_LEVEL;
130              
131 0           my $self = {
132             head => new Games::Checkers::BoardTreeNode($board, NO_MOVE),
133             max_level => $level,
134             real_level => undef,
135             color => $color,
136             };
137              
138 0           return bless $self, $class;
139             }
140              
141             sub choose_best_move ($) {
142 0     0 0   my $self = shift;
143              
144 0           my $max_level = $self->{max_level};
145 0           my $son = $self->{head}->choose_best_son($self->{color}, $max_level, $max_level);
146             # foreach my $son0 (@{$self->{sons}}) {
147             # next if defined $son && $son == $son0;
148             # $son0->unexpand;
149             # }
150 0 0         return NO_MOVE unless $son;
151 0           return $son->{move};
152             }
153              
154             sub choose_random_move ($) {
155 0     0 0   my $self = shift;
156              
157 0           $self->{head}->expand($self->{color});
158 0           my $sons = $self->{head}->{sons};
159 0           my $move = $sons->[int(rand(@$sons))]->{move};
160 0           $self->{head}->unexpand;
161 0           return $move;
162             }
163              
164             1;