Add files via upload

This commit is contained in:
Coding with Tom
2025-01-12 21:43:58 +00:00
committed by GitHub
parent 19c17b8bf3
commit dcf0d928b7
11 changed files with 3534 additions and 0 deletions

2008
Odin/Perft.odin Normal file

File diff suppressed because it is too large Load Diff

147
Odin/bbconstants.odin Normal file
View File

@@ -0,0 +1,147 @@
package main
SQUARE_BBS: [64]u64 = [64]u64{
1,
2,
4,
8,
16,
32,
64,
128,
256,
512,
1024,
2048,
4096,
8192,
16384,
32768,
65536,
131072,
262144,
524288,
1048576,
2097152,
4194304,
8388608,
16777216,
33554432,
67108864,
134217728,
268435456,
536870912,
1073741824,
2147483648,
4294967296,
8589934592,
17179869184,
34359738368,
68719476736,
137438953472,
274877906944,
549755813888,
1099511627776,
2199023255552,
4398046511104,
8796093022208,
17592186044416,
35184372088832,
70368744177664,
140737488355328,
281474976710656,
562949953421312,
1125899906842624,
2251799813685248,
4503599627370496,
9007199254740992,
18014398509481984,
36028797018963968,
72057594037927936,
144115188075855872,
288230376151711744,
576460752303423488,
1152921504606846976,
2305843009213693952,
4611686018427387904,
9223372036854775808,
};
ONE_U64:u64 : 1;
BP_STARTING_POSITIONS:u64 : 65280;
WP_STARTING_POSITIONS:u64 : 71776119061217280;
BK_STARTING_POSITION:u64 : 16;
WK_STARTING_POSITION:u64 : 1152921504606846976;
BN_STARTING_POSITIONS:u64 : 66;
WN_STARTING_POSITIONS: u64 : 4755801206503243776;
WR_STARTING_POSITIONS: u64 : 9295429630892703744;
BR_STARTING_POSITIONS: u64 : 129;
BB_STARTING_POSITIONS: u64 : 36;
WB_STARTING_POSITIONS: u64 : 2594073385365405696;
WQ_STARTING_POSITION: u64 : 576460752303423488;
BQ_STARTING_POSITION: u64 : 8;
EMPTY_BITBOARD: u64 : 0;
MAX_ULONG: u64 : 18446744073709551615;
MAGIC: u64 : 0x03f79d71b4cb0a89;
@(rodata)
DEBRUIJN64: [64]int = [64]int{
0, 47, 1, 56, 48, 27, 2, 60,
57, 49, 41, 37, 28, 16, 3, 61,
54, 58, 35, 52, 50, 42, 21, 44,
38, 32, 29, 23, 17, 11, 4, 62,
46, 55, 26, 59, 40, 36, 15, 53,
34, 51, 20, 43, 31, 22, 10, 45,
25, 39, 14, 33, 19, 30, 9, 24,
13, 18, 8, 12, 7, 6, 5, 63,
};
BitscanForward :: proc(tempBitboard: u64) -> int {
return (DEBRUIJN64[MAGIC*(tempBitboard~(tempBitboard-1))>>58]);
}
RANK_1_BITBOARD:u64: 18374686479671623680;
RANK_2_BITBOARD:u64: 71776119061217280;
RANK_3_BITBOARD:u64: 280375465082880;
RANK_4_BITBOARD:u64: 1095216660480;
RANK_5_BITBOARD:u64: 4278190080;
RANK_6_BITBOARD:u64: 16711680;
RANK_7_BITBOARD:u64: 65280;
RANK_8_BITBOARD:u64: 255;
FILE_A_BITBOARD:u64: 72340172838076673;
FILE_B_BITBOARD:u64: 144680345676153346;
FILE_C_BITBOARD:u64: 289360691352306692;
FILE_D_BITBOARD:u64: 578721382704613384;
FILE_E_BITBOARD:u64: 1157442765409226768;
FILE_F_BITBOARD:u64: 2314885530818453536;
FILE_G_BITBOARD:u64: 4629771061636907072;
FILE_H_BITBOARD:u64: 9259542123273814144;
@(rodata)
SQ_CHAR_Y: [65]byte = [65]byte{
'8', '8', '8', '8', '8', '8', '8', '8',
'7', '7', '7', '7', '7', '7', '7', '7',
'6', '6', '6', '6', '6', '6', '6', '6',
'5', '5', '5', '5', '5', '5', '5', '5',
'4', '4', '4', '4', '4', '4', '4', '4',
'3', '3', '3', '3', '3', '3', '3', '3',
'2', '2', '2', '2', '2', '2', '2', '2',
'1', '1', '1', '1', '1', '1', '1', '1', 'A',
};
@(rodata)
SQ_CHAR_X: [65]byte = [65]byte{
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'N',
};

