2 * zippy.c -- Implements Zippy the Pinhead chess player on ICS in XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
5 * Massachusetts. Enhancements Copyright
\r
6 * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software
\r
9 * The following terms apply to Digital Equipment Corporation's copyright
\r
10 * interest in XBoard:
\r
11 * ------------------------------------------------------------------------
\r
12 * All Rights Reserved
\r
14 * Permission to use, copy, modify, and distribute this software and its
\r
15 * documentation for any purpose and without fee is hereby granted,
\r
16 * provided that the above copyright notice appear in all copies and that
\r
17 * both that copyright notice and this permission notice appear in
\r
18 * supporting documentation, and that the name of Digital not be
\r
19 * used in advertising or publicity pertaining to distribution of the
\r
20 * software without specific, written prior permission.
\r
22 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
23 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
24 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
25 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
26 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
27 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
29 * ------------------------------------------------------------------------
\r
31 * The following terms apply to the enhanced version of XBoard
\r
32 * distributed by the Free Software Foundation:
\r
33 * ------------------------------------------------------------------------
\r
35 * GNU XBoard is free software: you can redistribute it and/or modify
\r
36 * it under the terms of the GNU General Public License as published by
\r
37 * the Free Software Foundation, either version 3 of the License, or (at
\r
38 * your option) any later version.
\r
40 * GNU XBoard is distributed in the hope that it will be useful, but
\r
41 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
42 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
43 * General Public License for more details.
\r
45 * You should have received a copy of the GNU General Public License
\r
46 * along with this program. If not, see http://www.gnu.org/licenses/.
\r
48 *------------------------------------------------------------------------
\r
49 ** See the file ChangeLog for a revision history. */
\r
55 #include <sys/types.h>
\r
56 #include <sys/stat.h>
\r
60 # include <stdlib.h>
\r
61 # include <string.h>
\r
62 #else /* not STDC_HEADERS */
\r
63 extern char *getenv();
\r
65 # include <string.h>
\r
66 # else /* not HAVE_STRING_H */
\r
67 # include <strings.h>
\r
68 # endif /* not HAVE_STRING_H */
\r
69 #endif /* not STDC_HEADERS */
\r
71 #if TIME_WITH_SYS_TIME
\r
72 # include <sys/time.h>
\r
75 # if HAVE_SYS_TIME_H
\r
76 # include <sys/time.h>
\r
84 # include <unistd.h>
\r
89 #include "frontend.h"
\r
90 #include "backend.h"
\r
91 #include "backendz.h"
\r
93 char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book
\r
94 void HandleMachineMove P((char *message, ChessProgramState *cps));
\r
96 static char zippyPartner[MSG_SIZ];
\r
97 static char zippyLastOpp[MSG_SIZ];
\r
98 static char zippyOffender[MSG_SIZ]; // [HGM] aborter
\r
99 static int zippyConsecGames;
\r
100 static time_t zippyLastGameEnd;
\r
102 extern void mysrandom(unsigned int seed);
\r
103 extern int myrandom(void);
\r
109 /* Get name of Zippy lines file */
\r
110 p = getenv("ZIPPYLINES");
\r
112 appData.zippyLines = p;
\r
115 /* Get word that Zippy thinks is insulting */
\r
116 p = getenv("ZIPPYPINHEAD");
\r
118 appData.zippyPinhead = p;
\r
121 /* What password is used for remote control? */
\r
122 p = getenv("ZIPPYPASSWORD");
\r
124 appData.zippyPassword = p;
\r
127 /* What password is used for remote commands to gnuchess? */
\r
128 p = getenv("ZIPPYPASSWORD2");
\r
130 appData.zippyPassword2 = p;
\r
133 /* Joke feature for people who try an old password */
\r
134 p = getenv("ZIPPYWRONGPASSWORD");
\r
136 appData.zippyWrongPassword = p;
\r
139 /* While testing, I want to accept challenges from only one person
\r
140 (namely, my "anonymous" account), so I set an environment
\r
141 variable ZIPPYACCEPTONLY. */
\r
142 p = getenv("ZIPPYACCEPTONLY");
\r
144 appData.zippyAcceptOnly = p;
\r
147 /* Should Zippy use "i" command? */
\r
148 /* Defaults to 1=true */
\r
149 p = getenv("ZIPPYUSEI");
\r
151 appData.zippyUseI = atoi(p);
\r
154 /* How does Zippy handle bughouse partnering? */
\r
155 /* 0=say we can't play, 1=manual partnering, 2=auto partnering */
\r
156 p = getenv("ZIPPYBUGHOUSE");
\r
158 appData.zippyBughouse = atoi(p);
\r
161 /* Does Zippy abort games with Crafty? */
\r
162 /* Defaults to 0=false */
\r
163 p = getenv("ZIPPYNOPLAYCRAFTY");
\r
165 appData.zippyNoplayCrafty = atoi(p);
\r
168 /* What ICS command does Zippy send at game end? Default: "gameend". */
\r
169 p = getenv("ZIPPYGAMEEND");
\r
171 appData.zippyGameEnd = p;
\r
174 /* What ICS command does Zippy send at game start? Default: none. */
\r
175 p = getenv("ZIPPYGAMESTART");
\r
177 appData.zippyGameStart = p;
\r
180 /* Should Zippy accept adjourns? */
\r
181 /* Defaults to 0=false */
\r
182 p = getenv("ZIPPYADJOURN");
\r
184 appData.zippyAdjourn = atoi(p);
\r
187 /* Should Zippy accept aborts? */
\r
188 /* Defaults to 0=false */
\r
189 p = getenv("ZIPPYABORT");
\r
191 appData.zippyAbort = atoi(p);
\r
194 /* Should Zippy play chess variants (besides bughouse)? */
\r
195 p = getenv("ZIPPYVARIANTS");
\r
197 appData.zippyVariants = p;
\r
199 strcpy(first.variants, appData.zippyVariants);
\r
201 srandom(time(NULL));
\r
205 * Routines to implement Zippy talking
\r
209 char *swifties[] = {
\r
210 "i acclaims:", "i admonishes:", "i advertises:", "i advises:",
\r
211 "i advocates:", "i affirms:", "i alleges:", "i anathematizes:",
\r
212 "i animadverts:", "i announces:", "i apostrophizes:",
\r
213 "i appeals:", "i applauds:", "i approves:", "i argues:",
\r
214 "i articulates:", "i asserts:", "i asseverates:", "i attests:",
\r
215 "i avers:", "i avows:", "i baas:", "i babbles:", "i banters:",
\r
216 "i barks:", "i bawls:", "i bays:", "i begs:", "i belches:",
\r
217 "i bellows:", "i belts out:", "i berates:", "i beshrews:",
\r
218 "i blabbers:", "i blabs:", "i blares:", "i blasphemes:",
\r
219 "i blasts:", "i blathers:", "i bleats:", "i blithers:",
\r
220 "i blubbers:", "i blurts out:", "i blusters:", "i boasts:",
\r
221 "i brags:", "i brays:", "i broadcasts:", "i burbles:",
\r
222 "i buzzes:", "i cachinnates:", "i cackles:", "i caterwauls:",
\r
223 "i calumniates:", "i caws:", "i censures:", "i chants:",
\r
224 "i chatters:", "i cheeps:", "i cheers:", "i chides:", "i chins:",
\r
225 "i chirps:", "i chortles:", "i chuckles:", "i claims:",
\r
226 "i clamors:", "i clucks:", "i commands:", "i commends:",
\r
227 "i comments:", "i commiserates:", "i communicates:",
\r
228 "i complains:", "i concludes:", "i confabulates:", "i confesses:",
\r
229 "i coos:", "i coughs:", "i counsels:", "i cries:", "i croaks:",
\r
230 "i crows:", "i curses:", "i daydreams:", "i debates:",
\r
231 "i declaims:", "i declares:", "i delivers:", "i denounces:",
\r
232 "i deposes:", "i directs:", "i discloses:", "i disparages:",
\r
233 "i discourses:", "i divulges:", "i documents:", "i drawls:",
\r
234 "i dreams:", "i drivels:", "i drones:", "i effuses:",
\r
235 /*"i ejaculates:",*/ "i elucidates:", "i emotes:", "i endorses:",
\r
236 "i enthuses:", "i entreats:", "i enunciates:", "i eulogizes:",
\r
237 "i exclaims:", "i execrates:", "i exhorts:", "i expatiates:",
\r
238 "i explains:", "i explicates:", "i explodes:", "i exposes:",
\r
239 "i exposits:", "i expostulates: ",
\r
240 "i expounds:", "i expresses:", "i extols:",
\r
241 "i exults:", "i fantasizes:", "i fibs:", "i filibusters:",
\r
242 "i flatters:", "i flutes:", "i fools:", "i free-associates:",
\r
243 "i fulminates:", "i gabbles:", "i gabs:", "i gasps:",
\r
244 "i giggles:", "i gossips:", "i gripes:", "i groans:", "i growls:",
\r
245 "i grunts:", "i guesses:", "i guffaws:", "i gushes:", "i hails:",
\r
246 "i hallucinates:", "i harangues:", "i harmonizes:", "i hectors:",
\r
247 "i hints:", "i hisses:", "i hollers:", "i honks:", "i hoots:",
\r
248 "i hosannas:", "i howls:", "i hums:", "i hypothecates:",
\r
249 "i hypothesizes:", "i imagines:", "i implies:", "i implores:",
\r
250 "i imprecates:", "i indicates:", "i infers:",
\r
251 "i informs everyone:", "i instructs:", "i interjects:",
\r
252 "i interposes:", "i intimates:", "i intones:", "i introspects:",
\r
253 "i inveighs:", "i jabbers:", "i japes:", "i jests:", "i jibes:",
\r
254 "i jives:", "i jokes:", "i joshes:", "i keens:", "i laments:",
\r
255 "i lauds:", "i laughs:", "i lectures:", "i lies:", "i lilts:",
\r
256 "i lisps:", "i maintains:", "i maledicts:", "i maunders:",
\r
257 "i meows:", "i mewls:", "i mimes:", "i minces:", "i moans:",
\r
258 "i moos:", "i mourns:", "i mouths:", "i mumbles:", "i murmurs:",
\r
259 "i muses:", "i mutters:", "i nags:", "i natters:", "i neighs:",
\r
260 "i notes:", "i nuncupates:", "i objurgates:", "i observes:",
\r
261 "i offers:", "i oinks:", "i opines:", "i orates:", "i orders:",
\r
262 "i panegyrizes:", "i pantomimes:", "i pants:", "i peals:",
\r
263 "i peeps:", "i perorates:", "i persuades:", "i petitions:",
\r
264 "i phonates:", "i pipes up:", "i pitches:", "i pleads:",
\r
265 "i points out:", "i pontificates:", "i postulates:", "i praises:",
\r
266 "i prates:", "i prattles:", "i preaches:", "i prescribes:",
\r
267 "i prevaricates:", "i proclaims:", "i projects:", "i pronounces:",
\r
268 "i proposes:", "i proscribes:", "i quacks:", "i queries:",
\r
269 "i questions:", "i quips:", "i quotes:", "i rages:", "i rambles:",
\r
270 "i rants:", "i raps:", "i rasps:", "i rattles:", "i raves:",
\r
271 "i reacts:", "i recites:", "i recommends:", "i records:",
\r
272 "i reiterates:", "i rejoins:", "i releases:", "i remarks:",
\r
273 "i reminisces:", "i remonstrates:", "i repeats:", "i replies:",
\r
274 "i reports:", "i reprimands:", "i reproaches:", "i reproves:",
\r
275 "i resounds:", "i responds:", "i retorts:", "i reveals:",
\r
276 "i reviles:", "i roars:", "i rumbles:", "i sanctions:",
\r
277 "i satirizes:", "i sauces:", "i scolds:", "i screams:",
\r
278 "i screeches:", "i semaphores:", "i sends:", "i sermonizes:",
\r
279 "i shrieks:", "i sibilates:", "i sighs:", "i signals:",
\r
280 "i signifies:", "i signs:", "i sings:", "i slurs:", "i snaps:",
\r
281 "i snarls:", "i sneezes:", "i snickers:", "i sniggers:",
\r
282 "i snivels:", "i snores:", "i snorts:", "i sobs:",
\r
283 "i soliloquizes:", "i sounds off:", "i sounds out:", "i speaks:",
\r
284 "i spews:", "i spits out:", "i splutters:", "i spoofs:",
\r
285 "i spouts:", "i sputters:", "i squalls:", "i squawks:",
\r
286 "i squeaks:", "i squeals:", "i stammers:", "i states:",
\r
287 "i stresses:", "i stutters:", "i submits:", "i suggests:",
\r
288 "i summarizes:", "i sums up:", "i swears:", "i talks:",
\r
289 "i tattles:", "i teases:", "i telegraphs:", "i testifies:",
\r
290 "i threatens:", "i thunders:", "i titters:", "i tongue-lashes:",
\r
291 "i toots:", "i transcribes:", "i transmits:", "i trills:",
\r
292 "i trumpets:", "i twaddles:", "i tweets:", "i twitters:",
\r
293 "i types:", "i upbraids:", "i urges:", "i utters:", "i ventures:",
\r
294 "i vibrates:", "i vilifies:", "i vituperates:", "i vocalizes:",
\r
295 "i vociferates:", "i voices:", "i waffles:", "i wails:",
\r
296 "i warbles:", "i warns:", "i weeps:", "i wheezes:", "i whimpers:",
\r
297 "i whines:", "i whinnies:", "i whistles:", "i wisecracks:",
\r
298 "i witnesses:", "i woofs:", "i writes:", "i yammers:", "i yawps:",
\r
299 "i yells:", "i yelps:", "i yodels:", "i yowls:", "i zings:",
\r
302 #define MAX_SPEECH 250
\r
304 void Speak(how, whom)
\r
307 static FILE *zipfile = NULL;
\r
308 static struct stat zipstat;
\r
309 char zipbuf[MAX_SPEECH + 1];
\r
310 static time_t lastShout = 0;
\r
316 if (strcmp(how, "shout") == 0) {
\r
317 now = time((time_t *) NULL);
\r
318 if (now - lastShout < 1*60) return;
\r
320 if (appData.zippyUseI) {
\r
321 how = swifties[(unsigned) random() %
\r
322 (sizeof(swifties)/sizeof(char *))];
\r
326 if (zipfile == NULL) {
\r
327 zipfile = fopen(appData.zippyLines, "r");
\r
328 if (zipfile == NULL) {
\r
329 DisplayFatalError("Can't open Zippy lines file", errno, 1);
\r
332 fstat(fileno(zipfile), &zipstat);
\r
336 fseek(zipfile, (unsigned) random() % zipstat.st_size, 0);
\r
339 } while (c != NULLCHAR && c != '^' && c != EOF);
\r
340 if (c == EOF) continue;
\r
341 while ((c = getc(zipfile)) == '\n') ;
\r
342 if (c == EOF) continue;
\r
347 /* Don't use ics_prefix; we need to let FICS expand the alias i -> it,
\r
348 but use the real command "i" on ICC */
\r
349 strcpy(zipbuf, how);
\r
350 strcat(zipbuf, " ");
\r
351 if (whom != NULL) {
\r
352 strcat(zipbuf, whom);
\r
353 strcat(zipbuf, " ");
\r
355 speechlen = strlen(zipbuf);
\r
356 p = zipbuf + speechlen;
\r
358 while (++speechlen < MAX_SPEECH) {
\r
359 if (c == NULLCHAR || c == '^') {
\r
364 } else if (c == '\n') {
\r
368 } while (c == ' ');
\r
369 } else if (c == EOF) {
\r
376 /* Tried to say something too long, or junk at the end of the
\r
377 file. Try something else. */
\r
378 Speak(how, whom); /* tail recursion */
\r
381 int ZippyCalled(str)
\r
384 return ics_handle[0] != NULLCHAR && StrCaseStr(str, ics_handle) != NULL;
\r
387 static char opp_name[128][32];
\r
388 static int num_opps=0;
\r
390 extern ColorClass curColor;
\r
392 static void SetCurColor( ColorClass color )
\r
397 static void ColorizeEx( ColorClass color, int cont )
\r
399 if( appData.colorize ) {
\r
400 Colorize( color, cont );
\r
401 SetCurColor( color );
\r
405 int ZippyControl(buf, i)
\r
410 char reply[MSG_SIZ];
\r
413 #include "trivia.c"
\r
416 /* Possibly reject Crafty as opponent */
\r
417 if (appData.zippyPlay && appData.zippyNoplayCrafty && forwardMostMove < 4
\r
418 && looking_at(buf, i, "* kibitzes: Hello from Crafty"))
\r
420 player = StripHighlightAndTitle(star_match[0]);
\r
421 if ((gameMode == IcsPlayingWhite &&
\r
422 StrCaseCmp(player, gameInfo.black) == 0) ||
\r
423 (gameMode == IcsPlayingBlack &&
\r
424 StrCaseCmp(player, gameInfo.white) == 0)) {
\r
426 sprintf(reply, "%ssay This computer does not play Crafty clones\n%sabort\n%s+noplay %s\n",
\r
427 ics_prefix, ics_prefix, ics_prefix, player);
\r
433 /* If this is a computer, save the name. Then later, once the */
\r
434 /* game is really started, we will send the "computer" notice to */
\r
436 if (appData.zippyPlay &&
\r
437 looking_at(buf, i, "* is in the computer list")) {
\r
439 for (i=0;i<num_opps;i++)
\r
440 if (!strcmp(opp_name[i],star_match[0])) break;
\r
441 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[0]);
\r
443 if (appData.zippyPlay && looking_at(buf, i, "* * is a computer *")) {
\r
445 for (i=0;i<num_opps;i++)
\r
446 if (!strcmp(opp_name[i],star_match[1])) break;
\r
447 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[1]);
\r
450 /* Tells and says */
\r
451 if (appData.zippyPlay &&
\r
452 (looking_at(buf, i, "* offers to be your bughouse partner") ||
\r
453 looking_at(buf, i, "* tells you: [automatic message] I chose you"))) {
\r
454 player = StripHighlightAndTitle(star_match[0]);
\r
455 if (appData.zippyBughouse > 1 && first.initDone) {
\r
456 sprintf(reply, "%spartner %s\n", ics_prefix, player);
\r
458 if (strcmp(zippyPartner, player) != 0) {
\r
459 strcpy(zippyPartner, player);
\r
460 SendToProgram(reply + strlen(ics_prefix), &first);
\r
462 } else if (appData.zippyBughouse > 0) {
\r
463 sprintf(reply, "%sdecline %s\n", ics_prefix, player);
\r
466 sprintf(reply, "%stell %s This computer cannot play bughouse\n",
\r
467 ics_prefix, player);
\r
473 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
474 looking_at(buf, i, "* agrees to be your partner")) {
\r
475 player = StripHighlightAndTitle(star_match[0]);
\r
476 sprintf(reply, "partner %s\n", player);
\r
477 if (strcmp(zippyPartner, player) != 0) {
\r
478 strcpy(zippyPartner, player);
\r
479 SendToProgram(reply, &first);
\r
484 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
485 (looking_at(buf, i, "are no longer *'s partner") ||
\r
487 "* tells you: [automatic message] I'm no longer your"))) {
\r
488 player = StripHighlightAndTitle(star_match[0]);
\r
489 if (strcmp(zippyPartner, player) == 0) {
\r
490 zippyPartner[0] = NULLCHAR;
\r
491 SendToProgram("partner\n", &first);
\r
496 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
497 (looking_at(buf, i, "no longer have a bughouse partner") ||
\r
498 looking_at(buf, i, "partner has disconnected") ||
\r
499 looking_at(buf, i, "partner has just chosen a new partner"))) {
\r
500 zippyPartner[0] = NULLCHAR;
\r
501 SendToProgram("partner\n", &first);
\r
505 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
506 looking_at(buf, i, "* (your partner) tells you: *")) {
\r
507 /* This pattern works on FICS but not ICC */
\r
508 player = StripHighlightAndTitle(star_match[0]);
\r
509 if (strcmp(zippyPartner, player) != 0) {
\r
510 strcpy(zippyPartner, player);
\r
511 sprintf(reply, "partner %s\n", player);
\r
512 SendToProgram(reply, &first);
\r
514 sprintf(reply, "ptell %s\n", star_match[1]);
\r
515 SendToProgram(reply, &first);
\r
519 if (looking_at(buf, i, "* tells you: *") ||
\r
520 looking_at(buf, i, "* says: *"))
\r
522 player = StripHighlightAndTitle(star_match[0]);
\r
523 if (appData.zippyPassword[0] != NULLCHAR &&
\r
524 strncmp(star_match[1], appData.zippyPassword,
\r
525 strlen(appData.zippyPassword)) == 0) {
\r
526 p = star_match[1] + strlen(appData.zippyPassword);
\r
527 while (*p == ' ') p++;
\r
530 } else if (appData.zippyPassword2[0] != NULLCHAR && first.initDone &&
\r
531 strncmp(star_match[1], appData.zippyPassword2,
\r
532 strlen(appData.zippyPassword2)) == 0) {
\r
533 p = star_match[1] + strlen(appData.zippyPassword2);
\r
534 while (*p == ' ') p++;
\r
535 SendToProgram(p, &first);
\r
536 SendToProgram("\n", &first);
\r
537 } else if (appData.zippyWrongPassword[0] != NULLCHAR &&
\r
538 strncmp(star_match[1], appData.zippyWrongPassword,
\r
539 strlen(appData.zippyWrongPassword)) == 0) {
\r
540 p = star_match[1] + strlen(appData.zippyWrongPassword);
\r
541 while (*p == ' ') p++;
\r
542 sprintf(reply, "wrong %s\n", player);
\r
544 } else if (appData.zippyBughouse && first.initDone &&
\r
545 strcmp(player, zippyPartner) == 0) {
\r
546 SendToProgram("ptell ", &first);
\r
547 SendToProgram(star_match[1], &first);
\r
548 SendToProgram("\n", &first);
\r
549 } else if (strncmp(star_match[1], HI, 6) == 0) {
\r
550 extern char* programVersion;
\r
551 sprintf(reply, "%stell %s %s\n",
\r
552 ics_prefix, player, programVersion);
\r
554 } else if (strncmp(star_match[1], "W0W!! ", 6) == 0) {
\r
555 extern char* programVersion;
\r
556 sprintf(reply, "%stell %s %s\n", ics_prefix,
\r
557 player, programVersion);
\r
559 } else if (appData.zippyTalk && (((unsigned) random() % 10) < 9)) {
\r
560 if (strcmp(player, ics_handle) != 0) {
\r
561 Speak("tell", player);
\r
565 ColorizeEx( ColorTell, FALSE );
\r
570 if( appData.colorize && looking_at(buf, i, "* (*) seeking") ) {
\r
571 ColorizeEx(ColorSeek, FALSE);
\r
575 if (looking_at(buf, i, "* spoofs you:")) {
\r
576 player = StripHighlightAndTitle(star_match[0]);
\r
577 sprintf(reply, "spoofedby %s\n", player);
\r
584 int ZippyConverse(buf, i)
\r
588 static char lastgreet[MSG_SIZ];
\r
589 char reply[MSG_SIZ];
\r
592 /* Shouts and emotes */
\r
593 if (looking_at(buf, i, "--> * *") ||
\r
594 looking_at(buf, i, "* shouts: *"))
\r
596 if (appData.zippyTalk) {
\r
597 char *player = StripHighlightAndTitle(star_match[0]);
\r
598 if (strcmp(player, ics_handle) == 0) {
\r
600 } else if (appData.zippyPinhead[0] != NULLCHAR &&
\r
601 StrCaseStr(star_match[1], appData.zippyPinhead) != NULL) {
\r
602 sprintf(reply, "insult %s\n", player);
\r
604 } else if (ZippyCalled(star_match[1])) {
\r
605 Speak("shout", NULL);
\r
609 ColorizeEx(ColorShout, FALSE);
\r
614 if (looking_at(buf, i, "* kibitzes: *")) {
\r
615 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
\r
616 char *player = StripHighlightAndTitle(star_match[0]);
\r
617 if (strcmp(player, ics_handle) != 0) {
\r
618 Speak("kibitz", NULL);
\r
622 ColorizeEx(ColorKibitz, FALSE);
\r
627 if (looking_at(buf, i, "* whispers: *")) {
\r
628 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
\r
629 char *player = StripHighlightAndTitle(star_match[0]);
\r
630 if (strcmp(player, ics_handle) != 0) {
\r
631 Speak("whisper", NULL);
\r
635 ColorizeEx(ColorKibitz, FALSE);
\r
641 if ((looking_at(buf, i, ". * (*:*): *") && isdigit(star_match[1][0])) ||
\r
642 looking_at(buf, i, ". * at *:*: *")) {
\r
643 if (appData.zippyTalk) {
\r
645 char *player = StripHighlightAndTitle(star_match[0]);
\r
647 if (strcmp(player, ics_handle) != 0) {
\r
648 if (((unsigned) random() % 10) < 9)
\r
649 Speak("message", player);
\r
650 f = fopen("zippy.messagelog", "a");
\r
651 fprintf(f, "%s (%s:%s): %s\n", player,
\r
652 star_match[1], star_match[2], star_match[3]);
\r
659 /* Channel tells */
\r
661 if (looking_at(buf, i, "*(*: *")) {
\r
664 if (star_match[0][0] == NULLCHAR ||
\r
665 strchr(star_match[0], ' ') ||
\r
666 strchr(star_match[1], ' ')) {
\r
667 /* Oops, did not want to match this; probably a message */
\r
671 if (appData.zippyTalk) {
\r
672 player = StripHighlightAndTitle(star_match[0]);
\r
673 channel = strrchr(star_match[1], '(');
\r
674 if (channel == NULL) {
\r
675 channel = star_match[1];
\r
679 channel[strlen(channel)-1] = NULLCHAR;
\r
681 /* Always tell to the channel (probability 90%) */
\r
682 if (strcmp(player, ics_handle) != 0 &&
\r
683 ((unsigned) random() % 10) < 9) {
\r
684 Speak("tell", channel);
\r
687 /* Tell to the channel only if someone mentions our name */
\r
688 if (ZippyCalled(star_match[2])) {
\r
689 Speak("tell", channel);
\r
693 ColorizeEx( atoi(channel) == 1 ? ColorChannel1 : ColorChannel, FALSE );
\r
698 if (!appData.zippyTalk) return FALSE;
\r
700 if ((looking_at(buf, i, "You have * message") &&
\r
701 atoi(star_match[0]) != 0) ||
\r
702 looking_at(buf, i, "* has left a message for you") ||
\r
703 looking_at(buf, i, "* just sent you a message")) {
\r
704 sprintf(reply, "%smessages\n%sclearmessages *\n",
\r
705 ics_prefix, ics_prefix);
\r
710 if (looking_at(buf, i, "Notification: * has arrived")) {
\r
711 if (((unsigned) random() % 3) == 0) {
\r
712 char *player = StripHighlightAndTitle(star_match[0]);
\r
713 strcpy(lastgreet, player);
\r
714 sprintf(reply, "greet %s\n", player);
\r
716 Speak("tell", player);
\r
720 if (looking_at(buf, i, "Notification: * has departed")) {
\r
721 if (((unsigned) random() % 3) == 0) {
\r
722 char *player = StripHighlightAndTitle(star_match[0]);
\r
723 sprintf(reply, "farewell %s\n", player);
\r
728 if (looking_at(buf, i, "Not sent -- * is censoring you")) {
\r
729 char *player = StripHighlightAndTitle(star_match[0]);
\r
730 if (strcmp(player, lastgreet) == 0) {
\r
731 sprintf(reply, "%s-notify %s\n", ics_prefix, player);
\r
736 if (looking_at(buf, i, "command is currently turned off")) {
\r
737 appData.zippyUseI = 0;
\r
743 void ZippyGameStart(white, black)
\r
744 char *white, *black;
\r
746 if (!first.initDone) {
\r
747 /* Game is starting prematurely. We can't deal with this */
\r
748 SendToICS(ics_prefix);
\r
749 SendToICS("abort\n");
\r
750 SendToICS(ics_prefix);
\r
751 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
755 if (appData.zippyGameStart[0] != NULLCHAR) {
\r
756 SendToICS(appData.zippyGameStart);
\r
761 void ZippyGameEnd(result, resultDetails)
\r
763 char *resultDetails;
\r
765 if (appData.zippyAcceptOnly[0] == NULLCHAR &&
\r
766 appData.zippyGameEnd[0] != NULLCHAR) {
\r
767 SendToICS(appData.zippyGameEnd);
\r
770 zippyLastGameEnd = time(0);
\r
771 if(forwardMostMove < appData.zippyShortGame)
\r
772 strcpy(zippyOffender, zippyLastOpp); else zippyOffender[0] = 0; // [HGM] aborter
\r
776 * Routines to implement Zippy playing chess
\r
779 void ZippyHandleChallenge(srated, swild, sbase, sincrement, opponent)
\r
780 char *srated, *swild, *sbase, *sincrement, *opponent;
\r
783 int base, increment, i=0;
\r
785 VariantClass variant;
\r
789 variant = StringToVariant(swild);
\r
790 varname = VariantName(variant);
\r
791 base = atoi(sbase);
\r
792 increment = atoi(sincrement);
\r
794 /* [DM] If icsAnalyzeEngine active we don't accept automatic games */
\r
795 if (appData.icsActive && appData.icsEngineAnalyze) return;
\r
797 /* If desired, you can insert more code here to decline matches
\r
798 based on rated, variant, base, and increment, but it is
\r
799 easier to use the ICS formula feature instead. */
\r
801 if (variant == VariantLoadable) {
\r
803 "%stell %s This computer can't play wild type %s\n%sdecline %s\n",
\r
804 ics_prefix, opponent, swild, ics_prefix, opponent);
\r
808 if (StrStr(appData.zippyVariants, varname) == NULL ||
\r
809 ((i=first.protocolVersion) != 1 && StrStr(first.variants, varname) == NULL) /* [HGM] zippyvar */
\r
812 "%stell %s This computer can't play %s [%s], only %s\n%sdecline %s\n",
\r
813 ics_prefix, opponent, swild, varname,
\r
814 i ? first.variants : appData.zippyVariants, /* [HGM] zippyvar */
\r
815 ics_prefix, opponent);
\r
820 /* Are we blocking match requests from all but one person? */
\r
821 if (appData.zippyAcceptOnly[0] != NULLCHAR &&
\r
822 StrCaseCmp(opponent, appData.zippyAcceptOnly)) {
\r
823 /* Yes, and this isn't him. Ignore challenge. */
\r
827 /* Too many consecutive games with same opponent? If so, make him
\r
828 wait until someone else has played or a timeout has elapsed. */
\r
829 if (appData.zippyMaxGames &&
\r
830 strcmp(opponent, zippyLastOpp) == 0 &&
\r
831 zippyConsecGames >= appData.zippyMaxGames &&
\r
832 difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
\r
833 sprintf(buf, "%stell %s Sorry, you have just played %d consecutive games against %s. To give others a chance, please wait %d seconds or until someone else has played.\n%sdecline %s\n",
\r
834 ics_prefix, opponent, zippyConsecGames, ics_handle,
\r
835 appData.zippyReplayTimeout, ics_prefix, opponent);
\r
840 /* [HGM] aborter: opponent is cheater that aborts games he doesn't like on first move. Make him wait */
\r
841 if (strcmp(opponent, zippyOffender) == 0 &&
\r
842 difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
\r
843 sprintf(buf, "%stell %s Sorry, your previous game against %s was rather short. "
\r
844 " It will wait %d seconds to see if a tougher opponent comes along.\n%sdecline %s\n",
\r
845 ics_prefix, opponent, ics_handle,
\r
846 appData.zippyReplayTimeout, ics_prefix, opponent);
\r
851 /* Engine not yet initialized or still thinking about last game? */
\r
852 if (!first.initDone || first.lastPing != first.lastPong) {
\r
853 sprintf(buf, "%stell %s I'm not quite ready for a new game yet; try again soon.\n%sdecline %s\n",
\r
854 ics_prefix, opponent, ics_prefix, opponent);
\r
859 sprintf(buf, "%saccept %s\n", ics_prefix, opponent);
\r
861 if (appData.zippyTalk) {
\r
862 Speak("tell", opponent);
\r
867 /* Accept matches */
\r
868 int ZippyMatch(buf, i)
\r
872 if (looking_at(buf, i, "* * match * * requested with * (*)")) {
\r
874 ZippyHandleChallenge(star_match[0], star_match[1],
\r
875 star_match[2], star_match[3],
\r
876 StripHighlightAndTitle(star_match[4]));
\r
880 /* Old FICS 0-increment form */
\r
881 if (looking_at(buf, i, "* * match * requested with * (*)")) {
\r
883 ZippyHandleChallenge(star_match[0], star_match[1],
\r
884 star_match[2], "0",
\r
885 StripHighlightAndTitle(star_match[3]));
\r
889 if (looking_at(buf, i,
\r
890 "* has made an alternate proposal of * * match * *.")) {
\r
892 ZippyHandleChallenge(star_match[1], star_match[2],
\r
893 star_match[3], star_match[4],
\r
894 StripHighlightAndTitle(star_match[0]));
\r
898 /* FICS wild/nonstandard forms */
\r
899 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * Loaded from *")) {
\r
900 /* note: star_match[2] can include "[white] " or "[black] "
\r
901 before our own name. */
\r
902 if(star_match[8] == NULL || star_match[8][0] == 0) // [HGM] chessd: open-source ICS has file on next line
\r
903 ZippyHandleChallenge(star_match[4], star_match[5],
\r
904 star_match[6], star_match[7], StripHighlightAndTitle(star_match[0]));
\r
905 else ZippyHandleChallenge(star_match[4], star_match[8],
\r
906 star_match[6], star_match[7],
\r
907 StripHighlightAndTitle(star_match[0]));
\r
911 if (looking_at(buf, i,
\r
912 "Challenge: * (*) *(*) * * * * : * * Loaded from *")) {
\r
913 /* note: star_match[2] can include "[white] " or "[black] "
\r
914 before our own name. */
\r
915 ZippyHandleChallenge(star_match[4], star_match[10],
\r
916 star_match[8], star_match[9],
\r
917 StripHighlightAndTitle(star_match[0]));
\r
921 /* Regular forms */
\r
922 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * : * *") |
\r
923 looking_at(buf, i, "Challenge: * (*) *(*) * * * * * *")) {
\r
924 /* note: star_match[2] can include "[white] " or "[black] "
\r
925 before our own name. */
\r
926 ZippyHandleChallenge(star_match[4], star_match[5],
\r
927 star_match[8], star_match[9],
\r
928 StripHighlightAndTitle(star_match[0]));
\r
932 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * *")) {
\r
933 /* note: star_match[2] can include "[white] " or "[black] "
\r
934 before our own name. */
\r
935 ZippyHandleChallenge(star_match[4], star_match[5],
\r
936 star_match[6], star_match[7],
\r
937 StripHighlightAndTitle(star_match[0]));
\r
942 if (ics_type == ICS_ICC) { // [DM]
\r
943 if (looking_at(buf, i, "Your opponent offers you a draw")) {
\r
944 if (first.sendDrawOffers && first.initDone)
\r
945 SendToProgram("draw\n", &first);
\r
949 if (looking_at(buf, i, "offers you a draw")) {
\r
950 if (first.sendDrawOffers && first.initDone) {
\r
951 SendToProgram("draw\n", &first);
\r
957 if (looking_at(buf, i, "requests that the game be aborted") ||
\r
958 looking_at(buf, i, "would like to abort")) {
\r
959 if (appData.zippyAbort ||
\r
960 (gameMode == IcsPlayingWhite && whiteTimeRemaining < 0) ||
\r
961 (gameMode == IcsPlayingBlack && blackTimeRemaining < 0)) {
\r
962 SendToICS(ics_prefix);
\r
963 SendToICS("abort\n");
\r
965 SendToICS(ics_prefix);
\r
966 if (appData.zippyTalk)
\r
967 SendToICS("say Whoa no! I am having FUN!!\n");
\r
969 SendToICS("say Sorry, this computer doesn't accept aborts.\n");
\r
974 if (looking_at(buf, i, "requests adjournment") ||
\r
975 looking_at(buf, i, "would like to adjourn")) {
\r
976 if (appData.zippyAdjourn) {
\r
977 SendToICS(ics_prefix);
\r
978 SendToICS("adjourn\n");
\r
980 SendToICS(ics_prefix);
\r
981 if (appData.zippyTalk)
\r
982 SendToICS("say Whoa no! I am having FUN playing NOW!!\n");
\r
984 SendToICS("say Sorry, this computer doesn't accept adjourns.\n");
\r
992 /* Initialize chess program with data from the first board
\r
993 * of a new or resumed game.
\r
995 void ZippyFirstBoard(moveNum, basetime, increment)
\r
996 int moveNum, basetime, increment;
\r
1000 char *opp = (gameMode==IcsPlayingWhite ? gameInfo.black : gameInfo.white);
\r
1001 Boolean sentPos = FALSE;
\r
1002 char *bookHit = NULL; // [HGM] book
\r
1004 if (!first.initDone) {
\r
1005 /* Game is starting prematurely. We can't deal with this */
\r
1006 SendToICS(ics_prefix);
\r
1007 SendToICS("abort\n");
\r
1008 SendToICS(ics_prefix);
\r
1009 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
1013 /* Send the variant command if needed */
\r
1014 if (gameInfo.variant != VariantNormal) {
\r
1015 sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
\r
1016 SendToProgram(buf, &first);
\r
1019 if ((startedFromSetupPosition && moveNum == 0) ||
\r
1020 (!appData.getMoveList && moveNum > 0)) {
\r
1021 SendToProgram("force\n", &first);
\r
1022 SendBoard(&first, moveNum);
\r
1026 sprintf(buf, "level 0 %d %d\n", basetime, increment);
\r
1027 SendToProgram(buf, &first);
\r
1029 /* Count consecutive games from one opponent */
\r
1030 if (strcmp(opp, zippyLastOpp) == 0) {
\r
1031 zippyConsecGames++;
\r
1033 zippyConsecGames = 1;
\r
1034 strcpy(zippyLastOpp, opp);
\r
1037 /* Send the "computer" command if the opponent is in the list
\r
1038 we've been gathering. */
\r
1039 for (w=0; w<num_opps; w++) {
\r
1040 if (!strcmp(opp_name[w], opp)) {
\r
1041 SendToProgram(first.computerString, &first);
\r
1046 /* Ratings might be < 0 which means "we haven't seen a ratings
\r
1047 message from ICS." Send 0 in that case */
\r
1048 w = (gameInfo.whiteRating >= 0) ? gameInfo.whiteRating : 0;
\r
1049 b = (gameInfo.blackRating >= 0) ? gameInfo.blackRating : 0;
\r
1051 firstMove = FALSE;
\r
1052 if (gameMode == IcsPlayingWhite) {
\r
1053 if (first.sendName) {
\r
1054 sprintf(buf, "name %s\n", gameInfo.black);
\r
1055 SendToProgram(buf, &first);
\r
1057 strcpy(ics_handle, gameInfo.white);
\r
1058 sprintf(buf, "rating %d %d\n", w, b);
\r
1059 SendToProgram(buf, &first);
\r
1061 /* Position sent above, engine is in force mode */
\r
1062 if (WhiteOnMove(moveNum)) {
\r
1063 /* Engine is on move now */
\r
1064 if (first.sendTime) {
\r
1065 if (first.useColors) {
\r
1066 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1067 SendTimeRemaining(&first, TRUE);
\r
1068 SendToProgram("white\n", &first);
\r
1070 SendTimeRemaining(&first, TRUE);
\r
1073 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1075 /* Engine's opponent is on move now */
\r
1076 if (first.usePlayother) {
\r
1077 if (first.sendTime) {
\r
1078 SendTimeRemaining(&first, TRUE);
\r
1080 SendToProgram("playother\n", &first);
\r
1082 /* Need to send a "go" after opponent moves */
\r
1087 /* Position not sent above, move list might be sent later */
\r
1088 if (moveNum == 0) {
\r
1089 /* No move list coming; at start of game */
\r
1090 if (first.sendTime) {
\r
1091 if (first.useColors) {
\r
1092 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1093 SendTimeRemaining(&first, TRUE);
\r
1094 SendToProgram("white\n", &first);
\r
1096 SendTimeRemaining(&first, TRUE);
\r
1099 // SendToProgram("go\n", &first);
\r
1100 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1103 } else if (gameMode == IcsPlayingBlack) {
\r
1104 if (first.sendName) {
\r
1105 sprintf(buf, "name %s\n", gameInfo.white);
\r
1106 SendToProgram(buf, &first);
\r
1108 strcpy(ics_handle, gameInfo.black);
\r
1109 sprintf(buf, "rating %d %d\n", b, w);
\r
1110 SendToProgram(buf, &first);
\r
1112 /* Position sent above, engine is in force mode */
\r
1113 if (!WhiteOnMove(moveNum)) {
\r
1114 /* Engine is on move now */
\r
1115 if (first.sendTime) {
\r
1116 if (first.useColors) {
\r
1117 SendToProgram("white\n", &first); /*gnu kludge*/
\r
1118 SendTimeRemaining(&first, FALSE);
\r
1119 SendToProgram("black\n", &first);
\r
1121 SendTimeRemaining(&first, FALSE);
\r
1124 // SendToProgram("go\n", &first);
\r
1125 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1127 /* Engine's opponent is on move now */
\r
1128 if (first.usePlayother) {
\r
1129 if (first.sendTime) {
\r
1130 SendTimeRemaining(&first, FALSE);
\r
1132 SendToProgram("playother\n", &first);
\r
1134 /* Need to send a "go" after opponent moves */
\r
1139 /* Position not sent above, move list might be sent later */
\r
1140 /* Nothing needs to be done here */
\r
1144 if(bookHit) { // [HGM] book: simulate book reply
\r
1145 static char bookMove[MSG_SIZ]; // a bit generous?
\r
1147 programStats.depth = programStats.nodes = programStats.time =
\r
1148 programStats.score = programStats.got_only_move = 0;
\r
1149 sprintf(programStats.movelist, "%s (xbook)", bookHit);
\r
1151 strcpy(bookMove, "move ");
\r
1152 strcat(bookMove, bookHit);
\r
1153 HandleMachineMove(bookMove, &first);
\r
1159 ZippyHoldings(white_holding, black_holding, new_piece)
\r
1160 char *white_holding, *black_holding, *new_piece;
\r
1162 char buf[MSG_SIZ];
\r
1163 if (gameMode != IcsPlayingBlack && gameMode != IcsPlayingWhite) return;
\r
1164 sprintf(buf, "holding [%s] [%s] %s\n",
\r
1165 white_holding, black_holding, new_piece);
\r
1166 SendToProgram(buf, &first);
\r