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
96 static char zippyPartner[MSG_SIZ];
\r
97 static char zippyLastOpp[MSG_SIZ];
\r
98 static int zippyConsecGames;
\r
99 static time_t zippyLastGameEnd;
\r
101 extern void mysrandom(unsigned int seed);
\r
102 extern int myrandom(void);
\r
108 /* Get name of Zippy lines file */
\r
109 p = getenv("ZIPPYLINES");
\r
111 appData.zippyLines = p;
\r
114 /* Get word that Zippy thinks is insulting */
\r
115 p = getenv("ZIPPYPINHEAD");
\r
117 appData.zippyPinhead = p;
\r
120 /* What password is used for remote control? */
\r
121 p = getenv("ZIPPYPASSWORD");
\r
123 appData.zippyPassword = p;
\r
126 /* What password is used for remote commands to gnuchess? */
\r
127 p = getenv("ZIPPYPASSWORD2");
\r
129 appData.zippyPassword2 = p;
\r
132 /* Joke feature for people who try an old password */
\r
133 p = getenv("ZIPPYWRONGPASSWORD");
\r
135 appData.zippyWrongPassword = p;
\r
138 /* While testing, I want to accept challenges from only one person
\r
139 (namely, my "anonymous" account), so I set an environment
\r
140 variable ZIPPYACCEPTONLY. */
\r
141 p = getenv("ZIPPYACCEPTONLY");
\r
143 appData.zippyAcceptOnly = p;
\r
146 /* Should Zippy use "i" command? */
\r
147 /* Defaults to 1=true */
\r
148 p = getenv("ZIPPYUSEI");
\r
150 appData.zippyUseI = atoi(p);
\r
153 /* How does Zippy handle bughouse partnering? */
\r
154 /* 0=say we can't play, 1=manual partnering, 2=auto partnering */
\r
155 p = getenv("ZIPPYBUGHOUSE");
\r
157 appData.zippyBughouse = atoi(p);
\r
160 /* Does Zippy abort games with Crafty? */
\r
161 /* Defaults to 0=false */
\r
162 p = getenv("ZIPPYNOPLAYCRAFTY");
\r
164 appData.zippyNoplayCrafty = atoi(p);
\r
167 /* What ICS command does Zippy send at game end? Default: "gameend". */
\r
168 p = getenv("ZIPPYGAMEEND");
\r
170 appData.zippyGameEnd = p;
\r
173 /* What ICS command does Zippy send at game start? Default: none. */
\r
174 p = getenv("ZIPPYGAMESTART");
\r
176 appData.zippyGameStart = p;
\r
179 /* Should Zippy accept adjourns? */
\r
180 /* Defaults to 0=false */
\r
181 p = getenv("ZIPPYADJOURN");
\r
183 appData.zippyAdjourn = atoi(p);
\r
186 /* Should Zippy accept aborts? */
\r
187 /* Defaults to 0=false */
\r
188 p = getenv("ZIPPYABORT");
\r
190 appData.zippyAbort = atoi(p);
\r
193 /* Should Zippy play chess variants (besides bughouse)? */
\r
194 p = getenv("ZIPPYVARIANTS");
\r
196 appData.zippyVariants = p;
\r
198 strcpy(first.variants, appData.zippyVariants);
\r
200 srandom(time(NULL));
\r
204 * Routines to implement Zippy talking
\r
208 char *swifties[] = {
\r
209 "i acclaims:", "i admonishes:", "i advertises:", "i advises:",
\r
210 "i advocates:", "i affirms:", "i alleges:", "i anathematizes:",
\r
211 "i animadverts:", "i announces:", "i apostrophizes:",
\r
212 "i appeals:", "i applauds:", "i approves:", "i argues:",
\r
213 "i articulates:", "i asserts:", "i asseverates:", "i attests:",
\r
214 "i avers:", "i avows:", "i baas:", "i babbles:", "i banters:",
\r
215 "i barks:", "i bawls:", "i bays:", "i begs:", "i belches:",
\r
216 "i bellows:", "i belts out:", "i berates:", "i beshrews:",
\r
217 "i blabbers:", "i blabs:", "i blares:", "i blasphemes:",
\r
218 "i blasts:", "i blathers:", "i bleats:", "i blithers:",
\r
219 "i blubbers:", "i blurts out:", "i blusters:", "i boasts:",
\r
220 "i brags:", "i brays:", "i broadcasts:", "i burbles:",
\r
221 "i buzzes:", "i cachinnates:", "i cackles:", "i caterwauls:",
\r
222 "i calumniates:", "i caws:", "i censures:", "i chants:",
\r
223 "i chatters:", "i cheeps:", "i cheers:", "i chides:", "i chins:",
\r
224 "i chirps:", "i chortles:", "i chuckles:", "i claims:",
\r
225 "i clamors:", "i clucks:", "i commands:", "i commends:",
\r
226 "i comments:", "i commiserates:", "i communicates:",
\r
227 "i complains:", "i concludes:", "i confabulates:", "i confesses:",
\r
228 "i coos:", "i coughs:", "i counsels:", "i cries:", "i croaks:",
\r
229 "i crows:", "i curses:", "i daydreams:", "i debates:",
\r
230 "i declaims:", "i declares:", "i delivers:", "i denounces:",
\r
231 "i deposes:", "i directs:", "i discloses:", "i disparages:",
\r
232 "i discourses:", "i divulges:", "i documents:", "i drawls:",
\r
233 "i dreams:", "i drivels:", "i drones:", "i effuses:",
\r
234 /*"i ejaculates:",*/ "i elucidates:", "i emotes:", "i endorses:",
\r
235 "i enthuses:", "i entreats:", "i enunciates:", "i eulogizes:",
\r
236 "i exclaims:", "i execrates:", "i exhorts:", "i expatiates:",
\r
237 "i explains:", "i explicates:", "i explodes:", "i exposes:",
\r
238 "i exposits:", "i expostulates: ",
\r
239 "i expounds:", "i expresses:", "i extols:",
\r
240 "i exults:", "i fantasizes:", "i fibs:", "i filibusters:",
\r
241 "i flatters:", "i flutes:", "i fools:", "i free-associates:",
\r
242 "i fulminates:", "i gabbles:", "i gabs:", "i gasps:",
\r
243 "i giggles:", "i gossips:", "i gripes:", "i groans:", "i growls:",
\r
244 "i grunts:", "i guesses:", "i guffaws:", "i gushes:", "i hails:",
\r
245 "i hallucinates:", "i harangues:", "i harmonizes:", "i hectors:",
\r
246 "i hints:", "i hisses:", "i hollers:", "i honks:", "i hoots:",
\r
247 "i hosannas:", "i howls:", "i hums:", "i hypothecates:",
\r
248 "i hypothesizes:", "i imagines:", "i implies:", "i implores:",
\r
249 "i imprecates:", "i indicates:", "i infers:",
\r
250 "i informs everyone:", "i instructs:", "i interjects:",
\r
251 "i interposes:", "i intimates:", "i intones:", "i introspects:",
\r
252 "i inveighs:", "i jabbers:", "i japes:", "i jests:", "i jibes:",
\r
253 "i jives:", "i jokes:", "i joshes:", "i keens:", "i laments:",
\r
254 "i lauds:", "i laughs:", "i lectures:", "i lies:", "i lilts:",
\r
255 "i lisps:", "i maintains:", "i maledicts:", "i maunders:",
\r
256 "i meows:", "i mewls:", "i mimes:", "i minces:", "i moans:",
\r
257 "i moos:", "i mourns:", "i mouths:", "i mumbles:", "i murmurs:",
\r
258 "i muses:", "i mutters:", "i nags:", "i natters:", "i neighs:",
\r
259 "i notes:", "i nuncupates:", "i objurgates:", "i observes:",
\r
260 "i offers:", "i oinks:", "i opines:", "i orates:", "i orders:",
\r
261 "i panegyrizes:", "i pantomimes:", "i pants:", "i peals:",
\r
262 "i peeps:", "i perorates:", "i persuades:", "i petitions:",
\r
263 "i phonates:", "i pipes up:", "i pitches:", "i pleads:",
\r
264 "i points out:", "i pontificates:", "i postulates:", "i praises:",
\r
265 "i prates:", "i prattles:", "i preaches:", "i prescribes:",
\r
266 "i prevaricates:", "i proclaims:", "i projects:", "i pronounces:",
\r
267 "i proposes:", "i proscribes:", "i quacks:", "i queries:",
\r
268 "i questions:", "i quips:", "i quotes:", "i rages:", "i rambles:",
\r
269 "i rants:", "i raps:", "i rasps:", "i rattles:", "i raves:",
\r
270 "i reacts:", "i recites:", "i recommends:", "i records:",
\r
271 "i reiterates:", "i rejoins:", "i releases:", "i remarks:",
\r
272 "i reminisces:", "i remonstrates:", "i repeats:", "i replies:",
\r
273 "i reports:", "i reprimands:", "i reproaches:", "i reproves:",
\r
274 "i resounds:", "i responds:", "i retorts:", "i reveals:",
\r
275 "i reviles:", "i roars:", "i rumbles:", "i sanctions:",
\r
276 "i satirizes:", "i sauces:", "i scolds:", "i screams:",
\r
277 "i screeches:", "i semaphores:", "i sends:", "i sermonizes:",
\r
278 "i shrieks:", "i sibilates:", "i sighs:", "i signals:",
\r
279 "i signifies:", "i signs:", "i sings:", "i slurs:", "i snaps:",
\r
280 "i snarls:", "i sneezes:", "i snickers:", "i sniggers:",
\r
281 "i snivels:", "i snores:", "i snorts:", "i sobs:",
\r
282 "i soliloquizes:", "i sounds off:", "i sounds out:", "i speaks:",
\r
283 "i spews:", "i spits out:", "i splutters:", "i spoofs:",
\r
284 "i spouts:", "i sputters:", "i squalls:", "i squawks:",
\r
285 "i squeaks:", "i squeals:", "i stammers:", "i states:",
\r
286 "i stresses:", "i stutters:", "i submits:", "i suggests:",
\r
287 "i summarizes:", "i sums up:", "i swears:", "i talks:",
\r
288 "i tattles:", "i teases:", "i telegraphs:", "i testifies:",
\r
289 "i threatens:", "i thunders:", "i titters:", "i tongue-lashes:",
\r
290 "i toots:", "i transcribes:", "i transmits:", "i trills:",
\r
291 "i trumpets:", "i twaddles:", "i tweets:", "i twitters:",
\r
292 "i types:", "i upbraids:", "i urges:", "i utters:", "i ventures:",
\r
293 "i vibrates:", "i vilifies:", "i vituperates:", "i vocalizes:",
\r
294 "i vociferates:", "i voices:", "i waffles:", "i wails:",
\r
295 "i warbles:", "i warns:", "i weeps:", "i wheezes:", "i whimpers:",
\r
296 "i whines:", "i whinnies:", "i whistles:", "i wisecracks:",
\r
297 "i witnesses:", "i woofs:", "i writes:", "i yammers:", "i yawps:",
\r
298 "i yells:", "i yelps:", "i yodels:", "i yowls:", "i zings:",
\r
301 #define MAX_SPEECH 250
\r
303 void Speak(how, whom)
\r
306 static FILE *zipfile = NULL;
\r
307 static struct stat zipstat;
\r
308 char zipbuf[MAX_SPEECH + 1];
\r
309 static time_t lastShout = 0;
\r
315 if (strcmp(how, "shout") == 0) {
\r
316 now = time((time_t *) NULL);
\r
317 if (now - lastShout < 1*60) return;
\r
319 if (appData.zippyUseI) {
\r
320 how = swifties[(unsigned) random() %
\r
321 (sizeof(swifties)/sizeof(char *))];
\r
325 if (zipfile == NULL) {
\r
326 zipfile = fopen(appData.zippyLines, "r");
\r
327 if (zipfile == NULL) {
\r
328 DisplayFatalError("Can't open Zippy lines file", errno, 1);
\r
331 fstat(fileno(zipfile), &zipstat);
\r
335 fseek(zipfile, (unsigned) random() % zipstat.st_size, 0);
\r
338 } while (c != NULLCHAR && c != '^' && c != EOF);
\r
339 if (c == EOF) continue;
\r
340 while ((c = getc(zipfile)) == '\n') ;
\r
341 if (c == EOF) continue;
\r
346 /* Don't use ics_prefix; we need to let FICS expand the alias i -> it,
\r
347 but use the real command "i" on ICC */
\r
348 strcpy(zipbuf, how);
\r
349 strcat(zipbuf, " ");
\r
350 if (whom != NULL) {
\r
351 strcat(zipbuf, whom);
\r
352 strcat(zipbuf, " ");
\r
354 speechlen = strlen(zipbuf);
\r
355 p = zipbuf + speechlen;
\r
357 while (++speechlen < MAX_SPEECH) {
\r
358 if (c == NULLCHAR || c == '^') {
\r
363 } else if (c == '\n') {
\r
367 } while (c == ' ');
\r
368 } else if (c == EOF) {
\r
375 /* Tried to say something too long, or junk at the end of the
\r
376 file. Try something else. */
\r
377 Speak(how, whom); /* tail recursion */
\r
380 int ZippyCalled(str)
\r
383 return ics_handle[0] != NULLCHAR && StrCaseStr(str, ics_handle) != NULL;
\r
386 static char opp_name[128][32];
\r
387 static int num_opps=0;
\r
389 extern ColorClass curColor;
\r
391 static void SetCurColor( ColorClass color )
\r
396 static void ColorizeEx( ColorClass color, int cont )
\r
398 if( appData.colorize ) {
\r
399 Colorize( color, cont );
\r
400 SetCurColor( color );
\r
404 int ZippyControl(buf, i)
\r
409 char reply[MSG_SIZ];
\r
412 #include "trivia.c"
\r
415 /* Possibly reject Crafty as opponent */
\r
416 if (appData.zippyPlay && appData.zippyNoplayCrafty && forwardMostMove < 4
\r
417 && looking_at(buf, i, "* kibitzes: Hello from Crafty"))
\r
419 player = StripHighlightAndTitle(star_match[0]);
\r
420 if ((gameMode == IcsPlayingWhite &&
\r
421 StrCaseCmp(player, gameInfo.black) == 0) ||
\r
422 (gameMode == IcsPlayingBlack &&
\r
423 StrCaseCmp(player, gameInfo.white) == 0)) {
\r
425 sprintf(reply, "%ssay This computer does not play Crafty clones\n%sabort\n%s+noplay %s\n",
\r
426 ics_prefix, ics_prefix, ics_prefix, player);
\r
432 /* If this is a computer, save the name. Then later, once the */
\r
433 /* game is really started, we will send the "computer" notice to */
\r
435 if (appData.zippyPlay &&
\r
436 looking_at(buf, i, "* is in the computer list")) {
\r
438 for (i=0;i<num_opps;i++)
\r
439 if (!strcmp(opp_name[i],star_match[0])) break;
\r
440 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[0]);
\r
442 if (appData.zippyPlay && looking_at(buf, i, "* * is a computer *")) {
\r
444 for (i=0;i<num_opps;i++)
\r
445 if (!strcmp(opp_name[i],star_match[1])) break;
\r
446 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[1]);
\r
449 /* Tells and says */
\r
450 if (appData.zippyPlay &&
\r
451 (looking_at(buf, i, "* offers to be your bughouse partner") ||
\r
452 looking_at(buf, i, "* tells you: [automatic message] I chose you"))) {
\r
453 player = StripHighlightAndTitle(star_match[0]);
\r
454 if (appData.zippyBughouse > 1 && first.initDone) {
\r
455 sprintf(reply, "%spartner %s\n", ics_prefix, player);
\r
457 if (strcmp(zippyPartner, player) != 0) {
\r
458 strcpy(zippyPartner, player);
\r
459 SendToProgram(reply + strlen(ics_prefix), &first);
\r
461 } else if (appData.zippyBughouse > 0) {
\r
462 sprintf(reply, "%sdecline %s\n", ics_prefix, player);
\r
465 sprintf(reply, "%stell %s This computer cannot play bughouse\n",
\r
466 ics_prefix, player);
\r
472 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
473 looking_at(buf, i, "* agrees to be your partner")) {
\r
474 player = StripHighlightAndTitle(star_match[0]);
\r
475 sprintf(reply, "partner %s\n", player);
\r
476 if (strcmp(zippyPartner, player) != 0) {
\r
477 strcpy(zippyPartner, player);
\r
478 SendToProgram(reply, &first);
\r
483 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
484 (looking_at(buf, i, "are no longer *'s partner") ||
\r
486 "* tells you: [automatic message] I'm no longer your"))) {
\r
487 player = StripHighlightAndTitle(star_match[0]);
\r
488 if (strcmp(zippyPartner, player) == 0) {
\r
489 zippyPartner[0] = NULLCHAR;
\r
490 SendToProgram("partner\n", &first);
\r
495 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
496 (looking_at(buf, i, "no longer have a bughouse partner") ||
\r
497 looking_at(buf, i, "partner has disconnected") ||
\r
498 looking_at(buf, i, "partner has just chosen a new partner"))) {
\r
499 zippyPartner[0] = NULLCHAR;
\r
500 SendToProgram("partner\n", &first);
\r
504 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
505 looking_at(buf, i, "* (your partner) tells you: *")) {
\r
506 /* This pattern works on FICS but not ICC */
\r
507 player = StripHighlightAndTitle(star_match[0]);
\r
508 if (strcmp(zippyPartner, player) != 0) {
\r
509 strcpy(zippyPartner, player);
\r
510 sprintf(reply, "partner %s\n", player);
\r
511 SendToProgram(reply, &first);
\r
513 sprintf(reply, "ptell %s\n", star_match[1]);
\r
514 SendToProgram(reply, &first);
\r
518 if (looking_at(buf, i, "* tells you: *") ||
\r
519 looking_at(buf, i, "* says: *"))
\r
521 player = StripHighlightAndTitle(star_match[0]);
\r
522 if (appData.zippyPassword[0] != NULLCHAR &&
\r
523 strncmp(star_match[1], appData.zippyPassword,
\r
524 strlen(appData.zippyPassword)) == 0) {
\r
525 p = star_match[1] + strlen(appData.zippyPassword);
\r
526 while (*p == ' ') p++;
\r
529 } else if (appData.zippyPassword2[0] != NULLCHAR && first.initDone &&
\r
530 strncmp(star_match[1], appData.zippyPassword2,
\r
531 strlen(appData.zippyPassword2)) == 0) {
\r
532 p = star_match[1] + strlen(appData.zippyPassword2);
\r
533 while (*p == ' ') p++;
\r
534 SendToProgram(p, &first);
\r
535 SendToProgram("\n", &first);
\r
536 } else if (appData.zippyWrongPassword[0] != NULLCHAR &&
\r
537 strncmp(star_match[1], appData.zippyWrongPassword,
\r
538 strlen(appData.zippyWrongPassword)) == 0) {
\r
539 p = star_match[1] + strlen(appData.zippyWrongPassword);
\r
540 while (*p == ' ') p++;
\r
541 sprintf(reply, "wrong %s\n", player);
\r
543 } else if (appData.zippyBughouse && first.initDone &&
\r
544 strcmp(player, zippyPartner) == 0) {
\r
545 SendToProgram("ptell ", &first);
\r
546 SendToProgram(star_match[1], &first);
\r
547 SendToProgram("\n", &first);
\r
548 } else if (strncmp(star_match[1], HI, 6) == 0) {
\r
549 extern char* programVersion;
\r
550 sprintf(reply, "%stell %s %s\n",
\r
551 ics_prefix, player, programVersion);
\r
553 } else if (strncmp(star_match[1], "W0W!! ", 6) == 0) {
\r
554 extern char* programVersion;
\r
555 sprintf(reply, "%stell %s %s\n", ics_prefix,
\r
556 player, programVersion);
\r
558 } else if (appData.zippyTalk && (((unsigned) random() % 10) < 9)) {
\r
559 if (strcmp(player, ics_handle) != 0) {
\r
560 Speak("tell", player);
\r
564 ColorizeEx( ColorTell, FALSE );
\r
569 if( appData.colorize && looking_at(buf, i, "* (*) seeking") ) {
\r
570 ColorizeEx(ColorSeek, FALSE);
\r
574 if (looking_at(buf, i, "* spoofs you:")) {
\r
575 player = StripHighlightAndTitle(star_match[0]);
\r
576 sprintf(reply, "spoofedby %s\n", player);
\r
583 int ZippyConverse(buf, i)
\r
587 static char lastgreet[MSG_SIZ];
\r
588 char reply[MSG_SIZ];
\r
591 /* Shouts and emotes */
\r
592 if (looking_at(buf, i, "--> * *") ||
\r
593 looking_at(buf, i, "* shouts: *"))
\r
595 if (appData.zippyTalk) {
\r
596 char *player = StripHighlightAndTitle(star_match[0]);
\r
597 if (strcmp(player, ics_handle) == 0) {
\r
599 } else if (appData.zippyPinhead[0] != NULLCHAR &&
\r
600 StrCaseStr(star_match[1], appData.zippyPinhead) != NULL) {
\r
601 sprintf(reply, "insult %s\n", player);
\r
603 } else if (ZippyCalled(star_match[1])) {
\r
604 Speak("shout", NULL);
\r
608 ColorizeEx(ColorShout, FALSE);
\r
613 if (looking_at(buf, i, "* kibitzes: *")) {
\r
614 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
\r
615 char *player = StripHighlightAndTitle(star_match[0]);
\r
616 if (strcmp(player, ics_handle) != 0) {
\r
617 Speak("kibitz", NULL);
\r
621 ColorizeEx(ColorKibitz, FALSE);
\r
626 if (looking_at(buf, i, "* whispers: *")) {
\r
627 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
\r
628 char *player = StripHighlightAndTitle(star_match[0]);
\r
629 if (strcmp(player, ics_handle) != 0) {
\r
630 Speak("whisper", NULL);
\r
634 ColorizeEx(ColorKibitz, FALSE);
\r
640 if ((looking_at(buf, i, ". * (*:*): *") && isdigit(star_match[1][0])) ||
\r
641 looking_at(buf, i, ". * at *:*: *")) {
\r
642 if (appData.zippyTalk) {
\r
644 char *player = StripHighlightAndTitle(star_match[0]);
\r
646 if (strcmp(player, ics_handle) != 0) {
\r
647 if (((unsigned) random() % 10) < 9)
\r
648 Speak("message", player);
\r
649 f = fopen("zippy.messagelog", "a");
\r
650 fprintf(f, "%s (%s:%s): %s\n", player,
\r
651 star_match[1], star_match[2], star_match[3]);
\r
658 /* Channel tells */
\r
660 if (looking_at(buf, i, "*(*: *")) {
\r
663 if (star_match[0][0] == NULLCHAR ||
\r
664 strchr(star_match[0], ' ') ||
\r
665 strchr(star_match[1], ' ')) {
\r
666 /* Oops, did not want to match this; probably a message */
\r
670 if (appData.zippyTalk) {
\r
671 player = StripHighlightAndTitle(star_match[0]);
\r
672 channel = strrchr(star_match[1], '(');
\r
673 if (channel == NULL) {
\r
674 channel = star_match[1];
\r
678 channel[strlen(channel)-1] = NULLCHAR;
\r
680 /* Always tell to the channel (probability 90%) */
\r
681 if (strcmp(player, ics_handle) != 0 &&
\r
682 ((unsigned) random() % 10) < 9) {
\r
683 Speak("tell", channel);
\r
686 /* Tell to the channel only if someone mentions our name */
\r
687 if (ZippyCalled(star_match[2])) {
\r
688 Speak("tell", channel);
\r
692 ColorizeEx( atoi(channel) == 1 ? ColorChannel1 : ColorChannel, FALSE );
\r
697 if (!appData.zippyTalk) return FALSE;
\r
699 if ((looking_at(buf, i, "You have * message") &&
\r
700 atoi(star_match[0]) != 0) ||
\r
701 looking_at(buf, i, "* has left a message for you") ||
\r
702 looking_at(buf, i, "* just sent you a message")) {
\r
703 sprintf(reply, "%smessages\n%sclearmessages *\n",
\r
704 ics_prefix, ics_prefix);
\r
709 if (looking_at(buf, i, "Notification: * has arrived")) {
\r
710 if (((unsigned) random() % 3) == 0) {
\r
711 char *player = StripHighlightAndTitle(star_match[0]);
\r
712 strcpy(lastgreet, player);
\r
713 sprintf(reply, "greet %s\n", player);
\r
715 Speak("tell", player);
\r
719 if (looking_at(buf, i, "Notification: * has departed")) {
\r
720 if (((unsigned) random() % 3) == 0) {
\r
721 char *player = StripHighlightAndTitle(star_match[0]);
\r
722 sprintf(reply, "farewell %s\n", player);
\r
727 if (looking_at(buf, i, "Not sent -- * is censoring you")) {
\r
728 char *player = StripHighlightAndTitle(star_match[0]);
\r
729 if (strcmp(player, lastgreet) == 0) {
\r
730 sprintf(reply, "%s-notify %s\n", ics_prefix, player);
\r
735 if (looking_at(buf, i, "command is currently turned off")) {
\r
736 appData.zippyUseI = 0;
\r
742 void ZippyGameStart(white, black)
\r
743 char *white, *black;
\r
745 if (!first.initDone) {
\r
746 /* Game is starting prematurely. We can't deal with this */
\r
747 SendToICS(ics_prefix);
\r
748 SendToICS("abort\n");
\r
749 SendToICS(ics_prefix);
\r
750 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
754 if (appData.zippyGameStart[0] != NULLCHAR) {
\r
755 SendToICS(appData.zippyGameStart);
\r
760 void ZippyGameEnd(result, resultDetails)
\r
762 char *resultDetails;
\r
764 if (appData.zippyAcceptOnly[0] == NULLCHAR &&
\r
765 appData.zippyGameEnd[0] != NULLCHAR) {
\r
766 SendToICS(appData.zippyGameEnd);
\r
769 zippyLastGameEnd = time(0);
\r
773 * Routines to implement Zippy playing chess
\r
776 void ZippyHandleChallenge(srated, swild, sbase, sincrement, opponent)
\r
777 char *srated, *swild, *sbase, *sincrement, *opponent;
\r
780 int base, increment, i=0;
\r
782 VariantClass variant;
\r
786 variant = StringToVariant(swild);
\r
787 varname = VariantName(variant);
\r
788 base = atoi(sbase);
\r
789 increment = atoi(sincrement);
\r
791 /* [DM] If icsAnalyzeEngine active we don't accept automatic games */
\r
792 if (appData.icsActive && appData.icsEngineAnalyze) return;
\r
794 /* If desired, you can insert more code here to decline matches
\r
795 based on rated, variant, base, and increment, but it is
\r
796 easier to use the ICS formula feature instead. */
\r
798 if (variant == VariantLoadable) {
\r
800 "%stell %s This computer can't play wild type %s\n%sdecline %s\n",
\r
801 ics_prefix, opponent, swild, ics_prefix, opponent);
\r
805 if (StrStr(appData.zippyVariants, varname) == NULL ||
\r
806 (i=first.protocolVersion) != 1 && StrStr(first.variants, varname) == NULL /* [HGM] zippyvar */
\r
809 "%stell %s This computer can't play %s [%s], only %s\n%sdecline %s\n",
\r
810 ics_prefix, opponent, swild, varname,
\r
811 i ? first.variants : appData.zippyVariants, /* [HGM] zippyvar */
\r
812 ics_prefix, opponent);
\r
817 /* Are we blocking match requests from all but one person? */
\r
818 if (appData.zippyAcceptOnly[0] != NULLCHAR &&
\r
819 StrCaseCmp(opponent, appData.zippyAcceptOnly)) {
\r
820 /* Yes, and this isn't him. Ignore challenge. */
\r
824 /* Too many consecutive games with same opponent? If so, make him
\r
825 wait until someone else has played or a timeout has elapsed. */
\r
826 if (appData.zippyMaxGames &&
\r
827 strcmp(opponent, zippyLastOpp) == 0 &&
\r
828 zippyConsecGames >= appData.zippyMaxGames &&
\r
829 difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
\r
830 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
831 ics_prefix, opponent, zippyConsecGames, ics_handle,
\r
832 appData.zippyReplayTimeout, ics_prefix, opponent);
\r
837 /* Engine not yet initialized or still thinking about last game? */
\r
838 if (!first.initDone || first.lastPing != first.lastPong) {
\r
839 sprintf(buf, "%stell %s I'm not quite ready for a new game yet; try again soon.\n%sdecline %s\n",
\r
840 ics_prefix, opponent, ics_prefix, opponent);
\r
845 sprintf(buf, "%saccept %s\n", ics_prefix, opponent);
\r
847 if (appData.zippyTalk) {
\r
848 Speak("tell", opponent);
\r
853 /* Accept matches */
\r
854 int ZippyMatch(buf, i)
\r
858 if (looking_at(buf, i, "* * match * * requested with * (*)")) {
\r
860 ZippyHandleChallenge(star_match[0], star_match[1],
\r
861 star_match[2], star_match[3],
\r
862 StripHighlightAndTitle(star_match[4]));
\r
866 /* Old FICS 0-increment form */
\r
867 if (looking_at(buf, i, "* * match * requested with * (*)")) {
\r
869 ZippyHandleChallenge(star_match[0], star_match[1],
\r
870 star_match[2], "0",
\r
871 StripHighlightAndTitle(star_match[3]));
\r
875 if (looking_at(buf, i,
\r
876 "* has made an alternate proposal of * * match * *.")) {
\r
878 ZippyHandleChallenge(star_match[1], star_match[2],
\r
879 star_match[3], star_match[4],
\r
880 StripHighlightAndTitle(star_match[0]));
\r
884 /* FICS wild/nonstandard forms */
\r
885 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * Loaded from *")) {
\r
886 /* note: star_match[2] can include "[white] " or "[black] "
\r
887 before our own name. */
\r
888 if(star_match[8] == NULL || star_match[8][0] == 0) // [HGM] chessd: open-source ICS has file on next line
\r
889 ZippyHandleChallenge(star_match[4], star_match[5],
\r
890 star_match[6], star_match[7], StripHighlightAndTitle(star_match[0]));
\r
891 else ZippyHandleChallenge(star_match[4], star_match[8],
\r
892 star_match[6], star_match[7],
\r
893 StripHighlightAndTitle(star_match[0]));
\r
897 if (looking_at(buf, i,
\r
898 "Challenge: * (*) *(*) * * * * : * * Loaded from *")) {
\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[10],
\r
902 star_match[8], star_match[9],
\r
903 StripHighlightAndTitle(star_match[0]));
\r
907 /* Regular forms */
\r
908 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * : * *") |
\r
909 looking_at(buf, i, "Challenge: * (*) *(*) * * * * * *")) {
\r
910 /* note: star_match[2] can include "[white] " or "[black] "
\r
911 before our own name. */
\r
912 ZippyHandleChallenge(star_match[4], star_match[5],
\r
913 star_match[8], star_match[9],
\r
914 StripHighlightAndTitle(star_match[0]));
\r
918 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * *")) {
\r
919 /* note: star_match[2] can include "[white] " or "[black] "
\r
920 before our own name. */
\r
921 ZippyHandleChallenge(star_match[4], star_match[5],
\r
922 star_match[6], star_match[7],
\r
923 StripHighlightAndTitle(star_match[0]));
\r
928 if (ics_type == ICS_ICC) { // [DM]
\r
929 if (looking_at(buf, i, "Your opponent offers you a draw")) {
\r
930 if (first.sendDrawOffers && first.initDone)
\r
931 SendToProgram("draw\n", &first);
\r
935 if (looking_at(buf, i, "offers you a draw")) {
\r
936 if (first.sendDrawOffers && first.initDone) {
\r
937 SendToProgram("draw\n", &first);
\r
943 if (looking_at(buf, i, "requests that the game be aborted") ||
\r
944 looking_at(buf, i, "would like to abort")) {
\r
945 if (appData.zippyAbort ||
\r
946 (gameMode == IcsPlayingWhite && whiteTimeRemaining < 0) ||
\r
947 (gameMode == IcsPlayingBlack && blackTimeRemaining < 0)) {
\r
948 SendToICS(ics_prefix);
\r
949 SendToICS("abort\n");
\r
951 SendToICS(ics_prefix);
\r
952 if (appData.zippyTalk)
\r
953 SendToICS("say Whoa no! I am having FUN!!\n");
\r
955 SendToICS("say Sorry, this computer doesn't accept aborts.\n");
\r
960 if (looking_at(buf, i, "requests adjournment") ||
\r
961 looking_at(buf, i, "would like to adjourn")) {
\r
962 if (appData.zippyAdjourn) {
\r
963 SendToICS(ics_prefix);
\r
964 SendToICS("adjourn\n");
\r
966 SendToICS(ics_prefix);
\r
967 if (appData.zippyTalk)
\r
968 SendToICS("say Whoa no! I am having FUN playing NOW!!\n");
\r
970 SendToICS("say Sorry, this computer doesn't accept adjourns.\n");
\r
978 /* Initialize chess program with data from the first board
\r
979 * of a new or resumed game.
\r
981 void ZippyFirstBoard(moveNum, basetime, increment)
\r
982 int moveNum, basetime, increment;
\r
986 char *opp = (gameMode==IcsPlayingWhite ? gameInfo.black : gameInfo.white);
\r
987 Boolean sentPos = FALSE;
\r
988 char *bookHit = NULL; // [HGM] book
\r
990 if (!first.initDone) {
\r
991 /* Game is starting prematurely. We can't deal with this */
\r
992 SendToICS(ics_prefix);
\r
993 SendToICS("abort\n");
\r
994 SendToICS(ics_prefix);
\r
995 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
999 /* Send the variant command if needed */
\r
1000 if (gameInfo.variant != VariantNormal) {
\r
1001 sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
\r
1002 SendToProgram(buf, &first);
\r
1005 if ((startedFromSetupPosition && moveNum == 0) ||
\r
1006 (!appData.getMoveList && moveNum > 0)) {
\r
1007 SendToProgram("force\n", &first);
\r
1008 SendBoard(&first, moveNum);
\r
1012 sprintf(buf, "level 0 %d %d\n", basetime, increment);
\r
1013 SendToProgram(buf, &first);
\r
1015 /* Count consecutive games from one opponent */
\r
1016 if (strcmp(opp, zippyLastOpp) == 0) {
\r
1017 zippyConsecGames++;
\r
1019 zippyConsecGames = 1;
\r
1020 strcpy(zippyLastOpp, opp);
\r
1023 /* Send the "computer" command if the opponent is in the list
\r
1024 we've been gathering. */
\r
1025 for (w=0; w<num_opps; w++) {
\r
1026 if (!strcmp(opp_name[w], opp)) {
\r
1027 SendToProgram(first.computerString, &first);
\r
1032 /* Ratings might be < 0 which means "we haven't seen a ratings
\r
1033 message from ICS." Send 0 in that case */
\r
1034 w = (gameInfo.whiteRating >= 0) ? gameInfo.whiteRating : 0;
\r
1035 b = (gameInfo.blackRating >= 0) ? gameInfo.blackRating : 0;
\r
1037 firstMove = FALSE;
\r
1038 if (gameMode == IcsPlayingWhite) {
\r
1039 if (first.sendName) {
\r
1040 sprintf(buf, "name %s\n", gameInfo.black);
\r
1041 SendToProgram(buf, &first);
\r
1043 strcpy(ics_handle, gameInfo.white);
\r
1044 sprintf(buf, "rating %d %d\n", w, b);
\r
1045 SendToProgram(buf, &first);
\r
1047 /* Position sent above, engine is in force mode */
\r
1048 if (WhiteOnMove(moveNum)) {
\r
1049 /* Engine is on move now */
\r
1050 if (first.sendTime) {
\r
1051 if (first.useColors) {
\r
1052 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1053 SendTimeRemaining(&first, TRUE);
\r
1054 SendToProgram("white\n", &first);
\r
1056 SendTimeRemaining(&first, TRUE);
\r
1059 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1061 /* Engine's opponent is on move now */
\r
1062 if (first.usePlayother) {
\r
1063 if (first.sendTime) {
\r
1064 SendTimeRemaining(&first, TRUE);
\r
1066 SendToProgram("playother\n", &first);
\r
1068 /* Need to send a "go" after opponent moves */
\r
1073 /* Position not sent above, move list might be sent later */
\r
1074 if (moveNum == 0) {
\r
1075 /* No move list coming; at start of game */
\r
1076 if (first.sendTime) {
\r
1077 if (first.useColors) {
\r
1078 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1079 SendTimeRemaining(&first, TRUE);
\r
1080 SendToProgram("white\n", &first);
\r
1082 SendTimeRemaining(&first, TRUE);
\r
1085 // SendToProgram("go\n", &first);
\r
1086 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1089 } else if (gameMode == IcsPlayingBlack) {
\r
1090 if (first.sendName) {
\r
1091 sprintf(buf, "name %s\n", gameInfo.white);
\r
1092 SendToProgram(buf, &first);
\r
1094 strcpy(ics_handle, gameInfo.black);
\r
1095 sprintf(buf, "rating %d %d\n", b, w);
\r
1096 SendToProgram(buf, &first);
\r
1098 /* Position sent above, engine is in force mode */
\r
1099 if (!WhiteOnMove(moveNum)) {
\r
1100 /* Engine is on move now */
\r
1101 if (first.sendTime) {
\r
1102 if (first.useColors) {
\r
1103 SendToProgram("white\n", &first); /*gnu kludge*/
\r
1104 SendTimeRemaining(&first, FALSE);
\r
1105 SendToProgram("black\n", &first);
\r
1107 SendTimeRemaining(&first, FALSE);
\r
1110 // SendToProgram("go\n", &first);
\r
1111 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1113 /* Engine's opponent is on move now */
\r
1114 if (first.usePlayother) {
\r
1115 if (first.sendTime) {
\r
1116 SendTimeRemaining(&first, FALSE);
\r
1118 SendToProgram("playother\n", &first);
\r
1120 /* Need to send a "go" after opponent moves */
\r
1125 /* Position not sent above, move list might be sent later */
\r
1126 /* Nothing needs to be done here */
\r
1130 if(bookHit) { // [HGM] book: simulate book reply
\r
1131 static char bookMove[MSG_SIZ]; // a bit generous?
\r
1133 programStats.depth = programStats.nodes = programStats.time =
\r
1134 programStats.score = programStats.got_only_move = 0;
\r
1135 sprintf(programStats.movelist, "%s (xbook)", bookHit);
\r
1137 strcpy(bookMove, "move ");
\r
1138 strcat(bookMove, bookHit);
\r
1139 HandleMachineMove(bookMove, &first);
\r
1145 ZippyHoldings(white_holding, black_holding, new_piece)
\r
1146 char *white_holding, *black_holding, *new_piece;
\r
1148 char buf[MSG_SIZ];
\r
1149 if (gameMode != IcsPlayingBlack && gameMode != IcsPlayingWhite) return;
\r
1150 sprintf(buf, "holding [%s] [%s] %s\n",
\r
1151 white_holding, black_holding, new_piece);
\r
1152 SendToProgram(buf, &first);
\r