X-Git-Url: https://git.sesse.net/?p=remoteglot;a=blobdiff_plain;f=remoteglot.pl;h=7f1a6687e72aa597827212a95a673b429e1b2bd2;hp=3cf3a6586a694e4dcb6a47e6f6020f26c97deff3;hb=7314c1611dbab18a6ebdd5934697dd5cf94ed437;hpb=6da09de90c65452ce191c77e8bf1c1bf38def823 diff --git a/remoteglot.pl b/remoteglot.pl index 3cf3a65..7f1a668 100755 --- a/remoteglot.pl +++ b/remoteglot.pl @@ -11,7 +11,9 @@ use AnyEvent; use AnyEvent::Handle; -use AnyEvent::Loop; +use AnyEvent::HTTP; +use Chess::PGN::Parse; +use EV; use Net::Telnet; use FileHandle; use IPC::Open2; @@ -40,6 +42,7 @@ my @masters = ( # Program starts here $SIG{ALRM} = sub { output(); }; my $latest_update = undef; +my $http_timer = undef; $| = 1; @@ -112,7 +115,7 @@ my $ev1 = AnyEvent->io( } ); # Engine events have already been set up by Engine.pm. -AnyEvent::Loop::run; +EV::run; sub handle_uci { my ($engine, $line, $primary) = @_; @@ -155,60 +158,7 @@ sub handle_uci { sub handle_fics { my $line = shift; if ($line =~ /^<12> /) { - my $pos = Position->new($line); - - # if this is already in the queue, ignore it - next if (defined($pos_waiting) && $pos->fen() eq $pos_waiting->fen()); - - # if we're already chewing on this and there's nothing else in the queue, - # also ignore it - next if (!defined($pos_waiting) && defined($pos_calculating) && - $pos->fen() eq $pos_calculating->fen()); - - # if we're already thinking on something, stop and wait for the engine - # to approve - if (defined($pos_calculating)) { - if (!defined($pos_waiting)) { - uciprint($engine, "stop"); - } - if ($uci_assume_full_compliance) { - $pos_waiting = $pos; - } else { - uciprint($engine, "position fen " . $pos->fen()); - uciprint($engine, "go infinite"); - $pos_calculating = $pos; - } - } else { - # it's wrong just to give the FEN (the move history is useful, - # and per the UCI spec, we should really have sent "ucinewgame"), - # but it's easier - uciprint($engine, "position fen " . $pos->fen()); - uciprint($engine, "go infinite"); - $pos_calculating = $pos; - } - - if (defined($engine2)) { - if (defined($pos_calculating_second_engine)) { - uciprint($engine2, "stop"); - } else { - uciprint($engine2, "position fen " . $pos->fen()); - uciprint($engine2, "go infinite"); - $pos_calculating_second_engine = $pos; - } - $engine2->{'info'} = {}; - } - - $engine->{'info'} = {}; - $last_move = time; - - # - # Output a command every move to note that we're - # still paying attention -- this is a good tradeoff, - # since if no move has happened in the last half - # hour, the analysis/relay has most likely stopped - # and we should stop hogging server resources. - # - $t->cmd("date"); + handle_position(Position->new($line)); } if ($line =~ /^([A-Za-z]+)(?:\([A-Z]+\))* tells you: (.*)$/) { my ($who, $msg) = ($1, $2); @@ -221,6 +171,18 @@ sub handle_fics { } elsif ($msg =~ /^uci (.*?)$/) { $t->cmd("tell $who Sending '$1' to the engine."); print { $engine->{'write'} } "$1\n"; + } elsif ($msg =~ /^pgn (.*?)$/) { + my $url = $1; + $t->cmd("tell $who Starting to poll '$url'."); + AnyEvent::HTTP::http_get($url, sub { + handle_pgn(@_, $url); + }); + } elsif ($msg =~ /^stoppgn$/) { + $t->cmd("tell $who Stopping poll."); + $http_timer = undef; + } elsif ($msg =~ /^quit$/) { + $t->cmd("tell $who Bye bye."); + exit; } else { $t->cmd("tell $who Couldn't understand '$msg', sorry."); } @@ -228,6 +190,86 @@ sub handle_fics { #print "FICS: [$line]\n"; } +sub handle_pgn { + my ($body, $header, $url) = @_; + my $pgn = Chess::PGN::Parse->new(undef, $body); + if (!defined($pgn) || !$pgn->read_game()) { + warn "Error in parsing PGN from $url\n"; + } else { + $pgn->quick_parse_game; + my $pos = Position->start_pos($pgn->white, $pgn->black); + my $moves = $pgn->moves; + for my $move (@$moves) { + my ($from_row, $from_col, $to_row, $to_col, $promo) = $pos->parse_pretty_move($move); + $pos = $pos->make_move($from_row, $from_col, $to_row, $to_col, $promo); + } + handle_position($pos); + } + + $http_timer = AnyEvent->timer(after => 1.0, cb => sub { + AnyEvent::HTTP::http_get($url, sub { + handle_pgn(@_, $url); + }); + }); +} + +sub handle_position { + my ($pos) = @_; + + # if this is already in the queue, ignore it + return if (defined($pos_waiting) && $pos->fen() eq $pos_waiting->fen()); + + # if we're already chewing on this and there's nothing else in the queue, + # also ignore it + return if (!defined($pos_waiting) && defined($pos_calculating) && + $pos->fen() eq $pos_calculating->fen()); + + # if we're already thinking on something, stop and wait for the engine + # to approve + if (defined($pos_calculating)) { + if (!defined($pos_waiting)) { + uciprint($engine, "stop"); + } + if ($uci_assume_full_compliance) { + $pos_waiting = $pos; + } else { + uciprint($engine, "position fen " . $pos->fen()); + uciprint($engine, "go infinite"); + $pos_calculating = $pos; + } + } else { + # it's wrong just to give the FEN (the move history is useful, + # and per the UCI spec, we should really have sent "ucinewgame"), + # but it's easier + uciprint($engine, "position fen " . $pos->fen()); + uciprint($engine, "go infinite"); + $pos_calculating = $pos; + } + + if (defined($engine2)) { + if (defined($pos_calculating_second_engine)) { + uciprint($engine2, "stop"); + } else { + uciprint($engine2, "position fen " . $pos->fen()); + uciprint($engine2, "go infinite"); + $pos_calculating_second_engine = $pos; + } + $engine2->{'info'} = {}; + } + + $engine->{'info'} = {}; + $last_move = time; + + # + # Output a command every move to note that we're + # still paying attention -- this is a good tradeoff, + # since if no move has happened in the last half + # hour, the analysis/relay has most likely stopped + # and we should stop hogging server resources. + # + $t->cmd("date"); +} + sub parse_infos { my ($engine, @x) = @_; my $mpv = ''; @@ -236,26 +278,28 @@ sub parse_infos { # Search for "multipv" first of all, since e.g. Stockfish doesn't put it first. for my $i (0..$#x - 1) { - if ($x[$i] =~ 'multipv') { + if ($x[$i] eq 'multipv') { $mpv = $x[$i + 1]; next; } } while (scalar @x > 0) { - if ($x[0] =~ 'multipv') { + if ($x[0] eq 'multipv') { # Dealt with above shift @x; shift @x; next; } - if ($x[0] =~ /^(currmove|currmovenumber|cpuload)$/) { + if ($x[0] eq 'currmove' || $x[0] eq 'currmovenumber' || $x[0] eq 'cpuload') { my $key = shift @x; my $value = shift @x; $info->{$key} = $value; next; } - if ($x[0] =~ /^(depth|seldepth|hashfull|time|nodes|nps|tbhits)$/) { + if ($x[0] eq 'depth' || $x[0] eq 'seldepth' || $x[0] eq 'hashfull' || + $x[0] eq 'time' || $x[0] eq 'nodes' || $x[0] eq 'nps' || + $x[0] eq 'tbhits') { my $key = shift @x; my $value = shift @x; $info->{$key . $mpv} = $value; @@ -267,7 +311,7 @@ sub parse_infos { delete $info->{'score_cp' . $mpv}; delete $info->{'score_mate' . $mpv}; - while ($x[0] =~ /^(cp|mate|lowerbound|upperbound)$/) { + while ($x[0] eq 'cp' || $x[0] eq 'mate' || $x[0] eq 'lowerbound' || $x[0] eq 'upperbound') { if ($x[0] eq 'cp') { shift @x; $info->{'score_cp' . $mpv} = shift @x; @@ -311,7 +355,7 @@ sub parse_ids { } } -sub prettyprint_pv { +sub prettyprint_pv_no_cache { my ($board, @pvs) = @_; if (scalar @pvs == 0 || !defined($pvs[0])) { @@ -321,7 +365,20 @@ sub prettyprint_pv { my $pv = shift @pvs; my ($from_col, $from_row, $to_col, $to_row, $promo) = parse_uci_move($pv); my ($pretty, $nb) = $board->prettyprint_move($from_row, $from_col, $to_row, $to_col, $promo); - return ($pretty, prettyprint_pv($nb, @pvs)); + return ( $pretty, prettyprint_pv_no_cache($nb, @pvs) ); +} + +sub prettyprint_pv { + my ($pos, @pvs) = @_; + + my $cachekey = join('', @pvs); + if (exists($pos->{'prettyprint_cache'}{$cachekey})) { + return @{$pos->{'prettyprint_cache'}{$cachekey}}; + } else { + my @res = prettyprint_pv_no_cache($pos->{'board'}, @pvs); + $pos->{'prettyprint_cache'}{$cachekey} = \@res; + return @res; + } } sub output { @@ -359,12 +416,12 @@ sub output { eval { my $dummy; if (exists($info->{'pv'})) { - $dummy = prettyprint_pv($pos_calculating->{'board'}, @{$info->{'pv'}}); + $dummy = prettyprint_pv($pos_calculating, @{$info->{'pv'}}); } my $mpv = 1; while (exists($info->{'pv' . $mpv})) { - $dummy = prettyprint_pv($pos_calculating->{'board'}, @{$info->{'pv' . $mpv}}); + $dummy = prettyprint_pv($pos_calculating, @{$info->{'pv' . $mpv}}); ++$mpv; } }; @@ -425,7 +482,7 @@ sub output_screen { } $text .= ":\n"; - $text .= " " . join(', ', prettyprint_pv($pos_calculating->{'board'}, @{$info->{'pv' . $mpv}})) . "\n"; + $text .= " " . join(', ', prettyprint_pv($pos_calculating, @{$info->{'pv' . $mpv}})) . "\n"; $text .= "\n"; ++$mpv; } @@ -433,7 +490,7 @@ sub output_screen { # single-PV my $score = long_score($info, $pos_calculating, ''); $text .= " $score\n" if defined($score); - $text .= " PV: " . join(', ', prettyprint_pv($pos_calculating->{'board'}, @{$info->{'pv'}})); + $text .= " PV: " . join(', ', prettyprint_pv($pos_calculating, @{$info->{'pv'}})); $text .= "\n"; if (exists($info->{'nodes'}) && exists($info->{'nps'}) && exists($info->{'depth'})) { @@ -463,8 +520,8 @@ sub output_screen { eval { my $pv = $info->{'pv' . $mpv}; - my $pretty_move = join('', prettyprint_pv($pos_calculating_second_engine->{'board'}, $pv->[0])); - my @pretty_pv = prettyprint_pv($pos_calculating_second_engine->{'board'}, @$pv); + my $pretty_move = join('', prettyprint_pv($pos_calculating_second_engine, $pv->[0])); + my @pretty_pv = prettyprint_pv($pos_calculating_second_engine, @$pv); if (scalar @pretty_pv > 5) { @pretty_pv = @pretty_pv[0..4]; push @pretty_pv, "..."; @@ -511,7 +568,7 @@ sub output_json { # single-PV only for now $json->{'pv_uci'} = $info->{'pv'}; - $json->{'pv_pretty'} = [ prettyprint_pv($pos_calculating->{'board'}, @{$info->{'pv'}}) ]; + $json->{'pv_pretty'} = [ prettyprint_pv($pos_calculating, @{$info->{'pv'}}) ]; my %refutation_lines = (); my @refutation_lines = (); @@ -524,8 +581,8 @@ sub output_json { eval { my $pv = $info->{'pv' . $mpv}; - my $pretty_move = join('', prettyprint_pv($pos_calculating->{'board'}, $pv->[0])); - my @pretty_pv = prettyprint_pv($pos_calculating->{'board'}, @$pv); + my $pretty_move = join('', prettyprint_pv($pos_calculating, $pv->[0])); + my @pretty_pv = prettyprint_pv($pos_calculating, @$pv); $refutation_lines{$pv->[0]} = { sort_key => $pretty_move, depth => $info->{'depth' . $mpv}, @@ -654,7 +711,7 @@ sub book_info { if ($move eq '') { $pmove = '(current)'; } else { - ($pmove) = prettyprint_pv($board, $move); + ($pmove) = prettyprint_pv_no_cache($board, $move); $pmove .= $annotation; }