#include <stdarg.h>
#include <errno.h>
#include <ctype.h>
#include "chess.h"
#include "data.h"
#if defined(UNIX)
# include <unistd.h>
# include <sys/types.h>
# include <signal.h>
# include <sys/wait.h>
# include <sys/times.h>
# include <sys/time.h>
#else
# include <windows.h>
# include <winbase.h>
# include <wincon.h>
# include <io.h>
# include <time.h>
#endif
/*
*******************************************************************************
* *
* AlignedMalloc() is used to allocate memory on a precise boundary, *
* primarily to optimize cache performance by forcing the start of the *
* memory region being allocated to match up so that a structure will lie *
* on a single cache line rather than being split across two, assuming the *
* structure is 64 bytes or less of course. *
* *
*******************************************************************************
*/
void AlignedMalloc(void **pointer, int alignment, size_t size) {
segments
[nsegments
][0] = malloc(size
+ alignment
- 1);
segments[nsegments][1] =
(void *) (((uintptr_t) segments[nsegments][0] + alignment -
1) & ~(alignment - 1));
*pointer = segments[nsegments][1];
nsegments++;
}
/*
*******************************************************************************
* *
* atoiKMB() is used to read in an integer value that can have a "K" or "M" *
* appended to it to multiply by 1024 or 1024*1024. It returns a 64 bit *
* value since memory sizes can exceed 4gb on modern hardware. *
* *
*******************************************************************************
*/
uint64_t atoiKMB(char *input) {
uint64_t size;
size *= 1 << 10;
size *= 1 << 20;
size *= 1 << 30;
return size;
}
/*
*******************************************************************************
* *
* AlignedRemalloc() is used to change the size of a memory block that has *
* previously been allocated using AlignedMalloc(). *
* *
*******************************************************************************
*/
void AlignedRemalloc(void **pointer, int alignment, size_t size) {
int i;
for (i = 0; i < nsegments; i++)
if (segments[i][1] == *pointer)
break;
if (i == nsegments) {
Print(4095, "ERROR AlignedRemalloc() given an invalid pointer\n");
}
segments
[i
][0] = malloc(size
+ alignment
- 1);
segments[i][1] =
(void *) (((uintptr_t) segments[i][0] + alignment - 1) & ~(alignment -
1));
*pointer = segments[i][1];
}
/*
*******************************************************************************
* *
* BookClusterIn() is used to read a cluster in as characters, then stuff *
* the data into a normal array of structures that can be used within Crafty *
* without any endian issues. *
* *
*******************************************************************************
*/
void BookClusterIn(FILE * file, int positions, BOOK_POSITION * buffer) {
int i;
char file_buffer[BOOK_CLUSTER_SIZE * sizeof(BOOK_POSITION)];
i
= fread(file_buffer
, positions
, sizeof(BOOK_POSITION
), file
);
if (i <= 0)
perror("BookClusterIn fread error: ");
for (i = 0; i < positions; i++) {
buffer[i].position =
BookIn64((unsigned char *) (file_buffer + i * sizeof(BOOK_POSITION)));
buffer[i].status_played =
BookIn32((unsigned char *) (file_buffer + i * sizeof(BOOK_POSITION) +
8));
buffer[i].learn =
BookIn32f((unsigned char *) (file_buffer + i * sizeof(BOOK_POSITION) +
12));
}
}
/*
*******************************************************************************
* *
* BookClusterOut() is used to write a cluster out as characters, after *
* converting the normal array of structures into character data that is *
* Endian-independent. *
* *
*******************************************************************************
*/
void BookClusterOut(FILE * file, int positions, BOOK_POSITION * buffer) {
int i;
char file_buffer[BOOK_CLUSTER_SIZE * sizeof(BOOK_POSITION)];
for (i = 0; i < positions; i++) {
memcpy(file_buffer
+ i
* sizeof(BOOK_POSITION
),
BookOut64(buffer[i].position), 8);
memcpy(file_buffer
+ i
* sizeof(BOOK_POSITION
) + 8,
BookOut32(buffer[i].status_played), 4);
memcpy(file_buffer
+ i
* sizeof(BOOK_POSITION
) + 12,
BookOut32f(buffer[i].learn), 4);
}
fwrite(file_buffer
, positions
, sizeof(BOOK_POSITION
), file
);
}
/*
*******************************************************************************
* *
* BookIn32f() is used to convert 4 bytes from the book file into a valid 32 *
* bit binary value. This eliminates endian worries that make the binary *
* book non-portable across many architectures. *
* *
*******************************************************************************
*/
float BookIn32f(unsigned char *ch) {
union {
float fv;
int iv;
} temp;
temp.iv = ch[3] << 24 | ch[2] << 16 | ch[1] << 8 | ch[0];
return temp.fv;
}
/*
*******************************************************************************
* *
* BookIn32() is used to convert 4 bytes from the book file into a valid 32 *
* bit binary value. this eliminates endian worries that make the binary *
* book non-portable across many architectures. *
* *
*******************************************************************************
*/
int BookIn32(unsigned char *ch) {
return ch[3] << 24 | ch[2] << 16 | ch[1] << 8 | ch[0];
}
/*
*******************************************************************************
* *
* BookIn64() is used to convert 8 bytes from the book file into a valid 64 *
* bit binary value. this eliminates endian worries that make the binary *
* book non-portable across many architectures. *
* *
*******************************************************************************
*/
uint64_t BookIn64(unsigned char *ch) {
return (uint64_t) ch[7] << 56 | (uint64_t) ch[6] << 48 | (uint64_t)
ch[5] << 40 | (uint64_t) ch[4] << 32 | (uint64_t) ch[3]
<< 24 | (uint64_t) ch[2] << 16 | (uint64_t) ch[1] << 8 | (uint64_t)
ch[0];
}
/*
*******************************************************************************
* *
* BookOut32() is used to convert 4 bytes from a valid 32 bit binary value *
* to a book value. this eliminates endian worries that make the binary *
* book non-portable across many architectures. *
* *
*******************************************************************************
*/
unsigned char *BookOut32(int val) {
convert_buff[3] = val >> 24 & 0xff;
convert_buff[2] = val >> 16 & 0xff;
convert_buff[1] = val >> 8 & 0xff;
convert_buff[0] = val & 0xff;
return convert_buff;
}
/*
*******************************************************************************
* *
* BookOut32f() is used to convert 4 bytes from a valid 32 bit binary value *
* to a book value. this eliminates endian worries that make the binary *
* book non-portable across many architectures. *
* *
*******************************************************************************
*/
unsigned char *BookOut32f(float val) {
union {
float fv;
int iv;
} temp;
temp.fv = val;
convert_buff[3] = temp.iv >> 24 & 0xff;
convert_buff[2] = temp.iv >> 16 & 0xff;
convert_buff[1] = temp.iv >> 8 & 0xff;
convert_buff[0] = temp.iv & 0xff;
return convert_buff;
}
/*
*******************************************************************************
* *
* BookOut64() is used to convert 8 bytes from a valid 64 bit binary value *
* to a book value. this eliminates endian worries that make the binary *
* book non-portable across many architectures. *
* *
*******************************************************************************
*/
unsigned char *BookOut64(uint64_t val) {
convert_buff[7] = val >> 56 & 0xff;
convert_buff[6] = val >> 48 & 0xff;
convert_buff[5] = val >> 40 & 0xff;
convert_buff[4] = val >> 32 & 0xff;
convert_buff[3] = val >> 24 & 0xff;
convert_buff[2] = val >> 16 & 0xff;
convert_buff[1] = val >> 8 & 0xff;
convert_buff[0] = val & 0xff;
return convert_buff;
}
/*
*******************************************************************************
* *
* the following functions are used to determine if keyboard input is *
* present. there are several ways this is done depending on which *
* operating system is used. The primary function name is CheckInput() but *
* for simplicity there are several O/S-specific versions. *
* *
*******************************************************************************
*/
#if !defined(UNIX)
# include <windows.h>
# include <conio.h>
/* Windows NT using PeekNamedPipe() function */
int CheckInput(void) {
int i;
static int init = 0, pipe;
static HANDLE inh;
DWORD dw;
if (!xboard && !_isatty(_fileno(stdin))) // Pierre-Marie Baty -- ISO C++ names fix
return 0;
if (batch_mode)
return 0;
return 1;
if (xboard) {
# if defined(FILE_CNT)
if (stdin->_cnt > 0)
return stdin->_cnt;
# endif
if (!init) {
init = 1;
inh = GetStdHandle(STD_INPUT_HANDLE);
pipe = !GetConsoleMode(inh, &dw);
if (!pipe) {
SetConsoleMode(inh, dw & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
FlushConsoleInputBuffer(inh);
}
}
if (pipe) {
if (!PeekNamedPipe(inh, NULL, 0, NULL, &dw, NULL)) {
return 1;
}
return dw;
} else {
GetNumberOfConsoleInputEvents(inh, &dw);
return dw <= 1 ? 0 : dw;
}
} else {
i = _kbhit();
}
return i;
}
#endif
#if defined(UNIX)
/* Simple UNIX approach using select with a zero timeout value */
int CheckInput(void) {
fd_set readfds;
struct timeval tv;
int data;
if (!xboard && !isatty(fileno(stdin)))
return 0;
if (batch_mode)
return 0;
return 1;
FD_ZERO(&readfds);
FD_SET(fileno(stdin), &readfds);
tv.tv_sec = 0;
tv.tv_usec = 0;
select(16, &readfds, 0, 0, &tv);
data = FD_ISSET(fileno(stdin), &readfds);
return data;
}
#endif
/*
*******************************************************************************
* *
* ClearHashTableScores() is used to clear hash table scores without *
* clearing the best move, so that move ordering information is preserved. *
* We clear the scorew as we approach a 50 move rule so that hash scores *
* won't give us false scores since the hash signature does not include any *
* search path information in it. *
* *
*******************************************************************************
*/
void ClearHashTableScores(void) {
int i;
if (hash_table)
for (i = 0; i < (int)hash_table_size; i++) { // Pierre-Marie Baty -- added type cast
(hash_table + i)->word2 ^= (hash_table + i)->word1;
(hash_table + i)->word1 =
((hash_table + i)->word1 & mask_clear_entry) | (uint64_t) 65536;
(hash_table + i)->word2 ^= (hash_table + i)->word1;
}
}
/* last modified 02/28/14 */
/*
*******************************************************************************
* *
* ComputeDifficulty() is used to compute the difficulty rating for the *
* current position, which really is based on nothing more than how many *
* times we changed our mind in an iteration. No changes caused the *
* difficulty to drop (easier, use less time), while more changes ramps the *
* difficulty up (harder, use more time). It is called at the end of an *
* iteration as well as when displaying fail-high/fail-low moves, in an *
* effort to give the operator a heads-up on how long we are going to be *
* stuck in an active search. *
* *
*******************************************************************************
*/
int ComputeDifficulty(int difficulty, int direction) {
int searched = 0, i;
/*
************************************************************
* *
* Step 1. Handle fail-high-fail low conditions, which *
* occur in the middle of an iteration. The actions taken *
* are as follows: *
* *
* (1) Determine how many moves we have searched first, as *
* this is important. If we have not searched anything *
* (which means we failed high on the first move at the *
* root, at the beginning of a new iteration), a fail low *
* will immediately set difficult back to 100% (if it is *
* currently below 100%). A fail high on the first move *
* will not change difficulty at all. Successive fail *
* highs or fail lows will not change difficulty, we will *
* not even get into this code on the repeats. *
* *
* (2) If we are beyond the first move, then this must be *
* a fail high condition. Since we are changing our mind, *
* we need to increase the difficulty level to expend more *
* time on this iteration. If difficulty is currently *
* less than 100%, we set it to 120%. If it is currently *
* at 100% or more, we simply add 20% to the value and *
* continue searching, but with a longer time constraint. *
* Each time we fail high, we are changing our mind, and *
* we will increase difficulty by another 20%. *
* *
* (3) Direction = 0 means we are at the end of an the *
* iteration. Here we simply note if we changed our mind *
* during this iteration. If not, we reduce difficulty *
* to 90% of its previous value. *
* *
* After any of these changes, we enforce a lower bound of *
* 60% and an upperbound of 200% before we return. *
* *
* Note: direction = +1 means we failed high on the move, *
* direction = -1 means we failed low on the move, and *
* direction = 0 means we have completed the iteration and *
* all moves were searched successfully. *
* *
************************************************************
*/
if (direction) {
for (i = 0; i < n_root_moves; i++)
if (root_moves[i].status & 8)
searched++;
if (searched == 0) {
if (direction > 0)
return difficulty;
if (direction < 0)
difficulty = Max(100, difficulty);
} else {
if (difficulty < 100)
difficulty = 120;
else
difficulty = difficulty + 20;
}
}
/*
************************************************************
* *
* Step 2. We are at the end of an iteration. If we did *
* not change our mind and stuck with one move, we reduce *
* difficulty by 10% since the move looks to be a little *
* "easier" when we don't change our mind. *
* *
************************************************************
*/
else {
searched = 0;
for (i = 0; i < n_root_moves; i++)
if (root_moves[i].bm_age == 3)
searched++;
if (searched <= 1)
difficulty = 90 * difficulty / 100;
}
/*
************************************************************
* *
* Step 4. Apply limits. We don't let difficulty go *
* above 200% (take 2x the target time) nor do we let it *
* drop below 60 (take .6x target time) to avoid moving *
* too quickly and missing something tactically where the *
* move initially looks obvious but really is not. *
* *
************************************************************
*/
difficulty = Max(60, Min(difficulty, 200));
return difficulty;
}
/*
*******************************************************************************
* *
* CraftyExit() is used to terminate the program. the main functionality *
* is to make sure the "quit" flag is set so that any spinning threads will *
* also exit() rather than spinning forever which can cause GUIs to hang *
* since all processes have not terminated. *
* *
*******************************************************************************
*/
void CraftyExit(int exit_type) {
int proc;
for (proc = 1; proc < CPUS; proc++)
thread[proc].terminate = 1;
while (smp_threads);
}
/*
*******************************************************************************
* *
* DisplayArray() prints array data either 8 or 16 values per line, and also *
* reverses the output for arrays that overlay the chess board so that the *
* 'white side" is at the bottom rather than the top. this is mainly used *
* from inside Option() to display the many evaluation terms. *
* *
*******************************************************************************
*/
void DisplayArray(int *array, int size) {
int i, j, len = 16;
if (Abs(size) % 10 == 0)
len = 10;
else if (Abs(size) % 8 == 0)
len = 8;
if (size > 0 && size % 16 == 0 && len == 8)
len = 16;
if (size > 0) {
for (i = 0; i < size; i++) {
if ((i + 1) % len == 0) {
if (i < size - 1)
}
}
if (i % len != 0)
}
if (size < 0) {
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++) {
printf("%3d ", array
[(7 - i
) * 8 + j
]);
}
}
printf(" ---------------------------------\n");
}
}
/*
*******************************************************************************
* *
* DisplayArray() prints array data either 8 or 16 values per line, and also *
* reverses the output for arrays that overlay the chess board so that the *
* 'white side" is at the bottom rather than the top. this is mainly used *
* from inside Option() to display the many evaluation terms. *
* *
*******************************************************************************
*/
void DisplayArrayX2(int *array, int *array2, int size) {
int i, j;
if (size == 256) {
printf(" ----------- Middlegame ----------- ");
printf(" ------------- Endgame -----------\n");
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++)
printf("%3d ", array
[(7 - i
) * 8 + j
]);
for (j = 0; j < 8; j++)
printf("%3d ", array2
[(7 - i
) * 8 + j
]);
}
(" ---------------------------------- ---------------------------------\n");
} else if (size == 32) {
printf(" ----------- Middlegame ----------- ");
printf(" ------------- Endgame -----------\n");
for (i = 0; i < 8; i++)
for (i = 0; i < 8; i++)
} else if (size <= 20) {
size = size / 2;
for (i = 0; i < size; i++)
for (i = 0; i < size; i++)
} else if (size > 128) {
printf(" ----------- Middlegame ----------- ");
printf(" ------------- Endgame -----------\n");
for (i = 0; i < size / 32; i++) {
for (j = 0; j < 8; j++)
printf("%3d ", array
[(7 - i
) * 8 + j
]);
for (j = 0; j < 8; j++)
printf("%3d ", array2
[(7 - i
) * 8 + j
]);
}
} else
Print(4095, "ERROR, invalid size = -%d in packet\n", size);
}
/*
*******************************************************************************
* *
* DisplayBitBoard() is a debugging function used to display bitboards in a *
* more visual way. they are displayed as an 8x8 matrix oriented as the *
* normal chess board is, with a1 at the lower left corner. *
* *
*******************************************************************************
*/
void DisplayBitBoard(uint64_t board) {
int i, j, x;
for (i = 56; i >= 0; i -= 8) {
x = (board >> i) & 255;
for (j = 1; j < 256; j = j << 1)
if (x & j)
Print(4095, "X ");
else
Print(4095, "- ");
Print(4095, "\n");
}
}
/*
*******************************************************************************
* *
* Display2BitBoards() is a debugging function used to display bitboards in *
* a more visual way. they are displayed as an 8x8 matrix oriented as the *
* normal chess board is, with a1 at the lower left corner. this function *
* displays 2 boards side by side for comparison. *
* *
*******************************************************************************
*/
void Display2BitBoards(uint64_t board1, uint64_t board2) {
int i, j, x, y;
for (i = 56; i >= 0; i -= 8) {
x = (board1 >> i) & 255;
for (j = 1; j < 256; j = j << 1)
if (x & j)
else
y = (board2 >> i) & 255;
for (j = 1; j < 256; j = j << 1)
if (y & j)
else
}
}
/*
*******************************************************************************
* *
* DisplayChessBoard() is used to display the board since it is kept in *
* both the bit-board and array formats, here we use the array format which *
* is nearly ready for display as is. *
* *
*******************************************************************************
*/
void DisplayChessBoard(FILE * display_file, POSITION pos) {
int display_board[64], i, j;
static const char display_string[16][4] =
{ "<K>", "<Q>", "<R>", "<B>", "<N>", "<P>", " ",
"-P-", "-N-", "-B-", "-R-", "-Q-", "-K-", " . "
};
/*
************************************************************
* *
* First, convert square values to indices to the proper *
* text string. *
* *
************************************************************
*/
for (i = 0; i < 64; i++) {
display_board[i] = pos.board[i] + 6;
if (pos.board[i] == 0) {
if (((i / 8) & 1) == ((i % 8) & 1))
display_board[i] = 13;
}
}
/*
************************************************************
* *
* Now that that's done, simply display using 8 squares *
* per line. *
* *
************************************************************
*/
fprintf(display_file
, "\n +---+---+---+---+---+---+---+---+\n");
for (i = 7; i >= 0; i--) {
fprintf(display_file
, " %2d ", i
+ 1);
for (j = 0; j < 8; j++)
fprintf(display_file
, "|%s", display_string
[display_board
[i
* 8 + j
]]);
fprintf(display_file
, " +---+---+---+---+---+---+---+---+\n");
}
fprintf(display_file
, " a b c d e f g h\n\n");
}
/*
*******************************************************************************
* *
* DisplayChessMove() is a debugging function that displays a chess move in *
* a very simple (non-algebraic) form. *
* *
*******************************************************************************
*/
void DisplayChessMove(char *title, int move) {
Print(4095, "%s piece=%d, from=%d, to=%d, captured=%d, promote=%d\n",
title, Piece(move), From(move), To(move), Captured(move),
Promote(move));
}
/*
*******************************************************************************
* *
* DisplayEvaluation() is used to convert the evaluation to a string that *
* can be displayed. The length is fixed so that screen formatting will *
* look nice and aligned. *
* *
*******************************************************************************
*/
char *DisplayEvaluation(int value, int wtm) {
int tvalue;
static char out[10];
tvalue = (wtm) ? value : -value;
if (!MateScore(value))
sprintf(out
, "%7.2f", ((float) tvalue
) / 100.0);
else if (Abs(value) > MATE) {
if (tvalue < 0)
else
} else if (value == MATE - 2 && wtm)
else if (value == MATE - 2 && !wtm)
else if (value == -(MATE - 1) && wtm)
else if (value == -(MATE - 1) && !wtm)
else if (value > 0 && wtm)
sprintf(out
, " Mat%.2d", (MATE
- value
) / 2);
else if (value > 0 && !wtm)
sprintf(out
, " -Mat%.2d", (MATE
- value
) / 2);
else if (wtm)
sprintf(out
, " -Mat%.2d", (MATE
- Abs
(value
)) / 2);
else
sprintf(out
, " Mat%.2d", (MATE
- Abs
(value
)) / 2);
return out;
}
/*
*******************************************************************************
* *
* DisplayEvaluationKibitz() is used to convert the evaluation to a string *
* that can be displayed. The length is variable so that ICC kibitzes and *
* whispers will look nicer. *
* *
*******************************************************************************
*/
char *DisplayEvaluationKibitz(int value, int wtm) {
int tvalue;
static char out[10];
tvalue = (wtm) ? value : -value;
if (!MateScore(value))
sprintf(out
, "%+.2f", ((float) tvalue
) / 100.0);
else if (Abs(value) > MATE) {
if (tvalue < 0)
else
} else if (value == MATE - 2 && wtm)
else if (value == MATE - 2 && !wtm)
else if (value == -(MATE - 1) && wtm)
else if (value == -(MATE - 1) && !wtm)
else if (value > 0 && wtm)
sprintf(out
, "Mat%.2d", (MATE
- value
) / 2);
else if (value > 0 && !wtm)
sprintf(out
, "-Mat%.2d", (MATE
- value
) / 2);
else if (wtm)
sprintf(out
, "-Mat%.2d", (MATE
- Abs
(value
)) / 2);
else
sprintf(out
, "Mat%.2d", (MATE
- Abs
(value
)) / 2);
return out;
}
/*
*******************************************************************************
* *
* DisplayPath() is used to display a PV during the root move search. *
* *
*******************************************************************************
*/
char *DisplayPath(TREE * RESTRICT tree, int wtm, PATH * pv) {
static char buffer[4096];
int i, t_move_number;
/*
************************************************************
* *
* Initialize. *
* *
************************************************************
*/
t_move_number = move_number;
sprintf(buffer
, " %d.", move_number
);
if (!wtm)
for (i = 1; i < (int) pv->pathl; i++) {
if (i > 1 && wtm)
pv->path[i]));
MakeMove(tree, i, wtm, pv->path[i]);
wtm = Flip(wtm);
if (wtm)
t_move_number++;
}
if (pv->pathh == 1)
else if (pv->pathh == 2)
else if (pv->pathh == 3)
else if (pv->pathh == 4)
for (i
= 0; i
< 30 - (int) strlen(buffer
); i
++) // Pierre-Marie Baty -- added type cast
for (i = pv->pathl - 1; i > 0; i--) {
wtm = Flip(wtm);
UnmakeMove(tree, i, wtm, pv->path[i]);
}
return buffer;
}
/*
*******************************************************************************
* *
* DisplayFail() is used to display a PV (moves only) during the search. *
* *
*******************************************************************************
*/
void DisplayFail
(TREE
* RESTRICT tree
, int type
, int level
, int wtm
, int time,
int move, int value, int force) {
char buffer[4096], *fh_indicator;
/*
************************************************************
* *
* If we have not used "noise_level" units of time, we *
* return immediately. Otherwise we add the fail high/low *
* indicator (++/--) and then display the times. *
* *
************************************************************
*/
if (time < (int) noise_level
) // Pierre-Marie Baty -- added type cast
return;
if (type == 1)
fh_indicator = (wtm) ? "++" : "--";
else
fh_indicator = (wtm) ? "--" : "++";
Print(4, " %2i %s %2s ", iteration,
Display2Times(end_time - start_time), fh_indicator);
/*
************************************************************
* *
* If we are pondering, we need to add the (ponder-move) *
* to the front of the buffer, correcting the move number *
* if necessary. Then fill in the move number and the *
* fail high/low bound. *
* *
************************************************************
*/
if (!pondering) {
sprintf(buffer
, "%d.", move_number
);
if (!wtm)
} else {
if (wtm)
sprintf(buffer
, "%d. ... (%s) %d.", move_number
- 1, ponder_text
,
move_number);
else
sprintf(buffer
, "%d. (%s)", move_number
, ponder_text
);
}
sprintf(buffer
+ strlen(buffer
), " %s%c", OutputMove
(tree
, 1, wtm
, move
),
(type == 1) ? '!' : '?');
if (time >= (int) noise_level
|| force
) { // Pierre-Marie Baty -- added type cast
noise_block = 0;
Lock(lock_io);
Print(4, "%s", buffer);
Unlock(lock_io);
if (type == 1)
Print(4, " (%c%s) \n", (wtm) ? '>' : '<',
DisplayEvaluationKibitz(value, wtm));
else
Print(4, " (%c%s) \n", (wtm) ? '<' : '>',
DisplayEvaluationKibitz(value, wtm));
}
}
/*
*******************************************************************************
* *
* DisplayPV() is used to display a PV during the search. *
* *
*******************************************************************************
*/
void DisplayPV
(TREE
* RESTRICT tree
, int level
, int wtm
, int time, PATH
* pv
,
int force) {
char buffer[4096], *buffp, *bufftemp;
char blanks[40] = { " " };
int i, len, t_move_number, nskip = 0, twtm = wtm, pv_depth = pv->pathd;;
unsigned int idle_time;
/*
************************************************************
* *
* Initialize. *
* *
************************************************************
*/
for (i = 0; i < n_root_moves; i++)
if (root_moves[i].status & 4)
nskip++;
for (i = 0; i < 4096; i++)
buffer[i] = ' ';
t_move_number = move_number;
if (!pondering || analyze_mode) {
sprintf(buffer
, "%d.", move_number
);
if (!wtm)
} else {
if (wtm)
sprintf(buffer
, "%d. ... (%s) %d.", move_number
- 1, ponder_text
,
move_number);
else
sprintf(buffer
, "%d. (%s)", move_number
, ponder_text
);
}
for (i = 1; i < (int) pv->pathl; i++) {
if (i > 1 && wtm)
pv->path[i]));
MakeMove(tree, i, wtm, pv->path[i]);
wtm = Flip(wtm);
if (wtm)
t_move_number++;
}
if (pv->pathh == 1)
else if (pv->pathh == 2)
else if (pv->pathh == 3)
else if (pv->pathh == 3)
if (nskip > 1 && smp_max_threads > 1)
for (i = 0; i < len; i++)
}
if (time >= (int) noise_level
|| force
) { // Pierre-Marie Baty -- added type cast
noise_block = 0;
Lock(lock_io);
Print(2, " ");
if (level == 6)
Print
(2, "%2i %s%s ", pv_depth
, Display2Times
(time),
DisplayEvaluation(pv->pathv, twtm));
else
Print
(2, "%2i-> %s%s ", pv_depth
, Display2Times
(time)
, DisplayEvaluation(pv->pathv, twtm));
buffp = buffer;
do {
if ((int) strlen(buffp
) > line_length
- 38) {
bufftemp = buffp + line_length - 38;
while (*bufftemp != ' ')
bufftemp--;
if (*(bufftemp - 1) == '.')
while (*(--bufftemp) != ' ');
} else
bufftemp = 0;
if (bufftemp)
*bufftemp = 0;
Print(2, "%s\n", buffp);
buffp = bufftemp + 1;
if (bufftemp)
bufftemp = 0;
if (bufftemp)
Print(2, " ");
} while (bufftemp);
idle_time = 0;
for (i = 0; i < (int) smp_max_threads; i++) // Pierre-Marie Baty -- added type cast
idle_time += thread[i].idle;
busy_percent =
100 - Min(100,
100 * idle_time / (smp_max_threads * (end_time - start_time) + 1));
Kibitz(level, twtm, pv_depth, end_time - start_time, pv->pathv,
tree->nodes_searched, busy_percent, (int) tree->egtb_hits, kibitz_text); // Pierre-Marie Baty -- added type cast
Unlock(lock_io);
}
for (i = pv->pathl - 1; i > 0; i--) {
wtm = Flip(wtm);
UnmakeMove(tree, i, wtm, pv->path[i]);
}
}
/*
*******************************************************************************
* *
* DisplayHHMMSS is used to convert integer time values in 1/100th second *
* units into a traditional output format for time, hh:mm:ss rather than *
* just nnn.n seconds. *
* *
*******************************************************************************
*/
char *DisplayHHMMSS
(unsigned int time) {
static char out[32];
return out;
}
/*
*******************************************************************************
* *
* DisplayHHMM is used to convert integer time values in 1/100th second *
* units into a traditional output format for time, mm:ss rather than just *
* nnn.n seconds. *
* *
*******************************************************************************
*/
char *DisplayHHMM
(unsigned int time) {
static char out[10];
return out;
}
/*
*******************************************************************************
* *
* DisplayKMB() takes an integer value that represents nodes per second, or *
* just total nodes, and converts it into a more compact form, so that *
* instead of nps=57200931, we get nps=57.2M. We use units of "K", "M", *
* "B" and "T". If type==0, K=1000, etc. If type=1, K=1024, etc. *
* *
*******************************************************************************
*/
char *DisplayKMB(uint64_t val, int type) {
static char out[10];
if (type == 0) {
if (val < 1000)
else if (val < 1000000)
sprintf(out
, "%.1fK", (double) val
/ 1000);
else if (val < 1000000000)
sprintf(out
, "%.1fM", (double) val
/ 1000000);
else
sprintf(out
, "%.1fB", (double) val
/ 1000000000);
} else {
if (val > 0 && !(val & 0x000000003fffffffULL))
sprintf(out
, "%dG", (int) (val
/ (1 << 30)));
else if (val > 0 && !(val & 0x00000000000fffffULL))
sprintf(out
, "%dM", (int) (val
/ (1 << 20)));
else if (val > 0 && !(val & 0x00000000000003ffULL))
sprintf(out
, "%dK", (int) (val
/ (1 << 10)));
else
}
return out;
}
/*
*******************************************************************************
* *
* DisplayTime() is used to display search times, and shows times in one of *
* two ways depending on the value passed in. If less than 60 seconds is to *
* be displayed, it is displayed as a decimal fraction like 32.7, while if *
* more than 60 seconds is to be displayed, it is converted to the more *
* traditional mm:ss form. The string it produces is of fixed length to *
* provide neater screen formatting. *
* *
*******************************************************************************
*/
char *DisplayTime
(unsigned int time) {
static char out[10];
else {
}
return out;
}
/*
*******************************************************************************
* *
* Display2Times() is used to display search times, and shows times in one *
* of two ways depending on the value passed in. If less than 60 seconds is *
* to be displayed, it is displayed as a decimal fraction like 32.7, while *
* if more than 60 seconds is to be displayed, it is converted to the more *
* traditional mm:ss form. The string it produces is of fixed length to *
* provide neater screen formatting. *
* *
* The second argument is the "difficulty" value which lets us display the *
* target time (as modified by difficulty) so that it is possible to know *
* roughly when the move will be announced. *
* *
*******************************************************************************
*/
char *Display2Times
(unsigned int time) {
int ttime, c, spaces;
static char out[20], tout[10];
else {
}
if (search_time_limit)
ttime = search_time_limit;
else
ttime = difficulty * time_limit / 100;
if (ttime < 360000) {
if (ttime < 6000)
sprintf(tout
, "%6.2f", (float) ttime
/ 100.0);
else {
ttime = ttime / 100;
sprintf(tout
, "%3u:%02u", ttime
/ 60, ttime
% 60);
}
}
for (c = 0; c < spaces; c++)
return out;
}
/*
*******************************************************************************
* *
* DisplayTimeKibitz() behaves just like DisplayTime() except that the *
* string it produces is a variable-length string that is as short as *
* possible to make ICC kibitzes/whispers look neater. *
* *
*******************************************************************************
*/
char *DisplayTimeKibitz
(unsigned int time) {
static char out[10];
else {
}
return out;
}
/*
*******************************************************************************
* *
* EGTBPV() is used to display the PV for a known EGTB position. It simply *
* makes moves, looks up the position to find the shortest mate, then it *
* follows that PV. It appends a "!" to a move that is the only move to *
* preserve the shortest path to mate (all other moves lead to longer mates *
* or even draws.) *
* *
*******************************************************************************
*/
#if !defined(NOEGTB)
void EGTBPV(TREE * RESTRICT tree, int wtm) {
uint64_t hk[1024], phk[1024], pos[1024];
unsigned moves[1024], current[256], *last;
int value, ply, i, j, nmoves;
int t_move_number, best = 0, bestmv = 0, optimal_mv = 0, legal;
char buffer[16384], *next;
/*
************************************************************
* *
* First, see if this is a known EGTB position. If not, *
* we can bug out right now. *
* *
************************************************************
*/
if (!EGTB_setup)
return;
tree->status[1] = tree->status[0];
if (Castle(1, white) + Castle(1, white))
return;
if (!EGTBProbe(tree, 1, wtm, &value))
return;
t_move_number = move_number;
sprintf(buffer
, "%d.", move_number
);
if (!wtm)
/*
************************************************************
* *
* The rest is simple, but messy. Generate all moves, *
* then find the move with the best egtb score and make it *
* (note that if there is only one that is optimal, it is *
* flagged as such). We then repeat this over and over *
* until we reach the end, or until we repeat a move and *
* can call it a repetition. *
* *
************************************************************
*/
for (ply = 1; ply < 1024; ply++) {
pos[ply] = HashKey;
last = GenerateCaptures(tree, 1, wtm, current);
last = GenerateNoncaptures(tree, 1, wtm, last);
nmoves = last - current;
best = -MATE - 1;
legal = 0;
for (i = 0; i < nmoves; i++) {
MakeMove(tree, 1, wtm, current[i]);
if (!Check(wtm)) {
legal++;
if (TotalAllPieces == 2 || EGTBProbe(tree, 2, Flip(wtm), &value)) {
if (TotalAllPieces > 2)
value = -value;
else
value = DrawScore(wtm);
if (value > best) {
best = value;
bestmv = current[i];
optimal_mv = 1;
} else if (value == best)
optimal_mv = 0;
}
}
UnmakeMove(tree, 1, wtm, current[i]);
}
if (best > -MATE - 1) {
moves[ply] = bestmv;
if (ply > 1 && wtm)
bestmv));
if (!strchr(buffer
, '#') && legal
> 1 && optimal_mv
)
hk[ply] = HashKey;
phk[ply] = PawnHashKey;
MakeMove(tree, 1, wtm, bestmv);
tree->status[1] = tree->status[2];
wtm = Flip(wtm);
for (j = 2 - (ply & 1); j < ply; j += 2)
if (pos[ply] == pos[j])
break;
if (j < ply)
break;
if (wtm)
t_move_number++;
break;
} else {
ply--;
break;
}
}
nmoves = ply;
for (; ply > 0; ply--) {
wtm = Flip(wtm);
tree->save_hash_key[1] = hk[ply];
tree->save_pawn_hash_key[1] = phk[ply];
UnmakeMove(tree, 1, wtm, moves[ply]);
tree->status[2] = tree->status[1];
}
next = buffer;
while (nmoves) {
if ((int) strlen(next
) > line_length
) { // Pierre-Marie Baty -- added type cast
int i;
for (i = 0; i < 16; i++)
if (*(next + 64 + i) == ' ')
break;
*(next + 64 + i) = 0;
next += 64 + i + 1;
} else {
break;
}
}
}
#endif
/*
*******************************************************************************
* *
* FormatPV() is used to display a PV during the search. It will also note *
* when the PV was terminated by a hash table hit. *
* *
*******************************************************************************
*/
char *FormatPV(TREE * RESTRICT tree, int wtm, PATH pv) {
int i, t_move_number;
static char buffer[4096];
/*
************************************************************
* *
* Initialize. *
* *
************************************************************
*/
t_move_number = move_number;
sprintf(buffer
, " %d.", move_number
);
if (!wtm)
for (i = 1; i < (int) pv.pathl; i++) {
if (i > 1 && wtm)
pv.path[i]));
MakeMove(tree, i, wtm, pv.path[i]);
wtm = Flip(wtm);
if (wtm)
t_move_number++;
}
for (i = pv.pathl - 1; i > 0; i--) {
wtm = Flip(wtm);
UnmakeMove(tree, i, wtm, pv.path[i]);
}
return buffer;
}
/* last modified 02/26/14 */
/*
*******************************************************************************
* *
* GameOver() is used to determine if the game is over by rule. More *
* specifically, after our move, the opponent has no legal move to play. He *
* is either checkmated or stalemated, either of which is sufficient reason *
* to terminate the game. *
* *
*******************************************************************************
*/
int GameOver(int wtm) {
TREE *const tree = block[0];
unsigned *mvp, *lastm, rmoves[256], over = 1;
/*
************************************************************
* *
* First, use GenerateMoves() to generate the set of *
* legal moves from the root position. *
* *
************************************************************
*/
lastm = GenerateCaptures(tree, 1, wtm, rmoves);
lastm = GenerateNoncaptures(tree, 1, wtm, lastm);
/*
************************************************************
* *
* Now make each move and determine if we are in check *
* after each one. Any move that does not leave us in *
* check is good enough to prove that the game is not yet *
* officially over. *
* *
************************************************************
*/
for (mvp = rmoves; mvp < lastm; mvp++) {
MakeMove(tree, 1, wtm, *mvp);
if (!Check(wtm))
over = 0;
UnmakeMove(tree, 1, wtm, *mvp);
}
/*
************************************************************
* *
* If we did not make it thru the complete move list, we *
* must have at least one legal move so the game is not *
* over. return 0. Otherwise, we have no move and the *
* game is over. We return 1 if this side is stalmated or *
* we return 2 if this side is mated. *
* *
************************************************************
*/
if (!over)
return 0;
else if (!Check(wtm))
return 1;
else
return 2;
}
/*
*******************************************************************************
* *
* ReadClock() is a procedure used to read the elapsed time. Since this *
* varies from system to system, this procedure has several flavors to *
* provide portability. *
* *
*******************************************************************************
*/
unsigned int ReadClock(void) {
#if defined(UNIX)
struct timeval timeval;
struct timezone timezone;
#else
//HANDLE hThread; // Pierre-Marie Baty -- unreferenced variable
//FILETIME ftCreate, ftExit, ftKernel, ftUser; // Pierre-Marie Baty -- unreferenced variables
//uint64_t tUser64; // Pierre-Marie Baty -- unreferenced variable
#endif
#if defined(UNIX)
gettimeofday(&timeval, &timezone);
return timeval.tv_sec * 100 + (timeval.tv_usec / 10000);
#else
return (unsigned int) GetTickCount() / 10;
#endif
}
/*
*******************************************************************************
* *
* FindBlockID() converts a thread block pointer into an ID that is easier *
* to understand when debugging. *
* *
*******************************************************************************
*/
int FindBlockID(TREE * RESTRICT which) {
int i;
for (i = 0; i <= (int) smp_max_threads * 64; i++) // Pierre-Marie Baty -- added type cast
if (which == block[i])
return i;
return -1;
}
/*
*******************************************************************************
* *
* InvalidPosition() is used to determine if the position just entered via a *
* FEN-string or the "edit" command is legal. This includes the expected *
* tests for too many pawns or pieces for one side, pawns on impossible *
* squares, and the like. *
* *
*******************************************************************************
*/
int InvalidPosition(TREE * RESTRICT tree) {
int error = 0, wp, wn, wb, wr, wq, wk, bp, bn, bb, br, bq, bk;
wp = PopCnt(Pawns(white));
wn = PopCnt(Knights(white));
wb = PopCnt(Bishops(white));
wr = PopCnt(Rooks(white));
wq = PopCnt(Queens(white));
wk = PopCnt(Kings(white));
bp = PopCnt(Pawns(black));
bn = PopCnt(Knights(black));
bb = PopCnt(Bishops(black));
br = PopCnt(Rooks(black));
bq = PopCnt(Queens(black));
bk = PopCnt(Kings(black));
if (wp > 8) {
Print(4095, "illegal position, too many white pawns\n");
error = 1;
}
if (wn && wp + wn > 10) {
Print(4095, "illegal position, too many white knights\n");
error = 1;
}
if (wb && wp + wb > 10) {
Print(4095, "illegal position, too many white bishops\n");
error = 1;
}
if (wr && wp + wr > 10) {
Print(4095, "illegal position, too many white rooks\n");
error = 1;
}
if (wq && wp + wq > 10) {
Print(4095, "illegal position, too many white queens\n");
error = 1;
}
if (wk == 0) {
Print(4095, "illegal position, no white king\n");
error = 1;
}
if (wk > 1) {
Print(4095, "illegal position, multiple white kings\n");
error = 1;
}
if ((wn + wb + wr + wq) && wp + wn + wb + wr + wq > 15) {
Print(4095, "illegal position, too many white pieces\n");
error = 1;
}
if (Pawns(white) & (rank_mask[RANK1] | rank_mask[RANK8])) {
Print(4095, "illegal position, white pawns on first/eighth rank(s)\n");
error = 1;
}
if (bp > 8) {
Print(4095, "illegal position, too many black pawns\n");
error = 1;
}
if (bn && bp + bn > 10) {
Print(4095, "illegal position, too many black knights\n");
error = 1;
}
if (bb && bp + bb > 10) {
Print(4095, "illegal position, too many black bishops\n");
error = 1;
}
if (br && bp + br > 10) {
Print(4095, "illegal position, too many black rooks\n");
error = 1;
}
if (bq && bp + bq > 10) {
Print(4095, "illegal position, too many black queens\n");
error = 1;
}
if (bk == 0) {
Print(4095, "illegal position, no black king\n");
error = 1;
}
if (bk > 1) {
Print(4095, "illegal position, multiple black kings\n");
error = 1;
}
if ((bn + bb + br + bq) && bp + bn + bb + br + bq > 15) {
Print(4095, "illegal position, too many black pieces\n");
error = 1;
}
if (Pawns(black) & (rank_mask[RANK1] | rank_mask[RANK8])) {
Print(4095, "illegal position, black pawns on first/eighth rank(s)\n");
error = 1;
}
if (error == 0 && Check(!game_wtm)) {
Print(4095, "ERROR side not on move is in check!\n");
error = 1;
}
return error;
}
/*
*******************************************************************************
* *
* KingPawnSquare() is used to initialize some of the passed pawn race *
* tables used by Evaluate(). It simply answers the question "is the king *
* in the square of the pawn so the pawn can't outrun it and promote?" *
* *
*******************************************************************************
*/
int KingPawnSquare(int pawn, int king, int queen, int ptm) {
int pdist, kdist;
pdist = Abs(Rank(pawn) - Rank(queen)) + !ptm;
kdist = Distance(king, queen);
return pdist >= kdist;
}
/* last modified 02/26/14 */
/*
*******************************************************************************
* *
* NewGame() is used to initialize the chess position and timing controls to *
* the setup needed to start a new game. *
* *
*******************************************************************************
*/
void NewGame(int save) {
TREE *const tree = block[0];
static int save_book_selection_width = 5, save_kibitz = 0;
static int save_resign = 0, save_resign_count = 0, save_draw_count = 0;
static int save_learning = 0, save_learn = 0, save_accept_draws = 0;
int id;
new_game = 0;
if (save) {
save_book_selection_width = book_selection_width;
save_kibitz = kibitz;
save_resign = resign;
save_resign_count = resign_count;
save_draw_count = draw_count;
save_learning = learning;
save_learn = learn;
save_accept_draws = accept_draws;
} else {
if (learn && moves_out_of_book) {
learn_value =
(crafty_is_white) ? last_search_value : -last_search_value;
LearnBook();
}
if (xboard) {
printf("tellicsnoalias set 1 Crafty v%s (%d cpus)\n", version
, Max
(1,
smp_max_threads));
}
over = 0;
moves_out_of_book = 0;
learn_positions_count = 0;
learn_value = 0;
ponder_move = 0;
last_search_value = 0;
last_pv.pathd = 0;
last_pv.pathl = 0;
InitializeChessBoard(tree);
InitializeHashTables(0);
force = 0;
books_file = normal_bs_file;
draw_score[0] = 0;
draw_score[1] = 0;
game_wtm = 1;
move_number = 1;
tc_time_remaining[white] = tc_time;
tc_time_remaining[black] = tc_time;
tc_moves_remaining[white] = tc_moves;
tc_moves_remaining[black] = tc_moves;
if (move_actually_played) {
if (log_file) {
id = InitializeGetLogID();
sprintf(log_filename
, "%s/log.%03d", log_path
, id
);
sprintf(history_filename
, "%s/game.%03d", log_path
, id
);
log_file
= fopen(log_filename
, "w");
history_file
= fopen(history_filename
, "w+");
if (!history_file) {
printf("ERROR, unable to open game history file, exiting\n");
CraftyExit(1);
}
}
}
move_actually_played = 0;
book_selection_width = save_book_selection_width;
kibitz = save_kibitz;
resign = save_resign;
resign_count = save_resign_count;
resign_counter = 0;
draw_count = save_draw_count;
accept_draws = save_accept_draws;
draw_counter = 0;
usage_level = 0;
learning = save_learning;
learn = save_learn;
predicted = 0;
kibitz_depth = 0;
tree->nodes_searched = 0;
kibitz_text[0] = 0;
}
}
/*
*******************************************************************************
* *
* ParseTime() is used to parse a time value that could be entered as s.ss, *
* mm:ss, or hh:mm:ss. It is converted to Crafty's internal 1/100th second *
* time resolution. *
* *
*******************************************************************************
*/
int ParseTime(char *string) {
int time = 0, minutes
= 0;
while (*string) {
switch (*string) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
minutes = minutes * 10 + (*string) - '0';
break;
case ':':
minutes = 0;
break;
default:
Print(4095, "illegal character in time, please re-enter\n");
break;
}
string++;
}
return time * 60 + minutes
;
}
/*
*******************************************************************************
* *
* Pass() was written by Tim Mann to handle the case where a position is set *
* using a FEN string, and then black moves first. The game.nnn file was *
* designed to start with a white move, so "pass" is now a "no-op" move for *
* the side whose turn it is to move. *
* *
*******************************************************************************
*/
void Pass(void) {
const int halfmoves_done = 2 * (move_number - 1) + (1 - game_wtm);
int prev_pass = 0;
char buffer[128];
/* Was previous move a pass? */
if (halfmoves_done > 0) {
if (history_file) {
fseek(history_file
, (halfmoves_done
- 1) * 10, SEEK_SET
);
if (fscanf(history_file
, "%s", buffer
) == 0 ||
prev_pass = 1;
}
}
if (prev_pass) {
if (game_wtm)
move_number--;
} else {
if (history_file) {
fseek(history_file
, halfmoves_done
* 10, SEEK_SET
);
fprintf(history_file
, "%9s\n", "pass");
}
if (!game_wtm)
move_number++;
}
game_wtm = Flip(game_wtm);
}
/*
*******************************************************************************
* *
* Print() is the main output procedure. The first argument is a bitmask *
* that identifies the type of output. If this argument is anded with the *
* "display" control variable, and a non-zero result is produced, then the *
* print is done, otherwise the print is skipped and we return (more details *
* can be found in the display command comments in option.c). This also *
* uses the "variable number of arguments" facility in ANSI C since the *
* normal printf() function accepts a variable number of arguments. *
* *
* Print() also sends output to the log.nnn file automatically, so that it *
* is recorded even if the above display control variable says "do not send *
* this to stdout" *
* *
*******************************************************************************
*/
void Print(int vb, char *fmt, ...) {
va_list ap;
if (vb == 4095 || vb & display_options) {
}
if (time_limit > 5 || tc_time_remaining[root_wtm] > 1000 || vb == 4095) {
if (log_file) {
}
}
}
/*
*******************************************************************************
* *
* A 32 bit random number generator. An implementation in C of the algorithm *
* given by Knuth, the art of computer programming, vol. 2, pp. 26-27. We use *
* e=32, so we have to evaluate y(n) = y(n - 24) + y(n - 55) mod 2^32, which *
* is implicitly done by unsigned arithmetic. *
* *
*******************************************************************************
*/
unsigned int Random32(void) {
/*
random numbers from Mathematica 2.0.
SeedRandom = 1;
Table[Random[Integer, {0, 2^32 - 1}]
*/
static const uint64_t x[55] = {
1410651636UL, 3012776752UL, 3497475623UL, 2892145026UL, 1571949714UL,
3253082284UL, 3489895018UL, 387949491UL, 2597396737UL, 1981903553UL,
3160251843UL, 129444464UL, 1851443344UL, 4156445905UL, 224604922UL,
1455067070UL, 3953493484UL, 1460937157UL, 2528362617UL, 317430674UL,
3229354360UL, 117491133UL, 832845075UL, 1961600170UL, 1321557429UL,
747750121UL, 545747446UL, 810476036UL, 503334515UL, 4088144633UL,
2824216555UL, 3738252341UL, 3493754131UL, 3672533954UL, 29494241UL,
1180928407UL, 4213624418UL, 33062851UL, 3221315737UL, 1145213552UL,
2957984897UL, 4078668503UL, 2262661702UL, 65478801UL, 2527208841UL,
1960622036UL, 315685891UL, 1196037864UL, 804614524UL, 1421733266UL,
2017105031UL, 3882325900UL, 810735053UL, 384606609UL, 2393861397UL
};
static int init = 1;
static uint64_t y[55];
static int j, k;
uint64_t ul;
if (init) {
int i;
init = 0;
for (i = 0; i < 55; i++)
y[i] = x[i];
j = 24 - 1;
k = 55 - 1;
}
ul = (y[k] += y[j]);
if (--j < 0)
j = 55 - 1;
if (--k < 0)
k = 55 - 1;
return (unsigned int) ul;
}
/*
*******************************************************************************
* *
* Random64() uses two calls to Random32() and then concatenates the two *
* values into one 64 bit random number, used for hash signature updates on *
* the Zobrist hash signatures. *
* *
*******************************************************************************
*/
uint64_t Random64(void) {
uint64_t result;
unsigned int r1, r2;
r1 = Random32();
r2 = Random32();
result = r1 | (uint64_t) r2 << 32;
return result;
}
/*
*******************************************************************************
* *
* Read() copies data from the command_buffer into a local buffer, and then *
* uses ReadParse to break this command up into tokens for processing. *
* *
*******************************************************************************
*/
int Read(int wait, char *buffer) {
char *eol, *ret, readdata;
*buffer = 0;
/*
case 1: We have a complete command line, with terminating
N/L character in the buffer. We can simply extract it from
the I/O buffer, parse it and return.
*/
if (strchr(cmd_buffer
, '\n'));
/*
case 2: The buffer does not contain a complete line. If we
were asked to not wait for a complete command, then we first
see if I/O is possible, and if so, read in what is available.
If that includes a N/L, then we are ready to parse and return.
If not, we return indicating no input available just yet.
*/
else if (!wait) {
if (CheckInput()) {
readdata = ReadInput();
if (!strchr(cmd_buffer
, '\n'))
return 0;
if (!readdata)
return -1;
} else
return 0;
}
/*
case 3: The buffer does not contain a complete line, but we
were asked to wait until a complete command is entered. So we
hang by doing a ReadInput() and continue doing so until we get
a N/L character in the buffer. Then we parse and return.
*/
else
while (!strchr(cmd_buffer
, '\n')) {
readdata = ReadInput();
if (!readdata)
return -1;
}
eol
= strchr(cmd_buffer
, '\n');
*eol = 0;
ret
= strchr(cmd_buffer
, '\r');
if (ret)
*ret = ' ';
return 1;
}
/*
*******************************************************************************
* *
* ReadClear() clears the input buffer when input_stream is being switched *
* to a file, since we have info buffered up from a different input stream. *
* *
*******************************************************************************
*/
void ReadClear() {
cmd_buffer[0] = 0;
}
/*
*******************************************************************************
* *
* ReadParse() takes one complete command-line, and breaks it up into tokens.*
* common delimiters are used, such as " ", ",", "/" and ";", any of which *
* delimit fields. *
* *
*******************************************************************************
*/
int ReadParse(char *buffer, char *args[], char *delims) {
int nargs;
char *next, tbuffer[4096];
for (nargs = 0; nargs < 512; nargs++)
*(args[nargs]) = 0;
next
= strtok(tbuffer
, delims
);
if (!next)
return 0;
Print(4095, "ERROR, ignoring token %s, max allowable len = 255\n", next);
else
for (nargs = 1; nargs < 512; nargs++) {
if (!next)
break;
Print(4095, "ERROR, ignoring token %s, max allowable len = 255\n",
next);
else
}
return nargs;
}
/*
*******************************************************************************
* *
* ReadInput() reads data from the input_stream, and buffers this into the *
* command_buffer for later processing. *
* *
*******************************************************************************
*/
int ReadInput(void) {
int bytes;
char buffer[4096], *end;
do
bytes = _read(_fileno(input_stream), buffer, 2048); // Pierre-Marie Baty -- POSIX/ISO C++ names fixes
while (bytes < 0 && errno == EINTR);
if (bytes == 0) {
if (input_stream != stdin)
input_stream = stdin;
return 0;
} else if (bytes < 0) {
Print(4095, "ERROR! input I/O stream is unreadable, exiting.\n");
CraftyExit(1);
}
end
= cmd_buffer
+ strlen(cmd_buffer
);
*(end + bytes) = 0;
return 1;
}
/*
*******************************************************************************
* *
* ReadChessMove() is used to read a move from an input file. The main *
* issue is to skip over "trash" like move numbers, times, comments, and so *
* forth, and find the next actual move. *
* *
*******************************************************************************
*/
int ReadChessMove(TREE * RESTRICT tree, FILE * input, int wtm, int one_move) {
int move = 0, status;
static char text[128];
char *tmove;
while (move == 0) {
status
= fscanf(input
, "%s", text
);
if (status <= 0)
return -1;
tmove
= text
+ strspn(text
, "0123456789.");
else
tmove = text;
if (((tmove[0] >= 'a' && tmove[0] <= 'z') || (tmove[0] >= 'A' &&
tmove
[0] <= 'Z')) || !strcmp(tmove
, "0-0")
return -1;
move = InputMove(tree, 0, wtm, 1, 0, tmove);
}
if (one_move)
break;
}
return move;
}
/*
*******************************************************************************
* *
* ReadNextMove() is used to take a text chess move from a file, and see if *
* if is legal, skipping a sometimes embedded move number (1.e4 for example) *
* to make PGN import easier. *
* *
*******************************************************************************
*/
int ReadNextMove(TREE * RESTRICT tree, char *text, int ply, int wtm) {
char *tmove;
int move = 0;
tmove
= text
+ strspn(text
, "0123456789./-");
else
tmove = text;
if (((tmove[0] >= 'a' && tmove[0] <= 'z') || (tmove[0] >= 'A' &&
tmove
[0] <= 'Z')) || !strcmp(tmove
, "0-0")
return -1;
move = InputMove(tree, ply, wtm, 1, 0, tmove);
}
return move;
}
/*
*******************************************************************************
* *
* This routine reads a move from a PGN file to build an opening book or for *
* annotating. It returns a 1 if a header is read, it returns a 0 if a move *
* is read, and returns a -1 on end of file. It counts lines and this *
* counter can be accessed by calling this function with a non-zero second *
* formal parameter. *
* *
*******************************************************************************
*/
int ReadPGN(FILE * input, int option) {
static int data = 0, lines_read = 0;
int braces = 0, parens = 0, brackets = 0, analysis = 0, last_good_line;
static char input_buffer[4096];
char *eof, analysis_move[64];
/*
************************************************************
* *
* If the line counter is being requested, return it with *
* no other changes being made. If "purge" is true, clear *
* the current input buffer. *
* *
************************************************************
*/
pgn_suggested_percent = 0;
if (!input) {
lines_read = 0;
data = 0;
return 0;
}
if (option == -1)
data = 0;
if (option == -2)
return lines_read;
/*
************************************************************
* *
* If we don't have any data in the buffer, the first step *
* is to read the next line. *
* *
************************************************************
*/
while (1) {
if (!data) {
eof
= fgets(input_buffer
, 4096, input
);
if (!eof)
return -1;
if (strchr(input_buffer
, '\n'))
*strchr(input_buffer
, '\n') = 0;
if (strchr(input_buffer
, '\r'))
*strchr(input_buffer
, '\r') = ' ';
lines_read++;
buffer[0] = 0;
sscanf(input_buffer
, "%s", buffer
);
if (buffer[0] == '[')
do {
char *bracket1, *bracket2, value[128];
bracket1
= strchr(input_buffer
, '\"');
if (bracket1 == 0)
return 1;
bracket2
= strchr(bracket1
+ 1, '\"');
if (bracket2 == 0)
return 1;
*bracket1 = 0;
*bracket2 = 0;
if (strstr(input_buffer
, "Event"))
else if (strstr(input_buffer
, "Site"))
else if (strstr(input_buffer
, "Round"))
else if (strstr(input_buffer
, "Date"))
else if (strstr(input_buffer
, "WhiteElo"))
else if (strstr(input_buffer
, "White"))
else if (strstr(input_buffer
, "BlackElo"))
else if (strstr(input_buffer
, "Black"))
else if (strstr(input_buffer
, "Result"))
else if (strstr(input_buffer
, "FEN")) {
sprintf(buffer
, "setboard %s", value
);
Option(block[0]);
continue;
}
return 1;
} while (0);
data = 1;
}
/*
************************************************************
* *
* If we already have data in the buffer, it is just a *
* matter of extracting the next move and returning it to *
* the caller. If the buffer is empty, another line has *
* to be read in. *
* *
************************************************************
*/
else {
buffer[0] = 0;
sscanf(input_buffer
, "%s", buffer
);
data = 0;
continue;
} else {
char *skip;
if (skip)
}
/*
************************************************************
* *
* This skips over nested {} or () characters and finds *
* the 'mate', before returning any more moves. It also *
* stops if a PGN header is encountered, probably due to *
* an incorrectly bracketed analysis variation. *
* *
************************************************************
*/
last_good_line = lines_read;
analysis_move[0] = 0;
while (1) {
char *skip, *ch;
analysis = 1;
while ((ch
= strpbrk(buffer
, "(){}[]"))) {
if (*ch == '(') {
if (!braces)
parens++;
}
if (*ch == ')') {
if (!braces)
parens--;
}
if (*ch == '{') {
braces++;
}
if (*ch == '}') {
braces--;
}
if (*ch == '[') {
if (!braces)
brackets++;
}
if (*ch == ']') {
if (!braces)
brackets--;
}
}
if (analysis && analysis_move[0] == 0) {
char *tmove = analysis_move;
sscanf(buffer
, "%64s", analysis_move
);
strcpy(buffer
, analysis_move
);
tmove
= buffer
+ strspn(buffer
, "0123456789.");
else
tmove = buffer;
if ((tmove[0] >= 'a' && tmove[0] <= 'z') || (tmove[0] >= 'A' &&
tmove
[0] <= 'Z') || !strcmp(tmove
, "0-0")
strcpy(analysis_move
, buffer
);
else
analysis_move[0] = 0;
}
}
if (parens == 0 && braces == 0 && brackets == 0)
break;
buffer[0] = 0;
sscanf(input_buffer
, "%s", buffer
);
eof
= fgets(input_buffer
, 4096, input
);
if (!eof) {
parens = 0;
braces = 0;
brackets = 0;
return -1;
}
if (strchr(input_buffer
, '\n'))
*strchr(input_buffer
, '\n') = 0;
if (strchr(input_buffer
, '\r'))
*strchr(input_buffer
, '\r') = ' ';
lines_read++;
if (lines_read - last_good_line >= 100) {
parens = 0;
braces = 0;
brackets = 0;
Print(4095,
"ERROR. comment spans over 100 lines, starting at line %d\n",
last_good_line);
break;
}
}
} else {
int skip;
if ((skip
= strspn(buffer
, "0123456789./-"))) {
if (skip > 1)
}
char *first, *last, *percent;
first
= input_buffer
+ strspn(input_buffer
, " ");
if (first == 0 || *first != '{')
return 0;
last
= strchr(input_buffer
, '}');
if (last == 0)
return 0;
percent
= strstr(first
, "play");
if (percent == 0)
return 0;
pgn_suggested_percent =
return 0;
}
}
if (analysis_move[0] && option == 1) {
strcpy(buffer
, analysis_move
);
return 2;
}
}
}
}
/*
*******************************************************************************
* *
* RestoreGame() resets the position to the beginning of the game, and then *
* reads in the game.nnn history file to set the position up so that the *
* game position matches the position at the end of the history file. *
* *
*******************************************************************************
*/
void RestoreGame(void) {
int i, v, move;
char cmd[16];
if (!history_file)
return;
game_wtm = 1;
InitializeChessBoard(block[0]);
for (i = 0; i < 500; i++) {
fseek(history_file
, i
* 10, SEEK_SET
);
v
= fscanf(history_file
, "%s", cmd
);
if (v < 0)
perror("RestoreGame fscanf error: ");
move = InputMove(block[0], 0, game_wtm, 1, 0, cmd);
if (move)
MakeMoveRoot(block[0], game_wtm, move);
else
break;
}
game_wtm = Flip(game_wtm);
}
}
/*
*******************************************************************************
* *
* Kibitz() is used to whisper/kibitz information to a chess server. It has *
* to handle the xboard whisper/kibitz interface. *
* *
*******************************************************************************
*/
void Kibitz
(int level
, int wtm
, int depth
, int time, int value
,
uint64_t nodes, int ip, int tb_hits, char *pv) {
int nps;
nps
= (int) ((time) ? 100 * nodes
/ (uint64_t) time : nodes
);
if (!puzzling) {
char prefix[128];
if (!(kibitz & 16))
else
switch (level) {
case 1:
if ((kibitz & 15) >= 1) {
if (value > 0) {
printf("%s mate in %d moves.\n\n", prefix
, value
);
}
if (value < 0) {
printf("%s mated in %d moves.\n\n", prefix
, -value
);
}
}
break;
case 2:
if ((kibitz & 15) >= 2)
printf("%s ply=%d; eval=%s; nps=%s; time=%s(%d%%); egtb=%d\n",
prefix, depth, DisplayEvaluationKibitz(value, wtm),
DisplayKMB
(nps
, 0), DisplayTimeKibitz
(time), ip
, tb_hits
);
case 3:
if ((kibitz & 15) >= 3 && (nodes > 5000 || level == 2))
printf("%s %s\n", prefix
, pv
);
break;
case 4:
if ((kibitz & 15) >= 4)
printf("%s %s\n", prefix
, pv
);
break;
case 5:
if ((kibitz & 15) >= 5 && nodes > 5000) {
printf("%s d%d-> %s/s %s(%d%%) %s %s ", prefix
, depth
,
DisplayKMB
(nps
, 0), DisplayTimeKibitz
(time), ip
,
DisplayEvaluationKibitz(value, wtm), pv);
if (tb_hits)
}
break;
}
value = (wtm) ? value : -value;
if (post && level > 1) {
printf(" %2d %5d %7d %" PRIu64
" %s\n", depth
, value
, time,
nodes, pv + 10);
else
printf(" %2d %5d %7d %" PRIu64
" %s\n", depth
, value
, time,
nodes, pv);
}
}
}
/*
*******************************************************************************
* *
* Output() is used to print the principal variation whenever it changes. *
* *
*******************************************************************************
*/
void Output(TREE * RESTRICT tree) {
int wtm, i;
/*
************************************************************
* *
* Output the PV by walking down the path being backed up. *
* We do set the "age" for this move to "4" which will *
* keep it in the group of "search with all threads" moves *
* so that it will be searched faster. *
* *
************************************************************
*/
wtm = root_wtm;
if (!abort_search) {
kibitz_depth = iteration;
end_time = ReadClock();
DisplayPV(tree, 6, wtm, end_time - start_time, &tree->pv[1], 0);
for (i = 0; i < n_root_moves; i++)
if (tree->pv[1].path[1] == root_moves[i].move)
break;
root_moves[i].path = tree->pv[1];
root_moves[i].bm_age = 4;
}
}
/*
*******************************************************************************
* *
* SortRootMoves() is used to sort the root move list based on the value *
* saved for each move. After a fail high or fail low, we always re-sort *
* the root move list so that the best move found so far is first in the *
* list. This is primarily intended as a defense against getting a score *
* for the first root move, and then getting a fail-high on the second move, *
* which should move this move to the front of the moves. But if the move *
* then fails low, we want to move it back down since a deeper/less-reduced *
* search did not verify the fail-high. *
* *
*******************************************************************************
*/
void SortRootMoves() {
ROOT_MOVE rtemp;
int mvp, done;
do {
done = 1;
for (mvp = 0; mvp < n_root_moves - 1; mvp++) {
if (root_moves[mvp].path.pathv < root_moves[mvp + 1].path.pathv) {
rtemp = root_moves[mvp];
root_moves[mvp] = root_moves[mvp + 1];
root_moves[mvp + 1] = rtemp;
done = 0;
}
}
} while (!done);
}
/*
*******************************************************************************
* *
* Trace() is used to print the search trace output each time a node is *
* traversed in the tree. *
* *
*******************************************************************************
*/
void Trace(TREE * RESTRICT tree, int ply, int depth, int wtm, int alpha,
int beta, const char *name, int mode, int phase, int order) {
int i;
Lock(lock_io);
for (i = 1; i < ply; i++)
Print(-1, " ");
if (phase != EVALUATION) {
Print(-1, "%d %s(%d) d:%2d [%s,", ply, OutputMove(tree, ply, wtm,
tree->curmv[ply]), order, depth, DisplayEvaluation(alpha, 1));
Print(-1, "%s] n:%" PRIu64 " %s(%c:%d)", DisplayEvaluation(beta, 1),
tree->nodes_searched, name, (mode) ? 'P' : 'S', phase);
Print(-1, " (t=%d)\n", tree->thread_id);
} else {
Print(-1, "%d window/eval(%s) = {", ply, name);
Print(-1, "%s, ", DisplayEvaluation(alpha, 1));
Print(-1, "%s, ", DisplayEvaluation(depth, 1));
Print(-1, "%s}\n", DisplayEvaluation(beta, 1));
}
Unlock(lock_io);
}
/*
*******************************************************************************
* *
* StrCnt() counts the number of times a character occurs in a string. *
* *
*******************************************************************************
*/
int StrCnt(char *string, char testchar) {
int count = 0, i;
for (i
= 0; i
< (int) strlen(string
); i
++) // Pierre-Marie Baty -- added type cast
if (string[i] == testchar)
count++;
return count;
}
/*
*******************************************************************************
* *
* ValidMove() is used to verify that a move is playable. It is mainly *
* used to confirm that a move retrieved from the transposition/refutation *
* and/or killer move is valid in the current position by checking the move *
* against the current chess board, castling status, en passant status, etc. *
* *
*******************************************************************************
*/
int ValidMove(TREE * RESTRICT tree, int ply, int wtm, int move) {
int btm = Flip(wtm);
/*
************************************************************
* *
* Make sure that the piece on <from> is the right color. *
* *
************************************************************
*/
if (PcOnSq(From(move)) != ((wtm) ? Piece(move) : -Piece(move)))
return 0;
switch (Piece(move)) {
/*
************************************************************
* *
* Null-moves are caught as it is possible for a killer *
* move entry to be zero at certain times. *
* *
************************************************************
*/
case empty:
return 0;
/*
************************************************************
* *
* King moves are validated here if the king is moving two *
* squares at one time (castling moves). Otherwise fall *
* into the normal piece validation routine below. For *
* castling moves, we need to verify that the castling *
* status is correct to avoid "creating" a new rook or *
* king. *
* *
************************************************************
*/
case king:
if (Abs(From(move) - To(move)) == 2) {
if (Castle(ply, wtm) > 0) {
if (To(move) == csq[wtm]) {
if ((!(Castle(ply, wtm) & 2)) || (OccupiedSquares & OOO[wtm])
|| (AttacksTo(tree, csq[wtm]) & Occupied(btm))
|| (AttacksTo(tree, dsq[wtm]) & Occupied(btm))
|| (AttacksTo(tree, esq[wtm]) & Occupied(btm)))
return 0;
} else if (To(move) == gsq[wtm]) {
if ((!(Castle(ply, wtm) & 1)) || (OccupiedSquares & OO[wtm])
|| (AttacksTo(tree, esq[wtm]) & Occupied(btm))
|| (AttacksTo(tree, fsq[wtm]) & Occupied(btm))
|| (AttacksTo(tree, gsq[wtm]) & Occupied(btm)))
return 0;
}
} else
return 0;
return 1;
}
break;
/*
************************************************************
* *
* Check for a normal pawn advance. *
* *
************************************************************
*/
case pawn:
if (((wtm) ? To(move) - From(move) : From(move) - To(move)) < 0)
return 0;
if (Abs(From(move) - To(move)) == 8)
return (PcOnSq(To(move))) ? 0 : 1;
if (Abs(From(move) - To(move)) == 16)
return (PcOnSq(To(move)) || PcOnSq(To(move) + epdir[wtm])) ? 0 : 1;
if (!Captured(move))
return 0;
/*
************************************************************
* *
* Check for an en passant capture which is somewhat *
* unusual in that the [to] square does not contain the *
* pawn being captured. Make sure that the pawn being *
* captured advanced two ranks the previous move. *
* *
************************************************************
*/
if ((PcOnSq(To(move)) == 0)
&& (PcOnSq(To(move) + epdir[wtm]) == ((wtm) ? -pawn : pawn))
&& (EnPassantTarget(ply) & SetMask(To(move))))
return 1;
break;
/*
************************************************************
* *
* Normal moves are all checked the same way. *
* *
************************************************************
*/
case queen:
case rook:
case bishop:
if (Attack(From(move), To(move)))
break;
return 0;
case knight:
break;
}
/*
************************************************************
* *
* All normal moves are validated in the same manner, by *
* checking the from and to squares and also the attack *
* status for completeness. *
* *
************************************************************
*/
return ((Captured(move) == ((wtm) ? -PcOnSq(To(move)) : PcOnSq(To(move))))
&& Captured(move) != king) ? 1 : 0;
}
/* last modified 02/26/14 */
/*
*******************************************************************************
* *
* VerifyMove() tests a move to confirm it is absolutely legal. It shouldn't *
* be used inside the search, but can be used to check a 21-bit (compressed) *
* move to be sure it is safe to make it on the permanent game board. *
* *
*******************************************************************************
*/
int VerifyMove(TREE * RESTRICT tree, int ply, int wtm, int move) {
unsigned moves[256], *mv, *mvp;
/*
Generate moves, then eliminate any that are illegal.
*/
if (move == 0)
return 0;
tree->status[MAXPLY] = tree->status[ply];
mvp = GenerateCaptures(tree, MAXPLY, wtm, moves);
mvp = GenerateNoncaptures(tree, MAXPLY, wtm, mvp);
for (mv = &moves[0]; mv < mvp; mv++) {
MakeMove(tree, MAXPLY, wtm, *mv);
if (!Check(wtm) && move == *mv) {
UnmakeMove(tree, MAXPLY, wtm, *mv);
return 1;
}
UnmakeMove(tree, MAXPLY, wtm, *mv);
}
return 0;
}
/*
*******************************************************************************
* *
* Windows NUMA support *
* *
*******************************************************************************
*/
#if !defined(UNIX)
static BOOL(WINAPI * pGetNumaHighestNodeNumber) (PULONG);
static BOOL(WINAPI * pGetNumaNodeProcessorMask) (UCHAR, PULONGLONG);
static DWORD(WINAPI * pSetThreadIdealProcessor) (HANDLE, DWORD);
static volatile BOOL fThreadsInitialized = FALSE;
static BOOL fSystemIsNUMA = FALSE;
static ULONGLONG ullProcessorMask[256];
static ULONG ulNumaNodes;
static ULONG ulNumaNode = 0;
// Get NUMA-related information from Windows
static void WinNumaInit(void) {
//DWORD_PTR dwMask; // Pierre-Marie Baty -- unreferenced variable
HMODULE hModule;
ULONG ulCPU, ulNode;
ULONGLONG ullMask;
DWORD dwCPU;
if (!fThreadsInitialized) {
Lock(lock_smp);
if (!fThreadsInitialized) {
printf("\nInitializing multiple threads.\n");
fThreadsInitialized = TRUE;
hModule = GetModuleHandle("kernel32");
pGetNumaHighestNodeNumber =
(void *) GetProcAddress(hModule, "GetNumaHighestNodeNumber");
pGetNumaNodeProcessorMask =
(void *) GetProcAddress(hModule, "GetNumaNodeProcessorMask");
pSetThreadIdealProcessor =
(void *) GetProcAddress(hModule, "SetThreadIdealProcessor");
if (pGetNumaHighestNodeNumber && pGetNumaNodeProcessorMask &&
pGetNumaHighestNodeNumber(&ulNumaNodes) && (ulNumaNodes > 0)) {
fSystemIsNUMA = TRUE;
if (ulNumaNodes > 255)
ulNumaNodes = 255;
printf("System is NUMA. %d nodes reported by Windows\n",
ulNumaNodes + 1);
for (ulNode = 0; ulNode <= ulNumaNodes; ulNode++) {
pGetNumaNodeProcessorMask((UCHAR) ulNode,
&ullProcessorMask[ulNode]);
printf("Node %d CPUs: ", ulNode
);
ullMask = ullProcessorMask[ulNode];
if (0 == ullMask)
fSystemIsNUMA = FALSE;
else {
ulCPU = 0;
do {
if (ullMask & 1)
ulCPU++;
ullMask >>= 1;
} while (ullMask);
}
}
// Thread 0 was already started on some CPU. To simplify things further,
// exchange ullProcessorMask[0] and ullProcessorMask[node for that CPU],
// so ullProcessorMask[0] would always be node for thread 0
dwCPU =
pSetThreadIdealProcessor(GetCurrentThread(), MAXIMUM_PROCESSORS);
printf("Current ideal CPU is %u\n", dwCPU
);
pSetThreadIdealProcessor(GetCurrentThread(), dwCPU);
if ((((DWORD) - 1) != dwCPU) && (MAXIMUM_PROCESSORS != dwCPU)
&& !(ullProcessorMask[0] & (1uLL << dwCPU))) { // Pierre-Marie Baty -- added type cast
for (ulNode = 1; ulNode <= ulNumaNodes; ulNode++) {
if (ullProcessorMask[ulNode] & (1uLL << dwCPU)) { // Pierre-Marie Baty -- added type cast
printf("Exchanging nodes 0 and %d\n", ulNode
);
ullMask = ullProcessorMask[ulNode];
ullProcessorMask[ulNode] = ullProcessorMask[0];
ullProcessorMask[0] = ullMask;
break;
}
}
}
} else
printf("System is SMP, not NUMA.\n");
}
Unlock(lock_smp);
}
}
// Start thread. For NUMA system set its affinity.
# if (CPUS > 1)
pthread_t NumaStartThread(void *func, void *args) {
HANDLE hThread;
ULONGLONG ullMask;
WinNumaInit();
if (fSystemIsNUMA) {
ulNumaNode++;
if (ulNumaNode > ulNumaNodes)
ulNumaNode = 0;
ullMask = ullProcessorMask[ulNumaNode];
printf("Starting thread on node %d CPU mask %I64d\n", ulNumaNode
,
ullMask);
SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR) ullMask);
hThread = (HANDLE) _beginthreadex(0, 0, func, args, CREATE_SUSPENDED, 0);
SetThreadAffinityMask(hThread, (DWORD_PTR) ullMask);
ResumeThread(hThread);
SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR) ullProcessorMask[0]); // Pierre-Marie Baty -- added type cast
} else
hThread = (HANDLE) _beginthreadex(0, 0, func, args, 0, 0);
return hThread;
}
# endif
// Allocate memory for thread #N
void *WinMalloc(size_t cbBytes, int iThread) {
HANDLE hThread;
DWORD_PTR dwAffinityMask;
void *pBytes;
ULONG ulNode;
WinNumaInit();
if (fSystemIsNUMA) {
ulNode = iThread % (ulNumaNodes + 1);
hThread = GetCurrentThread();
dwAffinityMask = SetThreadAffinityMask(hThread, (DWORD_PTR) ullProcessorMask[ulNode]); // Pierre-Marie Baty -- added type cast
pBytes = VirtualAlloc(NULL, cbBytes, MEM_COMMIT, PAGE_READWRITE);
if (pBytes == NULL)
ExitProcess(GetLastError());
SetThreadAffinityMask(hThread, dwAffinityMask);
} else {
pBytes = VirtualAlloc(NULL, cbBytes, MEM_COMMIT, PAGE_READWRITE);
if (pBytes == NULL)
ExitProcess(GetLastError());
}
return pBytes;
}
// Allocate interleaved memory
void *WinMallocInterleaved(size_t cbBytes, int cThreads) {
char *pBase;
char *pEnd;
char *pch;
HANDLE hThread;
DWORD_PTR dwAffinityMask;
ULONG ulNode;
SYSTEM_INFO sSysInfo;
size_t dwStep;
int iThread;
DWORD dwPageSize; // the page size on this computer
LPVOID lpvResult;
WinNumaInit();
if (fSystemIsNUMA && (cThreads > 1)) {
GetSystemInfo(&sSysInfo); // populate the system information structure
dwPageSize = sSysInfo.dwPageSize;
// Reserve pages in the process's virtual address space.
pBase = (char *) VirtualAlloc(NULL, cbBytes, MEM_RESERVE, PAGE_NOACCESS);
if (pBase == NULL) {
printf("VirtualAlloc() reserve failed\n");
CraftyExit(0);
}
// Now walk through memory, committing each page
hThread = GetCurrentThread();
dwStep = dwPageSize * cThreads;
pEnd = pBase + cbBytes;
for (iThread = 0; iThread < cThreads; iThread++) {
ulNode = iThread % (ulNumaNodes + 1);
dwAffinityMask =
SetThreadAffinityMask(hThread, (DWORD_PTR) ullProcessorMask[ulNode]); // Pierre-Marie Baty -- added type cast
for (pch = pBase + iThread * dwPageSize; pch < pEnd; pch += dwStep) {
lpvResult = VirtualAlloc(pch, // next page to commit
dwPageSize, // page size, in bytes
MEM_COMMIT, // allocate a committed page
PAGE_READWRITE); // read/write access
if (lpvResult == NULL)
ExitProcess(GetLastError());
memset(lpvResult
, 0, dwPageSize
);
}
SetThreadAffinityMask(hThread, dwAffinityMask);
}
} else {
pBase = VirtualAlloc(NULL, cbBytes, MEM_COMMIT, PAGE_READWRITE);
if (pBase == NULL)
ExitProcess(GetLastError());
}
return (void *) pBase;
}
// Free interleaved memory
void WinFreeInterleaved(void *pMemory, size_t cBytes) {
VirtualFree(pMemory, 0, MEM_RELEASE);
}
#endif