95f70d244e646ae66338897184d15a537f2e361c
[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 \r
923 VOID\r
924 SayMachineMove(int evenIfDuplicate)\r
925 {\r
926         int len, xPos, yPos, moveNr, secondSpace = 0, castle = 0, n;\r
927         ChessSquare currentpiece;\r
928         char *piece, *xchar, *ynum, *p;\r
929         char c, buf[MSG_SIZ], comment[MSG_SIZ];\r
930         static char disambiguation[2];\r
931         static int previousMove = 0;\r
932 \r
933         if(appData.debugMode) fprintf(debugFP, "Message = '%s'\n", messageText);\r
934         if(gameMode == BeginningOfGame) return;\r
935         if(messageText[0] == '[') return;\r
936         comment[0]= 0;\r
937             if(isdigit(messageText[0])) { // message is move, possibly with thinking output\r
938                 int dotCount = 0, spaceCount = 0;\r
939                 sscanf(messageText, "%d", &moveNr);\r
940                 len = 0;\r
941                 // [HGM] show: better extraction of move\r
942                 while (messageText[len] != NULLCHAR) {\r
943                     if(messageText[len] == '.' && spaceCount == 0) dotCount++;\r
944                     if(messageText[len] == ' ') { if(++spaceCount == 2) secondSpace = len; }\r
945                     if(messageText[len] == '{') { // we detected a comment\r
946                         if(isalpha(messageText[len+1]) ) sscanf(messageText+len, "{%[^}]}", comment);\r
947                         break;\r
948                     }\r
949                     if(messageText[len] == '[') { // we detected thinking output\r
950                         int depth; float score=0; char c, lastMover = (dotCount == 3 ? 'B' : 'W');\r
951                         if(sscanf(messageText+len+1, "%d]%c%f", &depth, &c, &score) > 1) {\r
952                             if(c == ' ') { // if not explicitly specified, figure out source of thinking output\r
953                                 switch(gameMode) {\r
954                                   case MachinePlaysWhite:\r
955                                   case IcsPlayingWhite:\r
956                                     c = 'W'; break;\r
957                                   case IcsPlayingBlack:\r
958                                   case MachinePlaysBlack:\r
959                                     c = 'B'; \r
960                                   default:\r
961                                     break;\r
962                                 }\r
963                             }\r
964                             if(c != lastMover) return; // line is thinking output of future move, ignore.\r
965                             if(2*moveNr - (dotCount < 2) == previousMove)\r
966                                 return; // do not repeat same move; likely ponder output\r
967                             sprintf(buf, "score %s %d at %d ply", \r
968                                         score > 0 ? "plus" : score < 0 ? "minus" : "",\r
969                                         (int) (fabs(score)*100+0.5),\r
970                                         depth );\r
971                             SayString(buf, FALSE); // move + thinking output describing it; say it.\r
972                         }\r
973                         while(messageText[len-1] == ' ') len--; // position just behind move;\r
974                         break;\r
975                     }\r
976                     if(messageText[len] == '(') { // ICS time printed behind move\r
977                         while(messageText[len+1] && messageText[len] != ')') len++; // skip it\r
978                     }\r
979                     len++;\r
980                 }\r
981                 if(secondSpace) len = secondSpace; // position behind move\r
982                 if(messageText[len-1] == '+' || messageText[len-1] == '#') {  /* you are in checkmate */\r
983                         len--; // strip off check or mate indicator\r
984                 }\r
985                 if(messageText[len-2] == '=') {  /* promotion */\r
986                         len-=2; // strip off promotion piece\r
987                         SayString("promotion", FALSE);\r
988                 }\r
989 \r
990                 n = 2*moveNr - (dotCount < 2);\r
991 \r
992                 if(previousMove != 2*moveNr + (dotCount > 1) || evenIfDuplicate) { \r
993                     char number[20];\r
994                     previousMove = 2*moveNr + (dotCount > 1); // remember move nr of move last spoken\r
995                     sprintf(number, "%d", moveNr);\r
996 \r
997                     yPos = CoordToNum(messageText[len-1]);  /* turn char coords to ints */\r
998                     xPos = CoordToNum(messageText[len-2]);\r
999                     if(xPos < 0 || xPos > 11) return; // prevent crashes if no coord string available to speak\r
1000                     if(yPos < 0 || yPos > 9)  return;\r
1001                     currentpiece = boards[n][yPos][xPos];       \r
1002                     piece = PieceToName(currentpiece,0);\r
1003                     ynum = SquareToNum(yPos);\r
1004                     xchar = SquareToChar(xPos);\r
1005                     c = messageText[len-3];\r
1006                     if(c == 'x') c = messageText[len-4];\r
1007                     if(!isdigit(c) && c < 'a' && c != '@') c = 0;\r
1008                     disambiguation[0] = c;\r
1009                     SayString(WhiteOnMove(n) ? "Black" : "White", FALSE);\r
1010                     SayString("move", FALSE);\r
1011                     SayString(number, FALSE);\r
1012 //                  if(c==0 || c=='@') SayString("a", FALSE);\r
1013                     // intercept castling moves\r
1014                     p = StrStr(messageText, "O-O-O");\r
1015                     if(p && p-messageText < len) {\r
1016                         SayString("queen side castling",FALSE);\r
1017                         castle = 1;\r
1018                     } else {\r
1019                         p = StrStr(messageText, "O-O");\r
1020                         if(p && p-messageText < len) {\r
1021                             SayString("king side castling",FALSE);\r
1022                             castle = 1;\r
1023                         }\r
1024                     }\r
1025                     if(!castle) {\r
1026                         SayString(piece, FALSE);\r
1027                         if(c == '@') SayString("dropped on", FALSE); else\r
1028                         if(c) SayString(disambiguation, FALSE);\r
1029                         SayString("to", FALSE);\r
1030                         SayString(xchar, FALSE);\r
1031                         SayString(ynum, FALSE);\r
1032                         if(messageText[len-3] == 'x') {\r
1033                                 currentpiece = boards[n-1][yPos][xPos];\r
1034                                 if(currentpiece != EmptySquare) {\r
1035                                         piece = PieceToName(currentpiece,0);\r
1036                                         SayString("Capturing a",FALSE);\r
1037                                         SayString(piece, FALSE);\r
1038                                 } else SayString("Capturing onn passann",FALSE);\r
1039                         }\r
1040                         if(messageText[len] == '+') SayString("check", FALSE); else\r
1041                         if(messageText[len] == '#') {\r
1042                                 SayString("finishing off", FALSE);\r
1043                                 SayString(WhiteOnMove(n) ? "White" : "Black", FALSE);\r
1044                         }\r
1045                     }\r
1046                 }\r
1047 \r
1048                 /* say comment after move, possibly with result */\r
1049                 p = NULL;\r
1050                 if(StrStr(messageText, " 1-0")) p = "white wins"; else\r
1051                 if(StrStr(messageText, " 0-1")) p = "black wins"; else\r
1052                 if(StrStr(messageText, " 1/2-1/2")) p = "game ends in a draw";\r
1053                 if(comment[0]) {\r
1054                     if(p) {\r
1055                         if(!StrCaseStr(comment, "draw") && \r
1056                            !StrCaseStr(comment, "white") && \r
1057                            !StrCaseStr(comment, "black") ) {\r
1058                                 SayString(p, FALSE);\r
1059                                 SayString("due to", FALSE);\r
1060                         }\r
1061                     }\r
1062                     SayString(comment, FALSE); // alphabetic comment (usually game end)\r
1063                 } else if(p) SayString(p, FALSE);\r
1064 \r
1065             } else {\r
1066                 /* starts not with digit */\r
1067                 if(StrCaseStr(messageText, "illegal")) PlayIcsUnfinishedSound();\r
1068                 SayString(messageText, FALSE);\r
1069             }\r
1070 \r
1071 }\r
1072 \r
1073 VOID\r
1074 SayClockTime()\r
1075 {\r
1076         char buf1[50], buf2[50];\r
1077         char *str1, *str2;\r
1078         static long int lastWhiteTime, lastBlackTime;\r
1079 \r
1080         suppressClocks = 1; // if user is using alt+T command, no reason to display them\r
1081         if(abs(lastWhiteTime - whiteTimeRemaining) < 1000 && abs(lastBlackTime - blackTimeRemaining) < 1000)\r
1082                 suppressClocks = 0; // back on after two requests in rapid succession\r
1083         sprintf(buf1, "%s", TimeString(whiteTimeRemaining));\r
1084         str1 = buf1;\r
1085         SayString("White's remaining time is", FALSE);\r
1086         SayString(str1, FALSE);\r
1087         sprintf(buf2, "%s", TimeString(blackTimeRemaining));\r
1088         str2 = buf2;\r
1089         SayString("Black's remaining time is", FALSE);\r
1090         SayString(str2, FALSE);\r
1091         lastWhiteTime = whiteTimeRemaining;\r
1092         lastBlackTime = blackTimeRemaining;\r
1093 }\r
1094 \r
1095 VOID\r
1096 Toggle(Boolean *b, char *mess)\r
1097 {\r
1098         *b = !*b;\r
1099         SayString(mess, FALSE);\r
1100         SayString("is now", FALSE);\r
1101         SayString(*b ? "on" : "off", FALSE);\r
1102 }\r
1103 \r
1104 /* handles keyboard moves in a click-click fashion */\r
1105 VOID\r
1106 KeyboardMove(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
1107 {\r
1108         ChessSquare currentpiece;\r
1109         char *piece;\r
1110         \r
1111         static BOOLEAN sameAgain = FALSE;\r
1112         switch (message) {\r
1113         case WM_KEYDOWN:\r
1114                 sameAgain = FALSE;\r
1115                 if(oldFromX == fromX && oldFromY == fromY) {\r
1116                         sameAgain = TRUE;\r
1117                         /* click on same square */\r
1118                         break;\r
1119                 }\r
1120                 else if(oldFromX != -1) {\r
1121                         \r
1122                         ChessSquare pdown, pup;\r
1123       pdown = boards[currentMove][oldFromY][oldFromX];\r
1124       pup = boards[currentMove][fromY][fromX];\r
1125                 \r
1126                 if (gameMode == EditPosition ||\r
1127                         !((WhitePawn <= pdown && pdown <= WhiteKing &&\r
1128                                  WhitePawn <= pup && pup <= WhiteKing) ||\r
1129                                 (BlackPawn <= pdown && pdown <= BlackKing &&\r
1130                                  BlackPawn <= pup && pup <= BlackKing))) {\r
1131                         /* EditPosition, empty square, or different color piece;\r
1132                         click-click move is possible */\r
1133                 \r
1134                         if (IsPromotion(oldFromX, oldFromY, fromX, fromY)) {\r
1135                                 if (appData.alwaysPromoteToQueen) {\r
1136                                         UserMoveEvent(oldFromX, oldFromY, fromX, fromY, 'q');\r
1137                                 }\r
1138                                 else {\r
1139                                         toX = fromX; toY = fromY; fromX = oldFromX; fromY = oldFromY;\r
1140                                         PromotionPopup(hwnd);\r
1141                                         fromX = toX; fromY = toY;\r
1142                                 }       \r
1143                         }\r
1144                         else {\r
1145                                 UserMoveEvent(oldFromX, oldFromY, fromX, fromY, NULLCHAR);\r
1146                         }\r
1147                 oldFromX = oldFromY = -1;\r
1148                 break;\r
1149                 }\r
1150                 \r
1151                 }\r
1152                 /* First downclick, or restart on a square with same color piece */\r
1153                 if (OKToStartUserMove(fromX, fromY)) {\r
1154                 oldFromX = fromX;\r
1155                 oldFromY = fromY;\r
1156                 currentpiece = boards[currentMove][fromY][fromX];       \r
1157                 piece = PieceToName(currentpiece,1);\r
1158                 SayString(piece, FALSE);\r
1159                 SayString("selected", FALSE);\r
1160                 }\r
1161                 else {\r
1162                 oldFromX = oldFromY = -1;\r
1163                 }\r
1164                 break;\r
1165 \r
1166         case WM_KEYUP:\r
1167                 if (oldFromX == fromX && oldFromY == fromY) {\r
1168       /* Upclick on same square */\r
1169       if (sameAgain) {\r
1170         /* Clicked same square twice: abort click-click move */\r
1171                         oldFromX = oldFromY = -1;\r
1172                         currentpiece = boards[currentMove][fromY][fromX];       \r
1173                         piece = PieceToName(currentpiece,0);\r
1174                         SayString(piece, FALSE);\r
1175                         SayString("unselected", FALSE);\r
1176                         }\r
1177                 }\r
1178         }\r
1179 }\r
1180 \r
1181 int\r
1182 NiceTime(int x)\r
1183 {       // return TRUE for times we want to announce\r
1184         if(x<0) return 0;\r
1185         x = (x+50)/100;   // tenth of seconds\r
1186         if(x <= 100) return (x%10 == 0);\r
1187         if(x <= 600) return (x%100 == 0);\r
1188         if(x <= 6000) return (x%600 == 0);\r
1189         return (x%3000 == 0);\r
1190 }\r
1191 \r
1192 #define JAWS_ARGS \\r
1193   { "beepOffBoard", ArgInt, (LPVOID) beeps, TRUE },\\r
1194   { "beepEmpty", ArgInt, (LPVOID) (beeps+1), TRUE },\\r
1195   { "beepWhite", ArgInt, (LPVOID) (beeps+2), TRUE },\\r
1196   { "beepBlack", ArgInt, (LPVOID) (beeps+3), TRUE },\\r
1197   { "beepHoldings", ArgInt, (LPVOID) (beeps+4), TRUE },\\r
1198 \r
1199 #define JAWS_ALT_INTERCEPT \\r
1200             if(suppressOneKey) {\\r
1201                 suppressOneKey = 0;\\r
1202                 if(GetKeyState(VK_MENU) < 0 && GetKeyState(VK_CONTROL) < 0) break;\\r
1203             }\\r
1204             if ((char)wParam == 022 && gameMode == EditPosition) { /* <Ctl R>. Pop up piece menu */\\r
1205                 POINT pt; int x, y;\\r
1206                 SquareToPos(fromY, fromX, &x, &y);\\r
1207                 pt.x = x; pt.y = y;\\r
1208                 if(gameInfo.variant != VariantShogi)\\r
1209                     MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\\r
1210                 else\\r
1211                     MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\\r
1212                 break;\\r
1213             }\\r
1214 \r
1215 #define JAWS_REPLAY \\r
1216     case '\020': /* ctrl P */\\r
1217       { char buf[MSG_SIZ];\\r
1218         if(GetWindowText(hwnd, buf, MSG_SIZ-1))\\r
1219                 SayString(buf, FALSE);\\r
1220       }\\r
1221       return 0;\\r
1222 \r
1223 #define JAWS_KB_NAVIGATION \\r
1224 \\r
1225         case WM_KEYDOWN:\\r
1226 \\r
1227                 if(GetKeyState(VK_MENU) < 0 && GetKeyState(VK_CONTROL) < 0) {\\r
1228                     /* Control + Alt + letter used for speaking piece positions */\\r
1229                     static int lastTime; static char lastChar;\\r
1230                     int mine = 0, time = GetTickCount(); char c;\\r
1231 \\r
1232                     if((char)wParam == lastChar && time-lastTime < 250) mine = 1;\\r
1233                     lastChar = wParam; lastTime = time;\\r
1234                     c = wParam;\\r
1235 \\r
1236                     if(gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) mine = !mine;\\r
1237 \\r
1238                     if(ToLower(c) == 'x') {\\r
1239                         SayPieces(mine ? WhitePlay : BlackPlay);\\r
1240                         suppressOneKey = 1;\\r
1241                         break;\\r
1242                     } else\\r
1243                     if(CharToPiece(c) != EmptySquare) {\\r
1244                         SayPieces(CharToPiece(mine ? ToUpper(c) : ToLower(c)));\\r
1245                         suppressOneKey = 1;\\r
1246                         break;\\r
1247                     }\\r
1248                 }\\r
1249 \\r
1250                 switch (wParam) {\\r
1251                 case VK_LEFT:\\r
1252                 case VK_RIGHT:\\r
1253                 case VK_UP:\\r
1254                 case VK_DOWN:\\r
1255                         KeyboardEvent(hwnd, message, wParam, lParam);\\r
1256                         break;\\r
1257                 case VK_SPACE:\\r
1258                         KeyboardMove(hwnd, message, wParam, lParam);\\r
1259                         break;\\r
1260                 }\\r
1261                 break;\\r
1262         case WM_KEYUP:\\r
1263                 switch (wParam) {\\r
1264                 case VK_SPACE:\\r
1265                         KeyboardMove(hwnd, message, wParam, lParam);\\r
1266                         break;\\r
1267                 }\\r
1268                 break;\\r
1269 \r
1270 #define JAWS_MENU_ITEMS \\r
1271                 case IDM_PossibleAttackMove:  /*What can I possible attack from here */\\r
1272                         PossibleAttackMove();\\r
1273                         break;\\r
1274 \\r
1275                 case IDM_PossibleAttacked:  /*what can possible attack this square*/\\r
1276                         PossibleAttacked();\\r
1277                         break;\\r
1278 \\r
1279                 case IDM_ReadRow:   /* Read the current row of pieces */\\r
1280                         ReadRow();\\r
1281                         break;\\r
1282 \\r
1283                 case IDM_ReadColumn:   /* Read the current column of pieces */\\r
1284                         ReadColumn();\\r
1285                         break;\\r
1286 \\r
1287                 case IDM_SayCurrentPos: /* Say current position including color */\\r
1288                         SayCurrentPos();\\r
1289                         break;\\r
1290 \\r
1291                 case IDM_SayAllBoard:  /* Say the whole board from bottom to top */\\r
1292                         SayAllBoard();\\r
1293                         break;\\r
1294 \\r
1295                 case IDM_SayMachineMove:  /* Say the last move made */\\r
1296                         timeflag = 1;\\r
1297                         SayMachineMove(1);\\r
1298                         break;\\r
1299 \\r
1300                 case IDM_SayUpperDiagnols:  /* Says the diagnol positions above you */\\r
1301                         SayUpperDiagnols();\\r
1302                         break;\\r
1303 \\r
1304                 case IDM_SayLowerDiagnols:  /* Say the diagnol positions below you */\\r
1305                         SayLowerDiagnols();\\r
1306                         break;\\r
1307 \\r
1308                 case IDM_SayBlackPieces: /*Say the opponents pieces */\\r
1309                         SayPieces(BlackPlay);\\r
1310                         break;\\r
1311 \\r
1312                 case IDM_SayWhitePieces: /*Say the opponents pieces */\\r
1313                         SayPieces(WhitePlay);\\r
1314                         break;\\r
1315 \\r
1316                 case IDM_SayClockTime:  /*Say the clock time */\\r
1317                         SayClockTime();\\r
1318                         break;\\r
1319 \\r
1320                 case IDM_SayWhosTurn:   /* Say whos turn it its */\\r
1321                         SayWhosTurn();\\r
1322                         break;\\r
1323 \\r
1324                 case IDM_SayKnightMoves:  /* Say Knights (L-shaped) move */\\r
1325                         SayKnightMoves();\\r
1326                         break;\\r
1327 \\r
1328                 case OPT_PonderNextMove:  /* Toggle option setting */\\r
1329                         Toggle(&appData.ponderNextMove, "ponder");\\r
1330                         break;\\r
1331 \\r
1332                 case OPT_AnimateMoving:  /* Toggle option setting */\\r
1333                         Toggle(&appData.animate, "animate moving");\\r
1334                         break;\\r
1335 \\r
1336                 case OPT_AutoFlag:  /* Toggle option setting */\\r
1337                         Toggle(&appData.autoCallFlag, "auto flag");\\r
1338                         break;\\r
1339 \\r
1340                 case OPT_AlwaysQueen:  /* Toggle option setting */\\r
1341                         Toggle(&appData.alwaysPromoteToQueen, "always promote to queen");\\r
1342                         break;\\r
1343 \\r
1344                 case OPT_TestLegality:  /* Toggle option setting */\\r
1345                         Toggle(&appData.testLegality, "legality testing");\\r
1346                         break;\\r
1347 \\r
1348                 case OPT_HideThinkFromHuman:  /* Toggle option setting */\\r
1349                         Toggle(&appData.hideThinkingFromHuman, "hide thinking");\\r
1350                         ShowThinkingEvent();\\r
1351                         break;\\r
1352 \\r
1353                 case OPT_SaveExtPGN:  /* Toggle option setting */\\r
1354                         Toggle(&appData.saveExtendedInfoInPGN, "extended P G N info");\\r
1355                         break;\\r
1356 \\r
1357                 case OPT_ExtraInfoInMoveHistory:  /* Toggle option setting */\\r
1358                         Toggle(&appData.showEvalInMoveHistory, "extra info in move histoty");\\r
1359                         break;\\r
1360 \\r
1361 \r
1362 \r
1363 #define JAWS_ACCEL \\r
1364         !(!frozen && TranslateAccelerator(hwndMain, hAccelJAWS, &msg)) &&\r
1365 \r
1366 #define JAWS_INIT if (!InitJAWS()) return (FALSE);\r
1367 \r
1368 #define JAWS_DELETE(X)\r
1369 \r
1370 #define JAWS_SILENCE if(suppressClocks) return;\r
1371 \r
1372 #define JAWS_COPYRIGHT \\r
1373         SetDlgItemText(hDlg, OPT_MESS, "Auditory/Keyboard Enhancements  By:  Ed Rodriguez (sort of)");\r
1374 \r
1375 #define SAY(S) SayString((S), FALSE)\r
1376 \r
1377 #define SAYMACHINEMOVE() SayMachineMove(0)\r
1378 \r
1379 // After inclusion of this file somewhere early in winboard.c, the remaining part of the patch\r
1380 // is scattered over winboard.c for actually calling the routines.\r