This could be a long shot... any programmers here?

I’m trying to do something weird.

Basically I’d like to convert Algeberic Notation to the computer talk, ie:

  1. e4 e5 to 1. e2e4 e7e5

If anyone here knows of any code samples, doesn’t matter what language it is, I’d appreciate it!

Thunderchicken:

There is the international correspondence notation. It is on page 221 - 222 of the Official Rules of Chess. What is nice about it, you will have a four digit number for all the moves. Example: 1 e4 = e2e4 = 5254.

Nope, that’s not what I need, but thanks anyway.

Computer talk is pgn, not the stuff you have. pgn is pretty much algebraic, except it is strict. What is your goal?

A simple way to convert algebrec notation to a webpage so users can click a button to view:

games.indianachess.org

Basically, people upload their PGN games, i take the data, and I want to write a dynamic program for all 1000 or so of our state’s games.

Chessbase allows you to export games, but the code behind uses a weird type of notation, coordinates from 0-63, thus, moving something like this is written:

  1. e4 Nf6 → 1. e2e4 g8f6 → 1. 5236 0621

Confused? This is how it works:

a8 = 0, b8=1, c8=2, …
a7 = 8, b7=9, …

In a standard 0-63, you can write your moves, so 52 corresponds to e2, e4 corresponds to 36, g8 corresponds to 06, f6 corresponds to 21.

So what I’m trying to do is to take this to get a piece’s starting position in either e2e4 format or how chessbase does it.

I was hoping someone had some way to convert this so i don’t have to spend the weekend into writing it myself :slight_smile:

I guess the principal is simple, I can just take the starting position, then find the piece according to how it moves, pawns stay on a-z, kings only go one place, etc.

Chessbase can export to PGN too. Most chess utilities (except for Chessbase, notably) are based on PGN and FEN. Take a look at http://www.mychess.com. This is also a good site with a list of game and diagram publishing utilities http://www.enpassant.dk/chess/homeeng.htm .

Why reinvent the wheel? If I get you right, all you want is to be able to display a database of games as per the link you list above. There’s plenty of stuff written to do that already.

I’m not clear on what you are trying to accomplish. Your link is to a Java replay file exported from ChessBase. It is true that you can’t do this directly from a PGN file, but you can either drag and drop them into a CBH file, or right-click and convert the PGN to CBH. It sounds like you are looking for a way to convert PGN directly to HTML/Java without going though ChessBase. I’m sure this is possible, but I don’t quite see the point.

15 or so years ago, when I was part of the production team for the Nebraska state chess magazine, I found a C program that converted from various notational systems to others. It worked under Unix, it would probably work on a Linux system with very few changes.

The challenge is finding the source.

The system I had that program on is long gone and I don’t seem to have the source lying around. I probably found it on a source code archive site like Simtel, but that site has changed quite a bit in the last decade (haven’t we all), and I didn’t see anything like that program when I was googling around. Maybe that’ll give you some ideas where to search, though.

If you reallly want to look at source for dealing with PGN, CB files, etc:

http://www.pitt.edu/~schach/Archives/ (or try http://www.pitt.edu/~schach/Archives/index2.html)
http://cap.connx.com/
http://savannah.gnu.org/projects/xboard/

The whole goal is NOT exporting from chessbase, but allowing players to upload their PGN games to a website, then they are viewable from a website.

This way, a player doesn’t have to import 300 chessbase html pages, but dynamically created. That’s where it gets tricky.

Here’s the process

  • Joe User uses chessbase / chessmaster and exports his personal database to PGN format, say he has 100 games.

  • He logs in, uploads the PGN to my website

  • I take that PGN file, split it up into 100 games, now 100 records in my database

  • Sally logs in and looks at the games, and searches for Joe User vs Sally in 2003.

  • She clicks on the Joe vs Sally link, a new page opens up

  • The page gets the source game, moves 1 to 50 and displays it as a java applet or whatever.

