From: H.G. Muller Date: Mon, 4 Oct 2010 09:50:38 +0000 (+0200) Subject: Enhance multi-session TC clock handling X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=01768d1677ff891d503bbfa250b09d373bfa7422;p=xboard.git Enhance multi-session TC clock handling Some new TC types are introduced: Bronstein and free sessions. The former is implemented by letting an exclamation point '!' in front of an increment (in the -tc string) indicate that the increment is limited to the time actually used on the previous move. The latter is an interval of given duraton in which you can do any number of moves (even zero), before the next session starts. This is needed for implementing Shogi byoyomi-type TC, as a first session, followed by a session of fixed-time per move. The latter can now be specified with the -tc argument as a degenerate case of Bronstein, where the time on the clock is <= the increment (so that it is never possible to think longer than the increment). A TC of 5 min + 10 sec byoyomi can then be indicated as 300:10+!10. The sessions are now separated by ':' in stead of '+', and all times in the TC string are converted to seconds first (in ParseTimeControl), so the fullTCstring can now be directly used in a PGN tag. Note that this patch only addresses clock handling; the engine will not be informed yet if the TC type or parameters change at the start of new session! --- diff --git a/backend.c b/backend.c index 2a6e2e6..e12be26 100644 --- a/backend.c +++ b/backend.c @@ -440,9 +440,10 @@ TimeMark lastNodeCountTime; long lastNodeCount=0; int have_sent_ICS_logon = 0; int movesPerSession; -long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement; +int suddenDeath, whiteStartMove, blackStartMove; /* [HGM] for implementation of 'any per time' sessions, as in first part of byoyomi TC */ +long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement, lastWhite, lastBlack; long timeControl_2; /* [AS] Allow separate time controls */ -char *fullTimeControlString = NULL; /* [HGM] secondary TC: merge of MPS, TC and inc */ +char *fullTimeControlString = NULL, *nextSession, *whiteTC, *blackTC; /* [HGM] secondary TC: merge of MPS, TC and inc */ long timeRemaining[2][MAX_MOVES]; int matchGame = 0; TimeMark programStartTime; @@ -970,46 +971,55 @@ int NextTimeControlFromString( char ** str, long * value ) return result; } -int NextSessionFromString( char ** str, int *moves, long * tc, long *inc) -{ /* [HGM] routine added to read '+moves/time' for secondary time control */ - int result = -1; long temp, temp2; +int NextSessionFromString( char ** str, int *moves, long * tc, long *inc, int *incType) +{ /* [HGM] routine added to read '+moves/time' for secondary time control. */ + int result = -1, type = 0; long temp, temp2; - if(**str != '+') return -1; // old params remain in force! + if(**str != ':') return -1; // old params remain in force! (*str)++; - if( NextTimeControlFromString( str, &temp ) ) return -1; + if(**str == '*') type = *(*str)++, temp = 0; // sandclock TC + if( NextIntegerFromString( str, &temp ) ) return -1; + if(type) { *moves = 0; *tc = temp * 500; *inc = temp * 1000; *incType = '*'; return 0; } if(**str != '/') { /* time only: incremental or sudden-death time control */ if(**str == '+') { /* increment follows; read it */ (*str)++; + if(**str == '!') type = *(*str)++; // Bronstein TC if(result = NextIntegerFromString( str, &temp2)) return -1; *inc = temp2 * 1000; } else *inc = 0; - *moves = 0; *tc = temp * 1000; + *moves = 0; *tc = temp * 1000; *incType = type; return 0; - } else if(temp % 60 != 0) return -1; /* moves was given as min:sec */ + } (*str)++; /* classical time control */ - result = NextTimeControlFromString( str, &temp2); + result = NextIntegerFromString( str, &temp2); // NOTE: already converted to seconds by ParseTimeControl() + if(result == 0) { - *moves = temp/60; + *moves = temp; *tc = temp2 * 1000; *inc = 0; + *incType = type; } return result; } -int GetTimeQuota(int movenr) +int GetTimeQuota(int movenr, int lastUsed, char *tcString) { /* [HGM] get time to add from the multi-session time-control string */ - int moves=1; /* kludge to force reading of first session */ + int incType, moves=1; /* kludge to force reading of first session */ long time, increment; - char *s = fullTimeControlString; + char *s = tcString; - if(appData.debugMode) fprintf(debugFP, "TC string = '%s'\n", fullTimeControlString); + if(!*s) return 0; // empty TC string means we ran out of the last sudden-death version + if(appData.debugMode) fprintf(debugFP, "TC string = '%s'\n", tcString); do { - if(moves) NextSessionFromString(&s, &moves, &time, &increment); + if(moves) NextSessionFromString(&s, &moves, &time, &increment, &incType); + nextSession = s; suddenDeath = moves == 0 && increment == 0; if(appData.debugMode) fprintf(debugFP, "mps=%d tc=%d inc=%d\n", moves, (int) time, (int) increment); if(movenr == -1) return time; /* last move before new session */ + if(incType == '*') increment = 0; else // for sandclock, time is added while not thinking + if(incType == '!' && lastUsed < increment) increment = lastUsed; if(!moves) return increment; /* current session is incremental */ if(movenr >= 0) movenr -= moves; /* we already finished this session */ } while(movenr >= -1); /* try again for next session */ @@ -1025,19 +1035,22 @@ ParseTimeControl(tc, ti, mps) { long tc1; long tc2; - char buf[MSG_SIZ]; + char buf[MSG_SIZ], buf2[MSG_SIZ], *mytc = tc; + int min, sec=0; if(ti >= 0 && !strchr(tc, '+') && !strchr(tc, '/') ) mps = 0; + if(!strchr(tc, '+') && !strchr(tc, '/') && sscanf(tc, "%d:%d", &min, &sec) >= 1) + sprintf(mytc=buf2, "%d", 60*min+sec); // convert 'classical' min:sec tc string to seconds if(ti > 0) { if(mps) - sprintf(buf, "+%d/%s+%d", mps, tc, ti); - else sprintf(buf, "+%s+%d", tc, ti); + sprintf(buf, ":%d/%s+%d", mps, mytc, ti); + else sprintf(buf, ":%s+%d", mytc, ti); } else { if(mps) - sprintf(buf, "+%d/%s", mps, tc); - else sprintf(buf, "+%s", tc); + sprintf(buf, ":%d/%s", mps, mytc); + else sprintf(buf, ":%s", mytc); } - fullTimeControlString = StrSave(buf); + fullTimeControlString = StrSave(buf); // this should now be in PGN format if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) { return FALSE; @@ -14058,15 +14071,20 @@ CheckTimeControl() /* * add time to clocks when time control is achieved ([HGM] now also used for increment) */ - if ( !WhiteOnMove(forwardMostMove) ) + if ( !WhiteOnMove(forwardMostMove) ) { /* White made time control */ - whiteTimeRemaining += GetTimeQuota((forwardMostMove-1)/2) + lastWhite -= whiteTimeRemaining; // [HGM] contains start time, socalculate thinking time + whiteTimeRemaining += GetTimeQuota((forwardMostMove-whiteStartMove-1)/2, lastWhite, whiteTC) /* [HGM] time odds: correct new time quota for time odds! */ / WhitePlayer()->timeOdds; - else + lastBlack = blackTimeRemaining; // [HGM] leave absolute time (after quota), so next switch we can us it to calculate thinking time + } else { + lastBlack -= blackTimeRemaining; /* Black made time control */ - blackTimeRemaining += GetTimeQuota((forwardMostMove-1)/2) + blackTimeRemaining += GetTimeQuota((forwardMostMove-blackStartMove-1)/2, lastBlack, blackTC) / WhitePlayer()->other->timeOdds; + lastWhite = whiteTimeRemaining; + } } void @@ -14182,13 +14200,15 @@ ResetClocks() whiteTimeRemaining = 1000*searchTime / WhitePlayer()->timeOdds; blackTimeRemaining = 1000*searchTime / WhitePlayer()->other->timeOdds; } else { /* [HGM] correct new time quote for time odds */ - whiteTimeRemaining = GetTimeQuota(-1) / WhitePlayer()->timeOdds; - blackTimeRemaining = GetTimeQuota(-1) / WhitePlayer()->other->timeOdds; + whiteTC = blackTC = fullTimeControlString; + whiteTimeRemaining = GetTimeQuota(-1, 0, whiteTC) / WhitePlayer()->timeOdds; + blackTimeRemaining = GetTimeQuota(-1, 0, blackTC) / WhitePlayer()->other->timeOdds; } if (whiteFlag || blackFlag) { DisplayTitle(""); whiteFlag = blackFlag = FALSE; } + lastWhite = lastBlack = whiteStartMove = blackStartMove = 0; DisplayBothClocks(); } @@ -14216,15 +14236,28 @@ DecrementClocks() if (WhiteOnMove(forwardMostMove)) { if(whiteNPS >= 0) lastTickLength = 0; timeRemaining = whiteTimeRemaining -= lastTickLength; + if(timeRemaining < 0) { + GetTimeQuota((forwardMostMove-whiteStartMove-1)/2, 0, whiteTC); // sets suddenDeath & nextSession; + if(suddenDeath) { // [HGM] if we run out of a non-last incremental session, go to the next + whiteStartMove = forwardMostMove; whiteTC = nextSession; + lastWhite= timeRemaining = whiteTimeRemaining += GetTimeQuota(-1, 0, whiteTC); + } + } DisplayWhiteClock(whiteTimeRemaining - fudge, WhiteOnMove(currentMove < forwardMostMove ? currentMove : forwardMostMove)); } else { if(blackNPS >= 0) lastTickLength = 0; timeRemaining = blackTimeRemaining -= lastTickLength; + if(timeRemaining < 0) { // [HGM] if we run out of a non-last incremental session, go to the next + GetTimeQuota((forwardMostMove-blackStartMove-1)/2, 0, blackTC); + if(suddenDeath) { + blackStartMove = forwardMostMove; + lastBlack = timeRemaining = blackTimeRemaining += GetTimeQuota(-1, 0, blackTC=nextSession); + } + } DisplayBlackClock(blackTimeRemaining - fudge, !WhiteOnMove(currentMove < forwardMostMove ? currentMove : forwardMostMove)); } - if (CheckFlags()) return; tickStartTM = now;