Любой двухходовки, трехходовки и т.д.
Кроме этюдов
Для задач только полный перебор графа вариантов.
Там нет обычно никакого позиционного понимания, в задачах, и нечего вырезать из расчета.
В этюдах может быть, но и там лучше считать все.
Известная история, как Ботвинник, возомнивший себя великим алгоритмистом, заставил свой драндулет Пионер решать этюд Надареишвили
В результате он фактически захардкодил свое решение этюда в программу, а оно оказалось неверным, как выяснилось потом
Проблемы
Первая проблема заключается в том, что, хотя одни и те же шахматные позиции часто повторяются во время альфа-бета-поиска, мы можем рассчитывать только на то, что это произойдет где-то в пределах 15% -20%.
Поэтому на каждые 100 записей, которые мы сохраняем в нашей таблице транспозиции, мы можем использовать только 20 записей.
Следовательно, какую бы схему таблицы транспозиции мы ни выбрали, она должна быть очень эффективной, потому что мы будем хранить и искать больше бесполезных записей, чем полезных.
В идеальном мире у нас была бы возможность просто сохранять каждый узел, который мы ищем, в нашей таблице транспозиции. К сожалению, это просто непрактично. Требования к памяти для этой схемы были бы выше, чем могут вместить большинство компьютеров. Кроме того, время, потраченное на поиск в такой большой таблице, перевесило бы любые преимущества экономии времени. Поэтому мы должны признать, что наша таблица транспозиции ограниченного размера не будет хранить все узлы, которые мы ищем.
Warning: Spoiler![ Click to expand ][ Click to hide ]
{
/**********************************\
==================================
Transposition table
==================================
\**********************************/
}
// Writeln(Format('Memory=%uMb',[GetMemoryUsed div 1048576]));
const
// размер массива в элементах
TableSize = 2000000; // Размер таблицы в количестве записей
//если запись не найдена
no_hash_entry:integer = 100000;
type
THashflag = (hash_flag_exact, hash_flag_alpha, hash_flag_beta);
hash_table = record
hash_key: UInt64;
depth: Integer;
flag: THashflag;
score: Integer;
end;
TTranspositionHashTable = record
size: Integer;
mask: Integer;
data: array of hash_table;
end;
var
tt: TTranspositionHashTable;
//https://stackoverflow.com/questions/437683/how-to-get-the-memory-used-by-a-delphi-program?utm_source=pocket_saves
function GetMemoryUsed: UInt64;
var
st: TMemoryManagerState;
sb: TSmallBlockTypeState;
begin
GetMemoryManagerState(st);
result := st.TotalAllocatedMediumBlockSize
+ st.TotalAllocatedLargeBlockSize;
for sb in st.SmallBlockTypeStates do begin
result := result + sb.UseableBlockSize * sb.AllocatedBlockCount;
end;
end;
// clear TT (hash table)
procedure clear_hash_table(flag:boolean);
var
index:integer;
begin
// Выделяем память под таблицу
SetLength(tt.data, TableSize);
// Инициализируем каждую запись в таблице
for index := 0 to TableSize - 1 do begin
tt.data[index].hash_key := 0; // Значение по умолчанию для ключа
tt.data[index].depth := 0; // Значение по умолчанию для глубины
tt.data[index].score := 0; // Значение по умолчанию для значения
tt.data[index].flag := hash_flag_exact; // Значение по умолчанию для флага
end;
// Устанавливаем размер и маску таблицы
tt.size := TableSize;
tt.mask := TableSize - 1;
if flag then begin
//Чтобы перевести байты в мегабайты,
//необходимо разделить количество байт на 1 048 576 (1024 * 1024),
//так как 1 мегабайт содержит 1 048 576 байт.
//Таким образом, 8390836 байта равны 8 мегабайтам.
if Board<>nil then Info.PrintInfo(Format('Memory=%uMb',[GetMemoryUsed div 1048576]))
else Writeln(Format('Memory=%uMb',[GetMemoryUsed div 1048576]));
end;
end;
Сначала нам нужно выяснить, как однозначно идентифицировать каждую позицию, с которой мы сталкиваемся. Это должно быть чрезвычайно эффективным, поскольку нам придется делать это для каждого узла в нашем поиске. Простое преобразование шахматной доски в строку значений типа FEN выполняется слишком медленно.
К счастью для нас, процесс индексации игровых позиций, называемый хешированием Zobrist, уже изобретен профессором Висконсинского университета по имени Альберт Зобрист.
Хэш Zobrist, однозначно представляющий нашу шахматную доску, будет 64-разрядной переменной.
Warning: Spoiler![ Click to expand ][ Click to hide ]
var
// pseudo random number state
//cardinal - беззнаковое целое
stateU32:cardinal = 1804289383;
{/**********************************\
==================================
Random numbers
==================================
\**********************************/}
// generate 32-bit pseudo legal numbers
function get_random_U32_number():cardinal;
begin
// XOR shift algorithm
stateU32:=stateU32 xor (stateU32 shl 13);
stateU32:=stateU32 xor (stateU32 shr 17);
stateU32:=stateU32 xor (stateU32 shl 5);
get_random_U32_number:=stateU32;
end;
// generate 64-bit pseudo legal numbers
function get_random_U64_number():UInt64;
var
// define 4 random numbers
n1,n2,n3,n4:UInt64;
begin
n1:=0;n2:=0;n3:=0; n4:=0;
// init random numbers slicing 16 bits from MS1B side
n1:=(n1 or (get_random_U32_number())) and $FFFF;
n2:=(n2 or (get_random_U32_number())) and $FFFF;
n3:=(n3 or (get_random_U32_number())) and $FFFF;
n4:=(n4 or (get_random_U32_number())) and $FFFF;
// return random number
get_random_U64_number:=n1 or (n2 shl 16) or (n3 shl 32) or (n4 shl 48);
end;
// generate magic number candidate
function generate_magic_number():UInt64;
begin
result:=get_random_U64_number() and get_random_U64_number() and get_random_U64_number();
end;
{
/**********************************\
==================================
Zobrist keys
==================================
\**********************************/
}
var
// random piece keys [piece][square]
piece_keys:array[Tencode_pieces,TBoard_squares] of UInt64;
// random enpassant keys [square]
enpassant_keys:array[TBoard_squares] of UInt64;
// random castling keys
castle_keys:array[0..15] of UInt64;
// random side key
side_key:UInt64;
// init random hash keys
procedure init_random_keys();
var
bb_piece:Tencode_pieces;
square:TBoard_squares;
j:integer;
begin
// update pseudo random number state
stateU32 := 1804289383;
// loop over piece codes
for bb_piece := P_ to k do begin
// loop over board squares
for square := a8 to h1 do begin
// init random piece keys
piece_keys[bb_piece][square] := get_random_U64_number();
end;
end;
// loop over board squares
for square := a8 to h1 do begin
// init random enpassant keys
enpassant_keys[square] := get_random_U64_number();
end;
// loop over castling keys
for j := 0 to 15 do begin
// init castling keys
castle_keys[j] := get_random_U64_number();
end;
// init random side key
side_key := get_random_U64_number();
end;
Коллизии
Прямо сейчас вы, возможно, заметили, что 64-битное значение недостаточно велико для представления всех возможных шахматных позиций. Таким образом, используя приведенную выше схему, можно получить 2 разные шахматные позиции, оцениваемые с одним и тем же 64-битным хэш-значением. Это называется коллизией, и независимо от того, как вы это реализуете, у вас всегда будет небольшая вероятность коллизии. Ключевым моментом здесь является минимизация вероятности столкновения до достаточно малого значения, чтобы выигрыш в скорости, который вы получаете от наличия таблицы транспозиции (и, возможно, постоянного углубленного поиска), перевешивал отрицательный эффект от возможности столкновения.
Warning: Spoiler![ Click to expand ][ Click to hide ]
interface
uses
SysUtils, // необходимый модуль для StrToInt
Classes, // необходимый модуль для TStringList
TypInfo;
...
{/**********************************\
==================================
Parse FEN
==================================
\**********************************/}
procedure ParseFEN(const fen: string);
var
parts: TStringList;
bb_piece:Tencode_pieces;
i, square, index, pos_s: Integer;
str_pieces:string;
begin
str_pieces := 'PNBRQKpnbrqk';
// reset board position (bitboards)
FillChar(bitboards_12, 12*SizeOf(UInt64), 0);
// reset occupancies (bitboards)
FillChar(occupancies_3, 3*SizeOf(UInt64), 0);
FillChar(color_pieces, SizeOf(color_pieces), both);
FillChar(type_pieces, SizeOf(type_pieces), t_no);
FillChar(tp_pc, SizeOf(tp_pc), no_p);
// side to move
side := black;
// castling rights
castle:=0;
enpassant := no_sq;
// Разбиваем FEN строку на части
parts := TStringList.Create;
try
parts.Delimiter := ' ';
parts.DelimitedText := fen;
// Парсим доску
square := ord(a8);
for i := 1 to Length(parts[0]) do
begin
if square>63 then break;
pos_s:=pos(parts[0][i],str_pieces)-1;
type_pieces[TBoard_squares(square)]:= t_no;
if pos_s>-1 then begin
bitboards_12[Tencode_pieces(pos_s)]:=bitboards_12[Tencode_pieces(pos_s)] or MaskOneBit[square];
type_pieces[TBoard_squares(square)]:= type_p[Tencode_pieces(pos_s)];
tp_pc[TBoard_squares(square)]:=Tencode_pieces(pos_s);
if pos_s>5 then color_pieces[TBoard_squares(square)]:= black
else color_pieces[TBoard_squares(square)]:= white;
if pos_s=5 then pos_kings[0]:=TBoard_squares(square);
if pos_s=11 then pos_kings[1]:=TBoard_squares(square);
Inc(square);
end;
case parts[0][i] of
'1', '2', '3', '4', '5', '6', '7', '8':
begin
index := StrToInt(parts[0][i]);
square := square + index;
end;
end;
end;
// loop over white pieces bitboardsoop over all piece bitboards
for bb_piece := P_ to K_ do
// populate white occupancy bitboard
occupancies_3[white] := occupancies_3[white] or bitboards_12[bb_piece];
// loop over black pieces bitboardsoop over all piece bitboards
for bb_piece := p to k do
// populate black occupancy bitboard
occupancies_3[black] := occupancies_3[black] or bitboards_12[bb_piece];
// init all occupancies
occupancies_3[both] := occupancies_3[both] or occupancies_3[white] or occupancies_3[black];
if parts.Count<2 then exit;
// Парсим сторону, которая должна ходить
if parts[1] = 'w' then side := white;
if parts.Count<3 then exit;
// Парсим право на рокировку
for i := 1 to Length(parts[2]) do
begin
case parts[2][i] of
'K':begin castle:=castle or integer(wk);end;
'Q':begin castle:=castle or integer(wq);end;
'k':begin castle:=castle or integer(bk);end;
'q':begin castle:=castle or integer(bq);end;
end;
end;
if parts.Count<4 then exit;
// Парсим en-passant клетку
if parts[3] <> '-' then
if (((char(parts[3][1]) >= 'a') and (char(parts[3][1]) <= 'h')) and ((char(parts[3][2]) = '3') or (char(parts[3][2]) = '6'))) then
enpassant := TBoard_squares(ord(char(parts[3][1])) - ord('a') + (8 - StrToInt(parts[3][2])) * 8);
finally
parts.Free;
end;
end;
Заодно и создание Fen
Warning: Spoiler![ Click to expand ][ Click to hide ]
{/**********************************\
==================================
Encode FEN
==================================
\**********************************/}
function encode_Fen():String;
var
source_square:TBoard_squares;
index,i:integer;
fen_str,fen_castle:String;
begin
index:=0;
i:=0;
fen_str:='';
fen_castle:='';
// loop over 64 board squares
for source_square := a8 to h1 do begin
if index=8 then index:=0;
Inc(index);
if type_pieces[source_square]=t_no then begin
Inc(i);
if index<8 then continue;
end;
if i>0 then begin
fen_str:=fen_str + IntToStr(i);
i:=0;
end;
if type_pieces[source_square]<>t_no then fen_str:=fen_str + Copy(GetEnumName(TypeInfo(Tencode_pieces), integer(Tencode_pieces(tp_pc[source_square]))),1,1);
if ((integer(source_square) and 7)=7) and (source_square<h1) then fen_str:=fen_str + '/';
end;//for
if side=white then fen_str:=fen_str + ' w ' else
fen_str:=fen_str + ' b ' ;
if (castle and integer(Tcastling(wk)))>0 then fen_castle:=fen_castle + 'K';
if (castle and integer(Tcastling(wq)))>0 then fen_castle:=fen_castle + 'Q';
if (castle and integer(Tcastling(bk)))>0 then fen_castle:=fen_castle + 'k';
if (castle and integer(Tcastling(bq)))>0 then fen_castle:=fen_castle + 'q';
if fen_castle='' then fen_castle:='-';
fen_str:=fen_str + fen_castle;
if enpassant<>no_sq then fen_str:=fen_str + ' ' + GetEnumName(TypeInfo(TBoard_squares), integer(enpassant)) else
fen_str:=fen_str + ' - ';
result:=fen_str;
end;
Решил все же разобраться с этим вопросом с помощью Coreinfo.
Утилита Coreinfo [1] от Марка Руссиновича предоставляет в этом плане гораздо больше возможностей.
Coreinfo также полезна для получения подробной информации о процессоре.
Warning: Spoiler![ Click to expand ][ Click to hide ]
Microsoft Windows [Version 6.2.9200]
(c) Корпорация Майкрософт, 2012. Все права защищены.
C:\Windows\system32>"G:\Radio_Electron_2021\Final chess engine\Coreinfo\Coreinfo
.exe" "f"
Coreinfo v3.6 - Dump information on system CPU and memory topology
Copyright (C) 2008-2022 Mark Russinovich
Sysinternals - www.sysinternals.com
Intel(R) Celeron(R) CPU 1005M @ 1.90GHz
Intel64 Family 6 Model 58 Stepping 9, GenuineIntel
Microcode signature: 00000017
HTT * Hyperthreading enabled
CET - Supports Control Flow Enforcement Technology
Kernel CET - Kernel-mode CET Enabled
User CET - User-mode CET Allowed
HYPERVISOR - Hypervisor is present
VMX * Supports Intel hardware-assisted virtualization
SVM - Supports AMD hardware-assisted virtualization
X64 * Supports 64-bit mode
SMX - Supports Intel trusted execution
SKINIT - Supports AMD SKINIT
SGX - Supports Intel SGX
NX * Supports no-execute page protection
SMEP * Supports Supervisor Mode Execution Prevention
SMAP - Supports Supervisor Mode Access Prevention
PAGE1GB - Supports 1 GB large pages
PAE * Supports > 32-bit physical addresses
PAT * Supports Page Attribute Table
PSE * Supports 4 MB pages
PSE36 * Supports > 32-bit address 4 MB pages
PGE * Supports global bit in page tables
SS * Supports bus snooping for cache operations
VME * Supports Virtual-8086 mode
RDWRFSGSBASE * Supports direct GS/FS base access
FPU * Implements i387 floating point instructions
MMX * Supports MMX instruction set
MMXEXT - Implements AMD MMX extensions
3DNOW - Supports 3DNow! instructions
3DNOWEXT - Supports 3DNow! extension instructions
SSE * Supports Streaming SIMD Extensions
SSE2 * Supports Streaming SIMD Extensions 2
SSE3 * Supports Streaming SIMD Extensions 3
SSSE3 * Supports Supplemental SIMD Extensions 3
SSE4a - Supports Streaming SIMDR Extensions 4a
SSE4.1 * Supports Streaming SIMD Extensions 4.1
SSE4.2 * Supports Streaming SIMD Extensions 4.2
AES - Supports AES extensions
AVX - Supports AVX instruction extensions
AVX2 - Supports AVX2 instruction extensions
AVX-512-F - Supports AVX-512 Foundation instructions
AVX-512-DQ - Supports AVX-512 double and quadword instructions
AVX-512-IFAMA - Supports AVX-512 integer Fused multiply-add instructions
AVX-512-PF - Supports AVX-512 prefetch instructions
AVX-512-ER - Supports AVX-512 exponential and reciprocal instructions
AVX-512-CD - Supports AVX-512 conflict detection instructions
AVX-512-BW - Supports AVX-512 byte and word instructions
AVX-512-VL - Supports AVX-512 vector length instructions
FMA - Supports FMA extensions using YMM state
MSR * Implements RDMSR/WRMSR instructions
MTRR * Supports Memory Type Range Registers
XSAVE * Supports XSAVE/XRSTOR instructions
OSXSAVE * Supports XSETBV/XGETBV instructions
RDRAND - Supports RDRAND instruction
RDSEED - Supports RDSEED instruction
CMOV * Supports CMOVcc instruction
CLFSH * Supports CLFLUSH instruction
CX8 * Supports compare and exchange 8-byte instructions
CX16 * Supports CMPXCHG16B instruction
BMI1 - Supports bit manipulation extensions 1
BMI2 - Supports bit manipulation extensions 2
ADX - Supports ADCX/ADOX instructions
DCA - Supports prefetch from memory-mapped device
F16C - Supports half-precision instruction
FXSR * Supports FXSAVE/FXSTOR instructions
FFXSR - Supports optimized FXSAVE/FSRSTOR instruction
MONITOR * Supports MONITOR and MWAIT instructions
MOVBE - Supports MOVBE instruction
ERMSB * Supports Enhanced REP MOVSB/STOSB
PCLMULDQ * Supports PCLMULDQ instruction
POPCNT * Supports POPCNT instruction
LZCNT - Supports LZCNT instruction
SEP * Supports fast system call instructions
LAHF-SAHF * Supports LAHF/SAHF instructions in 64-bit mode
HLE - Supports Hardware Lock Elision instructions
RTM - Supports Restricted Transactional Memory instructions
DE * Supports I/O breakpoints including CR4.DE
DTES64 * Can write history of 64-bit branch addresses
DS * Implements memory-resident debug buffer
DS-CPL * Supports Debug Store feature with CPL
PCID * Supports PCIDs and settable CR4.PCIDE
INVPCID - Supports INVPCID instruction
PDCM * Supports Performance Capabilities MSR
RDTSCP * Supports RDTSCP instruction
TSC * Supports RDTSC instruction
TSC-DEADLINE * Local APIC supports one-shot deadline timer
TSC-INVARIANT * TSC runs at constant rate
xTPR * Supports disabling task priority messages
EIST * Supports Enhanced Intel Speedstep
ACPI * Implements MSR for power management
TM * Implements thermal monitor circuitry
TM2 * Implements Thermal Monitor 2 control
APIC * Implements software-accessible local APIC
x2APIC * Supports x2APIC
CNXT-ID - L1 data cache mode adaptive or BIOS
MCE * Supports Machine Check, INT18 and CR4.MCE
MCA * Implements Machine Check Architecture
PBE * Supports use of FERR#/PBE# pin
PSN - Implements 96-bit processor serial number
PREFETCHW * Supports PREFETCHW instruction
Maximum implemented CPUID leaves: 0000000D (Basic), 80000008 (Extended).
Maximum implemented address width: 48 bits (virtual), 36 bits (physical).
Processor signature: 000306A9
Logical to Physical Processor Map:
*- Physical Processor 0
-* Physical Processor 1
Logical Processor to Socket Map:
** Socket 0
Logical Processor to NUMA Node Map:
** NUMA Node 0
No NUMA nodes.
Logical Processor to Cache Map:
*- Data Cache 0, Level 1, 32 KB, Assoc 8, LineSize 64
*- Instruction Cache 0, Level 1, 32 KB, Assoc 8, LineSize 64
*- Unified Cache 0, Level 2, 256 KB, Assoc 8, LineSize 64
** Unified Cache 1, Level 3, 2 MB, Assoc 8, LineSize 64
-* Data Cache 1, Level 1, 32 KB, Assoc 8, LineSize 64
-* Instruction Cache 1, Level 1, 32 KB, Assoc 8, LineSize 64
-* Unified Cache 2, Level 2, 256 KB, Assoc 8, LineSize 64
Logical Processor to Group Map:
** Group 0
C:\Windows\system32>
Теперь точно ясно, что popcnt поддерживает мой процессор.
Взял за горло искуственный интеллект и не отпустил его до тех пор, пока не получил правильный ответ
Warning: Spoiler![ Click to expand ][ Click to hide ]
function UsePopcnt(value: Cardinal): Cardinal;
asm
MOV EAX, value
DB $F3, $0F, $B8, $C0 // popcnt eax, eax
MOV Result, EAX
end;
Warning: Spoiler![ Click to expand ][ Click to hide ]
function UsePopcnt64(value: UInt64): Cardinal;
asm
PUSH EBX
PUSH EDI
PUSH ESI
MOV ESI, DWORD PTR [value] // Загружаем младшую часть значения в регистр ESI
MOV EDI, DWORD PTR [value + 4] // Загружаем старшую часть значения в регистр EDI
XOR EBX, EBX // Обнуляем регистр EBX для подсчета суммы
// Первая 32-битная часть
MOV EAX, ESI
DB $F3, $0F, $B8, $C0 // popcnt eax, eax
ADD EBX, EAX // Добавляем результат к общей сумме
// Вторая 32-битная часть
MOV EAX, EDI
DB $F3, $0F, $B8, $C0 // popcnt eax, eax
ADD EBX, EAX // Добавляем результат к общей сумме
MOV Result, EBX
POP ESI
POP EDI
POP EBX
end;
Warning: Spoiler![ Click to expand ][ Click to hide ]
Warning: Spoiler![ Click to expand ][ Click to hide ]
function IsPopcntSupported: Boolean;
var
IsPopcntSupportedResult: Integer; // Объявляем переменную для результата проверки
asm
PUSH EBX
PUSH EDI
MOV EAX, 1
XOR EDX, EDX
CPUID
MOV EBX, 1
SHL EBX, 23
AND EBX, ECX
POP EDI
POP EBX
TEST EBX, EBX
SETNZ AL
MOV IsPopcntSupportedResult, EAX
end;
Попытылся сравнить два метода подсчета бит.
Комбинированный метод
function count_bits(bb:UInt64):integer;
var
n:UInt64;
a1,a2,a3,a4:UInt64;
begin
a1:=$5555555555555555;
a2:=$3333333333333333;
a3:=$0F0F0F0F0F0F0F0F;
a4:=$0101010101010101;
n:=bb-(bb shr 1) and a1;
n:=((n shr 2) and a2) + (n and a2);
n:=((((n shr 4) + n) and a3) * a4) shr 56;
result:=n;
end;
и popcnt
function UsePopcnt64(value: UInt64): Cardinal;
asm
PUSH EBX
PUSH EDI
PUSH ESI
MOV ESI, DWORD PTR [value] // Загружаем младшую часть значения в регистр ESI
MOV EDI, DWORD PTR [value + 4] // Загружаем старшую часть значения в регистр EDI
XOR EBX, EBX // Обнуляем регистр EBX для подсчета суммы
// Первая 32-битная часть
MOV EAX, ESI
DB $F3, $0F, $B8, $C0 // popcnt eax, eax
ADD EBX, EAX // Добавляем результат к общей сумме
// Вторая 32-битная часть
MOV EAX, EDI
DB $F3, $0F, $B8, $C0 // popcnt eax, eax
ADD EBX, EAX // Добавляем результат к общей сумме
MOV Result, EBX
POP ESI
POP EDI
POP EBX
end;
Результат
Начальная позиция, perft=6
Для себя сделал вывод - оставить Комбинированный метод
кто может предложить математический алгоритм для решения композиции, только мат в n ходов?
Решил этот вопрос
Логическое описание:
Если на любой ход атакующей стороны найдется хотя бы один ход защищающейся стороны, который не ведет к мату, при максимальной глубине, то другие ходы атакующей стороны на данной глубине - можно обрезать.
Если на любой ход защищающейся стороны найдется хотя бы один ход атакующей стороны, который ведет к мату, при максимальной глубине, то другие ходы защищающейс стороны на данной глубине - можно обрезать.
Запутанно , но както так
Warning: Spoiler![ Click to expand ][ Click to hide ]
Проверил с помощью MateMaster
Warning: Spoiler![ Click to expand ][ Click to hide ]
Warning: Spoiler![ Click to expand ][ Click to hide ]
Warning: Spoiler![ Click to expand ][ Click to hide ]
function SearchMate(depth:integer):integer;
var
i:integer;
source_squareInt,target_squareInt,pieceInt,promotedInt:integer;
bitboards_12_temp:array[Tencode_pieces] of UInt64;
occupancies_3_temp:array[TSide] of UInt64;
side_temp:TSide;
enpassant_temp:TBoard_squares;
castle_temp:integer;
type_pieces_temp:array[TBoard_squares] of Ttype_pieces;
tp_pc_temp:array[TBoard_squares] of Tencode_pieces;
color_pieces_temp:Tcolor_pieces;
pos_kings_temp:array[0..1] of TBoard_squares;
begin
Application.ProcessMessages;
result:=-1;
generate_moves_list(add_move);
if (add_move^.count=0) and (count_piece_check<>0) then begin
Inc(Checkmates_);
result:=1;
end else result:=0;
if (ply_=0) then Checkmates_ :=0;
//мат определяется при ходе второго игрока
if (depth=0) then exit;
for i := 0 to add_move^.count-1 do begin
// preserve board state
//===================================================//
Move(type_pieces[a8],type_pieces_temp[a8],SizeOf(type_pieces));
Move(tp_pc[a8],tp_pc_temp[a8],SizeOf(tp_pc));
Move(color_pieces[a8],color_pieces_temp[a8],SizeOf(color_pieces));
Move(bitboards_12[P_],bitboards_12_temp[P_],12*SizeOf(UInt64));
Move(occupancies_3[white],occupancies_3_temp[white],3*SizeOf(UInt64));
Move(pos_kings[0],pos_kings_temp[0],SizeOf(pos_kings));
side_temp:=side;
enpassant_temp:=enpassant;
castle_temp:=castle;
//===================================================//
if (ply_=0) then begin
ClearDesk ();
DrawPieceBoard();
source_squareInt:=get_source(add_move^.move[i],get_move_source);
target_squareInt:=get_source(add_move^.move[i],get_move_target);
DrawLine(source_squareInt,target_squareInt,0);
end;
make_move(add_move^.move[i],all_moves);
Inc(ply_);
Inc(add_move);
result:=1 and SearchMate(depth - 1);
Dec(add_move);
Dec(ply_);
(*
если ход первого (ply_ and 1)=0 и найден мат result=1 или
если ход второго (ply_ and 1)=1 и мата нет result=0
*)
if ((ply_ and 1)=0) and (result=1) then begin
best_move:=add_move^.move[i];
exit;
end;
if ((ply_ and 1)=1) and (result=0) then exit;
// restore board state
//===================================================//
castle:=castle_temp;
enpassant:=enpassant_temp;
side:=side_temp;
Move(pos_kings_temp[0],pos_kings[0],SizeOf(pos_kings));
Move(occupancies_3_temp[white],occupancies_3[white],3*SizeOf(UInt64));
Move(bitboards_12_temp[P_],bitboards_12[P_],12*SizeOf(UInt64));
Move(color_pieces_temp[a8],color_pieces[a8],SizeOf(color_pieces));
Move(tp_pc_temp[a8],tp_pc[a8],SizeOf(tp_pc));
Move(type_pieces_temp[a8],type_pieces[a8],SizeOf(type_pieces));
//===================================================//
end;//for
if (ply_=0) then make_move(best_move,all_moves);
end;
Протестировал на разных задач.
Решение находит правильно.
Мат в 5 ходов нашел быстрее MatMaster
Warning: Spoiler![ Click to expand ][ Click to hide ]
Нужно запустить с параметром "show", нажать в правом углу >> и выбрать задачу, затем нажать меню-мат в n ходов.
После того, как прога сделает ход, можно сделать ответный ход.
В общем то мне было важно проверить идею нахождения мата в n ходов.
PS
Перекомпилировал (если нет решения задачи, то выводится соответствующее сообщение)
Но, в общем то я считаю, что эту задачу решил
С 1966 года Международный день шахмат принято отмечать 20 июля. Число выбрано не случайно: именно в эту дату в 1924 году в Париже, в период проведения Восьмых олимпийских летних игр, была основана Международная шахматная федерация.
Первый официальный чемпион мира по шахматам Вильгельм Стейниц в молодости жил в Англии, зарабатывая себе на жизнь игрой на ставку со случайными партнёрами в ресторане Simpson's Divan. Один из таких партнеров платил фунт стерлингов за каждую проигранную партию к большой радости Стейница, который неизменно побеждал. Но как-то ему сказали, что он может потерять выгодного партнера, если все время будет брать над ним верх. Этот совет показался Стейницу разумным, и в одной из партий он, умышленно подставив ферзя, признал себя побежденным. Партнер закричал: "Я выиграл у чемпиона мира! Моя мечта сбылась!" - и выбежал на улицу. Больше Стейниц его никогда не видел.
С наступающим!
Кстати, интересно было бы создать на сайте раздел композиции, где посетители бы решали шахматные задачи, а оппонентом выступал бот, использующий указанный алгоритм.
PS
точнее алгоритм модифицировать для защиты.
это не сложно
Интересная теория
Теория окончания «Два коня против пешки» была разработана известным исследователем шахмат А. Троицким еще в начале XX века. Эта теория получила название «Линия Троицкого». По ней, если пешка слабейшей стороны блокируется не позднее, чем показано на первой диаграмме, то возможность мата гарантируется (иногда пешка может находиться за линией Троицкого и тогда все зависит от положения короля). Но в определенных вариантах, чтобы соорудить мат требуется более 100 ходов. Это нарушает правила шахмат. Поэтому немецкий гроссмейстер К. Мюллер создал теорию, по которой число ходов для мата не превышает 50-ти – «Вторая линия Троицкого» (на нижней диаграмме).
8/8/1p4p1/2p2p2/p2pp2p/8/8/8 b - - 0 1
8/8/1p4p1/p1pppp1p/8/8/8/8 b - - 0 1
White is winning DTZ 138 DTM 143
3k3N/8/8/8/1N1p4/8/8/K7 w - - 0 1
'DTM' - Глубина для мата. Для каждой представленной позиции 'DTM' указывает теоретическое значение и количество ходов победителя для "мата" в случае выигрыша / проигрыша - при условии, что победитель минимизирует, а проигравший максимизирует DTM.
'DTZ' - Глубина до обнуления
DTZ (количества слоев) Количество слоев обнуляется тогда и только тогда, когда пешка выдвинута или сделан захват. Для каждой представленной позиции "DTZ" указывает теоретическое значение и количество ходов победителя до достижения цели "Выиграть и обнулить количество ходов" в случае выигрыша / проигрыша - при условии, что победитель минимизирует, а проигравший максимизирует DTZ.
tb_init initializes the tablebase
tb_probe_wdl probes the Win-Draw-Loss (WDL) table for a given position
tb_probe_root probes the Distance-To-Zero (DTZ) table for the given position.
tb_init initializes the tablebase
tb_probe_wdl probes the Win-Draw-Loss (WDL) table for a given position
tb_probe_root probes the Distance-To-Zero (DTZ) table for the given position.
//test
//-WDL (Win-Draw-Loss).
(*
1.Loss - Unconditional loss.
2.BlessedLoss - Loss that can be saved by the 50-move rule.
3.Draw - Unconditional draw.
4.CursedWin - Win that can be frustrated by the 50-move rule.
5.Win - Unconditional win.
*)
//-DTZ (Distance to zero).
try
(*
1-a:\
2-b:\
3-c:\
4-d:\
*)
GetDir (4,Dir_str);//возвращает в Dir_str букву диска(см пример)
//если файл fathom.exe существует по указанному пути
if FileExists(Dir_str+'Fathom-1.0\fathom.exe') then begin
//позиция для примера
fenpos:='3k3N/8/8/8/1N1p4/8/8/K7 w - - 0 1';
ParseFEN(fenpos);
ClearDesk ();
DrawPieceBoard();
//"D:\Fathom-1.0\fathom.exe" "--path=D:\3-4-5" "3k3N/8/8/8/1N1p4/8/8/K7 w - - 0 1"
Info.PrintInfo(GetDosOutput('"' + Dir_str + 'Fathom-1.0\Fathom.exe" "--path=' + Dir_str + '3-4-5" "' + fenpos + '"',Dir_str + 'Fathom-1.0',ResultCode));
end else begin
Info.PrintInfo(Dir_str+'Fathom-1.0\Fathom.exe не найден');
end;
finally
end;
//test
запуск приложения Fathom.exe и получение результатов
//http://delphimaster.net/view/4-1140074783
function GetDosOutput( const CommandLine, WorkDir: String;
var ResultCode: Cardinal ): String;
var StdOutPipeRead, StdOutPipeWrite: THandle;
SA : TSecurityAttributes;
SI : TStartupInfo;
PI : TProcessInformation;
WasOK : Boolean;
Buffer : array[0..255] of Char;
BytesRead : Cardinal;
Line : String;
Begin
Application.ProcessMessages;
With SA do
Begin
nLength := SizeOf( SA );
bInheritHandle := True;
lpSecurityDescriptor := nil;
end;
// создаём пайп для перенаправления стандартного вывода
CreatePipe( StdOutPipeRead, // дескриптор чтения
StdOutPipeWrite, // дескриптор записи
@SA, // аттрибуты безопасности
0 // количество байт принятых для пайпа - 0 по умолчанию
);
try
// Создаём дочерний процесс, используя StdOutPipeWrite в качестве стандартного вывода,
// а так же проверяем, чтобы он не показывался на экране.
with SI do
Begin
FillChar( SI, SizeOf( SI ), 0 );
cb := SizeOf( SI );
dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
wShowWindow := SW_HIDE; //не показывать окно
hStdInput := GetStdHandle( STD_INPUT_HANDLE ); // стандартный ввод не перенаправляем
hStdOutput := StdOutPipeWrite;
hStdError := StdOutPipeWrite;
end;
// Запускаем компилятор из командной строки
//WorkDir := ExtractFilePath(CommandLine);
WasOK := CreateProcess( nil,
PChar( CommandLine ),
nil,
nil,
True,
0,
nil,
PChar( WorkDir ),
SI,
PI );
// Теперь, когда дескриптор получен, для безопасности закрываем запись.
// Нам не нужно, чтобы произошло случайное чтение или запись.
CloseHandle( StdOutPipeWrite );
// если процесс может быть создан, то дескриптор, это его вывод
if not WasOK then
raise Exception.Create( 'Ошибка выполнения или компиляции: ' +
Chr( 10 ) + Chr( 13 ) + CommandLine )
else
try
// получаем весь вывод до тех пор, пока DOS-приложение не будет завершено
Line := '';
Repeat
// читаем блок символов (могут содержать возвраты каретки и переводы строки)
WasOK := ReadFile( StdOutPipeRead, Buffer, 255, BytesRead, nil );
// есть ли что-нибудь ещё для чтения?
if BytesRead > 0 then
Begin
// завершаем буфер PChar-ом
Buffer[BytesRead] := #0;
// добавляем буфер в общий вывод
Line := Line + Buffer;
end;
Application.ProcessMessages;
Until not WasOK or ( BytesRead = 0 );
// ждём, пока завершится консольное приложение
WaitForSingleObject( PI.hProcess, INFINITE );
ResultCode := 0;
GetExitCodeProcess( PI.hProcess, ResultCode );
finally
// Закрываем все оставшиеся дескрипторы
CloseHandle( PI.hThread );
CloseHandle( PI.hProcess );
end;
finally
Result := Line;
CloseHandle( StdOutPipeRead );
end;
end;
Не очень понимаю, точнее совсем не понимаю
Скачал только испорченные файлы:
Ссылка вверху этого сайта - "Таблицы эндшпиля" - "tablebase.lichess.ovh" - "standard/ " - "3-4-5/ "
Ошибка md5 осталась у одного файла
Massimiliano Goi (c) 2017 http://chess.massimilianogoi.com
Checking the integrity of the 3-4-5 pieces Syzygy files
(if every file if OK you're good to go)
SlavaSoft Optimizing Checksum Utility - fsum 2.52.00337
Implemented using SlavaSoft QuickHash Library <www.slavasoft.com>
Copyright (C) SlavaSoft Inc. 1999-2007. All rights reserved.
FAILED MD5 KPvK.rtbw
1 checksums failed
Для продолжения нажмите любую клавишу . . .