2 * zippy.c -- Implements Zippy the Pinhead chess player on ICS in XBoard
5 * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
6 * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
8 * The following terms apply to Digital Equipment Corporation's copyright
10 * ------------------------------------------------------------------------
13 * Permission to use, copy, modify, and distribute this software and its
14 * documentation for any purpose and without fee is hereby granted,
15 * provided that the above copyright notice appear in all copies and that
16 * both that copyright notice and this permission notice appear in
17 * supporting documentation, and that the name of Digital not be
18 * used in advertising or publicity pertaining to distribution of the
19 * software without specific, written prior permission.
21 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
22 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
23 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
24 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
25 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
26 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
28 * ------------------------------------------------------------------------
30 * The following terms apply to the enhanced version of XBoard distributed
31 * by the Free Software Foundation:
32 * ------------------------------------------------------------------------
33 * This program is free software; you can redistribute it and/or modify
34 * it under the terms of the GNU General Public License as published by
35 * the Free Software Foundation; either version 2 of the License, or
36 * (at your option) any later version.
38 * This program is distributed in the hope that it will be useful,
39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
41 * GNU General Public License for more details.
43 * You should have received a copy of the GNU General Public License
44 * along with this program; if not, write to the Free Software
45 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
46 * ------------------------------------------------------------------------
53 #include <sys/types.h>
60 #else /* not STDC_HEADERS */
61 extern char *getenv();
64 # else /* not HAVE_STRING_H */
66 # endif /* not HAVE_STRING_H */
67 #endif /* not STDC_HEADERS */
69 #if TIME_WITH_SYS_TIME
70 # include <sys/time.h>
74 # include <sys/time.h>
91 static char zippyPartner[MSG_SIZ];
92 static char zippyLastOpp[MSG_SIZ];
93 static int zippyConsecGames;
94 static time_t zippyLastGameEnd;
96 extern void mysrandom(unsigned int seed);
97 extern int myrandom(void);
103 /* Get name of Zippy lines file */
104 p = getenv("ZIPPYLINES");
106 appData.zippyLines = p;
109 /* Get word that Zippy thinks is insulting */
110 p = getenv("ZIPPYPINHEAD");
112 appData.zippyPinhead = p;
115 /* What password is used for remote control? */
116 p = getenv("ZIPPYPASSWORD");
118 appData.zippyPassword = p;
121 /* What password is used for remote commands to gnuchess? */
122 p = getenv("ZIPPYPASSWORD2");
124 appData.zippyPassword2 = p;
127 /* Joke feature for people who try an old password */
128 p = getenv("ZIPPYWRONGPASSWORD");
130 appData.zippyWrongPassword = p;
133 /* While testing, I want to accept challenges from only one person
134 (namely, my "anonymous" account), so I set an environment
135 variable ZIPPYACCEPTONLY. */
136 p = getenv("ZIPPYACCEPTONLY");
138 appData.zippyAcceptOnly = p;
141 /* Should Zippy use "i" command? */
142 /* Defaults to 1=true */
143 p = getenv("ZIPPYUSEI");
145 appData.zippyUseI = atoi(p);
148 /* How does Zippy handle bughouse partnering? */
149 /* 0=say we can't play, 1=manual partnering, 2=auto partnering */
150 p = getenv("ZIPPYBUGHOUSE");
152 appData.zippyBughouse = atoi(p);
155 /* Does Zippy abort games with Crafty? */
156 /* Defaults to 0=false */
157 p = getenv("ZIPPYNOPLAYCRAFTY");
159 appData.zippyNoplayCrafty = atoi(p);
162 /* What ICS command does Zippy send at game end? Default: "gameend". */
163 p = getenv("ZIPPYGAMEEND");
165 appData.zippyGameEnd = p;
168 /* What ICS command does Zippy send at game start? Default: none. */
169 p = getenv("ZIPPYGAMESTART");
171 appData.zippyGameStart = p;
174 /* Should Zippy accept adjourns? */
175 /* Defaults to 0=false */
176 p = getenv("ZIPPYADJOURN");
178 appData.zippyAdjourn = atoi(p);
181 /* Should Zippy accept aborts? */
182 /* Defaults to 0=false */
183 p = getenv("ZIPPYABORT");
185 appData.zippyAbort = atoi(p);
188 /* Should Zippy play chess variants (besides bughouse)? */
189 p = getenv("ZIPPYVARIANTS");
191 appData.zippyVariants = p;
193 strcpy(first.variants, appData.zippyVariants);
199 * Routines to implement Zippy talking
204 "i acclaims:", "i admonishes:", "i advertises:", "i advises:",
205 "i advocates:", "i affirms:", "i alleges:", "i anathematizes:",
206 "i animadverts:", "i announces:", "i apostrophizes:",
207 "i appeals:", "i applauds:", "i approves:", "i argues:",
208 "i articulates:", "i asserts:", "i asseverates:", "i attests:",
209 "i avers:", "i avows:", "i baas:", "i babbles:", "i banters:",
210 "i barks:", "i bawls:", "i bays:", "i begs:", "i belches:",
211 "i bellows:", "i belts out:", "i berates:", "i beshrews:",
212 "i blabbers:", "i blabs:", "i blares:", "i blasphemes:",
213 "i blasts:", "i blathers:", "i bleats:", "i blithers:",
214 "i blubbers:", "i blurts out:", "i blusters:", "i boasts:",
215 "i brags:", "i brays:", "i broadcasts:", "i burbles:",
216 "i buzzes:", "i cachinnates:", "i cackles:", "i caterwauls:",
217 "i calumniates:", "i caws:", "i censures:", "i chants:",
218 "i chatters:", "i cheeps:", "i cheers:", "i chides:", "i chins:",
219 "i chirps:", "i chortles:", "i chuckles:", "i claims:",
220 "i clamors:", "i clucks:", "i commands:", "i commends:",
221 "i comments:", "i commiserates:", "i communicates:",
222 "i complains:", "i concludes:", "i confabulates:", "i confesses:",
223 "i coos:", "i coughs:", "i counsels:", "i cries:", "i croaks:",
224 "i crows:", "i curses:", "i daydreams:", "i debates:",
225 "i declaims:", "i declares:", "i delivers:", "i denounces:",
226 "i deposes:", "i directs:", "i discloses:", "i disparages:",
227 "i discourses:", "i divulges:", "i documents:", "i drawls:",
228 "i dreams:", "i drivels:", "i drones:", "i effuses:",
229 /*"i ejaculates:",*/ "i elucidates:", "i emotes:", "i endorses:",
230 "i enthuses:", "i entreats:", "i enunciates:", "i eulogizes:",
231 "i exclaims:", "i execrates:", "i exhorts:", "i expatiates:",
232 "i explains:", "i explicates:", "i explodes:", "i exposes:",
233 "i exposits:", "i expostulates: ",
234 "i expounds:", "i expresses:", "i extols:",
235 "i exults:", "i fantasizes:", "i fibs:", "i filibusters:",
236 "i flatters:", "i flutes:", "i fools:", "i free-associates:",
237 "i fulminates:", "i gabbles:", "i gabs:", "i gasps:",
238 "i giggles:", "i gossips:", "i gripes:", "i groans:", "i growls:",
239 "i grunts:", "i guesses:", "i guffaws:", "i gushes:", "i hails:",
240 "i hallucinates:", "i harangues:", "i harmonizes:", "i hectors:",
241 "i hints:", "i hisses:", "i hollers:", "i honks:", "i hoots:",
242 "i hosannas:", "i howls:", "i hums:", "i hypothecates:",
243 "i hypothesizes:", "i imagines:", "i implies:", "i implores:",
244 "i imprecates:", "i indicates:", "i infers:",
245 "i informs everyone:", "i instructs:", "i interjects:",
246 "i interposes:", "i intimates:", "i intones:", "i introspects:",
247 "i inveighs:", "i jabbers:", "i japes:", "i jests:", "i jibes:",
248 "i jives:", "i jokes:", "i joshes:", "i keens:", "i laments:",
249 "i lauds:", "i laughs:", "i lectures:", "i lies:", "i lilts:",
250 "i lisps:", "i maintains:", "i maledicts:", "i maunders:",
251 "i meows:", "i mewls:", "i mimes:", "i minces:", "i moans:",
252 "i moos:", "i mourns:", "i mouths:", "i mumbles:", "i murmurs:",
253 "i muses:", "i mutters:", "i nags:", "i natters:", "i neighs:",
254 "i notes:", "i nuncupates:", "i objurgates:", "i observes:",
255 "i offers:", "i oinks:", "i opines:", "i orates:", "i orders:",
256 "i panegyrizes:", "i pantomimes:", "i pants:", "i peals:",
257 "i peeps:", "i perorates:", "i persuades:", "i petitions:",
258 "i phonates:", "i pipes up:", "i pitches:", "i pleads:",
259 "i points out:", "i pontificates:", "i postulates:", "i praises:",
260 "i prates:", "i prattles:", "i preaches:", "i prescribes:",
261 "i prevaricates:", "i proclaims:", "i projects:", "i pronounces:",
262 "i proposes:", "i proscribes:", "i quacks:", "i queries:",
263 "i questions:", "i quips:", "i quotes:", "i rages:", "i rambles:",
264 "i rants:", "i raps:", "i rasps:", "i rattles:", "i raves:",
265 "i reacts:", "i recites:", "i recommends:", "i records:",
266 "i reiterates:", "i rejoins:", "i releases:", "i remarks:",
267 "i reminisces:", "i remonstrates:", "i repeats:", "i replies:",
268 "i reports:", "i reprimands:", "i reproaches:", "i reproves:",
269 "i resounds:", "i responds:", "i retorts:", "i reveals:",
270 "i reviles:", "i roars:", "i rumbles:", "i sanctions:",
271 "i satirizes:", "i sauces:", "i scolds:", "i screams:",
272 "i screeches:", "i semaphores:", "i sends:", "i sermonizes:",
273 "i shrieks:", "i sibilates:", "i sighs:", "i signals:",
274 "i signifies:", "i signs:", "i sings:", "i slurs:", "i snaps:",
275 "i snarls:", "i sneezes:", "i snickers:", "i sniggers:",
276 "i snivels:", "i snores:", "i snorts:", "i sobs:",
277 "i soliloquizes:", "i sounds off:", "i sounds out:", "i speaks:",
278 "i spews:", "i spits out:", "i splutters:", "i spoofs:",
279 "i spouts:", "i sputters:", "i squalls:", "i squawks:",
280 "i squeaks:", "i squeals:", "i stammers:", "i states:",
281 "i stresses:", "i stutters:", "i submits:", "i suggests:",
282 "i summarizes:", "i sums up:", "i swears:", "i talks:",
283 "i tattles:", "i teases:", "i telegraphs:", "i testifies:",
284 "i threatens:", "i thunders:", "i titters:", "i tongue-lashes:",
285 "i toots:", "i transcribes:", "i transmits:", "i trills:",
286 "i trumpets:", "i twaddles:", "i tweets:", "i twitters:",
287 "i types:", "i upbraids:", "i urges:", "i utters:", "i ventures:",
288 "i vibrates:", "i vilifies:", "i vituperates:", "i vocalizes:",
289 "i vociferates:", "i voices:", "i waffles:", "i wails:",
290 "i warbles:", "i warns:", "i weeps:", "i wheezes:", "i whimpers:",
291 "i whines:", "i whinnies:", "i whistles:", "i wisecracks:",
292 "i witnesses:", "i woofs:", "i writes:", "i yammers:", "i yawps:",
293 "i yells:", "i yelps:", "i yodels:", "i yowls:", "i zings:",
296 #define MAX_SPEECH 250
298 void Speak(how, whom)
301 static FILE *zipfile = NULL;
302 static struct stat zipstat;
303 char zipbuf[MAX_SPEECH + 1];
304 static time_t lastShout = 0;
310 if (strcmp(how, "shout") == 0) {
311 now = time((time_t *) NULL);
312 if (now - lastShout < 1*60) return;
314 if (appData.zippyUseI) {
315 how = swifties[(unsigned) random() %
316 (sizeof(swifties)/sizeof(char *))];
320 if (zipfile == NULL) {
321 zipfile = fopen(appData.zippyLines, "r");
322 if (zipfile == NULL) {
323 DisplayFatalError("Can't open Zippy lines file", errno, 1);
326 fstat(fileno(zipfile), &zipstat);
330 fseek(zipfile, (unsigned) random() % zipstat.st_size, 0);
333 } while (c != NULLCHAR && c != '^' && c != EOF);
334 if (c == EOF) continue;
335 while ((c = getc(zipfile)) == '\n') ;
336 if (c == EOF) continue;
341 /* Don't use ics_prefix; we need to let FICS expand the alias i -> it,
342 but use the real command "i" on ICC */
346 strcat(zipbuf, whom);
349 speechlen = strlen(zipbuf);
350 p = zipbuf + speechlen;
352 while (++speechlen < MAX_SPEECH) {
353 if (c == NULLCHAR || c == '^') {
358 } else if (c == '\n') {
363 } else if (c == EOF) {
370 /* Tried to say something too long, or junk at the end of the
371 file. Try something else. */
372 Speak(how, whom); /* tail recursion */
378 return ics_handle[0] != NULLCHAR && StrCaseStr(str, ics_handle) != NULL;
381 static char opp_name[128][32];
382 static int num_opps=0;
384 int ZippyControl(buf, i)
395 /* Possibly reject Crafty as opponent */
396 if (appData.zippyPlay && appData.zippyNoplayCrafty && forwardMostMove < 4
397 && looking_at(buf, i, "* kibitzes: Hello from Crafty")) {
398 player = StripHighlightAndTitle(star_match[0]);
399 if ((gameMode == IcsPlayingWhite &&
400 StrCaseCmp(player, gameInfo.black) == 0) ||
401 (gameMode == IcsPlayingBlack &&
402 StrCaseCmp(player, gameInfo.white) == 0)) {
404 sprintf(reply, "%ssay This computer does not play Crafty clones\n%sabort\n%s+noplay %s\n",
405 ics_prefix, ics_prefix, ics_prefix, player);
411 /* If this is a computer, save the name. Then later, once the */
412 /* game is really started, we will send the "computer" notice to */
414 if (appData.zippyPlay &&
415 looking_at(buf, i, "* is in the computer list")) {
417 for (i=0;i<num_opps;i++)
418 if (!strcmp(opp_name[i],star_match[0])) break;
419 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[0]);
421 if (appData.zippyPlay && looking_at(buf, i, "* * is a computer *")) {
423 for (i=0;i<num_opps;i++)
424 if (!strcmp(opp_name[i],star_match[1])) break;
425 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[1]);
429 if (appData.zippyPlay &&
430 (looking_at(buf, i, "* offers to be your bughouse partner") ||
431 looking_at(buf, i, "* tells you: [automatic message] I chose you"))) {
432 player = StripHighlightAndTitle(star_match[0]);
433 if (appData.zippyBughouse > 1 && first.initDone) {
434 sprintf(reply, "%spartner %s\n", ics_prefix, player);
436 if (strcmp(zippyPartner, player) != 0) {
437 strcpy(zippyPartner, player);
438 SendToProgram(reply + strlen(ics_prefix), &first);
440 } else if (appData.zippyBughouse > 0) {
441 sprintf(reply, "%sdecline %s\n", ics_prefix, player);
444 sprintf(reply, "%stell %s This computer cannot play bughouse\n",
451 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
452 looking_at(buf, i, "* agrees to be your partner")) {
453 player = StripHighlightAndTitle(star_match[0]);
454 sprintf(reply, "partner %s\n", player);
455 if (strcmp(zippyPartner, player) != 0) {
456 strcpy(zippyPartner, player);
457 SendToProgram(reply, &first);
462 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
463 (looking_at(buf, i, "are no longer *'s partner") ||
465 "* tells you: [automatic message] I'm no longer your"))) {
466 player = StripHighlightAndTitle(star_match[0]);
467 if (strcmp(zippyPartner, player) == 0) {
468 zippyPartner[0] = NULLCHAR;
469 SendToProgram("partner\n", &first);
474 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
475 (looking_at(buf, i, "no longer have a bughouse partner") ||
476 looking_at(buf, i, "partner has disconnected") ||
477 looking_at(buf, i, "partner has just chosen a new partner"))) {
478 zippyPartner[0] = NULLCHAR;
479 SendToProgram("partner\n", &first);
483 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
484 looking_at(buf, i, "* (your partner) tells you: *")) {
485 /* This pattern works on FICS but not ICC */
486 player = StripHighlightAndTitle(star_match[0]);
487 if (strcmp(zippyPartner, player) != 0) {
488 strcpy(zippyPartner, player);
489 sprintf(reply, "partner %s\n", player);
490 SendToProgram(reply, &first);
492 sprintf(reply, "ptell %s\n", star_match[1]);
493 SendToProgram(reply, &first);
497 if (looking_at(buf, i, "* tells you: *") ||
498 looking_at(buf, i, "* says: *")) {
499 player = StripHighlightAndTitle(star_match[0]);
500 if (appData.zippyPassword[0] != NULLCHAR &&
501 strncmp(star_match[1], appData.zippyPassword,
502 strlen(appData.zippyPassword)) == 0) {
503 p = star_match[1] + strlen(appData.zippyPassword);
504 while (*p == ' ') p++;
507 } else if (appData.zippyPassword2[0] != NULLCHAR && first.initDone &&
508 strncmp(star_match[1], appData.zippyPassword2,
509 strlen(appData.zippyPassword2)) == 0) {
510 p = star_match[1] + strlen(appData.zippyPassword2);
511 while (*p == ' ') p++;
512 SendToProgram(p, &first);
513 SendToProgram("\n", &first);
514 } else if (appData.zippyWrongPassword[0] != NULLCHAR &&
515 strncmp(star_match[1], appData.zippyWrongPassword,
516 strlen(appData.zippyWrongPassword)) == 0) {
517 p = star_match[1] + strlen(appData.zippyWrongPassword);
518 while (*p == ' ') p++;
519 sprintf(reply, "wrong %s\n", player);
521 } else if (appData.zippyBughouse && first.initDone &&
522 strcmp(player, zippyPartner) == 0) {
523 SendToProgram("ptell ", &first);
524 SendToProgram(star_match[1], &first);
525 SendToProgram("\n", &first);
526 } else if (strncmp(star_match[1], HI, 6) == 0) {
527 extern char* programVersion;
528 sprintf(reply, "%stell %s %s\n",
529 ics_prefix, player, programVersion);
531 } else if (strncmp(star_match[1], "W0W!! ", 6) == 0) {
532 extern char* programVersion;
533 sprintf(reply, "%stell %s %s\n", ics_prefix,
534 player, programVersion);
536 } else if (appData.zippyTalk && (((unsigned) random() % 10) < 9)) {
537 if (strcmp(player, ics_handle) != 0) {
538 Speak("tell", player);
544 if (looking_at(buf, i, "* spoofs you:")) {
545 player = StripHighlightAndTitle(star_match[0]);
546 sprintf(reply, "spoofedby %s\n", player);
552 int ZippyConverse(buf, i)
556 static char lastgreet[MSG_SIZ];
560 /* Shouts and emotes */
561 if (looking_at(buf, i, "--> * *") ||
562 looking_at(buf, i, "* shouts: *")) {
563 if (appData.zippyTalk) {
564 char *player = StripHighlightAndTitle(star_match[0]);
565 if (strcmp(player, ics_handle) == 0) {
567 } else if (appData.zippyPinhead[0] != NULLCHAR &&
568 StrCaseStr(star_match[1], appData.zippyPinhead) != NULL) {
569 sprintf(reply, "insult %s\n", player);
571 } else if (ZippyCalled(star_match[1])) {
572 Speak("shout", NULL);
578 if (looking_at(buf, i, "* kibitzes: *")) {
579 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
580 char *player = StripHighlightAndTitle(star_match[0]);
581 if (strcmp(player, ics_handle) != 0) {
582 Speak("kibitz", NULL);
588 if (looking_at(buf, i, "* whispers: *")) {
589 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
590 char *player = StripHighlightAndTitle(star_match[0]);
591 if (strcmp(player, ics_handle) != 0) {
592 Speak("whisper", NULL);
599 if ((looking_at(buf, i, ". * (*:*): *") && isdigit(star_match[1][0])) ||
600 looking_at(buf, i, ". * at *:*: *")) {
601 if (appData.zippyTalk) {
603 char *player = StripHighlightAndTitle(star_match[0]);
605 if (strcmp(player, ics_handle) != 0) {
606 if (((unsigned) random() % 10) < 9)
607 Speak("message", player);
608 f = fopen("zippy.messagelog", "a");
609 fprintf(f, "%s (%s:%s): %s\n", player,
610 star_match[1], star_match[2], star_match[3]);
619 if (looking_at(buf, i, "*(*: *")) {
622 if (star_match[0][0] == NULLCHAR ||
623 strchr(star_match[0], ' ') ||
624 strchr(star_match[1], ' ')) {
625 /* Oops, did not want to match this; probably a message */
629 if (appData.zippyTalk) {
630 player = StripHighlightAndTitle(star_match[0]);
631 channel = strrchr(star_match[1], '(');
632 if (channel == NULL) {
633 channel = star_match[1];
637 channel[strlen(channel)-1] = NULLCHAR;
639 /* Always tell to the channel (probability 90%) */
640 if (strcmp(player, ics_handle) != 0 &&
641 ((unsigned) random() % 10) < 9) {
642 Speak("tell", channel);
645 /* Tell to the channel only if someone mentions our name */
646 if (ZippyCalled(star_match[2])) {
647 Speak("tell", channel);
654 if (!appData.zippyTalk) return FALSE;
656 if ((looking_at(buf, i, "You have * message") &&
657 atoi(star_match[0]) != 0) ||
658 looking_at(buf, i, "* has left a message for you") ||
659 looking_at(buf, i, "* just sent you a message")) {
660 sprintf(reply, "%smessages\n%sclearmessages *\n",
661 ics_prefix, ics_prefix);
666 if (looking_at(buf, i, "Notification: * has arrived")) {
667 if (((unsigned) random() % 3) == 0) {
668 char *player = StripHighlightAndTitle(star_match[0]);
669 strcpy(lastgreet, player);
670 sprintf(reply, "greet %s\n", player);
672 Speak("tell", player);
676 if (looking_at(buf, i, "Notification: * has departed")) {
677 if (((unsigned) random() % 3) == 0) {
678 char *player = StripHighlightAndTitle(star_match[0]);
679 sprintf(reply, "farewell %s\n", player);
684 if (looking_at(buf, i, "Not sent -- * is censoring you")) {
685 char *player = StripHighlightAndTitle(star_match[0]);
686 if (strcmp(player, lastgreet) == 0) {
687 sprintf(reply, "%s-notify %s\n", ics_prefix, player);
692 if (looking_at(buf, i, "command is currently turned off")) {
693 appData.zippyUseI = 0;
699 void ZippyGameStart(white, black)
702 if (!first.initDone) {
703 /* Game is starting prematurely. We can't deal with this */
704 SendToICS(ics_prefix);
705 SendToICS("abort\n");
706 SendToICS(ics_prefix);
707 SendToICS("say Sorry, the chess program is not initialized yet.\n");
711 if (appData.zippyGameStart[0] != NULLCHAR) {
712 SendToICS(appData.zippyGameStart);
717 void ZippyGameEnd(result, resultDetails)
721 if (appData.zippyAcceptOnly[0] == NULLCHAR &&
722 appData.zippyGameEnd[0] != NULLCHAR) {
723 SendToICS(appData.zippyGameEnd);
726 zippyLastGameEnd = time(0);
730 * Routines to implement Zippy playing chess
733 void ZippyHandleChallenge(srated, swild, sbase, sincrement, opponent)
734 char *srated, *swild, *sbase, *sincrement, *opponent;
739 VariantClass variant;
743 variant = StringToVariant(swild);
744 varname = VariantName(variant);
746 increment = atoi(sincrement);
748 /* If icsAnalyzeEngine active
749 we don't accept automatic games */
750 if (appData.icsActive)
751 if (appData.icsEngineAnalyze) return;
753 /* If desired, you can insert more code here to decline matches
754 based on rated, variant, base, and increment, but it is
755 easier to use the ICS formula feature instead. */
757 if (variant == VariantLoadable) {
759 "%stell %s This computer can't play wild type %s\n%sdecline %s\n",
760 ics_prefix, opponent, swild, ics_prefix, opponent);
764 if (StrStr(appData.zippyVariants, varname) == NULL) {
766 "%stell %s This computer can't play %s [%s], only %s\n%sdecline %s\n",
767 ics_prefix, opponent, swild, varname, appData.zippyVariants,
768 ics_prefix, opponent);
773 /* Are we blocking match requests from all but one person? */
774 if (appData.zippyAcceptOnly[0] != NULLCHAR &&
775 StrCaseCmp(opponent, appData.zippyAcceptOnly)) {
776 /* Yes, and this isn't him. Ignore challenge. */
780 /* Too many consecutive games with same opponent? If so, make him
781 wait until someone else has played or a timeout has elapsed. */
782 if (appData.zippyMaxGames &&
783 strcmp(opponent, zippyLastOpp) == 0 &&
784 zippyConsecGames >= appData.zippyMaxGames &&
785 difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
786 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",
787 ics_prefix, opponent, zippyConsecGames, ics_handle,
788 appData.zippyReplayTimeout, ics_prefix, opponent);
793 /* Engine not yet initialized or still thinking about last game? */
794 if (!first.initDone || first.lastPing != first.lastPong) {
795 sprintf(buf, "%stell %s I'm not quite ready for a new game yet; try again soon.\n%sdecline %s\n",
796 ics_prefix, opponent, ics_prefix, opponent);
801 sprintf(buf, "%saccept %s\n", ics_prefix, opponent);
803 if (appData.zippyTalk) {
804 Speak("tell", opponent);
810 int ZippyMatch(buf, i)
814 if (looking_at(buf, i, "* * match * * requested with * (*)")) {
816 ZippyHandleChallenge(star_match[0], star_match[1],
817 star_match[2], star_match[3],
818 StripHighlightAndTitle(star_match[4]));
822 /* Old FICS 0-increment form */
823 if (looking_at(buf, i, "* * match * requested with * (*)")) {
825 ZippyHandleChallenge(star_match[0], star_match[1],
827 StripHighlightAndTitle(star_match[3]));
831 if (looking_at(buf, i,
832 "* has made an alternate proposal of * * match * *.")) {
834 ZippyHandleChallenge(star_match[1], star_match[2],
835 star_match[3], star_match[4],
836 StripHighlightAndTitle(star_match[0]));
840 /* FICS wild/nonstandard forms */
841 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * Loaded from *")) {
842 /* note: star_match[2] can include "[white] " or "[black] "
843 before our own name. */
844 ZippyHandleChallenge(star_match[4], star_match[8],
845 star_match[6], star_match[7],
846 StripHighlightAndTitle(star_match[0]));
850 if (looking_at(buf, i,
851 "Challenge: * (*) *(*) * * * * : * * Loaded from *")) {
852 /* note: star_match[2] can include "[white] " or "[black] "
853 before our own name. */
854 ZippyHandleChallenge(star_match[4], star_match[10],
855 star_match[8], star_match[9],
856 StripHighlightAndTitle(star_match[0]));
861 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * : * *") |
862 looking_at(buf, i, "Challenge: * (*) *(*) * * * * * *")) {
863 /* note: star_match[2] can include "[white] " or "[black] "
864 before our own name. */
865 ZippyHandleChallenge(star_match[4], star_match[5],
866 star_match[8], star_match[9],
867 StripHighlightAndTitle(star_match[0]));
871 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * *")) {
872 /* note: star_match[2] can include "[white] " or "[black] "
873 before our own name. */
874 ZippyHandleChallenge(star_match[4], star_match[5],
875 star_match[6], star_match[7],
876 StripHighlightAndTitle(star_match[0]));
880 if (ics_type == ICS_ICC) {
881 if (looking_at(buf, i, "Your opponent offers you a draw")) {
882 if (first.sendDrawOffers && first.initDone)
883 SendToProgram("draw\n", &first);
887 if (looking_at(buf, i, "offers you a draw")) {
888 if (first.sendDrawOffers && first.initDone) {
889 SendToProgram("draw\n", &first);
895 if (looking_at(buf, i, "requests that the game be aborted") ||
896 looking_at(buf, i, "would like to abort")) {
897 if (appData.zippyAbort ||
898 (gameMode == IcsPlayingWhite && whiteTimeRemaining < 0) ||
899 (gameMode == IcsPlayingBlack && blackTimeRemaining < 0)) {
900 SendToICS(ics_prefix);
901 SendToICS("abort\n");
903 SendToICS(ics_prefix);
904 if (appData.zippyTalk)
905 SendToICS("say Whoa no! I am having FUN!!\n");
907 SendToICS("say Sorry, this computer doesn't accept aborts.\n");
912 if (looking_at(buf, i, "requests adjournment") ||
913 looking_at(buf, i, "would like to adjourn")) {
914 if (appData.zippyAdjourn) {
915 SendToICS(ics_prefix);
916 SendToICS("adjourn\n");
918 SendToICS(ics_prefix);
919 if (appData.zippyTalk)
920 SendToICS("say Whoa no! I am having FUN playing NOW!!\n");
922 SendToICS("say Sorry, this computer doesn't accept adjourns.\n");
930 /* Initialize chess program with data from the first board
931 * of a new or resumed game.
933 void ZippyFirstBoard(moveNum, basetime, increment)
934 int moveNum, basetime, increment;
938 char *opp = (gameMode==IcsPlayingWhite ? gameInfo.black : gameInfo.white);
939 Boolean sentPos = FALSE;
941 if (!first.initDone) {
942 /* Game is starting prematurely. We can't deal with this */
943 SendToICS(ics_prefix);
944 SendToICS("abort\n");
945 SendToICS(ics_prefix);
946 SendToICS("say Sorry, the chess program is not initialized yet.\n");
950 /* Send the variant command if needed */
951 if (gameInfo.variant != VariantNormal) {
952 sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
953 SendToProgram(buf, &first);
956 if ((startedFromSetupPosition && moveNum == 0) ||
957 (!appData.getMoveList && moveNum > 0)) {
958 SendToProgram("force\n", &first);
959 SendBoard(&first, moveNum);
963 sprintf(buf, "level 0 %d %d\n", basetime, increment);
964 SendToProgram(buf, &first);
966 /* Count consecutive games from one opponent */
967 if (strcmp(opp, zippyLastOpp) == 0) {
970 zippyConsecGames = 1;
971 strcpy(zippyLastOpp, opp);
974 /* Send the "computer" command if the opponent is in the list
975 we've been gathering. */
976 for (w=0; w<num_opps; w++) {
977 if (!strcmp(opp_name[w], opp)) {
978 SendToProgram(first.computerString, &first);
983 /* Ratings might be < 0 which means "we haven't seen a ratings
984 message from ICS." Send 0 in that case */
985 w = (gameInfo.whiteRating >= 0) ? gameInfo.whiteRating : 0;
986 b = (gameInfo.blackRating >= 0) ? gameInfo.blackRating : 0;
989 if (gameMode == IcsPlayingWhite) {
990 if (first.sendName) {
991 sprintf(buf, "name %s\n", gameInfo.black);
992 SendToProgram(buf, &first);
994 strcpy(ics_handle, gameInfo.white);
995 sprintf(buf, "rating %d %d\n", w, b);
996 SendToProgram(buf, &first);
998 /* Position sent above, engine is in force mode */
999 if (WhiteOnMove(moveNum)) {
1000 /* Engine is on move now */
1001 if (first.sendTime) {
1002 if (first.useColors) {
1003 SendToProgram("black\n", &first); /*gnu kludge*/
1004 SendTimeRemaining(&first, TRUE);
1005 SendToProgram("white\n", &first);
1007 SendTimeRemaining(&first, TRUE);
1010 SendToProgram("go\n", &first);
1012 /* Engine's opponent is on move now */
1013 if (first.usePlayother) {
1014 if (first.sendTime) {
1015 SendTimeRemaining(&first, TRUE);
1017 SendToProgram("playother\n", &first);
1019 /* Need to send a "go" after opponent moves */
1024 /* Position not sent above, move list might be sent later */
1026 /* No move list coming; at start of game */
1027 if (first.sendTime) {
1028 if (first.useColors) {
1029 SendToProgram("black\n", &first); /*gnu kludge*/
1030 SendTimeRemaining(&first, TRUE);
1031 SendToProgram("white\n", &first);
1033 SendTimeRemaining(&first, TRUE);
1036 SendToProgram("go\n", &first);
1039 } else if (gameMode == IcsPlayingBlack) {
1040 if (first.sendName) {
1041 sprintf(buf, "name %s\n", gameInfo.white);
1042 SendToProgram(buf, &first);
1044 strcpy(ics_handle, gameInfo.black);
1045 sprintf(buf, "rating %d %d\n", b, w);
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("white\n", &first); /*gnu kludge*/
1054 SendTimeRemaining(&first, FALSE);
1055 SendToProgram("black\n", &first);
1057 SendTimeRemaining(&first, FALSE);
1060 SendToProgram("go\n", &first);
1062 /* Engine's opponent is on move now */
1063 if (first.usePlayother) {
1064 if (first.sendTime) {
1065 SendTimeRemaining(&first, FALSE);
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 */
1075 /* Nothing needs to be done here */
1082 ZippyHoldings(white_holding, black_holding, new_piece)
1083 char *white_holding, *black_holding, *new_piece;
1086 if (gameMode != IcsPlayingBlack && gameMode != IcsPlayingWhite) return;
1087 sprintf(buf, "holding [%s] [%s] %s\n",
1088 white_holding, black_holding, new_piece);
1089 SendToProgram(buf, &first);