I’m now to the last step, after briefly looking at the chessbase export source, brings me to the problem.

I have implemented this process using Java on the server side. The PGN files get a counter prepended to their names to ensure they are unique, and then just stored on the server. The PGN header info is tucked away in a database to provide the search access, and the database is a local, embedded one. Game display in the web browser is done with a free Java applet. You can see it at:

nechessleague.com

I support regular PGN as the uploaded format. If you’re interested in seeing the code, just let me know.

I think you and I are on the right path / same page. Basically what it looks like you’re doing is storing the PGN’s in a database, then temporarily writing them to a pgn file, which is accessed by the applet itself.

I was wanting to specifically pass the game data to the applet / javascript, and not the pgn itself, but as I’m finding out, this might not be the case for me since it might be more trouble than it’s worth.

Thanks for the replies, guys.

You could pass whatever form of data you like that describes the moves of a chess game to an applet, but then you’d need an applet that can interpret that data. I chose a freely available applet (the same one chessgames.com uses) that can interpret PGN files, then wrote my server side code accordingly. That way I didn’t have to write both the server code and the applet code, which saved me quite a bit of time.

Kent

Here’s the code I use on my site (net-chess.com). The function which kicks everything off is the very last one in the source “Move”. You’d call it like:

use Chess;
my $g=new Chess();
$g->Move(“e4”);

The primary thing the code does is convert the PGN style move notation to the start row, start column, end row, and end column. You’ll probably notice that it’s a lot more involved than you thought it would be.

I wrote this about 7 years ago, now there are PGN parsers on cpan.org which may be a little more feature rich. That’s assuming your language is Perl.

package Chess;

sub new{
   my $proto=shift;
   my $class=ref($proto)||$proto;
   my $game={};

   $game->{board}="rnbqkbnrpppppppp................................PPPPPPPPRNBQKBNR";
   $game->{toplay}="white";
   $game->{whitecastleking} = 1;
   $game->{blackcastleking} = 1;
   $game->{whitecastlequeen} = 1;
   $game->{blackcastlequeen} = 1;
   $game->{capturedpieces}="................................";
   $game->{enpassent}=-1;
   $game->{promotepiece}="";
   $game->{debug}=0;
   $game->{movelist}="";
   $game->{statelist}=$game->{board};
   $game->{movenumber}=1;
   $game->{statecount}=1;
   $game->{gameover}="";
   $game->{winner}="";
   $game->{whiteofferdraw}=0;
   $game->{blackofferdraw}=0;
   $game->{drawaccept}=0;
   $game->{whiteresign}=0;
   $game->{blackresign}=0;
   $game->{whitetimeremaining}=-1;
   $game->{blacktimeremaining}=-1;
   $game->{moves}="";

   bless($game,$class);
   return $game;
}

sub copy{
   my $game=shift;
   my ($dest,$key);

   $dest=new Chess;

   foreach $key (keys(%{$game})){
      $dest->{$key}=$game->{$key};
   }
   return $dest;
}

