addition to being on the menus. (2) Large font for clock. (3) Pop-up
dialogs for errors.
+2.1, patchlevel 11 -- Sat Jun 5 00:01:01 PDT 1993 -- mann@src.dec.com
+
+* Added code to deal with "wild" games on ICS. This includes allowing
+castling with the king on d1 or d8, which is allowed in wild(1) games
+if the king started there. Notation is o-o to castle "short"---to
+whichever side the king is closer to---and o-o-o to castle "long."
+Right now wild castling is always allowed by xboard; we rely on ICS or
+gnuchess to reject it when we aren't really in wild mode.
+
+* memcpy call had arguments in wrong order.
+
+* Removed April Fool code
+
+2.1, patchlevel 10 -- Mon Feb 15 10:19:31 PST 1993 -- mann@src.dec.com
+
+* Avoid trying to select on a pipe when using System V. Needed to
+copy some code from InitChessProgram() up into establish().
+
+* Bug fix in disambiguating pawn moves like "ed".
+
+* Fix to error handling in ReceiveFromProgram.
+
+* Bug fix: entering EditPosition mode with black to play highlighted
+White's clock instead of Black's.
+
+* Added telnetProgram resource in case "telnet" is not the name of the
+telnet program.
+
+2.1, patchlevel 9 -- Fri Jan 22 19:08:27 PST 1993 -- mann@src.dec.com
+
+* Entering Force Moves mode clears "flag has fallen" messages from display.
+
+* ICS host name can now be in numeric format; for example 128.2.232.4.
+
+* Bug fix: LegalityTest was failing to test whether a pawn move was
+illegal because the move would discover a check.
+
+* Handle ICS message "mann asserts a win over manntest, who disconnected."
+
+* Bug fix: A recent change to PromotionCallBack had broken
+underpromotion to a knight.
+
+* Bug fix: In EditPosition mode, dragging a piece onto a square border
+would make it vanish. xboard wasn't distinguishing this case from
+dragging the piece off the board. Thanks to Matthew Kidd.
+
+* Removed "static" declaration from yywrap for compatibility with IRIX
+version of lex. Thanks to stiller@blaze.cs.jhu.edu.
+
+* Added substitutes for bzero, bcopy, and gethostname for Solaris
+SVR4. Thanks to Michael Grant.
+
+* Bug fixes to queen move disambiguation. Bugs showed up only when
+promotion resulted in more than one queen on the board.
+
+2.1, patchlevel 8 -- Fri Dec 11 17:54:18 PST 1992 -- mann@src.dec.com
+
+* parser bug fix: It now really works to leave off the piece a pawn is
+promoting to and let it default to queen.
+
+* When starting to observe or play an ICS game, don't draw board in
+initial position and then immediately redraw it in the current
+position.
+
+* Handle ICS messages when an "abuser" forfeits a game by
+disconnecting and when a game is aborted ("removed") by an
+administrator.
+
+* Bug fix: A user move when in LoadGame+Pause mode was resuming the
+game load instead of putting us in force mode.
+
+* It seems that crashes inside sscanf when xboard is compiled with gcc
+(as on IBM PS/2 AIX, mentioned below, and also on VAX Ultrix 3.1) are
+caused by an incompatibility between gcc and the sscanf implementation
+on these platforms. A workaround is to specify -fwritable-strings to
+gcc (see the gcc documentation). Added info on how to do this to the
+Imakefile. Thanks to Tom McConnell for this information.
+
+* Clocks are allowed to go negative. This is mostly for ICS
+compatibility, but it affects gnuchess mode too. Also, we give the
+time bonus when a player hits the time control boundary even if his
+flag is already down. This choice is a bit debatable, but it makes
+things look better when you are in TwoMachines mode and gnuchess's
+time management screws up causing it to exceed its time limit.
+
+* Implemented move legality checking code. Moves made with the mouse
+or parsed from a file are checked for legality before being made.
+This is mostly in preparation for future extensions, such as
+human-human play. For now it makes -noChessProgram mode more useful.
+
+* If the user takes back moves, we restore the clocks to the earlier
+settings.
+
+* We now handle the output of the ICS oldmoves command, including
+parsing the game end condition.
+
+* Added autoCallFlag mode.
+
+* Fixed glitches in the medium size outline king and rook bitmaps, and
+touched up the medium size solid king and outline queen.
+
+* bug fix: DisplayMove would not display backwardMostMove - 1.
+
+* Added CallFlag button and removed AcceptMatch. CallFlag is more
+important with new ICS, and accepting the current match offer is easy
+with the new ICS command "accept" (can be abbreviated "ac").
+
+* LoadGame and LoadPosition display tail of file name (plus index
+number, if any) as title.
+
+* MachineWhite, MachineBlack, and TwoMachines now work properly from
+LoadGame and LoadGame+Pause mode.
+
+* Use o-o and o-o-o to castle on ICS, not 00 and 000.
+
+* Stripped out code for old ICS messages; new ICS is now installed.
+
+* Bug fix: don't offer autosave when paused during LoadGame.
+
+2.1, patchlevel 7 - Fri Dec 11 17:40:56 PST 1992 - mann@src.dec.com
+
+* Track change to "Illegal move" message in new ICS.
+
+2.1, patchlevel 6 -- Tue Dec 8 10:48:44 PST 1992 -- mann@src.dec.com
+
+* Kludged around bug in keeping comment popup where you put it by
+adding borderXoffset and borderYoffset resources that give the width
+of the borders added by the window manager. Yucch.
+
+* Changed searchDepth kludge back to using "help" instead of "bd"; the
+latter didn't work because the output contains a line starting with
+"White", so we think gnuchess is telling us that White won. Oops.
+
+* Removed some code that uses an X11R5 feature, XrmGetDatabase.
+Without this, the auto font sizing code is harder to make work, so to
+keep my sanity I had to change it to affect only the fonts that appear
+in the main window and the comment popup, not the other popups. Maybe
+this is better anyway.
+
+* AcceptMatch button now works after a counterchallenge, too.
+
+* Added code for more variants of messages about games being adjourned
+or aborted. These messages need to be unified in new version of ICS.
+
+* Added workaround for minor bug in ICS; game number on first board
+can be wrong.
+
+* Removed code to say "refresh n" instead of "refresh"; was broken and
+wasn't needed anyway.
+
+* Added missing casts for compilers that distinguish enums from ints
+and missing cast in connect call.
+
+* Added code to recognize "both sides are out of time" draw message
+proposed for next version of ICS.
+
+* Thanks to Danny Sleator and Joe Peterson for bug reports.
+
+2.1, patchlevel 5 -- Sun Dec 6 19:52:40 PST 1992 -- mann@src.dec.com
+
+* Removed gcc and CDEBUGFLAGS from Imakefile.
+
+* Corrected setting of mode to ForceMoves when game file ends or
+contains an AmbiguousMove or BadMove. Previous bug was harmless.
+
+* Added AcceptMatch button and mention of 50-move rule for Draw button
+to man page.
+
+2.1, patchlevel 4 -- Sun Dec 6 02:55:42 PST 1992 -- mann@src.dec.com
+
+* Now handles revised messages from new version of ICS. New ICS is
+not released yet, so this code might change further. Next patchlevel
+will remove support for old ICS; both are there now.
+
+* Initial processing for a new game being watched or played now
+happens when we see the first board image. Removes the need to parse
+some messages and unifies some code.
+
+2.1, patchlevel 3 -- Tue Dec 1 19:40:40 PST 1992 -- mann@src.dec.com
+
+* Handles ICS messages "Draw : neither player has mating
+material" and "Draw : White has no material, Black has no time."
+
+* Added AcceptMatch button for ICS mode.
+
+* Fixed bug in previous fix to yylexstr().
+
+* Fixed bugs in code for loading old position files that don't start
+with "#" and for handling case where user asked for nth position in
+file but there aren't that many.
+
+* Handles ICS message "* has restored your old game"
+
+* ResetProc always clears title line now.
+
+* Don't issue just "refresh" when watching a game; use game number.
+Upcoming new version of ICS may need this.
+
+* Added code to try to prevent user from watching and/or playing more
+than one game at a time. There is a race condition inherent here; if
+we get more than one board from the game before our command to stop
+watching it takes effect, we'll think the user started it again. Not
+clear how to fix this.
+
+* Redid ParseBoard8 to use sscanf. Code is a good deal cleaner now.
+Also, we now parse out the game number too, though we don't make much
+use of it yet.
+
+* (Tried to put in a feature that recognizes the current game in a
+game file must have ended when we see the start of a new one, but had
+to disable it because we have too many false hits with the current
+parser, especially in gnuchess listing files.)
+
+* Moves read from game files or received from ICS are now translated
+into canonical algebraic form just like all other moves. Minor nit:
+If you use Reset while playing or observing a game in ICS mode, the
+game history (including the current board position) is lost, so the
+next move of the current game can't be translated.
+
+* Default fonts now vary with board size, and are chosen by pixel size
+instead of point size (since piece bitmaps have a fixed pixel size).
+
+* Bug fix: An extra Forward was required to skip over time indications
+in game files.
+
+2.1 patchlevel 2 -- Fri Nov 27 23:30:00 PST 1992 -- mann@src.dec.com
+
+* If you move the comment popup, the next time it pops up it will come
+back where you put it.
+
+* Fixed yylexstr() so calls to it can be interspersed with calls to
+yylex().
+
+* Fixed bugs in Forward/Backward while in LoadGame mode.
+
+* Changed Save{Game,Position} functions to append instead of
+overwriting if file exists. Changed Load{Game,Position} functions to
+deal with multiple games/positions per file.
+
+* Changed load{Game,Position}File resources to trigger automatic load
+on program startup. Changed save{Game,Position}File resources to
+trigger automatic save after every completed game and on program exit.
+
+* Added autoSaveGames mode.
+
+* Fixed bug with PromoPiece in call to MakeAlg from MakeMove.
+
+* Many changes to get rid of picky compiler warnings and generally
+clean up the code.
+
+2.1 patchlevel 1 -- Fri Nov 27 02:45:00 PST 1992 -- mann@src.dec.com
+
+* A last minute change before patchlevel 0 went out broke
+ParseGameHistory(). Immediately fixed in patchlevel 1.
+
+Version 2.1, patchlevel 0 -- Fri Nov 27 02:00:00 PST 1992 -- mann@src.dec.com
+
+* Uses "time" command of gnuchess 4.0 and following to keep clocks in
+sync. Still works with older versions without this command; we test
+whether the command is present the first time each chess program is
+started.
+
+* File name dialog pops up under the mouse cursor, so that it's got the
+keyboard focus if the window manager is using pointer focus.
+
+* Attempts to move the wrong color piece or an empty square are filtered
+out in xboard instead of being passed on to GNU Chess or the ICS.
+
+* The reason a game ended is now remembered even if you move backward
+and forward after it ends. It is forgotten only if you make a move
+(which is as it should be---this is now a different game, which hasn't
+ended yet). The message saying why the game ended no longer wipes out
+the last move.
+
+* Pause, Backward, Forward, ForceMoves, and EditPosition now work from
+TwoMachines mode.
+
+* Forward and Backward now change only the board display unless you
+are in ForceMoves mode. Pause mode keeps new moves that are received
+from being displayed on your screen until you unpause (or use
+Forward).
+
+* Added option to ring the bell after opponents' moves.
+
+* Saved games that start from other than the standard initial position
+now begin with a postion diagram as in saved position files. The
+loader is modified to understand such save files.
+
+* Improved error checking when trying to read from gnuchess.
+
+* Parser understands things that look like time indications, e.g., (0:12),
+instead of popping them up as comments.
+
+* Send "quit" to gnuchess before trying to kill it. I needed this
+locally because sending a SIGTERM to rsh was not killing the remote
+program on some internal field test systems we have.
+
+* Merged in code from John Chanak to make xboard a front end to the
+Internet Chess Server, and added several improvements of my own.
+(These include parsing end of game messages, loading the current state
+and previous history of a game that's joined in progress via "watch"
+or "load", automatic switch from board style 1 to 8 when needed,
+removing irrelevant buttons and adding some new ones, adding the
+useTelnet and gateway resources, and miscellaneous code cleanup, bug
+fixes, and documentation.) Many thanks to John for writing and
+contributing the initial version of this code.
+
+Fri Oct 30 20:16:40 PST 1992 (patchlevel 25) mann@src.dec.com
+
+* gnuchess now castles by sending us "o-o" or "o-o-o" (starting with a
+preliminary version of 4.0.pl60 that I have). Added code to handle
+this. The old format ("e1g1", etc.) still works too.
+
+* Added code to format moves in normal abbreviated algebraic notation
+(for example, e4, exd4, f8Q, Nf6, 0-0) instead of coordinate algebraic
+(for example, e2e4, e3d4, f7f8q, g1f6, e1g1). Moves entered with the
+mouse or received from gnuchess are translated into this canonical
+format for display on the message line or in saved game files. Moves
+read from game files are not translated, however; they are shown and
+saved just as they appear in the file.
+
+Mon Sep 14 13:19:01 PDT 1992 (patchlevel 24) mann@src.dec.com
+
+* It's not really correct to write an ep capture of a pawn on e5 as
+fxe5; this should be written as fxe6 because e6 is where the capturing
+pawn ends up. Nevertheless, the parser now interprets fxe5 as the ep
+capture f5xe6 unless there is a pawn on f4, in which case it
+interprets fxe5 as f4xe5. By design, the parser does not flag fxe5 as
+ambiguous if there are pawns on both f4 and f5; instead it prefers the
+more legitimate f4xe5 interpretation.
+
+* A move like fxe6 or fe6 is now interpreted as an e.p. capture of the
+pawn on e5 if there is one there and e6 is empty.
+
+* A move like fxe5 can no longer be interpreted as an e.p. capture if
+there isn't a pawn on e5 to be captured. Previously this could happen
+erroneously if there were pawns on both f4 and f5, and the f4 pawn was
+capturing a piece on e5.
+
+* The parser no longer munges the move it is parsing; in the past, for
+instance, it would remove the "x" from a move like dxc5 before echoing
+it to the screen.
+
+* The parser now detects when a move is ambiguous and returns an
+error, instead of arbitrarily choosing one possibility as it used to.
+
+Mon Aug 10 18:40:47 PDT 1992 (patchlevel 23) mann@src.dec.com
+
+* Added code so parser can handle fully qualified algebraic, e.g.,
+Ng1-f3, N/g1-f3, Ng1f3, or even Pe2-e4. The "P" and "/" work only for
+fully qualified moves, not generally.
+
+* Fixed parser bugs in handling moves with rank or file disambiguator,
+e.g. N1f3 or Ngf3.
+
+Mon Jul 6 17:55:32 PDT 1992 (patchlevel 22) mann@src.dec.com
+
+* AIXV3 patch from Tom McConnell; thanks!
+
+* After hitting the Backward button we see the last move made instead of
+the word "Pausing" displayed.
+
+* Fixed a couple of problems compiling on IRIX; thanks to Michel Arsenault
+and Alan Walsh for bug reports and help with fixes.
+
+* Added optional display of algebraic notation coordinates along left and
+bottom edges of board. This was inspired by some code from Jean-Christophe
+Engel; thanks!
+
+* Swapped EditPosition and Pause buttons to reduce danger of losing the
+game in progress by hitting EditPosition when you wanted Backward. Thanks
+to Ove Lundberg for complaining (though it bugged me too).
+
+* Despite one complaint, I kept the feature of changing both clocks when black
+makes time control, rather than changing each individually when the player
+involved makes it. This is to avoid having it look like White is way ahead on
+time while Black is thinking about the last move of the time control period
+(i.e., when White has made time control but Black hasn't yet).
+
+* Corrected clocks to work as in real tournaments. Time is *added* when you
+reach time control; previously the clocks were simply set to the length of the
+new time period. Also, when a player's flag falls, he still does not receive a
+new time allotment at the next time control, but his opponent does still
+receive more time if *his* flag has not fallen; previously, time controls were
+ignored for both players if either flag fell. I forget who reported this bug.
+
+* Added a small ESIX patch from Kayvan Sylvan.
+
+Mon Jun 22 13:24:38 PDT 1992 (patchlevel 21)
+
+* Added some SVR4 support code from Ronald Cole. It's Greek to me.
+
+* Note: Stephen Meatheringham reports a bug causing xboard to crash with an
+error from the X server when run on a Sun Sparcstation IPX with Solaris 1.0.1
+and OpenWindows 3.0. This bug has not been tracked down yet.
+
+* Fixed a problem with ATTENTION code. On some operating systems, you have to
+be even more cautious about when you send a SIGINT to gnuchess, because the
+signal handler gets deinstalled each time it is used, and gnuchess does not
+reinstall it immediately. In particular, if you send a SIGINT and make an
+illegal move or ask for a hint, gnuchess does not reinstall the handler until
+*after* you make your next move, so you had better not send a SIGINT before
+it. I put a workaround for this case into xboard; I hope there are no more.
+
+* The ATTENTION code is needed when compiling for the ESIX operating system,
+because ESIX has a bug in the FIONREAD ioctl, so the code in gnuchess that
+lets you interrupt its thinking on your time just by typing in your move does
+not work. Perhaps gnuchess will have a workaround for this eventually.
+
+* Fixed yet another bug in using XtGetValues; code to inhibit shell resizing
+was leaving a null pointer as the place to store the current sizes.
+
+* Man page fixes: corrected -sl to -sd, updated LIMITATIONS.
+
+* Added include of <sys/time.h> conditional on HAS_GETTIMEOFDAY. I hope this
+works for all systems; on mine, <time.h> and <sys/time.h> are the same file,
+but with an #ifndef that makes it harmless to include both.
+
+* Thanks for reports from Mike Pearlman, Stephen Meatheringham, Kayvan Sylvan,
+d87-mal@nada.kth.se, Urban Koistinen, and Ronald Cole.
+
+Mon Jun 8 11:39:18 PDT 1992 (patchlevel 20)
+
+* Documented default value of initString and explained what it means. This
+paragraph also serves to inform the user that xboard by default will tell
+gnuchess to think on his time, something one user apparently didn't understand.
+
+* Redid -searchDepth (formerly -searchLevel) and -searchTime code. They both
+work correctly with gnuchess 3.1 now. The -searchDepth option also works
+correctly with gnuchess 4.0. The -searchTime option does not work with
+gnuchess 4.0.pl50 or earlier because those gnuchess versions do not have a
+working searchTime feature. Instead of having xboard do a half-baked emulation
+of this feature by using a 1-move time control, I will be submitting a patch to
+the gnuchess folks to restore the feature as in 3.1.
+
+* Improved clock code. It now keeps accurate track of fractional seconds of
+clock running time, if your system has gettimeofday(); if not, it is generous
+about them---if a clock has been going for n.m seconds when it is stopped, only
+n seconds are charged. Previously, xboard would charge one second immediately
+when a clock was started, so if n.m had elapsed when it was stopped, n+1 would
+be charged. This should stop xboard from thinking gnuchess has let its flag
+fall on the last move of a time control, as it often did before. I also
+improved the comments and condensed the code a little. I hope there are no
+systems that have X11 and don't have gettimeofday(), but if there are, I'd like
+to know what to replace it with.
+
+* "Flag dropped" messages now show up on the top line, beside the clocks, so
+that they don't obscure more interesting messages on the second line.
+
+* Fixed confusion of NULL and '\000'.
+
+* Include time.h to get ctime() declaration.
+
+* Fix to "parser bug" messages in parser.l.
+
+* Changed code to work with old-fashioned tolower() macro that requires its
+argument to be an uppercase letter.
+
+* Minor fix to some usage error messages; was reversing program name and
+erroneous argument value.
+
+* Thanks to Mitch Wright, Ranier Orth, and Henrik Schmiediche for bug reports.
+
+Fri Jun 5 13:06:08 PDT 1992 (patchlevel 19)
+
+* Note: xboard does not seem to work when compiled with gcc 1.40 on a
+PS/2 running AIX. It crashes at the sscanf in line 768 of xboard.c.
+You can work around the problem by specifying the -timeControl option
+on the command line instead of defaulting it. This does not seem to
+be a bug in xboard itself; it runs okay on PS/2 AIX when compiled with
+MetaWare High C. [But see 2.1.pl8 above.]
+
+* Removed bogus files from distribution.
+
+* Changed variable name that conflicted with SVR4 ttyname function.
+
+* Thanks to Christoph Strozyk for another bug report, and to several
+of the folks mentioned under patchlevel 18 for testing it.
+
+Wed Jun 3 17:50:37 PDT 1992 (patchlevel 18) mann@src.dec.com
+
+* Added an include of sys/fcntl.h to pick up the definition of O_RDWR for
+System V users.
+
+* Fixed a bug in CatchPipeSignal; was testing the wrong variable to determine
+which chess program got the signal.
+
+* Removed the declaration of strrchr, which was causing problems for some
+people, and added an include of <string.h> or <strings.h> instead.
+
+* Fixed a bunch of type errors uncovered by compilers that check more carefully
+than the one I was using. I just patched many of them with casts---typically
+cases where a function didn't take the same number and type of arguments as the
+X header file prototype said it should---because I didn't see any other way to
+fix them. The versions of the X header files that I have don't actually have
+full prototypes for these function types; they declare only the return type and
+just give the argument types as comments.
+
+* InitChessProgram returns immediately if chess program died while we were
+waiting for it to say "Chess". I think this fixes a bug that could make xboard
+crash or go into an infinite loop printing "Warning: Select failed; error code
+9" if gnuchess crashed there.
+
+* Removed code that tries to put the file descriptor used to read messages from
+gnuchess into nonblocking mode. I don't see how this kludge could ever have
+fixed the problem it was intended to (see below), and it seemed to be causing
+a race condition when we were waiting for the initial message from gnuchess
+after it started up.
+
+* Thanks to Richard K. Lloyd, Konstantinos Konstantinides, Mark
+Seiden, Erik Schoenfelder, Andreas Stolcke, Rainer Orth, Henry Thomas,
+and Kris Van Hees for bug reports and help with fixes.
+
+Sun May 31 08:00:00 PDT 1992 (patchlevel 17) Bart Massey bart@cs.uoregon.edu
+
+* 'xboard' suffered from a couple of serious bugs. An initial
+ size needed to be given to the message label to avoid a race
+ condition (!) so I gave it some text (otherwise occasionally
+ xboard would fail because the message widget was 0 width under
+ MIT X11R4). The sizes needed to be Dimension, not int, because
+ they were written in by Xt -- on my big-endian box Dimension
+ is short, causing chaos. Also, I hacked a declaration in for
+ strrchr() to shut the compiler up -- this is arguably a kludge,
+ but I couldn't think of anything better; maybe you can.
+
+Fri May 22 13:40:50 PDT 1992 (patchlevel 16)
+
+* First real (non-beta) release of version 2.0.
+
+* Added GNU copyleft notices covering version 2.0 enhancements.
+
+Thu May 21 12:30:09 PDT 1992 (patchlevel 15)
+
+* Removed -g from Imakefile.
+
+* Added casts in parser.l to prevent warning messages from some C compilers.
+
+* Bugfix: code to exit if chess program couldn't be started had bug in error
+message printing.
+
+Tue May 19 11:58:06 PDT 1992 (patchlevel 14)
+
+* Fixes one small bug: If LoadGame does not find a game in the file, it resets
+xboard to BeginningOfGame mode (as if the Reset button was pressed), instead of
+leaving it in LoadGame mode.
+
+Mon Apr 13 18:32:27 PDT 1992 (patchlevel 13)
+
+* Improved error handling when gnuchessx crashes. Now displays a message and
+enters EndOfGame state without exiting. This should help with diagnosing
+gnuchessx problems, since the position and move history are not lost.
+
+* Debugged man page.
+
+Version 2.0 -- Thu Apr 2 16:04:54 PST 1992 (patchlevel 12)
+
+* Added small 40x40 pixel pieces; board size now specified as small, medium, or
+large.
+
+* Improved man page.
+
+* Saved position files now say "white to play" if it's white's move.
+
+* SavePosition while in EditPosition mode now gets side to play right.
+
+* Made "Pawn" the default on edit position menus.
+
+* Removed "To play" from edit position menus, since it's redundant. Left
+support code in so it can be restored if someone wants it.
+
+* Tightened parser's definition of a move number to help further with skipping
+over stuff before the first move.
+
+* Put back feature of skipping over stuff in game files before a start of game
+marker. This helps when loading electronic mail messages that have things
+like "00" in the header (looks like castling), or that talk about moves in
+running text before the actual game score. Tradeoff is that we can't load
+games that don't either have move numbers or say "gnuchess game" just before
+the moves.
+
+* Changed version number to 2.0 because others have used "1.3" for patched
+versions of 1.2. Display version number and patchlevel at startup.
+
+Thu Apr 2 15:30:22 PST 1992 (patchlevel 11)
+
+* Bugfix: Match started with TwoMachines button now handles the case where
+user loaded/edited a position *and* forced some moves.
+
+* added error message feedback to many cases where the user's button press is
+rejected because it isn't allowed in the current mode.
+
+* added -noChessProgram option to allow use as a passive chessboard without
+gnuchess, replacing old buggy scheme that tried to do this automatically when
+gnuchess was not found on the search path.
+
+* parser accepts "=" in front of promotion piece
+
+* parser accepts ":" notation for captures
+
+* Removed SwitchSides button to make room for EditPosition. Left code in
+place, ifdef'ed out.
+
+* Added EditPosition mode.
+
+* Reformatted xboard.c to eliminate long lines.
+
+* Promotion popup now appears near rank to which pawn is moving.
+
+* UI improvement: Button representing current program mode is kept highlighted.
+
+* Bug fix: Hint button is now disabled when machine is on move.
+
+Sat Mar 21 14:57:37 PST 1992 (patchlevel 10)
+
+* TwoMachines now works from MachineWhite or MachineBlack mode.
+
+* ForceMoves and Backward/Forward now work at the end of a game started by
+the TwoMachines button.
+
+* Sends o-o or o-o-o to gnuchess instead of O-O or O-O-O.
+
+* Bug fix: SwitchSides button is now disabled when machine is on move.
+
+* Added -searchLevel option and corrected handling of -searchTime to reflect
+the way gnuchess really handles command line options instead of what the
+documentation used to say. Allowed optional ":seconds" on times.
+
+Tue Mar 17 21:00:35 PST 1992 (patchlevel 9)
+
+* Removed non-working code that tried to recover when a remote chess program
+could not be started; now exits with a message instead.
+
+* Now waits for chess program to print its first message (normally "Chess")
+before sending it any commands.
+
+* Matches between two machines can now start from a black-to-play position or
+black-to-play opening.
+
+* Bug fix: The program would sometimes lose track of the board position after
+the end of the game, drawing a board full of white pawns next time it got an X
+redraw request. Now fixed.
+
+* Cleaned up handling of illegal moves in game files. Now we just stop reading
+and go into ForceMoves mode.
+
+* Saved games against gnuchess indicate who won (restored and debugged 1.2
+feature).
+
+* Setting -searchTime turns off -clockMode.
+
+* My trick for setting up a black-to-play position broke in the transition from
+gnuchess 3.1+ to 4.0. Installed a different trick that works with both.
+
+* Bug fix: Was clobbering memory by mallocing one byte too few for the file
+name in SetupPositionFromFileProc. This usually resulted in a crash on the
+second attempt to load a position from a file.
+
+Mon Mar 16 16:49:00 PST 1992 (patchlevel 8)
+
+* Game and position files are saved in CHESSDIR as well as loaded from there.
+We actually chdir there, so gnuchess listing files also wind up there.
+
+* Underpromotion moves from gnuchess really work now.
+
+Sat Mar 14 14:55:24 PST 1992 (patchlevel 7)
+
+* Cleaned up lots of bugs and kruft in parser.l. It's a wonder the old version
+worked at all. Improved rejection of random text interspersed with the moves.
+
+* Relabeled buttons to reduce confusion (Play From File --> Load Game;
+Setup From File --> Load Position).
+
+* Bug fix: Doesn't exit at the end of a game that was started by pressing
+the Two Machines button.
+
+* Bug fix: Doesn't get confused if you hit Machine Black (White) while
+the machine is already playing black (white) and is on move.
+
+* Prints "White to play" or "Black to play" after loading a position file.
+
+* Bug fix: No longer hangs if no start-of-game is found in a game file.
+
+* More permissive about game file format. The first line is not assumed
+to be a header comment unless it starts with "#". We don't insist on
+seeing a "1." before the first move.
+
+* Added pop-up dialog to allow underpromotion. Moving a pawn to the last rank
+pops up a dialog to let you select what piece to promote to. Also added code
+to handle underpromotion moves from gnuchess or from a game file correctly.
+
+* Added code to send SIGINT to gnuchess before trying to move, if gnuchess
+might be busy thinking on our time. The code is ifdef'ed out because it's
+not necessary; only beta-test versions of gnuchess 4.0 at patchlevel 19 or
+below require it.
+
+Mon Feb 24 20:10:22 PST 1992
+
+* Merged fixes received from Stuart Cracraft and Mike McGann, to iconic mode
+and to ShutdownChessPrograms. (They both sent the same fixes.) I did not
+install the change that removed "easy" from the initialization string, so
+xboard will still try to run gnuchessx in "hard" mode (thinking on opponent's
+time) by sending this command.
+
+Version 1.3 -- Thu Dec 19 18:19:00 PST 1991 mann@src.dec.com (Tim Mann)
+
+* I've fixed a slew of bugs in this version of xboard:
+
+* You can now back up from the end of a game, even if gnuchess was playing
+against you. Since gnuchess exits upon mate or draw (when it's not in force
+mode), this feature works by starting up a new copy of gnuchess and feeding it
+the saved starting position and moves. Drawback: the new gnuchess starts with
+clocks at the beginning of a fresh time control.
+
+* Note: the "force" command to gnuchess is a toggle. I fixed a couple of
+places in xboard where "force" was being issued when gnuchess was already in
+force mode, which resulted in chaos. I don't know if they were in 1.2 or if I
+added them myself in the course of putting in new features.
+
+* ForceMoves button is now legal in PlayFromFile mode. It closes the game file
+and lets you force moves (or go Backward) from whatever position you've
+reached.
+
+* The parser now recognizes "white resigns" and "black resigns" (in any
+combination of upper and lower case, with or without the closing "s"). These
+used to be misinterpreted as meaning "white wins" or "black wins" respectively!
+Added "1 - 0" and "0 - 1" as equivalents for "1-0" and "0-1".
+
+* I removed the parser feature of considering "!" as a comment delimiter.
+"!" is so common as an annotation in game scores that this feature seems like a
+very bad idea.
+
+* Added a wait() after each successful call to kill() to avoid leaving zombie
+processes around.
+
+* Removed the feature of putting an indication of how the game ended into the
+internal move list when playing from a file. The only benefit of this was that
+it would get written back out if you saved the game at that point. This
+feature doesn't seem too useful, since you are unlikely to save a game you've
+read in without making any changes, and it caused a lot of problems in the code
+and user interface. The end condition is still displayed as a message when
+it's first read in from the file. End conditions when playing against gnuchess
+are still saved, too.
+
+* programName is now the last file name component of argv[0], not all of it.
+
+* xboard.c got three compiler warnings due to arguments that didn't match the
+prototypes on X toolkit functions. I got rid of them by adding some casts, and
+adding extra unused arguments to the ReceiveFromProgram function.
+
+* As I received it, the distribution had an extra period glued on the front
+of every line that started with a period. This broke the manpage in an
+obvious way. It also broke parser.l in a nonobvious way that stopped
+PlayFromFile from working.
+
+* Fixed one or two bugs where the clock was started when it shouldn't have
+been. I think one example was if you tried an illegal move in ForceMoves mode.
+
+* Now starts the machine's clock if you hit MachineWhite when white is on move
+or MachineBlack when black is on move.
+
+* Now updates the to-move indicator (highlight on clock display) when you use
+Forward/Backward or ForceMoves.
+
+* gnuchess 3.1+ has a bug that makes it try to think on its opponent's time
+even when in force mode; this causes problems with xboard's SetupFromFile
+command because gnuchess's saved hint move is garbage after the new position is
+loaded, so gnuchess prints out an error message when it tries to think about
+what it would do if the user made this move. Here is a patch for gnuchess
+3.1+:
+
+*** nondsp.c~ Mon Dec 2 12:37:18 1991
+*-- nondsp.c Thu Dec 19 15:36:06 1991
+***************
+*** 1223,1229 ****
+ ok = flag.quit = false;
+ player = opponent;
+ ft = 0;
+! if (hint > 0 && !flag.easy && Book == NULL)
+ {
+ fflush (stdout);
+ time0 = time ((long *) 0);
+*-- 1223,1229 ----
+ ok = flag.quit = false;
+ player = opponent;
+ ft = 0;
+! if (hint > 0 && !flag.easy && Book == NULL && !flag.force)
+ {
+ fflush (stdout);
+ time0 = time ((long *) 0);
+
+* SavePosition/SetupFromFile now handle black-to-play positions. If it's
+black's move in a saved position, the line "black to play" is printed
+immediately after the board display. Upon loading a position, xboard
+looks for "black" at the beginning of this line to determine whether it's
+black's move.
+
+* SetupFromFile now leaves you (effectively) in ForceMoves mode. This
+eliminates some cases where the program would get into an inconsistent state,
+and hopefully is more intuitive for the user, too.
+
+* PlayFromFile now leaves xboard in ForceMoves mode after playing through all
+the moves in the file. You can use Backward/Forward, make moves, get gnuchess
+to join the game with MachineWhite or MachineBlack, etc. The game file can end
+with either white or black to play.
+
+* Backward/Forward now seem to work in all cases. Previously using these
+buttons during a game against the machine would get xboard out of sync with
+gnuchess's idea of what was going on, resulting in chaos. Now using these
+buttons puts xboard in ForceMoves mode; you can bring gnuchess back into the
+game using the MachineWhite or MachineBlack buttons.
+
+Thu Jun 27 15:06:25 PDT 1991
+
+ * Remove comment popups during a reset.
+
+ * Bug in my forward move fix. Didn't work for playgamefile mode.
+
+Thu Jun 20 15:04:06 PDT 1991
+
+ * Can't do a backward move when it isn't your turn because gnuchess
+ gets confused. Similar bug for forward move. Do a bunch of moves,
+ back up move and go forward. These get filtered out now.
+
+ * Put easy back into the initString. This DISABLES easy mode which by
+ default is ON.
+
+ * New-line wasn't always being added to forward move strings sent to gnuchess.
+
+ * Parsed comments as [ ..............text follows ] into d7d5
+
+ * Long game file comments cause core dumps on HP systems.
+
+ * Multiple uses of the get positition file button got confused
+ on the second click.
+
+ Minor fixes to the 1.2 release
+
+Version 1.2 -- Tue Jun 11 17:14:12 PDT 1991
+
+ * For version 1.2, Jeff Kenton, Richard LLoyd, David Jensen, Martin D.,
+ Bill Schmidt, Scott Hemphill, Paul Vaughan and Bill Shauck all found
+ a lot of bugs that we put into xboard just to see if they were paying
+ attention. They were.
+
+ * Changed the protocol that xboard uses to work with the new version of
+ gnuchessr.
+
+ * There was an off by one error with the clock resetting code. Also,
+ the clock display highliting was wrong. Thanks to Bill Shauck for
+ pointing these out.
+
+ * enumerations are not really integers on some C compilers. I added casting.
+ The new version of gnuchess expects the "go" command for two machine
+ and machine plays white cases. The whiteString resource is for
+ compatibility with previous gnuchess versions which get confused by go.
+ Thanks to Martin D. for catching these.
+
+ * Fixed a bug where the checkmate message wasn't being parsed and xboard
+ kept playing. Also, the message was added to game file if saved.
+ Thanks to Scott Hemhill for pointing this out.
+
+ * Fixed a bug where saving a long game, resetting and saving a short game
+ resulted in appending the end of the long game to the short one.
+ If a game is just being played out and there is no reason for gnuchessr
+ to be used, ignore pipe signals. This allows people to use xboard
+ as a chess board without need for gnuchess. Also, trivially bad moves
+ such as e2e2 are not passed on to gnuchessr but instead ignored out of
+ hand. This allows people using xboard as a chessboard with gnuchessr
+ to pick a piece up, think and put it back down without making a move.
+ Thanks to Jeff Kenton for pointing these out.
+
+ * Added the CHESSDIR environment variable. Game and position files are
+ found in this directory. If this variable is not declared, then the
+ current directory is used. File names starting with / are treated
+ specially.
+
+ * The bitmap file names were changed so that none exceeded 14 characters.
+ This is necessary for R5.
+
+ fcntl(from_prog[0], F_SETFL, O_NDELAY);
+
+ The xboard fix was to set non-blocking i/o on the read pipe for gnuchessr
+
+ printz ("Illegal move (no matching move generated)");
+
+ * fixed a problem with the new version of gnuchessr where xboard wasn't
+ getting gnuchessr error messages for illegal moves. The problem seems
+ to be fixed *without* any gnuchess changes but this is likely to be
+ highly system dependant. There should be a new line on line 246 of nondsp.c
+
+ * -iconic doesn't work at all now. The Iconify() does work. This was
+ a tradeoff and it is really an Xt bug.
+
+ * Fixed a compiler warning for gcc and an error for the IBM RT compilers.
+ This is the VOID_PROC macro in xboard.h. Thanks to David Jensen for this.
+
+ * If the DisplayWidth or DisplayHeight is less than 800, use small size mode.
+ Also the size of the name widget was reduced from 500 pixels to 400 pixels
+ because in small size mode there was a gap on the right.
+ Changed the default font from fixed to helvetica_oblique14 to:
+ **-helvetica-bold-r-normal--14-*-*-*-*-*-*-*
+ helvetica_oblique14 is a font alias not on all R4 systems.
+ Curiously enough, the 17 point is not available on 75dpi systems
+ and the 18 point font is not available on 100dpi systems.
+ Thanks to Richard K. Lloyd for pointing these out.
+
+ * Minor hacks to work with R5 alpha. Had to add an event handler to the
+ boardWidget to get translations to work. This may go away with the real R5.
+ Added <Message>WM_PROTOCOLS: QuitProc() for R5 ICCCM compatibility.
+
+ * If the first character of the file is not `1' then the first line
+ of a game or position file is displayed as the name in a label widget.
+
+ * Added a PopUp dialog for getting file names.
+
+ * Modified the code to use the R4 routines rather than R3 compatibility.
+
+ * Changed the forwards/backwards/readgamefile code to allow a play to step
+ back and forth in a game. If he steps all the way to the beginning
+ he has to restart the game. If he gets into a mated position, same problem.
+
+ * Moved the man page from xboard.1 to xboard.man. This makes imake man page
+ installs work correctly. Thanks to Richard K. Lloyd for pointing this out.
+
+ * Corrected the queening code. XBoard was sending the wrong syntax.
+ It was sending for example h8(Q) when it should have been sending h8q.
+ Thanks to Paul Vaughan and Mike McGann for pointing this out.
+
+ * Added a lex parser for algebraic games. It should be easy to use the parser
+ for other interfaces such as the Microsoft Windows version.
+ It parses comments of the form [anything] and ! to the end of a line.
+
+Version 1.1 -- Mon Jan 7 14:46:03 PST 1991
+
+ * Roger Dubar, Konstantinos Konstantinides, Wolfgang S. Rupprecht,
+ Paul Scowen, Mvh Smidt and Kayvan Sylvan all helped immensely during
+ beta-testing.
+
+ * older non-ANSI versions of Sun compilers complain vociferously.
+
+ * the remoteShell resource was added for HP-UX systems
+ and other systems where the remoteShell isn't rsh.
+
+ * -iconic doesn't work properly. If XBoard is opened iconic then
+ iconifying it later with a keystroke doesn't work. I think
+ this is an Xt bug.
+
+ * For systems with smaller screens, XBoard can use smaller pieces,
+ and a smaller board. -bigSizeMode False uses a smaller set of pieces.
+ These are scaled versions of the large pieces. They look ok but could
+ be improved.
+
+ * getlogin() does not work in all cases.
+ It was replaced by getpwuid(getuid())->pw_name).
+
+ * Warning messages get displayed in the message box.
+
+ * Any button restarts a paused game.
+
+ * Kayvan Sylvan contributed a patch for ESIX.
+ It seems that select() on pipes is broken on his system. We declined
+ to incorporate his patch though, because it was a work-around for
+ something that was broken on one system, and selfishly, that system
+ was not my own. Nevertheless, it is likely that other System V users
+ on PC's may need to use this patch and it is is included as the file
+ ESIX.patch. To use it, type
+
+ patch xboard.c ESIX.patch
+
+ * Fixed a bug where hint didn't work. The local version of gnuchessr
+ had been hacked. The fix was to clone stderr and stdout for gnuchessr.
+
+ * Fixed a bug where a player could play after a game had been finished.
+
+ * The bitmaps have been pushed into the bitmaps directory.
+
+ * Substantially rewrote and simplified the Two Machine code.
+
+ * Added StrStr() because many systems don't have this ANSI function.
+
+ * Added keyboard equivalents. Added an iconify keystroke, C or c.
+
+ * Works with gcc now. gcc complained about casting float constants
+ as XtPointers.
+
+ * Detect if the visual doesn't support color. If so, run in monoMode.
+ An alternative would be to detect grayscale visual and use a collection
+ of gray user interface colors.
+
+ XBoard.clockMode: False
+
+ * For people who don't like or use chess clocks a clockMode switch
+ has been added. It is on by default. It can be turned off in the
+ .Xdefaults file with
+
+ * Added a declaration, xtVersion, which will quickly break on R3 Intrinsics.
+
+ * Hal Peterson contributed a new Imakefile.
+
+ * Rewrote DisplayClocks().
+
+ * Rewrote TwoMachinesPlay mode.
+
+ * Konstantinos Konstantinides added the -searchTime option.
+
+ * Substantially rewrote and simplified the clock code. If a game was paused
+ and then resumed, the clocks were out of sync.
+
+ * Fixed a bug in HandleUserMove() where the user could make a move while the
+ machine was thinking. The fix detects and ignores these moves. onMove
+ was not being used and was removed.
+
+++ /dev/null
-2.1, patchlevel 11 -- Sat Jun 5 00:01:01 PDT 1993 -- mann@src.dec.com
-
-* Added code to deal with "wild" games on ICS. This includes allowing
-castling with the king on d1 or d8, which is allowed in wild(1) games
-if the king started there. Notation is o-o to castle "short"---to
-whichever side the king is closer to---and o-o-o to castle "long."
-Right now wild castling is always allowed by xboard; we rely on ICS or
-gnuchess to reject it when we aren't really in wild mode.
-
-* memcpy call had arguments in wrong order.
-
-* Removed April Fool code
-
-2.1, patchlevel 10 -- Mon Feb 15 10:19:31 PST 1993 -- mann@src.dec.com
-
-* Avoid trying to select on a pipe when using System V. Needed to
-copy some code from InitChessProgram() up into establish().
-
-* Bug fix in disambiguating pawn moves like "ed".
-
-* Fix to error handling in ReceiveFromProgram.
-
-* Bug fix: entering EditPosition mode with black to play highlighted
-White's clock instead of Black's.
-
-* Added telnetProgram resource in case "telnet" is not the name of the
-telnet program.
-
-2.1, patchlevel 9 -- Fri Jan 22 19:08:27 PST 1993 -- mann@src.dec.com
-
-* Entering Force Moves mode clears "flag has fallen" messages from display.
-
-* ICS host name can now be in numeric format; for example 128.2.232.4.
-
-* Bug fix: LegalityTest was failing to test whether a pawn move was
-illegal because the move would discover a check.
-
-* Handle ICS message "mann asserts a win over manntest, who disconnected."
-
-* Bug fix: A recent change to PromotionCallBack had broken
-underpromotion to a knight.
-
-* Bug fix: In EditPosition mode, dragging a piece onto a square border
-would make it vanish. xboard wasn't distinguishing this case from
-dragging the piece off the board. Thanks to Matthew Kidd.
-
-* Removed "static" declaration from yywrap for compatibility with IRIX
-version of lex. Thanks to stiller@blaze.cs.jhu.edu.
-
-* Added substitutes for bzero, bcopy, and gethostname for Solaris
-SVR4. Thanks to Michael Grant.
-
-* Bug fixes to queen move disambiguation. Bugs showed up only when
-promotion resulted in more than one queen on the board.
-
-2.1, patchlevel 8 -- Fri Dec 11 17:54:18 PST 1992 -- mann@src.dec.com
-
-* parser bug fix: It now really works to leave off the piece a pawn is
-promoting to and let it default to queen.
-
-* When starting to observe or play an ICS game, don't draw board in
-initial position and then immediately redraw it in the current
-position.
-
-* Handle ICS messages when an "abuser" forfeits a game by
-disconnecting and when a game is aborted ("removed") by an
-administrator.
-
-* Bug fix: A user move when in LoadGame+Pause mode was resuming the
-game load instead of putting us in force mode.
-
-* It seems that crashes inside sscanf when xboard is compiled with gcc
-(as on IBM PS/2 AIX, mentioned below, and also on VAX Ultrix 3.1) are
-caused by an incompatibility between gcc and the sscanf implementation
-on these platforms. A workaround is to specify -fwritable-strings to
-gcc (see the gcc documentation). Added info on how to do this to the
-Imakefile. Thanks to Tom McConnell for this information.
-
-* Clocks are allowed to go negative. This is mostly for ICS
-compatibility, but it affects gnuchess mode too. Also, we give the
-time bonus when a player hits the time control boundary even if his
-flag is already down. This choice is a bit debatable, but it makes
-things look better when you are in TwoMachines mode and gnuchess's
-time management screws up causing it to exceed its time limit.
-
-* Implemented move legality checking code. Moves made with the mouse
-or parsed from a file are checked for legality before being made.
-This is mostly in preparation for future extensions, such as
-human-human play. For now it makes -noChessProgram mode more useful.
-
-* If the user takes back moves, we restore the clocks to the earlier
-settings.
-
-* We now handle the output of the ICS oldmoves command, including
-parsing the game end condition.
-
-* Added autoCallFlag mode.
-
-* Fixed glitches in the medium size outline king and rook bitmaps, and
-touched up the medium size solid king and outline queen.
-
-* bug fix: DisplayMove would not display backwardMostMove - 1.
-
-* Added CallFlag button and removed AcceptMatch. CallFlag is more
-important with new ICS, and accepting the current match offer is easy
-with the new ICS command "accept" (can be abbreviated "ac").
-
-* LoadGame and LoadPosition display tail of file name (plus index
-number, if any) as title.
-
-* MachineWhite, MachineBlack, and TwoMachines now work properly from
-LoadGame and LoadGame+Pause mode.
-
-* Use o-o and o-o-o to castle on ICS, not 00 and 000.
-
-* Stripped out code for old ICS messages; new ICS is now installed.
-
-* Bug fix: don't offer autosave when paused during LoadGame.
-
-2.1, patchlevel 7 - Fri Dec 11 17:40:56 PST 1992 - mann@src.dec.com
-
-* Track change to "Illegal move" message in new ICS.
-
-2.1, patchlevel 6 -- Tue Dec 8 10:48:44 PST 1992 -- mann@src.dec.com
-
-* Kludged around bug in keeping comment popup where you put it by
-adding borderXoffset and borderYoffset resources that give the width
-of the borders added by the window manager. Yucch.
-
-* Changed searchDepth kludge back to using "help" instead of "bd"; the
-latter didn't work because the output contains a line starting with
-"White", so we think gnuchess is telling us that White won. Oops.
-
-* Removed some code that uses an X11R5 feature, XrmGetDatabase.
-Without this, the auto font sizing code is harder to make work, so to
-keep my sanity I had to change it to affect only the fonts that appear
-in the main window and the comment popup, not the other popups. Maybe
-this is better anyway.
-
-* AcceptMatch button now works after a counterchallenge, too.
-
-* Added code for more variants of messages about games being adjourned
-or aborted. These messages need to be unified in new version of ICS.
-
-* Added workaround for minor bug in ICS; game number on first board
-can be wrong.
-
-* Removed code to say "refresh n" instead of "refresh"; was broken and
-wasn't needed anyway.
-
-* Added missing casts for compilers that distinguish enums from ints
-and missing cast in connect call.
-
-* Added code to recognize "both sides are out of time" draw message
-proposed for next version of ICS.
-
-* Thanks to Danny Sleator and Joe Peterson for bug reports.
-
-2.1, patchlevel 5 -- Sun Dec 6 19:52:40 PST 1992 -- mann@src.dec.com
-
-* Removed gcc and CDEBUGFLAGS from Imakefile.
-
-* Corrected setting of mode to ForceMoves when game file ends or
-contains an AmbiguousMove or BadMove. Previous bug was harmless.
-
-* Added AcceptMatch button and mention of 50-move rule for Draw button
-to man page.
-
-2.1, patchlevel 4 -- Sun Dec 6 02:55:42 PST 1992 -- mann@src.dec.com
-
-* Now handles revised messages from new version of ICS. New ICS is
-not released yet, so this code might change further. Next patchlevel
-will remove support for old ICS; both are there now.
-
-* Initial processing for a new game being watched or played now
-happens when we see the first board image. Removes the need to parse
-some messages and unifies some code.
-
-2.1, patchlevel 3 -- Tue Dec 1 19:40:40 PST 1992 -- mann@src.dec.com
-
-* Handles ICS messages "Draw : neither player has mating
-material" and "Draw : White has no material, Black has no time."
-
-* Added AcceptMatch button for ICS mode.
-
-* Fixed bug in previous fix to yylexstr().
-
-* Fixed bugs in code for loading old position files that don't start
-with "#" and for handling case where user asked for nth position in
-file but there aren't that many.
-
-* Handles ICS message "* has restored your old game"
-
-* ResetProc always clears title line now.
-
-* Don't issue just "refresh" when watching a game; use game number.
-Upcoming new version of ICS may need this.
-
-* Added code to try to prevent user from watching and/or playing more
-than one game at a time. There is a race condition inherent here; if
-we get more than one board from the game before our command to stop
-watching it takes effect, we'll think the user started it again. Not
-clear how to fix this.
-
-* Redid ParseBoard8 to use sscanf. Code is a good deal cleaner now.
-Also, we now parse out the game number too, though we don't make much
-use of it yet.
-
-* (Tried to put in a feature that recognizes the current game in a
-game file must have ended when we see the start of a new one, but had
-to disable it because we have too many false hits with the current
-parser, especially in gnuchess listing files.)
-
-* Moves read from game files or received from ICS are now translated
-into canonical algebraic form just like all other moves. Minor nit:
-If you use Reset while playing or observing a game in ICS mode, the
-game history (including the current board position) is lost, so the
-next move of the current game can't be translated.
-
-* Default fonts now vary with board size, and are chosen by pixel size
-instead of point size (since piece bitmaps have a fixed pixel size).
-
-* Bug fix: An extra Forward was required to skip over time indications
-in game files.
-
-2.1 patchlevel 2 -- Fri Nov 27 23:30:00 PST 1992 -- mann@src.dec.com
-
-* If you move the comment popup, the next time it pops up it will come
-back where you put it.
-
-* Fixed yylexstr() so calls to it can be interspersed with calls to
-yylex().
-
-* Fixed bugs in Forward/Backward while in LoadGame mode.
-
-* Changed Save{Game,Position} functions to append instead of
-overwriting if file exists. Changed Load{Game,Position} functions to
-deal with multiple games/positions per file.
-
-* Changed load{Game,Position}File resources to trigger automatic load
-on program startup. Changed save{Game,Position}File resources to
-trigger automatic save after every completed game and on program exit.
-
-* Added autoSaveGames mode.
-
-* Fixed bug with PromoPiece in call to MakeAlg from MakeMove.
-
-* Many changes to get rid of picky compiler warnings and generally
-clean up the code.
-
-2.1 patchlevel 1 -- Fri Nov 27 02:45:00 PST 1992 -- mann@src.dec.com
-
-* A last minute change before patchlevel 0 went out broke
-ParseGameHistory(). Immediately fixed in patchlevel 1.
-
-Version 2.1, patchlevel 0 -- Fri Nov 27 02:00:00 PST 1992 -- mann@src.dec.com
-
-* Uses "time" command of gnuchess 4.0 and following to keep clocks in
-sync. Still works with older versions without this command; we test
-whether the command is present the first time each chess program is
-started.
-
-* File name dialog pops up under the mouse cursor, so that it's got the
-keyboard focus if the window manager is using pointer focus.
-
-* Attempts to move the wrong color piece or an empty square are filtered
-out in xboard instead of being passed on to GNU Chess or the ICS.
-
-* The reason a game ended is now remembered even if you move backward
-and forward after it ends. It is forgotten only if you make a move
-(which is as it should be---this is now a different game, which hasn't
-ended yet). The message saying why the game ended no longer wipes out
-the last move.
-
-* Pause, Backward, Forward, ForceMoves, and EditPosition now work from
-TwoMachines mode.
-
-* Forward and Backward now change only the board display unless you
-are in ForceMoves mode. Pause mode keeps new moves that are received
-from being displayed on your screen until you unpause (or use
-Forward).
-
-* Added option to ring the bell after opponents' moves.
-
-* Saved games that start from other than the standard initial position
-now begin with a postion diagram as in saved position files. The
-loader is modified to understand such save files.
-
-* Improved error checking when trying to read from gnuchess.
-
-* Parser understands things that look like time indications, e.g., (0:12),
-instead of popping them up as comments.
-
-* Send "quit" to gnuchess before trying to kill it. I needed this
-locally because sending a SIGTERM to rsh was not killing the remote
-program on some internal field test systems we have.
-
-* Merged in code from John Chanak to make xboard a front end to the
-Internet Chess Server, and added several improvements of my own.
-(These include parsing end of game messages, loading the current state
-and previous history of a game that's joined in progress via "watch"
-or "load", automatic switch from board style 1 to 8 when needed,
-removing irrelevant buttons and adding some new ones, adding the
-useTelnet and gateway resources, and miscellaneous code cleanup, bug
-fixes, and documentation.) Many thanks to John for writing and
-contributing the initial version of this code.
-
-Fri Oct 30 20:16:40 PST 1992 (patchlevel 25) mann@src.dec.com
-
-* gnuchess now castles by sending us "o-o" or "o-o-o" (starting with a
-preliminary version of 4.0.pl60 that I have). Added code to handle
-this. The old format ("e1g1", etc.) still works too.
-
-* Added code to format moves in normal abbreviated algebraic notation
-(for example, e4, exd4, f8Q, Nf6, 0-0) instead of coordinate algebraic
-(for example, e2e4, e3d4, f7f8q, g1f6, e1g1). Moves entered with the
-mouse or received from gnuchess are translated into this canonical
-format for display on the message line or in saved game files. Moves
-read from game files are not translated, however; they are shown and
-saved just as they appear in the file.
-
-Mon Sep 14 13:19:01 PDT 1992 (patchlevel 24) mann@src.dec.com
-
-* It's not really correct to write an ep capture of a pawn on e5 as
-fxe5; this should be written as fxe6 because e6 is where the capturing
-pawn ends up. Nevertheless, the parser now interprets fxe5 as the ep
-capture f5xe6 unless there is a pawn on f4, in which case it
-interprets fxe5 as f4xe5. By design, the parser does not flag fxe5 as
-ambiguous if there are pawns on both f4 and f5; instead it prefers the
-more legitimate f4xe5 interpretation.
-
-* A move like fxe6 or fe6 is now interpreted as an e.p. capture of the
-pawn on e5 if there is one there and e6 is empty.
-
-* A move like fxe5 can no longer be interpreted as an e.p. capture if
-there isn't a pawn on e5 to be captured. Previously this could happen
-erroneously if there were pawns on both f4 and f5, and the f4 pawn was
-capturing a piece on e5.
-
-* The parser no longer munges the move it is parsing; in the past, for
-instance, it would remove the "x" from a move like dxc5 before echoing
-it to the screen.
-
-* The parser now detects when a move is ambiguous and returns an
-error, instead of arbitrarily choosing one possibility as it used to.
-
-Mon Aug 10 18:40:47 PDT 1992 (patchlevel 23) mann@src.dec.com
-
-* Added code so parser can handle fully qualified algebraic, e.g.,
-Ng1-f3, N/g1-f3, Ng1f3, or even Pe2-e4. The "P" and "/" work only for
-fully qualified moves, not generally.
-
-* Fixed parser bugs in handling moves with rank or file disambiguator,
-e.g. N1f3 or Ngf3.
-
-Mon Jul 6 17:55:32 PDT 1992 (patchlevel 22) mann@src.dec.com
-
-* AIXV3 patch from Tom McConnell; thanks!
-
-* After hitting the Backward button we see the last move made instead of
-the word "Pausing" displayed.
-
-* Fixed a couple of problems compiling on IRIX; thanks to Michel Arsenault
-and Alan Walsh for bug reports and help with fixes.
-
-* Added optional display of algebraic notation coordinates along left and
-bottom edges of board. This was inspired by some code from Jean-Christophe
-Engel; thanks!
-
-* Swapped EditPosition and Pause buttons to reduce danger of losing the
-game in progress by hitting EditPosition when you wanted Backward. Thanks
-to Ove Lundberg for complaining (though it bugged me too).
-
-* Despite one complaint, I kept the feature of changing both clocks when black
-makes time control, rather than changing each individually when the player
-involved makes it. This is to avoid having it look like White is way ahead on
-time while Black is thinking about the last move of the time control period
-(i.e., when White has made time control but Black hasn't yet).
-
-* Corrected clocks to work as in real tournaments. Time is *added* when you
-reach time control; previously the clocks were simply set to the length of the
-new time period. Also, when a player's flag falls, he still does not receive a
-new time allotment at the next time control, but his opponent does still
-receive more time if *his* flag has not fallen; previously, time controls were
-ignored for both players if either flag fell. I forget who reported this bug.
-
-* Added a small ESIX patch from Kayvan Sylvan.
-
-Mon Jun 22 13:24:38 PDT 1992 (patchlevel 21)
-
-* Added some SVR4 support code from Ronald Cole. It's Greek to me.
-
-* Note: Stephen Meatheringham reports a bug causing xboard to crash with an
-error from the X server when run on a Sun Sparcstation IPX with Solaris 1.0.1
-and OpenWindows 3.0. This bug has not been tracked down yet.
-
-* Fixed a problem with ATTENTION code. On some operating systems, you have to
-be even more cautious about when you send a SIGINT to gnuchess, because the
-signal handler gets deinstalled each time it is used, and gnuchess does not
-reinstall it immediately. In particular, if you send a SIGINT and make an
-illegal move or ask for a hint, gnuchess does not reinstall the handler until
-*after* you make your next move, so you had better not send a SIGINT before
-it. I put a workaround for this case into xboard; I hope there are no more.
-
-* The ATTENTION code is needed when compiling for the ESIX operating system,
-because ESIX has a bug in the FIONREAD ioctl, so the code in gnuchess that
-lets you interrupt its thinking on your time just by typing in your move does
-not work. Perhaps gnuchess will have a workaround for this eventually.
-
-* Fixed yet another bug in using XtGetValues; code to inhibit shell resizing
-was leaving a null pointer as the place to store the current sizes.
-
-* Man page fixes: corrected -sl to -sd, updated LIMITATIONS.
-
-* Added include of <sys/time.h> conditional on HAS_GETTIMEOFDAY. I hope this
-works for all systems; on mine, <time.h> and <sys/time.h> are the same file,
-but with an #ifndef that makes it harmless to include both.
-
-* Thanks for reports from Mike Pearlman, Stephen Meatheringham, Kayvan Sylvan,
-d87-mal@nada.kth.se, Urban Koistinen, and Ronald Cole.
-
-Mon Jun 8 11:39:18 PDT 1992 (patchlevel 20)
-
-* Documented default value of initString and explained what it means. This
-paragraph also serves to inform the user that xboard by default will tell
-gnuchess to think on his time, something one user apparently didn't understand.
-
-* Redid -searchDepth (formerly -searchLevel) and -searchTime code. They both
-work correctly with gnuchess 3.1 now. The -searchDepth option also works
-correctly with gnuchess 4.0. The -searchTime option does not work with
-gnuchess 4.0.pl50 or earlier because those gnuchess versions do not have a
-working searchTime feature. Instead of having xboard do a half-baked emulation
-of this feature by using a 1-move time control, I will be submitting a patch to
-the gnuchess folks to restore the feature as in 3.1.
-
-* Improved clock code. It now keeps accurate track of fractional seconds of
-clock running time, if your system has gettimeofday(); if not, it is generous
-about them---if a clock has been going for n.m seconds when it is stopped, only
-n seconds are charged. Previously, xboard would charge one second immediately
-when a clock was started, so if n.m had elapsed when it was stopped, n+1 would
-be charged. This should stop xboard from thinking gnuchess has let its flag
-fall on the last move of a time control, as it often did before. I also
-improved the comments and condensed the code a little. I hope there are no
-systems that have X11 and don't have gettimeofday(), but if there are, I'd like
-to know what to replace it with.
-
-* "Flag dropped" messages now show up on the top line, beside the clocks, so
-that they don't obscure more interesting messages on the second line.
-
-* Fixed confusion of NULL and '\000'.
-
-* Include time.h to get ctime() declaration.
-
-* Fix to "parser bug" messages in parser.l.
-
-* Changed code to work with old-fashioned tolower() macro that requires its
-argument to be an uppercase letter.
-
-* Minor fix to some usage error messages; was reversing program name and
-erroneous argument value.
-
-* Thanks to Mitch Wright, Ranier Orth, and Henrik Schmiediche for bug reports.
-
-Fri Jun 5 13:06:08 PDT 1992 (patchlevel 19)
-
-* Note: xboard does not seem to work when compiled with gcc 1.40 on a
-PS/2 running AIX. It crashes at the sscanf in line 768 of xboard.c.
-You can work around the problem by specifying the -timeControl option
-on the command line instead of defaulting it. This does not seem to
-be a bug in xboard itself; it runs okay on PS/2 AIX when compiled with
-MetaWare High C. [But see 2.1.pl8 above.]
-
-* Removed bogus files from distribution.
-
-* Changed variable name that conflicted with SVR4 ttyname function.
-
-* Thanks to Christoph Strozyk for another bug report, and to several
-of the folks mentioned under patchlevel 18 for testing it.
-
-Wed Jun 3 17:50:37 PDT 1992 (patchlevel 18) mann@src.dec.com
-
-* Added an include of sys/fcntl.h to pick up the definition of O_RDWR for
-System V users.
-
-* Fixed a bug in CatchPipeSignal; was testing the wrong variable to determine
-which chess program got the signal.
-
-* Removed the declaration of strrchr, which was causing problems for some
-people, and added an include of <string.h> or <strings.h> instead.
-
-* Fixed a bunch of type errors uncovered by compilers that check more carefully
-than the one I was using. I just patched many of them with casts---typically
-cases where a function didn't take the same number and type of arguments as the
-X header file prototype said it should---because I didn't see any other way to
-fix them. The versions of the X header files that I have don't actually have
-full prototypes for these function types; they declare only the return type and
-just give the argument types as comments.
-
-* InitChessProgram returns immediately if chess program died while we were
-waiting for it to say "Chess". I think this fixes a bug that could make xboard
-crash or go into an infinite loop printing "Warning: Select failed; error code
-9" if gnuchess crashed there.
-
-* Removed code that tries to put the file descriptor used to read messages from
-gnuchess into nonblocking mode. I don't see how this kludge could ever have
-fixed the problem it was intended to (see below), and it seemed to be causing
-a race condition when we were waiting for the initial message from gnuchess
-after it started up.
-
-* Thanks to Richard K. Lloyd, Konstantinos Konstantinides, Mark
-Seiden, Erik Schoenfelder, Andreas Stolcke, Rainer Orth, Henry Thomas,
-and Kris Van Hees for bug reports and help with fixes.
-
-Sun May 31 08:00:00 PDT 1992 (patchlevel 17) Bart Massey bart@cs.uoregon.edu
-
-* 'xboard' suffered from a couple of serious bugs. An initial
- size needed to be given to the message label to avoid a race
- condition (!) so I gave it some text (otherwise occasionally
- xboard would fail because the message widget was 0 width under
- MIT X11R4). The sizes needed to be Dimension, not int, because
- they were written in by Xt -- on my big-endian box Dimension
- is short, causing chaos. Also, I hacked a declaration in for
- strrchr() to shut the compiler up -- this is arguably a kludge,
- but I couldn't think of anything better; maybe you can.
-
-Fri May 22 13:40:50 PDT 1992 (patchlevel 16)
-
-* First real (non-beta) release of version 2.0.
-
-* Added GNU copyleft notices covering version 2.0 enhancements.
-
-Thu May 21 12:30:09 PDT 1992 (patchlevel 15)
-
-* Removed -g from Imakefile.
-
-* Added casts in parser.l to prevent warning messages from some C compilers.
-
-* Bugfix: code to exit if chess program couldn't be started had bug in error
-message printing.
-
-Tue May 19 11:58:06 PDT 1992 (patchlevel 14)
-
-* Fixes one small bug: If LoadGame does not find a game in the file, it resets
-xboard to BeginningOfGame mode (as if the Reset button was pressed), instead of
-leaving it in LoadGame mode.
-
-Mon Apr 13 18:32:27 PDT 1992 (patchlevel 13)
-
-* Improved error handling when gnuchessx crashes. Now displays a message and
-enters EndOfGame state without exiting. This should help with diagnosing
-gnuchessx problems, since the position and move history are not lost.
-
-* Debugged man page.
-
-Version 2.0 -- Thu Apr 2 16:04:54 PST 1992 (patchlevel 12)
-
-* Added small 40x40 pixel pieces; board size now specified as small, medium, or
-large.
-
-* Improved man page.
-
-* Saved position files now say "white to play" if it's white's move.
-
-* SavePosition while in EditPosition mode now gets side to play right.
-
-* Made "Pawn" the default on edit position menus.
-
-* Removed "To play" from edit position menus, since it's redundant. Left
-support code in so it can be restored if someone wants it.
-
-* Tightened parser's definition of a move number to help further with skipping
-over stuff before the first move.
-
-* Put back feature of skipping over stuff in game files before a start of game
-marker. This helps when loading electronic mail messages that have things
-like "00" in the header (looks like castling), or that talk about moves in
-running text before the actual game score. Tradeoff is that we can't load
-games that don't either have move numbers or say "gnuchess game" just before
-the moves.
-
-* Changed version number to 2.0 because others have used "1.3" for patched
-versions of 1.2. Display version number and patchlevel at startup.
-
-Thu Apr 2 15:30:22 PST 1992 (patchlevel 11)
-
-* Bugfix: Match started with TwoMachines button now handles the case where
-user loaded/edited a position *and* forced some moves.
-
-* added error message feedback to many cases where the user's button press is
-rejected because it isn't allowed in the current mode.
-
-* added -noChessProgram option to allow use as a passive chessboard without
-gnuchess, replacing old buggy scheme that tried to do this automatically when
-gnuchess was not found on the search path.
-
-* parser accepts "=" in front of promotion piece
-
-* parser accepts ":" notation for captures
-
-* Removed SwitchSides button to make room for EditPosition. Left code in
-place, ifdef'ed out.
-
-* Added EditPosition mode.
-
-* Reformatted xboard.c to eliminate long lines.
-
-* Promotion popup now appears near rank to which pawn is moving.
-
-* UI improvement: Button representing current program mode is kept highlighted.
-
-* Bug fix: Hint button is now disabled when machine is on move.
-
-Sat Mar 21 14:57:37 PST 1992 (patchlevel 10)
-
-* TwoMachines now works from MachineWhite or MachineBlack mode.
-
-* ForceMoves and Backward/Forward now work at the end of a game started by
-the TwoMachines button.
-
-* Sends o-o or o-o-o to gnuchess instead of O-O or O-O-O.
-
-* Bug fix: SwitchSides button is now disabled when machine is on move.
-
-* Added -searchLevel option and corrected handling of -searchTime to reflect
-the way gnuchess really handles command line options instead of what the
-documentation used to say. Allowed optional ":seconds" on times.
-
-Tue Mar 17 21:00:35 PST 1992 (patchlevel 9)
-
-* Removed non-working code that tried to recover when a remote chess program
-could not be started; now exits with a message instead.
-
-* Now waits for chess program to print its first message (normally "Chess")
-before sending it any commands.
-
-* Matches between two machines can now start from a black-to-play position or
-black-to-play opening.
-
-* Bug fix: The program would sometimes lose track of the board position after
-the end of the game, drawing a board full of white pawns next time it got an X
-redraw request. Now fixed.
-
-* Cleaned up handling of illegal moves in game files. Now we just stop reading
-and go into ForceMoves mode.
-
-* Saved games against gnuchess indicate who won (restored and debugged 1.2
-feature).
-
-* Setting -searchTime turns off -clockMode.
-
-* My trick for setting up a black-to-play position broke in the transition from
-gnuchess 3.1+ to 4.0. Installed a different trick that works with both.
-
-* Bug fix: Was clobbering memory by mallocing one byte too few for the file
-name in SetupPositionFromFileProc. This usually resulted in a crash on the
-second attempt to load a position from a file.
-
-Mon Mar 16 16:49:00 PST 1992 (patchlevel 8)
-
-* Game and position files are saved in CHESSDIR as well as loaded from there.
-We actually chdir there, so gnuchess listing files also wind up there.
-
-* Underpromotion moves from gnuchess really work now.
-
-Sat Mar 14 14:55:24 PST 1992 (patchlevel 7)
-
-* Cleaned up lots of bugs and kruft in parser.l. It's a wonder the old version
-worked at all. Improved rejection of random text interspersed with the moves.
-
-* Relabeled buttons to reduce confusion (Play From File --> Load Game;
-Setup From File --> Load Position).
-
-* Bug fix: Doesn't exit at the end of a game that was started by pressing
-the Two Machines button.
-
-* Bug fix: Doesn't get confused if you hit Machine Black (White) while
-the machine is already playing black (white) and is on move.
-
-* Prints "White to play" or "Black to play" after loading a position file.
-
-* Bug fix: No longer hangs if no start-of-game is found in a game file.
-
-* More permissive about game file format. The first line is not assumed
-to be a header comment unless it starts with "#". We don't insist on
-seeing a "1." before the first move.
-
-* Added pop-up dialog to allow underpromotion. Moving a pawn to the last rank
-pops up a dialog to let you select what piece to promote to. Also added code
-to handle underpromotion moves from gnuchess or from a game file correctly.
-
-* Added code to send SIGINT to gnuchess before trying to move, if gnuchess
-might be busy thinking on our time. The code is ifdef'ed out because it's
-not necessary; only beta-test versions of gnuchess 4.0 at patchlevel 19 or
-below require it.
-
-Mon Feb 24 20:10:22 PST 1992
-
-* Merged fixes received from Stuart Cracraft and Mike McGann, to iconic mode
-and to ShutdownChessPrograms. (They both sent the same fixes.) I did not
-install the change that removed "easy" from the initialization string, so
-xboard will still try to run gnuchessx in "hard" mode (thinking on opponent's
-time) by sending this command.
-
-Version 1.3 -- Thu Dec 19 18:19:00 PST 1991 mann@src.dec.com (Tim Mann)
-
-* I've fixed a slew of bugs in this version of xboard:
-
-* You can now back up from the end of a game, even if gnuchess was playing
-against you. Since gnuchess exits upon mate or draw (when it's not in force
-mode), this feature works by starting up a new copy of gnuchess and feeding it
-the saved starting position and moves. Drawback: the new gnuchess starts with
-clocks at the beginning of a fresh time control.
-
-* Note: the "force" command to gnuchess is a toggle. I fixed a couple of
-places in xboard where "force" was being issued when gnuchess was already in
-force mode, which resulted in chaos. I don't know if they were in 1.2 or if I
-added them myself in the course of putting in new features.
-
-* ForceMoves button is now legal in PlayFromFile mode. It closes the game file
-and lets you force moves (or go Backward) from whatever position you've
-reached.
-
-* The parser now recognizes "white resigns" and "black resigns" (in any
-combination of upper and lower case, with or without the closing "s"). These
-used to be misinterpreted as meaning "white wins" or "black wins" respectively!
-Added "1 - 0" and "0 - 1" as equivalents for "1-0" and "0-1".
-
-* I removed the parser feature of considering "!" as a comment delimiter.
-"!" is so common as an annotation in game scores that this feature seems like a
-very bad idea.
-
-* Added a wait() after each successful call to kill() to avoid leaving zombie
-processes around.
-
-* Removed the feature of putting an indication of how the game ended into the
-internal move list when playing from a file. The only benefit of this was that
-it would get written back out if you saved the game at that point. This
-feature doesn't seem too useful, since you are unlikely to save a game you've
-read in without making any changes, and it caused a lot of problems in the code
-and user interface. The end condition is still displayed as a message when
-it's first read in from the file. End conditions when playing against gnuchess
-are still saved, too.
-
-* programName is now the last file name component of argv[0], not all of it.
-
-* xboard.c got three compiler warnings due to arguments that didn't match the
-prototypes on X toolkit functions. I got rid of them by adding some casts, and
-adding extra unused arguments to the ReceiveFromProgram function.
-
-* As I received it, the distribution had an extra period glued on the front
-of every line that started with a period. This broke the manpage in an
-obvious way. It also broke parser.l in a nonobvious way that stopped
-PlayFromFile from working.
-
-* Fixed one or two bugs where the clock was started when it shouldn't have
-been. I think one example was if you tried an illegal move in ForceMoves mode.
-
-* Now starts the machine's clock if you hit MachineWhite when white is on move
-or MachineBlack when black is on move.
-
-* Now updates the to-move indicator (highlight on clock display) when you use
-Forward/Backward or ForceMoves.
-
-* gnuchess 3.1+ has a bug that makes it try to think on its opponent's time
-even when in force mode; this causes problems with xboard's SetupFromFile
-command because gnuchess's saved hint move is garbage after the new position is
-loaded, so gnuchess prints out an error message when it tries to think about
-what it would do if the user made this move. Here is a patch for gnuchess
-3.1+:
-
-*** nondsp.c~ Mon Dec 2 12:37:18 1991
-*-- nondsp.c Thu Dec 19 15:36:06 1991
-***************
-*** 1223,1229 ****
- ok = flag.quit = false;
- player = opponent;
- ft = 0;
-! if (hint > 0 && !flag.easy && Book == NULL)
- {
- fflush (stdout);
- time0 = time ((long *) 0);
-*-- 1223,1229 ----
- ok = flag.quit = false;
- player = opponent;
- ft = 0;
-! if (hint > 0 && !flag.easy && Book == NULL && !flag.force)
- {
- fflush (stdout);
- time0 = time ((long *) 0);
-
-* SavePosition/SetupFromFile now handle black-to-play positions. If it's
-black's move in a saved position, the line "black to play" is printed
-immediately after the board display. Upon loading a position, xboard
-looks for "black" at the beginning of this line to determine whether it's
-black's move.
-
-* SetupFromFile now leaves you (effectively) in ForceMoves mode. This
-eliminates some cases where the program would get into an inconsistent state,
-and hopefully is more intuitive for the user, too.
-
-* PlayFromFile now leaves xboard in ForceMoves mode after playing through all
-the moves in the file. You can use Backward/Forward, make moves, get gnuchess
-to join the game with MachineWhite or MachineBlack, etc. The game file can end
-with either white or black to play.
-
-* Backward/Forward now seem to work in all cases. Previously using these
-buttons during a game against the machine would get xboard out of sync with
-gnuchess's idea of what was going on, resulting in chaos. Now using these
-buttons puts xboard in ForceMoves mode; you can bring gnuchess back into the
-game using the MachineWhite or MachineBlack buttons.
-
-Thu Jun 27 15:06:25 PDT 1991
-
- * Remove comment popups during a reset.
-
- * Bug in my forward move fix. Didn't work for playgamefile mode.
-
-Thu Jun 20 15:04:06 PDT 1991
-
- * Can't do a backward move when it isn't your turn because gnuchess
- gets confused. Similar bug for forward move. Do a bunch of moves,
- back up move and go forward. These get filtered out now.
-
- * Put easy back into the initString. This DISABLES easy mode which by
- default is ON.
-
- * New-line wasn't always being added to forward move strings sent to gnuchess.
-
- * Parsed comments as [ ..............text follows ] into d7d5
-
- * Long game file comments cause core dumps on HP systems.
-
- * Multiple uses of the get positition file button got confused
- on the second click.
-
- Minor fixes to the 1.2 release
-
-Version 1.2 -- Tue Jun 11 17:14:12 PDT 1991
-
- * For version 1.2, Jeff Kenton, Richard LLoyd, David Jensen, Martin D.,
- Bill Schmidt, Scott Hemphill, Paul Vaughan and Bill Shauck all found
- a lot of bugs that we put into xboard just to see if they were paying
- attention. They were.
-
- * Changed the protocol that xboard uses to work with the new version of
- gnuchessr.
-
- * There was an off by one error with the clock resetting code. Also,
- the clock display highliting was wrong. Thanks to Bill Shauck for
- pointing these out.
-
- * enumerations are not really integers on some C compilers. I added casting.
- The new version of gnuchess expects the "go" command for two machine
- and machine plays white cases. The whiteString resource is for
- compatibility with previous gnuchess versions which get confused by go.
- Thanks to Martin D. for catching these.
-
- * Fixed a bug where the checkmate message wasn't being parsed and xboard
- kept playing. Also, the message was added to game file if saved.
- Thanks to Scott Hemhill for pointing this out.
-
- * Fixed a bug where saving a long game, resetting and saving a short game
- resulted in appending the end of the long game to the short one.
- If a game is just being played out and there is no reason for gnuchessr
- to be used, ignore pipe signals. This allows people to use xboard
- as a chess board without need for gnuchess. Also, trivially bad moves
- such as e2e2 are not passed on to gnuchessr but instead ignored out of
- hand. This allows people using xboard as a chessboard with gnuchessr
- to pick a piece up, think and put it back down without making a move.
- Thanks to Jeff Kenton for pointing these out.
-
- * Added the CHESSDIR environment variable. Game and position files are
- found in this directory. If this variable is not declared, then the
- current directory is used. File names starting with / are treated
- specially.
-
- * The bitmap file names were changed so that none exceeded 14 characters.
- This is necessary for R5.
-
- fcntl(from_prog[0], F_SETFL, O_NDELAY);
-
- The xboard fix was to set non-blocking i/o on the read pipe for gnuchessr
-
- printz ("Illegal move (no matching move generated)");
-
- * fixed a problem with the new version of gnuchessr where xboard wasn't
- getting gnuchessr error messages for illegal moves. The problem seems
- to be fixed *without* any gnuchess changes but this is likely to be
- highly system dependant. There should be a new line on line 246 of nondsp.c
-
- * -iconic doesn't work at all now. The Iconify() does work. This was
- a tradeoff and it is really an Xt bug.
-
- * Fixed a compiler warning for gcc and an error for the IBM RT compilers.
- This is the VOID_PROC macro in xboard.h. Thanks to David Jensen for this.
-
- * If the DisplayWidth or DisplayHeight is less than 800, use small size mode.
- Also the size of the name widget was reduced from 500 pixels to 400 pixels
- because in small size mode there was a gap on the right.
- Changed the default font from fixed to helvetica_oblique14 to:
- **-helvetica-bold-r-normal--14-*-*-*-*-*-*-*
- helvetica_oblique14 is a font alias not on all R4 systems.
- Curiously enough, the 17 point is not available on 75dpi systems
- and the 18 point font is not available on 100dpi systems.
- Thanks to Richard K. Lloyd for pointing these out.
-
- * Minor hacks to work with R5 alpha. Had to add an event handler to the
- boardWidget to get translations to work. This may go away with the real R5.
- Added <Message>WM_PROTOCOLS: QuitProc() for R5 ICCCM compatibility.
-
- * If the first character of the file is not `1' then the first line
- of a game or position file is displayed as the name in a label widget.
-
- * Added a PopUp dialog for getting file names.
-
- * Modified the code to use the R4 routines rather than R3 compatibility.
-
- * Changed the forwards/backwards/readgamefile code to allow a play to step
- back and forth in a game. If he steps all the way to the beginning
- he has to restart the game. If he gets into a mated position, same problem.
-
- * Moved the man page from xboard.1 to xboard.man. This makes imake man page
- installs work correctly. Thanks to Richard K. Lloyd for pointing this out.
-
- * Corrected the queening code. XBoard was sending the wrong syntax.
- It was sending for example h8(Q) when it should have been sending h8q.
- Thanks to Paul Vaughan and Mike McGann for pointing this out.
-
- * Added a lex parser for algebraic games. It should be easy to use the parser
- for other interfaces such as the Microsoft Windows version.
- It parses comments of the form [anything] and ! to the end of a line.
-
-Version 1.1 -- Mon Jan 7 14:46:03 PST 1991
-
- * Roger Dubar, Konstantinos Konstantinides, Wolfgang S. Rupprecht,
- Paul Scowen, Mvh Smidt and Kayvan Sylvan all helped immensely during
- beta-testing.
-
- * older non-ANSI versions of Sun compilers complain vociferously.
-
- * the remoteShell resource was added for HP-UX systems
- and other systems where the remoteShell isn't rsh.
-
- * -iconic doesn't work properly. If XBoard is opened iconic then
- iconifying it later with a keystroke doesn't work. I think
- this is an Xt bug.
-
- * For systems with smaller screens, XBoard can use smaller pieces,
- and a smaller board. -bigSizeMode False uses a smaller set of pieces.
- These are scaled versions of the large pieces. They look ok but could
- be improved.
-
- * getlogin() does not work in all cases.
- It was replaced by getpwuid(getuid())->pw_name).
-
- * Warning messages get displayed in the message box.
-
- * Any button restarts a paused game.
-
- * Kayvan Sylvan contributed a patch for ESIX.
- It seems that select() on pipes is broken on his system. We declined
- to incorporate his patch though, because it was a work-around for
- something that was broken on one system, and selfishly, that system
- was not my own. Nevertheless, it is likely that other System V users
- on PC's may need to use this patch and it is is included as the file
- ESIX.patch. To use it, type
-
- patch xboard.c ESIX.patch
-
- * Fixed a bug where hint didn't work. The local version of gnuchessr
- had been hacked. The fix was to clone stderr and stdout for gnuchessr.
-
- * Fixed a bug where a player could play after a game had been finished.
-
- * The bitmaps have been pushed into the bitmaps directory.
-
- * Substantially rewrote and simplified the Two Machine code.
-
- * Added StrStr() because many systems don't have this ANSI function.
-
- * Added keyboard equivalents. Added an iconify keystroke, C or c.
-
- * Works with gcc now. gcc complained about casting float constants
- as XtPointers.
-
- * Detect if the visual doesn't support color. If so, run in monoMode.
- An alternative would be to detect grayscale visual and use a collection
- of gray user interface colors.
-
- XBoard.clockMode: False
-
- * For people who don't like or use chess clocks a clockMode switch
- has been added. It is on by default. It can be turned off in the
- .Xdefaults file with
-
- * Added a declaration, xtVersion, which will quickly break on R3 Intrinsics.
-
- * Hal Peterson contributed a new Imakefile.
-
- * Rewrote DisplayClocks().
-
- * Rewrote TwoMachinesPlay mode.
-
- * Konstantinos Konstantinides added the -searchTime option.
-
- * Substantially rewrote and simplified the clock code. If a game was paused
- and then resumed, the clocks were out of sync.
-
- * Fixed a bug in HandleUserMove() where the user could make a move while the
- machine was thinking. The fix detects and ignores these moves. onMove
- was not being used and was removed.
-
+++ /dev/null
-/*
- * backend.c -- Common back end for X and Windows NT versions of
- * XBoard $Id$
- *
- * 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.
- * ------------------------------------------------------------------------
- *
- * See the file ChangeLog for a revision history. */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <ctype.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <math.h>
-/* daniel */
-#include <windows.h>
-
-#if STDC_HEADERS
-# include <stdlib.h>
-# include <string.h>
-#else /* not STDC_HEADERS */
-# 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 HAVE_SYS_FCNTL_H
-# include <sys/fcntl.h>
-#else /* not HAVE_SYS_FCNTL_H */
-# if HAVE_FCNTL_H
-# include <fcntl.h>
-# endif /* HAVE_FCNTL_H */
-#endif /* not HAVE_SYS_FCNTL_H */
-
-#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
-
-#if defined(_amigados) && !defined(__GNUC__)
-struct timezone {
- int tz_minuteswest;
- int tz_dsttime;
-};
-extern int gettimeofday(struct timeval *, struct timezone *);
-#endif
-
-#if HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-#include "common.h"
-#include "frontend.h"
-#include "backend.h"
-#include "parser.h"
-#include "moves.h"
-#if ZIPPY
-# include "zippy.h"
-#endif
-#include "backendz.h"
-#include "winboard.h"
-
-/* A point in time */
-typedef struct {
- long sec; /* Assuming this is >= 32 bits */
- int ms; /* Assuming this is >= 16 bits */
-} TimeMark;
-
-int establish P((void));
-void read_from_player P((InputSourceRef isr, VOIDSTAR closure,
- char *buf, int count, int error));
-void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,
- char *buf, int count, int error));
-void SendToICS P((char *s));
-void SendToICSDelayed P((char *s, long msdelay));
-void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,
- int toX, int toY));
-void InitPosition P((int redraw));
-void HandleMachineMove P((char *message, ChessProgramState *cps));
-int AutoPlayOneMove P((void));
-int LoadGameOneMove P((ChessMove readAhead));
-int LoadGameFromFile P((char *filename, int n, char *title, int useList));
-int LoadPositionFromFile P((char *filename, int n, char *title));
-int SavePositionToFile P((char *filename));
-void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,
- Board board));
-void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));
-void ShowMove P((int fromX, int fromY, int toX, int toY));
-void FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
- /*char*/int promoChar));
-void BackwardInner P((int target));
-void ForwardInner P((int target));
-void GameEnds P((ChessMove result, char *resultDetails, int whosays));
-void EditPositionDone P((void));
-void PrintOpponents P((FILE *fp));
-void PrintPosition P((FILE *fp, int move));
-void StartChessProgram P((ChessProgramState *cps));
-void GuiCommand P((int command, int param));
-void IcsAnalyzeOutPut P((ChessProgramState *cps, int endThink));
-int SwitchGames P((void));
-void SendToProgram P((char *message, ChessProgramState *cps));
-void SendMoveToProgram P((int moveNum, ChessProgramState *cps));
-void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,
- char *buf, int count, int error));
-void SendTimeControl P((ChessProgramState *cps,
- int mps, long tc, int inc, int sd, int st));
-char *TimeControlTagValue P((void));
-void Attention P((ChessProgramState *cps));
-void FeedMovesToProgram P((ChessProgramState *cps, int upto));
-void ResurrectChessProgram P((void));
-void DisplayComment P((int moveNumber, char *text));
-void DisplayMove P((int moveNumber));
-void GetTimeMark P((TimeMark *));
-void ParseGameHistory P((char *game));
-void ParseBoard12 P((char *string));
-void StartClocks P((void));
-void SwitchClocks P((void));
-void StopClocks P((void));
-void ResetClocks P((void));
-char *PGNDate P((void));
-void SetGameInfo P((void));
-Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
-int RegisterMove P((void));
-void MakeRegisteredMove P((void));
-void TruncateGame P((void));
-int looking_at P((char *, int *, char *));
-void CopyPlayerNameIntoFileName P((char **, char *));
-char *SavePart P((char *));
-int SaveGameOldStyle P((FILE *));
-int SaveGamePGN P((FILE *));
-
-long SubtractTimeMarks P((TimeMark *, TimeMark *));
-int CheckFlags P((void));
-long NextTickLength P((long));
-void CheckTimeControl P((void));
-void show_bytes P((FILE *, char *, int));
-int string_to_rating P((char *str));
-void ParseFeatures P((char* args, ChessProgramState *cps));
-void InitBackEnd3 P((void));
-void FeatureDone P((ChessProgramState* cps, int val));
-void InitChessProgram P((ChessProgramState *cps));
-extern int tinyLayout, smallLayout;
-
-void ParseZippyP3 P((char* data, char* player));
-
-/* States for ics_getting_history */
-#define H_FALSE 0
-#define H_REQUESTED 1
-#define H_GOT_REQ_HEADER 2
-#define H_GOT_UNREQ_HEADER 3
-#define H_GETTING_MOVES 4
-#define H_GOT_UNWANTED_HEADER 5
-
-/* whosays values for GameEnds */
-#define GE_ICS 0
-#define GE_ENGINE 1
-#define GE_PLAYER 2
-#define GE_FILE 3
-#define GE_XBOARD 4
-
-/* Maximum number of games in a cmail message */
-#define CMAIL_MAX_GAMES 20
-
-/* Different types of move when calling RegisterMove */
-#define CMAIL_MOVE 0
-#define CMAIL_RESIGN 1
-#define CMAIL_DRAW 2
-#define CMAIL_ACCEPT 3
-
-/* Different types of result to remember for each game */
-#define CMAIL_NOT_RESULT 0
-#define CMAIL_OLD_RESULT 1
-#define CMAIL_NEW_RESULT 2
-
-/* Telnet protocol constants */
-#define TN_WILL 0373
-#define TN_WONT 0374
-#define TN_DO 0375
-#define TN_DONT 0376
-#define TN_IAC 0377
-#define TN_ECHO 0001
-#define TN_SGA 0003
-#define TN_PORT 23
-
-/* Fake up flags for now, as we aren't keeping track of castling
- availability yet */
-int
-PosFlags(index)
-{
- int flags = F_ALL_CASTLE_OK;
- if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;
- switch (gameInfo.variant) {
- case VariantSuicide:
- case VariantGiveaway:
- flags |= F_IGNORE_CHECK;
- flags &= ~F_ALL_CASTLE_OK;
- break;
- case VariantAtomic:
- flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;
- break;
- case VariantKriegspiel:
- flags |= F_KRIEGSPIEL_CAPTURE;
- break;
- case VariantNoCastle:
- flags &= ~F_ALL_CASTLE_OK;
- break;
- default:
- break;
- }
- return flags;
-}
-
-FILE *gameFileFP, *debugFP;
-
-char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
-char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
-char thinkOutput1[MSG_SIZ*10];
-
-/*ChessProgramState first, second; */
-
-/* premove variables */
-int premoveToX = 0;
-int premoveToY = 0;
-int premoveFromX = 0;
-int premoveFromY = 0;
-int premovePromoChar = 0;
-int gotPremove = 0;
-Boolean alarmSounded;
-/* end premove variables */
-
-#define ICS_GENERIC 0
-#define ICS_ICC 1
-#define ICS_FICS 2
-#define ICS_CHESSNET 3 /* not really supported */
-int ics_type = ICS_GENERIC;
-char *ics_prefix = "$";
-
-int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;
-int pauseExamForwardMostMove = 0;
-int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;
-int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];
-int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;
-int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;
-int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;
-int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;
-int whiteFlag = FALSE, blackFlag = FALSE;
-int userOfferedDraw = FALSE;
-int ics_user_moved = 0, ics_getting_history = H_FALSE, ics_gamenum = -1;
-int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;
-int cmailMoveType[CMAIL_MAX_GAMES];
-prefixHint = 0; /* PonderMove true/false */
-long ics_clock_paused = 0;
-ProcRef icsPR = NoProc, cmailPR = NoProc;
-InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;
-gameMode = BeginningOfGame;
-char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];
-char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];
-char white_holding[64], black_holding[64];
-TimeMark lastNodeCountTime;
-long lastNodeCount = 0;
-int have_sent_ICS_logon = 0;
-int movesPerSession;
-long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;
-long timeRemaining[2][MAX_MOVES];
-int matchGame = 0;
-TimeMark programStartTime;
-char ics_handle[MSG_SIZ];
-int have_set_title = 0;
-smartQueue icsQueue[max_gamenum];
-
-/* zippypassword3 */
-typedef struct {
- char string[512];
-} value[6];
-value command;
-
-/* Search stats from chessprogram */
-typedef struct {
- char movelist[MSG_SIZ]; /* Last PV we were sent */
- char ponderMove[12]; /* Current ponder move */
- int depth; /* Current search depth */
- int nr_moves; /* Total nr of root moves */
- int moves_left; /* Moves remaining to be searched */
- char move_name[MOVE_LEN]; /* Current move being searched, if provided */
- unsigned long nodes; /* # of nodes searched */
- int time; /* Search time (centiseconds) */
- int score; /* Score (centipawns) */
- int got_only_move; /* If last msg was "(only move)" */
- int got_fail; /* 0 - nothing, 1 - got "--", 2 - got "++" */
- int ok_to_send; /* handshaking between send & recv */
- int line_is_book; /* 1 if movelist is book moves */
- int seen_stat; /* 1 if we've seen the stat01: line */
- int GUI_time; /* Time from EngineRoom */
-} ChessProgramStats;
-
-static ChessProgramStats programStats;
-
-/* animateTraining preserves the state of appData.animate
- * when Training mode is activated. This allows the
- * response to be animated when appData.animate == TRUE and
- * appData.animateDragging == TRUE.
- */
-Boolean animateTraining;
-
-GameInfo gameInfo;
-
-AppData appData;
-
-Board boards[MAX_MOVES];
-Board initialPosition = {
- { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
- WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
- { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
- WhitePawn, WhitePawn, WhitePawn, WhitePawn },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
- BlackPawn, BlackPawn, BlackPawn, BlackPawn },
- { BlackRook, BlackKnight, BlackBishop, BlackQueen,
- BlackKing, BlackBishop, BlackKnight, BlackRook }
-};
-Board twoKingsPosition = {
- { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
- WhiteKing, WhiteKing, WhiteKnight, WhiteRook },
- { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
- WhitePawn, WhitePawn, WhitePawn, WhitePawn },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
- BlackPawn, BlackPawn, BlackPawn, BlackPawn },
- { BlackRook, BlackKnight, BlackBishop, BlackQueen,
- BlackKing, BlackKing, BlackKnight, BlackRook }
-};
-
-/* Convert str to a rating. Checks for special cases of "----",
- "++++", etc. Also strips ()'s */
-int
-string_to_rating(str)
- char *str;
-{
- while(*str && !isdigit(*str)) ++str;
- if (!*str)
- return 0; /* One of the special "no rating" cases */
- else
- return atoi(str);
-}
-
-void
-ClearProgramStats()
-{
- /* Init programStats */
- programStats.movelist[0] = NULLCHAR;
- programStats.ponderMove[0] = 0;
- programStats.depth = 0;
- programStats.nr_moves = 0;
- programStats.moves_left = 0;
- programStats.nodes = 0;
- programStats.time = 100;
- programStats.score = 0;
- programStats.got_only_move = 0;
- programStats.got_fail = 0;
- programStats.line_is_book = 0;
-}
-
-void
-InitBackEnd1()
-{
- int matched, min, sec;
-
- GetTimeMark(&programStartTime);
-
- ClearProgramStats();
- programStats.ok_to_send = 1;
- programStats.seen_stat = 0;
- supportStat = 0;
-
- /*
- * Initialize game list
- */
- ListNew(&gameList);
-
-
- /*
- * Internet chess server status
- */
- if (appData.icsActive) {
- appData.matchMode = FALSE;
- appData.matchGames = 0;
-#if ZIPPY
- appData.noChessProgram = !appData.zippyPlay;
-#else
- appData.zippyPlay = FALSE;
- appData.zippyTalk = FALSE;
- appData.noChessProgram = TRUE;
-#endif
- if (*appData.icsHelper != NULLCHAR) {
- appData.useTelnet = TRUE;
- appData.telnetProgram = appData.icsHelper;
- }
- } else {
- appData.zippyTalk = appData.zippyPlay = FALSE;
- }
-
- /*
- * Parse timeControl resource
- */
- if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
- appData.movesPerSession)) {
- char buf[MSG_SIZ];
- sprintf(buf, "bad timeControl option %s", appData.timeControl);
- DisplayFatalError(buf, 0, 2);
- }
-
- /*
- * Parse searchTime resource
- */
- if (*appData.searchTime != NULLCHAR) {
- matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
- if (matched == 1) {
- searchTime = min * 60;
- } else if (matched == 2) {
- searchTime = min * 60 + sec;
- } else {
- char buf[MSG_SIZ];
- sprintf(buf, "bad searchTime option %s", appData.searchTime);
- DisplayFatalError(buf, 0, 2);
- }
- }
-
- first.which = "first";
- second.which = "second";
- first.maybeThinking = second.maybeThinking = FALSE;
- first.pr = second.pr = NoProc;
- first.isr = second.isr = NULL;
- first.sendTime = second.sendTime = 2;
- first.sendDrawOffers = 1;
- if (appData.firstPlaysBlack) {
- first.twoMachinesColor = "black\n";
- second.twoMachinesColor = "white\n";
- } else {
- first.twoMachinesColor = "white\n";
- second.twoMachinesColor = "black\n";
- }
- first.program = appData.firstChessProgram;
- second.program = appData.secondChessProgram;
- first.host = appData.firstHost;
- second.host = appData.secondHost;
- first.dir = appData.firstDirectory;
- second.dir = appData.secondDirectory;
- first.other = &second;
- second.other = &first;
- first.initString = appData.initString;
- second.initString = appData.secondInitString;
- first.computerString = appData.firstComputerString;
- second.computerString = appData.secondComputerString;
- first.useSigint = second.useSigint = TRUE;
- first.useSigterm = second.useSigterm = TRUE;
- first.reuse = appData.reuseFirst;
- second.reuse = appData.reuseSecond;
- first.useSetboard = second.useSetboard = FALSE;
- first.useSAN = second.useSAN = FALSE;
- first.usePing = second.usePing = FALSE;
- first.lastPing = second.lastPing = 0;
- first.lastPong = second.lastPong = 0;
- first.usePlayother = second.usePlayother = FALSE;
- first.useColors = second.useColors = TRUE;
- first.useUsermove = second.useUsermove = FALSE;
- first.sendICS = second.sendICS = FALSE;
- first.sendName = second.sendName = appData.icsActive;
- first.sdKludge = second.sdKludge = FALSE;
- first.stKludge = second.stKludge = FALSE;
- TidyProgramName(first.program, first.host, first.tidy);
- TidyProgramName(second.program, second.host, second.tidy);
- first.matchWins = second.matchWins = 0;
- strcpy(first.variants, appData.variant);
- strcpy(second.variants, appData.variant);
- first.analysisSupport = second.analysisSupport = 2; /* detect */
- first.analyzing = second.analyzing = FALSE;
- first.initDone = second.initDone = FALSE;
-
- if (appData.firstProtocolVersion > PROTOVER ||
- appData.firstProtocolVersion < 1) {
- char buf[MSG_SIZ];
- sprintf(buf, "protocol version %d not supported",
- appData.firstProtocolVersion);
- DisplayFatalError(buf, 0, 2);
- } else {
- first.protocolVersion = appData.firstProtocolVersion;
- }
-
- if (appData.secondProtocolVersion > PROTOVER ||
- appData.secondProtocolVersion < 1) {
- char buf[MSG_SIZ];
- sprintf(buf, "protocol version %d not supported",
- appData.secondProtocolVersion);
- DisplayFatalError(buf, 0, 2);
- } else {
- second.protocolVersion = appData.secondProtocolVersion;
- }
-
- if (appData.icsActive) {
- appData.clockMode = TRUE; /* changes dynamically in ICS mode */
- } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
- appData.clockMode = FALSE;
- first.sendTime = second.sendTime = 0;
- }
-
-#if ZIPPY
- /* Override some settings from environment variables, for backward
- compatibility. Unfortunately it's not feasible to have the env
- vars just set defaults, at least in xboard. Ugh.
- */
- if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {
- ZippyInit();
- }
-#endif
-
- if (appData.noChessProgram) {
- programVersion = (char*) malloc(5 + strlen(PRODUCT) + strlen(VERSION)
- + strlen(PATCHLEVEL));
- sprintf(programVersion, "%s %s.%s", PRODUCT, VERSION, PATCHLEVEL);
- } else {
- char *p, *q;
- q = first.program;
- while (*q != ' ' && *q != NULLCHAR) q++;
- p = q;
- while (p > first.program && *(p-1) != '/') p--;
- programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)
- + strlen(PATCHLEVEL) + (q - p));
- sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);
- strncat(programVersion, p, q - p);
- }
-
- if (!appData.icsActive) {
- char buf[MSG_SIZ];
- /* Check for variants that are supported only in ICS mode,
- or not at all. Some that are accepted here nevertheless
- have bugs; see comments below.
- */
- VariantClass variant = StringToVariant(appData.variant);
- switch (variant) {
- case VariantBughouse: /* need four players and two boards */
- case VariantKriegspiel: /* need to hide pieces and move details */
- case VariantFischeRandom: /* castling doesn't work, shuffle not done */
- sprintf(buf, "Variant %s supported only in ICS mode", appData.variant);
- DisplayFatalError(buf, 0, 2);
- return;
-
- case VariantUnknown:
- case VariantLoadable:
- case Variant29:
- case Variant30:
- case Variant31:
- case Variant32:
- case Variant33:
- case Variant34:
- case Variant35:
- case Variant36:
- default:
- sprintf(buf, "Unknown variant name %s", appData.variant);
- DisplayFatalError(buf, 0, 2);
- return;
-
- case VariantNormal: /* definitely works! */
- case VariantWildCastle: /* pieces not automatically shuffled */
- case VariantNoCastle: /* pieces not automatically shuffled */
- case VariantCrazyhouse: /* holdings not shown,
- offboard interposition not understood */
- case VariantLosers: /* should work except for win condition,
- and doesn't know captures are mandatory */
- case VariantSuicide: /* should work except for win condition,
- and doesn't know captures are mandatory */
- case VariantGiveaway: /* should work except for win condition,
- and doesn't know captures are mandatory */
- case VariantTwoKings: /* should work */
- case VariantAtomic: /* should work except for win condition */
- case Variant3Check: /* should work except for win condition */
- case VariantShatranj: /* might work if TestLegality is off */
- break;
- }
- }
-}
-
-int
-ParseTimeControl(tc, ti, mps)
- char *tc;
- int ti;
- int mps;
-{
- int matched, min, sec;
-
- matched = sscanf(tc, "%d:%d", &min, &sec);
- if (matched == 1) {
- timeControl = min * 60 * 1000;
- } else if (matched == 2) {
- timeControl = (min * 60 + sec) * 1000;
- } else {
- return FALSE;
- }
-
- if (ti >= 0) {
- timeIncrement = ti * 1000; /* convert to ms */
- movesPerSession = 0;
- } else {
- timeIncrement = 0;
- movesPerSession = mps;
- }
- return TRUE;
-}
-
-void
-InitBackEnd2()
-{
- if (appData.debugMode) {
- fprintf(debugFP, "%s\n", programVersion);
- }
-
- if (appData.matchGames > 0) {
- appData.matchMode = TRUE;
- } else if (appData.matchMode) {
- appData.matchGames = 1;
- }
- Reset(TRUE, FALSE);
- if (appData.noChessProgram || first.protocolVersion == 1) {
- InitBackEnd3();
- } else {
- /* kludge: allow timeout for initial "feature" commands */
- FreezeUI();
- if (appData.icsActive) {
- DisplayMessage("", "ICS-Mode: Waiting for chessprogram...");
- } else {
- DisplayMessage("", "Starting chessprogram");
- }
- ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);
- }
-}
-
-void
-InitBackEnd3 P((void))
-{
- GameMode initialMode;
- char buf[MSG_SIZ];
- int err;
-
- InitChessProgram(&first);
-
- /* Make a console window if needed */
- if (appData.icsActive) {
- ConsoleCreate();
- }
- /* engine Room */
- supportStat = 0;
-
- if (appData.icsActive) {
- err = establish();
- if (err != 0) {
- if (*appData.icsCommPort != NULLCHAR) {
- sprintf(buf, "Could not open comm port %s",
- appData.icsCommPort);
- } else {
- sprintf(buf, "Could not connect to host %s, port %s",
- appData.icsHost, appData.icsPort);
- }
- DisplayFatalError(buf, err, 1);
- return;
- }
- SetICSMode();
- telnetISR =
- AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);
- fromUserISR =
- AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);
- } else if (appData.noChessProgram) {
- SetNCPMode();
- } else {
- SetGNUMode();
- }
-
- if (*appData.cmailGameName != NULLCHAR) {
- SetCmailMode();
- OpenLoopback(&cmailPR);
- cmailISR =
- AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);
- }
-
- ThawUI();
- DisplayMessage("", "");
- if (StrCaseCmp(appData.initialMode, "") == 0) {
- initialMode = BeginningOfGame;
- } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {
- initialMode = TwoMachinesPlay;
- } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {
- initialMode = AnalyzeFile;
- } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {
- initialMode = AnalyzeMode;
- } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {
- initialMode = MachinePlaysWhite;
- } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {
- initialMode = MachinePlaysBlack;
- } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {
- initialMode = EditGame;
- } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {
- initialMode = EditPosition;
- } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {
- initialMode = Training;
- } else {
- sprintf(buf, "Unknown initialMode %s", appData.initialMode);
- DisplayFatalError(buf, 0, 2);
- return;
- }
-
- if (appData.matchMode) {
- /* Set up machine vs. machine match */
- if (appData.noChessProgram) {
- DisplayFatalError("Can't have a match with no chess programs",
- 0, 2);
- return;
- }
- matchMode = TRUE;
- matchGame = 1;
- if (*appData.loadGameFile != NULLCHAR) {
- if (!LoadGameFromFile(appData.loadGameFile,
- appData.loadGameIndex,
- appData.loadGameFile, FALSE)) {
- DisplayFatalError("Bad game file", 0, 1);
- return;
- }
- } else if (*appData.loadPositionFile != NULLCHAR) {
- if (!LoadPositionFromFile(appData.loadPositionFile,
- appData.loadPositionIndex,
- appData.loadPositionFile)) {
- DisplayFatalError("Bad position file", 0, 1);
- return;
- }
- }
- TwoMachinesEvent();
- } else if (*appData.cmailGameName != NULLCHAR) {
- /* Set up cmail mode */
- ReloadCmailMsgEvent(TRUE);
- } else {
- /* Set up other modes */
- if (initialMode == AnalyzeFile) {
- if (*appData.loadGameFile == NULLCHAR) {
- DisplayFatalError("AnalyzeFile mode requires a game file", 0, 1);
- return;
- }
- }
- if (*appData.loadGameFile != NULLCHAR) {
- (void) LoadGameFromFile(appData.loadGameFile,
- appData.loadGameIndex,
- appData.loadGameFile, TRUE);
- } else if (*appData.loadPositionFile != NULLCHAR) {
- (void) LoadPositionFromFile(appData.loadPositionFile,
- appData.loadPositionIndex,
- appData.loadPositionFile);
- }
- if (initialMode == AnalyzeMode) {
- if (appData.noChessProgram) {
- DisplayFatalError("Analysis mode requires a chess engine", 0, 2);
- return;
- }
- if (appData.icsActive) {
- DisplayFatalError("Analysis mode does not work with ICS mode",0,2);
- return;
- }
- AnalyzeModeEvent();
- } else if (initialMode == AnalyzeFile) {
- ShowThinkingEvent(TRUE);
- AnalyzeFileEvent();
- AnalysisPeriodicEvent(1);
- } else if (initialMode == MachinePlaysWhite) {
- if (appData.noChessProgram) {
- DisplayFatalError("MachineWhite mode requires a chess engine",
- 0, 2);
- return;
- }
- if (appData.icsActive) {
- DisplayFatalError("MachineWhite mode does not work with ICS mode",
- 0, 2);
- return;
- }
- MachineWhiteEvent();
- } else if (initialMode == MachinePlaysBlack) {
- if (appData.noChessProgram) {
- DisplayFatalError("MachineBlack mode requires a chess engine",
- 0, 2);
- return;
- }
- if (appData.icsActive) {
- DisplayFatalError("MachineBlack mode does not work with ICS mode",
- 0, 2);
- return;
- }
- MachineBlackEvent();
- } else if (initialMode == TwoMachinesPlay) {
- if (appData.noChessProgram) {
- DisplayFatalError("TwoMachines mode requires a chess engine",
- 0, 2);
- return;
- }
- if (appData.icsActive) {
- DisplayFatalError("TwoMachines mode does not work with ICS mode",
- 0, 2);
- return;
- }
- TwoMachinesEvent();
- } else if (initialMode == EditGame) {
- EditGameEvent();
- } else if (initialMode == EditPosition) {
- EditPositionEvent();
- } else if (initialMode == Training) {
- if (*appData.loadGameFile == NULLCHAR) {
- DisplayFatalError("Training mode requires a game file", 0, 2);
- return;
- }
- TrainingEvent();
- }
- }
- /* If EngineRoom TRUE start */
- if (appData.AnalysisWindow && appData.showThinking &&
- !appData.noChessProgram) {
- if (appData.icsActive && appData.zippyPlay) {
- AnalysisPopUp(0,0,0,0,0,0,0,5);
- }
- if (!appData.icsActive) AnalysisPopUp(0,0,0,0,0,0,0,5);
- StartAnalysisClock();
- }
-}
-
-/*
- * Establish will establish a contact to a remote host.port.
- * Sets icsPR to a ProcRef for a process (or pseudo-process)
- * used to talk to the host.
- * Returns 0 if okay, error code if not.
- */
-int
-establish()
-{
- char buf[MSG_SIZ];
-
- if (*appData.icsCommPort != NULLCHAR) {
- /* Talk to the host through a serial comm port */
- return OpenCommPort(appData.icsCommPort, &icsPR);
-
- } else if (*appData.gateway != NULLCHAR) {
- if (*appData.remoteShell == NULLCHAR) {
- /* Use the rcmd protocol to run telnet program on a gateway host */
- sprintf(buf, "%s %s %s",
- appData.telnetProgram, appData.icsHost, appData.icsPort);
- return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);
-
- } else {
- /* Use the rsh program to run telnet program on a gateway host */
- if (*appData.remoteUser == NULLCHAR) {
- sprintf(buf, "%s %s %s %s %s", appData.remoteShell,
- appData.gateway, appData.telnetProgram,
- appData.icsHost, appData.icsPort);
- } else {
- sprintf(buf, "%s %s -l %s %s %s %s",
- appData.remoteShell, appData.gateway,
- appData.remoteUser, appData.telnetProgram,
- appData.icsHost, appData.icsPort);
- }
- return StartChildProcess(buf, "", &icsPR);
-
- }
- } else if (appData.useTelnet) {
- return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);
-
- } else {
- /* TCP socket interface differs somewhat between
- Unix and NT; handle details in the front end.
- */
- return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);
- }
-}
-
-void
-show_bytes(fp, buf, count)
- FILE *fp;
- char *buf;
- int count;
-{
- while (count--) {
- if (*buf < 040 || *(unsigned char *) buf > 0177) {
- fprintf(fp, "\\%03o", *buf & 0xff);
- } else {
- putc(*buf, fp);
- }
- buf++;
- }
- fflush(fp);
-}
-
-/* Returns an errno value */
-int
-OutputMaybeTelnet(pr, message, count, outError)
- ProcRef pr;
- char *message;
- int count;
- int *outError;
-{
- char buf[8192], *p, *q, *buflim;
- int left, newcount, outcount;
-
- if (*appData.icsCommPort != NULLCHAR || appData.useTelnet ||
- *appData.gateway != NULLCHAR) {
- if (appData.debugMode) {
- fprintf(debugFP, ">ICS: ");
- show_bytes(debugFP, message, count);
- fprintf(debugFP, "\n");
- }
- return OutputToProcess(pr, message, count, outError);
- }
-
- buflim = &buf[sizeof(buf)-1]; /* allow 1 byte for expanding last char */
- p = message;
- q = buf;
- left = count;
- newcount = 0;
- while (left) {
- if (q >= buflim) {
- if (appData.debugMode) {
- fprintf(debugFP, ">ICS: ");
- show_bytes(debugFP, buf, newcount);
- fprintf(debugFP, "\n");
- }
- outcount = OutputToProcess(pr, buf, newcount, outError);
- if (outcount < newcount) return -1; /* to be sure */
- q = buf;
- newcount = 0;
- }
- if (*p == '\n') {
- *q++ = '\r';
- newcount++;
- } else if (((unsigned char) *p) == TN_IAC) {
- *q++ = (char) TN_IAC;
- newcount ++;
- }
- *q++ = *p++;
- newcount++;
- left--;
- }
- if (appData.debugMode) {
- fprintf(debugFP, ">ICS: ");
- show_bytes(debugFP, buf, newcount);
- fprintf(debugFP, "\n");
- }
- outcount = OutputToProcess(pr, buf, newcount, outError);
- if (outcount < newcount) return -1; /* to be sure */
- return count;
-}
-
-void
-read_from_player(isr, closure, message, count, error)
- InputSourceRef isr;
- VOIDSTAR closure;
- char *message;
- int count;
- int error;
-{
- int outError, outCount;
- static int gotEof = 0;
-
- /* Pass data read from player on to ICS */
- if (count > 0) {
- gotEof = 0;
- outCount = OutputMaybeTelnet(icsPR, message, count, &outError);
- if (outCount < count) {
- DisplayFatalError("Error writing to ICS", outError, 1);
- }
- } else if (count < 0) {
- RemoveInputSource(isr);
- DisplayFatalError("Error reading from keyboard", error, 1);
- } else if (gotEof++ > 0) {
- RemoveInputSource(isr);
- DisplayFatalError("Got end of file from keyboard", 0, 0);
- }
-}
-
-void
-SendToICS(s)
- char *s;
-{
- int count, outCount, outError;
-
- if (icsPR == NULL) return;
-
- count = strlen(s);
- outCount = OutputMaybeTelnet(icsPR, s, count, &outError);
- if (outCount < count) {
- DisplayFatalError("Error writing to ICS", outError, 1);
- }
-}
-
-/* This is used for sending logon scripts to the ICS. Sending
- without a delay causes problems when using timestamp on ICC
- (at least on my machine). */
-void
-SendToICSDelayed(s,msdelay)
- char *s;
- long msdelay;
-{
- int count, outCount, outError;
-
- if (icsPR == NULL) return;
-
- count = strlen(s);
- if (appData.debugMode) {
- fprintf(debugFP, ">ICS: ");
- show_bytes(debugFP, s, count);
- fprintf(debugFP, "\n");
- }
- outCount = OutputToProcessDelayed(icsPR, s, count, &outError,
- msdelay);
- if (outCount < count) {
- DisplayFatalError("Error writing to ICS", outError, 1);
- }
-}
-
-
-/* Remove all highlighting escape sequences in s
- Also deletes any suffix starting with '('
- */
-char *
-StripHighlightAndTitle(s)
- char *s;
-{
- static char retbuf[MSG_SIZ];
- char *p = retbuf;
-
- while (*s != NULLCHAR) {
- while (*s == '\033') {
- while (*s != NULLCHAR && !isalpha(*s)) s++;
- if (*s != NULLCHAR) s++;
- }
- while (*s != NULLCHAR && *s != '\033') {
- if (*s == '(' || *s == '[') {
- *p = NULLCHAR;
- return retbuf;
- }
- *p++ = *s++;
- }
- }
- *p = NULLCHAR;
- return retbuf;
-}
-
-/* Remove all highlighting escape sequences in s */
-char *
-StripHighlight(s)
- char *s;
-{
- static char retbuf[MSG_SIZ];
- char *p = retbuf;
-
- while (*s != NULLCHAR) {
- while (*s == '\033') {
- while (*s != NULLCHAR && !isalpha(*s)) s++;
- if (*s != NULLCHAR) s++;
- }
- while (*s != NULLCHAR && *s != '\033') {
- *p++ = *s++;
- }
- }
- *p = NULLCHAR;
- return retbuf;
-}
-
-char *variantNames[] = VARIANT_NAMES;
-char *
-VariantName(v)
- VariantClass v;
-{
- return variantNames[v];
-}
-
-
-/* Identify a variant from the strings the chess servers use or the
- PGN Variant tag names we use. */
-VariantClass
-StringToVariant(e)
- char *e;
-{
- char *p;
- int wnum = -1;
- VariantClass v = VariantNormal;
- int i, found = FALSE;
- char buf[MSG_SIZ];
-
- if (!e) return v;
-
- for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {
- if (StrCaseStr(e, variantNames[i])) {
- v = (VariantClass) i;
- found = TRUE;
- break;
- }
- }
-
- if (!found) {
- if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))
- || StrCaseStr(e, "wild/fr")) {
- v = VariantFischeRandom;
- } else if ((i = 4, p = StrCaseStr(e, "wild")) ||
- (i = 1, p = StrCaseStr(e, "w"))) {
- p += i;
- while (*p && (isspace(*p) || *p == '(')) p++;
- if (isdigit(*p)) {
- wnum = atoi(p);
- } else {
- wnum = -1;
- }
- switch (wnum) {
- case 0: /* FICS only, actually */
- case 1:
- /* Castling legal even if K starts on d-file */
- v = VariantWildCastle;
- break;
- case 2:
- case 3:
- case 4:
- /* Castling illegal even if K & R happen to start in
- normal positions. */
- v = VariantNoCastle;
- break;
- case 5:
- case 7:
- case 8:
- case 10:
- case 11:
- case 12:
- case 13:
- case 14:
- case 15:
- case 18:
- case 19:
- /* Castling legal iff K & R start in normal positions */
- v = VariantNormal;
- break;
- case 6:
- case 20:
- case 21:
- /* Special wilds for position setup; unclear what to do here */
- v = VariantLoadable;
- break;
- case 9:
- /* Bizarre ICC game */
- v = VariantTwoKings;
- break;
- case 16:
- v = VariantKriegspiel;
- break;
- case 17:
- v = VariantLosers;
- break;
- case 22:
- v = VariantFischeRandom;
- break;
- case 23:
- v = VariantCrazyhouse;
- break;
- case 24:
- v = VariantBughouse;
- break;
- case 25:
- v = Variant3Check;
- break;
- case 26:
- /* Not quite the same as FICS suicide! */
- v = VariantGiveaway;
- break;
- case 27:
- v = VariantAtomic;
- break;
- case 28:
- v = VariantShatranj;
- break;
-
- /* Temporary names for future ICC types. The name *will* change in
- the next xboard/WinBoard release after ICC defines it. */
- case 29:
- v = Variant29;
- break;
- case 30:
- v = Variant30;
- break;
- case 31:
- v = Variant31;
- break;
- case 32:
- v = Variant32;
- break;
- case 33:
- v = Variant33;
- break;
- case 34:
- v = Variant34;
- break;
- case 35:
- v = Variant35;
- break;
- case 36:
- v = Variant36;
- break;
-
- case -1:
- /* Found "wild" or "w" in the string but no number;
- must assume it's normal chess. */
- v = VariantNormal;
- break;
- default:
- sprintf(buf, "Unknown wild type %d", wnum);
- DisplayError(buf, 0);
- v = VariantUnknown;
- break;
- }
- }
- }
- if (appData.debugMode) {
- fprintf(debugFP, "recognized '%s' (%d) as variant %s\n",
- e, wnum, VariantName(v));
- }
- return v;
-}
-
-static int leftover_start = 0, leftover_len = 0;
-char star_match[STAR_MATCH_N][MSG_SIZ];
-
-/* Test whether pattern is present at &buf[*index]; if so, return TRUE,
- advance *index beyond it, and set leftover_start to the new value of
- *index; else return FALSE. If pattern contains the character '*', it
- matches any sequence of characters not containing '\r', '\n', or the
- character following the '*' (if any), and the matched sequence(s) are
- copied into star_match.
- */
-int
-looking_at(buf, index, pattern)
- char *buf;
- int *index;
- char *pattern;
-{
- char *bufp = &buf[*index], *patternp = pattern;
- int star_count = 0;
- char *matchp = star_match[0];
-
- for (;;) {
- if (*patternp == NULLCHAR) {
- *index = leftover_start = bufp - buf;
- *matchp = NULLCHAR;
- return TRUE;
- }
- if (*bufp == NULLCHAR) return FALSE;
- if (*patternp == '*') {
- if (*bufp == *(patternp + 1)) {
- *matchp = NULLCHAR;
- matchp = star_match[++star_count];
- patternp += 2;
- bufp++;
- continue;
- } else if (*bufp == '\n' || *bufp == '\r') {
- patternp++;
- if (*patternp == NULLCHAR)
- continue;
- else
- return FALSE;
- } else {
- *matchp++ = *bufp++;
- continue;
- }
- }
- if (*patternp != *bufp) return FALSE;
- patternp++;
- bufp++;
- }
-}
-
-void
-SendToPlayer(data, length)
- char *data;
- int length;
-{
- int error, outCount;
- outCount = OutputToProcess(NoProc, data, length, &error);
- if (outCount < length) {
- DisplayFatalError("Error writing to display", error, 1);
- }
-}
-
-void
-PackHolding(packed, holding)
- char packed[];
- char *holding;
-{
- char *p = holding;
- char *q = packed;
- int runlength = 0;
- int curr = 9999;
- do {
- if (*p == curr) {
- runlength++;
- } else {
- switch (runlength) {
- case 0:
- break;
- case 1:
- *q++ = curr;
- break;
- case 2:
- *q++ = curr;
- *q++ = curr;
- break;
- default:
- sprintf(q, "%d", runlength);
- while (*q) q++;
- *q++ = curr;
- break;
- }
- runlength = 1;
- curr = *p;
- }
- } while (*p++);
- *q = NULLCHAR;
-}
-
-/* Telnet protocol requests from the front end */
-void
-TelnetRequest(ddww, option)
- unsigned char ddww, option;
-{
- unsigned char msg[3];
- int outCount, outError;
-
- if (*appData.icsCommPort != NULLCHAR || appData.useTelnet) return;
-
- if (appData.debugMode) {
- char buf1[8], buf2[8], *ddwwStr, *optionStr;
- switch (ddww) {
- case TN_DO:
- ddwwStr = "DO";
- break;
- case TN_DONT:
- ddwwStr = "DONT";
- break;
- case TN_WILL:
- ddwwStr = "WILL";
- break;
- case TN_WONT:
- ddwwStr = "WONT";
- break;
- default:
- ddwwStr = buf1;
- sprintf(buf1, "%d", ddww);
- break;
- }
- switch (option) {
- case TN_ECHO:
- optionStr = "ECHO";
- break;
- default:
- optionStr = buf2;
- sprintf(buf2, "%d", option);
- break;
- }
- fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);
- }
- msg[0] = TN_IAC;
- msg[1] = ddww;
- msg[2] = option;
- outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);
- if (outCount < 3) {
- DisplayFatalError("Error writing to ICS", outError, 1);
- }
-}
-
-void
-DoEcho()
-{
- if (!appData.icsActive) return;
- TelnetRequest(TN_DO, TN_ECHO);
-}
-
-void
-DontEcho()
-{
- if (!appData.icsActive) return;
- TelnetRequest(TN_DONT, TN_ECHO);
-}
-
-static int loggedOn = FALSE;
-
-/*-- Game start info cache: --*/
-int gs_gamenum;
-char gs_kind[MSG_SIZ];
-static char player1Name[128] = "";
-static char player2Name[128] = "";
-static int player1Rating = -1;
-static int player2Rating = -1;
-/*----------------------------*/
-
-
-/* ICC user send showinfo <game number> und we send latest pv */
-/* If username beginn with guest* send advertisement ;-) */
-void
-showInfo(data, player)
-char* data;
-char* player;
-{
- char *ptr = data;
- char temp[MSG_SIZ];
- char buf[MSG_SIZ];
- int i;
-
- /* this feature is only for ICC admins or chessprogramer */
- /* You must set commandline /icc to use this */
- if (appData.userVersion == TRUE) return;
- if (appData.ICC_feature == FALSE || ics_type != ICS_ICC) return;
- while (*ptr == ' ') ptr++;
- if (*ptr == 0 || (sizeof(ptr) > MSG_SIZ)) return;
- sscanf(ptr, "%s", buf);
-
- /* showgames command */
- if (strncmp(buf, "showgames", 9) == 0) {
- {
- int counter;
- for (counter = 1; counter <= max_gamenum; counter++) {
- if (icsQueue[counter].killPv > 0) {
- sprintf(temp, "tell %s I analyze game %d (%s/%s)\n", player, counter,
- icsQueue[counter].white, icsQueue[counter].black);
- SendToICS(temp);
- }
- }
- return;
- }
- }
- i = atoi(buf); /* security first - user could try to buffer overflow data ! */
- if (i > max_gamenum || icsQueue[i].killPv == 0) {
- sprintf(temp, "tell %s I don't analysis this game\n", player);
- SendToICS(temp);
- sprintf(temp, "tell %s \"tell %s showgames\" to see which games are currently analyzed.\n", player, ics_handle);
- SendToICS(temp);
- } else if (icsQueue[i].killPv > 0) {
- sprintf(temp, "tell %s Hello %s from Winboard-DM\n", player, player);
- SendToICS(temp);
- /* pos after "Depth " */
- if (icsQueue[i].lastpv[0]) {
- sprintf(temp, "tell %s Last analyze of game %d (%s/%s): (%s) Depth=%s\n",
- player, i, icsQueue[i].white, icsQueue[i].black, first.tidy, icsQueue[i].lastpv);
- } else {
- sprintf(temp, "tell %s Sorry, no analysis avaible from game %d at the moment. Please try it later again.\n",
- player, i);
- }
- SendToICS(temp);
- /* for ICC guest send member and register info */
- if (strncmp(player, "guest", 5) == 0) {
- sprintf(temp, "tell %s Be member of ICC. Get a free 7-days trial membership: \"http://www.chessclub.com/register/english.html\" For more information type: \"help reg\"\n", player);
- SendToICS(temp);
- }
- }
-}
-
-/* ICS-Analyze:
- Call this to see if we have a game where we not send a message
- for the current move
- */
-
-int
-SwitchGames()
-{
- int counter;
- char buf[32];
-
- for (counter = 1; counter <= max_gamenum; counter++) {
- if (icsQueue[counter].killPv > 0 && icsQueue[counter].CurrentMove >=
- icsQueue[counter].MessageMove && icsQueue[counter].flag == 0) {
- if (appData.debugMode) fprintf(debugFP, "ICS-Analyze: We switch to game %d \n", counter);
- sprintf(buf, "refresh %d \n", counter);
- SendToICS(buf);
- return 0;
- break;
- }
- }
- return 1;
-}
-
-/* parse and action from zippy password3 */
-void
-ParseZippyP3(data, player)
-char* data;
-char* player;
-{
- int counter = 0;
- int forward = 0;
- int i, j;
- char *ptr = data;
- char temp[MSG_SIZ];
-
- if (appData.userVersion == TRUE) return;
-
- for (;;) {
- if (counter > 6) {
- sprintf(temp, "tell %s Sorry, to many values. Max 6 values each line. Try again.\n", player);
- SendToICS(temp);
- sprintf(temp, "tell %s Or try send me \"help\"\n", player);
- SendToICS(temp);
- return;
- }
- while (*ptr == ' ') ptr++;
- if (*ptr == 0) break;
- sscanf(ptr, "%s", command[counter].string);
- if (appData.debugMode) fprintf(debugFP, "zp3: %s\n", command[counter].string);
- ptr = ptr + strlen(command[counter].string);
- counter++;
- }
- if(strncmp(command[forward].string, "help", 4) == 0) {
- /* icc don't accept text after new line :((( */
- /* So, we must write every line */
- sprintf(temp, "tell %s Hello %s from %s %s%s\n", player, player, PRODUCT, VERSION, PATCHLEVEL);
- SendToICS(temp);
- sprintf(temp, "tell %s analyze <observe|follow|unobserve|unfollow> <Playername|Gamenumber>\n", player);
- SendToICS(temp);
- sprintf(temp, "tell %s analyze <start|stop>\n", player);
- SendToICS(temp);
- sprintf(temp, "tell %s analyze output <whisper|kibitz|tell|none> if tell <value>\n", player);
- SendToICS(temp);
- sprintf(temp, "tell %s analyze killpv <value> <Gamenumber>\n", player);
- SendToICS(temp);
- sprintf(temp, "tell %s if you are setup killpv you enable it automaticly\n", player);
- SendToICS(temp);
- sprintf(temp, "tell %s analyze smartqueue <1|0>\n", player);
- SendToICS(temp);
- sprintf(temp, "tell %s analyze show <Gamenumber>\n", player);
- SendToICS(temp);
- sprintf(temp, "tell %s engine reset\n", player);
- SendToICS(temp);
- sprintf(temp, "tell %s <whisper|kibitz>\n", player);
- SendToICS(temp);
- return;
- }
-
- if (strncmp(command[forward].string, "engine", 6) == 0) {
- if (strncmp(command[forward+1].string, "reset", 5) == 0) {
- sprintf(temp, "tell %s reset engine...\n", player);
- SendToICS(temp);
- ResurrectChessProgram();
- }
- return;
- }
- /*
- if (strncmp(command[forward].string, "whisper", 7) == 0) {
- sprintf(temp, "tell %s GUI = whisper\n", player);
- SendToICS(temp);
- appData.SendOutPutToICS = 1;
- return;
- } else if (trncmp(command[forward].string, "kibitz", 6) == 0) {
- sprintf(temp, "tell %s GUI = kibitz\n", player);
- SendToICS(temp);
- appData.SendOutPutToICS = 2;
- return;
- }
- */
- if(gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) {
- sprintf(temp, "tell %s Sorry, i'm playing a game!\n", player);
- SendToICS(temp);
- return;
- }
- if (strncmp(command[forward].string, "analyze", 10) == 0) {
- if (strncmp(command[forward+1].string, "observe", 7) == 0 ||
- strncmp(command[forward+1].string, "follow", 6) == 0 ||
- strncmp(command[forward+1].string, "unobserve", 9) == 0 ||
- strncmp(command[forward+1].string, "unfollow", 8) == 0) {
-
- /* reset Queue if leave */
- if (strncmp(command[forward+1].string, "unobserve", 9) == 0 ||
- strncmp(command[forward+1].string, "unfollow", 8) == 0) ResetIcsQueue(ics_gamenum);
-
- /* secure */
- if(gameMode != IcsPlayingWhite || gameMode != IcsPlayingBlack) {
- sprintf(temp, "%s %s\n", command[forward+1].string,
- command[forward+2].string);
- SendToICS(temp);
- sprintf(temp, "tell %s action: %s %s\n", player, command[forward+1].string,
- command[forward+2].string);
- if (strncmp(command[forward+1].string, "observe", 7) == 0 ||
- strncmp(command[forward+1].string, "follow", 6) == 0) {
-
- if (ics_gamenum > max_gamenum) {
- sprintf(temp, "tell %s gamenumber to high\n", player);
- SendToICS(temp);
- return;
- }
- }
- } else {
- sprintf(temp, "tell %s Sorry, i'm playing\n", player);
- }
- SendToICS(temp);
- return;
- }
-
- if (strncmp(command[forward+1].string, "start", 5) == 0) {
- if (gameMode != IcsObserving) {
- sprintf(temp, "tell %s I must observe a game\n", player);
- } else if (gameMode == IcsObserving) {
- sprintf(temp, "tell %s Starting ICS analyze...\n", player);
- appData.icsAnalyze = TRUE;
- IcsAnalyze(TRUE);
- }
- SendToICS(temp);
- return;
- }
- if (strncmp(command[forward+1].string, "stop", 4) == 0) {
- if (gameMode != IcsObserving) {
- sprintf(temp, "tell %s I must observe a game\n", player);
- } else if (gameMode == IcsObserving) {
- sprintf(temp, "tell %s Stopping ICS analyze...\n", player);
- IcsAnalyze(FALSE);
- }
- SendToICS(temp);
- return;
- }
- if (strncmp(command[forward+1].string, "output", 6) == 0) {
- if(strncmp(command[forward+2].string, "whisper", 7) == 0) {
- appData.icsAnalyzeOutPut = 1;
- sprintf(temp, "tell %s analyze output is whisper\n", player);
- } else if (strncmp(command[forward+2].string, "kibitz", 6) == 0) {
- appData.icsAnalyzeOutPut = 2;
- sprintf(temp, "tell %s analyze output is kibitz\n", player);
- } else if (strncmp(command[forward+2].string, "tell", 4) == 0) {
- appData.icsAnalyzeOutPut = 3;
- if (command[forward+3].string[0] != NULLCHAR) {
- if (strncmp(command[forward+3].string, "none", 4) == 0) {
- appData.icsAnalyzeOutPut = 4;
- sprintf(temp, "tell %s analyze output is: none\n", player);
- SendToICS(temp);
- return;
- }
- strcpy(appData.icsTells, command[forward+3].string);
- sprintf(temp, "tell %s analyze output is tell: %s\n", player,
- appData.icsTells);
- } else {
- sprintf(temp, "tell %s Error: I don't know where i should send my analysis\n", player);
- appData.icsAnalyzeOutPut = 4;
- }
- }
- SendToICS(temp);
- }
- if (strncmp(command[forward+1].string, "killpv", 6) == 0) {
- i = atoi(command[forward+3].string);
- j = atoi(command[forward+2].string);
- if (i <= max_gamenum && i != 0) {
- if (icsQueue[i].killPv == 0) {
- sprintf(temp, "tell %s Error: Mh, killpv is zero. Wrong gamenumber?\n", player);
- SendToICS(temp);
- } else {
- if ( j > 20 || j < 1) {
- sprintf(temp, "tell %s Error: killpv must be 1-20\n", player);
- } else {
- sprintf(temp, "tell %s killpv for game %d is now %d\n", player, i, j);
- icsQueue[i].killPv = j;
- appData.icsEngineKillPV = TRUE;
- }
- SendToICS(temp);
- }
- } else {
- sprintf(temp, "tell %s Error: not possible gamenumber\n", player);
- SendToICS(temp);
- }
- return;
- }
- if (strncmp(command[forward+1].string, "smartqueue", 10) == 0) {
- j = atoi(command[forward+2].string);
- if (j == 0 || j == 1) {
- appData.smartQueue = j;
- sprintf(temp, "tell %s smartqueue is now: %d", player, appData.smartQueue);
- } else {
- sprintf(temp, "tell %s Error: not possible smartquere value\n", player);
- }
- SendToICS(temp);
- return;
- }
- if (strncmp(command[forward+1].string, "show", 4) == 0) {
- i = atoi(command[forward+2].string);
- if (i <= max_gamenum && i != 0) {
- if (icsQueue[i].killPv == 0) {
- sprintf(temp, "tell %s Error: emtpy game slot. Wrong gamenumber?\n", player);
- SendToICS(temp);
- } else {
- sprintf(temp, "tell %s summary information about game: %d\n", player, i);
- SendToICS(temp);
- sprintf(temp, "tell %s enable killpv: %d, killpv value = %d\n", player,
- appData.icsEngineKillPV, icsQueue[i].killPv);
- SendToICS(temp);
- sprintf(temp, "tell %s enable smartqueue (all games): %d\n", player, appData.smartQueue);
- SendToICS(temp);
- sprintf(temp, "tell %s analyze output (all games) is %d\n", player,
- appData.icsAnalyzeOutPut);
- SendToICS(temp);
- }
- } else {
- sprintf(temp, "tell %s Error: not possible gamenumber\n", player);
- SendToICS(temp);
- }
- return;
- }
- }
-}
-
-void
-read_from_ics(isr, closure, data, count, error)
- InputSourceRef isr;
- VOIDSTAR closure;
- char *data;
- int count;
- int error;
-{
-#define BUF_SIZE 8192
-#define STARTED_NONE 0
-#define STARTED_MOVES 1
-#define STARTED_BOARD 2
-#define STARTED_OBSERVE 3
-#define STARTED_HOLDINGS 4
-#define STARTED_CHATTER 5
-#define STARTED_COMMENT 6
-#define STARTED_MOVES_NOHIDE 7
-
- static int started = STARTED_NONE;
- static char parse[20000];
- static int parse_pos = 0;
- static char buf[BUF_SIZE + 1];
- static int firstTime = TRUE, intfSet = FALSE;
- static ColorClass curColor = ColorNormal;
- static ColorClass prevColor = ColorNormal;
- static int savingComment = FALSE;
- char str[512];
- int i, oldi;
- int buf_len;
- int next_out;
- int tkind;
- /* extra zippy vars */
- int save;
- char *q;
- char *p = 0;
- char *player;
- char reply[MSG_SIZ];
-
- if (count > 0) {
- /* If last read ended with a partial line that we couldn't parse,
- prepend it to the new read and try again. */
- if (leftover_len > 0) {
- for (i=0; i<leftover_len; i++)
- buf[i] = buf[leftover_start + i];
- }
-
- /* Copy in new characters, removing nulls and \r's */
- buf_len = leftover_len;
- for (i = 0; i < count; i++) {
- if (data[i] != NULLCHAR && data[i] != '\r')
- buf[buf_len++] = data[i];
- }
-
- buf[buf_len] = NULLCHAR;
- next_out = leftover_len;
- leftover_start = 0;
-
- i = 0;
- while (i < buf_len) {
- /* Deal with part of the TELNET option negotiation
- protocol. We refuse to do anything beyond the
- defaults, except that we allow the WILL ECHO option,
- which ICS uses to turn off password echoing when we are
- directly connected to it. We reject this option
- if localLineEditing mode is on (always on in xboard)
- and we are talking to port 23, which might be a real
- telnet server that will try to keep WILL ECHO on permanently.
- */
- if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {
- static int remoteEchoOption = FALSE; /* telnet ECHO option */
- unsigned char option;
- oldi = i;
- switch ((unsigned char) buf[++i]) {
- case TN_WILL:
- if (appData.debugMode)
- fprintf(debugFP, "\n<WILL ");
- switch (option = (unsigned char) buf[++i]) {
- case TN_ECHO:
- if (appData.debugMode)
- fprintf(debugFP, "ECHO ");
- /* Reply only if this is a change, according
- to the protocol rules. */
- if (remoteEchoOption) break;
- if (appData.localLineEditing &&
- atoi(appData.icsPort) == TN_PORT) {
- TelnetRequest(TN_DONT, TN_ECHO);
- } else {
- EchoOff();
- TelnetRequest(TN_DO, TN_ECHO);
- remoteEchoOption = TRUE;
- }
- break;
- default:
- if (appData.debugMode)
- fprintf(debugFP, "%d ", option);
- /* Whatever this is, we don't want it. */
- TelnetRequest(TN_DONT, option);
- break;
- }
- break;
- case TN_WONT:
- if (appData.debugMode)
- fprintf(debugFP, "\n<WONT ");
- switch (option = (unsigned char) buf[++i]) {
- case TN_ECHO:
- if (appData.debugMode)
- fprintf(debugFP, "ECHO ");
- /* Reply only if this is a change, according
- to the protocol rules. */
- if (!remoteEchoOption) break;
- EchoOn();
- TelnetRequest(TN_DONT, TN_ECHO);
- remoteEchoOption = FALSE;
- break;
- default:
- if (appData.debugMode)
- fprintf(debugFP, "%d ", (unsigned char) option);
- /* Whatever this is, it must already be turned
- off, because we never agree to turn on
- anything non-default, so according to the
- protocol rules, we don't reply. */
- break;
- }
- break;
- case TN_DO:
- if (appData.debugMode)
- fprintf(debugFP, "\n<DO ");
- switch (option = (unsigned char) buf[++i]) {
- default:
- /* Whatever this is, we refuse to do it. */
- if (appData.debugMode)
- fprintf(debugFP, "%d ", option);
- TelnetRequest(TN_WONT, option);
- break;
- }
- break;
- case TN_DONT:
- if (appData.debugMode)
- fprintf(debugFP, "\n<DONT ");
- switch (option = (unsigned char) buf[++i]) {
- default:
- if (appData.debugMode)
- fprintf(debugFP, "%d ", option);
- /* Whatever this is, we are already not doing
- it, because we never agree to do anything
- non-default, so according to the protocol
- rules, we don't reply. */
- break;
- }
- break;
- case TN_IAC:
- if (appData.debugMode)
- fprintf(debugFP, "\n<IAC ");
- /* Doubled IAC; pass it through */
- i--;
- break;
- default:
- if (appData.debugMode)
- fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);
- /* Drop all other telnet commands on the floor */
- break;
- }
- if (oldi > next_out)
- SendToPlayer(&buf[next_out], oldi - next_out);
- if (++i > next_out)
- next_out = i;
- continue;
- }
-
- /* OK, this at least will *usually* work */
- if (!loggedOn && looking_at(buf, &i, "ics%")) {
- loggedOn = TRUE;
- }
-
- if (loggedOn && !intfSet) {
- if (ics_type == ICS_ICC) {
- sprintf(str,
- "/set-quietly interface %s\n/set-quietly style 12\n",
- programVersion);
-
- } else if (ics_type == ICS_CHESSNET) {
- sprintf(str, "/style 12\n");
- } else {
- strcpy(str, "alias $ @\n$set interface ");
- strcat(str, programVersion);
- strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
-#ifdef WIN32
- strcat(str, "$iset nohighlight 1\n");
-#endif
- strcat(str, "$iset lock 1\n$style 12\n");
- }
- SendToICS(str);
- intfSet = TRUE;
- }
-
- if (started == STARTED_COMMENT) {
- /* Accumulate characters in comment */
- parse[parse_pos++] = buf[i];
- if (buf[i] == '\n') {
- parse[parse_pos] = NULLCHAR;
- AppendComment(forwardMostMove, StripHighlight(parse));
- started = STARTED_NONE;
- } else {
- /* Don't match patterns against characters in chatter */
- i++;
- continue;
- }
- }
- if (started == STARTED_CHATTER) {
- if (buf[i] != '\n') {
- /* Don't match patterns against characters in chatter */
- i++;
- continue;
- }
- started = STARTED_NONE;
- }
-
- /* Kludge to deal with rcmd protocol */
- if (firstTime && looking_at(buf, &i, "\001*")) {
- DisplayFatalError(&buf[1], 0, 1);
- continue;
- } else {
- firstTime = FALSE;
- }
-
- if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {
- ics_type = ICS_ICC;
- ics_prefix = "/";
- if (appData.debugMode)
- fprintf(debugFP, "ics_type %d\n", ics_type);
- continue;
- }
- if (!loggedOn && looking_at(buf, &i, "freechess.org")) {
- ics_type = ICS_FICS;
- ics_prefix = "$";
- if (appData.debugMode)
- fprintf(debugFP, "ics_type %d\n", ics_type);
- continue;
- }
- if (!loggedOn && looking_at(buf, &i, "chess.net")) {
- ics_type = ICS_CHESSNET;
- ics_prefix = "/";
- if (appData.debugMode)
- fprintf(debugFP, "ics_type %d\n", ics_type);
- continue;
- }
-
- if (!loggedOn &&
- (looking_at(buf, &i, "\"*\" is *a registered name") ||
- looking_at(buf, &i, "Logging you in as \"*\"") ||
- looking_at(buf, &i, "will be \"*\""))) {
- strcpy(ics_handle, star_match[0]);
- continue;
- }
-
- if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {
- char buf[MSG_SIZ];
- sprintf(buf, "%s@%s", ics_handle, appData.icsHost);
- DisplayIcsInteractionTitle(buf);
- have_set_title = TRUE;
- }
-
- /* skip finger notes */
- if (started == STARTED_NONE &&
- ((buf[i] == ' ' && isdigit(buf[i+1])) ||
- (buf[i] == '1' && buf[i+1] == '0')) &&
- buf[i+2] == ':' && buf[i+3] == ' ') {
- started = STARTED_CHATTER;
- i += 3;
- continue;
- }
-
- /* skip formula vars */
- if (started == STARTED_NONE &&
- buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {
- started = STARTED_CHATTER;
- i += 3;
- continue;
- }
- oldi = i;
-
- /* save position of char array pointer for zippy */
- save = i;
- /* Try found special things that never works with color */
- /* I really don't know why - code is ok. */
- q = p;
-
- if (appData.zippyTalk || appData.zippyPlay) {
- if (looking_at(buf, &save, "* tells you: *") ||
- looking_at(buf, &save, "* says: *")) {
- player = StripHighlightAndTitle(star_match[0]);
- if (appData.zippyPassword[0] != NULLCHAR &&
- strncmp(star_match[1], appData.zippyPassword,
- strlen(appData.zippyPassword)) == 0) {
- q = star_match[1] + strlen(appData.zippyPassword);
- while (*q == ' ') q++;
- fprintf(debugFP, "zippy 1 %s \n", q);
- SendToICS(q);
- SendToICS("\n");
- } else if(appData.zippyPassword2[0] != NULLCHAR && first.initDone &&
- strncmp(star_match[1], appData.zippyPassword2,
- strlen(appData.zippyPassword2)) == 0) {
- fprintf(debugFP, "zippy2vor: %s \n", q);
- q = star_match[1] + strlen(appData.zippyPassword2);
- while (*q == ' ') q++;
- SendToProgram(q, &first);
- SendToProgram("\n", &first);
-
- } else if (appData.zippyPassword3[0] != NULLCHAR && first.initDone &&
- strncmp(star_match[1], appData.zippyPassword3,
- strlen(appData.zippyPassword3)) == 0 &&
- appData.userVersion == FALSE) {
- q = star_match[1] + strlen(appData.zippyPassword3);
- ParseZippyP3(q, player);
-
- } else if (strncmp(star_match[1], "showinfo", 8) == 0 &&
- appData.userVersion == FALSE && appData.icsAnalyze == TRUE &&
- appData.icsAnalyzeOutPut == 3 && appData.ICC_feature == TRUE) {
- q = star_match[1] + strlen("showinfo");
- showInfo(q, player);
- } else if (strncmp(star_match[1], "showgames", 9) == 0 &&
- appData.userVersion == FALSE && appData.icsAnalyze == TRUE &&
- appData.icsAnalyzeOutPut == 3 && appData.ICC_feature == TRUE) {
- q = star_match[1];
- showInfo(q, player);
-
- } else if (appData.zippyWrongPassword[0] != NULLCHAR &&
- strncmp(star_match[1], appData.zippyWrongPassword,
- strlen(appData.zippyWrongPassword)) == 0) {
- q = star_match[1] + strlen(appData.zippyWrongPassword);
- while (*q == ' ') q++;
- sprintf(reply, "wrong %s\n", player);
- SendToICS(reply);
- }
- }
- /* workaround for icc and freechess.org */
- if (looking_at(buf, &save, "Your opponent offers you a draw") ||
- looking_at(buf, &save, "offers you a draw") ||
- looking_at(buf, &save, "* offers you a draw")) {
- if (first.sendDrawOffers && first.initDone) {
- SendToProgram("draw\n", &first);
- /* Handling zippy Draw */
- } else if (appData.zippyDraw && first.initDone) {
- //ZippyDraw(1, programStats.score, programStats.depth);
- }
- }
- if (appData.zippyPlay && first.initDone && loggedOn == TRUE) ZippyMatch(buf, &save, player);
- }
-
- /* Make color for all and for zippy */
- if (/* Don't color "message" or "messages" output */
- (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
- looking_at(buf, &i, "*. * at *:*: ") ||
- looking_at(buf, &i, "--* (*:*): ") ||
- /* Regular tells and says */
- (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||
- looking_at(buf, &i, "* (your partner) tells you: ") ||
- looking_at(buf, &i, "* says: ") ||
- /* Message notifications (same color as tells) */
- looking_at(buf, &i, "* has left a message ") ||
- looking_at(buf, &i, "* just sent you a message:\n") ||
- /* Whispers and kibitzes */
- (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||
- looking_at(buf, &i, "* kibitzes: ") ||
- /* Channel tells */
- (tkind = 3, looking_at(buf, &i, "*(*: "))) {
-
- if (tkind == 1 && strchr(star_match[0], ':')) {
- /* Avoid "tells you:" spoofs in channels */
- tkind = 3;
- }
- if (star_match[0][0] == NULLCHAR ||
- strchr(star_match[0], ' ') ||
- (tkind == 3 && strchr(star_match[1], ' '))) {
- /* Reject bogus matches */
- i = oldi;
- } else {
- if (appData.colorize) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- switch (tkind) {
- case 1:
- Colorize(ColorTell, FALSE);
- curColor = ColorTell;
- break;
- case 2:
- Colorize(ColorKibitz, FALSE);
- curColor = ColorKibitz;
- break;
- case 3:
- p = strrchr(star_match[1], '(');
- if (p == NULL) {
- p = star_match[1];
- } else {
- p++;
- }
- if (atoi(p) == 1) {
- Colorize(ColorChannel1, FALSE);
- curColor = ColorChannel1;
- } else {
- Colorize(ColorChannel, FALSE);
- curColor = ColorChannel;
- }
- break;
- case 5:
- curColor = ColorNormal;
- break;
- }
- }
- if (started == STARTED_NONE && appData.autoComment &&
- (gameMode == IcsObserving ||
- gameMode == IcsPlayingWhite ||
- gameMode == IcsPlayingBlack)) {
- parse_pos = i - oldi;
- memcpy(parse, &buf[oldi], parse_pos);
- parse[parse_pos] = NULLCHAR;
- started = STARTED_COMMENT;
- savingComment = TRUE;
- } else {
- started = STARTED_CHATTER;
- savingComment = FALSE;
- }
- loggedOn = TRUE;
- continue;
- }
- }
-
- if (looking_at(buf, &i, "* s-shouts: ") ||
- looking_at(buf, &i, "* c-shouts: ")) {
- if (appData.colorize) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- Colorize(ColorSShout, FALSE);
- curColor = ColorSShout;
- }
- loggedOn = TRUE;
- started = STARTED_CHATTER;
- continue;
- }
-
- if (looking_at(buf, &i, "--->")) {
- loggedOn = TRUE;
- continue;
- }
-
- if (looking_at(buf, &i, "* shouts: ") ||
- looking_at(buf, &i, "--> ")) {
- if (appData.colorize) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- Colorize(ColorShout, FALSE);
- curColor = ColorShout;
- }
- loggedOn = TRUE;
- started = STARTED_CHATTER;
- continue;
- }
-
- if (looking_at( buf, &i, "Challenge:")) {
- if (appData.colorize) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- Colorize(ColorChallenge, FALSE);
- curColor = ColorChallenge;
- }
- loggedOn = TRUE;
- continue;
- }
-
- if (looking_at(buf, &i, "* offers you") ||
- looking_at(buf, &i, "* offers to be") ||
- looking_at(buf, &i, "* would like to") ||
- looking_at(buf, &i, "* requests to") ||
- looking_at(buf, &i, "Your opponent offers") ||
- looking_at(buf, &i, "Your opponent requests")) {
-
- if (appData.colorize) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- Colorize(ColorRequest, FALSE);
- curColor = ColorRequest;
- }
- continue;
- }
-
- if (looking_at(buf, &i, "* (*) seeking")) {
- if (appData.colorize) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- Colorize(ColorSeek, FALSE);
- curColor = ColorSeek;
- }
- continue;
- }
-
-
- if (looking_at(buf, &i, "\\ ")) {
- if (prevColor != ColorNormal) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- Colorize(prevColor, TRUE);
- curColor = prevColor;
- }
- if (savingComment) {
- parse_pos = i - oldi;
- memcpy(parse, &buf[oldi], parse_pos);
- parse[parse_pos] = NULLCHAR;
- started = STARTED_COMMENT;
- } else {
- started = STARTED_CHATTER;
- }
- continue;
- }
-
- if (looking_at(buf, &i, "Black Strength :") ||
- looking_at(buf, &i, "<<< style 10 board >>>") ||
- looking_at(buf, &i, "<10>") ||
- looking_at(buf, &i, "#@#")) {
- /* Wrong board style */
- loggedOn = TRUE;
- SendToICS(ics_prefix);
- SendToICS("set style 12\n");
- SendToICS(ics_prefix);
- SendToICS("refresh\n");
- continue;
- }
-
- if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
- ICSInitScript();
- have_sent_ICS_logon = 1;
- continue;
- }
-
- if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ &&
- (looking_at(buf, &i, "\n<12> ") ||
- looking_at(buf, &i, "<12> "))) {
- loggedOn = TRUE;
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- }
- next_out = i;
- started = STARTED_BOARD;
- parse_pos = 0;
- continue;
- }
-
- if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||
- looking_at(buf, &i, "<b1> ")) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- }
- next_out = i;
- started = STARTED_HOLDINGS;
- parse_pos = 0;
- continue;
- }
-
- /* Send buf now to zippy */
- if (appData.zippyTalk || appData.zippyPlay) {
-#if ZIPPY
- if (ZippyControl(buf, &save) || ZippyConverse(buf, &save)) {
- loggedOn = TRUE;
- continue;
- }
-#endif
- }
-
- /* ICS: init icsQueue */
- if (appData.zippyPlay && first.initDone && gameMode == IcsObserving) {
- if (ics_gamenum > max_gamenum || ics_gamenum == -1) {
- if (appData.debugMode) fprintf(debugFP, "To high gamenumber or gamenumber -1 !\n");
- return;
- }
- if (icsQueue[ics_gamenum].killPv == 0) {
- icsQueue[ics_gamenum].move = currentMove;
- icsQueue[ics_gamenum].killPv = appData.icsKillPVs;
- icsQueue[ics_gamenum].counter = 0;
- strcpy(icsQueue[ics_gamenum].white, gameInfo.white);
- strcpy(icsQueue[ics_gamenum].black, gameInfo.black);
- }
- }
-
- if (looking_at(buf, &i, "* *vs. * *--- *")) {
- loggedOn = TRUE;
- /* Header for a move list -- first line */
- switch (ics_getting_history) {
- case H_FALSE:
- switch (gameMode) {
- case IcsIdle:
- case BeginningOfGame:
- /* User typed "moves" or "oldmoves" while we
- were idle. Pretend we asked for these
- moves and soak them up so user can step
- through them and/or save them.
- */
- Reset(FALSE, TRUE);
- gameMode = IcsObserving;
- ModeHighlight();
- ics_gamenum = -1;
- ics_getting_history = H_GOT_UNREQ_HEADER;
- break;
- case EditGame: /*?*/
- case EditPosition: /*?*/
- /* Should above feature work in these modes too? */
- /* For now it doesn't */
- ics_getting_history = H_GOT_UNWANTED_HEADER;
- break;
- default:
- ics_getting_history = H_GOT_UNWANTED_HEADER;
- break;
- }
- break;
- case H_REQUESTED:
- /* Is this the right one? */
- if (gameInfo.white && gameInfo.black &&
- strcmp(gameInfo.white, star_match[0]) == 0 &&
- strcmp(gameInfo.black, star_match[2]) == 0) {
- /* All is well */
- ics_getting_history = H_GOT_REQ_HEADER;
- }
- break;
- case H_GOT_REQ_HEADER:
- case H_GOT_UNREQ_HEADER:
- case H_GOT_UNWANTED_HEADER:
- case H_GETTING_MOVES:
- /* Should not happen */
- DisplayError("Error gathering move list: two headers", 0);
- ics_getting_history = H_FALSE;
- break;
- }
-
- /* Save player ratings into gameInfo if needed */
- if ((ics_getting_history == H_GOT_REQ_HEADER ||
- ics_getting_history == H_GOT_UNREQ_HEADER) &&
- (gameInfo.whiteRating == -1 ||
- gameInfo.blackRating == -1)) {
-
- gameInfo.whiteRating = string_to_rating(star_match[1]);
- gameInfo.blackRating = string_to_rating(star_match[3]);
- if (appData.debugMode)
- fprintf(debugFP, "Ratings from header: W %d, B %d\n",
- gameInfo.whiteRating, gameInfo.blackRating);
- }
- continue;
- }
-
- if (looking_at(buf, &i,
- "* * match, initial time: * minute*, increment: * second")) {
- /* Header for a move list -- second line */
- /* Initial board will follow if this is a wild game */
-
- if (gameInfo.event != NULL) free(gameInfo.event);
- sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);
- gameInfo.event = StrSave(str);
- gameInfo.variant = StringToVariant(gameInfo.event);
- continue;
- }
-
- if (looking_at(buf, &i, "Move ")) {
- /* Beginning of a move list */
- switch (ics_getting_history) {
- case H_FALSE:
- /* Normally should not happen */
- /* Maybe user hit reset while we were parsing */
- break;
- case H_REQUESTED:
- /* Happens if we are ignoring a move list that is not
- * the one we just requested. Common if the user
- * tries to observe two games without turning off
- * getMoveList */
- break;
- case H_GETTING_MOVES:
- /* Should not happen */
- DisplayError("Error gathering move list: nested", 0);
- ics_getting_history = H_FALSE;
- break;
- case H_GOT_REQ_HEADER:
- ics_getting_history = H_GETTING_MOVES;
- started = STARTED_MOVES;
- parse_pos = 0;
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- }
- break;
- case H_GOT_UNREQ_HEADER:
- ics_getting_history = H_GETTING_MOVES;
- started = STARTED_MOVES_NOHIDE;
- parse_pos = 0;
- break;
- case H_GOT_UNWANTED_HEADER:
- ics_getting_history = H_FALSE;
- break;
- }
- continue;
- }
-
- if (looking_at(buf, &i, "% ") ||
- ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
- && looking_at(buf, &i, "}*"))) {
- savingComment = FALSE;
- switch (started) {
- case STARTED_MOVES:
- case STARTED_MOVES_NOHIDE:
- memcpy(&parse[parse_pos], &buf[oldi], i - oldi);
- parse[parse_pos + i - oldi] = NULLCHAR;
- ParseGameHistory(parse);
-#if ZIPPY
- if (appData.zippyPlay && first.initDone) {
- if (appData.icsAnalyze && gameMode == IcsObserving) {
- IcsAnalyze(TRUE);
- }
- /* icsAnalyze send the moves to engine if we start new */
- if (!appData.icsAnalyze) FeedMovesToProgram(&first, forwardMostMove);
- /* Bug 4.2.6: Engine want play, skip that */
- if (gameMode == IcsExamining) {
- /* set idle mode */
- SendToProgram("force\n", &first);
- }
- if (gameMode == IcsPlayingWhite) {
- if (appData.icsAnalyze) {
- IcsAnalyze(FALSE);
- appData.icsAnalyze = FALSE;
-
- }
- if (WhiteOnMove(forwardMostMove)) {
- if (first.sendTime) {
- if (first.useColors) {
- SendToProgram("black\n", &first);
- }
- SendTimeRemaining(&first, TRUE);
- }
- if (first.useColors) {
- SendToProgram("white\ngo\n", &first);
- } else {
- SendToProgram("go\n", &first);
- }
- first.maybeThinking = TRUE;
- } else {
- if (first.usePlayother) {
- if (first.sendTime) {
- SendTimeRemaining(&first, TRUE);
- }
- SendToProgram("playother\n", &first);
- firstMove = FALSE;
- } else {
- firstMove = TRUE;
- }
- }
- } else if (gameMode == IcsPlayingBlack) {
- if (appData.icsAnalyze) {
- IcsAnalyze(FALSE);
- appData.icsAnalyze = FALSE;
- SendToICS("refresh\n");
- }
- /* engineRoom stay forever */
- if (!WhiteOnMove(forwardMostMove)) {
- if (first.sendTime) {
- if (first.useColors) {
- SendToProgram("white\n", &first);
- }
- SendTimeRemaining(&first, FALSE);
- }
- if (first.useColors) {
- SendToProgram("black\ngo\n", &first);
- } else {
- SendToProgram("go\n", &first);
- }
- first.maybeThinking = TRUE;
- } else {
- if (first.usePlayother) {
- if (first.sendTime) {
- SendTimeRemaining(&first, FALSE);
- }
- SendToProgram("playother\n", &first);
- firstMove = FALSE;
- } else {
- firstMove = TRUE;
- }
- }
- }
-
-#endif ZIPPY
-
- }
- if (gameMode == IcsObserving && ics_gamenum == -1) {
- /* Moves came from oldmoves or moves command
- while we weren't doing anything else.
- */
- currentMove = forwardMostMove;
- ClearHighlights();/*!!could figure this out*/
- flipView = appData.flipView;
- DrawPosition(FALSE, boards[currentMove]);
- DisplayBothClocks();
- sprintf(str, "%s vs. %s",
- gameInfo.white, gameInfo.black);
- DisplayTitle(str);
- gameMode = IcsIdle;
- } else {
- /* Moves were history of an active game */
- if (gameInfo.resultDetails != NULL) {
- free(gameInfo.resultDetails);
- gameInfo.resultDetails = NULL;
- }
- }
- HistorySet(parseList, backwardMostMove,
- forwardMostMove, currentMove-1);
- DisplayMove(currentMove - 1);
- if (started == STARTED_MOVES) next_out = i;
- started = STARTED_NONE;
- ics_getting_history = H_FALSE;
- break;
-
- case STARTED_OBSERVE:
- started = STARTED_NONE;
- SendToICS(ics_prefix);
- SendToICS("refresh\n");
- break;
-
- default:
- break;
- }
- continue;
- }
-
- if ((started == STARTED_MOVES || started == STARTED_BOARD ||
- started == STARTED_HOLDINGS ||
- started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {
- /* Accumulate characters in move list or board */
- parse[parse_pos++] = buf[i];
- }
-
- /* Start of game messages. Mostly we detect start of game
- when the first board image arrives. On some versions
- of the ICS, though, we need to do a "refresh" after starting
- to observe in order to get the current board right away. */
- if (looking_at(buf, &i, "Adding game * to observation list")) {
- started = STARTED_OBSERVE;
- continue;
- }
-
- /* Handle auto-observe */
- if (appData.autoObserve &&
- (gameMode == IcsIdle || gameMode == BeginningOfGame) &&
- looking_at(buf, &i, "Game notification: * (*) vs. * (*)")) {
- char *player;
- /* Choose the player that was highlighted, if any. */
- if (star_match[0][0] == '\033' ||
- star_match[1][0] != '\033') {
- player = star_match[0];
- } else {
- player = star_match[2];
- }
- sprintf(str, "%sobserve %s\n",
- ics_prefix, StripHighlightAndTitle(player));
- SendToICS(str);
-
- /* Save ratings from notify string */
- strcpy(player1Name, star_match[0]);
- player1Rating = string_to_rating(star_match[1]);
- strcpy(player2Name, star_match[2]);
- player2Rating = string_to_rating(star_match[3]);
-
- if (appData.debugMode)
- fprintf(debugFP,
- "Ratings from 'Game notification:' %s %d, %s %d\n",
- player1Name, player1Rating,
- player2Name, player2Rating);
-
- continue;
- }
-
- /* Deal with automatic examine mode after a game,
- and with IcsObserving -> IcsExamining transition */
- if (looking_at(buf, &i, "Entering examine mode for game *") ||
- looking_at(buf, &i, "has made you an examiner of game *")) {
-
- int gamenum = atoi(star_match[0]);
- if ((gameMode == IcsIdle || gameMode == IcsObserving) &&
- gamenum == ics_gamenum) {
- /* We were already playing or observing this game;
- no need to refetch history */
- gameMode = IcsExamining;
- if (pausing) {
- pauseExamForwardMostMove = forwardMostMove;
- } else if (currentMove < forwardMostMove) {
- ForwardInner(forwardMostMove);
- }
- } else {
- /* I don't think this case really can happen */
- SendToICS(ics_prefix);
- SendToICS("refresh\n");
- }
- continue;
- }
-
- /* Error messages */
- if (ics_user_moved) {
- if (looking_at(buf, &i, "Illegal move") ||
- looking_at(buf, &i, "Not a legal move") ||
- looking_at(buf, &i, "Your king is in check") ||
- looking_at(buf, &i, "It isn't your turn") ||
- looking_at(buf, &i, "It is not your move")) {
- /* Illegal move */
- ics_user_moved = 0;
- if (forwardMostMove > backwardMostMove) {
- currentMove = --forwardMostMove;
- DisplayMove(currentMove - 1); /* before DMError */
- DisplayMoveError("Illegal move (rejected by ICS)");
- DrawPosition(FALSE, boards[currentMove]);
- SwitchClocks();
- DisplayBothClocks();
- }
- continue;
- }
- }
-
- if (looking_at(buf, &i, "still have time") ||
- looking_at(buf, &i, "not out of time") ||
- looking_at(buf, &i, "either player is out of time") ||
- looking_at(buf, &i, "has timeseal; checking")) {
- /* We must have called his flag a little too soon */
- whiteFlag = blackFlag = FALSE;
- continue;
- }
-
- if (looking_at(buf, &i, "added * seconds to") ||
- looking_at(buf, &i, "seconds were added to")) {
- /* Update the clocks */
- SendToICS(ics_prefix);
- SendToICS("refresh\n");
- continue;
- }
-
- if (!ics_clock_paused && looking_at(buf, &i, "clock paused")) {
- ics_clock_paused = TRUE;
- StopClocks();
- continue;
- }
-
- if (ics_clock_paused && looking_at(buf, &i, "clock resumed")) {
- ics_clock_paused = FALSE;
- StartClocks();
- continue;
- }
-
- /* Grab player ratings from the Creating: message.
- Note we have to check for the special case when
- the ICS inserts things like [white] or [black]. */
- if (looking_at(buf, &i, "Creating: * (*)* * (*)") ||
- looking_at(buf, &i, "Creating: * (*) [*] * (*)")) {
- /* star_matches:
- 0 player 1 name (not necessarily white)
- 1 player 1 rating
- 2 empty, white, or black (IGNORED)
- 3 player 2 name (not necessarily black)
- 4 player 2 rating
-
- The names/ratings are sorted out when the game
- actually starts (below).
- */
- strcpy(player1Name, StripHighlightAndTitle(star_match[0]));
- player1Rating = string_to_rating(star_match[1]);
- strcpy(player2Name, StripHighlightAndTitle(star_match[3]));
- player2Rating = string_to_rating(star_match[4]);
-
- if (appData.debugMode)
- fprintf(debugFP,
- "Ratings from 'Creating:' %s %d, %s %d\n",
- player1Name, player1Rating,
- player2Name, player2Rating);
-
- continue;
- }
-
- /* Improved generic start/end-of-game messages */
- if (looking_at(buf, &i, "{Game * (* vs. *) *}*")) {
- /* star_match[0] is the game number */
- /* [1] is the white player's name */
- /* [2] is the black player's name */
- /* For end-of-game: */
- /* [3] is the reason for the game end */
- /* [4] is a PGN end game-token, preceded by " " */
- /* For start-of-game: */
- /* [3] begins with "Creating" or "Continuing" */
- /* [4] is " *" or empty (don't care). */
- int gamenum = atoi(star_match[0]);
- char *why = star_match[3];
- char *endtoken = star_match[4];
- ChessMove endtype = (ChessMove) 0;
-
- /* Game start messages */
- if (strncmp(why, "Creating ", 9) == 0 ||
- strncmp(why, "Continuing ", 11) == 0) {
- gs_gamenum = gamenum;
- strcpy(gs_kind, strchr(why, ' ') + 1);
-#if ZIPPY
- if (appData.zippyPlay) {
- ZippyGameStart(star_match[1], star_match[2]);
- }
-#endif /*ZIPPY*/
- continue;
- }
-
- /* Game end messages */
- if (gameMode == IcsIdle || gameMode == BeginningOfGame ||
- ics_gamenum != gamenum) {
- continue;
- }
- while (endtoken[0] == ' ') endtoken++;
- switch (endtoken[0]) {
- case '*':
- default:
- endtype = GameUnfinished;
- break;
- case '0':
- endtype = BlackWins;
- break;
- case '1':
- if (endtoken[1] == '/')
- endtype = GameIsDrawn;
- else
- endtype = WhiteWins;
- break;
- }
- GameEnds(endtype, why, GE_ICS);
-#if ZIPPY
- if (appData.zippyPlay && first.initDone) {
- ZippyGameEnd(endtype, why);
- if (first.pr == NULL) {
- /* Start the next process early so that we'll
- be ready for the next challenge */
- StartChessProgram(&first);
- }
- /* Send "new" early, in case this command takes
- a long time to finish, so that we'll be ready
- for the next challenge. */
- Reset(TRUE, TRUE);
- }
-#endif /*ZIPPY*/
- continue;
- }
-
- if (looking_at(buf, &i, "Removing game * from observation") ||
- looking_at(buf, &i, "no longer observing game *") ||
- looking_at(buf, &i, "Game * (*) has no examiners")) {
- if (gameMode == IcsObserving &&
- atoi(star_match[0]) == ics_gamenum)
- {
- if (appData.icsAnalyze) IcsAnalyze(FALSE);
- ResetIcsQueue(ics_gamenum);
- StopClocks();
- gameMode = IcsIdle;
- ics_gamenum = -1;
- ics_user_moved = FALSE;
- }
- continue;
- }
-
- if (looking_at(buf, &i, "no longer examining game *")) {
- if (gameMode == IcsExamining &&
- atoi(star_match[0]) == ics_gamenum)
- {
- if (appData.icsAnalyze) IcsAnalyze(FALSE);
- ResetIcsQueue(ics_gamenum);
- gameMode = IcsIdle;
- ics_gamenum = -1;
- ics_user_moved = FALSE;
- }
- continue;
- }
-
- /* Advance leftover_start past any newlines we find,
- so only partial lines can get reparsed */
- if (looking_at(buf, &i, "\n")) {
- prevColor = curColor;
- if (curColor != ColorNormal) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- Colorize(ColorNormal, FALSE);
- curColor = ColorNormal;
- }
- if (started == STARTED_BOARD) {
- started = STARTED_NONE;
- parse[parse_pos] = NULLCHAR;
- ParseBoard12(parse);
- ics_user_moved = 0;
-
- /* Send premove here */
- if (appData.premove) {
- char str[MSG_SIZ];
- if (currentMove == 0 &&
- gameMode == IcsPlayingWhite &&
- appData.premoveWhite) {
- sprintf(str, "%s%s\n", ics_prefix,
- appData.premoveWhiteText);
- if (appData.debugMode)
- fprintf(debugFP, "Sending premove:\n");
- SendToICS(str);
- } else if (currentMove == 1 &&
- gameMode == IcsPlayingBlack &&
- appData.premoveBlack) {
- sprintf(str, "%s%s\n", ics_prefix,
- appData.premoveBlackText);
- if (appData.debugMode)
- fprintf(debugFP, "Sending premove:\n");
- SendToICS(str);
- } else if (gotPremove) {
- gotPremove = 0;
- ClearPremoveHighlights();
- if (appData.debugMode)
- fprintf(debugFP, "Sending premove:\n");
- UserMoveEvent(premoveFromX, premoveFromY,
- premoveToX, premoveToY,
- premovePromoChar);
- }
- }
-
- /* Usually suppress following prompt */
- if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {
- if (looking_at(buf, &i, "*% ")) {
- savingComment = FALSE;
- }
- }
- next_out = i;
- } else if (started == STARTED_HOLDINGS) {
- int gamenum;
- char new_piece[MSG_SIZ];
- started = STARTED_NONE;
- parse[parse_pos] = NULLCHAR;
- if (appData.debugMode)
- fprintf(debugFP, "Parsing holdings: %s\n", parse);
- if (sscanf(parse, " game %d", &gamenum) == 1 &&
- gamenum == ics_gamenum) {
- if (gameInfo.variant == VariantNormal) {
- gameInfo.variant = VariantCrazyhouse; /*temp guess*/
- /* Get a move list just to see the header, which
- will tell us whether this is really bug or zh */
- if (ics_getting_history == H_FALSE) {
- ics_getting_history = H_REQUESTED;
- sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
- SendToICS(str);
- }
- }
- new_piece[0] = NULLCHAR;
- sscanf(parse, "game %d white [%s black [%s <- %s",
- &gamenum, white_holding, black_holding,
- new_piece);
- white_holding[strlen(white_holding)-1] = NULLCHAR;
- black_holding[strlen(black_holding)-1] = NULLCHAR;
-#if ZIPPY
- if (appData.zippyPlay && first.initDone) {
- ZippyHoldings(white_holding, black_holding,
- new_piece);
- }
-#endif /*ZIPPY*/
- if (tinyLayout || smallLayout) {
- char wh[16], bh[16];
- PackHolding(wh, white_holding);
- PackHolding(bh, black_holding);
- sprintf(str, "[%s-%s] %s-%s", wh, bh,
- gameInfo.white, gameInfo.black);
- } else {
- sprintf(str, "%s [%s] vs. %s [%s]",
- gameInfo.white, white_holding,
- gameInfo.black, black_holding);
- }
- DrawPosition(FALSE, NULL);
- DisplayTitle(str);
- }
- /* Suppress following prompt */
- if (looking_at(buf, &i, "*% ")) {
- savingComment = FALSE;
- }
- next_out = i;
- }
- continue;
- }
-
- i++; /* skip unparsed character and loop back */
- }
-
- if (started != STARTED_MOVES && started != STARTED_BOARD &&
- started != STARTED_HOLDINGS && i > next_out) {
- SendToPlayer(&buf[next_out], i - next_out);
- next_out = i;
- }
-
- leftover_len = buf_len - leftover_start;
- /* if buffer ends with something we couldn't parse,
- reparse it after appending the next read */
-
- } else if (count == 0) {
- RemoveInputSource(isr);
- DisplayFatalError("Connection closed by ICS", 0, 0);
- } else {
- DisplayFatalError("Error reading from ICS", error, 1);
- }
-}
-
-
-/* Board style 12 looks like this:
-
- <12> r-b---k- pp----pp ---bP--- ---p---- q------- ------P- P--Q--BP -----R-K W -1 0 0 0 0 0 0 paf MaxII 0 2 12 21 25 234 174 24 Q/d7-a4 (0:06) Qxa4 0 0
-
- * The "<12> " is stripped before it gets to this routine. The two
- * trailing 0's (flip state and clock ticking) are later addition, and
- * some chess servers may not have them, or may have only the first.
- * Additional trailing fields may be added in the future.
- */
-
-#define PATTERN "%72c%c%d%d%d%d%d%d%d%s%s%d%d%d%d%d%d%d%d%s%s%s%d%d"
-
-#define RELATION_OBSERVING_PLAYED 0
-#define RELATION_OBSERVING_STATIC -2 /* examined, oldmoves, or smoves */
-#define RELATION_PLAYING_MYMOVE 1
-#define RELATION_PLAYING_NOTMYMOVE -1
-#define RELATION_EXAMINING 2
-#define RELATION_ISOLATED_BOARD -3
-#define RELATION_STARTING_POSITION -4 /* FICS only */
-
-void
-ParseBoard12(string)
- char *string;
-{
- GameMode newGameMode;
- int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0;
- int j, k, n, moveNum, white_stren, black_stren, white_time, black_time;
- int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;
- int takeback, i;
- char to_play, board_chars[72];
- char move_str[500], str[500], elapsed_time[500];
- char black[32], white[32];
- Board board;
- int prevMove = currentMove;
- int ticking = 2;
- ChessMove moveType;
- int fromX, fromY, toX, toY;
- char promoChar;
-
- fromX = fromY = toX = toY = -1;
-
- newGame = FALSE;
-
- if (appData.debugMode)
- fprintf(debugFP, "Parsing board: %s\n", string);
-
- move_str[0] = NULLCHAR;
- elapsed_time[0] = NULLCHAR;
- n = sscanf(string, PATTERN, board_chars, &to_play, &double_push,
- &castle_ws, &castle_wl, &castle_bs, &castle_bl, &irrev_count,
- &gamenum, white, black, &relation, &basetime, &increment,
- &white_stren, &black_stren, &white_time, &black_time,
- &moveNum, str, elapsed_time, move_str, &ics_flip,
- &ticking);
-
- if (n < 22) {
- sprintf(str, "Failed to parse board string:\n\"%s\"", string);
- DisplayError(str, 0);
- return;
- }
-
- /* Convert the move number to internal form */
- moveNum = (moveNum - 1) * 2;
- if (to_play == 'B') moveNum++;
- if (moveNum >= MAX_MOVES) {
- DisplayFatalError("Game too long; increase MAX_MOVES and recompile",
- 0, 1);
- return;
- }
-
- switch (relation) {
- case RELATION_OBSERVING_PLAYED:
- case RELATION_OBSERVING_STATIC:
- if (gamenum == -1) {
- /* Old ICC buglet */
- relation = RELATION_OBSERVING_STATIC;
- }
- newGameMode = IcsObserving;
- break;
- case RELATION_PLAYING_MYMOVE:
- case RELATION_PLAYING_NOTMYMOVE:
- newGameMode =
- ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?
- IcsPlayingWhite : IcsPlayingBlack;
- break;
- case RELATION_EXAMINING:
- newGameMode = IcsExamining;
- break;
- case RELATION_ISOLATED_BOARD:
- default:
- /* Just display this board. If user was doing something else,
- we will forget about it until the next board comes. */
- newGameMode = IcsIdle;
- break;
- case RELATION_STARTING_POSITION:
- newGameMode = gameMode;
- /* if we switch to a new board start IcsAnalyze */
- if(appData.icsAnalyze) IcsAnalyze(TRUE);
- break;
- }
-
- /* Modify behavior for initial board display on move listing
- of wild games.
- */
- switch (ics_getting_history) {
- case H_FALSE:
- case H_REQUESTED:
- break;
- case H_GOT_REQ_HEADER:
- case H_GOT_UNREQ_HEADER:
- /* This is the initial position of the current game */
- gamenum = ics_gamenum;
- moveNum = 0; /* old ICS bug workaround */
- if (to_play == 'B') {
- startedFromSetupPosition = TRUE;
- blackPlaysFirst = TRUE;
- moveNum = 1;
- if (forwardMostMove == 0) forwardMostMove = 1;
- if (backwardMostMove == 0) backwardMostMove = 1;
- if (currentMove == 0) currentMove = 1;
- }
- newGameMode = gameMode;
- relation = RELATION_STARTING_POSITION; /* ICC needs this */
- break;
- case H_GOT_UNWANTED_HEADER:
- /* This is an initial board that we don't want */
- return;
- case H_GETTING_MOVES:
- /* Should not happen */
- DisplayError("Error gathering move list: extra board", 0);
- ics_getting_history = H_FALSE;
- return;
- }
-
- /* Take action if this is the first board of a new game, or of a
- different game than is currently being displayed. */
- if (gamenum != ics_gamenum || newGameMode != gameMode ||
- relation == RELATION_ISOLATED_BOARD) {
-
- /* Forget the old game and get the history (if any) of the new one */
- if (gameMode != BeginningOfGame) {
- Reset(FALSE, TRUE);
- }
- newGame = TRUE;
- if (appData.autoRaiseBoard) BoardToTop();
- prevMove = -3;
- if (gamenum == -1) {
- newGameMode = IcsIdle;
- } else if (moveNum > 0 && newGameMode != IcsIdle &&
- appData.getMoveList) {
- /* Need to get game history */
- ics_getting_history = H_REQUESTED;
- sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
- SendToICS(str);
- }
-
-
- /* Initially flip the board to have black on the bottom if playing
- black or if the ICS flip flag is set, but let the user change
- it with the Flip View button. */
- flipView = appData.autoFlipView ?
- (newGameMode == IcsPlayingBlack) || ics_flip :
- appData.flipView;
-
- /* Done with values from previous mode; copy in new ones */
- gameMode = newGameMode;
- ModeHighlight();
- ics_gamenum = gamenum;
- if (gamenum == gs_gamenum) {
- int klen = strlen(gs_kind);
- if (gs_kind[klen - 1] == '.') gs_kind[klen - 1] = NULLCHAR;
- sprintf(str, "ICS %s", gs_kind);
- gameInfo.event = StrSave(str);
- } else {
- gameInfo.event = StrSave("ICS game");
- }
- gameInfo.site = StrSave(appData.icsHost);
- gameInfo.date = PGNDate();
- gameInfo.round = StrSave("-");
- gameInfo.white = StrSave(white);
- gameInfo.black = StrSave(black);
- timeControl = basetime * 60 * 1000;
- timeIncrement = increment * 1000;
- movesPerSession = 0;
- gameInfo.timeControl = TimeControlTagValue();
- gameInfo.variant = StringToVariant(gameInfo.event);
-
- /* Do we have the ratings? */
- if (strcmp(player1Name, white) == 0 &&
- strcmp(player2Name, black) == 0) {
- if (appData.debugMode)
- fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
- player1Rating, player2Rating);
- gameInfo.whiteRating = player1Rating;
- gameInfo.blackRating = player2Rating;
- } else if (strcmp(player2Name, white) == 0 &&
- strcmp(player1Name, black) == 0) {
- if (appData.debugMode)
- fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
- player2Rating, player1Rating);
- gameInfo.whiteRating = player2Rating;
- gameInfo.blackRating = player1Rating;
- }
- player1Name[0] = player2Name[0] = NULLCHAR;
-
- /* Silence shouts if requested */
- if (appData.quietPlay &&
- (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)) {
- SendToICS(ics_prefix);
- SendToICS("set shout 0\n");
- }
- }
-
- /* Deal with midgame name changes */
- if (!newGame) {
- if (!gameInfo.white || strcmp(gameInfo.white, white) != 0) {
- if (gameInfo.white) free(gameInfo.white);
- gameInfo.white = StrSave(white);
- }
- if (!gameInfo.black || strcmp(gameInfo.black, black) != 0) {
- if (gameInfo.black) free(gameInfo.black);
- gameInfo.black = StrSave(black);
- }
- }
-
- /* Throw away game result if anything actually changes in examine mode */
- if (gameMode == IcsExamining && !newGame) {
- gameInfo.result = GameUnfinished;
- if (gameInfo.resultDetails != NULL) {
- free(gameInfo.resultDetails);
- gameInfo.resultDetails = NULL;
- }
- }
-
- /* In pausing && IcsExamining mode, we ignore boards coming
- in if they are in a different variation than we are. */
- if (pauseExamInvalid) return;
- if (pausing && gameMode == IcsExamining) {
- if (moveNum <= pauseExamForwardMostMove) {
- pauseExamInvalid = TRUE;
- forwardMostMove = pauseExamForwardMostMove;
- return;
- }
- }
-
- /* Parse the board */
- for (k = 0; k < 8; k++)
- for (j = 0; j < 8; j++)
- board[k][j] = CharToPiece(board_chars[(7-k)*9 + j]);
- CopyBoard(boards[moveNum], board);
- if (moveNum == 0) {
- startedFromSetupPosition =
- !CompareBoards(board, initialPosition);
- }
-
- if (ics_getting_history == H_GOT_REQ_HEADER ||
- ics_getting_history == H_GOT_UNREQ_HEADER) {
- /* This was an initial position from a move list, not
- the current position */
- return;
- }
-
- /* Update currentMove and known move number limits */
- newMove = newGame || moveNum > forwardMostMove;
-
-
- /* If we found takebacks during IcsAnalyze try send to engine */
- if (!newGame && appData.icsAnalyze && first.analyzing &&
- moveNum < forwardMostMove) {
- if (appData.debugMode) fprintf(debugFP, "take back move\n");
- takeback = forwardMostMove - moveNum;
- if (!appData.icsWBprotoAgr) {
- /* safty first */
- SendToProgram("exit\n", &first);
- SendToProgram("force\n", &first);
- for (i = 0; i < takeback; i++) {
- SendToProgram("undo\n", &first);
- /* Some engine need analyze and stat again */
- }
- SendToProgram("analyze\n", &first);
- } else {
- /* hard */
- IcsAnalyze(FALSE);
- IcsAnalyze(TRUE);
- }
- }
- if (newGame) {
- forwardMostMove = backwardMostMove = currentMove = moveNum;
- if (gameMode == IcsExamining && moveNum == 0) {
- /* Workaround for ICS limitation: we are not told the wild
- type when starting to examine a game. But if we ask for
- the move list, the move list header will tell us */
- ics_getting_history = H_REQUESTED;
- sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
- SendToICS(str);
- }
- } else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove
- || (moveNum < forwardMostMove && moveNum >= backwardMostMove)) {
- forwardMostMove = moveNum;
- if (!pausing || currentMove > forwardMostMove)
- currentMove = forwardMostMove;
- } else {
- /* New part of history that is not contiguous with old part */
- if (pausing && gameMode == IcsExamining) {
- pauseExamInvalid = TRUE;
- forwardMostMove = pauseExamForwardMostMove;
- return;
- }
- forwardMostMove = backwardMostMove = currentMove = moveNum;
- if (gameMode == IcsExamining && moveNum > 0 && appData.getMoveList) {
- ics_getting_history = H_REQUESTED;
- sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
- SendToICS(str);
- }
- }
-
- {
- int i = 0;
- /* Update the clocks */
- if (strchr(elapsed_time, '.')) {
- /* Time is in ms */
- timeRemaining[0][moveNum] = whiteTimeRemaining = white_time;
- timeRemaining[1][moveNum] = blackTimeRemaining = black_time;
- } else {
- /* Time is in seconds */
- i = 1;
- timeRemaining[0][moveNum] = whiteTimeRemaining = white_time * 1000;
- timeRemaining[1][moveNum] = blackTimeRemaining = black_time * 1000;
- }
- }
-
- /* Time workaround for ICC - send only sec :( */
- if (ics_type == ICS_ICC) {
- if (timeRemaining[0][moveNum] >= 1500) timeRemaining[0][moveNum] = timeRemaining[0][moveNum] - 1500;
- if (timeRemaining[1][moveNum] >= 1500) timeRemaining[1][moveNum] = timeRemaining[1][moveNum] - 1500;
-
- if (timeRemaining[0][moveNum] == 1000) timeRemaining[0][moveNum] = timeRemaining[0][moveNum] - 950;
- if (timeRemaining[0][moveNum] == 1000) timeRemaining[1][moveNum] = timeRemaining[1][moveNum] - 950;
-
- }
-
-#if ZIPPY
- if (appData.zippyPlay && newGame &&
- gameMode != IcsObserving && gameMode != IcsIdle &&
- gameMode != IcsExamining)
- ZippyFirstBoard(moveNum, basetime, increment);
-#endif
-
- /* Put the move on the move list, first converting
- to canonical algebraic form. */
- if (moveNum > 0) {
- if (moveNum <= backwardMostMove) {
- /* We don't know what the board looked like before
- this move. Punt. */
- strcpy(parseList[moveNum - 1], move_str);
- strcat(parseList[moveNum - 1], " ");
- strcat(parseList[moveNum - 1], elapsed_time);
- moveList[moveNum - 1][0] = NULLCHAR;
- } else if (ParseOneMove(move_str, moveNum - 1, &moveType,
- &fromX, &fromY, &toX, &toY, &promoChar)) {
- (void) CoordsToAlgebraic(boards[moveNum - 1],
- PosFlags(moveNum - 1), EP_UNKNOWN,
- fromY, fromX, toY, toX, promoChar,
- parseList[moveNum-1]);
- switch (MateTest(boards[moveNum], PosFlags(moveNum), EP_UNKNOWN)){
- case MT_NONE:
- case MT_STALEMATE:
- default:
- break;
- case MT_CHECK:
- strcat(parseList[moveNum - 1], "+");
- break;
- case MT_CHECKMATE:
- strcat(parseList[moveNum - 1], "#");
- break;
- }
- strcat(parseList[moveNum - 1], " ");
- strcat(parseList[moveNum - 1], elapsed_time);
- /* currentMoveString is set as a side-effect of ParseOneMove */
- strcpy(moveList[moveNum - 1], currentMoveString);
- strcat(moveList[moveNum - 1], "\n");
- } else if (strcmp(move_str, "none") == 0) {
- /* Again, we don't know what the board looked like;
- this is really the start of the game. */
- parseList[moveNum - 1][0] = NULLCHAR;
- moveList[moveNum - 1][0] = NULLCHAR;
- backwardMostMove = moveNum;
- startedFromSetupPosition = TRUE;
- fromX = fromY = toX = toY = -1;
- } else {
- /* Move from ICS was illegal!? Punt. */
-#if 0
- if (appData.testLegality && appData.debugMode) {
- sprintf(str, "Illegal move \"%s\" from ICS", move_str);
- DisplayError(str, 0);
- }
-#endif
- strcpy(parseList[moveNum - 1], move_str);
- strcat(parseList[moveNum - 1], " ");
- strcat(parseList[moveNum - 1], elapsed_time);
- moveList[moveNum - 1][0] = NULLCHAR;
- fromX = fromY = toX = toY = -1;
- }
-
-#if ZIPPY
- /* Send move to chess program (BEFORE animating it). */
- if (appData.zippyPlay && !newGame && newMove &&
- (!appData.getMoveList || backwardMostMove == 0) && first.initDone) {
-
- if ((gameMode == IcsPlayingWhite && WhiteOnMove(moveNum)) ||
- (gameMode == IcsPlayingBlack && !WhiteOnMove(moveNum))) {
- if (moveList[moveNum - 1][0] == NULLCHAR) {
- sprintf(str, "Couldn't parse move \"%s\" from ICS",
- move_str);
- DisplayError(str, 0);
- } else {
- if (first.sendTime) {
- SendTimeRemaining(&first, gameMode == IcsPlayingWhite);
- }
- SendMoveToProgram(moveNum - 1, &first);
- if (firstMove) {
- firstMove = FALSE;
- if (first.useColors) {
- SendToProgram(gameMode == IcsPlayingWhite ?
- "white\ngo\n" :
- "black\ngo\n", &first);
- } else {
- SendToProgram("go\n", &first);
- }
- first.maybeThinking = TRUE;
- }
- }
- } else if (gameMode == IcsObserving || gameMode == IcsExamining) {
- if (moveList[moveNum - 1][0] == NULLCHAR) {
- sprintf(str, "Couldn't parse move \"%s\" from ICS", move_str);
- DisplayError(str, 0);
- } else {
- SendMoveToProgram(moveNum - 1, &first);
- }
- }
- }
-#endif
- }
-
- if (moveNum > 0 && !gotPremove) {
- /* If move comes from a remote source, animate it. If it
- isn't remote, it will have already been animated. */
- if (!pausing && !ics_user_moved && prevMove == moveNum - 1) {
- AnimateMove(boards[moveNum - 1], fromX, fromY, toX, toY);
- }
- if (!pausing && appData.highlightLastMove) {
- SetHighlights(fromX, fromY, toX, toY);
- }
- }
-
- /* Start the clocks */
- whiteFlag = blackFlag = FALSE;
- appData.clockMode = !(basetime == 0 && increment == 0);
- if (ticking == 0) {
- ics_clock_paused = TRUE;
- StopClocks();
- } else if (ticking == 1) {
- ics_clock_paused = FALSE;
- }
- if (gameMode == IcsIdle ||
- relation == RELATION_OBSERVING_STATIC ||
- relation == RELATION_EXAMINING ||
- ics_clock_paused)
- DisplayBothClocks();
- else
- StartClocks();
-
- /* Display opponents and material strengths */
- if (gameInfo.variant != VariantBughouse &&
- gameInfo.variant != VariantCrazyhouse) {
- if (tinyLayout || smallLayout) {
- sprintf(str, "%s(%d) %s(%d) {%d %d}",
- gameInfo.white, white_stren, gameInfo.black, black_stren,
- basetime, increment);
- } else {
- sprintf(str, "%s (%d) vs. %s (%d) {%d %d}",
- gameInfo.white, white_stren, gameInfo.black, black_stren,
- basetime, increment);
- }
- DisplayTitle(str);
- }
-
-
- /* Display the board */
- if (!pausing) {
-
- if (appData.premove)
- if (!gotPremove ||
- ((gameMode == IcsPlayingWhite) && (WhiteOnMove(currentMove))) ||
- ((gameMode == IcsPlayingBlack) && (!WhiteOnMove(currentMove))))
- ClearPremoveHighlights();
-
- DrawPosition(FALSE, boards[currentMove]);
- DisplayMove(moveNum - 1);
- if (appData.ringBellAfterMoves && !ics_user_moved)
- RingBell();
- }
-
- HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
-}
-
-void
-GetMoveListEvent()
-{
- char buf[MSG_SIZ];
- if (appData.icsActive && gameMode != IcsIdle && ics_gamenum > 0) {
- ics_getting_history = H_REQUESTED;
- sprintf(buf, "%smoves %d\n", ics_prefix, ics_gamenum);
- SendToICS(buf);
- }
-}
-
-void
-AnalysisPeriodicEvent(force)
- int force;
-{
- /* WB engine room */
- if (appData.AnalysisWindow && programStats.depth > 2) {
- /* GUI disable send stat ? */
- if (!appData.engineStatLine) SendToProgram(".\n", &first);
- /* don't support Stats on game ?*/
- if (supportStat == 0) {
- /* call Display every sec for time and nodes */
- DisplayAnalysis(1,0);
- } else {
- /* GUI makes time ???? */
- /* at the moment: yes! */
- DisplayAnalysis(1,0);
- }
- } else if (appData.icsAnalyze && programStats.depth > 2) {
- SendToProgram(".\n", &first);
- DisplayAnalysis(1,0);
- } else if (((programStats.ok_to_send == 0 || programStats.line_is_book)
- && !force) || !appData.periodicUpdates)
- return;
-
- /* Send . command to Crafty to collect stats */
- if (!appData.AnalysisWindow && (gameMode == AnalyzeMode ||
- gameMode == AnalyzeFile)) SendToProgram(".\n", &first);
-
- /* Don't send another until we get a response (this makes
- us stop sending to old Crafty's which don't understand
- the "." command (sending illegal cmds resets node count & time,
- which looks bad)) */
- programStats.ok_to_send = 0;
-}
-
-void
-SendMoveToProgram(moveNum, cps)
- int moveNum;
- ChessProgramState *cps;
-{
- char buf[MSG_SIZ];
- if (cps->useUsermove) {
- SendToProgram("usermove ", cps);
- }
- if (cps->useSAN) {
- char *space;
- if ((space = strchr(parseList[moveNum], ' ')) != NULL) {
- int len = space - parseList[moveNum];
- memcpy(buf, parseList[moveNum], len);
- buf[len++] = '\n';
- buf[len] = NULLCHAR;
- } else {
- sprintf(buf, "%s\n", parseList[moveNum]);
- }
- SendToProgram(buf, cps);
- } else {
- SendToProgram(moveList[moveNum], cps);
- }
-}
-
-void
-SendMoveToICS(moveType, fromX, fromY, toX, toY)
- ChessMove moveType;
- int fromX, fromY, toX, toY;
-{
- char user_move[MSG_SIZ];
-
- switch (moveType) {
- default:
- sprintf(user_move, "say Internal error; bad moveType %d (%d,%d-%d,%d)",
- (int)moveType, fromX, fromY, toX, toY);
- DisplayError(user_move + strlen("say "), 0);
- break;
- case WhiteKingSideCastle:
- case BlackKingSideCastle:
- case WhiteQueenSideCastleWild:
- case BlackQueenSideCastleWild:
- sprintf(user_move, "o-o\n");
- break;
- case WhiteQueenSideCastle:
- case BlackQueenSideCastle:
- case WhiteKingSideCastleWild:
- case BlackKingSideCastleWild:
- sprintf(user_move, "o-o-o\n");
- break;
- case WhitePromotionQueen:
- case BlackPromotionQueen:
- case WhitePromotionRook:
- case BlackPromotionRook:
- case WhitePromotionBishop:
- case BlackPromotionBishop:
- case WhitePromotionKnight:
- case BlackPromotionKnight:
- case WhitePromotionKing:
- case BlackPromotionKing:
- sprintf(user_move, "%c%c%c%c=%c\n",
- 'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY,
- PieceToChar(PromoPiece(moveType)));
- break;
- case WhiteDrop:
- case BlackDrop:
- sprintf(user_move, "%c@%c%c\n",
- ToUpper(PieceToChar((ChessSquare) fromX)),
- 'a' + toX, '1' + toY);
- break;
- case NormalMove:
- case WhiteCapturesEnPassant:
- case BlackCapturesEnPassant:
- case IllegalMove: /* could be a variant we don't quite understand */
- sprintf(user_move, "%c%c%c%c\n",
- 'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY);
- break;
- }
- SendToICS(user_move);
-}
-
-void
-CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move)
- int rf, ff, rt, ft;
- char promoChar;
- char move[7];
-{
- if (rf == DROP_RANK) {
- sprintf(move, "%c@%c%c\n",
- ToUpper(PieceToChar((ChessSquare) ff)), 'a' + ft, '1' + rt);
- } else {
- if (promoChar == 'x' || promoChar == NULLCHAR) {
- sprintf(move, "%c%c%c%c\n",
- 'a' + ff, '1' + rf, 'a' + ft, '1' + rt);
- } else {
- sprintf(move, "%c%c%c%c%c\n",
- 'a' + ff, '1' + rf, 'a' + ft, '1' + rt, promoChar);
- }
- }
-}
-
-void
-ProcessICSInitScript(f)
- FILE *f;
-{
- char buf[MSG_SIZ];
-
- while (fgets(buf, MSG_SIZ, f)) {
- SendToICSDelayed(buf,(long)appData.msLoginDelay);
- }
-
- fclose(f);
-}
-
-
-/* Parser for moves from gnuchess, ICS, or user typein box */
-Boolean
-ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
- char *move;
- int moveNum;
- ChessMove *moveType;
- int *fromX, *fromY, *toX, *toY;
- char *promoChar;
-{
- *moveType = yylexstr(moveNum, move);
- switch (*moveType) {
- case WhitePromotionQueen:
- case BlackPromotionQueen:
- case WhitePromotionRook:
- case BlackPromotionRook:
- case WhitePromotionBishop:
- case BlackPromotionBishop:
- case WhitePromotionKnight:
- case BlackPromotionKnight:
- case WhitePromotionKing:
- case BlackPromotionKing:
- case NormalMove:
- case WhiteCapturesEnPassant:
- case BlackCapturesEnPassant:
- case WhiteKingSideCastle:
- case WhiteQueenSideCastle:
- case BlackKingSideCastle:
- case BlackQueenSideCastle:
- case WhiteKingSideCastleWild:
- case WhiteQueenSideCastleWild:
- case BlackKingSideCastleWild:
- case BlackQueenSideCastleWild:
- case IllegalMove: /* bug or odd chess variant */
- *fromX = currentMoveString[0] - 'a';
- *fromY = currentMoveString[1] - '1';
- *toX = currentMoveString[2] - 'a';
- *toY = currentMoveString[3] - '1';
- *promoChar = currentMoveString[4];
- if (*fromX < 0 || *fromX > 7 || *fromY < 0 || *fromY > 7 ||
- *toX < 0 || *toX > 7 || *toY < 0 || *toY > 7) {
- *fromX = *fromY = *toX = *toY = 0;
- return FALSE;
- }
- if (appData.testLegality) {
- return (*moveType != IllegalMove);
- } else {
- return !(fromX == fromY && toX == toY);
- }
-
- case WhiteDrop:
- case BlackDrop:
- *fromX = *moveType == WhiteDrop ?
- (int) CharToPiece(ToUpper(currentMoveString[0])) :
- (int) CharToPiece(ToLower(currentMoveString[0]));
- *fromY = DROP_RANK;
- *toX = currentMoveString[2] - 'a';
- *toY = currentMoveString[3] - '1';
- *promoChar = NULLCHAR;
- return TRUE;
-
- case AmbiguousMove:
- case ImpossibleMove:
- case (ChessMove) 0: /* end of file */
- case ElapsedTime:
- case Comment:
- case PGNTag:
- case NAG:
- case WhiteWins:
- case BlackWins:
- case GameIsDrawn:
- default:
- /* bug? */
- *fromX = *fromY = *toX = *toY = 0;
- *promoChar = NULLCHAR;
- return FALSE;
- }
-}
-
-
-void
-InitPosition(redraw)
- int redraw;
-{
- currentMove = forwardMostMove = backwardMostMove = 0;
- switch (gameInfo.variant) {
- default:
- CopyBoard(boards[0], initialPosition);
- break;
- case VariantTwoKings:
- CopyBoard(boards[0], twoKingsPosition);
- startedFromSetupPosition = TRUE;
- break;
- case VariantWildCastle:
- CopyBoard(boards[0], initialPosition);
- /* !!?shuffle with kings guaranteed to be on d or e file */
- break;
- case VariantNoCastle:
- CopyBoard(boards[0], initialPosition);
- /* !!?unconstrained back-rank shuffle */
- break;
- case VariantFischeRandom:
- CopyBoard(boards[0], initialPosition);
- /* !!shuffle according to FR rules */
- break;
- }
- if (redraw)
- DrawPosition(FALSE, boards[currentMove]);
-}
-
-void
-SendBoard(cps, moveNum)
- ChessProgramState *cps;
- int moveNum;
-{
- char message[MSG_SIZ];
-
- if (cps->useSetboard) {
- char* fen = PositionToFEN(moveNum);
- sprintf(message, "setboard %s\n", fen);
- SendToProgram(message, cps);
- free(fen);
-
- } else {
- ChessSquare *bp;
- int i, j;
- /* Kludge to set black to move, avoiding the troublesome and now
- * deprecated "black" command.
- */
- if (!WhiteOnMove(moveNum)) SendToProgram("a2a3\n", cps);
-
- SendToProgram("edit\n", cps);
- SendToProgram("#\n", cps);
- for (i = BOARD_SIZE - 1; i >= 0; i--) {
- bp = &boards[moveNum][i][0];
- for (j = 0; j < BOARD_SIZE; j++, bp++) {
- if ((int) *bp < (int) BlackPawn) {
- sprintf(message, "%c%c%c\n", PieceToChar(*bp),
- 'a' + j, '1' + i);
- SendToProgram(message, cps);
- }
- }
- }
-
- SendToProgram("c\n", cps);
- for (i = BOARD_SIZE - 1; i >= 0; i--) {
- bp = &boards[moveNum][i][0];
- for (j = 0; j < BOARD_SIZE; j++, bp++) {
- if (((int) *bp != (int) EmptySquare)
- && ((int) *bp >= (int) BlackPawn)) {
- sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)),
- 'a' + j, '1' + i);
- SendToProgram(message, cps);
- }
- }
- }
-
- SendToProgram(".\n", cps);
- }
-}
-
-int
-IsPromotion(fromX, fromY, toX, toY)
- int fromX, fromY, toX, toY;
-{
- return gameMode != EditPosition &&
- fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0 &&
- ((boards[currentMove][fromY][fromX] == WhitePawn && toY == 7) ||
- (boards[currentMove][fromY][fromX] == BlackPawn && toY == 0));
-}
-
-
-int
-PieceForSquare (x, y)
- int x;
- int y;
-{
- if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE)
- return -1;
- else
- return boards[currentMove][y][x];
-}
-
-int
-OKToStartUserMove(x, y)
- int x, y;
-{
- ChessSquare from_piece;
- int white_piece;
-
- if (matchMode) return FALSE;
- if (gameMode == EditPosition) return TRUE;
-
- if (x >= 0 && y >= 0)
- from_piece = boards[currentMove][y][x];
- else
- from_piece = EmptySquare;
-
- if (from_piece == EmptySquare) return FALSE;
-
- white_piece = (int)from_piece >= (int)WhitePawn &&
- (int)from_piece <= (int)WhiteKing;
-
- switch (gameMode) {
- case PlayFromGameFile:
- case AnalyzeFile:
- case TwoMachinesPlay:
- case EndOfGame:
- return FALSE;
-
- case IcsObserving:
- case IcsIdle:
- return FALSE;
-
- case MachinePlaysWhite:
- case IcsPlayingBlack:
- if (appData.zippyPlay) return FALSE;
- if (white_piece) {
- DisplayMoveError("You are playing Black");
- return FALSE;
- }
- break;
-
- case MachinePlaysBlack:
- case IcsPlayingWhite:
- if (appData.zippyPlay) return FALSE;
- if (!white_piece) {
- DisplayMoveError("You are playing White");
- return FALSE;
- }
- break;
-
- case EditGame:
- if (!white_piece && WhiteOnMove(currentMove)) {
- DisplayMoveError("It is White's turn");
- return FALSE;
- }
- if (white_piece && !WhiteOnMove(currentMove)) {
- DisplayMoveError("It is Black's turn");
- return FALSE;
- }
- if (cmailMsgLoaded && (currentMove < cmailOldMove)) {
- /* Editing correspondence game history */
- /* Could disallow this or prompt for confirmation */
- cmailOldMove = -1;
- }
- if (currentMove < forwardMostMove) {
- /* Discarding moves */
- /* Could prompt for confirmation here,
- but I don't think that's such a good idea */
- forwardMostMove = currentMove;
- }
- break;
-
- case BeginningOfGame:
- if (appData.icsActive) return FALSE;
- if (!appData.noChessProgram) {
- if (!white_piece) {
- DisplayMoveError("You are playing White");
- return FALSE;
- }
- }
- break;
-
- case Training:
- if (!white_piece && WhiteOnMove(currentMove)) {
- DisplayMoveError("It is White's turn");
- return FALSE;
- }
- if (white_piece && !WhiteOnMove(currentMove)) {
- DisplayMoveError("It is Black's turn");
- return FALSE;
- }
- break;
-
- default:
- case IcsExamining:
- break;
- }
- if (currentMove != forwardMostMove && gameMode != AnalyzeMode
- && gameMode != AnalyzeFile && gameMode != Training) {
- DisplayMoveError("Displayed position is not current");
- return FALSE;
- }
- return TRUE;
-}
-
-FILE *lastLoadGameFP = NULL, *lastLoadPositionFP = NULL;
-int lastLoadGameNumber = 0, lastLoadPositionNumber = 0;
-int lastLoadGameUseList = FALSE;
-char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ];
-ChessMove lastLoadGameStart = (ChessMove) 0;
-
-
-void
-UserMoveEvent(fromX, fromY, toX, toY, promoChar)
- int fromX, fromY, toX, toY;
- int promoChar;
-{
- ChessMove moveType;
-
- if (fromX < 0 || fromY < 0) return;
- if ((fromX == toX) && (fromY == toY)) {
- return;
- }
-
- /* Check if the user is playing in turn. This is complicated because we
- let the user "pick up" a piece before it is his turn. So the piece he
- tried to pick up may have been captured by the time he puts it down!
- Therefore we use the color the user is supposed to be playing in this
- test, not the color of the piece that is currently on the starting
- square---except in EditGame mode, where the user is playing both
- sides; fortunately there the capture race can't happen. (It can
- now happen in IcsExamining mode, but that's just too bad. The user
- will get a somewhat confusing message in that case.)
- */
-
- switch (gameMode) {
- case PlayFromGameFile:
- case AnalyzeFile:
- case TwoMachinesPlay:
- case EndOfGame:
- case IcsObserving:
- case IcsIdle:
- /* We switched into a game mode where moves are not accepted,
- perhaps while the mouse button was down. */
- return;
-
- case MachinePlaysWhite:
- /* User is moving for Black */
- if (WhiteOnMove(currentMove)) {
- DisplayMoveError("It is White's turn");
- return;
- }
- break;
-
- case MachinePlaysBlack:
- /* User is moving for White */
- if (!WhiteOnMove(currentMove)) {
- DisplayMoveError("It is Black's turn");
- return;
- }
- break;
-
- case EditGame:
- case IcsExamining:
- case BeginningOfGame:
- case AnalyzeMode:
- case Training:
- if ((int) boards[currentMove][fromY][fromX] >= (int) BlackPawn &&
- (int) boards[currentMove][fromY][fromX] <= (int) BlackKing) {
- /* User is moving for Black */
- if (WhiteOnMove(currentMove)) {
- DisplayMoveError("It is White's turn");
- return;
- }
- } else {
- /* User is moving for White */
- if (!WhiteOnMove(currentMove)) {
- DisplayMoveError("It is Black's turn");
- return;
- }
- }
- break;
-
- case IcsPlayingBlack:
- /* User is moving for Black */
- if (WhiteOnMove(currentMove)) {
- if (!appData.premove) {
- DisplayMoveError("It is White's turn");
- } else if (toX >= 0 && toY >= 0) {
- premoveToX = toX;
- premoveToY = toY;
- premoveFromX = fromX;
- premoveFromY = fromY;
- premovePromoChar = promoChar;
- gotPremove = 1;
- if (appData.debugMode)
- fprintf(debugFP, "Got premove: fromX %d,"
- "fromY %d, toX %d, toY %d\n",
- fromX, fromY, toX, toY);
- }
- return;
- }
- break;
-
- case IcsPlayingWhite:
- /* User is moving for White */
- if (!WhiteOnMove(currentMove)) {
- if (!appData.premove) {
- DisplayMoveError("It is Black's turn");
- } else if (toX >= 0 && toY >= 0) {
- premoveToX = toX;
- premoveToY = toY;
- premoveFromX = fromX;
- premoveFromY = fromY;
- premovePromoChar = promoChar;
- gotPremove = 1;
- if (appData.debugMode)
- fprintf(debugFP, "Got premove: fromX %d,"
- "fromY %d, toX %d, toY %d\n",
- fromX, fromY, toX, toY);
- }
- return;
- }
- break;
-
- default:
- break;
-
- case EditPosition:
- if (toX == -2 || toY == -2) {
- boards[0][fromY][fromX] = EmptySquare;
- DrawPosition(FALSE, boards[currentMove]);
- } else if (toX >= 0 && toY >= 0) {
- boards[0][toY][toX] = boards[0][fromY][fromX];
- boards[0][fromY][fromX] = EmptySquare;
- DrawPosition(FALSE, boards[currentMove]);
- }
- return;
- }
-
- if (toX < 0 || toY < 0) return;
- userOfferedDraw = FALSE;
-
- if (appData.testLegality) {
- moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
- EP_UNKNOWN, fromY, fromX, toY, toX, promoChar);
- if (moveType == IllegalMove || moveType == ImpossibleMove) {
- DisplayMoveError("Illegal move");
- return;
- }
- } else {
- moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);
- }
-
- if (gameMode == Training) {
- /* compare the move played on the board to the next move in the
- * game. If they match, display the move and the opponent's response.
- * If they don't match, display an error message.
- */
- int saveAnimate;
- Board testBoard;
- CopyBoard(testBoard, boards[currentMove]);
- ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard);
-
- if (CompareBoards(testBoard, boards[currentMove+1])) {
- ForwardInner(currentMove+1);
-
- /* Autoplay the opponent's response.
- * if appData.animate was TRUE when Training mode was entered,
- * the response will be animated.
- */
- saveAnimate = appData.animate;
- appData.animate = animateTraining;
- ForwardInner(currentMove+1);
- appData.animate = saveAnimate;
-
- /* check for the end of the game */
- if (currentMove >= forwardMostMove) {
- gameMode = PlayFromGameFile;
- ModeHighlight();
- SetTrainingModeOff();
- DisplayInformation("End of game");
- }
- } else {
- DisplayError("Incorrect move", 0);
- }
- return;
- }
-
- FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
-}
-
-/* Common tail of UserMoveEvent and DropMenuEvent */
-void
-FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
- ChessMove moveType;
- int fromX, fromY, toX, toY;
- /*char*/int promoChar;
-{
- /* Ok, now we know that the move is good, so we can kill
- the previous line in Analysis Mode */
- if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {
- forwardMostMove = currentMove;
- }
-
- /* If we need the chess program but it's dead, restart it */
- ResurrectChessProgram();
-
- /* A user move restarts a paused game*/
- if (pausing)
- PauseEvent();
-
- thinkOutput[0] = NULLCHAR;
-
- MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/
-
- if (gameMode == BeginningOfGame) {
- if (appData.noChessProgram) {
- gameMode = EditGame;
- SetGameInfo();
- } else {
- char buf[MSG_SIZ];
- gameMode = MachinePlaysBlack;
- SetGameInfo();
- sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
- DisplayTitle(buf);
- if (first.sendName) {
- sprintf(buf, "name %s\n", gameInfo.white);
- SendToProgram(buf, &first);
- }
- }
- ModeHighlight();
- }
-
- /* Relay move to ICS or chess engine */
- if (appData.icsActive) {
- if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
- gameMode == IcsExamining) {
- SendMoveToICS(moveType, fromX, fromY, toX, toY);
- ics_user_moved = 1;
- }
- } else {
- if (first.sendTime && (gameMode == BeginningOfGame ||
- gameMode == MachinePlaysWhite ||
- gameMode == MachinePlaysBlack)) {
- SendTimeRemaining(&first, gameMode != MachinePlaysBlack);
- }
-
- SendMoveToProgram(forwardMostMove-1, &first);
- if (gameMode != EditGame && gameMode != PlayFromGameFile) {
- first.maybeThinking = TRUE;
- }
- if (currentMove == cmailOldMove + 1) {
- cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
- }
- }
-
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
-
- switch (gameMode) {
- case EditGame:
- switch (MateTest(boards[currentMove], PosFlags(currentMove),
- EP_UNKNOWN)) {
- case MT_NONE:
- case MT_CHECK:
- break;
- case MT_CHECKMATE:
- if (WhiteOnMove(currentMove)) {
- GameEnds(BlackWins, "Black mates", GE_PLAYER);
- } else {
- GameEnds(WhiteWins, "White mates", GE_PLAYER);
- }
- break;
- case MT_STALEMATE:
- GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);
- break;
- }
- break;
-
- case MachinePlaysBlack:
- case MachinePlaysWhite:
- /* disable certain menu options while machine is thinking */
- SetMachineThinkingEnables();
- break;
-
- default:
- break;
- }
-}
-
-void
-HandleMachineMove(message, cps)
- char *message;
- ChessProgramState *cps;
-{
- char machineMove[MSG_SIZ], buf1[MSG_SIZ*10], buf2[MSG_SIZ];
- char realname[MSG_SIZ];
- int fromX, fromY, toX, toY;
- ChessMove moveType;
- char promoChar;
- char *p;
- int machineWhite;
-
- /*
- * Kludge to ignore BEL characters
- */
- while (*message == '\007') message++;
-
- /*
- * Look for book output
- */
- if (cps == &first && bookRequested) {
- if (message[0] == '\t' || message[0] == ' ') {
- /* Part of the book output is here; append it */
- strcat(bookOutput, message);
- strcat(bookOutput, " \n");
- return;
- } else if (bookOutput[0] != NULLCHAR) {
- /* All of book output has arrived; display it */
- char *p = bookOutput;
- while (*p != NULLCHAR) {
- if (*p == '\t') *p = ' ';
- p++;
- }
- DisplayInformation(bookOutput);
- bookRequested = FALSE;
- /* Fall through to parse the current output */
- }
- }
-
- /*
- * Look for machine move.
- */
- if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 &&
- strcmp(buf2, "...") == 0) ||
- (sscanf(message, "%s %s", buf1, machineMove) == 2 &&
- strcmp(buf1, "move") == 0)) {
- /* Save last score befor move for zippy draw handling */
- if (appData.icsActive && appData.zippyDraw) {
- // ZippyDraw(0, programStats.score, programStats.depth);
- }
- /* This method is only useful on engines that support ping */
- if (cps->lastPing != cps->lastPong) {
- if (gameMode == BeginningOfGame) {
- /* Extra move from before last new; ignore */
- if (appData.debugMode) {
- fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);
- }
- } else {
- if (appData.debugMode) {
- fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",
- cps->which, gameMode);
- }
- SendToProgram("undo\n", cps);
- }
- return;
- }
-
- switch (gameMode) {
- case BeginningOfGame:
- /* Extra move from before last reset; ignore */
- if (appData.debugMode) {
- fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);
- }
- return;
-
- case EndOfGame:
- case IcsIdle:
- default:
- /* Extra move after we tried to stop. The mode test is
- not a reliable way of detecting this problem, but it's
- the best we can do on engines that don't support ping.
- */
- if (appData.debugMode) {
- fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",
- cps->which, gameMode);
- }
- SendToProgram("undo\n", cps);
- return;
-
- case MachinePlaysWhite:
- case IcsPlayingWhite:
- machineWhite = TRUE;
- break;
-
- case MachinePlaysBlack:
- case IcsPlayingBlack:
- machineWhite = FALSE;
- break;
-
- case TwoMachinesPlay:
- machineWhite = (cps->twoMachinesColor[0] == 'w');
- break;
- }
- if (WhiteOnMove(forwardMostMove) != machineWhite) {
- if (appData.debugMode) {
- fprintf(debugFP,
- "Ignoring move out of turn by %s, gameMode %d"
- ", forwardMost %d\n",
- cps->which, gameMode, forwardMostMove);
- }
- return;
- }
-
- if (!ParseOneMove(machineMove, forwardMostMove, &moveType,
- &fromX, &fromY, &toX, &toY, &promoChar)) {
- /* Machine move could not be parsed; ignore it. */
- sprintf(buf1, "Illegal move \"%s\" from %s machine",
- machineMove, cps->which);
- /*!!if (appData.debugMode)*/ DisplayError(buf1, 0);
- return;
- }
-
- hintRequested = FALSE;
- lastHint[0] = NULLCHAR;
- bookRequested = FALSE;
- /* Program may be pondering now */
- cps->maybeThinking = TRUE;
- if (cps->sendTime == 2) cps->sendTime = 1;
- if (cps->offeredDraw) cps->offeredDraw--;
-
-#if ZIPPY
- if ((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) &&
- first.initDone) {
- SendMoveToICS(moveType, fromX, fromY, toX, toY);
- ics_user_moved = 1;
- }
-#endif
- /* currentMoveString is set as a side-effect of ParseOneMove */
- strcpy(machineMove, currentMoveString);
- strcat(machineMove, "\n");
- strcpy(moveList[forwardMostMove], machineMove);
-
- MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/
-
- if (gameMode == TwoMachinesPlay) {
- if (cps->other->sendTime) {
- SendTimeRemaining(cps->other,
- cps->other->twoMachinesColor[0] == 'w');
- }
- SendMoveToProgram(forwardMostMove-1, cps->other);
- if (firstMove) {
- firstMove = FALSE;
- if (cps->other->useColors) {
- SendToProgram(cps->other->twoMachinesColor, cps->other);
- }
- SendToProgram("go\n", cps->other);
- }
- cps->other->maybeThinking = TRUE;
- }
-
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
- if (!pausing && appData.ringBellAfterMoves) {
- RingBell();
- }
-
- /*
- * Reenable menu items that were disabled while
- * machine was thinking
- */
- if (gameMode != TwoMachinesPlay)
- SetUserThinkingEnables();
- return;
- }
-
-
- /* Set special modes for chess engines. Later something general
- * could be added here; for now there is just one kludge feature,
- * needed because Crafty 15.10 and earlier don't ignore SIGINT
- * when "xboard" is given as an interactive command.
- */
- if (strncmp(message, "kibitz Hello from Crafty", 24) == 0) {
- cps->useSigint = FALSE;
- cps->useSigterm = FALSE;
- }
-
- /*
- * Look for communication commands
- */
- if (!strncmp(message, "telluser ", 9)) {
- DisplayInformation(message + 9);
- return;
- }
- if (!strncmp(message, "tellusererror ", 14)) {
- DisplayError(message + 14, 0);
- return;
- }
- if (!strncmp(message, "tellopponent ", 13)) {
- if (appData.icsActive) {
- if (loggedOn) {
- sprintf(buf1, "%ssay %s\n", ics_prefix, message + 13);
- SendToICS(buf1);
- }
- } else {
- DisplayInformation(message + 13);
- }
- return;
- }
- if (!strncmp(message, "tellothers ", 11)) {
- if (appData.icsActive) {
- if (loggedOn) {
- sprintf(buf1, "%swhisper %s\n", ics_prefix, message + 11);
- SendToICS(buf1);
- }
- }
- return;
- }
- if (!strncmp(message, "tellall ", 8)) {
- if (appData.icsActive) {
- /* daniel*/
- if (loggedOn && !appData.icsAnalyze) {
- sprintf(buf1, "%skibitz %s\n", ics_prefix, message + 8);
- SendToICS(buf1);
- }
- } else {
- DisplayInformation(message + 8);
- }
- return;
- }
- if (strncmp(message, "warning", 7) == 0) {
- /* Undocumented feature, use tellusererror in new code */
- DisplayError(message, 0);
- return;
- }
- if (sscanf(message, "askuser %s %[^\n]", buf1, buf2) == 2) {
- strcpy(realname, cps->tidy);
- strcat(realname, " query");
- AskQuestion(realname, buf2, buf1, cps->pr);
- return;
- }
- /* Commands from the engine directly to ICS. We don't allow these to be
- * sent until we are logged on. Crafty kibitzes have been known to
- * interfere with the login process.
- */
- if (loggedOn) {
- if (!strncmp(message, "tellics ", 8)) {
- SendToICS(message + 8);
- SendToICS("\n");
- return;
- }
- if (!strncmp(message, "tellicsnoalias ", 15)) {
- SendToICS(ics_prefix);
- SendToICS(message + 15);
- SendToICS("\n");
- return;
- }
- /* The following are for backward compatibility only */
- if (!strncmp(message,"whisper",7) || !strncmp(message,"kibitz",6) ||
- !strncmp(message,"draw",4) || !strncmp(message,"tell",3)) {
- SendToICS(ics_prefix);
- SendToICS(message);
- SendToICS("\n");
- return;
- }
- }
- if (strncmp(message, "feature ", 8) == 0) {
- ParseFeatures(message+8, cps);
- }
- if (sscanf(message, "pong %d", &cps->lastPong) == 1) {
- return;
- }
- /*
- * If the move is illegal, cancel it and redraw the board.
- * Also deal with other error cases. Matching is rather loose
- * here to accommodate engines written before the spec.
- */
- if (strncmp(message + 1, "llegal move", 11) == 0 ||
- strncmp(message, "Error", 5) == 0) {
- if (StrStr(message, "name") ||
- StrStr(message, "rating") || StrStr(message, "?") ||
- StrStr(message, "result") || StrStr(message, "board") ||
- StrStr(message, "bk") || StrStr(message, "computer") ||
- StrStr(message, "variant") || StrStr(message, "hint") ||
- StrStr(message, "random") || StrStr(message, "depth") ||
- StrStr(message, "accepted")) {
- return;
- }
- if (StrStr(message, "protover")) {
- /* Program is responding to input, so it's apparently done
- initializing, and this error message indicates it is
- protocol version 1. So we don't need to wait any longer
- for it to initialize and send feature commands. */
- FeatureDone(cps, 1);
- cps->protocolVersion = 1;
- return;
- }
- cps->maybeThinking = FALSE;
-
- if (StrStr(message, "draw")) {
- /* Program doesn't have "draw" command */
- cps->sendDrawOffers = 0;
- return;
- }
- if (cps->sendTime != 1 &&
- (StrStr(message, "time") || StrStr(message, "otim"))) {
- /* Program apparently doesn't have "time" or "otim" command */
- cps->sendTime = 0;
- return;
- }
- if (StrStr(message, "analyze")) {
- cps->analysisSupport = FALSE;
- cps->analyzing = FALSE;
- Reset(FALSE, TRUE);
- sprintf(buf2, "%s does not support analysis", cps->tidy);
- DisplayError(buf2, 0);
- return;
- }
- if (StrStr(message, "st")) {
- cps->stKludge = TRUE;
- SendTimeControl(cps, movesPerSession, timeControl,
- timeIncrement, appData.searchDepth,
- searchTime);
- return;
- }
- if (StrStr(message, "sd")) {
- cps->sdKludge = TRUE;
- SendTimeControl(cps, movesPerSession, timeControl,
- timeIncrement, appData.searchDepth,
- searchTime);
- return;
- }
- if (!StrStr(message, "llegal")) return;
- if (gameMode == BeginningOfGame || gameMode == EndOfGame ||
- gameMode == IcsIdle) return;
- if (forwardMostMove <= backwardMostMove) return;
- if (cps == &first && programStats.ok_to_send == 0) {
- /* Bogus message from Crafty responding to "." This filtering
- can miss some of the bad messages, but fortunately the bug
- is fixed in current Crafty versions, so it doesn't matter. */
- return;
- }
- if (pausing) PauseEvent();
- if (gameMode == PlayFromGameFile) {
- /* Stop reading this game file */
- gameMode = EditGame;
- ModeHighlight();
- }
-
-
- currentMove = --forwardMostMove;
- DisplayMove(currentMove-1); /* before DisplayMoveError */
- SwitchClocks();
- DisplayBothClocks();
- sprintf(buf1, "Illegal move \"%s\" (rejected by %s chess program)",
- parseList[currentMove], cps->which);
- DisplayMoveError(buf1);
- DrawPosition(FALSE, boards[currentMove]);
- return;
- }
- if (strncmp(message, "time", 4) == 0 && StrStr(message, "Illegal")) {
- /* Program has a broken "time" command that
- outputs a string not ending in newline.
- Don't use it. */
- cps->sendTime = 0;
- }
-
- /*
- * If chess program startup fails, exit with an error message.
- * Attempts to recover here are futile.
- */
- if ((StrStr(message, "unknown host") != NULL)
- || (StrStr(message, "No remote directory") != NULL)
- || (StrStr(message, "not found") != NULL)
- || (StrStr(message, "No such file") != NULL)
- || (StrStr(message, "can't alloc") != NULL)
- || (StrStr(message, "Permission denied") != NULL)) {
-
- cps->maybeThinking = FALSE;
- sprintf(buf1, "Failed to start %s chess program %s on %s: %s\n",
- cps->which, cps->program, cps->host, message);
- RemoveInputSource(cps->isr);
- DisplayFatalError(buf1, 0, 1);
- return;
- }
-
- /*
- * Look for hint output
- */
- if (sscanf(message, "Hint: %s", buf1) == 1) {
- switch (gameMode) {
- case IcsPlayingWhite:
- case IcsPlayingBlack:
- case MachinePlaysWhite:
- case MachinePlaysBlack:
- strcpy(programStats.ponderMove, buf1);
- break;
- default:
- programStats.ponderMove[0] = NULLCHAR;
- break;
- }
- if (cps == &first && hintRequested) {
- hintRequested = FALSE;
- if (ParseOneMove(buf1, forwardMostMove, &moveType,
- &fromX, &fromY, &toX, &toY, &promoChar)) {
- (void) CoordsToAlgebraic(boards[forwardMostMove],
- PosFlags(forwardMostMove), EP_UNKNOWN,
- fromY, fromX, toY, toX, promoChar, buf1);
- sprintf(buf2, "Hint: %s", buf1);
- DisplayInformation(buf2);
- } else {
- /* Hint move could not be parsed!? */
- sprintf(buf2,
- "Illegal hint move \"%s\"\nfrom %s chess program",
- buf1, cps->which);
- DisplayError(buf2, 0);
- }
- } else {
- /* Copy ponder move */
- strcpy(lastHint, buf1);
- }
- return;
- }
-
- /*
- * Ignore other messages if game is not in progress
- */
- if (gameMode == BeginningOfGame || gameMode == EndOfGame ||
- gameMode == IcsIdle || cps->lastPing != cps->lastPong) return;
-
- /*
- * look for win, lose, draw, or draw offer
- */
- if (strncmp(message, "1-0", 3) == 0) {
- char *p, *q, *r = "";
- p = strchr(message, '{');
- if (p) {
- q = strchr(p, '}');
- if (q) {
- *q = NULLCHAR;
- r = p + 1;
- }
- }
- GameEnds(WhiteWins, r, GE_ENGINE);
- return;
- } else if (strncmp(message, "0-1", 3) == 0) {
- char *p, *q, *r = "";
- p = strchr(message, '{');
- if (p) {
- q = strchr(p, '}');
- if (q) {
- *q = NULLCHAR;
- r = p + 1;
- }
- }
- /* Kludge for Arasan 4.1 bug */
- if (strcmp(r, "Black resigns") == 0) {
- GameEnds(WhiteWins, r, GE_ENGINE);
- return;
- }
- GameEnds(BlackWins, r, GE_ENGINE);
- return;
- } else if (strncmp(message, "1/2", 3) == 0) {
- char *p, *q, *r = "";
- p = strchr(message, '{');
- if (p) {
- q = strchr(p, '}');
- if (q) {
- *q = NULLCHAR;
- r = p + 1;
- }
- }
- GameEnds(GameIsDrawn, r, GE_ENGINE);
- return;
-
- } else if (strncmp(message, "White resign", 12) == 0) {
- GameEnds(BlackWins, "White resigns", GE_ENGINE);
- return;
- } else if (strncmp(message, "Black resign", 12) == 0) {
- GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
- return;
- } else if (strncmp(message, "White", 5) == 0 &&
- message[5] != '(' &&
- StrStr(message, "Black") == NULL) {
- GameEnds(WhiteWins, "White mates", GE_ENGINE);
- return;
- } else if (strncmp(message, "Black", 5) == 0 &&
- message[5] != '(') {
- GameEnds(BlackWins, "Black mates", GE_ENGINE);
- return;
- } else if (strcmp(message, "resign") == 0 ||
- strcmp(message, "computer resigns") == 0) {
- switch (gameMode) {
- case MachinePlaysBlack:
- case IcsPlayingBlack:
- GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
- break;
- case MachinePlaysWhite:
- case IcsPlayingWhite:
- GameEnds(BlackWins, "White resigns", GE_ENGINE);
- break;
- case TwoMachinesPlay:
- if (cps->twoMachinesColor[0] == 'w')
- GameEnds(BlackWins, "White resigns", GE_ENGINE);
- else
- GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
- break;
- default:
- /* can't happen */
- break;
- }
- return;
- } else if (strncmp(message, "opponent mates", 14) == 0) {
- switch (gameMode) {
- case MachinePlaysBlack:
- case IcsPlayingBlack:
- GameEnds(WhiteWins, "White mates", GE_ENGINE);
- break;
- case MachinePlaysWhite:
- case IcsPlayingWhite:
- GameEnds(BlackWins, "Black mates", GE_ENGINE);
- break;
- case TwoMachinesPlay:
- if (cps->twoMachinesColor[0] == 'w')
- GameEnds(BlackWins, "Black mates", GE_ENGINE);
- else
- GameEnds(WhiteWins, "White mates", GE_ENGINE);
- break;
- default:
- /* can't happen */
- break;
- }
- return;
- } else if (strncmp(message, "computer mates", 14) == 0) {
- switch (gameMode) {
- case MachinePlaysBlack:
- case IcsPlayingBlack:
- GameEnds(BlackWins, "Black mates", GE_ENGINE);
- break;
- case MachinePlaysWhite:
- case IcsPlayingWhite:
- GameEnds(WhiteWins, "White mates", GE_ENGINE);
- break;
- case TwoMachinesPlay:
- if (cps->twoMachinesColor[0] == 'w')
- GameEnds(WhiteWins, "White mates", GE_ENGINE);
- else
- GameEnds(BlackWins, "Black mates", GE_ENGINE);
- break;
- default:
- /* can't happen */
- break;
- }
- return;
- } else if (strncmp(message, "checkmate", 9) == 0) {
- if (WhiteOnMove(forwardMostMove)) {
- GameEnds(BlackWins, "Black mates", GE_ENGINE);
- } else {
- GameEnds(WhiteWins, "White mates", GE_ENGINE);
- }
- return;
- } else if (strstr(message, "Draw") != NULL ||
- strstr(message, "game is a draw") != NULL) {
- GameEnds(GameIsDrawn, "Draw", GE_ENGINE);
- return;
- } else if (strstr(message, "offer") != NULL &&
- strstr(message, "draw") != NULL) {
-#if ZIPPY
- if (appData.zippyPlay && first.initDone) {
- /* Relay offer to ICS */
- SendToICS(ics_prefix);
- SendToICS("draw\n");
- }
-#endif
- cps->offeredDraw = 2; /* valid until this engine moves twice */
- if (gameMode == TwoMachinesPlay) {
- if (cps->other->offeredDraw) {
- GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
- } else {
- if (cps->other->sendDrawOffers) {
- SendToProgram("draw\n", cps->other);
- }
- }
- } else if (gameMode == MachinePlaysWhite ||
- gameMode == MachinePlaysBlack) {
- if (userOfferedDraw) {
- DisplayInformation("Machine accepts your draw offer");
- GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
- } else {
- DisplayInformation("Machine offers a draw\nSelect Action / Draw to agree");
- }
- }
- }
-
-
- /*
- * Look for thinking output
- */
- /* Using icsAnalyze for future function */
- if (appData.showThinking || appData.icsAnalyze) {
- int plylev, mvleft, mvtot, curscore, time;
- char mvname[MOVE_LEN];
- unsigned long nodes;
- char plyext;
- int ignore = FALSE;
- prefixHint = FALSE;
- mvname[0] = NULLCHAR;
-
- /* reset thinkoutput */
- thinkOutput[0] = NULLCHAR;
-
- switch (gameMode) {
- case MachinePlaysBlack:
- case IcsPlayingBlack:
- if (WhiteOnMove(forwardMostMove)) prefixHint = TRUE;
- break;
- case MachinePlaysWhite:
- case IcsPlayingWhite:
- if (!WhiteOnMove(forwardMostMove)) prefixHint = TRUE;
- break;
- case AnalyzeMode:
- case AnalyzeFile:
- break;
- case IcsObserving:
- ignore = FALSE;
- break;
- case TwoMachinesPlay:
- if ((cps->twoMachinesColor[0] == 'w') !=
- WhiteOnMove(forwardMostMove)) {
- ignore = TRUE;
- }
- break;
- default:
- ignore = TRUE;
- break;
- }
-
- if (!ignore) {
- /* reset ponder move display */
- buf1[0] = NULLCHAR;
- if (sscanf(message, "%d%c %d %d %lu %[^\n]\n",
- &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {
-
- if (plyext != ' ' && plyext != '\t') {
- time *= 100;
- }
- programStats.depth = plylev;
- programStats.nodes = nodes;
- programStats.time = time;
- programStats.score = curscore;
- programStats.got_only_move = 0;
-
- /* Buffer overflow protection */
- if (buf1[0] != NULLCHAR) {
- if (strlen(buf1) >= MSG_SIZ) {
- if (appData.debugMode) fprintf(debugFP, "PV is to long. I use the first %d bytes. \n", MSG_SIZ);
- strncpy(programStats.movelist, buf1, MSG_SIZ);
- } else {
- strcpy(programStats.movelist, buf1);
- }
- } else {
- sprintf(programStats.movelist, " no PV \n");
- if (appData.debugMode) fprintf(debugFP, "I found no PV \n");
- }
-
- if (programStats.seen_stat) {
- programStats.ok_to_send = 1;
- }
-
- if (strchr(programStats.movelist, '(') != NULL) {
- programStats.line_is_book = 1;
- programStats.nr_moves = 0;
- programStats.moves_left = 0;
- } else {
- programStats.line_is_book = 0;
- }
-
- sprintf(thinkOutput, "%d %c%+.2f %s%s%s",
- plylev,
- (gameMode == TwoMachinesPlay ?
- ToUpper(cps->twoMachinesColor[0]) : ' '),
- ((double) programStats.score) / 100.0,
- prefixHint ? lastHint : "",
- prefixHint ? " " : "", buf1);
- /* Using icsAnalyze for future function */
- if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
- appData.icsAnalyzeWindow || appData.AnalysisWindow ||
- gameMode == AnalyzeFile) {
- if (appData.icsAnalyzeWindow ||
- appData.AnalysisWindow) DisplayAnalysis(0,1);
- DisplayMove(currentMove - 1);
- } else {
- if (appData.showThinking) DisplayMove(currentMove - 1);
- }
- return;
-
- } else if ((p=StrStr(message, "(only move)")) != NULL) {
- /* crafty (9.25+) says "(only move) <move>"
- * if there is only 1 legal move
- */
- sscanf(p, "(only move) %s", buf1);
- sprintf(thinkOutput, "%s (only move)", buf1);
- sprintf(programStats.movelist, "%s (only move)", buf1);
- programStats.depth = 2; /* don't use 0 or 1 it's book depth */
- programStats.nr_moves = 1;
- programStats.moves_left = 1;
- programStats.nodes = 1;
- programStats.time = 1;
- programStats.got_only_move = 1;
-
- /* Not really, but we also use this member to
- mean "line isn't going to change" (Crafty
- isn't searching, so stats won't change) */
- programStats.line_is_book = 1;
- if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
- appData.icsAnalyzeWindow || appData.AnalysisWindow ||
- gameMode == AnalyzeFile) {
- if (appData.icsAnalyzeWindow ||
- appData.AnalysisWindow) DisplayAnalysis(0,1);
- DisplayMove(currentMove - 1);
- } else {
- if (appData.showThinking) DisplayMove(currentMove - 1);
- }
- return;
-
- } else if (sscanf(message,"stat01: %d %lu %d %d %d %s",
- &time, &nodes, &plylev, &mvleft,
- &mvtot, mvname) >= 5) {
- /* The stat01: line is from Crafty (9.29+) in response
- to the "." command */
- programStats.seen_stat = 1;
- /* for display engine room */
- supportStat = 1;
- cps->maybeThinking = TRUE;
-
- if (programStats.got_only_move || !appData.periodicUpdates) return;
-
- programStats.depth = plylev;
- programStats.time = time;
- programStats.nodes = nodes;
- programStats.moves_left = mvleft;
- programStats.nr_moves = mvtot;
- strcpy(programStats.move_name, mvname);
- programStats.ok_to_send = 1;
- if (appData.icsAnalyzeWindow ||
- appData.AnalysisWindow) DisplayAnalysis(0,0);
- return;
-
- } else if (strncmp(message,"++",2) == 0) {
- /* Crafty 9.29+ outputs this */
- programStats.got_fail = 2;
- return;
-
- } else if (strncmp(message,"--",2) == 0) {
- /* Crafty 9.29+ outputs this */
- programStats.got_fail = 1;
- return;
-
- } else if (thinkOutput[0] != NULLCHAR &&
- strncmp(message, " ", 4) == 0) {
- p = message;
- while (*p && *p == ' ') p++;
- strcat(thinkOutput, " ");
- strcat(thinkOutput, p);
- strcat(programStats.movelist, " ");
- strcat(programStats.movelist, p);
-
- if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
- appData.icsAnalyzeWindow || appData.AnalysisWindow ||
- gameMode == AnalyzeFile) {
- if (appData.icsAnalyzeWindow ||
- appData.AnalysisWindow) DisplayAnalysis(0,1);
- DisplayMove(currentMove - 1);
- } else {
- if (appData.showThinking) DisplayMove(currentMove - 1);
- }
- return;
-
- }
- }
- }
-}
-
-
-/* Parse a game score from the character string "game", and
- record it as the history of the current game. The game
- score is NOT assumed to start from the standard position.
- The display is not updated in any way.
- */
-void
-ParseGameHistory(game)
- char *game;
-{
- ChessMove moveType;
- int fromX, fromY, toX, toY, boardIndex;
- char promoChar;
- char *p, *q;
- char buf[MSG_SIZ];
-
- if (appData.debugMode)
- fprintf(debugFP, "Parsing game history: %s\n", game);
-
- if (gameInfo.event == NULL) gameInfo.event = StrSave("ICS game");
- gameInfo.site = StrSave(appData.icsHost);
- gameInfo.date = PGNDate();
- gameInfo.round = StrSave("-");
-
- /* Parse out names of players */
- while (*game == ' ') game++;
- p = buf;
- while (*game != ' ') *p++ = *game++;
- *p = NULLCHAR;
- gameInfo.white = StrSave(buf);
- while (*game == ' ') game++;
- p = buf;
- while (*game != ' ' && *game != '\n') *p++ = *game++;
- *p = NULLCHAR;
- gameInfo.black = StrSave(buf);
-
- /* Parse moves */
- boardIndex = blackPlaysFirst ? 1 : 0;
- yynewstr(game);
- for (;;) {
- yyboardindex = boardIndex;
- moveType = (ChessMove) yylex();
- switch (moveType) {
- case WhitePromotionQueen:
- case BlackPromotionQueen:
- case WhitePromotionRook:
- case BlackPromotionRook:
- case WhitePromotionBishop:
- case BlackPromotionBishop:
- case WhitePromotionKnight:
- case BlackPromotionKnight:
- case WhitePromotionKing:
- case BlackPromotionKing:
- case NormalMove:
- case WhiteCapturesEnPassant:
- case BlackCapturesEnPassant:
- case WhiteKingSideCastle:
- case WhiteQueenSideCastle:
- case BlackKingSideCastle:
- case BlackQueenSideCastle:
- case WhiteKingSideCastleWild:
- case WhiteQueenSideCastleWild:
- case BlackKingSideCastleWild:
- case BlackQueenSideCastleWild:
- case IllegalMove: /* maybe suicide chess, etc. */
- fromX = currentMoveString[0] - 'a';
- fromY = currentMoveString[1] - '1';
- toX = currentMoveString[2] - 'a';
- toY = currentMoveString[3] - '1';
- promoChar = currentMoveString[4];
- break;
- case WhiteDrop:
- case BlackDrop:
- fromX = moveType == WhiteDrop ?
- (int) CharToPiece(ToUpper(currentMoveString[0])) :
- (int) CharToPiece(ToLower(currentMoveString[0]));
- fromY = DROP_RANK;
- toX = currentMoveString[2] - 'a';
- toY = currentMoveString[3] - '1';
- promoChar = NULLCHAR;
- break;
- case AmbiguousMove:
- /* bug? */
- sprintf(buf, "Ambiguous move in ICS output: \"%s\"", yy_text);
- DisplayError(buf, 0);
- return;
- case ImpossibleMove:
- /* bug? */
- sprintf(buf, "Illegal move in ICS output: \"%s\"", yy_text);
- DisplayError(buf, 0);
- return;
- case (ChessMove) 0: /* end of file */
- if (boardIndex < backwardMostMove) {
- /* Oops, gap. How did that happen? */
- DisplayError("Gap in move list", 0);
- return;
- }
- backwardMostMove = blackPlaysFirst ? 1 : 0;
- if (boardIndex > forwardMostMove) {
- forwardMostMove = boardIndex;
- }
- return;
- case ElapsedTime:
- if (boardIndex > 0) {
- strcat(parseList[boardIndex-1], " ");
- strcat(parseList[boardIndex-1], yy_text);
- }
- continue;
- case Comment:
- case PGNTag:
- case NAG:
- default:
- /* ignore */
- continue;
- case WhiteWins:
- case BlackWins:
- case GameIsDrawn:
- case GameUnfinished:
- if (gameMode == IcsExamining) {
- if (boardIndex < backwardMostMove) {
- /* Oops, gap. How did that happen? */
- return;
- }
- backwardMostMove = blackPlaysFirst ? 1 : 0;
- return;
- }
- gameInfo.result = moveType;
- p = strchr(yy_text, '{');
- if (p == NULL) p = strchr(yy_text, '(');
- if (p == NULL) {
- p = yy_text;
- if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";
- } else {
- q = strchr(p, *p == '{' ? '}' : ')');
- if (q != NULL) *q = NULLCHAR;
- p++;
- }
- gameInfo.resultDetails = StrSave(p);
- continue;
- }
- if (boardIndex >= forwardMostMove &&
- !(gameMode == IcsObserving && ics_gamenum == -1)) {
- backwardMostMove = blackPlaysFirst ? 1 : 0;
- return;
- }
- (void) CoordsToAlgebraic(boards[boardIndex], PosFlags(boardIndex),
- EP_UNKNOWN, fromY, fromX, toY, toX, promoChar,
- parseList[boardIndex]);
- CopyBoard(boards[boardIndex + 1], boards[boardIndex]);
- /* currentMoveString is set as a side-effect of yylex */
- strcpy(moveList[boardIndex], currentMoveString);
- strcat(moveList[boardIndex], "\n");
- boardIndex++;
- ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);
- switch (MateTest(boards[boardIndex],
- PosFlags(boardIndex), EP_UNKNOWN)) {
- case MT_NONE:
- case MT_STALEMATE:
- default:
- break;
- case MT_CHECK:
- strcat(parseList[boardIndex - 1], "+");
- break;
- case MT_CHECKMATE:
- strcat(parseList[boardIndex - 1], "#");
- break;
- }
- }
-}
-
-
-/* Apply a move to the given board */
-void
-ApplyMove(fromX, fromY, toX, toY, promoChar, board)
- int fromX, fromY, toX, toY;
- int promoChar;
- Board board;
-{
- ChessSquare captured = board[toY][toX];
- if (fromY == DROP_RANK) {
- /* must be first */
- board[toY][toX] = (ChessSquare) fromX;
- } else if (fromX == toX && fromY == toY) {
- return;
- } else if (fromY == 0 && fromX == 4
- && board[fromY][fromX] == WhiteKing
- && toY == 0 && toX == 6) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = WhiteKing;
- board[fromY][7] = EmptySquare;
- board[toY][5] = WhiteRook;
- } else if (fromY == 0 && fromX == 4
- && board[fromY][fromX] == WhiteKing
- && toY == 0 && toX == 2) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = WhiteKing;
- board[fromY][0] = EmptySquare;
- board[toY][3] = WhiteRook;
- } else if (fromY == 0 && fromX == 3
- && board[fromY][fromX] == WhiteKing
- && toY == 0 && toX == 5) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = WhiteKing;
- board[fromY][7] = EmptySquare;
- board[toY][4] = WhiteRook;
- } else if (fromY == 0 && fromX == 3
- && board[fromY][fromX] == WhiteKing
- && toY == 0 && toX == 1) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = WhiteKing;
- board[fromY][0] = EmptySquare;
- board[toY][2] = WhiteRook;
- } else if (board[fromY][fromX] == WhitePawn
- && toY == 7) {
- /* white pawn promotion */
- board[7][toX] = CharToPiece(ToUpper(promoChar));
- if (board[7][toX] == EmptySquare) {
- board[7][toX] = WhiteQueen;
- }
- board[fromY][fromX] = EmptySquare;
- } else if ((fromY == 4)
- && (toX != fromX)
- && (board[fromY][fromX] == WhitePawn)
- && (board[toY][toX] == EmptySquare)) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = WhitePawn;
- captured = board[toY - 1][toX];
- board[toY - 1][toX] = EmptySquare;
- } else if (fromY == 7 && fromX == 4
- && board[fromY][fromX] == BlackKing
- && toY == 7 && toX == 6) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = BlackKing;
- board[fromY][7] = EmptySquare;
- board[toY][5] = BlackRook;
- } else if (fromY == 7 && fromX == 4
- && board[fromY][fromX] == BlackKing
- && toY == 7 && toX == 2) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = BlackKing;
- board[fromY][0] = EmptySquare;
- board[toY][3] = BlackRook;
- } else if (fromY == 7 && fromX == 3
- && board[fromY][fromX] == BlackKing
- && toY == 7 && toX == 5) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = BlackKing;
- board[fromY][7] = EmptySquare;
- board[toY][4] = BlackRook;
- } else if (fromY == 7 && fromX == 3
- && board[fromY][fromX] == BlackKing
- && toY == 7 && toX == 1) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = BlackKing;
- board[fromY][0] = EmptySquare;
- board[toY][2] = BlackRook;
- } else if (board[fromY][fromX] == BlackPawn
- && toY == 0) {
- /* black pawn promotion */
- board[0][toX] = CharToPiece(ToLower(promoChar));
- if (board[0][toX] == EmptySquare) {
- board[0][toX] = BlackQueen;
- }
- board[fromY][fromX] = EmptySquare;
- } else if ((fromY == 3)
- && (toX != fromX)
- && (board[fromY][fromX] == BlackPawn)
- && (board[toY][toX] == EmptySquare)) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = BlackPawn;
- captured = board[toY + 1][toX];
- board[toY + 1][toX] = EmptySquare;
- } else {
- board[toY][toX] = board[fromY][fromX];
- board[fromY][fromX] = EmptySquare;
- }
- if (gameInfo.variant == VariantCrazyhouse) {
-#if 0
- /* !!A lot more code needs to be written to support holdings */
- if (fromY == DROP_RANK) {
- /* Delete from holdings */
- if (holdings[(int) fromX] > 0) holdings[(int) fromX]--;
- }
- if (captured != EmptySquare) {
- /* Add to holdings */
- if (captured < BlackPawn) {
- holdings[(int)captured - (int)BlackPawn + (int)WhitePawn]++;
- } else {
- holdings[(int)captured - (int)WhitePawn + (int)BlackPawn]++;
- }
- }
-#endif
- } else if (gameInfo.variant == VariantAtomic) {
- if (captured != EmptySquare) {
- int y, x;
- for (y = toY-1; y <= toY+1; y++) {
- for (x = toX-1; x <= toX+1; x++) {
- if (y >= 0 && y <= 7 && x >= 0 && x <= 7 &&
- board[y][x] != WhitePawn && board[y][x] != BlackPawn) {
- board[y][x] = EmptySquare;
- }
- }
- }
- board[toY][toX] = EmptySquare;
- }
- }
-}
-
-/* Updates forwardMostMove */
-void
-MakeMove(fromX, fromY, toX, toY, promoChar)
- int fromX, fromY, toX, toY;
- int promoChar;
-{
- forwardMostMove++;
- if (forwardMostMove >= MAX_MOVES) {
- DisplayFatalError("Game too long; increase MAX_MOVES and recompile",
- 0, 1);
- return;
- }
- SwitchClocks();
- timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
- timeRemaining[1][forwardMostMove] = blackTimeRemaining;
- if (commentList[forwardMostMove] != NULL) {
- free(commentList[forwardMostMove]);
- commentList[forwardMostMove] = NULL;
- }
- CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]);
- ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove]);
- gameInfo.result = GameUnfinished;
- if (gameInfo.resultDetails != NULL) {
- free(gameInfo.resultDetails);
- gameInfo.resultDetails = NULL;
- }
- CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar,
- moveList[forwardMostMove - 1]);
- (void) CoordsToAlgebraic(boards[forwardMostMove - 1],
- PosFlags(forwardMostMove - 1), EP_UNKNOWN,
- fromY, fromX, toY, toX, promoChar,
- parseList[forwardMostMove - 1]);
- switch (MateTest(boards[forwardMostMove],
- PosFlags(forwardMostMove), EP_UNKNOWN)){
- case MT_NONE:
- case MT_STALEMATE:
- default:
- break;
- case MT_CHECK:
- strcat(parseList[forwardMostMove - 1], "+");
- break;
- case MT_CHECKMATE:
- strcat(parseList[forwardMostMove - 1], "#");
- break;
- }
-}
-
-/* Updates currentMove if not pausing */
-void
-ShowMove(fromX, fromY, toX, toY)
-{
- int instant = (gameMode == PlayFromGameFile) ?
- (matchMode || (appData.timeDelay == 0 && !pausing)) : pausing;
- if (!pausing || gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
- if (!instant) {
- if (forwardMostMove == currentMove + 1) {
- AnimateMove(boards[forwardMostMove - 1],
- fromX, fromY, toX, toY);
- }
- if (appData.highlightLastMove) {
- SetHighlights(fromX, fromY, toX, toY);
- }
- }
- currentMove = forwardMostMove;
- }
-
- if (instant) return;
- DisplayMove(currentMove - 1);
- DrawPosition(FALSE, boards[currentMove]);
- DisplayBothClocks();
- HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
-}
-
-
-void
-InitChessProgram(cps)
- ChessProgramState *cps;
-{
- char buf[MSG_SIZ];
- if (appData.noChessProgram) return;
- hintRequested = FALSE;
- bookRequested = FALSE;
- SendToProgram(cps->initString, cps);
- if (gameInfo.variant != VariantNormal &&
- gameInfo.variant != VariantLoadable) {
- char *v = VariantName(gameInfo.variant);
- if (StrStr(cps->variants, v) == NULL) {
- sprintf(buf, "Variant %s not supported by %s", v, cps->tidy);
- DisplayFatalError(buf, 0, 1);
- return;
- }
- sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
- SendToProgram(buf, cps);
- }
- if (cps->sendICS) {
- sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-");
- SendToProgram(buf, cps);
- }
- cps->maybeThinking = FALSE;
- cps->offeredDraw = 0;
- if (!appData.icsActive) {
- SendTimeControl(cps, movesPerSession, timeControl,
- timeIncrement, appData.searchDepth,
- searchTime);
- }
- if (appData.showThinking) {
- SendToProgram("post\n", cps);
- }
- SendToProgram("hard\n", cps);
- if (!appData.ponderNextMove) {
- /* Warning: "easy" is a toggle in GNU Chess, so don't send
- it without being sure what state we are in first. "hard"
- is not a toggle, so that one is OK.
- */
- SendToProgram("easy\n", cps);
- }
- if (cps->usePing) {
- sprintf(buf, "ping %d\n", ++cps->lastPing);
- SendToProgram(buf, cps);
- }
- cps->initDone = TRUE;
-}
-
-
-void
-StartChessProgram(cps)
- ChessProgramState *cps;
-{
- char buf[MSG_SIZ];
- int err;
-
- if (appData.noChessProgram) return;
- cps->initDone = FALSE;
-
- if (strcmp(cps->host, "localhost") == 0) {
- err = StartChildProcess(cps->program, cps->dir, &cps->pr);
- } else if (*appData.remoteShell == NULLCHAR) {
- err = OpenRcmd(cps->host, appData.remoteUser, cps->program, &cps->pr);
- } else {
- if (*appData.remoteUser == NULLCHAR) {
- sprintf(buf, "%s %s %s", appData.remoteShell, cps->host,
- cps->program);
- } else {
- sprintf(buf, "%s %s -l %s %s", appData.remoteShell,
- cps->host, appData.remoteUser, cps->program);
- }
- err = StartChildProcess(buf, "", &cps->pr);
- }
-
- if (err != 0) {
- sprintf(buf, "Startup failure on '%s'", cps->program);
- DisplayFatalError(buf, err, 1);
- cps->pr = NoProc;
- cps->isr = NULL;
- return;
- }
-
- cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps);
- if (cps->protocolVersion > 1) {
- sprintf(buf, "xboard\nprotover %d\n", cps->protocolVersion);
- SendToProgram(buf, cps);
- } else {
- SendToProgram("xboard\n", cps);
- }
-}
-
-
-void
-TwoMachinesEventIfReady P((void))
-{
- if (first.lastPing != first.lastPong) {
- DisplayMessage("", "Waiting for first chess program");
- ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);
- return;
- }
- if (second.lastPing != second.lastPong) {
- DisplayMessage("", "Waiting for second chess program");
- ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);
- return;
- }
- ThawUI();
- TwoMachinesEvent();
-}
-
-void
-NextMatchGame P((void))
-{
- Reset(FALSE, TRUE);
- if (*appData.loadGameFile != NULLCHAR) {
- LoadGameFromFile(appData.loadGameFile,
- appData.loadGameIndex,
- appData.loadGameFile, FALSE);
- } else if (*appData.loadPositionFile != NULLCHAR) {
- LoadPositionFromFile(appData.loadPositionFile,
- appData.loadPositionIndex,
- appData.loadPositionFile);
- }
- TwoMachinesEventIfReady();
-}
-
-void
-GameEnds(result, resultDetails, whosays)
- ChessMove result;
- char *resultDetails;
- int whosays;
-{
- GameMode nextGameMode;
- int isIcsGame;
-
- if (appData.debugMode) {
- fprintf(debugFP, "GameEnds(%d, %s, %d)\n",
- result, resultDetails ? resultDetails : "(null)", whosays);
- }
-
- if (appData.icsAnalyze && gameMode == IcsObserving) ResetIcsQueue(ics_gamenum);
-
- if (appData.icsActive && whosays == GE_ENGINE) {
- /* If we are playing on ICS, the server decides when the
- game is over, but the engine can offer to draw, claim
- a draw, or resign.
- */
-#if ZIPPY
- if (appData.zippyPlay && first.initDone) {
- if (result == GameIsDrawn) {
- /* In case draw still needs to be claimed */
- SendToICS(ics_prefix);
- SendToICS("draw\n");
- } else if (StrCaseStr(resultDetails, "resign")) {
- SendToICS(ics_prefix);
- SendToICS("resign\n");
- }
- }
-#endif
- return;
- }
-
- /* If we're loading the game from a file, stop */
- if (whosays == GE_FILE) {
- (void) StopLoadGameTimer();
- gameFileFP = NULL;
- }
-
- /* Cancel draw offers */
- first.offeredDraw = second.offeredDraw = 0;
-
- /* If this is an ICS game, only ICS can really say it's done;
- if not, anyone can. */
- isIcsGame = (gameMode == IcsPlayingWhite ||
- gameMode == IcsPlayingBlack ||
- gameMode == IcsObserving ||
- gameMode == IcsExamining);
-
- if (!isIcsGame || whosays == GE_ICS) {
- /* OK -- not an ICS game, or ICS said it was done */
- StopClocks();
- if (!isIcsGame && !appData.noChessProgram)
- SetUserThinkingEnables();
-
- if (resultDetails != NULL) {
- gameInfo.result = result;
- gameInfo.resultDetails = StrSave(resultDetails);
-
- /* Tell program how game ended in case it is learning */
- if (gameMode == MachinePlaysWhite ||
- gameMode == MachinePlaysBlack ||
- gameMode == TwoMachinesPlay ||
- gameMode == IcsPlayingWhite ||
- gameMode == IcsPlayingBlack ||
- gameMode == BeginningOfGame) {
- char buf[MSG_SIZ];
- sprintf(buf, "result %s {%s}\n", PGNResult(result),
- resultDetails);
- if (first.pr != NoProc) {
- SendToProgram(buf, &first);
- }
- if (second.pr != NoProc &&
- gameMode == TwoMachinesPlay) {
- SendToProgram(buf, &second);
- }
- }
-
- /* display last move only if game was not loaded from file */
- if ((whosays != GE_FILE) && (currentMove == forwardMostMove))
- DisplayMove(currentMove - 1);
-
- if (forwardMostMove != 0) {
- if (gameMode != PlayFromGameFile && gameMode != EditGame) {
- if (*appData.saveGameFile != NULLCHAR) {
- SaveGameToFile(appData.saveGameFile, TRUE);
- } else if (appData.autoSaveGames) {
- AutoSaveGame();
- }
- if (*appData.savePositionFile != NULLCHAR) {
- SavePositionToFile(appData.savePositionFile);
- }
- }
- }
- }
-
- if (appData.icsActive) {
- if (appData.quietPlay &&
- (gameMode == IcsPlayingWhite ||
- gameMode == IcsPlayingBlack)) {
- SendToICS(ics_prefix);
- SendToICS("set shout 1\n");
- }
- nextGameMode = IcsIdle;
- ics_user_moved = FALSE;
- /* clean up premove. It's ugly when the game has ended and the
- * premove highlights are still on the board.
- */
- if (gotPremove) {
- gotPremove = FALSE;
- ClearPremoveHighlights();
- DrawPosition(FALSE, boards[currentMove]);
- }
- if (whosays == GE_ICS) {
- switch (result) {
- case WhiteWins:
- if (gameMode == IcsPlayingWhite)
- PlayIcsWinSound();
- else if(gameMode == IcsPlayingBlack)
- PlayIcsLossSound();
- break;
- case BlackWins:
- if (gameMode == IcsPlayingBlack)
- PlayIcsWinSound();
- else if(gameMode == IcsPlayingWhite)
- PlayIcsLossSound();
- break;
- case GameIsDrawn:
- PlayIcsDrawSound();
- break;
- default:
- PlayIcsUnfinishedSound();
- }
- }
- } else if (gameMode == EditGame ||
- gameMode == PlayFromGameFile ||
- gameMode == AnalyzeMode ||
- gameMode == AnalyzeFile) {
- nextGameMode = gameMode;
- } else {
- nextGameMode = EndOfGame;
- }
- pausing = FALSE;
- ModeHighlight();
- } else {
- nextGameMode = gameMode;
- }
-
- if (appData.noChessProgram) {
- gameMode = nextGameMode;
- ModeHighlight();
- return;
- }
-
- if (first.reuse) {
- /* Put first chess program into idle state */
- if (first.pr != NoProc &&
- (gameMode == MachinePlaysWhite ||
- gameMode == MachinePlaysBlack ||
- gameMode == TwoMachinesPlay ||
- gameMode == IcsPlayingWhite ||
- gameMode == IcsPlayingBlack ||
- gameMode == BeginningOfGame)) {
- SendToProgram("force\n", &first);
- if (first.usePing) {
- char buf[MSG_SIZ];
- sprintf(buf, "ping %d\n", ++first.lastPing);
- SendToProgram(buf, &first);
- }
- }
- } else if (result != GameUnfinished || nextGameMode == IcsIdle) {
- /* Kill off first chess program */
- if (first.isr != NULL)
- RemoveInputSource(first.isr);
- first.isr = NULL;
-
- if (first.pr != NoProc) {
- ExitAnalyzeMode();
- SendToProgram("quit\n", &first);
- DestroyChildProcess(first.pr, first.useSigterm);
- }
- first.pr = NoProc;
- }
- if (second.reuse) {
- /* Put second chess program into idle state */
- if (second.pr != NoProc &&
- gameMode == TwoMachinesPlay) {
- SendToProgram("force\n", &second);
- if (second.usePing) {
- char buf[MSG_SIZ];
- sprintf(buf, "ping %d\n", ++second.lastPing);
- SendToProgram(buf, &second);
- }
- }
- } else if (result != GameUnfinished || nextGameMode == IcsIdle) {
- /* Kill off second chess program */
- if (second.isr != NULL)
- RemoveInputSource(second.isr);
- second.isr = NULL;
-
- if (second.pr != NoProc) {
- SendToProgram("quit\n", &second);
- DestroyChildProcess(second.pr, second.useSigterm);
- }
- second.pr = NoProc;
- }
-
- if (matchMode && gameMode == TwoMachinesPlay) {
- switch (result) {
- case WhiteWins:
- if (first.twoMachinesColor[0] == 'w') {
- first.matchWins++;
- } else {
- second.matchWins++;
- }
- break;
- case BlackWins:
- if (first.twoMachinesColor[0] == 'b') {
- first.matchWins++;
- } else {
- second.matchWins++;
- }
- break;
- default:
- break;
- }
- if (matchGame < appData.matchGames) {
- char *tmp;
- tmp = first.twoMachinesColor;
- first.twoMachinesColor = second.twoMachinesColor;
- second.twoMachinesColor = tmp;
- gameMode = nextGameMode;
- matchGame++;
- ScheduleDelayedEvent(NextMatchGame, 10000);
- return;
- } else {
- char buf[MSG_SIZ];
- gameMode = nextGameMode;
- sprintf(buf, "Match %s vs. %s: final score %d-%d-%d",
- first.tidy, second.tidy,
- first.matchWins, second.matchWins,
- appData.matchGames - (first.matchWins + second.matchWins));
- DisplayFatalError(buf, 0, 0);
- }
- }
- if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
- !(nextGameMode == AnalyzeMode || nextGameMode == AnalyzeFile))
- ExitAnalyzeMode();
- gameMode = nextGameMode;
- ModeHighlight();
-}
-
-/* Assumes program was just initialized (initString sent).
- Leaves program in force mode. */
-void
-FeedMovesToProgram(cps, upto)
- ChessProgramState *cps;
- int upto;
-
-{
- int i;
- if (appData.debugMode)
- fprintf(debugFP, "Feeding %smoves %d through %d to %s chess program\n",
- startedFromSetupPosition ? "position and " : "",
- backwardMostMove, upto, cps->which);
- /* daniel */
- if(!appData.icsAnalyze) SendToProgram("force\n", cps);
- if (startedFromSetupPosition) {
- SendBoard(cps, backwardMostMove);
- }
- for (i = backwardMostMove; i < upto; i++) {
-
- SendMoveToProgram(i, cps);
- }
-}
-
-
-void
-ResurrectChessProgram()
-{
- /* The chess program may have exited.
- If so, restart it and feed it all the moves made so far. */
-
- if (appData.noChessProgram || first.pr != NoProc) return;
-
- StartChessProgram(&first);
- InitChessProgram(&first);
- FeedMovesToProgram(&first, currentMove);
-
- if (!first.sendTime) {
- /* can't tell gnuchess what its clock should read,
- so we bow to its notion. */
- ResetClocks();
- timeRemaining[0][currentMove] = whiteTimeRemaining;
- timeRemaining[1][currentMove] = blackTimeRemaining;
- }
-
- if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
- first.analysisSupport) {
- SendToProgram("analyze\n", &first);
- first.analyzing = TRUE;
- }
-}
-
-/*
- * Button procedures
- */
-void
-Reset(redraw, init)
- int redraw, init;
-{
- int i;
-
- if (appData.debugMode) {
- fprintf(debugFP, "Reset(%d, %d) from gameMode %d\n",
- redraw, init, gameMode);
- }
-
- /* reset send engine output to ics */
- appData.SendOutPutToICS = 1;
-
- pausing = pauseExamInvalid = FALSE;
- startedFromSetupPosition = blackPlaysFirst = FALSE;
- firstMove = TRUE;
- whiteFlag = blackFlag = FALSE;
- userOfferedDraw = FALSE;
- hintRequested = bookRequested = FALSE;
- first.maybeThinking = FALSE;
- second.maybeThinking = FALSE;
- thinkOutput[0] = NULLCHAR;
- lastHint[0] = NULLCHAR;
- ClearGameInfo(&gameInfo);
- gameInfo.variant = StringToVariant(appData.variant);
- ics_user_moved = ics_clock_paused = FALSE;
- ics_getting_history = H_FALSE;
- ics_gamenum = -1;
- white_holding[0] = black_holding[0] = NULLCHAR;
- ClearProgramStats();
-
- ResetFrontEnd();
- ClearHighlights();
- flipView = appData.flipView;
- ClearPremoveHighlights();
- gotPremove = FALSE;
- alarmSounded = FALSE;
-
- GameEnds((ChessMove) 0, NULL, GE_PLAYER);
- ExitAnalyzeMode();
- gameMode = BeginningOfGame;
- ModeHighlight();
- InitPosition(redraw);
- for (i = 0; i < MAX_MOVES; i++) {
- if (commentList[i] != NULL) {
- free(commentList[i]);
- commentList[i] = NULL;
- }
- }
- ResetClocks();
- timeRemaining[0][0] = whiteTimeRemaining;
- timeRemaining[1][0] = blackTimeRemaining;
- if (first.pr == NULL) {
- StartChessProgram(&first);
- }
- if (init) InitChessProgram(&first);
- DisplayTitle("");
- DisplayMessage("", "");
- HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
-}
-
-void
-AutoPlayGameLoop()
-{
- for (;;) {
- if (!AutoPlayOneMove())
- return;
- if (matchMode || appData.timeDelay == 0)
- continue;
- if (appData.timeDelay < 0 || gameMode == AnalyzeFile)
- return;
- StartLoadGameTimer((long)(1000.0 * appData.timeDelay));
- break;
- }
-}
-
-
-int
-AutoPlayOneMove()
-{
- int fromX, fromY, toX, toY;
-
- if (appData.debugMode) {
- fprintf(debugFP, "AutoPlayOneMove(): current %d\n", currentMove);
- }
-
- if (gameMode != PlayFromGameFile)
- return FALSE;
-
- if (currentMove >= forwardMostMove) {
- gameMode = EditGame;
- ModeHighlight();
- return FALSE;
- }
-
- toX = moveList[currentMove][2] - 'a';
- toY = moveList[currentMove][3] - '1';
-
- if (moveList[currentMove][1] == '@') {
- if (appData.highlightLastMove) {
- SetHighlights(-1, -1, toX, toY);
- }
- } else {
- fromX = moveList[currentMove][0] - 'a';
- fromY = moveList[currentMove][1] - '1';
- AnimateMove(boards[currentMove], fromX, fromY