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