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 char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book
\r
93 static char zippyPartner[MSG_SIZ];
\r
94 static char zippyLastOpp[MSG_SIZ];
\r
95 static int zippyConsecGames;
\r
96 static time_t zippyLastGameEnd;
\r
98 extern void mysrandom(unsigned int seed);
\r
99 extern int myrandom(void);
\r
105 /* Get name of Zippy lines file */
\r
106 p = getenv("ZIPPYLINES");
\r
108 appData.zippyLines = p;
\r
111 /* Get word that Zippy thinks is insulting */
\r
112 p = getenv("ZIPPYPINHEAD");
\r
114 appData.zippyPinhead = p;
\r
117 /* What password is used for remote control? */
\r
118 p = getenv("ZIPPYPASSWORD");
\r
120 appData.zippyPassword = p;
\r
123 /* What password is used for remote commands to gnuchess? */
\r
124 p = getenv("ZIPPYPASSWORD2");
\r
126 appData.zippyPassword2 = p;
\r
129 /* Joke feature for people who try an old password */
\r
130 p = getenv("ZIPPYWRONGPASSWORD");
\r
132 appData.zippyWrongPassword = p;
\r
135 /* While testing, I want to accept challenges from only one person
\r
136 (namely, my "anonymous" account), so I set an environment
\r
137 variable ZIPPYACCEPTONLY. */
\r
138 p = getenv("ZIPPYACCEPTONLY");
\r
140 appData.zippyAcceptOnly = p;
\r
143 /* Should Zippy use "i" command? */
\r
144 /* Defaults to 1=true */
\r
145 p = getenv("ZIPPYUSEI");
\r
147 appData.zippyUseI = atoi(p);
\r
150 /* How does Zippy handle bughouse partnering? */
\r
151 /* 0=say we can't play, 1=manual partnering, 2=auto partnering */
\r
152 p = getenv("ZIPPYBUGHOUSE");
\r
154 appData.zippyBughouse = atoi(p);
\r
157 /* Does Zippy abort games with Crafty? */
\r
158 /* Defaults to 0=false */
\r
159 p = getenv("ZIPPYNOPLAYCRAFTY");
\r
161 appData.zippyNoplayCrafty = atoi(p);
\r
164 /* What ICS command does Zippy send at game end? Default: "gameend". */
\r
165 p = getenv("ZIPPYGAMEEND");
\r
167 appData.zippyGameEnd = p;
\r
170 /* What ICS command does Zippy send at game start? Default: none. */
\r
171 p = getenv("ZIPPYGAMESTART");
\r
173 appData.zippyGameStart = p;
\r
176 /* Should Zippy accept adjourns? */
\r
177 /* Defaults to 0=false */
\r
178 p = getenv("ZIPPYADJOURN");
\r
180 appData.zippyAdjourn = atoi(p);
\r
183 /* Should Zippy accept aborts? */
\r
184 /* Defaults to 0=false */
\r
185 p = getenv("ZIPPYABORT");
\r
187 appData.zippyAbort = atoi(p);
\r
190 /* Should Zippy play chess variants (besides bughouse)? */
\r
191 p = getenv("ZIPPYVARIANTS");
\r
193 appData.zippyVariants = p;
\r
195 strcpy(first.variants, appData.zippyVariants);
\r
197 srandom(time(NULL));
\r
201 * Routines to implement Zippy talking
\r
205 char *swifties[] = {
\r
206 "i acclaims:", "i admonishes:", "i advertises:", "i advises:",
\r
207 "i advocates:", "i affirms:", "i alleges:", "i anathematizes:",
\r
208 "i animadverts:", "i announces:", "i apostrophizes:",
\r
209 "i appeals:", "i applauds:", "i approves:", "i argues:",
\r
210 "i articulates:", "i asserts:", "i asseverates:", "i attests:",
\r
211 "i avers:", "i avows:", "i baas:", "i babbles:", "i banters:",
\r
212 "i barks:", "i bawls:", "i bays:", "i begs:", "i belches:",
\r
213 "i bellows:", "i belts out:", "i berates:", "i beshrews:",
\r
214 "i blabbers:", "i blabs:", "i blares:", "i blasphemes:",
\r
215 "i blasts:", "i blathers:", "i bleats:", "i blithers:",
\r
216 "i blubbers:", "i blurts out:", "i blusters:", "i boasts:",
\r
217 "i brags:", "i brays:", "i broadcasts:", "i burbles:",
\r
218 "i buzzes:", "i cachinnates:", "i cackles:", "i caterwauls:",
\r
219 "i calumniates:", "i caws:", "i censures:", "i chants:",
\r
220 "i chatters:", "i cheeps:", "i cheers:", "i chides:", "i chins:",
\r
221 "i chirps:", "i chortles:", "i chuckles:", "i claims:",
\r
222 "i clamors:", "i clucks:", "i commands:", "i commends:",
\r
223 "i comments:", "i commiserates:", "i communicates:",
\r
224 "i complains:", "i concludes:", "i confabulates:", "i confesses:",
\r
225 "i coos:", "i coughs:", "i counsels:", "i cries:", "i croaks:",
\r
226 "i crows:", "i curses:", "i daydreams:", "i debates:",
\r
227 "i declaims:", "i declares:", "i delivers:", "i denounces:",
\r
228 "i deposes:", "i directs:", "i discloses:", "i disparages:",
\r
229 "i discourses:", "i divulges:", "i documents:", "i drawls:",
\r
230 "i dreams:", "i drivels:", "i drones:", "i effuses:",
\r
231 /*"i ejaculates:",*/ "i elucidates:", "i emotes:", "i endorses:",
\r
232 "i enthuses:", "i entreats:", "i enunciates:", "i eulogizes:",
\r
233 "i exclaims:", "i execrates:", "i exhorts:", "i expatiates:",
\r
234 "i explains:", "i explicates:", "i explodes:", "i exposes:",
\r
235 "i exposits:", "i expostulates: ",
\r
236 "i expounds:", "i expresses:", "i extols:",
\r
237 "i exults:", "i fantasizes:", "i fibs:", "i filibusters:",
\r
238 "i flatters:", "i flutes:", "i fools:", "i free-associates:",
\r
239 "i fulminates:", "i gabbles:", "i gabs:", "i gasps:",
\r
240 "i giggles:", "i gossips:", "i gripes:", "i groans:", "i growls:",
\r
241 "i grunts:", "i guesses:", "i guffaws:", "i gushes:", "i hails:",
\r
242 "i hallucinates:", "i harangues:", "i harmonizes:", "i hectors:",
\r
243 "i hints:", "i hisses:", "i hollers:", "i honks:", "i hoots:",
\r
244 "i hosannas:", "i howls:", "i hums:", "i hypothecates:",
\r
245 "i hypothesizes:", "i imagines:", "i implies:", "i implores:",
\r
246 "i imprecates:", "i indicates:", "i infers:",
\r
247 "i informs everyone:", "i instructs:", "i interjects:",
\r
248 "i interposes:", "i intimates:", "i intones:", "i introspects:",
\r
249 "i inveighs:", "i jabbers:", "i japes:", "i jests:", "i jibes:",
\r
250 "i jives:", "i jokes:", "i joshes:", "i keens:", "i laments:",
\r
251 "i lauds:", "i laughs:", "i lectures:", "i lies:", "i lilts:",
\r
252 "i lisps:", "i maintains:", "i maledicts:", "i maunders:",
\r
253 "i meows:", "i mewls:", "i mimes:", "i minces:", "i moans:",
\r
254 "i moos:", "i mourns:", "i mouths:", "i mumbles:", "i murmurs:",
\r
255 "i muses:", "i mutters:", "i nags:", "i natters:", "i neighs:",
\r
256 "i notes:", "i nuncupates:", "i objurgates:", "i observes:",
\r
257 "i offers:", "i oinks:", "i opines:", "i orates:", "i orders:",
\r
258 "i panegyrizes:", "i pantomimes:", "i pants:", "i peals:",
\r
259 "i peeps:", "i perorates:", "i persuades:", "i petitions:",
\r
260 "i phonates:", "i pipes up:", "i pitches:", "i pleads:",
\r
261 "i points out:", "i pontificates:", "i postulates:", "i praises:",
\r
262 "i prates:", "i prattles:", "i preaches:", "i prescribes:",
\r
263 "i prevaricates:", "i proclaims:", "i projects:", "i pronounces:",
\r
264 "i proposes:", "i proscribes:", "i quacks:", "i queries:",
\r
265 "i questions:", "i quips:", "i quotes:", "i rages:", "i rambles:",
\r
266 "i rants:", "i raps:", "i rasps:", "i rattles:", "i raves:",
\r
267 "i reacts:", "i recites:", "i recommends:", "i records:",
\r
268 "i reiterates:", "i rejoins:", "i releases:", "i remarks:",
\r
269 "i reminisces:", "i remonstrates:", "i repeats:", "i replies:",
\r
270 "i reports:", "i reprimands:", "i reproaches:", "i reproves:",
\r
271 "i resounds:", "i responds:", "i retorts:", "i reveals:",
\r
272 "i reviles:", "i roars:", "i rumbles:", "i sanctions:",
\r
273 "i satirizes:", "i sauces:", "i scolds:", "i screams:",
\r
274 "i screeches:", "i semaphores:", "i sends:", "i sermonizes:",
\r
275 "i shrieks:", "i sibilates:", "i sighs:", "i signals:",
\r
276 "i signifies:", "i signs:", "i sings:", "i slurs:", "i snaps:",
\r
277 "i snarls:", "i sneezes:", "i snickers:", "i sniggers:",
\r
278 "i snivels:", "i snores:", "i snorts:", "i sobs:",
\r
279 "i soliloquizes:", "i sounds off:", "i sounds out:", "i speaks:",
\r
280 "i spews:", "i spits out:", "i splutters:", "i spoofs:",
\r
281 "i spouts:", "i sputters:", "i squalls:", "i squawks:",
\r
282 "i squeaks:", "i squeals:", "i stammers:", "i states:",
\r
283 "i stresses:", "i stutters:", "i submits:", "i suggests:",
\r
284 "i summarizes:", "i sums up:", "i swears:", "i talks:",
\r
285 "i tattles:", "i teases:", "i telegraphs:", "i testifies:",
\r
286 "i threatens:", "i thunders:", "i titters:", "i tongue-lashes:",
\r
287 "i toots:", "i transcribes:", "i transmits:", "i trills:",
\r
288 "i trumpets:", "i twaddles:", "i tweets:", "i twitters:",
\r
289 "i types:", "i upbraids:", "i urges:", "i utters:", "i ventures:",
\r
290 "i vibrates:", "i vilifies:", "i vituperates:", "i vocalizes:",
\r
291 "i vociferates:", "i voices:", "i waffles:", "i wails:",
\r
292 "i warbles:", "i warns:", "i weeps:", "i wheezes:", "i whimpers:",
\r
293 "i whines:", "i whinnies:", "i whistles:", "i wisecracks:",
\r
294 "i witnesses:", "i woofs:", "i writes:", "i yammers:", "i yawps:",
\r
295 "i yells:", "i yelps:", "i yodels:", "i yowls:", "i zings:",
\r
298 #define MAX_SPEECH 250
\r
300 void Speak(how, whom)
\r
303 static FILE *zipfile = NULL;
\r
304 static struct stat zipstat;
\r
305 char zipbuf[MAX_SPEECH + 1];
\r
306 static time_t lastShout = 0;
\r
312 if (strcmp(how, "shout") == 0) {
\r
313 now = time((time_t *) NULL);
\r
314 if (now - lastShout < 1*60) return;
\r
316 if (appData.zippyUseI) {
\r
317 how = swifties[(unsigned) random() %
\r
318 (sizeof(swifties)/sizeof(char *))];
\r
322 if (zipfile == NULL) {
\r
323 zipfile = fopen(appData.zippyLines, "r");
\r
324 if (zipfile == NULL) {
\r
325 DisplayFatalError("Can't open Zippy lines file", errno, 1);
\r
328 fstat(fileno(zipfile), &zipstat);
\r
332 fseek(zipfile, (unsigned) random() % zipstat.st_size, 0);
\r
335 } while (c != NULLCHAR && c != '^' && c != EOF);
\r
336 if (c == EOF) continue;
\r
337 while ((c = getc(zipfile)) == '\n') ;
\r
338 if (c == EOF) continue;
\r
343 /* Don't use ics_prefix; we need to let FICS expand the alias i -> it,
\r
344 but use the real command "i" on ICC */
\r
345 strcpy(zipbuf, how);
\r
346 strcat(zipbuf, " ");
\r
347 if (whom != NULL) {
\r
348 strcat(zipbuf, whom);
\r
349 strcat(zipbuf, " ");
\r
351 speechlen = strlen(zipbuf);
\r
352 p = zipbuf + speechlen;
\r
354 while (++speechlen < MAX_SPEECH) {
\r
355 if (c == NULLCHAR || c == '^') {
\r
360 } else if (c == '\n') {
\r
364 } while (c == ' ');
\r
365 } else if (c == EOF) {
\r
372 /* Tried to say something too long, or junk at the end of the
\r
373 file. Try something else. */
\r
374 Speak(how, whom); /* tail recursion */
\r
377 int ZippyCalled(str)
\r
380 return ics_handle[0] != NULLCHAR && StrCaseStr(str, ics_handle) != NULL;
\r
383 static char opp_name[128][32];
\r
384 static int num_opps=0;
\r
386 extern ColorClass curColor;
\r
388 static void SetCurColor( ColorClass color )
\r
393 static void ColorizeEx( ColorClass color, int cont )
\r
395 if( appData.colorize ) {
\r
396 Colorize( color, cont );
\r
397 SetCurColor( color );
\r
401 int ZippyControl(buf, i)
\r
406 char reply[MSG_SIZ];
\r
409 #include "trivia.c"
\r
412 /* Possibly reject Crafty as opponent */
\r
413 if (appData.zippyPlay && appData.zippyNoplayCrafty && forwardMostMove < 4
\r
414 && looking_at(buf, i, "* kibitzes: Hello from Crafty"))
\r
416 player = StripHighlightAndTitle(star_match[0]);
\r
417 if ((gameMode == IcsPlayingWhite &&
\r
418 StrCaseCmp(player, gameInfo.black) == 0) ||
\r
419 (gameMode == IcsPlayingBlack &&
\r
420 StrCaseCmp(player, gameInfo.white) == 0)) {
\r
422 sprintf(reply, "%ssay This computer does not play Crafty clones\n%sabort\n%s+noplay %s\n",
\r
423 ics_prefix, ics_prefix, ics_prefix, player);
\r
429 /* If this is a computer, save the name. Then later, once the */
\r
430 /* game is really started, we will send the "computer" notice to */
\r
432 if (appData.zippyPlay &&
\r
433 looking_at(buf, i, "* is in the computer list")) {
\r
435 for (i=0;i<num_opps;i++)
\r
436 if (!strcmp(opp_name[i],star_match[0])) break;
\r
437 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[0]);
\r
439 if (appData.zippyPlay && looking_at(buf, i, "* * is a computer *")) {
\r
441 for (i=0;i<num_opps;i++)
\r
442 if (!strcmp(opp_name[i],star_match[1])) break;
\r
443 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[1]);
\r
446 /* Tells and says */
\r
447 if (appData.zippyPlay &&
\r
448 (looking_at(buf, i, "* offers to be your bughouse partner") ||
\r
449 looking_at(buf, i, "* tells you: [automatic message] I chose you"))) {
\r
450 player = StripHighlightAndTitle(star_match[0]);
\r
451 if (appData.zippyBughouse > 1 && first.initDone) {
\r
452 sprintf(reply, "%spartner %s\n", ics_prefix, player);
\r
454 if (strcmp(zippyPartner, player) != 0) {
\r
455 strcpy(zippyPartner, player);
\r
456 SendToProgram(reply + strlen(ics_prefix), &first);
\r
458 } else if (appData.zippyBughouse > 0) {
\r
459 sprintf(reply, "%sdecline %s\n", ics_prefix, player);
\r
462 sprintf(reply, "%stell %s This computer cannot play bughouse\n",
\r
463 ics_prefix, player);
\r
469 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
470 looking_at(buf, i, "* agrees to be your partner")) {
\r
471 player = StripHighlightAndTitle(star_match[0]);
\r
472 sprintf(reply, "partner %s\n", player);
\r
473 if (strcmp(zippyPartner, player) != 0) {
\r
474 strcpy(zippyPartner, player);
\r
475 SendToProgram(reply, &first);
\r
480 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
481 (looking_at(buf, i, "are no longer *'s partner") ||
\r
483 "* tells you: [automatic message] I'm no longer your"))) {
\r
484 player = StripHighlightAndTitle(star_match[0]);
\r
485 if (strcmp(zippyPartner, player) == 0) {
\r
486 zippyPartner[0] = NULLCHAR;
\r
487 SendToProgram("partner\n", &first);
\r
492 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
493 (looking_at(buf, i, "no longer have a bughouse partner") ||
\r
494 looking_at(buf, i, "partner has disconnected") ||
\r
495 looking_at(buf, i, "partner has just chosen a new partner"))) {
\r
496 zippyPartner[0] = NULLCHAR;
\r
497 SendToProgram("partner\n", &first);
\r
501 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
502 looking_at(buf, i, "* (your partner) tells you: *")) {
\r
503 /* This pattern works on FICS but not ICC */
\r
504 player = StripHighlightAndTitle(star_match[0]);
\r
505 if (strcmp(zippyPartner, player) != 0) {
\r
506 strcpy(zippyPartner, player);
\r
507 sprintf(reply, "partner %s\n", player);
\r
508 SendToProgram(reply, &first);
\r
510 sprintf(reply, "ptell %s\n", star_match[1]);
\r
511 SendToProgram(reply, &first);
\r
515 if (looking_at(buf, i, "* tells you: *") ||
\r
516 looking_at(buf, i, "* says: *"))
\r
518 player = StripHighlightAndTitle(star_match[0]);
\r
519 if (appData.zippyPassword[0] != NULLCHAR &&
\r
520 strncmp(star_match[1], appData.zippyPassword,
\r
521 strlen(appData.zippyPassword)) == 0) {
\r
522 p = star_match[1] + strlen(appData.zippyPassword);
\r
523 while (*p == ' ') p++;
\r
526 } else if (appData.zippyPassword2[0] != NULLCHAR && first.initDone &&
\r
527 strncmp(star_match[1], appData.zippyPassword2,
\r
528 strlen(appData.zippyPassword2)) == 0) {
\r
529 p = star_match[1] + strlen(appData.zippyPassword2);
\r
530 while (*p == ' ') p++;
\r
531 SendToProgram(p, &first);
\r
532 SendToProgram("\n", &first);
\r
533 } else if (appData.zippyWrongPassword[0] != NULLCHAR &&
\r
534 strncmp(star_match[1], appData.zippyWrongPassword,
\r
535 strlen(appData.zippyWrongPassword)) == 0) {
\r
536 p = star_match[1] + strlen(appData.zippyWrongPassword);
\r
537 while (*p == ' ') p++;
\r
538 sprintf(reply, "wrong %s\n", player);
\r
540 } else if (appData.zippyBughouse && first.initDone &&
\r
541 strcmp(player, zippyPartner) == 0) {
\r
542 SendToProgram("ptell ", &first);
\r
543 SendToProgram(star_match[1], &first);
\r
544 SendToProgram("\n", &first);
\r
545 } else if (strncmp(star_match[1], HI, 6) == 0) {
\r
546 extern char* programVersion;
\r
547 sprintf(reply, "%stell %s %s\n",
\r
548 ics_prefix, player, programVersion);
\r
550 } else if (strncmp(star_match[1], "W0W!! ", 6) == 0) {
\r
551 extern char* programVersion;
\r
552 sprintf(reply, "%stell %s %s\n", ics_prefix,
\r
553 player, programVersion);
\r
555 } else if (appData.zippyTalk && (((unsigned) random() % 10) < 9)) {
\r
556 if (strcmp(player, ics_handle) != 0) {
\r
557 Speak("tell", player);
\r
561 ColorizeEx( ColorTell, FALSE );
\r
566 if( appData.colorize && looking_at(buf, i, "* (*) seeking") ) {
\r
567 ColorizeEx(ColorSeek, FALSE);
\r
571 if (looking_at(buf, i, "* spoofs you:")) {
\r
572 player = StripHighlightAndTitle(star_match[0]);
\r
573 sprintf(reply, "spoofedby %s\n", player);
\r
580 int ZippyConverse(buf, i)
\r
584 static char lastgreet[MSG_SIZ];
\r
585 char reply[MSG_SIZ];
\r
588 /* Shouts and emotes */
\r
589 if (looking_at(buf, i, "--> * *") ||
\r
590 looking_at(buf, i, "* shouts: *"))
\r
592 if (appData.zippyTalk) {
\r
593 char *player = StripHighlightAndTitle(star_match[0]);
\r
594 if (strcmp(player, ics_handle) == 0) {
\r
596 } else if (appData.zippyPinhead[0] != NULLCHAR &&
\r
597 StrCaseStr(star_match[1], appData.zippyPinhead) != NULL) {
\r
598 sprintf(reply, "insult %s\n", player);
\r
600 } else if (ZippyCalled(star_match[1])) {
\r
601 Speak("shout", NULL);
\r
605 ColorizeEx(ColorShout, FALSE);
\r
610 if (looking_at(buf, i, "* kibitzes: *")) {
\r
611 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
\r
612 char *player = StripHighlightAndTitle(star_match[0]);
\r
613 if (strcmp(player, ics_handle) != 0) {
\r
614 Speak("kibitz", NULL);
\r
618 ColorizeEx(ColorKibitz, FALSE);
\r
623 if (looking_at(buf, i, "* whispers: *")) {
\r
624 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
\r
625 char *player = StripHighlightAndTitle(star_match[0]);
\r
626 if (strcmp(player, ics_handle) != 0) {
\r
627 Speak("whisper", NULL);
\r
631 ColorizeEx(ColorKibitz, FALSE);
\r
637 if ((looking_at(buf, i, ". * (*:*): *") && isdigit(star_match[1][0])) ||
\r
638 looking_at(buf, i, ". * at *:*: *")) {
\r
639 if (appData.zippyTalk) {
\r
641 char *player = StripHighlightAndTitle(star_match[0]);
\r
643 if (strcmp(player, ics_handle) != 0) {
\r
644 if (((unsigned) random() % 10) < 9)
\r
645 Speak("message", player);
\r
646 f = fopen("zippy.messagelog", "a");
\r
647 fprintf(f, "%s (%s:%s): %s\n", player,
\r
648 star_match[1], star_match[2], star_match[3]);
\r
655 /* Channel tells */
\r
657 if (looking_at(buf, i, "*(*: *")) {
\r
660 if (star_match[0][0] == NULLCHAR ||
\r
661 strchr(star_match[0], ' ') ||
\r
662 strchr(star_match[1], ' ')) {
\r
663 /* Oops, did not want to match this; probably a message */
\r
667 if (appData.zippyTalk) {
\r
668 player = StripHighlightAndTitle(star_match[0]);
\r
669 channel = strrchr(star_match[1], '(');
\r
670 if (channel == NULL) {
\r
671 channel = star_match[1];
\r
675 channel[strlen(channel)-1] = NULLCHAR;
\r
677 /* Always tell to the channel (probability 90%) */
\r
678 if (strcmp(player, ics_handle) != 0 &&
\r
679 ((unsigned) random() % 10) < 9) {
\r
680 Speak("tell", channel);
\r
683 /* Tell to the channel only if someone mentions our name */
\r
684 if (ZippyCalled(star_match[2])) {
\r
685 Speak("tell", channel);
\r
689 ColorizeEx( atoi(channel) == 1 ? ColorChannel1 : ColorChannel, FALSE );
\r
694 if (!appData.zippyTalk) return FALSE;
\r
696 if ((looking_at(buf, i, "You have * message") &&
\r
697 atoi(star_match[0]) != 0) ||
\r
698 looking_at(buf, i, "* has left a message for you") ||
\r
699 looking_at(buf, i, "* just sent you a message")) {
\r
700 sprintf(reply, "%smessages\n%sclearmessages *\n",
\r
701 ics_prefix, ics_prefix);
\r
706 if (looking_at(buf, i, "Notification: * has arrived")) {
\r
707 if (((unsigned) random() % 3) == 0) {
\r
708 char *player = StripHighlightAndTitle(star_match[0]);
\r
709 strcpy(lastgreet, player);
\r
710 sprintf(reply, "greet %s\n", player);
\r
712 Speak("tell", player);
\r
716 if (looking_at(buf, i, "Notification: * has departed")) {
\r
717 if (((unsigned) random() % 3) == 0) {
\r
718 char *player = StripHighlightAndTitle(star_match[0]);
\r
719 sprintf(reply, "farewell %s\n", player);
\r
724 if (looking_at(buf, i, "Not sent -- * is censoring you")) {
\r
725 char *player = StripHighlightAndTitle(star_match[0]);
\r
726 if (strcmp(player, lastgreet) == 0) {
\r
727 sprintf(reply, "%s-notify %s\n", ics_prefix, player);
\r
732 if (looking_at(buf, i, "command is currently turned off")) {
\r
733 appData.zippyUseI = 0;
\r
739 void ZippyGameStart(white, black)
\r
740 char *white, *black;
\r
742 if (!first.initDone) {
\r
743 /* Game is starting prematurely. We can't deal with this */
\r
744 SendToICS(ics_prefix);
\r
745 SendToICS("abort\n");
\r
746 SendToICS(ics_prefix);
\r
747 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
751 if (appData.zippyGameStart[0] != NULLCHAR) {
\r
752 SendToICS(appData.zippyGameStart);
\r
757 void ZippyGameEnd(result, resultDetails)
\r
759 char *resultDetails;
\r
761 if (appData.zippyAcceptOnly[0] == NULLCHAR &&
\r
762 appData.zippyGameEnd[0] != NULLCHAR) {
\r
763 SendToICS(appData.zippyGameEnd);
\r
766 zippyLastGameEnd = time(0);
\r
770 * Routines to implement Zippy playing chess
\r
773 void ZippyHandleChallenge(srated, swild, sbase, sincrement, opponent)
\r
774 char *srated, *swild, *sbase, *sincrement, *opponent;
\r
777 int base, increment, i=0;
\r
779 VariantClass variant;
\r
783 variant = StringToVariant(swild);
\r
784 varname = VariantName(variant);
\r
785 base = atoi(sbase);
\r
786 increment = atoi(sincrement);
\r
788 /* [DM] If icsAnalyzeEngine active we don't accept automatic games */
\r
789 if (appData.icsActive && appData.icsEngineAnalyze) return;
\r
791 /* If desired, you can insert more code here to decline matches
\r
792 based on rated, variant, base, and increment, but it is
\r
793 easier to use the ICS formula feature instead. */
\r
795 if (variant == VariantLoadable) {
\r
797 "%stell %s This computer can't play wild type %s\n%sdecline %s\n",
\r
798 ics_prefix, opponent, swild, ics_prefix, opponent);
\r
802 if (StrStr(appData.zippyVariants, varname) == NULL ||
\r
803 (i=first.protocolVersion) != 1 && StrStr(first.variants, varname) == NULL /* [HGM] zippyvar */
\r
806 "%stell %s This computer can't play %s [%s], only %s\n%sdecline %s\n",
\r
807 ics_prefix, opponent, swild, varname,
\r
808 i ? first.variants : appData.zippyVariants, /* [HGM] zippyvar */
\r
809 ics_prefix, opponent);
\r
814 /* Are we blocking match requests from all but one person? */
\r
815 if (appData.zippyAcceptOnly[0] != NULLCHAR &&
\r
816 StrCaseCmp(opponent, appData.zippyAcceptOnly)) {
\r
817 /* Yes, and this isn't him. Ignore challenge. */
\r
821 /* Too many consecutive games with same opponent? If so, make him
\r
822 wait until someone else has played or a timeout has elapsed. */
\r
823 if (appData.zippyMaxGames &&
\r
824 strcmp(opponent, zippyLastOpp) == 0 &&
\r
825 zippyConsecGames >= appData.zippyMaxGames &&
\r
826 difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
\r
827 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
828 ics_prefix, opponent, zippyConsecGames, ics_handle,
\r
829 appData.zippyReplayTimeout, ics_prefix, opponent);
\r
834 /* Engine not yet initialized or still thinking about last game? */
\r
835 if (!first.initDone || first.lastPing != first.lastPong) {
\r
836 sprintf(buf, "%stell %s I'm not quite ready for a new game yet; try again soon.\n%sdecline %s\n",
\r
837 ics_prefix, opponent, ics_prefix, opponent);
\r
842 sprintf(buf, "%saccept %s\n", ics_prefix, opponent);
\r
844 if (appData.zippyTalk) {
\r
845 Speak("tell", opponent);
\r
850 /* Accept matches */
\r
851 int ZippyMatch(buf, i)
\r
855 if (looking_at(buf, i, "* * match * * requested with * (*)")) {
\r
857 ZippyHandleChallenge(star_match[0], star_match[1],
\r
858 star_match[2], star_match[3],
\r
859 StripHighlightAndTitle(star_match[4]));
\r
863 /* Old FICS 0-increment form */
\r
864 if (looking_at(buf, i, "* * match * requested with * (*)")) {
\r
866 ZippyHandleChallenge(star_match[0], star_match[1],
\r
867 star_match[2], "0",
\r
868 StripHighlightAndTitle(star_match[3]));
\r
872 if (looking_at(buf, i,
\r
873 "* has made an alternate proposal of * * match * *.")) {
\r
875 ZippyHandleChallenge(star_match[1], star_match[2],
\r
876 star_match[3], star_match[4],
\r
877 StripHighlightAndTitle(star_match[0]));
\r
881 /* FICS wild/nonstandard forms */
\r
882 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * Loaded from *")) {
\r
883 /* note: star_match[2] can include "[white] " or "[black] "
\r
884 before our own name. */
\r
885 if(star_match[8] == NULL || star_match[8][0] == 0) // [HGM] chessd: open-source ICS has file on next line
\r
886 ZippyHandleChallenge(star_match[4], star_match[5],
\r
887 star_match[6], star_match[7], StripHighlightAndTitle(star_match[0]));
\r
888 else ZippyHandleChallenge(star_match[4], star_match[8],
\r
889 star_match[6], star_match[7],
\r
890 StripHighlightAndTitle(star_match[0]));
\r
894 if (looking_at(buf, i,
\r
895 "Challenge: * (*) *(*) * * * * : * * Loaded from *")) {
\r
896 /* note: star_match[2] can include "[white] " or "[black] "
\r
897 before our own name. */
\r
898 ZippyHandleChallenge(star_match[4], star_match[10],
\r
899 star_match[8], star_match[9],
\r
900 StripHighlightAndTitle(star_match[0]));
\r
904 /* Regular forms */
\r
905 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * : * *") |
\r
906 looking_at(buf, i, "Challenge: * (*) *(*) * * * * * *")) {
\r
907 /* note: star_match[2] can include "[white] " or "[black] "
\r
908 before our own name. */
\r
909 ZippyHandleChallenge(star_match[4], star_match[5],
\r
910 star_match[8], star_match[9],
\r
911 StripHighlightAndTitle(star_match[0]));
\r
915 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * *")) {
\r
916 /* note: star_match[2] can include "[white] " or "[black] "
\r
917 before our own name. */
\r
918 ZippyHandleChallenge(star_match[4], star_match[5],
\r
919 star_match[6], star_match[7],
\r
920 StripHighlightAndTitle(star_match[0]));
\r
925 if (ics_type == ICS_ICC) { // [DM]
\r
926 if (looking_at(buf, i, "Your opponent offers you a draw")) {
\r
927 if (first.sendDrawOffers && first.initDone)
\r
928 SendToProgram("draw\n", &first);
\r
932 if (looking_at(buf, i, "offers you a draw")) {
\r
933 if (first.sendDrawOffers && first.initDone) {
\r
934 SendToProgram("draw\n", &first);
\r
940 if (looking_at(buf, i, "requests that the game be aborted") ||
\r
941 looking_at(buf, i, "would like to abort")) {
\r
942 if (appData.zippyAbort ||
\r
943 (gameMode == IcsPlayingWhite && whiteTimeRemaining < 0) ||
\r
944 (gameMode == IcsPlayingBlack && blackTimeRemaining < 0)) {
\r
945 SendToICS(ics_prefix);
\r
946 SendToICS("abort\n");
\r
948 SendToICS(ics_prefix);
\r
949 if (appData.zippyTalk)
\r
950 SendToICS("say Whoa no! I am having FUN!!\n");
\r
952 SendToICS("say Sorry, this computer doesn't accept aborts.\n");
\r
957 if (looking_at(buf, i, "requests adjournment") ||
\r
958 looking_at(buf, i, "would like to adjourn")) {
\r
959 if (appData.zippyAdjourn) {
\r
960 SendToICS(ics_prefix);
\r
961 SendToICS("adjourn\n");
\r
963 SendToICS(ics_prefix);
\r
964 if (appData.zippyTalk)
\r
965 SendToICS("say Whoa no! I am having FUN playing NOW!!\n");
\r
967 SendToICS("say Sorry, this computer doesn't accept adjourns.\n");
\r
975 /* Initialize chess program with data from the first board
\r
976 * of a new or resumed game.
\r
978 void ZippyFirstBoard(moveNum, basetime, increment)
\r
979 int moveNum, basetime, increment;
\r
983 char *opp = (gameMode==IcsPlayingWhite ? gameInfo.black : gameInfo.white);
\r
984 Boolean sentPos = FALSE;
\r
985 char *bookHit = NULL; // [HGM] book
\r
987 if (!first.initDone) {
\r
988 /* Game is starting prematurely. We can't deal with this */
\r
989 SendToICS(ics_prefix);
\r
990 SendToICS("abort\n");
\r
991 SendToICS(ics_prefix);
\r
992 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
996 /* Send the variant command if needed */
\r
997 if (gameInfo.variant != VariantNormal) {
\r
998 sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
\r
999 SendToProgram(buf, &first);
\r
1002 if ((startedFromSetupPosition && moveNum == 0) ||
\r
1003 (!appData.getMoveList && moveNum > 0)) {
\r
1004 SendToProgram("force\n", &first);
\r
1005 SendBoard(&first, moveNum);
\r
1009 sprintf(buf, "level 0 %d %d\n", basetime, increment);
\r
1010 SendToProgram(buf, &first);
\r
1012 /* Count consecutive games from one opponent */
\r
1013 if (strcmp(opp, zippyLastOpp) == 0) {
\r
1014 zippyConsecGames++;
\r
1016 zippyConsecGames = 1;
\r
1017 strcpy(zippyLastOpp, opp);
\r
1020 /* Send the "computer" command if the opponent is in the list
\r
1021 we've been gathering. */
\r
1022 for (w=0; w<num_opps; w++) {
\r
1023 if (!strcmp(opp_name[w], opp)) {
\r
1024 SendToProgram(first.computerString, &first);
\r
1029 /* Ratings might be < 0 which means "we haven't seen a ratings
\r
1030 message from ICS." Send 0 in that case */
\r
1031 w = (gameInfo.whiteRating >= 0) ? gameInfo.whiteRating : 0;
\r
1032 b = (gameInfo.blackRating >= 0) ? gameInfo.blackRating : 0;
\r
1034 firstMove = FALSE;
\r
1035 if (gameMode == IcsPlayingWhite) {
\r
1036 if (first.sendName) {
\r
1037 sprintf(buf, "name %s\n", gameInfo.black);
\r
1038 SendToProgram(buf, &first);
\r
1040 strcpy(ics_handle, gameInfo.white);
\r
1041 sprintf(buf, "rating %d %d\n", w, b);
\r
1042 SendToProgram(buf, &first);
\r
1044 /* Position sent above, engine is in force mode */
\r
1045 if (WhiteOnMove(moveNum)) {
\r
1046 /* Engine is on move now */
\r
1047 if (first.sendTime) {
\r
1048 if (first.useColors) {
\r
1049 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1050 SendTimeRemaining(&first, TRUE);
\r
1051 SendToProgram("white\n", &first);
\r
1053 SendTimeRemaining(&first, TRUE);
\r
1056 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1058 /* Engine's opponent is on move now */
\r
1059 if (first.usePlayother) {
\r
1060 if (first.sendTime) {
\r
1061 SendTimeRemaining(&first, TRUE);
\r
1063 SendToProgram("playother\n", &first);
\r
1065 /* Need to send a "go" after opponent moves */
\r
1070 /* Position not sent above, move list might be sent later */
\r
1071 if (moveNum == 0) {
\r
1072 /* No move list coming; at start of game */
\r
1073 if (first.sendTime) {
\r
1074 if (first.useColors) {
\r
1075 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1076 SendTimeRemaining(&first, TRUE);
\r
1077 SendToProgram("white\n", &first);
\r
1079 SendTimeRemaining(&first, TRUE);
\r
1082 // SendToProgram("go\n", &first);
\r
1083 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1086 } else if (gameMode == IcsPlayingBlack) {
\r
1087 if (first.sendName) {
\r
1088 sprintf(buf, "name %s\n", gameInfo.white);
\r
1089 SendToProgram(buf, &first);
\r
1091 strcpy(ics_handle, gameInfo.black);
\r
1092 sprintf(buf, "rating %d %d\n", b, w);
\r
1093 SendToProgram(buf, &first);
\r
1095 /* Position sent above, engine is in force mode */
\r
1096 if (!WhiteOnMove(moveNum)) {
\r
1097 /* Engine is on move now */
\r
1098 if (first.sendTime) {
\r
1099 if (first.useColors) {
\r
1100 SendToProgram("white\n", &first); /*gnu kludge*/
\r
1101 SendTimeRemaining(&first, FALSE);
\r
1102 SendToProgram("black\n", &first);
\r
1104 SendTimeRemaining(&first, FALSE);
\r
1107 // SendToProgram("go\n", &first);
\r
1108 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1110 /* Engine's opponent is on move now */
\r
1111 if (first.usePlayother) {
\r
1112 if (first.sendTime) {
\r
1113 SendTimeRemaining(&first, FALSE);
\r
1115 SendToProgram("playother\n", &first);
\r
1117 /* Need to send a "go" after opponent moves */
\r
1122 /* Position not sent above, move list might be sent later */
\r
1123 /* Nothing needs to be done here */
\r
1127 if(bookHit) { // [HGM] book: simulate book reply
\r
1128 static char bookMove[MSG_SIZ]; // a bit generous?
\r
1130 programStats.depth = programStats.nodes = programStats.time =
\r
1131 programStats.score = programStats.got_only_move = 0;
\r
1132 sprintf(programStats.movelist, "%s (xbook)", bookHit);
\r
1134 strcpy(bookMove, "move ");
\r
1135 strcat(bookMove, bookHit);
\r
1136 HandleMachineMove(bookMove, &first);
\r
1142 ZippyHoldings(white_holding, black_holding, new_piece)
\r
1143 char *white_holding, *black_holding, *new_piece;
\r
1145 char buf[MSG_SIZ];
\r
1146 if (gameMode != IcsPlayingBlack && gameMode != IcsPlayingWhite) return;
\r
1147 sprintf(buf, "holding [%s] [%s] %s\n",
\r
1148 white_holding, black_holding, new_piece);
\r
1149 SendToProgram(buf, &first);
\r