15
Odin/bbutils.odin Normal file
View File

@@ -0,0 +1,15 @@
package main
EmptyBit :: proc(bitboard: u64, square: int) -> u64 {
shifted_bitboard := SQUARE_BBS[square];
return bitboard & ~shifted_bitboard;
}
SetBit :: proc(bitboard: u64, square: int) -> u64 {
return bitboard | SQUARE_BBS[square];
}
MoveBit :: proc(bitboard: u64, starting_square: int, target_square: int) -> u64 {
copy_bitboard :u64= EmptyBit(bitboard, starting_square);
return SetBit(copy_bitboard, target_square);
}

122
Odin/board.odin Normal file
View File

@@ -0,0 +1,122 @@
package main
import "core:fmt"
PieceArray: [12]u64 = [12]u64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
whiteToPlay: bool = true;
CastleRights: [4]bool = [4]bool{true, true, true, true};
ep:int = NO_SQUARE;
BoardPly: u8 = 0;
SetStartingPosition :: proc() {
ep = NO_SQUARE;
whiteToPlay = true
CastleRights[0] = true
CastleRights[1] = true
CastleRights[2] = true
CastleRights[3] = true
PieceArray[WP] = WP_STARTING_POSITIONS
PieceArray[WN] = WN_STARTING_POSITIONS
PieceArray[WB] = WB_STARTING_POSITIONS
PieceArray[WR] = WR_STARTING_POSITIONS
PieceArray[WQ] = WQ_STARTING_POSITION
PieceArray[WK] = WK_STARTING_POSITION
PieceArray[BP] = BP_STARTING_POSITIONS
PieceArray[BN] = BN_STARTING_POSITIONS
PieceArray[BB] = BB_STARTING_POSITIONS
PieceArray[BR] = BR_STARTING_POSITIONS
PieceArray[BQ] = BQ_STARTING_POSITION
PieceArray[BK] = BK_STARTING_POSITION
}
PrintBoard :: proc() {
fmt.println("Board:")
boardArray: [64]int = FillBoardArray();
for rank := 0; rank < 8; rank+=1 {
fmt.print(" ")
for file := 0; file < 8; file+=1 {
square: int = (rank * 8) + file
fmt.printf("%c%c ", PieceColours[boardArray[square]], PieceNames[boardArray[square]])
}
fmt.println()
}
fmt.println()
fmt.printf("White to play: %t\n", whiteToPlay)
fmt.printf("Castle: %t %t %t %t\n", CastleRights[0], CastleRights[1], CastleRights[2], CastleRights[3])
fmt.printf("ep: %d\n", ep)
fmt.printf("ply: %d\n", BoardPly)
fmt.println()
fmt.println()
}
PrintAllBitboards :: proc() {
for i := 0; i < 12; i+=1 {
fmt.println(PieceArray[i])
}
}
printBoardBasic :: proc() {
fmt.println("PieceArray:")
for i := 0; i < 12; i+=1 {
fmt.println(PieceArray[i])
}
fmt.printf("white to play: %t\n", whiteToPlay);
fmt.printf("Castle: %t %t %t %t\n", CastleRights[0], CastleRights[1], CastleRights[2], CastleRights[3]);
fmt.printf("ep: %d\n", ep);
fmt.printf("ply %d\n", BoardPly);
}
FillBoardArray :: proc() -> [64]int {
boardArray: [64]int;
for i := 0; i < 64; i+=1 {
boardArray[i] = GetOccupiedIndex(i)
}
return boardArray
}
IsBoardArraySame :: proc(copy: [12]u64) -> bool {
for i := 0; i < 12; i+=1 {
if PieceArray[i] != copy[i] {
fmt.println("ERROR piece not same: %d", i);
return false;
}
}
return true
}
PrintBitboard :: proc(bitboard: u64) {
for rank := 0; rank < 8; rank+=1 {
for file := 0; file < 8; file+=1 {
square :int= (rank * 8) + file;
if (bitboard & (SQUARE_BBS[square])) != 0 {
fmt.print("X ");
continue;
}
fmt.print("_ ");
}
fmt.println();
}
fmt.println(bitboard);
}
ResetBoard :: proc() {
for i := 0; i < 12; i+=1 {
PieceArray[i] = EMPTY_BITBOARD;
}
whiteToPlay = true;
for i := 0; i < 4; i+=1 {
CastleRights[i] = true;
}
ep = NO_SQUARE;
BoardPly = 0;
}

