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