sub ValidMove{
   my $game=shift;
   my ($source,$dest,$piece,$destpiece,$pieceval,$destpieceval);
   my ($erow,$ecol,$srow,$scol);

   #//$game->{move} in format of "rcrc"
   ($scol,$srow,$ecol,$erow)=split(" ",$game->{move});

   $source=((8-$srow)*8)+($scol-1);
   $dest=((8-$erow)*8)+($ecol-1);

   $piece=substr($game->{board},$source,1);
   $destpiece=substr($game->{board},$dest,1);
   $pieceval=ord($piece);
   $destpieceval=ord($destpiece);
   $piece=uc($piece);
   $destpiece=uc($destpiece);

   #//Can't move an empty square
   if($piece eq "."){
      return "Empty Square";
   }

   if($game->{gameover}){
      return "Game is over: $game->{gameover}";
   }

   if($source==$dest){
      return "Not a Move: $source $dest";
   }

   #//Person moving is color to play
   if(($pieceval >90 && $game->{toplay} eq "white") ||
      ($pieceval <90 && $game->{toplay} eq "black")){
         return "It's Not Your Turn to Move";
   }

   #//Dest square isn't occupied by own piece
   $destpiece=substr($game->{board},$dest,1);
   if(((($game->{toplay} eq "white") && ($destpieceval <90)) ||
       (($game->{toplay} eq "black") && ($destpieceval >90))) &&
      ($destpiece ne ".")){
         return "You Can't Capture Your Own Piece";
   }

   #//if bishop:
   if($piece eq "B"){
     if(!(abs($srow-$erow) == abs($scol-$ecol))){
        return "Invalid Move for Bishop";
     }
     if(PiecesBetween($game,$srow,$erow,$scol,$ecol,$source,$dest)){
        return "Bishops Cannot Jump Over Pieces";
     }
   }

   #//If rook:
   if($piece eq "R"){
     if(!(($srow==$erow) || ($scol==$ecol))){
        return "Invalid Move for Rook $srow $erow $scol $ecol";
     }
     if(PiecesBetween($game,$srow,$erow,$scol,$ecol,$source,$dest)){
        return "Rooks Cannot Jump Over Pieces";
     }
   }

   #//Queen:
   if($piece eq "Q"){
     if((abs($srow-$erow) != abs($scol-$ecol)) &&
        !(($srow==$erow) || ($scol==$ecol))){
          return "Invalid Move for Queen";
     }
     if(PiecesBetween($game,$srow,$erow,$scol,$ecol,$source,$dest)){
        return "Queens Cannot Jump Over Pieces";
     }
   }

   #//Knight
   if($piece eq "N"){
     if(!(((abs($scol-$ecol)==2) && (abs($srow-$erow)==1)) ||
          ((abs($srow-$erow)==2) && (abs($scol-$ecol)==1))
       )){
          return "Invalid Move for Knight";
     }
   }

   #//King
   if($piece eq "K"){
     if(abs($scol-$ecol)==2){
        if(($ecol != 3 && $ecol != 7) || ($srow != $erow)){
           return "Invalid move for king";
        }
        if(($ecol == 3) &&
           (($game->{toplay} eq "white" && $game->{whitecastlequeen} != 1) ||
            ($game->{toplay} eq "black" && $game->{blackcastlequeen} != 1))){
            return "Invalid Move for King";
        }
        if(($ecol == 7) &&
           (($game->{toplay} eq "white" && $game->{whitecastleking} != 1) ||
            ($game->{toplay} eq "black" && $game->{blackcastleking} != 1))){
            return "Invalid Move for King";
        }
        #no pieces between, not guarded between
        if($ecol==3 && $erow==8){
           if(#WhiteAttacks($game,2) ||
              WhiteAttacks($game,3) ||
              WhiteAttacks($game,4) ||
              WhiteAttacks($game,5)){
                 return "You Would Move Through Check";
           }
           if(substr($game->{board},0,5) ne "r...k"){
              return "Invalid Move for King";
           }
        }
        if($ecol==3 && $erow==1){
           if(#BlackAttacks($game,58) ||
              BlackAttacks($game,59) ||
              BlackAttacks($game,60) ||
              BlackAttacks($game,61)){
                 return "You Would Move Through Check";
           }
           if(substr($game->{board},56,5) ne "R...K"){
              return "Invalid Move for King";
           }
        }
        if($ecol==7 && $erow==8){
           if(WhiteAttacks($game,5) ||
              WhiteAttacks($game,6) ||
              WhiteAttacks($game,7)){
                 return "You Would Move Through Check";
           }
           if(substr($game->{board},4,4) ne "k..r"){
              return "Invalid Move for King";
           }
        }
        if($ecol==7 && $erow==1){
           if(BlackAttacks($game,61) ||
              BlackAttacks($game,62) ||
              BlackAttacks($game,63)){
                 return "You Would Move Through Check";
           }
           if(substr($game->{board},60,4) ne "K..R"){
              return "Invalid Move for King";
           }
        }
     } else {
        #//One square in any direction
        if(((abs($srow-$erow)>1) || (abs($scol-$ecol)>1))){
           return "Invalid Move for King";
        }
     }
   }

   #//Pawn
   if($piece eq "P"){
     if((abs($srow-$erow)>2) || (abs($scol-$ecol)>1)){
        return "Invalid Move for Pawn: $scol $srow $ecol $erow";
     }
     #//Move one square up/down for white/black
     if(($game->{toplay} eq "white" && $srow > $erow) ||
        ($game->{toplay} eq "black" && $srow < $erow)){
           return "Invalid Move for Pawn";
     }
     #//If start row, move two squares
     if((abs($srow-$erow)==2) && (($srow != 2) && ($srow != 7))){
        return "Invalid Move for Pawn";
     }
     if((abs($srow-$erow)==2)&&
        (
         (($srow==2) && (substr($game->{board},39+$scol,1) ne ".")) ||
         (($srow==7) && (substr($game->{board},15+$scol,1) ne ".")) 
        )
        ){
        return "Pawns Can't Jump Over Pieces";
     }
     if($scol==$ecol && $destpiece ne "."){
        return "Pawns capture diagonally";
     }
     if($scol != $ecol){
        if(abs($srow-$erow)!=1){
           return "Invalid move for pawn";
        }
        if($destpiece eq "." && $dest != $game->{enpassent}){
           return "Invalid move for pawn";
        }
     }
     if(($game->{toplay} eq "white" && $erow == 8 && $game->{promotepiece} eq "") ||
        ($game->{toplay} eq "black" && $erow == 1 && $game->{promotepiece} eq "")){
           return "You must specify a piece to promote the pawn to. (IE e8=Q)";
     }
   }

   return "";
}

