game_io.cpp

Go to the documentation of this file.
00001 /*****************************************************************************
00002  * Copyright (C) 1999-2005 Eliot
00003  * Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
00004  *          Olivier Teuliere  <ipkiss@via.ecp.fr>
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019  *****************************************************************************/
00020 
00021 /**
00022  *  \file   game_io.cpp
00023  *  \brief  Eliot game class file load/save handling
00024  *  \author Antoine Fraboulet & Olivier Teuliere
00025  *  \date   2002 - 2005
00026  */
00027 
00028 #include "pldrack.h"
00029 #include "round.h"
00030 #include "turn.h"
00031 #include "player.h"
00032 #include "game.h"
00033 #include "game_factory.h"
00034 #include "debug.h"
00035 
00036 using namespace std;
00037 
00038 /*************************
00039  * Ident string used to identify saved Eliot games
00040  *************************/
00041 #define IDENT_STRING "Eliot"
00042 #define IDENT_FORMAT_14 ""
00043 #define IDENT_FORMAT_15 "1.5"
00044 
00045 
00046 /********************************************************
00047  *
00048  * Loading games
00049  *
00050  ********************************************************/
00051 
00052 Game * Game::load(FILE *fin, const Dictionary& iDic)
00053 {
00054     char buff[4096];
00055     // 10d is \012
00056     // 13d is \015
00057     char delim[] = " \t\n\012\015|";
00058     char *token;
00059 
00060     // Check characteristic string
00061     if (fgets(buff, sizeof(buff), fin) == NULL)
00062     {
00063         debug("Game::load cannot load first line\n");
00064         return NULL;
00065     }
00066 
00067     if ((token = strtok(buff, delim)) == NULL)
00068     {
00069         debug("Game::load first line is empty\n");
00070         return NULL;
00071     }
00072 
00073     /* checks for IDENT_STRING and file format */
00074     if (string(token) != IDENT_STRING)
00075     {
00076         debug("Game::load IDENT_STRING %s unknown\n",token);
00077         return NULL;
00078     }
00079 
00080     if ((token = strtok(NULL, delim)) == NULL)
00081       {
00082         debug("Game_io::loading file format 1.4\n");
00083         return Game::gameLoadFormat_14(fin,iDic);
00084       }
00085 
00086     if (string(token) == string(IDENT_FORMAT_15))
00087       {
00088         debug("Game_io::loading file format 1.5\n");
00089         return Game::gameLoadFormat_15(fin,iDic);
00090       }
00091 
00092     debug("Game::load unknown format %s\n",token);
00093     return NULL;
00094 }
00095 
00096 
00097 Game* Game::gameLoadFormat_14(FILE *fin, const Dictionary& iDic)
00098 {
00099      Tile tile;
00100      char buff[4096];
00101      char rack[20];
00102      char word[20];
00103      char pos [5];
00104      char delim[]=" \t\n\012\015";
00105      char *token;
00106      Game *pGame = NULL;
00107 
00108      debug("Game::gameLoadFormat_14\n");
00109 
00110      pGame = GameFactory::Instance()->createTraining(iDic);
00111      pGame->start();
00112 
00113      while(fgets(buff,sizeof(buff),fin))
00114      {
00115          token = strtok(buff,delim);
00116          if (token != NULL)
00117          {
00118              if (strcmp(token,"total")==0)
00119              {
00120                  break;
00121              }
00122 
00123              /* rack */
00124              strncpy(rack,token,sizeof(rack));
00125              ((Training*)pGame)->setRack(RACK_MANUAL,false,string(rack));
00126              debug("%s ",rack);
00127 
00128              /* word */
00129              token = strtok(NULL,delim);
00130              if (!token || strcmp(token,"total")==0)
00131              {
00132                  break;
00133              }
00134 
00135              strncpy(word,token,sizeof(word));
00136              debug("\t%s ",word);
00137 
00138              /* bonus */
00139              if ((token = strtok(NULL,delim)) == NULL)
00140                  break;
00141 
00142              /* points */
00143              if (token[0]=='*')
00144              {
00145                  debug("%s\t",token);
00146                  if ((token = strtok(NULL,delim)) == NULL)
00147                      break;
00148              }
00149 
00150              /* pos 1 */
00151              if ((token = strtok(NULL,delim)) == NULL)
00152                  break;
00153 
00154              debug("(%s ",token);
00155              strncpy(pos,token,sizeof(pos));
00156 
00157              /* pos 2 */
00158              if ((token = strtok(NULL,delim)) == NULL)
00159                  break;
00160 
00161              debug("%s)",token);
00162              strncat(pos,token,sizeof(pos));
00163              debug("%s\n",pos);
00164 
00165              debug("   play %s %s\n",pos, word);
00166              pGame->play(string(pos),string(word));
00167          }
00168      }
00169      return pGame;
00170 }
00171 
00172 
00173 Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic)
00174 {
00175     Game *pGame = NULL;
00176 #if 0
00177     char buff[4096];
00178     int num;
00179     char rack[20];
00180     char word[20];
00181     char ref[4];
00182     int pts;
00183     int player;
00184     char *pos;
00185     Tile tile;
00186 
00187     while (fgets(buff, sizeof(buff), fin))
00188     {
00189         // Indication of game type
00190         pos = strstr(buff, "Game type: ");
00191         if (pos != NULL)
00192         {
00193             // No Game object should have been created yet
00194             if (pGame != NULL)
00195             {
00196                 delete pGame;
00197                 return NULL;
00198             }
00199             // Create the correct Game object
00200             if (strstr(buff, "Training"))
00201                 pGame = GameFactory::Instance()->createTraining(iDic);
00202             else if (strstr(buff, "Free game"))
00203                 pGame = GameFactory::Instance()->createFreeGame(iDic);
00204             else if (strstr(buff, "Duplicate"))
00205                 pGame = GameFactory::Instance()->createDuplicate(iDic);
00206             else
00207                 return NULL;
00208             // Read next line
00209             continue;
00210         }
00211 
00212         // Players type
00213         pos = strstr(buff, "Player ");
00214         if (pos != NULL && pGame != NULL)
00215         {
00216             int nb = 0;
00217             char type[20];
00218             if (sscanf(pos, "Player %d: %19s", &nb, type) > 1)
00219             {
00220                 if (pGame->getMode() != kTRAINING)
00221                 {
00222                     if (string(type) == "Human")
00223                         pGame->addHumanPlayer();
00224                     else if (string(type) == "Computer")
00225                         pGame->addAIPlayer();
00226                     else
00227                         ;
00228                 }
00229             }
00230             // Read next line
00231             continue;
00232         }
00233 
00234         // Last racks
00235         pos = strstr(buff, "Rack ");
00236         if (pos != NULL && pGame != NULL)
00237         {
00238             int nb = 0;
00239             char letters[20];
00240             if (sscanf(pos, "Rack %d: %19s", &nb, letters) > 1)
00241             {
00242                 // Create the played rack
00243                 PlayedRack pldrack;
00244                 char *r = letters;
00245                 if (strchr(r, '+'))
00246                 {
00247                     while (*r != '+')
00248                     {
00249                         pldrack.addOld(Tile(*r));
00250                         r++;
00251                     }
00252                     r++;
00253                 }
00254                 while (*r)
00255                 {
00256                     pldrack.addNew(Tile(*r));
00257                     r++;
00258                 }
00259 
00260                 // Give the rack to the player
00261                 pGame->m_players[nb]->setCurrentRack(pldrack);
00262             }
00263             // Read next line
00264             continue;
00265         }
00266 
00267         // Skip columns title
00268         if (strstr(buff, "==") != NULL ||
00269             strstr(buff, "| PTS | P |") != NULL)
00270         {
00271             continue;
00272         }
00273 
00274         if (string(buff) != "\n" && pGame != NULL)
00275         {
00276             char bonus = 0;
00277             int res = sscanf(buff, "   %2d | %8s | %s | %3s | %3d | %1d | %c",
00278                              &num, rack, word, ref, &pts, &player, &bonus);
00279             if (res < 6)
00280                 continue;
00281 
00282             // Integrity checks
00283             // TODO: add more checks
00284             if (pts < 0)
00285                 continue;
00286             if (player < 0 || player > pGame->getNPlayers())
00287                 continue;
00288             if (bonus && bonus != '*')
00289                 continue;
00290 
00291             // Build a rack for the correct player
00292             PlayedRack pldrack;
00293             char *r = rack;
00294             if (strchr(r, '+'))
00295             {
00296                 while (*r != '+')
00297                 {
00298                     pldrack.addOld(Tile(*r));
00299                     r++;
00300                 }
00301                 r++;
00302             }
00303 
00304             while (*r)
00305             {
00306                 pldrack.addNew(Tile(*r));
00307                 r++;
00308             }
00309 
00310             // Build a round
00311             Round round;
00312             round.setPoints(pts);
00313             if (bonus == '*')
00314                 round.setBonus(1);
00315 
00316             if (isalpha(ref[0]))
00317             {
00318                 // Horizontal word
00319                 round.setDir(HORIZONTAL);
00320                 round.setRow(ref[0] - 'A' + 1);
00321                 round.setCol(atoi(ref + 1));
00322 
00323                 for (unsigned int i = 0; i < strlen(word); i++)
00324                 {
00325                     tile = Tile(word[i]);
00326 
00327                     if (!pGame->m_board.getTile(round.getRow(), round.getCol() + i).isEmpty())
00328                     {
00329                         round.addRightFromBoard(tile);
00330                     }
00331                     else
00332                     {
00333                         round.addRightFromRack(tile, islower(word[i]));
00334                         pGame->m_bag.takeTile((islower(word[i])) ? Tile::Joker() : tile);
00335                     }
00336                 }
00337             }
00338             else
00339             {
00340                 // Vertical word
00341                 round.setDir(Coord::VERTICAL);
00342                 round.setRow(ref[strlen(ref) - 1] - 'A' + 1);
00343                 round.setCol(atoi(ref));
00344 
00345                 for (unsigned int i = 0; i < strlen(word); i++)
00346                 {
00347                     tile = Tile(word[i]);
00348 
00349                     if (!pGame->m_board.getTile(round.getRow() + i, round.getCol()).isEmpty())
00350                     {
00351                         round.addRightFromBoard(tile);
00352                     }
00353                     else
00354                     {
00355                         round.addRightFromRack(tile, islower(word[i]));
00356                         pGame->m_bag.takeTile((islower(word[i])) ? Tile::Joker() : tile);
00357                     }
00358                 }
00359             }
00360 
00361             pGame->m_currPlayer = player;
00362             // Update the rack for the player
00363             pGame->m_players[player]->setCurrentRack(pldrack);
00364             // End the turn for the current player (this creates a new rack)
00365             pGame->m_players[player]->playRound(num - 1, round);
00366             // Play the round
00367             pGame->helperPlayRound(round);
00368         }
00369     }
00370 
00371     // Finalize the game
00372     if (pGame)
00373     {
00374         // We don't really know whose turn it is, but at least we know that
00375         // the game was saved while a human was to play.
00376         for (int i = 0; i < pGame->getNPlayers(); i++)
00377         {
00378             if (pGame->m_players[i]->isHuman())
00379             {
00380                 pGame->m_currPlayer = i;
00381                 break;
00382             }
00383         }
00384     }
00385 #endif
00386     return pGame;
00387 }
00388 
00389 /********************************************************
00390  *
00391  * Loading games
00392  *
00393  ********************************************************/
00394 
00395 void Game::save(ostream &out, game_file_format format) const
00396 {
00397     if (getMode() == kTRAINING && format == FILE_FORMAT_STANDARD)
00398         {
00399             gameSaveFormat_14(out);
00400         }
00401     else
00402         {
00403             gameSaveFormat_15(out);
00404         }
00405 }
00406 
00407 
00408 void Game::gameSaveFormat_14(ostream &out) const
00409 {
00410     int i;
00411     char line[100];
00412     const char decal[]="   ";
00413     out << IDENT_STRING << endl << endl;
00414 
00415     for(i = 0; i < m_history.getSize(); i++)
00416         {
00417             const Turn& turn = m_history.getTurn(i);
00418             string rack = turn.getPlayedRack().toString(PlayedRack::RACK_EXTRA);
00419             string word = turn.getRound().getWord();
00420             string coord = turn.getRound().getCoord().toString(Coord::COORD_MODE_LONG);
00421 
00422             // rack [space] word [space] bonus points coord
00423             sprintf(line,"%s%s%s%s%c%4d %s",
00424                     rack.c_str(),
00425                     string(12 - rack.size(), ' ').c_str(),
00426                     word.c_str(),
00427                     string(16 - word.size(), ' ').c_str(),
00428                     turn.getRound().getBonus() ? '*' : ' ',
00429                     turn.getRound().getPoints(),
00430                     coord.c_str()
00431                     );
00432 
00433             out << decal << line << endl;
00434         }
00435 
00436     out << endl;
00437     out << decal << "total" << string(24,' ');
00438     sprintf(line,"%4d", getCurrentPlayer().getPoints());
00439     out << line << endl;
00440 }
00441 
00442 
00443 void Game::gameSaveFormat_15(ostream &out) const
00444 {
00445     const string decal = "   ";
00446     // "Header" of the game
00447     out << IDENT_STRING << " " << IDENT_FORMAT_15 << endl << endl;
00448     // Game type
00449     out << "Game type: " << getModeAsString() << endl;
00450     // Player list
00451     for (int i = 0; i < getNPlayers(); i++)
00452     {
00453         out << "Player " << i << ": ";
00454         if (m_players[i]->isHuman())
00455             out << "Human" << endl;
00456         else
00457             out << "Computer" << endl;
00458     }
00459     out << endl;
00460 
00461     // Title of the columns
00462     char line[100];
00463     out << decal << " N |   RACK   |    SOLUTION     | REF | PTS | P | BONUS" << endl;
00464     out << decal << "===|==========|=================|=====|=====|===|======" << endl;
00465 
00466     // Print the game itself
00467     for (int i = 0; i < m_history.getSize(); i++)
00468     {
00469         const Turn& turn = m_history.getTurn(i);
00470         string word = turn.getRound().getWord();
00471         string coord = turn.getRound().getCoord().toString();
00472         sprintf(line, "%2d | %8s | %s%s | %3s | %3d | %1d | %c",
00473                 i + 1,
00474                 turn.getPlayedRack().toString().c_str(),    /* pldrack     */
00475                 word.c_str(),                               /* word        */
00476                 string(15 - word.size(), ' ').c_str(),      /* fill spaces */
00477                 coord.c_str(),                              /* coord       */
00478                 turn.getRound().getPoints(),
00479                 turn.getPlayer(),
00480                 turn.getRound().getBonus() ? '*' : ' ');
00481 
00482         out << decal << line << endl;
00483     }
00484 
00485     // A game has no points in total (should it be the sum of all player's points)
00486     //    out << endl << decal << "Total: " << m_points << endl;
00487 
00488     // Print current rack for all the players
00489     out << endl;
00490     for (int i = 0; i < getNPlayers(); i++)
00491     {
00492         string rack = m_players[i]->getCurrentRack().toString();
00493         out << "Rack " << i << ": " << rack << endl;
00494     }
00495 }
00496 
00497 /// Local Variables:
00498 /// mode: c++
00499 /// mode: hs-minor
00500 /// c-basic-offset: 4
00501 /// indent-tabs-mode: nil
00502 /// End:

Generated on Thu Dec 29 02:01:14 2005 for Eliot by  doxygen 1.4.5