=pod
File "AIFdata.pm", by TTB and KWR.  Module for holding AIF data.

=cut

use warnings;
use strict;
require "Notation.pm";

# use Notation;
# use AIFutility;

package AIFset;

# <AIF setid="Wijk aan Zee 2015" contents="event">

my %AIFsetFields = (
   SetID => "??",
   Contents => "??",
   Sources => "??",
   Items => undef       # Can be a set of events
);

sub new {
   my $class = shift;
   my $self = { %AIFsetFields };
   bless $self, $class;
   return $self;
}


package AIFevent;

use constant { true => 1, false => 0 };
use Scalar::Util qw(looks_like_number);
use Scalar::Util qw(openhandle);
use Scalar::Util qw(weaken);


=pod
<AIFEvent id="Tata Steel Masters" ord="77" div="A">
<Site city="Wijk aan Zee" icc="NED" />
<EventDate iso="2015-01-10" />
<Frame type="tourn" rounds="13" tc="100:50:15+30spm(1)" offers="free" />
<Players num="14" cat="20" />
=cut

my %AIFeventFields = (
   EventID => "??",
   Ordinal => "",
   Division => "",
   City => "??",
   Country => "??",
   EventDate => "??",
   EventType => "??",
   EventRounds => "??",
   TimeControl => "??",
   DrawRules => "??",
   NumPlayers => "??",
   AverageElo => "??",
   Category => "??",
   Games => undef,       # array (alternatively could hash by GameID)
   files => undef,       # from glob
   fileIndex => 0,
   prevLine => "",       # kludge to avoid backseeking, holds last line read if [GameID else ""
   gameHandle => undef,  # mutable filehandle points to next game
);

