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