home *** CD-ROM | disk | FTP | other *** search
/ High Voltage Shareware / high1.zip / high1 / DIR2 / CBUFF09.ZIP / SRC.ZIP / GAME.C < prev    next >
C/C++ Source or Header  |  1993-11-16  |  31KB  |  998 lines

  1. /*  $Id: game.c,v 1.1.1.1 1993/06/21 11:11:59 anjo Exp $
  2.  *  
  3.  *  File    game.c
  4.  *  Part of    ChessBase utilities file format (CBUFF)
  5.  *  Author    Anjo Anjewierden, anjo@swi.psy.uva.nl
  6.  *  Purpose    Representation of a chess game
  7.  *  Works with    GNU CC 2.4.5
  8.  *  
  9.  *  Notice    Copyright (c) 1993  Anjo Anjewierden
  10.  *  
  11.  *  History    09/06/93  (Created)
  12.  *          03/11/93  (Last modified)
  13.  */ 
  14.  
  15.  
  16. /*------------------------------------------------------------
  17.  *  Directives
  18.  *------------------------------------------------------------*/
  19.  
  20. #include "cbuff.h"
  21.  
  22.  
  23. /*------------------------------------------------------------
  24.  *  Definitions
  25.  *------------------------------------------------------------*/
  26.  
  27. static void    requireHeaderGame(Game);
  28. static void    requireMovesGame(Game);
  29.  
  30.  
  31. /*------------------------------------------------------------
  32.  *  Initialisation
  33.  *------------------------------------------------------------*/
  34.  
  35. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  36. @node newGame
  37. @deftypefun Game newGame ()
  38. Returns a new instance of a (chess) game structure.  The instance
  39. returned is initialised.
  40. @end deftypefun
  41. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  42.  
  43. Game
  44. newGame()
  45. { Game g;
  46.  
  47.   g = alloc(sizeof(struct game));
  48.   g->cbGame = NULL;
  49.   g->firstMove = NULL;
  50.   g->position = NULL;
  51.  
  52.   resetGame(g);
  53.  
  54.   return g;
  55. }
  56.  
  57.  
  58. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  59. @node freeGame
  60. @deftypefun void freeGame (Game @var{g})
  61. Unallocates the game.
  62. @end deftypefun
  63. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  64.  
  65. void
  66. freeGame(Game g)
  67. { resetGame(g);
  68.   unalloc(g);
  69. }
  70.  
  71.  
  72. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  73. @node resetGame
  74. @deftypefun void resetGame (Game @var{g})
  75. Resets the game such that it may be used to load another.
  76. @code{resetGame} is conceptually identical to:
  77. @example
  78. freeGame(g);
  79. g = newGame();
  80. @end example
  81. @end deftypefun
  82. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  83.  
  84. void
  85. resetGame(Game g)
  86. { if (g->firstMove)
  87.   { freeMove(g->firstMove);
  88.     g->firstMove = NULL;
  89.   }
  90.   if (g->cbGame)
  91.   { freeCbGame(g->cbGame);
  92.     g->cbGame = NULL;
  93.   }
  94.   if (g->position)
  95.   { freePosition(g->position);
  96.     g->position = NULL;
  97.   }
  98.   g->number = 0L;
  99.   g->database = NULL;
  100.   g->flags = 0;
  101.   g->players[0] = '\0';
  102.   g->source[0] = '\0';
  103.   g->year = 0;
  104.   g->result = 0;
  105.   g->eloWhite = 0;
  106.   g->eloBlack = 0;
  107.   g->eco[0] = '\0';
  108.   g->moves = 0;
  109.   g->plies = 0;
  110.   g->place = NullString;
  111.   g->event = NullString;
  112.   g->annotator = NullString;
  113. }
  114.  
  115.  
  116. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  117. @node initialiseGame
  118. @deftypefun bool initialiseGame (Game @var{g}, long @var{number}, CBase @var{database})
  119. Initialises a game to be read from an existing ChessBase database.  The
  120. @var{number} is the game number relative to the @var{database}.
  121. @end deftypefun
  122. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  123.  
  124. bool
  125. initialiseGame(Game g, long number, CBase database)
  126. { if (g->flags & GAME_INIT_OK)
  127.     resetGame(g);
  128.  
  129.   if (number < 1 || number > database->noGames)
  130.   { setError(ERR_GAME_NUMBER_OUT_OF_RANGE);
  131.     return FALSE;
  132.   }
  133.  
  134.   g->number = number;
  135.   g->database = database;
  136.   g->flags |= GAME_INIT_OK;  
  137.  
  138.   return TRUE;
  139. }
  140.  
  141.  
  142. /*------------------------------------------------------------
  143.  *  Accessing game properties
  144.  *------------------------------------------------------------*/
  145.  
  146. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  147. @node flags2Bit6GameP
  148. @deftypefun bool flags2Bit6GameP (Game @var{g})
  149. Succeeds if bit 6 of the flags2 field of the game header is set.  This
  150. bit apparently decides on the character set being used, but is still a
  151. slightly mysterious.  At the moment, only games that this bit set print
  152. all comments correctly.
  153. @end deftypefun
  154. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  155.  
  156. bool
  157. flags2Bit6GameP(Game g)
  158. { return (g->flags & GAME_FLAGS2_BIT6 ? TRUE : FALSE);
  159. }
  160.  
  161.  
  162. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  163. @node fullGameP
  164. @deftypefun bool fullGameP (Game @var{g})
  165. Succeeds if the game is a complete game (starting from the initial position), 
  166. and fails if a starting position is provided with the game.
  167. @end deftypefun
  168. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  169.  
  170. bool
  171. fullGameP(Game g)
  172. { requireHeaderGame(g);
  173.   return (g->flags & GAME_INITIAL_POSITION ? TRUE : FALSE);
  174. }
  175.  
  176.  
  177. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  178. @node deletedGameP
  179. @deftypefun bool deletedGameP (Game @var{g})
  180. Succeeds if the game has been logically deleted from the database.  A
  181. game is fysically deleted by the @file{cbfresh} utility provided with
  182. ChessBase.
  183. @end deftypefun
  184. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  185.  
  186. bool
  187. deletedGameP(Game g)
  188. { requireHeaderGame(g);
  189.   return (g->flags & GAME_DELETED ? TRUE : FALSE);
  190. }
  191.  
  192.  
  193. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  194. @node markedGameP
  195. @deftypefun bool markedGameP (Game @var{g})
  196. Succeeds if the game has been marked in the database.  The meaning of a
  197. marked game is up to the user.  It presumably means the game is more
  198. interesting than the unmarked games in the database.
  199. @end deftypefun
  200. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  201.  
  202. bool
  203. markedGameP(Game g)
  204. { requireHeaderGame(g);
  205.   return (g->flags & GAME_MARKED ? TRUE : FALSE);
  206. }
  207.  
  208.  
  209. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  210. @node containsCommentsGameP
  211. @deftypefun bool containsCommentsGameP (Game @var{g})
  212. Succeeds if the game contains comments for at least one of the moves.
  213. Examples of comments are ``@code{!!}'', ``@code{+-}'' and ``This is
  214. a great move''.
  215. @end deftypefun
  216. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  217.  
  218. bool
  219. containsCommentsGameP(Game g)
  220. { requireHeaderGame(g);
  221.   if (g->cbGame->commentLength)
  222.     return TRUE;
  223.   return FALSE;
  224. }
  225.  
  226.  
  227. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  228. @node containsVariationsGameP
  229. @deftypefun bool containsVariationsGameP (Game @var{g})
  230. Succeeds if the game contains at least one variation.
  231. @end deftypefun
  232. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  233.  
  234. bool
  235. containsVariationsGameP(Game g)
  236. { requireHeaderGame(g);
  237.   if (g->plies < g->cbGame->movesLength)
  238.     return TRUE;
  239.   return FALSE;
  240. }
  241.  
  242.  
  243. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  244. @node getPlayersGame
  245. @deftypefun {char *} getPlayersGame (Game @var{g})
  246. Returns the names of the players of the game.  When the application
  247. wants to manipulate the players, a copy of the @w{@code{char *}}
  248. returned should be made.
  249. @end deftypefun
  250. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  251.  
  252. char *
  253. getPlayersGame(Game g)
  254. { requireHeaderGame(g);
  255.   return g->players;
  256. }
  257.  
  258.  
  259. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  260. @node getSourceGame
  261. @deftypefun {char *} getSourceGame (Game @var{g})
  262. Returns the names of the source of the game.  When the application
  263. wants to manipulate the source, a copy of the @w{@code{char *}}
  264. returned should be made.
  265. @end deftypefun
  266. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  267.  
  268. char *
  269. getSourceGame(Game g)
  270. { requireHeaderGame(g);
  271.   return g->source;
  272. }
  273.  
  274.  
  275. char *
  276. getPlaceGame(Game g)
  277. { requireHeaderGame(g);
  278.   return normalisePlace(g->source);
  279. }
  280.  
  281.  
  282. char *
  283. getAnnotatorGame(Game g)
  284. { requireHeaderGame(g);
  285.   return g->annotator;
  286. }
  287.  
  288.  
  289. char *
  290. getEventGame(Game g)
  291. { requireHeaderGame(g);
  292.   return g->event;
  293. }
  294.  
  295.  
  296. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  297. @node containsPlayerNamesGameP
  298. @deftypefun bool containsPlayerNamesGameP (Game @var{g})
  299. Succeeds if the game contains the names of the players.  The heuristic
  300. is to check whether the players field contains at least one hyphen.
  301. Note that some games may actually be an overview of a certain opening,
  302. where the players field is used to name the opening.
  303. @end deftypefun
  304. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  305.  
  306. bool
  307. containsPlayerNamesGameP(Game g)
  308. { requireHeaderGame(g);
  309.   if (strchr(g->players, '-'))
  310.     return TRUE;
  311.   return FALSE;
  312. }
  313.  
  314.  
  315. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  316. @node getWhiteGame
  317. @deftypefun {char *} getWhiteGame (Game @var{g})
  318. Returns the name of the player with the white pieces.  The @w{@code{char
  319. *}} is allocated statically.  A return value of @code{NULL} means that
  320. the information could not be extracted.
  321. @end deftypefun
  322. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  323.  
  324. static bool
  325. substituteHyphen(char *s, char *pattern)
  326. { char *t;
  327.  
  328.   for (t=s; *t; t++)
  329.   { if (strhead(t, pattern))
  330.     { int l = strlen(pattern);
  331.  
  332.       while (l--)
  333.       { if (*t == '-')
  334.       *t = ' ';
  335.     t++;
  336.       }
  337.       return TRUE;
  338.     }
  339.   }
  340.   return FALSE;
  341. }
  342.  
  343.  
  344. char *
  345. getWhiteGame(Game g)
  346. { static char white[MAX_NAME_SIZE+1];
  347.   char *s;
  348.  
  349.   requireHeaderGame(g);
  350. again:
  351.   s = strchr(g->players, '-');
  352.   if (s && s == strrchr(g->players, '-'))
  353.   { *s = '\0';
  354.     strcpy(white, g->players);
  355.     *s = '-';
  356.     return normalisePlayer(white);
  357.   }
  358.   if (s && strrchr(g->players, '-'))    /* At least two hyphens */
  359.   {
  360.     if (substituteHyphen(g->players, "Al-Khateeb")) goto again;
  361.     if (substituteHyphen(g->players, "Al-Mansouri")) goto again;
  362.     if (substituteHyphen(g->players, "Al-Modiahki")) goto again;
  363.     if (substituteHyphen(g->players, "Ana-L")) goto again;
  364.     if (substituteHyphen(g->players, "Ana-Maria")) goto again;
  365.     if (substituteHyphen(g->players, "Anna-Maria")) goto again;
  366.     if (substituteHyphen(g->players, "Anne-Marie")) goto again;
  367.     if (substituteHyphen(g->players, "Armel-David")) goto again;
  368.     if (substituteHyphen(g->players, "Astolfi-Perez")) goto again;
  369.     if (substituteHyphen(g->players, "Badea-Takacs")) goto again;
  370.     if (substituteHyphen(g->players, "Bazaj-Bocka")) goto again;
  371.     if (substituteHyphen(g->players, "Bert-Steffen")) goto again;
  372.     if (substituteHyphen(g->players, "Bos-Swiecik")) goto again;
  373.     if (substituteHyphen(g->players, "Brilla-Banfalvi")) goto again;
  374.     if (substituteHyphen(g->players, "Brinck-Claussen")) goto again;
  375.     if (substituteHyphen(g->players, "Carl-Magnus")) goto again;
  376.     if (substituteHyphen(g->players, "Chekhova-Kostina")) goto again;
  377.     if (substituteHyphen(g->players, "Chin-Hoe")) goto again;
  378.     if (substituteHyphen(g->players, "Chong-Ghee")) goto again;
  379.     if (substituteHyphen(g->players, "Cruz-Lopez")) goto again;
  380.     if (substituteHyphen(g->players, "Cuevas-Rodriguez")) goto again;
  381.     if (substituteHyphen(g->players, "Dan-Robert")) goto again;
  382.     if (substituteHyphen(g->players, "Donaldson-Akhmilovskaya")) goto again;
  383.     if (substituteHyphen(g->players, "Dus-Chotimirski")) goto again;
  384.     if (substituteHyphen(g->players, "Erenska-Radzewska")) goto again;
  385.     if (substituteHyphen(g->players, "Eric-Andre")) goto again;
  386.     if (substituteHyphen(g->players, "Esko-Matti")) goto again;
  387.     if (substituteHyphen(g->players, "Fauland-Borek")) goto again;
  388.     if (substituteHyphen(g->players, "Flear-Leroy")) goto again;
  389.     if (substituteHyphen(g->players, "Foong-Yin")) goto again;
  390.     if (substituteHyphen(g->players, "Fries-Nielsen")) goto again;
  391.     if (substituteHyphen(g->players, "Frolich-Dill")) goto again;
  392.     if (substituteHyphen(g->players, "Garcia-Palermo")) goto again;
  393.     if (substituteHyphen(g->players, "Gerd-Peter")) goto again;
  394.     if (substituteHyphen(g->players, "Gerrit-Hans")) goto again;
  395.     if (substituteHyphen(g->players, "Gert-Jan")) goto again;
  396.     if (substituteHyphen(g->players, "Gian-Thier")) goto again;
  397.     if (substituteHyphen(g->players, "Gustav-Schutz")) goto again;
  398.     if (substituteHyphen(g->players, "Hajkova-Maskova")) goto again;
  399.     if (substituteHyphen(g->players, "Hans-Elmar")) goto again;
  400.     if (substituteHyphen(g->players, "Hans-Georg")) goto again;
  401.     if (substituteHyphen(g->players, "Hans-Gunther")) goto again;
  402.     if (substituteHyphen(g->players, "Hans-Hilmar")) goto again;
  403.     if (substituteHyphen(g->players, "Hans-Hubert")) goto again;
  404.     if (substituteHyphen(g->players, "Hans-J")) goto again;
  405.     if (substituteHyphen(g->players, "Hans-Otto")) goto again;
  406.     if (substituteHyphen(g->players, "Hans-Peter")) goto again;
  407.     if (substituteHyphen(g->players, "Hans-U")) goto again;
  408.     if (substituteHyphen(g->players, "Har-Zvi")) goto again;
  409.     if (substituteHyphen(g->players, "Hartung-Nielsen")) goto again;
  410.     if (substituteHyphen(g->players, "Heine-Nielsen")) goto again;
  411.     if (substituteHyphen(g->players, "Heinz-Dieter")) goto again;
  412.     if (substituteHyphen(g->players, "Heinz-Jurg")) goto again;
  413.     if (substituteHyphen(g->players, "Heinz-Wilhelm")) goto again;
  414.     if (substituteHyphen(g->players, "Hoon-Cheng")) goto again;
  415.     if (substituteHyphen(g->players, "Hoyos-Millan")) goto again;
  416.     if (substituteHyphen(g->players, "Ionescu-Ilie")) goto again;
  417.     if (substituteHyphen(g->players, "Ivanka-Budinsky")) goto again;
  418.     if (substituteHyphen(g->players, "Jan-Hein")) goto again;
  419.     if (substituteHyphen(g->players, "Jan-Michel")) goto again;
  420.     if (substituteHyphen(g->players, "Jan-Olav")) goto again;
  421.     if (substituteHyphen(g->players, "Jan-Olov")) goto again;
  422.     if (substituteHyphen(g->players, "Jan-Willem")) goto again;
  423.     if (substituteHyphen(g->players, "Jean-Alexis")) goto again;
  424.     if (substituteHyphen(g->players, "Jean-Claude")) goto again;
  425.     if (substituteHyphen(g->players, "Jean-Luc")) goto again;
  426.     if (substituteHyphen(g->players, "Jean-Marc")) goto again;
  427.     if (substituteHyphen(g->players, "Jean-Philip")) goto again;
  428.     if (substituteHyphen(g->players, "Jean-Pierre")) goto again;
  429.     if (substituteHyphen(g->players, "Jean-Rene")) goto again;
  430.     if (substituteHyphen(g->players, "Jean-Robert")) goto again;
  431.     if (substituteHyphen(g->players, "Jens-Euw")) goto again;
  432.     if (substituteHyphen(g->players, "Jens-Ove")) goto again;
  433.     if (substituteHyphen(g->players, "Jens-Peter")) goto again;
  434.     if (substituteHyphen(g->players, "Jens-Uwe")) goto again;
  435.     if (substituteHyphen(g->players, "John-Paul")) goto again;
  436.     if (substituteHyphen(g->players, "Juan-Pablo")) goto again;
  437.     if (substituteHyphen(g->players, "Jukka-Pekka")) goto again;
  438.     if (substituteHyphen(g->players, "Kai-Uwe")) goto again;
  439.     if (substituteHyphen(g->players, "Kaj-Erik")) goto again;
  440.     if (substituteHyphen(g->players, "Kari-Juhani")) goto again;
  441.     if (substituteHyphen(g->players, "Karl-Heinz")) goto again;
  442.     if (substituteHyphen(g->players, "Karl-Johan")) goto again;
  443.     if (substituteHyphen(g->players, "Karl-Josef")) goto again;
  444.     if (substituteHyphen(g->players, "Karl-Willi")) goto again;
  445.     if (substituteHyphen(g->players, "Kick-Worthmuller")) goto again;
  446.     if (substituteHyphen(g->players, "Kien-Hua")) goto again;
  447.     if (substituteHyphen(g->players, "Klaus-Dieter")) goto again;
  448.     if (substituteHyphen(g->players, "Klaus-Jurgen")) goto again;
  449.     if (substituteHyphen(g->players, "Klimova-Richtrova")) goto again;
  450.     if (substituteHyphen(g->players, "Kok-Siong")) goto again;
  451.     if (substituteHyphen(g->players, "Lars-Ake")) goto again;
  452.     if (substituteHyphen(g->players, "Lars-Bo")) goto again;
  453.     if (substituteHyphen(g->players, "Lars-Erik")) goto again;
  454.     if (substituteHyphen(g->players, "Lian-Ann")) goto again;
  455.     if (substituteHyphen(g->players, "Litinskaya-Shul")) goto again;
  456.     if (substituteHyphen(g->players, "Loheac-Amoun")) goto again;
  457.     if (substituteHyphen(g->players, "M-Viorel")) goto again;
  458.     if (substituteHyphen(g->players, "Macek-Kalchbrenner")) goto again;
  459.     if (substituteHyphen(g->players, "Mads-Smith")) goto again;
  460.     if (substituteHyphen(g->players, "Maki-Uuro")) goto again;
  461.     if (substituteHyphen(g->players, "Maskova-Hajkova")) goto again;
  462.     if (substituteHyphen(g->players, "Massoud-Amir")) goto again;
  463.     if (substituteHyphen(g->players, "Meng-Kong")) goto again;
  464.     if (substituteHyphen(g->players, "Mephisto-16")) goto again;
  465.     if (substituteHyphen(g->players, "Mihai-Viorel")) goto again;
  466.     if (substituteHyphen(g->players, "Mihail-Viorel")) goto again;
  467.     if (substituteHyphen(g->players, "Milligan-Scott")) goto again;
  468.     if (substituteHyphen(g->players, "Milner-Barry")) goto again;
  469.     if (substituteHyphen(g->players, "Mircea-Sergiu")) goto again;
  470.     if (substituteHyphen(g->players, "Mitaru-Serban")) goto again;
  471.     if (substituteHyphen(g->players, "Neeraj-Kumar")) goto again;
  472.     if (substituteHyphen(g->players, "Niels-Peter")) goto again;
  473.     if (substituteHyphen(g->players, "Nils-Gus")) goto again;
  474.     if (substituteHyphen(g->players, "Nils-Johan")) goto again;
  475.     if (substituteHyphen(g->players, "Nokso-Koivisto")) goto again;
  476.     if (substituteHyphen(g->players, "Nutu-Gajic")) goto again;
  477.     if (substituteHyphen(g->players, "Ole-Christian")) goto again;
  478.     if (substituteHyphen(g->players, "Peicheva-Hansen")) goto again;
  479.     if (substituteHyphen(g->players, "Peicheva-Jurgens")) goto again;
  480.     if (substituteHyphen(g->players, "Peitscheva-Hansen")) goto again;
  481.     if (substituteHyphen(g->players, "Peitscheva-Jurgens")) goto again;
  482.     if (substituteHyphen(g->players, "Per-Erik")) goto again;
  483.     if (substituteHyphen(g->players, "Plauth-Herr")) goto again;
  484.     if (substituteHyphen(g->players, "Popova-Lelchuk")) goto again;
  485.     if (substituteHyphen(g->players, "Ralf-Axel")) goto again;
  486.     if (substituteHyphen(g->players, "Renoy-Chevrier")) goto again;
  487.     if (substituteHyphen(g->players, "Richtrova-Klimova")) goto again;
  488.     if (substituteHyphen(g->players, "Rivas-Pastor")) goto again;
  489.     if (substituteHyphen(g->players, "Sathe-Thipsay")) goto again;
  490.     if (substituteHyphen(g->players, "Schmidt-Brauns")) goto again;
  491.     if (substituteHyphen(g->players, "Shi-Lan")) goto again;
  492.     if (substituteHyphen(g->players, "Sideif-Zade")) goto again;
  493.     if (substituteHyphen(g->players, "Sieiro-Gonzalez")) goto again;
  494.     if (substituteHyphen(g->players, "Sikora-Gizynska")) goto again;
  495.     if (substituteHyphen(g->players, "Sikora-Lerch")) goto again;
  496.     if (substituteHyphen(g->players, "Suan-Shiau")) goto again;
  497.     if (substituteHyphen(g->players, "Tri-Sa-Ard")) goto again;
  498.     if (substituteHyphen(g->players, "Tze-Meng")) goto again;
  499.     if (substituteHyphen(g->players, "Veroci-Petronic")) goto again;
  500.     if (substituteHyphen(g->players, "Victor-Angel")) goto again;
  501.     if (substituteHyphen(g->players, "Videla-Seminara")) goto again;
  502.     if (substituteHyphen(g->players, "Wagner-Michel")) goto again;
  503.     if (substituteHyphen(g->players, "Weiss-Nowak")) goto again;
  504.     if (substituteHyphen(g->players, "Wiese-Jozwiak")) goto again;
  505.     if (substituteHyphen(g->players, "Znosko-Borovsky")) goto again;
  506.   }
  507.   return NULL;
  508. }
  509.  
  510.  
  511. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  512. @node getBlackGame
  513. @deftypefun {char *} getBlackGame (Game @var{g})
  514. Returns the name of the player with the black pieces.  The @w{@code{char
  515. *}} is allocated statically.  A return value of @code{NULL} means that
  516. the information could not be extracted.
  517. @end deftypefun
  518. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  519.  
  520. char *
  521. getBlackGame(Game g)
  522. { static char black[MAX_NAME_SIZE+1];
  523.   char *s;
  524.  
  525.   requireHeaderGame(g);
  526.   s = strchr(g->players, '-');
  527.   if (s && s == strrchr(g->players, '-'))
  528.   { strcpy(black, ++s);
  529.     return normalisePlayer(black);
  530.   }
  531.   return NULL;
  532. }
  533.  
  534.  
  535. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  536. @node getNumberGame
  537. @deftypefun long getNumberGame (Game @var{g})
  538. Returns the number of the game (starting with 1) relative to the entire
  539. database.
  540. @end deftypefun
  541. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  542.  
  543. long
  544. getNumberGame(Game g)
  545. { requireHeaderGame(g);
  546.   return g->number;
  547. }
  548.  
  549.  
  550. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  551. @node getYearGame
  552. @deftypefun short getYearGame (Game @var{g})
  553. Returns the year the game was played.  If the year is not known zero is
  554. returned. 
  555. @end deftypefun
  556. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  557.  
  558. short
  559. getYearGame(Game g)
  560. { requireHeaderGame(g);
  561.   return g->year;
  562. }
  563.  
  564.  
  565. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  566. @node getEcoGame
  567. @deftypefun {char *} getEcoGame (Game @var{g})
  568. Returns the ECO (Encyclopedia of Chess Openings) code of the game.
  569. @code{NULL} is returned when the ECO code is not known.
  570. @end deftypefun
  571. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  572.  
  573. char *
  574. getEcoGame(Game g)
  575. { requireHeaderGame(g);
  576.   if (g->eco[0])
  577.     return g->eco;
  578.   return NULL;
  579. }
  580.  
  581.  
  582. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  583. @node getEcoMajorGame
  584. @deftypefun {char *} getEcoMajorGame (Game @var{g})
  585. Returns the ECO (Encyclopedia of Chess Openings) code of the game.
  586. @code{NULL} is returned when the ECO code is not known.
  587. @end deftypefun
  588. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  589.  
  590. char *
  591. getEcoMajorGame(Game g)
  592. { static char eco[4];
  593.   
  594.   requireHeaderGame(g);
  595.   if (g->eco[0])
  596.   { strncpy(eco, g->eco, 3);
  597.     eco[3] = '\0';
  598.     return eco;
  599.   }
  600.   return NULL;
  601. }
  602.  
  603.  
  604. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  605. @node getFirstMoveGame
  606. @deftypefun Move getFirstMoveGame (Game @var{g})
  607. Returns the first move of the game.
  608. @end deftypefun
  609. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  610.  
  611. Move
  612. getFirstMoveGame(Game g)
  613. { requireMovesGame(g);
  614.   return g->firstMove;
  615. }
  616.  
  617.  
  618. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  619. @node getResultGame
  620. @deftypefun Result getResultGame (Game @var{g})
  621. Returns the result of game @var{g}.  The result is @code{WHITE_WINS},
  622. @code{DRAW}, @code{BLACK_WINS} or one of the position evaluation codes
  623. (e.g. @code{WHITE_ADVANTAGE}).  @code{NO_RESULT} is returned when the
  624. result is not known.
  625. @end deftypefun
  626. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  627.  
  628. Result
  629. getResultGame(Game g)
  630. { requireHeaderGame(g);
  631.   return g->result;
  632. }
  633.  
  634.  
  635. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  636. @node getRoundGame
  637. @deftypefun short getRoundGame (Game @var{g})
  638. Returns the round in which game @var{g} was played.  The round is
  639. extracted from the source field using the heuristic that the round
  640. is between brackets (e.g @code{(15)}).  The constant @code{NO_ROUND}
  641. is returned when the round could not be determined.
  642. @end deftypefun
  643. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  644.  
  645. short
  646. getRoundGame(Game g)
  647. { char *source;
  648.   char *s;
  649.   int round;
  650.  
  651.   requireHeaderGame(g);
  652.   source = getSourceGame(g);
  653.   for (s=source; *s; s++)
  654.   { if (*s == '(' && isdigit(*(s+1)))
  655.     { round = atoi(s+1);
  656.       if (round > 0 && round < 40)
  657.     return round;
  658.     }
  659.   }
  660.   return NO_ROUND;
  661. }
  662.  
  663.  
  664. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  665. @node checkHeaderGame
  666. @deftypefun bool checkHeaderGame (Game @var{g})
  667. Returns @code{FALSE} when decoding the header of the game resulted
  668. in an error and @code{TRUE} otherwise.
  669. @end deftypefun
  670. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  671.  
  672. bool
  673. checkHeaderGame(Game g)
  674. { requireHeaderGame(g);
  675.   if (foundError())
  676.     return FALSE;
  677.   return TRUE;
  678. }
  679.  
  680.  
  681. short
  682. getMovesGame(Game g)
  683. { return g->moves;
  684. }
  685.  
  686.  
  687. short
  688. getEloWhiteGame(Game g)
  689. { return g->eloWhite;
  690. }
  691.  
  692.  
  693. short
  694. getEloBlackGame(Game g)
  695. { return g->eloBlack;
  696. }
  697.  
  698.  
  699. void
  700. printGame(Game g, FILE *fd)
  701. { TextBuffer tb;
  702.   Position pos;
  703.  
  704.   tb = newTextBuffer(fd, 75);
  705.  
  706.   stringTextBuffer(tb, "GAME");
  707.   formatSpaceTextBuffer(tb, "%ld", getNumberGame(g));
  708.   if (getEcoGame(g))
  709.   { stringSpaceTextBuffer(tb, "\"");
  710.     stringTextBuffer(tb, getEcoGame(g));
  711.     stringTextBuffer(tb, "\"");
  712.   } else
  713.     stringSpaceTextBuffer(tb, "\"\"");
  714.   stringSpaceTextBuffer(tb, getPlayersGame(g));
  715.   stringSpaceTextBuffer(tb, getSourceGame(g));
  716.  
  717.   if (getYearGame(g))
  718.     formatSpaceTextBuffer(tb, "%d", getYearGame(g));
  719.   stringNewlineTextBuffer(tb, "");
  720.  
  721.   pos = newPosition();
  722.   if (!fullGameP(g))
  723.   { copyPosition(pos, g->position);
  724.     diagramPosition(pos, tb);
  725.   }
  726.  
  727.   printMovesGame(g, getFirstMoveGame(g), pos, 0, 0, TRUE, tb);
  728.  
  729.   stringNewlineTextBuffer(tb, "SCORE ");
  730.   if (getResultGame(g) != NO_RESULT)
  731.     stringTextBuffer(tb, chessSymbol(getResultGame(g)));
  732.  
  733.   stringNewlineTextBuffer(tb, "\n");
  734.   freeTextBuffer(tb);
  735. }
  736.  
  737.  
  738. void
  739. printMovesGame(Game g,            /* Game we are printing */
  740.            Move move,        /* Move under consideration */
  741.            Position pos,        /* Position before ``move'' */
  742.            int ply,            /* Move number as a ply: 0.. */
  743.            int level,        /* Depth of variations: 0.. */
  744.            int notation,        /* SHORT/LONG_ALGEBRAIC */
  745.            TextBuffer tb)        /* Output TextBuffer */
  746. { int startPly = ply;
  747.   bool printSpace = FALSE;
  748.   bool printDots = TRUE;
  749.   bool printNextDots = FALSE;
  750.   bool marked = (level == 0 && markedGameP(g));
  751.   int algebraic = (level == 0 ? SHORT_ALGEBRAIC : notation);
  752.  
  753.   if (marked && ply == 0)
  754.     stringTextBuffer(tb, "\\begin{moves}");
  755.  
  756.   for (; move; move=move->next, ply++)
  757.   { Position nextPos;
  758.     bool comments = containsCommentsMoveP(move);
  759.     bool variations = containsVariationsMoveP(move);
  760.  
  761.     if (printSpace)
  762.       stringSpaceTextBuffer(tb, "");
  763.     printSpace = TRUE;
  764.  
  765.     if (pos->toMove == WHITE)
  766.     { formatTextBuffer(tb, "%d", (ply/2+1));
  767.       stringTextBuffer(tb, chessSymbol(WHITE_MOVE_SYMBOL));
  768.     } else
  769.     { if (ply == startPly || printDots == TRUE)
  770.       { formatTextBuffer(tb, "%d", (ply/2+1));
  771.     stringTextBuffer(tb, chessSymbol(BLACK_MOVE_SYMBOL));
  772.       }
  773.     }
  774.  
  775.     if (variations)
  776.     { nextPos = newPosition();
  777.       copyPosition(nextPos, pos);
  778.     } else
  779.     { nextPos = NULL;
  780.     }
  781.  
  782.     if (comments || variations || diagramMoveP(move))
  783.       printNextDots = TRUE;
  784.     else
  785.       printNextDots = FALSE;
  786.  
  787.     outputMove(move, tb, pos, algebraic);
  788.  
  789.     if (marked && (comments || variations || diagramMoveP(move)))
  790.       stringTextBuffer(tb, "\\end{moves}");
  791.  
  792.     if (comments || variations)
  793.     { if (level == 0)
  794.     stringSpaceTextBuffer(tb, chessSymbol(START_MAJOR_ALTERNATIVE));
  795.       else
  796.     stringSpaceTextBuffer(tb, chessSymbol(START_ALTERNATIVE));
  797.     }
  798.  
  799.     if (comments)
  800.       outputCommentsMove(move, tb, pos);
  801.     doMovePosition(pos, move);
  802.  
  803.     if (variations)
  804.     { if (comments)
  805.     stringSpaceTextBuffer(tb, "");
  806.       printMovesGame(g, move->alternative, nextPos, ply, level+1, algebraic, tb);
  807.     }
  808.  
  809.     if (comments || variations)
  810.     { if (level == 0)
  811.     stringTextBuffer(tb, chessSymbol(END_MAJOR_ALTERNATIVE));
  812.       else
  813.     stringTextBuffer(tb, chessSymbol(END_ALTERNATIVE));
  814.     }
  815.  
  816.     if (diagramMoveP(move) && !comments)
  817.     { diagramPosition(pos, tb);
  818.       printNextDots = TRUE;
  819.       printSpace = FALSE;
  820.     }
  821.  
  822.     if (marked && (comments || variations || diagramMoveP(move)))
  823.       stringTextBuffer(tb, "\\begin{moves}");
  824.  
  825.     printDots = printNextDots;
  826.     printNextDots = FALSE;
  827.   }
  828.  
  829.   if (marked)
  830.     stringTextBuffer(tb, "\\end{moves}");
  831.  
  832. /*
  833.   if (level == 1)
  834.     stringTextBuffer(tb, chessSymbol(END_MAJOR_ALTERNATIVE));
  835.   if (level > 1)
  836.     stringTextBuffer(tb, chessSymbol(END_ALTERNATIVE));
  837. */
  838.   freePosition(pos);
  839. }
  840.  
  841.  
  842. /*------------------------------------------------------------
  843.  *  Portable Game Notation
  844.  *------------------------------------------------------------*/
  845.  
  846. void
  847. pgnGame(Game g, FILE *fd, int notation)
  848. { TextBuffer tb;
  849.   Position pos;
  850.  
  851.   if (!fullGameP(g))
  852.   { fprintf(stderr, "Don't know how to print partial game (%ld) in PGN\n",
  853.         g->number);
  854.     return;
  855.   }
  856.  
  857.   tb = newTextBuffer(fd, 75);
  858.  
  859.   formatTextBuffer(tb, "[Event \"%s\"]\n", getSourceGame(g));
  860.   formatTextBuffer(tb, "[Site \"%s\"]\n", getSourceGame(g));
  861.  
  862.   if (getYearGame(g))
  863.     formatTextBuffer(tb, "[Date \"%d\"]\n", getYearGame(g));
  864.   else
  865.     stringTextBuffer(tb, "[Date \"\"]\n");
  866.  
  867.   if (getRoundGame(g))
  868.     formatTextBuffer(tb, "[Round \"%d\"]\n", getRoundGame(g));
  869.   else
  870.     stringTextBuffer(tb, "[Round \"\"]\n");
  871.  
  872.   if (getWhiteGame(g))
  873.     formatTextBuffer(tb, "[White \"%s\"]\n", getWhiteGame(g));
  874.   else
  875.     stringTextBuffer(tb, "[White \"\"]\n");
  876.  
  877.   if (getBlackGame(g))
  878.     formatTextBuffer(tb, "[Black \"%s\"]\n", getBlackGame(g));
  879.   else
  880.     stringTextBuffer(tb, "[Black \"\"]\n");
  881.  
  882.   if (getResultGame(g) != NO_RESULT)
  883.     formatTextBuffer(tb, "[Result \"%s\"]\n", chessSymbol(getResultGame(g)));
  884.   else
  885.     stringTextBuffer(tb, "[Result \"\"]\n");
  886.  
  887.   stringTextBuffer(tb, "\n");
  888.  
  889.   pos = newPosition();
  890.  
  891.   pgnMovesGame(g, getFirstMoveGame(g), pos, 0, notation, tb);
  892.  
  893.   if (getResultGame(g) != NO_RESULT)
  894.     stringSpaceTextBuffer(tb, chessSymbol(getResultGame(g)));
  895.  
  896.   stringNewlineTextBuffer(tb, "\n\n");
  897.   freeTextBuffer(tb);
  898. }
  899.  
  900.  
  901. void
  902. pgnMovesGame(Game g,        /* Game we are printing */
  903.          Move move,        /* Move under consideration */
  904.          Position pos,    /* Position before ``move'' */
  905.          int ply,        /* Move number as a ply (0..) */
  906.          int notation,    /* SHORT/LONG_ALGEBRAIC */
  907.          TextBuffer tb)    /* Output TextBuffer */
  908. { for (; move; move=move->next, ply++)
  909.   { stringSpaceTextBuffer(tb, "");
  910.       
  911.     if (pos->toMove == WHITE)
  912.     { formatTextBuffer(tb, "%d", (ply/2+1));
  913.       stringTextBuffer(tb, chessSymbol(WHITE_MOVE_SYMBOL));
  914.     }
  915.  
  916.     stringTextBuffer(tb, getStringMovePosition(pos, move, notation));
  917.     doMovePosition(pos, move);
  918.   }
  919.   freePosition(pos);
  920. }
  921.  
  922.  
  923. /*------------------------------------------------------------
  924.  *  Status of a game
  925.  *------------------------------------------------------------*/
  926.  
  927. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  928. @node bytesGame
  929. @deftypefun {unsigned long} bytesGame (Game @var{g})
  930. Returns the number of bytes game @var{g} occupies on disk.  @code{NULL}
  931. is returned on an error.
  932. @end deftypefun
  933. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  934.  
  935. unsigned long
  936. bytesGame(Game g)
  937. { if (g->flags & GAME_INIT_OK)
  938.   { unsigned long bytes;
  939.  
  940.     g->cbGame = newCbGame();
  941.     if ((bytes=readCbGame(g->cbGame, g->database, g->number)))
  942.     { if (extractHeaderCbGame(g->cbGame, g))
  943.       { g->flags |= GAME_HEADER_OK;
  944.         return bytes;
  945.       }
  946.       freeCbGame(g->cbGame);
  947.       g->cbGame = NULL;
  948.       return (unsigned long) NULL;
  949.     }
  950.     freeCbGame(g->cbGame);
  951.     g->cbGame = NULL;
  952.     return (unsigned long) NULL;
  953.   }
  954.   if (g->cbGame)
  955.     freeCbGame(g->cbGame);
  956.   g->cbGame = NULL;
  957.   return (unsigned long) NULL;
  958. }
  959.  
  960.  
  961. static void
  962. requireHeaderGame(Game g)
  963. { if (g->flags & GAME_HEADER_OK)
  964.     return;
  965.   if (g->flags & GAME_INIT_OK)
  966.   { g->cbGame = newCbGame();
  967.     if (readCbGame(g->cbGame, g->database, g->number))
  968.     { if (extractHeaderCbGame(g->cbGame, g))
  969.       { g->flags |= GAME_HEADER_OK;
  970.         return;
  971.       }
  972.       freeCbGame(g->cbGame);
  973.       g->cbGame = NULL;
  974.       return;
  975.     }
  976.     freeCbGame(g->cbGame);
  977.     g->cbGame = NULL;
  978.     return;
  979.   }
  980.   if (g->cbGame)
  981.     freeCbGame(g->cbGame);
  982.   g->cbGame = NULL;
  983. }
  984.  
  985.  
  986. static void
  987. requireMovesGame(Game g)
  988. { if (g->flags & GAME_MOVES_OK)
  989.     return;
  990.   requireHeaderGame(g);
  991.   if (g->cbGame)
  992.   { if (extractMovesCbGame(g->cbGame, g))
  993.     { g->flags |= GAME_MOVES_OK;
  994.       return;
  995.     }
  996.   }
  997. }
  998.