Merge commit 'v4.3.16'
[xboard.git] / zippy.c
diff --git a/zippy.c b/zippy.c
index aed634e..9170000 100644 (file)
--- a/zippy.c
+++ b/zippy.c
-/*
- * zippy.c -- Implements Zippy the Pinhead chess player on ICS in XBoard
- * $Id: zippy.c,v 2.2 2003/11/25 05:25:20 mann Exp $
- *
- * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
- * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
- *
- * 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:
- * ------------------------------------------------------------------------
- * This program 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 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * ------------------------------------------------------------------------
- */
-
-#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"
-
-static char zippyPartner[MSG_SIZ];
-static char zippyLastOpp[MSG_SIZ];
-static int zippyConsecGames;
-static time_t zippyLastGameEnd;
-
-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;
-    }
-    strcpy(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 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(how, whom) 
-     char *how, *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;
-    Boolean done;
-               
-    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;
-    }
-    done = FALSE;
-
-    /* Don't use ics_prefix; we need to let FICS expand the alias i -> it,
-       but use the real command "i" on ICC */
-    strcpy(zipbuf, how);
-    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(str)
-     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(buf, i)
-     char *buf;
-     int *i;
-{
-    char *player, *p;
-    char reply[MSG_SIZ];
-
-#if TRIVIA
-#include "trivia.c"
-#endif
-
-    /* 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)) {
-
-         sprintf(reply, "%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) strcpy(opp_name[num_opps++],star_match[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) strcpy(opp_name[num_opps++],star_match[1]);
-    }
-
-    /* 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) {
-           sprintf(reply, "%spartner %s\n", ics_prefix, player);
-           SendToICS(reply);
-           if (strcmp(zippyPartner, player) != 0) {
-               strcpy(zippyPartner, player);
-               SendToProgram(reply + strlen(ics_prefix), &first);
-           }
-       } else if (appData.zippyBughouse > 0) {
-           sprintf(reply, "%sdecline %s\n", ics_prefix, player);
-           SendToICS(reply);
-       } else {
-           sprintf(reply, "%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]);
-       sprintf(reply, "partner %s\n", player);
-       if (strcmp(zippyPartner, player) != 0) {
-           strcpy(zippyPartner, player);
-           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) {
-           strcpy(zippyPartner, player);
-           sprintf(reply, "partner %s\n", player);
-           SendToProgram(reply, &first);
-       }
-       sprintf(reply, "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++;
-           sprintf(reply, "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;
-           sprintf(reply, "%stell %s %s\n",
-                   ics_prefix, player, programVersion);
-           SendToICS(reply);
-       } else if (strncmp(star_match[1], "W0W!! ", 6) == 0) {
-           extern char* programVersion;
-           sprintf(reply, "%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]);
-        sprintf(reply, "spoofedby %s\n", player);
-        SendToICS(reply);
-    }
-
-    return FALSE;
-}
-
-int ZippyConverse(buf, i)
-     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) {
-           sprintf(reply, "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 *player;
-       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) {
-         player = StripHighlightAndTitle(star_match[0]);
-         channel = strrchr(star_match[1], '(');
-         if (channel == NULL) {
-           channel = star_match[1];
-         } else {
-           channel++;
-         }
-         channel[strlen(channel)-1] = NULLCHAR;
-#if 0
-         /* Always tell to the channel (probability 90%) */
-         if (strcmp(player, ics_handle) != 0 &&
-             ((unsigned) random() % 10) < 9) {
-           Speak("tell", channel);
-         }
-#else
-         /* Tell to the channel only if someone mentions our name */
-         if (ZippyCalled(star_match[2])) {
-           Speak("tell", channel);
-         }
-#endif
-
-          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")) {
-        sprintf(reply, "%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]);
-           strcpy(lastgreet, player);
-           sprintf(reply, "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]);
-           sprintf(reply, "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) {
-           sprintf(reply, "%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(white, black)
-     char *white, *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(result, resultDetails)
-     ChessMove result;
-     char *resultDetails;
-{
-    if (appData.zippyAcceptOnly[0] == NULLCHAR &&
-       appData.zippyGameEnd[0] != NULLCHAR) {
-      SendToICS(appData.zippyGameEnd);
-      SendToICS("\n");
-    }
-    zippyLastGameEnd = time(0);
-}
-
-/*
- * Routines to implement Zippy playing chess
- */
-
-void ZippyHandleChallenge(srated, swild, sbase, sincrement, opponent)
-     char *srated, *swild, *sbase, *sincrement, *opponent;
-{
-    char buf[MSG_SIZ];
-    int base, increment;
-    char rated;
-    VariantClass variant;
-    char *varname;
-
-    rated = srated[0];
-    variant = StringToVariant(swild);
-    varname = VariantName(variant);
-    base = atoi(sbase);
-    increment = atoi(sincrement);
-
-    /* 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) {
-        sprintf(buf,
-        "%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) {
-        sprintf(buf,
-        "%stell %s This computer can't play %s [%s], only %s\n%sdecline %s\n",
-               ics_prefix, opponent, swild, varname, appData.zippyVariants,
-               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) {
-      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",
-             ics_prefix, opponent, zippyConsecGames, 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) {
-      sprintf(buf, "%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;
-    }
-
-    sprintf(buf, "%saccept %s\n", ics_prefix, opponent);
-    SendToICS(buf);
-    if (appData.zippyTalk) {
-      Speak("tell", opponent);
-    }
-}
-
-
-/* Accept matches */
-int ZippyMatch(buf, i)
-     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. */
-       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, "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(moveNum, basetime, increment)
-     int moveNum, basetime, increment;
-{
-    char buf[MSG_SIZ];
-    int w, b;
-    char *opp = (gameMode==IcsPlayingWhite ? gameInfo.black : gameInfo.white);
-    Boolean sentPos = FALSE;
-
-    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) {
-      sprintf(buf, "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;
-    }
-
-    sprintf(buf, "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;
-      strcpy(zippyLastOpp, opp);
-    }
-
-    /* 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) {
-         sprintf(buf, "name %s\n", gameInfo.black);
-         SendToProgram(buf, &first);
-       }
-       strcpy(ics_handle, gameInfo.white);
-       sprintf(buf, "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);
-               }
-             }
-             SendToProgram("go\n", &first);
-           } 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);
-           }
-       }
-    } else if (gameMode == IcsPlayingBlack) {
-        if (first.sendName) {
-         sprintf(buf, "name %s\n", gameInfo.white);
-         SendToProgram(buf, &first);
-       }
-       strcpy(ics_handle, gameInfo.black);
-       sprintf(buf, "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);
-           } 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 */
-       }       
-    }
-}
-
-
-void
-ZippyHoldings(white_holding, black_holding, new_piece)
-     char *white_holding, *black_holding, *new_piece;
-{
-    char buf[MSG_SIZ];
-    if (gameMode != IcsPlayingBlack && gameMode != IcsPlayingWhite) return;
-    sprintf(buf, "holding [%s] [%s] %s\n",
-           white_holding, black_holding, new_piece);
-    SendToProgram(buf, &first);
-}
+/*\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