sub PiecesBetween{
   my $game=shift;
   my ($srow,$erow,$scol,$ecol,$source,$dest)=@_;
   my ($temp,$x,$y,$count,$result,$board);

   $board=$game->{board};

   if($scol > $ecol){
      $temp=$scol; $scol=$ecol; $ecol=$temp;
      $temp=$srow; $srow=$erow; $erow=$temp;
      $temp=$source; $source=$dest; $dest=$temp;
   }

   $result=0;
   if($ecol==$scol){
      if($erow<$srow){
         $temp=$source; $source=$dest; $dest=$temp;
      }
      for($x=$source-8;$x>$dest;$x-=8){
         if(substr($board,$x,1) ne "."){$result=1;}
      }
   } else {if($erow==$srow){
      for($x=$source+1;$x<$dest;$x++){
         if(substr($board,$x,1) ne "."){$result=1;}
      }
   } else {
      $inc=9;
      if($srow<$erow){$inc=-7;}
      $count=abs($srow-$erow)-1;
      $temp=0;
      for($x=$source+$inc;$temp<$count;$x+=$inc){
         if(substr($board,$x,1) ne "."){$result=1;}
         $temp++;
      }
   }}
   return $result;
}
   
sub ValidateMove{
   my $game=shift;
   my ($result,$game1,@coords);

   $result=ValidMove($game);
   if($result eq ""){
      $game1=$game->copy(); 
      MakeMove($game1);
      $temp=InCheck($game1);
      if($temp ne ""){
         @coords=split(" ",$temp);
         $coords[0]=~tr/[1-8]/[a-h]/;
         $result="You would be in check by the piece on $coords[0]$coords[1]";
      }
   }

   return $result;
}

