]> git.sesse.net Git - remoteglot/blobdiff - Position.pm
Support flipped boards (no UI, you have to activate it through JS).
[remoteglot] / Position.pm
index c3dbccaedde8b026a9585410c64134798e650d04..3e3e84fd3f0282d55878f1a47ca3d215277c0000 100644 (file)
@@ -19,10 +19,10 @@ sub new {
        $pos->{'board'} = Board->new(@x[1..8]);
        $pos->{'toplay'} = $x[9];
        $pos->{'ep_file_num'} = $x[10];
-       $pos->{'white_castle_k'} = $x[11];
-       $pos->{'white_castle_q'} = $x[12];
-       $pos->{'black_castle_k'} = $x[13];
-       $pos->{'black_castle_q'} = $x[14];
+       $pos->{'white_castle_k'} = $x[11] ? 'h' : undef;
+       $pos->{'white_castle_q'} = $x[12] ? 'a' : undef;
+       $pos->{'black_castle_k'} = $x[13] ? 'h' : undef;
+       $pos->{'black_castle_q'} = $x[14] ? 'a' : undef;
        $pos->{'time_since_100move_rule_reset'} = $x[15];
        $pos->{'player_w'} = $x[17];
        $pos->{'player_b'} = $x[18];
@@ -58,6 +58,7 @@ sub from_fen {
        my $pos = {};
        $board =~ s/(\d)/"-"x$1/ge;
        $pos->{'board'} = Board->new(split /\//, $board);
+       $board = $pos->{'board'};
        $pos->{'toplay'} = uc($toplay);
 
        if ($ep_square =~ /^([a-h])/) {
@@ -66,10 +67,37 @@ sub from_fen {
                $pos->{'ep_file_num'} = -1;
        }
 
-       $pos->{'white_castle_k'} = ($castling =~ /K/) ? 1 : 0;
-       $pos->{'white_castle_q'} = ($castling =~ /Q/) ? 1 : 0;
-       $pos->{'black_castle_k'} = ($castling =~ /k/) ? 1 : 0;
-       $pos->{'black_castle_q'} = ($castling =~ /q/) ? 1 : 0;
+       # X-FEN castling rights parsing.
+       if ($castling =~ /K/) {
+               $pos->{'white_castle_k'} = _col_num_to_letter(_find_piece_col_from_right($board->[7], 'R'));
+       }
+       if ($castling =~ /Q/) {
+               $pos->{'white_castle_q'} = _col_num_to_letter(_find_piece_col($board->[7], 'R'));
+       }
+       while ($castling =~ s/([A-H])//) {
+               my $rook_col = lc($1);
+               my $king_col = _col_num_to_letter(_find_piece_col($board->[7], 'K'));
+               if ($rook_col lt $king_col) {
+                       $pos->{'white_castle_q'} = $rook_col;
+               } else {
+                       $pos->{'white_castle_k'} = $rook_col;
+               }
+       }
+       if ($castling =~ /k/) {
+               $pos->{'black_castle_k'} = _col_num_to_letter(_find_piece_col_from_right($board->[0], 'r'));
+       }
+       if ($castling =~ /q/) {
+               $pos->{'black_castle_q'} = _col_num_to_letter(_find_piece_col($board->[0], 'r'));
+       }
+       while ($castling =~ s/([a-h])//) {
+               my $rook_col = $1;
+               my $king_col = _col_num_to_letter(_find_piece_col($board->[0], 'k'));
+               if ($rook_col lt $king_col) {
+                       $pos->{'black_castle_q'} = $rook_col;
+               } else {
+                       $pos->{'black_castle_k'} = $rook_col;
+               }
+       }
        $pos->{'time_since_100move_rule_reset'} = $halfmove_clock // 0;
        $pos->{'player_w'} = 'white';
        $pos->{'player_b'} = 'black';
@@ -95,12 +123,40 @@ sub fen {
        $fen .= " ";
        $fen .= lc($pos->{'toplay'});
 
-       # castling
+       # Castling (X-FEN compatible).
        my $castling = "";
-       $castling .= "K" if ($pos->{'white_castle_k'} == 1);
-       $castling .= "Q" if ($pos->{'white_castle_q'} == 1);
-       $castling .= "k" if ($pos->{'black_castle_k'} == 1);
-       $castling .= "q" if ($pos->{'black_castle_q'} == 1);
+       if (defined($pos->{'white_castle_k'})) {
+               my $outer_rook_col = _col_num_to_letter(_find_piece_col_from_right($pos->{'board'}[7], 'R'));
+               if ($outer_rook_col eq $pos->{'white_castle_k'}) {
+                       $castling .= "K";
+               } else {
+                       $castling .= uc($pos->{'white_castle_k'});
+               }
+       }
+       if (defined($pos->{'white_castle_q'})) {
+               my $outer_rook_col = _col_num_to_letter(_find_piece_col($pos->{'board'}[7], 'R'));
+               if ($outer_rook_col eq $pos->{'white_castle_q'}) {
+                       $castling .= "Q";
+               } else {
+                       $castling .= uc($pos->{'white_castle_q'});
+               }
+       }
+       if (defined($pos->{'black_castle_k'})) {
+               my $outer_rook_col = _col_num_to_letter(_find_piece_col_from_right($pos->{'board'}[0], 'r'));
+               if ($outer_rook_col eq $pos->{'black_castle_k'}) {
+                       $castling .= "k";
+               } else {
+                       $castling .= $pos->{'black_castle_k'};
+               }
+       }
+       if (defined($pos->{'black_castle_q'})) {
+               my $outer_rook_col = _col_num_to_letter(_find_piece_col($pos->{'board'}[0], 'r'));
+               if ($outer_rook_col eq $pos->{'black_castle_q'}) {
+                       $castling .= "q";
+               } else {
+                       $castling .= $pos->{'black_castle_q'};
+               }
+       }
        $castling = "-" if ($castling eq "");
        # $castling = "-"; # chess960
        $fen .= " ";
@@ -132,35 +188,6 @@ sub fen {
        return $fen;
 }
 
-# Returns a compact bit string describing the same data as fen(),
-# except for the half-move and full-move clock.
-sub bitpacked_fen {
-       my $pos = shift;
-       my $board = $pos->{'board'}->bitpacked_fen();
-
-       my $bits = "";
-       if ($pos->{'toplay'} eq 'W') {
-               $bits .= "0";
-       } else {
-               $bits .= "1";
-       }
-
-       $bits .= $pos->{'white_castle_k'};
-       $bits .= $pos->{'white_castle_q'};
-       $bits .= $pos->{'black_castle_k'};
-       $bits .= $pos->{'black_castle_q'};
-
-       my $col = $pos->{'ep_file_num'};
-       if ($col == -1) {
-               $bits .= "0";
-       } else {
-               $bits .= "1";
-               $bits .= (qw(000 001 010 011 100 101 110 111))[$col];
-       }
-
-       return $board . pack('b*', $bits);
-}
-
 sub to_json_hash {
        my $pos = shift;
        my $json = { %$pos, fen => $pos->fen() };
@@ -172,6 +199,7 @@ sub to_json_hash {
        delete $json->{'white_castle_k'};
        delete $json->{'white_castle_q'};
        delete $json->{'time_since_100move_rule_reset'};
+       delete $json->{'chess960'} if (!$json->{'chess960'});
        if ($json->{'player_w'} =~ /^base64:(.*)$/) {
                $json->{'player_w'} = MIME::Base64::decode_base64($1);
        }
@@ -183,7 +211,7 @@ sub to_json_hash {
 
 sub parse_pretty_move {
         my ($pos, $move) = @_;
-       return $pos->{'board'}->parse_pretty_move($move, $pos->{'toplay'});
+       return $pos->{'board'}->parse_pretty_move($move, $pos->{'toplay'}, $pos->{'chess960'}, $pos->{'white_castle_k'}, $pos->{'white_castle_q'}, $pos->{'black_castle_k'}, $pos->{'black_castle_q'});
 }
 
 sub num_pieces {
@@ -224,19 +252,27 @@ sub make_move {
        $np->{'black_castle_k'} = $pos->{'black_castle_k'};
        $np->{'black_castle_q'} = $pos->{'black_castle_q'};
        if ($piece eq 'K') {
-               $np->{'white_castle_k'} = 0;
-               $np->{'white_castle_q'} = 0;
+               $np->{'white_castle_k'} = undef;
+               $np->{'white_castle_q'} = undef;
        } elsif ($piece eq 'k') {
-               $np->{'black_castle_k'} = 0;
-               $np->{'black_castle_q'} = 0;
-       } elsif ($from_square eq 'a1' || $to_square eq 'a1') {
-               $np->{'white_castle_q'} = 0;
-       } elsif ($from_square eq 'h1' || $to_square eq 'h1') {
-               $np->{'white_castle_k'} = 0;
-       } elsif ($from_square eq 'a8' || $to_square eq 'a8') {
-               $np->{'black_castle_q'} = 0;
-       } elsif ($from_square eq 'h8' || $to_square eq 'h8') {
-               $np->{'black_castle_k'} = 0;
+               $np->{'black_castle_k'} = undef;
+               $np->{'black_castle_q'} = undef;
+       } elsif (defined($np->{'white_castle_q'}) &&
+                ($from_square eq ($np->{'white_castle_q'} . '1') ||
+                 $to_square   eq ($np->{'white_castle_q'} . '1'))) {
+               $np->{'white_castle_q'} = undef;
+       } elsif (defined($np->{'white_castle_k'}) &&
+                ($from_square eq ($np->{'white_castle_k'} . '1') ||
+                 $to_square   eq ($np->{'white_castle_k'} . '1'))) {
+               $np->{'white_castle_k'} = undef;
+       } elsif (defined($np->{'black_castle_q'}) &&
+                ($from_square eq ($np->{'black_castle_q'} . '8') ||
+                 $to_square   eq ($np->{'black_castle_q'} . '8'))) {
+               $np->{'black_castle_q'} = undef;
+       } elsif (defined($np->{'black_castle_k'}) &&
+                ($from_square eq ($np->{'black_castle_k'} . '8') ||
+                 $to_square   eq ($np->{'black_castle_k'} . '8'))) {
+               $np->{'black_castle_k'} = undef;
        }
 
        # 50-move rule.
@@ -247,6 +283,10 @@ sub make_move {
        }
        $np->{'player_w'} = $pos->{'player_w'};
        $np->{'player_b'} = $pos->{'player_b'};
+       $np->{'chess960'} = $pos->{'chess960'};
+       if (exists($pos->{'start_fen'})) {
+               $np->{'start_fen'} = $pos->{'start_fen'};
+       }
        if (defined($pretty_move)) {
                $np->{'last_move'} = $pretty_move;
        } else {
@@ -285,6 +325,11 @@ sub apply_uci_pv {
        return $pvpos;
 }
 
+sub _col_num_to_letter {
+       my $col = shift;
+       return sprintf("%c", ord('a') + $col);
+}
+
 sub _col_letter_to_num {
        return ord(shift) - ord('a');
 }
@@ -293,6 +338,22 @@ sub _row_letter_to_num {
        return 7 - (ord(shift) - ord('1'));
 }
 
+sub _find_piece_col {
+       my ($row, $piece) = @_;
+       for my $col (0..7) {
+               return $col if ($row->[$col] eq $piece);
+       }
+       die "Could not find piece $piece";
+}
+
+sub _find_piece_col_from_right {
+       my ($row, $piece) = @_;
+       for my $col (reverse 0..7) {
+               return $col if ($row->[$col] eq $piece);
+       }
+       die "Could not find piece $piece";
+}
+
 sub _parse_uci_move {
         my $move = shift;
         my $from_col = _col_letter_to_num(substr($move, 0, 1));