68
Odin/boardconstants.odin Normal file
View File

@@ -0,0 +1,68 @@
package main
A8 :: 0
B8 :: 1
C8 :: 2
D8 :: 3
E8 :: 4
F8 :: 5
G8 :: 6
H8 :: 7
A7 :: 8
B7 :: 9
C7 :: 10
D7 :: 11
E7 :: 12
F7 :: 13
G7 :: 14
H7 :: 15
A6 :: 16
B6 :: 17
C6 :: 18
D6 :: 19
E6 :: 20
F6 :: 21
G6 :: 22
H6 :: 23
A5 :: 24
B5 :: 25
C5 :: 26
D5 :: 27
E5 :: 28
F5 :: 29
G5 :: 30
H5 :: 31
A4 :: 32
B4 :: 33
C4 :: 34
D4 :: 35
E4 :: 36
F4 :: 37
G4 :: 38
H4 :: 39
A3 :: 40
B3 :: 41
C3 :: 42
D3 :: 43
E3 :: 44
F3 :: 45
G3 :: 46
H3 :: 47
A2 :: 48
B2 :: 49
C2 :: 50
D2 :: 51
E2 :: 52
F2 :: 53
G2 :: 54
H2 :: 55
A1 :: 56
B1 :: 57
C1 :: 58
D1 :: 59
E1 :: 60
F1 :: 61
G1 :: 62
H1 :: 63
NO_SQUARE :: 65

25
Odin/boardutils.odin Normal file
View File

@@ -0,0 +1,25 @@
package main
IsOccupied :: proc(bitboard: u64, square: int) -> bool {
return (bitboard & SQUARE_BBS[square]) != 0
}
GetOccupiedIndex :: proc(square: int) -> int {
for i := 0; i < 12; i+=1 {
if IsOccupied(PieceArray[i], square) {
return i
}
}
return EMPTY
}
OutOfBounds :: proc(move: int) -> bool {
if move < 0 {
return true
}
if move > 63 {
return true
}
return false
}

239
Odin/fen.odin Normal file
View File

