X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=remoteglot.pl;h=8b3a0b776f40678ae3c6e58244580a5fd6e32c60;hb=8733e976299634c8bce83bc8908a99ff669bc2fd;hp=abbbd983a7127abf317ef1c61916d2038db70c76;hpb=b827590935f10fd11c664e513f4daf94c52cce2a;p=remoteglot diff --git a/remoteglot.pl b/remoteglot.pl index abbbd98..8b3a0b7 100755 --- a/remoteglot.pl +++ b/remoteglot.pl @@ -35,7 +35,6 @@ my $output_timer = undef; my $http_timer = undef; my $stop_pgn_fetch = 0; my $tb_retry_timer = undef; -my %tb_cache = (); my $tb_lookup_running = 0; my $last_written_json = undef; @@ -152,7 +151,7 @@ sub handle_uci { return if ($engine->{'stopping'} && $line !~ /^bestmove/); $engine->{'stopping'} = 0; - if ($line =~ /^info/) { + if ($line =~ /^info/ && $line !~ / cluster /) { my (@infos) = split / /, $line; shift @infos; @@ -339,9 +338,12 @@ sub handle_pgn { if ($pgn->result eq '1-0' || $pgn->result eq '1/2-1/2' || $pgn->result eq '0-1') { $pos->{'result'} = $pgn->result; } - $pos->{'history'} = \@repretty_moves; + my @extra_moves = (); + $pos = extend_from_manual_override($pos, \@repretty_moves, \@extra_moves); extract_clock($pgn, $pos); + $pos->{'history'} = \@repretty_moves; + $pos->{'extra_moves'} = \@extra_moves; # Sometimes, PGNs lose a move or two for a short while, # or people push out new ones non-atomically. @@ -452,8 +454,6 @@ sub handle_position { $engine->{'info'} = {}; $last_move = time; - schedule_tb_lookup(); - # # Output a command every move to note that we're # still paying attention -- this is a good tradeoff, @@ -480,8 +480,6 @@ sub parse_infos { } } - my $prev_depth = $info->{'depth1'} // $info->{'depth'}; - while (scalar @x > 0) { if ($x[0] eq 'multipv') { # Dealt with above @@ -536,22 +534,6 @@ sub parse_infos { die "Unknown info '" . join(',', @x) . "'"; } - - my $now_depth = $info->{'depth1'} // $info->{'depth'}; - if (defined($prev_depth) && POSIX::floor($now_depth / 10) > POSIX::floor($prev_depth / 10)) { - my $d = POSIX::floor($now_depth / 10) * 10; # In case we skipped some. - complete_using_tbprobe($pos_calculating, $info, exists($info->{'depth1'}) ? '1' : ''); - my $cp = $info->{'score_cp1'} // $info->{'score_cp'}; - my $mate = $info->{'score_mate1'} // $info->{'score_mate'}; - my $splicepos = $info->{'splicepos1'} // $info->{'splicepos'}; - my $bestmove; - if (defined($info->{'pv1'})) { # Avoid autovivification. - $bestmove = $info->{'pv1'}[0]; - } else { - $bestmove = $info->{'pv'}[0]; - } - push @{$info->{'lowdepth'}}, [ $d, $cp, $mate, $splicepos, $bestmove ]; - } } sub parse_ids { @@ -708,46 +690,6 @@ sub output { # for new positions is off. undef $pos_calculating_started; - # - # If we have tablebase data from a previous lookup, replace the - # engine data with the data from the tablebase. - # - my $fen = $pos_calculating->fen(); - if (exists($tb_cache{$fen})) { - for my $key (qw(pv score_cp score_mate nodes nps depth seldepth tbhits splicepos)) { - delete $info->{$key . '1'}; - delete $info->{$key}; - } - $info->{'nodes'} = 0; - $info->{'nps'} = 0; - $info->{'depth'} = 0; - $info->{'seldepth'} = 0; - $info->{'tbhits'} = 0; - - my $t = $tb_cache{$fen}; - my $pv = $t->{'pv'}; - my $matelen = int((1 + $t->{'score'}) / 2); - if ($t->{'result'} eq '1/2-1/2') { - $info->{'score_cp'} = 0; - } elsif ($t->{'result'} eq '1-0') { - if ($pos_calculating->{'toplay'} eq 'B') { - $info->{'score_mate'} = -$matelen; - } else { - $info->{'score_mate'} = $matelen; - } - } else { - if ($pos_calculating->{'toplay'} eq 'B') { - $info->{'score_mate'} = $matelen; - } else { - $info->{'score_mate'} = -$matelen; - } - } - $info->{'pv'} = $pv; - $info->{'tablebase'} = 1; - } else { - $info->{'tablebase'} = 0; - } - # # Some programs _always_ report MultiPV, even with only one PV. # In this case, we simply use that data as if MultiPV was never @@ -832,6 +774,11 @@ sub output_screen { } return unless (exists($pos_calculating->{'board'})); + + my $extra_moves = $pos_calculating->{'extra_moves'}; + if (defined($extra_moves) && scalar @$extra_moves > 0) { + $text .= " Manual move extensions: " . join(' ', @$extra_moves) . "\n"; + } if (exists($info->{'pv1'}) && exists($info->{'pv2'})) { # multi-PV @@ -952,7 +899,6 @@ sub output_json { $json->{'move_source_url'} = $remoteglotconf::move_source_url; } $json->{'score'} = score_digest($info, $pos_calculating, ''); - $json->{'using_lomonosov'} = defined($remoteglotconf::tb_serial_key); $json->{'nodes'} = $info->{'nodes'}; $json->{'nps'} = $info->{'nps'}; @@ -962,15 +908,6 @@ sub output_json { $json->{'tablebase'} = $info->{'tablebase'}; $json->{'pv'} = [ prettyprint_pv($pos_calculating, @{$info->{'pv'}}) ]; - $json->{'lowdepth'} = {}; - if (exists($info->{'lowdepth'})) { - for my $ld (@{$info->{'lowdepth'}}) { - my $score = score_digest_inner($ld->[1], $ld->[2], $ld->[3], 0, $pos_calculating); - push @$score, prettyprint_pv($pos_calculating, $ld->[4]); - $json->{'lowdepth'}{$ld->[0]} = $score; - } - } - my %refutation_lines = (); my @refutation_lines = (); if (defined($engine2)) { @@ -1099,7 +1036,7 @@ sub output_json { } if (exists($pos_calculating->{'history'}) && - defined($remoteglotconf::json_history_dir)) { + defined($remoteglotconf::json_history_dir) && defined($json->{'engine'}{name})) { my $id = id_for_pos($pos_calculating); my $filename = $remoteglotconf::json_history_dir . "/" . $id . ".json"; @@ -1199,21 +1136,14 @@ sub short_score { # (with side-to-play information) score_sort_key. sub score_digest { my ($info, $pos, $mpv) = @_; - return score_digest_inner( - $info->{'score_cp' . $mpv}, - $info->{'score_mate' . $mpv}, - $info->{'splicepos' . $mpv}, - $info->{'tablebase'}, - $pos); -} -sub score_digest_inner { - my ($score, $mate, $sp, $tablebase, $pos) = @_; - if (defined($mate)) { + if (defined($info->{'score_mate' . $mpv})) { + my $mate = $info->{'score_mate' . $mpv}; if ($pos->{'toplay'} eq 'B') { $mate = -$mate; } - if (defined($sp)) { + if (exists($info->{'splicepos' . $mpv})) { + my $sp = $info->{'splicepos' . $mpv}; if ($mate > 0) { return ['T', $sp]; } else { @@ -1231,11 +1161,12 @@ sub score_digest_inner { } } } else { - if (defined($score)) { + if (exists($info->{'score_cp' . $mpv})) { + my $score = $info->{'score_cp' . $mpv}; if ($pos->{'toplay'} eq 'B') { $score = -$score; } - if ($score == 0 && $tablebase) { + if ($score == 0 && $info->{'tablebase'}) { return ['d', undef]; } else { return ['cp', int($score)]; @@ -1318,6 +1249,34 @@ sub plot_score { return undef; } +sub extend_from_manual_override { + my ($pos, $moves, $extra_moves) = @_; + + my $q = $dbh->prepare('SELECT next_move FROM game_extensions WHERE fen=? AND history=? AND player_w=? AND player_b=? AND (CURRENT_TIMESTAMP - ts) < INTERVAL \'1 hour\''); + while (1) { + my $player_w = $pos->{'player_w'}; + my $player_b = $pos->{'player_b'}; + if ($player_w =~ /^base64:(.*)$/) { + $player_w = MIME::Base64::decode_base64($1); + } + if ($player_b =~ /^base64:(.*)$/) { + $player_b = MIME::Base64::decode_base64($1); + } + #use Data::Dumper; print Dumper([$pos->fen(), JSON::XS::encode_json($moves), $player_w, $player_b]); + $q->execute($pos->fen(), JSON::XS::encode_json($moves), $player_w, $player_b); + my $ref = $q->fetchrow_hashref; + if (defined($ref)) { + my $move = $ref->{'next_move'}; + ($pos) = $pos->make_pretty_move($move); + push @$moves, $move; + push @$extra_moves, $move; + } else { + last; + } + } + return $pos; +} + sub extract_clock { my ($pgn, $pos) = @_; @@ -1436,84 +1395,6 @@ sub find_clock_start { $dbh->commit; } -sub schedule_tb_lookup { - return if (!defined($remoteglotconf::tb_serial_key)); - my $pos = $pos_calculating; - return if (exists($tb_cache{$pos->fen()})); - - # If there's more than seven pieces, there's not going to be an answer, - # so don't bother. - return if ($pos->num_pieces() > 7); - - # Max one at a time. If it's still relevant when it returns, - # schedule_tb_lookup() will be called again. - return if ($tb_lookup_running); - - $tb_lookup_running = 1; - my $url = 'http://tb7-api.chessok.com:6904/tasks/addtask?auth.login=' . - $remoteglotconf::tb_serial_key . - '&auth.password=aquarium&type=0&fen=' . - URI::Escape::uri_escape($pos->fen()); - print TBLOG "Downloading $url...\n"; - AnyEvent::HTTP::http_get($url, sub { - handle_tb_lookup_return(@_, $pos, $pos->fen()); - }); -} - -sub handle_tb_lookup_return { - my ($body, $header, $pos, $fen) = @_; - print TBLOG "Response for [$fen]:\n"; - print TBLOG $header . "\n\n"; - print TBLOG $body . "\n\n"; - eval { - my $response = JSON::XS::decode_json($body); - if ($response->{'ErrorCode'} != 0) { - die "Unknown tablebase server error: " . $response->{'ErrorDesc'}; - } - my $state = $response->{'Response'}{'StateString'}; - if ($state eq 'COMPLETE') { - my $pgn = Chess::PGN::Parse->new(undef, $response->{'Response'}{'Moves'}); - if (!defined($pgn) || !$pgn->read_game()) { - warn "Error in parsing PGN\n"; - } else { - $pgn->quick_parse_game; - my $pvpos = $pos; - my $moves = $pgn->moves; - my @uci_moves = (); - for my $move (@$moves) { - my $uci_move; - ($pvpos, $uci_move) = $pvpos->make_pretty_move($move); - push @uci_moves, $uci_move; - } - $tb_cache{$fen} = { - result => $pgn->result, - pv => \@uci_moves, - score => $response->{'Response'}{'Score'}, - }; - output(); - } - } elsif ($state =~ /QUEUED/ || $state =~ /PROCESSING/) { - # Try again in a second. Note that if we have changed - # position in the meantime, we might query a completely - # different position! But that's fine. - } else { - die "Unknown response state " . $state; - } - - # Wait a second before we schedule another one. - $tb_retry_timer = AnyEvent->timer(after => 1.0, cb => sub { - $tb_lookup_running = 0; - schedule_tb_lookup(); - }); - }; - if ($@) { - warn "Error in tablebase lookup: $@"; - - # Don't try this one again, but don't block new lookups either. - $tb_lookup_running = 0; - } -} - sub open_engine { my ($cmdline, $tag, $cb) = @_; return undef if (!defined($cmdline));