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