sub BlackAttacks{
   my $game1=shift; 
   my $sq=shift;
   my ($x,$p,$r,$result);
   my ($srow,$erow,$scol,$ecol,$game);

   $game=$game1->copy();
   $game->{toplay}="black";
   $game->{promotepiece}="q";
   $ecol=(($sq-1)%8)+1;
   $erow=9-(int(($sq-1)/8)+1);
   $result="";
   for($x=0;$x<64;$x++){
      $p=substr($game->{board},$x,1);
      if(ord($p)>90){
         $scol=(($x)%8)+1;
         $srow=9-(int(($x)/8)+1);
         $game->{move}=join(" ",($scol,$srow,$ecol,$erow));
         $r=ValidMove($game);
         if($r eq ""){$result=$game->{move}; $x=65;}
      }
   }
   return $result;
}

sub WhiteAttacks{
   my $game1=shift;
   my $sq=shift;
   my ($x,$p,$r,$result);
   my ($srow,$erow,$scol,$ecol,%game);

   $game=$game1->copy();
   $game->{toplay}="white";
   $game->{promotepiece}="Q";
   $ecol=(($sq-1)%8)+1;
   $erow=9-(int(($sq-1)/8)+1);
   $result="";
   for($x=0;$x<64;$x++){
      $p=substr($game->{board},$x,1);
      if((ord($p)<90)&&($p ne ".")){
         $scol=(($x)%8)+1;
         $srow=9-(int(($x)/8)+1);
         $game->{move}=join(" ",($scol,$srow,$ecol,$erow));
         $r=ValidMove($game);
         if($r eq ""){$result=$game->{move}; $x=65;}
      }
   }
   return $result;
}

sub InCheck{
   my $game=shift;
   my ($s);

   if($game->{toplay} eq "white"){
      return WhiteAttacks($game,index($game->{board},"k")+1);
   } else {
      return BlackAttacks($game,index($game->{board},"K")+1);
   }
}

sub MakeMove{
   my $game=shift;
   my ($source,$dest,$scol,$srow,$erow,$ecol);
   my ($spiece,$cpiece);

   ($scol,$srow,$ecol,$erow)=split(" ",$game->{move});
   $source=((8-$srow)*8)+($scol-1);
   $dest=((8-$erow)*8)+($ecol-1);
   
   $cpiece=substr($game->{board},$dest,1);
   $spiece=substr($game->{board},$source,1);

   substr($game->{board},$dest,1)=substr($game->{board},$source,1);
   substr($game->{board},$source,1)=".";

   $spiece=uc($spiece);

    if($spiece eq "P" && $dest==$game->{enpassent}){
      if($erow==3){
         substr($game->{board},$dest-8,1)=".";
         $cpiece="P";
      }else{
         substr($game->{board},$dest+8,1)=".";         
         $cpiece="p";
      }
    }
    $game->{enpassent}=-1;
    if($spiece eq "P" &&  abs($srow-$erow)==2){
       if($srow==2){
          $game->{enpassent}=$source-8;
       } else {
          $game->{enpassent}=$source+8;
       }
    }

    #//set captured pieces
    substr($game->{capturedpieces},index($game->{capturedpieces},"."),1)=$cpiece;

    #Castle
    if($spiece eq "K" && abs($scol-$ecol)==2){
       if($srow==1){
          if($ecol==7){
             substr($game->{board},60,4)=".RK.";
          } else {
             substr($game->{board},56,5)="..KR.";
          }
       } else {
          if($ecol==7){
             substr($game->{board},4,4)=".rk.";
          } else {
             substr($game->{board},0,5)="..kr.";
          }
       }
    }
    if($spiece eq "K"){
       if($game->{toplay} eq "white"){
          $game->{whitecastleking}=0;
          $game->{whitecastlequeen}=0;
       } else {
          $game->{blackcastleking}=0;
          $game->{blackcastlequeen}=0;
       }
    }

    #//if rook or king set castle status
    if($spiece eq "R"){
       if($srow==8 && $scol==1){
          $game->{blackcastlequeen}=0;
       }
       if($srow==8 && $scol==8){
          $game->{blackcastleking}=0;
       }
       if($srow==1 && $scol==1){
          $game->{whitecastlequeen}=0;
       }
       if($srow==1 && $scol==8){
          $game->{whitecastleking}=0;
       }
    }

    #Add move to compressed move list
    $game->{moves}.=chr((($scol-1)*8)+($srow-1)+48).chr((($ecol-1)*8)+($erow-1)+48);

    #//if promotion change piece
    if($spiece eq "P" && ($erow==8 || $erow==1)){
       substr($game->{board},$dest,1)=$game->{promotepiece};
       $game->{moves}.=uc($game->{promotepiece});
       if($game->{UsersMove}!~/\=/){
          $game->{UsersMove}.="=".uc($game->{promotepiece});
       }
       $game->{promotepiece}="";
    }


    #//Add move to move list
    if($game->{toplay} eq "white"){
       $game->{movelist}.="$game->{movenumber}.$game->{UsersMove}";
    } else {
       $game->{movelist}.=" $game->{UsersMove} ";
    }

    if(($cpiece ne ".") || ($spiece eq "P")){
       $game->{statelist}="";
       $game->{statecount}=0;
    }

    #//Add board to state list
    $game->{statelist}.=$game->{board};
    $game->{statecount}++;

    if($game->{toplay} eq "white"){
       $game->{toplay}="black";
    } else {
       $game->{toplay}="white";
       $game->{movenumber}++;
    }
}

