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, Massachusetts.
\r
6 * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
\r
8 * The following terms apply to Digital Equipment Corporation's copyright
\r
9 * interest in XBoard:
\r
10 * ------------------------------------------------------------------------
\r
11 * All Rights Reserved
\r
13 * Permission to use, copy, modify, and distribute this software and its
\r
14 * documentation for any purpose and without fee is hereby granted,
\r
15 * provided that the above copyright notice appear in all copies and that
\r
16 * both that copyright notice and this permission notice appear in
\r
17 * supporting documentation, and that the name of Digital not be
\r
18 * used in advertising or publicity pertaining to distribution of the
\r
19 * software without specific, written prior permission.
\r
21 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
22 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
23 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
24 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
25 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
26 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
28 * ------------------------------------------------------------------------
\r
30 * The following terms apply to the enhanced version of XBoard distributed
\r
31 * by the Free Software Foundation:
\r
32 * ------------------------------------------------------------------------
\r
33 * This program is free software; you can redistribute it and/or modify
\r
34 * it under the terms of the GNU General Public License as published by
\r
35 * the Free Software Foundation; either version 2 of the License, or
\r
36 * (at your option) any later version.
\r
38 * This program is distributed in the hope that it will be useful,
\r
39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
41 * GNU General Public License for more details.
\r
43 * You should have received a copy of the GNU General Public License
\r
44 * along with this program; if not, write to the Free Software
\r
45 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
\r
46 * ------------------------------------------------------------------------
\r
53 #include <sys/types.h>
\r
54 #include <sys/stat.h>
\r
58 # include <stdlib.h>
\r
59 # include <string.h>
\r
60 #else /* not STDC_HEADERS */
\r
61 extern char *getenv();
\r
63 # include <string.h>
\r
64 # else /* not HAVE_STRING_H */
\r
65 # include <strings.h>
\r
66 # endif /* not HAVE_STRING_H */
\r
67 #endif /* not STDC_HEADERS */
\r
69 #if TIME_WITH_SYS_TIME
\r
70 # include <sys/time.h>
\r
73 # if HAVE_SYS_TIME_H
\r
74 # include <sys/time.h>
\r
82 # include <unistd.h>
\r
87 #include "frontend.h"
\r
88 #include "backend.h"
\r
89 #include "backendz.h"
\r
91 static char zippyPartner[MSG_SIZ];
\r
92 static char zippyLastOpp[MSG_SIZ];
\r
93 static int zippyConsecGames;
\r
94 static time_t zippyLastGameEnd;
\r
100 /* Get name of Zippy lines file */
\r
101 p = getenv("ZIPPYLINES");
\r
103 appData.zippyLines = p;
\r
106 /* Get word that Zippy thinks is insulting */
\r
107 p = getenv("ZIPPYPINHEAD");
\r
109 appData.zippyPinhead = p;
\r
112 /* What password is used for remote control? */
\r
113 p = getenv("ZIPPYPASSWORD");
\r
115 appData.zippyPassword = p;
\r
118 /* What password is used for remote commands to gnuchess? */
\r
119 p = getenv("ZIPPYPASSWORD2");
\r
121 appData.zippyPassword2 = p;
\r
124 /* Joke feature for people who try an old password */
\r
125 p = getenv("ZIPPYWRONGPASSWORD");
\r
127 appData.zippyWrongPassword = p;
\r
130 /* While testing, I want to accept challenges from only one person
\r
131 (namely, my "anonymous" account), so I set an environment
\r
132 variable ZIPPYACCEPTONLY. */
\r
133 p = getenv("ZIPPYACCEPTONLY");
\r
135 appData.zippyAcceptOnly = p;
\r
138 /* Should Zippy use "i" command? */
\r
139 /* Defaults to 1=true */
\r
140 p = getenv("ZIPPYUSEI");
\r
142 appData.zippyUseI = atoi(p);
\r
145 /* How does Zippy handle bughouse partnering? */
\r
146 /* 0=say we can't play, 1=manual partnering, 2=auto partnering */
\r
147 p = getenv("ZIPPYBUGHOUSE");
\r
149 appData.zippyBughouse = atoi(p);
\r
152 /* Does Zippy abort games with Crafty? */
\r
153 /* Defaults to 0=false */
\r
154 p = getenv("ZIPPYNOPLAYCRAFTY");
\r
156 appData.zippyNoplayCrafty = atoi(p);
\r
159 /* What ICS command does Zippy send at game end? Default: "gameend". */
\r
160 p = getenv("ZIPPYGAMEEND");
\r
162 appData.zippyGameEnd = p;
\r
165 /* What ICS command does Zippy send at game start? Default: none. */
\r
166 p = getenv("ZIPPYGAMESTART");
\r
168 appData.zippyGameStart = p;
\r
171 /* Should Zippy accept adjourns? */
\r
172 /* Defaults to 0=false */
\r
173 p = getenv("ZIPPYADJOURN");
\r
175 appData.zippyAdjourn = atoi(p);
\r
178 /* Should Zippy accept aborts? */
\r
179 /* Defaults to 0=false */
\r
180 p = getenv("ZIPPYABORT");
\r
182 appData.zippyAbort = atoi(p);
\r
185 /* Should Zippy play chess variants (besides bughouse)? */
\r
186 p = getenv("ZIPPYVARIANTS");
\r
188 appData.zippyVariants = p;
\r
190 strcpy(first.variants, appData.zippyVariants);
\r
192 srandom(time(NULL));
\r
196 * Routines to implement Zippy talking
\r
200 char *swifties[] = {
\r
201 "i acclaims:", "i admonishes:", "i advertises:", "i advises:",
\r
202 "i advocates:", "i affirms:", "i alleges:", "i anathematizes:",
\r
203 "i animadverts:", "i announces:", "i apostrophizes:",
\r
204 "i appeals:", "i applauds:", "i approves:", "i argues:",
\r
205 "i articulates:", "i asserts:", "i asseverates:", "i attests:",
\r
206 "i avers:", "i avows:", "i baas:", "i babbles:", "i banters:",
\r
207 "i barks:", "i bawls:", "i bays:", "i begs:", "i belches:",
\r
208 "i bellows:", "i belts out:", "i berates:", "i beshrews:",
\r
209 "i blabbers:", "i blabs:", "i blares:", "i blasphemes:",
\r
210 "i blasts:", "i blathers:", "i bleats:", "i blithers:",
\r
211 "i blubbers:", "i blurts out:", "i blusters:", "i boasts:",
\r
212 "i brags:", "i brays:", "i broadcasts:", "i burbles:",
\r
213 "i buzzes:", "i cachinnates:", "i cackles:", "i caterwauls:",
\r
214 "i calumniates:", "i caws:", "i censures:", "i chants:",
\r
215 "i chatters:", "i cheeps:", "i cheers:", "i chides:", "i chins:",
\r
216 "i chirps:", "i chortles:", "i chuckles:", "i claims:",
\r
217 "i clamors:", "i clucks:", "i commands:", "i commends:",
\r
218 "i comments:", "i commiserates:", "i communicates:",
\r
219 "i complains:", "i concludes:", "i confabulates:", "i confesses:",
\r
220 "i coos:", "i coughs:", "i counsels:", "i cries:", "i croaks:",
\r
221 "i crows:", "i curses:", "i daydreams:", "i debates:",
\r
222 "i declaims:", "i declares:", "i delivers:", "i denounces:",
\r
223 "i deposes:", "i directs:", "i discloses:", "i disparages:",
\r
224 "i discourses:", "i divulges:", "i documents:", "i drawls:",
\r
225 "i dreams:", "i drivels:", "i drones:", "i effuses:",
\r
226 /*"i ejaculates:",*/ "i elucidates:", "i emotes:", "i endorses:",
\r
227 "i enthuses:", "i entreats:", "i enunciates:", "i eulogizes:",
\r
228 "i exclaims:", "i execrates:", "i exhorts:", "i expatiates:",
\r
229 "i explains:", "i explicates:", "i explodes:", "i exposes:",
\r
230 "i exposits:", "i expounds:", "i expresses:", "i extols:",
\r
231 "i exults:", "i fantasizes:", "i fibs:", "i filibusters:",
\r
232 "i flatters:", "i flutes:", "i fools:", "i free-associates:",
\r
233 "i fulminates:", "i gabbles:", "i gabs:", "i gasps:",
\r
234 "i giggles:", "i gossips:", "i gripes:", "i groans:", "i growls:",
\r
235 "i grunts:", "i guesses:", "i guffaws:", "i gushes:", "i hails:",
\r
236 "i hallucinates:", "i harangues:", "i harmonizes:", "i hectors:",
\r
237 "i hints:", "i hisses:", "i hollers:", "i honks:", "i hoots:",
\r
238 "i hosannas:", "i howls:", "i hums:", "i hypothecates:",
\r
239 "i hypothesizes:", "i imagines:", "i implies:", "i implores:",
\r
240 "i imprecates:", "i indicates:", "i infers:",
\r
241 "i informs everyone:", "i instructs:", "i interjects:",
\r
242 "i interposes:", "i intimates:", "i intones:", "i introspects:",
\r
243 "i inveighs:", "i jabbers:", "i japes:", "i jests:", "i jibes:",
\r
244 "i jives:", "i jokes:", "i joshes:", "i keens:", "i laments:",
\r
245 "i lauds:", "i laughs:", "i lectures:", "i lies:", "i lilts:",
\r
246 "i lisps:", "i maintains:", "i maledicts:", "i maunders:",
\r
247 "i meows:", "i mewls:", "i mimes:", "i minces:", "i moans:",
\r
248 "i moos:", "i mourns:", "i mouths:", "i mumbles:", "i murmurs:",
\r
249 "i muses:", "i mutters:", "i nags:", "i natters:", "i neighs:",
\r
250 "i notes:", "i nuncupates:", "i objurgates:", "i observes:",
\r
251 "i offers:", "i oinks:", "i opines:", "i orates:", "i orders:",
\r
252 "i panegyrizes:", "i pantomimes:", "i pants:", "i peals:",
\r
253 "i peeps:", "i perorates:", "i persuades:", "i petitions:",
\r
254 "i phonates:", "i pipes up:", "i pitches:", "i pleads:",
\r
255 "i points out:", "i pontificates:", "i postulates:", "i praises:",
\r
256 "i prates:", "i prattles:", "i preaches:", "i prescribes:",
\r
257 "i prevaricates:", "i proclaims:", "i projects:", "i pronounces:",
\r
258 "i proposes:", "i proscribes:", "i quacks:", "i queries:",
\r
259 "i questions:", "i quips:", "i quotes:", "i rages:", "i rambles:",
\r
260 "i rants:", "i raps:", "i rasps:", "i rattles:", "i raves:",
\r
261 "i reacts:", "i recites:", "i recommends:", "i records:",
\r
262 "i reiterates:", "i rejoins:", "i releases:", "i remarks:",
\r
263 "i reminisces:", "i remonstrates:", "i repeats:", "i replies:",
\r
264 "i reports:", "i reprimands:", "i reproaches:", "i reproves:",
\r
265 "i resounds:", "i responds:", "i retorts:", "i reveals:",
\r
266 "i reviles:", "i roars:", "i rumbles:", "i sanctions:",
\r
267 "i satirizes:", "i sauces:", "i scolds:", "i screams:",
\r
268 "i screeches:", "i semaphores:", "i sends:", "i sermonizes:",
\r
269 "i shrieks:", "i sibilates:", "i sighs:", "i signals:",
\r
270 "i signifies:", "i signs:", "i sings:", "i slurs:", "i snaps:",
\r
271 "i snarls:", "i sneezes:", "i snickers:", "i sniggers:",
\r
272 "i snivels:", "i snores:", "i snorts:", "i sobs:",
\r
273 "i soliloquizes:", "i sounds off:", "i sounds out:", "i speaks:",
\r
274 "i spews:", "i spits out:", "i splutters:", "i spoofs:",
\r
275 "i spouts:", "i sputters:", "i squalls:", "i squawks:",
\r
276 "i squeaks:", "i squeals:", "i stammers:", "i states:",
\r
277 "i stresses:", "i stutters:", "i submits:", "i suggests:",
\r
278 "i summarizes:", "i sums up:", "i swears:", "i talks:",
\r
279 "i tattles:", "i teases:", "i telegraphs:", "i testifies:",
\r
280 "i threatens:", "i thunders:", "i titters:", "i tongue-lashes:",
\r
281 "i toots:", "i transcribes:", "i transmits:", "i trills:",
\r
282 "i trumpets:", "i twaddles:", "i tweets:", "i twitters:",
\r
283 "i types:", "i upbraids:", "i urges:", "i utters:", "i ventures:",
\r
284 "i vibrates:", "i vilifies:", "i vituperates:", "i vocalizes:",
\r
285 "i vociferates:", "i voices:", "i waffles:", "i wails:",
\r
286 "i warbles:", "i warns:", "i weeps:", "i wheezes:", "i whimpers:",
\r
287 "i whines:", "i whinnies:", "i whistles:", "i wisecracks:",
\r
288 "i witnesses:", "i woofs:", "i writes:", "i yammers:", "i yawps:",
\r
289 "i yells:", "i yelps:", "i yodels:", "i yowls:", "i zings:",
\r
292 #define MAX_SPEECH 250
\r
294 void Speak(how, whom)
\r
297 static FILE *zipfile = NULL;
\r
298 static struct stat zipstat;
\r
299 char zipbuf[MAX_SPEECH + 1];
\r
300 static time_t lastShout = 0;
\r
306 if (strcmp(how, "shout") == 0) {
\r
307 now = time((time_t *) NULL);
\r
308 if (now - lastShout < 1*60) return;
\r
310 if (appData.zippyUseI) {
\r
311 how = swifties[(unsigned) random() %
\r
312 (sizeof(swifties)/sizeof(char *))];
\r
316 if (zipfile == NULL) {
\r
317 zipfile = fopen(appData.zippyLines, "r");
\r
318 if (zipfile == NULL) {
\r
319 DisplayFatalError("Can't open Zippy lines file", errno, 1);
\r
322 fstat(fileno(zipfile), &zipstat);
\r
326 fseek(zipfile, (unsigned) random() % zipstat.st_size, 0);
\r
329 } while (c != NULLCHAR && c != '^' && c != EOF);
\r
330 if (c == EOF) continue;
\r
331 while ((c = getc(zipfile)) == '\n') ;
\r
332 if (c == EOF) continue;
\r
337 /* Don't use ics_prefix; we need to let FICS expand the alias i -> it,
\r
338 but use the real command "i" on ICC */
\r
339 strcpy(zipbuf, how);
\r
340 strcat(zipbuf, " ");
\r
341 if (whom != NULL) {
\r
342 strcat(zipbuf, whom);
\r
343 strcat(zipbuf, " ");
\r
345 speechlen = strlen(zipbuf);
\r
346 p = zipbuf + speechlen;
\r
348 while (++speechlen < MAX_SPEECH) {
\r
349 if (c == NULLCHAR || c == '^') {
\r
354 } else if (c == '\n') {
\r
358 } while (c == ' ');
\r
359 } else if (c == EOF) {
\r
366 /* Tried to say something too long, or junk at the end of the
\r
367 file. Try something else. */
\r
368 Speak(how, whom); /* tail recursion */
\r
371 int ZippyCalled(str)
\r
374 return ics_handle[0] != NULLCHAR && StrCaseStr(str, ics_handle) != NULL;
\r
377 static char opp_name[128][32];
\r
378 static int num_opps=0;
\r
380 extern ColorClass curColor;
\r
382 static void SetCurColor( ColorClass color )
\r
387 static void ColorizeEx( ColorClass color, int cont )
\r
389 if( appData.colorize ) {
\r
390 Colorize( color, cont );
\r
391 SetCurColor( color );
\r
395 int ZippyControl(buf, i)
\r
400 char reply[MSG_SIZ];
\r
403 #include "trivia.c"
\r
406 /* Possibly reject Crafty as opponent */
\r
407 if (appData.zippyPlay && appData.zippyNoplayCrafty && forwardMostMove < 4
\r
408 && looking_at(buf, i, "* kibitzes: Hello from Crafty"))
\r
410 player = StripHighlightAndTitle(star_match[0]);
\r
411 if ((gameMode == IcsPlayingWhite &&
\r
412 StrCaseCmp(player, gameInfo.black) == 0) ||
\r
413 (gameMode == IcsPlayingBlack &&
\r
414 StrCaseCmp(player, gameInfo.white) == 0)) {
\r
416 sprintf(reply, "%ssay This computer does not play Crafty clones\n%sabort\n%s+noplay %s\n",
\r
417 ics_prefix, ics_prefix, ics_prefix, player);
\r
423 /* If this is a computer, save the name. Then later, once the */
\r
424 /* game is really started, we will send the "computer" notice to */
\r
426 if (appData.zippyPlay &&
\r
427 looking_at(buf, i, "* is in the computer list")) {
\r
429 for (i=0;i<num_opps;i++)
\r
430 if (!strcmp(opp_name[i],star_match[0])) break;
\r
431 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[0]);
\r
433 if (appData.zippyPlay && looking_at(buf, i, "* * is a computer *")) {
\r
435 for (i=0;i<num_opps;i++)
\r
436 if (!strcmp(opp_name[i],star_match[1])) break;
\r
437 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[1]);
\r
440 /* Tells and says */
\r
441 if (appData.zippyPlay &&
\r
442 (looking_at(buf, i, "* offers to be your bughouse partner") ||
\r
443 looking_at(buf, i, "* tells you: [automatic message] I chose you"))) {
\r
444 player = StripHighlightAndTitle(star_match[0]);
\r
445 if (appData.zippyBughouse > 1 && first.initDone) {
\r
446 sprintf(reply, "%spartner %s\n", ics_prefix, player);
\r
448 if (strcmp(zippyPartner, player) != 0) {
\r
449 strcpy(zippyPartner, player);
\r
450 SendToProgram(reply + strlen(ics_prefix), &first);
\r
452 } else if (appData.zippyBughouse > 0) {
\r
453 sprintf(reply, "%sdecline %s\n", ics_prefix, player);
\r
456 sprintf(reply, "%stell %s This computer cannot play bughouse\n",
\r
457 ics_prefix, player);
\r
463 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
464 looking_at(buf, i, "* agrees to be your partner")) {
\r
465 player = StripHighlightAndTitle(star_match[0]);
\r
466 sprintf(reply, "partner %s\n", player);
\r
467 if (strcmp(zippyPartner, player) != 0) {
\r
468 strcpy(zippyPartner, player);
\r
469 SendToProgram(reply, &first);
\r
474 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
475 (looking_at(buf, i, "are no longer *'s partner") ||
\r
477 "* tells you: [automatic message] I'm no longer your"))) {
\r
478 player = StripHighlightAndTitle(star_match[0]);
\r
479 if (strcmp(zippyPartner, player) == 0) {
\r
480 zippyPartner[0] = NULLCHAR;
\r
481 SendToProgram("partner\n", &first);
\r
486 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
487 (looking_at(buf, i, "no longer have a bughouse partner") ||
\r
488 looking_at(buf, i, "partner has disconnected") ||
\r
489 looking_at(buf, i, "partner has just chosen a new partner"))) {
\r
490 zippyPartner[0] = NULLCHAR;
\r
491 SendToProgram("partner\n", &first);
\r
495 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
496 looking_at(buf, i, "* (your partner) tells you: *")) {
\r
497 /* This pattern works on FICS but not ICC */
\r
498 player = StripHighlightAndTitle(star_match[0]);
\r
499 if (strcmp(zippyPartner, player) != 0) {
\r
500 strcpy(zippyPartner, player);
\r
501 sprintf(reply, "partner %s\n", player);
\r
502 SendToProgram(reply, &first);
\r
504 sprintf(reply, "ptell %s\n", star_match[1]);
\r
505 SendToProgram(reply, &first);
\r
509 if (looking_at(buf, i, "* tells you: *") ||
\r
510 looking_at(buf, i, "* says: *"))
\r
512 player = StripHighlightAndTitle(star_match[0]);
\r
513 if (appData.zippyPassword[0] != NULLCHAR &&
\r
514 strncmp(star_match[1], appData.zippyPassword,
\r
515 strlen(appData.zippyPassword)) == 0) {
\r
516 p = star_match[1] + strlen(appData.zippyPassword);
\r
517 while (*p == ' ') p++;
\r
520 } else if (appData.zippyPassword2[0] != NULLCHAR && first.initDone &&
\r
521 strncmp(star_match[1], appData.zippyPassword2,
\r
522 strlen(appData.zippyPassword2)) == 0) {
\r
523 p = star_match[1] + strlen(appData.zippyPassword2);
\r
524 while (*p == ' ') p++;
\r
525 SendToProgram(p, &first);
\r
526 SendToProgram("\n", &first);
\r
527 } else if (appData.zippyWrongPassword[0] != NULLCHAR &&
\r
528 strncmp(star_match[1], appData.zippyWrongPassword,
\r
529 strlen(appData.zippyWrongPassword)) == 0) {
\r
530 p = star_match[1] + strlen(appData.zippyWrongPassword);
\r
531 while (*p == ' ') p++;
\r
532 sprintf(reply, "wrong %s\n", player);
\r
534 } else if (appData.zippyBughouse && first.initDone &&
\r
535 strcmp(player, zippyPartner) == 0) {
\r
536 SendToProgram("ptell ", &first);
\r
537 SendToProgram(star_match[1], &first);
\r
538 SendToProgram("\n", &first);
\r
539 } else if (strncmp(star_match[1], HI, 6) == 0) {
\r
540 extern char* programVersion;
\r
541 sprintf(reply, "%stell %s %s\n",
\r
542 ics_prefix, player, programVersion);
\r
544 } else if (strncmp(star_match[1], "W0W!! ", 6) == 0) {
\r
545 extern char* programVersion;
\r
546 sprintf(reply, "%stell %s %s\n", ics_prefix,
\r
547 player, programVersion);
\r
549 } else if (appData.zippyTalk && (((unsigned) random() % 10) < 9)) {
\r
550 if (strcmp(player, ics_handle) != 0) {
\r
551 Speak("tell", player);
\r
555 ColorizeEx( ColorTell, FALSE );
\r
560 if( appData.colorize && looking_at(buf, i, "* (*) seeking") ) {
\r
561 ColorizeEx(ColorSeek, FALSE);
\r
565 if (looking_at(buf, i, "* spoofs you:")) {
\r
566 player = StripHighlightAndTitle(star_match[0]);
\r
567 sprintf(reply, "spoofedby %s\n", player);
\r
574 int ZippyConverse(buf, i)
\r
578 static char lastgreet[MSG_SIZ];
\r
579 char reply[MSG_SIZ];
\r
582 /* Shouts and emotes */
\r
583 if (looking_at(buf, i, "--> * *") ||
\r
584 looking_at(buf, i, "* shouts: *"))
\r
586 if (appData.zippyTalk) {
\r
587 char *player = StripHighlightAndTitle(star_match[0]);
\r
588 if (strcmp(player, ics_handle) == 0) {
\r
590 } else if (appData.zippyPinhead[0] != NULLCHAR &&
\r
591 StrCaseStr(star_match[1], appData.zippyPinhead) != NULL) {
\r
592 sprintf(reply, "insult %s\n", player);
\r
594 } else if (ZippyCalled(star_match[1])) {
\r
595 Speak("shout", NULL);
\r
599 ColorizeEx(ColorShout, FALSE);
\r
604 if (looking_at(buf, i, "* kibitzes: *")) {
\r
605 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
\r
606 char *player = StripHighlightAndTitle(star_match[0]);
\r
607 if (strcmp(player, ics_handle) != 0) {
\r
608 Speak("kibitz", NULL);
\r
612 ColorizeEx(ColorKibitz, FALSE);
\r
617 if (looking_at(buf, i, "* whispers: *")) {
\r
618 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
\r
619 char *player = StripHighlightAndTitle(star_match[0]);
\r
620 if (strcmp(player, ics_handle) != 0) {
\r
621 Speak("whisper", NULL);
\r
625 ColorizeEx(ColorKibitz, FALSE);
\r
631 if ((looking_at(buf, i, ". * (*:*): *") && isdigit(star_match[1][0])) ||
\r
632 looking_at(buf, i, ". * at *:*: *")) {
\r
633 if (appData.zippyTalk) {
\r
635 char *player = StripHighlightAndTitle(star_match[0]);
\r
637 if (strcmp(player, ics_handle) != 0) {
\r
638 if (((unsigned) random() % 10) < 9)
\r
639 Speak("message", player);
\r
640 f = fopen("zippy.messagelog", "a");
\r
641 fprintf(f, "%s (%s:%s): %s\n", player,
\r
642 star_match[1], star_match[2], star_match[3]);
\r
649 /* Channel tells */
\r
651 if (looking_at(buf, i, "*(*: *")) {
\r
654 if (star_match[0][0] == NULLCHAR ||
\r
655 strchr(star_match[0], ' ') ||
\r
656 strchr(star_match[1], ' ')) {
\r
657 /* Oops, did not want to match this; probably a message */
\r
661 if (appData.zippyTalk) {
\r
662 player = StripHighlightAndTitle(star_match[0]);
\r
663 channel = strrchr(star_match[1], '(');
\r
664 if (channel == NULL) {
\r
665 channel = star_match[1];
\r
669 channel[strlen(channel)-1] = NULLCHAR;
\r
671 /* Always tell to the channel (probability 90%) */
\r
672 if (strcmp(player, ics_handle) != 0 &&
\r
673 ((unsigned) random() % 10) < 9) {
\r
674 Speak("tell", channel);
\r
677 /* Tell to the channel only if someone mentions our name */
\r
678 if (ZippyCalled(star_match[2])) {
\r
679 Speak("tell", channel);
\r
683 ColorizeEx( atoi(channel) == 1 ? ColorChannel1 : ColorChannel, FALSE );
\r
688 if (!appData.zippyTalk) return FALSE;
\r
690 if ((looking_at(buf, i, "You have * message") &&
\r
691 atoi(star_match[0]) != 0) ||
\r
692 looking_at(buf, i, "* has left a message for you") ||
\r
693 looking_at(buf, i, "* just sent you a message")) {
\r
694 sprintf(reply, "%smessages\n%sclearmessages *\n",
\r
695 ics_prefix, ics_prefix);
\r
700 if (looking_at(buf, i, "Notification: * has arrived")) {
\r
701 if (((unsigned) random() % 3) == 0) {
\r
702 char *player = StripHighlightAndTitle(star_match[0]);
\r
703 strcpy(lastgreet, player);
\r
704 sprintf(reply, "greet %s\n", player);
\r
706 Speak("tell", player);
\r
710 if (looking_at(buf, i, "Notification: * has departed")) {
\r
711 if (((unsigned) random() % 3) == 0) {
\r
712 char *player = StripHighlightAndTitle(star_match[0]);
\r
713 sprintf(reply, "farewell %s\n", player);
\r
718 if (looking_at(buf, i, "Not sent -- * is censoring you")) {
\r
719 char *player = StripHighlightAndTitle(star_match[0]);
\r
720 if (strcmp(player, lastgreet) == 0) {
\r
721 sprintf(reply, "%s-notify %s\n", ics_prefix, player);
\r
726 if (looking_at(buf, i, "command is currently turned off")) {
\r
727 appData.zippyUseI = 0;
\r
733 void ZippyGameStart(white, black)
\r
734 char *white, *black;
\r
736 if (!first.initDone) {
\r
737 /* Game is starting prematurely. We can't deal with this */
\r
738 SendToICS(ics_prefix);
\r
739 SendToICS("abort\n");
\r
740 SendToICS(ics_prefix);
\r
741 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
745 if (appData.zippyGameStart[0] != NULLCHAR) {
\r
746 SendToICS(appData.zippyGameStart);
\r
751 void ZippyGameEnd(result, resultDetails)
\r
753 char *resultDetails;
\r
755 if (appData.zippyAcceptOnly[0] == NULLCHAR &&
\r
756 appData.zippyGameEnd[0] != NULLCHAR) {
\r
757 SendToICS(appData.zippyGameEnd);
\r
760 zippyLastGameEnd = time(0);
\r
764 * Routines to implement Zippy playing chess
\r
767 void ZippyHandleChallenge(srated, swild, sbase, sincrement, opponent)
\r
768 char *srated, *swild, *sbase, *sincrement, *opponent;
\r
771 int base, increment, i=0;
\r
773 VariantClass variant;
\r
777 variant = StringToVariant(swild);
\r
778 varname = VariantName(variant);
\r
779 base = atoi(sbase);
\r
780 increment = atoi(sincrement);
\r
782 /* If desired, you can insert more code here to decline matches
\r
783 based on rated, variant, base, and increment, but it is
\r
784 easier to use the ICS formula feature instead. */
\r
786 if (variant == VariantLoadable) {
\r
788 "%stell %s This computer can't play wild type %s\n%sdecline %s\n",
\r
789 ics_prefix, opponent, swild, ics_prefix, opponent);
\r
793 if (StrStr(appData.zippyVariants, varname) == NULL ||
\r
794 (i=first.protocolVersion) != 1 && StrStr(first.variants, varname) == NULL /* [HGM] zippyvar */
\r
797 "%stell %s This computer can't play %s [%s], only %s\n%sdecline %s\n",
\r
798 ics_prefix, opponent, swild, varname,
\r
799 i ? first.variants : appData.zippyVariants, /* [HGM] zippyvar */
\r
800 ics_prefix, opponent);
\r
805 /* Are we blocking match requests from all but one person? */
\r
806 if (appData.zippyAcceptOnly[0] != NULLCHAR &&
\r
807 StrCaseCmp(opponent, appData.zippyAcceptOnly)) {
\r
808 /* Yes, and this isn't him. Ignore challenge. */
\r
812 /* Too many consecutive games with same opponent? If so, make him
\r
813 wait until someone else has played or a timeout has elapsed. */
\r
814 if (appData.zippyMaxGames &&
\r
815 strcmp(opponent, zippyLastOpp) == 0 &&
\r
816 zippyConsecGames >= appData.zippyMaxGames &&
\r
817 difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
\r
818 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
819 ics_prefix, opponent, zippyConsecGames, ics_handle,
\r
820 appData.zippyReplayTimeout, ics_prefix, opponent);
\r
825 /* Engine not yet initialized or still thinking about last game? */
\r
826 if (!first.initDone || first.lastPing != first.lastPong) {
\r
827 sprintf(buf, "%stell %s I'm not quite ready for a new game yet; try again soon.\n%sdecline %s\n",
\r
828 ics_prefix, opponent, ics_prefix, opponent);
\r
833 sprintf(buf, "%saccept %s\n", ics_prefix, opponent);
\r
835 if (appData.zippyTalk) {
\r
836 Speak("tell", opponent);
\r
841 /* Accept matches */
\r
842 int ZippyMatch(buf, i)
\r
846 if (looking_at(buf, i, "* * match * * requested with * (*)")) {
\r
848 ZippyHandleChallenge(star_match[0], star_match[1],
\r
849 star_match[2], star_match[3],
\r
850 StripHighlightAndTitle(star_match[4]));
\r
854 /* Old FICS 0-increment form */
\r
855 if (looking_at(buf, i, "* * match * requested with * (*)")) {
\r
857 ZippyHandleChallenge(star_match[0], star_match[1],
\r
858 star_match[2], "0",
\r
859 StripHighlightAndTitle(star_match[3]));
\r
863 if (looking_at(buf, i,
\r
864 "* has made an alternate proposal of * * match * *.")) {
\r
866 ZippyHandleChallenge(star_match[1], star_match[2],
\r
867 star_match[3], star_match[4],
\r
868 StripHighlightAndTitle(star_match[0]));
\r
872 /* FICS wild/nonstandard forms */
\r
873 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * Loaded from *")) {
\r
874 /* note: star_match[2] can include "[white] " or "[black] "
\r
875 before our own name. */
\r
876 ZippyHandleChallenge(star_match[4], star_match[8],
\r
877 star_match[6], star_match[7],
\r
878 StripHighlightAndTitle(star_match[0]));
\r
882 if (looking_at(buf, i,
\r
883 "Challenge: * (*) *(*) * * * * : * * Loaded from *")) {
\r
884 /* note: star_match[2] can include "[white] " or "[black] "
\r
885 before our own name. */
\r
886 ZippyHandleChallenge(star_match[4], star_match[10],
\r
887 star_match[8], star_match[9],
\r
888 StripHighlightAndTitle(star_match[0]));
\r
892 /* Regular forms */
\r
893 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * : * *") |
\r
894 looking_at(buf, i, "Challenge: * (*) *(*) * * * * * *")) {
\r
895 /* note: star_match[2] can include "[white] " or "[black] "
\r
896 before our own name. */
\r
897 ZippyHandleChallenge(star_match[4], star_match[5],
\r
898 star_match[8], star_match[9],
\r
899 StripHighlightAndTitle(star_match[0]));
\r
903 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * *")) {
\r
904 /* note: star_match[2] can include "[white] " or "[black] "
\r
905 before our own name. */
\r
906 ZippyHandleChallenge(star_match[4], star_match[5],
\r
907 star_match[6], star_match[7],
\r
908 StripHighlightAndTitle(star_match[0]));
\r
912 if (looking_at(buf, i, "offers you a draw")) {
\r
913 if (first.sendDrawOffers && first.initDone) {
\r
914 SendToProgram("draw\n", &first);
\r
919 if (looking_at(buf, i, "requests that the game be aborted") ||
\r
920 looking_at(buf, i, "would like to abort")) {
\r
921 if (appData.zippyAbort ||
\r
922 (gameMode == IcsPlayingWhite && whiteTimeRemaining < 0) ||
\r
923 (gameMode == IcsPlayingBlack && blackTimeRemaining < 0)) {
\r
924 SendToICS(ics_prefix);
\r
925 SendToICS("abort\n");
\r
927 SendToICS(ics_prefix);
\r
928 if (appData.zippyTalk)
\r
929 SendToICS("say Whoa no! I am having FUN!!\n");
\r
931 SendToICS("say Sorry, this computer doesn't accept aborts.\n");
\r
936 if (looking_at(buf, i, "requests adjournment") ||
\r
937 looking_at(buf, i, "would like to adjourn")) {
\r
938 if (appData.zippyAdjourn) {
\r
939 SendToICS(ics_prefix);
\r
940 SendToICS("adjourn\n");
\r
942 SendToICS(ics_prefix);
\r
943 if (appData.zippyTalk)
\r
944 SendToICS("say Whoa no! I am having FUN playing NOW!!\n");
\r
946 SendToICS("say Sorry, this computer doesn't accept adjourns.\n");
\r
954 /* Initialize chess program with data from the first board
\r
955 * of a new or resumed game.
\r
957 void ZippyFirstBoard(moveNum, basetime, increment)
\r
958 int moveNum, basetime, increment;
\r
962 char *opp = (gameMode==IcsPlayingWhite ? gameInfo.black : gameInfo.white);
\r
963 Boolean sentPos = FALSE;
\r
965 if (!first.initDone) {
\r
966 /* Game is starting prematurely. We can't deal with this */
\r
967 SendToICS(ics_prefix);
\r
968 SendToICS("abort\n");
\r
969 SendToICS(ics_prefix);
\r
970 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
974 /* Send the variant command if needed */
\r
975 if (gameInfo.variant != VariantNormal) {
\r
976 sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
\r
977 SendToProgram(buf, &first);
\r
980 if ((startedFromSetupPosition && moveNum == 0) ||
\r
981 (!appData.getMoveList && moveNum > 0)) {
\r
982 SendToProgram("force\n", &first);
\r
983 SendBoard(&first, moveNum);
\r
987 sprintf(buf, "level 0 %d %d\n", basetime, increment);
\r
988 SendToProgram(buf, &first);
\r
990 /* Count consecutive games from one opponent */
\r
991 if (strcmp(opp, zippyLastOpp) == 0) {
\r
992 zippyConsecGames++;
\r
994 zippyConsecGames = 1;
\r
995 strcpy(zippyLastOpp, opp);
\r
998 /* Send the "computer" command if the opponent is in the list
\r
999 we've been gathering. */
\r
1000 for (w=0; w<num_opps; w++) {
\r
1001 if (!strcmp(opp_name[w], opp)) {
\r
1002 SendToProgram(first.computerString, &first);
\r
1007 /* Ratings might be < 0 which means "we haven't seen a ratings
\r
1008 message from ICS." Send 0 in that case */
\r
1009 w = (gameInfo.whiteRating >= 0) ? gameInfo.whiteRating : 0;
\r
1010 b = (gameInfo.blackRating >= 0) ? gameInfo.blackRating : 0;
\r
1012 firstMove = FALSE;
\r
1013 if (gameMode == IcsPlayingWhite) {
\r
1014 if (first.sendName) {
\r
1015 sprintf(buf, "name %s\n", gameInfo.black);
\r
1016 SendToProgram(buf, &first);
\r
1018 strcpy(ics_handle, gameInfo.white);
\r
1019 sprintf(buf, "rating %d %d\n", w, b);
\r
1020 SendToProgram(buf, &first);
\r
1022 /* Position sent above, engine is in force mode */
\r
1023 if (WhiteOnMove(moveNum)) {
\r
1024 /* Engine is on move now */
\r
1025 if (first.sendTime) {
\r
1026 if (first.useColors) {
\r
1027 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1028 SendTimeRemaining(&first, TRUE);
\r
1029 SendToProgram("white\n", &first);
\r
1031 SendTimeRemaining(&first, TRUE);
\r
1034 SendToProgram("go\n", &first);
\r
1036 /* Engine's opponent is on move now */
\r
1037 if (first.usePlayother) {
\r
1038 if (first.sendTime) {
\r
1039 SendTimeRemaining(&first, TRUE);
\r
1041 SendToProgram("playother\n", &first);
\r
1043 /* Need to send a "go" after opponent moves */
\r
1048 /* Position not sent above, move list might be sent later */
\r
1049 if (moveNum == 0) {
\r
1050 /* No move list coming; at start of game */
\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 SendToProgram("go\n", &first);
\r
1063 } else if (gameMode == IcsPlayingBlack) {
\r
1064 if (first.sendName) {
\r
1065 sprintf(buf, "name %s\n", gameInfo.white);
\r
1066 SendToProgram(buf, &first);
\r
1068 strcpy(ics_handle, gameInfo.black);
\r
1069 sprintf(buf, "rating %d %d\n", b, w);
\r
1070 SendToProgram(buf, &first);
\r
1072 /* Position sent above, engine is in force mode */
\r
1073 if (!WhiteOnMove(moveNum)) {
\r
1074 /* Engine is on move now */
\r
1075 if (first.sendTime) {
\r
1076 if (first.useColors) {
\r
1077 SendToProgram("white\n", &first); /*gnu kludge*/
\r
1078 SendTimeRemaining(&first, FALSE);
\r
1079 SendToProgram("black\n", &first);
\r
1081 SendTimeRemaining(&first, FALSE);
\r
1084 SendToProgram("go\n", &first);
\r
1086 /* Engine's opponent is on move now */
\r
1087 if (first.usePlayother) {
\r
1088 if (first.sendTime) {
\r
1089 SendTimeRemaining(&first, FALSE);
\r
1091 SendToProgram("playother\n", &first);
\r
1093 /* Need to send a "go" after opponent moves */
\r
1098 /* Position not sent above, move list might be sent later */
\r
1099 /* Nothing needs to be done here */
\r
1106 ZippyHoldings(white_holding, black_holding, new_piece)
\r
1107 char *white_holding, *black_holding, *new_piece;
\r
1109 char buf[MSG_SIZ];
\r
1110 if (gameMode != IcsPlayingBlack && gameMode != IcsPlayingWhite) return;
\r
1111 sprintf(buf, "holding [%s] [%s] %s\n",
\r
1112 white_holding, black_holding, new_piece);
\r
1113 SendToProgram(buf, &first);
\r