@@ -0,0 +1,239 @@
package main
FEN_STARTING_POSITION :: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
FEN_STARTING_POSITION_BLACK :: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1";
FEN_STARTING_POSITION_ONLY_PAWNS :: "4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w KQkq - 0 1";
FEN_STARTING_POSITION_EP_E4 :: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq e4 0 1";
FEN_STARTING_POSITION_ONLY_KNIGHTS :: "1n2k1n1/8/8/8/8/8/8/1N2K1N1 w - - 0 1";
FEN_STARTING_POSITION_ONLY_KNIGHTS_BLACK :: "1n2k1n1/8/8/8/8/8/8/1N2K1N1 b - - 0 1";
FEN_TEST_KNIGHT_CAPTURES :: "4k1n1/8/8/8/8/2n5/8/1N2K1N1 b - - 0 1";
FEN_TEST_ONLY_KINGS :: "8/8/3k4/8/3K4/8/8/8 w - - 0 1";
FEN_TEST_ONLY_KNIGHTS :: "8/8/3k4/8/3K4/8/8/8 w - - 0 1";
FEN_TRICKY_WHITE :: "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - ";
FEN_TRICKY_BLACK :: "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R b KQkq - ";
FEN_TEST_EP :: "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - ";
FEN_SCHOLARS_MATE :: "r1bqkb1r/pppp1ppp/2n2n2/4p2Q/2B1P3/8/PPPP1PPP/RNB1K1NR w KQkq - 4 4";
FEN_FRIED_LIVER_BLACK :: "r1bqkb1r/pppp1ppp/2n2n2/4p1N1/2B1P3/8/PPPP1PPP/RNBQK2R b KQkq - 5 4";
FEN_FRIED_LIVER_WHITE :: "r1bqkb1r/ppp2ppp/2np1n2/4p1N1/2B1P3/8/PPPP1PPP/RNBQK2R w KQkq - 0 5";
FEN_KNIGHT_FORK :: "rn1qk2r/pp3ppp/2p1pn2/6N1/8/1PP3P1/P4PBP/R2Q1RK1 w - - 0 1";
FEN_MATE_0_TEST :: "r1bqkb1r/pppp1Qpp/2n2n2/4p3/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq - 0 1";
FEN_MATE_4_TEST :: "r7/q6p/2k1p3/2N1Q3/1ppP4/P2b4/1P4PP/K2R4 b - - 0 1";
FEN_BONGCLOUD :: "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPPKPPP/RNBQ1BNR b kq - 1 2";
FEN_PERPETUAL :: "6k1/5pp1/6p1/8/8/3Q3P/5PP1/6K1 w - - 0 1";
FEN_MATE_3 :: "r4rk1/ppp1q1p1/2n2pP1/8/8/2N5/PPP1BQ2/2KR3R w - - 0 1";
FEN_BLACK_DOUBLE_CHECK :: "4k3/8/8/8/4N3/8/8/4R1K1 w - - 0 1";
FEN_PIECES :: 0;
FEN_SIDE :: 1;
FEN_CASTLING :: 2;
FEN_EP :: 3;
FEN_FIFTY :: 4;
FEN_PLY :: 5;
LoadFen :: proc(fen: string) {
ResetBoard();
bracketCount: int = 0;
squareCount: int = 0;
setting: int = FEN_PIECES;
ep_file_index:int = -1;
ep_rank_index:int = -1;
for character_index := 0; character_index < len(fen); character_index+=1 {
switch setting {
case FEN_PIECES:
Load_Fen_Sort_Pieces(&bracketCount, fen[character_index], &setting, &squareCount);
case FEN_SIDE:
Load_Fen_Sort_Side(&setting, fen[character_index]);
case FEN_CASTLING:
Load_Fen_Sort_Castling(&setting, fen[character_index]);
case FEN_EP:
Load_Fen_Sort_EP(&setting, fen[character_index], &ep_file_index, &ep_rank_index);
case FEN_FIFTY:
Load_Fen_Sort_Fifty_Move(&setting, fen[character_index]);
case FEN_PLY:
return;
}
}
}
Load_Fen_Sort_Pieces :: proc(bracketCount: ^int, character_in_fen: byte, setting: ^int, squareCount: ^int) {
if bracketCount^ == 7 && character_in_fen == ' ' {
setting^+=1
return
}
if bracketCount^ > 7 {
bracketCount^ = 0
}
if squareCount^ > 7 {
squareCount^ = 0
}
square:int = (bracketCount^ * 8) + squareCount^;
switch character_in_fen {
case 'B':
PieceArray[WB] = SetBit(PieceArray[WB], square)
squareCount^+=1;
case 'R':
PieceArray[WR] = SetBit(PieceArray[WR], square)
squareCount^+=1;
case 'P':
PieceArray[WP] = SetBit(PieceArray[WP], square)
squareCount^+=1;
case 'Q':
PieceArray[WQ] = SetBit(PieceArray[WQ], square)
squareCount^+=1;
case 'K':
PieceArray[WK] = SetBit(PieceArray[WK], square)
squareCount^+=1;
case 'N':
PieceArray[WN] = SetBit(PieceArray[WN], square)
squareCount^+=1;
case 'b':
PieceArray[BB] = SetBit(PieceArray[BB], square)
squareCount^+=1;
case 'p':
PieceArray[BP] = SetBit(PieceArray[BP], square)
squareCount^+=1;
case 'q':
PieceArray[BQ] = SetBit(PieceArray[BQ], square)
squareCount^+=1;
case 'r':
PieceArray[BR] = SetBit(PieceArray[BR], square)
squareCount^+=1;
case 'n':
PieceArray[BN] = SetBit(PieceArray[BN], square)
squareCount^+=1;
case 'k':
PieceArray[BK] = SetBit(PieceArray[BK], square)
squareCount^+=1;
case '/':
squareCount^ = 0
bracketCount^+=1;
case '1':
squareCount^ += 1
case '2':
squareCount^ += 2
case '3':
squareCount^ += 3
case '4':
squareCount^ += 4
case '5':
squareCount^ += 5
case '6':
squareCount^ += 6
case '7':
squareCount^ += 7
case '8':
squareCount^ += 8
}
}
Load_Fen_Sort_Side :: proc(setting: ^int, character_in_fen: byte) {
switch character_in_fen {
case 'w':
whiteToPlay = true;
case 'b':
whiteToPlay = false;
case ' ':
setting^+=1;
}
}
Load_Fen_Sort_Castling :: proc(setting: ^int, character_in_fen: byte) {
switch character_in_fen {
case 'K':
CastleRights[0] = true
case 'Q':
CastleRights[1] = true
case 'k':
CastleRights[2] = true
case 'q':
CastleRights[3] = true
case '-':
CastleRights[0] = false
CastleRights[1] = false
CastleRights[2] = false
CastleRights[3] = false
case ' ':
setting^+=1
}
}
Load_Fen_Sort_EP :: proc(setting: ^int, character_in_fen: byte, file_index: ^int, rank_index: ^int) {
if character_in_fen == '-' {
ep = NO_SQUARE
}
if character_in_fen == ' ' {
if file_index^ != -1 && rank_index^ != -1 {
ep = Convert_to_64(file_index^, rank_index^)
}
setting^+=1
}
switch character_in_fen {
case 'a':
file_index^ = 0
return
case 'b':
file_index^ = 1
return
case 'c':
file_index^ = 2
return
case 'd':
file_index^ = 3
return
case 'e':
file_index^ = 4
return
case 'f':
file_index^ = 5
return
case 'g':
file_index^ = 6
return
case 'h':
file_index^ = 7
return
}
switch character_in_fen {
case '1':
rank_index^ = 7
return
case '2':
rank_index^ = 6
return
case '3':
rank_index^ = 5
return
case '4':
rank_index^ = 4
return
case '5':
rank_index^ = 3
return
case '6':
rank_index^ = 2
return
case '7':
rank_index^ = 1
return
case '8':
rank_index^ = 0
return
}
}
Convert_to_64 :: proc(file: int, rank: int) -> int {
return (rank * 8) + file;
}
Load_Fen_Sort_Fifty_Move :: proc(setting: ^int, character_in_fen: byte) {
if character_in_fen == ' ' {
setting^+=1;
return;
}
}

