fixed jaws version
[xboard.git] / winboard / jaws.c
1 /*\r
2  * JAWS.c -- Code for Windows front end to XBoard to use it with JAWS\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts.\r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009 Free Software Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 // This file collects all patches for the JAWS version, so they can all be included in winboard.c\r
56 // in one big swoop. At the bottom of this file you can read instructions for how to patch\r
57 // WinBoard to work with JAWS with the aid of this file. Note that the code in this file\r
58 // is for WinBoard 4.3 and later; for older WB versions you would have to throw out the\r
59 // piece names for all pieces from Guard to Unicorn, #define ONE as '1', AAA as 'a',\r
60 // BOARD_LEFT as 0, BOARD_RGHT and BOARD_HEIGHT as 8, and set holdingssizes to 0.\r
61 // You will need to build with jaws.rc in stead of winboard.rc.\r
62 \r
63 // from resource.h\r
64 \r
65 #define IDM_PossibleAttackMove          1800\r
66 #define IDM_PossibleAttacked            1801\r
67 #define IDM_SayMachineMove              1802\r
68 #define IDM_ReadRow                     1803\r
69 #define IDM_ReadColumn                  1804\r
70 #define IDM_SayCurrentPos               1805\r
71 #define IDM_SayAllBoard                 1806\r
72 #define IDM_SayUpperDiagnols            1807\r
73 #define IDM_SayLowerDiagnols            1808\r
74 #define IDM_SayClockTime                1810\r
75 #define IDM_SayWhosTurn                 1811\r
76 #define IDM_SayKnightMoves              1812\r
77 #define ID_SHITTY_HI                    1813\r
78 #define IDM_SayWhitePieces              1816\r
79 #define IDM_SayBlackPieces              1817\r
80 \r
81 \r
82 // from common.h, but 'extern' added to it, so the actual declaraton can remain in backend.c\r
83 \r
84 extern long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;\r
85 \r
86 // from moves.c, added WinBoard_F piece types and ranks / files\r
87 \r
88 char *squareToChar[] = { "ay", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l" };\r
89 \r
90 char *squareToNum[] = {"naught", "1", "2", "3", "4", "5", "6", "7", "8", "9" };\r
91 \r
92 char *ordinals[] = {"zeroth", "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "nineth"};\r
93 \r
94 char *pieceToName[] = {\r
95                 "White Pawn", "White Knight", "White Bishop", "White Rook", "White Queen", \r
96                 "White Guard", "White Elephant", "White Arch Bishop", "White Chancellor",\r
97                 "White General", "White Man", "White Cannon", "White Night Rider",\r
98                 "White Crowned Bishop", "White Crowned Rook", "White Grass Hopper", "White Veteran",\r
99                 "White Falcon", "White Amazon", "White Snake", "White Unicorn",\r
100                 "White King",\r
101                 "Black Pawn", "Black Knight", "Black Bishop", "Black Rook", "Black Queen",\r
102                 "Black Guard", "Black Elephant", "Black Arch Bishop", "Black Chancellor",\r
103                 "Black General", "Black Man", "Black Cannon", "Black Night Rider",\r
104                 "Black Crowned Bishop", "Black Crowned Rook", "Black Grass Hopper", "Black Veteran",\r
105                 "Black Falcon", "Black Amazon", "Black Snake", "Black Unicorn",\r
106                 "Black King",\r
107                 "Empty"\r
108         };\r
109 \r
110 char *pieceTypeName[] = {\r
111                 "Pawn", "Knight", "Bishop", "Rook", "Queen", \r
112                 "Guard", "Elephant", "Arch Bishop", "Chancellor",\r
113                 "General", "Man", "Cannon", "Night Rider",\r
114                 "Crowned Bishop", "Crowned Rook", "Grass Hopper", "Veteran",\r
115                 "Falcon", "Amazon", "Snake", "Unicorn",\r
116                 "King",\r
117                 "Pawn", "Knight", "Bishop", "Rook", "Queen", \r
118                 "Guard", "Elephant", "Arch Bishop", "Chancellor",\r
119                 "General", "Man", "Cannon", "Night Rider",\r
120                 "Crowned Bishop", "Crowned Rook", "Grass Hopper", "Veteran",\r
121                 "Falcon", "Amazon", "Snake", "Unicorn",\r
122                 "King",\r
123                 "Empty"\r
124         };\r
125 \r
126 int CoordToNum(c)\r
127                 char c;\r
128 {\r
129         if(isdigit(c)) return c - ONE;\r
130         if(c >= 'a') return c - AAA;\r
131         return 0;\r
132 }\r
133 \r
134 char* PieceToName(p, i)\r
135         ChessSquare p;\r
136         int i;\r
137 {\r
138         if(i) return pieceToName[(int) p];\r
139                 return pieceTypeName[(int) p];\r
140 }\r
141 \r
142 char* SquareToChar(x)\r
143                         int x;\r
144 {\r
145                 return squareToChar[x - BOARD_LEFT];\r
146 }\r
147 \r
148 char* SquareToNum(y)\r
149                         int y;\r
150 {\r
151                 return squareToNum[y + (gameInfo.boardHeight < 10)];\r
152 }\r
153 \r
154 \r
155 // from winboard.c: all new routines\r
156 #define JFWAPI __declspec(dllimport)\r
157 JFWAPI BOOL WINAPI JFWSayString (LPCTSTR lpszStrinToSpeak, BOOL bInterrupt);\r
158 \r
159 typedef JFWAPI BOOL (WINAPI *PSAYSTRING)(LPCTSTR lpszStrinToSpeak, BOOL bInterrupt);\r
160 \r
161 PSAYSTRING RealSayString;\r
162 \r
163 VOID SayString(char *mess, BOOL flag)\r
164 { // for debug file\r
165         char buf[MSG_SIZ], *p;\r
166         if(appData.debugMode) fprintf(debugFP, "SAY '%s'\n", mess);\r
167         strcpy(buf, mess);\r
168         if(p = StrCaseStr(buf, "Xboard adjudication:")) {\r
169                 int i;\r
170                 for(i=19; i>1; i--) p[i] = p[i-1];\r
171                 p[1] = ' ';\r
172         }\r
173         RealSayString(buf, flag);\r
174 }\r
175 \r
176 //static int fromX = 0, fromY = 0;\r
177 static int oldFromX, oldFromY;\r
178 static int timeflag;\r
179 static int suppressClocks = 0;\r
180 static int suppressOneKey = 0;\r
181 static HANDLE hAccelJAWS;\r
182 \r
183 typedef struct { char *name; int code; } MenuItemDesc;\r
184 \r
185 MenuItemDesc menuItemJAWS[] = {\r
186 {"Say Clock &Time\tAlt+T",      IDM_SayClockTime },\r
187 {"-", 0 },\r
188 {"Say Last &Move\tAlt+M",       IDM_SayMachineMove },\r
189 {"Say W&ho's Turn\tAlt+X",      IDM_SayWhosTurn },\r
190 {"-", 0 },\r
191 {"Say Complete &Position\tAlt+P",IDM_SayAllBoard },\r
192 {"Say &White Pieces\tAlt+W",    IDM_SayWhitePieces },\r
193 {"Say &Black Pieces\tAlt+B",    IDM_SayBlackPieces },\r
194 {"Say Board &Rank\tAlt+R",      IDM_ReadRow },\r
195 {"Say Board &File\tAlt+F",      IDM_ReadColumn },\r
196 {"-", 0 },\r
197 {"Say &Upper Diagonals\tAlt+U",  IDM_SayUpperDiagnols },\r
198 {"Say &Lower Diagonals\tAlt+L",  IDM_SayLowerDiagnols },\r
199 {"Say K&night Moves\tAlt+N",    IDM_SayKnightMoves },\r
200 {"Say Current &Square\tAlt+S",  IDM_SayCurrentPos },\r
201 {"Say &Attacks\tAlt+A",         IDM_PossibleAttackMove },\r
202 {"Say Attacke&d\tAlt+D",        IDM_PossibleAttacked },\r
203 {NULL, 0}\r
204 };\r
205 \r
206 ACCEL acceleratorsJAWS[] = {\r
207 {FVIRTKEY|FALT, 'T', IDM_SayClockTime },\r
208 {FVIRTKEY|FALT, 'M', IDM_SayMachineMove },\r
209 {FVIRTKEY|FALT, 'X', IDM_SayWhosTurn },\r
210 {FVIRTKEY|FALT, 'P', IDM_SayAllBoard },\r
211 {FVIRTKEY|FALT, 'W', IDM_SayWhitePieces },\r
212 {FVIRTKEY|FALT, 'B', IDM_SayBlackPieces },\r
213 {FVIRTKEY|FALT, 'R', IDM_ReadRow },\r
214 {FVIRTKEY|FALT, 'F', IDM_ReadColumn },\r
215 {FVIRTKEY|FALT, 'U', IDM_SayUpperDiagnols },\r
216 {FVIRTKEY|FALT, 'L', IDM_SayLowerDiagnols },\r
217 {FVIRTKEY|FALT, 'N', IDM_SayKnightMoves },\r
218 {FVIRTKEY|FALT, 'S', IDM_SayCurrentPos },\r
219 {FVIRTKEY|FALT, 'A', IDM_PossibleAttackMove },\r
220 {FVIRTKEY|FALT, 'D', IDM_PossibleAttacked }\r
221 };\r
222 \r
223 void\r
224 AdaptMenu()\r
225 {\r
226         HMENU menuMain, menuJAWS;\r
227         MENUBARINFO helpMenuInfo;\r
228         int i;\r
229 \r
230         helpMenuInfo.cbSize = sizeof(helpMenuInfo);\r
231         menuMain = GetMenu(hwndMain);\r
232         menuJAWS = CreatePopupMenu();\r
233         \r
234         for(i=0; menuItemJAWS[i].name; i++) {\r
235             if(menuItemJAWS[i].name[0] == '-') \r
236                  AppendMenu(menuJAWS, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
237             else AppendMenu(menuJAWS, MF_ENABLED|MF_STRING, \r
238                         (UINT_PTR) menuItemJAWS[i].code, (LPCTSTR) menuItemJAWS[i].name);\r
239         }\r
240         InsertMenu(menuMain, 5, MF_BYPOSITION|MF_POPUP|MF_ENABLED|MF_STRING, \r
241                 (UINT_PTR) menuJAWS, "&JAWS");\r
242         oldMenuItemState[6] = oldMenuItemState[5];\r
243         DrawMenuBar(hwndMain);\r
244 }\r
245 \r
246 BOOL\r
247 InitJAWS()\r
248 {       // to be called at beginning of WinMain, after InitApplication and InitInstance\r
249         HINSTANCE hApi = LoadLibrary("jfwapi32.dll");\r
250         if(!hApi) {\r
251                 DisplayInformation("Missing jfwapi32.dll");        \r
252                 return (FALSE);\r
253         }\r
254 \r
255         RealSayString = (PSAYSTRING)GetProcAddress(hApi, "JFWSayString");\r
256         if(!RealSayString) {\r
257                 DisplayInformation("SayString returned a null pointer");\r
258                 return (FALSE);\r
259         }\r
260 \r
261         {\r
262                 // [HGM] kludge to reduce need for modification of winboard.c: make tinyLayout menu identical\r
263                 // to standard layout, so that code for switching between them does not have to be deleted\r
264                 int i;\r
265 \r
266                 AdaptMenu();\r
267                 menuBarText[0][5] = "&JAWS";\r
268                 for(i=0; i<7; i++) menuBarText[1][i] = menuBarText[0][i];\r
269         }\r
270 \r
271         hAccelJAWS = CreateAcceleratorTable(acceleratorsJAWS, 14);\r
272 \r
273         /* initialize cursor position */\r
274         fromX = fromY = 0;\r
275         SetHighlights(fromX, fromY, -1, -1);\r
276         DrawPosition(FALSE, NULL);\r
277         oldFromX = oldFromY = -1;\r
278 \r
279         if(hwndConsole) SetFocus(hwndConsole);\r
280         return TRUE;\r
281 }\r
282 \r
283 int beeps[] = { 1, 0, 0, 0, 0 };\r
284 int beepCodes[] = { 0, MB_OK, MB_ICONERROR, MB_ICONQUESTION, MB_ICONEXCLAMATION, MB_ICONASTERISK };\r
285 \r
286 VOID\r
287 KeyboardEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
288 {\r
289         ChessSquare currentPiece;\r
290         char *piece, *xchar, *ynum ;\r
291         int n, beepType = 1; // empty beep\r
292 \r
293         if(fromX == -1 || fromY == -1) {\r
294                 fromX = BOARD_LEFT; fromY = 0;\r
295         }\r
296         switch(wParam) {\r
297         case VK_LEFT:\r
298                 if(fromX == BOARD_RGHT+1) fromX -= 2; else\r
299                 if(fromX == BOARD_LEFT) { if(fromY >= BOARD_HEIGHT - gameInfo.holdingsSize) fromX -= 2; else beepType = 0; } else\r
300                 if(fromX > BOARD_LEFT) fromX--; else beepType = 0; // off-board beep\r
301                 break;\r
302         case VK_RIGHT:\r
303                 if(fromX == BOARD_LEFT-2) fromX += 2; else\r
304                 if(fromX == BOARD_RGHT-1) { if(fromY < gameInfo.holdingsSize) fromX += 2; else beepType = 0; } else\r
305                 if(fromX < BOARD_RGHT-1) fromX++; else beepType = 0;\r
306                 break;\r
307         case VK_UP:\r
308                 if(fromX == BOARD_RGHT+1) { if(fromY < gameInfo.holdingsSize - 1) fromY++; else beepType = 0; } else\r
309                 if(fromY < BOARD_HEIGHT-1) fromY++; else beepType = 0;\r
310                 break;\r
311         case VK_DOWN:\r
312                 if(fromX == BOARD_LEFT-2) { if(fromY > BOARD_HEIGHT - gameInfo.holdingsSize) fromY--; else beepType = 0; } else\r
313                 if(fromY > 0) fromY--; else beepType = 0;\r
314                 break;\r
315         }\r
316         SetHighlights(fromX, fromY, -1, -1);\r
317         DrawPosition(FALSE, NULL);\r
318         currentPiece = boards[currentMove][fromY][fromX];\r
319         piece = PieceToName(currentPiece,1);\r
320         if(beepType == 1 && currentPiece != EmptySquare) beepType = currentPiece < (int) BlackPawn ? 2 : 3; // white or black beep\r
321         if(beeps[beepType] == beeps[1] && (fromX == BOARD_RGHT+1 || fromX == BOARD_LEFT-2)) beepType = 4; // holdings beep\r
322         beepType = beeps[beepType]%6;\r
323         if(beepType) MessageBeep(beepCodes[beepType]);\r
324         if(fromX == BOARD_LEFT - 2) {\r
325                 SayString("black holdings", FALSE);\r
326                 if(currentPiece != EmptySquare) {\r
327                         char buf[MSG_SIZ];\r
328                         n = boards[currentMove][fromY][1];\r
329                         sprintf(buf, "%d %s%s", n, PieceToName(currentPiece,0), n == 1 ? "" : "s");\r
330                         SayString(buf, TRUE);\r
331                 }\r
332         } else\r
333         if(fromX == BOARD_RGHT + 1) {\r
334                 SayString("white holdings", FALSE);\r
335                 if(currentPiece != EmptySquare) {\r
336                         char buf[MSG_SIZ];\r
337                         n = boards[currentMove][fromY][BOARD_WIDTH-2];\r
338                         sprintf(buf, "%d %s%s", n, PieceToName(currentPiece,0), n == 1 ? "" : "s");\r
339                         SayString(buf, TRUE);\r
340                 }\r
341         } else\r
342         if(fromX >= BOARD_LEFT && fromX < BOARD_RGHT) {\r
343                 char buf[MSG_SIZ];\r
344                 xchar = SquareToChar(fromX);\r
345                 ynum = SquareToNum(fromY);\r
346                 if(currentPiece != EmptySquare) {\r
347 //                      SayString(piece[0] == 'W' ? "white" : "black", TRUE);\r
348                         sprintf(buf, "%s %s %s", piece, xchar, ynum);\r
349                 } else sprintf(buf, "%s %s", xchar, ynum);\r
350                 SayString(buf, TRUE);\r
351         }\r
352         return;\r
353 }\r
354 \r
355 extern char castlingRights[MAX_MOVES][BOARD_SIZE];\r
356 int PosFlags(int nr);\r
357 \r
358 typedef struct {\r
359     int rf, ff, rt, ft;\r
360     int onlyCaptures;\r
361     int count;\r
362 } ReadClosure;\r
363 \r
364 extern void ReadCallback P((Board board, int flags, ChessMove kind,\r
365                                 int rf, int ff, int rt, int ft,\r
366                                 VOIDSTAR closure));\r
367 \r
368 void ReadCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
369      Board board;\r
370      int flags;\r
371      ChessMove kind;\r
372      int rf, ff, rt, ft;\r
373      VOIDSTAR closure;\r
374 {\r
375     register ReadClosure *cl = (ReadClosure *) closure;\r
376     ChessSquare possiblepiece;\r
377     char *piece, *xchar, *ynum ;\r
378 \r
379 //if(appData.debugMode) fprintf(debugFP, "%c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);\r
380     if(cl->ff == ff && cl->rf == rf) {\r
381         possiblepiece = board[rt][ft];\r
382         if(possiblepiece != EmptySquare) {\r
383                 piece = PieceToName(possiblepiece,1);\r
384                 xchar = SquareToChar(ft);\r
385                 ynum  = SquareToNum(rt);\r
386                 SayString(xchar , FALSE);\r
387                 SayString(ynum, FALSE);\r
388                 SayString(piece, FALSE);\r
389                 cl->count++;\r
390         }\r
391     }\r
392     if(cl->ft == ft && cl->rt == rt) {\r
393         possiblepiece = board[rf][ff];\r
394                 piece = PieceToName(possiblepiece,1);\r
395                 xchar = SquareToChar(ff);\r
396                 ynum  = SquareToNum(rf);\r
397                 SayString(xchar , FALSE);\r
398                 SayString(ynum, FALSE);\r
399                 SayString(piece, FALSE);\r
400                 cl->count++;\r
401     }\r
402 }\r
403 \r
404 VOID\r
405 PossibleAttackMove()\r
406 {\r
407         ReadClosure cl;\r
408         ChessSquare piece, victim;\r
409         int removedSelectedPiece = 0, swapColor;\r
410 \r
411 //if(appData.debugMode) fprintf(debugFP, "PossibleAttackMove %d %d %d %d\n", fromX, fromY, oldFromX, oldFromY);\r
412         if(fromY < 0 || fromY >= BOARD_HEIGHT) return;\r
413         if(fromX < BOARD_LEFT || fromX >= BOARD_RGHT) { SayString("holdings",FALSE); return; }\r
414 \r
415         piece = boards[currentMove][fromY][fromX];\r
416         if(piece == EmptySquare) { // if square is empty, try to substitute selected piece\r
417             if(oldFromX >= 0 && oldFromY >= 0) {\r
418                 piece = boards[currentMove][oldFromY][oldFromX];\r
419                 boards[currentMove][oldFromY][oldFromX] = EmptySquare;\r
420                 removedSelectedPiece = 1;\r
421                 SayString("Your", FALSE);\r
422                 SayString(PieceToName(piece, 0), FALSE);\r
423                 SayString("would have", FALSE);\r
424             } else { SayString("You must select a piece first", FALSE); return; }\r
425         }\r
426 \r
427         victim = boards[currentMove][fromY][fromX];\r
428         boards[currentMove][fromY][fromX] = piece; // make sure piece is actally there\r
429         SayString("possible captures from here are", FALSE);\r
430 \r
431         swapColor = piece <  (int)BlackPawn && !WhiteOnMove(currentMove) ||\r
432                     piece >= (int)BlackPawn &&  WhiteOnMove(currentMove);\r
433         cl.count = 0; cl.rf = fromY; cl.ff = fromX; cl.rt = cl.ft = -1;\r
434         GenLegal(boards[currentMove], PosFlags(currentMove + swapColor), EP_NONE, \r
435                                                 castlingRights[currentMove], ReadCallback, (VOIDSTAR) &cl);\r
436         if(cl.count == 0) SayString("None", FALSE);\r
437         boards[currentMove][fromY][fromX] = victim; // repair\r
438 \r
439         if( removedSelectedPiece ) boards[currentMove][oldFromY][oldFromX] = piece;\r
440 }\r
441 \r
442 \r
443 VOID\r
444 PossibleAttacked()\r
445 {\r
446         ReadClosure cl;\r
447         ChessSquare piece = EmptySquare, victim;\r
448 \r
449         if(fromY < 0 || fromY >= BOARD_HEIGHT) return;\r
450         if(fromX < BOARD_LEFT || fromX >= BOARD_RGHT) { SayString("holdings",FALSE); return; }\r
451 \r
452         if(oldFromX >= 0 && oldFromY >= 0) { // if piece is selected, remove it\r
453                 piece = boards[currentMove][oldFromY][oldFromX];\r
454                 boards[currentMove][oldFromY][oldFromX] = EmptySquare;\r
455         }\r
456 \r
457         SayString("Pieces that can capture you are", FALSE);\r
458 \r
459         victim = boards[currentMove][fromY][fromX]; // put dummy piece on target square, to activate Pawn captures\r
460         boards[currentMove][fromY][fromX] = WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen;\r
461         cl.count = 0; cl.rt = fromY; cl.ft = fromX; cl.rf = cl.ff = -1;\r
462         GenLegal(boards[currentMove], PosFlags(currentMove+1), EP_NONE, \r
463                                                 castlingRights[currentMove], ReadCallback, (VOIDSTAR) &cl);\r
464         if(cl.count == 0) SayString("None", FALSE);\r
465 \r
466         SayString("You are defended by", FALSE);\r
467 \r
468         boards[currentMove][fromY][fromX] = WhiteOnMove(currentMove) ? BlackQueen : WhiteQueen;\r
469         cl.count = 0; cl.rt = fromY; cl.ft = fromX; cl.rf = cl.ff = -1;\r
470         GenLegal(boards[currentMove], PosFlags(currentMove), EP_NONE, \r
471                                                 castlingRights[currentMove], ReadCallback, (VOIDSTAR) &cl);\r
472         if(cl.count == 0) SayString("None", FALSE);\r
473         boards[currentMove][fromY][fromX] = victim; // put back original occupant\r
474 \r
475         if(oldFromX >= 0 && oldFromY >= 0) { // put back possibl selected piece\r
476                 boards[currentMove][oldFromY][oldFromX] = piece;\r
477         }\r
478 }\r
479 \r
480 VOID\r
481 ReadRow()\r
482 {\r
483         ChessSquare currentpiece; \r
484         char *piece, *xchar, *ynum ;\r
485         int xPos, count=0;\r
486         ynum = SquareToNum(fromY);\r
487         \r
488         if(fromY < 0) return;\r
489 \r
490         for (xPos=BOARD_LEFT; xPos<BOARD_RGHT; xPos++) {\r
491                 currentpiece = boards[currentMove][fromY][xPos];        \r
492                 if(currentpiece != EmptySquare) {\r
493                         piece = PieceToName(currentpiece,1);\r
494                         xchar = SquareToChar(xPos);\r
495                         SayString(xchar , FALSE);\r
496                         SayString(ynum, FALSE);\r
497                         SayString(piece, FALSE);\r
498                         count++;\r
499                 }\r
500         }\r
501         if(count == 0) {\r
502                 SayString("rank", FALSE);\r
503                 SayString(ynum, FALSE);\r
504                 SayString("empty", FALSE);\r
505         }\r
506 }\r
507 \r
508 VOID\r
509 ReadColumn()\r
510 {\r
511         ChessSquare currentpiece; \r
512         char *piece, *xchar, *ynum ;\r
513         int yPos, count=0;\r
514         xchar = SquareToChar(fromX);\r
515         \r
516         if(fromX < 0) return;\r
517 \r
518         for (yPos=0; yPos<BOARD_HEIGHT; yPos++) {\r
519                 currentpiece = boards[currentMove][yPos][fromX];        \r
520                 if(currentpiece != EmptySquare) {\r
521                         piece = PieceToName(currentpiece,1);\r
522                         ynum = SquareToNum(yPos);\r
523                         SayString(xchar , FALSE);\r
524                         SayString(ynum, FALSE);\r
525                         SayString(piece, FALSE);\r
526                         count++;\r
527                 }\r
528         }\r
529         if(count == 0) {\r
530                 SayString(xchar, FALSE);\r
531                 SayString("file empty", FALSE);\r
532         }\r
533 }\r
534 \r
535 VOID\r
536 SayUpperDiagnols()\r
537 {\r
538         ChessSquare currentpiece; \r
539         char *piece, *xchar, *ynum ;\r
540         int yPos, xPos;\r
541         \r
542         if(fromX < 0 || fromY < 0) return;\r
543 \r
544         if(fromX < BOARD_RGHT-1 && fromY < BOARD_HEIGHT-1) {\r
545                 SayString("The diagnol squares to your upper right contain", FALSE);\r
546                 yPos = fromY+1;\r
547                 xPos = fromX+1;\r
548                 while(yPos<BOARD_HEIGHT && xPos<BOARD_RGHT) {\r
549                         currentpiece = boards[currentMove][yPos][xPos]; \r
550                         piece = PieceToName(currentpiece,1);\r
551                         xchar = SquareToChar(xPos);\r
552                         ynum = SquareToNum(yPos);\r
553                         SayString(xchar , FALSE);\r
554                         SayString(ynum, FALSE);\r
555                         SayString(piece, FALSE);\r
556                         yPos++;\r
557                         xPos++;\r
558                 }\r
559         }\r
560         else SayString("There is no squares to your upper right", FALSE);\r
561 \r
562         if(fromX > BOARD_LEFT && fromY < BOARD_HEIGHT-1) {\r
563                 SayString("The diagnol squares to your upper left contain", FALSE);\r
564                 yPos = fromY+1;\r
565                 xPos = fromX-1;\r
566                 while(yPos<BOARD_HEIGHT && xPos>=BOARD_LEFT) {\r
567                         currentpiece = boards[currentMove][yPos][xPos]; \r
568                         piece = PieceToName(currentpiece,1);\r
569                         xchar = SquareToChar(xPos);\r
570                         ynum = SquareToNum(yPos);\r
571                         SayString(xchar , FALSE);\r
572                         SayString(ynum, FALSE);\r
573                         SayString(piece, FALSE);\r
574                         yPos++;\r
575                         xPos--;\r
576                 }\r
577         }\r
578         else SayString("There is no squares to your upper left", FALSE);\r
579 }\r
580 \r
581 VOID\r
582 SayLowerDiagnols()\r
583 {\r
584         ChessSquare currentpiece; \r
585         char *piece, *xchar, *ynum ;\r
586         int yPos, xPos;\r
587         \r
588         if(fromX < 0 || fromY < 0) return;\r
589 \r
590         if(fromX < BOARD_RGHT-1 && fromY > 0) {\r
591                 SayString("The diagnol squares to your lower right contain", FALSE);\r
592                 yPos = fromY-1;\r
593                 xPos = fromX+1;\r
594                 while(yPos>=0 && xPos<BOARD_RGHT) {\r
595                         currentpiece = boards[currentMove][yPos][xPos]; \r
596                         piece = PieceToName(currentpiece,1);\r
597                         xchar = SquareToChar(xPos);\r
598                         ynum = SquareToNum(yPos);\r
599                         SayString(xchar , FALSE);\r
600                         SayString(ynum, FALSE);\r
601                         SayString(piece, FALSE);\r
602                         yPos--;\r
603                         xPos++;\r
604                 }\r
605         }\r
606         else SayString("There is no squares to your lower right", FALSE);\r
607 \r
608         if(fromX > BOARD_LEFT && fromY > 0) {\r
609                 SayString("The diagnol squares to your lower left contain", FALSE);\r
610                 yPos = fromY-1;\r
611                 xPos = fromX-1;\r
612                 while(yPos>=0 && xPos>=BOARD_LEFT) {\r
613                         currentpiece = boards[currentMove][yPos][xPos]; \r
614                         piece = PieceToName(currentpiece,1);\r
615                         xchar = SquareToChar(xPos);\r
616                         ynum = SquareToNum(yPos);\r
617                         SayString(xchar , FALSE);\r
618                         SayString(ynum, FALSE);\r
619                         SayString(piece, FALSE);\r
620                         yPos--;\r
621                         xPos--;\r
622                 }\r
623         }\r
624         else SayString("There is no squares to your lower left", FALSE);\r
625 }\r
626 \r
627 VOID\r
628 SayKnightMoves()\r
629 {\r
630         ChessSquare currentpiece, oldpiece; \r
631         char *piece, *xchar, *ynum ;\r
632 \r
633         oldpiece = boards[currentMove][fromY][fromX];\r
634         if(oldpiece == WhiteKnight || oldpiece == BlackKnight) \r
635                 SayString("The possible squares a Knight could move to are", FALSE);\r
636         else\r
637                 SayString("The squares a Knight could possibly attack from are", FALSE);\r
638 \r
639         if (fromY+2 < BOARD_HEIGHT && fromX-1 >= BOARD_LEFT) {\r
640                 currentpiece = boards[currentMove][fromY+2][fromX-1];\r
641                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
642                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
643                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
644                 {\r
645                         piece = PieceToName(currentpiece,1);\r
646                         xchar = SquareToChar(fromX-1);\r
647                         ynum = SquareToNum(fromY+2);\r
648                         SayString(xchar , FALSE);\r
649                         SayString(ynum, FALSE);\r
650                         SayString(piece, FALSE);\r
651                 }\r
652         }\r
653 \r
654         if (fromY+2 < BOARD_HEIGHT && fromX+1 < BOARD_RGHT) {\r
655                 currentpiece = boards[currentMove][fromY+2][fromX+1];\r
656                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
657                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
658                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
659                 {\r
660                         piece = PieceToName(currentpiece,1);\r
661                         xchar = SquareToChar(fromX+1);\r
662                         ynum = SquareToNum(fromY+2);\r
663                         SayString(xchar , FALSE);\r
664                         SayString(ynum, FALSE);\r
665                         SayString(piece, FALSE);\r
666                 }\r
667         }\r
668         \r
669         if (fromY+1 < BOARD_HEIGHT && fromX+2 < BOARD_RGHT) {\r
670                 currentpiece = boards[currentMove][fromY+1][fromX+2];\r
671                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
672                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
673                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
674                 {\r
675                         piece = PieceToName(currentpiece,1);\r
676                         xchar = SquareToChar(fromX+2);\r
677                         ynum = SquareToNum(fromY+1);\r
678                         SayString(xchar , FALSE);\r
679                         SayString(ynum, FALSE);\r
680                         SayString(piece, FALSE);\r
681                 }\r
682         }\r
683         \r
684         if (fromY-1 >= 0 && fromX+2 < BOARD_RGHT) {\r
685                 currentpiece = boards[currentMove][fromY-1][fromX+2];\r
686                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
687                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
688                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
689                 {\r
690                         piece = PieceToName(currentpiece,1);\r
691                         xchar = SquareToChar(fromX+2);\r
692                         ynum = SquareToNum(fromY-1);\r
693                         SayString(xchar , FALSE);\r
694                         SayString(ynum, FALSE);\r
695                         SayString(piece, FALSE);\r
696                 }\r
697         }\r
698         \r
699         if (fromY-2 >= 0 && fromX+1 < BOARD_RGHT) {\r
700                 currentpiece = boards[currentMove][fromY-2][fromX+1];\r
701                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
702                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
703                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
704                 {\r
705                         piece = PieceToName(currentpiece,1);\r
706                         xchar = SquareToChar(fromX+1);\r
707                         ynum = SquareToNum(fromY-2);\r
708                         SayString(xchar , FALSE);\r
709                         SayString(ynum, FALSE);\r
710                         SayString(piece, FALSE);\r
711                 }\r
712         }\r
713         \r
714         if (fromY-2 >= 0 && fromX-1 >= BOARD_LEFT) {\r
715                 currentpiece = boards[currentMove][fromY-2][fromX-1];\r
716                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
717                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
718                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
719                 {\r
720                         piece = PieceToName(currentpiece,1);\r
721                         xchar = SquareToChar(fromX-1);\r
722                         ynum = SquareToNum(fromY-2);\r
723                         SayString(xchar , FALSE);\r
724                         SayString(ynum, FALSE);\r
725                         SayString(piece, FALSE);\r
726                 }\r
727         }\r
728         \r
729         if (fromY-1 >= 0 && fromX-2 >= BOARD_LEFT) {\r
730                 currentpiece = boards[currentMove][fromY-1][fromX-2];\r
731                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
732                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
733                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
734                 {\r
735                         piece = PieceToName(currentpiece,1);\r
736                         xchar = SquareToChar(fromX-2);\r
737                         ynum = SquareToNum(fromY-1);\r
738                         SayString(xchar , FALSE);\r
739                         SayString(ynum, FALSE);\r
740                         SayString(piece, FALSE);\r
741                 }\r
742         }\r
743         \r
744         if (fromY+1 < BOARD_HEIGHT && fromX-2 >= BOARD_LEFT) {\r
745                 currentpiece = boards[currentMove][fromY+1][fromX-2];\r
746                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
747                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
748                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
749                 {\r
750                         piece = PieceToName(currentpiece,1);\r
751                         xchar = SquareToChar(fromX-2);\r
752                         ynum = SquareToNum(fromY+1);\r
753                         SayString(xchar , FALSE);\r
754                         SayString(ynum, FALSE);\r
755                         SayString(piece, FALSE);\r
756                 }\r
757         }\r
758 }\r
759 \r
760 VOID\r
761 SayPieces(ChessSquare p)\r
762 {\r
763         ChessSquare currentpiece;  \r
764         char *piece, *xchar, *ynum ;\r
765         int yPos, xPos, count = 0;\r
766         char buf[50];\r
767 \r
768         if(p == WhitePlay)   SayString("White pieces", FALSE); else\r
769         if(p == BlackPlay)   SayString("Black pieces", FALSE); else\r
770         if(p == EmptySquare) SayString("Pieces", FALSE); else {\r
771                 sprintf(buf, "%ss", PieceToName(p,1));\r
772                 SayString(buf, FALSE);\r
773         }\r
774         SayString("are located", FALSE);\r
775         for(yPos=0; yPos<BOARD_HEIGHT; yPos++) {\r
776                 for(xPos=BOARD_LEFT; xPos<BOARD_RGHT; xPos++) {\r
777                         currentpiece = boards[currentMove][yPos][xPos]; \r
778                         if(p == BlackPlay && currentpiece >= BlackPawn && currentpiece <= BlackKing ||\r
779                            p == WhitePlay && currentpiece >= WhitePawn && currentpiece <= WhiteKing   )\r
780                                 piece = PieceToName(currentpiece,0);\r
781                         else if(p == EmptySquare && currentpiece != EmptySquare)\r
782                                 piece = PieceToName(currentpiece,1);\r
783                         else if(p == currentpiece)\r
784                                 piece = NULL;\r
785                         else continue;\r
786                                 \r
787                                 if(count == 0) SayString("at", FALSE);\r
788                                 xchar = SquareToChar(xPos);\r
789                                 ynum = SquareToNum(yPos);\r
790                                 SayString(xchar , FALSE);\r
791                                 SayString(ynum, FALSE);\r
792                                 if(piece) SayString(piece, FALSE);\r
793                                 count++;\r
794                 }\r
795         }\r
796         if(count == 0) SayString("nowhere", FALSE);\r
797 }\r
798 \r
799 VOID\r
800 SayCurrentPos()\r
801 {\r
802         ChessSquare currentpiece;\r
803         char *piece, *xchar, *ynum ;\r
804         if(fromX <  BOARD_LEFT) { SayString("You strayed into the white holdings", FALSE); return; }\r
805         if(fromX >= BOARD_RGHT) { SayString("You strayed into the black holdings", FALSE); return; }\r
806         currentpiece = boards[currentMove][fromY][fromX];       \r
807         piece = PieceToName(currentpiece,1);\r
808         ynum = SquareToNum(fromY);\r
809         xchar = SquareToChar(fromX);\r
810         SayString("Your current position is", FALSE);\r
811         SayString(xchar, FALSE);\r
812         SayString(ynum, FALSE);\r
813         SayString(piece, FALSE);\r
814         if(((fromX-BOARD_LEFT) ^ fromY)&1)\r
815                 SayString("on a light square",FALSE);\r
816         else \r
817                 SayString("on a dark square",FALSE);\r
818 \r
819         PossibleAttacked();\r
820         return;\r
821 }\r
822 \r
823 VOID\r
824 SayAllBoard()\r
825 {\r
826         int Xpos, Ypos;\r
827         ChessSquare currentpiece;\r
828         char *piece, *ynum ;\r
829         \r
830         if(gameInfo.holdingsWidth) {\r
831                 int first = 0;\r
832                 for(Ypos=0; Ypos<gameInfo.holdingsSize; Ypos++) {\r
833                         int n = boards[currentMove][Ypos][BOARD_WIDTH-2];\r
834                         if(n) {  char buf[MSG_SIZ];\r
835                                 if(!first++) SayString("white holds", FALSE);\r
836                                 currentpiece = boards[currentMove][Ypos][BOARD_WIDTH-1];        \r
837                                 piece = PieceToName(currentpiece,0);\r
838                                 sprintf(buf, "%d %s%s", n, piece, (n==1 ? "" : "s") );\r
839                                 SayString(buf, FALSE);\r
840                         }\r
841                 }\r
842                 first = 0;\r
843                 for(Ypos=BOARD_HEIGHT-1; Ypos>=BOARD_HEIGHT - gameInfo.holdingsSize; Ypos--) {\r
844                         int n = boards[currentMove][Ypos][1];\r
845                         if(n) {  char buf[MSG_SIZ];\r
846                                 if(!first++) SayString("black holds", FALSE);\r
847                                 currentpiece = boards[currentMove][Ypos][0];    \r
848                                 piece = PieceToName(currentpiece,0);\r
849                                 sprintf(buf, "%d %s%s", n, piece, (n==1 ? "" : "s") );\r
850                                 SayString(buf, FALSE);\r
851                         }\r
852                 }\r
853         }\r
854 \r
855         for(Ypos=BOARD_HEIGHT-1; Ypos>=0; Ypos--) {\r
856                 ynum = ordinals[Ypos + (gameInfo.boardHeight < 10)];\r
857                 SayString(ynum, FALSE);\r
858                 SayString("rank", FALSE);\r
859                 for(Xpos=BOARD_LEFT; Xpos<BOARD_RGHT; Xpos++) {\r
860                         currentpiece = boards[currentMove][Ypos][Xpos]; \r
861                         if(currentpiece != EmptySquare) {\r
862                                 int count = 0;\r
863                                 char buf[50];\r
864                                 piece = PieceToName(currentpiece,1);\r
865                                 while(Xpos < BOARD_RGHT && boards[currentMove][Ypos][Xpos] == currentpiece)\r
866                                         Xpos++, count++;\r
867                                 if(count > 1) { \r
868                                         sprintf(buf, "%d %ss", count, piece);\r
869                                 } else  sprintf(buf, "%s", piece);\r
870                                 Xpos--;\r
871                                 SayString(buf, FALSE);\r
872                         } else {\r
873                                 int count = 0, oldX = Xpos;\r
874                                 while(Xpos < BOARD_RGHT && boards[currentMove][Ypos][Xpos] == EmptySquare)\r
875                                         Xpos++, count++;\r
876                                 if(Xpos == BOARD_RGHT && oldX == BOARD_LEFT)\r
877                                         SayString("all", FALSE);\r
878                                 else{\r
879                                     if(count > 1) { \r
880                                         char buf[10];\r
881                                         sprintf(buf, "%d", count);\r
882                                         SayString(buf, FALSE);\r
883                                     }\r
884                                     Xpos--;\r
885                                 }\r
886                                 SayString("empty", FALSE);\r
887                         }\r
888                 }\r
889         }\r
890         \r
891 }\r
892 \r
893 VOID\r
894 SayWhosTurn()\r
895 {\r
896         if(gameMode == MachinePlaysBlack || gameMode == IcsPlayingBlack) {\r
897                 if(WhiteOnMove(currentMove))\r
898                         SayString("It is your turn", FALSE);\r
899                 else    SayString("It is your opponents turn", FALSE);\r
900         } else if(gameMode == MachinePlaysWhite || gameMode == IcsPlayingWhite) {\r
901                 if(WhiteOnMove(currentMove))\r
902                         SayString("It is your opponents turn", FALSE);\r
903                 else    SayString("It is your turn", FALSE);\r
904         } else {\r
905                 if(WhiteOnMove(currentMove)) \r
906                         SayString("White is on move here", FALSE);\r
907                 else    SayString("Black is on move here", FALSE);\r
908         }\r
909 }\r
910         \r
911 extern char *commentList[];\r
912 \r
913 VOID\r
914 SayMachineMove(int evenIfDuplicate)\r
915 {\r
916         int len, xPos, yPos, moveNr, secondSpace = 0, castle = 0, n;\r
917         ChessSquare currentpiece;\r
918         char *piece, *xchar, *ynum, *p, checkMark = 0;\r
919         char c, buf[MSG_SIZ], comment[MSG_SIZ];\r
920         static char disambiguation[2];\r
921         static int previousMove = 0;\r
922 \r
923         if(appData.debugMode) fprintf(debugFP, "Message = '%s'\n", messageText);\r
924         if(gameMode == BeginningOfGame) return;\r
925         if(messageText[0] == '[') return;\r
926         comment[0]= 0;\r
927             if(isdigit(messageText[0])) { // message is move, possibly with thinking output\r
928                 int dotCount = 0, spaceCount = 0;\r
929                 sscanf(messageText, "%d", &moveNr);\r
930                 len = 0;\r
931                 // [HGM] show: better extraction of move\r
932                 while (messageText[len] != NULLCHAR) {\r
933                     if(messageText[len] == '.' && spaceCount == 0) dotCount++;\r
934                     if(messageText[len] == ' ') { if(++spaceCount == 2) secondSpace = len; }\r
935                     if(messageText[len] == '{') { // we detected a comment\r
936                         if(isalpha(messageText[len+1]) ) sscanf(messageText+len, "{%[^}]}", comment);\r
937                         break;\r
938                     }\r
939                     if(messageText[len] == '[') { // we detected thinking output\r
940                         int depth; float score=0; char c, lastMover = (dotCount == 3 ? 'B' : 'W');\r
941                         if(sscanf(messageText+len+1, "%d]%c%f", &depth, &c, &score) > 1) {\r
942                             if(c == ' ') { // if not explicitly specified, figure out source of thinking output\r
943                                 switch(gameMode) {\r
944                                   case MachinePlaysWhite:\r
945                                   case IcsPlayingWhite:\r
946                                     c = 'W'; break;\r
947                                   case IcsPlayingBlack:\r
948                                   case MachinePlaysBlack:\r
949                                     c = 'B'; \r
950                                   default:\r
951                                     break;\r
952                                 }\r
953                             }\r
954                             if(c != lastMover) return; // line is thinking output of future move, ignore.\r
955                             if(2*moveNr - (dotCount < 2) == previousMove)\r
956                                 return; // do not repeat same move; likely ponder output\r
957                             sprintf(buf, "score %s %d at %d ply", \r
958                                         score > 0 ? "plus" : score < 0 ? "minus" : "",\r
959                                         (int) (fabs(score)*100+0.5),\r
960                                         depth );\r
961                             SayString(buf, FALSE); // move + thinking output describing it; say it.\r
962                         }\r
963                         while(messageText[len-1] == ' ') len--; // position just behind move;\r
964                         break;\r
965                     }\r
966                     if(messageText[len] == '(') { // ICS time printed behind move\r
967                         while(messageText[len+1] && messageText[len] != ')') len++; // skip it\r
968                     }\r
969                     len++;\r
970                 }\r
971                 if(secondSpace) len = secondSpace; // position behind move\r
972                 if(messageText[len-1] == '+' || messageText[len-1] == '#') {  /* you are in checkmate */\r
973                         len--; // strip off check or mate indicator\r
974                       checkMark = messageText[len]; // make sure still seen after we stip off promo piece\r
975                 }\r
976                 if(messageText[len-2] == '=') {  /* promotion */\r
977                         len-=2; // strip off promotion piece\r
978                         SayString("promotion", FALSE);\r
979                 }\r
980 \r
981                 n = 2*moveNr - (dotCount < 2);\r
982 \r
983                 if(previousMove != 2*moveNr + (dotCount > 1) || evenIfDuplicate) { \r
984                     char number[20];\r
985                     previousMove = 2*moveNr + (dotCount > 1); // remember move nr of move last spoken\r
986                     sprintf(number, "%d", moveNr);\r
987 \r
988                     yPos = CoordToNum(messageText[len-1]);  /* turn char coords to ints */\r
989                     xPos = CoordToNum(messageText[len-2]);\r
990                     if(xPos < 0 || xPos > 11) return; // prevent crashes if no coord string available to speak\r
991                     if(yPos < 0 || yPos > 9)  return;\r
992                     currentpiece = boards[n][yPos][xPos];       \r
993                     piece = PieceToName(currentpiece,0);\r
994                     ynum = SquareToNum(yPos);\r
995                     xchar = SquareToChar(xPos);\r
996                     c = messageText[len-3];\r
997                     if(c == 'x') c = messageText[len-4];\r
998                     if(!isdigit(c) && c < 'a' && c != '@') c = 0;\r
999                     disambiguation[0] = c;\r
1000                     SayString(WhiteOnMove(n) ? "Black" : "White", FALSE);\r
1001                     SayString("move", FALSE);\r
1002                     SayString(number, FALSE);\r
1003 //                  if(c==0 || c=='@') SayString("a", FALSE);\r
1004                     // intercept castling moves\r
1005                     p = StrStr(messageText, "O-O-O");\r
1006                     if(p && p-messageText < len) {\r
1007                         SayString("queen side castling",FALSE);\r
1008                         castle = 1;\r
1009                     } else {\r
1010                         p = StrStr(messageText, "O-O");\r
1011                         if(p && p-messageText < len) {\r
1012                             SayString("king side castling",FALSE);\r
1013                             castle = 1;\r
1014                         }\r
1015                     }\r
1016                     if(!castle) {\r
1017                         SayString(piece, FALSE);\r
1018                         if(c == '@') SayString("dropped on", FALSE); else\r
1019                         if(c) SayString(disambiguation, FALSE);\r
1020                         SayString("to", FALSE);\r
1021                         SayString(xchar, FALSE);\r
1022                         SayString(ynum, FALSE);\r
1023                         if(messageText[len-3] == 'x') {\r
1024                                 currentpiece = boards[n-1][yPos][xPos];\r
1025                                 if(currentpiece != EmptySquare) {\r
1026                                         piece = PieceToName(currentpiece,0);\r
1027                                         SayString("Capturing a",FALSE);\r
1028                                         SayString(piece, FALSE);\r
1029                                 } else SayString("Capturing onn passann",FALSE);\r
1030                         }\r
1031                     }\r
1032                     if(checkMark == '+') SayString("check", FALSE); else\r
1033                     if(checkMark == '#') {\r
1034                                 SayString("finishing off", FALSE);\r
1035                                 SayString(WhiteOnMove(n) ? "White" : "Black", FALSE);\r
1036                     }\r
1037                 }\r
1038 \r
1039                 /* say comment after move, possibly with result */\r
1040                 p = NULL;\r
1041                 if(StrStr(messageText, " 1-0")) p = "white wins"; else\r
1042                 if(StrStr(messageText, " 0-1")) p = "black wins"; else\r
1043                 if(StrStr(messageText, " 1/2-1/2")) p = "game ends in a draw";\r
1044                 if(comment[0]) {\r
1045                     if(p) {\r
1046                         if(!StrCaseStr(comment, "draw") && \r
1047                            !StrCaseStr(comment, "white") && \r
1048                            !StrCaseStr(comment, "black") ) {\r
1049                                 SayString(p, FALSE);\r
1050                                 SayString("due to", FALSE);\r
1051                         }\r
1052                     }\r
1053                     SayString(comment, FALSE); // alphabetic comment (usually game end)\r
1054                 } else if(p) SayString(p, FALSE);\r
1055 \r
1056                 if(commentDialog && commentList[currentMove]) SetFocus(commentDialog);\r
1057 \r
1058             } else {\r
1059                 /* starts not with digit */\r
1060                 if(StrCaseStr(messageText, "illegal")) PlayIcsUnfinishedSound();\r
1061                 SayString(messageText, FALSE);\r
1062             }\r
1063 \r
1064 }\r
1065 \r
1066 VOID\r
1067 SayClockTime()\r
1068 {\r
1069         char buf1[50], buf2[50];\r
1070         char *str1, *str2;\r
1071         static long int lastWhiteTime, lastBlackTime;\r
1072 \r
1073         suppressClocks = 1; // if user is using alt+T command, no reason to display them\r
1074         if(abs(lastWhiteTime - whiteTimeRemaining) < 1000 && abs(lastBlackTime - blackTimeRemaining) < 1000)\r
1075                 suppressClocks = 0; // back on after two requests in rapid succession\r
1076         sprintf(buf1, "%s", TimeString(whiteTimeRemaining));\r
1077         str1 = buf1;\r
1078         SayString("White's remaining time is", FALSE);\r
1079         SayString(str1, FALSE);\r
1080         sprintf(buf2, "%s", TimeString(blackTimeRemaining));\r
1081         str2 = buf2;\r
1082         SayString("Black's remaining time is", FALSE);\r
1083         SayString(str2, FALSE);\r
1084         lastWhiteTime = whiteTimeRemaining;\r
1085         lastBlackTime = blackTimeRemaining;\r
1086 }\r
1087 \r
1088 VOID\r
1089 Toggle(Boolean *b, char *mess)\r
1090 {\r
1091         *b = !*b;\r
1092         SayString(mess, FALSE);\r
1093         SayString("is now", FALSE);\r
1094         SayString(*b ? "on" : "off", FALSE);\r
1095 }\r
1096 \r
1097 /* handles keyboard moves in a click-click fashion */\r
1098 VOID\r
1099 KeyboardMove(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
1100 {\r
1101         ChessSquare currentpiece;\r
1102         char *piece;\r
1103         \r
1104         static BOOLEAN sameAgain = FALSE;\r
1105         switch (message) {\r
1106         case WM_KEYDOWN:\r
1107                 sameAgain = FALSE;\r
1108                 if(oldFromX == fromX && oldFromY == fromY) {\r
1109                         sameAgain = TRUE;\r
1110                         /* click on same square */\r
1111                         break;\r
1112                 }\r
1113                 else if(oldFromX != -1) {\r
1114                         \r
1115                         ChessSquare pdown, pup;\r
1116       pdown = boards[currentMove][oldFromY][oldFromX];\r
1117       pup = boards[currentMove][fromY][fromX];\r
1118                 \r
1119                 if (gameMode == EditPosition ||\r
1120                         !((WhitePawn <= pdown && pdown <= WhiteKing &&\r
1121                                  WhitePawn <= pup && pup <= WhiteKing) ||\r
1122                                 (BlackPawn <= pdown && pdown <= BlackKing &&\r
1123                                  BlackPawn <= pup && pup <= BlackKing))) {\r
1124                         /* EditPosition, empty square, or different color piece;\r
1125                         click-click move is possible */         \r
1126                         char promoChoice = NULLCHAR;\r
1127                 \r
1128                         if (HasPromotionChoice(oldFromX, oldFromY, fromX, fromY, &promoChoice)) {\r
1129                                 if (appData.alwaysPromoteToQueen) {\r
1130                                         UserMoveEvent(oldFromX, oldFromY, fromX, fromY, 'q');\r
1131                                 }\r
1132                                 else {\r
1133                                         toX = fromX; toY = fromY; fromX = oldFromX; fromY = oldFromY;\r
1134                                         PromotionPopup(hwnd);\r
1135                                         fromX = toX; fromY = toY;\r
1136                                 }       \r
1137                         }\r
1138                         else {\r
1139                                 UserMoveEvent(oldFromX, oldFromY, fromX, fromY, promoChoice);\r
1140                         }\r
1141                 oldFromX = oldFromY = -1;\r
1142                 break;\r
1143                 }\r
1144                 \r
1145                 }\r
1146                 /* First downclick, or restart on a square with same color piece */\r
1147                 if (OKToStartUserMove(fromX, fromY)) {\r
1148                 oldFromX = fromX;\r
1149                 oldFromY = fromY;\r
1150                 currentpiece = boards[currentMove][fromY][fromX];       \r
1151                 piece = PieceToName(currentpiece,1);\r
1152                 SayString(piece, FALSE);\r
1153                 SayString("selected", FALSE);\r
1154                 }\r
1155                 else {\r
1156                 oldFromX = oldFromY = -1;\r
1157                 }\r
1158                 break;\r
1159 \r
1160         case WM_KEYUP:\r
1161                 if (oldFromX == fromX && oldFromY == fromY) {\r
1162       /* Upclick on same square */\r
1163       if (sameAgain) {\r
1164         /* Clicked same square twice: abort click-click move */\r
1165                         oldFromX = oldFromY = -1;\r
1166                         currentpiece = boards[currentMove][fromY][fromX];       \r
1167                         piece = PieceToName(currentpiece,0);\r
1168                         SayString(piece, FALSE);\r
1169                         SayString("unselected", FALSE);\r
1170                         }\r
1171                 }\r
1172         }\r
1173 }\r
1174 \r
1175 int\r
1176 NiceTime(int x)\r
1177 {       // return TRUE for times we want to announce\r
1178         if(x<0) return 0;\r
1179         x = (x+50)/100;   // tenth of seconds\r
1180         if(x <= 100) return (x%10 == 0);\r
1181         if(x <= 600) return (x%100 == 0);\r
1182         if(x <= 6000) return (x%600 == 0);\r
1183         return (x%3000 == 0);\r
1184 }\r
1185 \r
1186 #define JAWS_ARGS \\r
1187   { "beepOffBoard", ArgInt, (LPVOID) beeps, TRUE },\\r
1188   { "beepEmpty", ArgInt, (LPVOID) (beeps+1), TRUE },\\r
1189   { "beepWhite", ArgInt, (LPVOID) (beeps+2), TRUE },\\r
1190   { "beepBlack", ArgInt, (LPVOID) (beeps+3), TRUE },\\r
1191   { "beepHoldings", ArgInt, (LPVOID) (beeps+4), TRUE },\\r
1192 \r
1193 #define JAWS_ALT_INTERCEPT \\r
1194             if(suppressOneKey) {\\r
1195                 suppressOneKey = 0;\\r
1196                 if(GetKeyState(VK_MENU) < 0 && GetKeyState(VK_CONTROL) < 0) break;\\r
1197             }\\r
1198             if ((char)wParam == 022 && gameMode == EditPosition) { /* <Ctl R>. Pop up piece menu */\\r
1199                 POINT pt; int x, y;\\r
1200                 SquareToPos(fromY, fromX, &x, &y);\\r
1201                 pt.x = x; pt.y = y;\\r
1202                 if(gameInfo.variant != VariantShogi)\\r
1203                     MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\\r
1204                 else\\r
1205                     MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\\r
1206                 break;\\r
1207             }\\r
1208 \r
1209 #define JAWS_REPLAY \\r
1210     case '\020': /* ctrl P */\\r
1211       { char buf[MSG_SIZ];\\r
1212         if(GetWindowText(hwnd, buf, MSG_SIZ-1))\\r
1213                 SayString(buf, FALSE);\\r
1214       }\\r
1215       return 0;\\r
1216 \r
1217 #define JAWS_KB_NAVIGATION \\r
1218 \\r
1219         case WM_KEYDOWN:\\r
1220 \\r
1221                 if(GetKeyState(VK_MENU) < 0 && GetKeyState(VK_CONTROL) < 0) {\\r
1222                     /* Control + Alt + letter used for speaking piece positions */\\r
1223                     static int lastTime; static char lastChar;\\r
1224                     int mine = 0, time = GetTickCount(); char c;\\r
1225 \\r
1226                     if((char)wParam == lastChar && time-lastTime < 250) mine = 1;\\r
1227                     lastChar = wParam; lastTime = time;\\r
1228                     c = wParam;\\r
1229 \\r
1230                     if(gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) mine = !mine;\\r
1231 \\r
1232                     if(ToLower(c) == 'x') {\\r
1233                         SayPieces(mine ? WhitePlay : BlackPlay);\\r
1234                         suppressOneKey = 1;\\r
1235                         break;\\r
1236                     } else\\r
1237                     if(CharToPiece(c) != EmptySquare) {\\r
1238                         SayPieces(CharToPiece(mine ? ToUpper(c) : ToLower(c)));\\r
1239                         suppressOneKey = 1;\\r
1240                         break;\\r
1241                     }\\r
1242                 }\\r
1243 \\r
1244                 switch (wParam) {\\r
1245                 case VK_LEFT:\\r
1246                 case VK_RIGHT:\\r
1247                 case VK_UP:\\r
1248                 case VK_DOWN:\\r
1249                         KeyboardEvent(hwnd, message, wParam, lParam);\\r
1250                         break;\\r
1251                 case VK_SPACE:\\r
1252                         KeyboardMove(hwnd, message, wParam, lParam);\\r
1253                         break;\\r
1254                 }\\r
1255                 break;\\r
1256         case WM_KEYUP:\\r
1257                 switch (wParam) {\\r
1258                 case VK_SPACE:\\r
1259                         KeyboardMove(hwnd, message, wParam, lParam);\\r
1260                         break;\\r
1261                 }\\r
1262                 break;\\r
1263 \r
1264 #define JAWS_MENU_ITEMS \\r
1265                 case IDM_PossibleAttackMove:  /*What can I possible attack from here */\\r
1266                         PossibleAttackMove();\\r
1267                         break;\\r
1268 \\r
1269                 case IDM_PossibleAttacked:  /*what can possible attack this square*/\\r
1270                         PossibleAttacked();\\r
1271                         break;\\r
1272 \\r
1273                 case IDM_ReadRow:   /* Read the current row of pieces */\\r
1274                         ReadRow();\\r
1275                         break;\\r
1276 \\r
1277                 case IDM_ReadColumn:   /* Read the current column of pieces */\\r
1278                         ReadColumn();\\r
1279                         break;\\r
1280 \\r
1281                 case IDM_SayCurrentPos: /* Say current position including color */\\r
1282                         SayCurrentPos();\\r
1283                         break;\\r
1284 \\r
1285                 case IDM_SayAllBoard:  /* Say the whole board from bottom to top */\\r
1286                         SayAllBoard();\\r
1287                         break;\\r
1288 \\r
1289                 case IDM_SayMachineMove:  /* Say the last move made */\\r
1290                         timeflag = 1;\\r
1291                         SayMachineMove(1);\\r
1292                         break;\\r
1293 \\r
1294                 case IDM_SayUpperDiagnols:  /* Says the diagnol positions above you */\\r
1295                         SayUpperDiagnols();\\r
1296                         break;\\r
1297 \\r
1298                 case IDM_SayLowerDiagnols:  /* Say the diagnol positions below you */\\r
1299                         SayLowerDiagnols();\\r
1300                         break;\\r
1301 \\r
1302                 case IDM_SayBlackPieces: /*Say the opponents pieces */\\r
1303                         SayPieces(BlackPlay);\\r
1304                         break;\\r
1305 \\r
1306                 case IDM_SayWhitePieces: /*Say the opponents pieces */\\r
1307                         SayPieces(WhitePlay);\\r
1308                         break;\\r
1309 \\r
1310                 case IDM_SayClockTime:  /*Say the clock time */\\r
1311                         SayClockTime();\\r
1312                         break;\\r
1313 \\r
1314                 case IDM_SayWhosTurn:   /* Say whos turn it its */\\r
1315                         SayWhosTurn();\\r
1316                         break;\\r
1317 \\r
1318                 case IDM_SayKnightMoves:  /* Say Knights (L-shaped) move */\\r
1319                         SayKnightMoves();\\r
1320                         break;\\r
1321 \\r
1322                 case OPT_PonderNextMove:  /* Toggle option setting */\\r
1323                         Toggle(&appData.ponderNextMove, "ponder");\\r
1324                         break;\\r
1325 \\r
1326                 case OPT_AnimateMoving:  /* Toggle option setting */\\r
1327                         Toggle(&appData.animate, "animate moving");\\r
1328                         break;\\r
1329 \\r
1330                 case OPT_AutoFlag:  /* Toggle option setting */\\r
1331                         Toggle(&appData.autoCallFlag, "auto flag");\\r
1332                         break;\\r
1333 \\r
1334                 case OPT_AlwaysQueen:  /* Toggle option setting */\\r
1335                         Toggle(&appData.alwaysPromoteToQueen, "always promote to queen");\\r
1336                         break;\\r
1337 \\r
1338                 case OPT_TestLegality:  /* Toggle option setting */\\r
1339                         Toggle(&appData.testLegality, "legality testing");\\r
1340                         break;\\r
1341 \\r
1342                 case OPT_HideThinkFromHuman:  /* Toggle option setting */\\r
1343                         Toggle(&appData.hideThinkingFromHuman, "hide thinking");\\r
1344                         ShowThinkingEvent();\\r
1345                         break;\\r
1346 \\r
1347                 case OPT_SaveExtPGN:  /* Toggle option setting */\\r
1348                         Toggle(&appData.saveExtendedInfoInPGN, "extended P G N info");\\r
1349                         break;\\r
1350 \\r
1351                 case OPT_ExtraInfoInMoveHistory:  /* Toggle option setting */\\r
1352                         Toggle(&appData.showEvalInMoveHistory, "extra info in move histoty");\\r
1353                         break;\\r
1354 \\r
1355 \r
1356 \r
1357 #define JAWS_ACCEL \\r
1358         !(!frozen && TranslateAccelerator(hwndMain, hAccelJAWS, &msg)) &&\r
1359 \r
1360 #define JAWS_INIT if (!InitJAWS()) return (FALSE);\r
1361 \r
1362 #define JAWS_DELETE(X)\r
1363 \r
1364 #define JAWS_SILENCE if(suppressClocks) return;\r
1365 \r
1366 #define JAWS_COPYRIGHT \\r
1367         SetDlgItemText(hDlg, OPT_MESS, "Auditory/Keyboard Enhancements  By:  Ed Rodriguez (sort of)");\r
1368 \r
1369 #define SAY(S) SayString((S), FALSE)\r
1370 \r
1371 #define SAYMACHINEMOVE() SayMachineMove(0)\r
1372 \r
1373 // After inclusion of this file somewhere early in winboard.c, the remaining part of the patch\r
1374 // is scattered over winboard.c for actually calling the routines.\r