 |
-
alexlaw
-
-
OFFLINE
-
Жилец
-
- Posts: 67
-
Karma: 0
-
|
10 урок - нахождение индекса младшего значащего бита и возвращение его координаты в шахматной нотации program bbc;
{$APPTYPE CONSOLE}
uses
SysUtils,Windows,TypInfo;
type
U64 = UInt64;
TBoard_squares = (
a8, b8, c8, d8, e8, f8, g8, h8,
a7, b7, c7, d7, e7, f7, g7, h7,
a6, b6, c6, d6, e6, f6, g6, h6,
a5, b5, c5, d5, e5, f5, g5, h5,
a4, b4, c4, d4, e4, f4, g4, h4,
a3, b3, c3, d3, e3, f3, g3, h3,
a2, b2, c2, d2, e2, f2, g2, h2,
a1, b1, c1, d1, e1, f1, g1, h1
);
TSide = (white, black);
//Первый индекс обозначает номер строки в матрице, второй индекс – номер столбца
TPawn_attacks=array[TSide,TBoard_squares] of U64;
TChip_attacks=array[TBoard_squares] of U64;
var
bitboard:U64=0;
block:U64=0;
// pawn attacks table [side][square]
pawn_attacks:TPawn_attacks;
// knight attacks table [square]
knight_attacks:TChip_attacks;
// king attacks table [square]
king_attacks:TChip_attacks;
square:TBoard_squares;
const
{/*
not A file
8 0 1 1 1 1 1 1 1
7 0 1 1 1 1 1 1 1
6 0 1 1 1 1 1 1 1
5 0 1 1 1 1 1 1 1
4 0 1 1 1 1 1 1 1
3 0 1 1 1 1 1 1 1
2 0 1 1 1 1 1 1 1
1 0 1 1 1 1 1 1 1
a b c d e f g h
not H file
8 1 1 1 1 1 1 1 0
7 1 1 1 1 1 1 1 0
6 1 1 1 1 1 1 1 0
5 1 1 1 1 1 1 1 0
4 1 1 1 1 1 1 1 0
3 1 1 1 1 1 1 1 0
2 1 1 1 1 1 1 1 0
1 1 1 1 1 1 1 1 0
a b c d e f g h
not HG file
8 1 1 1 1 1 1 0 0
7 1 1 1 1 1 1 0 0
6 1 1 1 1 1 1 0 0
5 1 1 1 1 1 1 0 0
4 1 1 1 1 1 1 0 0
3 1 1 1 1 1 1 0 0
2 1 1 1 1 1 1 0 0
1 1 1 1 1 1 1 0 0
a b c d e f g h
not AB file
8 0 0 1 1 1 1 1 1
7 0 0 1 1 1 1 1 1
6 0 0 1 1 1 1 1 1
5 0 0 1 1 1 1 1 1
4 0 0 1 1 1 1 1 1
3 0 0 1 1 1 1 1 1
2 0 0 1 1 1 1 1 1
1 0 0 1 1 1 1 1 1
a b c d e f g h
*/}
// not A file constant
not_a_file:U64 = $FEFEFEFEFEFEFEFE;//18374403900871474942;
// not H file constant
not_h_file:U64 = $7F7F7F7F7F7F7F7F;//9187201950435737471;
// not HG file constant
not_hg_file:U64 =$3F3F3F3F3F3F3F3F;//4557430888798830399;
// not AB file constant
not_ab_file:U64 =$FCFCFCFCFCFCFCFC;//18229723555195321596;
square_to_coordinates: array [0..63] of string = (
'a8', 'b8', 'c8', 'd8', 'e8', 'f8', 'g8', 'h8',
'a7', 'b7', 'c7', 'd7', 'e7', 'f7', 'g7', 'h7',
'a6', 'b6', 'c6', 'd6', 'e6', 'f6', 'g6', 'h6',
'a5', 'b5', 'c5', 'd5', 'e5', 'f5', 'g5', 'h5',
'a4', 'b4', 'c4', 'd4', 'e4', 'f4', 'g4', 'h4',
'a3', 'b3', 'c3', 'd3', 'e3', 'f3', 'g3', 'h3',
'a2', 'b2', 'c2', 'd2', 'e2', 'f2', 'g2', 'h2',
'a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1'
);
{/**********************************\
==================================
Bit manipulations
==================================
\**********************************/}
// set/get/pop
function get_bit(bb:U64;const sq:integer):integer;
begin
get_bit:=(bb shr sq) and 1;
end;
procedure set_bit(var bb:U64;const sq:TBoard_squares);
begin
bb:=bb or ($8000000000000000 shr (63-Ord(sq)));
end;
procedure pop_bit(var bb:U64;const sq:TBoard_squares);
begin
if get_bit(bb,Ord(sq))>0 then bb:=bb xor ($8000000000000000 shr (63-Ord(sq)));
end;
// count bits within a bitboard (Brian Kernighan's way)
function count_bits(bb:U64):integer;
var
count:integer;
begin
// bit counter
count := 0;
// consecutively reset least significant 1st bit
while bb>0
do begin
// increment count
Inc(count);
// reset least significant 1st bit
bb := bb and (bb - 1);
end;
count_bits:=count;
end;
// get least significant 1st bit index
function get_ls1b_index(bb:U64):integer;
begin
// make sure bitboard is not 0
if bb<>0 then
// count trailing bits before LS1B
result:=count_bits((bb and (-bb))-1)
// return illegal index
else result:=-1;
end;
{/**********************************\
==================================
Input & Output
==================================
\**********************************/}
// print bitboard
procedure print_bitboard(bb:U64);
var
rank_,file_,sq:integer;
begin
Writeln('');
// loop over board ranks
for rank_ := 0 to 7 do begin
// loop over board files
for file_ := 0 to 7 do begin
// convert file & rank into square index
sq:=rank_*8 + file_;
//// print ranks
if file_ = 0 then begin
Write(8 - rank_);Write(' ');
end;
Write(' ');
// print bit state (either 1 or 0)
Write(get_bit(bb,sq));
end;
// print new line every rank
Writeln('');
end;
// print new line
Writeln('');
// print board files
Writeln(' a b c d e f g h');
Writeln('');
// print bitboard as unsigned decimal number
Write('bitboard = ',bb);
Writeln('');
end;
{/**********************************\
==================================
Attacks
==================================
\**********************************/}
// generate pawn attacks
function mask_pawn_attacks(side:TSide;square:TBoard_squares):U64;
var
attacks,bitboard:U64;
begin
// result attacks bitboard
attacks := 0;
// piece bitboard
bitboard := 0;
// set piece on board
set_bit(bitboard, square);
// white pawns
if (side=white) then begin
if ((bitboard shr 7) and not_a_file)>0 then attacks:=attacks or (bitboard shr 7);
if ((bitboard shr 9) and not_h_file)>0 then attacks:=attacks or (bitboard shr 9);
end
// black pawns
else begin
if ((bitboard shl 7) and not_h_file)>0 then attacks:=attacks or (bitboard shl 7);
if ((bitboard shl 9) and not_a_file)>0 then attacks:=attacks or (bitboard shl 9);
end;
// return attack map
mask_pawn_attacks:=attacks;
end;
// generate knight attacks
function mask_knight_attacks(square:TBoard_squares):U64;
var
attacks,bitboard:U64;
begin
// result attacks bitboard
attacks := 0;
// piece bitboard
bitboard := 0;
// set piece on board
set_bit(bitboard, square);
// generate knight attacks
if ((bitboard shr 17) and not_h_file)>0 then attacks:=attacks or (bitboard shr 17);
if ((bitboard shr 15) and not_a_file)>0 then attacks:=attacks or (bitboard shr 15);
if ((bitboard shr 10) and not_hg_file)>0 then attacks:=attacks or (bitboard shr 10);
if ((bitboard shr 6) and not_ab_file)>0 then attacks:=attacks or (bitboard shr 6);
if ((bitboard shl 17) and not_a_file)>0 then attacks:=attacks or (bitboard shl 17);
if ((bitboard shl 15) and not_h_file)>0 then attacks:=attacks or (bitboard shl 15);
if ((bitboard shl 10) and not_ab_file)>0 then attacks:=attacks or (bitboard shl 10);
if ((bitboard shl 6) and not_hg_file)>0 then attacks:=attacks or (bitboard shl 6);
// return attack map
mask_knight_attacks:=attacks;
end;
// generate king attacks
function mask_king_attacks(square:TBoard_squares):U64;
var
attacks,bitboard:U64;
begin
// result attacks bitboard
attacks := 0;
// piece bitboard
bitboard := 0;
// set piece on board
set_bit(bitboard, square);
// generate king attacks
if (bitboard shr 8)>0 then attacks:=attacks or (bitboard shr 8);
if ((bitboard shr 9) and not_h_file)>0 then attacks:=attacks or (bitboard shr 9);
if ((bitboard shr 1) and not_h_file)>0 then attacks:=attacks or (bitboard shr 1);
if ((bitboard shr 7) and not_a_file)>0 then attacks:=attacks or (bitboard shr 7);
if (bitboard shl 8)>0 then attacks:=attacks or (bitboard shl 8);
if ((bitboard shl 9) and not_a_file)>0 then attacks:=attacks or (bitboard shl 9);
if ((bitboard shl 1) and not_a_file)>0 then attacks:=attacks or (bitboard shl 1);
if ((bitboard shl 7) and not_h_file)>0 then attacks:=attacks or (bitboard shl 7);
// return attack map
mask_king_attacks:=attacks;
end;
//урок 6-masking relevant bishop occupancy bits to form a key for MAGIC BITBOARDS
// mask bishop attacks
function mask_bishop_attacks(square:TBoard_squares):U64;
var
attacks,n:U64;
r,f,tr,tf:integer;
begin
n:=1;
// result attacks bitboard
attacks := 0;
// init target rank & files
tr := Ord(square) div 8;//целочисленное деление
tf := Ord(square) mod 8;//остаток от деления
// mask relevant bishop occupancy bits
f:=tf+1;
for r:=tr+1 to 6 do begin
if f>6 then break;
attacks := attacks or (n shl (r*8+f));
Inc(f);
end;
f:=tf+1;
for r:=tr-1 downto 1 do begin
if f>6 then break;
attacks := attacks or (n shl (r*8+f));
Inc(f);
end;
f:=tf-1;
for r:=tr+1 to 6 do begin
if f<1 then break;
attacks := attacks or (n shl (r*8+f));
Dec(f);
end;
f:=tf-1;
for r:=tr-1 downto 1 do begin
if f<1 then break;
attacks := attacks or (n shl (r*8+f));
Dec(f);
end;
// return attack map
mask_bishop_attacks:=attacks;
end;
//урок 7-masking relevant ROOK OCCUPANCY BITS to form a key for MAGIC BITBOARDS
// mask rook attacks
function mask_rook_attacks(square:TBoard_squares):U64;
var
attacks,n:U64;
r,f,tr,tf:integer;
begin
n:=1;
// result attacks bitboard
attacks := 0;
// init target rank & files
tr := Ord(square) div 8;//целочисленное деление
tf := Ord(square) mod 8;//остаток от деления
// mask relevant bishop occupancy bits
for r:=tr+1 to 6 do attacks := attacks or (n shl (r*8+tf));
for r:=tr-1 downto 1 do attacks := attacks or (n shl (r*8+tf));
for f:=tf+1 to 6 do attacks := attacks or (n shl (tr*8+f));
for f:=tf-1 downto 1 do attacks := attacks or (n shl (tr*8+f));
// return attack map
mask_rook_attacks:=attacks;
end;
//урок 8-generating SLIDER PIECES ATTACKS on the fly for MAGIC BITBOARD purposes
// generate bishop attacks on the fly
function bishop_attacks_on_the_fly(square:TBoard_squares;blk:U64):U64;
var
attacks,n:U64;
r,f,tr,tf:integer;
begin
n:=1;
// result attacks bitboard
attacks := 0;
// init target rank & files
tr := Ord(square) div 8;//целочисленное деление
tf := Ord(square) mod 8;//остаток от деления
// generate bishop atacks
f:=tf+1;
for r:=tr+1 to 7 do begin
if f>7 then break;
attacks := attacks or (n shl (r*8+f));
if ((n shl (r*8+f)) and blk)>0 then break;
Inc(f);
end;
f:=tf+1;
for r:=tr-1 downto 0 do begin
if f>7 then break;
attacks := attacks or (n shl (r*8+f));
if ((n shl (r*8+f)) and blk)>0 then break;
Inc(f);
end;
f:=tf-1;
for r:=tr+1 to 7 do begin
if f<0 then break;
attacks := attacks or (n shl (r*8+f));
if ((n shl (r*8+f)) and blk)>0 then break;
Dec(f);
end;
f:=tf-1;
for r:=tr-1 downto 0 do begin
if f<0 then break;
attacks := attacks or (n shl (r*8+f));
if ((n shl (r*8+f)) and blk)>0 then break;
Dec(f);
end;
// return attack map
bishop_attacks_on_the_fly:=attacks;
end;
// generate rook attacks on the fly
function rook_attacks_on_the_fly(square:TBoard_squares;blk:U64):U64;
var
attacks,n:U64;
r,f,tr,tf:integer;
begin
n:=1;
// result attacks bitboard
attacks := 0;
// init target rank & files
tr := Ord(square) div 8;//целочисленное деление
tf := Ord(square) mod 8;//остаток от деления
// generate rook attacks
for r:=tr+1 to 7 do begin
attacks := attacks or (n shl (r*8+tf));
if ((n shl (r*8+tf)) and blk)>0 then break;
end;
for r:=tr-1 downto 0 do begin
attacks := attacks or (n shl (r*8+tf));
if ((n shl (r*8+tf)) and blk)>0 then break;
end;
for f:=tf+1 to 7 do begin
attacks := attacks or (n shl (tr*8+f));
if ((n shl (tr*8+f)) and blk)>0 then break;
end;
for f:=tf-1 downto 0 do begin
attacks := attacks or (n shl (tr*8+f));
if ((n shl (tr*8+f)) and blk)>0 then break;
end;
// return attack map
rook_attacks_on_the_fly:=attacks;
end;
// init leaper pieces attacks
procedure init_leapers_attacks();
var
square:TBoard_squares;
begin
// loop over 64 board squares
for square := a8 to h1 do begin
//урок 3-generating pre-calculated PAWN ATTACK tables
// init pawn attacks
pawn_attacks[white][square] := mask_pawn_attacks(white,square);
pawn_attacks[black][square] := mask_pawn_attacks(black,square);
//урок 4-generating pre-calculated KNIGHT ATTACK table
// init knight attacks
knight_attacks[square] := mask_knight_attacks(square);
//урок 5-generating pre-calculated KING ATTACK tables
// init king attacks
king_attacks[square] := mask_king_attacks(square);
end;
end;
{/**********************************\
==================================
Main driver
==================================
\**********************************/}
begin
//Переключение окна консоли на кодовую страницу CP1251 (Win-1251).
//Если после переключения русские буквы показываются неверно,
//следует открыть системное меню консольного окна - щелчком мыши в левом
//верхнем углу окна консоли. И выбрать:
//Свойства - закладка "Шрифт" - выбрать шрифт: "Lucida Console".
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
// ==================================
init_leapers_attacks();
block:=4512464976740352;
print_bitboard(block);
square:=TBoard_squares(get_ls1b_index(block));
Write('get_ls1b_index:',get_ls1b_index(block));
Writeln(' coordinate:',square_to_coordinates[Ord(square)]);
set_bit(bitboard,square);
print_bitboard(bitboard);
Writeln('Нажмите <ENTER>, чтобы выйти ...');
Readln;
end.
|
|
-
alexlaw
-
-
OFFLINE
-
Жилец
-
- Posts: 67
-
Karma: 0
-
|
Разобрался с 11 уроком - Bitboard CHESS ENGINE in C: populating OCCUPANCY sets to multiply them later by MAGIC NUMBERS.
Пока это все подготовка к реализации Magic Bitboards
В этом уроке описывается функция нызываемая - начальными занятиями (set_occupancy).
По сути это комбинация всех различных расположений фигур на линии атаки скользящей фигуры (ладьи, слона).
8/8/8/8/8/8/8/R7
Например при ладье на a1 и ее атаках по линиям A2-A7 и B1-G1 (края доски не учитываются), теоретически эти поля могут быть заняты другими фигурами в различных комбинациях. По сути мы имеем дело с 6 клетками по вертикали и 6 по горизонали.
6 клеток это число с 6 включенными битами, соответствует числу 63.
Правило умножения (основная формула комбинаторики)
Общее число N способов, которыми можно выбрать по одному элементу из каждой группы и расставить их в определенном порядке (то есть получить упорядоченную совокупность a,b,c,d), равно:
N=n1*n2*n3* ... nk
T.e. в нашел случае при ладье на a1
N=64*64=4096 различных комбинаций занятости этих линий.
В двоичном виде это число выглядит так
По сути пробегая в цикле от нуля до 4095 мы получим все эти различные комбинации перестановок block:=mask_rook_attacks(a1);
print_bitboard(block);
for i:=0 to 4095 do begin
block:=mask_rook_attacks(a1);
bitboard:=set_occupancy(i,count_bits(block),block);
print_bitboard(bitboard);
Readln;
end;
Writeln('Нажмите <ENTER>, чтобы выйти ...');
Readln; // make sure occupancy is on board
if (index and (1 shl count))>0 then
occupancy:=occupancy or (n shl integer(square_)); Index -это как раз число перестановок (в конкретном случае - 4096)
Т.е. пробигая по вариантам занятости for i:=0 to 4095 при условии - i and (1 shl count) мы находим установленный бит и и получаем конкретную занятость для i.
Короче вот реализация на Делфи // set occupancies
//перебираем по очереди биты в attack_mask
function set_occupancy(index,bits_in_mask:integer;var attack_mask:U64):U64;
var
occupancy,n:U64;
count:integer;
square_:TBoard_squares;
begin
n:=1;
// occupancy map
occupancy:=0;
// loop over the range of bits within attack mask
for count:=0 to bits_in_mask-1 do begin
// get LS1B index of attacks mask
square_:=TBoard_squares(get_ls1b_index(attack_mask));
// pop LS1B in attack map
pop_bit(attack_mask,square_);
// make sure occupancy is on board
if (index and (n shl count))>0 then
occupancy:=occupancy or (n shl integer(square_));
end;
set_occupancy:=occupancy;
end;
|
Last Edit: 24 Янв 2023 10:02 by alexlaw.
|
-
alexlaw
-
-
OFFLINE
-
Жилец
-
- Posts: 67
-
Karma: 0
-
|
12 урок - Bitboard CHESS ENGINE in C: generating relevant OCCUPANCY BIT COUNT lookup tables for sliding pieces
Просто заполнение таблицы количеста атакуемых клеток скользящих фигур из каждой клетки доски // bishop relevant occupancy bit count for every square on board
bishop_relevant_bits: array [0..63] of integer =(
6, 5, 5, 5, 5, 5, 5, 6,
5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 7, 7, 7, 7, 5, 5,
5, 5, 7, 9, 9, 7, 5, 5,
5, 5, 7, 9, 9, 7, 5, 5,
5, 5, 7, 7, 7, 7, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
6, 5, 5, 5, 5, 5, 5, 6
);
// rook relevant occupancy bit count for every square on board
rook_relevant_bits: array [0..63] of integer =(
12, 11, 11, 11, 11, 11, 11, 12,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
12, 11, 11, 11, 11, 11, 11, 12
); Writeln('bishop_relevant_bits');
for square := a8 to h1 do begin
if (((integer(square) mod 8)=0) and (square > a8)) then Writeln('');
Write(count_bits(mask_bishop_attacks(square))); Write(' ');
end;
Writeln('');
Writeln('rook_relevant_bits');
for square := a8 to h1 do begin
if (((integer(square) mod 8)=0) and (square > a8)) then Writeln('');
Write(count_bits(mask_rook_attacks(square))); Write(' ');
end;
Writeln('');
Writeln('Нажмите <ENTER>, чтобы выйти ...');
Readln;
|
|
-
alexlaw
-
-
OFFLINE
-
Жилец
-
- Posts: 67
-
Karma: 0
-
|
Уроки 13 и 14
13.Bitboard CHESS ENGINE in C: implementing pseudo RANDOM NUMBER generator using XORSHIFT32 algorithm
14.Bitboard CHESS ENGINE in C: generating MAGIC NUMBER candidates
Макс рассказывает как сгенерировать числа - кандитаты в магические числа.
По сути - это псевдослучайные числа.
И самое интересное, далее он расскажет, как их использовать в качестве магических чисел. {/**********************************\
==================================
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():U64;
var
// define 4 random numbers
n1,n2,n3,n4:U64;
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():U64;
begin
result:=get_random_U64_number() and get_random_U64_number() and get_random_U64_number();
end;
Хотя эти числа всегда одни и те же, но раз это работает, то делаем так же.
|
|
-
alexlaw
-
-
OFFLINE
-
Жилец
-
- Posts: 67
-
Karma: 0
-
|
Попытка объяснить Magic Bitboard
|
|
-
alexlaw
-
-
OFFLINE
-
Жилец
-
- Posts: 67
-
Karma: 0
-
|
Урок 15.Bitboard CHESS ENGINE in C: generating MAGIC NUMBERS via brute force trial and error method.
Разобрался с этим уроком - генерация магических чисел.
Попробую рассказать свое понимание этого.
Итак, какие исходные данные есть у нас.
1. Позиция дальнобойной фигуры (например ладья на a1)
TBoard_squares = (
a8, b8, c8, d8, e8, f8, g8, h8,
a7, b7, c7, d7, e7, f7, g7, h7,
a6, b6, c6, d6, e6, f6, g6, h6,
a5, b5, c5, d5, e5, f5, g5, h5,
a4, b4, c4, d4, e4, f4, g4, h4,
a3, b3, c3, d3, e3, f3, g3, h3,
a2, b2, c2, d2, e2, f2, g2, h2,
a1, b1, c1, d1, e1, f1, g1, h1
);
2. Количество атакуемых клеток из этой позиции при отсутствии блокирующих фигур (края доски не учитываются)
(ладья на a1 атакует 6 клеток по горизонтали, 6 по вертикали: всего 12)
// rook relevant occupancy bit count for every square on board
rook_relevant_bits: array [0..63] of integer =(
12, 11, 11, 11, 11, 11, 11, 12,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
12, 11, 11, 11, 11, 11, 11, 12
);
Производные от первоначальных данных
1.битбоард атаки (ладьи) этих 12 клеток
attack_mask:=mask_bishop_attacks(square)
2. Количество перестановок блокирующих фигур на атакуемых ладьей полях
occupancy_indicies:integer;
- 64*64
- 2 в степени 12
- 1 << 12
occupancy_indicies:=1 shl relevant_bits;
3.Комбинации занятости блокирующих фигур
occupancies:array[0..4095] of U64;
4.Самое важное это атаки с учетом блокирующих фигур
// init attack tables
attacks:array[0..4095] of U64;
Вроде бы все прекрасно - эти атаки нам и нужны в идеале.
Но есть одно большое НО.
Чтобы найти их в таблице array[0..4095], придется долго и упорно ее перелапачивать.
Умные люди решили проблему так -
Придумать такую хэш функция, чтобы с помощью простого ключа быстро находить нужные атаки в таблице.
Ключ, это и есть - магическое число с простыми манипуляциями с ним.
Это как подойти к реальной двери, глянуть на скважину замка, достать отмычку, туда сюда ее изогнуть на глаз и пробовать открыть замок.
Этот урок Макса, как раз и есть, описание способа, гнуть отмычку туда сюда , чтобы получить ключ.
Способ такой.
1.Создается массив
// init used attacks
used_attacks:array[0..4095] of U64;
Инициализируется 0.
FillChar(used_attacks, 4096*SizeOf(UInt64), n); - делфи
memset(used_attacks, 0ULL, sizeof(used_attacks)); - СИ
2.U64 magic_number - генерируем 64 битные случайные числа
3.Подставляем ключ в хэш функцию
magic_index := integer((occupancies[index] * magic_number) shr (64 - relevant_bits)); - делфи
int magic_index = (int)((occupancies[index] * magic_number) >> (64 - relevant_bits)); - Си
Пробуем полученный ключ - если уже был другой ключ, то это коллизия ( ключ выбрасываем)
Как то так в моем понимании.
|
|
-
alexlaw
-
-
OFFLINE
-
Жилец
-
- Posts: 67
-
Karma: 0
-
|
На языке Си (Макс) /**********************************\
==================================
Didactic
BITBOARD CHESS ENGINE
by
Code Monkey King
==================================
\**********************************/
// system headers
#include <stdio.h>
#include <string.h>
// define bitboard data type
#define U64 unsigned long long
// board squares
enum {
a8, b8, c8, d8, e8, f8, g8, h8,
a7, b7, c7, d7, e7, f7, g7, h7,
a6, b6, c6, d6, e6, f6, g6, h6,
a5, b5, c5, d5, e5, f5, g5, h5,
a4, b4, c4, d4, e4, f4, g4, h4,
a3, b3, c3, d3, e3, f3, g3, h3,
a2, b2, c2, d2, e2, f2, g2, h2,
a1, b1, c1, d1, e1, f1, g1, h1
};
// sides to move (colors)
enum { white, black };
// bishop and rook
enum { rook, bishop };
// convert squares to coordinates
const char *square_to_coordinates[] = {
"a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8",
"a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7",
"a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6",
"a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5",
"a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4",
"a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3",
"a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2",
"a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1",
};
/**********************************\
==================================
Random numbers
==================================
\**********************************/
// pseudo random number state
unsigned int random_state = 1804289383;
// generate 32-bit pseudo legal numbers
unsigned int get_random_U32_number()
{
// get current state
unsigned int number = random_state;
// XOR shift algorithm
number ^= number << 13;
number ^= number >> 17;
number ^= number << 5;
// update random number state
random_state = number;
// return random number
return number;
}
// generate 64-bit pseudo legal numbers
U64 get_random_U64_number()
{
// define 4 random numbers
U64 n1, n2, n3, n4;
// init random numbers slicing 16 bits from MS1B side
n1 = (U64)(get_random_U32_number()) & 0xFFFF;
n2 = (U64)(get_random_U32_number()) & 0xFFFF;
n3 = (U64)(get_random_U32_number()) & 0xFFFF;
n4 = (U64)(get_random_U32_number()) & 0xFFFF;
// return random number
return n1 | (n2 << 16) | (n3 << 32) | (n4 << 48);
}
// generate magic number candidate
U64 generate_magic_number()
{
return get_random_U64_number() & get_random_U64_number() & get_random_U64_number();
}
/**********************************\
==================================
Bit manipulations
==================================
\**********************************/
// set/get/pop bit macros
#define set_bit(bitboard, square) (bitboard |= (1ULL << square))
#define get_bit(bitboard, square) (bitboard & (1ULL << square))
#define pop_bit(bitboard, square) (get_bit(bitboard, square) ? bitboard ^= (1ULL << square) : 0)
// count bits within a bitboard (Brian Kernighan's way)
static inline int count_bits(U64 bitboard)
{
// bit counter
int count = 0;
// consecutively reset least significant 1st bit
while (bitboard)
{
// increment count
count++;
// reset least significant 1st bit
bitboard &= bitboard - 1;
}
// return bit count
return count;
}
// get least significant 1st bit index
static inline int get_ls1b_index(U64 bitboard)
{
// make sure bitboard is not 0
if (bitboard)
{
// count trailing bits before LS1B
return count_bits((bitboard & -bitboard) - 1);
}
//otherwise
else
// return illegal index
return -1;
}
/**********************************\
==================================
Input & Output
==================================
\**********************************/
// print bitboard
void print_bitboard(U64 bitboard)
{
printf("\n");
// loop over board ranks
for (int rank = 0; rank < 8; rank++)
{
// loop over board files
for (int file = 0; file < 8; file++)
{
// convert file & rank into square index
int square = rank * 8 + file;
// print ranks
if (!file)
printf(" %d ", 8 - rank);
// print bit state (either 1 or 0)
printf(" %d", get_bit(bitboard, square) ? 1 : 0);
}
// print new line every rank
printf("\n");
}
// print board files
printf("\n a b c d e f g h\n\n");
// print bitboard as unsigned decimal number
printf(" Bitboard: %llud\n\n", bitboard);
}
/**********************************\
==================================
Attacks
==================================
\**********************************/
/*
not A file
8 0 1 1 1 1 1 1 1
7 0 1 1 1 1 1 1 1
6 0 1 1 1 1 1 1 1
5 0 1 1 1 1 1 1 1
4 0 1 1 1 1 1 1 1
3 0 1 1 1 1 1 1 1
2 0 1 1 1 1 1 1 1
1 0 1 1 1 1 1 1 1
a b c d e f g h
not H file
8 1 1 1 1 1 1 1 0
7 1 1 1 1 1 1 1 0
6 1 1 1 1 1 1 1 0
5 1 1 1 1 1 1 1 0
4 1 1 1 1 1 1 1 0
3 1 1 1 1 1 1 1 0
2 1 1 1 1 1 1 1 0
1 1 1 1 1 1 1 1 0
a b c d e f g h
not HG file
8 1 1 1 1 1 1 0 0
7 1 1 1 1 1 1 0 0
6 1 1 1 1 1 1 0 0
5 1 1 1 1 1 1 0 0
4 1 1 1 1 1 1 0 0
3 1 1 1 1 1 1 0 0
2 1 1 1 1 1 1 0 0
1 1 1 1 1 1 1 0 0
a b c d e f g h
not AB file
8 0 0 1 1 1 1 1 1
7 0 0 1 1 1 1 1 1
6 0 0 1 1 1 1 1 1
5 0 0 1 1 1 1 1 1
4 0 0 1 1 1 1 1 1
3 0 0 1 1 1 1 1 1
2 0 0 1 1 1 1 1 1
1 0 0 1 1 1 1 1 1
a b c d e f g h
*/
// not A file constant
const U64 not_a_file = 18374403900871474942ULL;
// not H file constant
const U64 not_h_file = 9187201950435737471ULL;
// not HG file constant
const U64 not_hg_file = 4557430888798830399ULL;
// not AB file constant
const U64 not_ab_file = 18229723555195321596ULL;
// bishop relevant occupancy bit count for every square on board
const int bishop_relevant_bits[64] = {
6, 5, 5, 5, 5, 5, 5, 6,
5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 7, 7, 7, 7, 5, 5,
5, 5, 7, 9, 9, 7, 5, 5,
5, 5, 7, 9, 9, 7, 5, 5,
5, 5, 7, 7, 7, 7, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
6, 5, 5, 5, 5, 5, 5, 6
};
// rook relevant occupancy bit count for every square on board
const int rook_relevant_bits[64] = {
12, 11, 11, 11, 11, 11, 11, 12,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
12, 11, 11, 11, 11, 11, 11, 12
};
// rook magic numbers
U64 rook_magic_numbers[64] = {
0x8a80104000800020ULL,
0x140002000100040ULL,
0x2801880a0017001ULL,
0x100081001000420ULL,
0x200020010080420ULL,
0x3001c0002010008ULL,
0x8480008002000100ULL,
0x2080088004402900ULL,
0x800098204000ULL,
0x2024401000200040ULL,
0x100802000801000ULL,
0x120800800801000ULL,
0x208808088000400ULL,
0x2802200800400ULL,
0x2200800100020080ULL,
0x801000060821100ULL,
0x80044006422000ULL,
0x100808020004000ULL,
0x12108a0010204200ULL,
0x140848010000802ULL,
0x481828014002800ULL,
0x8094004002004100ULL,
0x4010040010010802ULL,
0x20008806104ULL,
0x100400080208000ULL,
0x2040002120081000ULL,
0x21200680100081ULL,
0x20100080080080ULL,
0x2000a00200410ULL,
0x20080800400ULL,
0x80088400100102ULL,
0x80004600042881ULL,
0x4040008040800020ULL,
0x440003000200801ULL,
0x4200011004500ULL,
0x188020010100100ULL,
0x14800401802800ULL,
0x2080040080800200ULL,
0x124080204001001ULL,
0x200046502000484ULL,
0x480400080088020ULL,
0x1000422010034000ULL,
0x30200100110040ULL,
0x100021010009ULL,
0x2002080100110004ULL,
0x202008004008002ULL,
0x20020004010100ULL,
0x2048440040820001ULL,
0x101002200408200ULL,
0x40802000401080ULL,
0x4008142004410100ULL,
0x2060820c0120200ULL,
0x1001004080100ULL,
0x20c020080040080ULL,
0x2935610830022400ULL,
0x44440041009200ULL,
0x280001040802101ULL,
0x2100190040002085ULL,
0x80c0084100102001ULL,
0x4024081001000421ULL,
0x20030a0244872ULL,
0x12001008414402ULL,
0x2006104900a0804ULL,
0x1004081002402ULL
};
// bishop magic numbers
U64 bishop_magic_numbers[64] = {
0x40040844404084ULL,
0x2004208a004208ULL,
0x10190041080202ULL,
0x108060845042010ULL,
0x581104180800210ULL,
0x2112080446200010ULL,
0x1080820820060210ULL,
0x3c0808410220200ULL,
0x4050404440404ULL,
0x21001420088ULL,
0x24d0080801082102ULL,
0x1020a0a020400ULL,
0x40308200402ULL,
0x4011002100800ULL,
0x401484104104005ULL,
0x801010402020200ULL,
0x400210c3880100ULL,
0x404022024108200ULL,
0x810018200204102ULL,
0x4002801a02003ULL,
0x85040820080400ULL,
0x810102c808880400ULL,
0xe900410884800ULL,
0x8002020480840102ULL,
0x220200865090201ULL,
0x2010100a02021202ULL,
0x152048408022401ULL,
0x20080002081110ULL,
0x4001001021004000ULL,
0x800040400a011002ULL,
0xe4004081011002ULL,
0x1c004001012080ULL,
0x8004200962a00220ULL,
0x8422100208500202ULL,
0x2000402200300c08ULL,
0x8646020080080080ULL,
0x80020a0200100808ULL,
0x2010004880111000ULL,
0x623000a080011400ULL,
0x42008c0340209202ULL,
0x209188240001000ULL,
0x400408a884001800ULL,
0x110400a6080400ULL,
0x1840060a44020800ULL,
0x90080104000041ULL,
0x201011000808101ULL,
0x1a2208080504f080ULL,
0x8012020600211212ULL,
0x500861011240000ULL,
0x180806108200800ULL,
0x4000020e01040044ULL,
0x300000261044000aULL,
0x802241102020002ULL,
0x20906061210001ULL,
0x5a84841004010310ULL,
0x4010801011c04ULL,
0xa010109502200ULL,
0x4a02012000ULL,
0x500201010098b028ULL,
0x8040002811040900ULL,
0x28000010020204ULL,
0x6000020202d0240ULL,
0x8918844842082200ULL,
0x4010011029020020ULL
};
// pawn attacks table [side][square]
U64 pawn_attacks[2][64];
// knight attacks table [square]
U64 knight_attacks[64];
// king attacks table [square]
U64 king_attacks[64];
// generate pawn attacks
U64 mask_pawn_attacks(int side, int square)
{
// result attacks bitboard
U64 attacks = 0ULL;
// piece bitboard
U64 bitboard = 0ULL;
// set piece on board
set_bit(bitboard, square);
// white pawns
if (!side)
{
// generate pawn attacks
if ((bitboard >> 7) & not_a_file) attacks |= (bitboard >> 7);
if ((bitboard >> 9) & not_h_file) attacks |= (bitboard >> 9);
}
// black pawns
else
{
// generate pawn attacks
if ((bitboard << 7) & not_h_file) attacks |= (bitboard << 7);
if ((bitboard << 9) & not_a_file) attacks |= (bitboard << 9);
}
// return attack map
return attacks;
}
// generate knight attacks
U64 mask_knight_attacks(int square)
{
// result attacks bitboard
U64 attacks = 0ULL;
// piece bitboard
U64 bitboard = 0ULL;
// set piece on board
set_bit(bitboard, square);
// generate knight attacks
if ((bitboard >> 17) & not_h_file) attacks |= (bitboard >> 17);
if ((bitboard >> 15) & not_a_file) attacks |= (bitboard >> 15);
if ((bitboard >> 10) & not_hg_file) attacks |= (bitboard >> 10);
if ((bitboard >> 6) & not_ab_file) attacks |= (bitboard >> 6);
if ((bitboard << 17) & not_a_file) attacks |= (bitboard << 17);
if ((bitboard << 15) & not_h_file) attacks |= (bitboard << 15);
if ((bitboard << 10) & not_ab_file) attacks |= (bitboard << 10);
if ((bitboard << 6) & not_hg_file) attacks |= (bitboard << 6);
// return attack map
return attacks;
}
// generate king attacks
U64 mask_king_attacks(int square)
{
// result attacks bitboard
U64 attacks = 0ULL;
// piece bitboard
U64 bitboard = 0ULL;
// set piece on board
set_bit(bitboard, square);
// generate king attacks
if (bitboard >> 8) attacks |= (bitboard >> 8);
if ((bitboard >> 9) & not_h_file) attacks |= (bitboard >> 9);
if ((bitboard >> 7) & not_a_file) attacks |= (bitboard >> 7);
if ((bitboard >> 1) & not_h_file) attacks |= (bitboard >> 1);
if (bitboard << 8) attacks |= (bitboard << 8);
if ((bitboard << 9) & not_a_file) attacks |= (bitboard << 9);
if ((bitboard << 7) & not_h_file) attacks |= (bitboard << 7);
if ((bitboard << 1) & not_a_file) attacks |= (bitboard << 1);
// return attack map
return attacks;
}
// mask bishop attacks
U64 mask_bishop_attacks(int square)
{
// result attacks bitboard
U64 attacks = 0ULL;
// init ranks & files
int r, f;
// init target rank & files
int tr = square / 8;
int tf = square % 8;
// mask relevant bishop occupancy bits
for (r = tr + 1, f = tf + 1; r <= 6 && f <= 6; r++, f++) attacks |= (1ULL << (r * 8 + f));
for (r = tr - 1, f = tf + 1; r >= 1 && f <= 6; r--, f++) attacks |= (1ULL << (r * 8 + f));
for (r = tr + 1, f = tf - 1; r <= 6 && f >= 1; r++, f--) attacks |= (1ULL << (r * 8 + f));
for (r = tr - 1, f = tf - 1; r >= 1 && f >= 1; r--, f--) attacks |= (1ULL << (r * 8 + f));
// return attack map
return attacks;
}
// mask rook attacks
U64 mask_rook_attacks(int square)
{
// result attacks bitboard
U64 attacks = 0ULL;
// init ranks & files
int r, f;
// init target rank & files
int tr = square / 8;
int tf = square % 8;
// mask relevant rook occupancy bits
for (r = tr + 1; r <= 6; r++) attacks |= (1ULL << (r * 8 + tf));
for (r = tr - 1; r >= 1; r--) attacks |= (1ULL << (r * 8 + tf));
for (f = tf + 1; f <= 6; f++) attacks |= (1ULL << (tr * 8 + f));
for (f = tf - 1; f >= 1; f--) attacks |= (1ULL << (tr * 8 + f));
// return attack map
return attacks;
}
// generate bishop attacks on the fly
U64 bishop_attacks_on_the_fly(int square, U64 block)
{
// result attacks bitboard
U64 attacks = 0ULL;
// init ranks & files
int r, f;
// init target rank & files
int tr = square / 8;
int tf = square % 8;
// generate bishop atacks
for (r = tr + 1, f = tf + 1; r <= 7 && f <= 7; r++, f++)
{
attacks |= (1ULL << (r * 8 + f));
if ((1ULL << (r * 8 + f)) & block) break;
}
for (r = tr - 1, f = tf + 1; r >= 0 && f <= 7; r--, f++)
{
attacks |= (1ULL << (r * 8 + f));
if ((1ULL << (r * 8 + f)) & block) break;
}
for (r = tr + 1, f = tf - 1; r <= 7 && f >= 0; r++, f--)
{
attacks |= (1ULL << (r * 8 + f));
if ((1ULL << (r * 8 + f)) & block) break;
}
for (r = tr - 1, f = tf - 1; r >= 0 && f >= 0; r--, f--)
{
attacks |= (1ULL << (r * 8 + f));
if ((1ULL << (r * 8 + f)) & block) break;
}
// return attack map
return attacks;
}
// generate rook attacks on the fly
U64 rook_attacks_on_the_fly(int square, U64 block)
{
// result attacks bitboard
U64 attacks = 0ULL;
// init ranks & files
int r, f;
// init target rank & files
int tr = square / 8;
int tf = square % 8;
// generate rook attacks
for (r = tr + 1; r <= 7; r++)
{
attacks |= (1ULL << (r * 8 + tf));
if ((1ULL << (r * 8 + tf)) & block) break;
}
for (r = tr - 1; r >= 0; r--)
{
attacks |= (1ULL << (r * 8 + tf));
if ((1ULL << (r * 8 + tf)) & block) break;
}
for (f = tf + 1; f <= 7; f++)
{
attacks |= (1ULL << (tr * 8 + f));
if ((1ULL << (tr * 8 + f)) & block) break;
}
for (f = tf - 1; f >= 0; f--)
{
attacks |= (1ULL << (tr * 8 + f));
if ((1ULL << (tr * 8 + f)) & block) break;
}
// return attack map
return attacks;
}
// init leaper pieces attacks
void init_leapers_attacks()
{
// loop over 64 board squares
for (int square = 0; square < 64; square++)
{
// init pawn attacks
pawn_attacks[white][square] = mask_pawn_attacks(white, square);
pawn_attacks[black][square] = mask_pawn_attacks(black, square);
// init knight attacks
knight_attacks[square] = mask_knight_attacks(square);
// init king attacks
king_attacks[square] = mask_king_attacks(square);
}
}
// set occupancies
U64 set_occupancy(int index, int bits_in_mask, U64 attack_mask)
{
// occupancy map
U64 occupancy = 0ULL;
// loop over the range of bits within attack mask
for (int count = 0; count < bits_in_mask; count++)
{
// get LS1B index of attacks mask
int square = get_ls1b_index(attack_mask);
// pop LS1B in attack map
pop_bit(attack_mask, square);
// make sure occupancy is on board
if (index & (1 << count))
// populate occupancy map
occupancy |= (1ULL << square);
}
// return occupancy map
return occupancy;
}
/**********************************\
==================================
Magics
==================================
\**********************************/
// find appropriate magic number
U64 find_magic_number(int square, int relevant_bits, int bishop)
{
// init occupancies
U64 occupancies[4096];
// init attack tables
U64 attacks[4096];
// init used attacks
U64 used_attacks[4096];
// init attack mask for a current piece
U64 attack_mask = bishop ? mask_bishop_attacks(square) : mask_rook_attacks(square);
// init occupancy indicies
int occupancy_indicies = 1 << relevant_bits;
// loop over occupancy indicies
for (int index = 0; index < occupancy_indicies; index++)
{
// init occupancies
occupancies[index] = set_occupancy(index, relevant_bits, attack_mask);
// init attacks
attacks[index] = bishop ? bishop_attacks_on_the_fly(square, occupancies[index]) :
rook_attacks_on_the_fly(square, occupancies[index]);
}
// test magic numbers loop
for (int random_count = 0; random_count < 100000000; random_count++)
{
// generate magic number candidate
U64 magic_number = generate_magic_number();
// skip inappropriate magic numbers
if (count_bits((attack_mask * magic_number) & 0xFF00000000000000) < 6) continue;
// init used attacks
memset(used_attacks, 0ULL, sizeof(used_attacks));
// init index & fail flag
int index, fail;
// test magic index loop
for (index = 0, fail = 0; !fail && index < occupancy_indicies; index++)
{
// init magic index
int magic_index = (int)((occupancies[index] * magic_number) >> (64 - relevant_bits));
//printf("occupancies[%d] = %llud\n\n",index,occupancies[index]);
//printf("magic_index = %d\n\n", magic_index);
//printf("magic_number = %llud\n\n", magic_number);
//getchar();
// if magic index works
if (used_attacks[magic_index] == 0ULL)
// init used attacks
used_attacks[magic_index] = attacks[index];
// otherwise
else if (used_attacks[magic_index] != attacks[index])
// magic index doesn't work
fail = 1;
}
// if magic number works
if (!fail)
// return it
return magic_number;
}
// if magic number doesn't work
printf(" Magic number fails!\n");
return 0ULL;
}
// init magic numbers
void init_magic_numbers()
{
// loop over 64 board squares
for (int square = 0; square < 64; square++)
// init rook magic numbers
rook_magic_numbers[square] = find_magic_number(square, rook_relevant_bits[square], rook);
// loop over 64 board squares
for (int square = 0; square < 64; square++)
// init bishop magic numbers
bishop_magic_numbers[square] = find_magic_number(square, bishop_relevant_bits[square], bishop);
}
/**********************************\
==================================
Init all
==================================
\**********************************/
// init all variables
void init_all()
{
// init leaper pieces attacks
init_leapers_attacks();
// init magic numbers
init_magic_numbers();
}
/**********************************\
==================================
Main driver
==================================
\**********************************/
int main()
{
// init all
init_all();
return 0;
}
Делфи program bbc;
{$APPTYPE CONSOLE}
uses
SysUtils,Windows,TypInfo;
type
U64 = UInt64;
TBoard_squares = (
a8, b8, c8, d8, e8, f8, g8, h8,
a7, b7, c7, d7, e7, f7, g7, h7,
a6, b6, c6, d6, e6, f6, g6, h6,
a5, b5, c5, d5, e5, f5, g5, h5,
a4, b4, c4, d4, e4, f4, g4, h4,
a3, b3, c3, d3, e3, f3, g3, h3,
a2, b2, c2, d2, e2, f2, g2, h2,
a1, b1, c1, d1, e1, f1, g1, h1
);
// sides to move (colors)
TSide = (white, black);
// bishop and rook
Tbishop = (rook, bishop);
//Первый индекс обозначает номер строки в матрице, второй индекс – номер столбца
TPawn_attacks=array[TSide,TBoard_squares] of U64;
TChip_attacks=array[TBoard_squares] of U64;
var
bitboard:U64=0;
block:U64=0;
// pawn attacks table [side][square]
pawn_attacks:TPawn_attacks;
// knight attacks table [square]
knight_attacks:TChip_attacks;
// king attacks table [square]
king_attacks:TChip_attacks;
square:TBoard_squares;
// pseudo random number state
//cardinal - беззнаковое целое
stateU32:cardinal = 1804289383;
const
{/*
not A file
8 0 1 1 1 1 1 1 1
7 0 1 1 1 1 1 1 1
6 0 1 1 1 1 1 1 1
5 0 1 1 1 1 1 1 1
4 0 1 1 1 1 1 1 1
3 0 1 1 1 1 1 1 1
2 0 1 1 1 1 1 1 1
1 0 1 1 1 1 1 1 1
a b c d e f g h
not H file
8 1 1 1 1 1 1 1 0
7 1 1 1 1 1 1 1 0
6 1 1 1 1 1 1 1 0
5 1 1 1 1 1 1 1 0
4 1 1 1 1 1 1 1 0
3 1 1 1 1 1 1 1 0
2 1 1 1 1 1 1 1 0
1 1 1 1 1 1 1 1 0
a b c d e f g h
not HG file
8 1 1 1 1 1 1 0 0
7 1 1 1 1 1 1 0 0
6 1 1 1 1 1 1 0 0
5 1 1 1 1 1 1 0 0
4 1 1 1 1 1 1 0 0
3 1 1 1 1 1 1 0 0
2 1 1 1 1 1 1 0 0
1 1 1 1 1 1 1 0 0
a b c d e f g h
not AB file
8 0 0 1 1 1 1 1 1
7 0 0 1 1 1 1 1 1
6 0 0 1 1 1 1 1 1
5 0 0 1 1 1 1 1 1
4 0 0 1 1 1 1 1 1
3 0 0 1 1 1 1 1 1
2 0 0 1 1 1 1 1 1
1 0 0 1 1 1 1 1 1
a b c d e f g h
*/}
// not A file constant
not_a_file:U64 = $FEFEFEFEFEFEFEFE;//18374403900871474942;
// not H file constant
not_h_file:U64 = $7F7F7F7F7F7F7F7F;//9187201950435737471;
// not HG file constant
not_hg_file:U64 =$3F3F3F3F3F3F3F3F;//4557430888798830399;
// not AB file constant
not_ab_file:U64 =$FCFCFCFCFCFCFCFC;//18229723555195321596;
square_to_coordinates: array [0..63] of string = (
'a8', 'b8', 'c8', 'd8', 'e8', 'f8', 'g8', 'h8',
'a7', 'b7', 'c7', 'd7', 'e7', 'f7', 'g7', 'h7',
'a6', 'b6', 'c6', 'd6', 'e6', 'f6', 'g6', 'h6',
'a5', 'b5', 'c5', 'd5', 'e5', 'f5', 'g5', 'h5',
'a4', 'b4', 'c4', 'd4', 'e4', 'f4', 'g4', 'h4',
'a3', 'b3', 'c3', 'd3', 'e3', 'f3', 'g3', 'h3',
'a2', 'b2', 'c2', 'd2', 'e2', 'f2', 'g2', 'h2',
'a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1'
);
// bishop relevant occupancy bit count for every square on board
bishop_relevant_bits: array [0..63] of integer =(
6, 5, 5, 5, 5, 5, 5, 6,
5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 7, 7, 7, 7, 5, 5,
5, 5, 7, 9, 9, 7, 5, 5,
5, 5, 7, 9, 9, 7, 5, 5,
5, 5, 7, 7, 7, 7, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
6, 5, 5, 5, 5, 5, 5, 6
);
// rook relevant occupancy bit count for every square on board
rook_relevant_bits: array [0..63] of integer =(
12, 11, 11, 11, 11, 11, 11, 12,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
12, 11, 11, 11, 11, 11, 11, 12
);
{/**********************************\
==================================
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():U64;
var
// define 4 random numbers
n1,n2,n3,n4:U64;
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():U64;
begin
result:=get_random_U64_number() and get_random_U64_number() and get_random_U64_number();
end;
{/**********************************\
==================================
Bit manipulations
==================================
\**********************************/}
// set/get/pop
function get_bit(bb:U64;const sq:integer):integer;
begin
get_bit:=(bb shr sq) and 1;
end;
procedure set_bit(var bb:U64;const sq:TBoard_squares);
begin
bb:=bb or ($8000000000000000 shr (63-Ord(sq)));
end;
procedure pop_bit(var bb:U64;const sq:TBoard_squares);
begin
if get_bit(bb,Ord(sq))>0 then bb:=bb xor ($8000000000000000 shr (63-Ord(sq)));
end;
// count bits within a bitboard (Brian Kernighan's way)
function count_bits(bb:U64):integer;
var
count:integer;
begin
// bit counter
count := 0;
// consecutively reset least significant 1st bit
while bb>0
do begin
// increment count
Inc(count);
// reset least significant 1st bit
bb := bb and (bb - 1);
end;
count_bits:=count;
end;
// get least significant 1st bit index
function get_ls1b_index(bb:U64):integer;
begin
// make sure bitboard is not 0
if bb<>0 then
// count trailing bits before LS1B
result:=count_bits((bb and (-bb))-1)
// return illegal index
else result:=-1;
end;
{/**********************************\
==================================
Input & Output
==================================
\**********************************/}
// print bitboard
procedure print_bitboard(bb:U64);
var
rank_,file_,sq:integer;
begin
Writeln('');
// loop over board ranks
for rank_ := 0 to 7 do begin
// loop over board files
for file_ := 0 to 7 do begin
// convert file & rank into square index
sq:=rank_*8 + file_;
//// print ranks
if file_ = 0 then begin
Write(8 - rank_);Write(' ');
end;
Write(' ');
// print bit state (either 1 or 0)
Write(get_bit(bb,sq));
end;
// print new line every rank
Writeln('');
end;
// print new line
Writeln('');
// print board files
Writeln(' a b c d e f g h');
Writeln('');
// print bitboard as unsigned decimal number
Write('bitboard = ',bb);
Writeln('');
end;
{/**********************************\
==================================
Attacks
==================================
\**********************************/}
// generate pawn attacks
function mask_pawn_attacks(side:TSide;square:TBoard_squares):U64;
var
attacks,bitboard:U64;
begin
// result attacks bitboard
attacks := 0;
// piece bitboard
bitboard := 0;
// set piece on board
set_bit(bitboard, square);
// white pawns
if (side=white) then begin
if ((bitboard shr 7) and not_a_file)>0 then attacks:=attacks or (bitboard shr 7);
if ((bitboard shr 9) and not_h_file)>0 then attacks:=attacks or (bitboard shr 9);
end
// black pawns
else begin
if ((bitboard shl 7) and not_h_file)>0 then attacks:=attacks or (bitboard shl 7);
if ((bitboard shl 9) and not_a_file)>0 then attacks:=attacks or (bitboard shl 9);
end;
// return attack map
mask_pawn_attacks:=attacks;
end;
// generate knight attacks
function mask_knight_attacks(square:TBoard_squares):U64;
var
attacks,bitboard:U64;
begin
// result attacks bitboard
attacks := 0;
// piece bitboard
bitboard := 0;
// set piece on board
set_bit(bitboard, square);
// generate knight attacks
if ((bitboard shr 17) and not_h_file)>0 then attacks:=attacks or (bitboard shr 17);
if ((bitboard shr 15) and not_a_file)>0 then attacks:=attacks or (bitboard shr 15);
if ((bitboard shr 10) and not_hg_file)>0 then attacks:=attacks or (bitboard shr 10);
if ((bitboard shr 6) and not_ab_file)>0 then attacks:=attacks or (bitboard shr 6);
if ((bitboard shl 17) and not_a_file)>0 then attacks:=attacks or (bitboard shl 17);
if ((bitboard shl 15) and not_h_file)>0 then attacks:=attacks or (bitboard shl 15);
if ((bitboard shl 10) and not_ab_file)>0 then attacks:=attacks or (bitboard shl 10);
if ((bitboard shl 6) and not_hg_file)>0 then attacks:=attacks or (bitboard shl 6);
// return attack map
mask_knight_attacks:=attacks;
end;
// generate king attacks
function mask_king_attacks(square:TBoard_squares):U64;
var
attacks,bitboard:U64;
begin
// result attacks bitboard
attacks := 0;
// piece bitboard
bitboard := 0;
// set piece on board
set_bit(bitboard, square);
// generate king attacks
if (bitboard shr 8)>0 then attacks:=attacks or (bitboard shr 8);
if ((bitboard shr 9) and not_h_file)>0 then attacks:=attacks or (bitboard shr 9);
if ((bitboard shr 1) and not_h_file)>0 then attacks:=attacks or (bitboard shr 1);
if ((bitboard shr 7) and not_a_file)>0 then attacks:=attacks or (bitboard shr 7);
if (bitboard shl 8)>0 then attacks:=attacks or (bitboard shl 8);
if ((bitboard shl 9) and not_a_file)>0 then attacks:=attacks or (bitboard shl 9);
if ((bitboard shl 1) and not_a_file)>0 then attacks:=attacks or (bitboard shl 1);
if ((bitboard shl 7) and not_h_file)>0 then attacks:=attacks or (bitboard shl 7);
// return attack map
mask_king_attacks:=attacks;
end;
//урок 6-masking relevant bishop occupancy bits to form a key for MAGIC BITBOARDS
// mask bishop attacks
function mask_bishop_attacks(square:TBoard_squares):U64;
var
attacks,n:U64;
r,f,tr,tf:integer;
begin
n:=1;
// result attacks bitboard
attacks := 0;
// init target rank & files
tr := Ord(square) div 8;//целочисленное деление
tf := Ord(square) mod 8;//остаток от деления
// mask relevant bishop occupancy bits
f:=tf+1;
for r:=tr+1 to 6 do begin
if f>6 then break;
attacks := attacks or (n shl (r*8+f));
Inc(f);
end;
f:=tf+1;
for r:=tr-1 downto 1 do begin
if f>6 then break;
attacks := attacks or (n shl (r*8+f));
Inc(f);
end;
f:=tf-1;
for r:=tr+1 to 6 do begin
if f<1 then break;
attacks := attacks or (n shl (r*8+f));
Dec(f);
end;
f:=tf-1;
for r:=tr-1 downto 1 do begin
if f<1 then break;
attacks := attacks or (n shl (r*8+f));
Dec(f);
end;
// return attack map
mask_bishop_attacks:=attacks;
end;
//урок 7-masking relevant ROOK OCCUPANCY BITS to form a key for MAGIC BITBOARDS
// mask rook attacks
function mask_rook_attacks(square:TBoard_squares):U64;
var
attacks,n:U64;
r,f,tr,tf:integer;
begin
n:=1;
// result attacks bitboard
attacks := 0;
// init target rank & files
tr := Ord(square) div 8;//целочисленное деление
tf := Ord(square) mod 8;//остаток от деления
// mask relevant bishop occupancy bits
for r:=tr+1 to 6 do attacks := attacks or (n shl (r*8+tf));
for r:=tr-1 downto 1 do attacks := attacks or (n shl (r*8+tf));
for f:=tf+1 to 6 do attacks := attacks or (n shl (tr*8+f));
for f:=tf-1 downto 1 do attacks := attacks or (n shl (tr*8+f));
// return attack map
mask_rook_attacks:=attacks;
end;
//урок 8-generating SLIDER PIECES ATTACKS on the fly for MAGIC BITBOARD purposes
// generate bishop attacks on the fly
function bishop_attacks_on_the_fly(square:TBoard_squares;blk:U64):U64;
var
attacks,n:U64;
r,f,tr,tf:integer;
begin
n:=1;
// result attacks bitboard
attacks := 0;
// init target rank & files
tr := Ord(square) div 8;//целочисленное деление
tf := Ord(square) mod 8;//остаток от деления
// generate bishop atacks
f:=tf+1;
for r:=tr+1 to 7 do begin
if f>7 then break;
attacks := attacks or (n shl (r*8+f));
if ((n shl (r*8+f)) and blk)>0 then break;
Inc(f);
end;
f:=tf+1;
for r:=tr-1 downto 0 do begin
if f>7 then break;
attacks := attacks or (n shl (r*8+f));
if ((n shl (r*8+f)) and blk)>0 then break;
Inc(f);
end;
f:=tf-1;
for r:=tr+1 to 7 do begin
if f<0 then break;
attacks := attacks or (n shl (r*8+f));
if ((n shl (r*8+f)) and blk)>0 then break;
Dec(f);
end;
f:=tf-1;
for r:=tr-1 downto 0 do begin
if f<0 then break;
attacks := attacks or (n shl (r*8+f));
if ((n shl (r*8+f)) and blk)>0 then break;
Dec(f);
end;
// return attack map
bishop_attacks_on_the_fly:=attacks;
end;
// generate rook attacks on the fly
function rook_attacks_on_the_fly(square:TBoard_squares;blk:U64):U64;
var
attacks,n:U64;
r,f,tr,tf:integer;
begin
n:=1;
// result attacks bitboard
attacks := 0;
// init target rank & files
tr := Ord(square) div 8;//целочисленное деление
tf := Ord(square) mod 8;//остаток от деления
// generate rook attacks
for r:=tr+1 to 7 do begin
attacks := attacks or (n shl (r*8+tf));
if ((n shl (r*8+tf)) and blk)>0 then break;
end;
for r:=tr-1 downto 0 do begin
attacks := attacks or (n shl (r*8+tf));
if ((n shl (r*8+tf)) and blk)>0 then break;
end;
for f:=tf+1 to 7 do begin
attacks := attacks or (n shl (tr*8+f));
if ((n shl (tr*8+f)) and blk)>0 then break;
end;
for f:=tf-1 downto 0 do begin
attacks := attacks or (n shl (tr*8+f));
if ((n shl (tr*8+f)) and blk)>0 then break;
end;
// return attack map
rook_attacks_on_the_fly:=attacks;
end;
// init leaper pieces attacks
procedure init_leapers_attacks();
var
square:TBoard_squares;
begin
// loop over 64 board squares
for square := a8 to h1 do begin
//урок 3-generating pre-calculated PAWN ATTACK tables
// init pawn attacks
pawn_attacks[white][square] := mask_pawn_attacks(white,square);
pawn_attacks[black][square] := mask_pawn_attacks(black,square);
//урок 4-generating pre-calculated KNIGHT ATTACK table
// init knight attacks
knight_attacks[square] := mask_knight_attacks(square);
//урок 5-generating pre-calculated KING ATTACK tables
// init king attacks
king_attacks[square] := mask_king_attacks(square);
end;
end;
// set occupancies
//перебираем по очереди биты в attack_mask
//испр. ошибка - было -var attack_mask:U64, стало-attack_mask:U64
function set_occupancy(index,bits_in_mask:integer;attack_mask:U64):U64;
var
occupancy,n:U64;
count:integer;
square_:TBoard_squares;
begin
n:=1;
// occupancy map
occupancy:=0;
// loop over the range of bits within attack mask
for count:=0 to bits_in_mask-1 do begin
// get LS1B index of attacks mask
square_:=TBoard_squares(get_ls1b_index(attack_mask));
// pop LS1B in attack map
pop_bit(attack_mask,square_);
// make sure occupancy is on board
if (index and (n shl count))>0 then
occupancy:=occupancy or (n shl integer(square_));
end;
set_occupancy:=occupancy;
end;
{/**********************************\
==================================
Magics
function rook_attacks_on_the_fly(square:TBoard_squares;blk:U64):U64;
==================================
\**********************************/}
// find appropriate magic number
function find_magic_number(square:TBoard_squares;relevant_bits:integer;bishop_flag:Tbishop):U64;
//function find_magic_number():U64;
var
// init occupancies
occupancies:array[0..4095] of U64;
// init attack tables
attacks:array[0..4095] of U64;
// init used attacks
used_attacks:array[0..4095] of U64;
n:Byte;
// init attack mask for a current piece
attack_mask:U64;
// init occupancy indicies
occupancy_indicies:integer;
magic_index:integer;
index,fail,random_count:integer;
magic_number:U64;
//memset
//https://metanit.com/c/tutorial/8.3.php?ysclid=ldc20xz850854644891
begin
attack_mask:=0;
// init attack mask for a current piece
if bishop_flag=bishop then attack_mask:=mask_bishop_attacks(square)
else attack_mask:=mask_rook_attacks(square);
// init occupancy indicies напр. ладья на a1 - rook_relevant_bits: array [ ] = 12
// 1 shl 12 = 4096
occupancy_indicies:=1 shl relevant_bits;
// loop over occupancy indicies
for index:=0 to occupancy_indicies-1 do begin
// init occupancies
occupancies[index] := set_occupancy(index,relevant_bits,attack_mask);
// init attacks
if bishop_flag=bishop then attacks[index]:= bishop_attacks_on_the_fly(square, occupancies[index])
else attacks[index]:= rook_attacks_on_the_fly(square, occupancies[index]);
end;
// test magic numbers loop
n:=0;
for random_count:= 0 to 100000000 do begin
// generate magic number candidate
magic_number := generate_magic_number();
// skip inappropriate magic numbers
if (count_bits((attack_mask * magic_number) and $FF00000000000000) < 6) then continue;
//http://www.delphibasics.ru/FillChar.php
// procedure FillChar ( var Buffer; FillCount : Integer; FillValue : Byte ) ;
{Процедура FillChar заполняет раздел памяти Buffer
тем же самым байтом или символом FillValue FillCount раз.
Это используется, преимущественно, для инициализирования массивов чисел.}
// Теперь заполняем массив used_attacks значением 0
// UInt64 - 8 байт
FillChar(used_attacks, 4096*SizeOf(UInt64), n);
fail := 0;
for index:=0 to occupancy_indicies-1 do begin
// init magic index
magic_index := integer((occupancies[index] * magic_number) shr (64 - relevant_bits));
{Writeln('occupancies[',index,'] = ',occupancies[index]);
Writeln('magic_index = ',magic_index);
Writeln('magic_number = ',magic_number);
Readln;}
// if magic index works
if (used_attacks[magic_index] = 0) then
// init used attacks
used_attacks[magic_index] := attacks[index]
// otherwise
else if (used_attacks[magic_index] <> attacks[index]) then begin
// magic index doesn't work
fail := 1;break;
end;
end;
// if magic number works
if (fail=0) then begin
// return it
result:=magic_number;
exit;
end;
end;
// if magic number doesn't work
Writeln(' Magic number fails! ');
result:=0;
end;
// init magic numbers
procedure init_magic_numbers();
var
tem64:U64;
temp32:integer;
begin
// loop over 64 board squares
for square := a8 to h1 do begin
tem64:=find_magic_number(square,rook_relevant_bits[integer(square)],rook);
Write('0x');Write(Format('%x',[tem64]));Write('ULL');
Writeln('');
//Write(tem64);
//Readln;
end;
Writeln('====================');
for square := a8 to h1 do begin
tem64:=find_magic_number(square,bishop_relevant_bits[integer(square)],bishop);
Write('0x');Write(Format('%x',[tem64]));Write('ULL');
Writeln('');
//Write(tem64);
//Readln;
end;
end;
{/**********************************\
==================================
Main driver
==================================
\**********************************/}
begin
//Переключение окна консоли на кодовую страницу CP1251 (Win-1251).
//Если после переключения русские буквы показываются неверно,
//следует открыть системное меню консольного окна - щелчком мыши в левом
//верхнем углу окна консоли. И выбрать:
//Свойства - закладка "Шрифт" - выбрать шрифт: "Lucida Console".
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
// ==================================
init_leapers_attacks();
init_magic_numbers();
Writeln('Нажмите <ENTER>, чтобы выйти ...');
Readln;
end.
{print_bitboard(get_random_U32_number());
print_bitboard(get_random_U32_number());
print_bitboard(get_random_U64_number());
print_bitboard(generate_magic_number());}
{Writeln('bishop_relevant_bits');
for square := a8 to h1 do begin
if (integer(square) mod 8)=0 then Writeln('');
Write(count_bits(mask_bishop_attacks(square))); Write(' ');
end;
Writeln('');
Writeln('rook_relevant_bits');
for square := a8 to h1 do begin
if (integer(square) mod 8)=0 then Writeln('');
Write(count_bits(mask_rook_attacks(square))); Write(' ');
end;
Writeln('');}
{ block:=4512464976740352;
print_bitboard(block);
square:=TBoard_squares(get_ls1b_index(block));
Write('get_ls1b_index:',integer(square));
Writeln(' coordinate:',square_to_coordinates[integer(square)]);
set_bit(bitboard,square);
print_bitboard(bitboard);}
//ZobristKey[i][j][z]=rd.nextLong()^(rd.nextLong()<<15)^(rd.nextLong()<<30)^(rd.nextLong()<<45)^(rd.nextLong()<<60);
//<< shl
Магические числа, полученные с помощью кода адаптированного на Делфи 0x8A80104000800020ULL
0x140002000100040ULL
0x2801880A0017001ULL
0x100081001000420ULL
0x200020010080420ULL
0x3001C0002010008ULL
0x8480008002000100ULL
0x2080088004402900ULL
0x800098204000ULL
0x2024401000200040ULL
0x100802000801000ULL
0x120800800801000ULL
0x208808088000400ULL
0x2802200800400ULL
0x2200800100020080ULL
0x801000060821100ULL
0x80044006422000ULL
0x100808020004000ULL
0x12108A0010204200ULL
0x140848010000802ULL
0x481828014002800ULL
0x8094004002004100ULL
0x4010040010010802ULL
0x20008806104ULL
0x100400080208000ULL
0x2040002120081000ULL
0x21200680100081ULL
0x20100080080080ULL
0x2000A00200410ULL
0x20080800400ULL
0x80088400100102ULL
0x80004600042881ULL
0x4040008040800020ULL
0x440003000200801ULL
0x4200011004500ULL
0x188020010100100ULL
0x14800401802800ULL
0x2080040080800200ULL
0x124080204001001ULL
0x200046502000484ULL
0x480400080088020ULL
0x1000422010034000ULL
0x30200100110040ULL
0x100021010009ULL
0x2002080100110004ULL
0x202008004008002ULL
0x20020004010100ULL
0x2048440040820001ULL
0x101002200408200ULL
0x40802000401080ULL
0x4008142004410100ULL
0x2060820C0120200ULL
0x1001004080100ULL
0x20C020080040080ULL
0x2935610830022400ULL
0x44440041009200ULL
0x280001040802101ULL
0x2100190040002085ULL
0x80C0084100102001ULL
0x4024081001000421ULL
0x20030A0244872ULL
0x12001008414402ULL
0x2006104900A0804ULL
0x1004081002402ULL
====================
0x40040844404084ULL
0x2004208A004208ULL
0x10190041080202ULL
0x108060845042010ULL
0x581104180800210ULL
0x2112080446200010ULL
0x1080820820060210ULL
0x3C0808410220200ULL
0x4050404440404ULL
0x21001420088ULL
0x24D0080801082102ULL
0x1020A0A020400ULL
0x40308200402ULL
0x4011002100800ULL
0x401484104104005ULL
0x801010402020200ULL
0x400210C3880100ULL
0x404022024108200ULL
0x810018200204102ULL
0x4002801A02003ULL
0x85040820080400ULL
0x810102C808880400ULL
0xE900410884800ULL
0x8002020480840102ULL
0x220200865090201ULL
0x2010100A02021202ULL
0x152048408022401ULL
0x20080002081110ULL
0x4001001021004000ULL
0x800040400A011002ULL
0xE4004081011002ULL
0x1C004001012080ULL
0x8004200962A00220ULL
0x8422100208500202ULL
0x2000402200300C08ULL
0x8646020080080080ULL
0x80020A0200100808ULL
0x2010004880111000ULL
0x623000A080011400ULL
0x42008C0340209202ULL
0x209188240001000ULL
0x400408A884001800ULL
0x110400A6080400ULL
0x1840060A44020800ULL
0x90080104000041ULL
0x201011000808101ULL
0x1A2208080504F080ULL
0x8012020600211212ULL
0x500861011240000ULL
0x180806108200800ULL
0x4000020E01040044ULL
0x300000261044000AULL
0x802241102020002ULL
0x20906061210001ULL
0x5A84841004010310ULL
0x4010801011C04ULL
0xA010109502200ULL
0x4A02012000ULL
0x500201010098B028ULL
0x8040002811040900ULL
0x28000010020204ULL
0x6000020202D0240ULL
0x8918844842082200ULL
0x4010011029020020ULL
Нажмите <ENTER>, чтобы выйти ...
Вроде такие же, как у Макса
|
Last Edit: 27 Янв 2023 12:58 by alexlaw.
|
|
|
 |