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