sub UnHashBoard{
   my $self=shift;
   my $hash=shift;
   my ($board,$i,$l,$h,$pieces);

   $board="";
   $pieces=" rnbqkp.RNBQKP";
   for($i=0;$i<32;$i++){
      $board.=substr($pieces,int(ord(substr($hash,$i,1))/16),1);
      $board.=substr($pieces,ord(substr($hash,$i,1)) & 15,1);
   }
   return $board;
}

sub HashBoard{
   my $game=shift;
   my ($hash,$i,$value,$hval,$p);

   for($i=0;$i<64;$i++){
      $p=substr($game->{board},$i,1);
      if($p eq "r"){$value=1;}
      if($p eq "n"){$value=2;}
      if($p eq "b"){$value=3;}
      if($p eq "q"){$value=4;}
      if($p eq "k"){$value=5;}
      if($p eq "p"){$value=6;}
      if($p eq "."){$value=7;}
      if($p eq "R"){$value=8;}
      if($p eq "N"){$value=9;}
      if($p eq "B"){$value=10;}
      if($p eq "Q"){$value=11;}
      if($p eq "K"){$value=12;}
      if($p eq "P"){$value=13;}

      if(($i%2)==0){
         $hval=$value*16;
      } else {
         $hval+=$value; 
         $hash.=pack("C",$hval);
      }
   }
   return $hash;
}

sub DumpGame{
   my $game=shift;
   my $key;

   foreach $key (keys(%{$game})){
      print "$key=$game->{$key}\n";
   }
}

