1 /* $Id: cboard.c,v 1.98 2003-09-23 14:30:08 bjk Exp $ */
3 Copyright (C) 2002-2003 Ben Kibbey <bjk@arbornet.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <sys/types.h>
25 #include <sys/socket.h>
52 if (index
== -1 || !config
.agony
|| !curses_initialized
||
53 (status
.mode
== MODE_HISTORY
&& !config
.historyagony
))
57 if ((fp
= fopen(config
.agonyfile
, "r")) == NULL
) {
59 cmessage(ERROR
, ANYKEY
, "%s: %s", config
.agonyfile
, strerror(errno
));
64 if (fscanf(fp
, " %[^\n] ", line
) == 1) {
65 agony
= Realloc(agony
, (index
+ 2) * sizeof(char *));
66 agony
[index
++] = strdup(trim(line
));
73 if (agony
[0] == NULL
|| !index
) {
79 return agony
[random() % index
];
82 void draw_board(BOARD b
, int crow
, int ccol
)
85 int bcol
= 0, brow
= 0;
86 int maxy
= BOARD_HEIGHT
, maxx
= BOARD_WIDTH
;
87 int ncols
= 0, offset
= 1;
88 unsigned coords_y
= 8;
90 for (row
= 0; row
< maxy
; row
++) {
93 for (col
= 0; col
< maxx
; col
++) {
96 chtype piece
, movecount
= 0;
99 if (row
== 0 || row
== maxy
- 2) {
101 mvwaddch(boardw
, row
, col
,
103 ACS_LLCORNER
| CP_BOARD_GRAPHICS
:
104 ACS_ULCORNER
| CP_BOARD_GRAPHICS
));
105 else if (col
== maxx
- 2)
106 mvwaddch(boardw
, row
, col
,
108 ACS_LRCORNER
| CP_BOARD_GRAPHICS
:
109 ACS_URCORNER
| CP_BOARD_GRAPHICS
));
111 mvwaddch(boardw
, row
, col
,
113 ACS_BTEE
| CP_BOARD_GRAPHICS
:
114 ACS_TTEE
| CP_BOARD_GRAPHICS
));
117 mvwaddch(boardw
, row
, col
,
118 LINE_GRAPHIC(ACS_HLINE
| CP_BOARD_GRAPHICS
));
124 if ((row
% 2) && col
== maxx
- 1 && coords_y
) {
125 wattron(boardw
, CP_BOARD_COORDS
);
126 mvwprintw(boardw
, row
, col
, "%d", coords_y
--);
127 wattroff(boardw
, CP_BOARD_COORDS
);
131 if ((col
== 0 || col
== maxx
- 2) && row
!= maxy
- 1) {
133 mvwaddch(boardw
, row
, col
,
135 ACS_RTEE
| CP_BOARD_GRAPHICS
:
136 ACS_LTEE
| CP_BOARD_GRAPHICS
));
138 mvwaddch(boardw
, row
, col
,
139 LINE_GRAPHIC(ACS_VLINE
| CP_BOARD_GRAPHICS
));
144 if ((row
% 2) && !(col
% 4) && row
!= maxy
- 1) {
145 mvwaddch(boardw
, row
, col
,
146 LINE_GRAPHIC(ACS_VLINE
| CP_BOARD_GRAPHICS
));
150 if (!(col
% 4) && row
!= maxy
- 1) {
151 mvwaddch(boardw
, row
, col
,
152 LINE_GRAPHIC(ACS_PLUS
| CP_BOARD_GRAPHICS
));
163 if (((ncols
% 2) && !(offset
% 2)) || (!(ncols
% 2)
169 if (config
.validmoves
&& b
[brow
][bcol
].valid
) {
170 attrs
= (attrwhich
== WHITE
) ? CP_BOARD_MOVES_WHITE
:
171 CP_BOARD_MOVES_BLACK
;
173 if (b
[brow
][bcol
].movecount
) {
174 if (brow
+ 1 != crow
&& bcol
+ 1 != ccol
)
175 movecount
= (b
[brow
][bcol
].movecount
+ '0');
179 attrs
= (attrwhich
== WHITE
) ? CP_BOARD_WHITE
:
182 if (row
== ROWTOMATRIX(crow
) && col
== COLTOMATRIX(ccol
)) {
183 attrs
= CP_BOARD_CURSOR
;
186 if (row
== ROWTOMATRIX(sp
.row
) &&
187 col
== COLTOMATRIX(sp
.col
)) {
188 attrs
= CP_BOARD_SELECTED
;
194 mvwaddch(boardw
, row
, col
, ' ' | attrs
);
197 waddch(boardw
, x_grid_chars
[bcol
] | CP_BOARD_COORDS
);
199 piece
= b
[row
/ 2][bcol
].icon
;
204 if (status
.side
== WHITE
&& isupper(piece
))
206 else if (status
.side
== BLACK
&& islower(piece
))
210 (piece
&& piece
!= int_to_piece(OPEN_SQUARE
)) ?
211 piece
| attrs
: ' ' | attrs
);
217 if (movecount
&& row
!= maxy
-1)
218 waddch(boardw
, movecount
| CP_BOARD_COUNT
);
220 waddch(boardw
, ' ' | attrs
);
228 mvwaddch(boardw
, row
, col
,
229 LINE_GRAPHIC(ACS_HLINE
| CP_BOARD_GRAPHICS
));
239 void copy_board(BOARD s
, BOARD d
)
243 for (row
= 0; row
< 8; row
++) {
244 for (col
= 0; col
< 8; col
++) {
245 d
[row
][col
].icon
= s
[row
][col
].icon
;
246 d
[row
][col
].valid
= s
[row
][col
].valid
;
247 d
[row
][col
].movecount
= s
[row
][col
].movecount
;
254 /* Convert the selected piece to SAN format and validate it. */
255 static char *board_to_san(BOARD b
)
257 static char str
[MAX_PGN_MOVE_LEN
+ 1], *p
;
262 snprintf(str
, sizeof(str
), "%c%i%c%i", x_grid_chars
[sp
.col
- 1], sp
.row
,
263 x_grid_chars
[sp
.destcol
- 1], sp
.destrow
);
266 piece
= piece_to_int(b
[ROWTOBOARD(sp
.row
)][COLTOBOARD(sp
.col
)].icon
);
268 if (piece
== PAWN
&& ((sp
.destrow
== 8 && status
.turn
== WHITE
) ||
269 (sp
.destrow
== 1 && status
.turn
== BLACK
))) {
270 promo
= cmessage(PROMOTION_TITLE
, PROMOTION_PROMPT
, PROMOTION_TEXT
);
272 if (piece_to_int(promo
) == -1)
275 p
= str
+ strlen(str
);
276 *p
++ = toupper(promo
);
282 if ((p
= a2a4tosan(t
, str
)) == NULL
) {
283 cmessage(p
, ANYKEY
, "%s", E_A2A4_PARSE
);
289 if (parse_move_text(t
, p
)) {
290 cmessage(ERROR
, ANYKEY
, "%s: %s", E_INVALID_MOVE
, p
);
299 static int move_to_engine(BOARD b
)
303 if ((p
= board_to_san(b
)) == NULL
)
306 SEND_TO_ENGINE("%s\n", p
);
311 char *book_method(int method
)
317 book
= BOOK_BEST_STR
;
320 book
= BOOK_WORST_STR
;
323 book
= BOOK_PREFER_STR
;
326 book
= BOOK_RANDOM_STR
;
339 static void update_clock(int n
, int *h
, int *m
, int *s
)
342 *m
= (n
% 3600) / 60;
343 *s
= (n
% 3600) % 60;
348 void update_status_window()
351 char buf
[STATUS_WIDTH
- 7];
352 char tmp
[15], *engine
, *mode
;
353 int w
= STATUS_WIDTH
- 10;
360 if (TEST_FLAG(game
[gindex
].flags
, GF_DELETE
)) {
366 if (TEST_FLAG(game
[gindex
].flags
, GF_PERROR
)) {
375 if (TEST_FLAG(game
[gindex
].flags
, GF_MODIFIED
)) {
389 mvwprintw(statusw
, 2, 1, "%*s %-*s", 7, STATUS_FILE_STR
, w
,
390 (loadfile
[0]) ? str_etc(loadfile
, w
, 1) : UNAVAILABLE
);
391 snprintf(buf
, sizeof(buf
), "%i %s %i %s", gindex
+ 1, N_OF_N_STR
, gtotal
,
393 mvwprintw(statusw
, 3, 1, "%*s %-*s", 7, STATUS_GAME_STR
, w
, buf
);
395 switch (status
.mode
) {
397 mode
= MODE_HISTORY_STR
;
400 mode
= MODE_EDIT_STR
;
403 mode
= MODE_PLAY_STR
;
410 mvwprintw(statusw
, 4, 1, "%*s %-*s", 7, STATUS_MODE_STR
, w
, mode
);
412 switch (status
.engine
) {
413 case ENGINE_THINKING
:
414 engine
= ENGINE_THINKING_STR
;
417 engine
= ENGINE_READY_STR
;
419 case ENGINE_INITIALIZING
:
420 engine
= ENGINE_INITIALIZING_STR
;
423 engine
= ENGINE_OFFLINE_STR
;
430 mvwprintw(statusw
, 5, 1, "%*s %-*s", 7, STATUS_ENGINE_STR
, w
, " ");
431 wattron(statusw
, CP_STATUS_ENGINE
);
432 mvwaddstr(statusw
, 5, 9, engine
);
433 wattroff(statusw
, CP_STATUS_ENGINE
);
435 mvwprintw(statusw
, 6, 1, "%*s %-*i", 7, STATUS_DEPTH_STR
, w
,
436 config
.engine_depth
);
438 mvwprintw(statusw
, 7, 1, "%*s %-*s", 7, STATUS_BOOK_STR
, w
,
439 book_method(config
.book_method
));
441 mvwprintw(statusw
, 8, 1, "%*s %-*s", 7, STATUS_TURN_STR
, w
,
442 (status
.turn
== WHITE
) ? WHITE_STR
: BLACK_STR
);
444 strncpy(tmp
, WHITE_STR
, sizeof(tmp
));
445 tmp
[0] = toupper(tmp
[0]);
446 update_clock(game
[gindex
].moveclock
, &h
, &m
, &s
);
447 snprintf(buf
, sizeof(buf
), "c/%-2i %.2i:%.2i:%.2i", game
[gindex
].wcaptures
,
449 mvwprintw(statusw
, 9, 1, "%*s: %-*s", 6, tmp
, w
, buf
);
451 strncpy(tmp
, BLACK_STR
, sizeof(tmp
));
452 tmp
[0] = toupper(tmp
[0]);
453 update_clock(game
[gindex
].moveclock
, &h
, &m
, &s
);
454 snprintf(buf
, sizeof(buf
), "c/%-2i %.2i:%.2i:%.2i", game
[gindex
].bcaptures
,
456 mvwprintw(statusw
, 10, 1, "%*s: %-*s", 6, tmp
, w
, buf
);
458 for (i
= 1; i
< STATUS_WIDTH
- 4; i
++)
459 mvwprintw(statusw
, STATUS_HEIGHT
- 2, i
, " ");
461 status
.notify
= (status
.notify
) ? status
.notify
: GAME_HELP_PROMPT
;
464 wattron(statusw
, CP_STATUS_NOTIFY
);
465 mvwprintw(statusw
, STATUS_HEIGHT
- 2,
466 CENTERX(STATUS_WIDTH
, status
.notify
), "%s", status
.notify
);
467 wattroff(statusw
, CP_STATUS_NOTIFY
);
473 void update_history_window()
475 char buf
[HISTORY_WIDTH
];
476 HISTORY h
= {{0},NULL
,{0}};
479 index
= (game
[gindex
].hindex
+ 1) / 2;
481 if ((game
[gindex
].htotal
% 2))
482 total
= (game
[gindex
].htotal
+ 1) / 2;
484 total
= game
[gindex
].htotal
/ 2;
486 if (game
[gindex
].htotal
)
487 snprintf(buf
, sizeof(buf
), "%u %s %u%s", index
, N_OF_N_STR
, total
,
488 (movestep
== 1) ? HISTORY_MOVE_STEP
: "");
490 strncpy(buf
, UNAVAILABLE
, sizeof(buf
));
492 mvwprintw(historyw
, 2, 1, "%*s %-*s", 10, HISTORY_MOVE_STR
,
493 HISTORY_WIDTH
- 13, buf
);
495 if (get_history_by_index(game
[gindex
].hindex
, &h
))
496 memset(&h
, 0, sizeof(HISTORY
));
498 snprintf(buf
, sizeof(buf
), "%s %s", (h
.move
[0]) ? h
.move
: UNAVAILABLE
,
499 ((h
.comment
&& h
.comment
[0]) || h
.nag
[0]) ? HISTORY_ANNO_NEXT
: "");
500 mvwprintw(historyw
, 3, 1, "%s %-*s", HISTORY_MOVE_NEXT_STR
,
501 HISTORY_WIDTH
- 13, buf
);
503 if (get_history_by_index(game
[gindex
].hindex
- 1, &h
))
504 memset(&h
, 0, sizeof(HISTORY
));
506 snprintf(buf
, sizeof(buf
), "%s %s", (h
.move
[0]) ? h
.move
: UNAVAILABLE
,
507 ((h
.comment
&& h
.comment
[0]) || h
.nag
[0]) ? HISTORY_ANNO_PREV
: "");
508 mvwprintw(historyw
, 4, 1, "%s %-*s", HISTORY_MOVE_PREV_STR
,
509 HISTORY_WIDTH
- 13, buf
);
513 void update_tag_window()
516 int w
= TAG_WIDTH
- 10;
518 for (i
= 0; i
< 7; i
++) {
519 char *value
= game
[gindex
].tag
[i
].value
;
522 if ((*value
== '?' || *value
== '-') && value
[1] == '\0')
524 else if (strcmp(game
[gindex
].tag
[i
].name
, "Result") == 0) {
525 for (n
= 0; n
< NARRAY(fancy_results
); n
++) {
526 if (strcmp(value
, fancy_results
[n
].pgn
) == 0) {
527 value
= fancy_results
[n
].fancy
;
533 value
= str_etc(value
, w
, 0);
535 mvwprintw(tagw
, (i
+ 2), 1, "%*s: %-*s", 6, game
[gindex
].tag
[i
].name
,
542 void draw_prompt(WINDOW
*win
, int y
, int width
, const char *str
, chtype attr
)
548 for (i
= 1; i
< width
- 1; i
++)
549 mvwaddch(win
, y
, i
, ' ');
551 mvwprintw(win
, y
, CENTERX(width
, str
), "%s", str
);
557 void draw_window_title(WINDOW
*win
, const char *title
, int width
, chtype attr
,
565 for (i
= 1; i
< width
- 1; i
++)
566 mvwaddch(win
, 1, i
, ' ');
568 mvwprintw(win
, 1, CENTERX(width
, title
), "%s", title
);
573 box(win
, ACS_VLINE
, ACS_HLINE
);
574 wattroff(win
, battr
);
581 update_status_window();
582 update_history_window();
586 static void game_next_prev(int n
, int count
)
592 if (gindex
+ count
> gtotal
- 1) {
602 if (gindex
- count
< 0) {
618 void free_game_data()
625 for (i
= 0; i
< gtotal
; i
++) {
626 free_historydata(&game
[i
].history
, 0, game
[i
].htotal
);
627 free(game
[i
].history
);
628 free_tag_data(game
[i
].tag
, game
[i
].tindex
);
635 static void delete_game(int which
)
641 for (i
= 0; i
< gtotal
; i
++) {
642 if (i
== which
|| TEST_FLAG(game
[i
].flags
, GF_DELETE
)) {
643 free_historydata(&game
[i
].history
, 0, game
[i
].htotal
);
644 free(game
[i
].history
);
645 free_tag_data(game
[i
].tag
, game
[i
].tindex
);
650 g
= Realloc(g
, (gi
+ 2) * sizeof(GAME
));
652 memcpy(&g
[gi
], &game
[i
], sizeof(GAME
));
654 g
[gi
].tag
= game
[i
].tag
;
655 g
[gi
].history
= game
[i
].history
;
663 if (which
+ 1 >= gtotal
)
674 /* FIXME dont show out of reach counts. Diagonals. */
676 static void number_valid_moves(BOARD b, int srow, int scol)
681 for (row = srow + 1, col = scol, count = 1; VALIDFILE(row); row++) {
682 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].valid)
685 b[ROWTOBOARD(row)][COLTOBOARD(col)].movecount = count++;
688 for (row = srow - 1, col = scol, count = 1; VALIDFILE(row); row--) {
689 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].valid)
692 b[ROWTOBOARD(row)][COLTOBOARD(col)].movecount = count++;
695 for (col = scol + 1, row = srow, count = 1; VALIDFILE(col); col++) {
696 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].valid)
699 b[ROWTOBOARD(row)][COLTOBOARD(col)].movecount = count++;
702 for (col = scol - 1, row = srow, count = 1; VALIDFILE(col); col--) {
703 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].valid)
706 b[ROWTOBOARD(row)][COLTOBOARD(col)].movecount = count++;
714 static void get_valid_cursor(BOARD b, int which, int count, int *crow,
715 int *ccol, int minr, int maxr, int minc, int maxc)
720 if (which == UP || which == RIGHT)
736 if (!VALIDFILE(*crow))
740 for (row = *crow + incr, col = *ccol; VALIDFILE(row); row += incr) {
741 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].valid)
759 if (!VALIDFILE(*ccol))
763 for (col = *ccol + incr, row = *crow; VALIDFILE(col); col += incr) {
764 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].valid)
776 if (*ccol < sp.col || (which == DOWN || which == LEFT))
781 if (*ccol < sp.col && (which == DOWN || which == LEFT))
784 for (row = *crow + incr; VALIDFILE(row); row += incr) {
785 for (col = *ccol + cincr; VALIDFILE(col); col += cincr) {
786 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].valid)
796 number_valid_moves(board, *crow, *ccol);
801 static int find_move_exp(const char *str
, int init
, int which
, int count
)
806 static int firstrun
= 1;
815 if ((ret
= regcomp(&r
, str
, REG_EXTENDED
|REG_NOSUB
)) != 0) {
816 regerror(ret
, &r
, errbuf
, sizeof(errbuf
));
817 cmessage(E_REGCOMP_TITLE
, ANYKEY
, "%s", errbuf
);
824 incr
= (which
== 0) ? -(1) : 1;
826 for (i
= game
[gindex
].hindex
+ incr
- 1, found
= 0; ; i
+= incr
) {
827 if (i
== game
[gindex
].hindex
- 1)
830 if (i
> game
[gindex
].htotal
)
833 i
= game
[gindex
].htotal
;
835 ret
= regexec(&r
, game
[gindex
].history
[i
].move
, 0, 0, 0);
838 if (count
== ++found
) {
843 if (ret
!= REG_NOMATCH
) {
844 regerror(ret
, &r
, errbuf
, sizeof(errbuf
));
845 cmessage(E_REGEXEC_TITLE
, ANYKEY
, "%s", errbuf
);
854 static int toggle_delete_flag(int index
)
858 if (TEST_FLAG(game
[index
].flags
, GF_DELETE
))
859 CLEAR_FLAG(game
[index
].flags
, GF_DELETE
);
861 SET_FLAG(game
[index
].flags
, GF_DELETE
);
864 for (i
= n
= 0; i
< gtotal
; i
++) {
865 if (TEST_FLAG(game
[i
].flags
, GF_DELETE
))
870 cmessage(NULL
, ANYKEY
, "%s", E_DELETE_GAME
);
871 CLEAR_FLAG(game
[index
].flags
, GF_DELETE
);
878 static void edit_save_tags(int index
)
883 if ((t
= edit_tags(board
, game
[index
].tag
, game
[index
].tindex
, 1)) == NULL
)
886 game
[index
].tindex
= 0;
888 for (i
= 0; t
[i
].name
; i
++) {
889 add_tag(&game
[index
].tag
, &game
[index
].tindex
, t
[i
].name
, t
[i
].value
);
894 SET_FLAG(game
[index
].flags
, GF_MODIFIED
);
898 static int find_game_exp(char *str
, int which
, int count
)
900 char *nstr
= NULL
, *exp
= NULL
;
907 int incr
= (which
== 0) ? -(1) : 1;
909 strncpy(buf
, str
, sizeof(buf
));
912 if (strstr(tmp
, ":") != NULL
) {
913 nstr
= strsep(&tmp
, ":");
915 if ((ret
= regcomp(&nexp
, nstr
,
916 REG_ICASE
|REG_EXTENDED
|REG_NOSUB
)) != 0) {
917 regerror(ret
, &nexp
, errbuf
, sizeof(errbuf
));
918 cmessage(E_REGCOMP_TITLE
, ANYKEY
, "%s", errbuf
);
929 if ((ret
= regcomp(&vexp
, exp
, REG_EXTENDED
|REG_NOSUB
)) != 0) {
930 regerror(ret
, &vexp
, errbuf
, sizeof(errbuf
));
931 cmessage(E_REGCOMP_TITLE
, ANYKEY
, "%s", errbuf
);
938 for (g
= gindex
+ incr
, found
= 0; ; g
+= incr
) {
949 for (t
= 0; t
< game
[g
].tindex
; t
++) {
951 if (regexec(&nexp
, game
[g
].tag
[t
].name
, 0, 0, 0) == 0) {
952 if (regexec(&vexp
, game
[g
].tag
[t
].value
, 0, 0, 0) == 0) {
953 if (count
== ++found
) {
961 if (regexec(&vexp
, game
[g
].tag
[t
].value
, 0, 0, 0) == 0) {
962 if (count
== ++found
) {
983 void edit_board(BOARD b
)
987 p
= b
[ROWTOBOARD(sp
.row
)][COLTOBOARD(sp
.col
)].icon
;
988 b
[ROWTOBOARD(sp
.destrow
)][COLTOBOARD(sp
.destcol
)].icon
= p
;
989 b
[ROWTOBOARD(sp
.row
)][COLTOBOARD(sp
.col
)].icon
=
990 int_to_piece(OPEN_SQUARE
);
995 // Updates the notification line in the status window then refreshes the
997 void update_status_notify(char *fmt
, ...)
1000 #ifdef HAVE_VASPRINTF
1007 if (status
.notify
) {
1008 free(status
.notify
);
1009 status
.notify
= NULL
;
1010 update_status_window();
1017 #ifdef HAVE_VASPRINTF
1018 vasprintf(&line
, fmt
, ap
);
1020 vsnprintf(line
, sizeof(line
), fmt
, ap
);
1025 free(status
.notify
);
1027 status
.notify
= strdup(line
);
1029 #ifdef HAVE_VASPRINTF
1032 update_status_window();
1037 int error_recover
= 0;
1040 int crow
= 2, ccol
= 5;
1041 char moveexp
[255] = {0};
1042 char gameexp
[255] = {0};
1043 int delete_count
= 0;
1044 int markstart
= -1, markend
= -1;
1047 /* This is to just initialize the default tags etc. */
1048 parse_pgn_file(board
, moveexp
);
1049 draw_board(board
, crow
, ccol
);
1050 update_tag_window();
1055 if (parse_pgn_file(board
, loadfile
))
1059 if (parse_fen_file(board
, loadfile
))
1067 gindex
= gtotal
- 1;
1068 markstart
= -1, markend
= -1;
1071 init_history(board
);
1073 update_status_notify("%s", GAME_HELP_PROMPT
);
1079 update_tag_window();
1084 int i
, x
, n
= 0, len
= 0;
1085 char fdbuf
[8192] = {0};
1089 char tfile
[FILENAME_MAX
];
1090 int minr
, maxr
, minc
, maxc
;
1092 if (engine_initialized
) {
1097 FD_SET(enginefd
[0], &fds
);
1099 for (i
= 0; i
< gtotal
; i
++) {
1100 if (game
[i
].sockfd
> 0) {
1101 if (game
[i
].sockfd
> n
)
1104 FD_SET(game
[i
].sockfd
, &fds
);
1108 n
= (n
> enginefd
[0]) ? n
: enginefd
[0];
1110 if ((n
= select(n
+ 1, &fds
, NULL
, NULL
, &tv
)) > 0) {
1111 if (FD_ISSET(enginefd
[0], &fds
)) {
1112 len
= read(enginefd
[0], fdbuf
, sizeof(fdbuf
));
1115 if (errno
!= EAGAIN
) {
1116 cmessage(ERROR
, ANYKEY
, "Attempt #%i. read(): %s",
1117 ++error_recover
, strerror(errno
));
1123 parse_engine_output(board
, fdbuf
);
1129 for (i
= 0; i
< gtotal
; i
++) {
1130 if (game
[i
].sockfd
<= 0)
1133 if (FD_ISSET(game
[i
].sockfd
, &fds
)) {
1134 len
= recv(game
[i
].sockfd
, fdbuf
, sizeof(fdbuf
), 0);
1137 if (errno
!= EAGAIN
) {
1138 cmessage(ERROR
, ANYKEY
,
1139 "Attempt #%i. recv(): %s",
1140 ++error_recover
, strerror(errno
));
1146 parse_ics_output(fdbuf
);
1155 cmessage(ERROR
, ANYKEY
, "select(): %s", strerror(errno
));
1163 draw_board(board
, crow
, ccol
);
1165 wmove(boardw
, ROWTOMATRIX(crow
), COLTOMATRIX(ccol
));
1176 if ((c
= wgetch(boardw
)) == ERR
)
1191 if (game
[gindex
].htotal
)
1196 add_tag(&game
[gindex
].tag
, &game
[gindex
].tindex
,
1197 "FEN", board_to_fen(board
, game
[gindex
]));
1198 status
.mode
= MODE_PLAY
;
1199 game
[gindex
].fentag
= game
[gindex
].tindex
- 1;
1202 status
.mode
= MODE_EDIT
;
1214 if (!*gameexp
|| c
== '?') {
1215 if ((tmp
= get_input(GAME_FIND_EXPRESSION_TITLE
, gameexp
,
1216 1, 1, GAME_FIND_EXPRESSION_PROMPT
, NULL
,
1217 NULL
, 0, -1)) == NULL
)
1220 strncpy(gameexp
, tmp
, sizeof(gameexp
));
1223 if ((n
= find_game_exp(gameexp
, (c
== '{') ? 0 : 1,
1224 (count
) ? count
: 1)) == -1)
1228 init_history(board
);
1230 update_tag_window();
1282 if (status
.engine
!= ENGINE_READY
)
1285 n
= (count
) ? count
: 1;
1288 if (config
.engine_depth
- n
< 0)
1291 n
-= config
.engine_depth
;
1294 n
+= config
.engine_depth
;
1296 SEND_TO_ENGINE("depth %i\n", abs(n
));
1301 if (game
[gindex
].htotal
< 2)
1306 if (!*moveexp
|| c
== '/') {
1307 if ((tmp
= get_input(FIND_REGEXP
, moveexp
, 1, 1, NULL
,
1308 NULL
, NULL
, 0, -1)) == NULL
)
1311 strncpy(moveexp
, tmp
, sizeof(moveexp
));
1315 if ((n
= find_move_exp(moveexp
, n
, (c
== '[') ? 0 : 1,
1316 (count
) ? count
: 1)) == -1)
1319 game
[gindex
].hindex
= n
;
1320 parse_history_move(board
, game
[gindex
].hindex
);
1324 view_annotation(game
[gindex
].hindex
);
1327 view_annotation(game
[gindex
].hindex
- 1);
1330 game_next_prev(1, (count
) ? count
: 1);
1338 status
.mode
= MODE_HISTORY
;
1342 game_next_prev(0, (count
) ? count
: 1);
1350 status
.mode
= MODE_HISTORY
;
1354 if (status
.mode
!= MODE_HISTORY
|| game
[gindex
].htotal
< 2)
1358 if ((tmp = get_input(GAME_HISTORY_JUMP_TITLE, NULL, 1, 1,
1359 NULL, NULL, NULL, 0, FIELD_TYPE_INTEGER, 1, 0,
1360 game[gindex].htotal)) == NULL)
1365 if ((tmp
= get_input(GAME_HISTORY_JUMP_TITLE
, NULL
, 1, 1,
1366 NULL
, NULL
, NULL
, 0, -1)) == NULL
)
1369 if (!isinteger(tmp
))
1377 if (i
> game
[gindex
].htotal
|| i
< 0)
1380 game
[gindex
].hindex
= i
* 2;
1381 init_history(board
);
1389 if ((tmp = get_input(GAME_JUMP_TITLE, NULL, 1, 1, NULL, NULL,
1390 NULL, 0, FIELD_TYPE_INTEGER, 1, 1, gtotal))
1396 if ((tmp
= get_input(GAME_JUMP_TITLE
, NULL
, 1, 1, NULL
,
1397 NULL
, NULL
, 0, -1)) == NULL
)
1400 if (!isinteger(tmp
))
1408 if (--i
> gtotal
- 1 || i
< 0)
1412 init_history(board
);
1414 update_tag_window();
1421 board
[ROWTOBOARD(sp
.row
)][COLTOBOARD(sp
.col
)].icon
=
1422 int_to_piece(OPEN_SQUARE
);
1424 board
[ROWTOBOARD(crow
)][COLTOBOARD(ccol
)].icon
=
1425 int_to_piece(OPEN_SQUARE
);
1427 sp
.icon
= sp
.row
= sp
.col
= 0;
1434 if (count
&& !delete_count
) {
1440 if (markstart
>= 0 && markend
>= 0) {
1441 if (markstart
> markend
) {
1443 markstart
= markend
;
1447 for (i
= markstart
; i
<= markend
; i
++) {
1448 if (toggle_delete_flag(i
))
1453 if (toggle_delete_flag(gindex
))
1457 markstart
= markend
= -1;
1458 update_status_window();
1462 cmessage(NULL
, ANYKEY
, "%s", E_DELETE_GAME
);
1468 for (i
= n
= 0; i
< gtotal
; i
++) {
1469 if (TEST_FLAG(game
[i
].flags
, GF_DELETE
))
1474 tmp
= GAME_DELETE_GAME_TEXT
;
1477 cmessage(NULL
, ANYKEY
, "%s", E_DELETE_GAME
);
1481 tmp
= GAME_DELETE_ALL_TEXT
;
1484 if (config
.deleteprompt
) {
1485 if ((c
= cmessage(NULL
, YESNO
, "%s", tmp
)) != 'y')
1489 delete_game((!n
) ? gindex
: -1);
1490 init_history(board
);
1492 update_tag_window();
1495 annotate
= game
[gindex
].hindex
;
1497 if (annotate
&& game
[gindex
].history
[annotate
- 1].move
[0])
1502 snprintf(buf
, sizeof(buf
), "%s \"%s\"", ANNOTATION_EDIT_TITLE
,
1503 game
[gindex
].history
[annotate
].move
);
1505 tmp
= get_input(buf
, game
[gindex
].history
[annotate
].comment
,
1506 0, 0, NAG_PROMPT
, history_edit_nag
, (void *)annotate
,
1509 if (!tmp
&& (!game
[gindex
].history
[annotate
].comment
||
1510 !*game
[gindex
].history
[annotate
].comment
))
1512 else if (tmp
&& game
[gindex
].history
[annotate
].comment
) {
1513 if (strcmp(tmp
, game
[gindex
].history
[annotate
].comment
)
1518 len
= (tmp
) ? strlen(tmp
) + 1 : 1;
1520 game
[gindex
].history
[annotate
].comment
=
1521 Realloc(game
[gindex
].history
[annotate
].comment
, len
);
1523 strncpy(game
[gindex
].history
[annotate
].comment
,
1524 (tmp
) ? tmp
: "", len
);
1526 SET_FLAG(game
[gindex
].flags
, GF_MODIFIED
);
1531 edit_save_tags(gindex
);
1533 update_tag_window();
1539 c
= message(GAME_EDIT_TITLE
, GAME_EDIT_PROMPT
, "%s",
1542 if (piece_to_int(c
) == -1)
1545 board
[ROWTOBOARD(crow
)][COLTOBOARD(ccol
)].icon
= c
;
1548 edit_tags(board
, game
[gindex
].tag
, game
[gindex
].tindex
, 0);
1551 if (status
.mode
== MODE_HISTORY
||
1552 status
.engine
== ENGINE_THINKING
)
1555 status
.engine
= ENGINE_THINKING
;
1556 update_status_window();
1557 SEND_TO_ENGINE("go\n");
1560 if (config
.book_method
== -1 || status
.engine
==
1561 ENGINE_THINKING
|| config
.engine
!= GNUCHESS
)
1564 if (config
.book_method
+ 1 >= BOOK_MAX
)
1567 n
= config
.book_method
+ 1;
1569 SEND_TO_ENGINE("book %s\n", book_methods
[n
]);
1572 if (status
.mode
== MODE_HISTORY
) {
1573 if (game
[gindex
].openingside
== BLACK
) {
1574 cmessage(NULL
, ANYKEY
, "%s", E_RESUME_BLACK
);
1578 if (game
[gindex
].hindex
!= game
[gindex
].htotal
) {
1580 if ((c
= message(NULL
, YESNO
, "%s",
1581 GAME_RESUME_HISTORY_TEXT
)) != 'y')
1586 if (TEST_FLAG(game
[gindex
].flags
, GF_GAMEOVER
))
1590 wtimeout(boardw
, 70);
1592 if (!engine_initialized
) {
1593 if (start_chess_engine() < 0)
1601 oldhistorytotal
= game
[gindex
].htotal
;
1602 game
[gindex
].htotal
= game
[gindex
].hindex
;
1603 status
.mode
= MODE_PLAY
;
1604 status
.engine
= ENGINE_READY
;
1607 if (config
.engine
!= GNUCHESS
)
1608 SEND_TO_ENGINE("read %s\n", config
.fifo
);
1610 SEND_TO_ENGINE("\npgnload %s\n", config
.fifo
);
1616 if (!game
[gindex
].htotal
|| status
.engine
== ENGINE_THINKING
)
1619 wtimeout(boardw
, -1);
1620 init_history(board
);
1623 /* FIXME dies reading FIFO sometimes. */
1624 if (status
.mode
!= MODE_PLAY
|| !game
[gindex
].htotal
)
1627 history_previous(board
, (count
) ? count
* 2 : 2, &crow
, &ccol
);
1628 oldhistorytotal
= game
[gindex
].htotal
;
1629 game
[gindex
].htotal
= game
[gindex
].hindex
;
1631 if (status
.engine
== CRAFTY
)
1632 SEND_TO_ENGINE("read %s\n", config
.fifo
);
1634 SEND_TO_ENGINE("\npgnload %s\n", config
.fifo
);
1636 update_history_window();
1639 if ((tmp
= get_input(GAME_LOAD_TITLE
, NULL
, 1, 1,
1640 BROWSER_PROMPT
, browse_directory
, NULL
,
1644 tmp
= tilde_expand(tmp
);
1646 if (parse_pgn_file(board
, tmp
))
1649 gindex
= gtotal
- 1;
1650 strncpy(loadfile
, tmp
, sizeof(loadfile
));
1651 init_history(board
);
1653 update_tag_window();
1660 n
= message(NULL
, GAME_SAVE_MULTI_PROMPT
, "%s",
1661 GAME_SAVE_MULTI_TEXT
);
1668 update_status_notify("%s", NOTIFY_SAVE_ABORTED
);
1673 if ((tmp
= get_input(GAME_SAVE_TITLE
, loadfile
, 1, 1,
1674 BROWSER_PROMPT
, browse_directory
, NULL
,
1675 '\t', -1)) == NULL
) {
1676 update_status_notify("%s", NOTIFY_SAVE_ABORTED
);
1680 tmp
= tilde_expand(tmp
);
1682 if (strstr(tmp
, ".") == NULL
&& compression_cmd(tmp
, 0)
1684 snprintf(tfile
, sizeof(tfile
), "%s.pgn", tmp
);
1688 if (save_pgn(tmp
, 0, x
)) {
1689 update_status_notify("%s", NOTIFY_SAVE_FAILED
);
1693 update_status_notify("%s", NOTIFY_SAVED
);
1700 n
= help(GAME_HELP_INDEX_TITLE
,
1701 GAME_HELP_INDEX_PROMPT
, mainhelp
);
1705 help(GAME_HELP_HISTORY_TITLE
, ANYKEY
, historyhelp
);
1708 help(GAME_HELP_PLAY_TITLE
, ANYKEY
, playhelp
);
1711 help(GAME_HELP_EDIT_TITLE
, ANYKEY
, edithelp
);
1714 help(GAME_HELP_GAME_TITLE
, ANYKEY
, gamehelp
);
1726 if (cmessage(NULL
, YESNO
, "%s", GAME_NEW_PROMPT
) != 'y')
1730 status
.mode
= MODE_PLAY
;
1741 parse_pgn_file(board
, loadfile
);
1744 game
[gindex
].wcaptures
= game
[gindex
].bcaptures
= 0;
1745 crow
= (status
.side
== WHITE
) ? 2 : 7;
1748 if (status
.engine
== ENGINE_OFFLINE
||
1749 engine_initialized
== 0) {
1750 if (start_chess_engine() < 0)
1754 SEND_TO_ENGINE("\nnew\n");
1755 set_engine_defaults();
1756 status
.engine
= ENGINE_READY
;
1757 update_status_notify(NULL
);
1759 update_tag_window();
1764 keypad(boardw
, TRUE
);
1769 if (status
.engine
== ENGINE_THINKING
)
1772 if (status
.engine
== ENGINE_OFFLINE
)
1775 if ((tmp
= get_input_str_clear(ENGINE_CMD_TITLE
, NULL
))
1777 SEND_TO_ENGINE("%s\n", tmp
);
1781 sp
.icon
= sp
.row
= sp
.col
= 0;
1782 markend
= markstart
= 0;
1786 update_status_notify(NULL
);
1789 if (config
.validmoves
)
1790 reset_valid_moves(board
);
1797 count
= count
* 10 + n
;
1801 update_status_notify("Repeat %i", count
);
1804 if (status
.mode
== MODE_HISTORY
) {
1805 history_next(board
, (count
> 0) ?
1806 config
.jumpcount
* count
* movestep
:
1807 config
.jumpcount
* movestep
, &crow
, &ccol
);
1813 if (sp.icon && config.validmoves) {
1814 get_valid_cursor(board, UP, (count) ? count : 1,
1815 &crow, &ccol, minr, maxr, minc, maxc);
1832 if (status
.mode
== MODE_HISTORY
) {
1833 history_previous(board
, (count
) ?
1834 config
.jumpcount
* count
* movestep
:
1835 config
.jumpcount
* movestep
, &crow
, &ccol
);
1841 if (sp.icon && config.validmoves) {
1842 get_valid_cursor(board, DOWN, (count) ? count : 1,
1843 &crow, &ccol, minr, maxr, minc, maxc);
1851 update_status_notify(NULL
);
1861 if (status
.mode
== MODE_HISTORY
) {
1862 history_previous(board
, (count
) ?
1863 count
* movestep
: movestep
, &crow
, &ccol
);
1869 if (sp.icon && config.validmoves) {
1870 get_valid_cursor(board, LEFT, (count) ? count : 1,
1871 &crow, &ccol, minr, maxr, minc, maxc);
1888 if (status
.mode
== MODE_HISTORY
) {
1889 history_next(board
, (count
) ? count
* movestep
: movestep
,
1896 if (sp.icon && config.validmoves) {
1897 get_valid_cursor(board, RIGHT, (count) ? count : 1,
1898 &crow, &ccol, minr, maxr, minc, maxc);
1915 if (status
.mode
== MODE_HISTORY
)
1918 if (status
.turn
== BLACK
&& status
.side
== WHITE
) {
1919 status
.side
= BLACK
;
1922 else if (status
.turn
== WHITE
&& status
.side
== BLACK
) {
1923 status
.side
= WHITE
;
1928 SEND_TO_ENGINE("\nswitch\n");
1931 if (!editmode
&& status
.mode
== MODE_HISTORY
) {
1937 update_history_window();
1941 if ((status
.engine
== ENGINE_OFFLINE
|| !engine_initialized
)
1943 if (start_chess_engine() < 0) {
1951 wtimeout(boardw
, 70);
1953 if (sp
.icon
|| (!editmode
&& status
.engine
== ENGINE_THINKING
)) {
1958 sp
.icon
= mvwinch(boardw
, ROWTOMATRIX(crow
),
1959 COLTOMATRIX(ccol
)+1) & A_CHARTEXT
;
1961 if (sp
.icon
== ' ') {
1966 if (!editmode
&& ((islower(sp
.icon
) && status
.turn
!= BLACK
) ||
1967 (isupper(sp
.icon
) && status
.turn
!= WHITE
))) {
1968 message(NULL
, ANYKEY
, "%s", E_SELECT_TURN
);
1976 if (!editmode
&& config
.validmoves
) {
1977 get_valid_moves(board
, piece_to_int(sp
.icon
), sp
.row
,
1978 sp
.col
, &minr
, &maxr
, &minc
, &maxc
);
1980 number_valid_moves(board, sp.row, sp.col);
1984 if (status
.mode
== MODE_PLAY
)
1990 pushkey
= count
= 0;
1991 update_status_notify(NULL
);
1993 if (!editmode
&& status
.mode
== MODE_HISTORY
)
1996 if (status
.engine
== ENGINE_THINKING
) {
2009 sp
.icon
= sp
.row
= sp
.col
= 0;
2013 if (move_to_engine(board
)) {
2014 if (config
.validmoves
)
2015 reset_valid_moves(board
);
2017 if (TEST_FLAG(game
[gindex
].flags
, GF_GAMEOVER
)) {
2018 CLEAR_FLAG(game
[gindex
].flags
, GF_GAMEOVER
);
2019 SET_FLAG(game
[gindex
].flags
, GF_MODIFIED
);
2040 void usage(const char *pn
)
2044 for (i
= 0; cmdlinehelp
[i
]; i
++)
2045 fputs(cmdlinehelp
[i
], stderr
);
2050 void catch_signal(int which
)
2062 cmessage(NULL
, ANYKEY
, "%s", E_BROKEN_PIPE
);
2071 keypad(boardw
, TRUE
);
2080 int main(int argc
, char *argv
[])
2084 char buf
[FILENAME_MAX
];
2085 char datadir
[FILENAME_MAX
];
2087 if ((config
.pwd
= getpwuid(getuid())) == NULL
)
2088 err(EXIT_FAILURE
, "getpwuid()");
2090 snprintf(datadir
, sizeof(datadir
), "%s/.cboard", config
.pwd
->pw_dir
);
2091 snprintf(buf
, sizeof(buf
), "%s/cc.data", datadir
);
2092 config
.ccfile
= strdup(buf
);
2093 snprintf(buf
, sizeof(buf
), "%s/nag.data", datadir
);
2094 config
.nagfile
= strdup(buf
);
2095 snprintf(buf
, sizeof(buf
), "%s/agony.data", datadir
);
2096 config
.agonyfile
= strdup(buf
);
2097 snprintf(buf
, sizeof(buf
), "%s/config", datadir
);
2098 config
.configfile
= strdup(buf
);
2099 snprintf(buf
, sizeof(buf
), "%s/fifo", datadir
);
2100 config
.fifo
= strdup(buf
);
2101 snprintf(buf
, sizeof(buf
), "%s/tmpfile", datadir
);
2102 config
.tmpfile
= strdup(buf
);
2104 if (stat(datadir
, &st
) == -1) {
2105 if (errno
== ENOENT
) {
2106 if (mkdir(datadir
, 0755) == -1)
2107 err(EXIT_FAILURE
, "%s", datadir
);
2110 err(EXIT_FAILURE
, "%s", datadir
);
2115 if (!S_ISDIR(st
.st_mode
))
2116 errx(EXIT_FAILURE
, "%s: %s", datadir
, E_NOTADIR
);
2118 if (access(config
.fifo
, R_OK
) == -1 && errno
== ENOENT
) {
2119 if (mkfifo(config
.fifo
, 0600) == -1)
2120 err(EXIT_FAILURE
, "%s", config
.fifo
);
2125 while ((opt
= getopt(argc
, argv
, "hp:vu:e:f:i:")) != -1) {
2133 while ((tmp
= strsep(&optarg
, ":")) != NULL
) {
2136 config
.ics_user
= optarg
;
2139 config
.ics_passwd
= optarg
;
2149 while ((tmp
= strsep(&optarg
, ":")) != NULL
) {
2152 strncpy(config
.ics_server
, tmp
,
2153 sizeof(config
.ics_server
));
2156 if (!isinteger(tmp
))
2159 config
.ics_port
= atoi(tmp
);
2167 printf("%s (%s)\n%s\n", PACKAGE_STRING
, curses_version(),
2171 filetype
= PGN_FILE
;
2172 strncpy(loadfile
, optarg
, sizeof(loadfile
));
2175 filetype
= FEN_FILE
;
2176 strncpy(loadfile
, optarg
, sizeof(loadfile
));
2179 filetype
= EPD_FILE
;
2180 strncpy(loadfile
, optarg
, sizeof(loadfile
));
2188 if (access(config
.configfile
, R_OK
) == 0)
2189 parse_rcfile(config
.configfile
);
2191 signal(SIGPIPE
, catch_signal
);
2192 signal(SIGCONT
, catch_signal
);
2193 signal(SIGSTOP
, catch_signal
);
2194 signal(SIGINT
, catch_signal
);
2202 if (parse_pgn_file(board, loadfile))
2206 if (parse_fen_file(board, loadfile))
2216 if (initscr() == NULL
)
2217 errx(EXIT_FAILURE
, "%s", E_INITCURSES
);
2219 curses_initialized
= 1;
2221 if (has_colors() == TRUE
&& start_color() == OK
)
2224 boardw
= newwin(BOARD_HEIGHT
, BOARD_WIDTH
, 0, COLS
- BOARD_WIDTH
);
2225 boardp
= new_panel(boardw
);
2226 historyw
= newwin(HISTORY_HEIGHT
, HISTORY_WIDTH
, LINES
- HISTORY_HEIGHT
,
2227 COLS
- HISTORY_WIDTH
);
2228 historyp
= new_panel(historyw
);
2229 statusw
= newwin(STATUS_HEIGHT
, STATUS_WIDTH
, LINES
- STATUS_HEIGHT
, 0);
2230 statusp
= new_panel(statusw
);
2231 tagw
= newwin(TAG_HEIGHT
, TAG_WIDTH
, 0, 0);
2232 tagp
= new_panel(tagw
);
2233 keypad(boardw
, TRUE
);
2234 // leaveok(boardw, TRUE);
2235 leaveok(tagw
, TRUE
);
2236 leaveok(statusw
, TRUE
);
2237 leaveok(historyw
, TRUE
);
2242 wbkgd(boardw
, CP_BOARD_WINDOW
);
2243 wbkgd(statusw
, CP_STATUS_WINDOW
);
2244 draw_window_title(statusw
, STATUS_WINDOW_TITLE
, STATUS_WIDTH
,
2245 CP_STATUS_TITLE
, CP_STATUS_BORDER
);
2246 wbkgd(tagw
, CP_TAG_WINDOW
);
2247 draw_window_title(tagw
, TAG_WINDOW_TITLE
, TAG_WIDTH
, CP_TAG_TITLE
,
2249 wbkgd(historyw
, CP_HISTORY_WINDOW
);
2250 draw_window_title(historyw
, HISTORY_WINDOW_TITLE
, HISTORY_WIDTH
,
2251 CP_HISTORY_TITLE
, CP_HISTORY_BORDER
);
2260 del_panel(historyp
);