sub newFromGlob {
   my ($class, $inputFileGlob, $includeMoves) = @_;
   warn "Glob in constructor is $inputFileGlob\n";
   my $self = { %AIFsetFields };

   my @files = glob($inputFileGlob);
   $self->{files} = \@files;

   my $fileName;
   my $line;
   my @gameList = ();

   foreach $fileName (@files) {
      warn "Reading analyzed games in $fileName:\n";
      open(my $FILE, '<', $fileName) or die "Could not open $fileName: $!";
   
      my @gameLines = ();
      while($line = <$FILE>)  { # File may have multiple games, each headed not terminated
   
         if ($line =~ m/^\s*;/ || $line =~ m/^\s*$/) {    # comment lines and blank lines excluded
            next;
         }
         if ($line =~ m/^\[GameID/) {   # head of next game signals to write previous one
            my $currentGameSize = @gameLines;   # zero only at start
            if ($currentGameSize != 0) { # Creating new game object
               push (@gameList, AIFgame->new(\@gameLines, $includeMoves));
               print STDERR ($includeMoves ? "." : ":");
               @gameLines = ();
            }
         }
         push (@gameLines,$line);
      } # Storing the last game
      my $gameSize = @gameLines;
      print STDERR ($includeMoves ? ".\n" : ":\n");
      push (@gameList, AIFgame->new(\@gameLines, $includeMoves));
      close ($FILE);
   }

   if (@gameList) {
      my $numGames = scalar(@gameList);
      $self->{Games} = \@gameList;
      print STDERR "Found $numGames games.\n";
   } else {
      print STDERR "No games parsed.\n";
   }

   my $handle;
   $self->{fileIndex} = 0;
   open ($handle, '<', $files[$self->{fileIndex}]);
   $self->{gameHandle} = *$handle{IO};   # the (in)famous "*foo{THING} syntax"

   bless $self, $class;
   return $self;
}

sub nextGame {   # includes the moves, advances iterator to next game
   my $self = shift;
   my $ghr = $self->{gameHandle};  # already a reference
   if (!openhandle($ghr)) { return ""; }
   my ($line, $game, $currentGameSize);
   my @gameLines = ($self->{prevLine} ? ($self->{prevLine}) : () ); # if not (0 then in game
   while ($line = <$ghr>)  {    # File may have multiple games, each headed but not terminated

      if ($line =~ m/^\s*;/ || $line =~ m/^\s*$/) {    # comment lines and blank lines excluded
         next;
      }
      if ($line =~ m/^\[GameID/) {   # head of next game signals to write previous one
         $currentGameSize = @gameLines;   # zero only at start
         if ($currentGameSize > 0) { # Creating new game object
            $game = AIFgame->new(\@gameLines, true);
            print STDERR ".";
            $self->{prevLine} = $line;  # {gameHandle} auto-changed, fileIndex the same
            return $game;
         }
      }
      push (@gameLines,$line);
   } # Storing the last game in the current file

   $currentGameSize = @gameLines;
   if ($currentGameSize > 0) {
      $game = AIFgame->new(\@gameLines, true);
      print STDERR ".\n";
      $self->{prevLine} = "";
      close($ghr);
      my $ind = 1 + $self->{fileIndex};
      my @files = @{$self->{files}};
      if ($ind <= $#files) {
         open (my $gh, '<', $files[$ind]);
         $self->{gameHandle} = *$gh{IO};  # *foo{IO} stores IO filehandle reference
         $self->{fileIndex}= $ind;
      }
      return $game;
   } else {  # we have called past-end iterator, return nothing
      return "";
   }
}



package AIFgame;
use Scalar::Util qw(weaken);


=pod
[GameID "Plaskett;Tudela;2013.01.03;XXIV;2.7"]
[EngineID "Rybka 3 1-cpu LPV;d13;pv50;r;512"]
[Event "XXIV Roquetas Open"]
[Site "Roquetas de Mar ESP"]
[Date "2013.01.03"]
[Round "2.7"]
[White "Plaskett, J."]
[Black "Tudela Corbalan, C."]
[Result "1-0"]
[ECO "A45"]
[WhiteElo "2495"]
[BlackElo "2242"]
[PlyCount "61"]
[EventDate "2013.01.02"]
[Source "Mark Crowther"]
[SourceDate "2013.01.07"]
[EngineID "Rybka 3 1-cpu LPV"]
[PlyDepth "13"]
[NumPV "50"]
[Hash "512"]

1. d4 Nf6 2. Bg5 d6 3. Nc3 Nbd7 4. e4 e5 5. Nf3 Be7 6. Qd2 c6 7. O-O-O Qc7 8. d5 O-O 9. h3 a6 10. g4 c5 11. Nh4 b5 12. Nf5 Bd8 13. f3 c4 14. Ne2 Rb8 15. Kb1 b4 16. Qe3 a5 17. Neg3 a4 18. Rh2 c3 19. b3 Ra8 20. Bb5 Nc5 21. Nxg7 axb3 22. cxb3 Ra3 23. Bc4 Qa5 24. Bxf6 Bxf6 25. N7h5 Rd8 26. Nxf6+ Kf8 27. Qg5 Nxb3 28. Bxb3 Rxb3+ 29. axb3 Qa3 30. Rc1 Be6 31. Ra2 1-0
=cut

my %AIFgameFields = (
   Event => "??",
   Site => "??",
   Date => "????.??.??",
   Round => "??",
   White => "??",
   Black => "??",
   Result => "??",
   ECO => "??",
   WhiteElo => "??",
   BlackElo => "??",
   PlyCount => "??",
   EventDate => "??",
   Source => "??",
   SourceDate => "??",
   GameID => "??",
   EngineID => "??",
   Platform => "??",
   Threads => "??",
   Hash => "??",
   MultiPV => "??",
   DepthRange => "??",
   EGT => "??",
   Mode => "??",
   FromTurn => "??",
   MultiPV_cp => "??",
   ### Extra Info
   gameBody => "??",
   AIFMoveList => [],
);


# sub new {
#    my $class = shift;
#    my $self = { %AIFgameFields };
#    bless $self, $class;
#    return $self;
# }

sub new {
   my ($class, $gameLinesRef, $includeMoves) = @_;  # last includes all moves for AIF game
   my $self = { %AIFgameFields };
   bless $self,$class;

   my @moves = ();
   my @aifMoveList = ();
  
   my $gameSize = @$gameLinesRef;  # impt because game not terminated
   my $lineCounter = 0;
   my $line = ${$gameLinesRef}[0];

   while ($lineCounter < $gameSize and $line !~ m/^\[GameID/) {   
      $line = ${$gameLinesRef}[++$lineCounter];
   }
   while ($lineCounter < $gameSize and $line =~ m/^\[(\S+)\s+\"([^"]*)\"/) {
      my $fileTag = $1;
      if ($fileTag eq "PlyDepth") { $fileTag = "DepthRange"; }
      elsif ($fileTag eq "HashSize") { $fileTag = "Hash"; }
      elsif ($fileTag eq "NumPV") { $fileTag = "NumPVs"; }

      if (((!defined($self->{$fileTag})) or substr($self->{$fileTag},0,1) eq "?")
            and substr($2,0,1) ne "?") {
         $self->{$fileTag} = $2;
      }
      $line = ${$gameLinesRef}[++$lineCounter];
   }

   # Done with tag pairs.  Until "[GID" the rest is game body

   my $gameBody = "";
   while ($lineCounter < $gameSize and $line !~ m/^\[GID/) {
      chomp($line);
      $gameBody .= &Notation::trim($line) . " ";
      $line = ${$gameLinesRef}[++$lineCounter];
   }
   $gameBody = &Notation::trim($gameBody);
   $self->{gameBody} = $gameBody;

   my @moveLines = ();
   my $prevAIFMove = undef;

#print STDERR "Line after body is $line\n";

   if ($includeMoves) {
      while($lineCounter < $gameSize) {  # $line starts on [GID
         push (@moveLines, $line);
         if ($line =~ m/^=====/) {      # used as end-of-move delimiter
            my $aifMove = AIFmove->new(\@moveLines);
            $aifMove->{gameRef} = \%$self;
            weaken($aifMove->{gameRef});
            if (defined($prevAIFMove)) {
               $prevAIFMove->{nextMoveObjRef} = \%$aifMove;
               # $%{prevAIFMove}->{nextMoveObjRef} = \%{$aifMove};
               $aifMove->{prevMoveObjRef} = \%$prevAIFMove;
               weaken($prevAIFMove->{nextMoveObjRef});
               weaken($aifMove->{prevMoveObjRef});
            }
            $prevAIFMove = \%$aifMove;
            push (@aifMoveList, $aifMove);
            @moveLines = ();
         }
         $line = ${$gameLinesRef}[++$lineCounter];
      }
      $self->{AIFMoveList} = \@aifMoveList; 
   }
   
   return $self;
}

sub DESTROY {
   my $self = shift;
   my $num = scalar (@{$self->{AIFMoveList}});
   for (my $i = 0; $i < $num; $i++) {
      undef($self->{AIFMoveList}[$i]);
   }
   undef(@{$self->{AIFMoveList}});
   #print STDERR "-G\n";
}


1; # To make Perl happy again


package AIFmove;
use Scalar::Util qw(weaken);

=pod
[GID "Plaskett;Tudela;2013.01.03;XXIV;2.7"]
[EID "Rybka 3 1-cpu LPV;d13;pv50;r;512"]
[MoveNo "9-w"]
[MovePlayed "h3"]
[EngineMove "Bxf6"]
[FEN  "r1b2rk1/ppqnbppp/2pp1n2/3Pp1B1/4P3/2N2N2/PPPQ1PPP/2KR1B1R w - - 1 9"]
[RepCount "0"]
[Eval " -013"]
[PrevEval " n.a."]
[NextEval " -036"]
[NumLegalMoves "37"]
[LegalMoves "a3, b3, g3, h3, a4, b4, g4, h4, dxc6, Nb1, Ne2, Na4, Nb5, Ne1, Ng1, Nd4, Nh4, Nxe5, Be3, Bf4, Bh4, Bxf6, Bh6, Be2, Bd3, Bc4, Bb5, Ba6, Re1, Rg1, Qe1, Qe2, Qd3, Qe3, Qd4, Qf4, Kb1"]
[FiftyMR "1"]
[RepLine1 "??"]
[RepLine2 "??"]
[NodeCount "35239180"]
=cut

my $PRUN_DIFF = 500; # Subtract from value of best move when multipv_cp in force
my $patCastles = '([O0]-[O0])((-[O0])?)'; # NOTE: 0-0-0 already matches '([O0]-[O0])'
my $patNonCastle = '([KQRBN]?)([a-h]?)([1-8]?)([-x:]?)([a-h])([1-8])[QRBN]?';
my $patNote = "($patCastles|$patNonCastle)";  # double quotes are vital!

my %AIFmoveFields = (  # First one must exactly match AIF field names
   GID => "??",   # Any non-? value will overwrite.
   EID => "??",
   Turn => "??",
   MovePlayed => "??",
   EngineMove => "??",
   Eval => "??",
   Depth => "??",           # might be less than min of game's depth range---?
   NodeCount => "??",
   FEN  => "??",
   RepCount => "??",
   NumLegalMoves => "??",
   LegalMoves => "??",      # Stores array reference?  or string?
   FiftyMR => "??",
   RepLine1 => "??",
   RepLine2 => "??",

   ###### Extra Information---lowercase since not AIF field
   gameRef => undef,             # caller sets the refs
   prevMoveObjRef => undef,
   nextMoveObjRef => undef,
   turnNumber => undef,
   whiteToMove => undef,
   actualDepth => undef,         # might be less than min of game's depth range
   numDepths  => 0,              # might not be same as actualDepth field if engine starts at 4 say
   numConsideredMoves => 0,      # Can be greater, especially in Single-PV mode
   consideredMovesRef => undef,  # string of all moves reported at any depth
   evalLines => undef,           # Lines of AIF move-eval section read as array of strings.
   evalMatrix => undef,          # Stores the eval Information after NREC,PRUN are removed.
   bestEvalByDepth => undef,     # return value of fillEvals method
   moveHashRef => undef,         # Stores Hash ref from Move to Index
);

# sub new {
#    my $class = shift;
#    my $self = { %AIFmoveFields };
#    bless $self, $class;
# }

# Constructor for AIFmoveFields.  Is passed copied chunk of lines.
# Could instead be passed ref to whole file and starting line number.
#
sub new {
   my ($class, $moveLinesRef) = @_;
   my $self = { %AIFmoveFields };
   bless $self,$class;

   my $moveCounter = 0;
   my @consideredMoves = ();
   my @evalMatrix= ();  
   my %moveHash = ();
   my $numLines = @{$moveLinesRef};
   my $lineCounter = 0;
   my $line = ${$moveLinesRef}[0];   # assumed positioned at or before next move
   while ($line !~ m/^\[GID/) {   # hop over any intervening lines
      $line = $$moveLinesRef[++$lineCounter];
      # $line = $movesLineRef[++$lineCounter];
   }
   while ($line =~ m/^\[(\S+)\s+\"([^"]*)\"/) {
      my $field = $1;
      if ($field eq "MoveNo") { $field = "Turn"; }
      if ($field eq "PrevEval" || $field eq "NextEval") {
         $line = ${$moveLinesRef}[++$lineCounter];
         next;
      }
      if (substr($self->{$field},0,1) eq "?" and substr($2,0,1) ne "?") {
         $self->{$field} = $2;
      }
      $line = ${$moveLinesRef}[++$lineCounter];
   }
   $self->{LegalMoves} =~ s/,\s+/,/g;  #so comma is only separator
   my $gid = $self->{GID};
   my $eid = $self->{EID};
   my $turn = $self->{Turn};

   # Done with tag pairs.  Now fill remaining fields.
   # prev and next move refs are set by the caller.
  
   my $wtm;
   if ($turn =~ m/(\d+)-([wb])/) {
      $self->{turnNumber} = (0 + $1);
      $self->{whiteToMove} = $wtm = ($2 eq "w");
   } else {
      print STDERR "Bad turn number in $gid, turn $turn\n";
   }
=pod
   if ($eid =~ m/;d(\d+);pv(\d+);/) {
      $self->{depth} = $1;
      $self->{numPVs} = $2;
   } else {
      print STDERR "Bad engine ID $eid in game $gid\n";
   }
=cut

   $self->{actualDepth} = $self->{Depth};   # for now

  
   #For legal moves, storing array reference instead of string
   #my @legalMoveList = split(' ',$self->{LegalMoves});
   #$self->{LegalMoves} =\@legalMoveList;

   while ($lineCounter < $numLines and $line !~ m/^$patNote/) {
      $line = ${$moveLinesRef}[++$lineCounter];
   } #now on first analysis line

   if ($lineCounter == $numLines) { 
      chomp(my $firstLine = ${$moveLinesRef}[1]);
      my $thirdLine = ${$moveLinesRef}[3];
      print STDERR "No eval data at $firstLine:$thirdLine\n";
   }

   # while ($line =~ m/^($patNote)\s+(.*)$/) {
   while ($lineCounter < $numLines and $line !~ m/^=====/
                                   and $line =~ m/^(\S+)\s+(.*)$/) {
      push (@{$self->{evalLines}}, $line);  #kept to write as string
      my $move = $1;
      my $evalPart = $2;

      #Once again we store all glyphs internally as centipawn values for
      #purpose of ordering by numerical comparisons.
      #Next we translate all glyphs to numeric values, leaving existing +- in place.
      #Perl /eg evaluates the object being substituted

      if ($evalPart =~ m/[MmXxCc]/) {
         $evalPart =~ s/M(\d+)/$Notation::MATEVAL - $1/eg;
         $evalPart =~ s/(\d+)M/$Notation::MATEVAL - $1/eg;
         $evalPart =~ s/(\d+)m/$1 - $Notation::MATEVAL/eg;
         $evalPart =~ s/(\d+)X/10*$1/eg;
         $evalPart =~ s/(\d+)x/-10*$1/eg;
         $evalPart =~ s/(\d+)C/100*$1/eg;
         $evalPart =~ s/(\d+)c/-100*$1/eg;
      }
=pod
      my $toMate;
      if ($evalPart =~ m/\+M(\d+)/) {
         $toMate = 100*$Notation::MATEVAL - $1;  # mate in 1 counts as less value than checkmate
         $evalPart =~ s/\+M\d+/$toMate/g;
      } elsif ($evalPart =~ m/-M(\d+)/) {
         $toMate = -100*$Notation::MATEVAL + $1; # MATEVAL is in ab.00 units not centipawns
         $evalPart =~ s/-M\d+/$toMate/g;
      }
=cut

      push (@consideredMoves,$move);
      $moveHash{$move} = $self->{numConsideredMoves};   #0-based
      $self->{numConsideredMoves} += 1;
      my @moveEvalList = split(' ', $evalPart);  #splits on \s+
      push (@evalMatrix,\@moveEvalList);
      my $numDepths = scalar (@moveEvalList);
      if ($numDepths > $self->{numDepths}) {
         $self->{numDepths} = $numDepths;
      }
      $line = ${$moveLinesRef}[++$lineCounter];
   }
   
   # Now a very tricky point.  Say {numPVs} = 3.  The top 3 moves at
   # depth 19 may differ from the top 3 moves at depth 8.  We want to
   # infer lower-depth values for those moves, which might be PRUN or NREC.
    
   my @bestEvalsByDepth = $self->fillEvals(\@evalMatrix,$wtm,$self->{numConsideredMoves});
   $self->{evalMatrix} = \@evalMatrix;
   $self->{bestEvalByDepth} = \@bestEvalsByDepth, # return value
   $self->{moveHashRef} = \%moveHash;
   $self->{consideredMovesRef} = \@consideredMoves;

   return $self;
}


sub DESTROY {
   my $self = shift;
   my $num = scalar (@{$self->{evalMatrix}});
   for (my $i = 0; $i < $num; $i++) {
      undef($self->{evalMatrix}[$i]);
   }

   undef(@{$self->{evalMatrix}});
   undef(%{$self->{moveHash}});
   # print STDERR "-";
}

sub writeAIFmove {    # or can just convert to string
   my $self = shift;

}


sub moveToString {
   my $self = shift;

}

=pod
Sometimes engines don't report values for all depths even on the best move when requested.  
Example: 
[GID "Mamedyarov;Stefanova;2014.11.26;Qatar;1.3"]
[EID "Komodo-8-32bit;d17;pv1;r;512"]
[MoveNo "41-w"]
[MovePlayed "Rxd3"]
[EngineMove "Rxd3"]
[FEN  "8/4kp2/1R1p3p/rp6/2bKPP1P/1P1r4/P2R4/8 w - - 0 41"]

Rxd3 is the only legal move.
--------------------------------------------------------------------------------------------
         1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17
--------------------------------------------------------------------------------------------
Rxd3   +100 +100 +100 +086 +099 NREC NREC NREC NREC NREC NREC NREC NREC NREC NREC NREC NREC
=cut

sub fillEvals {  # Gets called in the constructor
   my ($self,$evalRef,$whiteToMove,$numConsideredMoves) = @_;

   foreach my $row (0..$numConsideredMoves-1) {
      # first replace "n.a." with next-higher-depth value.
      foreach my $col (reverse(0..$self->{numDepths}-2)) {
         if (($evalRef->[$row])->[$col]) {
            if (($evalRef->[$row])->[$col] eq "n.a.") {
               ($evalRef->[$row])->[$col] = ($evalRef->[$row])->[$col+1];
            }
         } else {
            print STDERR "Bad eval entry ($row,$col) at turn ".$self->{Turn}." in game\n";
            print STDERR "".$self->{GID};
         }
      }
   }
   # Also replace any NRECs in the top row with latest value.
   
   my $lastEval = 0;
   foreach my $col (0..$self->{numDepths}-1) { 
      if (($evalRef->[0])->[$col] eq "NREC") {
         ($evalRef->[0])->[$col] = $lastEval;
      } else {
         $lastEval = ($evalRef->[0])->[$col];
      }
   }
   $self->{Eval} = $lastEval;

# Before pushing replace any PRUN by bestmove+PRUN_ADD worst move;
# Replace PRUN by bestmove+PRUN_ADD  or Worst move; 
# whichever is lower; DO that once PRUN is done
# Replace NREC by the worst Evaluation

   # Getting the best eval for each depth
   #
   my @bestEvals = ();
   my $limit = $self->{numDepths}-1;
   foreach my $col (0 .. $limit) {
      my $bestEval = -999;  # just for this column; this and worstEval
      my $worstEval = +999; # from player perspective, inverted at end
      my $prunValue ;
      foreach my $row (0 .. ($numConsideredMoves-1)) {
         my $eval = ($evalRef->[$row])->[$col];
         if ($eval eq "PRUN" || $eval eq "NREC") {
            next;
         } else {
            my $stmeval = ($whiteToMove ? $eval : -$eval);
            if ($stmeval > $bestEval) { $bestEval = $stmeval; }
            if ($stmeval < $worstEval) { $worstEval = $stmeval; }
         }
      }

      if ($bestEval-$PRUN_DIFF < $worstEval) {
         $prunValue = $bestEval-$PRUN_DIFF;
      } else {
         $prunValue = $worstEval;
      }
      $prunValue = ($whiteToMove) ? $prunValue : -$prunValue;
      $worstEval = ($whiteToMove) ? $worstEval : -$worstEval;
      $bestEval = ($whiteToMove) ? $bestEval : -$bestEval;

      foreach my $row (0 .. ($numConsideredMoves-1)) {

         if (($evalRef->[$row])->[$col] eq "PRUN") {

            ($evalRef->[$row])->[$col] = $prunValue;  
            $worstEval = $prunValue;  # updated values needed next for NREC
         } 
      }
      foreach my $row (0 .. ($numConsideredMoves-1)) {
         if (($evalRef->[$row])->[$col] eq "NREC") {
            ($evalRef->[$row])->[$col] = $worstEval; 
         }
      }

      push (@bestEvals, $bestEval); # all from White's perspective again
   }
   return @bestEvals;
}


sub getLegalMoves{  # returns array
   my $self = shift;
   return split(',', $self->{LegalMoves});
}

sub getBestEvalAtDepth { # From *White's* perspective
   my ($self,$depth) = @_;
   my $atDepth = ($depth <= $self->{actualDepth} ? $depth : $self->{actualDepth});
   my $offset = 0;  # $self->{actualDepth} - $self->{numDepths};
   return 0 + $self->{bestEvalByDepth}[$atDepth - $offset - 1];
}

sub getPenDepthEval {   # From *White's* perspective
   my $self = shift;
   return $self->getBestEvalAtDepth($self->{actualDepth} - 1);
}


# Get Raw Eval
sub getRawEvalForMoveAtDepth {
   my ($self,$move,$depth)  = @_;
   my $moveIndex = $self->{moveHashRef}{$move};
   # my $moveIndex = $self->{moveHashRef}->{$move};

# print STDERR "$move at depth $depth has index $moveIndex.\n";

   if (!defined $moveIndex) {
      print STDERR "No entry for $move at turn ".$self->{Turn}." in ".$self->{GID}."\n";
      return undef;  # KWR2TTB: changed from "NULL"
   }

   my $ret = $self->{evalMatrix}->[$moveIndex]->[$depth-1] ;
   if (!Scalar::Util::looks_like_number($ret)) {
      print STDERR "No return $ret for $move at depth $depth in turn ".$self->{Turn}." in ".$self->{GID}."\n";
      return undef;
   }
   return $ret;
}

sub getRawEvalPlayedMoveAtHighestDepth {
   my $self = shift;
   return $self->getRawEvalForMoveAtDepth($self->{MovePlayed},$self->{actualDepth});
}

sub getRawEvalsAtDepth {  #sorted by order at top depth, not here
   my ($self,$depth)  = @_;
   my @evalsAtDepth = ();
   foreach my $row (@{$self->{evalMatrix}}) {
      push(@evalsAtDepth,$row->[$depth-1]);
   }
   return @evalsAtDepth;
}

sub getRawEvalsForMove {
   my ($self,$move) = @_;
   my @evalsForMove = ();  # 0-based list of 1-based evals over depths
   my $moveIndex = $self->{moveHashRef}->{$move};
   for my $depth (1 .. $self->{actualDepth}) {
      push (@evalsForMove, $self->{evalMatrix}->[$moveIndex]->[$depth-1]);
   }
   return @evalsForMove;
}
      

1; # To make Perl happy