74
Odin/generatemoves.odin Normal file
View File

@@ -0,0 +1,74 @@
package main
GetRookAttacksFast :: proc(startingSquare: int, occupancy: u64) -> u64 {
mutable_occupancy := occupancy;
mutable_occupancy &= ROOK_MASKS[startingSquare]
mutable_occupancy *= ROOK_MAGIC_NUMBERS[startingSquare]
mutable_occupancy >>= 64 - ROOK_REL_BITS[startingSquare]
return ROOK_ATTACKS[startingSquare][mutable_occupancy]
}
GetBishopAttacksFast :: proc(startingSquare: int, occupancy: u64) -> u64 {
mutable_occupancy := occupancy;
mutable_occupancy &= BISHOP_MASKS[startingSquare]
mutable_occupancy *= BISHOP_MAGIC_NUMBERS[startingSquare]
mutable_occupancy >>= 64 - BISHOP_REL_BITS[startingSquare]
return BISHOP_ATTACKS[startingSquare][mutable_occupancy]
}
Is_Square_Attacked_By_Black :: proc(square: int, occupancy: u64) -> bool {
if (PieceArray[BP] & WHITE_PAWN_ATTACKS[square]) != 0 {
return true
}
if (PieceArray[BN] & KNIGHT_ATTACKS[square]) != 0 {
return true
}
if (PieceArray[BK] & KING_ATTACKS[square]) != 0 {
return true
}
bishopAttacks := GetBishopAttacksFast(square, occupancy)
if (PieceArray[BB] & bishopAttacks) != 0 {
return true
}
if (PieceArray[BQ] & bishopAttacks) != 0 {
return true
}
rookAttacks := GetRookAttacksFast(square, occupancy)
if (PieceArray[BR] & rookAttacks) != 0 {
return true
}
if (PieceArray[BQ] & rookAttacks) != 0 {
return true
}
return false
}
Is_Square_Attacked_By_White :: proc(square: int, occupancy: u64) -> bool {
if (PieceArray[WP] & BLACK_PAWN_ATTACKS[square]) != 0 {
return true
}
if (PieceArray[WN] & KNIGHT_ATTACKS[square]) != 0 {
return true
}
if (PieceArray[WK] & KING_ATTACKS[square]) != 0 {
return true
}
bishopAttacks := GetBishopAttacksFast(square, occupancy)
if (PieceArray[WB] & bishopAttacks) != 0 {
return true
}
if (PieceArray[WQ] & bishopAttacks) != 0 {
return true
}
rookAttacks := GetRookAttacksFast(square, occupancy)
if (PieceArray[WR] & rookAttacks) != 0 {
return true
}
if (PieceArray[WQ] & rookAttacks) != 0 {
return true
}
return false
}

