2 * zippy.c -- Implements Zippy the Pinhead chess player on ICS in XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * The following terms apply to Digital Equipment Corporation's copyright
\r
13 * interest in XBoard:
\r
14 * ------------------------------------------------------------------------
\r
15 * All Rights Reserved
\r
17 * Permission to use, copy, modify, and distribute this software and its
\r
18 * documentation for any purpose and without fee is hereby granted,
\r
19 * provided that the above copyright notice appear in all copies and that
\r
20 * both that copyright notice and this permission notice appear in
\r
21 * supporting documentation, and that the name of Digital not be
\r
22 * used in advertising or publicity pertaining to distribution of the
\r
23 * software without specific, written prior permission.
\r
25 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
26 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
27 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
28 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
29 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
30 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
32 * ------------------------------------------------------------------------
\r
34 * The following terms apply to the enhanced version of XBoard
\r
35 * distributed by the Free Software Foundation:
\r
36 * ------------------------------------------------------------------------
\r
38 * GNU XBoard is free software: you can redistribute it and/or modify
\r
39 * it under the terms of the GNU General Public License as published by
\r
40 * the Free Software Foundation, either version 3 of the License, or (at
\r
41 * your option) any later version.
\r
43 * GNU XBoard is distributed in the hope that it will be useful, but
\r
44 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
45 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
46 * General Public License for more details.
\r
48 * You should have received a copy of the GNU General Public License
\r
49 * along with this program. If not, see http://www.gnu.org/licenses/.
\r
51 *------------------------------------------------------------------------
\r
52 ** See the file ChangeLog for a revision history. */
\r
58 #include <sys/types.h>
\r
59 #include <sys/stat.h>
\r
63 # include <stdlib.h>
\r
64 # include <string.h>
\r
65 #else /* not STDC_HEADERS */
\r
66 extern char *getenv();
\r
68 # include <string.h>
\r
69 # else /* not HAVE_STRING_H */
\r
70 # include <strings.h>
\r
71 # endif /* not HAVE_STRING_H */
\r
72 #endif /* not STDC_HEADERS */
\r
74 #if TIME_WITH_SYS_TIME
\r
75 # include <sys/time.h>
\r
78 # if HAVE_SYS_TIME_H
\r
79 # include <sys/time.h>
\r
87 # include <unistd.h>
\r
92 #include "frontend.h"
\r
93 #include "backend.h"
\r
94 #include "backendz.h"
\r
96 char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book
\r
97 void HandleMachineMove P((char *message, ChessProgramState *cps));
\r
99 static char zippyPartner[MSG_SIZ];
\r
100 static char zippyLastOpp[MSG_SIZ];
\r
101 static char zippyOffender[MSG_SIZ]; // [HGM] aborter
\r
102 static int zippyConsecGames;
\r
103 static time_t zippyLastGameEnd;
\r
105 extern void mysrandom(unsigned int seed);
\r
106 extern int myrandom(void);
\r
112 /* Get name of Zippy lines file */
\r
113 p = getenv("ZIPPYLINES");
\r
115 appData.zippyLines = p;
\r
118 /* Get word that Zippy thinks is insulting */
\r
119 p = getenv("ZIPPYPINHEAD");
\r
121 appData.zippyPinhead = p;
\r
124 /* What password is used for remote control? */
\r
125 p = getenv("ZIPPYPASSWORD");
\r
127 appData.zippyPassword = p;
\r
130 /* What password is used for remote commands to gnuchess? */
\r
131 p = getenv("ZIPPYPASSWORD2");
\r
133 appData.zippyPassword2 = p;
\r
136 /* Joke feature for people who try an old password */
\r
137 p = getenv("ZIPPYWRONGPASSWORD");
\r
139 appData.zippyWrongPassword = p;
\r
142 /* While testing, I want to accept challenges from only one person
\r
143 (namely, my "anonymous" account), so I set an environment
\r
144 variable ZIPPYACCEPTONLY. */
\r
145 p = getenv("ZIPPYACCEPTONLY");
\r
147 appData.zippyAcceptOnly = p;
\r
150 /* Should Zippy use "i" command? */
\r
151 /* Defaults to 1=true */
\r
152 p = getenv("ZIPPYUSEI");
\r
154 appData.zippyUseI = atoi(p);
\r
157 /* How does Zippy handle bughouse partnering? */
\r
158 /* 0=say we can't play, 1=manual partnering, 2=auto partnering */
\r
159 p = getenv("ZIPPYBUGHOUSE");
\r
161 appData.zippyBughouse = atoi(p);
\r
164 /* Does Zippy abort games with Crafty? */
\r
165 /* Defaults to 0=false */
\r
166 p = getenv("ZIPPYNOPLAYCRAFTY");
\r
168 appData.zippyNoplayCrafty = atoi(p);
\r
171 /* What ICS command does Zippy send at game end? Default: "gameend". */
\r
172 p = getenv("ZIPPYGAMEEND");
\r
174 appData.zippyGameEnd = p;
\r
177 /* What ICS command does Zippy send at game start? Default: none. */
\r
178 p = getenv("ZIPPYGAMESTART");
\r
180 appData.zippyGameStart = p;
\r
183 /* Should Zippy accept adjourns? */
\r
184 /* Defaults to 0=false */
\r
185 p = getenv("ZIPPYADJOURN");
\r
187 appData.zippyAdjourn = atoi(p);
\r
190 /* Should Zippy accept aborts? */
\r
191 /* Defaults to 0=false */
\r
192 p = getenv("ZIPPYABORT");
\r
194 appData.zippyAbort = atoi(p);
\r
197 /* Should Zippy play chess variants (besides bughouse)? */
\r
198 p = getenv("ZIPPYVARIANTS");
\r
200 appData.zippyVariants = p;
\r
202 strcpy(first.variants, appData.zippyVariants);
\r
204 srandom(time(NULL));
\r
208 * Routines to implement Zippy talking
\r
212 char *swifties[] = {
\r
213 "i acclaims:", "i admonishes:", "i advertises:", "i advises:",
\r
214 "i advocates:", "i affirms:", "i alleges:", "i anathematizes:",
\r
215 "i animadverts:", "i announces:", "i apostrophizes:",
\r
216 "i appeals:", "i applauds:", "i approves:", "i argues:",
\r
217 "i articulates:", "i asserts:", "i asseverates:", "i attests:",
\r
218 "i avers:", "i avows:", "i baas:", "i babbles:", "i banters:",
\r
219 "i barks:", "i bawls:", "i bays:", "i begs:", "i belches:",
\r
220 "i bellows:", "i belts out:", "i berates:", "i beshrews:",
\r
221 "i blabbers:", "i blabs:", "i blares:", "i blasphemes:",
\r
222 "i blasts:", "i blathers:", "i bleats:", "i blithers:",
\r
223 "i blubbers:", "i blurts out:", "i blusters:", "i boasts:",
\r
224 "i brags:", "i brays:", "i broadcasts:", "i burbles:",
\r
225 "i buzzes:", "i cachinnates:", "i cackles:", "i caterwauls:",
\r
226 "i calumniates:", "i caws:", "i censures:", "i chants:",
\r
227 "i chatters:", "i cheeps:", "i cheers:", "i chides:", "i chins:",
\r
228 "i chirps:", "i chortles:", "i chuckles:", "i claims:",
\r
229 "i clamors:", "i clucks:", "i commands:", "i commends:",
\r
230 "i comments:", "i commiserates:", "i communicates:",
\r
231 "i complains:", "i concludes:", "i confabulates:", "i confesses:",
\r
232 "i coos:", "i coughs:", "i counsels:", "i cries:", "i croaks:",
\r
233 "i crows:", "i curses:", "i daydreams:", "i debates:",
\r
234 "i declaims:", "i declares:", "i delivers:", "i denounces:",
\r
235 "i deposes:", "i directs:", "i discloses:", "i disparages:",
\r
236 "i discourses:", "i divulges:", "i documents:", "i drawls:",
\r
237 "i dreams:", "i drivels:", "i drones:", "i effuses:",
\r
238 /*"i ejaculates:",*/ "i elucidates:", "i emotes:", "i endorses:",
\r
239 "i enthuses:", "i entreats:", "i enunciates:", "i eulogizes:",
\r
240 "i exclaims:", "i execrates:", "i exhorts:", "i expatiates:",
\r
241 "i explains:", "i explicates:", "i explodes:", "i exposes:",
\r
242 "i exposits:", "i expostulates: ",
\r
243 "i expounds:", "i expresses:", "i extols:",
\r
244 "i exults:", "i fantasizes:", "i fibs:", "i filibusters:",
\r
245 "i flatters:", "i flutes:", "i fools:", "i free-associates:",
\r
246 "i fulminates:", "i gabbles:", "i gabs:", "i gasps:",
\r
247 "i giggles:", "i gossips:", "i gripes:", "i groans:", "i growls:",
\r
248 "i grunts:", "i guesses:", "i guffaws:", "i gushes:", "i hails:",
\r
249 "i hallucinates:", "i harangues:", "i harmonizes:", "i hectors:",
\r
250 "i hints:", "i hisses:", "i hollers:", "i honks:", "i hoots:",
\r
251 "i hosannas:", "i howls:", "i hums:", "i hypothecates:",
\r
252 "i hypothesizes:", "i imagines:", "i implies:", "i implores:",
\r
253 "i imprecates:", "i indicates:", "i infers:",
\r
254 "i informs everyone:", "i instructs:", "i interjects:",
\r
255 "i interposes:", "i intimates:", "i intones:", "i introspects:",
\r
256 "i inveighs:", "i jabbers:", "i japes:", "i jests:", "i jibes:",
\r
257 "i jives:", "i jokes:", "i joshes:", "i keens:", "i laments:",
\r
258 "i lauds:", "i laughs:", "i lectures:", "i lies:", "i lilts:",
\r
259 "i lisps:", "i maintains:", "i maledicts:", "i maunders:",
\r
260 "i meows:", "i mewls:", "i mimes:", "i minces:", "i moans:",
\r
261 "i moos:", "i mourns:", "i mouths:", "i mumbles:", "i murmurs:",
\r
262 "i muses:", "i mutters:", "i nags:", "i natters:", "i neighs:",
\r
263 "i notes:", "i nuncupates:", "i objurgates:", "i observes:",
\r
264 "i offers:", "i oinks:", "i opines:", "i orates:", "i orders:",
\r
265 "i panegyrizes:", "i pantomimes:", "i pants:", "i peals:",
\r
266 "i peeps:", "i perorates:", "i persuades:", "i petitions:",
\r
267 "i phonates:", "i pipes up:", "i pitches:", "i pleads:",
\r
268 "i points out:", "i pontificates:", "i postulates:", "i praises:",
\r
269 "i prates:", "i prattles:", "i preaches:", "i prescribes:",
\r
270 "i prevaricates:", "i proclaims:", "i projects:", "i pronounces:",
\r
271 "i proposes:", "i proscribes:", "i quacks:", "i queries:",
\r
272 "i questions:", "i quips:", "i quotes:", "i rages:", "i rambles:",
\r
273 "i rants:", "i raps:", "i rasps:", "i rattles:", "i raves:",
\r
274 "i reacts:", "i recites:", "i recommends:", "i records:",
\r
275 "i reiterates:", "i rejoins:", "i releases:", "i remarks:",
\r
276 "i reminisces:", "i remonstrates:", "i repeats:", "i replies:",
\r
277 "i reports:", "i reprimands:", "i reproaches:", "i reproves:",
\r
278 "i resounds:", "i responds:", "i retorts:", "i reveals:",
\r
279 "i reviles:", "i roars:", "i rumbles:", "i sanctions:",
\r
280 "i satirizes:", "i sauces:", "i scolds:", "i screams:",
\r
281 "i screeches:", "i semaphores:", "i sends:", "i sermonizes:",
\r
282 "i shrieks:", "i sibilates:", "i sighs:", "i signals:",
\r
283 "i signifies:", "i signs:", "i sings:", "i slurs:", "i snaps:",
\r
284 "i snarls:", "i sneezes:", "i snickers:", "i sniggers:",
\r
285 "i snivels:", "i snores:", "i snorts:", "i sobs:",
\r
286 "i soliloquizes:", "i sounds off:", "i sounds out:", "i speaks:",
\r
287 "i spews:", "i spits out:", "i splutters:", "i spoofs:",
\r
288 "i spouts:", "i sputters:", "i squalls:", "i squawks:",
\r
289 "i squeaks:", "i squeals:", "i stammers:", "i states:",
\r
290 "i stresses:", "i stutters:", "i submits:", "i suggests:",
\r
291 "i summarizes:", "i sums up:", "i swears:", "i talks:",
\r
292 "i tattles:", "i teases:", "i telegraphs:", "i testifies:",
\r
293 "i threatens:", "i thunders:", "i titters:", "i tongue-lashes:",
\r
294 "i toots:", "i transcribes:", "i transmits:", "i trills:",
\r
295 "i trumpets:", "i twaddles:", "i tweets:", "i twitters:",
\r
296 "i types:", "i upbraids:", "i urges:", "i utters:", "i ventures:",
\r
297 "i vibrates:", "i vilifies:", "i vituperates:", "i vocalizes:",
\r
298 "i vociferates:", "i voices:", "i waffles:", "i wails:",
\r
299 "i warbles:", "i warns:", "i weeps:", "i wheezes:", "i whimpers:",
\r
300 "i whines:", "i whinnies:", "i whistles:", "i wisecracks:",
\r
301 "i witnesses:", "i woofs:", "i writes:", "i yammers:", "i yawps:",
\r
302 "i yells:", "i yelps:", "i yodels:", "i yowls:", "i zings:",
\r
305 #define MAX_SPEECH 250
\r
307 void Speak(how, whom)
\r
310 static FILE *zipfile = NULL;
\r
311 static struct stat zipstat;
\r
312 char zipbuf[MAX_SPEECH + 1];
\r
313 static time_t lastShout = 0;
\r
319 if (strcmp(how, "shout") == 0) {
\r
320 now = time((time_t *) NULL);
\r
321 if (now - lastShout < 1*60) return;
\r
323 if (appData.zippyUseI) {
\r
324 how = swifties[(unsigned) random() %
\r
325 (sizeof(swifties)/sizeof(char *))];
\r
329 if (zipfile == NULL) {
\r
330 zipfile = fopen(appData.zippyLines, "r");
\r
331 if (zipfile == NULL) {
\r
332 DisplayFatalError("Can't open Zippy lines file", errno, 1);
\r
335 fstat(fileno(zipfile), &zipstat);
\r
339 fseek(zipfile, (unsigned) random() % zipstat.st_size, 0);
\r
342 } while (c != NULLCHAR && c != '^' && c != EOF);
\r
343 if (c == EOF) continue;
\r
344 while ((c = getc(zipfile)) == '\n') ;
\r
345 if (c == EOF) continue;
\r
350 /* Don't use ics_prefix; we need to let FICS expand the alias i -> it,
\r
351 but use the real command "i" on ICC */
\r
352 strcpy(zipbuf, how);
\r
353 strcat(zipbuf, " ");
\r
354 if (whom != NULL) {
\r
355 strcat(zipbuf, whom);
\r
356 strcat(zipbuf, " ");
\r
358 speechlen = strlen(zipbuf);
\r
359 p = zipbuf + speechlen;
\r
361 while (++speechlen < MAX_SPEECH) {
\r
362 if (c == NULLCHAR || c == '^') {
\r
367 } else if (c == '\n') {
\r
371 } while (c == ' ');
\r
372 } else if (c == EOF) {
\r
379 /* Tried to say something too long, or junk at the end of the
\r
380 file. Try something else. */
\r
381 Speak(how, whom); /* tail recursion */
\r
384 int ZippyCalled(str)
\r
387 return ics_handle[0] != NULLCHAR && StrCaseStr(str, ics_handle) != NULL;
\r
390 static char opp_name[128][32];
\r
391 static int num_opps=0;
\r
393 extern ColorClass curColor;
\r
395 static void SetCurColor( ColorClass color )
\r
400 static void ColorizeEx( ColorClass color, int cont )
\r
402 if( appData.colorize ) {
\r
403 Colorize( color, cont );
\r
404 SetCurColor( color );
\r
408 int ZippyControl(buf, i)
\r
413 char reply[MSG_SIZ];
\r
416 #include "trivia.c"
\r
419 /* Possibly reject Crafty as opponent */
\r
420 if (appData.zippyPlay && appData.zippyNoplayCrafty && forwardMostMove < 4
\r
421 && looking_at(buf, i, "* kibitzes: Hello from Crafty"))
\r
423 player = StripHighlightAndTitle(star_match[0]);
\r
424 if ((gameMode == IcsPlayingWhite &&
\r
425 StrCaseCmp(player, gameInfo.black) == 0) ||
\r
426 (gameMode == IcsPlayingBlack &&
\r
427 StrCaseCmp(player, gameInfo.white) == 0)) {
\r
429 sprintf(reply, "%ssay This computer does not play Crafty clones\n%sabort\n%s+noplay %s\n",
\r
430 ics_prefix, ics_prefix, ics_prefix, player);
\r
436 /* If this is a computer, save the name. Then later, once the */
\r
437 /* game is really started, we will send the "computer" notice to */
\r
439 if (appData.zippyPlay &&
\r
440 looking_at(buf, i, "* is in the computer list")) {
\r
442 for (i=0;i<num_opps;i++)
\r
443 if (!strcmp(opp_name[i],star_match[0])) break;
\r
444 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[0]);
\r
446 if (appData.zippyPlay && looking_at(buf, i, "* * is a computer *")) {
\r
448 for (i=0;i<num_opps;i++)
\r
449 if (!strcmp(opp_name[i],star_match[1])) break;
\r
450 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[1]);
\r
453 /* Tells and says */
\r
454 if (appData.zippyPlay &&
\r
455 (looking_at(buf, i, "* offers to be your bughouse partner") ||
\r
456 looking_at(buf, i, "* tells you: [automatic message] I chose you"))) {
\r
457 player = StripHighlightAndTitle(star_match[0]);
\r
458 if (appData.zippyBughouse > 1 && first.initDone) {
\r
459 sprintf(reply, "%spartner %s\n", ics_prefix, player);
\r
461 if (strcmp(zippyPartner, player) != 0) {
\r
462 strcpy(zippyPartner, player);
\r
463 SendToProgram(reply + strlen(ics_prefix), &first);
\r
465 } else if (appData.zippyBughouse > 0) {
\r
466 sprintf(reply, "%sdecline %s\n", ics_prefix, player);
\r
469 sprintf(reply, "%stell %s This computer cannot play bughouse\n",
\r
470 ics_prefix, player);
\r
476 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
477 looking_at(buf, i, "* agrees to be your partner")) {
\r
478 player = StripHighlightAndTitle(star_match[0]);
\r
479 sprintf(reply, "partner %s\n", player);
\r
480 if (strcmp(zippyPartner, player) != 0) {
\r
481 strcpy(zippyPartner, player);
\r
482 SendToProgram(reply, &first);
\r
487 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
488 (looking_at(buf, i, "are no longer *'s partner") ||
\r
490 "* tells you: [automatic message] I'm no longer your"))) {
\r
491 player = StripHighlightAndTitle(star_match[0]);
\r
492 if (strcmp(zippyPartner, player) == 0) {
\r
493 zippyPartner[0] = NULLCHAR;
\r
494 SendToProgram("partner\n", &first);
\r
499 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
500 (looking_at(buf, i, "no longer have a bughouse partner") ||
\r
501 looking_at(buf, i, "partner has disconnected") ||
\r
502 looking_at(buf, i, "partner has just chosen a new partner"))) {
\r
503 zippyPartner[0] = NULLCHAR;
\r
504 SendToProgram("partner\n", &first);
\r
508 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
509 looking_at(buf, i, "* (your partner) tells you: *")) {
\r
510 /* This pattern works on FICS but not ICC */
\r
511 player = StripHighlightAndTitle(star_match[0]);
\r
512 if (strcmp(zippyPartner, player) != 0) {
\r
513 strcpy(zippyPartner, player);
\r
514 sprintf(reply, "partner %s\n", player);
\r
515 SendToProgram(reply, &first);
\r
517 sprintf(reply, "ptell %s\n", star_match[1]);
\r
518 SendToProgram(reply, &first);
\r
522 if (looking_at(buf, i, "* tells you: *") ||
\r
523 looking_at(buf, i, "* says: *"))
\r
525 player = StripHighlightAndTitle(star_match[0]);
\r
526 if (appData.zippyPassword[0] != NULLCHAR &&
\r
527 strncmp(star_match[1], appData.zippyPassword,
\r
528 strlen(appData.zippyPassword)) == 0) {
\r
529 p = star_match[1] + strlen(appData.zippyPassword);
\r
530 while (*p == ' ') p++;
\r
533 } else if (appData.zippyPassword2[0] != NULLCHAR && first.initDone &&
\r
534 strncmp(star_match[1], appData.zippyPassword2,
\r
535 strlen(appData.zippyPassword2)) == 0) {
\r
536 p = star_match[1] + strlen(appData.zippyPassword2);
\r
537 while (*p == ' ') p++;
\r
538 SendToProgram(p, &first);
\r
539 SendToProgram("\n", &first);
\r
540 } else if (appData.zippyWrongPassword[0] != NULLCHAR &&
\r
541 strncmp(star_match[1], appData.zippyWrongPassword,
\r
542 strlen(appData.zippyWrongPassword)) == 0) {
\r
543 p = star_match[1] + strlen(appData.zippyWrongPassword);
\r
544 while (*p == ' ') p++;
\r
545 sprintf(reply, "wrong %s\n", player);
\r
547 } else if (appData.zippyBughouse && first.initDone &&
\r
548 strcmp(player, zippyPartner) == 0) {
\r
549 SendToProgram("ptell ", &first);
\r
550 SendToProgram(star_match[1], &first);
\r
551 SendToProgram("\n", &first);
\r
552 } else if (strncmp(star_match[1], HI, 6) == 0) {
\r
553 extern char* programVersion;
\r
554 sprintf(reply, "%stell %s %s\n",
\r
555 ics_prefix, player, programVersion);
\r
557 } else if (strncmp(star_match[1], "W0W!! ", 6) == 0) {
\r
558 extern char* programVersion;
\r
559 sprintf(reply, "%stell %s %s\n", ics_prefix,
\r
560 player, programVersion);
\r
562 } else if (appData.zippyTalk && (((unsigned) random() % 10) < 9)) {
\r
563 if (strcmp(player, ics_handle) != 0) {
\r
564 Speak("tell", player);
\r
568 ColorizeEx( ColorTell, FALSE );
\r
573 if( appData.colorize && looking_at(buf, i, "* (*) seeking") ) {
\r
574 ColorizeEx(ColorSeek, FALSE);
\r
578 if (looking_at(buf, i, "* spoofs you:")) {
\r
579 player = StripHighlightAndTitle(star_match[0]);
\r
580 sprintf(reply, "spoofedby %s\n", player);
\r
587 int ZippyConverse(buf, i)
\r
591 static char lastgreet[MSG_SIZ];
\r
592 char reply[MSG_SIZ];
\r
595 /* Shouts and emotes */
\r
596 if (looking_at(buf, i, "--> * *") ||
\r
597 looking_at(buf, i, "* shouts: *"))
\r
599 if (appData.zippyTalk) {
\r
600 char *player = StripHighlightAndTitle(star_match[0]);
\r
601 if (strcmp(player, ics_handle) == 0) {
\r
603 } else if (appData.zippyPinhead[0] != NULLCHAR &&
\r
604 StrCaseStr(star_match[1], appData.zippyPinhead) != NULL) {
\r
605 sprintf(reply, "insult %s\n", player);
\r
607 } else if (ZippyCalled(star_match[1])) {
\r
608 Speak("shout", NULL);
\r
612 ColorizeEx(ColorShout, FALSE);
\r
617 if (looking_at(buf, i, "* kibitzes: *")) {
\r
618 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
\r
619 char *player = StripHighlightAndTitle(star_match[0]);
\r
620 if (strcmp(player, ics_handle) != 0) {
\r
621 Speak("kibitz", NULL);
\r
625 ColorizeEx(ColorKibitz, FALSE);
\r
630 if (looking_at(buf, i, "* whispers: *")) {
\r
631 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
\r
632 char *player = StripHighlightAndTitle(star_match[0]);
\r
633 if (strcmp(player, ics_handle) != 0) {
\r
634 Speak("whisper", NULL);
\r
638 ColorizeEx(ColorKibitz, FALSE);
\r
644 if ((looking_at(buf, i, ". * (*:*): *") && isdigit(star_match[1][0])) ||
\r
645 looking_at(buf, i, ". * at *:*: *")) {
\r
646 if (appData.zippyTalk) {
\r
648 char *player = StripHighlightAndTitle(star_match[0]);
\r
650 if (strcmp(player, ics_handle) != 0) {
\r
651 if (((unsigned) random() % 10) < 9)
\r
652 Speak("message", player);
\r
653 f = fopen("zippy.messagelog", "a");
\r
654 fprintf(f, "%s (%s:%s): %s\n", player,
\r
655 star_match[1], star_match[2], star_match[3]);
\r
662 /* Channel tells */
\r
664 if (looking_at(buf, i, "*(*: *")) {
\r
667 if (star_match[0][0] == NULLCHAR ||
\r
668 strchr(star_match[0], ' ') ||
\r
669 strchr(star_match[1], ' ')) {
\r
670 /* Oops, did not want to match this; probably a message */
\r
674 if (appData.zippyTalk) {
\r
675 player = StripHighlightAndTitle(star_match[0]);
\r
676 channel = strrchr(star_match[1], '(');
\r
677 if (channel == NULL) {
\r
678 channel = star_match[1];
\r
682 channel[strlen(channel)-1] = NULLCHAR;
\r
684 /* Always tell to the channel (probability 90%) */
\r
685 if (strcmp(player, ics_handle) != 0 &&
\r
686 ((unsigned) random() % 10) < 9) {
\r
687 Speak("tell", channel);
\r
690 /* Tell to the channel only if someone mentions our name */
\r
691 if (ZippyCalled(star_match[2])) {
\r
692 Speak("tell", channel);
\r
696 ColorizeEx( atoi(channel) == 1 ? ColorChannel1 : ColorChannel, FALSE );
\r
701 if (!appData.zippyTalk) return FALSE;
\r
703 if ((looking_at(buf, i, "You have * message") &&
\r
704 atoi(star_match[0]) != 0) ||
\r
705 looking_at(buf, i, "* has left a message for you") ||
\r
706 looking_at(buf, i, "* just sent you a message")) {
\r
707 sprintf(reply, "%smessages\n%sclearmessages *\n",
\r
708 ics_prefix, ics_prefix);
\r
713 if (looking_at(buf, i, "Notification: * has arrived")) {
\r
714 if (((unsigned) random() % 3) == 0) {
\r
715 char *player = StripHighlightAndTitle(star_match[0]);
\r
716 strcpy(lastgreet, player);
\r
717 sprintf(reply, "greet %s\n", player);
\r
719 Speak("tell", player);
\r
723 if (looking_at(buf, i, "Notification: * has departed")) {
\r
724 if (((unsigned) random() % 3) == 0) {
\r
725 char *player = StripHighlightAndTitle(star_match[0]);
\r
726 sprintf(reply, "farewell %s\n", player);
\r
731 if (looking_at(buf, i, "Not sent -- * is censoring you")) {
\r
732 char *player = StripHighlightAndTitle(star_match[0]);
\r
733 if (strcmp(player, lastgreet) == 0) {
\r
734 sprintf(reply, "%s-notify %s\n", ics_prefix, player);
\r
739 if (looking_at(buf, i, "command is currently turned off")) {
\r
740 appData.zippyUseI = 0;
\r
746 void ZippyGameStart(white, black)
\r
747 char *white, *black;
\r
749 if (!first.initDone) {
\r
750 /* Game is starting prematurely. We can't deal with this */
\r
751 SendToICS(ics_prefix);
\r
752 SendToICS("abort\n");
\r
753 SendToICS(ics_prefix);
\r
754 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
758 if (appData.zippyGameStart[0] != NULLCHAR) {
\r
759 SendToICS(appData.zippyGameStart);
\r
764 void ZippyGameEnd(result, resultDetails)
\r
766 char *resultDetails;
\r
768 if (appData.zippyAcceptOnly[0] == NULLCHAR &&
\r
769 appData.zippyGameEnd[0] != NULLCHAR) {
\r
770 SendToICS(appData.zippyGameEnd);
\r
773 zippyLastGameEnd = time(0);
\r
774 if(forwardMostMove < appData.zippyShortGame)
\r
775 strcpy(zippyOffender, zippyLastOpp); else zippyOffender[0] = 0; // [HGM] aborter
\r
779 * Routines to implement Zippy playing chess
\r
782 void ZippyHandleChallenge(srated, swild, sbase, sincrement, opponent)
\r
783 char *srated, *swild, *sbase, *sincrement, *opponent;
\r
786 int base, increment, i=0;
\r
788 VariantClass variant;
\r
792 variant = StringToVariant(swild);
\r
793 varname = VariantName(variant);
\r
794 base = atoi(sbase);
\r
795 increment = atoi(sincrement);
\r
797 /* [DM] If icsAnalyzeEngine active we don't accept automatic games */
\r
798 if (appData.icsActive && appData.icsEngineAnalyze) return;
\r
800 /* If desired, you can insert more code here to decline matches
\r
801 based on rated, variant, base, and increment, but it is
\r
802 easier to use the ICS formula feature instead. */
\r
804 if (variant == VariantLoadable) {
\r
806 "%stell %s This computer can't play wild type %s\n%sdecline %s\n",
\r
807 ics_prefix, opponent, swild, ics_prefix, opponent);
\r
811 if (StrStr(appData.zippyVariants, varname) == NULL ||
\r
812 ((i=first.protocolVersion) != 1 && StrStr(first.variants, varname) == NULL) /* [HGM] zippyvar */
\r
815 "%stell %s This computer can't play %s [%s], only %s\n%sdecline %s\n",
\r
816 ics_prefix, opponent, swild, varname,
\r
817 i ? first.variants : appData.zippyVariants, /* [HGM] zippyvar */
\r
818 ics_prefix, opponent);
\r
823 /* Are we blocking match requests from all but one person? */
\r
824 if (appData.zippyAcceptOnly[0] != NULLCHAR &&
\r
825 StrCaseCmp(opponent, appData.zippyAcceptOnly)) {
\r
826 /* Yes, and this isn't him. Ignore challenge. */
\r
830 /* Too many consecutive games with same opponent? If so, make him
\r
831 wait until someone else has played or a timeout has elapsed. */
\r
832 if (appData.zippyMaxGames &&
\r
833 strcmp(opponent, zippyLastOpp) == 0 &&
\r
834 zippyConsecGames >= appData.zippyMaxGames &&
\r
835 difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
\r
836 sprintf(buf, "%stell %s Sorry, you have just played %d consecutive games against %s. To give others a chance, please wait %d seconds or until someone else has played.\n%sdecline %s\n",
\r
837 ics_prefix, opponent, zippyConsecGames, ics_handle,
\r
838 appData.zippyReplayTimeout, ics_prefix, opponent);
\r
843 /* [HGM] aborter: opponent is cheater that aborts games he doesn't like on first move. Make him wait */
\r
844 if (strcmp(opponent, zippyOffender) == 0 &&
\r
845 difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
\r
846 sprintf(buf, "%stell %s Sorry, your previous game against %s was rather short. "
\r
847 " It will wait %d seconds to see if a tougher opponent comes along.\n%sdecline %s\n",
\r
848 ics_prefix, opponent, ics_handle,
\r
849 appData.zippyReplayTimeout, ics_prefix, opponent);
\r
854 /* Engine not yet initialized or still thinking about last game? */
\r
855 if (!first.initDone || first.lastPing != first.lastPong) {
\r
856 sprintf(buf, "%stell %s I'm not quite ready for a new game yet; try again soon.\n%sdecline %s\n",
\r
857 ics_prefix, opponent, ics_prefix, opponent);
\r
862 sprintf(buf, "%saccept %s\n", ics_prefix, opponent);
\r
864 if (appData.zippyTalk) {
\r
865 Speak("tell", opponent);
\r
870 /* Accept matches */
\r
871 int ZippyMatch(buf, i)
\r
875 if (looking_at(buf, i, "* * match * * requested with * (*)")) {
\r
877 ZippyHandleChallenge(star_match[0], star_match[1],
\r
878 star_match[2], star_match[3],
\r
879 StripHighlightAndTitle(star_match[4]));
\r
883 /* Old FICS 0-increment form */
\r
884 if (looking_at(buf, i, "* * match * requested with * (*)")) {
\r
886 ZippyHandleChallenge(star_match[0], star_match[1],
\r
887 star_match[2], "0",
\r
888 StripHighlightAndTitle(star_match[3]));
\r
892 if (looking_at(buf, i,
\r
893 "* has made an alternate proposal of * * match * *.")) {
\r
895 ZippyHandleChallenge(star_match[1], star_match[2],
\r
896 star_match[3], star_match[4],
\r
897 StripHighlightAndTitle(star_match[0]));
\r
901 /* FICS wild/nonstandard forms */
\r
902 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * Loaded from *")) {
\r
903 /* note: star_match[2] can include "[white] " or "[black] "
\r
904 before our own name. */
\r
905 if(star_match[8] == NULL || star_match[8][0] == 0) // [HGM] chessd: open-source ICS has file on next line
\r
906 ZippyHandleChallenge(star_match[4], star_match[5],
\r
907 star_match[6], star_match[7], StripHighlightAndTitle(star_match[0]));
\r
908 else ZippyHandleChallenge(star_match[4], star_match[8],
\r
909 star_match[6], star_match[7],
\r
910 StripHighlightAndTitle(star_match[0]));
\r
914 if (looking_at(buf, i,
\r
915 "Challenge: * (*) *(*) * * * * : * * Loaded from *")) {
\r
916 /* note: star_match[2] can include "[white] " or "[black] "
\r
917 before our own name. */
\r
918 ZippyHandleChallenge(star_match[4], star_match[10],
\r
919 star_match[8], star_match[9],
\r
920 StripHighlightAndTitle(star_match[0]));
\r
924 /* Regular forms */
\r
925 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * : * *") |
\r
926 looking_at(buf, i, "Challenge: * (*) *(*) * * * * * *")) {
\r
927 /* note: star_match[2] can include "[white] " or "[black] "
\r
928 before our own name. */
\r
929 ZippyHandleChallenge(star_match[4], star_match[5],
\r
930 star_match[8], star_match[9],
\r
931 StripHighlightAndTitle(star_match[0]));
\r
935 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * *")) {
\r
936 /* note: star_match[2] can include "[white] " or "[black] "
\r
937 before our own name. */
\r
938 ZippyHandleChallenge(star_match[4], star_match[5],
\r
939 star_match[6], star_match[7],
\r
940 StripHighlightAndTitle(star_match[0]));
\r
945 if (ics_type == ICS_ICC) { // [DM]
\r
946 if (looking_at(buf, i, "Your opponent offers you a draw")) {
\r
947 if (first.sendDrawOffers && first.initDone)
\r
948 SendToProgram("draw\n", &first);
\r
952 if (looking_at(buf, i, "offers you a draw")) {
\r
953 if (first.sendDrawOffers && first.initDone) {
\r
954 SendToProgram("draw\n", &first);
\r
960 if (looking_at(buf, i, "requests that the game be aborted") ||
\r
961 looking_at(buf, i, "would like to abort")) {
\r
962 if (appData.zippyAbort ||
\r
963 (gameMode == IcsPlayingWhite && whiteTimeRemaining < 0) ||
\r
964 (gameMode == IcsPlayingBlack && blackTimeRemaining < 0)) {
\r
965 SendToICS(ics_prefix);
\r
966 SendToICS("abort\n");
\r
968 SendToICS(ics_prefix);
\r
969 if (appData.zippyTalk)
\r
970 SendToICS("say Whoa no! I am having FUN!!\n");
\r
972 SendToICS("say Sorry, this computer doesn't accept aborts.\n");
\r
977 if (looking_at(buf, i, "requests adjournment") ||
\r
978 looking_at(buf, i, "would like to adjourn")) {
\r
979 if (appData.zippyAdjourn) {
\r
980 SendToICS(ics_prefix);
\r
981 SendToICS("adjourn\n");
\r
983 SendToICS(ics_prefix);
\r
984 if (appData.zippyTalk)
\r
985 SendToICS("say Whoa no! I am having FUN playing NOW!!\n");
\r
987 SendToICS("say Sorry, this computer doesn't accept adjourns.\n");
\r
995 /* Initialize chess program with data from the first board
\r
996 * of a new or resumed game.
\r
998 void ZippyFirstBoard(moveNum, basetime, increment)
\r
999 int moveNum, basetime, increment;
\r
1001 char buf[MSG_SIZ];
\r
1003 char *opp = (gameMode==IcsPlayingWhite ? gameInfo.black : gameInfo.white);
\r
1004 Boolean sentPos = FALSE;
\r
1005 char *bookHit = NULL; // [HGM] book
\r
1007 if (!first.initDone) {
\r
1008 /* Game is starting prematurely. We can't deal with this */
\r
1009 SendToICS(ics_prefix);
\r
1010 SendToICS("abort\n");
\r
1011 SendToICS(ics_prefix);
\r
1012 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
1016 /* Send the variant command if needed */
\r
1017 if (gameInfo.variant != VariantNormal) {
\r
1018 sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
\r
1019 SendToProgram(buf, &first);
\r
1022 if ((startedFromSetupPosition && moveNum == 0) ||
\r
1023 (!appData.getMoveList && moveNum > 0)) {
\r
1024 SendToProgram("force\n", &first);
\r
1025 SendBoard(&first, moveNum);
\r
1029 sprintf(buf, "level 0 %d %d\n", basetime, increment);
\r
1030 SendToProgram(buf, &first);
\r
1032 /* Count consecutive games from one opponent */
\r
1033 if (strcmp(opp, zippyLastOpp) == 0) {
\r
1034 zippyConsecGames++;
\r
1036 zippyConsecGames = 1;
\r
1037 strcpy(zippyLastOpp, opp);
\r
1040 /* Send the "computer" command if the opponent is in the list
\r
1041 we've been gathering. */
\r
1042 for (w=0; w<num_opps; w++) {
\r
1043 if (!strcmp(opp_name[w], opp)) {
\r
1044 SendToProgram(first.computerString, &first);
\r
1049 /* Ratings might be < 0 which means "we haven't seen a ratings
\r
1050 message from ICS." Send 0 in that case */
\r
1051 w = (gameInfo.whiteRating >= 0) ? gameInfo.whiteRating : 0;
\r
1052 b = (gameInfo.blackRating >= 0) ? gameInfo.blackRating : 0;
\r
1054 firstMove = FALSE;
\r
1055 if (gameMode == IcsPlayingWhite) {
\r
1056 if (first.sendName) {
\r
1057 sprintf(buf, "name %s\n", gameInfo.black);
\r
1058 SendToProgram(buf, &first);
\r
1060 strcpy(ics_handle, gameInfo.white);
\r
1061 sprintf(buf, "rating %d %d\n", w, b);
\r
1062 SendToProgram(buf, &first);
\r
1064 /* Position sent above, engine is in force mode */
\r
1065 if (WhiteOnMove(moveNum)) {
\r
1066 /* Engine is on move now */
\r
1067 if (first.sendTime) {
\r
1068 if (first.useColors) {
\r
1069 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1070 SendTimeRemaining(&first, TRUE);
\r
1071 SendToProgram("white\n", &first);
\r
1073 SendTimeRemaining(&first, TRUE);
\r
1076 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1078 /* Engine's opponent is on move now */
\r
1079 if (first.usePlayother) {
\r
1080 if (first.sendTime) {
\r
1081 SendTimeRemaining(&first, TRUE);
\r
1083 SendToProgram("playother\n", &first);
\r
1085 /* Need to send a "go" after opponent moves */
\r
1090 /* Position not sent above, move list might be sent later */
\r
1091 if (moveNum == 0) {
\r
1092 /* No move list coming; at start of game */
\r
1093 if (first.sendTime) {
\r
1094 if (first.useColors) {
\r
1095 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1096 SendTimeRemaining(&first, TRUE);
\r
1097 SendToProgram("white\n", &first);
\r
1099 SendTimeRemaining(&first, TRUE);
\r
1102 // SendToProgram("go\n", &first);
\r
1103 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1106 } else if (gameMode == IcsPlayingBlack) {
\r
1107 if (first.sendName) {
\r
1108 sprintf(buf, "name %s\n", gameInfo.white);
\r
1109 SendToProgram(buf, &first);
\r
1111 strcpy(ics_handle, gameInfo.black);
\r
1112 sprintf(buf, "rating %d %d\n", b, w);
\r
1113 SendToProgram(buf, &first);
\r
1115 /* Position sent above, engine is in force mode */
\r
1116 if (!WhiteOnMove(moveNum)) {
\r
1117 /* Engine is on move now */
\r
1118 if (first.sendTime) {
\r
1119 if (first.useColors) {
\r
1120 SendToProgram("white\n", &first); /*gnu kludge*/
\r
1121 SendTimeRemaining(&first, FALSE);
\r
1122 SendToProgram("black\n", &first);
\r
1124 SendTimeRemaining(&first, FALSE);
\r
1127 // SendToProgram("go\n", &first);
\r
1128 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1130 /* Engine's opponent is on move now */
\r
1131 if (first.usePlayother) {
\r
1132 if (first.sendTime) {
\r
1133 SendTimeRemaining(&first, FALSE);
\r
1135 SendToProgram("playother\n", &first);
\r
1137 /* Need to send a "go" after opponent moves */
\r
1142 /* Position not sent above, move list might be sent later */
\r
1143 /* Nothing needs to be done here */
\r
1147 if(bookHit) { // [HGM] book: simulate book reply
\r
1148 static char bookMove[MSG_SIZ]; // a bit generous?
\r
1150 programStats.depth = programStats.nodes = programStats.time =
\r
1151 programStats.score = programStats.got_only_move = 0;
\r
1152 sprintf(programStats.movelist, "%s (xbook)", bookHit);
\r
1154 strcpy(bookMove, "move ");
\r
1155 strcat(bookMove, bookHit);
\r
1156 HandleMachineMove(bookMove, &first);
\r
1162 ZippyHoldings(white_holding, black_holding, new_piece)
\r
1163 char *white_holding, *black_holding, *new_piece;
\r
1165 char buf[MSG_SIZ];
\r
1166 if (gameMode != IcsPlayingBlack && gameMode != IcsPlayingWhite) return;
\r
1167 sprintf(buf, "holding [%s] [%s] %s\n",
\r
1168 white_holding, black_holding, new_piece);
\r
1169 SendToProgram(buf, &first);
\r