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 int currentMove; /* from backend.c */
102 /* Get name of Zippy lines file */
103 p = getenv("ZIPPYLINES");
105 appData.zippyLines = p;
108 /* Get word that Zippy thinks is insulting */
109 p = getenv("ZIPPYPINHEAD");
111 appData.zippyPinhead = p;
114 /* What password is used for remote control? */
115 p = getenv("ZIPPYPASSWORD");
117 appData.zippyPassword = p;
120 /* What password is used for remote commands to gnuchess? */
121 p = getenv("ZIPPYPASSWORD2");
123 appData.zippyPassword2 = p;
126 /* What password is used for GUI commands? */
127 p = getenv("ZIPPYPASSWORD3");
129 appData.zippyPassword3 = p;
132 /* Joke feature for people who try an old password */
133 p = getenv("ZIPPYWRONGPASSWORD");
135 appData.zippyWrongPassword = p;
138 /* While testing, I want to accept challenges from only one person
139 (namely, my "anonymous" account), so I set an environment
140 variable ZIPPYACCEPTONLY. */
141 p = getenv("ZIPPYACCEPTONLY");
143 appData.zippyAcceptOnly = p;
146 /* Should Zippy use "i" command? */
147 /* Defaults to 1=true */
148 p = getenv("ZIPPYUSEI");
150 appData.zippyUseI = atoi(p);
153 /* How does Zippy handle bughouse partnering? */
154 /* 0=say we can't play, 1=manual partnering, 2=auto partnering */
155 p = getenv("ZIPPYBUGHOUSE");
157 appData.zippyBughouse = atoi(p);
160 /* Does Zippy abort games with Crafty? */
161 /* Defaults to 0=false */
162 p = getenv("ZIPPYNOPLAYCRAFTY");
164 appData.zippyNoplayCrafty = atoi(p);
167 /* What ICS command does Zippy send at game end? Default: "gameend". */
168 p = getenv("ZIPPYGAMEEND");
170 appData.zippyGameEnd = p;
173 /* What ICS command does Zippy send at game start? Default: none. */
174 p = getenv("ZIPPYGAMESTART");
176 appData.zippyGameStart = p;
179 /* Should Zippy accept adjourns? */
180 /* Defaults to 0=false */
181 p = getenv("ZIPPYADJOURN");
183 appData.zippyAdjourn = atoi(p);
186 /* Should Zippy accept aborts? */
187 /* Defaults to 0=false */
188 p = getenv("ZIPPYABORT");
190 appData.zippyAbort = atoi(p);
193 /* Should Zippy play chess variants (besides bughouse)? */
194 p = getenv("ZIPPYVARIANTS");
196 appData.zippyVariants = p;
198 strcpy(first.variants, appData.zippyVariants);
204 * Routines to implement Zippy talking
209 "i acclaims:", "i admonishes:", "i advertises:", "i advises:",
210 "i advocates:", "i affirms:", "i alleges:", "i anathematizes:",
211 "i animadverts:", "i announces:", "i apostrophizes:",
212 "i appeals:", "i applauds:", "i approves:", "i argues:",
213 "i articulates:", "i asserts:", "i asseverates:", "i attests:",
214 "i avers:", "i avows:", "i baas:", "i babbles:", "i banters:",
215 "i barks:", "i bawls:", "i bays:", "i begs:", "i belches:",
216 "i bellows:", "i belts out:", "i berates:", "i beshrews:",
217 "i blabbers:", "i blabs:", "i blares:", "i blasphemes:",
218 "i blasts:", "i blathers:", "i bleats:", "i blithers:",
219 "i blubbers:", "i blurts out:", "i blusters:", "i boasts:",
220 "i brags:", "i brays:", "i broadcasts:", "i burbles:",
221 "i buzzes:", "i cachinnates:", "i cackles:", "i caterwauls:",
222 "i calumniates:", "i caws:", "i censures:", "i chants:",
223 "i chatters:", "i cheeps:", "i cheers:", "i chides:", "i chins:",
224 "i chirps:", "i chortles:", "i chuckles:", "i claims:",
225 "i clamors:", "i clucks:", "i commands:", "i commends:",
226 "i comments:", "i commiserates:", "i communicates:",
227 "i complains:", "i concludes:", "i confabulates:", "i confesses:",
228 "i coos:", "i coughs:", "i counsels:", "i cries:", "i croaks:",
229 "i crows:", "i curses:", "i daydreams:", "i debates:",
230 "i declaims:", "i declares:", "i delivers:", "i denounces:",
231 "i deposes:", "i directs:", "i discloses:", "i disparages:",
232 "i discourses:", "i divulges:", "i documents:", "i drawls:",
233 "i dreams:", "i drivels:", "i drones:", "i effuses:",
234 /*"i ejaculates:",*/ "i elucidates:", "i emotes:", "i endorses:",
235 "i enthuses:", "i entreats:", "i enunciates:", "i eulogizes:",
236 "i exclaims:", "i execrates:", "i exhorts:", "i expatiates:",
237 "i explains:", "i explicates:", "i explodes:", "i exposes:",
238 "i exposits:", "i expounds:", "i expresses:", "i extols:",
239 "i exults:", "i fantasizes:", "i fibs:", "i filibusters:",
240 "i flatters:", "i flutes:", "i fools:", "i free-associates:",
241 "i fulminates:", "i gabbles:", "i gabs:", "i gasps:",
242 "i giggles:", "i gossips:", "i gripes:", "i groans:", "i growls:",
243 "i grunts:", "i guesses:", "i guffaws:", "i gushes:", "i hails:",
244 "i hallucinates:", "i harangues:", "i harmonizes:", "i hectors:",
245 "i hints:", "i hisses:", "i hollers:", "i honks:", "i hoots:",
246 "i hosannas:", "i howls:", "i hums:", "i hypothecates:",
247 "i hypothesizes:", "i imagines:", "i implies:", "i implores:",
248 "i imprecates:", "i indicates:", "i infers:",
249 "i informs everyone:", "i instructs:", "i interjects:",
250 "i interposes:", "i intimates:", "i intones:", "i introspects:",
251 "i inveighs:", "i jabbers:", "i japes:", "i jests:", "i jibes:",
252 "i jives:", "i jokes:", "i joshes:", "i keens:", "i laments:",
253 "i lauds:", "i laughs:", "i lectures:", "i lies:", "i lilts:",
254 "i lisps:", "i maintains:", "i maledicts:", "i maunders:",
255 "i meows:", "i mewls:", "i mimes:", "i minces:", "i moans:",
256 "i moos:", "i mourns:", "i mouths:", "i mumbles:", "i murmurs:",
257 "i muses:", "i mutters:", "i nags:", "i natters:", "i neighs:",
258 "i notes:", "i nuncupates:", "i objurgates:", "i observes:",
259 "i offers:", "i oinks:", "i opines:", "i orates:", "i orders:",
260 "i panegyrizes:", "i pantomimes:", "i pants:", "i peals:",
261 "i peeps:", "i perorates:", "i persuades:", "i petitions:",
262 "i phonates:", "i pipes up:", "i pitches:", "i pleads:",
263 "i points out:", "i pontificates:", "i postulates:", "i praises:",
264 "i prates:", "i prattles:", "i preaches:", "i prescribes:",
265 "i prevaricates:", "i proclaims:", "i projects:", "i pronounces:",
266 "i proposes:", "i proscribes:", "i quacks:", "i queries:",
267 "i questions:", "i quips:", "i quotes:", "i rages:", "i rambles:",
268 "i rants:", "i raps:", "i rasps:", "i rattles:", "i raves:",
269 "i reacts:", "i recites:", "i recommends:", "i records:",
270 "i reiterates:", "i rejoins:", "i releases:", "i remarks:",
271 "i reminisces:", "i remonstrates:", "i repeats:", "i replies:",
272 "i reports:", "i reprimands:", "i reproaches:", "i reproves:",
273 "i resounds:", "i responds:", "i retorts:", "i reveals:",
274 "i reviles:", "i roars:", "i rumbles:", "i sanctions:",
275 "i satirizes:", "i sauces:", "i scolds:", "i screams:",
276 "i screeches:", "i semaphores:", "i sends:", "i sermonizes:",
277 "i shrieks:", "i sibilates:", "i sighs:", "i signals:",
278 "i signifies:", "i signs:", "i sings:", "i slurs:", "i snaps:",
279 "i snarls:", "i sneezes:", "i snickers:", "i sniggers:",
280 "i snivels:", "i snores:", "i snorts:", "i sobs:",
281 "i soliloquizes:", "i sounds off:", "i sounds out:", "i speaks:",
282 "i spews:", "i spits out:", "i splutters:", "i spoofs:",
283 "i spouts:", "i sputters:", "i squalls:", "i squawks:",
284 "i squeaks:", "i squeals:", "i stammers:", "i states:",
285 "i stresses:", "i stutters:", "i submits:", "i suggests:",
286 "i summarizes:", "i sums up:", "i swears:", "i talks:",
287 "i tattles:", "i teases:", "i telegraphs:", "i testifies:",
288 "i threatens:", "i thunders:", "i titters:", "i tongue-lashes:",
289 "i toots:", "i transcribes:", "i transmits:", "i trills:",
290 "i trumpets:", "i twaddles:", "i tweets:", "i twitters:",
291 "i types:", "i upbraids:", "i urges:", "i utters:", "i ventures:",
292 "i vibrates:", "i vilifies:", "i vituperates:", "i vocalizes:",
293 "i vociferates:", "i voices:", "i waffles:", "i wails:",
294 "i warbles:", "i warns:", "i weeps:", "i wheezes:", "i whimpers:",
295 "i whines:", "i whinnies:", "i whistles:", "i wisecracks:",
296 "i witnesses:", "i woofs:", "i writes:", "i yammers:", "i yawps:",
297 "i yells:", "i yelps:", "i yodels:", "i yowls:", "i zings:",
300 #define MAX_SPEECH 250
302 void Speak(how, whom)
305 static FILE *zipfile = NULL;
306 static struct stat zipstat;
307 char zipbuf[MAX_SPEECH + 1];
308 static time_t lastShout = 0;
314 if (strcmp(how, "shout") == 0) {
315 now = time((time_t *) NULL);
316 if (now - lastShout < 1*60) return;
318 if (appData.zippyUseI) {
319 how = swifties[random() % (sizeof(swifties)/sizeof(char *))];
323 if (zipfile == NULL) {
324 zipfile = fopen(appData.zippyLines, "r");
325 if (zipfile == NULL) {
326 DisplayFatalError("Can't open Zippy lines file", errno, 1);
329 fstat(fileno(zipfile), &zipstat);
333 fseek(zipfile, random() % zipstat.st_size, 0);
336 } while (c != NULLCHAR && c != '^' && c != EOF);
337 if (c == EOF) continue;
338 while ((c = getc(zipfile)) == '\n') ;
339 if (c == EOF) continue;
344 /* Don't use ics_prefix; we need to let FICS expand the alias i -> it,
345 but use the real command "i" on ICC */
349 strcat(zipbuf, whom);
352 speechlen = strlen(zipbuf);
353 p = zipbuf + speechlen;
355 while (++speechlen < MAX_SPEECH) {
356 if (c == NULLCHAR || c == '^') {
361 } else if (c == '\n') {
366 } else if (c == EOF) {
373 /* Tried to say something too long, or junk at the end of the
374 file. Try something else. */
375 Speak(how, whom); /* tail recursion */
381 return ics_handle[0] != NULLCHAR && StrCaseStr(str, ics_handle) != NULL;
384 static char opp_name[128][32];
385 static int num_opps=0;
387 int ZippyControl(buf, i)
398 /* Possibly reject Crafty as opponent */
399 if (appData.zippyPlay && appData.zippyNoplayCrafty && forwardMostMove < 4
400 && looking_at(buf, i, "* kibitzes: Hello from Crafty")) {
401 player = StripHighlightAndTitle(star_match[0]);
402 if ((gameMode == IcsPlayingWhite &&
403 StrCaseCmp(player, gameInfo.black) == 0) ||
404 (gameMode == IcsPlayingBlack &&
405 StrCaseCmp(player, gameInfo.white) == 0)) {
407 sprintf(reply, "%ssay This computer does not play Crafty clones\n%sabort\n%s+noplay %s\n",
408 ics_prefix, ics_prefix, ics_prefix, player);
414 /* If this is a computer, save the name. Then later, once the */
415 /* game is really started, we will send the "computer" notice to */
417 if (appData.zippyPlay &&
418 looking_at(buf, i, "* is in the computer list")) {
421 for (i=0;i<num_opps;i++)
422 if (!strcmp(opp_name[i],star_match[0])) break;
423 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[0]);
425 if (appData.zippyPlay && looking_at(buf, i, "* * is a computer *")) {
428 for (i=0;i<num_opps;i++)
429 if (!strcmp(opp_name[i],star_match[1])) break;
430 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[1]);
434 if (appData.zippyPlay &&
435 (looking_at(buf, i, "* offers to be your bughouse partner") ||
436 looking_at(buf, i, "* tells you: [automatic message] I chose you"))) {
437 player = StripHighlightAndTitle(star_match[0]);
438 if (appData.zippyBughouse > 1 && first.initDone) {
439 sprintf(reply, "%spartner %s\n", ics_prefix, player);
441 if (strcmp(zippyPartner, player) != 0) {
442 strcpy(zippyPartner, player);
443 SendToProgram(reply + strlen(ics_prefix), &first);
445 } else if (appData.zippyBughouse > 0) {
446 sprintf(reply, "%sdecline %s\n", ics_prefix, player);
449 sprintf(reply, "%stell %s This computer cannot play bughouse\n",
456 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
457 looking_at(buf, i, "* agrees to be your partner")) {
458 player = StripHighlightAndTitle(star_match[0]);
459 sprintf(reply, "partner %s\n", player);
460 if (strcmp(zippyPartner, player) != 0) {
461 strcpy(zippyPartner, player);
462 SendToProgram(reply, &first);
467 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
468 (looking_at(buf, i, "are no longer *'s partner") ||
470 "* tells you: [automatic message] I'm no longer your"))) {
471 player = StripHighlightAndTitle(star_match[0]);
472 if (strcmp(zippyPartner, player) == 0) {
473 zippyPartner[0] = NULLCHAR;
474 SendToProgram("partner\n", &first);
479 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
480 (looking_at(buf, i, "no longer have a bughouse partner") ||
481 looking_at(buf, i, "partner has disconnected") ||
482 looking_at(buf, i, "partner has just chosen a new partner"))) {
483 zippyPartner[0] = NULLCHAR;
484 SendToProgram("partner\n", &first);
488 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
489 looking_at(buf, i, "* (your partner) tells you: *")) {
490 /* This pattern works on FICS but not ICC */
491 player = StripHighlightAndTitle(star_match[0]);
492 if (strcmp(zippyPartner, player) != 0) {
493 strcpy(zippyPartner, player);
494 sprintf(reply, "partner %s\n", player);
495 SendToProgram(reply, &first);
497 sprintf(reply, "ptell %s\n", star_match[1]);
498 SendToProgram(reply, &first);
502 if (looking_at(buf, i, "* tells you: *") ||
503 looking_at(buf, i, "* says: *")) {
504 player = StripHighlightAndTitle(star_match[0]);
505 if (appData.zippyBughouse && first.initDone &&
506 strcmp(player, zippyPartner) == 0) {
507 SendToProgram("ptell ", &first);
508 SendToProgram(star_match[1], &first);
509 SendToProgram("\n", &first);
510 } else if (strncmp(star_match[1], HI, 6) == 0) {
511 extern char* programVersion;
512 sprintf(reply, "%stell %s %s\n",
513 ics_prefix, player, programVersion);
515 } else if (strncmp(star_match[1], "W0W!! ", 6) == 0) {
516 extern char* programVersion;
517 sprintf(reply, "%stell %s %s\n", ics_prefix,
518 player, programVersion);
520 } else if (appData.zippyTalk && ((random() % 10) < 9)) {
521 if (strcmp(player, ics_handle) != 0) {
522 Speak("tell", player);
528 if (looking_at(buf, i, "* spoofs you:")) {
529 player = StripHighlightAndTitle(star_match[0]);
530 sprintf(reply, "spoofedby %s\n", player);
532 sprintf(reply, "tell %s Yes sir, i do it \n", player);
538 int ZippyConverse(buf, i)
542 static char lastgreet[MSG_SIZ];
546 /* Shouts and emotes */
547 if (looking_at(buf, i, "--> * *") ||
548 looking_at(buf, i, "* shouts: *")) {
549 if (appData.zippyTalk) {
550 char *player = StripHighlightAndTitle(star_match[0]);
551 if (strcmp(player, ics_handle) == 0) {
553 } else if (appData.zippyPinhead[0] != NULLCHAR &&
554 StrCaseStr(star_match[1], appData.zippyPinhead) != NULL) {
555 sprintf(reply, "insult %s\n", player);
557 } else if (ZippyCalled(star_match[1])) {
558 Speak("shout", NULL);
564 if (looking_at(buf, i, "* kibitzes: *")) {
565 if (appData.zippyTalk && (random() % 10) < 9) {
566 char *player = StripHighlightAndTitle(star_match[0]);
567 if (strcmp(player, ics_handle) != 0) {
568 Speak("kibitz", NULL);
574 if (looking_at(buf, i, "* whispers: *")) {
575 if (appData.zippyTalk && (random() % 10) < 9) {
576 char *player = StripHighlightAndTitle(star_match[0]);
577 if (strcmp(player, ics_handle) != 0) {
578 Speak("whisper", NULL);
585 if ((looking_at(buf, i, ". * (*:*): *") && isdigit(star_match[1][0])) ||
586 looking_at(buf, i, ". * at *:*: *")) {
587 if (appData.zippyTalk) {
589 char *player = StripHighlightAndTitle(star_match[0]);
591 if (strcmp(player, ics_handle) != 0) {
592 if ((random() % 10) < 9)
593 Speak("message", player);
594 f = fopen("zippy.messagelog", "a");
595 fprintf(f, "%s (%s:%s): %s\n", player,
596 star_match[1], star_match[2], star_match[3]);
605 if (looking_at(buf, i, "*(*: *")) {
608 if (star_match[0][0] == NULLCHAR ||
609 strchr(star_match[0], ' ') ||
610 strchr(star_match[1], ' ')) {
611 /* Oops, did not want to match this; probably a message */
615 if (appData.zippyTalk) {
616 player = StripHighlightAndTitle(star_match[0]);
617 channel = strrchr(star_match[1], '(');
618 if (channel == NULL) {
619 channel = star_match[1];
623 channel[strlen(channel)-1] = NULLCHAR;
625 /* Always tell to the channel (probability 90%) */
626 if (strcmp(player, ics_handle) != 0 && (random() % 10) < 9) {
627 Speak("tell", channel);
630 /* Tell to the channel only if someone mentions our name */
631 if (ZippyCalled(star_match[2])) {
632 Speak("tell", channel);
639 if (!appData.zippyTalk) return FALSE;
641 if ((looking_at(buf, i, "You have * message") &&
642 atoi(star_match[0]) != 0) ||
643 looking_at(buf, i, "* has left a message for you") ||
644 looking_at(buf, i, "* just sent you a message")) {
645 sprintf(reply, "%smessages\n%sclearmessages *\n",
646 ics_prefix, ics_prefix);
651 if (looking_at(buf, i, "Notification: * has arrived")) {
652 if ((random() % 3) == 0) {
653 char *player = StripHighlightAndTitle(star_match[0]);
654 strcpy(lastgreet, player);
655 sprintf(reply, "greet %s\n", player);
657 Speak("tell", player);
661 if (looking_at(buf, i, "Notification: * has departed")) {
662 if ((random() % 3) == 0) {
663 char *player = StripHighlightAndTitle(star_match[0]);
664 sprintf(reply, "farewell %s\n", player);
669 if (looking_at(buf, i, "Not sent -- * is censoring you")) {
670 char *player = StripHighlightAndTitle(star_match[0]);
671 if (strcmp(player, lastgreet) == 0) {
672 sprintf(reply, "%s-notify %s\n", ics_prefix, player);
677 if (looking_at(buf, i, "command is currently turned off")) {
678 appData.zippyUseI = 0;
684 void ZippyGameStart(white, black)
687 if (!first.initDone) {
688 /* Game is starting prematurely. We can't deal with this */
689 SendToICS(ics_prefix);
690 SendToICS("abort\n");
691 SendToICS(ics_prefix);
692 SendToICS("say Sorry, the chess program is not initialized yet.\n");
696 if (appData.zippyGameStart[0] != NULLCHAR) {
697 SendToICS(appData.zippyGameStart);
702 void ZippyGameEnd(result, resultDetails)
706 if (appData.zippyAcceptOnly[0] == NULLCHAR &&
707 appData.zippyGameEnd[0] != NULLCHAR) {
708 SendToICS(appData.zippyGameEnd);
711 zippyLastGameEnd = time(0);
715 * Routines to implement Zippy playing chess
718 void ZippyHandleChallenge(srated, swild, sbase, sincrement, opponent)
719 char *srated, *swild, *sbase, *sincrement, *opponent;
724 VariantClass variant;
728 variant = StringToVariant(swild);
729 varname = VariantName(variant);
731 increment = atoi(sincrement);
733 /* If desired, you can insert more code here to decline matches
734 based on rated, variant, base, and increment, but it is
735 easier to use the ICS formula feature instead. */
737 if (variant == VariantLoadable) {
739 "%stell %s This computer can't play wild type %s\n%sdecline %s\n",
740 ics_prefix, opponent, swild, ics_prefix, opponent);
744 if (StrStr(appData.zippyVariants, varname) == NULL) {
746 "%stell %s This computer can't play %s [%s], only %s\n%sdecline %s\n",
747 ics_prefix, opponent, swild, varname, appData.zippyVariants,
748 ics_prefix, opponent);
753 /* Are we blocking match requests from all but one person? */
754 if (appData.zippyAcceptOnly[0] != NULLCHAR &&
755 StrCaseCmp(opponent, appData.zippyAcceptOnly)) {
756 /* Yes, and this isn't him. Ignore challenge. */
760 /* Too many consecutive games with same opponent? If so, make him
761 wait until someone else has played or a timeout has elapsed. */
762 if (appData.zippyMaxGames &&
763 strcmp(opponent, zippyLastOpp) == 0 &&
764 zippyConsecGames >= appData.zippyMaxGames &&
765 difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
766 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",
767 ics_prefix, opponent, zippyConsecGames, ics_handle,
768 appData.zippyReplayTimeout, ics_prefix, opponent);
773 /* Engine not yet initialized or still thinking about last game? */
774 if (!first.initDone || first.lastPing != first.lastPong) {
775 sprintf(buf, "%stell %s I'm not quite ready for a new game yet; try again soon.\n%sdecline %s\n",
776 ics_prefix, opponent, ics_prefix, opponent);
781 sprintf(buf, "%saccept %s\n", ics_prefix, opponent);
783 if (appData.zippyTalk) {
784 Speak("tell", opponent);
790 int ZippyMatch(buf, i, player)
796 player = StripHighlightAndTitle(star_match[0]);
798 if (looking_at(buf, i, "* * match * * requested with * (*)")) {
800 /* don't accept matches if ICS analyze run */
801 if (gameMode == IcsObserving || gameMode == IcsExamining) {
802 sprintf(reply, "tell %s Sorry, i'm analysis a game and don't accept matches. Please try it later again \n", player);
807 ZippyHandleChallenge(star_match[0], star_match[1],
808 star_match[2], star_match[3],
809 StripHighlightAndTitle(star_match[4]));
813 /* Old FICS 0-increment form */
814 if (looking_at(buf, i, "* * match * requested with * (*)")) {
816 /* don't accept matches if ICS analyze run */
817 if (gameMode == IcsObserving || gameMode == IcsExamining) {
818 sprintf(reply, "tell %s Sorry, i'm analysis a game and don't accept matches. Please try it later again \n", player);
823 ZippyHandleChallenge(star_match[0], star_match[1],
825 StripHighlightAndTitle(star_match[3]));
829 if (looking_at(buf, i,
830 "* has made an alternate proposal of * * match * *.")) {
832 /* don't accept matches if ICS analyze run */
833 if (gameMode == IcsObserving || gameMode == IcsExamining) {
834 sprintf(reply, "tell %s Sorry, i'm analysis a game and don't accept matches. Please try it later again \n", player);
839 ZippyHandleChallenge(star_match[1], star_match[2],
840 star_match[3], star_match[4],
841 StripHighlightAndTitle(star_match[0]));
845 /* FICS wild/nonstandard forms */
846 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * Loaded from *")) {
848 /* don't accept matches if ICS analyze run */
849 if (gameMode == IcsObserving || gameMode == IcsExamining) {
850 sprintf(reply, "tell %s Sorry, i'm analysis a game and don't accept matches. Please try it later again \n", player);
855 /* note: star_match[2] can include "[white] " or "[black] "
856 before our own name. */
857 ZippyHandleChallenge(star_match[4], star_match[8],
858 star_match[6], star_match[7],
859 StripHighlightAndTitle(star_match[0]));
863 if (looking_at(buf, i,
864 "Challenge: * (*) *(*) * * * * : * * Loaded from *")) {
866 /* don't accept matches if ICS analyze run */
867 if (gameMode == IcsObserving || gameMode == IcsExamining) {
868 sprintf(reply, "tell %s Sorry, i'm analysis a game and don't accept matches. Please try it later again \n", player);
873 /* note: star_match[2] can include "[white] " or "[black] "
874 before our own name. */
875 ZippyHandleChallenge(star_match[4], star_match[10],
876 star_match[8], star_match[9],
877 StripHighlightAndTitle(star_match[0]));
882 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * : * *") |
883 looking_at(buf, i, "Challenge: * (*) *(*) * * * * * *")) {
885 /* don't accept matches if ICS analyze run */
886 if (gameMode == IcsObserving || gameMode == IcsExamining) {
887 sprintf(reply, "tell %s Sorry, i'm analysis a game and don't accept matches. Please try it later again \n", player);
892 /* note: star_match[2] can include "[white] " or "[black] "
893 before our own name. */
894 ZippyHandleChallenge(star_match[4], star_match[5],
895 star_match[8], star_match[9],
896 StripHighlightAndTitle(star_match[0]));
900 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * *")) {
902 /* don't accept matches if ICS analyze run */
903 if (gameMode == IcsObserving || gameMode == IcsExamining) {
904 sprintf(reply, "tell %s Sorry, i'm analysis a game and don't accept matches. Please try it later again \n", player);
909 /* note: star_match[2] can include "[white] " or "[black] "
910 before our own name. */
911 ZippyHandleChallenge(star_match[4], star_match[5],
912 star_match[6], star_match[7],
913 StripHighlightAndTitle(star_match[0]));
916 /* works now at backend.c
917 if (looking_at(buf, i, "offers you a draw")) {
918 if (first.sendDrawOffers && first.initDone) {
919 SendToProgram("draw\n", &first);
924 if (looking_at(buf, i, "requests that the game be aborted") ||
925 looking_at(buf, i, "would like to abort")) {
926 if (appData.zippyAbort ||
927 (gameMode == IcsPlayingWhite && whiteTimeRemaining < 0) ||
928 (gameMode == IcsPlayingBlack && blackTimeRemaining < 0)) {
929 SendToICS(ics_prefix);
930 SendToICS("abort\n");
932 SendToICS(ics_prefix);
933 if (appData.zippyTalk)
934 SendToICS("say Whoa no! I am having FUN!!\n");
936 SendToICS("say Sorry, this computer doesn't accept aborts.\n");
941 if (looking_at(buf, i, "requests adjournment") ||
942 looking_at(buf, i, "would like to adjourn")) {
943 if (appData.zippyAdjourn) {
944 SendToICS(ics_prefix);
945 SendToICS("adjourn\n");
947 SendToICS(ics_prefix);
948 if (appData.zippyTalk)
949 SendToICS("say Whoa no! I am having FUN playing NOW!!\n");
951 SendToICS("say Sorry, this computer doesn't accept adjourns.\n");
959 /* Initialize chess program with data from the first board
960 * of a new or resumed game.
962 void ZippyFirstBoard(moveNum, basetime, increment)
963 int moveNum, basetime, increment;
967 char *opp = (gameMode==IcsPlayingWhite ? gameInfo.black : gameInfo.white);
968 Boolean sentPos = FALSE;
970 if (!first.initDone) {
971 /* Game is starting prematurely. We can't deal with this */
972 SendToICS(ics_prefix);
973 SendToICS("abort\n");
974 SendToICS(ics_prefix);
975 SendToICS("say Sorry, the chess program is not initialized yet.\n");
979 /* reset ZippyDraw for a new or resumed game */
980 if (appData.zippyDraw) ZippyDraw(2, 0, 0);
982 /* Send the variant command if needed */
983 if (gameInfo.variant != VariantNormal) {
984 sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
985 SendToProgram(buf, &first);
988 if ((startedFromSetupPosition && moveNum == 0) ||
989 (!appData.getMoveList && moveNum > 0)) {
990 SendToProgram("force\n", &first);
991 SendBoard(&first, moveNum);
995 sprintf(buf, "level 0 %d %d\n", basetime, increment);
996 SendToProgram(buf, &first);
998 /* Count consecutive games from one opponent */
999 if (strcmp(opp, zippyLastOpp) == 0) {
1002 zippyConsecGames = 1;
1003 strcpy(zippyLastOpp, opp);
1006 /* Send the "computer" command if the opponent is in the list
1007 we've been gathering. */
1008 for (w=0; w<num_opps; w++) {
1009 if (!strcmp(opp_name[w], opp)) {
1010 SendToProgram(first.computerString, &first);
1012 /* If enable send engine output to ics we set to kibitz */
1013 if (appData.icsActive && appData.AnalysisWindow) appData.SendOutPutToICS = 2;
1018 /* Ratings might be < 0 which means "we haven't seen a ratings
1019 message from ICS." Send 0 in that case */
1020 w = (gameInfo.whiteRating >= 0) ? gameInfo.whiteRating : 0;
1021 b = (gameInfo.blackRating >= 0) ? gameInfo.blackRating : 0;
1024 if (gameMode == IcsPlayingWhite) {
1025 if (first.sendName) {
1026 sprintf(buf, "name %s\n", gameInfo.black);
1027 SendToProgram(buf, &first);
1029 strcpy(ics_handle, gameInfo.white);
1030 sprintf(buf, "rating %d %d\n", w, b);
1031 SendToProgram(buf, &first);
1033 /* Position sent above, engine is in force mode */
1034 if (WhiteOnMove(moveNum)) {
1035 /* Engine is on move now */
1036 if (first.sendTime) {
1037 if (first.useColors) {
1038 SendToProgram("black\n", &first); /*gnu kludge*/
1039 SendTimeRemaining(&first, TRUE);
1040 SendToProgram("white\n", &first);
1042 SendTimeRemaining(&first, TRUE);
1045 SendToProgram("go\n", &first);
1047 /* Engine's opponent is on move now */
1048 if (first.usePlayother) {
1049 if (first.sendTime) {
1050 SendTimeRemaining(&first, TRUE);
1052 SendToProgram("playother\n", &first);
1054 /* Need to send a "go" after opponent moves */
1059 /* Position not sent above, move list might be sent later */
1061 /* No move list coming; at start of game */
1062 if (first.sendTime) {
1063 if (first.useColors) {
1064 SendToProgram("black\n", &first); /*gnu kludge*/
1065 SendTimeRemaining(&first, TRUE);
1066 SendToProgram("white\n", &first);
1068 SendTimeRemaining(&first, TRUE);
1071 SendToProgram("go\n", &first);
1074 } else if (gameMode == IcsPlayingBlack) {
1075 if (first.sendName) {
1076 sprintf(buf, "name %s\n", gameInfo.white);
1077 SendToProgram(buf, &first);
1079 strcpy(ics_handle, gameInfo.black);
1080 sprintf(buf, "rating %d %d\n", b, w);
1081 SendToProgram(buf, &first);
1083 /* Position sent above, engine is in force mode */
1084 if (!WhiteOnMove(moveNum)) {
1085 /* Engine is on move now */
1086 if (first.sendTime) {
1087 if (first.useColors) {
1088 SendToProgram("white\n", &first); /*gnu kludge*/
1089 SendTimeRemaining(&first, FALSE);
1090 SendToProgram("black\n", &first);
1092 SendTimeRemaining(&first, FALSE);
1095 SendToProgram("go\n", &first);
1097 /* Engine's opponent is on move now */
1098 if (first.usePlayother) {
1099 if (first.sendTime) {
1100 SendTimeRemaining(&first, FALSE);
1102 SendToProgram("playother\n", &first);
1104 /* Need to send a "go" after opponent moves */
1109 /* Position not sent above, move list might be sent later */
1110 /* Nothing needs to be done here */
1117 ZippyHoldings(white_holding, black_holding, new_piece)
1118 char *white_holding, *black_holding, *new_piece;
1121 if (gameMode != IcsPlayingBlack && gameMode != IcsPlayingWhite) return;
1122 sprintf(buf, "holding [%s] [%s] %s\n",
1123 white_holding, black_holding, new_piece);
1124 SendToProgram(buf, &first);
1128 ZippyDraw(request, score, depth)
1129 int request; /* 0 no request 1 opponent offer draw 2 New Game */
1133 static int drawCounter; /* 0.00 engine score */
1134 static int lastDrawMove; /* Last move we offer a draw */
1135 static int RatingDiff; /* Diff between our rating */
1136 int drawFactor = 0; /* Setup which score is draw. Default is 0 */
1140 /* If request 1 we only become a offer from opponent. We
1141 don't save engine score and looking about some stuff to
1142 accept or decline the draw.
1143 If resquest 0 the engine make a new move and we looking
1144 to the last program score before that move. If 0.00 the
1147 If the score is not zero we reset the counter to zero.
1151 if (score == drawFactor || (score < 0 && score > -10)) {
1152 if (depth > 1) drawCounter++;
1153 if (drawCounter >= 5 && (currentMove - lastDrawMove) > 20 &&
1154 currentMove > 40 && depth > 1) {
1156 if (RatingDiff > 50) {
1157 sprintf(str, "whisper Winboard-DM: ZippyDraw: No draw offer - Rating of opponent to low \n");
1162 if (appData.debugMode) fprintf(debugFP,
1163 "Offer draw. counter: %d\n", drawCounter);
1164 sprintf(str, "kibitz Winboard-DM: ZippyDraw = counter %d; LastDraw: %d; \n",
1165 drawCounter, lastDrawMove);
1167 sprintf(str, "draw \n");
1169 lastDrawMove = currentMove;
1174 } else if (request == 1) {
1175 if (drawCounter >= 5 && currentMove > 40 && depth >= 1 && score <= drawFactor &&
1177 if (appData.debugMode) fprintf(debugFP, "accept draw \n");
1178 sprintf(str, "kibitz Winboard-DM: ZippyDraw = counter %d; LastDraw: %d; score %d R-Diff %d \n",
1179 drawCounter, lastDrawMove, score, RatingDiff);
1181 sprintf(str, "draw\n");
1186 if (score < -50 && depth > 1) {
1187 if (appData.debugMode) fprintf(debugFP, "draw \n");
1188 sprintf(str, "kibitz Winboard-DM: ZippyDraw = counter %d; LastDraw: %d; score %d R-Diff %d \n",
1189 drawCounter, lastDrawMove, score, RatingDiff);
1191 sprintf(str, "draw\n");
1195 if (appData.debugMode) fprintf(debugFP, "decline Draw \n");
1196 sprintf(str, "kibitz Winboard-DM: ZippyDraw = counter %d; LastDraw: %d; R-Diff %d\n",
1197 drawCounter, lastDrawMove, RatingDiff);
1200 sprintf(str, "kibitz No thank you. I wanna play \n");
1204 /* request 2 = new game - reset data */
1210 if (gameMode == IcsPlayingBlack) {
1211 RatingDiff = gameInfo.blackRating - gameInfo.whiteRating;
1212 } else if (gameMode == IcsPlayingWhite) {
1213 RatingDiff = gameInfo.whiteRating - gameInfo.blackRating;
1215 sprintf(str, "whisper Winboard-DM: ZippyDraw = enable dynamic draw; R-Diff %d \n", RatingDiff);