783
Odin/moveconstants.odin Normal file

File diff suppressed because one or more lines are too long

24
Odin/piececonstants.odin Normal file
View File

@@ -0,0 +1,24 @@
package main
WP :: 0
WN :: 1
WB :: 2
WR :: 3
WQ :: 4
WK :: 5
BP :: 6
BN :: 7
BB :: 8
BR :: 9
BQ :: 10
BK :: 11
EMPTY :: 12
@(rodata)
PieceNames := [13]byte{'P', 'N', 'B', 'R', 'Q', 'K', 'P', 'N', 'B', 'R', 'Q', 'K', '_'};
@(rodata)
PieceColours := [13]byte{'W', 'W', 'W', 'W', 'W', 'W', 'B', 'B', 'B', 'B', 'B', 'B', '_'};
WHITE_START_INDEX :: WP;
WHITE_END_INDEX :: WK;
BLACK_START_INDEX :: BP;
BLACK_END_INDEX :: BK;

29
Odin/printing.odin Normal file
View File

@@ -0,0 +1,29 @@
package main
import "core:fmt"
PrintMoveNoNL :: proc(starting: int, target_square: int, tag: int) { //starting
if OutOfBounds(starting) == true {
fmt.printf("%d", starting);
} else {
fmt.printf("%c", SQ_CHAR_X[starting]);
fmt.printf("%c", SQ_CHAR_Y[starting]);
}
//target
if OutOfBounds(target_square) == true {
fmt.printf("%d", target_square);
} else {
fmt.printf("%c", SQ_CHAR_X[target_square]);
fmt.printf("%c", SQ_CHAR_Y[target_square]);
}
if tag == TAG_BCaptureKnightPromotion || tag == TAG_BKnightPromotion || tag == TAG_WKnightPromotion || tag == TAG_WCaptureKnightPromotion {
fmt.printf("n");
} else if tag == TAG_BCaptureRookPromotion || tag == TAG_BRookPromotion || tag == TAG_WRookPromotion || tag == TAG_WCaptureRookPromotion {
fmt.printf("r");
} else if tag == TAG_BCaptureBishopPromotion || tag == TAG_BBishopPromotion || tag == TAG_WBishopPromotion || tag == TAG_WCaptureBishopPromotion {
fmt.printf("b");
} else if tag == TAG_BCaptureQueenPromotion || tag == TAG_BQueenPromotion || tag == TAG_WQueenPromotion || tag == TAG_WCaptureQueenPromotion {
fmt.printf("q");
}
}