#ifdef _DEBUG #define _CRTDBG_MAP_ALLOC #include #include #endif // This pragma is only because of a bug in MSVC that gives warning // with too long variable name (>255). #ifdef _DEBUG #pragma warning( disable : 4786 ) #endif #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; #define PROGRAM_NAME "Wb2Uci 1.2" enum {ospin=1,obutton,ocheck,ocombo,ostring}; struct EXTRAOPTION { string optionCommand; string engineCommand; string defaultValue; string minValue; string maxValue; int type; }; list extras; list preExtras; struct CHESSGAME { ChessBoard board; ChessMove move[1024]; void clear() { board.clear(); for (int i=0;i<1024;i++) move[i].clear(); }; int size() { for (int i=0;i<1024;i++) if (move[i].empty()) return i; return 0; }; void activeBoard(ChessBoard& b) { int i=0; b.copy(board); while ((i<1024)&&(!move[i].empty())) b.move(move[i++]); } }; // Engine states enum {waiting,thinking,pondering,analyze}; CHESSGAME servergame; // The game as the engine have CHESSGAME clientgame; // The game as the gui have int engine=waiting; // The state the engine is in. ChessMove hintMove; // Move recived from the engine as (real) pondermove. ChessMove ponderMove; // Move given to the GUI as pondermove ChessMove lastPV[2]; // Last PV (2 first move) int eTime=-1; // Latest enginetime int oTime=-1; // Latest opponenttime int eMovestogo=-1; // Moves to go for pondermove CountDown eClock; // Engine clock CountDown oClock; // Opponent clock TimerThread timer; // Timer for statline etc.. int sendDot=0; // Send the dot command 0=no, 1=waitfor respons, 2=ok int maxDepth=-1; // Max search depth to simulate 'sd n' int interuptNow=0; // Interupting move now and send new. bool allowPriorityChange=true; // False under start and stop // Commandline or inifile option // Setupmethod for the engine. enum {setboard,edit,cb_edit}; enum {progressive,strict,failsafe}; string cmdLine = ""; string initString = ""; string hashcmd = ""; string name = ""; string author = ""; int editType = setboard; bool useSan = false; DWORD priority = THREAD_PRIORITY_NORMAL; DWORD priorityClass = NORMAL_PRIORITY_CLASS; bool idleClass = false; DWORD initTime = 2; bool ponder = true; int hashsize = 2; bool hashOnCmd = false; bool resignmode = false; bool waitdone = false; bool usermove = false; bool addHint = false; int protocol = 2; bool stat01 = false; bool stopPV = true; bool whiteScore = false; bool ownBook = false; bool useHint = true; bool useColor = true; bool pvRight = false; int levelExt = 0; bool useAnalyze = true; bool useUndo = true; int delay = 0; bool computer = false; bool useTime = true; //int moveCount = 0; int levM=0,levT=0,levS=0,levI=0; typeColor engineColor=BLACK; list visibleOption; bool useVisible = false; bool terminateHard= false; int noise = 0; // Output pipe that is used to send info to the gui. HANDLE stdOutput = GetStdHandle(STD_OUTPUT_HANDLE); // Logfile WinFile log; // The file StopWatch watch; // Clock for logging CRITICAL_SECTION logCS; // Thread security object list prelog; // Buffer for loggingline before the log is opened. bool usePrelog; // Switch to tell if logging buffer is in use. // The server process ExternProcess server; // Engine process CRITICAL_SECTION serverCS; // Security object for writing to the engine. InputThread serverThread; // Reading thread for input from the engine. HANDLE engineStarted; // Placeholder when the engine is not started. // Client process (GUI) CRITICAL_SECTION clientCS; InputThread clientThread; // Objects for the main waiting loop HANDLE handles[2]; // Locale routines bool startServer(); bool ServerProc(const char* sz, void* data); void writeLog(string& s, char* c); void showPriorityClass(DWORD cls); void showThreadPriority(DWORD pri); void writeServer(std::string line); void writeClient(std::string line); void setHash(); void setPonder(); void randomMoveToClient(); void checkPriority(); // Set the hash size void setHash() { char str[80]; if (server.running()) { if (!hashOnCmd && hashcmd.length()>0) { if (hashcmd!="") { wsprintf(str,hashcmd.c_str(),hashsize); writeServer(str); } } } } // Set the noiselevel void setNoise() { if (server.running()) { if (noise<0) writeServer("nopost"); else writeServer("post"); } } // Set the engine to ponder mode or disable ponder mode void setPonder() { if (server.running()) { if (ponder) writeServer("hard"); else writeServer("hard\neasy"); } } // Convert moves to the uci-format char* longMove(ChessMove& m, char* buf) { char piece[]={' ','p','n','b','r','q','k'}; buf[0]='a'+FILE(m.fromSquare); buf[1]='1'+RANK(m.fromSquare); buf[2]='a'+FILE(m.toSquare); buf[3]='1'+RANK(m.toSquare); if (m.moveType&PROMOTE) { buf[4]=piece[PIECE(m.promotePiece)]; buf[5]='\0'; }else { buf[4]='\0'; } return buf; } // Callback for timer process void timerProc() { // Ask for search statistic (ShowThinkingMove) if (sendDot==2) writeServer("."); // Interupting a ask for stop if the engine don't respond to // to a 'move now' command. if (interuptNow) { if (interuptNow==1) { randomMoveToClient(); writeServer("new"); } interuptNow--; } } // Spool all saved extra options to the engine. void drainExtras() { if (server.running()) { list::iterator it=preExtras.begin(); while (it!=preExtras.end()) { writeServer(*it); ++it; } } preExtras.clear(); } void setExtrasFromFile(const std::string& val) { string line; string fname; if (val.length()<1) return; WinFile setupFile; fname=val; fname+=".txt"; if (setupFile.open(fname,GENERIC_READ,false,OPEN_EXISTING,false)) { line="Read pre setup from "; line+=fname; writeLog(line,"--"); while (setupFile.readLine(line)) { line=trim(line); if (line.length()) { if (line.at(0)!=';') { if (server.running()) writeServer(line); else preExtras.push_back(line); } } } setupFile.close(); }else { line="Failed to read pre setup from "; line+=fname; writeLog(line,"--"); } } // Send an (extras)option command to the engine. void setExtras(string& opt, string& val) { char str[255]; string line; int i; EXTRAOPTION eo; list::iterator it=extras.begin(); while(it!=extras.end()) { eo=*it; if (eo.optionCommand==opt) { switch (eo.type) { case ospin: if (val.length()<1) return; wsprintf(str,eo.engineCommand.c_str(),atoi(val.c_str())); if (server.running()) writeServer(str); else preExtras.push_back(str); return; case ocheck: if (val.length()<1) return; if ((val.at(0)=='t') || (val.at(0)=='T') || (val.at(0)=='1')) wsprintf(str,eo.engineCommand.c_str(),eo.maxValue.c_str()); else wsprintf(str,eo.engineCommand.c_str(),eo.minValue.c_str()); if (server.running()) writeServer(str); else preExtras.push_back(str); return; case obutton: if (server.running()) writeServer(eo.engineCommand); else preExtras.push_back(eo.engineCommand); return; case ostring: if (val=="") val=""; wsprintf(str,eo.engineCommand.c_str(),val.c_str()); if (server.running()) writeServer(str); else preExtras.push_back(str); return; case ocombo: if (val.length()<1) return; // Correction for CA that send a number if (isdigit(val.at(0))) { // is this an var value i=1; while (getWord(eo.minValue,i,"|").length()>0) { if (getWord(eo.minValue,i,"|")==val) { i=0; break; } i++; } if (i) { i=atoi(val.c_str()); val=getWord(eo.minValue.c_str(),i+1,"|"); if (val.length()<1) return; } } if (eo.engineCommand==" %s") { setExtrasFromFile(val); }else { wsprintf(str,eo.engineCommand.c_str(),val.c_str()); if (server.running()) writeServer(str); else preExtras.push_back(str); } return; default: return; } } ++it; } } // Handle an option command from the gui. void setOption(std::string& s) { // stringformat: // setoption name [value ] string cmd; string val; char str[255]; int i; bool btn; // Check if there is any command in this string. if (getWord(s,3).length()<1) return; // Read the command i=3; btn=true; cmd=""; while (getWord(s,i).length()>0) { if (getWord(s,i)=="value") { i++; btn=false; break; } cmd+=getWord(s,i); cmd+=' '; i++; } cmd=trim(cmd); // If there was no 'value' it is a buttons if (btn) { if (cmd=="Help") { ShellExecute(NULL, NULL, "Wb2Uci.html", NULL, NULL, SW_SHOWNORMAL); // WinHelp(NULL,"Wb2Uci.hlp",HELP_CONTENTS,0); return; } // Check for enginespesific command setExtras(cmd,string("")); return; } // Get the value val=""; while (getWord(s,i).length()>0) { val+=getWord(s,i++); val+=' '; } val=trim(val); if (val.length()<1) return; if (val=="") val=""; if (cmd=="Program") { cmdLine=val; }else if (cmd=="InitString") { strcpy(str,val.c_str()); initString=escape2normal(str); }else if (cmd=="HashCommand") { strcpy(str,val.c_str()); hashcmd=escape2normal(str); if (hashcmd.length()) if (hashcmd.find("%i")==string::npos) hashcmd+=" %i"; }else if (cmd=="InitTime") { initTime=atoi(val.c_str()); }else if (cmd=="Delay") { delay=atoi(val.c_str()); }else if (cmd=="Hash") { if (hashsize!=atoi(val.c_str())) { hashsize=atoi(val.c_str()); setHash(); } }else if (cmd=="Noise") { if (noise!=atoi(val.c_str())) { noise=atoi(val.c_str()); setNoise(); } }else if (cmd=="Protocol") { protocol=atoi(val.c_str()); }else if (cmd=="Edit") { switch (toupper(val.at(0))) { case 'E': case '1': editType=1;break; case 'C': case '2': editType=2;break; default: editType=0;break; } }else if (cmd=="Priority") { switch (toupper(val.at(0))) { case 'B': case '1': priority=THREAD_PRIORITY_BELOW_NORMAL;break; case 'L': case '2': priority=THREAD_PRIORITY_IDLE;break; default: priority=THREAD_PRIORITY_NORMAL;break; } }else if (cmd=="AddHintMove") { if (toupper(val.at(0))=='T') addHint=true; else addHint=false; }else if (cmd=="Analyze") { if (toupper(val.at(0))=='T') useAnalyze=true; else useAnalyze=false; }else if (cmd=="UseUndo") { if (toupper(val.at(0))=='T') useUndo=true; else useUndo=false; }else if (cmd=="RunIdle") { if (toupper(val.at(0))=='T') idleClass=true; else idleClass=false; }else if (cmd=="Computer") { if (toupper(val.at(0))=='T') computer=true; else computer=false; if (server.running()) if (computer) writeServer("computer"); }else if (cmd=="ShowThinkingMove") { if (toupper(val.at(0))=='T') stat01=true; else stat01=false; sendDot=0; }else if (cmd=="TerminateHard") { if (toupper(val.at(0))=='T') terminateHard=true; else terminateHard=false; }else if (cmd=="Ponder") { if (toupper(val.at(0))=='T') ponder=true; else ponder=false; setPonder(); }else if (cmd=="HashOnCommandline") { if (toupper(val.at(0))=='T') hashOnCmd=true; else hashOnCmd=false; }else if (cmd=="LevelExtend") { switch (toupper(val.at(0))) { case 'S': case '1': levelExt=strict;break; case 'F': case '2': levelExt=failsafe;break; default: levelExt=progressive;break; } }else if (cmd=="WhiteScore") { if (toupper(val.at(0))=='T') whiteScore=true; else whiteScore=false; }else if (cmd=="OwnBook") { if (toupper(val.at(0))=='T') ownBook=true; else ownBook=false; }else if (cmd=="Logfile") { if (toupper(val.at(0))=='T') { if (!log.isOpen()) { // Check for logfile usage. // If it has got any logfile it would be #ifdef _DEBUG writeClient("** Open logfile Wb2Uci.log and push all recorded logs to the file."); #endif EnterCriticalSection(&logCS); usePrelog=false; log.open(string("Wb2Uci.log"),GENERIC_WRITE,true,OPEN_ALWAYS,false); // Save logging from before the logfile was opened. list::iterator it=prelog.begin(); if (log.isOpen()) { while (it!=prelog.end()) { log.writeLine(*it); ++it; } } prelog.clear(); LeaveCriticalSection(&logCS); } }else { #ifdef _DEBUG writeClient("** Closing logfile."); #endif EnterCriticalSection(&logCS); if (log.isOpen()) log.close(); prelog.clear(); usePrelog=false; LeaveCriticalSection(&logCS); } }else { // Check if this option was a user defined option setExtras(cmd,val); } } // Starting the server bool startServer() { char cmd[MAX_PATH]; char str[255]; string s; int i; // Check if it's alredy started if (server.pi.hProcess!=NULL) return true; #ifdef _DEBUG s="** Starting server: "; s+=cmdLine; writeClient(s); #endif // Missing program to start if ((cmdLine.length()<1) || (cmdLine=="")) return false; strcpy(cmd,trim(cmdLine).c_str()); if (hashOnCmd) { if (hashcmd.length()>0) { if (hashcmd!="") { wsprintf(str,hashcmd.c_str(),hashsize); strcat(cmd," "); strcat(cmd,str); } } } s= "Starting server: "; s+=cmd; writeLog(s,"S-"); // Stop the main thread to adjust the priorities allowPriorityChange=false; // Set the priorities to normal for the server under startup server.setProcessPriority(NORMAL_PRIORITY_CLASS); server.setThreadPriority(THREAD_PRIORITY_NORMAL); // Start the engine if (server.start(cmd)==0) { server.stop(terminateHard); writeLog(string("Error starting server program."),"S-"); allowPriorityChange=true; return false; } #ifdef _DEBUG writeClient("** Starting server thread for reading input from engine"); #endif // Create a thread for reading from server and writing to the client. serverThread.setInputHandle(server.hRead); serverThread.setCallback(ServerProc,NULL); serverThread.start(); if (protocol==1) { Sleep(initTime*1000); // Switch to xboard protocol writeServer("xboard"); levM=0; levT=5; levI=0; }else { // Switch to xboard protocol 2 writeServer("xboard\nprotover 2"); Sleep(1000); if (!waitdone) { if (initTime) Sleep((initTime-1)*1000); } if (waitdone) { i=1; while (waitdone) { if (i++>=60) break; Sleep(1000); } } } // Send standard initstring writeServer("new\nlevel 0 5 0"); levM=0; levT=5; levI=0; // Send userdefined initstring if (initString.length()>0) if (initString!="") writeServer(initString); // Set post level setNoise(); // Set standard hash setHash(); // Set standard ponder setPonder(); // Send extras that is allready received drainExtras(); // Set the priority back to startup/user/gui defaults if (idleClass) { server.setProcessPriority(IDLE_PRIORITY_CLASS); }else { if (priorityClass!=NORMAL_PRIORITY_CLASS) { server.setProcessPriority(priorityClass); showPriorityClass(priorityClass); } if (priority!=THREAD_PRIORITY_NORMAL) { server.setThreadPriority(priority); showThreadPriority(priority); } } allowPriorityChange=true; // Tell the main thread that he engine is started. if (engineStarted!=NULL) SetEvent(engineStarted); return true; } void stopServer() { sendDot=0; // Run the stopprocess with normal priority allowPriorityChange=false; server.setProcessPriority(NORMAL_PRIORITY_CLASS); server.setThreadPriority(THREAD_PRIORITY_NORMAL); if (engine==thinking) { writeServer("?"); interuptNow=2; Sleep(3000); }else if (engine==analyze) { writeServer("exit"); } if (server.pi.hProcess) { writeServer("quit"); server.stop(terminateHard); }else { if (engineStarted!=NULL) SetEvent(engineStarted); else handles[0]=NULL; } allowPriorityChange=true; } // Write to a logfile. When the program starts it set // the usePrelog to true so it saves all logging to the memory. // Then when it receives the switches about if the user want // to log it empties this to the log-file. If the user have // decided to not log this prlog is cleared. This is done so that // the startup of the program also could be logged. void writeLog(string& s, char* c) { char szWatch[64]; DWORD dw; EnterCriticalSection(&logCS); if (!usePrelog && !log.isOpen()) { LeaveCriticalSection(&logCS); return; } dw=watch.read(); wsprintf(szWatch,"%d.%03d: %s ",dw/1000,dw%1000,c); if (usePrelog) { string line=szWatch; line+=s; prelog.push_back(line); }else { log.write(szWatch,strlen(szWatch)); log.writeLine(s); } LeaveCriticalSection(&logCS); } // Send commands to the engine. void writeServer(std::string line) { if (line.length()<1) return; if (delay) Sleep(delay); EnterCriticalSection(&serverCS); DWORD dwWrite; if (line.at(line.length()-1)!='\n') line+='\n'; if (server.hWrite) WriteFile(server.hWrite,line.c_str(),line.length(),&dwWrite,NULL); writeLog(line,"S<"); LeaveCriticalSection(&serverCS); } // Send command to the GUI. void writeClient(std::string line) { if (line.length()<1) return; EnterCriticalSection(&clientCS); DWORD dwWrite; if (line.at(line.length()-1)!='\n') line+='\n'; WriteFile(stdOutput,line.c_str(),line.length(),&dwWrite,NULL); writeLog(line,"C<"); LeaveCriticalSection(&clientCS); } // Interpret the feature command from the engine. void feature(std::string& s) { int i=2; string cmd; int it; string s1,s2; cmd=getWord(s,i); while (cmd.length()) { it=cmd.find("="); if (it!=string::npos) { s1=trim(cmd.substr(0,it)); s2=trim(cmd.substr(it+1)); if (s1=="setboard") { if (s2=="1") editType=setboard; else if (s2=="0") editType=edit; }else if (s1=="san") { if (s2=="1") useSan=true; else if (s2=="0") useSan=false; }else if (s1=="colors") { if (s2=="1") useColor=true; else if (s2=="0") useColor=false; }else if (s1=="time") { if (s2=="1") useTime=true; else if (s2=="0") useTime=false; }else if (s1=="usermove") { if (s2=="1") usermove=true; else if (s2=="0") usermove=false; }else if (s1=="analyze") { if (s2=="1") useAnalyze=true; else if (s2=="0") useAnalyze=false; }else if (s1=="done") { if (s2=="0") waitdone=true; else if (s2=="1") waitdone=false; }else if (s1=="myname") { name=""; if (s2.length()) { while(s2.at(s2.length()-1)!='"') { i++; s2=getWord(s,i); if (s2.length()<1) break; } name+=s2; } // resend the id is not tested // cmd="id name "; // cmd+=name; // writeClient(cmd); }else if (s1=="variants") { if (s2.length()) { while(s2.at(s2.length()-1)!='"') { i++; s2=getWord(s,i); if (s2.length()<1) break; } } } } cmd="accepted "; cmd+=s1; writeServer(cmd); i++; cmd=getWord(s,i); } }; // Create a string for sending to the engine if the board // is needed to be setup. eg. no standard startposition. void makeEditString(int type, string fen, string& buf) { ChessBoard b; if (type==setboard) { buf="setboard "; buf+=fen; buf+='\n'; return; } b.setFen(fen.c_str()); if (b.whiteToMove()) { buf="new\nedit\n#\n"; }else { buf="new\nforce\n"; if (usermove) buf+="usermove "; if (useSan) buf+="a3\nedit\n#\n"; else buf+="a2a3\nedit\n#\n"; } typePiece piece; int rank,file,i; int color=WHITE; char c; for (i=0;i<2;i++) { for (rank=0;rank<8;rank++) { for (file=0;file<8;file++) { piece=b.getPiece(SQUARE(file,rank)); if (piece!=EMPTY) { if (PIECECOLOR(piece)==color) { c=b.getCharFromPiece(piece); if (c==' ') c='P'; buf+=c; buf+=(char)(file+'a'); buf+=(char)(rank+'1'); buf+='\n'; } } } } buf+="c\n"; color=BLACK; } if (type==cb_edit) { if (b.whiteKingSideCastle()) buf+="+w00\n"; else buf+="-w00\n"; if (b.whiteQueenSideCastle()) buf+="+w000\n"; else buf+="-w000\n"; if (b.blackKingSideCastle()) buf+="+b00\n"; else buf+="-b00\n"; if (b.blackQueenSideCastle()) buf+="+b000\n"; else buf+="-b000\n"; if (b.enPassantSquare()!=UNDEF) { buf+="ep "; buf+=(char)(FILE(b.enPassantSquare())+'a'); buf+=(char)(RANK(b.enPassantSquare())+'1'); buf+='\n'; } } if (b.whiteToMove()) buf+="c\n"; buf+=".\n"; } void addVisible(std::string& line) { int i=1; useVisible=true; string s; s=getWord(line,i++,","); while (s.length()) { s=trim(s); if (s.length()) visibleOption.push_back(s); s=getWord(line,i++,","); } } bool visible(std::string s) { if (!useVisible) return true; list::iterator it=visibleOption.begin(); while (it!=visibleOption.end()) { if (*it==s) return true; ++it; } return false; } // Interpret the [EXTRAS] info from the eng file format. void addExtras(string& s1, string& s2) { // s2=command|spin|default|min|max // s2=command|check|default|false-value|true-value // s2=command|button // s2=command|combo|default|var1|var2|var3|...|varn // s2=command|string|default EXTRAOPTION eo; int i; char str[255]; if (s1.length()<1) return; if (getWord(s2,2,"|").length()<1) return; eo.optionCommand=s1; eo.type=0; if (getWord(s2,2,"|")=="spin") eo.type=ospin; else if (getWord(s2,2,"|")=="check") eo.type=ocheck; else if (getWord(s2,2,"|")=="button") eo.type=obutton; else if (getWord(s2,2,"|")=="combo") eo.type=ocombo; else if (getWord(s2,2,"|")=="string") eo.type=ostring; switch (eo.type) { case ospin: case ocheck: if (getWord(s2,5,"|").length()<1) return; eo.defaultValue=getWord(s2,3,"|"); eo.minValue=getWord(s2,4,"|"); eo.maxValue=getWord(s2,5,"|"); break; case obutton: eo.defaultValue=""; eo.minValue=""; eo.maxValue=""; break; case ostring: if (getWord(s2,3,"|").length()<1) eo.defaultValue=""; else eo.defaultValue=getWord(s2,3,"|"); break; case ocombo: eo.defaultValue=getWord(s2,3,"|"); eo.minValue=""; eo.maxValue=""; i=4; while (getWord(s2,i,"|").length()>0) { if (i>4) eo.minValue+='|'; eo.minValue+=getWord(s2,i++,"|"); } break; default: return; } strcpy(str,getWord(s2,1,"|").c_str()); eo.engineCommand=escape2normal(str); if (eo.type==ospin) { if (eo.engineCommand.find("%i")==string::npos) eo.engineCommand+=" %i"; } if ((eo.type==ocheck) || (eo.type==ostring) || (eo.type==ocombo)) { if (eo.engineCommand.find("%s")==string::npos) eo.engineCommand+=" %s"; } extras.push_back(eo); } // Read the .eng file. bool readIniFile(std::string& fname) { int in=0; // string line,s1,s2; int it; #ifdef _DEBUG line="** Reading .eng file: "; line+=fname; writeClient(line); line=""; #endif WinFile iniFile; if (iniFile.open(fname,GENERIC_READ,false,OPEN_EXISTING,false)) { #ifdef _DEBUG writeClient("** eng-file open, start reading."); #endif while (iniFile.readLine(line)) { line=trim(line); if (line.length()) { if (line.at(0)!=';') { if (!stricmp(line.c_str(),"[ENGINE]")) { in=1; }else if (!stricmp(line.c_str(),"[OPTIONS]")) { in=2; }else if (!stricmp(line.c_str(),"[EXTRAS]")) { in=3; }else { if (in) { it=line.find("="); if (it!=string::npos) { s1=trim(line.substr(0,it)); s2=trim(line.substr(it+1)); #ifdef _DEBUG line="** Key="; line+=s1; line+=", Value="; line+=s2; writeClient(line); #endif switch (in) { case 1: if (!stricmp(s1.c_str(),"Name")) { name=s2; }else if (!stricmp(s1.c_str(),"Author")) { author=s2; }else if (!stricmp(s1.c_str(),"Visible")) { addVisible(s2); /* }else if (!stricmp(s1.c_str(),"Filename")) { line="setoption name Program value "; line+=s2; */ } break; case 2: if (s1=="Visible") { addVisible(s2); break; } line="setoption name "; line+=s1; line+=" value "; line+=s2; setOption(line); break; case 3: addExtras(s1,s2); break; } } } } } } } iniFile.close(); #ifdef _DEBUG writeClient("** eng-file closed."); #endif }else { #ifdef _DEBUG line="**eng-file: "; line+=fname; line+=" not found."; writeClient(line); #endif return false; } return true; } // This routine will set up the clientgame so that it // correspond to the GUI's board. Eg. the moves and position // from the GUI command 'position' void setupBoard(std::string& s) { string cmd; ChessBoard board; ChessMove m; int i=2,n; clientgame.clear(); cmd=getWord(s,i++); while (cmd.length()>0) { if (cmd=="fen") { cmd=getWord(s,i++); if ((getWord(s,i)=="w") || (getWord(s,i)=="b")) { cmd+=' '; cmd+=getWord(s,i++); if (existIn(getWord(s,i).at(0),"KQkq-")) { cmd+=' '; cmd+=getWord(s,i++); if (existIn(getWord(s,i).at(0),"abcdefgh")) { if (existIn(getWord(s,i).at(1),"36")) { cmd+=' '; cmd+=getWord(s,i++); } } } } clientgame.board.setFen(cmd.c_str()); }else if (cmd=="startpos") { clientgame.board.setFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -"); }else if (cmd=="moves") { board.copy(clientgame.board); n=0; cmd=getWord(s,i++); while (cmd.length()) { m=board.getMoveFromText(cmd); if (!m.empty()) if (board.move(m)&&(n<1024)) clientgame.move[n++]=m; cmd=getWord(s,i++); } } cmd=getWord(s,i++); } } // This routine is used when the engine don't have any move to send // to the GUI. This could be that it have resigned or that it is pondering // and the GUI ask him to stop and excpect a bestmove in return. // When a Winboard engine ponder he hasn't done any move on his internal // board so there is no need to retract anything. // All moves that comes out from this routine is legal move from the GUI's point. // except if there isn't any move to give (mate, stalemate) then the move is e2e4. void randomMoveToClient() { MoveList ml; MoveGenerator mg; ChessBoard b; string line; char sz[20]; eClock.stop(); clientgame.activeBoard(b); mg.makeMoves(b,ml); line="bestmove "; if (ml.size) { if (useHint) { if (!lastPV[0].empty() && ml.exist(lastPV[0])) line+=longMove(lastPV[0],sz); else if (!lastPV[1].empty() && ml.exist(lastPV[1])) line+=longMove(lastPV[1],sz); else line+=longMove(ml.list[0],sz); }else { line+=longMove(ml.list[0],sz); } }else { line+="e2e4"; } // line += " ponder"; writeClient(line); stopPV=true; engine=waiting; } // This will send a go command to the engine with first update the // engines game so that this is the same as the GUI's game. // An exception from this is when the engine is asked to ponder, then // this command is ignored so that the engine could do it's own // pondering. void go(std::string& s) { int i,n; bool force=false; char str[80]; int wtime=-1,btime=-1,winc=-1,binc=-1,movestogo=-1,depth=-1,nodes=-1,mate=-1,movetime=-1; bool infinite=false; bool enginewhite; ChessBoard board; string cmd; bool only_ponder=false; bool newgame=false; int serversize,clientsize; // Get the input from the gui i=2; cmd=getWord(s,i++); while (cmd.length()>0) { if (cmd=="searchmoves") // Winboard don't have this { return; }else if (cmd=="ponder") // Let the winboard do it's own ponder. { only_ponder=true; }else if (cmd=="wtime") { wtime=atoi(getWord(s,i++).c_str()); }else if (cmd=="btime") { btime=atoi(getWord(s,i++).c_str()); }else if (cmd=="winc") { winc=atoi(getWord(s,i++).c_str()); }else if (cmd=="binc") { binc=atoi(getWord(s,i++).c_str()); }else if (cmd=="movestogo") { movestogo=atoi(getWord(s,i++).c_str()); }else if (cmd=="depth") { depth=atoi(getWord(s,i++).c_str()); }else if (cmd=="nodes") { nodes=atoi(getWord(s,i++).c_str()); }else if (cmd=="mate") { mate=atoi(getWord(s,i++).c_str()); }else if (cmd=="movetime") { movetime=atoi(getWord(s,i++).c_str()); }else if (cmd=="infinite") { infinite=true; } cmd=getWord(s,i++); } // Just for speed clientsize=clientgame.size(); serversize=servergame.size(); // Who is on move? // In normal game white is on move for plies=0,2,4 etc. if (clientsize%2) enginewhite=false; else enginewhite=true; // If the game was started from a position with black on move // change the color. if (clientgame.board.toMove==BLACK) enginewhite=enginewhite?false:true; // convert timecontrols to winboardformat if (enginewhite) { eTime=wtime; oTime=btime; }else { eTime=btime; oTime=wtime; } // Response to the Client so it knows we are alive writeClient("info depth 1 score cp 1");// seldepth 2");//0 time 10 nodes 1 nps 1"); // Start sending pv to the gui. This is so there isnt sent any // pv after a move is given and until it expectes a pv again. stopPV=false; // Prepare to send dot command if (stat01) sendDot=1; // To simulate 'sd n' if the engine don't support it if ((maxDepth!=-1) && (depth==-1)) { depth=99; maxDepth=-1; }else { maxDepth=depth; } // Ponder isn't sent to the engine, so I only collect some data // and start an opponent clock because an ponderhit message don't // have this. if (only_ponder) { if (oTime>0) { oClock.start(); oClock.set(oTime); }else { oClock.reset(); } eMovestogo=movestogo; return; } // Time to use in infinite level if (infinite) eTime=oTime=8640000; // Check for new game. if (clientgame.board.compare(servergame.board)) { // Startposition is different servergame.clear(); serversize=0; }else // if (clientsize!=serversize+1) { // Check if it only have made some more moves from the gui-book if (clientsize>serversize) { for (i=0;iclientsize) { writeServer("force"); force=true; while (serversize>clientsize) { writeServer("undo"); serversize--; servergame.move[serversize].clear(); } } }else { servergame.clear(); // Fewer move== new game. serversize=0; } } } // New game if (serversize<1) { resignmode=false; hintMove.clear(); if (useHint) { lastPV[0].clear(); lastPV[1].clear(); ponderMove.clear(); } writeServer("new"); setNoise(); // post or nopost setPonder(); if (server.running()) if (computer) writeServer("computer"); servergame.board.copy(clientgame.board); board.setFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -"); // Startup from a position if (board.compare(servergame.board)) { writeServer("force"); force=true; servergame.board.getFen(str); cmd=""; makeEditString(editType,string(str),cmd); writeServer(cmd); } if (infinite && !useAnalyze) { writeServer("level 1 1440 0"); levM=1; levT=1440; levI=0; } newgame=true; engineColor=OTHERPLAYER(servergame.board.toMove); eClock.reset(); oClock.reset(); } // Always post in infinite mode (analyzing). if (infinite && (noise=-1)) { noise=0; setNoise(); noise=-1; } // Send level if: this is a new game, or the engine have more than expected // time left (window=10 sec.). Level for infinite is already sent. // wsprintf(str,"eTime=%d, levI=%d",eClock.read(),levI); // writeLog(string(str),"**"); if (!infinite && (newgame || (((int)eClock.read()+levI*1000)<(eTime-10000)))) { int xtime=1440,xinc=0,xsec=0,xmove; if (eTime>=0) xtime=(eTime+30000-(eClock.read()+levI*1000))/60000; // Round up if (xtime==0) xsec=(eTime+500-(eClock.read()+levI*1000))/1000; if (enginewhite && (winc>0)) xinc=(winc+500)/1000; else if (!enginewhite && (binc>0)) xinc=(binc+500)/1000; if (movestogo>=0) xmove=movestogo+(clientsize-serversize)/2; else xmove=0; // The new level command would be wrong so use all moves for this // level command. if ((levelExt==strict) && (xmove>0)) if (((clientsize/2)%xmove)!=0) xmove=movestogo+clientsize/2; // Only send a new levelcommand if there is some changes or it is a new game. if ((levM!=xmove) || (xtime!=levT) || (xinc!=levI) || newgame) { if ((levelExt==failsafe) && !newgame) { servergame.clear(); go(s); return; }else if (xtime!=0) wsprintf(str,"level %d %d %d",xmove,xtime,xinc); else wsprintf(str,"level %d 0:%02d %d",xmove,xsec,xinc); writeServer(str); } levM=xmove; if (levT>xtime) levT=xtime; levI=xinc; } // The engine have allready resigned so a random legal move is sent instead. // This is done so that the user get the pleasure to mate the machine and // he do not need to sit an wait to the time is run out. if (resignmode) { randomMoveToClient(); return; } n=0; while ((n<1024)&& !clientgame.move[n].empty() && (servergame.move[n]==clientgame.move[n])) n++; // set forcemode if we need a 'go' if (((clientsize-serversize)>1) || (clientsize==0) || (clientsize==serversize)) { if ((clientsize!=0) && (clientsize!=serversize)) writeServer("force"); force=true; } // Set in forcemode if analyzing if (infinite && useAnalyze) { if (!force) writeServer("force"); force=true; } servergame.activeBoard(board); while ((n<1024)&&!clientgame.move[n].empty()) { // Send time before last move if (!force && (n==clientsize-1)) { if (useTime) { if (eTime>-1) { wsprintf(str,"time %i",eTime/10); writeServer(str); } if (oTime>-1) { wsprintf(str,"otim %i",oTime/10); writeServer(str); } } if (depth>0) { wsprintf(str,"sd %i",depth); writeServer(str); } if (movetime>0) { wsprintf(str,"st %i",movetime); writeServer(str); } } servergame.move[n]=clientgame.move[n]; if (useSan==true) board.makeMoveText(servergame.move[n],str,SAN); else longMove(servergame.move[n],str); if (usermove) { cmd="usermove "; cmd+=str; writeServer(cmd); }else { writeServer(str); } board.move(servergame.move[n]); n++; } if (infinite && useAnalyze) engine=analyze; else engine=thinking; if (force) { if (engine!=analyze) { // If the engine is not to move on this move it could lead to that wrong // clock inside the engine would be set so we trix it a bit here so the // wrong side is to move. if (useColor) { if (enginewhite && (engineColor!=WHITE)) writeServer("black"); else if (!enginewhite && (engineColor!=BLACK)) writeServer("white"); } if (useTime) { if (eTime>-1) { wsprintf(str,"time %i",eTime/10); writeServer(str); } if (oTime>-1) { wsprintf(str,"otim %i",oTime/10); writeServer(str); } } if (depth>0) { wsprintf(str,"sd %i",depth); writeServer(str); } if (movetime>0) { wsprintf(str,"st %i",movetime); writeServer(str); } // Ensure right side on move if (useColor) { if (enginewhite) writeServer("white"); else writeServer("black"); } } if (engine==analyze) writeServer("analyze"); else writeServer("go"); } if (enginewhite) engineColor=WHITE; else engineColor=BLACK; eClock.start(); if (eTime>=0) eClock.set(eTime); else eClock.set(0); } // On ponderhit just make a go command since the // position (clientgame) should already been set up. void ponderhit() { bool enginewhite; char str[64]; DWORD oc; string s="go"; if (eMovestogo>=0) { s+=" movestogo "; s+=itoa(eMovestogo,str,10); eMovestogo=-1; } if (maxDepth>0) { s+=" depth "; s+=itoa(maxDepth,str,10); } if (eTime>=0) { // Who is on move if (clientgame.size()%2) enginewhite=false; else enginewhite=true; if (clientgame.board.toMove==BLACK) enginewhite=enginewhite?false:true; oc=oClock.read(); if (enginewhite) { s+=" wtime "; s+=itoa(eTime,str,10); if (oc>0) { s+=" btime "; s+=itoa(oc,str,10); oClock.reset(); } }else { s+=" btime "; s+=itoa(eTime,str,10); if (oc>0) { s+=" wtime "; s+=itoa(oc,str,10); oClock.reset(); } } } go(s); } // Just for the logfile. void showThreadPriority(DWORD pri) { string s="Thread priority set to "; char sz[10]; switch (pri) { case THREAD_PRIORITY_ABOVE_NORMAL : s+="above normal\n"; break; case THREAD_PRIORITY_BELOW_NORMAL : s+="below normal\n"; break; case THREAD_PRIORITY_HIGHEST : s+="highest\n"; break; case THREAD_PRIORITY_IDLE : s+="idle\n"; break; case THREAD_PRIORITY_LOWEST : s+="lowest\n"; break; case THREAD_PRIORITY_NORMAL : s+="normal\n"; break; case THREAD_PRIORITY_TIME_CRITICAL : s+="time critical\n"; break; default : s+=itoa(pri,sz,10); s+="\n"; break; } writeLog(s,"S-"); } // Just for the logfile. void showPriorityClass(DWORD cls) { string s="Priority class set to "; char sz[10]; switch (cls) { case HIGH_PRIORITY_CLASS : s+="high\n"; break; case IDLE_PRIORITY_CLASS : s+="idle\n"; break; case NORMAL_PRIORITY_CLASS : s+="normal\n"; break; case REALTIME_PRIORITY_CLASS : s+="realtime\n"; break; default : s+=itoa(cls,sz,10); s+="\n"; break; } writeLog(s,"S-"); } // This routine will check the priority of current process and thread // and set the engine process according to this. void checkPriority() { DWORD cls=GetPriorityClass(GetCurrentProcess()); DWORD pri=GetThreadPriority(GetCurrentThread()); if (cls!=NORMAL_PRIORITY_CLASS) { // SetPriorityClass(GetCurrentProcess(),NORMAL_PRIORITY_CLASS); priorityClass=cls; } if (pri!=THREAD_PRIORITY_NORMAL) { // writeLog(string("Change main threadpriority to normal"),"--"); // SetThreadPriority(GetCurrentProcess(),THREAD_PRIORITY_NORMAL); priority=pri; } if (allowPriorityChange) { if (!idleClass && (priorityClass!=server.priorityClass)) { server.setProcessPriority(priorityClass); showPriorityClass(priorityClass); } if (!idleClass && (priority!=server.threadPriority)) { server.setThreadPriority(priority); showThreadPriority(priority); } } } void sendExtras() { char str[255]; string line; int i; EXTRAOPTION eo; list::iterator it=extras.begin(); while(it!=extras.end()) { eo=*it; switch (eo.type) { case ospin: wsprintf(str,"option name %s type spin default %s min %s max %s",eo.optionCommand.c_str(),eo.defaultValue.c_str(),eo.minValue.c_str(),eo.maxValue.c_str()); writeClient(str); break; case ocheck: wsprintf(str,"option name %s type check default %s",eo.optionCommand.c_str(),eo.defaultValue.c_str()); writeClient(str); break; case obutton: wsprintf(str,"option name %s type button",eo.optionCommand.c_str()); writeClient(str); break; case ostring: wsprintf(str,"option name %s type string default %s",eo.optionCommand.c_str(),eo.defaultValue.c_str()); writeClient(str); break; case ocombo: wsprintf(str,"option name %s type combo default %s",eo.optionCommand.c_str(),eo.defaultValue.c_str()); i=1; line=str; while (getWord(eo.minValue,i,"|").length()>0) { line+=" var "; line+=getWord(eo.minValue,i++,"|"); } writeClient(line); break; default: break; } ++it; } } void sendOptions() { char str[255]; if (visible("Extras")) sendExtras(); if (visible("Program")) { wsprintf(str,"option name Program type string default %s",cmdLine.c_str()); writeClient(str); } if (visible("InitString")) { wsprintf(str,"option name InitString type string default %s",initString.c_str()); writeClient(str); } if (visible("Hash")) { wsprintf(str,"option name Hash type spin default %i min 0 max 256",hashsize); writeClient(str); } if (visible("HashCommand")) { wsprintf(str,"option name HashCommand type string default %s",hashcmd.c_str()); writeClient(str); } if (visible("HashOnCommandline")) { if (hashOnCmd) writeClient("option name HashOnCommandline type check default true"); else writeClient("option name HashOnCommandline type check default false"); } if (visible("InitTime")) { wsprintf(str,"option name InitTime type spin default %i min 0 max 30",initTime); writeClient(str); } if (visible("Delay")) { wsprintf(str,"option name Delay type spin default %i min 0 max 1000",delay); writeClient(str); } if (visible("Edit")) { switch (editType) { case edit: writeClient("option name Edit type combo default edit var setboard var edit var cb-edit"); break; case cb_edit: writeClient("option name Edit type combo default cb-edit var setboard var edit var cb-edit"); break; default: writeClient("option name Edit type combo default setboard var setboard var edit var cb-edit"); break; } } if (visible("Ponder")) { if (ponder) writeClient("option name Ponder type check default true"); else writeClient("option name Ponder type check default false"); } if (visible("OwnBook")) { if (ownBook) writeClient("option name OwnBook type check default true"); else writeClient("option name OwnBook type check default false"); } if (visible("ShowThinkingMove")) { if (stat01) writeClient("option name ShowThinkingMove type check default true"); else writeClient("option name ShowThinkingMove type check default false"); } if (visible("Analyze")) { if (useAnalyze) writeClient("option name Analyze type check default true"); else writeClient("option name Analyze type check default false"); } if (visible("UseUndo")) { if (useUndo) writeClient("option name UseUndo type check default true"); else writeClient("option name UseUndo type check default false"); } if (visible("WhiteScore")) { if (whiteScore) writeClient("option name WhiteScore type check default true"); else writeClient("option name WhiteScore type check default false"); } if (visible("AddHintMove")) { if (addHint) writeClient("option name AddHintMove type check default true"); else writeClient("option name AddHintMove type check default false"); } if (visible("Logfile")) { if (log.isOpen()) writeClient("option name Logfile type check default true"); else writeClient("option name Logfile type check default false"); } if (visible("Priority")) { switch (priority) { case THREAD_PRIORITY_BELOW_NORMAL: writeClient("option name Priority type combo default BelowNormal var Normal var BelowNormal var Low"); break; case THREAD_PRIORITY_IDLE: writeClient("option name Priority type combo default Low var Normal var BelowNormal var Low"); break; default: writeClient("option name Priority type combo default Normal var Normal var BelowNormal var Low"); break; } } if (visible("RunIdle")) { if (idleClass) writeClient("option name RunIdle type check default true"); else writeClient("option name RunIdle type check default false"); } if (visible("Computer")) { if (computer) writeClient("option name Computer type check default true"); else writeClient("option name Computer type check default false"); } if (visible("LevelExtend")) { switch (levelExt) { case strict: writeClient("option name LevelExtend type combo default Strict var Progressive var Strict var Failsafe"); break; case failsafe: writeClient("option name LevelExtend type combo default Failsafe var Progressive var Strict var Failsafe"); break; default: writeClient("option name LevelExtend type combo default Progressive var Progressive var Strict var Failsafe"); break; } } if (visible("Protocol")) { wsprintf(str,"option name Protocol type spin default %i min 1 max 2",protocol); writeClient(str); } if (visible("Noise")) { wsprintf(str,"option name Noise type spin default %i min -1 max 99",noise); writeClient(str); } if (visible("Help")) writeClient("option name Help type button"); } // The reading routine for command from the GUI. bool ClientProc(const char* sz, void* data) { string line=trim(sz); string s; char str[255]; if (line.length()>0) { writeLog(line,"C>"); if (line=="uci") { wsprintf(str,"id name %s",name.c_str()); writeClient(str); wsprintf(str,"id author %s",author.c_str()); writeClient(str); sendOptions(); writeClient("uciok"); }else if (line=="isready") { // Stop and clear pre-logging. // This isn't done before if there isn't any Logfile option in the // engfile if (usePrelog==true) { usePrelog=false; prelog.clear(); } if (server.pi.hProcess==NULL) { if (!startServer()) { writeClient("Error starting engine."); if (engineStarted!=NULL) SetEvent(engineStarted); return false; } } writeClient("readyok"); }else if (line=="quit") { stopServer(); }else if (line=="stop") { sendDot=0; if (engine==thinking) { writeServer("?"); interuptNow=4; }else if (engine==analyze) { writeServer("exit"); randomMoveToClient(); }else { randomMoveToClient(); } }else if (getWord(line,1)=="setoption") { setOption(line); }else if (getWord(line,1)=="go") { go(line); }else if (getWord(line,1)=="position") { setupBoard(line); }else if (getWord(line,1)=="ponderhit") { ponderhit(); } } return true; } // The reading routine for command from the engine. ChessBoard tempboard; bool ServerProc(const char* sz, void* data) { string line=trim(sz); string s; ChessMove move; char str[80]; char c; int i,j,depth; double d; int type=0; if (line.length()>0) { writeLog(line,"S>"); // Old move syntax n ... move if (getWord(line,2)=="...") if (isdigit(line.at(0)) || (line.at(0)=='.')) { s="move "; s+=getWord(line,3); line=s; } s=getWord(line,1); if (s=="move") { eClock.stop(); interuptNow=0; sendDot=0; servergame.activeBoard(tempboard); move=tempboard.getMoveFromText(getWord(line,2)); if (!move.empty()) { servergame.move[servergame.size()]=move; line="bestmove "; line+=longMove(move,str); }else { writeLog(string("illegal move"),"S>"); } ponderMove.clear(); pvRight=false; if (useHint && (move==lastPV[0])) { tempboard.move(move); if (!lastPV[1].empty() && tempboard.move(lastPV[1])) { line+= " ponder "; line+= longMove(lastPV[1],str); ponderMove=lastPV[1]; pvRight=true; } lastPV[0].clear(); lastPV[1].clear(); } writeClient(line); hintMove.clear(); engine=waiting; stopPV=true; }else if (s=="Hint:") { servergame.activeBoard(tempboard); hintMove=tempboard.getMoveFromText(getWord(line,2)); // Use only one of these // If there is a hintMove the ponderMove given to the // GUI is wrong and we need to send the PV with the // hintMove instead. if (useHint) { if (hintMove==ponderMove) pvRight=true; else pvRight=false; } }else if ((s=="resign") && (getWord(line,2).length()==0)) { sendDot=0; interuptNow=0; engine=waiting; // What to send here? s="info string "; s+=line; writeClient(s); hintMove.clear(); if (useHint) { lastPV[0].clear(); lastPV[1].clear(); ponderMove.clear(); } bool enginewhite; if (clientgame.size()%2) enginewhite=false; else enginewhite=true; if (clientgame.board.toMove==BLACK) enginewhite=enginewhite?false:true; if (enginewhite) writeServer("result 0-1"); else writeServer("result 1-0"); resignmode=true; randomMoveToClient(); }else if (s=="1-0") { sendDot=0; interuptNow=0; engine=waiting; // What to send here? s="info string "; s+=line; writeClient(s); writeServer("result 1-0"); hintMove.clear(); if (useHint) { lastPV[0].clear(); lastPV[1].clear(); ponderMove.clear(); } resignmode=true; randomMoveToClient(); }else if (s=="0-1") { sendDot=0; interuptNow=0; engine=waiting; // What to send here? s="info string "; s+=line; writeClient(s); writeServer("result 0-1"); hintMove.clear(); if (useHint) { lastPV[0].clear(); lastPV[1].clear(); ponderMove.clear(); } resignmode=true; randomMoveToClient(); }else if (s=="1/2-1/2") { sendDot=0; interuptNow=0; engine=waiting; // What to send here? s="info string "; // Hopefully the GUI agrees to this s+=line; // message so it not happend. writeClient(s); // The engine should still send a move (except for stalemate) // hintMove.clear(); // to the GUI so no need to do anything. // lastPV[0].clear(); // lastPV[1].clear(); // ponderMove.clear(); }else if (s=="feature") { feature(line); }else if ((!stopPV) && (s=="stat01:")) { //stat01: time nodes ply mvleft mvtot mvname if (getWord(line,2).length()) { s="info"; s+=" time "; i=atoi(getWord(line,2).c_str())*10; s+=itoa(i,str,10); if (!i) i=1; d=1000.0/i; if (getWord(line,3).length()) { s+=" nodes "; i=atoi(getWord(line,3).c_str()); s+=itoa(i,str,10); s+=" nps "; i*=d; s+=itoa(i,str,10); if (getWord(line,4).length()) { s+=" depth "; i=atoi(getWord(line,4).c_str()); if (i>999) i/=1000; s+=itoa(i,str,10); if ((maxDepth>0) && (engine==thinking)) if (i>maxDepth) writeServer("?"); if (getWord(line,5).length() && getWord(line,6).length()) { s+=" currmovenumber "; i=atoi(getWord(line,6).c_str())-atoi(getWord(line,5).c_str()); s+=itoa(i,str,10); if (getWord(line,7).length()) { servergame.activeBoard(tempboard); if ((engine==thinking) || (engine==analyze)) { move=tempboard.getMoveFromText(getWord(line,7)); if (!move.empty()) { s+=" currmove "; s+=longMove(move,str); } }else if (!ponderMove.empty()) { tempboard.move(ponderMove); move=tempboard.getMoveFromText(getWord(line,7)); if (!move.empty()) { s+=" currmove "; s+=longMove(move,str); } } } } } } writeClient(s); } }else { // Check for any PV-line. if ((!stopPV) && (getWord(line,4).length()>0)) { c=getWord(line,1).at(0); if (isdigit(c)) // ply { c=getWord(line,2).at(0); if (isdigit(c)||(c=='-')||(c=='+')) // Score { c=getWord(line,3).at(0); if (isdigit(c)) // time { c=getWord(line,4).at(0); if (isdigit(c)) // nodes { if (engine==waiting) engine=pondering; servergame.activeBoard(tempboard); s="info depth "; depth=atoi(getWord(line,1).c_str()); if (depth>999) depth/=1000; s+=itoa(depth,str,10); if ((maxDepth>0) && (engine==thinking)) if (depth>maxDepth) writeServer("?"); s+=" score cp "; i=atoi(getWord(line,2).c_str()); if (whiteScore) { if ((engine==thinking)||(engine==analyze)) { if (tempboard.toMove==BLACK) i*=-1; }else { if (hintMove.empty()) { if (tempboard.toMove==BLACK) i*=-1; }else { if (tempboard.toMove==WHITE) i*=-1; } } } s+=itoa(i,str,10); s+=" time "; i=atoi(getWord(line,3).c_str())*10; s+=itoa(i,str,10); if (!i) i=1; d=1000.0/i; s+=" nodes "; i=atoi(getWord(line,4).c_str()); s+=itoa(i,str,10); s+=" nps "; i*=d; s+=itoa(i,str,10); if (((depth>=noise) && ((engine==thinking) || pvRight || !useHint)) || (engine==analyze)) { i=5; j=i; bool pvSent=false; // If pondering the pv start with the move after the hintmove if ((engine==pondering) && !hintMove.empty()) { tempboard.move(hintMove); if (addHint) { s+=" pv "; s+=longMove(hintMove,str); pvSent=true; } j++; } while (getWord(line,i).length()) { move=tempboard.getMoveFromText(getWord(line,i)); // Update the lastPV for guessing on a pondermove // on the next move. // Add the move to pv-line if (!move.empty()) { // Collect info for next pondermove. if (useHint && (engine!=pondering)) { if (j==5) lastPV[0]=move; else if (j==6) lastPV[1]=move; } tempboard.move(move); if ((engine==thinking) || (engine==analyze) || addHint || (j>5)) { if (!pvSent) { s+=" pv "; pvSent=true; }else { s+=' '; } s+=longMove(move,str); } j++; } i++; } } writeClient(s); if (sendDot==1) sendDot=2; } } } } } } } return true; } int main(int argc, char* argv[]) { string input; DWORD timeout=5000; int i; usePrelog=true; InitializeCriticalSection(&logCS); InitializeCriticalSection(&serverCS); InitializeCriticalSection(&clientCS); watch.start(); writeLog(string(PROGRAM_NAME),"** Adapter:"); #ifdef _DEBUG writeClient("** Critical section initialized."); writeClient("** Starting clock for timestamp."); #endif // Set and save startup defaults for priorities. priorityClass=GetPriorityClass(GetCurrentProcess()); priority=GetThreadPriority(GetCurrentThread()); if (priorityClass!=NORMAL_PRIORITY_CLASS) SetPriorityClass(GetCurrentProcess(),NORMAL_PRIORITY_CLASS); if (priority!=THREAD_PRIORITY_NORMAL) SetThreadPriority(GetCurrentProcess(),THREAD_PRIORITY_NORMAL); /* input=""; i=0; while (i