2 * zippy.c -- Implements Zippy the Pinhead chess player on ICS in XBoard
\r
3 * $Id: zippy.c,v 2.2 2003/11/25 05:25:20 mann Exp $
\r
5 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
6 * Massachusetts. Enhancements Copyright
\r
7 * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software
\r
10 * The following terms apply to Digital Equipment Corporation's copyright
\r
11 * interest in XBoard:
\r
12 * ------------------------------------------------------------------------
\r
13 * All Rights Reserved
\r
15 * Permission to use, copy, modify, and distribute this software and its
\r
16 * documentation for any purpose and without fee is hereby granted,
\r
17 * provided that the above copyright notice appear in all copies and that
\r
18 * both that copyright notice and this permission notice appear in
\r
19 * supporting documentation, and that the name of Digital not be
\r
20 * used in advertising or publicity pertaining to distribution of the
\r
21 * software without specific, written prior permission.
\r
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
30 * ------------------------------------------------------------------------
\r
32 * The following terms apply to the enhanced version of XBoard
\r
33 * distributed by the Free Software Foundation:
\r
34 * ------------------------------------------------------------------------
\r
36 * GNU XBoard is free software: you can redistribute it and/or modify
\r
37 * it under the terms of the GNU General Public License as published by
\r
38 * the Free Software Foundation, either version 3 of the License, or (at
\r
39 * your option) any later version.
\r
41 * GNU XBoard is distributed in the hope that it will be useful, but
\r
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
44 * General Public License for more details.
\r
46 * You should have received a copy of the GNU General Public License
\r
47 * along with this program. If not, see http://www.gnu.org/licenses/.
\r
49 *------------------------------------------------------------------------
\r
50 ** See the file ChangeLog for a revision history. */
\r
56 #include <sys/types.h>
\r
57 #include <sys/stat.h>
\r
61 # include <stdlib.h>
\r
62 # include <string.h>
\r
63 #else /* not STDC_HEADERS */
\r
64 extern char *getenv();
\r
66 # include <string.h>
\r
67 # else /* not HAVE_STRING_H */
\r
68 # include <strings.h>
\r
69 # endif /* not HAVE_STRING_H */
\r
70 #endif /* not STDC_HEADERS */
\r
72 #if TIME_WITH_SYS_TIME
\r
73 # include <sys/time.h>
\r
76 # if HAVE_SYS_TIME_H
\r
77 # include <sys/time.h>
\r
85 # include <unistd.h>
\r
90 #include "frontend.h"
\r
91 #include "backend.h"
\r
92 #include "backendz.h"
\r
94 char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book
\r
95 void HandleMachineMove P((char *message, ChessProgramState *cps));
\r
97 static char zippyPartner[MSG_SIZ];
\r
98 static char zippyLastOpp[MSG_SIZ];
\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
774 * Routines to implement Zippy playing chess
\r
777 void ZippyHandleChallenge(srated, swild, sbase, sincrement, opponent)
\r
778 char *srated, *swild, *sbase, *sincrement, *opponent;
\r
781 int base, increment, i=0;
\r
783 VariantClass variant;
\r
787 variant = StringToVariant(swild);
\r
788 varname = VariantName(variant);
\r
789 base = atoi(sbase);
\r
790 increment = atoi(sincrement);
\r
792 /* [DM] If icsAnalyzeEngine active we don't accept automatic games */
\r
793 if (appData.icsActive && appData.icsEngineAnalyze) return;
\r
795 /* If desired, you can insert more code here to decline matches
\r
796 based on rated, variant, base, and increment, but it is
\r
797 easier to use the ICS formula feature instead. */
\r
799 if (variant == VariantLoadable) {
\r
801 "%stell %s This computer can't play wild type %s\n%sdecline %s\n",
\r
802 ics_prefix, opponent, swild, ics_prefix, opponent);
\r
806 if (StrStr(appData.zippyVariants, varname) == NULL ||
\r
807 ((i=first.protocolVersion) != 1 && StrStr(first.variants, varname) == NULL) /* [HGM] zippyvar */
\r
810 "%stell %s This computer can't play %s [%s], only %s\n%sdecline %s\n",
\r
811 ics_prefix, opponent, swild, varname,
\r
812 i ? first.variants : appData.zippyVariants, /* [HGM] zippyvar */
\r
813 ics_prefix, opponent);
\r
818 /* Are we blocking match requests from all but one person? */
\r
819 if (appData.zippyAcceptOnly[0] != NULLCHAR &&
\r
820 StrCaseCmp(opponent, appData.zippyAcceptOnly)) {
\r
821 /* Yes, and this isn't him. Ignore challenge. */
\r
825 /* Too many consecutive games with same opponent? If so, make him
\r
826 wait until someone else has played or a timeout has elapsed. */
\r
827 if (appData.zippyMaxGames &&
\r
828 strcmp(opponent, zippyLastOpp) == 0 &&
\r
829 zippyConsecGames >= appData.zippyMaxGames &&
\r
830 difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
\r
831 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
832 ics_prefix, opponent, zippyConsecGames, ics_handle,
\r
833 appData.zippyReplayTimeout, ics_prefix, opponent);
\r
838 /* Engine not yet initialized or still thinking about last game? */
\r
839 if (!first.initDone || first.lastPing != first.lastPong) {
\r
840 sprintf(buf, "%stell %s I'm not quite ready for a new game yet; try again soon.\n%sdecline %s\n",
\r
841 ics_prefix, opponent, ics_prefix, opponent);
\r
846 sprintf(buf, "%saccept %s\n", ics_prefix, opponent);
\r
848 if (appData.zippyTalk) {
\r
849 Speak("tell", opponent);
\r
854 /* Accept matches */
\r
855 int ZippyMatch(buf, i)
\r
859 if (looking_at(buf, i, "* * match * * requested with * (*)")) {
\r
861 ZippyHandleChallenge(star_match[0], star_match[1],
\r
862 star_match[2], star_match[3],
\r
863 StripHighlightAndTitle(star_match[4]));
\r
867 /* Old FICS 0-increment form */
\r
868 if (looking_at(buf, i, "* * match * requested with * (*)")) {
\r
870 ZippyHandleChallenge(star_match[0], star_match[1],
\r
871 star_match[2], "0",
\r
872 StripHighlightAndTitle(star_match[3]));
\r
876 if (looking_at(buf, i,
\r
877 "* has made an alternate proposal of * * match * *.")) {
\r
879 ZippyHandleChallenge(star_match[1], star_match[2],
\r
880 star_match[3], star_match[4],
\r
881 StripHighlightAndTitle(star_match[0]));
\r
885 /* FICS wild/nonstandard forms */
\r
886 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * Loaded from *")) {
\r
887 /* note: star_match[2] can include "[white] " or "[black] "
\r
888 before our own name. */
\r
889 if(star_match[8] == NULL || star_match[8][0] == 0) // [HGM] chessd: open-source ICS has file on next line
\r
890 ZippyHandleChallenge(star_match[4], star_match[5],
\r
891 star_match[6], star_match[7], StripHighlightAndTitle(star_match[0]));
\r
892 else ZippyHandleChallenge(star_match[4], star_match[8],
\r
893 star_match[6], star_match[7],
\r
894 StripHighlightAndTitle(star_match[0]));
\r
898 if (looking_at(buf, i,
\r
899 "Challenge: * (*) *(*) * * * * : * * Loaded from *")) {
\r
900 /* note: star_match[2] can include "[white] " or "[black] "
\r
901 before our own name. */
\r
902 ZippyHandleChallenge(star_match[4], star_match[10],
\r
903 star_match[8], star_match[9],
\r
904 StripHighlightAndTitle(star_match[0]));
\r
908 /* Regular forms */
\r
909 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * : * *") |
\r
910 looking_at(buf, i, "Challenge: * (*) *(*) * * * * * *")) {
\r
911 /* note: star_match[2] can include "[white] " or "[black] "
\r
912 before our own name. */
\r
913 ZippyHandleChallenge(star_match[4], star_match[5],
\r
914 star_match[8], star_match[9],
\r
915 StripHighlightAndTitle(star_match[0]));
\r
919 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * *")) {
\r
920 /* note: star_match[2] can include "[white] " or "[black] "
\r
921 before our own name. */
\r
922 ZippyHandleChallenge(star_match[4], star_match[5],
\r
923 star_match[6], star_match[7],
\r
924 StripHighlightAndTitle(star_match[0]));
\r
929 if (ics_type == ICS_ICC) { // [DM]
\r
930 if (looking_at(buf, i, "Your opponent offers you a draw")) {
\r
931 if (first.sendDrawOffers && first.initDone)
\r
932 SendToProgram("draw\n", &first);
\r
936 if (looking_at(buf, i, "offers you a draw")) {
\r
937 if (first.sendDrawOffers && first.initDone) {
\r
938 SendToProgram("draw\n", &first);
\r
944 if (looking_at(buf, i, "requests that the game be aborted") ||
\r
945 looking_at(buf, i, "would like to abort")) {
\r
946 if (appData.zippyAbort ||
\r
947 (gameMode == IcsPlayingWhite && whiteTimeRemaining < 0) ||
\r
948 (gameMode == IcsPlayingBlack && blackTimeRemaining < 0)) {
\r
949 SendToICS(ics_prefix);
\r
950 SendToICS("abort\n");
\r
952 SendToICS(ics_prefix);
\r
953 if (appData.zippyTalk)
\r
954 SendToICS("say Whoa no! I am having FUN!!\n");
\r
956 SendToICS("say Sorry, this computer doesn't accept aborts.\n");
\r
961 if (looking_at(buf, i, "requests adjournment") ||
\r
962 looking_at(buf, i, "would like to adjourn")) {
\r
963 if (appData.zippyAdjourn) {
\r
964 SendToICS(ics_prefix);
\r
965 SendToICS("adjourn\n");
\r
967 SendToICS(ics_prefix);
\r
968 if (appData.zippyTalk)
\r
969 SendToICS("say Whoa no! I am having FUN playing NOW!!\n");
\r
971 SendToICS("say Sorry, this computer doesn't accept adjourns.\n");
\r
979 /* Initialize chess program with data from the first board
\r
980 * of a new or resumed game.
\r
982 void ZippyFirstBoard(moveNum, basetime, increment)
\r
983 int moveNum, basetime, increment;
\r
987 char *opp = (gameMode==IcsPlayingWhite ? gameInfo.black : gameInfo.white);
\r
988 Boolean sentPos = FALSE;
\r
989 char *bookHit = NULL; // [HGM] book
\r
991 if (!first.initDone) {
\r
992 /* Game is starting prematurely. We can't deal with this */
\r
993 SendToICS(ics_prefix);
\r
994 SendToICS("abort\n");
\r
995 SendToICS(ics_prefix);
\r
996 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
1000 /* Send the variant command if needed */
\r
1001 if (gameInfo.variant != VariantNormal) {
\r
1002 sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
\r
1003 SendToProgram(buf, &first);
\r
1006 if ((startedFromSetupPosition && moveNum == 0) ||
\r
1007 (!appData.getMoveList && moveNum > 0)) {
\r
1008 SendToProgram("force\n", &first);
\r
1009 SendBoard(&first, moveNum);
\r
1013 sprintf(buf, "level 0 %d %d\n", basetime, increment);
\r
1014 SendToProgram(buf, &first);
\r
1016 /* Count consecutive games from one opponent */
\r
1017 if (strcmp(opp, zippyLastOpp) == 0) {
\r
1018 zippyConsecGames++;
\r
1020 zippyConsecGames = 1;
\r
1021 strcpy(zippyLastOpp, opp);
\r
1024 /* Send the "computer" command if the opponent is in the list
\r
1025 we've been gathering. */
\r
1026 for (w=0; w<num_opps; w++) {
\r
1027 if (!strcmp(opp_name[w], opp)) {
\r
1028 SendToProgram(first.computerString, &first);
\r
1033 /* Ratings might be < 0 which means "we haven't seen a ratings
\r
1034 message from ICS." Send 0 in that case */
\r
1035 w = (gameInfo.whiteRating >= 0) ? gameInfo.whiteRating : 0;
\r
1036 b = (gameInfo.blackRating >= 0) ? gameInfo.blackRating : 0;
\r
1038 firstMove = FALSE;
\r
1039 if (gameMode == IcsPlayingWhite) {
\r
1040 if (first.sendName) {
\r
1041 sprintf(buf, "name %s\n", gameInfo.black);
\r
1042 SendToProgram(buf, &first);
\r
1044 strcpy(ics_handle, gameInfo.white);
\r
1045 sprintf(buf, "rating %d %d\n", w, b);
\r
1046 SendToProgram(buf, &first);
\r
1048 /* Position sent above, engine is in force mode */
\r
1049 if (WhiteOnMove(moveNum)) {
\r
1050 /* Engine is on move now */
\r
1051 if (first.sendTime) {
\r
1052 if (first.useColors) {
\r
1053 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1054 SendTimeRemaining(&first, TRUE);
\r
1055 SendToProgram("white\n", &first);
\r
1057 SendTimeRemaining(&first, TRUE);
\r
1060 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1062 /* Engine's opponent is on move now */
\r
1063 if (first.usePlayother) {
\r
1064 if (first.sendTime) {
\r
1065 SendTimeRemaining(&first, TRUE);
\r
1067 SendToProgram("playother\n", &first);
\r
1069 /* Need to send a "go" after opponent moves */
\r
1074 /* Position not sent above, move list might be sent later */
\r
1075 if (moveNum == 0) {
\r
1076 /* No move list coming; at start of game */
\r
1077 if (first.sendTime) {
\r
1078 if (first.useColors) {
\r
1079 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1080 SendTimeRemaining(&first, TRUE);
\r
1081 SendToProgram("white\n", &first);
\r
1083 SendTimeRemaining(&first, TRUE);
\r
1086 // SendToProgram("go\n", &first);
\r
1087 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1090 } else if (gameMode == IcsPlayingBlack) {
\r
1091 if (first.sendName) {
\r
1092 sprintf(buf, "name %s\n", gameInfo.white);
\r
1093 SendToProgram(buf, &first);
\r
1095 strcpy(ics_handle, gameInfo.black);
\r
1096 sprintf(buf, "rating %d %d\n", b, w);
\r
1097 SendToProgram(buf, &first);
\r
1099 /* Position sent above, engine is in force mode */
\r
1100 if (!WhiteOnMove(moveNum)) {
\r
1101 /* Engine is on move now */
\r
1102 if (first.sendTime) {
\r
1103 if (first.useColors) {
\r
1104 SendToProgram("white\n", &first); /*gnu kludge*/
\r
1105 SendTimeRemaining(&first, FALSE);
\r
1106 SendToProgram("black\n", &first);
\r
1108 SendTimeRemaining(&first, FALSE);
\r
1111 // SendToProgram("go\n", &first);
\r
1112 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1114 /* Engine's opponent is on move now */
\r
1115 if (first.usePlayother) {
\r
1116 if (first.sendTime) {
\r
1117 SendTimeRemaining(&first, FALSE);
\r
1119 SendToProgram("playother\n", &first);
\r
1121 /* Need to send a "go" after opponent moves */
\r
1126 /* Position not sent above, move list might be sent later */
\r
1127 /* Nothing needs to be done here */
\r
1131 if(bookHit) { // [HGM] book: simulate book reply
\r
1132 static char bookMove[MSG_SIZ]; // a bit generous?
\r
1134 programStats.depth = programStats.nodes = programStats.time =
\r
1135 programStats.score = programStats.got_only_move = 0;
\r
1136 sprintf(programStats.movelist, "%s (xbook)", bookHit);
\r
1138 strcpy(bookMove, "move ");
\r
1139 strcat(bookMove, bookHit);
\r
1140 HandleMachineMove(bookMove, &first);
\r
1146 ZippyHoldings(white_holding, black_holding, new_piece)
\r
1147 char *white_holding, *black_holding, *new_piece;
\r
1149 char buf[MSG_SIZ];
\r
1150 if (gameMode != IcsPlayingBlack && gameMode != IcsPlayingWhite) return;
\r
1151 sprintf(buf, "holding [%s] [%s] %s\n",
\r
1152 white_holding, black_holding, new_piece);
\r
1153 SendToProgram(buf, &first);
\r