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