4 * Curses interface for GNU Shogi
6 * ----------------------------------------------------------------------
7 * Copyright (c) 1993, 1994, 1995 Matthias Mutz
8 * Copyright (c) 1999 Michael Vanier and the Free Software Foundation
10 * GNU SHOGI is based on GNU CHESS
12 * Copyright (c) 1988, 1989, 1990 John Stanback
13 * Copyright (c) 1992 Free Software Foundation
15 * This file is part of GNU SHOGI.
17 * GNU Shogi is free software; you can redistribute it and/or modify it
18 * under the terms of the GNU General Public License as published by the
19 * Free Software Foundation; either version 3 of the License,
20 * or (at your option) any later version.
22 * GNU Shogi is distributed in the hope that it will be useful, but WITHOUT
23 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
24 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 * You should have received a copy of the GNU General Public License along
28 * with GNU Shogi; see the file COPYING. If not, see
29 * <http://www.gnu.org/licenses/>.
30 * ----------------------------------------------------------------------
38 #include <sys/param.h>
39 #include <sys/types.h>
44 #include "cursesdsp.h"
51 /* Definition of FIONREAD */
52 #include <sys/filio.h>
56 /* Definition of errno(). */
60 #define FLUSH_SCANW fflush(stdout), scanw
66 #define VIR_C(s) ((flag.reverse) ? (NO_COLS - 1 - column(s)) : column(s))
67 #define VIR_R(s) ((flag.reverse) ? (NO_ROWS - 1 - row(s)) : row(s))
69 unsigned short MV[MAXDEPTH];
73 /* Forward declarations. */
74 /* FIXME: change this name, puh-leeze! */
76 static void UpdateCatched(void);
77 static void DrawPiece(short sq);
78 static void ShowScore(short score);
80 /****************************************
81 * Trivial output functions.
82 ****************************************/
93 Curses_ClearScreen(void)
109 gotoXY(short x, short y)
116 Curses_ShowCurrentMove(short pnt, short f, short t)
120 printw("(%2d) %5s ", pnt, mvstr[0]);
125 Curses_ShowDepth(char ch)
128 printw(CP[53], Sdepth, ch); /* Depth = %d%c */
134 Curses_ShowGameType(void)
139 printw("%c vs. %c", GameType[black], GameType[white]);
148 printw(CP[69], version, patchlevel);
153 Curses_ShowLine(unsigned short *bstline)
159 Curses_ShowMessage(char *s)
168 Curses_AlwaysShowMessage(const char *format, ...)
170 static char buffer[60];
172 va_start(ap, format);
173 vsnprintf(buffer, sizeof(buffer), format, ap);
174 Curses_ShowMessage(buffer);
180 Curses_Printf(const char *format, ...)
182 static char buffer[60];
184 va_start(ap, format);
185 vsnprintf(buffer, sizeof(buffer), format, ap);
186 printw("%s", buffer);
192 Curses_doRequestInputString(const char* fmt, char* buffer)
194 FLUSH_SCANW(fmt, buffer);
199 Curses_GetString(char* sx)
202 return (getstr(sx) == ERR);
207 Curses_ShowNodeCnt(long NodeCnt)
210 /* printw(CP[90], NodeCnt, (et > 100) ? NodeCnt / (et / 100) : 0); */
211 printw("n = %ld n/s = %ld",
212 NodeCnt, (et > 100) ? NodeCnt / (et / 100) : 0);
218 Curses_ShowPatternCount(short side, short n)
222 gotoXY(TAB + 10 + 3 * side, 20); /* CHECKME */
235 gotoXY(5, ((flag.reverse) ? (5 + 2*NO_ROWS) : 2));
236 printw("%s", (computer == white) ? CP[218] : CP[74]);
237 gotoXY(5, ((flag.reverse) ? 2 : (5 + 2*NO_ROWS)));
238 printw("%s", (computer == black) ? CP[218] : CP[74]);
243 Curses_ShowPrompt(void)
245 Curses_ShowSidetoMove();
247 printw(CP[121]); /* Your move is? */
253 Curses_ShowResponseTime(void)
259 /* printw("RT = %ld TCC = %d TCL = %ld EX = %ld ET = %ld TO = %d",
260 ResponseTime, TCC, TCleft, ExtraTime, et, flag.timeout); */
261 printw("%ld, %d, %ld, %ld, %ld, %d",
262 ResponseTime, TCC, TCleft, ExtraTime, et, flag.timeout);
269 Curses_ShowResults(short score, unsigned short *bstline, char ch)
271 unsigned char d, ply;
275 Curses_ShowDepth(ch);
279 for (ply = 1; bstline[ply] > 0; ply++)
287 algbr((short) bstline[ply] >> 8,
288 (short) bstline[ply] & 0xFF, false);
289 printw("%5s ", mvstr[0]);
304 ShowScore(short score)
307 printw(CP[104], score);
313 Curses_ShowSidetoMove(void)
316 printw("%2d: %s", 1 + GameCnt / 2, ColorStr[player]);
322 Curses_ShowStage(void)
325 printw("Stage= %2d%c B= %2d W= %2d",
326 stage, flag.tsume?'T':' ', balance[black], balance[white]);
330 /****************************************
331 * End of trivial output routines.
332 ****************************************/
335 Curses_Initialize(void)
337 signal(SIGINT, Curses_Die);
338 signal(SIGQUIT, Curses_Die);
345 Curses_ExitShogi(void)
365 signal(SIGINT, SIG_IGN);
366 signal(SIGQUIT, SIG_IGN);
368 Curses_ShowMessage(CP[31]); /* Abort? */
369 FLUSH_SCANW("%s", s);
371 if (strcmp(s, CP[210]) == 0) /* yes */
374 signal(SIGINT, Curses_Die);
375 signal(SIGQUIT, Curses_Die);
380 Curses_TerminateSearch(int sig)
382 signal(SIGINT, SIG_IGN);
383 signal(SIGQUIT, SIG_IGN);
386 flag.musttimeout = true;
388 Curses_ShowMessage("Terminate Search");
389 flag.bothsides = false;
390 signal(SIGINT, Curses_Die);
391 signal(SIGQUIT, Curses_Die);
398 Curses_ClearScreen();
399 /* printw("GNU Shogi ??p? command summary\n"); */
400 printw(CP[40], version, patchlevel);
401 printw("-------------------------------"
402 "---------------------------------\n");
403 /* printw("7g7f move from 7g to 7f quit
406 /* printw("S6h move silver to 6h beep
407 * turn %s\n", (flag.beep) ? "off" : "on"); */
408 printw(CP[86], (flag.beep) ? CP[92] : CP[93]);
409 /* printw("2d2c+ move to 2c and promote\n"); */
410 printw(CP[128], (flag.material) ? CP[92] : CP[93]);
411 /* printw("P*5e drop a pawn to 5e easy
412 * turn %s\n", (flag.easy) ? "off" : "on"); */
413 printw(CP[173], (flag.easy) ? CP[92] : CP[93]);
415 * turn %s\n", (flag.hash) ? "off" : "on"); */
416 printw(CP[174], (flag.hash) ? CP[92] : CP[93]);
417 /* printw("bd redraw board reverse
418 * board display\n"); */
420 /* printw("list game to shogi.lst book
421 * turn %s used %d of %d\n", (Book) ? "off" : "on", book
423 printw(CP[170], (Book) ? CP[92] : CP[93], bookcount, BOOKSIZE);
424 /* printw("undo undo last ply remove
425 * take back a move\n"); */
427 /* printw("edit edit board force
428 * enter game moves\n"); */
430 /* printw("switch sides with computer both
431 * computer match\n"); */
433 /* printw("black computer plays black white
434 * computer plays white\n"); */
436 /* printw("depth set search depth clock
437 * set time control\n"); */
439 /* printw("hint suggest a move post
440 * turn %s principle variation\n", (flag.post) ? "off" :
442 printw(CP[177], (flag.post) ? CP[92] : CP[93]);
443 /* printw("save game to file get
444 * game from file\n"); */
446 /* printw("random randomize play new
447 * start new game\n"); */
450 printw(CP[47], ColorStr[computer]);
452 printw(CP[97], ColorStr[opponent]);
454 printw(CP[79], MaxResponseTime/100);
456 printw(CP[59], (flag.easy) ? CP[93] : CP[92]);
458 printw(CP[231], (flag.tsume) ? CP[93] : CP[92]);
460 printw(CP[52], MaxSearchDepth);
462 printw(CP[100], (dither) ? CP[93] : CP[92]);
464 printw(CP[112], (flag.hash) ? CP[93] : CP[92]);
468 printw(CP[110], (TCflag) ? CP[93] : CP[92],
469 TimeControl.moves[black],
470 TimeControl.clock[black] / 100,
471 OperatorTime, MaxSearchDepth);
476 fflush(stdin); /* what is this supposed to do?? */
480 Curses_ClearScreen();
481 Curses_UpdateDisplay(0, 0, 1, 0);
485 static const short x0[2] = { 54, 2 };
486 static const short y0[2] = { 20, 4 };
490 * Set up a board position. Pieces are entered by typing the piece followed
491 * by the location. For example, N3f will place a knight on square 3f.
492 * P* will put a pawn to the captured pieces.
496 Curses_EditBoard(void)
502 flag.regularstart = true;
504 Curses_ClearScreen();
505 Curses_UpdateDisplay(0, 0, 1, 0);
519 printw(CP[60], ColorStr[a]); /* Editing %s */
522 FLUSH_SCANW("%s", s);
524 if (s[0] == CP[28][0]) /* # */
526 for (sq = 0; sq < NO_SQUARES; sq++)
528 board[sq] = no_piece;
537 if (s[0] == CP[136][0]) /* c */
542 for (i = NO_PIECES; i > no_piece; i--)
544 if ((s[0] == pxx[i]) || (s[0] == qxx[i]))
548 Captured[a][unpromoted[i]]++;
558 if ((c >= 0) && (c < NO_COLS) && (r >= 0) && (r < NO_ROWS))
562 for (i = NO_PIECES; i > no_piece; i--)
564 if ((s[0] == pxx[i]) || (s[0] == qxx[i]))
574 color[sq] = ((board[sq] == no_piece) ? neutral : a);
578 while (s[0] != CP[29][0]); /* . */
580 for (sq = 0; sq < NO_SQUARES; sq++)
581 Mvboard[sq] = ((board[sq] != Stboard[sq]) ? 10 : 0);
588 Curses_ClearScreen();
589 Curses_UpdateDisplay(0, 0, 1, 0);
598 for (side = black; side <= white; side++)
600 short x, y, piece, cside, k;
602 cside = flag.reverse ? (side ^ 1) : side;
607 for (piece = pawn; piece <= king; piece++)
611 if ((n = Captured[side][piece]))
614 printw("%i%c", n, pxx[piece]);
645 Curses_SearchStartStuff(short side)
649 signal(SIGINT, Curses_TerminateSearch);
650 signal(SIGQUIT, Curses_TerminateSearch);
652 for (i = 4; i < 14; i++) /* CHECKME */
661 Curses_OutputMove(void)
664 Curses_UpdateDisplay(root->f, root->t, 0, (short) root->flags);
673 printw(CP[84], mvstr[0]); /* My move is %s */
682 if (root->flags & draw)
684 else if (root->score == -(SCORE_LIMIT + 999))
686 else if (root->score == SCORE_LIMIT + 998)
689 else if (root->score < -SCORE_LIMIT)
690 printw(CP[96], SCORE_LIMIT + 999 + root->score - 1);
691 else if (root->score > SCORE_LIMIT)
692 printw(CP[45], SCORE_LIMIT + 998 - root->score - 1);
693 #endif /* VERYBUGGY */
707 if (Tree[t].f || Tree[t].t)
715 ShowNodeCnt(NodeCnt);
717 printw(CP[81], t); /* Max Tree= */
721 Curses_ShowSidetoMove();
733 m = (short) ((dt = (TimeControl.clock[player] - et)) / 6000);
734 s = (short) ((dt - 6000 * (long) m) / 100);
738 m = (short) ((dt = et) / 6000);
739 s = (short) (et - 6000 * (long) m) / 100;
749 gotoXY(20, (flag.reverse) ? 2 : 23);
751 gotoXY(20, (flag.reverse) ? 23 : 2);
753 /* printw("%d:%02d %ld ", m, s, dt); */
754 printw("%d:%02d ", m, s);
757 ShowNodeCnt(NodeCnt);
769 if (color[sq] == neutral)
773 else if (flag.reverse ^ (color[sq] == black))
785 if (is_promoted[(int)piece])
788 y = pxx[unpromoted[(int)piece]];
796 gotoXY(8 + 5 * VIR_C(sq), 4 + 2 * ((NO_ROWS - 1) - VIR_R(sq)));
797 printw("%c%c%c%c", l, p, y, r);
802 * Curses_ShowPostnValue(): must have called ExaminePosition() first
805 Curses_ShowPostnValue(short sq)
809 gotoXY(4 + 5 * VIR_C(sq), 5 + 2 * (7 - VIR_R(sq))); /* CHECKME */
810 score = ScorePosition(color[sq]);
812 if (color[sq] != neutral)
813 #if defined SAVE_SVALUE
819 printw("%3d ", svalue[sq]);
830 Curses_ShowPostnValues(void)
834 ExaminePosition(opponent);
836 for (sq = 0; sq < NO_SQUARES; sq++)
837 Curses_ShowPostnValue(sq);
839 score = ScorePosition(opponent);
841 printw(CP[103], score,
842 mtl[computer], pscore[computer], GameType[computer],
843 mtl[opponent], pscore[opponent], GameType[opponent]);
850 Curses_UpdateDisplay(short f, short t, short redraw, short isspec)
864 for (j=0; j<NO_COLS; j++)
867 while (i <= 1 + 2*NO_ROWS)
874 z = NO_ROWS + 2 - ((i + 1) / 2);
876 printw(" %c |", ROW_NAME(z+1));
877 for (j=0; j<NO_COLS; j++)
882 if (i < 2 + 2*NO_ROWS)
885 for (j=0; j<NO_COLS; j++)
891 for (j=0; j<NO_COLS; j++)
894 gotoXY(3, 4 + 2*NO_ROWS);
902 for (sq = 0; sq < NO_SQUARES; sq++)
905 else /* not redraw */
913 if ((isspec & capture) || (isspec & dropmask) || redraw)
917 for (side = black; side <= white; side++)
919 short x, y, piece, cside, k;
920 cside = flag.reverse ? (side ^ 1) : side;
925 for (piece = pawn; piece <= king; piece++)
929 if ((n = Captured[side][piece]))
932 printw("%i%c", n, pxx[piece]);
934 if (cside == black) y--; else y++;
961 Curses_ChangeAlphaWindow(void)
963 Curses_ShowMessage(CP[114]);
964 FLUSH_SCANW("%hd", &WAwindow);
965 Curses_ShowMessage(CP[34]);
966 FLUSH_SCANW("%hd", &BAwindow);
971 Curses_ChangeBetaWindow(void)
973 Curses_ShowMessage(CP[115]);
974 FLUSH_SCANW("%hd", &WBwindow);
975 Curses_ShowMessage(CP[35]);
976 FLUSH_SCANW("%hd", &BBwindow);
981 Curses_GiveHint(void)
987 algbr((short) (hint >> 8), (short) (hint & 0xFF), false);
988 strcpy(s, CP[198]); /* try */
990 Curses_ShowMessage(s);
994 Curses_ShowMessage(CP[223]);
1000 Curses_ChangeSearchDepth(void)
1002 Curses_ShowMessage(CP[150]);
1003 FLUSH_SCANW("%hd", &MaxSearchDepth);
1004 TCflag = !(MaxSearchDepth > 0);
1009 Curses_ChangeHashDepth(void)
1011 Curses_ShowMessage(CP[163]);
1012 FLUSH_SCANW("%hd", &HashDepth);
1013 Curses_ShowMessage(CP[82]);
1014 FLUSH_SCANW("%hd", &HashMoveLimit);
1019 Curses_SetContempt(void)
1021 Curses_ShowMessage(CP[142]);
1022 FLUSH_SCANW("%hd", &contempt);
1027 Curses_ChangeXwindow(void)
1029 Curses_ShowMessage(CP[208]);
1030 FLUSH_SCANW("%hd", &xwndw);
1035 Curses_SelectLevel(char *sx)
1039 Curses_ClearScreen();
1041 printw(CP[41], version, patchlevel);
1071 FLUSH_SCANW("%d", &item);
1118 flag.onemove = true;
1124 flag.onemove = true;
1130 flag.onemove = true;
1134 TCflag = (TCmoves > 0);
1136 TimeControl.clock[black] = TimeControl.clock[white] = 0;
1139 Curses_ClearScreen();
1140 Curses_UpdateDisplay(0, 0, 1, 0);
1145 Curses_DoDebug(void)
1147 short c, p, sq, tp, tc, tsq, score;
1150 ExaminePosition(opponent);
1151 Curses_ShowMessage(CP[65]);
1152 FLUSH_SCANW("%s", s);
1155 if ((s[0] == CP[9][0]) || (s[0] == CP[9][1])) /* b B */
1158 if ((s[0] == CP[9][2]) || (s[0] == CP[9][3])) /* w W */
1161 for (p = king; p > no_piece; p--)
1163 if ((s[1] == pxx[p]) || (s[1] == qxx[p]))
1167 for (sq = 0; sq < NO_SQUARES; sq++)
1173 tsq = PieceList[c][1];
1174 PieceList[c][1] = sq;
1175 Curses_ShowPostnValue(sq);
1176 PieceList[c][1] = tsq;
1181 score = ScorePosition(opponent);
1183 printw(CP[103], score,
1184 mtl[computer], pscore[computer], GameType[computer],
1185 mtl[opponent], pscore[opponent], GameType[opponent]);
1192 Curses_DoTable(short table[NO_SQUARES])
1195 ExaminePosition(opponent);
1197 for (sq = 0; sq < NO_SQUARES; sq++)
1199 gotoXY(4 + 5 * VIR_C(sq), 5 + 2 * (7 - VIR_R(sq)));
1200 printw("%3d ", table[sq]);
1206 * Determine the time that has passed since the search was started. If the
1207 * elapsed time exceeds the target(ResponseTime + ExtraTime) then set timeout
1208 * to true which will terminate the search.
1209 * iop = COMPUTE_MODE calculate et, bump ETnodes
1210 * iop = COMPUTE_AND_INIT_MODE calculate et, set timeout if time exceeded,
1211 * set reference time
1214 Curses_ElapsedTime(ElapsedTime_mode iop)
1220 #ifdef HAVE_GETTIMEOFDAY
1224 if ((i = ioctl((int) 0, FIONREAD, &nchar)))
1228 "You probably have a non-ANSI <ioctl.h>; "
1229 "see README. %d %d %x\n",
1230 i, errno, FIONREAD);
1239 flag.bothsides = false;
1242 #ifdef HAVE_GETTIMEOFDAY
1243 gettimeofday(&tv, NULL);
1244 current_time = tv.tv_sec*100 + (tv.tv_usec/10000);
1246 et = ((current_time = time((long *) 0)) - time0) * 100;
1249 #ifdef INTERRUPT_TEST
1250 if (iop == INIT_INTERRUPT_MODE)
1252 itime0 = current_time;
1254 else if (iop == COMPUTE_INTERRUPT_MODE)
1256 it = current_time - itime0;
1261 #ifdef HAVE_GETTIMEOFDAY
1262 et = current_time - time0;
1264 ETnodes = NodeCnt + znodes;
1268 #ifdef INTERRUPT_TEST
1269 printf("elapsed time %ld not positive\n", et);
1274 if (iop == COMPUTE_AND_INIT_MODE)
1276 if ((et > (ResponseTime + ExtraTime)) && (Sdepth > MINDEPTH))
1277 flag.timeout = true;
1279 time0 = current_time;
1284 #ifdef QUIETBACKGROUND
1293 Curses_SetupBoard(void)
1295 Curses_ShowMessage("'setup' command is not supported in Cursesmode");