X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=gnushogi%2Fcommondsp.c;h=2d4fed577ec9bde7876c1d286caa66cce646b6f0;hb=da2a684903fc7a96d47b43dc2feeecde53bd1aa6;hp=90a3ca0d72f42c1dc02a4676630864b42ca97250;hpb=6ec68dbedbcb4225cfe8e29e32675cb0fa486b34;p=gnushogi.git diff --git a/gnushogi/commondsp.c b/gnushogi/commondsp.c index 90a3ca0..2d4fed5 100644 --- a/gnushogi/commondsp.c +++ b/gnushogi/commondsp.c @@ -54,6 +54,7 @@ int mycnt1, mycnt2; static char *InPtr; struct display *dsp = &raw_display; +short xboard = false; #if defined(BOOKTEST) @@ -181,7 +182,7 @@ algbr(short f, short t, short flag) short piece = flag & pmask; mvstr[0][0] = pxx[piece]; - mvstr[0][1] = '*'; + mvstr[0][1] = xboard ? '@' : '*'; mvstr[0][2] = COL_NAME(column(t)); mvstr[0][3] = ROW_NAME(row(t)); mvstr[0][4] = '\0'; @@ -284,6 +285,7 @@ VerifyMove(char *s, VerifyMove_mode iop, unsigned short *mv) MoveList(opponent, 2, -1, true); generate_move_flags = false; pnt = TrPnt[2]; + if(s[4] == '=') s[4] = '\0'; /* deferral is implied */ while (pnt < TrPnt[3]) { @@ -310,7 +312,7 @@ VerifyMove(char *s, VerifyMove_mode iop, unsigned short *mv) if (SqAttacked(PieceList[opponent][0], computer, &blocked)) { UnmakeMove(opponent, &xnode, &tempb, &tempc, &tempsf, &tempst); - dsp->AlwaysShowMessage("Illegal move (in check) %s", s); + dsp->AlwaysShowMessage("Illegal move (in check): %s", s); return false; } else @@ -352,7 +354,7 @@ VerifyMove(char *s, VerifyMove_mode iop, unsigned short *mv) } } - dsp->AlwaysShowMessage("Illegal move (no match) %s", s); + dsp->AlwaysShowMessage("Illegal move (no match): %s", s); if (!XSHOGI && (cnt > 1)) { @@ -384,17 +386,17 @@ parser(char *f, short *fpiece) if (f[1] == '*' || f[1] == '\'') { - c2 = COL_NAME(f[2]); - r2 = ROW_NAME(f[3]); + c2 = COL_NUM(f[2]); + r2 = ROW_NUM(f[3]); return ((NO_SQUARES + *fpiece) << 8) | locn(r2, c2); } else { - c1 = COL_NAME(f[1]); - r1 = ROW_NAME(f[2]); - c2 = COL_NAME(f[3]); - r2 = ROW_NAME(f[4]); + c1 = COL_NUM(f[1]); + r1 = ROW_NUM(f[2]); + c2 = COL_NUM(f[3]); + r2 = ROW_NUM(f[4]); p = (f[5] == '+') ? 0x80 : 0; return (locn(r1, c1) << 8) | locn(r2, c2) | p; @@ -1038,8 +1040,10 @@ BookSave(void) RequestInputString(fname, sizeof(fname)-1); } - if (fname[0] == '\0') + if (fname[0] == '\0') { + dsp->AlwaysShowMessage("aborting book save"); return; + } if ((fd = fopen(fname, "a")) != NULL) { @@ -1419,16 +1423,15 @@ TestPSpeed(short(*f) (short side), unsigned j) static void SetOppTime(char *time) { - int m, t, sec; + int m, t; - sec = 0; t = (int)strtol(time, &time, 10); if (*time == ':') { time++; /* FIXME: sec is parsed but ignored */ - sec = (int)strtol(time, &time, 10); + (void)strtol(time, &time, 10); } m = (int)strtol(time, &time, 10); @@ -1452,16 +1455,15 @@ SetOppTime(char *time) static void SetMachineTime(char *time) { - int m, t, sec; + int m, t; - sec = 0; t = (int)strtol(time, &time, 10); if (*time == ':') { time++; /* FIXME: sec is parsed but ignored */ - sec = (int)strtol(time, &time, 10); + (void)strtol(time, &time, 10); } m = (int)strtol(time, &time, 10); @@ -1481,8 +1483,122 @@ SetMachineTime(char *time) } } +/* + * Set up a board position. Pieces are entered by typing the piece followed + * by the location. For example, Nf3 will place a knight on square f3. + */ +static void +ReadFEN(char *fen) +{ + short a = white, r, c, sq, i, error = 0; + char s[80]; + + flag.regularstart = true; + Book = BOOKFAIL; + + for (sq = 0; sq < NO_SQUARES; sq++) + { + board[sq] = no_piece; + color[sq] = neutral; + } + + ClearCaptured(); + + /* read board */ + r = NO_ROWS-1; c = 0; + while(*fen) + { + if (isdigit(*fen)) + { + c += *fen++ - '0'; /* assumes single digit! */ + } + else if (*fen == '/') + { /* next rank */ + if (c != NO_COLS) error++; + c = 0; fen++; + if (--r < 0) break; + } + else + { + int promo = 0, found = 0; + if (*fen == '+') + { + promo++; fen++; + } + + if (!isalpha(*fen)) break; + + for (i = no_piece; i <= king; i++) + { + if ((*fen == pxx[i]) || (*fen == qxx[i])) + { + sq = locn(r, c); + color[sq] = (islower(*fen) ? white : black); + if (promo) + board[sq] = promoted[i]; + else + board[sq] = i; + + found = 1; + break; + } + } + + if (!found) error++; + c++; fen++; + } + } + if(r || c != NO_COLS) error++; + + while (*fen == ' ') fen++; + + /* read holdings */ + if(!strncmp(fen, "[-]", 3)) fen += 3; /* empty holdings */ + else if(*fen == '[') + { + fen++; + while(isalpha(*fen)) + { + int found = 0; + for (i = pawn; i <= king; i++) + { + if ((*fen == pxx[i]) || (*fen == qxx[i])) + { + Captured[islower(*fen) ? white : black][i]++; + found = 1; + break; + } + } + if (!found) error++; + fen++; + } + if(*fen == ']') fen++; else error++; + } + + while (*fen == ' ') fen++; + + if (*fen == 'w') + a = black; + else if (*fen == 'b') + a = white; + else + error++; + + if (error) printf("tellusererror bad FEN\n"); + + for (sq = 0; sq < NO_SQUARES; sq++) + Mvboard[sq] = ((board[sq] != Stboard[sq]) ? 10 : 0); + + computer = otherside[a]; + opponent = a; + flag.force = true; + GameCnt = 0; + Game50 = 1; + ZeroRPT(); + Sdepth = 0; + InitializeStats(); +} -/* FIXME! This is truly the function from hell! */ /* * Process the user's command. If easy mode is OFF (the computer is thinking @@ -1492,24 +1608,16 @@ SetMachineTime(char *time) * the hint move, then set Sdepth to zero. */ +static char ponderString[20]; + void -InputCommand(char *command) +PonderOnHintMove(void) { +#ifdef QUIETBACKGROUND short have_shown_prompt = false; - short ok, done, is_move = false; - unsigned short mv; - char s[80], sx[80]; - - ok = flag.quit = done = false; - player = opponent; - -#if ttblsz - if (TTadd > ttbllimit) - ZeroTTable(); #endif + unsigned short mv; - if ((hint > 0) && !flag.easy && !flag.force) - { /* * A hint move for the player is available. Compute a move for the * opponent in background mode assuming that the hint move will be @@ -1519,13 +1627,13 @@ InputCommand(char *command) ft = time0; /* Save reference time for the player. */ fflush(stdout); algbr((short) hint >> 8, (short) hint & 0xff, false); - strcpy(s, mvstr[0]); + strcpy(ponderString, mvstr[0]); if (flag.post) dsp->GiveHint(); /* do the hint move */ - if (VerifyMove(s, VERIFY_AND_TRY_MODE, &mv)) + if (VerifyMove(ponderString, VERIFY_AND_TRY_MODE, &mv)) { Sdepth = 0; @@ -1560,43 +1668,36 @@ InputCommand(char *command) } #endif - /* undo the hint and carry on */ - VerifyMove(s, UNMAKE_MODE, &mv); + if (strcmp(ponderString, "hit")) + { /* undo the hint and carry on */ + VerifyMove(ponderString, UNMAKE_MODE, &mv); + } + else + { /* otherwise SelectMove will have played the computer's reply */ + /* update ponder-move stats, which was skipped in TRY_MODE */ + GameList[GameCnt-1].depth = GameList[GameCnt].score = 0; + GameList[GameCnt-1].nodes = 0; + ElapsedTime(COMPUTE_AND_INIT_MODE); + GameList[GameCnt-1].time = (short) (et + 50)/100; /* FIXME: this is wrong */ + + RenewTimeControl(computer, 0); /* add time for next session */ + } Sdepth = 0; } time0 = ft; /* Restore reference time for the player. */ - } - - while(!(ok || flag.quit || done)) - { - player = opponent; - -#ifdef QUIETBACKGROUND - if (!have_shown_prompt) - { -#endif /* QUIETBACKGROUND */ - - dsp->ShowPrompt(); - -#ifdef QUIETBACKGROUND - } - - have_shown_prompt = false; -#endif /* QUIETBACKGROUND */ - - if (command == NULL) { - int eof = dsp->GetString(sx); - if (eof) - dsp->ExitShogi(); - } else { - strcpy(sx, command); - done = true; - } + ponderString[0] = '\0'; +} - /* extract first word */ - if (sscanf(sx, "%s", s) < 1) - continue; +/* + * Recognize the command s from input line sx, and perform the action it specifies. + * Returns whether the command could cause it to be out turn to move. + */ +int +ParseAndExecuteCommand(char *s, char *sx) +{ + short ok; + unsigned short mv; if (strcmp(s, "bd") == 0) /* bd -- display board */ { @@ -1614,19 +1715,79 @@ InputCommand(char *command) } else if (strcmp(s, "post") == 0) { - flag.post = !flag.post; + flag.post = (xboard ? 1 : !flag.post); + } + else if (strcmp(s, "nopost") == 0) + { + flag.post = 0; } - else if (strcmp(s, "alg") == 0) +#ifdef MINISHOGI + else if (strcmp(s, "variant") == 0) + { /* only variant we play is minishogi */ + printf("setup (P.BR.S...G.+.++.+Kp.br.s...g.+.++.+k) 5x5+5_shogi rbsgk/4p/5/P4/KGSBR [-] w 0 1\n"); + } +#endif + else if (strcmp(s, "alg") == 0 || + strcmp(s, "accepted") == 0 || strcmp(s, "rejected") == 0 || + strcmp(s, "variant") == 0 || strcmp(s, "computer") == 0) { /* noop */ ; } - else if ((strcmp(s, "quit") == 0) - || (strcmp(s, "exit") == 0)) + else if ((strcmp(s, "quit") == 0) || + (strcmp(s, "exit") == 0) && !xboard) { flag.quit = true; } - else if ((strcmp(s, "set") == 0) - || (strcmp(s, "edit") == 0)) + else if (strcmp(s, "xboard") == 0) + { + xboard = true; + strcpy(ColorStr[0], "White"); + strcpy(ColorStr[1], "Black"); + } + else if (strcmp(s, "protover") == 0) + { + printf("feature option=\"tsume -check 0\"\n"); + printf("feature option=\"contempt -spin %d -1000 1000\"\n", contempt); + printf("feature option=\"Hash-file search depth -spin %d 0 100\"\n", HashDepth); + printf("feature option=\"Hash-file move number -spin %d 0 100\"\n", HashMoveLimit); + printf("feature myname=\"GNU %s %s\" ", +#ifdef MINISHOGI + "MiniShogi", +#else + "Shogi", +#endif + PACKAGE_VERSION + ); + printf("variants=\"%s\" ", +#ifdef MINISHOGI + "5x5+5_shogi,minishogi" +#else + "shogi" +#endif + ); + printf("debug=1 setboard=1 sigint=0 memory=1 usermove=1 done=1\n"); + } + else if (strcmp(s, ".") == 0) + { // periodic update request of analysis info: send stat01 info + ElapsedTime(2); + algbr((short)(currentMove >> 8), (short)(currentMove & 0xFF), 0); + printf("stat01: %4ld %8ld %2d %2d %2d %s\n", + et, NodeCnt, Sdepth, movesLeft, TrPnt[2]-TrPnt[1], mvstr[0]); + fflush(stdout); + if (!root) return false; /* signal no abort needed */ + } + else if (strcmp(s, "exit") == 0) + { + flag.analyze = false; + flag.force = true; + } + else if (strcmp(s, "analyze") == 0) + { + flag.analyze = true; + flag.force = true; + } + else if ((strcmp(s, "set") == 0) || + (strcmp(s, "edit") == 0)) { dsp->EditBoard(); } @@ -1638,6 +1799,17 @@ InputCommand(char *command) { ok = true; } +#if ttblsz + else if (strcmp(s, "memory") == 0) + { + unsigned int mem, size, t = 1; + sscanf(sx, "memory %d", &mem); + if(mem > 2048) mem = 2048; /* prevent integer overflow for > 2GB hash */ + size = (mem << 20) / sizeof(struct hashentry) - rehash; + while(t <= size/4) t <<= 1; + AllocateTT(t); + } +#endif else if (strcmp(s, "go") == 0) { ok = true; @@ -1684,6 +1856,10 @@ InputCommand(char *command) NewGame(); dsp->UpdateDisplay(0, 0, 1, 0); } + else if (strcmp(s, "setboard") == 0) + { + ReadFEN(sx + 9); + } else if (strcmp(s, "list") == 0) { ListGame(); @@ -1712,7 +1888,8 @@ InputCommand(char *command) { SetMachineTime(sx + strlen("time")); } - else if (strcmp(s, "otime") == 0) + else if ((strcmp(s, "otime") == 0) || + (xboard && (strcmp(s, "otim")) == 0)) { SetOppTime(sx + strlen("otime")); } @@ -1755,8 +1932,9 @@ InputCommand(char *command) flag.force = false; Sdepth = 0; ok = true; + dsp->UpdateDisplay(0, 0, 1, 0); } - else if (strcmp(s, "black") == 0) + else if (xboard ? strcmp(s, "white") == 0 : strcmp(s, "black") == 0) { computer = white; opponent = black; @@ -1768,7 +1946,7 @@ InputCommand(char *command) * ok = true; don't automatically start with black command */ } - else if (strcmp(s, "white") == 0) + else if (xboard ? strcmp(s, "black") == 0 : strcmp(s, "white") == 0) { computer = black; opponent = white; @@ -1803,12 +1981,12 @@ InputCommand(char *command) BookSave(); } #ifdef EASY_OPENINGS - else if ((strcmp(s, "?") == 0) - || (strcmp(s, "!") == 0) - || (strcmp(s, "~") == 0)) + else if ((strcmp(s, "?") == 0) || + (strcmp(s, "!") == 0) || + (strcmp(s, "~") == 0)) #else - else if ((strcmp(s, "?") == 0) - || (strcmp(s, "!") == 0)) + else if ((strcmp(s, "?") == 0) || + (strcmp(s, "!") == 0)) #endif { FlagMove(*s); @@ -1845,6 +2023,14 @@ InputCommand(char *command) { flag.easy = !flag.easy; } + else if (strcmp(s, "option") == 0) + { + sscanf(sx, "option tsume=%hd", &flag.tsume) || + sscanf(sx, "option hash=%hd", &flag.hash) || + sscanf(sx, "option Hash-file search depth=%hd", &HashDepth) || + sscanf(sx, "option Hash-file move number=%hd", &HashMoveLimit) || + sscanf(sx, "option contempt=%hd", &contempt); + } else if (strcmp(s, "tsume") == 0) { flag.tsume = !flag.tsume; @@ -1929,6 +2115,9 @@ InputCommand(char *command) } else { + if (strcmp(s, "usermove") == 0) + sscanf(sx + 9, "%s", s); + if (flag.mate) { ok = true; @@ -1946,31 +2135,134 @@ InputCommand(char *command) flag.mate = true; } - else + else if (XSHOGI) { - is_move = true; - } + /* add remaining time in milliseconds for xshogi */ + printf("%d. %s %ld\n", + ++mycnt2, s, TimeControl.clock[player] * 10); + } } Sdepth = 0; } + + return ok; +} + +/* + * Read commands from input, and execute them, until it becomes our turn to move. + * When called during a background search (root = false) it just backlogs the + * input command without executing it, and returns immediately. Unless the command + * was the move on which the search was pondering. In that case we turn the ongoing + * search into a foreground search. To judge this, it is also necessary to process + * the 'time' and 'otim' commands that preceed the move. The '.' command is also + * always processed, to prevent it from aborting an analysis search. + * The time spent waiting for input can be filled with background searches for + * pondering or analysis. (In !root mode input is guaranteed to be pending already!) + */ +int +InputCommand(int root) +{ +#ifdef QUIETBACKGROUND + short have_shown_prompt = false; +#endif + short ok; + char s[200], sx[200], s2[200]; + static char backlog[200]; + + ok = flag.quit = false; + player = opponent; + +#if ttblsz + /* CHECKME: should this also be done in the following while loop? */ + if (TTadd > ttbllimit) + ZeroTTable(); +#endif + + while ((hint > 0) && !flag.easy && !flag.force && !backlog[0] && root) + { + /* + * A hint move for the player is available. Compute a move for the + * opponent in background mode assuming that the hint move will be + * selected by the player. + * Terminate this search on input, which will then be saved in backlog[]. + * Unless the input was the hint move ('ponder hit'). Then that move will + * be played (after the search times out) in addition to the hint. There + * will then be no backlog, and we start pondering on the new hint move. + */ + + PonderOnHintMove(); + } + + while(!(ok || flag.quit)) + { /* process input commands until our it becomes our turn to move */ + player = opponent; + + /* in analysis mode we do a background search while waiting for input */ + if (flag.analyze && !backlog[0] && root) { + SelectMove(opponent, BACKGROUND_MODE); + } + +#ifdef QUIETBACKGROUND + if (!have_shown_prompt) + { +#endif /* QUIETBACKGROUND */ + + dsp->ShowPrompt(); + +#ifdef QUIETBACKGROUND + } + + have_shown_prompt = false; +#endif /* QUIETBACKGROUND */ + + if (!backlog[0]) { /* read new input line */ + int eof = dsp->GetString(sx); + if (eof) + dsp->ExitShogi(); + } else { /* or use backlogged input line */ + strcpy(sx, backlog); + backlog[0]= '\0'; /* make sure no backlog is left */ + } + + /* extract first word */ + if (sscanf(sx, "%s %s", s, s2) < 1) + continue; + + if (!root && (strcmp(s, "usermove") == 0) + && (strcmp(s2, ponderString) == 0)) + { /* ponder hit; switch to normal search */ + background = false; + hint = 0; + if (TCflag) + { /* account opponent time and moves */ + TimeControl.clock[opponent] -= et; + timeopp[oppptr] = et; + if (--TimeControl.moves[opponent] == 0) + TimeControl.moves[opponent] = TCmoves; /* assumes uni-TC! */ + } + SetResponseTime(computer); + strcpy(ponderString, "hit"); + return false; /* no search abort */ + } + + if (!root && strcmp(s, ".") && strcmp(s, "time") && strcmp(s, "otim")) + { /* during search most commands can only be done after abort */ + strcpy(backlog, sx); /* backlog the command */ + return true; /* and order search abort */ + } + + ok = ParseAndExecuteCommand(s, sx); /* returns whether turn changed */ } ElapsedTime(COMPUTE_AND_INIT_MODE); + /* kludge alert: change the side we play to prevent starting a search */ if (flag.force) { computer = opponent; opponent = computer ^ 1; } - if (XSHOGI) - { - /* add remaining time in milliseconds for xshogi */ - if (is_move) - { - printf("%d. %s %ld\n", - ++mycnt2, s, TimeControl.clock[player] * 10); - } - } + return true; }