sub DecodeSAN{
   my $game=shift;
   my ($note,$a,$b,$piece,$x,$j,$k,$f,$result);

   $game->{UsersMove}=$game->{move};
   $scol=0; $ecol=0; $srow=0; $erow=0;
   $note=$game->{move};
   $note=~s/\+//g; $note=~s/\s//g; $note=~s/\#//g;
   if($note=~/(\=.)/){
      $game->{promotepiece}=substr($1,1,1);
      if(!($game->{promotepiece}=~/[QRBN]/)){return "SAN syntax error";}
      if($game->{toplay} eq "black"){
         $game->{promotepiece}=lc($game->{promotepiece});
      } else {
         $game->{promotepiece}=uc($game->{promotepiece});
      }
      $note=~s/\=.*//g;
   }
   $b=0;
   $a=substr($note,$b,1);
   if(ord($a)>96){
      if(ord($a)>104){
         return "SAN syntax error";
      }
      $piece="P";
      $scol=$a;
      $b++;
      $a=substr($note,$b,1);
      if($a eq "x"){
         $b++; $a=substr($note,$b,1);
         $ecol=$a; $b++; $a=substr($note,$b,1);
         $erow=$a; $b++;
         if($game->{toplay} eq "white"){
            $srow=$erow-1;
         } else {
            $srow=$erow+1;
         }
      } else {
         $ecol=$scol;
         $erow=$a; $b++;
         #have erow, scol, ecol, need srow
      }
      $a=ord($scol);
      if(($a<97) || ($a>104)){return "SAN syntax error";}
      $a=ord($ecol);
      if(($a<97) || ($a>104)){return "SAN syntax error";}
      if(($erow<1) || ($erow>8)){return "SAN syntax error";}
   } else {
      if($a ne "R" && $a ne "N" && $a ne "B" && $a ne "Q"
         && $a ne "K" && $a ne "0"){
         return "SAN syntax error";
      }
      if(length($note)<3){
         return "SAN syntax error";
      }
      if($note eq "0-0"){
         $piece="K";
         if($game->{toplay} eq "white"){
            $srow=1;
         } else {
            $srow=8;
         }
         $scol=5; $ecol=7; $erow=$srow;
      } else {if($note eq "0-0-0"){
         $piece="K";
         if($game->{toplay} eq "white"){
            $srow=1;
         } else {
            $srow=8;
         }
         $scol=5; $ecol=3; $erow=$srow;
      } else {
         $note=~s/x//g;
         $piece = $a;
         $b++;
         $a=substr($note,$b,1);
         if(length($note) eq 4){
            if((ord($a)>48) && (ord($a)<57)){
               $srow=$a; $b++; $a=substr($note,$b,1);
            } else {
               if((ord($a)<97) || (ord($a)>104)){
                  return "SAN syntax error";
               }
               $scol=$a; $b++; $a=substr($note,$b,1);
            }
         }
         if(length($note) eq 5){
            $scol=$a; $b++; $a=substr($note,$b,1);
            $srow=$a; $b++; $a=substr($note,$b,1);
         }
         if(length($note) > 5){
            return "SAN syntax error";
         }
         $ecol=$a; $b++; $a=substr($note,$b,1);
         $erow=$a; $b++;
         if((ord($ecol)>104) || (ord($ecol)<97) ||
            ($erow <1) || ($erow > 8)){
            return "SAN syntax error.";
         }
      }}
   }
   #must have piece, end row & col.  Find start row & col
   $scol=~tr/[a-h]/[1-8]/;
   $ecol=~tr/[a-h]/[1-8]/;
   $f="";
   $result="";
   if($game->{toplay} eq "black"){$piece=lc($piece);}
   for($x=0;$x<64;$x++){
      if(substr($game->{board},$x,1) eq $piece){
         $j=(($x)%8)+1;
         $k=9-(int(($x)/8)+1);
         if((($j==$scol) || ($scol==0)) &&
            (($k==$srow) || ($srow==0))){
               $game->{move} = join(" ",($j,$k,$ecol,$erow));
               $result=ValidateMove($game);
               if($result eq ""){
                  if($f ne ""){
                     return "Ambiguous move";
                  }
                  $f=$game->{move};
               }
         }
      }
   }
   $game->{move}=$f;
   if($f ne ""){$result="";}
   return $result;
}

