Merge branch 'maint' into HEAD
[gnushogi.git] / gnushogi / cursesdsp.c
1 /*
2  * FILE: cursesdsp.c
3  *
4  *     Curses interface for GNU Shogi
5  *
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
10  *
11  * GNU SHOGI is based on GNU CHESS
12  *
13  * Copyright (c) 1988, 1989, 1990 John Stanback
14  * Copyright (c) 1992 Free Software Foundation
15  *
16  * This file is part of GNU SHOGI.
17  *
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.
22  *
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
26  * for more details.
27  *
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  * ----------------------------------------------------------------------
32  */
33
34 /* request *snprintf prototypes*/
35 #define _POSIX_C_SOURCE 200112L
36
37 #include <ctype.h>
38 #include <signal.h>
39 #include <stdio.h>
40 #include <stdarg.h>
41
42 #include <sys/param.h>
43 #include <sys/types.h>
44 #include <sys/file.h>
45 #include <curses.h>
46
47 #include "gnushogi.h"
48
49 #if HAVE_UNISTD_H
50 #include <unistd.h>
51 #endif
52
53 #if HAVE_SYS_FILIO_H
54 /* Definition of FIONREAD */
55 #include <sys/filio.h>
56 #endif
57
58 #if HAVE_ERRNO_H
59 /* Definition of errno(). */
60 #include <errno.h>
61 #endif
62
63 #define FLUSH_SCANW fflush(stdout), scanw
64
65 #define MARGIN (5)
66 #define TAB (58)
67
68 #define VIR_C(s)  ((flag.reverse) ? (NO_COLS - 1 - column(s)) : column(s))
69 #define VIR_R(s)  ((flag.reverse) ? (NO_ROWS - 1 - row(s)) : row(s))
70
71 /****************************************
72  * forward declarations
73  ****************************************/
74
75 /* FIXME: change this name, puh-leeze! */
76 static void UpdateCatched(void);
77 static void DrawPiece(short sq);
78 static void ShowScore(short score);
79 static void Curses_UpdateDisplay(short f, short t, short redraw, short isspec);
80 static void Curses_Die(int sig);
81 static void Curses_ShowSidetoMove(void);
82
83 /****************************************
84  * Trivial output functions.
85  ****************************************/
86
87 static void
88 ClearEoln(void)
89 {
90     clrtoeol();
91     refresh();
92 }
93
94
95 static void
96 Curses_ClearScreen(void)
97 {
98     clear();
99     refresh();
100 }
101
102
103 static void
104 gotoXY(short x, short y)
105 {
106     move(y - 1, x - 1);
107 }
108
109
110 static void
111 ClearMessage(void)
112 {
113     gotoXY(TAB, 6);
114     ClearEoln();
115 }
116
117 static void
118 Curses_ShowCurrentMove(short pnt, short f, short t)
119 {
120     algbr(f, t, false);
121     gotoXY(TAB, 7);
122     printw("(%2d) %5s ", pnt, mvstr[0]);
123 }
124
125
126 static void
127 Curses_ShowDepth(char ch)
128 {
129     gotoXY(TAB, 4);
130     printw("Depth= %d%c ", Sdepth, ch);
131     ClearEoln();
132 }
133
134
135 static void
136 Curses_ShowGameType(void)
137 {
138     if (flag.post)
139     {
140         gotoXY(TAB, 20);
141         printw("%c vs. %c", GameType[black], GameType[white]);
142     }
143 }
144
145
146 static void
147 ShowHeader(void)
148 {
149     gotoXY(TAB, 2);
150     printw("GNU Shogi %s", PACKAGE_VERSION);
151 }
152
153
154 static void
155 Curses_ShowLine(unsigned short *bstline)
156 {
157 }
158
159
160 static void
161 Curses_ShowMessage(char *s)
162 {
163     gotoXY(TAB, 6);
164     printw("%s", s);
165     ClearEoln();
166 }
167
168
169 static void
170 Curses_AlwaysShowMessage(const char *format, ...)
171 {
172     static char buffer[60];
173     va_list ap;
174     va_start(ap, format);
175     vsnprintf(buffer, sizeof(buffer), format, ap);
176     Curses_ShowMessage(buffer);
177     va_end(ap);
178 }
179
180
181 static void
182 Curses_Printf(const char *format, ...)
183 {
184     static char buffer[60];
185     va_list ap;
186     va_start(ap, format);
187     vsnprintf(buffer, sizeof(buffer), format, ap);
188     printw("%s", buffer);
189     va_end(ap);
190 }
191
192
193 static void
194 Curses_doRequestInputString(const char* fmt, char* buffer)
195 {
196     FLUSH_SCANW(fmt, buffer);
197 }
198
199
200 static int
201 Curses_GetString(char* sx)
202 {
203     fflush(stdout);
204     return (getstr(sx) == ERR);
205 }
206
207
208 static void
209 Curses_ShowNodeCnt(long NodeCnt)
210 {
211     gotoXY(TAB, 22);
212     /* printw("Nodes = %8ld, Nodes/Sec = %5ld", NodeCnt, (et > 100) ? NodeCnt / (et / 100) : 0); */
213     printw("n = %ld n/s = %ld", 
214            NodeCnt, (et > 100) ? NodeCnt / (et / 100) : 0);
215     ClearEoln();
216 }
217
218
219 static void
220 Curses_ShowPatternCount(short side, short n)
221 {
222     if (flag.post)
223     {
224         gotoXY(TAB + 10 + 3 * side, 20);          /* CHECKME */
225
226         if (n >= 0)
227             printw("%3d", n);
228         else
229             printw("   ");
230     }
231 }
232
233
234 static void
235 ShowPlayers(void)
236 {
237     gotoXY(5, ((flag.reverse) ? (5 + 2*NO_ROWS) : 2));
238     printw("%s", (computer == white) ? "Computer" : "Human   ");
239     gotoXY(5, ((flag.reverse) ? 2 : (5 + 2*NO_ROWS)));
240     printw("%s", (computer == black) ? "Computer" : "Human   ");
241 }
242
243
244 static void
245 Curses_ShowPrompt(void)
246 {
247     Curses_ShowSidetoMove();
248     gotoXY(TAB, 17);
249     printw("Your move is? ");
250     ClearEoln();
251 }
252
253
254 static void
255 Curses_ShowResponseTime(void)
256 {
257     if (flag.post)
258     {
259         short TCC = TCcount;
260         gotoXY(TAB, 21);
261         printw("%ld, %d, %ld, %ld, %ld, %d",
262                ResponseTime, TCC, TCleft, ExtraTime, et, flag.timeout);
263         ClearEoln();
264     }
265 }
266
267
268 static void
269 Curses_ShowResults(short score, unsigned short *bstline, char ch)
270 {
271     unsigned char d, ply;
272
273     if (flag.post)
274     {
275         Curses_ShowDepth(ch);
276         ShowScore(score);
277         d = 7;
278
279         for (ply = 1; bstline[ply] > 0; ply++)
280         {
281             if (ply % 2 == 1)
282             {
283                 gotoXY(TAB, ++d);
284                 ClearEoln();
285             }
286
287             algbr((short) bstline[ply] >> 8, 
288                   (short) bstline[ply] & 0xFF, false);
289             printw("%5s ", mvstr[0]);
290         }
291
292         ClearEoln();
293
294         while (d < 13)
295         {
296             gotoXY(TAB, ++d);
297             ClearEoln();
298         }
299     }
300 }
301
302
303 static void
304 ShowScore(short score)
305 {
306     gotoXY(TAB, 5);
307     printw("Score= %d", score);
308     ClearEoln();
309 }
310
311
312 static void
313 Curses_ShowSidetoMove(void)
314 {
315     gotoXY(TAB, 14);
316     printw("%2d:   %s", 1 + GameCnt / 2, ColorStr[player]);
317     ClearEoln();
318 }
319
320
321 static void
322 Curses_ShowStage(void)
323 {
324     gotoXY(TAB, 19);
325     printw("Stage= %2d%c B= %2d W= %2d",
326            stage, flag.tsume?'T':' ', balance[black], balance[white]);
327     ClearEoln();
328 }
329
330 /****************************************
331  * End of trivial output routines.
332  ****************************************/
333
334 static void
335 Curses_Initialize(void)
336 {
337     signal(SIGINT, Curses_Die);
338     signal(SIGQUIT, Curses_Die);
339     initscr();
340     crmode();
341 }
342
343
344 static void
345 Curses_ExitShogi(void)
346
347     if (!nolist)
348         ListGame();
349
350     gotoXY(1, 24);
351
352     refresh();
353     nocrmode();
354     endwin();
355
356     exit(0);
357 }
358
359
360 static void
361 Curses_Die(int sig)
362 {
363     char s[80];
364
365     signal(SIGINT, SIG_IGN);
366     signal(SIGQUIT, SIG_IGN);
367
368     Curses_ShowMessage("Abort? ");
369     FLUSH_SCANW("%s", s);
370
371     if (strcmp(s, "yes") == 0)
372         Curses_ExitShogi();
373
374     signal(SIGINT, Curses_Die);
375     signal(SIGQUIT, Curses_Die);
376 }
377
378
379 static void
380 Curses_TerminateSearch(int sig)
381 {
382     signal(SIGINT, SIG_IGN);
383     signal(SIGQUIT, SIG_IGN);
384
385     if (!flag.timeout)
386         flag.musttimeout = true;
387
388     Curses_ShowMessage("Terminate Search");
389     flag.bothsides = false;
390     signal(SIGINT, Curses_Die);
391     signal(SIGQUIT, Curses_Die);
392 }
393
394
395 static void
396 Curses_help(void)
397 {
398     Curses_ClearScreen();
399     printw("GNU Shogi %s command summary\n", PACKAGE_VERSION);
400     printw("-------------------------------"
401            "---------------------------------\n");
402     printw("7g7f      move from 7g to 7f      quit      Exit Shogi\n");
403     printw("S6h       move silver to 6h       beep      turn %s\n", (flag.beep) ? "OFF" : "ON");
404     printw("2d2c+     move to 2c and promote  material  turn %s\n", (flag.material) ? "OFF" : "ON");
405     printw("P*5e      drop pawn to 5e         easy      turn %s\n", (flag.easy) ? "OFF" : "ON");
406     printw("tsume     toggle tsume mode       hash      turn %s\n", (flag.hash) ? "OFF" : "ON");
407     printw("bd        redraw board            reverse   board display\n");
408     printw("list      game to shogi.lst       book      turn %s used %d of %d\n", (Book) ? "OFF" : "ON", bookcount, BOOKSIZE);
409     printw("undo      undo last ply           remove    take back a move\n");
410     printw("edit      edit board              force     toggle manual move mode\n");
411     printw("switch    sides with computer     both      computer match\n");
412     printw("black     computer plays black    white     computer plays white\n");
413     printw("depth     set search depth        clock     set time control\n");
414     printw("post      principle variation     hint      suggest a move\n", (flag.post) ? "OFF" : "ON");
415     printw("save      game to file            get       game from file\n");
416     printw("random    randomize play          new       start new game\n");
417     gotoXY(10, 20);
418     printw("Computer: %s", ColorStr[computer]);
419     gotoXY(10, 21);
420     printw("Opponent: %s", ColorStr[opponent]);
421     gotoXY(10, 22);
422     printw("Level: %ld", MaxResponseTime/100);
423     gotoXY(10, 23);
424     printw("Easy mode: %s", (flag.easy) ? "ON" : "OFF");
425     gotoXY(25, 23);
426     printw("Tsume: %s", (flag.tsume) ? "ON" : "OFF");
427     gotoXY(40, 20);
428     printw("Depth: %d", MaxSearchDepth);
429     gotoXY(40, 21);
430     printw("Random: %s", (dither) ? "ON" : "OFF");
431     gotoXY(40, 22);
432     printw("Transposition table: %s", (flag.hash) ? "ON" : "OFF");
433     gotoXY(40, 23);
434     printw("Hit <RET> to return: ");
435     gotoXY(10, 24);
436     printw("Time Control %s %d moves %d sec %d add %d depth\n", (TCflag) ? "ON" : "OFF",
437            TimeControl.moves[black], 
438            TimeControl.clock[black] / 100, 
439            OperatorTime, MaxSearchDepth);
440
441     refresh();
442
443 #ifdef BOGUS
444     fflush(stdin); /* what is this supposed to do?? */
445 #endif /* BOGUS */
446
447     getchar();
448     Curses_ClearScreen();
449     Curses_UpdateDisplay(0, 0, 1, 0);
450 }
451
452
453 static const short x0[2] = { MARGIN + 5*NO_COLS + 4, 2 };
454 static const short y0[2] = { 4 + 2 * (NO_ROWS - 1), 4 };
455
456
457 /*
458  * Set up a board position. Pieces are entered by typing the piece followed
459  * by the location. For example, N3f will place a knight on square 3f.
460  * P* will put a pawn to the captured pieces.
461  */
462
463 static void
464 Curses_EditBoard(void)
465 {
466     short a, c, sq, i, found;
467     short r = 0;
468     char s[80];
469
470     flag.regularstart = true;
471     Book = BOOKFAIL;
472     Curses_ClearScreen();
473     Curses_UpdateDisplay(0, 0, 1, 0);
474     gotoXY(TAB, 11);
475     printw(".   Exit to main\n");
476     gotoXY(TAB, 12);
477     printw("#   Clear board\n");
478     gotoXY(TAB, 13);
479     printw("c   Change sides\n");
480     gotoXY(TAB, 7);
481     printw("Enter piece & location: ");
482     a = black;
483
484     while(1)
485     {
486         gotoXY(TAB, 4);
487         printw("Editing: %s", ColorStr[a]);
488         gotoXY(TAB + 2, 8);
489         ClearEoln();
490         FLUSH_SCANW("%s", s);
491         found = 0;
492         ClearMessage();
493
494         if (s[0] == '.')
495             break;
496
497         if (s[0] == '#')
498         {
499             for (sq = 0; sq < NO_SQUARES; sq++)
500             {
501                 board[sq] = no_piece;
502                 color[sq] = neutral;
503                 DrawPiece(sq);
504             }
505
506             ClearCaptured();
507             UpdateCatched();
508             continue;
509         }
510
511         if (s[0] == 'c') {
512             a = otherside[a];
513             continue;
514         }
515
516         if (s[1] == '*')
517         {
518             for (i = NO_PIECES; i > no_piece; i--)
519             {
520                 if ((s[0] == pxx[i]) || (s[0] == qxx[i]))
521                 {
522                     Captured[a][unpromoted[i]]++;
523                     UpdateCatched();
524                     found = 1;
525                     break;
526                 }
527             }
528             if (!found)
529                 dsp->AlwaysShowMessage("Invalid piece type '%c'", s[0]);
530             continue;
531         }
532
533         c = COL_NUM(s[1]);
534         r = ROW_NUM(s[2]);
535
536         if ((c < 0) || (c >= NO_COLS) || (r < 0) || (r >= NO_ROWS)) {
537             dsp->AlwaysShowMessage("Out-of-board '%c%c'", s[1], s[2]);
538             continue;
539         }
540
541         sq = locn(r, c);
542
543         for (i = NO_PIECES; i > no_piece; i--)
544         {
545             if ((s[0] == pxx[i]) || (s[0] == qxx[i]))
546             {
547                 color[sq] = a;
548                 if (s[3] == '+')
549                     board[sq] = promoted[i];
550                 else
551                     board[sq] = unpromoted[i];
552
553                 found = 1;
554                 break;
555             }
556         }
557
558         if (!found)
559             dsp->AlwaysShowMessage("Invalid piece type '%c'", s[0]);
560
561         DrawPiece(sq);
562     }
563
564     for (sq = 0; sq < NO_SQUARES; sq++)
565         Mvboard[sq] = ((board[sq] != Stboard[sq]) ? 10 : 0);
566
567     GameCnt = 0;
568     Game50 = 1;
569     ZeroRPT();
570     Sdepth = 0;
571     InitializeStats();
572     Curses_ClearScreen();
573     Curses_UpdateDisplay(0, 0, 1, 0);
574 }
575
576
577 static void 
578 UpdateCatched()
579 {
580     short side;
581
582     for (side = black; side <= white; side++)
583     { 
584         short x, y, piece, cside, k;
585
586         cside = flag.reverse ? (side ^ 1) : side;
587         x = x0[cside];
588         y = y0[cside];
589         k = 0;
590
591         for (piece = pawn; piece <= king; piece++)
592         {
593             short n;
594
595             if ((n = Captured[side][piece]))
596             {
597                 gotoXY(x, y); 
598                 printw("%i%c", n, pxx[piece]);
599
600                 if (cside == black) 
601                     y--; 
602                 else 
603                     y++;
604             }
605             else
606             {
607                 k++;
608             }
609         }
610
611         while (k)
612         {
613             k--;
614             gotoXY(x, y);
615             printw("  ");
616
617             if (cside == black) 
618                 y--; 
619             else 
620                 y++;
621         }
622     }
623
624     refresh();
625 }
626
627
628 static void
629 Curses_SearchStartStuff(short side)
630 {
631     short i;
632
633     signal(SIGINT, Curses_TerminateSearch);
634     signal(SIGQUIT, Curses_TerminateSearch);
635
636     for (i = 4; i < 14; i++)                      /* CHECKME */
637     {
638         gotoXY(TAB, i);
639         ClearEoln();
640     }
641 }
642
643
644 static void
645 Curses_OutputMove(void)
646 {
647
648     Curses_UpdateDisplay(root->f, root->t, 0, (short) root->flags);
649     gotoXY(TAB, 16);
650
651     if (flag.illegal) 
652     {
653         printw("Illegal position.");
654         return;
655     }
656
657     printw("My move is: %5s", mvstr[0]);
658
659     if (flag.beep)
660         putchar(7);
661
662     ClearEoln();
663
664     gotoXY(TAB, 18);
665
666     if (root->flags & draw)
667         printw("Drawn game!");
668     else if (root->score == -(SCORE_LIMIT + 999))
669         printw("Opponent mates!");
670     else if (root->score == SCORE_LIMIT + 998)
671         printw("Computer mates!");
672 #ifdef VERYBUGGY
673     else if (root->score < -SCORE_LIMIT)
674         printw("Opp: mate in %d!", SCORE_LIMIT + 999 + root->score - 1);
675     else if (root->score > SCORE_LIMIT)
676         printw("Comp: mate in %d!", SCORE_LIMIT + 998 - root->score - 1);
677 #endif /* VERYBUGGY */
678
679     ClearEoln();
680
681     if (flag.post)
682     {
683         short h, l, t;
684
685         h = TREE;
686         l = 0;
687         t = TREE >> 1;
688
689         while (l != t)
690         {
691             if (Tree[t].f || Tree[t].t)
692                 l = t;
693             else
694                 h = t;
695
696             t = (l + h) >> 1;
697         }
698
699         Curses_ShowNodeCnt(NodeCnt);
700         gotoXY(TAB, 23);
701         printw("Max Tree = %5d", t);
702         ClearEoln();
703     }
704
705     Curses_ShowSidetoMove();
706 }
707
708
709 static void
710 Curses_UpdateClocks(void)
711 {
712     short m, s;
713     long dt;
714
715     if (TCflag)
716     {
717         m = (short) ((dt = (TimeControl.clock[player] - et)) / 6000);
718         s = (short) ((dt - 6000 * (long) m) / 100);
719     }
720     else
721     {
722         m = (short) ((dt = et) / 6000);
723         s = (short) (et - 6000 * (long) m) / 100;
724     }
725
726     if (m < 0)
727         m = 0;
728
729     if (s < 0)
730         s = 0;
731
732     if (player == black)
733         gotoXY(20, (flag.reverse) ? 2 : (5 + 2*NO_ROWS));
734     else
735         gotoXY(20, (flag.reverse) ? (5 + 2*NO_ROWS) : 2);
736
737     /* printw("%d:%02d %ld  ", m, s, dt); */
738     printw("%d:%02d  ", m, s); 
739
740     if (flag.post)
741         Curses_ShowNodeCnt(NodeCnt);
742
743     refresh();
744 }
745
746
747 static void
748 DrawPiece(short sq)
749 {
750     char y;
751     char piece, l, r, p; 
752
753     if (color[sq] == neutral)
754     {
755         l = r = ' ';
756     }
757     else if (flag.reverse ^ (color[sq] == black))
758     {
759         l = '/';
760         r = '\\';
761     } 
762     else
763     {
764         l = '\\', r = '/';
765     }
766
767     piece = board[sq];
768
769     if (is_promoted[(int)piece])
770     {
771         p = '+';
772         y = pxx[unpromoted[(int)piece]];
773     } 
774     else
775     {
776         p = ' ';
777         y = pxx[(int)piece];
778     }
779
780     gotoXY(MARGIN + 3 + 5 * VIR_C(sq), 4 + 2 * ((NO_ROWS - 1) - VIR_R(sq)));
781     printw("%c%c%c%c", l, p, y, r);
782 }
783
784
785 /*
786  * Curses_ShowPostnValue(): must have called ExaminePosition() first
787  */
788 static void
789 Curses_ShowPostnValue(short sq)
790 {
791     gotoXY(4 + 5 * VIR_C(sq), 5 + 2 * (7 - VIR_R(sq))); /* CHECKME */
792     (void) ScorePosition(color[sq]);
793
794     if (color[sq] != neutral)
795 #if defined SAVE_SVALUE
796     {
797         printw("??? ");
798     }
799 #else
800     {
801         printw("%3d ", svalue[sq]);
802     }
803 #endif
804     else
805     {
806         printw("   ");
807     }
808 }
809
810
811 static void
812 Curses_ShowPostnValues(void)
813 {
814     short sq, score;
815
816     ExaminePosition(opponent);
817
818     for (sq = 0; sq < NO_SQUARES; sq++)
819         Curses_ShowPostnValue(sq);
820
821     score = ScorePosition(opponent);
822     gotoXY(TAB, 5);
823     printw("S%d m%d ps%d gt%c m%d ps%d gt%c", score,
824            mtl[computer], pscore[computer], GameType[computer],
825            mtl[opponent], pscore[opponent], GameType[opponent]);
826
827     ClearEoln();
828 }
829
830
831 static void
832 Curses_UpdateDisplay(short f, short t, short redraw, short isspec)
833 {
834     short i, sq, z;
835     int j;
836
837     if (redraw)
838     {
839         ShowHeader();
840         ShowPlayers();
841
842         i = 2;
843         gotoXY(MARGIN, ++i);
844
845         printw("  +");
846         for (j=0; j<NO_COLS; j++)
847             printw("----+");
848
849         while (i <= 1 + 2*NO_ROWS)
850         {
851             gotoXY(MARGIN, ++i);
852
853             if (flag.reverse)
854                 z = (i / 2) - 1;
855             else
856                 z = NO_ROWS + 2 - ((i + 1) / 2);
857
858             printw("%c |", ROW_NAME(z+1));
859             for (j=0; j<NO_COLS; j++)
860                 printw("    |");
861
862             gotoXY(MARGIN, ++i);
863
864             if (i < 2 + 2*NO_ROWS)
865             {
866                 printw("  +");
867                 for (j=0; j<NO_COLS; j++)
868                     printw("----+");
869             }
870         }
871
872         printw("  +");
873         for (j=0; j<NO_COLS; j++)
874             printw("----+");
875
876         gotoXY(MARGIN, 4 + 2*NO_ROWS);
877         printw("  ");
878 #ifndef MINISHOGI
879         if (flag.reverse)
880             printw("  1    2    3    4    5    6    7    8    9");
881         else
882             printw("  9    8    7    6    5    4    3    2    1");
883 #else
884         if (flag.reverse)
885             printw("  1    2    3    4    5");
886         else
887             printw("  5    4    3    2    1");
888 #endif
889
890         for (sq = 0; sq < NO_SQUARES; sq++)
891             DrawPiece(sq);
892     }
893     else /* not redraw */
894     {
895         if (f < NO_SQUARES)
896             DrawPiece(f);
897
898         DrawPiece(t & 0x7f);
899     }
900
901     if ((isspec & capture) || (isspec & dropmask) || redraw)
902     {
903         short side;
904
905         for (side = black; side <= white; side++)
906         {
907             short x, y, piece, cside, k;
908             cside = flag.reverse ? (side ^ 1) : side;
909             x = x0[cside];
910             y = y0[cside];
911             k = 0;
912
913             for (piece = pawn; piece <= king; piece++)
914             {
915                 short n;
916
917                 if ((n = Captured[side][piece]))
918                 {
919                     gotoXY(x, y); 
920                     printw("%i%c", n, pxx[piece]);
921
922                     if (cside == black) y--; else y++;
923                 }
924                 else
925                 {
926                     k++;
927                 }
928             }
929
930             while (k)
931             {
932                 k--;
933                 gotoXY(x, y);
934                 printw("  ");
935
936                 if (cside == black) 
937                     y--;
938                 else 
939                     y++;
940             }
941         }
942     }
943
944     refresh();
945 }
946
947
948 static void
949 Curses_ChangeAlphaWindow(void)
950 {
951     Curses_ShowMessage("WAwindow = ");
952     FLUSH_SCANW("%hd", &WAwindow);
953     Curses_ShowMessage("BAwindow = ");
954     FLUSH_SCANW("%hd", &BAwindow);
955 }
956
957
958 static void
959 Curses_ChangeBetaWindow(void)
960 {
961     Curses_ShowMessage("WBwindow = ");
962     FLUSH_SCANW("%hd", &WBwindow);
963     Curses_ShowMessage("BBwindow = ");
964     FLUSH_SCANW("%hd", &BBwindow);
965 }
966
967
968 static void
969 Curses_GiveHint(void)
970 {
971     char s[40];
972
973     if (hint)
974     {
975         algbr((short) (hint >> 8), (short) (hint & 0xFF), false);
976         strcpy(s, "try ");
977         strcat(s, mvstr[0]);
978         Curses_ShowMessage(s);
979     }
980     else
981     {
982         Curses_ShowMessage("I have no idea.\n");
983     }
984 }
985
986
987 static void
988 Curses_ChangeSearchDepth(char* sx)
989 {
990     Curses_ShowMessage("depth = ");
991     FLUSH_SCANW("%hd", &MaxSearchDepth);
992     TCflag = !(MaxSearchDepth > 0);
993 }
994
995
996 static void
997 Curses_ChangeHashDepth(void)
998 {
999     Curses_ShowMessage("hashdepth = ");
1000     FLUSH_SCANW("%hd", &HashDepth);
1001     Curses_ShowMessage("MoveLimit = ");
1002     FLUSH_SCANW("%hd", &HashMoveLimit);
1003 }
1004
1005
1006 static void
1007 Curses_SetContempt(void)
1008 {
1009     Curses_ShowMessage("contempt = ");
1010     FLUSH_SCANW("%hd", &contempt);
1011 }
1012
1013
1014 static void
1015 Curses_ChangeXwindow(void)
1016 {
1017     Curses_ShowMessage("xwndw= ");
1018     FLUSH_SCANW("%hd", &xwndw);
1019 }
1020
1021
1022 static void
1023 Curses_SelectLevel(char *sx)
1024 {
1025     int item;
1026
1027     Curses_ClearScreen();
1028     gotoXY(32, 2);
1029     printw("GNU Shogi %s", PACKAGE_VERSION);
1030     gotoXY(20, 4);
1031     printw(" 1.   40 moves in   5 minutes");
1032     gotoXY(20, 5);
1033     printw(" 2.   40 moves in  15 minutes");
1034     gotoXY(20, 6);
1035     printw(" 3.   40 moves in  30 minutes");
1036     gotoXY(20, 7);
1037     printw(" 4.  all moves in  15 minutes");
1038     gotoXY(20, 8);
1039     printw(" 5.  all moves in  30 minutes");
1040     gotoXY(20, 9);
1041     printw(" 6.  all moves in  15 minutes, 30 seconds fischer clock");
1042     gotoXY(20, 10);
1043     printw(" 7.  all moves in  30 minutes, 30 seconds fischer clock");
1044     gotoXY(20, 11);
1045     printw(" 8.    1 move  in   1 minute");
1046     gotoXY(20, 12);
1047     printw(" 9.    1 move  in  15 minutes");
1048     gotoXY(20, 13);
1049     printw("10.    1 move  in  30 minutes");
1050
1051     OperatorTime = 0;
1052     TCmoves = 40;
1053     TCminutes = 5;
1054     TCseconds = 0;
1055
1056     gotoXY(20, 17);
1057     printw("Enter Level: ");
1058     refresh();
1059     FLUSH_SCANW("%d", &item);
1060
1061     switch(item)
1062     {
1063     case 1:
1064         TCmoves = 40;
1065         TCminutes = 5;
1066         break;
1067
1068     case 2:
1069         TCmoves = 40;
1070         TCminutes = 15;
1071         break;
1072
1073     case 3:
1074         TCmoves = 40;
1075         TCminutes = 30;
1076         break;
1077
1078     case 4:
1079         TCmoves = 80;
1080         TCminutes = 15;
1081         flag.gamein = true;
1082         break;
1083
1084     case 5:
1085         TCmoves = 80;
1086         TCminutes = 30;
1087         flag.gamein = true;
1088         break;
1089
1090     case 6:
1091         TCmoves = 80;
1092         TCminutes = 15;
1093         TCadd = 3000;
1094         flag.gamein = true;
1095         break;
1096
1097     case 7:
1098         TCmoves = 80;
1099         TCminutes = 30;
1100         TCadd = 3000;
1101         break;
1102
1103     case 8:
1104         TCmoves = 1;
1105         TCminutes = 1;
1106         flag.onemove = true;
1107         break;
1108
1109     case 9:
1110         TCmoves = 1;
1111         TCminutes = 15;
1112         flag.onemove = true;
1113         break;
1114
1115     case 10:
1116         TCmoves = 1;
1117         TCminutes = 30;
1118         flag.onemove = true;
1119         break;
1120     }
1121
1122     TCflag = (TCmoves > 0);
1123
1124     TimeControl.clock[black] = TimeControl.clock[white] = 0; 
1125
1126     SetTimeControl();
1127     Curses_ClearScreen();
1128     Curses_UpdateDisplay(0, 0, 1, 0);
1129 }
1130
1131
1132 static void
1133 Curses_DoDebug(void)
1134 {
1135     short c, p, sq, tp, tc, tsq, score;
1136     char s[40];
1137
1138     ExaminePosition(opponent);
1139     Curses_ShowMessage("Enter piece: ");
1140     FLUSH_SCANW("%s", s);
1141     c = neutral;
1142
1143     if ((s[0] == 'b') || (s[0] == 'B'))
1144         c = black;
1145
1146     if ((s[0] == 'w') || (s[0] == 'W'))
1147         c = white;
1148
1149     for (p = king; p > no_piece; p--)
1150     {
1151         if ((s[1] == pxx[p]) || (s[1] == qxx[p]))
1152             break;
1153     }
1154
1155     for (sq = 0; sq < NO_SQUARES; sq++)
1156     {
1157         tp = board[sq];
1158         tc = color[sq];
1159         board[sq] = p;
1160         color[sq] = c;
1161         tsq = PieceList[c][1];
1162         PieceList[c][1] = sq;
1163         Curses_ShowPostnValue(sq);
1164         PieceList[c][1] = tsq;
1165         board[sq] = tp;
1166         color[sq] = tc;
1167     }
1168
1169     score = ScorePosition(opponent);
1170     gotoXY(TAB, 5);
1171     printw("S%d m%d ps%d gt%c m%d ps%d gt%c", score,
1172            mtl[computer], pscore[computer], GameType[computer],
1173            mtl[opponent], pscore[opponent], GameType[opponent]);
1174
1175     ClearEoln();
1176 }
1177
1178
1179 static void
1180 Curses_DoTable(short table[NO_SQUARES])
1181 {
1182     short  sq;
1183     ExaminePosition(opponent);
1184
1185     for (sq = 0; sq < NO_SQUARES; sq++)
1186     {
1187         gotoXY(4 + 5 * VIR_C(sq), 5 + 2 * (7 - VIR_R(sq)));
1188         printw("%3d ", table[sq]);
1189     }
1190
1191
1192
1193 static void
1194 Curses_PollForInput(void)
1195 {
1196     int  i;
1197     int  nchar;
1198
1199     if ((i = ioctl((int) 0, FIONREAD, &nchar)))
1200     {
1201         perror("FIONREAD");
1202         fprintf(stderr,
1203                 "You probably have a non-ANSI <ioctl.h>; "
1204                 "see README. %d %d %x\n",
1205                 i, errno, FIONREAD);
1206         exit(1);
1207     }
1208
1209     if (nchar)
1210     {
1211         if (!flag.timeout)
1212             flag.back = true;
1213
1214         flag.bothsides = false;
1215     }
1216 }
1217
1218
1219 static void
1220 Curses_SetupBoard(void)
1221 {
1222     Curses_ShowMessage("'setup' command is not supported in Cursesmode");
1223 }
1224
1225
1226 struct display curses_display =
1227 {
1228     .ChangeAlphaWindow    = Curses_ChangeAlphaWindow,
1229     .ChangeBetaWindow     = Curses_ChangeBetaWindow,
1230     .ChangeHashDepth      = Curses_ChangeHashDepth,
1231     .ChangeSearchDepth    = Curses_ChangeSearchDepth,
1232     .ChangeXwindow        = Curses_ChangeXwindow,
1233     .ClearScreen          = Curses_ClearScreen,
1234     .DoDebug              = Curses_DoDebug,
1235     .DoTable              = Curses_DoTable,
1236     .EditBoard            = Curses_EditBoard,
1237     .ExitShogi            = Curses_ExitShogi,
1238     .GiveHint             = Curses_GiveHint,
1239     .Initialize           = Curses_Initialize,
1240     .ShowNodeCnt          = Curses_ShowNodeCnt,
1241     .OutputMove           = Curses_OutputMove,
1242     .PollForInput         = Curses_PollForInput,
1243     .SetContempt          = Curses_SetContempt,
1244     .SearchStartStuff     = Curses_SearchStartStuff,
1245     .SelectLevel          = Curses_SelectLevel,
1246     .ShowCurrentMove      = Curses_ShowCurrentMove,
1247     .ShowDepth            = Curses_ShowDepth,
1248     .ShowGameType         = Curses_ShowGameType,
1249     .ShowLine             = Curses_ShowLine,
1250     .ShowMessage          = Curses_ShowMessage,
1251     .AlwaysShowMessage    = Curses_AlwaysShowMessage,
1252     .Printf               = Curses_Printf,
1253     .doRequestInputString = Curses_doRequestInputString,
1254     .GetString            = Curses_GetString,
1255     .SetupBoard           = Curses_SetupBoard,
1256     .ShowPatternCount     = Curses_ShowPatternCount,
1257     .ShowPostnValue       = Curses_ShowPostnValue,
1258     .ShowPostnValues      = Curses_ShowPostnValues,
1259     .ShowPrompt           = Curses_ShowPrompt,
1260     .ShowResponseTime     = Curses_ShowResponseTime,
1261     .ShowResults          = Curses_ShowResults,
1262     .ShowSidetoMove       = Curses_ShowSidetoMove,
1263     .ShowStage            = Curses_ShowStage,
1264     .TerminateSearch      = Curses_TerminateSearch,
1265     .UpdateClocks         = Curses_UpdateClocks,
1266     .UpdateDisplay        = Curses_UpdateDisplay,
1267     .help                 = Curses_help,
1268 };