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 if(star_match[8] == NULL || star_match[8][0] == 0) // [HGM] chessd: open-source ICS has file on next line
\r
877 ZippyHandleChallenge(star_match[4], star_match[5],
\r
878 star_match[6], star_match[7],
\r
879 StripHighlightAndTitle(star_match[0]));
\r
880 else ZippyHandleChallenge(star_match[4], star_match[8],
\r
881 star_match[6], star_match[7],
\r
882 StripHighlightAndTitle(star_match[0]));
\r
886 if (looking_at(buf, i,
\r
887 "Challenge: * (*) *(*) * * * * : * * Loaded from *")) {
\r
888 /* note: star_match[2] can include "[white] " or "[black] "
\r
889 before our own name. */
\r
890 ZippyHandleChallenge(star_match[4], star_match[10],
\r
891 star_match[8], star_match[9],
\r
892 StripHighlightAndTitle(star_match[0]));
\r
896 /* Regular forms */
\r
897 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * : * *") |
\r
898 looking_at(buf, i, "Challenge: * (*) *(*) * * * * * *")) {
\r
899 /* note: star_match[2] can include "[white] " or "[black] "
\r
900 before our own name. */
\r
901 ZippyHandleChallenge(star_match[4], star_match[5],
\r
902 star_match[8], star_match[9],
\r
903 StripHighlightAndTitle(star_match[0]));
\r
907 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * *")) {
\r
908 /* note: star_match[2] can include "[white] " or "[black] "
\r
909 before our own name. */
\r
910 ZippyHandleChallenge(star_match[4], star_match[5],
\r
911 star_match[6], star_match[7],
\r
912 StripHighlightAndTitle(star_match[0]));
\r
916 if (looking_at(buf, i, "offers you a draw")) {
\r
917 if (first.sendDrawOffers && first.initDone) {
\r
918 SendToProgram("draw\n", &first);
\r
923 if (looking_at(buf, i, "requests that the game be aborted") ||
\r
924 looking_at(buf, i, "would like to abort")) {
\r
925 if (appData.zippyAbort ||
\r
926 (gameMode == IcsPlayingWhite && whiteTimeRemaining < 0) ||
\r
927 (gameMode == IcsPlayingBlack && blackTimeRemaining < 0)) {
\r
928 SendToICS(ics_prefix);
\r
929 SendToICS("abort\n");
\r
931 SendToICS(ics_prefix);
\r
932 if (appData.zippyTalk)
\r
933 SendToICS("say Whoa no! I am having FUN!!\n");
\r
935 SendToICS("say Sorry, this computer doesn't accept aborts.\n");
\r
940 if (looking_at(buf, i, "requests adjournment") ||
\r
941 looking_at(buf, i, "would like to adjourn")) {
\r
942 if (appData.zippyAdjourn) {
\r
943 SendToICS(ics_prefix);
\r
944 SendToICS("adjourn\n");
\r
946 SendToICS(ics_prefix);
\r
947 if (appData.zippyTalk)
\r
948 SendToICS("say Whoa no! I am having FUN playing NOW!!\n");
\r
950 SendToICS("say Sorry, this computer doesn't accept adjourns.\n");
\r
958 /* Initialize chess program with data from the first board
\r
959 * of a new or resumed game.
\r
961 void ZippyFirstBoard(moveNum, basetime, increment)
\r
962 int moveNum, basetime, increment;
\r
966 char *opp = (gameMode==IcsPlayingWhite ? gameInfo.black : gameInfo.white);
\r
967 Boolean sentPos = FALSE;
\r
969 if (!first.initDone) {
\r
970 /* Game is starting prematurely. We can't deal with this */
\r
971 SendToICS(ics_prefix);
\r
972 SendToICS("abort\n");
\r
973 SendToICS(ics_prefix);
\r
974 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
978 /* Send the variant command if needed */
\r
979 if (gameInfo.variant != VariantNormal) {
\r
980 sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
\r
981 SendToProgram(buf, &first);
\r
984 if ((startedFromSetupPosition && moveNum == 0) ||
\r
985 (!appData.getMoveList && moveNum > 0)) {
\r
986 SendToProgram("force\n", &first);
\r
987 SendBoard(&first, moveNum);
\r
991 sprintf(buf, "level 0 %d %d\n", basetime, increment);
\r
992 SendToProgram(buf, &first);
\r
994 /* Count consecutive games from one opponent */
\r
995 if (strcmp(opp, zippyLastOpp) == 0) {
\r
996 zippyConsecGames++;
\r
998 zippyConsecGames = 1;
\r
999 strcpy(zippyLastOpp, opp);
\r
1002 /* Send the "computer" command if the opponent is in the list
\r
1003 we've been gathering. */
\r
1004 for (w=0; w<num_opps; w++) {
\r
1005 if (!strcmp(opp_name[w], opp)) {
\r
1006 SendToProgram(first.computerString, &first);
\r
1011 /* Ratings might be < 0 which means "we haven't seen a ratings
\r
1012 message from ICS." Send 0 in that case */
\r
1013 w = (gameInfo.whiteRating >= 0) ? gameInfo.whiteRating : 0;
\r
1014 b = (gameInfo.blackRating >= 0) ? gameInfo.blackRating : 0;
\r
1016 firstMove = FALSE;
\r
1017 if (gameMode == IcsPlayingWhite) {
\r
1018 if (first.sendName) {
\r
1019 sprintf(buf, "name %s\n", gameInfo.black);
\r
1020 SendToProgram(buf, &first);
\r
1022 strcpy(ics_handle, gameInfo.white);
\r
1023 sprintf(buf, "rating %d %d\n", w, b);
\r
1024 SendToProgram(buf, &first);
\r
1026 /* Position sent above, engine is in force mode */
\r
1027 if (WhiteOnMove(moveNum)) {
\r
1028 /* Engine is on move now */
\r
1029 if (first.sendTime) {
\r
1030 if (first.useColors) {
\r
1031 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1032 SendTimeRemaining(&first, TRUE);
\r
1033 SendToProgram("white\n", &first);
\r
1035 SendTimeRemaining(&first, TRUE);
\r
1038 SendToProgram("go\n", &first);
\r
1040 /* Engine's opponent is on move now */
\r
1041 if (first.usePlayother) {
\r
1042 if (first.sendTime) {
\r
1043 SendTimeRemaining(&first, TRUE);
\r
1045 SendToProgram("playother\n", &first);
\r
1047 /* Need to send a "go" after opponent moves */
\r
1052 /* Position not sent above, move list might be sent later */
\r
1053 if (moveNum == 0) {
\r
1054 /* No move list coming; at start of game */
\r
1055 if (first.sendTime) {
\r
1056 if (first.useColors) {
\r
1057 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1058 SendTimeRemaining(&first, TRUE);
\r
1059 SendToProgram("white\n", &first);
\r
1061 SendTimeRemaining(&first, TRUE);
\r
1064 SendToProgram("go\n", &first);
\r
1067 } else if (gameMode == IcsPlayingBlack) {
\r
1068 if (first.sendName) {
\r
1069 sprintf(buf, "name %s\n", gameInfo.white);
\r
1070 SendToProgram(buf, &first);
\r
1072 strcpy(ics_handle, gameInfo.black);
\r
1073 sprintf(buf, "rating %d %d\n", b, w);
\r
1074 SendToProgram(buf, &first);
\r
1076 /* Position sent above, engine is in force mode */
\r
1077 if (!WhiteOnMove(moveNum)) {
\r
1078 /* Engine is on move now */
\r
1079 if (first.sendTime) {
\r
1080 if (first.useColors) {
\r
1081 SendToProgram("white\n", &first); /*gnu kludge*/
\r
1082 SendTimeRemaining(&first, FALSE);
\r
1083 SendToProgram("black\n", &first);
\r
1085 SendTimeRemaining(&first, FALSE);
\r
1088 SendToProgram("go\n", &first);
\r
1090 /* Engine's opponent is on move now */
\r
1091 if (first.usePlayother) {
\r
1092 if (first.sendTime) {
\r
1093 SendTimeRemaining(&first, FALSE);
\r
1095 SendToProgram("playother\n", &first);
\r
1097 /* Need to send a "go" after opponent moves */
\r
1102 /* Position not sent above, move list might be sent later */
\r
1103 /* Nothing needs to be done here */
\r
1110 ZippyHoldings(white_holding, black_holding, new_piece)
\r
1111 char *white_holding, *black_holding, *new_piece;
\r
1113 char buf[MSG_SIZ];
\r
1114 if (gameMode != IcsPlayingBlack && gameMode != IcsPlayingWhite) return;
\r
1115 sprintf(buf, "holding [%s] [%s] %s\n",
\r
1116 white_holding, black_holding, new_piece);
\r
1117 SendToProgram(buf, &first);
\r