sub IsGameOver{
   my $game=shift;
   my ($NotDone,$x,$y,$i,$t,$result,$j,$k,$game1,$r);

   $result="Stalemate";
   #Is checkmate or Stalemate?
   for($i=0;$i<64;$i++){
      $t=substr($game->{board},$i,1);
      if(((ord($t)<90) && ($t ne ".") && ($game->{toplay} eq "white")) ||
         ((ord($t)>90) && ($game->{toplay} eq "black"))){

         $j=(($i)%8)+1;
         $k=9-(int(($i)/8)+1);
         for($x=1;$x<9;$x++){
            for($y=1;$y<9;$y++){
               $game1=$game->copy();
               $game1->{promotepiece}="Q";
					if($game1->{toplay} eq "black"){$game1->{promotepiece}="q";}
               $game1->{move}="$j $k $x $y";
               $r=ValidateMove($game1);
               if($r eq ""){
                  $x=10; $y=10; $i=65;
                  $result="";
               }
            }
         }
      }
   }

   if($result ne ""){
      $game1=$game->copy();
      if($game1->{toplay} eq "white"){
         $game1->{toplay}="black";
      } else {
         $game1->{toplay}="white";
      }
      $r=InCheck($game1);
      if($r ne ""){
         $result="Checkmate";
         $game->{winner}=$game1->{toplay};
      }
   }

   if($result eq ""){
      #is three move repetition?
      $i=0;
      $t=$game->{board};
      for($x=0;$x<$game->{statecount};$x++){
         if(substr($game->{statelist},$x*64,64) eq $t){$i++;}
      }
      if($i>2){$result="Draw: 3 move repetition";}
   }
   return $result;
}

sub EncodeMove{
   my $game=shift;
   my $move=shift;
   my ($sr,$sc,$er,$ec,$p,$c,$m,$s,$e);

   $sc=ord(substr($move,0,1))-96;
   $sr=substr($move,1,1)-1;
   $ec=ord(substr($move,2,1))-96;
   $er=substr($move,3,1)-1;

#   $source=((8-$srow)*8)+($scol-1);
#   $dest=((8-$erow)*8)+($ecol-1);
   $s=$sc+((7-$sr)*8)-1;
   $e=$ec+((7-$er)*8)-1;
   $p=substr($game->{board},$s,1);
   $c=substr($game->{board},$e,1);
   if((lc($p) eq "k") && (abs($sc-$ec)==2)){
      if($ec == 7){$m="0-0";}
      if($ec == 3){$m="0-0-0";}
   } elsif(lc($p) eq "p"){
      $m=substr($move,2,2);
      if($sc != $ec){$m=substr($move,0,1)."x$m";}
   } else {
      $m=uc($p);
      if($c ne "."){
         $m.=substr($move,0,2)."x".substr($move,2,2);
      } else {
         $m.=$move;
      }
   }
   return $m;
}

sub Move{
   my $game=shift;
   my $move=shift;
   my $promote=shift;
   my $prevalidated=shift;
   my ($result);

   $game->{promotepiece}=uc($promote);
   if($game->{toplay} eq "black"){$game->{promotepiece}=lc($game->{promotepiece});}
   if((substr($move,0,1)=~/[a-h]/) && (substr($move,1,1)=~/[1-8]/) &&
      (substr($move,2,1)=~/[a-h]/) && (substr($move,3,1)=~/[1-8]/) &&
      (length($move)==4)){
      $game->{UsersMove}=EncodeMove($game,$move);      
      $move=~tr/[a-h]/[1-8]/;
      $move=join(" ",(substr($move,0,1),substr($move,1,1),substr($move,2,1),
                      substr($move,3,1)));
      $game->{promotepiece}=uc($promote);
      if($game->{toplay} eq "black"){$game->{promotepiece}=lc($game->{promotepiece});}
      $game->{move}=$move;
   } else {
      $game->{promotepiece}="";
      $game->{move}=$move;
      $result=DecodeSAN($game);
   }
   if($result ne ""){return $result;}
   if($prevalidated ne "y"){
      $result=ValidateMove($game);
      if($result ne ""){return $result;}
   }
   MakeMove($game);
   $result=IsGameOver($game);
   if($result){$game->{gameover}=$result;}
   return "";
}
1;