freegame.cpp

Go to the documentation of this file.
00001 /*****************************************************************************
00002  * Copyright (C) 2005 Eliot
00003  * Authors: Olivier Teuliere  <ipkiss@via.ecp.fr>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  *****************************************************************************/
00019 
00020 #include <iomanip>
00021 #include "dic.h"
00022 #include "tile.h"
00023 #include "rack.h"
00024 #include "round.h"
00025 #include "pldrack.h"
00026 #include "results.h"
00027 #include "player.h"
00028 #include "ai_player.h"
00029 #include "freegame.h"
00030 
00031 #include "debug.h"
00032 
00033 
00034 FreeGame::FreeGame(const Dictionary &iDic): Game(iDic)
00035 {
00036 }
00037 
00038 
00039 FreeGame::~FreeGame()
00040 {
00041 }
00042 
00043 
00044 int FreeGame::setRackRandom(int p, bool iCheck, set_rack_mode mode)
00045 {
00046     int res;
00047     do
00048     {
00049         res = helperSetRackRandom(p, iCheck, mode);
00050     } while (res == 2);
00051     return res;
00052 }
00053 
00054 
00055 int FreeGame::play(const string &iCoord, const string &iWord)
00056 {
00057     /* Perform all the validity checks, and fill a round */
00058     Round round;
00059 
00060     int res = checkPlayedWord(iCoord, iWord, round);
00061     if (res != 0)
00062     {
00063         return res;
00064     }
00065 
00066     /* Update the rack and the score of the current player */
00067     m_players[m_currPlayer]->addPoints(round.getPoints());
00068     m_players[m_currPlayer]->endTurn(round, m_history.getSize());
00069 
00070     /* Everything is OK, we can play the word */
00071     helperPlayRound(round);
00072 
00073     /* Next turn */
00074     // XXX: Should it be done by the interface instead?
00075     endTurn();
00076 
00077     return 0;
00078 }
00079 
00080 
00081 void FreeGame::freegameAI(int n)
00082 {
00083     ASSERT(0 <= n && n < getNPlayers(), "Wrong player number");
00084     ASSERT(!m_players[n]->isHuman(), "AI requested for a human player");
00085 
00086     AIPlayer *player = static_cast<AIPlayer*>(m_players[n]);
00087 
00088     player->compute(*m_dic, m_board, m_history.getSize());
00089     if (player->changesLetters())
00090     {
00091         helperPass(player->getChangedLetters(), n);
00092         endTurn();
00093     }
00094     else
00095     {
00096         const Round &round = player->getChosenRound();
00097         /* Update the rack and the score of the current player */
00098         player->addPoints(round.getPoints());
00099         player->endTurn(round, m_history.getSize());
00100 
00101         helperPlayRound(round);
00102         endTurn();
00103     }
00104 }
00105 
00106 
00107 int FreeGame::start()
00108 {
00109     ASSERT(getNPlayers(), "Cannot start a game without any player");
00110 
00111     /* Set the initial racks of the players */
00112     for (int i = 0; i < getNPlayers(); i++)
00113     {
00114         setRackRandom(i, false, RACK_NEW);
00115     }
00116 
00117     // XXX
00118     m_currPlayer = 0;
00119 
00120     /* If the first player is an AI, make it play now */
00121     if (!m_players[0]->isHuman())
00122     {
00123         freegameAI(0);
00124     }
00125 
00126     return 0;
00127 }
00128 
00129 
00130 int FreeGame::endTurn()
00131 {
00132     /* Complete the rack for the player that just played */
00133     if (setRackRandom(m_currPlayer, false, RACK_NEW) == 1)
00134     {
00135         /* End of the game */
00136         end();
00137         return 1;
00138     }
00139 
00140     /* Next player */
00141     nextPlayer();
00142 
00143     /* If this player is an AI, make it play now */
00144     if (!m_players[m_currPlayer]->isHuman())
00145     {
00146         freegameAI(m_currPlayer);
00147     }
00148 
00149     return 0;
00150 }
00151 
00152 
00153 // Adjust the scores of the players with the points of the remaining tiles
00154 void FreeGame::end()
00155 {
00156     vector<Tile> tiles;
00157 
00158     // TODO: According to the rules of the game in the ODS, a game can end in 3
00159     // cases:
00160     // 1) no more letter in the bag, and one player has no letter in his rack
00161     // 2) the game is "blocked", no one can play
00162     // 3) the players have used all the time they had (for example: 30 min
00163     //    in total, for each player)
00164     // We currently handle case 1, and cannot handle case 3 until timers are
00165     // implemented.
00166     // For case 2, we need both to detect a blocked situation (not easy...) and
00167     // to handle it in the end() method (very easy).
00168 
00169     /* Add the points of the remaining tiles to the score of the current
00170      * player (i.e. the first player with an empty rack), and remove them
00171      * from the score of the players who still have tiles */
00172     for (int i = 0; i < getNPlayers(); i++)
00173     {
00174         if (i != m_currPlayer)
00175         {
00176             const PlayedRack &pld = m_players[i]->getCurrentRack();
00177             pld.getAllTiles(tiles);
00178             for (unsigned int j = 0; j < tiles.size(); j++)
00179             {
00180                 m_players[i]->addPoints(- tiles[j].getPoints());
00181                 m_players[m_currPlayer]->addPoints(tiles[j].getPoints());
00182             }
00183         }
00184     }
00185 
00186     /* Lock game */
00187     m_finished = true;
00188 }
00189 
00190 
00191 int FreeGame::pass(const string &iToChange, int n)
00192 {
00193     if (m_finished)
00194         return 3;
00195 
00196     // According to the rules in the ODS, it is allowed to pass its turn (no
00197     // need to change letters for that).
00198     // TODO: However, if all the players pass their turn, the first one has to
00199     // play, or change at least one letter. To implement this behaviour, we
00200     // must also take care of blocked positions, where no one _can_ play (see
00201     // also comment in the end() method).
00202 
00203     // Convert the string into tiles
00204     vector<Tile> tilesVect;
00205     for (unsigned int i = 0; i < iToChange.size(); i++)
00206     {
00207         Tile tile(toupper(iToChange[i]));
00208         tilesVect.push_back(tile);
00209     }
00210 
00211     int res = helperPass(tilesVect, n);
00212     if (res == 0)
00213         endTurn();
00214     return res;
00215 }
00216 
00217 
00218 int FreeGame::helperPass(const vector<Tile> &iToChange, int n)
00219 {
00220     ASSERT(0 <= n && n < getNPlayers(), "Wrong player number");
00221 
00222     // It is forbidden to change letters when the bag does not contain at
00223     // least 7 letters (this is explicitely stated in the ODS).
00224     Bag bag;
00225     realBag(bag);
00226     if (bag.nTiles() < 7)
00227     {
00228         return 1;
00229     }
00230 
00231     Player *player = m_players[n];
00232     PlayedRack pld = player->getCurrentRack();
00233     Rack rack;
00234     pld.getRack(rack);
00235 
00236     for (unsigned int i = 0; i < iToChange.size(); i++)
00237     {
00238         /* Remove the letter from the rack */
00239         if (!rack.in(iToChange[i]))
00240         {
00241             return 2;
00242         }
00243         rack.remove(iToChange[i]);
00244     }
00245 
00246     pld.reset();
00247     pld.setOld(rack);
00248 
00249     player->setCurrentRack(pld);
00250 
00251     // FIXME: the letters to change should not be in the bag while generating
00252     // the new rack!
00253 
00254     return 0;
00255 }
00256 
00257 /// Local Variables:
00258 /// mode: c++
00259 /// mode: hs-minor
00260 /// c-basic-offset: 4
00261 /// indent-tabs-mode: nil
00262 /// End:

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