2 * zippy.c -- Implements Zippy the Pinhead chess player on ICS in XBoard
\r
3 * $Id: zippy.c,v 2.2 2003/11/25 05:25:20 mann Exp $
\r
5 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
6 * Massachusetts. Enhancements Copyright
\r
7 * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software
\r
10 * The following terms apply to Digital Equipment Corporation's copyright
\r
11 * interest in XBoard:
\r
12 * ------------------------------------------------------------------------
\r
13 * All Rights Reserved
\r
15 * Permission to use, copy, modify, and distribute this software and its
\r
16 * documentation for any purpose and without fee is hereby granted,
\r
17 * provided that the above copyright notice appear in all copies and that
\r
18 * both that copyright notice and this permission notice appear in
\r
19 * supporting documentation, and that the name of Digital not be
\r
20 * used in advertising or publicity pertaining to distribution of the
\r
21 * software without specific, written prior permission.
\r
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
30 * ------------------------------------------------------------------------
\r
32 * The following terms apply to the enhanced version of XBoard
\r
33 * distributed by the Free Software Foundation:
\r
34 * ------------------------------------------------------------------------
\r
36 * GNU XBoard is free software: you can redistribute it and/or modify
\r
37 * it under the terms of the GNU General Public License as published by
\r
38 * the Free Software Foundation, either version 3 of the License, or (at
\r
39 * your option) any later version.
\r
41 * GNU XBoard is distributed in the hope that it will be useful, but
\r
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
44 * General Public License for more details.
\r
46 * You should have received a copy of the GNU General Public License
\r
47 * along with this program. If not, see http://www.gnu.org/licenses/.
\r
49 *------------------------------------------------------------------------
\r
50 ** See the file ChangeLog for a revision history. */
\r
56 #include <sys/types.h>
\r
57 #include <sys/stat.h>
\r
61 # include <stdlib.h>
\r
62 # include <string.h>
\r
63 #else /* not STDC_HEADERS */
\r
64 extern char *getenv();
\r
66 # include <string.h>
\r
67 # else /* not HAVE_STRING_H */
\r
68 # include <strings.h>
\r
69 # endif /* not HAVE_STRING_H */
\r
70 #endif /* not STDC_HEADERS */
\r
72 #if TIME_WITH_SYS_TIME
\r
73 # include <sys/time.h>
\r
76 # if HAVE_SYS_TIME_H
\r
77 # include <sys/time.h>
\r
85 # include <unistd.h>
\r
90 #include "frontend.h"
\r
91 #include "backend.h"
\r
92 #include "backendz.h"
\r
94 char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book
\r
95 void HandleMachineMove P((char *message, ChessProgramState *cps));
\r
97 static char zippyPartner[MSG_SIZ];
\r
98 static char zippyLastOpp[MSG_SIZ];
\r
99 static char zippyOffender[MSG_SIZ]; // [HGM] aborter
\r
100 static int zippyConsecGames;
\r
101 static time_t zippyLastGameEnd;
\r
103 extern void mysrandom(unsigned int seed);
\r
104 extern int myrandom(void);
\r
110 /* Get name of Zippy lines file */
\r
111 p = getenv("ZIPPYLINES");
\r
113 appData.zippyLines = p;
\r
116 /* Get word that Zippy thinks is insulting */
\r
117 p = getenv("ZIPPYPINHEAD");
\r
119 appData.zippyPinhead = p;
\r
122 /* What password is used for remote control? */
\r
123 p = getenv("ZIPPYPASSWORD");
\r
125 appData.zippyPassword = p;
\r
128 /* What password is used for remote commands to gnuchess? */
\r
129 p = getenv("ZIPPYPASSWORD2");
\r
131 appData.zippyPassword2 = p;
\r
134 /* Joke feature for people who try an old password */
\r
135 p = getenv("ZIPPYWRONGPASSWORD");
\r
137 appData.zippyWrongPassword = p;
\r
140 /* While testing, I want to accept challenges from only one person
\r
141 (namely, my "anonymous" account), so I set an environment
\r
142 variable ZIPPYACCEPTONLY. */
\r
143 p = getenv("ZIPPYACCEPTONLY");
\r
145 appData.zippyAcceptOnly = p;
\r
148 /* Should Zippy use "i" command? */
\r
149 /* Defaults to 1=true */
\r
150 p = getenv("ZIPPYUSEI");
\r
152 appData.zippyUseI = atoi(p);
\r
155 /* How does Zippy handle bughouse partnering? */
\r
156 /* 0=say we can't play, 1=manual partnering, 2=auto partnering */
\r
157 p = getenv("ZIPPYBUGHOUSE");
\r
159 appData.zippyBughouse = atoi(p);
\r
162 /* Does Zippy abort games with Crafty? */
\r
163 /* Defaults to 0=false */
\r
164 p = getenv("ZIPPYNOPLAYCRAFTY");
\r
166 appData.zippyNoplayCrafty = atoi(p);
\r
169 /* What ICS command does Zippy send at game end? Default: "gameend". */
\r
170 p = getenv("ZIPPYGAMEEND");
\r
172 appData.zippyGameEnd = p;
\r
175 /* What ICS command does Zippy send at game start? Default: none. */
\r
176 p = getenv("ZIPPYGAMESTART");
\r
178 appData.zippyGameStart = p;
\r
181 /* Should Zippy accept adjourns? */
\r
182 /* Defaults to 0=false */
\r
183 p = getenv("ZIPPYADJOURN");
\r
185 appData.zippyAdjourn = atoi(p);
\r
188 /* Should Zippy accept aborts? */
\r
189 /* Defaults to 0=false */
\r
190 p = getenv("ZIPPYABORT");
\r
192 appData.zippyAbort = atoi(p);
\r
195 /* Should Zippy play chess variants (besides bughouse)? */
\r
196 p = getenv("ZIPPYVARIANTS");
\r
198 appData.zippyVariants = p;
\r
200 strcpy(first.variants, appData.zippyVariants);
\r
202 srandom(time(NULL));
\r
206 * Routines to implement Zippy talking
\r
210 char *swifties[] = {
\r
211 "i acclaims:", "i admonishes:", "i advertises:", "i advises:",
\r
212 "i advocates:", "i affirms:", "i alleges:", "i anathematizes:",
\r
213 "i animadverts:", "i announces:", "i apostrophizes:",
\r
214 "i appeals:", "i applauds:", "i approves:", "i argues:",
\r
215 "i articulates:", "i asserts:", "i asseverates:", "i attests:",
\r
216 "i avers:", "i avows:", "i baas:", "i babbles:", "i banters:",
\r
217 "i barks:", "i bawls:", "i bays:", "i begs:", "i belches:",
\r
218 "i bellows:", "i belts out:", "i berates:", "i beshrews:",
\r
219 "i blabbers:", "i blabs:", "i blares:", "i blasphemes:",
\r
220 "i blasts:", "i blathers:", "i bleats:", "i blithers:",
\r
221 "i blubbers:", "i blurts out:", "i blusters:", "i boasts:",
\r
222 "i brags:", "i brays:", "i broadcasts:", "i burbles:",
\r
223 "i buzzes:", "i cachinnates:", "i cackles:", "i caterwauls:",
\r
224 "i calumniates:", "i caws:", "i censures:", "i chants:",
\r
225 "i chatters:", "i cheeps:", "i cheers:", "i chides:", "i chins:",
\r
226 "i chirps:", "i chortles:", "i chuckles:", "i claims:",
\r
227 "i clamors:", "i clucks:", "i commands:", "i commends:",
\r
228 "i comments:", "i commiserates:", "i communicates:",
\r
229 "i complains:", "i concludes:", "i confabulates:", "i confesses:",
\r
230 "i coos:", "i coughs:", "i counsels:", "i cries:", "i croaks:",
\r
231 "i crows:", "i curses:", "i daydreams:", "i debates:",
\r
232 "i declaims:", "i declares:", "i delivers:", "i denounces:",
\r
233 "i deposes:", "i directs:", "i discloses:", "i disparages:",
\r
234 "i discourses:", "i divulges:", "i documents:", "i drawls:",
\r
235 "i dreams:", "i drivels:", "i drones:", "i effuses:",
\r
236 /*"i ejaculates:",*/ "i elucidates:", "i emotes:", "i endorses:",
\r
237 "i enthuses:", "i entreats:", "i enunciates:", "i eulogizes:",
\r
238 "i exclaims:", "i execrates:", "i exhorts:", "i expatiates:",
\r
239 "i explains:", "i explicates:", "i explodes:", "i exposes:",
\r
240 "i exposits:", "i expostulates: ",
\r
241 "i expounds:", "i expresses:", "i extols:",
\r
242 "i exults:", "i fantasizes:", "i fibs:", "i filibusters:",
\r
243 "i flatters:", "i flutes:", "i fools:", "i free-associates:",
\r
244 "i fulminates:", "i gabbles:", "i gabs:", "i gasps:",
\r
245 "i giggles:", "i gossips:", "i gripes:", "i groans:", "i growls:",
\r
246 "i grunts:", "i guesses:", "i guffaws:", "i gushes:", "i hails:",
\r
247 "i hallucinates:", "i harangues:", "i harmonizes:", "i hectors:",
\r
248 "i hints:", "i hisses:", "i hollers:", "i honks:", "i hoots:",
\r
249 "i hosannas:", "i howls:", "i hums:", "i hypothecates:",
\r
250 "i hypothesizes:", "i imagines:", "i implies:", "i implores:",
\r
251 "i imprecates:", "i indicates:", "i infers:",
\r
252 "i informs everyone:", "i instructs:", "i interjects:",
\r
253 "i interposes:", "i intimates:", "i intones:", "i introspects:",
\r
254 "i inveighs:", "i jabbers:", "i japes:", "i jests:", "i jibes:",
\r
255 "i jives:", "i jokes:", "i joshes:", "i keens:", "i laments:",
\r
256 "i lauds:", "i laughs:", "i lectures:", "i lies:", "i lilts:",
\r
257 "i lisps:", "i maintains:", "i maledicts:", "i maunders:",
\r
258 "i meows:", "i mewls:", "i mimes:", "i minces:", "i moans:",
\r
259 "i moos:", "i mourns:", "i mouths:", "i mumbles:", "i murmurs:",
\r
260 "i muses:", "i mutters:", "i nags:", "i natters:", "i neighs:",
\r
261 "i notes:", "i nuncupates:", "i objurgates:", "i observes:",
\r
262 "i offers:", "i oinks:", "i opines:", "i orates:", "i orders:",
\r
263 "i panegyrizes:", "i pantomimes:", "i pants:", "i peals:",
\r
264 "i peeps:", "i perorates:", "i persuades:", "i petitions:",
\r
265 "i phonates:", "i pipes up:", "i pitches:", "i pleads:",
\r
266 "i points out:", "i pontificates:", "i postulates:", "i praises:",
\r
267 "i prates:", "i prattles:", "i preaches:", "i prescribes:",
\r
268 "i prevaricates:", "i proclaims:", "i projects:", "i pronounces:",
\r
269 "i proposes:", "i proscribes:", "i quacks:", "i queries:",
\r
270 "i questions:", "i quips:", "i quotes:", "i rages:", "i rambles:",
\r
271 "i rants:", "i raps:", "i rasps:", "i rattles:", "i raves:",
\r
272 "i reacts:", "i recites:", "i recommends:", "i records:",
\r
273 "i reiterates:", "i rejoins:", "i releases:", "i remarks:",
\r
274 "i reminisces:", "i remonstrates:", "i repeats:", "i replies:",
\r
275 "i reports:", "i reprimands:", "i reproaches:", "i reproves:",
\r
276 "i resounds:", "i responds:", "i retorts:", "i reveals:",
\r
277 "i reviles:", "i roars:", "i rumbles:", "i sanctions:",
\r
278 "i satirizes:", "i sauces:", "i scolds:", "i screams:",
\r
279 "i screeches:", "i semaphores:", "i sends:", "i sermonizes:",
\r
280 "i shrieks:", "i sibilates:", "i sighs:", "i signals:",
\r
281 "i signifies:", "i signs:", "i sings:", "i slurs:", "i snaps:",
\r
282 "i snarls:", "i sneezes:", "i snickers:", "i sniggers:",
\r
283 "i snivels:", "i snores:", "i snorts:", "i sobs:",
\r
284 "i soliloquizes:", "i sounds off:", "i sounds out:", "i speaks:",
\r
285 "i spews:", "i spits out:", "i splutters:", "i spoofs:",
\r
286 "i spouts:", "i sputters:", "i squalls:", "i squawks:",
\r
287 "i squeaks:", "i squeals:", "i stammers:", "i states:",
\r
288 "i stresses:", "i stutters:", "i submits:", "i suggests:",
\r
289 "i summarizes:", "i sums up:", "i swears:", "i talks:",
\r
290 "i tattles:", "i teases:", "i telegraphs:", "i testifies:",
\r
291 "i threatens:", "i thunders:", "i titters:", "i tongue-lashes:",
\r
292 "i toots:", "i transcribes:", "i transmits:", "i trills:",
\r
293 "i trumpets:", "i twaddles:", "i tweets:", "i twitters:",
\r
294 "i types:", "i upbraids:", "i urges:", "i utters:", "i ventures:",
\r
295 "i vibrates:", "i vilifies:", "i vituperates:", "i vocalizes:",
\r
296 "i vociferates:", "i voices:", "i waffles:", "i wails:",
\r
297 "i warbles:", "i warns:", "i weeps:", "i wheezes:", "i whimpers:",
\r
298 "i whines:", "i whinnies:", "i whistles:", "i wisecracks:",
\r
299 "i witnesses:", "i woofs:", "i writes:", "i yammers:", "i yawps:",
\r
300 "i yells:", "i yelps:", "i yodels:", "i yowls:", "i zings:",
\r
303 #define MAX_SPEECH 250
\r
305 void Speak(how, whom)
\r
308 static FILE *zipfile = NULL;
\r
309 static struct stat zipstat;
\r
310 char zipbuf[MAX_SPEECH + 1];
\r
311 static time_t lastShout = 0;
\r
317 if (strcmp(how, "shout") == 0) {
\r
318 now = time((time_t *) NULL);
\r
319 if (now - lastShout < 1*60) return;
\r
321 if (appData.zippyUseI) {
\r
322 how = swifties[(unsigned) random() %
\r
323 (sizeof(swifties)/sizeof(char *))];
\r
327 if (zipfile == NULL) {
\r
328 zipfile = fopen(appData.zippyLines, "r");
\r
329 if (zipfile == NULL) {
\r
330 DisplayFatalError("Can't open Zippy lines file", errno, 1);
\r
333 fstat(fileno(zipfile), &zipstat);
\r
337 fseek(zipfile, (unsigned) random() % zipstat.st_size, 0);
\r
340 } while (c != NULLCHAR && c != '^' && c != EOF);
\r
341 if (c == EOF) continue;
\r
342 while ((c = getc(zipfile)) == '\n') ;
\r
343 if (c == EOF) continue;
\r
348 /* Don't use ics_prefix; we need to let FICS expand the alias i -> it,
\r
349 but use the real command "i" on ICC */
\r
350 strcpy(zipbuf, how);
\r
351 strcat(zipbuf, " ");
\r
352 if (whom != NULL) {
\r
353 strcat(zipbuf, whom);
\r
354 strcat(zipbuf, " ");
\r
356 speechlen = strlen(zipbuf);
\r
357 p = zipbuf + speechlen;
\r
359 while (++speechlen < MAX_SPEECH) {
\r
360 if (c == NULLCHAR || c == '^') {
\r
365 } else if (c == '\n') {
\r
369 } while (c == ' ');
\r
370 } else if (c == EOF) {
\r
377 /* Tried to say something too long, or junk at the end of the
\r
378 file. Try something else. */
\r
379 Speak(how, whom); /* tail recursion */
\r
382 int ZippyCalled(str)
\r
385 return ics_handle[0] != NULLCHAR && StrCaseStr(str, ics_handle) != NULL;
\r
388 static char opp_name[128][32];
\r
389 static int num_opps=0;
\r
391 extern ColorClass curColor;
\r
393 static void SetCurColor( ColorClass color )
\r
398 static void ColorizeEx( ColorClass color, int cont )
\r
400 if( appData.colorize ) {
\r
401 Colorize( color, cont );
\r
402 SetCurColor( color );
\r
406 int ZippyControl(buf, i)
\r
411 char reply[MSG_SIZ];
\r
414 #include "trivia.c"
\r
417 /* Possibly reject Crafty as opponent */
\r
418 if (appData.zippyPlay && appData.zippyNoplayCrafty && forwardMostMove < 4
\r
419 && looking_at(buf, i, "* kibitzes: Hello from Crafty"))
\r
421 player = StripHighlightAndTitle(star_match[0]);
\r
422 if ((gameMode == IcsPlayingWhite &&
\r
423 StrCaseCmp(player, gameInfo.black) == 0) ||
\r
424 (gameMode == IcsPlayingBlack &&
\r
425 StrCaseCmp(player, gameInfo.white) == 0)) {
\r
427 sprintf(reply, "%ssay This computer does not play Crafty clones\n%sabort\n%s+noplay %s\n",
\r
428 ics_prefix, ics_prefix, ics_prefix, player);
\r
434 /* If this is a computer, save the name. Then later, once the */
\r
435 /* game is really started, we will send the "computer" notice to */
\r
437 if (appData.zippyPlay &&
\r
438 looking_at(buf, i, "* is in the computer list")) {
\r
440 for (i=0;i<num_opps;i++)
\r
441 if (!strcmp(opp_name[i],star_match[0])) break;
\r
442 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[0]);
\r
444 if (appData.zippyPlay && looking_at(buf, i, "* * is a computer *")) {
\r
446 for (i=0;i<num_opps;i++)
\r
447 if (!strcmp(opp_name[i],star_match[1])) break;
\r
448 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[1]);
\r
451 /* Tells and says */
\r
452 if (appData.zippyPlay &&
\r
453 (looking_at(buf, i, "* offers to be your bughouse partner") ||
\r
454 looking_at(buf, i, "* tells you: [automatic message] I chose you"))) {
\r
455 player = StripHighlightAndTitle(star_match[0]);
\r
456 if (appData.zippyBughouse > 1 && first.initDone) {
\r
457 sprintf(reply, "%spartner %s\n", ics_prefix, player);
\r
459 if (strcmp(zippyPartner, player) != 0) {
\r
460 strcpy(zippyPartner, player);
\r
461 SendToProgram(reply + strlen(ics_prefix), &first);
\r
463 } else if (appData.zippyBughouse > 0) {
\r
464 sprintf(reply, "%sdecline %s\n", ics_prefix, player);
\r
467 sprintf(reply, "%stell %s This computer cannot play bughouse\n",
\r
468 ics_prefix, player);
\r
474 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
475 looking_at(buf, i, "* agrees to be your partner")) {
\r
476 player = StripHighlightAndTitle(star_match[0]);
\r
477 sprintf(reply, "partner %s\n", player);
\r
478 if (strcmp(zippyPartner, player) != 0) {
\r
479 strcpy(zippyPartner, player);
\r
480 SendToProgram(reply, &first);
\r
485 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
486 (looking_at(buf, i, "are no longer *'s partner") ||
\r
488 "* tells you: [automatic message] I'm no longer your"))) {
\r
489 player = StripHighlightAndTitle(star_match[0]);
\r
490 if (strcmp(zippyPartner, player) == 0) {
\r
491 zippyPartner[0] = NULLCHAR;
\r
492 SendToProgram("partner\n", &first);
\r
497 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
498 (looking_at(buf, i, "no longer have a bughouse partner") ||
\r
499 looking_at(buf, i, "partner has disconnected") ||
\r
500 looking_at(buf, i, "partner has just chosen a new partner"))) {
\r
501 zippyPartner[0] = NULLCHAR;
\r
502 SendToProgram("partner\n", &first);
\r
506 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
507 looking_at(buf, i, "* (your partner) tells you: *")) {
\r
508 /* This pattern works on FICS but not ICC */
\r
509 player = StripHighlightAndTitle(star_match[0]);
\r
510 if (strcmp(zippyPartner, player) != 0) {
\r
511 strcpy(zippyPartner, player);
\r
512 sprintf(reply, "partner %s\n", player);
\r
513 SendToProgram(reply, &first);
\r
515 sprintf(reply, "ptell %s\n", star_match[1]);
\r
516 SendToProgram(reply, &first);
\r
520 if (looking_at(buf, i, "* tells you: *") ||
\r
521 looking_at(buf, i, "* says: *"))
\r
523 player = StripHighlightAndTitle(star_match[0]);
\r
524 if (appData.zippyPassword[0] != NULLCHAR &&
\r
525 strncmp(star_match[1], appData.zippyPassword,
\r
526 strlen(appData.zippyPassword)) == 0) {
\r
527 p = star_match[1] + strlen(appData.zippyPassword);
\r
528 while (*p == ' ') p++;
\r
531 } else if (appData.zippyPassword2[0] != NULLCHAR && first.initDone &&
\r
532 strncmp(star_match[1], appData.zippyPassword2,
\r
533 strlen(appData.zippyPassword2)) == 0) {
\r
534 p = star_match[1] + strlen(appData.zippyPassword2);
\r
535 while (*p == ' ') p++;
\r
536 SendToProgram(p, &first);
\r
537 SendToProgram("\n", &first);
\r
538 } else if (appData.zippyWrongPassword[0] != NULLCHAR &&
\r
539 strncmp(star_match[1], appData.zippyWrongPassword,
\r
540 strlen(appData.zippyWrongPassword)) == 0) {
\r
541 p = star_match[1] + strlen(appData.zippyWrongPassword);
\r
542 while (*p == ' ') p++;
\r
543 sprintf(reply, "wrong %s\n", player);
\r
545 } else if (appData.zippyBughouse && first.initDone &&
\r
546 strcmp(player, zippyPartner) == 0) {
\r
547 SendToProgram("ptell ", &first);
\r
548 SendToProgram(star_match[1], &first);
\r
549 SendToProgram("\n", &first);
\r
550 } else if (strncmp(star_match[1], HI, 6) == 0) {
\r
551 extern char* programVersion;
\r
552 sprintf(reply, "%stell %s %s\n",
\r
553 ics_prefix, player, programVersion);
\r
555 } else if (strncmp(star_match[1], "W0W!! ", 6) == 0) {
\r
556 extern char* programVersion;
\r
557 sprintf(reply, "%stell %s %s\n", ics_prefix,
\r
558 player, programVersion);
\r
560 } else if (appData.zippyTalk && (((unsigned) random() % 10) < 9)) {
\r
561 if (strcmp(player, ics_handle) != 0) {
\r
562 Speak("tell", player);
\r
566 ColorizeEx( ColorTell, FALSE );
\r
571 if( appData.colorize && looking_at(buf, i, "* (*) seeking") ) {
\r
572 ColorizeEx(ColorSeek, FALSE);
\r
576 if (looking_at(buf, i, "* spoofs you:")) {
\r
577 player = StripHighlightAndTitle(star_match[0]);
\r
578 sprintf(reply, "spoofedby %s\n", player);
\r
585 int ZippyConverse(buf, i)
\r
589 static char lastgreet[MSG_SIZ];
\r
590 char reply[MSG_SIZ];
\r
593 /* Shouts and emotes */
\r
594 if (looking_at(buf, i, "--> * *") ||
\r
595 looking_at(buf, i, "* shouts: *"))
\r
597 if (appData.zippyTalk) {
\r
598 char *player = StripHighlightAndTitle(star_match[0]);
\r
599 if (strcmp(player, ics_handle) == 0) {
\r
601 } else if (appData.zippyPinhead[0] != NULLCHAR &&
\r
602 StrCaseStr(star_match[1], appData.zippyPinhead) != NULL) {
\r
603 sprintf(reply, "insult %s\n", player);
\r
605 } else if (ZippyCalled(star_match[1])) {
\r
606 Speak("shout", NULL);
\r
610 ColorizeEx(ColorShout, FALSE);
\r
615 if (looking_at(buf, i, "* kibitzes: *")) {
\r
616 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
\r
617 char *player = StripHighlightAndTitle(star_match[0]);
\r
618 if (strcmp(player, ics_handle) != 0) {
\r
619 Speak("kibitz", NULL);
\r
623 ColorizeEx(ColorKibitz, FALSE);
\r
628 if (looking_at(buf, i, "* whispers: *")) {
\r
629 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
\r
630 char *player = StripHighlightAndTitle(star_match[0]);
\r
631 if (strcmp(player, ics_handle) != 0) {
\r
632 Speak("whisper", NULL);
\r
636 ColorizeEx(ColorKibitz, FALSE);
\r
642 if ((looking_at(buf, i, ". * (*:*): *") && isdigit(star_match[1][0])) ||
\r
643 looking_at(buf, i, ". * at *:*: *")) {
\r
644 if (appData.zippyTalk) {
\r
646 char *player = StripHighlightAndTitle(star_match[0]);
\r
648 if (strcmp(player, ics_handle) != 0) {
\r
649 if (((unsigned) random() % 10) < 9)
\r
650 Speak("message", player);
\r
651 f = fopen("zippy.messagelog", "a");
\r
652 fprintf(f, "%s (%s:%s): %s\n", player,
\r
653 star_match[1], star_match[2], star_match[3]);
\r
660 /* Channel tells */
\r
662 if (looking_at(buf, i, "*(*: *")) {
\r
665 if (star_match[0][0] == NULLCHAR ||
\r
666 strchr(star_match[0], ' ') ||
\r
667 strchr(star_match[1], ' ')) {
\r
668 /* Oops, did not want to match this; probably a message */
\r
672 if (appData.zippyTalk) {
\r
673 player = StripHighlightAndTitle(star_match[0]);
\r
674 channel = strrchr(star_match[1], '(');
\r
675 if (channel == NULL) {
\r
676 channel = star_match[1];
\r
680 channel[strlen(channel)-1] = NULLCHAR;
\r
682 /* Always tell to the channel (probability 90%) */
\r
683 if (strcmp(player, ics_handle) != 0 &&
\r
684 ((unsigned) random() % 10) < 9) {
\r
685 Speak("tell", channel);
\r
688 /* Tell to the channel only if someone mentions our name */
\r
689 if (ZippyCalled(star_match[2])) {
\r
690 Speak("tell", channel);
\r
694 ColorizeEx( atoi(channel) == 1 ? ColorChannel1 : ColorChannel, FALSE );
\r
699 if (!appData.zippyTalk) return FALSE;
\r
701 if ((looking_at(buf, i, "You have * message") &&
\r
702 atoi(star_match[0]) != 0) ||
\r
703 looking_at(buf, i, "* has left a message for you") ||
\r
704 looking_at(buf, i, "* just sent you a message")) {
\r
705 sprintf(reply, "%smessages\n%sclearmessages *\n",
\r
706 ics_prefix, ics_prefix);
\r
711 if (looking_at(buf, i, "Notification: * has arrived")) {
\r
712 if (((unsigned) random() % 3) == 0) {
\r
713 char *player = StripHighlightAndTitle(star_match[0]);
\r
714 strcpy(lastgreet, player);
\r
715 sprintf(reply, "greet %s\n", player);
\r
717 Speak("tell", player);
\r
721 if (looking_at(buf, i, "Notification: * has departed")) {
\r
722 if (((unsigned) random() % 3) == 0) {
\r
723 char *player = StripHighlightAndTitle(star_match[0]);
\r
724 sprintf(reply, "farewell %s\n", player);
\r
729 if (looking_at(buf, i, "Not sent -- * is censoring you")) {
\r
730 char *player = StripHighlightAndTitle(star_match[0]);
\r
731 if (strcmp(player, lastgreet) == 0) {
\r
732 sprintf(reply, "%s-notify %s\n", ics_prefix, player);
\r
737 if (looking_at(buf, i, "command is currently turned off")) {
\r
738 appData.zippyUseI = 0;
\r
744 void ZippyGameStart(white, black)
\r
745 char *white, *black;
\r
747 if (!first.initDone) {
\r
748 /* Game is starting prematurely. We can't deal with this */
\r
749 SendToICS(ics_prefix);
\r
750 SendToICS("abort\n");
\r
751 SendToICS(ics_prefix);
\r
752 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
756 if (appData.zippyGameStart[0] != NULLCHAR) {
\r
757 SendToICS(appData.zippyGameStart);
\r
762 void ZippyGameEnd(result, resultDetails)
\r
764 char *resultDetails;
\r
766 if (appData.zippyAcceptOnly[0] == NULLCHAR &&
\r
767 appData.zippyGameEnd[0] != NULLCHAR) {
\r
768 SendToICS(appData.zippyGameEnd);
\r
771 zippyLastGameEnd = time(0);
\r
772 if(forwardMostMove < appData.zippyShortGame)
\r
773 strcpy(zippyOffender, zippyLastOpp); else zippyOffender[0] = 0; // [HGM] aborter
\r
777 * Routines to implement Zippy playing chess
\r
780 void ZippyHandleChallenge(srated, swild, sbase, sincrement, opponent)
\r
781 char *srated, *swild, *sbase, *sincrement, *opponent;
\r
784 int base, increment, i=0;
\r
786 VariantClass variant;
\r
790 variant = StringToVariant(swild);
\r
791 varname = VariantName(variant);
\r
792 base = atoi(sbase);
\r
793 increment = atoi(sincrement);
\r
795 /* [DM] If icsAnalyzeEngine active we don't accept automatic games */
\r
796 if (appData.icsActive && appData.icsEngineAnalyze) return;
\r
798 /* If desired, you can insert more code here to decline matches
\r
799 based on rated, variant, base, and increment, but it is
\r
800 easier to use the ICS formula feature instead. */
\r
802 if (variant == VariantLoadable) {
\r
804 "%stell %s This computer can't play wild type %s\n%sdecline %s\n",
\r
805 ics_prefix, opponent, swild, ics_prefix, opponent);
\r
809 if (StrStr(appData.zippyVariants, varname) == NULL ||
\r
810 ((i=first.protocolVersion) != 1 && StrStr(first.variants, varname) == NULL) /* [HGM] zippyvar */
\r
813 "%stell %s This computer can't play %s [%s], only %s\n%sdecline %s\n",
\r
814 ics_prefix, opponent, swild, varname,
\r
815 i ? first.variants : appData.zippyVariants, /* [HGM] zippyvar */
\r
816 ics_prefix, opponent);
\r
821 /* Are we blocking match requests from all but one person? */
\r
822 if (appData.zippyAcceptOnly[0] != NULLCHAR &&
\r
823 StrCaseCmp(opponent, appData.zippyAcceptOnly)) {
\r
824 /* Yes, and this isn't him. Ignore challenge. */
\r
828 /* Too many consecutive games with same opponent? If so, make him
\r
829 wait until someone else has played or a timeout has elapsed. */
\r
830 if (appData.zippyMaxGames &&
\r
831 strcmp(opponent, zippyLastOpp) == 0 &&
\r
832 zippyConsecGames >= appData.zippyMaxGames &&
\r
833 difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
\r
834 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
835 ics_prefix, opponent, zippyConsecGames, ics_handle,
\r
836 appData.zippyReplayTimeout, ics_prefix, opponent);
\r
841 /* [HGM] aborter: opponent is cheater that aborts games he doesn't like on first move. Make him wait */
\r
842 if (strcmp(opponent, zippyOffender) == 0 &&
\r
843 difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
\r
844 sprintf(buf, "%stell %s Sorry, your previous game against %s was rather short. "
\r
845 " It will wait %d seconds to see if a tougher opponent comes along.\n%sdecline %s\n",
\r
846 ics_prefix, opponent, ics_handle,
\r
847 appData.zippyReplayTimeout, ics_prefix, opponent);
\r
852 /* Engine not yet initialized or still thinking about last game? */
\r
853 if (!first.initDone || first.lastPing != first.lastPong) {
\r
854 sprintf(buf, "%stell %s I'm not quite ready for a new game yet; try again soon.\n%sdecline %s\n",
\r
855 ics_prefix, opponent, ics_prefix, opponent);
\r
860 sprintf(buf, "%saccept %s\n", ics_prefix, opponent);
\r
862 if (appData.zippyTalk) {
\r
863 Speak("tell", opponent);
\r
868 /* Accept matches */
\r
869 int ZippyMatch(buf, i)
\r
873 if (looking_at(buf, i, "* * match * * requested with * (*)")) {
\r
875 ZippyHandleChallenge(star_match[0], star_match[1],
\r
876 star_match[2], star_match[3],
\r
877 StripHighlightAndTitle(star_match[4]));
\r
881 /* Old FICS 0-increment form */
\r
882 if (looking_at(buf, i, "* * match * requested with * (*)")) {
\r
884 ZippyHandleChallenge(star_match[0], star_match[1],
\r
885 star_match[2], "0",
\r
886 StripHighlightAndTitle(star_match[3]));
\r
890 if (looking_at(buf, i,
\r
891 "* has made an alternate proposal of * * match * *.")) {
\r
893 ZippyHandleChallenge(star_match[1], star_match[2],
\r
894 star_match[3], star_match[4],
\r
895 StripHighlightAndTitle(star_match[0]));
\r
899 /* FICS wild/nonstandard forms */
\r
900 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * Loaded from *")) {
\r
901 /* note: star_match[2] can include "[white] " or "[black] "
\r
902 before our own name. */
\r
903 if(star_match[8] == NULL || star_match[8][0] == 0) // [HGM] chessd: open-source ICS has file on next line
\r
904 ZippyHandleChallenge(star_match[4], star_match[5],
\r
905 star_match[6], star_match[7], StripHighlightAndTitle(star_match[0]));
\r
906 else ZippyHandleChallenge(star_match[4], star_match[8],
\r
907 star_match[6], star_match[7],
\r
908 StripHighlightAndTitle(star_match[0]));
\r
912 if (looking_at(buf, i,
\r
913 "Challenge: * (*) *(*) * * * * : * * Loaded from *")) {
\r
914 /* note: star_match[2] can include "[white] " or "[black] "
\r
915 before our own name. */
\r
916 ZippyHandleChallenge(star_match[4], star_match[10],
\r
917 star_match[8], star_match[9],
\r
918 StripHighlightAndTitle(star_match[0]));
\r
922 /* Regular forms */
\r
923 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * : * *") |
\r
924 looking_at(buf, i, "Challenge: * (*) *(*) * * * * * *")) {
\r
925 /* note: star_match[2] can include "[white] " or "[black] "
\r
926 before our own name. */
\r
927 ZippyHandleChallenge(star_match[4], star_match[5],
\r
928 star_match[8], star_match[9],
\r
929 StripHighlightAndTitle(star_match[0]));
\r
933 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * *")) {
\r
934 /* note: star_match[2] can include "[white] " or "[black] "
\r
935 before our own name. */
\r
936 ZippyHandleChallenge(star_match[4], star_match[5],
\r
937 star_match[6], star_match[7],
\r
938 StripHighlightAndTitle(star_match[0]));
\r
943 if (ics_type == ICS_ICC) { // [DM]
\r
944 if (looking_at(buf, i, "Your opponent offers you a draw")) {
\r
945 if (first.sendDrawOffers && first.initDone)
\r
946 SendToProgram("draw\n", &first);
\r
950 if (looking_at(buf, i, "offers you a draw")) {
\r
951 if (first.sendDrawOffers && first.initDone) {
\r
952 SendToProgram("draw\n", &first);
\r
958 if (looking_at(buf, i, "requests that the game be aborted") ||
\r
959 looking_at(buf, i, "would like to abort")) {
\r
960 if (appData.zippyAbort ||
\r
961 (gameMode == IcsPlayingWhite && whiteTimeRemaining < 0) ||
\r
962 (gameMode == IcsPlayingBlack && blackTimeRemaining < 0)) {
\r
963 SendToICS(ics_prefix);
\r
964 SendToICS("abort\n");
\r
966 SendToICS(ics_prefix);
\r
967 if (appData.zippyTalk)
\r
968 SendToICS("say Whoa no! I am having FUN!!\n");
\r
970 SendToICS("say Sorry, this computer doesn't accept aborts.\n");
\r
975 if (looking_at(buf, i, "requests adjournment") ||
\r
976 looking_at(buf, i, "would like to adjourn")) {
\r
977 if (appData.zippyAdjourn) {
\r
978 SendToICS(ics_prefix);
\r
979 SendToICS("adjourn\n");
\r
981 SendToICS(ics_prefix);
\r
982 if (appData.zippyTalk)
\r
983 SendToICS("say Whoa no! I am having FUN playing NOW!!\n");
\r
985 SendToICS("say Sorry, this computer doesn't accept adjourns.\n");
\r
993 /* Initialize chess program with data from the first board
\r
994 * of a new or resumed game.
\r
996 void ZippyFirstBoard(moveNum, basetime, increment)
\r
997 int moveNum, basetime, increment;
\r
1001 char *opp = (gameMode==IcsPlayingWhite ? gameInfo.black : gameInfo.white);
\r
1002 Boolean sentPos = FALSE;
\r
1003 char *bookHit = NULL; // [HGM] book
\r
1005 if (!first.initDone) {
\r
1006 /* Game is starting prematurely. We can't deal with this */
\r
1007 SendToICS(ics_prefix);
\r
1008 SendToICS("abort\n");
\r
1009 SendToICS(ics_prefix);
\r
1010 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
1014 /* Send the variant command if needed */
\r
1015 if (gameInfo.variant != VariantNormal) {
\r
1016 sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
\r
1017 SendToProgram(buf, &first);
\r
1020 if ((startedFromSetupPosition && moveNum == 0) ||
\r
1021 (!appData.getMoveList && moveNum > 0)) {
\r
1022 SendToProgram("force\n", &first);
\r
1023 SendBoard(&first, moveNum);
\r
1027 sprintf(buf, "level 0 %d %d\n", basetime, increment);
\r
1028 SendToProgram(buf, &first);
\r
1030 /* Count consecutive games from one opponent */
\r
1031 if (strcmp(opp, zippyLastOpp) == 0) {
\r
1032 zippyConsecGames++;
\r
1034 zippyConsecGames = 1;
\r
1035 strcpy(zippyLastOpp, opp);
\r
1038 /* Send the "computer" command if the opponent is in the list
\r
1039 we've been gathering. */
\r
1040 for (w=0; w<num_opps; w++) {
\r
1041 if (!strcmp(opp_name[w], opp)) {
\r
1042 SendToProgram(first.computerString, &first);
\r
1047 /* Ratings might be < 0 which means "we haven't seen a ratings
\r
1048 message from ICS." Send 0 in that case */
\r
1049 w = (gameInfo.whiteRating >= 0) ? gameInfo.whiteRating : 0;
\r
1050 b = (gameInfo.blackRating >= 0) ? gameInfo.blackRating : 0;
\r
1052 firstMove = FALSE;
\r
1053 if (gameMode == IcsPlayingWhite) {
\r
1054 if (first.sendName) {
\r
1055 sprintf(buf, "name %s\n", gameInfo.black);
\r
1056 SendToProgram(buf, &first);
\r
1058 strcpy(ics_handle, gameInfo.white);
\r
1059 sprintf(buf, "rating %d %d\n", w, b);
\r
1060 SendToProgram(buf, &first);
\r
1062 /* Position sent above, engine is in force mode */
\r
1063 if (WhiteOnMove(moveNum)) {
\r
1064 /* Engine is on move now */
\r
1065 if (first.sendTime) {
\r
1066 if (first.useColors) {
\r
1067 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1068 SendTimeRemaining(&first, TRUE);
\r
1069 SendToProgram("white\n", &first);
\r
1071 SendTimeRemaining(&first, TRUE);
\r
1074 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1076 /* Engine's opponent is on move now */
\r
1077 if (first.usePlayother) {
\r
1078 if (first.sendTime) {
\r
1079 SendTimeRemaining(&first, TRUE);
\r
1081 SendToProgram("playother\n", &first);
\r
1083 /* Need to send a "go" after opponent moves */
\r
1088 /* Position not sent above, move list might be sent later */
\r
1089 if (moveNum == 0) {
\r
1090 /* No move list coming; at start of game */
\r
1091 if (first.sendTime) {
\r
1092 if (first.useColors) {
\r
1093 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1094 SendTimeRemaining(&first, TRUE);
\r
1095 SendToProgram("white\n", &first);
\r
1097 SendTimeRemaining(&first, TRUE);
\r
1100 // SendToProgram("go\n", &first);
\r
1101 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1104 } else if (gameMode == IcsPlayingBlack) {
\r
1105 if (first.sendName) {
\r
1106 sprintf(buf, "name %s\n", gameInfo.white);
\r
1107 SendToProgram(buf, &first);
\r
1109 strcpy(ics_handle, gameInfo.black);
\r
1110 sprintf(buf, "rating %d %d\n", b, w);
\r
1111 SendToProgram(buf, &first);
\r
1113 /* Position sent above, engine is in force mode */
\r
1114 if (!WhiteOnMove(moveNum)) {
\r
1115 /* Engine is on move now */
\r
1116 if (first.sendTime) {
\r
1117 if (first.useColors) {
\r
1118 SendToProgram("white\n", &first); /*gnu kludge*/
\r
1119 SendTimeRemaining(&first, FALSE);
\r
1120 SendToProgram("black\n", &first);
\r
1122 SendTimeRemaining(&first, FALSE);
\r
1125 // SendToProgram("go\n", &first);
\r
1126 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1128 /* Engine's opponent is on move now */
\r
1129 if (first.usePlayother) {
\r
1130 if (first.sendTime) {
\r
1131 SendTimeRemaining(&first, FALSE);
\r
1133 SendToProgram("playother\n", &first);
\r
1135 /* Need to send a "go" after opponent moves */
\r
1140 /* Position not sent above, move list might be sent later */
\r
1141 /* Nothing needs to be done here */
\r
1145 if(bookHit) { // [HGM] book: simulate book reply
\r
1146 static char bookMove[MSG_SIZ]; // a bit generous?
\r
1148 programStats.depth = programStats.nodes = programStats.time =
\r
1149 programStats.score = programStats.got_only_move = 0;
\r
1150 sprintf(programStats.movelist, "%s (xbook)", bookHit);
\r
1152 strcpy(bookMove, "move ");
\r
1153 strcat(bookMove, bookHit);
\r
1154 HandleMachineMove(bookMove, &first);
\r
1160 ZippyHoldings(white_holding, black_holding, new_piece)
\r
1161 char *white_holding, *black_holding, *new_piece;
\r
1163 char buf[MSG_SIZ];
\r
1164 if (gameMode != IcsPlayingBlack && gameMode != IcsPlayingWhite) return;
\r
1165 sprintf(buf, "holding [%s] [%s] %s\n",
\r
1166 white_holding, black_holding, new_piece);
\r
1167 SendToProgram(buf, &first);
\r