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
9 * Copyright (c) 2008, 2013, 2014 Yann Dirson and the Free Software Foundation
11 * GNU SHOGI is based on GNU CHESS
13 * Copyright (c) 1988, 1989, 1990 John Stanback
14 * Copyright (c) 1992 Free Software Foundation
16 * This file is part of GNU SHOGI.
18 * GNU Shogi is free software; you can redistribute it and/or modify it
19 * under the terms of the GNU General Public License as published by the
20 * Free Software Foundation; either version 3 of the License,
21 * or (at your option) any later version.
23 * GNU Shogi is distributed in the hope that it will be useful, but WITHOUT
24 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
25 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
28 * You should have received a copy of the GNU General Public License along
29 * with GNU Shogi; see the file COPYING. If not, see
30 * <http://www.gnu.org/licenses/>.
31 * ----------------------------------------------------------------------
34 /* request *snprintf prototypes*/
35 #define _POSIX_C_SOURCE 200112L
41 #include <sys/param.h>
42 #include <sys/types.h>
47 #include "cursesdsp.h"
54 /* Definition of FIONREAD */
55 #include <sys/filio.h>
59 /* Definition of errno(). */
63 #define FLUSH_SCANW fflush(stdout), scanw
69 #define VIR_C(s) ((flag.reverse) ? (NO_COLS - 1 - column(s)) : column(s))
70 #define VIR_R(s) ((flag.reverse) ? (NO_ROWS - 1 - row(s)) : row(s))
72 unsigned short MV
[MAXDEPTH
];
76 /* Forward declarations. */
77 /* FIXME: change this name, puh-leeze! */
79 static void UpdateCatched(void);
80 static void DrawPiece(short sq
);
81 static void ShowScore(short score
);
83 /****************************************
84 * Trivial output functions.
85 ****************************************/
96 Curses_ClearScreen(void)
104 gotoXY(short x
, short y
)
119 Curses_ShowCurrentMove(short pnt
, short f
, short t
)
123 printw("(%2d) %5s ", pnt
, mvstr
[0]);
128 Curses_ShowDepth(char ch
)
131 printw("Depth= %d%c ", Sdepth
, ch
);
137 Curses_ShowGameType(void)
142 printw("%c vs. %c", GameType
[black
], GameType
[white
]);
151 printw("GNU Shogi %s", PACKAGE_VERSION
);
156 Curses_ShowLine(unsigned short *bstline
)
162 Curses_ShowMessage(char *s
)
171 Curses_AlwaysShowMessage(const char *format
, va_list ap
)
173 static char buffer
[60];
174 vsnprintf(buffer
, sizeof(buffer
), format
, ap
);
175 Curses_ShowMessage(buffer
);
180 Curses_Printf(const char *format
, va_list ap
)
182 static char buffer
[60];
183 vsnprintf(buffer
, sizeof(buffer
), format
, ap
);
184 printw("%s", buffer
);
189 Curses_doRequestInputString(const char* fmt
, char* buffer
)
191 FLUSH_SCANW(fmt
, buffer
);
196 Curses_GetString(char* sx
)
199 return (getstr(sx
) == ERR
);
204 Curses_ShowNodeCnt(long NodeCnt
)
207 /* printw("Nodes = %8ld, Nodes/Sec = %5ld", NodeCnt, (et > 100) ? NodeCnt / (et / 100) : 0); */
208 printw("n = %ld n/s = %ld",
209 NodeCnt
, (et
> 100) ? NodeCnt
/ (et
/ 100) : 0);
215 Curses_ShowPatternCount(short side
, short n
)
219 gotoXY(TAB
+ 10 + 3 * side
, 20); /* CHECKME */
232 gotoXY(5, ((flag
.reverse
) ? (5 + 2*NO_ROWS
) : 2));
233 printw("%s", (computer
== white
) ? "Computer" : "Human ");
234 gotoXY(5, ((flag
.reverse
) ? 2 : (5 + 2*NO_ROWS
)));
235 printw("%s", (computer
== black
) ? "Computer" : "Human ");
240 Curses_ShowPrompt(void)
242 Curses_ShowSidetoMove();
244 printw("Your move is? ");
250 Curses_ShowResponseTime(void)
256 printw("%ld, %d, %ld, %ld, %ld, %d",
257 ResponseTime
, TCC
, TCleft
, ExtraTime
, et
, flag
.timeout
);
264 Curses_ShowResults(short score
, unsigned short *bstline
, char ch
)
266 unsigned char d
, ply
;
270 Curses_ShowDepth(ch
);
274 for (ply
= 1; bstline
[ply
] > 0; ply
++)
282 algbr((short) bstline
[ply
] >> 8,
283 (short) bstline
[ply
] & 0xFF, false);
284 printw("%5s ", mvstr
[0]);
299 ShowScore(short score
)
302 printw("Score= %d", score
);
308 Curses_ShowSidetoMove(void)
311 printw("%2d: %s", 1 + GameCnt
/ 2, ColorStr
[player
]);
317 Curses_ShowStage(void)
320 printw("Stage= %2d%c B= %2d W= %2d",
321 stage
, flag
.tsume
?'T':' ', balance
[black
], balance
[white
]);
325 /****************************************
326 * End of trivial output routines.
327 ****************************************/
330 Curses_Initialize(void)
332 signal(SIGINT
, Curses_Die
);
333 signal(SIGQUIT
, Curses_Die
);
340 Curses_ExitShogi(void)
360 signal(SIGINT
, SIG_IGN
);
361 signal(SIGQUIT
, SIG_IGN
);
363 Curses_ShowMessage("Abort? ");
364 FLUSH_SCANW("%s", s
);
366 if (strcmp(s
, "yes") == 0)
369 signal(SIGINT
, Curses_Die
);
370 signal(SIGQUIT
, Curses_Die
);
375 Curses_TerminateSearch(int sig
)
377 signal(SIGINT
, SIG_IGN
);
378 signal(SIGQUIT
, SIG_IGN
);
381 flag
.musttimeout
= true;
383 Curses_ShowMessage("Terminate Search");
384 flag
.bothsides
= false;
385 signal(SIGINT
, Curses_Die
);
386 signal(SIGQUIT
, Curses_Die
);
393 Curses_ClearScreen();
394 printw("GNU Shogi %s command summary\n", PACKAGE_VERSION
);
395 printw("-------------------------------"
396 "---------------------------------\n");
397 printw("7g7f move from 7g to 7f quit Exit Shogi\n");
398 printw("S6h move silver to 6h beep turn %s\n", (flag
.beep
) ? "OFF" : "ON");
399 printw("2d2c+ move to 2c and promote material turn %s\n", (flag
.material
) ? "OFF" : "ON");
400 printw("P*5e drop pawn to 5e easy turn %s\n", (flag
.easy
) ? "OFF" : "ON");
401 printw("tsume toggle tsume mode hash turn %s\n", (flag
.hash
) ? "OFF" : "ON");
402 printw("bd redraw board reverse board display\n");
403 printw("list game to shogi.lst book turn %s used %d of %d\n", (Book
) ? "OFF" : "ON", bookcount
, BOOKSIZE
);
404 printw("undo undo last ply remove take back a move\n");
405 printw("edit edit board force toggle manual move mode\n");
406 printw("switch sides with computer both computer match\n");
407 printw("black computer plays black white computer plays white\n");
408 printw("depth set search depth clock set time control\n");
409 printw("post principle variation hint suggest a move\n", (flag
.post
) ? "OFF" : "ON");
410 printw("save game to file get game from file\n");
411 printw("random randomize play new start new game\n");
413 printw("Computer: %s", ColorStr
[computer
]);
415 printw("Opponent: %s", ColorStr
[opponent
]);
417 printw("Level: %ld", MaxResponseTime
/100);
419 printw("Easy mode: %s", (flag
.easy
) ? "ON" : "OFF");
421 printw("Tsume: %s", (flag
.tsume
) ? "ON" : "OFF");
423 printw("Depth: %d", MaxSearchDepth
);
425 printw("Random: %s", (dither
) ? "ON" : "OFF");
427 printw("Transposition table: %s", (flag
.hash
) ? "ON" : "OFF");
429 printw("Hit <RET> to return: ");
431 printw("Time Control %s %d moves %d sec %d add %d depth\n", (TCflag
) ? "ON" : "OFF",
432 TimeControl
.moves
[black
],
433 TimeControl
.clock
[black
] / 100,
434 OperatorTime
, MaxSearchDepth
);
439 fflush(stdin
); /* what is this supposed to do?? */
443 Curses_ClearScreen();
444 Curses_UpdateDisplay(0, 0, 1, 0);
448 static const short x0
[2] = { 54, 2 };
449 static const short y0
[2] = { 20, 4 };
453 * Set up a board position. Pieces are entered by typing the piece followed
454 * by the location. For example, N3f will place a knight on square 3f.
455 * P* will put a pawn to the captured pieces.
459 Curses_EditBoard(void)
465 flag
.regularstart
= true;
467 Curses_ClearScreen();
468 Curses_UpdateDisplay(0, 0, 1, 0);
470 printw(". Exit to main\n");
472 printw("# Clear board\n");
474 printw("c Change sides\n");
476 printw("Enter piece & location: ");
482 printw("Editing: %s", ColorStr
[a
]);
485 FLUSH_SCANW("%s", s
);
489 for (sq
= 0; sq
< NO_SQUARES
; sq
++)
491 board
[sq
] = no_piece
;
505 for (i
= NO_PIECES
; i
> no_piece
; i
--)
507 if ((s
[0] == pxx
[i
]) || (s
[0] == qxx
[i
]))
511 Captured
[a
][unpromoted
[i
]]++;
521 if ((c
>= 0) && (c
< NO_COLS
) && (r
>= 0) && (r
< NO_ROWS
))
525 for (i
= NO_PIECES
; i
> no_piece
; i
--)
527 if ((s
[0] == pxx
[i
]) || (s
[0] == qxx
[i
]))
537 color
[sq
] = ((board
[sq
] == no_piece
) ? neutral
: a
);
543 for (sq
= 0; sq
< NO_SQUARES
; sq
++)
544 Mvboard
[sq
] = ((board
[sq
] != Stboard
[sq
]) ? 10 : 0);
551 Curses_ClearScreen();
552 Curses_UpdateDisplay(0, 0, 1, 0);
561 for (side
= black
; side
<= white
; side
++)
563 short x
, y
, piece
, cside
, k
;
565 cside
= flag
.reverse
? (side
^ 1) : side
;
570 for (piece
= pawn
; piece
<= king
; piece
++)
574 if ((n
= Captured
[side
][piece
]))
577 printw("%i%c", n
, pxx
[piece
]);
608 Curses_SearchStartStuff(short side
)
612 signal(SIGINT
, Curses_TerminateSearch
);
613 signal(SIGQUIT
, Curses_TerminateSearch
);
615 for (i
= 4; i
< 14; i
++) /* CHECKME */
624 Curses_OutputMove(void)
627 Curses_UpdateDisplay(root
->f
, root
->t
, 0, (short) root
->flags
);
632 printw("Illegal position.");
636 printw("My move is: %5s", mvstr
[0]);
645 if (root
->flags
& draw
)
646 printw("Drawn game!");
647 else if (root
->score
== -(SCORE_LIMIT
+ 999))
648 printw("Opponent mates!");
649 else if (root
->score
== SCORE_LIMIT
+ 998)
650 printw("Computer mates!");
652 else if (root
->score
< -SCORE_LIMIT
)
653 printw("Opp: mate in %d!", SCORE_LIMIT
+ 999 + root
->score
- 1);
654 else if (root
->score
> SCORE_LIMIT
)
655 printw("Comp: mate in %d!", SCORE_LIMIT
+ 998 - root
->score
- 1);
656 #endif /* VERYBUGGY */
670 if (Tree
[t
].f
|| Tree
[t
].t
)
678 ShowNodeCnt(NodeCnt
);
680 printw("Max Tree = %5d", t
);
684 Curses_ShowSidetoMove();
689 Curses_UpdateClocks(void)
696 m
= (short) ((dt
= (TimeControl
.clock
[player
] - et
)) / 6000);
697 s
= (short) ((dt
- 6000 * (long) m
) / 100);
701 m
= (short) ((dt
= et
) / 6000);
702 s
= (short) (et
- 6000 * (long) m
) / 100;
712 gotoXY(20, (flag
.reverse
) ? 2 : 23);
714 gotoXY(20, (flag
.reverse
) ? 23 : 2);
716 /* printw("%d:%02d %ld ", m, s, dt); */
717 printw("%d:%02d ", m
, s
);
720 ShowNodeCnt(NodeCnt
);
732 if (color
[sq
] == neutral
)
736 else if (flag
.reverse
^ (color
[sq
] == black
))
748 if (is_promoted
[(int)piece
])
751 y
= pxx
[unpromoted
[(int)piece
]];
759 gotoXY(8 + 5 * VIR_C(sq
), 4 + 2 * ((NO_ROWS
- 1) - VIR_R(sq
)));
760 printw("%c%c%c%c", l
, p
, y
, r
);
765 * Curses_ShowPostnValue(): must have called ExaminePosition() first
768 Curses_ShowPostnValue(short sq
)
772 gotoXY(4 + 5 * VIR_C(sq
), 5 + 2 * (7 - VIR_R(sq
))); /* CHECKME */
773 score
= ScorePosition(color
[sq
]);
775 if (color
[sq
] != neutral
)
776 #if defined SAVE_SVALUE
782 printw("%3d ", svalue
[sq
]);
793 Curses_ShowPostnValues(void)
797 ExaminePosition(opponent
);
799 for (sq
= 0; sq
< NO_SQUARES
; sq
++)
800 Curses_ShowPostnValue(sq
);
802 score
= ScorePosition(opponent
);
804 printw("S%d m%d ps%d gt%c m%d ps%d gt%c", score
,
805 mtl
[computer
], pscore
[computer
], GameType
[computer
],
806 mtl
[opponent
], pscore
[opponent
], GameType
[opponent
]);
813 Curses_UpdateDisplay(short f
, short t
, short redraw
, short isspec
)
827 for (j
=0; j
<NO_COLS
; j
++)
830 while (i
<= 1 + 2*NO_ROWS
)
837 z
= NO_ROWS
+ 2 - ((i
+ 1) / 2);
839 printw(" %c |", ROW_NAME(z
+1));
840 for (j
=0; j
<NO_COLS
; j
++)
845 if (i
< 2 + 2*NO_ROWS
)
848 for (j
=0; j
<NO_COLS
; j
++)
854 for (j
=0; j
<NO_COLS
; j
++)
857 gotoXY(3, 4 + 2*NO_ROWS
);
862 printw(" 1 2 3 4 5 6 7 8 9");
864 printw(" 9 8 7 6 5 4 3 2 1");
867 printw(" 1 2 3 4 5");
869 printw(" 1 2 3 4 5");
872 for (sq
= 0; sq
< NO_SQUARES
; sq
++)
875 else /* not redraw */
883 if ((isspec
& capture
) || (isspec
& dropmask
) || redraw
)
887 for (side
= black
; side
<= white
; side
++)
889 short x
, y
, piece
, cside
, k
;
890 cside
= flag
.reverse
? (side
^ 1) : side
;
895 for (piece
= pawn
; piece
<= king
; piece
++)
899 if ((n
= Captured
[side
][piece
]))
902 printw("%i%c", n
, pxx
[piece
]);
904 if (cside
== black
) y
--; else y
++;
931 Curses_ChangeAlphaWindow(void)
933 Curses_ShowMessage("WAwindow = ");
934 FLUSH_SCANW("%hd", &WAwindow
);
935 Curses_ShowMessage("BAwindow = ");
936 FLUSH_SCANW("%hd", &BAwindow
);
941 Curses_ChangeBetaWindow(void)
943 Curses_ShowMessage("WBwindow = ");
944 FLUSH_SCANW("%hd", &WBwindow
);
945 Curses_ShowMessage("BBwindow = ");
946 FLUSH_SCANW("%hd", &BBwindow
);
951 Curses_GiveHint(void)
957 algbr((short) (hint
>> 8), (short) (hint
& 0xFF), false);
960 Curses_ShowMessage(s
);
964 Curses_ShowMessage("I have no idea.\n");
970 Curses_ChangeSearchDepth(void)
972 Curses_ShowMessage("depth = ");
973 FLUSH_SCANW("%hd", &MaxSearchDepth
);
974 TCflag
= !(MaxSearchDepth
> 0);
979 Curses_ChangeHashDepth(void)
981 Curses_ShowMessage("hashdepth = ");
982 FLUSH_SCANW("%hd", &HashDepth
);
983 Curses_ShowMessage("MoveLimit = ");
984 FLUSH_SCANW("%hd", &HashMoveLimit
);
989 Curses_SetContempt(void)
991 Curses_ShowMessage("contempt = ");
992 FLUSH_SCANW("%hd", &contempt
);
997 Curses_ChangeXwindow(void)
999 Curses_ShowMessage("xwndw= ");
1000 FLUSH_SCANW("%hd", &xwndw
);
1005 Curses_SelectLevel(char *sx
)
1009 Curses_ClearScreen();
1011 printw("GNU Shogi %s", PACKAGE_VERSION
);
1013 printw(" 1. 40 moves in 5 minutes");
1015 printw(" 2. 40 moves in 15 minutes");
1017 printw(" 3. 40 moves in 30 minutes");
1019 printw(" 4. all moves in 15 minutes");
1021 printw(" 5. all moves in 30 minutes");
1023 printw(" 6. all moves in 15 minutes, 30 seconds fischer clock");
1025 printw(" 7. all moves in 30 minutes, 30 seconds fischer clock");
1027 printw(" 8. 1 move in 1 minute");
1029 printw(" 9. 1 move in 15 minutes");
1031 printw("10. 1 move in 30 minutes");
1039 printw("Enter Level: ");
1041 FLUSH_SCANW("%d", &item
);
1088 flag
.onemove
= true;
1094 flag
.onemove
= true;
1100 flag
.onemove
= true;
1104 TCflag
= (TCmoves
> 0);
1106 TimeControl
.clock
[black
] = TimeControl
.clock
[white
] = 0;
1109 Curses_ClearScreen();
1110 Curses_UpdateDisplay(0, 0, 1, 0);
1115 Curses_DoDebug(void)
1117 short c
, p
, sq
, tp
, tc
, tsq
, score
;
1120 ExaminePosition(opponent
);
1121 Curses_ShowMessage("Enter piece: ");
1122 FLUSH_SCANW("%s", s
);
1125 if ((s
[0] == 'b') || (s
[0] == 'B'))
1128 if ((s
[0] == 'w') || (s
[0] == 'W'))
1131 for (p
= king
; p
> no_piece
; p
--)
1133 if ((s
[1] == pxx
[p
]) || (s
[1] == qxx
[p
]))
1137 for (sq
= 0; sq
< NO_SQUARES
; sq
++)
1143 tsq
= PieceList
[c
][1];
1144 PieceList
[c
][1] = sq
;
1145 Curses_ShowPostnValue(sq
);
1146 PieceList
[c
][1] = tsq
;
1151 score
= ScorePosition(opponent
);
1153 printw("S%d m%d ps%d gt%c m%d ps%d gt%c", score
,
1154 mtl
[computer
], pscore
[computer
], GameType
[computer
],
1155 mtl
[opponent
], pscore
[opponent
], GameType
[opponent
]);
1162 Curses_DoTable(short table
[NO_SQUARES
])
1165 ExaminePosition(opponent
);
1167 for (sq
= 0; sq
< NO_SQUARES
; sq
++)
1169 gotoXY(4 + 5 * VIR_C(sq
), 5 + 2 * (7 - VIR_R(sq
)));
1170 printw("%3d ", table
[sq
]);
1176 Curses_PollForInput(void)
1181 if ((i
= ioctl((int) 0, FIONREAD
, &nchar
)))
1185 "You probably have a non-ANSI <ioctl.h>; "
1186 "see README. %d %d %x\n",
1187 i
, errno
, FIONREAD
);
1196 flag
.bothsides
= false;
1202 Curses_SetupBoard(void)
1204 Curses_ShowMessage("'setup' command is not supported in Cursesmode");