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
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
102 /* Get name of Zippy lines file */
\r
103 p = getenv("ZIPPYLINES");
\r
105 appData.zippyLines = p;
\r
108 /* Get word that Zippy thinks is insulting */
\r
109 p = getenv("ZIPPYPINHEAD");
\r
111 appData.zippyPinhead = p;
\r
114 /* What password is used for remote control? */
\r
115 p = getenv("ZIPPYPASSWORD");
\r
117 appData.zippyPassword = p;
\r
120 /* What password is used for remote commands to gnuchess? */
\r
121 p = getenv("ZIPPYPASSWORD2");
\r
123 appData.zippyPassword2 = p;
\r
126 /* Joke feature for people who try an old password */
\r
127 p = getenv("ZIPPYWRONGPASSWORD");
\r
129 appData.zippyWrongPassword = p;
\r
132 /* While testing, I want to accept challenges from only one person
\r
133 (namely, my "anonymous" account), so I set an environment
\r
134 variable ZIPPYACCEPTONLY. */
\r
135 p = getenv("ZIPPYACCEPTONLY");
\r
137 appData.zippyAcceptOnly = p;
\r
140 /* Should Zippy use "i" command? */
\r
141 /* Defaults to 1=true */
\r
142 p = getenv("ZIPPYUSEI");
\r
144 appData.zippyUseI = atoi(p);
\r
147 /* How does Zippy handle bughouse partnering? */
\r
148 /* 0=say we can't play, 1=manual partnering, 2=auto partnering */
\r
149 p = getenv("ZIPPYBUGHOUSE");
\r
151 appData.zippyBughouse = atoi(p);
\r
154 /* Does Zippy abort games with Crafty? */
\r
155 /* Defaults to 0=false */
\r
156 p = getenv("ZIPPYNOPLAYCRAFTY");
\r
158 appData.zippyNoplayCrafty = atoi(p);
\r
161 /* What ICS command does Zippy send at game end? Default: "gameend". */
\r
162 p = getenv("ZIPPYGAMEEND");
\r
164 appData.zippyGameEnd = p;
\r
167 /* What ICS command does Zippy send at game start? Default: none. */
\r
168 p = getenv("ZIPPYGAMESTART");
\r
170 appData.zippyGameStart = p;
\r
173 /* Should Zippy accept adjourns? */
\r
174 /* Defaults to 0=false */
\r
175 p = getenv("ZIPPYADJOURN");
\r
177 appData.zippyAdjourn = atoi(p);
\r
180 /* Should Zippy accept aborts? */
\r
181 /* Defaults to 0=false */
\r
182 p = getenv("ZIPPYABORT");
\r
184 appData.zippyAbort = atoi(p);
\r
187 /* Should Zippy play chess variants (besides bughouse)? */
\r
188 p = getenv("ZIPPYVARIANTS");
\r
190 appData.zippyVariants = p;
\r
192 strcpy(first.variants, appData.zippyVariants);
\r
194 srandom(time(NULL));
\r
198 * Routines to implement Zippy talking
\r
202 char *swifties[] = {
\r
203 "i acclaims:", "i admonishes:", "i advertises:", "i advises:",
\r
204 "i advocates:", "i affirms:", "i alleges:", "i anathematizes:",
\r
205 "i animadverts:", "i announces:", "i apostrophizes:",
\r
206 "i appeals:", "i applauds:", "i approves:", "i argues:",
\r
207 "i articulates:", "i asserts:", "i asseverates:", "i attests:",
\r
208 "i avers:", "i avows:", "i baas:", "i babbles:", "i banters:",
\r
209 "i barks:", "i bawls:", "i bays:", "i begs:", "i belches:",
\r
210 "i bellows:", "i belts out:", "i berates:", "i beshrews:",
\r
211 "i blabbers:", "i blabs:", "i blares:", "i blasphemes:",
\r
212 "i blasts:", "i blathers:", "i bleats:", "i blithers:",
\r
213 "i blubbers:", "i blurts out:", "i blusters:", "i boasts:",
\r
214 "i brags:", "i brays:", "i broadcasts:", "i burbles:",
\r
215 "i buzzes:", "i cachinnates:", "i cackles:", "i caterwauls:",
\r
216 "i calumniates:", "i caws:", "i censures:", "i chants:",
\r
217 "i chatters:", "i cheeps:", "i cheers:", "i chides:", "i chins:",
\r
218 "i chirps:", "i chortles:", "i chuckles:", "i claims:",
\r
219 "i clamors:", "i clucks:", "i commands:", "i commends:",
\r
220 "i comments:", "i commiserates:", "i communicates:",
\r
221 "i complains:", "i concludes:", "i confabulates:", "i confesses:",
\r
222 "i coos:", "i coughs:", "i counsels:", "i cries:", "i croaks:",
\r
223 "i crows:", "i curses:", "i daydreams:", "i debates:",
\r
224 "i declaims:", "i declares:", "i delivers:", "i denounces:",
\r
225 "i deposes:", "i directs:", "i discloses:", "i disparages:",
\r
226 "i discourses:", "i divulges:", "i documents:", "i drawls:",
\r
227 "i dreams:", "i drivels:", "i drones:", "i effuses:",
\r
228 /*"i ejaculates:",*/ "i elucidates:", "i emotes:", "i endorses:",
\r
229 "i enthuses:", "i entreats:", "i enunciates:", "i eulogizes:",
\r
230 "i exclaims:", "i execrates:", "i exhorts:", "i expatiates:",
\r
231 "i explains:", "i explicates:", "i explodes:", "i exposes:",
\r
232 "i exposits:", "i expounds:", "i expresses:", "i extols:",
\r
233 "i exults:", "i fantasizes:", "i fibs:", "i filibusters:",
\r
234 "i flatters:", "i flutes:", "i fools:", "i free-associates:",
\r
235 "i fulminates:", "i gabbles:", "i gabs:", "i gasps:",
\r
236 "i giggles:", "i gossips:", "i gripes:", "i groans:", "i growls:",
\r
237 "i grunts:", "i guesses:", "i guffaws:", "i gushes:", "i hails:",
\r
238 "i hallucinates:", "i harangues:", "i harmonizes:", "i hectors:",
\r
239 "i hints:", "i hisses:", "i hollers:", "i honks:", "i hoots:",
\r
240 "i hosannas:", "i howls:", "i hums:", "i hypothecates:",
\r
241 "i hypothesizes:", "i imagines:", "i implies:", "i implores:",
\r
242 "i imprecates:", "i indicates:", "i infers:",
\r
243 "i informs everyone:", "i instructs:", "i interjects:",
\r
244 "i interposes:", "i intimates:", "i intones:", "i introspects:",
\r
245 "i inveighs:", "i jabbers:", "i japes:", "i jests:", "i jibes:",
\r
246 "i jives:", "i jokes:", "i joshes:", "i keens:", "i laments:",
\r
247 "i lauds:", "i laughs:", "i lectures:", "i lies:", "i lilts:",
\r
248 "i lisps:", "i maintains:", "i maledicts:", "i maunders:",
\r
249 "i meows:", "i mewls:", "i mimes:", "i minces:", "i moans:",
\r
250 "i moos:", "i mourns:", "i mouths:", "i mumbles:", "i murmurs:",
\r
251 "i muses:", "i mutters:", "i nags:", "i natters:", "i neighs:",
\r
252 "i notes:", "i nuncupates:", "i objurgates:", "i observes:",
\r
253 "i offers:", "i oinks:", "i opines:", "i orates:", "i orders:",
\r
254 "i panegyrizes:", "i pantomimes:", "i pants:", "i peals:",
\r
255 "i peeps:", "i perorates:", "i persuades:", "i petitions:",
\r
256 "i phonates:", "i pipes up:", "i pitches:", "i pleads:",
\r
257 "i points out:", "i pontificates:", "i postulates:", "i praises:",
\r
258 "i prates:", "i prattles:", "i preaches:", "i prescribes:",
\r
259 "i prevaricates:", "i proclaims:", "i projects:", "i pronounces:",
\r
260 "i proposes:", "i proscribes:", "i quacks:", "i queries:",
\r
261 "i questions:", "i quips:", "i quotes:", "i rages:", "i rambles:",
\r
262 "i rants:", "i raps:", "i rasps:", "i rattles:", "i raves:",
\r
263 "i reacts:", "i recites:", "i recommends:", "i records:",
\r
264 "i reiterates:", "i rejoins:", "i releases:", "i remarks:",
\r
265 "i reminisces:", "i remonstrates:", "i repeats:", "i replies:",
\r
266 "i reports:", "i reprimands:", "i reproaches:", "i reproves:",
\r
267 "i resounds:", "i responds:", "i retorts:", "i reveals:",
\r
268 "i reviles:", "i roars:", "i rumbles:", "i sanctions:",
\r
269 "i satirizes:", "i sauces:", "i scolds:", "i screams:",
\r
270 "i screeches:", "i semaphores:", "i sends:", "i sermonizes:",
\r
271 "i shrieks:", "i sibilates:", "i sighs:", "i signals:",
\r
272 "i signifies:", "i signs:", "i sings:", "i slurs:", "i snaps:",
\r
273 "i snarls:", "i sneezes:", "i snickers:", "i sniggers:",
\r
274 "i snivels:", "i snores:", "i snorts:", "i sobs:",
\r
275 "i soliloquizes:", "i sounds off:", "i sounds out:", "i speaks:",
\r
276 "i spews:", "i spits out:", "i splutters:", "i spoofs:",
\r
277 "i spouts:", "i sputters:", "i squalls:", "i squawks:",
\r
278 "i squeaks:", "i squeals:", "i stammers:", "i states:",
\r
279 "i stresses:", "i stutters:", "i submits:", "i suggests:",
\r
280 "i summarizes:", "i sums up:", "i swears:", "i talks:",
\r
281 "i tattles:", "i teases:", "i telegraphs:", "i testifies:",
\r
282 "i threatens:", "i thunders:", "i titters:", "i tongue-lashes:",
\r
283 "i toots:", "i transcribes:", "i transmits:", "i trills:",
\r
284 "i trumpets:", "i twaddles:", "i tweets:", "i twitters:",
\r
285 "i types:", "i upbraids:", "i urges:", "i utters:", "i ventures:",
\r
286 "i vibrates:", "i vilifies:", "i vituperates:", "i vocalizes:",
\r
287 "i vociferates:", "i voices:", "i waffles:", "i wails:",
\r
288 "i warbles:", "i warns:", "i weeps:", "i wheezes:", "i whimpers:",
\r
289 "i whines:", "i whinnies:", "i whistles:", "i wisecracks:",
\r
290 "i witnesses:", "i woofs:", "i writes:", "i yammers:", "i yawps:",
\r
291 "i yells:", "i yelps:", "i yodels:", "i yowls:", "i zings:",
\r
294 #define MAX_SPEECH 250
\r
296 void Speak(how, whom)
\r
299 static FILE *zipfile = NULL;
\r
300 static struct stat zipstat;
\r
301 char zipbuf[MAX_SPEECH + 1];
\r
302 static time_t lastShout = 0;
\r
308 if (strcmp(how, "shout") == 0) {
\r
309 now = time((time_t *) NULL);
\r
310 if (now - lastShout < 1*60) return;
\r
312 if (appData.zippyUseI) {
\r
313 how = swifties[(unsigned) random() %
\r
314 (sizeof(swifties)/sizeof(char *))];
\r
318 if (zipfile == NULL) {
\r
319 zipfile = fopen(appData.zippyLines, "r");
\r
320 if (zipfile == NULL) {
\r
321 DisplayFatalError("Can't open Zippy lines file", errno, 1);
\r
324 fstat(fileno(zipfile), &zipstat);
\r
328 fseek(zipfile, (unsigned) random() % zipstat.st_size, 0);
\r
331 } while (c != NULLCHAR && c != '^' && c != EOF);
\r
332 if (c == EOF) continue;
\r
333 while ((c = getc(zipfile)) == '\n') ;
\r
334 if (c == EOF) continue;
\r
339 /* Don't use ics_prefix; we need to let FICS expand the alias i -> it,
\r
340 but use the real command "i" on ICC */
\r
341 strcpy(zipbuf, how);
\r
342 strcat(zipbuf, " ");
\r
343 if (whom != NULL) {
\r
344 strcat(zipbuf, whom);
\r
345 strcat(zipbuf, " ");
\r
347 speechlen = strlen(zipbuf);
\r
348 p = zipbuf + speechlen;
\r
350 while (++speechlen < MAX_SPEECH) {
\r
351 if (c == NULLCHAR || c == '^') {
\r
356 } else if (c == '\n') {
\r
360 } while (c == ' ');
\r
361 } else if (c == EOF) {
\r
368 /* Tried to say something too long, or junk at the end of the
\r
369 file. Try something else. */
\r
370 Speak(how, whom); /* tail recursion */
\r
373 int ZippyCalled(str)
\r
376 return ics_handle[0] != NULLCHAR && StrCaseStr(str, ics_handle) != NULL;
\r
379 static char opp_name[128][32];
\r
380 static int num_opps=0;
\r
382 extern ColorClass curColor;
\r
384 static void SetCurColor( ColorClass color )
\r
389 static void ColorizeEx( ColorClass color, int cont )
\r
391 if( appData.colorize ) {
\r
392 Colorize( color, cont );
\r
393 SetCurColor( color );
\r
397 int ZippyControl(buf, i)
\r
402 char reply[MSG_SIZ];
\r
405 #include "trivia.c"
\r
408 /* Possibly reject Crafty as opponent */
\r
409 if (appData.zippyPlay && appData.zippyNoplayCrafty && forwardMostMove < 4
\r
410 && looking_at(buf, i, "* kibitzes: Hello from Crafty"))
\r
412 player = StripHighlightAndTitle(star_match[0]);
\r
413 if ((gameMode == IcsPlayingWhite &&
\r
414 StrCaseCmp(player, gameInfo.black) == 0) ||
\r
415 (gameMode == IcsPlayingBlack &&
\r
416 StrCaseCmp(player, gameInfo.white) == 0)) {
\r
418 sprintf(reply, "%ssay This computer does not play Crafty clones\n%sabort\n%s+noplay %s\n",
\r
419 ics_prefix, ics_prefix, ics_prefix, player);
\r
425 /* If this is a computer, save the name. Then later, once the */
\r
426 /* game is really started, we will send the "computer" notice to */
\r
428 if (appData.zippyPlay &&
\r
429 looking_at(buf, i, "* is in the computer list")) {
\r
431 for (i=0;i<num_opps;i++)
\r
432 if (!strcmp(opp_name[i],star_match[0])) break;
\r
433 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[0]);
\r
435 if (appData.zippyPlay && looking_at(buf, i, "* * is a computer *")) {
\r
437 for (i=0;i<num_opps;i++)
\r
438 if (!strcmp(opp_name[i],star_match[1])) break;
\r
439 if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[1]);
\r
442 /* Tells and says */
\r
443 if (appData.zippyPlay &&
\r
444 (looking_at(buf, i, "* offers to be your bughouse partner") ||
\r
445 looking_at(buf, i, "* tells you: [automatic message] I chose you"))) {
\r
446 player = StripHighlightAndTitle(star_match[0]);
\r
447 if (appData.zippyBughouse > 1 && first.initDone) {
\r
448 sprintf(reply, "%spartner %s\n", ics_prefix, player);
\r
450 if (strcmp(zippyPartner, player) != 0) {
\r
451 strcpy(zippyPartner, player);
\r
452 SendToProgram(reply + strlen(ics_prefix), &first);
\r
454 } else if (appData.zippyBughouse > 0) {
\r
455 sprintf(reply, "%sdecline %s\n", ics_prefix, player);
\r
458 sprintf(reply, "%stell %s This computer cannot play bughouse\n",
\r
459 ics_prefix, player);
\r
465 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
466 looking_at(buf, i, "* agrees to be your partner")) {
\r
467 player = StripHighlightAndTitle(star_match[0]);
\r
468 sprintf(reply, "partner %s\n", player);
\r
469 if (strcmp(zippyPartner, player) != 0) {
\r
470 strcpy(zippyPartner, player);
\r
471 SendToProgram(reply, &first);
\r
476 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
477 (looking_at(buf, i, "are no longer *'s partner") ||
\r
479 "* tells you: [automatic message] I'm no longer your"))) {
\r
480 player = StripHighlightAndTitle(star_match[0]);
\r
481 if (strcmp(zippyPartner, player) == 0) {
\r
482 zippyPartner[0] = NULLCHAR;
\r
483 SendToProgram("partner\n", &first);
\r
488 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
489 (looking_at(buf, i, "no longer have a bughouse partner") ||
\r
490 looking_at(buf, i, "partner has disconnected") ||
\r
491 looking_at(buf, i, "partner has just chosen a new partner"))) {
\r
492 zippyPartner[0] = NULLCHAR;
\r
493 SendToProgram("partner\n", &first);
\r
497 if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
\r
498 looking_at(buf, i, "* (your partner) tells you: *")) {
\r
499 /* This pattern works on FICS but not ICC */
\r
500 player = StripHighlightAndTitle(star_match[0]);
\r
501 if (strcmp(zippyPartner, player) != 0) {
\r
502 strcpy(zippyPartner, player);
\r
503 sprintf(reply, "partner %s\n", player);
\r
504 SendToProgram(reply, &first);
\r
506 sprintf(reply, "ptell %s\n", star_match[1]);
\r
507 SendToProgram(reply, &first);
\r
511 if (looking_at(buf, i, "* tells you: *") ||
\r
512 looking_at(buf, i, "* says: *"))
\r
514 player = StripHighlightAndTitle(star_match[0]);
\r
515 if (appData.zippyPassword[0] != NULLCHAR &&
\r
516 strncmp(star_match[1], appData.zippyPassword,
\r
517 strlen(appData.zippyPassword)) == 0) {
\r
518 p = star_match[1] + strlen(appData.zippyPassword);
\r
519 while (*p == ' ') p++;
\r
522 } else if (appData.zippyPassword2[0] != NULLCHAR && first.initDone &&
\r
523 strncmp(star_match[1], appData.zippyPassword2,
\r
524 strlen(appData.zippyPassword2)) == 0) {
\r
525 p = star_match[1] + strlen(appData.zippyPassword2);
\r
526 while (*p == ' ') p++;
\r
527 SendToProgram(p, &first);
\r
528 SendToProgram("\n", &first);
\r
529 } else if (appData.zippyWrongPassword[0] != NULLCHAR &&
\r
530 strncmp(star_match[1], appData.zippyWrongPassword,
\r
531 strlen(appData.zippyWrongPassword)) == 0) {
\r
532 p = star_match[1] + strlen(appData.zippyWrongPassword);
\r
533 while (*p == ' ') p++;
\r
534 sprintf(reply, "wrong %s\n", player);
\r
536 } else if (appData.zippyBughouse && first.initDone &&
\r
537 strcmp(player, zippyPartner) == 0) {
\r
538 SendToProgram("ptell ", &first);
\r
539 SendToProgram(star_match[1], &first);
\r
540 SendToProgram("\n", &first);
\r
541 } else if (strncmp(star_match[1], HI, 6) == 0) {
\r
542 extern char* programVersion;
\r
543 sprintf(reply, "%stell %s %s\n",
\r
544 ics_prefix, player, programVersion);
\r
546 } else if (strncmp(star_match[1], "W0W!! ", 6) == 0) {
\r
547 extern char* programVersion;
\r
548 sprintf(reply, "%stell %s %s\n", ics_prefix,
\r
549 player, programVersion);
\r
551 } else if (appData.zippyTalk && (((unsigned) random() % 10) < 9)) {
\r
552 if (strcmp(player, ics_handle) != 0) {
\r
553 Speak("tell", player);
\r
557 ColorizeEx( ColorTell, FALSE );
\r
562 if( appData.colorize && looking_at(buf, i, "* (*) seeking") ) {
\r
563 ColorizeEx(ColorSeek, FALSE);
\r
567 if (looking_at(buf, i, "* spoofs you:")) {
\r
568 player = StripHighlightAndTitle(star_match[0]);
\r
569 sprintf(reply, "spoofedby %s\n", player);
\r
576 int ZippyConverse(buf, i)
\r
580 static char lastgreet[MSG_SIZ];
\r
581 char reply[MSG_SIZ];
\r
584 /* Shouts and emotes */
\r
585 if (looking_at(buf, i, "--> * *") ||
\r
586 looking_at(buf, i, "* shouts: *"))
\r
588 if (appData.zippyTalk) {
\r
589 char *player = StripHighlightAndTitle(star_match[0]);
\r
590 if (strcmp(player, ics_handle) == 0) {
\r
592 } else if (appData.zippyPinhead[0] != NULLCHAR &&
\r
593 StrCaseStr(star_match[1], appData.zippyPinhead) != NULL) {
\r
594 sprintf(reply, "insult %s\n", player);
\r
596 } else if (ZippyCalled(star_match[1])) {
\r
597 Speak("shout", NULL);
\r
601 ColorizeEx(ColorShout, FALSE);
\r
606 if (looking_at(buf, i, "* kibitzes: *")) {
\r
607 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
\r
608 char *player = StripHighlightAndTitle(star_match[0]);
\r
609 if (strcmp(player, ics_handle) != 0) {
\r
610 Speak("kibitz", NULL);
\r
614 ColorizeEx(ColorKibitz, FALSE);
\r
619 if (looking_at(buf, i, "* whispers: *")) {
\r
620 if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
\r
621 char *player = StripHighlightAndTitle(star_match[0]);
\r
622 if (strcmp(player, ics_handle) != 0) {
\r
623 Speak("whisper", NULL);
\r
627 ColorizeEx(ColorKibitz, FALSE);
\r
633 if ((looking_at(buf, i, ". * (*:*): *") && isdigit(star_match[1][0])) ||
\r
634 looking_at(buf, i, ". * at *:*: *")) {
\r
635 if (appData.zippyTalk) {
\r
637 char *player = StripHighlightAndTitle(star_match[0]);
\r
639 if (strcmp(player, ics_handle) != 0) {
\r
640 if (((unsigned) random() % 10) < 9)
\r
641 Speak("message", player);
\r
642 f = fopen("zippy.messagelog", "a");
\r
643 fprintf(f, "%s (%s:%s): %s\n", player,
\r
644 star_match[1], star_match[2], star_match[3]);
\r
651 /* Channel tells */
\r
653 if (looking_at(buf, i, "*(*: *")) {
\r
656 if (star_match[0][0] == NULLCHAR ||
\r
657 strchr(star_match[0], ' ') ||
\r
658 strchr(star_match[1], ' ')) {
\r
659 /* Oops, did not want to match this; probably a message */
\r
663 if (appData.zippyTalk) {
\r
664 player = StripHighlightAndTitle(star_match[0]);
\r
665 channel = strrchr(star_match[1], '(');
\r
666 if (channel == NULL) {
\r
667 channel = star_match[1];
\r
671 channel[strlen(channel)-1] = NULLCHAR;
\r
673 /* Always tell to the channel (probability 90%) */
\r
674 if (strcmp(player, ics_handle) != 0 &&
\r
675 ((unsigned) random() % 10) < 9) {
\r
676 Speak("tell", channel);
\r
679 /* Tell to the channel only if someone mentions our name */
\r
680 if (ZippyCalled(star_match[2])) {
\r
681 Speak("tell", channel);
\r
685 ColorizeEx( atoi(channel) == 1 ? ColorChannel1 : ColorChannel, FALSE );
\r
690 if (!appData.zippyTalk) return FALSE;
\r
692 if ((looking_at(buf, i, "You have * message") &&
\r
693 atoi(star_match[0]) != 0) ||
\r
694 looking_at(buf, i, "* has left a message for you") ||
\r
695 looking_at(buf, i, "* just sent you a message")) {
\r
696 sprintf(reply, "%smessages\n%sclearmessages *\n",
\r
697 ics_prefix, ics_prefix);
\r
702 if (looking_at(buf, i, "Notification: * has arrived")) {
\r
703 if (((unsigned) random() % 3) == 0) {
\r
704 char *player = StripHighlightAndTitle(star_match[0]);
\r
705 strcpy(lastgreet, player);
\r
706 sprintf(reply, "greet %s\n", player);
\r
708 Speak("tell", player);
\r
712 if (looking_at(buf, i, "Notification: * has departed")) {
\r
713 if (((unsigned) random() % 3) == 0) {
\r
714 char *player = StripHighlightAndTitle(star_match[0]);
\r
715 sprintf(reply, "farewell %s\n", player);
\r
720 if (looking_at(buf, i, "Not sent -- * is censoring you")) {
\r
721 char *player = StripHighlightAndTitle(star_match[0]);
\r
722 if (strcmp(player, lastgreet) == 0) {
\r
723 sprintf(reply, "%s-notify %s\n", ics_prefix, player);
\r
728 if (looking_at(buf, i, "command is currently turned off")) {
\r
729 appData.zippyUseI = 0;
\r
735 void ZippyGameStart(white, black)
\r
736 char *white, *black;
\r
738 if (!first.initDone) {
\r
739 /* Game is starting prematurely. We can't deal with this */
\r
740 SendToICS(ics_prefix);
\r
741 SendToICS("abort\n");
\r
742 SendToICS(ics_prefix);
\r
743 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
747 if (appData.zippyGameStart[0] != NULLCHAR) {
\r
748 SendToICS(appData.zippyGameStart);
\r
753 void ZippyGameEnd(result, resultDetails)
\r
755 char *resultDetails;
\r
757 if (appData.zippyAcceptOnly[0] == NULLCHAR &&
\r
758 appData.zippyGameEnd[0] != NULLCHAR) {
\r
759 SendToICS(appData.zippyGameEnd);
\r
762 zippyLastGameEnd = time(0);
\r
766 * Routines to implement Zippy playing chess
\r
769 void ZippyHandleChallenge(srated, swild, sbase, sincrement, opponent)
\r
770 char *srated, *swild, *sbase, *sincrement, *opponent;
\r
773 int base, increment, i=0;
\r
775 VariantClass variant;
\r
779 variant = StringToVariant(swild);
\r
780 varname = VariantName(variant);
\r
781 base = atoi(sbase);
\r
782 increment = atoi(sincrement);
\r
784 /* If desired, you can insert more code here to decline matches
\r
785 based on rated, variant, base, and increment, but it is
\r
786 easier to use the ICS formula feature instead. */
\r
788 if (variant == VariantLoadable) {
\r
790 "%stell %s This computer can't play wild type %s\n%sdecline %s\n",
\r
791 ics_prefix, opponent, swild, ics_prefix, opponent);
\r
795 if (StrStr(appData.zippyVariants, varname) == NULL ||
\r
796 (i=first.protocolVersion) != 1 && StrStr(first.variants, varname) == NULL /* [HGM] zippyvar */
\r
799 "%stell %s This computer can't play %s [%s], only %s\n%sdecline %s\n",
\r
800 ics_prefix, opponent, swild, varname,
\r
801 i ? first.variants : appData.zippyVariants, /* [HGM] zippyvar */
\r
802 ics_prefix, opponent);
\r
807 /* Are we blocking match requests from all but one person? */
\r
808 if (appData.zippyAcceptOnly[0] != NULLCHAR &&
\r
809 StrCaseCmp(opponent, appData.zippyAcceptOnly)) {
\r
810 /* Yes, and this isn't him. Ignore challenge. */
\r
814 /* Too many consecutive games with same opponent? If so, make him
\r
815 wait until someone else has played or a timeout has elapsed. */
\r
816 if (appData.zippyMaxGames &&
\r
817 strcmp(opponent, zippyLastOpp) == 0 &&
\r
818 zippyConsecGames >= appData.zippyMaxGames &&
\r
819 difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
\r
820 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
821 ics_prefix, opponent, zippyConsecGames, ics_handle,
\r
822 appData.zippyReplayTimeout, ics_prefix, opponent);
\r
827 /* Engine not yet initialized or still thinking about last game? */
\r
828 if (!first.initDone || first.lastPing != first.lastPong) {
\r
829 sprintf(buf, "%stell %s I'm not quite ready for a new game yet; try again soon.\n%sdecline %s\n",
\r
830 ics_prefix, opponent, ics_prefix, opponent);
\r
835 sprintf(buf, "%saccept %s\n", ics_prefix, opponent);
\r
837 if (appData.zippyTalk) {
\r
838 Speak("tell", opponent);
\r
843 /* Accept matches */
\r
844 int ZippyMatch(buf, i)
\r
848 if (looking_at(buf, i, "* * match * * requested with * (*)")) {
\r
850 ZippyHandleChallenge(star_match[0], star_match[1],
\r
851 star_match[2], star_match[3],
\r
852 StripHighlightAndTitle(star_match[4]));
\r
856 /* Old FICS 0-increment form */
\r
857 if (looking_at(buf, i, "* * match * requested with * (*)")) {
\r
859 ZippyHandleChallenge(star_match[0], star_match[1],
\r
860 star_match[2], "0",
\r
861 StripHighlightAndTitle(star_match[3]));
\r
865 if (looking_at(buf, i,
\r
866 "* has made an alternate proposal of * * match * *.")) {
\r
868 ZippyHandleChallenge(star_match[1], star_match[2],
\r
869 star_match[3], star_match[4],
\r
870 StripHighlightAndTitle(star_match[0]));
\r
874 /* FICS wild/nonstandard forms */
\r
875 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * Loaded from *")) {
\r
876 /* note: star_match[2] can include "[white] " or "[black] "
\r
877 before our own name. */
\r
878 if(star_match[8] == NULL || star_match[8][0] == 0) // [HGM] chessd: open-source ICS has file on next line
\r
879 ZippyHandleChallenge(star_match[4], star_match[5],
\r
880 star_match[6], star_match[7],
\r
881 StripHighlightAndTitle(star_match[0]));
\r
882 else ZippyHandleChallenge(star_match[4], star_match[8],
\r
883 star_match[6], star_match[7],
\r
884 StripHighlightAndTitle(star_match[0]));
\r
888 if (looking_at(buf, i,
\r
889 "Challenge: * (*) *(*) * * * * : * * Loaded from *")) {
\r
890 /* note: star_match[2] can include "[white] " or "[black] "
\r
891 before our own name. */
\r
892 ZippyHandleChallenge(star_match[4], star_match[10],
\r
893 star_match[8], star_match[9],
\r
894 StripHighlightAndTitle(star_match[0]));
\r
898 /* Regular forms */
\r
899 if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * : * *") |
\r
900 looking_at(buf, i, "Challenge: * (*) *(*) * * * * * *")) {
\r
901 /* note: star_match[2] can include "[white] " or "[black] "
\r
902 before our own name. */
\r
903 ZippyHandleChallenge(star_match[4], star_match[5],
\r
904 star_match[8], star_match[9],
\r
905 StripHighlightAndTitle(star_match[0]));
\r
909 if (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[6], star_match[7],
\r
914 StripHighlightAndTitle(star_match[0]));
\r
918 if (looking_at(buf, i, "offers you a draw")) {
\r
919 if (first.sendDrawOffers && first.initDone) {
\r
920 SendToProgram("draw\n", &first);
\r
925 if (looking_at(buf, i, "requests that the game be aborted") ||
\r
926 looking_at(buf, i, "would like to abort")) {
\r
927 if (appData.zippyAbort ||
\r
928 (gameMode == IcsPlayingWhite && whiteTimeRemaining < 0) ||
\r
929 (gameMode == IcsPlayingBlack && blackTimeRemaining < 0)) {
\r
930 SendToICS(ics_prefix);
\r
931 SendToICS("abort\n");
\r
933 SendToICS(ics_prefix);
\r
934 if (appData.zippyTalk)
\r
935 SendToICS("say Whoa no! I am having FUN!!\n");
\r
937 SendToICS("say Sorry, this computer doesn't accept aborts.\n");
\r
942 if (looking_at(buf, i, "requests adjournment") ||
\r
943 looking_at(buf, i, "would like to adjourn")) {
\r
944 if (appData.zippyAdjourn) {
\r
945 SendToICS(ics_prefix);
\r
946 SendToICS("adjourn\n");
\r
948 SendToICS(ics_prefix);
\r
949 if (appData.zippyTalk)
\r
950 SendToICS("say Whoa no! I am having FUN playing NOW!!\n");
\r
952 SendToICS("say Sorry, this computer doesn't accept adjourns.\n");
\r
960 /* Initialize chess program with data from the first board
\r
961 * of a new or resumed game.
\r
963 void ZippyFirstBoard(moveNum, basetime, increment)
\r
964 int moveNum, basetime, increment;
\r
968 char *opp = (gameMode==IcsPlayingWhite ? gameInfo.black : gameInfo.white);
\r
969 Boolean sentPos = FALSE;
970 char *bookHit = NULL; // [HGM] book
\r
972 if (!first.initDone) {
\r
973 /* Game is starting prematurely. We can't deal with this */
\r
974 SendToICS(ics_prefix);
\r
975 SendToICS("abort\n");
\r
976 SendToICS(ics_prefix);
\r
977 SendToICS("say Sorry, the chess program is not initialized yet.\n");
\r
981 /* Send the variant command if needed */
\r
982 if (gameInfo.variant != VariantNormal) {
\r
983 sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
\r
984 SendToProgram(buf, &first);
\r
987 if ((startedFromSetupPosition && moveNum == 0) ||
\r
988 (!appData.getMoveList && moveNum > 0)) {
\r
989 SendToProgram("force\n", &first);
\r
990 SendBoard(&first, moveNum);
\r
994 sprintf(buf, "level 0 %d %d\n", basetime, increment);
\r
995 SendToProgram(buf, &first);
\r
997 /* Count consecutive games from one opponent */
\r
998 if (strcmp(opp, zippyLastOpp) == 0) {
\r
999 zippyConsecGames++;
\r
1001 zippyConsecGames = 1;
\r
1002 strcpy(zippyLastOpp, opp);
\r
1005 /* Send the "computer" command if the opponent is in the list
\r
1006 we've been gathering. */
\r
1007 for (w=0; w<num_opps; w++) {
\r
1008 if (!strcmp(opp_name[w], opp)) {
\r
1009 SendToProgram(first.computerString, &first);
\r
1014 /* Ratings might be < 0 which means "we haven't seen a ratings
\r
1015 message from ICS." Send 0 in that case */
\r
1016 w = (gameInfo.whiteRating >= 0) ? gameInfo.whiteRating : 0;
\r
1017 b = (gameInfo.blackRating >= 0) ? gameInfo.blackRating : 0;
\r
1019 firstMove = FALSE;
\r
1020 if (gameMode == IcsPlayingWhite) {
\r
1021 if (first.sendName) {
\r
1022 sprintf(buf, "name %s\n", gameInfo.black);
\r
1023 SendToProgram(buf, &first);
\r
1025 strcpy(ics_handle, gameInfo.white);
\r
1026 sprintf(buf, "rating %d %d\n", w, b);
\r
1027 SendToProgram(buf, &first);
\r
1029 /* Position sent above, engine is in force mode */
\r
1030 if (WhiteOnMove(moveNum)) {
\r
1031 /* Engine is on move now */
\r
1032 if (first.sendTime) {
\r
1033 if (first.useColors) {
\r
1034 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1035 SendTimeRemaining(&first, TRUE);
\r
1036 SendToProgram("white\n", &first);
\r
1038 SendTimeRemaining(&first, TRUE);
\r
1041 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1043 /* Engine's opponent is on move now */
\r
1044 if (first.usePlayother) {
\r
1045 if (first.sendTime) {
\r
1046 SendTimeRemaining(&first, TRUE);
\r
1048 SendToProgram("playother\n", &first);
\r
1050 /* Need to send a "go" after opponent moves */
\r
1055 /* Position not sent above, move list might be sent later */
\r
1056 if (moveNum == 0) {
\r
1057 /* No move list coming; at start of game */
\r
1058 if (first.sendTime) {
\r
1059 if (first.useColors) {
\r
1060 SendToProgram("black\n", &first); /*gnu kludge*/
\r
1061 SendTimeRemaining(&first, TRUE);
\r
1062 SendToProgram("white\n", &first);
\r
1064 SendTimeRemaining(&first, TRUE);
\r
1067 // SendToProgram("go\n", &first);
\r
1068 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1071 } else if (gameMode == IcsPlayingBlack) {
\r
1072 if (first.sendName) {
\r
1073 sprintf(buf, "name %s\n", gameInfo.white);
\r
1074 SendToProgram(buf, &first);
\r
1076 strcpy(ics_handle, gameInfo.black);
\r
1077 sprintf(buf, "rating %d %d\n", b, w);
\r
1078 SendToProgram(buf, &first);
\r
1080 /* Position sent above, engine is in force mode */
\r
1081 if (!WhiteOnMove(moveNum)) {
\r
1082 /* Engine is on move now */
\r
1083 if (first.sendTime) {
\r
1084 if (first.useColors) {
\r
1085 SendToProgram("white\n", &first); /*gnu kludge*/
\r
1086 SendTimeRemaining(&first, FALSE);
\r
1087 SendToProgram("black\n", &first);
\r
1089 SendTimeRemaining(&first, FALSE);
\r
1092 // SendToProgram("go\n", &first);
\r
1093 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
\r
1095 /* Engine's opponent is on move now */
\r
1096 if (first.usePlayother) {
\r
1097 if (first.sendTime) {
\r
1098 SendTimeRemaining(&first, FALSE);
\r
1100 SendToProgram("playother\n", &first);
\r
1102 /* Need to send a "go" after opponent moves */
\r
1107 /* Position not sent above, move list might be sent later */
\r
1108 /* Nothing needs to be done here */
\r
1112 if(bookHit) { // [HGM] book: simulate book reply
1113 static char bookMove[MSG_SIZ]; // a bit generous?
1115 programStats.depth = programStats.nodes = programStats.time =
1116 programStats.score = programStats.got_only_move = 0;
1117 sprintf(programStats.movelist, "%s (xbook)", bookHit);
1119 strcpy(bookMove, "move ");
1120 strcat(bookMove, bookHit);
1121 HandleMachineMove(bookMove, &first);
1127 ZippyHoldings(white_holding, black_holding, new_piece)
\r
1128 char *white_holding, *black_holding, *new_piece;
\r
1130 char buf[MSG_SIZ];
\r
1131 if (gameMode != IcsPlayingBlack && gameMode != IcsPlayingWhite) return;
\r
1132 sprintf(buf, "holding [%s] [%s] %s\n",
\r
1133 white_holding, black_holding, new_piece);
\r
1134 SendToProgram(buf, &first);
\r