updated year in copyright info
[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, 2010 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[8000], *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 int PosFlags(int nr);\r
356 \r
357 typedef struct {\r
358     int rf, ff, rt, ft;\r
359     int onlyCaptures;\r
360     int count;\r
361 } ReadClosure;\r
362 \r
363 extern void ReadCallback P((Board board, int flags, ChessMove kind,\r
364                                 int rf, int ff, int rt, int ft,\r
365                                 VOIDSTAR closure));\r
366 \r
367 void ReadCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
368      Board board;\r
369      int flags;\r
370      ChessMove kind;\r
371      int rf, ff, rt, ft;\r
372      VOIDSTAR closure;\r
373 {\r
374     register ReadClosure *cl = (ReadClosure *) closure;\r
375     ChessSquare possiblepiece;\r
376     char *piece, *xchar, *ynum ;\r
377 \r
378 //if(appData.debugMode) fprintf(debugFP, "%c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);\r
379     if(cl->ff == ff && cl->rf == rf) {\r
380         possiblepiece = board[rt][ft];\r
381         if(possiblepiece != EmptySquare) {\r
382                 piece = PieceToName(possiblepiece,1);\r
383                 xchar = SquareToChar(ft);\r
384                 ynum  = SquareToNum(rt);\r
385                 SayString(xchar , FALSE);\r
386                 SayString(ynum, FALSE);\r
387                 SayString(piece, FALSE);\r
388                 cl->count++;\r
389         }\r
390     }\r
391     if(cl->ft == ft && cl->rt == rt) {\r
392         possiblepiece = board[rf][ff];\r
393                 piece = PieceToName(possiblepiece,1);\r
394                 xchar = SquareToChar(ff);\r
395                 ynum  = SquareToNum(rf);\r
396                 SayString(xchar , FALSE);\r
397                 SayString(ynum, FALSE);\r
398                 SayString(piece, FALSE);\r
399                 cl->count++;\r
400     }\r
401 }\r
402 \r
403 VOID\r
404 PossibleAttackMove()\r
405 {\r
406         ReadClosure cl;\r
407         ChessSquare piece, victim;\r
408         int removedSelectedPiece = 0, swapColor;\r
409 \r
410 //if(appData.debugMode) fprintf(debugFP, "PossibleAttackMove %d %d %d %d\n", fromX, fromY, oldFromX, oldFromY);\r
411         if(fromY < 0 || fromY >= BOARD_HEIGHT) return;\r
412         if(fromX < BOARD_LEFT || fromX >= BOARD_RGHT) { SayString("holdings",FALSE); return; }\r
413 \r
414         piece = boards[currentMove][fromY][fromX];\r
415         if(piece == EmptySquare) { // if square is empty, try to substitute selected piece\r
416             if(oldFromX >= 0 && oldFromY >= 0) {\r
417                 piece = boards[currentMove][oldFromY][oldFromX];\r
418                 boards[currentMove][oldFromY][oldFromX] = EmptySquare;\r
419                 removedSelectedPiece = 1;\r
420                 SayString("Your", FALSE);\r
421                 SayString(PieceToName(piece, 0), FALSE);\r
422                 SayString("would have", FALSE);\r
423             } else { SayString("You must select a piece first", FALSE); return; }\r
424         }\r
425 \r
426         victim = boards[currentMove][fromY][fromX];\r
427         boards[currentMove][fromY][fromX] = piece; // make sure piece is actally there\r
428         SayString("possible captures from here are", FALSE);\r
429 \r
430         swapColor = piece <  (int)BlackPawn && !WhiteOnMove(currentMove) ||\r
431                     piece >= (int)BlackPawn &&  WhiteOnMove(currentMove);\r
432         cl.count = 0; cl.rf = fromY; cl.ff = fromX; cl.rt = cl.ft = -1;\r
433         GenLegal(boards[currentMove], PosFlags(currentMove + swapColor), ReadCallback, (VOIDSTAR) &cl);\r
434         if(cl.count == 0) SayString("None", FALSE);\r
435         boards[currentMove][fromY][fromX] = victim; // repair\r
436 \r
437         if( removedSelectedPiece ) boards[currentMove][oldFromY][oldFromX] = piece;\r
438 }\r
439 \r
440 \r
441 VOID\r
442 PossibleAttacked()\r
443 {\r
444         ReadClosure cl;\r
445         ChessSquare piece = EmptySquare, victim;\r
446 \r
447         if(fromY < 0 || fromY >= BOARD_HEIGHT) return;\r
448         if(fromX < BOARD_LEFT || fromX >= BOARD_RGHT) { SayString("holdings",FALSE); return; }\r
449 \r
450         if(oldFromX >= 0 && oldFromY >= 0) { // if piece is selected, remove it\r
451                 piece = boards[currentMove][oldFromY][oldFromX];\r
452                 boards[currentMove][oldFromY][oldFromX] = EmptySquare;\r
453         }\r
454 \r
455         SayString("Pieces that can capture you are", FALSE);\r
456 \r
457         victim = boards[currentMove][fromY][fromX]; // put dummy piece on target square, to activate Pawn captures\r
458         boards[currentMove][fromY][fromX] = WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen;\r
459         cl.count = 0; cl.rt = fromY; cl.ft = fromX; cl.rf = cl.ff = -1;\r
460         GenLegal(boards[currentMove], PosFlags(currentMove+1), ReadCallback, (VOIDSTAR) &cl);\r
461         if(cl.count == 0) SayString("None", FALSE);\r
462 \r
463         SayString("You are defended by", FALSE);\r
464 \r
465         boards[currentMove][fromY][fromX] = WhiteOnMove(currentMove) ? BlackQueen : WhiteQueen;\r
466         cl.count = 0; cl.rt = fromY; cl.ft = fromX; cl.rf = cl.ff = -1;\r
467         GenLegal(boards[currentMove], PosFlags(currentMove), ReadCallback, (VOIDSTAR) &cl);\r
468         if(cl.count == 0) SayString("None", FALSE);\r
469         boards[currentMove][fromY][fromX] = victim; // put back original occupant\r
470 \r
471         if(oldFromX >= 0 && oldFromY >= 0) { // put back possibl selected piece\r
472                 boards[currentMove][oldFromY][oldFromX] = piece;\r
473         }\r
474 }\r
475 \r
476 VOID\r
477 ReadRow()\r
478 {\r
479         ChessSquare currentpiece; \r
480         char *piece, *xchar, *ynum ;\r
481         int xPos, count=0;\r
482         ynum = SquareToNum(fromY);\r
483         \r
484         if(fromY < 0) return;\r
485 \r
486         for (xPos=BOARD_LEFT; xPos<BOARD_RGHT; xPos++) {\r
487                 currentpiece = boards[currentMove][fromY][xPos];        \r
488                 if(currentpiece != EmptySquare) {\r
489                         piece = PieceToName(currentpiece,1);\r
490                         xchar = SquareToChar(xPos);\r
491                         SayString(xchar , FALSE);\r
492                         SayString(ynum, FALSE);\r
493                         SayString(piece, FALSE);\r
494                         count++;\r
495                 }\r
496         }\r
497         if(count == 0) {\r
498                 SayString("rank", FALSE);\r
499                 SayString(ynum, FALSE);\r
500                 SayString("empty", FALSE);\r
501         }\r
502 }\r
503 \r
504 VOID\r
505 ReadColumn()\r
506 {\r
507         ChessSquare currentpiece; \r
508         char *piece, *xchar, *ynum ;\r
509         int yPos, count=0;\r
510         xchar = SquareToChar(fromX);\r
511         \r
512         if(fromX < 0) return;\r
513 \r
514         for (yPos=0; yPos<BOARD_HEIGHT; yPos++) {\r
515                 currentpiece = boards[currentMove][yPos][fromX];        \r
516                 if(currentpiece != EmptySquare) {\r
517                         piece = PieceToName(currentpiece,1);\r
518                         ynum = SquareToNum(yPos);\r
519                         SayString(xchar , FALSE);\r
520                         SayString(ynum, FALSE);\r
521                         SayString(piece, FALSE);\r
522                         count++;\r
523                 }\r
524         }\r
525         if(count == 0) {\r
526                 SayString(xchar, FALSE);\r
527                 SayString("file empty", FALSE);\r
528         }\r
529 }\r
530 \r
531 VOID\r
532 SayUpperDiagnols()\r
533 {\r
534         ChessSquare currentpiece; \r
535         char *piece, *xchar, *ynum ;\r
536         int yPos, xPos;\r
537         \r
538         if(fromX < 0 || fromY < 0) return;\r
539 \r
540         if(fromX < BOARD_RGHT-1 && fromY < BOARD_HEIGHT-1) {\r
541                 SayString("The diagnol squares to your upper right contain", FALSE);\r
542                 yPos = fromY+1;\r
543                 xPos = fromX+1;\r
544                 while(yPos<BOARD_HEIGHT && xPos<BOARD_RGHT) {\r
545                         currentpiece = boards[currentMove][yPos][xPos]; \r
546                         piece = PieceToName(currentpiece,1);\r
547                         xchar = SquareToChar(xPos);\r
548                         ynum = SquareToNum(yPos);\r
549                         SayString(xchar , FALSE);\r
550                         SayString(ynum, FALSE);\r
551                         SayString(piece, FALSE);\r
552                         yPos++;\r
553                         xPos++;\r
554                 }\r
555         }\r
556         else SayString("There is no squares to your upper right", FALSE);\r
557 \r
558         if(fromX > BOARD_LEFT && fromY < BOARD_HEIGHT-1) {\r
559                 SayString("The diagnol squares to your upper left contain", FALSE);\r
560                 yPos = fromY+1;\r
561                 xPos = fromX-1;\r
562                 while(yPos<BOARD_HEIGHT && xPos>=BOARD_LEFT) {\r
563                         currentpiece = boards[currentMove][yPos][xPos]; \r
564                         piece = PieceToName(currentpiece,1);\r
565                         xchar = SquareToChar(xPos);\r
566                         ynum = SquareToNum(yPos);\r
567                         SayString(xchar , FALSE);\r
568                         SayString(ynum, FALSE);\r
569                         SayString(piece, FALSE);\r
570                         yPos++;\r
571                         xPos--;\r
572                 }\r
573         }\r
574         else SayString("There is no squares to your upper left", FALSE);\r
575 }\r
576 \r
577 VOID\r
578 SayLowerDiagnols()\r
579 {\r
580         ChessSquare currentpiece; \r
581         char *piece, *xchar, *ynum ;\r
582         int yPos, xPos;\r
583         \r
584         if(fromX < 0 || fromY < 0) return;\r
585 \r
586         if(fromX < BOARD_RGHT-1 && fromY > 0) {\r
587                 SayString("The diagnol squares to your lower right contain", FALSE);\r
588                 yPos = fromY-1;\r
589                 xPos = fromX+1;\r
590                 while(yPos>=0 && xPos<BOARD_RGHT) {\r
591                         currentpiece = boards[currentMove][yPos][xPos]; \r
592                         piece = PieceToName(currentpiece,1);\r
593                         xchar = SquareToChar(xPos);\r
594                         ynum = SquareToNum(yPos);\r
595                         SayString(xchar , FALSE);\r
596                         SayString(ynum, FALSE);\r
597                         SayString(piece, FALSE);\r
598                         yPos--;\r
599                         xPos++;\r
600                 }\r
601         }\r
602         else SayString("There is no squares to your lower right", FALSE);\r
603 \r
604         if(fromX > BOARD_LEFT && fromY > 0) {\r
605                 SayString("The diagnol squares to your lower left contain", FALSE);\r
606                 yPos = fromY-1;\r
607                 xPos = fromX-1;\r
608                 while(yPos>=0 && xPos>=BOARD_LEFT) {\r
609                         currentpiece = boards[currentMove][yPos][xPos]; \r
610                         piece = PieceToName(currentpiece,1);\r
611                         xchar = SquareToChar(xPos);\r
612                         ynum = SquareToNum(yPos);\r
613                         SayString(xchar , FALSE);\r
614                         SayString(ynum, FALSE);\r
615                         SayString(piece, FALSE);\r
616                         yPos--;\r
617                         xPos--;\r
618                 }\r
619         }\r
620         else SayString("There is no squares to your lower left", FALSE);\r
621 }\r
622 \r
623 VOID\r
624 SayKnightMoves()\r
625 {\r
626         ChessSquare currentpiece, oldpiece; \r
627         char *piece, *xchar, *ynum ;\r
628 \r
629         oldpiece = boards[currentMove][fromY][fromX];\r
630         if(oldpiece == WhiteKnight || oldpiece == BlackKnight) \r
631                 SayString("The possible squares a Knight could move to are", FALSE);\r
632         else\r
633                 SayString("The squares a Knight could possibly attack from are", FALSE);\r
634 \r
635         if (fromY+2 < BOARD_HEIGHT && fromX-1 >= BOARD_LEFT) {\r
636                 currentpiece = boards[currentMove][fromY+2][fromX-1];\r
637                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
638                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
639                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
640                 {\r
641                         piece = PieceToName(currentpiece,1);\r
642                         xchar = SquareToChar(fromX-1);\r
643                         ynum = SquareToNum(fromY+2);\r
644                         SayString(xchar , FALSE);\r
645                         SayString(ynum, FALSE);\r
646                         SayString(piece, FALSE);\r
647                 }\r
648         }\r
649 \r
650         if (fromY+2 < BOARD_HEIGHT && fromX+1 < BOARD_RGHT) {\r
651                 currentpiece = boards[currentMove][fromY+2][fromX+1];\r
652                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
653                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
654                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
655                 {\r
656                         piece = PieceToName(currentpiece,1);\r
657                         xchar = SquareToChar(fromX+1);\r
658                         ynum = SquareToNum(fromY+2);\r
659                         SayString(xchar , FALSE);\r
660                         SayString(ynum, FALSE);\r
661                         SayString(piece, FALSE);\r
662                 }\r
663         }\r
664         \r
665         if (fromY+1 < BOARD_HEIGHT && fromX+2 < BOARD_RGHT) {\r
666                 currentpiece = boards[currentMove][fromY+1][fromX+2];\r
667                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
668                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
669                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
670                 {\r
671                         piece = PieceToName(currentpiece,1);\r
672                         xchar = SquareToChar(fromX+2);\r
673                         ynum = SquareToNum(fromY+1);\r
674                         SayString(xchar , FALSE);\r
675                         SayString(ynum, FALSE);\r
676                         SayString(piece, FALSE);\r
677                 }\r
678         }\r
679         \r
680         if (fromY-1 >= 0 && fromX+2 < BOARD_RGHT) {\r
681                 currentpiece = boards[currentMove][fromY-1][fromX+2];\r
682                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
683                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
684                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
685                 {\r
686                         piece = PieceToName(currentpiece,1);\r
687                         xchar = SquareToChar(fromX+2);\r
688                         ynum = SquareToNum(fromY-1);\r
689                         SayString(xchar , FALSE);\r
690                         SayString(ynum, FALSE);\r
691                         SayString(piece, FALSE);\r
692                 }\r
693         }\r
694         \r
695         if (fromY-2 >= 0 && fromX+1 < BOARD_RGHT) {\r
696                 currentpiece = boards[currentMove][fromY-2][fromX+1];\r
697                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
698                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
699                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
700                 {\r
701                         piece = PieceToName(currentpiece,1);\r
702                         xchar = SquareToChar(fromX+1);\r
703                         ynum = SquareToNum(fromY-2);\r
704                         SayString(xchar , FALSE);\r
705                         SayString(ynum, FALSE);\r
706                         SayString(piece, FALSE);\r
707                 }\r
708         }\r
709         \r
710         if (fromY-2 >= 0 && fromX-1 >= BOARD_LEFT) {\r
711                 currentpiece = boards[currentMove][fromY-2][fromX-1];\r
712                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
713                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
714                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
715                 {\r
716                         piece = PieceToName(currentpiece,1);\r
717                         xchar = SquareToChar(fromX-1);\r
718                         ynum = SquareToNum(fromY-2);\r
719                         SayString(xchar , FALSE);\r
720                         SayString(ynum, FALSE);\r
721                         SayString(piece, FALSE);\r
722                 }\r
723         }\r
724         \r
725         if (fromY-1 >= 0 && fromX-2 >= BOARD_LEFT) {\r
726                 currentpiece = boards[currentMove][fromY-1][fromX-2];\r
727                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
728                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
729                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
730                 {\r
731                         piece = PieceToName(currentpiece,1);\r
732                         xchar = SquareToChar(fromX-2);\r
733                         ynum = SquareToNum(fromY-1);\r
734                         SayString(xchar , FALSE);\r
735                         SayString(ynum, FALSE);\r
736                         SayString(piece, FALSE);\r
737                 }\r
738         }\r
739         \r
740         if (fromY+1 < BOARD_HEIGHT && fromX-2 >= BOARD_LEFT) {\r
741                 currentpiece = boards[currentMove][fromY+1][fromX-2];\r
742                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
743                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
744                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
745                 {\r
746                         piece = PieceToName(currentpiece,1);\r
747                         xchar = SquareToChar(fromX-2);\r
748                         ynum = SquareToNum(fromY+1);\r
749                         SayString(xchar , FALSE);\r
750                         SayString(ynum, FALSE);\r
751                         SayString(piece, FALSE);\r
752                 }\r
753         }\r
754 }\r
755 \r
756 VOID\r
757 SayPieces(ChessSquare p)\r
758 {\r
759         ChessSquare currentpiece;  \r
760         char *piece, *xchar, *ynum ;\r
761         int yPos, xPos, count = 0;\r
762         char buf[50];\r
763 \r
764         if(p == WhitePlay)   SayString("White pieces", FALSE); else\r
765         if(p == BlackPlay)   SayString("Black pieces", FALSE); else\r
766         if(p == EmptySquare) SayString("Pieces", FALSE); else {\r
767                 sprintf(buf, "%ss", PieceToName(p,1));\r
768                 SayString(buf, FALSE);\r
769         }\r
770         SayString("are located", FALSE);\r
771         for(yPos=0; yPos<BOARD_HEIGHT; yPos++) {\r
772                 for(xPos=BOARD_LEFT; xPos<BOARD_RGHT; xPos++) {\r
773                         currentpiece = boards[currentMove][yPos][xPos]; \r
774                         if(p == BlackPlay && currentpiece >= BlackPawn && currentpiece <= BlackKing ||\r
775                            p == WhitePlay && currentpiece >= WhitePawn && currentpiece <= WhiteKing   )\r
776                                 piece = PieceToName(currentpiece,0);\r
777                         else if(p == EmptySquare && currentpiece != EmptySquare)\r
778                                 piece = PieceToName(currentpiece,1);\r
779                         else if(p == currentpiece)\r
780                                 piece = NULL;\r
781                         else continue;\r
782                                 \r
783                                 if(count == 0) SayString("at", FALSE);\r
784                                 xchar = SquareToChar(xPos);\r
785                                 ynum = SquareToNum(yPos);\r
786                                 SayString(xchar , FALSE);\r
787                                 SayString(ynum, FALSE);\r
788                                 if(piece) SayString(piece, FALSE);\r
789                                 count++;\r
790                 }\r
791         }\r
792         if(count == 0) SayString("nowhere", FALSE);\r
793 }\r
794 \r
795 VOID\r
796 SayCurrentPos()\r
797 {\r
798         ChessSquare currentpiece;\r
799         char *piece, *xchar, *ynum ;\r
800         if(fromX <  BOARD_LEFT) { SayString("You strayed into the white holdings", FALSE); return; }\r
801         if(fromX >= BOARD_RGHT) { SayString("You strayed into the black holdings", FALSE); return; }\r
802         currentpiece = boards[currentMove][fromY][fromX];       \r
803         piece = PieceToName(currentpiece,1);\r
804         ynum = SquareToNum(fromY);\r
805         xchar = SquareToChar(fromX);\r
806         SayString("Your current position is", FALSE);\r
807         SayString(xchar, FALSE);\r
808         SayString(ynum, FALSE);\r
809         SayString(piece, FALSE);\r
810         if(((fromX-BOARD_LEFT) ^ fromY)&1)\r
811                 SayString("on a light square",FALSE);\r
812         else \r
813                 SayString("on a dark square",FALSE);\r
814 \r
815         PossibleAttacked();\r
816         return;\r
817 }\r
818 \r
819 VOID\r
820 SayAllBoard()\r
821 {\r
822         int Xpos, Ypos;\r
823         ChessSquare currentpiece;\r
824         char *piece, *ynum ;\r
825         \r
826         if(gameInfo.holdingsWidth) {\r
827                 int first = 0;\r
828                 for(Ypos=0; Ypos<gameInfo.holdingsSize; Ypos++) {\r
829                         int n = boards[currentMove][Ypos][BOARD_WIDTH-2];\r
830                         if(n) {  char buf[MSG_SIZ];\r
831                                 if(!first++) SayString("white holds", FALSE);\r
832                                 currentpiece = boards[currentMove][Ypos][BOARD_WIDTH-1];        \r
833                                 piece = PieceToName(currentpiece,0);\r
834                                 sprintf(buf, "%d %s%s", n, piece, (n==1 ? "" : "s") );\r
835                                 SayString(buf, FALSE);\r
836                         }\r
837                 }\r
838                 first = 0;\r
839                 for(Ypos=BOARD_HEIGHT-1; Ypos>=BOARD_HEIGHT - gameInfo.holdingsSize; Ypos--) {\r
840                         int n = boards[currentMove][Ypos][1];\r
841                         if(n) {  char buf[MSG_SIZ];\r
842                                 if(!first++) SayString("black holds", FALSE);\r
843                                 currentpiece = boards[currentMove][Ypos][0];    \r
844                                 piece = PieceToName(currentpiece,0);\r
845                                 sprintf(buf, "%d %s%s", n, piece, (n==1 ? "" : "s") );\r
846                                 SayString(buf, FALSE);\r
847                         }\r
848                 }\r
849         }\r
850 \r
851         for(Ypos=BOARD_HEIGHT-1; Ypos>=0; Ypos--) {\r
852                 ynum = ordinals[Ypos + (gameInfo.boardHeight < 10)];\r
853                 SayString(ynum, FALSE);\r
854                 SayString("rank", FALSE);\r
855                 for(Xpos=BOARD_LEFT; Xpos<BOARD_RGHT; Xpos++) {\r
856                         currentpiece = boards[currentMove][Ypos][Xpos]; \r
857                         if(currentpiece != EmptySquare) {\r
858                                 int count = 0;\r
859                                 char buf[50];\r
860                                 piece = PieceToName(currentpiece,1);\r
861                                 while(Xpos < BOARD_RGHT && boards[currentMove][Ypos][Xpos] == currentpiece)\r
862                                         Xpos++, count++;\r
863                                 if(count > 1) { \r
864                                         sprintf(buf, "%d %ss", count, piece);\r
865                                 } else  sprintf(buf, "%s", piece);\r
866                                 Xpos--;\r
867                                 SayString(buf, FALSE);\r
868                         } else {\r
869                                 int count = 0, oldX = Xpos;\r
870                                 while(Xpos < BOARD_RGHT && boards[currentMove][Ypos][Xpos] == EmptySquare)\r
871                                         Xpos++, count++;\r
872                                 if(Xpos == BOARD_RGHT && oldX == BOARD_LEFT)\r
873                                         SayString("all", FALSE);\r
874                                 else{\r
875                                     if(count > 1) { \r
876                                         char buf[10];\r
877                                         sprintf(buf, "%d", count);\r
878                                         SayString(buf, FALSE);\r
879                                     }\r
880                                     Xpos--;\r
881                                 }\r
882                                 SayString("empty", FALSE);\r
883                         }\r
884                 }\r
885         }\r
886         \r
887 }\r
888 \r
889 VOID\r
890 SayWhosTurn()\r
891 {\r
892         if(gameMode == MachinePlaysBlack || gameMode == IcsPlayingBlack) {\r
893                 if(WhiteOnMove(currentMove))\r
894                         SayString("It is your turn", FALSE);\r
895                 else    SayString("It is your opponents turn", FALSE);\r
896         } else if(gameMode == MachinePlaysWhite || gameMode == IcsPlayingWhite) {\r
897                 if(WhiteOnMove(currentMove))\r
898                         SayString("It is your opponents turn", FALSE);\r
899                 else    SayString("It is your turn", FALSE);\r
900         } else {\r
901                 if(WhiteOnMove(currentMove)) \r
902                         SayString("White is on move here", FALSE);\r
903                 else    SayString("Black is on move here", FALSE);\r
904         }\r
905 }\r
906         \r
907 extern char *commentList[];\r
908 \r
909 VOID\r
910 SayMachineMove(int evenIfDuplicate)\r
911 {\r
912         int len, xPos, yPos, moveNr, secondSpace = 0, castle = 0, n;\r
913         ChessSquare currentpiece;\r
914         char *piece, *xchar, *ynum, *p, checkMark = 0;\r
915         char c, buf[MSG_SIZ], comment[MSG_SIZ];\r
916         static char disambiguation[2];\r
917         static int previousMove = 0;\r
918 \r
919         if(appData.debugMode) fprintf(debugFP, "Message = '%s'\n", messageText);\r
920         if(gameMode == BeginningOfGame) return;\r
921         if(messageText[0] == '[') return;\r
922         comment[0]= 0;\r
923             if(isdigit(messageText[0])) { // message is move, possibly with thinking output\r
924                 int dotCount = 0, spaceCount = 0;\r
925                 sscanf(messageText, "%d", &moveNr);\r
926                 len = 0;\r
927                 // [HGM] show: better extraction of move\r
928                 while (messageText[len] != NULLCHAR) {\r
929                     if(messageText[len] == '.' && spaceCount == 0) dotCount++;\r
930                     if(messageText[len] == ' ') { if(++spaceCount == 2) secondSpace = len; }\r
931                     if(messageText[len] == '{') { // we detected a comment\r
932                         if(isalpha(messageText[len+1]) ) sscanf(messageText+len, "{%[^}]}", comment);\r
933                         break;\r
934                     }\r
935                     if(messageText[len] == '[') { // we detected thinking output\r
936                         int depth; float score=0; char c, lastMover = (dotCount == 3 ? 'B' : 'W');\r
937                         if(sscanf(messageText+len+1, "%d]%c%f", &depth, &c, &score) > 1) {\r
938                             if(c == ' ') { // if not explicitly specified, figure out source of thinking output\r
939                                 switch(gameMode) {\r
940                                   case MachinePlaysWhite:\r
941                                   case IcsPlayingWhite:\r
942                                     c = 'W'; break;\r
943                                   case IcsPlayingBlack:\r
944                                   case MachinePlaysBlack:\r
945                                     c = 'B'; \r
946                                   default:\r
947                                     break;\r
948                                 }\r
949                             }\r
950                             if(c != lastMover) return; // line is thinking output of future move, ignore.\r
951                             if(2*moveNr - (dotCount < 2) == previousMove)\r
952                                 return; // do not repeat same move; likely ponder output\r
953                             sprintf(buf, "score %s %d at %d ply", \r
954                                         score > 0 ? "plus" : score < 0 ? "minus" : "",\r
955                                         (int) (fabs(score)*100+0.5),\r
956                                         depth );\r
957                             SayString(buf, FALSE); // move + thinking output describing it; say it.\r
958                         }\r
959                         while(messageText[len-1] == ' ') len--; // position just behind move;\r
960                         break;\r
961                     }\r
962                     if(messageText[len] == '(') { // ICS time printed behind move\r
963                         while(messageText[len+1] && messageText[len] != ')') len++; // skip it\r
964                     }\r
965                     len++;\r
966                 }\r
967                 if(secondSpace) len = secondSpace; // position behind move\r
968                 if(messageText[len-1] == '+' || messageText[len-1] == '#') {  /* you are in checkmate */\r
969                         len--; // strip off check or mate indicator\r
970                       checkMark = messageText[len]; // make sure still seen after we stip off promo piece\r
971                 }\r
972                 if(messageText[len-2] == '=') {  /* promotion */\r
973                         len-=2; // strip off promotion piece\r
974                         SayString("promotion", FALSE);\r
975                 }\r
976 \r
977                 n = 2*moveNr - (dotCount < 2);\r
978 \r
979                 if(previousMove != 2*moveNr + (dotCount > 1) || evenIfDuplicate) { \r
980                     char number[20];\r
981                     previousMove = 2*moveNr + (dotCount > 1); // remember move nr of move last spoken\r
982                     sprintf(number, "%d", moveNr);\r
983 \r
984                     yPos = CoordToNum(messageText[len-1]);  /* turn char coords to ints */\r
985                     xPos = CoordToNum(messageText[len-2]);\r
986                     if(xPos < 0 || xPos > 11) return; // prevent crashes if no coord string available to speak\r
987                     if(yPos < 0 || yPos > 9)  return;\r
988                     currentpiece = boards[n][yPos][xPos];       \r
989                     piece = PieceToName(currentpiece,0);\r
990                     ynum = SquareToNum(yPos);\r
991                     xchar = SquareToChar(xPos);\r
992                     c = messageText[len-3];\r
993                     if(c == 'x') c = messageText[len-4];\r
994                     if(!isdigit(c) && c < 'a' && c != '@') c = 0;\r
995                     disambiguation[0] = c;\r
996                     SayString(WhiteOnMove(n) ? "Black" : "White", FALSE);\r
997                     SayString("move", FALSE);\r
998                     SayString(number, FALSE);\r
999 //                  if(c==0 || c=='@') SayString("a", FALSE);\r
1000                     // intercept castling moves\r
1001                     p = StrStr(messageText, "O-O-O");\r
1002                     if(p && p-messageText < len) {\r
1003                         SayString("queen side castling",FALSE);\r
1004                         castle = 1;\r
1005                     } else {\r
1006                         p = StrStr(messageText, "O-O");\r
1007                         if(p && p-messageText < len) {\r
1008                             SayString("king side castling",FALSE);\r
1009                             castle = 1;\r
1010                         }\r
1011                     }\r
1012                     if(!castle) {\r
1013                         SayString(piece, FALSE);\r
1014                         if(c == '@') SayString("dropped on", FALSE); else\r
1015                         if(c) SayString(disambiguation, FALSE);\r
1016                         SayString("to", FALSE);\r
1017                         SayString(xchar, FALSE);\r
1018                         SayString(ynum, FALSE);\r
1019                         if(messageText[len-3] == 'x') {\r
1020                                 currentpiece = boards[n-1][yPos][xPos];\r
1021                                 if(currentpiece != EmptySquare) {\r
1022                                         piece = PieceToName(currentpiece,0);\r
1023                                         SayString("Capturing a",FALSE);\r
1024                                         SayString(piece, FALSE);\r
1025                                 } else SayString("Capturing onn passann",FALSE);\r
1026                         }\r
1027                     }\r
1028                     if(checkMark == '+') SayString("check", FALSE); else\r
1029                     if(checkMark == '#') {\r
1030                                 SayString("finishing off", FALSE);\r
1031                                 SayString(WhiteOnMove(n) ? "White" : "Black", FALSE);\r
1032                     }\r
1033                 }\r
1034 \r
1035                 /* say comment after move, possibly with result */\r
1036                 p = NULL;\r
1037                 if(StrStr(messageText, " 1-0")) p = "white wins"; else\r
1038                 if(StrStr(messageText, " 0-1")) p = "black wins"; else\r
1039                 if(StrStr(messageText, " 1/2-1/2")) p = "game ends in a draw";\r
1040                 if(comment[0]) {\r
1041                     if(p) {\r
1042                         if(!StrCaseStr(comment, "draw") && \r
1043                            !StrCaseStr(comment, "white") && \r
1044                            !StrCaseStr(comment, "black") ) {\r
1045                                 SayString(p, FALSE);\r
1046                                 SayString("due to", FALSE);\r
1047                         }\r
1048                     }\r
1049                     SayString(comment, FALSE); // alphabetic comment (usually game end)\r
1050                 } else if(p) SayString(p, FALSE);\r
1051 \r
1052                 if(commentDialog && commentList[currentMove]) SetFocus(commentDialog);\r
1053 \r
1054             } else {\r
1055                 /* starts not with digit */\r
1056                 if(StrCaseStr(messageText, "illegal")) PlayIcsUnfinishedSound();\r
1057                 SayString(messageText, FALSE);\r
1058             }\r
1059 \r
1060 }\r
1061 \r
1062 VOID\r
1063 SayClockTime()\r
1064 {\r
1065         char buf1[50], buf2[50];\r
1066         char *str1, *str2;\r
1067         static long int lastWhiteTime, lastBlackTime;\r
1068 \r
1069         suppressClocks = 1; // if user is using alt+T command, no reason to display them\r
1070         if(abs(lastWhiteTime - whiteTimeRemaining) < 1000 && abs(lastBlackTime - blackTimeRemaining) < 1000)\r
1071                 suppressClocks = 0; // back on after two requests in rapid succession\r
1072         sprintf(buf1, "%s", TimeString(whiteTimeRemaining));\r
1073         str1 = buf1;\r
1074         SayString("White's remaining time is", FALSE);\r
1075         SayString(str1, FALSE);\r
1076         sprintf(buf2, "%s", TimeString(blackTimeRemaining));\r
1077         str2 = buf2;\r
1078         SayString("Black's remaining time is", FALSE);\r
1079         SayString(str2, FALSE);\r
1080         lastWhiteTime = whiteTimeRemaining;\r
1081         lastBlackTime = blackTimeRemaining;\r
1082 }\r
1083 \r
1084 VOID\r
1085 Toggle(Boolean *b, char *mess)\r
1086 {\r
1087         *b = !*b;\r
1088         SayString(mess, FALSE);\r
1089         SayString("is now", FALSE);\r
1090         SayString(*b ? "on" : "off", FALSE);\r
1091 }\r
1092 \r
1093 /* handles keyboard moves in a click-click fashion */\r
1094 VOID\r
1095 KeyboardMove(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
1096 {\r
1097         ChessSquare currentpiece;\r
1098         char *piece;\r
1099         \r
1100         static BOOLEAN sameAgain = FALSE;\r
1101         switch (message) {\r
1102         case WM_KEYDOWN:\r
1103                 sameAgain = FALSE;\r
1104                 if(oldFromX == fromX && oldFromY == fromY) {\r
1105                         sameAgain = TRUE;\r
1106                         /* click on same square */\r
1107                         break;\r
1108                 }\r
1109                 else if(oldFromX != -1) {\r
1110                         \r
1111                         ChessSquare pdown, pup;\r
1112       pdown = boards[currentMove][oldFromY][oldFromX];\r
1113       pup = boards[currentMove][fromY][fromX];\r
1114                 \r
1115                 if (gameMode == EditPosition ||\r
1116                         !((WhitePawn <= pdown && pdown <= WhiteKing &&\r
1117                                  WhitePawn <= pup && pup <= WhiteKing) ||\r
1118                                 (BlackPawn <= pdown && pdown <= BlackKing &&\r
1119                                  BlackPawn <= pup && pup <= BlackKing))) {\r
1120                         /* EditPosition, empty square, or different color piece;\r
1121                         click-click move is possible */         \r
1122                         char promoChoice = NULLCHAR;\r
1123                 \r
1124                         if (HasPromotionChoice(oldFromX, oldFromY, fromX, fromY, &promoChoice)) {\r
1125                                 if (appData.alwaysPromoteToQueen) {\r
1126                                         UserMoveEvent(oldFromX, oldFromY, fromX, fromY, 'q');\r
1127                                 }\r
1128                                 else {\r
1129                                         toX = fromX; toY = fromY; fromX = oldFromX; fromY = oldFromY;\r
1130                                         PromotionPopup(hwnd);\r
1131                                         fromX = toX; fromY = toY;\r
1132                                 }       \r
1133                         }\r
1134                         else {\r
1135                                 UserMoveEvent(oldFromX, oldFromY, fromX, fromY, promoChoice);\r
1136                         }\r
1137                 oldFromX = oldFromY = -1;\r
1138                 break;\r
1139                 }\r
1140                 \r
1141                 }\r
1142                 /* First downclick, or restart on a square with same color piece */\r
1143                 if (OKToStartUserMove(fromX, fromY)) {\r
1144                 oldFromX = fromX;\r
1145                 oldFromY = fromY;\r
1146                 currentpiece = boards[currentMove][fromY][fromX];       \r
1147                 piece = PieceToName(currentpiece,1);\r
1148                 SayString(piece, FALSE);\r
1149                 SayString("selected", FALSE);\r
1150                 }\r
1151                 else {\r
1152                 oldFromX = oldFromY = -1;\r
1153                 }\r
1154                 break;\r
1155 \r
1156         case WM_KEYUP:\r
1157                 if (oldFromX == fromX && oldFromY == fromY) {\r
1158       /* Upclick on same square */\r
1159       if (sameAgain) {\r
1160         /* Clicked same square twice: abort click-click move */\r
1161                         oldFromX = oldFromY = -1;\r
1162                         currentpiece = boards[currentMove][fromY][fromX];       \r
1163                         piece = PieceToName(currentpiece,0);\r
1164                         SayString(piece, FALSE);\r
1165                         SayString("unselected", FALSE);\r
1166                         }\r
1167                 }\r
1168         }\r
1169 }\r
1170 \r
1171 int\r
1172 NiceTime(int x)\r
1173 {       // return TRUE for times we want to announce\r
1174         if(x<0) return 0;\r
1175         x = (x+50)/100;   // tenth of seconds\r
1176         if(x <= 100) return (x%10 == 0);\r
1177         if(x <= 600) return (x%100 == 0);\r
1178         if(x <= 6000) return (x%600 == 0);\r
1179         return (x%3000 == 0);\r
1180 }\r
1181 \r
1182 #define JAWS_ARGS \\r
1183   { "beepOffBoard", ArgInt, (LPVOID) beeps, TRUE, (ArgIniType) 1 },\\r
1184   { "beepEmpty", ArgInt, (LPVOID) (beeps+1), TRUE, (ArgIniType) 0 },\\r
1185   { "beepWhite", ArgInt, (LPVOID) (beeps+2), TRUE, (ArgIniType) 0 },\\r
1186   { "beepBlack", ArgInt, (LPVOID) (beeps+3), TRUE, (ArgIniType) 0 },\\r
1187   { "beepHoldings", ArgInt, (LPVOID) (beeps+4), TRUE, (ArgIniType) 0 },\\r
1188 \r
1189 #define JAWS_ALT_INTERCEPT \\r
1190             if(suppressOneKey) {\\r
1191                 suppressOneKey = 0;\\r
1192                 if(GetKeyState(VK_MENU) < 0 && GetKeyState(VK_CONTROL) < 0) break;\\r
1193             }\\r
1194             if ((char)wParam == 022 && gameMode == EditPosition) { /* <Ctl R>. Pop up piece menu */\\r
1195                 POINT pt; int x, y;\\r
1196                 SquareToPos(fromY, fromX, &x, &y);\\r
1197                 pt.x = x; pt.y = y;\\r
1198                 if(gameInfo.variant != VariantShogi)\\r
1199                     MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\\r
1200                 else\\r
1201                     MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\\r
1202                 break;\\r
1203             }\\r
1204 \r
1205 #define JAWS_REPLAY \\r
1206     case '\020': /* ctrl P */\\r
1207       { char buf[MSG_SIZ];\\r
1208         if(GetWindowText(hwnd, buf, MSG_SIZ-1))\\r
1209                 SayString(buf, FALSE);\\r
1210       }\\r
1211       return 0;\\r
1212 \r
1213 #define JAWS_KB_NAVIGATION \\r
1214 \\r
1215         case WM_KEYDOWN:\\r
1216 \\r
1217                 if(GetKeyState(VK_MENU) < 0 && GetKeyState(VK_CONTROL) < 0) {\\r
1218                     /* Control + Alt + letter used for speaking piece positions */\\r
1219                     static int lastTime; static char lastChar;\\r
1220                     int mine = 0, time = GetTickCount(); char c;\\r
1221 \\r
1222                     if((char)wParam == lastChar && time-lastTime < 250) mine = 1;\\r
1223                     lastChar = wParam; lastTime = time;\\r
1224                     c = wParam;\\r
1225 \\r
1226                     if(gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) mine = !mine;\\r
1227 \\r
1228                     if(ToLower(c) == 'x') {\\r
1229                         SayPieces(mine ? WhitePlay : BlackPlay);\\r
1230                         suppressOneKey = 1;\\r
1231                         break;\\r
1232                     } else\\r
1233                     if(CharToPiece(c) != EmptySquare) {\\r
1234                         SayPieces(CharToPiece(mine ? ToUpper(c) : ToLower(c)));\\r
1235                         suppressOneKey = 1;\\r
1236                         break;\\r
1237                     }\\r
1238                 }\\r
1239 \\r
1240                 switch (wParam) {\\r
1241                 case VK_LEFT:\\r
1242                 case VK_RIGHT:\\r
1243                 case VK_UP:\\r
1244                 case VK_DOWN:\\r
1245                         KeyboardEvent(hwnd, message, wParam, lParam);\\r
1246                         break;\\r
1247                 case VK_SPACE:\\r
1248                         KeyboardMove(hwnd, message, wParam, lParam);\\r
1249                         break;\\r
1250                 }\\r
1251                 break;\\r
1252         case WM_KEYUP:\\r
1253                 switch (wParam) {\\r
1254                 case VK_SPACE:\\r
1255                         KeyboardMove(hwnd, message, wParam, lParam);\\r
1256                         break;\\r
1257                 }\\r
1258                 break;\\r
1259 \r
1260 #define JAWS_MENU_ITEMS \\r
1261                 case IDM_PossibleAttackMove:  /*What can I possible attack from here */\\r
1262                         PossibleAttackMove();\\r
1263                         break;\\r
1264 \\r
1265                 case IDM_PossibleAttacked:  /*what can possible attack this square*/\\r
1266                         PossibleAttacked();\\r
1267                         break;\\r
1268 \\r
1269                 case IDM_ReadRow:   /* Read the current row of pieces */\\r
1270                         ReadRow();\\r
1271                         break;\\r
1272 \\r
1273                 case IDM_ReadColumn:   /* Read the current column of pieces */\\r
1274                         ReadColumn();\\r
1275                         break;\\r
1276 \\r
1277                 case IDM_SayCurrentPos: /* Say current position including color */\\r
1278                         SayCurrentPos();\\r
1279                         break;\\r
1280 \\r
1281                 case IDM_SayAllBoard:  /* Say the whole board from bottom to top */\\r
1282                         SayAllBoard();\\r
1283                         break;\\r
1284 \\r
1285                 case IDM_SayMachineMove:  /* Say the last move made */\\r
1286                         timeflag = 1;\\r
1287                         SayMachineMove(1);\\r
1288                         break;\\r
1289 \\r
1290                 case IDM_SayUpperDiagnols:  /* Says the diagnol positions above you */\\r
1291                         SayUpperDiagnols();\\r
1292                         break;\\r
1293 \\r
1294                 case IDM_SayLowerDiagnols:  /* Say the diagnol positions below you */\\r
1295                         SayLowerDiagnols();\\r
1296                         break;\\r
1297 \\r
1298                 case IDM_SayBlackPieces: /*Say the opponents pieces */\\r
1299                         SayPieces(BlackPlay);\\r
1300                         break;\\r
1301 \\r
1302                 case IDM_SayWhitePieces: /*Say the opponents pieces */\\r
1303                         SayPieces(WhitePlay);\\r
1304                         break;\\r
1305 \\r
1306                 case IDM_SayClockTime:  /*Say the clock time */\\r
1307                         SayClockTime();\\r
1308                         break;\\r
1309 \\r
1310                 case IDM_SayWhosTurn:   /* Say whos turn it its */\\r
1311                         SayWhosTurn();\\r
1312                         break;\\r
1313 \\r
1314                 case IDM_SayKnightMoves:  /* Say Knights (L-shaped) move */\\r
1315                         SayKnightMoves();\\r
1316                         break;\\r
1317 \\r
1318                 case OPT_PonderNextMove:  /* Toggle option setting */\\r
1319                         Toggle(&appData.ponderNextMove, "ponder");\\r
1320                         break;\\r
1321 \\r
1322                 case OPT_AnimateMoving:  /* Toggle option setting */\\r
1323                         Toggle(&appData.animate, "animate moving");\\r
1324                         break;\\r
1325 \\r
1326                 case OPT_AutoFlag:  /* Toggle option setting */\\r
1327                         Toggle(&appData.autoCallFlag, "auto flag");\\r
1328                         break;\\r
1329 \\r
1330                 case OPT_AlwaysQueen:  /* Toggle option setting */\\r
1331                         Toggle(&appData.alwaysPromoteToQueen, "always promote to queen");\\r
1332                         break;\\r
1333 \\r
1334                 case OPT_TestLegality:  /* Toggle option setting */\\r
1335                         Toggle(&appData.testLegality, "legality testing");\\r
1336                         break;\\r
1337 \\r
1338                 case OPT_HideThinkFromHuman:  /* Toggle option setting */\\r
1339                         Toggle(&appData.hideThinkingFromHuman, "hide thinking");\\r
1340                         ShowThinkingEvent();\\r
1341                         break;\\r
1342 \\r
1343                 case OPT_SaveExtPGN:  /* Toggle option setting */\\r
1344                         Toggle(&appData.saveExtendedInfoInPGN, "extended P G N info");\\r
1345                         break;\\r
1346 \\r
1347                 case OPT_ExtraInfoInMoveHistory:  /* Toggle option setting */\\r
1348                         Toggle(&appData.showEvalInMoveHistory, "extra info in move histoty");\\r
1349                         break;\\r
1350 \\r
1351 \r
1352 \r
1353 #define JAWS_ACCEL \\r
1354         !(!frozen && TranslateAccelerator(hwndMain, hAccelJAWS, &msg)) &&\r
1355 \r
1356 #define JAWS_INIT if (!InitJAWS()) return (FALSE);\r
1357 \r
1358 #define JAWS_DELETE(X)\r
1359 \r
1360 #define JAWS_SILENCE if(suppressClocks) return;\r
1361 \r
1362 #define JAWS_COPYRIGHT \\r
1363         SetDlgItemText(hDlg, OPT_MESS, "Auditory/Keyboard Enhancements  By:  Ed Rodriguez (sort of)");\r
1364 \r
1365 #define SAY(S) SayString((S), FALSE)\r
1366 \r
1367 #define SAYMACHINEMOVE() SayMachineMove(0)\r
1368 \r
1369 // After inclusion of this file somewhere early in winboard.c, the remaining part of the patch\r
1370 // is scattered over winboard.c for actually calling the routines.\r