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