cleanup: removed "#if 0" from source
[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         if(appData.debugMode) fprintf(debugFP, "hwndMain: %8x %8x\n", hwndMain, menuMain);\r
233         menuJAWS = CreatePopupMenu();\r
234         \r
235         for(i=0; menuItemJAWS[i].name; i++) {\r
236             if(menuItemJAWS[i].name[0] == '-') \r
237                  AppendMenu(menuJAWS, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
238             else AppendMenu(menuJAWS, MF_ENABLED|MF_STRING, \r
239                         (UINT_PTR) menuItemJAWS[i].code, (LPCTSTR) menuItemJAWS[i].name);\r
240         }\r
241         InsertMenu(menuMain, 5, MF_BYPOSITION|MF_POPUP|MF_ENABLED|MF_STRING, \r
242                 (UINT_PTR) menuJAWS, "&JAWS");\r
243         oldMenuItemState[6] = oldMenuItemState[5];\r
244         DrawMenuBar(hwndMain);\r
245 }\r
246 \r
247 BOOL\r
248 InitJAWS()\r
249 {       // to be called at beginning of WinMain, after InitApplication and InitInstance\r
250         HINSTANCE hApi = LoadLibrary("jfwapi32.dll");\r
251         if(!hApi) {\r
252                 DisplayInformation("Missing jfwapi32.dll");        \r
253                 return (FALSE);\r
254         }\r
255 \r
256         RealSayString = (PSAYSTRING)GetProcAddress(hApi, "JFWSayString");\r
257         if(!RealSayString) {\r
258                 DisplayInformation("SayString returned a null pointer");\r
259                 return (FALSE);\r
260         }\r
261 \r
262         {\r
263                 // [HGM] kludge to reduce need for modification of winboard.c: make tinyLayout menu identical\r
264                 // to standard layout, so that code for switching between them does not have to be deleted\r
265                 int i;\r
266 \r
267                 AdaptMenu();\r
268                 menuBarText[0][5] = "&JAWS";\r
269                 for(i=0; i<7; i++) menuBarText[1][i] = menuBarText[0][i];\r
270         }\r
271 \r
272         hAccelJAWS = CreateAcceleratorTable(acceleratorsJAWS, 14);\r
273 \r
274         /* initialize cursor position */\r
275         fromX = fromY = 0;\r
276         SetHighlights(fromX, fromY, -1, -1);\r
277         DrawPosition(FALSE, NULL);\r
278         oldFromX = oldFromY = -1;\r
279 \r
280         if(hwndConsole) SetFocus(hwndConsole);\r
281         return TRUE;\r
282 }\r
283 \r
284 int beeps[] = { 1, 0, 0, 0, 0 };\r
285 int beepCodes[] = { 0, MB_OK, MB_ICONERROR, MB_ICONQUESTION, MB_ICONEXCLAMATION, MB_ICONASTERISK };\r
286 \r
287 VOID\r
288 KeyboardEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
289 {\r
290         ChessSquare currentPiece;\r
291         char *piece, *xchar, *ynum ;\r
292         int n, beepType = 1; // empty beep\r
293 \r
294         if(fromX == -1 || fromY == -1) {\r
295                 fromX = BOARD_LEFT; fromY = 0;\r
296         }\r
297         switch(wParam) {\r
298         case VK_LEFT:\r
299                 if(fromX == BOARD_RGHT+1) fromX -= 2; else\r
300                 if(fromX == BOARD_LEFT) { if(fromY >= BOARD_HEIGHT - gameInfo.holdingsSize) fromX -= 2; else beepType = 0; } else\r
301                 if(fromX > BOARD_LEFT) fromX--; else beepType = 0; // off-board beep\r
302                 break;\r
303         case VK_RIGHT:\r
304                 if(fromX == BOARD_LEFT-2) fromX += 2; else\r
305                 if(fromX == BOARD_RGHT-1) { if(fromY < gameInfo.holdingsSize) fromX += 2; else beepType = 0; } else\r
306                 if(fromX < BOARD_RGHT-1) fromX++; else beepType = 0;\r
307                 break;\r
308         case VK_UP:\r
309                 if(fromX == BOARD_RGHT+1) { if(fromY < gameInfo.holdingsSize - 1) fromY++; else beepType = 0; } else\r
310                 if(fromY < BOARD_HEIGHT-1) fromY++; else beepType = 0;\r
311                 break;\r
312         case VK_DOWN:\r
313                 if(fromX == BOARD_LEFT-2) { if(fromY > BOARD_HEIGHT - gameInfo.holdingsSize) fromY--; else beepType = 0; } else\r
314                 if(fromY > 0) fromY--; else beepType = 0;\r
315                 break;\r
316         }\r
317         SetHighlights(fromX, fromY, -1, -1);\r
318         DrawPosition(FALSE, NULL);\r
319         currentPiece = boards[currentMove][fromY][fromX];\r
320         piece = PieceToName(currentPiece,1);\r
321         if(beepType == 1 && currentPiece != EmptySquare) beepType = currentPiece < (int) BlackPawn ? 2 : 3; // white or black beep\r
322         if(beeps[beepType] == beeps[1] && (fromX == BOARD_RGHT+1 || fromX == BOARD_LEFT-2)) beepType = 4; // holdings beep\r
323         beepType = beeps[beepType]%6;\r
324         if(beepType) MessageBeep(beepCodes[beepType]);\r
325         if(fromX == BOARD_LEFT - 2) {\r
326                 SayString("black holdings", FALSE);\r
327                 if(currentPiece != EmptySquare) {\r
328                         char buf[MSG_SIZ];\r
329                         n = boards[currentMove][fromY][1];\r
330                         sprintf(buf, "%d %s%s", n, PieceToName(currentPiece,0), n == 1 ? "" : "s");\r
331                         SayString(buf, TRUE);\r
332                 }\r
333         } else\r
334         if(fromX == BOARD_RGHT + 1) {\r
335                 SayString("white holdings", FALSE);\r
336                 if(currentPiece != EmptySquare) {\r
337                         char buf[MSG_SIZ];\r
338                         n = boards[currentMove][fromY][BOARD_WIDTH-2];\r
339                         sprintf(buf, "%d %s%s", n, PieceToName(currentPiece,0), n == 1 ? "" : "s");\r
340                         SayString(buf, TRUE);\r
341                 }\r
342         } else\r
343         if(fromX >= BOARD_LEFT && fromX < BOARD_RGHT) {\r
344                 char buf[MSG_SIZ];\r
345                 xchar = SquareToChar(fromX);\r
346                 ynum = SquareToNum(fromY);\r
347                 if(currentPiece != EmptySquare) {\r
348 //                      SayString(piece[0] == 'W' ? "white" : "black", TRUE);\r
349                         sprintf(buf, "%s %s %s", piece, xchar, ynum);\r
350                 } else sprintf(buf, "%s %s", xchar, ynum);\r
351                 SayString(buf, TRUE);\r
352         }\r
353         return;\r
354 }\r
355 \r
356 extern char castlingRights[MAX_MOVES][BOARD_SIZE];\r
357 int PosFlags(int nr);\r
358 \r
359 typedef struct {\r
360     int rf, ff, rt, ft;\r
361     int onlyCaptures;\r
362     int count;\r
363 } ReadClosure;\r
364 \r
365 extern void ReadCallback P((Board board, int flags, ChessMove kind,\r
366                                 int rf, int ff, int rt, int ft,\r
367                                 VOIDSTAR closure));\r
368 \r
369 void ReadCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
370      Board board;\r
371      int flags;\r
372      ChessMove kind;\r
373      int rf, ff, rt, ft;\r
374      VOIDSTAR closure;\r
375 {\r
376     register ReadClosure *cl = (ReadClosure *) closure;\r
377     ChessSquare possiblepiece;\r
378     char *piece, *xchar, *ynum ;\r
379 \r
380 //if(appData.debugMode) fprintf(debugFP, "%c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);\r
381     if(cl->ff == ff && cl->rf == rf) {\r
382         possiblepiece = board[rt][ft];\r
383         if(possiblepiece != EmptySquare) {\r
384                 piece = PieceToName(possiblepiece,1);\r
385                 xchar = SquareToChar(ft);\r
386                 ynum  = SquareToNum(rt);\r
387                 SayString(xchar , FALSE);\r
388                 SayString(ynum, FALSE);\r
389                 SayString(piece, FALSE);\r
390                 cl->count++;\r
391         }\r
392     }\r
393     if(cl->ft == ft && cl->rt == rt) {\r
394         possiblepiece = board[rf][ff];\r
395                 piece = PieceToName(possiblepiece,1);\r
396                 xchar = SquareToChar(ff);\r
397                 ynum  = SquareToNum(rf);\r
398                 SayString(xchar , FALSE);\r
399                 SayString(ynum, FALSE);\r
400                 SayString(piece, FALSE);\r
401                 cl->count++;\r
402     }\r
403 }\r
404 \r
405 VOID\r
406 PossibleAttackMove()\r
407 {\r
408         ReadClosure cl;\r
409         ChessSquare piece, victim;\r
410         int removedSelectedPiece = 0, swapColor;\r
411 \r
412 //if(appData.debugMode) fprintf(debugFP, "PossibleAttackMove %d %d %d %d\n", fromX, fromY, oldFromX, oldFromY);\r
413         if(fromY < 0 || fromY >= BOARD_HEIGHT) return;\r
414         if(fromX < BOARD_LEFT || fromX >= BOARD_RGHT) { SayString("holdings",FALSE); return; }\r
415 \r
416         piece = boards[currentMove][fromY][fromX];\r
417         if(piece == EmptySquare) { // if square is empty, try to substitute selected piece\r
418             if(oldFromX >= 0 && oldFromY >= 0) {\r
419                 piece = boards[currentMove][oldFromY][oldFromX];\r
420                 boards[currentMove][oldFromY][oldFromX] = EmptySquare;\r
421                 removedSelectedPiece = 1;\r
422                 SayString("Your", FALSE);\r
423                 SayString(PieceToName(piece, 0), FALSE);\r
424                 SayString("would have", FALSE);\r
425             } else { SayString("You must select a piece first", FALSE); return; }\r
426         }\r
427 \r
428         victim = boards[currentMove][fromY][fromX];\r
429         boards[currentMove][fromY][fromX] = piece; // make sure piece is actally there\r
430         SayString("possible captures from here are", FALSE);\r
431 \r
432         swapColor = piece <  (int)BlackPawn && !WhiteOnMove(currentMove) ||\r
433                     piece >= (int)BlackPawn &&  WhiteOnMove(currentMove);\r
434         cl.count = 0; cl.rf = fromY; cl.ff = fromX; cl.rt = cl.ft = -1;\r
435         GenLegal(boards[currentMove], PosFlags(currentMove + swapColor), EP_NONE, \r
436                                                 castlingRights[currentMove], ReadCallback, (VOIDSTAR) &cl);\r
437         if(cl.count == 0) SayString("None", FALSE);\r
438         boards[currentMove][fromY][fromX] = victim; // repair\r
439 \r
440         if( removedSelectedPiece ) boards[currentMove][oldFromY][oldFromX] = piece;\r
441 }\r
442 \r
443 \r
444 VOID\r
445 PossibleAttacked()\r
446 {\r
447         ReadClosure cl;\r
448         ChessSquare piece = EmptySquare, victim;\r
449 \r
450         if(fromY < 0 || fromY >= BOARD_HEIGHT) return;\r
451         if(fromX < BOARD_LEFT || fromX >= BOARD_RGHT) { SayString("holdings",FALSE); return; }\r
452 \r
453         if(oldFromX >= 0 && oldFromY >= 0) { // if piece is selected, remove it\r
454                 piece = boards[currentMove][oldFromY][oldFromX];\r
455                 boards[currentMove][oldFromY][oldFromX] = EmptySquare;\r
456         }\r
457 \r
458         SayString("Pieces that can capture you are", FALSE);\r
459 \r
460         victim = boards[currentMove][fromY][fromX]; // put dummy piece on target square, to activate Pawn captures\r
461         boards[currentMove][fromY][fromX] = WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen;\r
462         cl.count = 0; cl.rt = fromY; cl.ft = fromX; cl.rf = cl.ff = -1;\r
463         GenLegal(boards[currentMove], PosFlags(currentMove+1), EP_NONE, \r
464                                                 castlingRights[currentMove], ReadCallback, (VOIDSTAR) &cl);\r
465         if(cl.count == 0) SayString("None", FALSE);\r
466 \r
467         SayString("You are defended by", FALSE);\r
468 \r
469         boards[currentMove][fromY][fromX] = WhiteOnMove(currentMove) ? BlackQueen : WhiteQueen;\r
470         cl.count = 0; cl.rt = fromY; cl.ft = fromX; cl.rf = cl.ff = -1;\r
471         GenLegal(boards[currentMove], PosFlags(currentMove), EP_NONE, \r
472                                                 castlingRights[currentMove], ReadCallback, (VOIDSTAR) &cl);\r
473         if(cl.count == 0) SayString("None", FALSE);\r
474         boards[currentMove][fromY][fromX] = victim; // put back original occupant\r
475 \r
476         if(oldFromX >= 0 && oldFromY >= 0) { // put back possibl selected piece\r
477                 boards[currentMove][oldFromY][oldFromX] = piece;\r
478         }\r
479 }\r
480 \r
481 VOID\r
482 ReadRow()\r
483 {\r
484         ChessSquare currentpiece; \r
485         char *piece, *xchar, *ynum ;\r
486         int xPos, count=0;\r
487         ynum = SquareToNum(fromY);\r
488         \r
489         if(fromY < 0) return;\r
490 \r
491         for (xPos=BOARD_LEFT; xPos<BOARD_RGHT; xPos++) {\r
492                 currentpiece = boards[currentMove][fromY][xPos];        \r
493                 if(currentpiece != EmptySquare) {\r
494                         piece = PieceToName(currentpiece,1);\r
495                         xchar = SquareToChar(xPos);\r
496                         SayString(xchar , FALSE);\r
497                         SayString(ynum, FALSE);\r
498                         SayString(piece, FALSE);\r
499                         count++;\r
500                 }\r
501         }\r
502         if(count == 0) {\r
503                 SayString("rank", FALSE);\r
504                 SayString(ynum, FALSE);\r
505                 SayString("empty", FALSE);\r
506         }\r
507 }\r
508 \r
509 VOID\r
510 ReadColumn()\r
511 {\r
512         ChessSquare currentpiece; \r
513         char *piece, *xchar, *ynum ;\r
514         int yPos, count=0;\r
515         xchar = SquareToChar(fromX);\r
516         \r
517         if(fromX < 0) return;\r
518 \r
519         for (yPos=0; yPos<BOARD_HEIGHT; yPos++) {\r
520                 currentpiece = boards[currentMove][yPos][fromX];        \r
521                 if(currentpiece != EmptySquare) {\r
522                         piece = PieceToName(currentpiece,1);\r
523                         ynum = SquareToNum(yPos);\r
524                         SayString(xchar , FALSE);\r
525                         SayString(ynum, FALSE);\r
526                         SayString(piece, FALSE);\r
527                         count++;\r
528                 }\r
529         }\r
530         if(count == 0) {\r
531                 SayString(xchar, FALSE);\r
532                 SayString("file empty", FALSE);\r
533         }\r
534 }\r
535 \r
536 VOID\r
537 SayUpperDiagnols()\r
538 {\r
539         ChessSquare currentpiece; \r
540         char *piece, *xchar, *ynum ;\r
541         int yPos, xPos;\r
542         \r
543         if(fromX < 0 || fromY < 0) return;\r
544 \r
545         if(fromX < BOARD_RGHT-1 && fromY < BOARD_HEIGHT-1) {\r
546                 SayString("The diagnol squares to your upper right contain", FALSE);\r
547                 yPos = fromY+1;\r
548                 xPos = fromX+1;\r
549                 while(yPos<BOARD_HEIGHT && xPos<BOARD_RGHT) {\r
550                         currentpiece = boards[currentMove][yPos][xPos]; \r
551                         piece = PieceToName(currentpiece,1);\r
552                         xchar = SquareToChar(xPos);\r
553                         ynum = SquareToNum(yPos);\r
554                         SayString(xchar , FALSE);\r
555                         SayString(ynum, FALSE);\r
556                         SayString(piece, FALSE);\r
557                         yPos++;\r
558                         xPos++;\r
559                 }\r
560         }\r
561         else SayString("There is no squares to your upper right", FALSE);\r
562 \r
563         if(fromX > BOARD_LEFT && fromY < BOARD_HEIGHT-1) {\r
564                 SayString("The diagnol squares to your upper left contain", FALSE);\r
565                 yPos = fromY+1;\r
566                 xPos = fromX-1;\r
567                 while(yPos<BOARD_HEIGHT && xPos>=BOARD_LEFT) {\r
568                         currentpiece = boards[currentMove][yPos][xPos]; \r
569                         piece = PieceToName(currentpiece,1);\r
570                         xchar = SquareToChar(xPos);\r
571                         ynum = SquareToNum(yPos);\r
572                         SayString(xchar , FALSE);\r
573                         SayString(ynum, FALSE);\r
574                         SayString(piece, FALSE);\r
575                         yPos++;\r
576                         xPos--;\r
577                 }\r
578         }\r
579         else SayString("There is no squares to your upper left", FALSE);\r
580 }\r
581 \r
582 VOID\r
583 SayLowerDiagnols()\r
584 {\r
585         ChessSquare currentpiece; \r
586         char *piece, *xchar, *ynum ;\r
587         int yPos, xPos;\r
588         \r
589         if(fromX < 0 || fromY < 0) return;\r
590 \r
591         if(fromX < BOARD_RGHT-1 && fromY > 0) {\r
592                 SayString("The diagnol squares to your lower right contain", FALSE);\r
593                 yPos = fromY-1;\r
594                 xPos = fromX+1;\r
595                 while(yPos>=0 && xPos<BOARD_RGHT) {\r
596                         currentpiece = boards[currentMove][yPos][xPos]; \r
597                         piece = PieceToName(currentpiece,1);\r
598                         xchar = SquareToChar(xPos);\r
599                         ynum = SquareToNum(yPos);\r
600                         SayString(xchar , FALSE);\r
601                         SayString(ynum, FALSE);\r
602                         SayString(piece, FALSE);\r
603                         yPos--;\r
604                         xPos++;\r
605                 }\r
606         }\r
607         else SayString("There is no squares to your lower right", FALSE);\r
608 \r
609         if(fromX > BOARD_LEFT && fromY > 0) {\r
610                 SayString("The diagnol squares to your lower left contain", FALSE);\r
611                 yPos = fromY-1;\r
612                 xPos = fromX-1;\r
613                 while(yPos>=0 && xPos>=BOARD_LEFT) {\r
614                         currentpiece = boards[currentMove][yPos][xPos]; \r
615                         piece = PieceToName(currentpiece,1);\r
616                         xchar = SquareToChar(xPos);\r
617                         ynum = SquareToNum(yPos);\r
618                         SayString(xchar , FALSE);\r
619                         SayString(ynum, FALSE);\r
620                         SayString(piece, FALSE);\r
621                         yPos--;\r
622                         xPos--;\r
623                 }\r
624         }\r
625         else SayString("There is no squares to your lower left", FALSE);\r
626 }\r
627 \r
628 VOID\r
629 SayKnightMoves()\r
630 {\r
631         ChessSquare currentpiece, oldpiece; \r
632         char *piece, *xchar, *ynum ;\r
633 \r
634         oldpiece = boards[currentMove][fromY][fromX];\r
635         if(oldpiece == WhiteKnight || oldpiece == BlackKnight) \r
636                 SayString("The possible squares a Knight could move to are", FALSE);\r
637         else\r
638                 SayString("The squares a Knight could possibly attack from are", FALSE);\r
639 \r
640         if (fromY+2 < BOARD_HEIGHT && fromX-1 >= BOARD_LEFT) {\r
641                 currentpiece = boards[currentMove][fromY+2][fromX-1];\r
642                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
643                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
644                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
645                 {\r
646                         piece = PieceToName(currentpiece,1);\r
647                         xchar = SquareToChar(fromX-1);\r
648                         ynum = SquareToNum(fromY+2);\r
649                         SayString(xchar , FALSE);\r
650                         SayString(ynum, FALSE);\r
651                         SayString(piece, FALSE);\r
652                 }\r
653         }\r
654 \r
655         if (fromY+2 < BOARD_HEIGHT && fromX+1 < BOARD_RGHT) {\r
656                 currentpiece = boards[currentMove][fromY+2][fromX+1];\r
657                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
658                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
659                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
660                 {\r
661                         piece = PieceToName(currentpiece,1);\r
662                         xchar = SquareToChar(fromX+1);\r
663                         ynum = SquareToNum(fromY+2);\r
664                         SayString(xchar , FALSE);\r
665                         SayString(ynum, FALSE);\r
666                         SayString(piece, FALSE);\r
667                 }\r
668         }\r
669         \r
670         if (fromY+1 < BOARD_HEIGHT && fromX+2 < BOARD_RGHT) {\r
671                 currentpiece = boards[currentMove][fromY+1][fromX+2];\r
672                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
673                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
674                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
675                 {\r
676                         piece = PieceToName(currentpiece,1);\r
677                         xchar = SquareToChar(fromX+2);\r
678                         ynum = SquareToNum(fromY+1);\r
679                         SayString(xchar , FALSE);\r
680                         SayString(ynum, FALSE);\r
681                         SayString(piece, FALSE);\r
682                 }\r
683         }\r
684         \r
685         if (fromY-1 >= 0 && fromX+2 < BOARD_RGHT) {\r
686                 currentpiece = boards[currentMove][fromY-1][fromX+2];\r
687                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
688                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
689                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
690                 {\r
691                         piece = PieceToName(currentpiece,1);\r
692                         xchar = SquareToChar(fromX+2);\r
693                         ynum = SquareToNum(fromY-1);\r
694                         SayString(xchar , FALSE);\r
695                         SayString(ynum, FALSE);\r
696                         SayString(piece, FALSE);\r
697                 }\r
698         }\r
699         \r
700         if (fromY-2 >= 0 && fromX+1 < BOARD_RGHT) {\r
701                 currentpiece = boards[currentMove][fromY-2][fromX+1];\r
702                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
703                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
704                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
705                 {\r
706                         piece = PieceToName(currentpiece,1);\r
707                         xchar = SquareToChar(fromX+1);\r
708                         ynum = SquareToNum(fromY-2);\r
709                         SayString(xchar , FALSE);\r
710                         SayString(ynum, FALSE);\r
711                         SayString(piece, FALSE);\r
712                 }\r
713         }\r
714         \r
715         if (fromY-2 >= 0 && fromX-1 >= BOARD_LEFT) {\r
716                 currentpiece = boards[currentMove][fromY-2][fromX-1];\r
717                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
718                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
719                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
720                 {\r
721                         piece = PieceToName(currentpiece,1);\r
722                         xchar = SquareToChar(fromX-1);\r
723                         ynum = SquareToNum(fromY-2);\r
724                         SayString(xchar , FALSE);\r
725                         SayString(ynum, FALSE);\r
726                         SayString(piece, FALSE);\r
727                 }\r
728         }\r
729         \r
730         if (fromY-1 >= 0 && fromX-2 >= BOARD_LEFT) {\r
731                 currentpiece = boards[currentMove][fromY-1][fromX-2];\r
732                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
733                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
734                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
735                 {\r
736                         piece = PieceToName(currentpiece,1);\r
737                         xchar = SquareToChar(fromX-2);\r
738                         ynum = SquareToNum(fromY-1);\r
739                         SayString(xchar , FALSE);\r
740                         SayString(ynum, FALSE);\r
741                         SayString(piece, FALSE);\r
742                 }\r
743         }\r
744         \r
745         if (fromY+1 < BOARD_HEIGHT && fromX-2 >= BOARD_LEFT) {\r
746                 currentpiece = boards[currentMove][fromY+1][fromX-2];\r
747                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
748                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
749                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
750                 {\r
751                         piece = PieceToName(currentpiece,1);\r
752                         xchar = SquareToChar(fromX-2);\r
753                         ynum = SquareToNum(fromY+1);\r
754                         SayString(xchar , FALSE);\r
755                         SayString(ynum, FALSE);\r
756                         SayString(piece, FALSE);\r
757                 }\r
758         }\r
759 }\r
760 \r
761 VOID\r
762 SayPieces(ChessSquare p)\r
763 {\r
764         ChessSquare currentpiece;  \r
765         char *piece, *xchar, *ynum ;\r
766         int yPos, xPos, count = 0;\r
767         char buf[50];\r
768 \r
769         if(p == WhitePlay)   SayString("White pieces", FALSE); else\r
770         if(p == BlackPlay)   SayString("Black pieces", FALSE); else\r
771         if(p == EmptySquare) SayString("Pieces", FALSE); else {\r
772                 sprintf(buf, "%ss", PieceToName(p,1));\r
773                 SayString(buf, FALSE);\r
774         }\r
775         SayString("are located", FALSE);\r
776         for(yPos=0; yPos<BOARD_HEIGHT; yPos++) {\r
777                 for(xPos=BOARD_LEFT; xPos<BOARD_RGHT; xPos++) {\r
778                         currentpiece = boards[currentMove][yPos][xPos]; \r
779                         if(p == BlackPlay && currentpiece >= BlackPawn && currentpiece <= BlackKing ||\r
780                            p == WhitePlay && currentpiece >= WhitePawn && currentpiece <= WhiteKing   )\r
781                                 piece = PieceToName(currentpiece,0);\r
782                         else if(p == EmptySquare && currentpiece != EmptySquare)\r
783                                 piece = PieceToName(currentpiece,1);\r
784                         else if(p == currentpiece)\r
785                                 piece = NULL;\r
786                         else continue;\r
787                                 \r
788                                 if(count == 0) SayString("at", FALSE);\r
789                                 xchar = SquareToChar(xPos);\r
790                                 ynum = SquareToNum(yPos);\r
791                                 SayString(xchar , FALSE);\r
792                                 SayString(ynum, FALSE);\r
793                                 if(piece) SayString(piece, FALSE);\r
794                                 count++;\r
795                 }\r
796         }\r
797         if(count == 0) SayString("nowhere", FALSE);\r
798 }\r
799 \r
800 VOID\r
801 SayCurrentPos()\r
802 {\r
803         ChessSquare currentpiece;\r
804         char *piece, *xchar, *ynum ;\r
805         if(fromX <  BOARD_LEFT) { SayString("You strayed into the white holdings", FALSE); return; }\r
806         if(fromX >= BOARD_RGHT) { SayString("You strayed into the black holdings", FALSE); return; }\r
807         currentpiece = boards[currentMove][fromY][fromX];       \r
808         piece = PieceToName(currentpiece,1);\r
809         ynum = SquareToNum(fromY);\r
810         xchar = SquareToChar(fromX);\r
811         SayString("Your current position is", FALSE);\r
812         SayString(xchar, FALSE);\r
813         SayString(ynum, FALSE);\r
814         SayString(piece, FALSE);\r
815         if(((fromX-BOARD_LEFT) ^ fromY)&1)\r
816                 SayString("on a light square",FALSE);\r
817         else \r
818                 SayString("on a dark square",FALSE);\r
819 \r
820         PossibleAttacked();\r
821         return;\r
822 }\r
823 \r
824 VOID\r
825 SayAllBoard()\r
826 {\r
827         int Xpos, Ypos;\r
828         ChessSquare currentpiece;\r
829         char *piece, *ynum ;\r
830         \r
831         if(gameInfo.holdingsWidth) {\r
832                 int first = 0;\r
833                 for(Ypos=0; Ypos<gameInfo.holdingsSize; Ypos++) {\r
834                         int n = boards[currentMove][Ypos][BOARD_WIDTH-2];\r
835                         if(n) {  char buf[MSG_SIZ];\r
836                                 if(!first++) SayString("white holds", FALSE);\r
837                                 currentpiece = boards[currentMove][Ypos][BOARD_WIDTH-1];        \r
838                                 piece = PieceToName(currentpiece,0);\r
839                                 sprintf(buf, "%d %s%s", n, piece, (n==1 ? "" : "s") );\r
840                                 SayString(buf, FALSE);\r
841                         }\r
842                 }\r
843                 first = 0;\r
844                 for(Ypos=BOARD_HEIGHT-1; Ypos>=BOARD_HEIGHT - gameInfo.holdingsSize; Ypos--) {\r
845                         int n = boards[currentMove][Ypos][1];\r
846                         if(n) {  char buf[MSG_SIZ];\r
847                                 if(!first++) SayString("black holds", FALSE);\r
848                                 currentpiece = boards[currentMove][Ypos][0];    \r
849                                 piece = PieceToName(currentpiece,0);\r
850                                 sprintf(buf, "%d %s%s", n, piece, (n==1 ? "" : "s") );\r
851                                 SayString(buf, FALSE);\r
852                         }\r
853                 }\r
854         }\r
855 \r
856         for(Ypos=BOARD_HEIGHT-1; Ypos>=0; Ypos--) {\r
857                 ynum = ordinals[Ypos + (gameInfo.boardHeight < 10)];\r
858                 SayString(ynum, FALSE);\r
859                 SayString("rank", FALSE);\r
860                 for(Xpos=BOARD_LEFT; Xpos<BOARD_RGHT; Xpos++) {\r
861                         currentpiece = boards[currentMove][Ypos][Xpos]; \r
862                         if(currentpiece != EmptySquare) {\r
863                                 int count = 0;\r
864                                 char buf[50];\r
865                                 piece = PieceToName(currentpiece,1);\r
866                                 while(Xpos < BOARD_RGHT && boards[currentMove][Ypos][Xpos] == currentpiece)\r
867                                         Xpos++, count++;\r
868                                 if(count > 1) { \r
869                                         sprintf(buf, "%d %ss", count, piece);\r
870                                 } else  sprintf(buf, "%s", piece);\r
871                                 Xpos--;\r
872                                 SayString(buf, FALSE);\r
873                         } else {\r
874                                 int count = 0, oldX = Xpos;\r
875                                 while(Xpos < BOARD_RGHT && boards[currentMove][Ypos][Xpos] == EmptySquare)\r
876                                         Xpos++, count++;\r
877                                 if(Xpos == BOARD_RGHT && oldX == BOARD_LEFT)\r
878                                         SayString("all", FALSE);\r
879                                 else{\r
880                                     if(count > 1) { \r
881                                         char buf[10];\r
882                                         sprintf(buf, "%d", count);\r
883                                         SayString(buf, FALSE);\r
884                                     }\r
885                                     Xpos--;\r
886                                 }\r
887                                 SayString("empty", FALSE);\r
888                         }\r
889                 }\r
890         }\r
891         \r
892 }\r
893 \r
894 VOID\r
895 SayWhosTurn()\r
896 {\r
897         if(gameMode == MachinePlaysBlack || gameMode == IcsPlayingBlack) {\r
898                 if(WhiteOnMove(currentMove))\r
899                         SayString("It is your turn", FALSE);\r
900                 else    SayString("It is your opponents turn", FALSE);\r
901         } else if(gameMode == MachinePlaysWhite || gameMode == IcsPlayingWhite) {\r
902                 if(WhiteOnMove(currentMove))\r
903                         SayString("It is your opponents turn", FALSE);\r
904                 else    SayString("It is your turn", FALSE);\r
905         } else {\r
906                 if(WhiteOnMove(currentMove)) \r
907                         SayString("White is on move here", FALSE);\r
908                 else    SayString("Black is on move here", FALSE);\r
909         }\r
910 }\r
911         \r
912 extern char *commentList[];\r
913 \r
914 VOID\r
915 SayMachineMove(int evenIfDuplicate)\r
916 {\r
917         int len, xPos, yPos, moveNr, secondSpace = 0, castle = 0, n;\r
918         ChessSquare currentpiece;\r
919         char *piece, *xchar, *ynum, *p, checkMark = 0;\r
920         char c, buf[MSG_SIZ], comment[MSG_SIZ];\r
921         static char disambiguation[2];\r
922         static int previousMove = 0;\r
923 \r
924         if(appData.debugMode) fprintf(debugFP, "Message = '%s'\n", messageText);\r
925         if(gameMode == BeginningOfGame) return;\r
926         if(messageText[0] == '[') return;\r
927         comment[0]= 0;\r
928             if(isdigit(messageText[0])) { // message is move, possibly with thinking output\r
929                 int dotCount = 0, spaceCount = 0;\r
930                 sscanf(messageText, "%d", &moveNr);\r
931                 len = 0;\r
932                 // [HGM] show: better extraction of move\r
933                 while (messageText[len] != NULLCHAR) {\r
934                     if(messageText[len] == '.' && spaceCount == 0) dotCount++;\r
935                     if(messageText[len] == ' ') { if(++spaceCount == 2) secondSpace = len; }\r
936                     if(messageText[len] == '{') { // we detected a comment\r
937                         if(isalpha(messageText[len+1]) ) sscanf(messageText+len, "{%[^}]}", comment);\r
938                         break;\r
939                     }\r
940                     if(messageText[len] == '[') { // we detected thinking output\r
941                         int depth; float score=0; char c, lastMover = (dotCount == 3 ? 'B' : 'W');\r
942                         if(sscanf(messageText+len+1, "%d]%c%f", &depth, &c, &score) > 1) {\r
943                             if(c == ' ') { // if not explicitly specified, figure out source of thinking output\r
944                                 switch(gameMode) {\r
945                                   case MachinePlaysWhite:\r
946                                   case IcsPlayingWhite:\r
947                                     c = 'W'; break;\r
948                                   case IcsPlayingBlack:\r
949                                   case MachinePlaysBlack:\r
950                                     c = 'B'; \r
951                                   default:\r
952                                     break;\r
953                                 }\r
954                             }\r
955                             if(c != lastMover) return; // line is thinking output of future move, ignore.\r
956                             if(2*moveNr - (dotCount < 2) == previousMove)\r
957                                 return; // do not repeat same move; likely ponder output\r
958                             sprintf(buf, "score %s %d at %d ply", \r
959                                         score > 0 ? "plus" : score < 0 ? "minus" : "",\r
960                                         (int) (fabs(score)*100+0.5),\r
961                                         depth );\r
962                             SayString(buf, FALSE); // move + thinking output describing it; say it.\r
963                         }\r
964                         while(messageText[len-1] == ' ') len--; // position just behind move;\r
965                         break;\r
966                     }\r
967                     if(messageText[len] == '(') { // ICS time printed behind move\r
968                         while(messageText[len+1] && messageText[len] != ')') len++; // skip it\r
969                     }\r
970                     len++;\r
971                 }\r
972                 if(secondSpace) len = secondSpace; // position behind move\r
973                 if(messageText[len-1] == '+' || messageText[len-1] == '#') {  /* you are in checkmate */\r
974                         len--; // strip off check or mate indicator\r
975                       checkMark = messageText[len]; // make sure still seen after we stip off promo piece\r
976                 }\r
977                 if(messageText[len-2] == '=') {  /* promotion */\r
978                         len-=2; // strip off promotion piece\r
979                         SayString("promotion", FALSE);\r
980                 }\r
981 \r
982                 n = 2*moveNr - (dotCount < 2);\r
983 \r
984                 if(previousMove != 2*moveNr + (dotCount > 1) || evenIfDuplicate) { \r
985                     char number[20];\r
986                     previousMove = 2*moveNr + (dotCount > 1); // remember move nr of move last spoken\r
987                     sprintf(number, "%d", moveNr);\r
988 \r
989                     yPos = CoordToNum(messageText[len-1]);  /* turn char coords to ints */\r
990                     xPos = CoordToNum(messageText[len-2]);\r
991                     if(xPos < 0 || xPos > 11) return; // prevent crashes if no coord string available to speak\r
992                     if(yPos < 0 || yPos > 9)  return;\r
993                     currentpiece = boards[n][yPos][xPos];       \r
994                     piece = PieceToName(currentpiece,0);\r
995                     ynum = SquareToNum(yPos);\r
996                     xchar = SquareToChar(xPos);\r
997                     c = messageText[len-3];\r
998                     if(c == 'x') c = messageText[len-4];\r
999                     if(!isdigit(c) && c < 'a' && c != '@') c = 0;\r
1000                     disambiguation[0] = c;\r
1001                     SayString(WhiteOnMove(n) ? "Black" : "White", FALSE);\r
1002                     SayString("move", FALSE);\r
1003                     SayString(number, FALSE);\r
1004 //                  if(c==0 || c=='@') SayString("a", FALSE);\r
1005                     // intercept castling moves\r
1006                     p = StrStr(messageText, "O-O-O");\r
1007                     if(p && p-messageText < len) {\r
1008                         SayString("queen side castling",FALSE);\r
1009                         castle = 1;\r
1010                     } else {\r
1011                         p = StrStr(messageText, "O-O");\r
1012                         if(p && p-messageText < len) {\r
1013                             SayString("king side castling",FALSE);\r
1014                             castle = 1;\r
1015                         }\r
1016                     }\r
1017                     if(!castle) {\r
1018                         SayString(piece, FALSE);\r
1019                         if(c == '@') SayString("dropped on", FALSE); else\r
1020                         if(c) SayString(disambiguation, FALSE);\r
1021                         SayString("to", FALSE);\r
1022                         SayString(xchar, FALSE);\r
1023                         SayString(ynum, FALSE);\r
1024                         if(messageText[len-3] == 'x') {\r
1025                                 currentpiece = boards[n-1][yPos][xPos];\r
1026                                 if(currentpiece != EmptySquare) {\r
1027                                         piece = PieceToName(currentpiece,0);\r
1028                                         SayString("Capturing a",FALSE);\r
1029                                         SayString(piece, FALSE);\r
1030                                 } else SayString("Capturing onn passann",FALSE);\r
1031                         }\r
1032                     }\r
1033                     if(checkMark == '+') SayString("check", FALSE); else\r
1034                     if(checkMark == '#') {\r
1035                                 SayString("finishing off", FALSE);\r
1036                                 SayString(WhiteOnMove(n) ? "White" : "Black", FALSE);\r
1037                     }\r
1038                 }\r
1039 \r
1040                 /* say comment after move, possibly with result */\r
1041                 p = NULL;\r
1042                 if(StrStr(messageText, " 1-0")) p = "white wins"; else\r
1043                 if(StrStr(messageText, " 0-1")) p = "black wins"; else\r
1044                 if(StrStr(messageText, " 1/2-1/2")) p = "game ends in a draw";\r
1045                 if(comment[0]) {\r
1046                     if(p) {\r
1047                         if(!StrCaseStr(comment, "draw") && \r
1048                            !StrCaseStr(comment, "white") && \r
1049                            !StrCaseStr(comment, "black") ) {\r
1050                                 SayString(p, FALSE);\r
1051                                 SayString("due to", FALSE);\r
1052                         }\r
1053                     }\r
1054                     SayString(comment, FALSE); // alphabetic comment (usually game end)\r
1055                 } else if(p) SayString(p, FALSE);\r
1056 \r
1057                 if(commentDialog && commentList[currentMove]) SetFocus(commentDialog);\r
1058 \r
1059             } else {\r
1060                 /* starts not with digit */\r
1061                 if(StrCaseStr(messageText, "illegal")) PlayIcsUnfinishedSound();\r
1062                 SayString(messageText, FALSE);\r
1063             }\r
1064 \r
1065 }\r
1066 \r
1067 VOID\r
1068 SayClockTime()\r
1069 {\r
1070         char buf1[50], buf2[50];\r
1071         char *str1, *str2;\r
1072         static long int lastWhiteTime, lastBlackTime;\r
1073 \r
1074         suppressClocks = 1; // if user is using alt+T command, no reason to display them\r
1075         if(abs(lastWhiteTime - whiteTimeRemaining) < 1000 && abs(lastBlackTime - blackTimeRemaining) < 1000)\r
1076                 suppressClocks = 0; // back on after two requests in rapid succession\r
1077         sprintf(buf1, "%s", TimeString(whiteTimeRemaining));\r
1078         str1 = buf1;\r
1079         SayString("White's remaining time is", FALSE);\r
1080         SayString(str1, FALSE);\r
1081         sprintf(buf2, "%s", TimeString(blackTimeRemaining));\r
1082         str2 = buf2;\r
1083         SayString("Black's remaining time is", FALSE);\r
1084         SayString(str2, FALSE);\r
1085         lastWhiteTime = whiteTimeRemaining;\r
1086         lastBlackTime = blackTimeRemaining;\r
1087 }\r
1088 \r
1089 VOID\r
1090 Toggle(Boolean *b, char *mess)\r
1091 {\r
1092         *b = !*b;\r
1093         SayString(mess, FALSE);\r
1094         SayString("is now", FALSE);\r
1095         SayString(*b ? "on" : "off", FALSE);\r
1096 }\r
1097 \r
1098 /* handles keyboard moves in a click-click fashion */\r
1099 VOID\r
1100 KeyboardMove(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
1101 {\r
1102         ChessSquare currentpiece;\r
1103         char *piece;\r
1104         \r
1105         static BOOLEAN sameAgain = FALSE;\r
1106         switch (message) {\r
1107         case WM_KEYDOWN:\r
1108                 sameAgain = FALSE;\r
1109                 if(oldFromX == fromX && oldFromY == fromY) {\r
1110                         sameAgain = TRUE;\r
1111                         /* click on same square */\r
1112                         break;\r
1113                 }\r
1114                 else if(oldFromX != -1) {\r
1115                         \r
1116                         ChessSquare pdown, pup;\r
1117       pdown = boards[currentMove][oldFromY][oldFromX];\r
1118       pup = boards[currentMove][fromY][fromX];\r
1119                 \r
1120                 if (gameMode == EditPosition ||\r
1121                         !((WhitePawn <= pdown && pdown <= WhiteKing &&\r
1122                                  WhitePawn <= pup && pup <= WhiteKing) ||\r
1123                                 (BlackPawn <= pdown && pdown <= BlackKing &&\r
1124                                  BlackPawn <= pup && pup <= BlackKing))) {\r
1125                         /* EditPosition, empty square, or different color piece;\r
1126                         click-click move is possible */\r
1127                 \r
1128                         if (IsPromotion(oldFromX, oldFromY, fromX, fromY)) {\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, NULLCHAR);\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