File Coverage

blib/lib/Chess/Plisco/Engine/TimeControl.pm
Criterion Covered Total %
statement 28 59 47.4
branch 10 20 50.0
condition 0 3 0.0
subroutine 3 5 60.0
pod 0 3 0.0
total 41 90 45.5


line stmt bran cond sub pod time code
1             #! /bin/false
2              
3             # Copyright (C) 2021 Guido Flohr ,
4             # all rights reserved.
5              
6             # This program is free software. It comes without any warranty, to
7             # the extent permitted by applicable law. You can redistribute it
8             # and/or modify it under the terms of the Do What the Fuck You Want
9             # to Public License, Version 2, as published by Sam Hocevar. See
10             # http://www.wtfpl.net/ for more details.
11              
12             # Make Dist::Zilla happy.
13             # ABSTRACT: Analyze chess games in PGN format
14              
15             package Chess::Plisco::Engine::TimeControl;
16             $Chess::Plisco::Engine::TimeControl::VERSION = '0.4';
17 4     4   2270 use strict;
  4         9  
  4         121  
18              
19 4     4   20 use Time::HiRes qw(gettimeofday);
  4         9  
  4         29  
20              
21             sub new {
22 307     307 0 3196 my ($class, $tree, %params) = @_;
23              
24 307         1149 my $black_to_move = $tree->{position}->toMove;
25              
26 307         938 my $self = {
27             __tree => $tree,
28             };
29 307         673 bless $self, $class;
30              
31 307 100       857 if ($black_to_move) {
32 97         310 $params{mytime} = delete $params{btime};
33 97         258 $params{myinc} = delete $params{binc};
34 97         244 $params{hertime} = delete $params{wtime};
35 97         272 $params{herinc} = delete $params{winc};
36             } else {
37 210         663 $params{mytime} = delete $params{wtime};
38 210         454 $params{myinc} = delete $params{winc};
39 210         416 $params{hertime} = delete $params{btime};
40 210         547 $params{herinc} = delete $params{binc};
41             }
42              
43 307 100       970 if ($params{mate}) {
44 305         1033 $params{depth} = 2 * $params{mate} - 1;
45             }
46              
47 307 50       646 if ($params{depth}) {
48 307         777 $tree->{max_depth} = $params{depth};
49             } else {
50             # Think for 5 seconds by default.
51 0         0 $tree->{allocated_time} = 5000;
52 0         0 delete $tree->{max_depth};
53             }
54              
55             # Initial value for calibration.
56 307         926 $tree->{nodes_to_tc} = 1000;
57              
58 307 50       1652 if ($params{movetime}) {
    50          
    50          
    50          
59 0         0 $tree->{allocated_time} = $params{movetime};
60 0         0 $tree->{fixed_time} = 1;
61             } elsif ($params{infinite}) {
62 0         0 $tree->{max_depth} = Plisco::Engine::Tree->MAX_PLY;
63             } elsif ($params{nodes}) {
64 0         0 $tree->{max_nodes} = $params{nodes};
65             } elsif ($params{mytime}) {
66 0         0 $self->allocateTime($tree, \%params);
67             }
68              
69 307 50       894 if ($params{searchmoves}) {
70 0         0 $tree->{searchmoves} = $params{searchmoves};
71             }
72              
73 307         1701 $tree->{start_time} = [gettimeofday];
74              
75 307         1186 bless $self, $class;
76             }
77              
78             sub allocateTime {
79 0     0 0   my ($self, $tree, $params) = @_;
80              
81             # First get a rough estimate of the moves to go.
82 0           my $mtg = $self->movesToGo;
83              
84 0 0 0       if ($params->{movestogo} && $params->{movestogo} < $mtg) {
85 0           $mtg = $params->{movestogo};
86             }
87              
88 0           my $time_left = $params->{mytime} + $params->{movestogo} * $params->{myinc};
89              
90             # FIXME! This should not be fixed_time but have a better name.
91             # FIXME! Depending on the volatility of the position, there should be
92             # a time cushion that can be used if the evaluation changes a lot between
93             # iterations.
94 0           $tree->{allocated_time} = int (0.5 + $time_left / $mtg);
95             }
96              
97             sub movesToGo {
98 0     0 0   my ($self) = @_;
99              
100             # FIXME! These parameters should be configurable and their defaults
101             # should be tuned!
102 0           my $min_moves_remaining = 20;
103 0           my $max_moves_remaining = 60;
104 0           my $moves_range = $max_moves_remaining - $min_moves_remaining;
105              
106             # We make two very simple assumptions. The popcount of the weaker
107             # party decreases in the course of the game from 16 to 1. That
108             # allows us a linear interpolation for the number of moves to go.
109             # On the other hand, the material imbalance may change from 0
110             # to 9 queens (81 for our purposes). But an imbalance of 10
111             # (one queen plus a pawn) should guaranty a trivial win for the side
112             # to move and we can limit the material imbalance to that.
113             #
114             # And then we simply give each a result a weight with the two results
115             # summing up to 1.0.
116 0           my $popcount_weight = 0.75;
117 0           my $material_weight = (1 - $popcount_weight);
118              
119 0           my $pos = $self->{__tree}->{position};
120 0           my $wpopcount = $pos->bitboardPopcount($pos->whitePieces);
121 0           my $bpopcount = $pos->bitboardPopcount($pos->blackPieces);
122 0           my $material = $pos->material;
123              
124 0 0         my $popcount = $wpopcount < $bpopcount ? $wpopcount : $bpopcount;
125              
126             # Popcount slope and constant offset.
127 0           my $mpc = my $moves_range / (16 - 1);
128 0           my $cpc = $min_moves_remaining - $mpc;
129              
130             # Material imbalance slope and constant offset.
131 0           my $mmc = -$moves_range / 10 - 0;
132 0           my $cmc = $max_moves_remaining;
133              
134             # FIXME! Since this is only done once per ply, a full evaluation of
135             # the position should be done instead of just looking at the material
136             # balance.
137 0           my $mtg = $popcount_weight * ($mpc * $popcount + $cpc)
138             + $material_weight * ($mmc * $material + $cmc);
139              
140 0           return $mtg;
141             }
142              
143             1;