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