Chess-Plisco
view release on metacpan or search on metacpan
lib/Chess/Plisco/Tablebase/Syzygy.pm view on Meta::CPAN
#! /bin/false
# Copyright (C) 2021-2026 Guido Flohr <guido.flohr@cantanea.com>,
# all rights reserved.
# This program is free software. It comes without any warranty, to
# the extent permitted by applicable law. You can redistribute it
# and/or modify it under the terms of the Do What the Fuck You Want
# to Public License, Version 2, as published by Sam Hocevar. See
# http://www.wtfpl.net/ for more details.
# This file is heavily inspired by python-chess.
use strict;
use integer;
use List::Util qw(reduce);
use Locale::TextDomain qw('Chess-Plisco');
use Scalar::Util qw(reftype);
use Chess::Plisco qw(:all);
# Macros from Chess::Plisco::Macro are already expanded here!
my $TBPIECES = 7;
use constant PD_INDEXTABLE => 0;
use constant PD_SIZETABLE => 1;
use constant PD_DATA => 2;
use constant PD_OFFSET => 3;
use constant PD_SYMLEN => 4;
use constant PD_SYMPAT => 5;
use constant PD_BLOCKSIZE => 6;
use constant PD_IDXBITS => 7;
use constant PD_MIN_LEN => 8;
use constant PD_BASE => 9;
use constant INVTRIANGLE => [1, 2, 3, 10, 11, 19, 0, 9, 18, 27];
# FIXME! These are candidates for macros!
my $offdiag = sub {
my ($shift) = @_;
my ($file, $rank) = ($shift & 0x7, $shift >> 3);
return $rank - $file;
};
my $flipdiag = sub {
my ($shift) = @_;
return (($shift >> 3) | ($shift << 3)) & 63;
};
my $read_byte = sub {
my ($data_ref, $offset) = @_;
return ord substr $$data_ref, $offset, 1;
};
my $remove_ep = sub {
my ($pos) = @_;
my $pos2 = $pos->copy;
$pos2->[CP_POS_EN_PASSANT_SHIFT] = 0;
return $pos2;
};
my $is_checkmate = sub {
my ($pos) = @_;
my $game_over = $pos->gameOver or return;
return 1 if ($game_over && CP_GAME_WHITE_WINS);
return 1 if ($game_over && CP_GAME_BLACK_WINS);
};
use constant PD_INDEXTABLE => 0;
use constant PD_SIZETABLE => 1;
use constant PD_DATA => 2;
use constant PD_OFFSET => 3;
use constant PD_SYMLEN => 4;
lib/Chess/Plisco/Tablebase/Syzygy.pm view on Meta::CPAN
my $p = [(0) x $TBPIECES];
my $i = 0;
while ($i < $self->{num}) {
my $piece_type = $self->{pieces}->[$bside]->[$i] & 0x07;
my $colour = ($self->{pieces}->[$bside]->[$i] ^ $cmirror) >> 3;
my $bb = $colour ? ($pos->[$piece_type] & $pos->[CP_POS_BLACK_PIECES]) : ($pos->[$piece_type] & $pos->[CP_POS_WHITE_PIECES]);
while ($bb) {
my $shift = (do { my $B = $bb & -$bb; my $A = $B - 1 - ((($B - 1) >> 1) & 0x5555_5555_5555_5555); my $C = ($A & 0x3333_3333_3333_3333) + (($A >> 2) & 0x3333_3333_3333_3333); my $n = $C + ($C >> 32); $n = ($n & 0x0f0f0f0f) + (($n >> 4) & 0x0f0f0f0...
$p->[$i++] = $shift;
$bb = (($bb) & (($bb) - 1));
}
}
# FIXME! idx is not always used by decompressPairs()!
my $idx = $self->_encodePiece($self->{norm}->[$bside], $p, $self->{factor}->[$bside]);
$res = $self->_decompressPairs($self->{precomp}->[$bside], $idx);
} else {
my $p = [(0) x $TBPIECES];
my $i = 0;
my $k = $self->{files}->[0]->{pieces}->[0]->[0] ^ $cmirror;
my $colour = $k >> 3;
my $piece_type = $k & 0x07;
my $bb = $colour ? ($pos->[$piece_type] & $pos->[CP_POS_BLACK_PIECES]) : ($pos->[$piece_type] & $pos->[CP_POS_WHITE_PIECES]);
while ($bb) {
my $shift = (do { my $B = $bb & -$bb; my $A = $B - 1 - ((($B - 1) >> 1) & 0x5555_5555_5555_5555); my $C = ($A & 0x3333_3333_3333_3333) + (($A >> 2) & 0x3333_3333_3333_3333); my $n = $C + ($C >> 32); $n = ($n & 0x0f0f0f0f) + (($n >> 4) & 0x0f0f0f0f...
$p->[$i++] = $shift ^ $mirror;
$bb = (($bb) & (($bb) - 1));
}
my $f = $self->_pawnFile($p);
my $pc = $self->{files}->[$f]->{pieces}->[$bside];
while ($i < $self->{num}) {
my $colour = ($pc->[$i] ^ $cmirror) >> 3;
my $piece_type = $pc->[$i] & 0x07;
my $bb = $colour ? ($pos->[$piece_type] & $pos->[CP_POS_BLACK_PIECES]) : ($pos->[$piece_type] & $pos->[CP_POS_WHITE_PIECES]);
while ($bb) {
my $shift = (do { my $B = $bb & -$bb; my $A = $B - 1 - ((($B - 1) >> 1) & 0x5555_5555_5555_5555); my $C = ($A & 0x3333_3333_3333_3333) + (($A >> 2) & 0x3333_3333_3333_3333); my $n = $C + ($C >> 32); $n = ($n & 0x0f0f0f0f) + (($n >> 4) & 0x0f0f0f0...
$p->[$i++] = $shift ^ $mirror;
$bb = (($bb) & (($bb) - 1));
}
}
# FIXME! idx is not always used by decompressPairs()!
my $idx = $self->_encodePawn($self->{files}->[$f]->{norm}->[$bside], $p, $self->{files}->[$f]->{factor}->[$bside]);
$res = $self->_decompressPairs($self->{files}->[$f]->{precomp}->[$bside], $idx);
}
return $res - 2;
}
package Chess::Plisco::Tablebase::Syzygy::DtzTable;
$Chess::Plisco::Tablebase::Syzygy::DtzTable::VERSION = 'v1.0.3';
use Chess::Plisco qw(:all);
# Macros from Chess::Plisco::Macro are already expanded here!
use base qw(Chess::Plisco::Tablebase::Syzygy::Table);
use constant TBZ_MAGIC => "\xd7\x66\x0c\xa5";
use constant WDL_TO_MAP => [1, 3, 0, 2, 0];
use constant PA_FLAGS => [8, 0, 0, 0, 4];
use constant PD_INDEXTABLE => 0;
use constant PD_SIZETABLE => 1;
use constant PD_DATA => 2;
use constant PD_OFFSET => 3;
use constant PD_SYMLEN => 4;
use constant PD_SYMPAT => 5;
use constant PD_BLOCKSIZE => 6;
use constant PD_IDXBITS => 7;
use constant PD_MIN_LEN => 8;
use constant PD_BASE => 9;
sub __initTableDtz {
my ($self) = @_;
$self->_initMmap;
if ($self->{initialized}) {
return;
}
$self->_checkMagic(TBZ_MAGIC);
$self->{factor} = [(0) x $TBPIECES];
$self->{norm} = [(0) x $self->{num}];
$self->{tb_size} = [0, 0, 0, 0];
$self->{size} = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
$self->{files} = [
Chess::Plisco::Tablebase::Syzygy::PawnFileDataDtz->new,
Chess::Plisco::Tablebase::Syzygy::PawnFileDataDtz->new,
Chess::Plisco::Tablebase::Syzygy::PawnFileDataDtz->new,
Chess::Plisco::Tablebase::Syzygy::PawnFileDataDtz->new,
];
my $files = ($read_byte->($self->{data}, 4) & 0x2) ? 4 : 1;
my $p_data = 5;
if (!$self->{has_pawns}) {
$self->{map_idx} = [[0, 0, 0, 0]];
$self->__setupPiecesPieceDtz($p_data, 0);
$p_data += $self->{num} + 1;
$p_data += $p_data & 0x01;
$self->{precomp} = $self->_setupPairs($p_data, $self->{tb_size}->[0], 0);
$self->{flags} = $self->{_flags};
$p_data = $self->{_next};
$self->{p_map} = $p_data;
if ($self->{flags} & 2) {
lib/Chess/Plisco/Tablebase/Syzygy.pm view on Meta::CPAN
my $k = $self->{files}->[0]->{pieces}->[0] ^ $cmirror;
my $piece_type = $k & 0x07;
my $colour = $k >> 3;
my $bb = $colour ? ($pos->[$piece_type] & $pos->[CP_POS_BLACK_PIECES]) : ($pos->[$piece_type] & $pos->[CP_POS_WHITE_PIECES]);
my $i = 0;
my $p = [(0) x ($TBPIECES - 1)];
while ($bb) {
my $shift = (do { my $B = $bb & -$bb; my $A = $B - 1 - ((($B - 1) >> 1) & 0x5555_5555_5555_5555); my $C = ($A & 0x3333_3333_3333_3333) + (($A >> 2) & 0x3333_3333_3333_3333); my $n = $C + ($C >> 32); $n = ($n & 0x0f0f0f0f) + (($n >> 4) & 0x0f0f0f0f...
$p->[$i++] = $shift ^ $mirror;
$bb = (($bb) & (($bb) - 1));
}
my $f = $self->_pawnFile($p);
if (($self->{flags}->[$f] & 1) != $bside) {
return 0, -1;
}
my $pc = $self->{files}->[$f]->{pieces};
while ($i < $self->{num}) {
$piece_type = $pc->[$i] & 0x07;
$colour = ($pc->[$i] ^ $cmirror) >> 3;
my $bb = $colour ? ($pos->[$piece_type] & $pos->[CP_POS_BLACK_PIECES]) : ($pos->[$piece_type] & $pos->[CP_POS_WHITE_PIECES]);
while ($bb) {
my $shift = (do { my $B = $bb & -$bb; my $A = $B - 1 - ((($B - 1) >> 1) & 0x5555_5555_5555_5555); my $C = ($A & 0x3333_3333_3333_3333) + (($A >> 2) & 0x3333_3333_3333_3333); my $n = $C + ($C >> 32); $n = ($n & 0x0f0f0f0f) + (($n >> 4) & 0x0f0f0f0...
$p->[$i++] = $shift ^ $mirror;
$bb = (($bb) & (($bb) - 1));
}
}
my $idx = $self->_encodePawn($self->{files}->[$f]->{norm}, $p, $self->{files}->[$f]->{factor});
$res = $self->_decompressPairs($self->{files}->[$f]->{precomp}, $idx);
if ($self->{flags}->[$f] & 2) {
if (!($self->{flags}->[$f] & 16)) {
$res = $read_byte->($self->{data}, $self->{p_map} + $self->{map_idx}->[$f]->[WDL_TO_MAP->[$wdl + 2]] + $res);
} else {
$res = $self->_readUint16($self->{p_map} + 2 * ($self->{map_idx}->[$f]->[WDL_TO_MAP->[$wdl + 2]] + $res));
}
}
if (!($self->{flags}->[$f] & $->[$wdl + 2]) || ($wdl & 1)) {
$res *= 2;
}
}
return $res, 1;
}
package Chess::Plisco::Tablebase::Syzygy;
$Chess::Plisco::Tablebase::Syzygy::VERSION = 'v1.0.3';
use File::Basename qw(basename);
use File::Globstar qw(globstar);
use Locale::TextDomain qw('Chess-Plisco');
use Tie::Cache::LRU;
use Chess::Plisco qw(:all);
# Macros from Chess::Plisco::Macro are already expanded here!
use base qw(Chess::Plisco::Tablebase);
use constant TBW_SUFFIX => 'rtbw';
use constant TBZ_SUFFIX => 'rtbz';
use constant WDL_TO_DTZ => [-1, -101, 0, 101, 1];
sub new {
my ($class, $directory, %__options) = @_;
my %options = (
load_wdl => 1,
load_dtz => 1,
max_fds => 128,
%__options
);
my %tables;
if (0 + $options{max_fds} > 0) {
tie %tables, 'Tie::Cache::LRU', $options{max_fds};
}
my $self = bless {
tables => \%tables,
wdl => {},
dtz => {},
wdl_files => 0,
dtz_files => 0,
}, $class;
$self->addDirectory($directory, %options) if defined $directory;
return $self;
}
sub addDirectory {
my ($self, $directory, %__options) = @_;
my %options = (
load_wdl => 1,
load_dtz => 1,
%__options
);
my (@rtbw_files, @rtbz_files);
$directory = File::Spec->rel2abs($directory);
if ($options{recursive}) {
@rtbw_files = globstar "$directory/**/*.rtbw" if $options{load_wdl};
@rtbz_files = globstar "$directory/**/*.rtbz" if $options{load_dtz};
} else {
@rtbw_files = globstar "$directory/*.rtbw" if $options{load_wdl};
@rtbz_files = globstar "$directory/*.rtbz" if $options{load_dtz};
}
my @files = (@rtbw_files, @rtbz_files);
my $num_files = 0;
foreach my $filename (@files) {
++$num_files if $self->__addFile($filename, %options)
( run in 1.705 second using v1.01-cache-2.11-cpan-5a3173703d6 )