-/*\r
- * zippy.c -- Implements Zippy the Pinhead chess player on ICS in XBoard\r
- * $Id: zippy.c,v 2.2 2003/11/25 05:25:20 mann Exp $\r
- *\r
- * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
- * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
- *\r
- * The following terms apply to Digital Equipment Corporation's copyright\r
- * interest in XBoard:\r
- * ------------------------------------------------------------------------\r
- * All Rights Reserved\r
- *\r
- * Permission to use, copy, modify, and distribute this software and its\r
- * documentation for any purpose and without fee is hereby granted,\r
- * provided that the above copyright notice appear in all copies and that\r
- * both that copyright notice and this permission notice appear in\r
- * supporting documentation, and that the name of Digital not be\r
- * used in advertising or publicity pertaining to distribution of the\r
- * software without specific, written prior permission.\r
- *\r
- * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
- * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
- * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
- * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
- * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
- * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
- * SOFTWARE.\r
- * ------------------------------------------------------------------------\r
- *\r
- * The following terms apply to the enhanced version of XBoard distributed\r
- * by the Free Software Foundation:\r
- * ------------------------------------------------------------------------\r
- * This program is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 2 of the License, or\r
- * (at your option) any later version.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
- * ------------------------------------------------------------------------\r
- */\r
-\r
-#include "config.h"\r
-\r
-#include <stdio.h>\r
-#include <errno.h>\r
-#include <sys/types.h>\r
-#include <sys/stat.h>\r
-#include <ctype.h>\r
-\r
-#if STDC_HEADERS\r
-# include <stdlib.h>\r
-# include <string.h>\r
-#else /* not STDC_HEADERS */\r
-extern char *getenv();\r
-# if HAVE_STRING_H\r
-# include <string.h>\r
-# else /* not HAVE_STRING_H */\r
-# include <strings.h>\r
-# endif /* not HAVE_STRING_H */\r
-#endif /* not STDC_HEADERS */\r
-\r
-#if TIME_WITH_SYS_TIME\r
-# include <sys/time.h>\r
-# include <time.h>\r
-#else\r
-# if HAVE_SYS_TIME_H\r
-# include <sys/time.h>\r
-# else\r
-# include <time.h>\r
-# endif\r
-#endif\r
-#define HI "hlelo "\r
-\r
-#if HAVE_UNISTD_H\r
-# include <unistd.h>\r
-#endif\r
-\r
-#include "common.h"\r
-#include "zippy.h"\r
-#include "frontend.h"\r
-#include "backend.h"\r
-#include "backendz.h"\r
-\r
-char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book\r
-\r
-static char zippyPartner[MSG_SIZ];\r
-static char zippyLastOpp[MSG_SIZ];\r
-static int zippyConsecGames;\r
-static time_t zippyLastGameEnd;\r
-\r
-extern void mysrandom(unsigned int seed);\r
-extern int myrandom(void);\r
-\r
-void ZippyInit()\r
-{\r
- char *p;\r
-\r
- /* Get name of Zippy lines file */\r
- p = getenv("ZIPPYLINES");\r
- if (p != NULL) {\r
- appData.zippyLines = p;\r
- }\r
-\r
- /* Get word that Zippy thinks is insulting */\r
- p = getenv("ZIPPYPINHEAD");\r
- if (p != NULL) {\r
- appData.zippyPinhead = p;\r
- }\r
-\r
- /* What password is used for remote control? */\r
- p = getenv("ZIPPYPASSWORD");\r
- if (p != NULL) {\r
- appData.zippyPassword = p;\r
- }\r
-\r
- /* What password is used for remote commands to gnuchess? */\r
- p = getenv("ZIPPYPASSWORD2");\r
- if (p != NULL) {\r
- appData.zippyPassword2 = p;\r
- }\r
-\r
- /* Joke feature for people who try an old password */\r
- p = getenv("ZIPPYWRONGPASSWORD");\r
- if (p != NULL) {\r
- appData.zippyWrongPassword = p;\r
- }\r
-\r
- /* While testing, I want to accept challenges from only one person\r
- (namely, my "anonymous" account), so I set an environment\r
- variable ZIPPYACCEPTONLY. */\r
- p = getenv("ZIPPYACCEPTONLY");\r
- if ( p != NULL ) {\r
- appData.zippyAcceptOnly = p;\r
- }\r
- \r
- /* Should Zippy use "i" command? */\r
- /* Defaults to 1=true */\r
- p = getenv("ZIPPYUSEI");\r
- if (p != NULL) {\r
- appData.zippyUseI = atoi(p);\r
- }\r
-\r
- /* How does Zippy handle bughouse partnering? */\r
- /* 0=say we can't play, 1=manual partnering, 2=auto partnering */\r
- p = getenv("ZIPPYBUGHOUSE");\r
- if (p != NULL) {\r
- appData.zippyBughouse = atoi(p);\r
- }\r
-\r
- /* Does Zippy abort games with Crafty? */\r
- /* Defaults to 0=false */\r
- p = getenv("ZIPPYNOPLAYCRAFTY");\r
- if (p != NULL) {\r
- appData.zippyNoplayCrafty = atoi(p);\r
- }\r
-\r
- /* What ICS command does Zippy send at game end? Default: "gameend". */\r
- p = getenv("ZIPPYGAMEEND");\r
- if (p != NULL) {\r
- appData.zippyGameEnd = p;\r
- }\r
-\r
- /* What ICS command does Zippy send at game start? Default: none. */\r
- p = getenv("ZIPPYGAMESTART");\r
- if (p != NULL) {\r
- appData.zippyGameStart = p;\r
- }\r
-\r
- /* Should Zippy accept adjourns? */\r
- /* Defaults to 0=false */\r
- p = getenv("ZIPPYADJOURN");\r
- if (p != NULL) {\r
- appData.zippyAdjourn = atoi(p);\r
- }\r
-\r
- /* Should Zippy accept aborts? */\r
- /* Defaults to 0=false */\r
- p = getenv("ZIPPYABORT");\r
- if (p != NULL) {\r
- appData.zippyAbort = atoi(p);\r
- }\r
-\r
- /* Should Zippy play chess variants (besides bughouse)? */\r
- p = getenv("ZIPPYVARIANTS");\r
- if (p != NULL) {\r
- appData.zippyVariants = p;\r
- }\r
- strcpy(first.variants, appData.zippyVariants);\r
-\r
- srandom(time(NULL));\r
-}\r
-\r
-/*\r
- * Routines to implement Zippy talking\r
- */\r
-\r
-\r
-char *swifties[] = { \r
- "i acclaims:", "i admonishes:", "i advertises:", "i advises:",\r
- "i advocates:", "i affirms:", "i alleges:", "i anathematizes:",\r
- "i animadverts:", "i announces:", "i apostrophizes:",\r
- "i appeals:", "i applauds:", "i approves:", "i argues:",\r
- "i articulates:", "i asserts:", "i asseverates:", "i attests:",\r
- "i avers:", "i avows:", "i baas:", "i babbles:", "i banters:",\r
- "i barks:", "i bawls:", "i bays:", "i begs:", "i belches:",\r
- "i bellows:", "i belts out:", "i berates:", "i beshrews:",\r
- "i blabbers:", "i blabs:", "i blares:", "i blasphemes:",\r
- "i blasts:", "i blathers:", "i bleats:", "i blithers:",\r
- "i blubbers:", "i blurts out:", "i blusters:", "i boasts:",\r
- "i brags:", "i brays:", "i broadcasts:", "i burbles:",\r
- "i buzzes:", "i cachinnates:", "i cackles:", "i caterwauls:",\r
- "i calumniates:", "i caws:", "i censures:", "i chants:",\r
- "i chatters:", "i cheeps:", "i cheers:", "i chides:", "i chins:",\r
- "i chirps:", "i chortles:", "i chuckles:", "i claims:",\r
- "i clamors:", "i clucks:", "i commands:", "i commends:",\r
- "i comments:", "i commiserates:", "i communicates:",\r
- "i complains:", "i concludes:", "i confabulates:", "i confesses:",\r
- "i coos:", "i coughs:", "i counsels:", "i cries:", "i croaks:",\r
- "i crows:", "i curses:", "i daydreams:", "i debates:",\r
- "i declaims:", "i declares:", "i delivers:", "i denounces:",\r
- "i deposes:", "i directs:", "i discloses:", "i disparages:",\r
- "i discourses:", "i divulges:", "i documents:", "i drawls:",\r
- "i dreams:", "i drivels:", "i drones:", "i effuses:",\r
- /*"i ejaculates:",*/ "i elucidates:", "i emotes:", "i endorses:",\r
- "i enthuses:", "i entreats:", "i enunciates:", "i eulogizes:",\r
- "i exclaims:", "i execrates:", "i exhorts:", "i expatiates:",\r
- "i explains:", "i explicates:", "i explodes:", "i exposes:",\r
- "i exposits:", "i expostulates: ",\r
- "i expounds:", "i expresses:", "i extols:",\r
- "i exults:", "i fantasizes:", "i fibs:", "i filibusters:",\r
- "i flatters:", "i flutes:", "i fools:", "i free-associates:",\r
- "i fulminates:", "i gabbles:", "i gabs:", "i gasps:",\r
- "i giggles:", "i gossips:", "i gripes:", "i groans:", "i growls:",\r
- "i grunts:", "i guesses:", "i guffaws:", "i gushes:", "i hails:",\r
- "i hallucinates:", "i harangues:", "i harmonizes:", "i hectors:",\r
- "i hints:", "i hisses:", "i hollers:", "i honks:", "i hoots:",\r
- "i hosannas:", "i howls:", "i hums:", "i hypothecates:",\r
- "i hypothesizes:", "i imagines:", "i implies:", "i implores:",\r
- "i imprecates:", "i indicates:", "i infers:",\r
- "i informs everyone:", "i instructs:", "i interjects:", \r
- "i interposes:", "i intimates:", "i intones:", "i introspects:",\r
- "i inveighs:", "i jabbers:", "i japes:", "i jests:", "i jibes:",\r
- "i jives:", "i jokes:", "i joshes:", "i keens:", "i laments:",\r
- "i lauds:", "i laughs:", "i lectures:", "i lies:", "i lilts:",\r
- "i lisps:", "i maintains:", "i maledicts:", "i maunders:",\r
- "i meows:", "i mewls:", "i mimes:", "i minces:", "i moans:",\r
- "i moos:", "i mourns:", "i mouths:", "i mumbles:", "i murmurs:",\r
- "i muses:", "i mutters:", "i nags:", "i natters:", "i neighs:",\r
- "i notes:", "i nuncupates:", "i objurgates:", "i observes:",\r
- "i offers:", "i oinks:", "i opines:", "i orates:", "i orders:",\r
- "i panegyrizes:", "i pantomimes:", "i pants:", "i peals:",\r
- "i peeps:", "i perorates:", "i persuades:", "i petitions:",\r
- "i phonates:", "i pipes up:", "i pitches:", "i pleads:",\r
- "i points out:", "i pontificates:", "i postulates:", "i praises:",\r
- "i prates:", "i prattles:", "i preaches:", "i prescribes:",\r
- "i prevaricates:", "i proclaims:", "i projects:", "i pronounces:",\r
- "i proposes:", "i proscribes:", "i quacks:", "i queries:",\r
- "i questions:", "i quips:", "i quotes:", "i rages:", "i rambles:",\r
- "i rants:", "i raps:", "i rasps:", "i rattles:", "i raves:",\r
- "i reacts:", "i recites:", "i recommends:", "i records:",\r
- "i reiterates:", "i rejoins:", "i releases:", "i remarks:",\r
- "i reminisces:", "i remonstrates:", "i repeats:", "i replies:",\r
- "i reports:", "i reprimands:", "i reproaches:", "i reproves:",\r
- "i resounds:", "i responds:", "i retorts:", "i reveals:",\r
- "i reviles:", "i roars:", "i rumbles:", "i sanctions:",\r
- "i satirizes:", "i sauces:", "i scolds:", "i screams:",\r
- "i screeches:", "i semaphores:", "i sends:", "i sermonizes:",\r
- "i shrieks:", "i sibilates:", "i sighs:", "i signals:",\r
- "i signifies:", "i signs:", "i sings:", "i slurs:", "i snaps:",\r
- "i snarls:", "i sneezes:", "i snickers:", "i sniggers:",\r
- "i snivels:", "i snores:", "i snorts:", "i sobs:",\r
- "i soliloquizes:", "i sounds off:", "i sounds out:", "i speaks:",\r
- "i spews:", "i spits out:", "i splutters:", "i spoofs:",\r
- "i spouts:", "i sputters:", "i squalls:", "i squawks:",\r
- "i squeaks:", "i squeals:", "i stammers:", "i states:",\r
- "i stresses:", "i stutters:", "i submits:", "i suggests:",\r
- "i summarizes:", "i sums up:", "i swears:", "i talks:",\r
- "i tattles:", "i teases:", "i telegraphs:", "i testifies:",\r
- "i threatens:", "i thunders:", "i titters:", "i tongue-lashes:",\r
- "i toots:", "i transcribes:", "i transmits:", "i trills:",\r
- "i trumpets:", "i twaddles:", "i tweets:", "i twitters:",\r
- "i types:", "i upbraids:", "i urges:", "i utters:", "i ventures:",\r
- "i vibrates:", "i vilifies:", "i vituperates:", "i vocalizes:",\r
- "i vociferates:", "i voices:", "i waffles:", "i wails:",\r
- "i warbles:", "i warns:", "i weeps:", "i wheezes:", "i whimpers:",\r
- "i whines:", "i whinnies:", "i whistles:", "i wisecracks:",\r
- "i witnesses:", "i woofs:", "i writes:", "i yammers:", "i yawps:",\r
- "i yells:", "i yelps:", "i yodels:", "i yowls:", "i zings:",\r
-};\r
-\r
-#define MAX_SPEECH 250\r
-\r
-void Speak(how, whom) \r
- char *how, *whom;\r
-{\r
- static FILE *zipfile = NULL;\r
- static struct stat zipstat;\r
- char zipbuf[MAX_SPEECH + 1];\r
- static time_t lastShout = 0;\r
- time_t now;\r
- char *p;\r
- int c, speechlen;\r
- Boolean done;\r
- \r
- if (strcmp(how, "shout") == 0) {\r
- now = time((time_t *) NULL);\r
- if (now - lastShout < 1*60) return;\r
- lastShout = now;\r
- if (appData.zippyUseI) {\r
- how = swifties[(unsigned) random() %\r
- (sizeof(swifties)/sizeof(char *))];\r
- }\r
- }\r
-\r
- if (zipfile == NULL) {\r
- zipfile = fopen(appData.zippyLines, "r");\r
- if (zipfile == NULL) {\r
- DisplayFatalError("Can't open Zippy lines file", errno, 1);\r
- return;\r
- }\r
- fstat(fileno(zipfile), &zipstat);\r
- }\r
- \r
- for (;;) {\r
- fseek(zipfile, (unsigned) random() % zipstat.st_size, 0);\r
- do {\r
- c = getc(zipfile);\r
- } while (c != NULLCHAR && c != '^' && c != EOF);\r
- if (c == EOF) continue;\r
- while ((c = getc(zipfile)) == '\n') ;\r
- if (c == EOF) continue;\r
- break;\r
- }\r
- done = FALSE;\r
-\r
- /* Don't use ics_prefix; we need to let FICS expand the alias i -> it,\r
- but use the real command "i" on ICC */\r
- strcpy(zipbuf, how);\r
- strcat(zipbuf, " ");\r
- if (whom != NULL) {\r
- strcat(zipbuf, whom);\r
- strcat(zipbuf, " ");\r
- }\r
- speechlen = strlen(zipbuf);\r
- p = zipbuf + speechlen;\r
-\r
- while (++speechlen < MAX_SPEECH) {\r
- if (c == NULLCHAR || c == '^') {\r
- *p++ = '\n';\r
- *p = '\0';\r
- SendToICS(zipbuf);\r
- return;\r
- } else if (c == '\n') {\r
- *p++ = ' ';\r
- do {\r
- c = getc(zipfile);\r
- } while (c == ' ');\r
- } else if (c == EOF) {\r
- break;\r
- } else {\r
- *p++ = c;\r
- c = getc(zipfile);\r
- }\r
- }\r
- /* Tried to say something too long, or junk at the end of the\r
- file. Try something else. */\r
- Speak(how, whom); /* tail recursion */\r
-}\r
-\r
-int ZippyCalled(str)\r
- char *str;\r
-{\r
- return ics_handle[0] != NULLCHAR && StrCaseStr(str, ics_handle) != NULL;\r
-}\r
-\r
-static char opp_name[128][32];\r
-static int num_opps=0;\r
-\r
-extern ColorClass curColor;\r
-\r
-static void SetCurColor( ColorClass color )\r
-{\r
- curColor = color;\r
-}\r
-\r
-static void ColorizeEx( ColorClass color, int cont )\r
-{\r
- if( appData.colorize ) {\r
- Colorize( color, cont );\r
- SetCurColor( color );\r
- }\r
-}\r
-\r
-int ZippyControl(buf, i)\r
- char *buf;\r
- int *i;\r
-{\r
- char *player, *p;\r
- char reply[MSG_SIZ];\r
-\r
-#if TRIVIA\r
-#include "trivia.c"\r
-#endif\r
-\r
- /* Possibly reject Crafty as opponent */\r
- if (appData.zippyPlay && appData.zippyNoplayCrafty && forwardMostMove < 4\r
- && looking_at(buf, i, "* kibitzes: Hello from Crafty")) \r
- {\r
- player = StripHighlightAndTitle(star_match[0]);\r
- if ((gameMode == IcsPlayingWhite &&\r
- StrCaseCmp(player, gameInfo.black) == 0) ||\r
- (gameMode == IcsPlayingBlack &&\r
- StrCaseCmp(player, gameInfo.white) == 0)) {\r
-\r
- sprintf(reply, "%ssay This computer does not play Crafty clones\n%sabort\n%s+noplay %s\n",\r
- ics_prefix, ics_prefix, ics_prefix, player);\r
- SendToICS(reply);\r
- }\r
- return TRUE;\r
- }\r
-\r
- /* If this is a computer, save the name. Then later, once the */\r
- /* game is really started, we will send the "computer" notice to */\r
- /* the engine. */ \r
- if (appData.zippyPlay &&\r
- looking_at(buf, i, "* is in the computer list")) {\r
- int i;\r
- for (i=0;i<num_opps;i++)\r
- if (!strcmp(opp_name[i],star_match[0])) break;\r
- if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[0]);\r
- }\r
- if (appData.zippyPlay && looking_at(buf, i, "* * is a computer *")) {\r
- int i;\r
- for (i=0;i<num_opps;i++)\r
- if (!strcmp(opp_name[i],star_match[1])) break;\r
- if (i >= num_opps) strcpy(opp_name[num_opps++],star_match[1]);\r
- }\r
-\r
- /* Tells and says */\r
- if (appData.zippyPlay && \r
- (looking_at(buf, i, "* offers to be your bughouse partner") ||\r
- looking_at(buf, i, "* tells you: [automatic message] I chose you"))) {\r
- player = StripHighlightAndTitle(star_match[0]);\r
- if (appData.zippyBughouse > 1 && first.initDone) {\r
- sprintf(reply, "%spartner %s\n", ics_prefix, player);\r
- SendToICS(reply);\r
- if (strcmp(zippyPartner, player) != 0) {\r
- strcpy(zippyPartner, player);\r
- SendToProgram(reply + strlen(ics_prefix), &first);\r
- }\r
- } else if (appData.zippyBughouse > 0) {\r
- sprintf(reply, "%sdecline %s\n", ics_prefix, player);\r
- SendToICS(reply);\r
- } else {\r
- sprintf(reply, "%stell %s This computer cannot play bughouse\n",\r
- ics_prefix, player);\r
- SendToICS(reply);\r
- }\r
- return TRUE;\r
- }\r
-\r
- if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&\r
- looking_at(buf, i, "* agrees to be your partner")) {\r
- player = StripHighlightAndTitle(star_match[0]);\r
- sprintf(reply, "partner %s\n", player);\r
- if (strcmp(zippyPartner, player) != 0) {\r
- strcpy(zippyPartner, player);\r
- SendToProgram(reply, &first);\r
- }\r
- return TRUE;\r
- }\r
-\r
- if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&\r
- (looking_at(buf, i, "are no longer *'s partner") ||\r
- looking_at(buf, i,\r
- "* tells you: [automatic message] I'm no longer your"))) {\r
- player = StripHighlightAndTitle(star_match[0]);\r
- if (strcmp(zippyPartner, player) == 0) {\r
- zippyPartner[0] = NULLCHAR;\r
- SendToProgram("partner\n", &first);\r
- }\r
- return TRUE;\r
- }\r
-\r
- if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&\r
- (looking_at(buf, i, "no longer have a bughouse partner") ||\r
- looking_at(buf, i, "partner has disconnected") ||\r
- looking_at(buf, i, "partner has just chosen a new partner"))) {\r
- zippyPartner[0] = NULLCHAR;\r
- SendToProgram("partner\n", &first);\r
- return TRUE;\r
- }\r
-\r
- if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&\r
- looking_at(buf, i, "* (your partner) tells you: *")) {\r
- /* This pattern works on FICS but not ICC */\r
- player = StripHighlightAndTitle(star_match[0]);\r
- if (strcmp(zippyPartner, player) != 0) {\r
- strcpy(zippyPartner, player);\r
- sprintf(reply, "partner %s\n", player);\r
- SendToProgram(reply, &first);\r
- }\r
- sprintf(reply, "ptell %s\n", star_match[1]);\r
- SendToProgram(reply, &first);\r
- return TRUE;\r
- }\r
-\r
- if (looking_at(buf, i, "* tells you: *") ||\r
- looking_at(buf, i, "* says: *")) \r
- {\r
- player = StripHighlightAndTitle(star_match[0]);\r
- if (appData.zippyPassword[0] != NULLCHAR &&\r
- strncmp(star_match[1], appData.zippyPassword,\r
- strlen(appData.zippyPassword)) == 0) {\r
- p = star_match[1] + strlen(appData.zippyPassword);\r
- while (*p == ' ') p++;\r
- SendToICS(p);\r
- SendToICS("\n");\r
- } else if (appData.zippyPassword2[0] != NULLCHAR && first.initDone &&\r
- strncmp(star_match[1], appData.zippyPassword2,\r
- strlen(appData.zippyPassword2)) == 0) {\r
- p = star_match[1] + strlen(appData.zippyPassword2);\r
- while (*p == ' ') p++;\r
- SendToProgram(p, &first);\r
- SendToProgram("\n", &first);\r
- } else if (appData.zippyWrongPassword[0] != NULLCHAR &&\r
- strncmp(star_match[1], appData.zippyWrongPassword,\r
- strlen(appData.zippyWrongPassword)) == 0) {\r
- p = star_match[1] + strlen(appData.zippyWrongPassword);\r
- while (*p == ' ') p++;\r
- sprintf(reply, "wrong %s\n", player);\r
- SendToICS(reply);\r
- } else if (appData.zippyBughouse && first.initDone &&\r
- strcmp(player, zippyPartner) == 0) {\r
- SendToProgram("ptell ", &first);\r
- SendToProgram(star_match[1], &first);\r
- SendToProgram("\n", &first);\r
- } else if (strncmp(star_match[1], HI, 6) == 0) {\r
- extern char* programVersion;\r
- sprintf(reply, "%stell %s %s\n",\r
- ics_prefix, player, programVersion);\r
- SendToICS(reply);\r
- } else if (strncmp(star_match[1], "W0W!! ", 6) == 0) {\r
- extern char* programVersion;\r
- sprintf(reply, "%stell %s %s\n", ics_prefix,\r
- player, programVersion);\r
- SendToICS(reply);\r
- } else if (appData.zippyTalk && (((unsigned) random() % 10) < 9)) {\r
- if (strcmp(player, ics_handle) != 0) {\r
- Speak("tell", player);\r
- }\r
- }\r
-\r
- ColorizeEx( ColorTell, FALSE );\r
-\r
- return TRUE;\r
- }\r
-\r
- if( appData.colorize && looking_at(buf, i, "* (*) seeking") ) {\r
- ColorizeEx(ColorSeek, FALSE);\r
- return FALSE;\r
- }\r
-\r
- if (looking_at(buf, i, "* spoofs you:")) {\r
- player = StripHighlightAndTitle(star_match[0]);\r
- sprintf(reply, "spoofedby %s\n", player);\r
- SendToICS(reply);\r
- }\r
-\r
- return FALSE;\r
-}\r
-\r
-int ZippyConverse(buf, i)\r
- char *buf;\r
- int *i;\r
-{\r
- static char lastgreet[MSG_SIZ];\r
- char reply[MSG_SIZ];\r
- int oldi;\r
-\r
- /* Shouts and emotes */\r
- if (looking_at(buf, i, "--> * *") ||\r
- looking_at(buf, i, "* shouts: *")) \r
- {\r
- if (appData.zippyTalk) {\r
- char *player = StripHighlightAndTitle(star_match[0]);\r
- if (strcmp(player, ics_handle) == 0) {\r
- return TRUE;\r
- } else if (appData.zippyPinhead[0] != NULLCHAR &&\r
- StrCaseStr(star_match[1], appData.zippyPinhead) != NULL) {\r
- sprintf(reply, "insult %s\n", player);\r
- SendToICS(reply);\r
- } else if (ZippyCalled(star_match[1])) {\r
- Speak("shout", NULL);\r
- }\r
- }\r
-\r
- ColorizeEx(ColorShout, FALSE);\r
-\r
- return TRUE;\r
- }\r
-\r
- if (looking_at(buf, i, "* kibitzes: *")) {\r
- if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {\r
- char *player = StripHighlightAndTitle(star_match[0]);\r
- if (strcmp(player, ics_handle) != 0) {\r
- Speak("kibitz", NULL);\r
- }\r
- }\r
-\r
- ColorizeEx(ColorKibitz, FALSE);\r
-\r
- return TRUE;\r
- }\r
-\r
- if (looking_at(buf, i, "* whispers: *")) {\r
- if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {\r
- char *player = StripHighlightAndTitle(star_match[0]);\r
- if (strcmp(player, ics_handle) != 0) {\r
- Speak("whisper", NULL);\r
- }\r
- }\r
-\r
- ColorizeEx(ColorKibitz, FALSE);\r
-\r
- return TRUE;\r
- }\r
-\r
- /* Messages */\r
- if ((looking_at(buf, i, ". * (*:*): *") && isdigit(star_match[1][0])) ||\r
- looking_at(buf, i, ". * at *:*: *")) {\r
- if (appData.zippyTalk) {\r
- FILE *f;\r
- char *player = StripHighlightAndTitle(star_match[0]);\r
-\r
- if (strcmp(player, ics_handle) != 0) {\r
- if (((unsigned) random() % 10) < 9)\r
- Speak("message", player);\r
- f = fopen("zippy.messagelog", "a");\r
- fprintf(f, "%s (%s:%s): %s\n", player,\r
- star_match[1], star_match[2], star_match[3]);\r
- fclose(f);\r
- }\r
- }\r
- return TRUE;\r
- }\r
-\r
- /* Channel tells */\r
- oldi = *i;\r
- if (looking_at(buf, i, "*(*: *")) {\r
- char *player;\r
- char *channel;\r
- if (star_match[0][0] == NULLCHAR ||\r
- strchr(star_match[0], ' ') ||\r
- strchr(star_match[1], ' ')) {\r
- /* Oops, did not want to match this; probably a message */\r
- *i = oldi;\r
- return FALSE;\r
- }\r
- if (appData.zippyTalk) {\r
- player = StripHighlightAndTitle(star_match[0]);\r
- channel = strrchr(star_match[1], '(');\r
- if (channel == NULL) {\r
- channel = star_match[1];\r
- } else {\r
- channel++;\r
- }\r
- channel[strlen(channel)-1] = NULLCHAR;\r
-#if 0\r
- /* Always tell to the channel (probability 90%) */\r
- if (strcmp(player, ics_handle) != 0 &&\r
- ((unsigned) random() % 10) < 9) {\r
- Speak("tell", channel);\r
- }\r
-#else\r
- /* Tell to the channel only if someone mentions our name */\r
- if (ZippyCalled(star_match[2])) {\r
- Speak("tell", channel);\r
- }\r
-#endif\r
-\r
- ColorizeEx( atoi(channel) == 1 ? ColorChannel1 : ColorChannel, FALSE );\r
- }\r
- return TRUE;\r
- }\r
-\r
- if (!appData.zippyTalk) return FALSE;\r
-\r
- if ((looking_at(buf, i, "You have * message") &&\r
- atoi(star_match[0]) != 0) ||\r
- looking_at(buf, i, "* has left a message for you") ||\r
- looking_at(buf, i, "* just sent you a message")) {\r
- sprintf(reply, "%smessages\n%sclearmessages *\n",\r
- ics_prefix, ics_prefix);\r
- SendToICS(reply);\r
- return TRUE;\r
- }\r
-\r
- if (looking_at(buf, i, "Notification: * has arrived")) {\r
- if (((unsigned) random() % 3) == 0) {\r
- char *player = StripHighlightAndTitle(star_match[0]);\r
- strcpy(lastgreet, player);\r
- sprintf(reply, "greet %s\n", player);\r
- SendToICS(reply);\r
- Speak("tell", player);\r
- }\r
- } \r
-\r
- if (looking_at(buf, i, "Notification: * has departed")) {\r
- if (((unsigned) random() % 3) == 0) {\r
- char *player = StripHighlightAndTitle(star_match[0]);\r
- sprintf(reply, "farewell %s\n", player);\r
- SendToICS(reply);\r
- }\r
- } \r
-\r
- if (looking_at(buf, i, "Not sent -- * is censoring you")) {\r
- char *player = StripHighlightAndTitle(star_match[0]);\r
- if (strcmp(player, lastgreet) == 0) {\r
- sprintf(reply, "%s-notify %s\n", ics_prefix, player);\r
- SendToICS(reply);\r
- }\r
- } \r
-\r
- if (looking_at(buf, i, "command is currently turned off")) {\r
- appData.zippyUseI = 0;\r
- }\r
-\r
- return FALSE;\r
-}\r
-\r
-void ZippyGameStart(white, black)\r
- char *white, *black;\r
-{\r
- if (!first.initDone) {\r
- /* Game is starting prematurely. We can't deal with this */\r
- SendToICS(ics_prefix);\r
- SendToICS("abort\n");\r
- SendToICS(ics_prefix);\r
- SendToICS("say Sorry, the chess program is not initialized yet.\n");\r
- return;\r
- }\r
-\r
- if (appData.zippyGameStart[0] != NULLCHAR) {\r
- SendToICS(appData.zippyGameStart);\r
- SendToICS("\n");\r
- }\r
-}\r
-\r
-void ZippyGameEnd(result, resultDetails)\r
- ChessMove result;\r
- char *resultDetails;\r
-{\r
- if (appData.zippyAcceptOnly[0] == NULLCHAR &&\r
- appData.zippyGameEnd[0] != NULLCHAR) {\r
- SendToICS(appData.zippyGameEnd);\r
- SendToICS("\n");\r
- }\r
- zippyLastGameEnd = time(0);\r
-}\r
-\r
-/*\r
- * Routines to implement Zippy playing chess\r
- */\r
-\r
-void ZippyHandleChallenge(srated, swild, sbase, sincrement, opponent)\r
- char *srated, *swild, *sbase, *sincrement, *opponent;\r
-{\r
- char buf[MSG_SIZ];\r
- int base, increment, i=0;\r
- char rated;\r
- VariantClass variant;\r
- char *varname;\r
-\r
- rated = srated[0];\r
- variant = StringToVariant(swild);\r
- varname = VariantName(variant);\r
- base = atoi(sbase);\r
- increment = atoi(sincrement);\r
-\r
- /* [DM] If icsAnalyzeEngine active we don't accept automatic games */\r
- if (appData.icsActive && appData.icsEngineAnalyze) return;\r
-\r
- /* If desired, you can insert more code here to decline matches\r
- based on rated, variant, base, and increment, but it is\r
- easier to use the ICS formula feature instead. */\r
-\r
- if (variant == VariantLoadable) {\r
- sprintf(buf,\r
- "%stell %s This computer can't play wild type %s\n%sdecline %s\n",\r
- ics_prefix, opponent, swild, ics_prefix, opponent);\r
- SendToICS(buf);\r
- return;\r
- }\r
- if (StrStr(appData.zippyVariants, varname) == NULL ||\r
- (i=first.protocolVersion) != 1 && StrStr(first.variants, varname) == NULL /* [HGM] zippyvar */\r
- ) {\r
- sprintf(buf,\r
- "%stell %s This computer can't play %s [%s], only %s\n%sdecline %s\n",\r
- ics_prefix, opponent, swild, varname, \r
- i ? first.variants : appData.zippyVariants, /* [HGM] zippyvar */\r
- ics_prefix, opponent);\r
- SendToICS(buf);\r
- return;\r
- }\r
-\r
- /* Are we blocking match requests from all but one person? */\r
- if (appData.zippyAcceptOnly[0] != NULLCHAR &&\r
- StrCaseCmp(opponent, appData.zippyAcceptOnly)) {\r
- /* Yes, and this isn't him. Ignore challenge. */\r
- return;\r
- }\r
- \r
- /* Too many consecutive games with same opponent? If so, make him\r
- wait until someone else has played or a timeout has elapsed. */\r
- if (appData.zippyMaxGames &&\r
- strcmp(opponent, zippyLastOpp) == 0 &&\r
- zippyConsecGames >= appData.zippyMaxGames &&\r
- difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {\r
- 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
- ics_prefix, opponent, zippyConsecGames, ics_handle,\r
- appData.zippyReplayTimeout, ics_prefix, opponent);\r
- SendToICS(buf);\r
- return;\r
- }\r
-\r
- /* Engine not yet initialized or still thinking about last game? */\r
- if (!first.initDone || first.lastPing != first.lastPong) {\r
- sprintf(buf, "%stell %s I'm not quite ready for a new game yet; try again soon.\n%sdecline %s\n",\r
- ics_prefix, opponent, ics_prefix, opponent);\r
- SendToICS(buf);\r
- return;\r
- }\r
-\r
- sprintf(buf, "%saccept %s\n", ics_prefix, opponent);\r
- SendToICS(buf);\r
- if (appData.zippyTalk) {\r
- Speak("tell", opponent);\r
- }\r
-}\r
-\r
-\r
-/* Accept matches */\r
-int ZippyMatch(buf, i)\r
- char *buf;\r
- int *i;\r
-{\r
- if (looking_at(buf, i, "* * match * * requested with * (*)")) {\r
-\r
- ZippyHandleChallenge(star_match[0], star_match[1],\r
- star_match[2], star_match[3],\r
- StripHighlightAndTitle(star_match[4]));\r
- return TRUE;\r
- }\r
-\r
- /* Old FICS 0-increment form */\r
- if (looking_at(buf, i, "* * match * requested with * (*)")) {\r
-\r
- ZippyHandleChallenge(star_match[0], star_match[1],\r
- star_match[2], "0",\r
- StripHighlightAndTitle(star_match[3]));\r
- return TRUE;\r
- }\r
-\r
- if (looking_at(buf, i,\r
- "* has made an alternate proposal of * * match * *.")) {\r
-\r
- ZippyHandleChallenge(star_match[1], star_match[2],\r
- star_match[3], star_match[4],\r
- StripHighlightAndTitle(star_match[0]));\r
- return TRUE;\r
- }\r
-\r
- /* FICS wild/nonstandard forms */\r
- if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * Loaded from *")) {\r
- /* note: star_match[2] can include "[white] " or "[black] "\r
- before our own name. */\r
- if(star_match[8] == NULL || star_match[8][0] == 0) // [HGM] chessd: open-source ICS has file on next line\r
- ZippyHandleChallenge(star_match[4], star_match[5],\r
- star_match[6], star_match[7], StripHighlightAndTitle(star_match[0]));\r
- else ZippyHandleChallenge(star_match[4], star_match[8],\r
- star_match[6], star_match[7],\r
- StripHighlightAndTitle(star_match[0]));\r
- return TRUE;\r
- }\r
-\r
- if (looking_at(buf, i,\r
- "Challenge: * (*) *(*) * * * * : * * Loaded from *")) {\r
- /* note: star_match[2] can include "[white] " or "[black] "\r
- before our own name. */\r
- ZippyHandleChallenge(star_match[4], star_match[10],\r
- star_match[8], star_match[9],\r
- StripHighlightAndTitle(star_match[0]));\r
- return TRUE;\r
- }\r
-\r
- /* Regular forms */\r
- if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * : * *") |\r
- looking_at(buf, i, "Challenge: * (*) *(*) * * * * * *")) {\r
- /* note: star_match[2] can include "[white] " or "[black] "\r
- before our own name. */\r
- ZippyHandleChallenge(star_match[4], star_match[5],\r
- star_match[8], star_match[9],\r
- StripHighlightAndTitle(star_match[0]));\r
- return TRUE;\r
- }\r
-\r
- if (looking_at(buf, i, "Challenge: * (*) *(*) * * * *")) {\r
- /* note: star_match[2] can include "[white] " or "[black] "\r
- before our own name. */\r
- ZippyHandleChallenge(star_match[4], star_match[5],\r
- star_match[6], star_match[7],\r
- StripHighlightAndTitle(star_match[0]));\r
- return TRUE;\r
- }\r
-\r
-\r
- if (ics_type == ICS_ICC) { // [DM]\r
- if (looking_at(buf, i, "Your opponent offers you a draw")) {\r
- if (first.sendDrawOffers && first.initDone)\r
- SendToProgram("draw\n", &first);\r
- return TRUE;\r
- }\r
- } else {\r
- if (looking_at(buf, i, "offers you a draw")) {\r
- if (first.sendDrawOffers && first.initDone) {\r
- SendToProgram("draw\n", &first);\r
- }\r
- return TRUE;\r
- }\r
- }\r
-\r
- if (looking_at(buf, i, "requests that the game be aborted") ||\r
- looking_at(buf, i, "would like to abort")) {\r
- if (appData.zippyAbort ||\r
- (gameMode == IcsPlayingWhite && whiteTimeRemaining < 0) ||\r
- (gameMode == IcsPlayingBlack && blackTimeRemaining < 0)) {\r
- SendToICS(ics_prefix);\r
- SendToICS("abort\n");\r
- } else {\r
- SendToICS(ics_prefix);\r
- if (appData.zippyTalk)\r
- SendToICS("say Whoa no! I am having FUN!!\n");\r
- else\r
- SendToICS("say Sorry, this computer doesn't accept aborts.\n");\r
- }\r
- return TRUE;\r
- }\r
-\r
- if (looking_at(buf, i, "requests adjournment") ||\r
- looking_at(buf, i, "would like to adjourn")) {\r
- if (appData.zippyAdjourn) {\r
- SendToICS(ics_prefix);\r
- SendToICS("adjourn\n");\r
- } else {\r
- SendToICS(ics_prefix);\r
- if (appData.zippyTalk)\r
- SendToICS("say Whoa no! I am having FUN playing NOW!!\n");\r
- else\r
- SendToICS("say Sorry, this computer doesn't accept adjourns.\n");\r
- }\r
- return TRUE;\r
- }\r
-\r
- return FALSE;\r
-}\r
-\r
-/* Initialize chess program with data from the first board \r
- * of a new or resumed game.\r
- */\r
-void ZippyFirstBoard(moveNum, basetime, increment)\r
- int moveNum, basetime, increment;\r
-{\r
- char buf[MSG_SIZ];\r
- int w, b;\r
- char *opp = (gameMode==IcsPlayingWhite ? gameInfo.black : gameInfo.white);\r
- Boolean sentPos = FALSE;\r
- char *bookHit = NULL; // [HGM] book\r
-\r
- if (!first.initDone) {\r
- /* Game is starting prematurely. We can't deal with this */\r
- SendToICS(ics_prefix);\r
- SendToICS("abort\n");\r
- SendToICS(ics_prefix);\r
- SendToICS("say Sorry, the chess program is not initialized yet.\n");\r
- return;\r
- }\r
-\r
- /* Send the variant command if needed */\r
- if (gameInfo.variant != VariantNormal) {\r
- sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));\r
- SendToProgram(buf, &first);\r
- }\r
-\r
- if ((startedFromSetupPosition && moveNum == 0) ||\r
- (!appData.getMoveList && moveNum > 0)) {\r
- SendToProgram("force\n", &first);\r
- SendBoard(&first, moveNum);\r
- sentPos = TRUE;\r
- }\r
-\r
- sprintf(buf, "level 0 %d %d\n", basetime, increment);\r
- SendToProgram(buf, &first);\r
-\r
- /* Count consecutive games from one opponent */\r
- if (strcmp(opp, zippyLastOpp) == 0) {\r
- zippyConsecGames++;\r
- } else {\r
- zippyConsecGames = 1;\r
- strcpy(zippyLastOpp, opp);\r
- }\r
-\r
- /* Send the "computer" command if the opponent is in the list\r
- we've been gathering. */\r
- for (w=0; w<num_opps; w++) {\r
- if (!strcmp(opp_name[w], opp)) {\r
- SendToProgram(first.computerString, &first);\r
- break;\r
- }\r
- }\r
-\r
- /* Ratings might be < 0 which means "we haven't seen a ratings\r
- message from ICS." Send 0 in that case */\r
- w = (gameInfo.whiteRating >= 0) ? gameInfo.whiteRating : 0;\r
- b = (gameInfo.blackRating >= 0) ? gameInfo.blackRating : 0;\r
- \r
- firstMove = FALSE;\r
- if (gameMode == IcsPlayingWhite) {\r
- if (first.sendName) {\r
- sprintf(buf, "name %s\n", gameInfo.black);\r
- SendToProgram(buf, &first);\r
- }\r
- strcpy(ics_handle, gameInfo.white);\r
- sprintf(buf, "rating %d %d\n", w, b);\r
- SendToProgram(buf, &first);\r
- if (sentPos) {\r
- /* Position sent above, engine is in force mode */\r
- if (WhiteOnMove(moveNum)) {\r
- /* Engine is on move now */\r
- if (first.sendTime) {\r
- if (first.useColors) {\r
- SendToProgram("black\n", &first); /*gnu kludge*/\r
- SendTimeRemaining(&first, TRUE);\r
- SendToProgram("white\n", &first);\r
- } else {\r
- SendTimeRemaining(&first, TRUE);\r
- }\r
- }\r
- bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move\r
- } else {\r
- /* Engine's opponent is on move now */\r
- if (first.usePlayother) {\r
- if (first.sendTime) {\r
- SendTimeRemaining(&first, TRUE);\r
- }\r
- SendToProgram("playother\n", &first);\r
- } else {\r
- /* Need to send a "go" after opponent moves */\r
- firstMove = TRUE;\r
- }\r
- }\r
- } else {\r
- /* Position not sent above, move list might be sent later */\r
- if (moveNum == 0) {\r
- /* No move list coming; at start of game */\r
- if (first.sendTime) {\r
- if (first.useColors) {\r
- SendToProgram("black\n", &first); /*gnu kludge*/\r
- SendTimeRemaining(&first, TRUE);\r
- SendToProgram("white\n", &first);\r
- } else {\r
- SendTimeRemaining(&first, TRUE);\r
- }\r
- }\r
-// SendToProgram("go\n", &first);\r
- bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move\r
- }\r
- }\r
- } else if (gameMode == IcsPlayingBlack) {\r
- if (first.sendName) {\r
- sprintf(buf, "name %s\n", gameInfo.white);\r
- SendToProgram(buf, &first);\r
- }\r
- strcpy(ics_handle, gameInfo.black);\r
- sprintf(buf, "rating %d %d\n", b, w);\r
- SendToProgram(buf, &first);\r
- if (sentPos) {\r
- /* Position sent above, engine is in force mode */\r
- if (!WhiteOnMove(moveNum)) {\r
- /* Engine is on move now */\r
- if (first.sendTime) {\r
- if (first.useColors) {\r
- SendToProgram("white\n", &first); /*gnu kludge*/\r
- SendTimeRemaining(&first, FALSE);\r
- SendToProgram("black\n", &first);\r
- } else {\r
- SendTimeRemaining(&first, FALSE);\r
- }\r
- }\r
-// SendToProgram("go\n", &first);\r
- bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move\r
- } else {\r
- /* Engine's opponent is on move now */\r
- if (first.usePlayother) {\r
- if (first.sendTime) {\r
- SendTimeRemaining(&first, FALSE);\r
- }\r
- SendToProgram("playother\n", &first);\r
- } else {\r
- /* Need to send a "go" after opponent moves */\r
- firstMove = TRUE;\r
- }\r
- }\r
- } else {\r
- /* Position not sent above, move list might be sent later */\r
- /* Nothing needs to be done here */\r
- } \r
- }\r
-\r
- if(bookHit) { // [HGM] book: simulate book reply\r
- static char bookMove[MSG_SIZ]; // a bit generous?\r
-\r
- programStats.depth = programStats.nodes = programStats.time = \r
- programStats.score = programStats.got_only_move = 0;\r
- sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
-\r
- strcpy(bookMove, "move ");\r
- strcat(bookMove, bookHit);\r
- HandleMachineMove(bookMove, &first);\r
- }\r
-}\r
-\r
-\r
-void\r
-ZippyHoldings(white_holding, black_holding, new_piece)\r
- char *white_holding, *black_holding, *new_piece;\r
-{\r
- char buf[MSG_SIZ];\r
- if (gameMode != IcsPlayingBlack && gameMode != IcsPlayingWhite) return;\r
- sprintf(buf, "holding [%s] [%s] %s\n",\r
- white_holding, black_holding, new_piece);\r
- SendToProgram(buf, &first);\r
-}\r
+/*
+ * zippy.c -- Implements Zippy the Pinhead chess player on ICS in XBoard
+ *
+ * Copyright 1991 by Digital Equipment Corporation, Maynard,
+ * Massachusetts.
+ *
+ * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
+ *
+ * Enhancements Copyright 2005 Alessandro Scotti
+ *
+ * The following terms apply to Digital Equipment Corporation's copyright
+ * interest in XBoard:
+ * ------------------------------------------------------------------------
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Digital not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * ------------------------------------------------------------------------
+ *
+ * The following terms apply to the enhanced version of XBoard
+ * distributed by the Free Software Foundation:
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+extern char *getenv();
+# if HAVE_STRING_H
+# include <string.h>
+# else /* not HAVE_STRING_H */
+# include <strings.h>
+# endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+#define HI "hlelo "
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "common.h"
+#include "zippy.h"
+#include "frontend.h"
+#include "backend.h"
+#include "backendz.h"
+
+char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book
+void HandleMachineMove P((char *message, ChessProgramState *cps));
+
+static char zippyPartner[MSG_SIZ];
+static char zippyLastOpp[MSG_SIZ];
+static char zippyOffender[MSG_SIZ]; // [HGM] aborter
+static int zippyConsecGames;
+static time_t zippyLastGameEnd;
+
+extern void mysrandom(unsigned int seed);
+extern int myrandom(void);
+
+void
+ZippyInit ()
+{
+ char *p;
+
+ /* Get name of Zippy lines file */
+ p = getenv("ZIPPYLINES");
+ if (p != NULL) {
+ appData.zippyLines = p;
+ }
+
+ /* Get word that Zippy thinks is insulting */
+ p = getenv("ZIPPYPINHEAD");
+ if (p != NULL) {
+ appData.zippyPinhead = p;
+ }
+
+ /* What password is used for remote control? */
+ p = getenv("ZIPPYPASSWORD");
+ if (p != NULL) {
+ appData.zippyPassword = p;
+ }
+
+ /* What password is used for remote commands to gnuchess? */
+ p = getenv("ZIPPYPASSWORD2");
+ if (p != NULL) {
+ appData.zippyPassword2 = p;
+ }
+
+ /* Joke feature for people who try an old password */
+ p = getenv("ZIPPYWRONGPASSWORD");
+ if (p != NULL) {
+ appData.zippyWrongPassword = p;
+ }
+
+ /* While testing, I want to accept challenges from only one person
+ (namely, my "anonymous" account), so I set an environment
+ variable ZIPPYACCEPTONLY. */
+ p = getenv("ZIPPYACCEPTONLY");
+ if ( p != NULL ) {
+ appData.zippyAcceptOnly = p;
+ }
+
+ /* Should Zippy use "i" command? */
+ /* Defaults to 1=true */
+ p = getenv("ZIPPYUSEI");
+ if (p != NULL) {
+ appData.zippyUseI = atoi(p);
+ }
+
+ /* How does Zippy handle bughouse partnering? */
+ /* 0=say we can't play, 1=manual partnering, 2=auto partnering */
+ p = getenv("ZIPPYBUGHOUSE");
+ if (p != NULL) {
+ appData.zippyBughouse = atoi(p);
+ }
+
+ /* Does Zippy abort games with Crafty? */
+ /* Defaults to 0=false */
+ p = getenv("ZIPPYNOPLAYCRAFTY");
+ if (p != NULL) {
+ appData.zippyNoplayCrafty = atoi(p);
+ }
+
+ /* What ICS command does Zippy send at game end? Default: "gameend". */
+ p = getenv("ZIPPYGAMEEND");
+ if (p != NULL) {
+ appData.zippyGameEnd = p;
+ }
+
+ /* What ICS command does Zippy send at game start? Default: none. */
+ p = getenv("ZIPPYGAMESTART");
+ if (p != NULL) {
+ appData.zippyGameStart = p;
+ }
+
+ /* Should Zippy accept adjourns? */
+ /* Defaults to 0=false */
+ p = getenv("ZIPPYADJOURN");
+ if (p != NULL) {
+ appData.zippyAdjourn = atoi(p);
+ }
+
+ /* Should Zippy accept aborts? */
+ /* Defaults to 0=false */
+ p = getenv("ZIPPYABORT");
+ if (p != NULL) {
+ appData.zippyAbort = atoi(p);
+ }
+
+ /* Should Zippy play chess variants (besides bughouse)? */
+ p = getenv("ZIPPYVARIANTS");
+ if (p != NULL) {
+ appData.zippyVariants = p;
+ }
+ ASSIGN(first.variants, appData.zippyVariants);
+
+ srandom(time(NULL));
+}
+
+/*
+ * Routines to implement Zippy talking
+ */
+
+
+char *swifties[] = {
+ "i acclaims:", "i admonishes:", "i advertises:", "i advises:",
+ "i advocates:", "i affirms:", "i alleges:", "i anathematizes:",
+ "i animadverts:", "i announces:", "i apostrophizes:",
+ "i appeals:", "i applauds:", "i approves:", "i argues:",
+ "i articulates:", "i asserts:", "i asseverates:", "i attests:",
+ "i avers:", "i avows:", "i baas:", "i babbles:", "i banters:",
+ "i barks:", "i bawls:", "i bays:", "i begs:", "i belches:",
+ "i bellows:", "i belts out:", "i berates:", "i beshrews:",
+ "i blabbers:", "i blabs:", "i blares:", "i blasphemes:",
+ "i blasts:", "i blathers:", "i bleats:", "i blithers:",
+ "i blubbers:", "i blurts out:", "i blusters:", "i boasts:",
+ "i brags:", "i brays:", "i broadcasts:", "i burbles:",
+ "i buzzes:", "i cachinnates:", "i cackles:", "i caterwauls:",
+ "i calumniates:", "i caws:", "i censures:", "i chants:",
+ "i chatters:", "i cheeps:", "i cheers:", "i chides:", "i chins:",
+ "i chirps:", "i chortles:", "i chuckles:", "i claims:",
+ "i clamors:", "i clucks:", "i commands:", "i commends:",
+ "i comments:", "i commiserates:", "i communicates:",
+ "i complains:", "i concludes:", "i confabulates:", "i confesses:",
+ "i coos:", "i coughs:", "i counsels:", "i cries:", "i croaks:",
+ "i crows:", "i curses:", "i daydreams:", "i debates:",
+ "i declaims:", "i declares:", "i delivers:", "i denounces:",
+ "i deposes:", "i directs:", "i discloses:", "i disparages:",
+ "i discourses:", "i divulges:", "i documents:", "i drawls:",
+ "i dreams:", "i drivels:", "i drones:", "i effuses:",
+ /*"i ejaculates:",*/ "i elucidates:", "i emotes:", "i endorses:",
+ "i enthuses:", "i entreats:", "i enunciates:", "i eulogizes:",
+ "i exclaims:", "i execrates:", "i exhorts:", "i expatiates:",
+ "i explains:", "i explicates:", "i explodes:", "i exposes:",
+ "i exposits:", "i expostulates: ",
+ "i expounds:", "i expresses:", "i extols:",
+ "i exults:", "i fantasizes:", "i fibs:", "i filibusters:",
+ "i flatters:", "i flutes:", "i fools:", "i free-associates:",
+ "i fulminates:", "i gabbles:", "i gabs:", "i gasps:",
+ "i giggles:", "i gossips:", "i gripes:", "i groans:", "i growls:",
+ "i grunts:", "i guesses:", "i guffaws:", "i gushes:", "i hails:",
+ "i hallucinates:", "i harangues:", "i harmonizes:", "i hectors:",
+ "i hints:", "i hisses:", "i hollers:", "i honks:", "i hoots:",
+ "i hosannas:", "i howls:", "i hums:", "i hypothecates:",
+ "i hypothesizes:", "i imagines:", "i implies:", "i implores:",
+ "i imprecates:", "i indicates:", "i infers:",
+ "i informs everyone:", "i instructs:", "i interjects:",
+ "i interposes:", "i intimates:", "i intones:", "i introspects:",
+ "i inveighs:", "i jabbers:", "i japes:", "i jests:", "i jibes:",
+ "i jives:", "i jokes:", "i joshes:", "i keens:", "i laments:",
+ "i lauds:", "i laughs:", "i lectures:", "i lies:", "i lilts:",
+ "i lisps:", "i maintains:", "i maledicts:", "i maunders:",
+ "i meows:", "i mewls:", "i mimes:", "i minces:", "i moans:",
+ "i moos:", "i mourns:", "i mouths:", "i mumbles:", "i murmurs:",
+ "i muses:", "i mutters:", "i nags:", "i natters:", "i neighs:",
+ "i notes:", "i nuncupates:", "i objurgates:", "i observes:",
+ "i offers:", "i oinks:", "i opines:", "i orates:", "i orders:",
+ "i panegyrizes:", "i pantomimes:", "i pants:", "i peals:",
+ "i peeps:", "i perorates:", "i persuades:", "i petitions:",
+ "i phonates:", "i pipes up:", "i pitches:", "i pleads:",
+ "i points out:", "i pontificates:", "i postulates:", "i praises:",
+ "i prates:", "i prattles:", "i preaches:", "i prescribes:",
+ "i prevaricates:", "i proclaims:", "i projects:", "i pronounces:",
+ "i proposes:", "i proscribes:", "i quacks:", "i queries:",
+ "i questions:", "i quips:", "i quotes:", "i rages:", "i rambles:",
+ "i rants:", "i raps:", "i rasps:", "i rattles:", "i raves:",
+ "i reacts:", "i recites:", "i recommends:", "i records:",
+ "i reiterates:", "i rejoins:", "i releases:", "i remarks:",
+ "i reminisces:", "i remonstrates:", "i repeats:", "i replies:",
+ "i reports:", "i reprimands:", "i reproaches:", "i reproves:",
+ "i resounds:", "i responds:", "i retorts:", "i reveals:",
+ "i reviles:", "i roars:", "i rumbles:", "i sanctions:",
+ "i satirizes:", "i sauces:", "i scolds:", "i screams:",
+ "i screeches:", "i semaphores:", "i sends:", "i sermonizes:",
+ "i shrieks:", "i sibilates:", "i sighs:", "i signals:",
+ "i signifies:", "i signs:", "i sings:", "i slurs:", "i snaps:",
+ "i snarls:", "i sneezes:", "i snickers:", "i sniggers:",
+ "i snivels:", "i snores:", "i snorts:", "i sobs:",
+ "i soliloquizes:", "i sounds off:", "i sounds out:", "i speaks:",
+ "i spews:", "i spits out:", "i splutters:", "i spoofs:",
+ "i spouts:", "i sputters:", "i squalls:", "i squawks:",
+ "i squeaks:", "i squeals:", "i stammers:", "i states:",
+ "i stresses:", "i stutters:", "i submits:", "i suggests:",
+ "i summarizes:", "i sums up:", "i swears:", "i talks:",
+ "i tattles:", "i teases:", "i telegraphs:", "i testifies:",
+ "i threatens:", "i thunders:", "i titters:", "i tongue-lashes:",
+ "i toots:", "i transcribes:", "i transmits:", "i trills:",
+ "i trumpets:", "i twaddles:", "i tweets:", "i twitters:",
+ "i types:", "i upbraids:", "i urges:", "i utters:", "i ventures:",
+ "i vibrates:", "i vilifies:", "i vituperates:", "i vocalizes:",
+ "i vociferates:", "i voices:", "i waffles:", "i wails:",
+ "i warbles:", "i warns:", "i weeps:", "i wheezes:", "i whimpers:",
+ "i whines:", "i whinnies:", "i whistles:", "i wisecracks:",
+ "i witnesses:", "i woofs:", "i writes:", "i yammers:", "i yawps:",
+ "i yells:", "i yelps:", "i yodels:", "i yowls:", "i zings:",
+};
+
+#define MAX_SPEECH 250
+
+void
+Speak (char *how, char *whom)
+{
+ static FILE *zipfile = NULL;
+ static struct stat zipstat;
+ char zipbuf[MAX_SPEECH + 1];
+ static time_t lastShout = 0;
+ time_t now;
+ char *p;
+ int c, speechlen;
+
+ if (strcmp(how, "shout") == 0) {
+ now = time((time_t *) NULL);
+ if (now - lastShout < 1*60) return;
+ lastShout = now;
+ if (appData.zippyUseI) {
+ how = swifties[(unsigned) random() %
+ (sizeof(swifties)/sizeof(char *))];
+ }
+ }
+
+ if (zipfile == NULL) {
+ zipfile = fopen(appData.zippyLines, "r");
+ if (zipfile == NULL) {
+ DisplayFatalError("Can't open Zippy lines file", errno, 1);
+ return;
+ }
+ fstat(fileno(zipfile), &zipstat);
+ }
+
+ for (;;) {
+ fseek(zipfile, (unsigned) random() % zipstat.st_size, 0);
+ do {
+ c = getc(zipfile);
+ } while (c != NULLCHAR && c != '^' && c != EOF);
+ if (c == EOF) continue;
+ while ((c = getc(zipfile)) == '\n') ;
+ if (c == EOF) continue;
+ break;
+ }
+
+ /* Don't use ics_prefix; we need to let FICS expand the alias i -> it,
+ but use the real command "i" on ICC */
+ safeStrCpy(zipbuf, how, sizeof(zipbuf)/sizeof(zipbuf[0]));
+ strcat(zipbuf, " ");
+ if (whom != NULL) {
+ strcat(zipbuf, whom);
+ strcat(zipbuf, " ");
+ }
+ speechlen = strlen(zipbuf);
+ p = zipbuf + speechlen;
+
+ while (++speechlen < MAX_SPEECH) {
+ if (c == NULLCHAR || c == '^') {
+ *p++ = '\n';
+ *p = '\0';
+ SendToICS(zipbuf);
+ return;
+ } else if (c == '\n') {
+ *p++ = ' ';
+ do {
+ c = getc(zipfile);
+ } while (c == ' ');
+ } else if (c == EOF) {
+ break;
+ } else {
+ *p++ = c;
+ c = getc(zipfile);
+ }
+ }
+ /* Tried to say something too long, or junk at the end of the
+ file. Try something else. */
+ Speak(how, whom); /* tail recursion */
+}
+
+int
+ZippyCalled (char *str)
+{
+ return ics_handle[0] != NULLCHAR && StrCaseStr(str, ics_handle) != NULL;
+}
+
+static char opp_name[128][32];
+static int num_opps=0;
+
+extern ColorClass curColor;
+
+static void
+SetCurColor (ColorClass color)
+{
+ curColor = color;
+}
+
+static void
+ColorizeEx (ColorClass color, int cont)
+{
+ if( appData.colorize ) {
+ Colorize( color, cont );
+ SetCurColor( color );
+ }
+}
+
+int
+ZippyControl (char *buf, int *i)
+{
+ char *player, *p;
+ char reply[MSG_SIZ];
+
+ /* Possibly reject Crafty as opponent */
+ if (appData.zippyPlay && appData.zippyNoplayCrafty && forwardMostMove < 4
+ && looking_at(buf, i, "* kibitzes: Hello from Crafty"))
+ {
+ player = StripHighlightAndTitle(star_match[0]);
+ if ((gameMode == IcsPlayingWhite &&
+ StrCaseCmp(player, gameInfo.black) == 0) ||
+ (gameMode == IcsPlayingBlack &&
+ StrCaseCmp(player, gameInfo.white) == 0)) {
+
+ snprintf(reply, MSG_SIZ, "%ssay This computer does not play Crafty clones\n%sabort\n%s+noplay %s\n",
+ ics_prefix, ics_prefix, ics_prefix, player);
+ SendToICS(reply);
+ }
+ return TRUE;
+ }
+
+ /* If this is a computer, save the name. Then later, once the */
+ /* game is really started, we will send the "computer" notice to */
+ /* the engine. */
+ if (appData.zippyPlay &&
+ looking_at(buf, i, "* is in the computer list")) {
+ int i;
+ for (i=0;i<num_opps;i++)
+ if (!strcmp(opp_name[i],star_match[0])) break;
+ if (i >= num_opps) safeStrCpy(opp_name[num_opps++],star_match[0], sizeof(opp_name[num_opps])/sizeof(opp_name[num_opps][0]));
+ }
+ if (appData.zippyPlay && looking_at(buf, i, "* * is a computer *")) {
+ int i;
+ for (i=0;i<num_opps;i++)
+ if (!strcmp(opp_name[i],star_match[1])) break;
+ if (i >= num_opps) safeStrCpy(opp_name[num_opps++],star_match[1], sizeof(opp_name[num_opps])/sizeof(opp_name[num_opps][0]));
+ }
+
+ /* Tells and says */
+ if (appData.zippyPlay &&
+ (looking_at(buf, i, "* offers to be your bughouse partner") ||
+ looking_at(buf, i, "* tells you: [automatic message] I chose you"))) {
+ player = StripHighlightAndTitle(star_match[0]);
+ if (appData.zippyBughouse > 1 && first.initDone) {
+ snprintf(reply, MSG_SIZ,"%spartner %s\n", ics_prefix, player);
+ SendToICS(reply);
+ if (strcmp(zippyPartner, player) != 0) {
+ safeStrCpy(zippyPartner, player, sizeof(zippyPartner)/sizeof(zippyPartner[0]));
+ SendToProgram(reply + strlen(ics_prefix), &first);
+ }
+ } else if (appData.zippyBughouse > 0) {
+ snprintf(reply, MSG_SIZ, "%sdecline %s\n", ics_prefix, player);
+ SendToICS(reply);
+ } else {
+ snprintf(reply, MSG_SIZ, "%stell %s This computer cannot play bughouse\n",
+ ics_prefix, player);
+ SendToICS(reply);
+ }
+ return TRUE;
+ }
+
+ if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
+ looking_at(buf, i, "* agrees to be your partner")) {
+ player = StripHighlightAndTitle(star_match[0]);
+ snprintf(reply, MSG_SIZ, "partner %s\n", player);
+ if (strcmp(zippyPartner, player) != 0) {
+ safeStrCpy(zippyPartner, player, sizeof(zippyPartner)/sizeof(zippyPartner[0]));
+ SendToProgram(reply, &first);
+ }
+ return TRUE;
+ }
+
+ if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
+ (looking_at(buf, i, "are no longer *'s partner") ||
+ looking_at(buf, i,
+ "* tells you: [automatic message] I'm no longer your"))) {
+ player = StripHighlightAndTitle(star_match[0]);
+ if (strcmp(zippyPartner, player) == 0) {
+ zippyPartner[0] = NULLCHAR;
+ SendToProgram("partner\n", &first);
+ }
+ return TRUE;
+ }
+
+ if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
+ (looking_at(buf, i, "no longer have a bughouse partner") ||
+ looking_at(buf, i, "partner has disconnected") ||
+ looking_at(buf, i, "partner has just chosen a new partner"))) {
+ zippyPartner[0] = NULLCHAR;
+ SendToProgram("partner\n", &first);
+ return TRUE;
+ }
+
+ if (appData.zippyPlay && appData.zippyBughouse && first.initDone &&
+ looking_at(buf, i, "* (your partner) tells you: *")) {
+ /* This pattern works on FICS but not ICC */
+ player = StripHighlightAndTitle(star_match[0]);
+ if (strcmp(zippyPartner, player) != 0) {
+ safeStrCpy(zippyPartner, player, sizeof(zippyPartner)/sizeof(zippyPartner[0]));
+ snprintf(reply, MSG_SIZ, "partner %s\n", player);
+ SendToProgram(reply, &first);
+ }
+ snprintf(reply, MSG_SIZ, "ptell %s\n", star_match[1]);
+ SendToProgram(reply, &first);
+ return TRUE;
+ }
+
+ if (looking_at(buf, i, "* tells you: *") ||
+ looking_at(buf, i, "* says: *"))
+ {
+ player = StripHighlightAndTitle(star_match[0]);
+ if (appData.zippyPassword[0] != NULLCHAR &&
+ strncmp(star_match[1], appData.zippyPassword,
+ strlen(appData.zippyPassword)) == 0) {
+ p = star_match[1] + strlen(appData.zippyPassword);
+ while (*p == ' ') p++;
+ SendToICS(p);
+ SendToICS("\n");
+ } else if (appData.zippyPassword2[0] != NULLCHAR && first.initDone &&
+ strncmp(star_match[1], appData.zippyPassword2,
+ strlen(appData.zippyPassword2)) == 0) {
+ p = star_match[1] + strlen(appData.zippyPassword2);
+ while (*p == ' ') p++;
+ SendToProgram(p, &first);
+ SendToProgram("\n", &first);
+ } else if (appData.zippyWrongPassword[0] != NULLCHAR &&
+ strncmp(star_match[1], appData.zippyWrongPassword,
+ strlen(appData.zippyWrongPassword)) == 0) {
+ p = star_match[1] + strlen(appData.zippyWrongPassword);
+ while (*p == ' ') p++;
+ snprintf(reply, MSG_SIZ, "wrong %s\n", player);
+ SendToICS(reply);
+ } else if (appData.zippyBughouse && first.initDone &&
+ strcmp(player, zippyPartner) == 0) {
+ SendToProgram("ptell ", &first);
+ SendToProgram(star_match[1], &first);
+ SendToProgram("\n", &first);
+ } else if (strncmp(star_match[1], HI, 6) == 0) {
+ extern char* programVersion;
+ snprintf(reply, MSG_SIZ, "%stell %s %s\n",
+ ics_prefix, player, programVersion);
+ SendToICS(reply);
+ } else if (strncmp(star_match[1], "W0W!! ", 6) == 0) {
+ extern char* programVersion;
+ snprintf(reply, MSG_SIZ, "%stell %s %s\n", ics_prefix,
+ player, programVersion);
+ SendToICS(reply);
+ } else if (appData.zippyTalk && (((unsigned) random() % 10) < 9)) {
+ if (strcmp(player, ics_handle) != 0) {
+ Speak("tell", player);
+ }
+ }
+
+ ColorizeEx( ColorTell, FALSE );
+
+ return TRUE;
+ }
+
+ if( appData.colorize && looking_at(buf, i, "* (*) seeking") ) {
+ ColorizeEx(ColorSeek, FALSE);
+ return FALSE;
+ }
+
+ if (looking_at(buf, i, "* spoofs you:")) {
+ player = StripHighlightAndTitle(star_match[0]);
+ snprintf(reply, MSG_SIZ, "spoofedby %s\n", player);
+ SendToICS(reply);
+ }
+
+ return FALSE;
+}
+
+int
+ZippyConverse(char *buf, int *i)
+{
+ static char lastgreet[MSG_SIZ];
+ char reply[MSG_SIZ];
+ int oldi;
+
+ /* Shouts and emotes */
+ if (looking_at(buf, i, "--> * *") ||
+ looking_at(buf, i, "* shouts: *"))
+ {
+ if (appData.zippyTalk) {
+ char *player = StripHighlightAndTitle(star_match[0]);
+ if (strcmp(player, ics_handle) == 0) {
+ return TRUE;
+ } else if (appData.zippyPinhead[0] != NULLCHAR &&
+ StrCaseStr(star_match[1], appData.zippyPinhead) != NULL) {
+ snprintf(reply, MSG_SIZ, "insult %s\n", player);
+ SendToICS(reply);
+ } else if (ZippyCalled(star_match[1])) {
+ Speak("shout", NULL);
+ }
+ }
+
+ ColorizeEx(ColorShout, FALSE);
+
+ return TRUE;
+ }
+
+ if (looking_at(buf, i, "* kibitzes: *")) {
+ if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
+ char *player = StripHighlightAndTitle(star_match[0]);
+ if (strcmp(player, ics_handle) != 0) {
+ Speak("kibitz", NULL);
+ }
+ }
+
+ ColorizeEx(ColorKibitz, FALSE);
+
+ return TRUE;
+ }
+
+ if (looking_at(buf, i, "* whispers: *")) {
+ if (appData.zippyTalk && ((unsigned) random() % 10) < 9) {
+ char *player = StripHighlightAndTitle(star_match[0]);
+ if (strcmp(player, ics_handle) != 0) {
+ Speak("whisper", NULL);
+ }
+ }
+
+ ColorizeEx(ColorKibitz, FALSE);
+
+ return TRUE;
+ }
+
+ /* Messages */
+ if ((looking_at(buf, i, ". * (*:*): *") && isdigit(star_match[1][0])) ||
+ looking_at(buf, i, ". * at *:*: *")) {
+ if (appData.zippyTalk) {
+ FILE *f;
+ char *player = StripHighlightAndTitle(star_match[0]);
+
+ if (strcmp(player, ics_handle) != 0) {
+ if (((unsigned) random() % 10) < 9)
+ Speak("message", player);
+ f = fopen("zippy.messagelog", "a");
+ fprintf(f, "%s (%s:%s): %s\n", player,
+ star_match[1], star_match[2], star_match[3]);
+ fclose(f);
+ }
+ }
+ return TRUE;
+ }
+
+ /* Channel tells */
+ oldi = *i;
+ if (looking_at(buf, i, "*(*: *")) {
+ char *channel;
+ if (star_match[0][0] == NULLCHAR ||
+ strchr(star_match[0], ' ') ||
+ strchr(star_match[1], ' ')) {
+ /* Oops, did not want to match this; probably a message */
+ *i = oldi;
+ return FALSE;
+ }
+ if (appData.zippyTalk) {
+ channel = strrchr(star_match[1], '(');
+ if (channel == NULL) {
+ channel = star_match[1];
+ } else {
+ channel++;
+ }
+ channel[strlen(channel)-1] = NULLCHAR;
+
+ /* Tell to the channel only if someone mentions our name */
+ if (ZippyCalled(star_match[2])) {
+ Speak("tell", channel);
+ }
+
+ ColorizeEx( atoi(channel) == 1 ? ColorChannel1 : ColorChannel, FALSE );
+ }
+ return TRUE;
+ }
+
+ if (!appData.zippyTalk) return FALSE;
+
+ if ((looking_at(buf, i, "You have * message") &&
+ atoi(star_match[0]) != 0) ||
+ looking_at(buf, i, "* has left a message for you") ||
+ looking_at(buf, i, "* just sent you a message")) {
+ snprintf(reply, MSG_SIZ, "%smessages\n%sclearmessages *\n",
+ ics_prefix, ics_prefix);
+ SendToICS(reply);
+ return TRUE;
+ }
+
+ if (looking_at(buf, i, "Notification: * has arrived")) {
+ if (((unsigned) random() % 3) == 0) {
+ char *player = StripHighlightAndTitle(star_match[0]);
+ safeStrCpy(lastgreet, player, sizeof(lastgreet)/sizeof(lastgreet[0]));
+ snprintf(reply, MSG_SIZ, "greet %s\n", player);
+ SendToICS(reply);
+ Speak("tell", player);
+ }
+ }
+
+ if (looking_at(buf, i, "Notification: * has departed")) {
+ if (((unsigned) random() % 3) == 0) {
+ char *player = StripHighlightAndTitle(star_match[0]);
+ snprintf(reply, MSG_SIZ, "farewell %s\n", player);
+ SendToICS(reply);
+ }
+ }
+
+ if (looking_at(buf, i, "Not sent -- * is censoring you")) {
+ char *player = StripHighlightAndTitle(star_match[0]);
+ if (strcmp(player, lastgreet) == 0) {
+ snprintf(reply, MSG_SIZ, "%s-notify %s\n", ics_prefix, player);
+ SendToICS(reply);
+ }
+ }
+
+ if (looking_at(buf, i, "command is currently turned off")) {
+ appData.zippyUseI = 0;
+ }
+
+ return FALSE;
+}
+
+void
+ZippyGameStart (char *white, char* black)
+{
+ if (!first.initDone) {
+ /* Game is starting prematurely. We can't deal with this */
+ SendToICS(ics_prefix);
+ SendToICS("abort\n");
+ SendToICS(ics_prefix);
+ SendToICS("say Sorry, the chess program is not initialized yet.\n");
+ return;
+ }
+
+ if (appData.zippyGameStart[0] != NULLCHAR) {
+ SendToICS(appData.zippyGameStart);
+ SendToICS("\n");
+ }
+}
+
+void
+ZippyGameEnd (ChessMove result, char *resultDetails)
+{
+ if (appData.zippyAcceptOnly[0] == NULLCHAR &&
+ appData.zippyGameEnd[0] != NULLCHAR) {
+ SendToICS(appData.zippyGameEnd);
+ SendToICS("\n");
+ }
+ zippyLastGameEnd = time(0);
+ if(forwardMostMove < appData.zippyShortGame)
+ safeStrCpy(zippyOffender, zippyLastOpp, sizeof(zippyOffender)/sizeof(zippyOffender[0]));
+ else
+ zippyOffender[0] = 0; // [HGM] aborter
+}
+
+/*
+ * Routines to implement Zippy playing chess
+ */
+
+void
+ZippyHandleChallenge (char *srated, char *swild, char *sbase, char *sincrement, char *opponent)
+{
+ char buf[MSG_SIZ];
+ int i=0;
+ VariantClass variant;
+ char *varname;
+
+ variant = StringToVariant(swild);
+ varname = VariantName(variant);
+
+ /* [DM] If icsAnalyzeEngine active we don't accept automatic games */
+ if (appData.icsActive && appData.icsEngineAnalyze) return;
+
+ /* If desired, you can insert more code here to decline matches
+ based on rated, variant, base, and increment, but it is
+ easier to use the ICS formula feature instead. */
+
+ if (variant == VariantLoadable) {
+ snprintf(buf, MSG_SIZ,
+ "%stell %s This computer can't play wild type %s\n%sdecline %s\n",
+ ics_prefix, opponent, swild, ics_prefix, opponent);
+ SendToICS(buf);
+ return;
+ }
+ if (StrStr(appData.zippyVariants, varname) == NULL ||
+ ((i=first.protocolVersion) != 1 && StrStr(first.variants, varname) == NULL) /* [HGM] zippyvar */
+ ) {
+ snprintf(buf, MSG_SIZ,
+ "%stell %s This computer can't play %s [%s], only %s\n%sdecline %s\n",
+ ics_prefix, opponent, swild, varname,
+ i ? first.variants : appData.zippyVariants, /* [HGM] zippyvar */
+ ics_prefix, opponent);
+ SendToICS(buf);
+ return;
+ }
+
+ /* Are we blocking match requests from all but one person? */
+ if (appData.zippyAcceptOnly[0] != NULLCHAR &&
+ StrCaseCmp(opponent, appData.zippyAcceptOnly)) {
+ /* Yes, and this isn't him. Ignore challenge. */
+ return;
+ }
+
+ /* Too many consecutive games with same opponent? If so, make him
+ wait until someone else has played or a timeout has elapsed. */
+ if (appData.zippyMaxGames &&
+ strcmp(opponent, zippyLastOpp) == 0 &&
+ zippyConsecGames >= appData.zippyMaxGames &&
+ difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
+ snprintf(buf, MSG_SIZ, "%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",
+ ics_prefix, opponent, zippyConsecGames, ics_handle,
+ appData.zippyReplayTimeout, ics_prefix, opponent);
+ SendToICS(buf);
+ return;
+ }
+
+ /* [HGM] aborter: opponent is cheater that aborts games he doesn't like on first move. Make him wait */
+ if (strcmp(opponent, zippyOffender) == 0 &&
+ difftime(time(0), zippyLastGameEnd) < appData.zippyReplayTimeout) {
+ snprintf(buf, MSG_SIZ, "%stell %s Sorry, your previous game against %s was rather short. "
+ " It will wait %d seconds to see if a tougher opponent comes along.\n%sdecline %s\n",
+ ics_prefix, opponent, ics_handle,
+ appData.zippyReplayTimeout, ics_prefix, opponent);
+ SendToICS(buf);
+ return;
+ }
+
+ /* Engine not yet initialized or still thinking about last game? */
+ if (!first.initDone || first.lastPing != first.lastPong) {
+ snprintf(buf, MSG_SIZ, "%stell %s I'm not quite ready for a new game yet; try again soon.\n%sdecline %s\n",
+ ics_prefix, opponent, ics_prefix, opponent);
+ SendToICS(buf);
+ return;
+ }
+
+ snprintf(buf, MSG_SIZ, "%saccept %s\n", ics_prefix, opponent);
+ SendToICS(buf);
+ if (appData.zippyTalk) {
+ Speak("tell", opponent);
+ }
+}
+
+
+/* Accept matches */
+int
+ZippyMatch (char *buf, int *i)
+{
+ if (looking_at(buf, i, "* * match * * requested with * (*)")) {
+
+ ZippyHandleChallenge(star_match[0], star_match[1],
+ star_match[2], star_match[3],
+ StripHighlightAndTitle(star_match[4]));
+ return TRUE;
+ }
+
+ /* Old FICS 0-increment form */
+ if (looking_at(buf, i, "* * match * requested with * (*)")) {
+
+ ZippyHandleChallenge(star_match[0], star_match[1],
+ star_match[2], "0",
+ StripHighlightAndTitle(star_match[3]));
+ return TRUE;
+ }
+
+ if (looking_at(buf, i,
+ "* has made an alternate proposal of * * match * *.")) {
+
+ ZippyHandleChallenge(star_match[1], star_match[2],
+ star_match[3], star_match[4],
+ StripHighlightAndTitle(star_match[0]));
+ return TRUE;
+ }
+
+ /* FICS wild/nonstandard forms */
+ if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * Loaded from *")) {
+ /* note: star_match[2] can include "[white] " or "[black] "
+ before our own name. */
+ if(star_match[8] == NULL || star_match[8][0] == 0) // [HGM] chessd: open-source ICS has file on next line
+ ZippyHandleChallenge(star_match[4], star_match[5],
+ star_match[6], star_match[7], StripHighlightAndTitle(star_match[0]));
+ else ZippyHandleChallenge(star_match[4], star_match[8],
+ star_match[6], star_match[7],
+ StripHighlightAndTitle(star_match[0]));
+ return TRUE;
+ }
+
+ if (looking_at(buf, i,
+ "Challenge: * (*) *(*) * * * * : * * Loaded from *")) {
+ /* note: star_match[2] can include "[white] " or "[black] "
+ before our own name. */
+ ZippyHandleChallenge(star_match[4], star_match[10],
+ star_match[8], star_match[9],
+ StripHighlightAndTitle(star_match[0]));
+ return TRUE;
+ }
+
+ /* Regular forms */
+ if (looking_at(buf, i, "Challenge: * (*) *(*) * * * * : * *") |
+ looking_at(buf, i, "Challenge: * (*) *(*) * * * * * *")) {
+ /* note: star_match[2] can include "[white] " or "[black] "
+ before our own name. */
+ ZippyHandleChallenge(star_match[4], star_match[5],
+ star_match[8], star_match[9],
+ StripHighlightAndTitle(star_match[0]));
+ return TRUE;
+ }
+
+ if (looking_at(buf, i, "Challenge: * (*) *(*) * * * *")) {
+ /* note: star_match[2] can include "[white] " or "[black] "
+ before our own name. */
+ ZippyHandleChallenge(star_match[4], star_match[5],
+ star_match[6], star_match[7],
+ StripHighlightAndTitle(star_match[0]));
+ return TRUE;
+ }
+
+
+ if (looking_at(buf, i, "Your opponent offers you a draw") ||
+ looking_at(buf, i, "* offers you a draw")) {
+ if (first.sendDrawOffers && first.initDone) {
+ SendToProgram("draw\n", &first);
+ }
+ return TRUE;
+ }
+
+ if (looking_at(buf, i, "requests that the game be aborted") ||
+ looking_at(buf, i, "would like to abort")) {
+ if (appData.zippyAbort ||
+ (gameMode == IcsPlayingWhite && whiteTimeRemaining < 0) ||
+ (gameMode == IcsPlayingBlack && blackTimeRemaining < 0)) {
+ SendToICS(ics_prefix);
+ SendToICS("abort\n");
+ } else {
+ SendToICS(ics_prefix);
+ if (appData.zippyTalk)
+ SendToICS("say Whoa no! I am having FUN!!\n");
+ else
+ SendToICS("say Sorry, this computer doesn't accept aborts.\n");
+ }
+ return TRUE;
+ }
+
+ if (looking_at(buf, i, "requests adjournment") ||
+ looking_at(buf, i, "would like to adjourn")) {
+ if (appData.zippyAdjourn) {
+ SendToICS(ics_prefix);
+ SendToICS("adjourn\n");
+ } else {
+ SendToICS(ics_prefix);
+ if (appData.zippyTalk)
+ SendToICS("say Whoa no! I am having FUN playing NOW!!\n");
+ else
+ SendToICS("say Sorry, this computer doesn't accept adjourns.\n");
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Initialize chess program with data from the first board
+ * of a new or resumed game.
+ */
+void
+ZippyFirstBoard (int moveNum, int basetime, int increment)
+{
+ char buf[MSG_SIZ];
+ int w, b;
+ char *opp = (gameMode==IcsPlayingWhite ? gameInfo.black : gameInfo.white);
+ Boolean sentPos = FALSE;
+ char *bookHit = NULL; // [HGM] book
+
+ if (!first.initDone) {
+ /* Game is starting prematurely. We can't deal with this */
+ SendToICS(ics_prefix);
+ SendToICS("abort\n");
+ SendToICS(ics_prefix);
+ SendToICS("say Sorry, the chess program is not initialized yet.\n");
+ return;
+ }
+
+ /* Send the variant command if needed */
+ if (gameInfo.variant != VariantNormal) {
+ snprintf(buf, MSG_SIZ, "variant %s\n", VariantName(gameInfo.variant));
+ SendToProgram(buf, &first);
+ }
+
+ if ((startedFromSetupPosition && moveNum == 0) ||
+ (!appData.getMoveList && moveNum > 0)) {
+ SendToProgram("force\n", &first);
+ SendBoard(&first, moveNum);
+ sentPos = TRUE;
+ }
+
+ snprintf(buf, MSG_SIZ, "level 0 %d %d\n", basetime, increment);
+ SendToProgram(buf, &first);
+
+ /* Count consecutive games from one opponent */
+ if (strcmp(opp, zippyLastOpp) == 0) {
+ zippyConsecGames++;
+ } else {
+ zippyConsecGames = 1;
+ safeStrCpy(zippyLastOpp, opp, sizeof(zippyLastOpp)/sizeof(zippyLastOpp[0]));
+ }
+
+ /* Send the "computer" command if the opponent is in the list
+ we've been gathering. */
+ for (w=0; w<num_opps; w++) {
+ if (!strcmp(opp_name[w], opp)) {
+ SendToProgram(first.computerString, &first);
+ break;
+ }
+ }
+
+ /* Ratings might be < 0 which means "we haven't seen a ratings
+ message from ICS." Send 0 in that case */
+ w = (gameInfo.whiteRating >= 0) ? gameInfo.whiteRating : 0;
+ b = (gameInfo.blackRating >= 0) ? gameInfo.blackRating : 0;
+
+ firstMove = FALSE;
+ if (gameMode == IcsPlayingWhite) {
+ if (first.sendName) {
+ snprintf(buf, MSG_SIZ, "name %s\n", gameInfo.black);
+ SendToProgram(buf, &first);
+ }
+ safeStrCpy(ics_handle, gameInfo.white, MSG_SIZ);
+ snprintf(buf, MSG_SIZ, "rating %d %d\n", w, b);
+ SendToProgram(buf, &first);
+ if (sentPos) {
+ /* Position sent above, engine is in force mode */
+ if (WhiteOnMove(moveNum)) {
+ /* Engine is on move now */
+ if (first.sendTime) {
+ if (first.useColors) {
+ SendToProgram("black\n", &first); /*gnu kludge*/
+ SendTimeRemaining(&first, TRUE);
+ SendToProgram("white\n", &first);
+ } else {
+ SendTimeRemaining(&first, TRUE);
+ }
+ }
+ bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
+ } else {
+ /* Engine's opponent is on move now */
+ if (first.usePlayother) {
+ if (first.sendTime) {
+ SendTimeRemaining(&first, TRUE);
+ }
+ SendToProgram("playother\n", &first);
+ } else {
+ /* Need to send a "go" after opponent moves */
+ firstMove = TRUE;
+ }
+ }
+ } else {
+ /* Position not sent above, move list might be sent later */
+ if (moveNum == 0) {
+ /* No move list coming; at start of game */
+ if (first.sendTime) {
+ if (first.useColors) {
+ SendToProgram("black\n", &first); /*gnu kludge*/
+ SendTimeRemaining(&first, TRUE);
+ SendToProgram("white\n", &first);
+ } else {
+ SendTimeRemaining(&first, TRUE);
+ }
+ }
+// SendToProgram("go\n", &first);
+ bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
+ }
+ }
+ } else if (gameMode == IcsPlayingBlack) {
+ if (first.sendName) {
+ snprintf(buf, MSG_SIZ, "name %s\n", gameInfo.white);
+ SendToProgram(buf, &first);
+ }
+ safeStrCpy(ics_handle, gameInfo.black, MSG_SIZ);
+ snprintf(buf, MSG_SIZ, "rating %d %d\n", b, w);
+ SendToProgram(buf, &first);
+ if (sentPos) {
+ /* Position sent above, engine is in force mode */
+ if (!WhiteOnMove(moveNum)) {
+ /* Engine is on move now */
+ if (first.sendTime) {
+ if (first.useColors) {
+ SendToProgram("white\n", &first); /*gnu kludge*/
+ SendTimeRemaining(&first, FALSE);
+ SendToProgram("black\n", &first);
+ } else {
+ SendTimeRemaining(&first, FALSE);
+ }
+ }
+// SendToProgram("go\n", &first);
+ bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
+ } else {
+ /* Engine's opponent is on move now */
+ if (first.usePlayother) {
+ if (first.sendTime) {
+ SendTimeRemaining(&first, FALSE);
+ }
+ SendToProgram("playother\n", &first);
+ } else {
+ /* Need to send a "go" after opponent moves */
+ firstMove = TRUE;
+ }
+ }
+ } else {
+ /* Position not sent above, move list might be sent later */
+ /* Nothing needs to be done here */
+ }
+ }
+
+ if(bookHit) { // [HGM] book: simulate book reply
+ static char bookMove[MSG_SIZ]; // a bit generous?
+
+ programStats.depth = programStats.nodes = programStats.time =
+ programStats.score = programStats.got_only_move = 0;
+ sprintf(programStats.movelist, "%s (xbook)", bookHit);
+
+ safeStrCpy(bookMove, "move ", sizeof(bookMove)/sizeof(bookMove[0]));
+ strcat(bookMove, bookHit);
+ HandleMachineMove(bookMove, &first);
+ }
+}
+
+
+void
+ZippyHoldings (char *white_holding, char *black_holding, char *new_piece)
+{
+ char buf[MSG_SIZ];
+ if (gameMode != IcsPlayingBlack && gameMode != IcsPlayingWhite) return;
+ snprintf(buf, MSG_SIZ, "holding [%s] [%s] %s\n",
+ white_holding, black_holding, new_piece);
+ SendToProgram(buf, &first);
+}