src/World.cpp

Go to the documentation of this file.
00001 
00008 /*
00009 This file is part of Teapot Colony Wars.
00010 
00011 Teapot Colony Wars is free software: you can redistribute it and/or modify
00012 it under the terms of the GNU General Public License as published by
00013 the Free Software Foundation, either version 2 of the License, or
00014 (at your option) any later version.
00015 
00016 Teapot Colony Wars is distributed in the hope that it will be useful,
00017 but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019 GNU General Public License for more details.
00020 
00021 You should have received a copy of the GNU General Public License
00022 along with Teapot Colony Wars.  If not, see <http://www.gnu.org/licenses/>.
00023 */
00024 
00025 #include "World.h"
00026 
00027 #include <GL/glut.h>
00028 #include <list>
00029 #include <utility>
00030 
00031 #include "Constants.h"
00032 #include "Random.h"
00033 
00034 #include "WallCell.h"
00035 #include "ColonyCell.h"
00036 #include "EmptyCell.h"
00037 #include "CorridorCell.h"
00038 #include "InfiniteFood.h"
00039 #include "RenewableFood.h"
00040 #include "GodsGiftFood.h"
00041 #include "CorpseFood.h"
00042 
00043 #include "NorthMove.h"
00044 #include "SouthMove.h"
00045 #include "EastMove.h"
00046 #include "WestMove.h"
00047 #include "NorthEastMove.h"
00048 #include "NorthWestMove.h"
00049 #include "SouthEastMove.h"
00050 #include "SouthWestMove.h"
00051 #include "NoMove.h"
00052 
00053 #include "Colony.h"
00054 #include "Worshiper.h"
00055 #include "VisualContext.h"
00056 #include "UntilVictoryFight.h"
00057 #include "HeuristicReplication.h"
00058 #include "GeneticReplication.h"
00059 
00060 void World::addNewWorshipers() {
00061   std::list<ColonyCell*>::iterator it, begin, end;
00062   std::list<Worshiper*> *worshipers = NULL;
00063   Worshiper *w = NULL;
00064 
00065   // on itère sur la liste des colonies
00066   for(int i = 0; i < _colonies.size(); i++) {
00067     // on récupère les nouveaux individus créés par chaque colonie
00068     worshipers = _colonies[i]->getNewWorshipers();
00069     if(worshipers != NULL) {
00070       _colonies[i]->getCellIterators(begin, end);
00071       // tant qu'il y en a qui n'ont pas été placés sur le monde
00072       while(! worshipers->empty()) {
00073         for(it = begin; it != end && ! worshipers->empty(); ++it) {
00074           // on récupère le 1er individu de la liste
00075           w = worshipers->front();
00076           if(w != NULL) {
00077             // on met à jour son contexte de vision
00078             w->setVisualContext(
00079               new VisualContext(this, (*it)->getHeight(), (*it)->getWidth()));
00080             // et on l'ajoutes à une case appartenant à la colonie
00081             (*it)->addWorshiper(w);
00082           }
00083           // il ne reste plus qu'à l'enlever de la liste...
00084           worshipers->pop_front();
00085         }
00086       }
00087     }
00088   }
00089 }
00090 
00091 void World::addPheromone(Cell *cell, Worshiper *w) {
00092   std::list<Pheromone*>::iterator it, end;
00093   std::list<Pheromone*> *pheromones = NULL;
00094 
00095   // on récupère les phéromones a créer par l'individu
00096   pheromones = w->getPheromones();
00097   if(pheromones != NULL) {
00098     end = pheromones->end();
00099     // et on ajoute chacune des phéromones à la case (mise à jour si nécessaire)
00100     for(it = pheromones->begin(); it != end; ++it) {
00101       cell->addPheromone(*it);
00102     }
00103   }
00104 }
00105 
00106 void World::addCommand(Worshiper *w, unsigned int i, unsigned int j) {
00107   std::pair<unsigned int, unsigned int> coord;
00108 
00109   // ou doit on aller ?
00110   coord = _move->getAbsCoords(w->getAction(), i, j);
00111   // est-ce que l'on se déplace bien, que les coordonées sont valides
00112   // et que la case atteignable ?
00113   if((coord.first != i || coord.second != j) &&
00114       coord.first > 0 && coord.first < _height - 1 &&
00115       coord.second > 0 && coord.second < _width - 1 &&
00116       getCell(coord.first, coord.second)->reachable(w)) {
00117     // si c'est le cas, alors on met en attente une commande de déplacement
00118     coord_t c =  {coord.first, coord.second, i, j};
00119     _commandes[w] = c;
00120   }
00121 }
00122 
00123 void World::makeCommand() {
00124   std::map<Worshiper*, coord_t>::iterator it, end;
00125   VisualContext *view = NULL;
00126 
00127   // pour chacunes des commandes qui ont été passées
00128   end = _commandes.end();
00129   for(it = _commandes.begin(); it != end; ++it) {
00130     // si on a bien réussit à déplacer le worshiper dans sa nouvelle case
00131     if(getCell(it->second.i, it->second.j)->addWorshiper(it->first)) {
00132       // on met à jour la vision du monde de l'individu
00133       view = it->first->getVisualContext();
00134       if(view != NULL) {
00135         view->_i = it->second.i;
00136         view->_j = it->second.j;
00137       }
00138       // et on l'enlève de l'ancienne
00139       getCell(it->second.old_i, it->second.old_j)->removeWorshiper(it->first);
00140     }
00141   }
00142   // une fois toutes les commandes traitées, on les supprimes.
00143   _commandes.clear();
00144 }
00145 
00146 Cell* World::fight(Cell *cell) {
00147   ReachableCell *next = NULL;
00148   ColonyCell *colony = NULL;
00149   CorpseFood *dead = NULL;
00150   unsigned int food;
00151 
00152   // on effectue le combat conformément à la stratégie établie
00153   food = _fightStrategy->resolve(cell);
00154   // que leurs cadavres nourrissent la terre (et leurs congénères)
00155   if(food > 0) {
00156     if(dynamic_cast<CorpseFood*>(cell)) {
00157       // on est déjà sur une CorpseFood, on ajoute juste de la nouriture.
00158       cell->addFood(food);
00159     } else {
00160       // sinon on en crée une.
00161       // la case à recouvrir doit être atteignable
00162       next = dynamic_cast<ReachableCell*>(cell);
00163       //Une CorpseFood ne doit pas recouvrir une colonie
00164       if(colony = dynamic_cast<ColonyCell*>(cell)) {
00165         // on ajoute plutôt la nouriture produite à la colonie
00166         colony->getColony()->incrementFood(food);
00167       } else if(next) {
00168         // création de la CorpseFood
00169         dead = new CorpseFood(food);
00170         if(dead != NULL) {
00171           // on l'empile par dessus l'ancienne case
00172           dead->push(next);
00173           // et on s'assure que la case retournée sera bien la nouvelle.
00174           cell = dead;
00175         }
00176       }
00177     }
00178   }
00179 
00180   return cell;
00181 }
00182 
00183 void World::feed(Cell *cell) {
00184   std::list<Worshiper*> *worshipers = NULL;
00185   std::list<Worshiper*>::iterator it, end;
00186   unsigned int nb_people, ratio;
00187   unsigned int needed, consumed;
00188   unsigned int food;
00189   bool full;
00190 
00191   // qui est sur la case ?
00192   worshipers = cell->getPeople();
00193   // combien de nouriture est diponible ?
00194   food = cell->getFood();
00195   // si il y a de la nouriture et des worshipers sur la case
00196   if(food > 0 && worshipers != NULL && (nb_people = worshipers->size()) > 0) {
00197     // tout le monde n'a pas mangé à sa faim (et pour cause : personne
00198     // n'a encore mangé quoi que ce soit)
00199     full = false;
00200     // tant qu'il reste de la nouriture et que tout le monde n'est pas gavé
00201     while(food > 0 && ! full) {
00202       // on part du principe que tu le monde aura eu sa dose, les insatisfaits
00203       // n'ont qu'a faire une réclamation
00204       full = true;
00205       // on partage la nouriture en portion égales
00206       ratio = (unsigned int) (food / nb_people) + 1;
00207       // et chaque worshipers reçoit sa part
00208       end = worshipers->end();
00209       for(it = worshipers->begin(); food > 0 && it != end; ++it) {
00210         // on calcule dabord ce dont il a besoin
00211         needed = (*it)->getCapacity() - (*it)->getFood();
00212         // on vérifie ensuite si il pourra manger à sa faim
00213         full = !(full && needed > ratio);
00214         // on lui attribue sa part
00215         needed = (needed > ratio ? ratio : needed);
00216         // on la soustrait de la quantité totale
00217         consumed = cell->subFood(needed);
00218         // on met la quantité de nouriture disponible à jour
00219         food = cell->getFood();
00220         // et on lui donne ce qui a été réellement soustrait
00221         (*it)->setFood((*it)->getFood() + consumed);
00222       }
00223     }
00224   }
00225   // maintenant on décrémente d'une unité la nouriture de chaque individu
00226   end = worshipers->end();
00227   for(it = worshipers->begin(); it != end; ++it) {
00228     if(! (*it)->subFood()) {
00229       // oups... je crois qu'on est mort de faim. que c'est bête...
00230       // adieux !
00231       delete (*it);
00232       cell->removeWorshiper(*it);
00233       --it;
00234     }
00235   }
00236 }
00237 
00238 World::World(unsigned int width, unsigned int height, unsigned int nb_colonies):
00239     _width(width), _height(height), _colonies(nb_colonies) {
00240   unsigned int max;
00241   int i, j, n = 0;
00242 
00243   // TODO Initialisation + propre des colonies
00244   for(int i = 0; i < nb_colonies; i++){
00245     _colonies[i] = new Colony(MAX_FOOD_CAPACITY * 30, new GeneticReplication);
00246   }
00247 
00248   _map = new Cell*[_width * _height];
00249   for(i = 0; i < _height; i++) {
00250     for(j = 0; j < _width; j++) {
00251       if(i == 0 || i == _height - 1 || j == 0 || j == _width - 1) {
00252         // on s'assure que tous les bords sont bien des murs
00253         getCell(i, j) = new WallCell;
00254       } else {
00255         /*if((i + j) % 8 == 0) {
00256           getCell(i, j) = new WallCell;
00257         } else {
00258           max = MAX_FOOD_CAPACITY + 1;
00259           getCell(i, j) = new EmptyCell;
00260         }
00261         */
00262         // une case sur deux sera vide par défaut en moyenne
00263         switch(Random::value() % 10) {
00264           case 0:
00265                getCell(i, j) = new ColonyCell(_colonies[n++ % _colonies.size()], i, j);
00266             break;
00267           case 1:
00268             // il doit toujours y avoir au moins un peu de nouriture
00269             // et le taux de régénération doit être inférieur à la capacité
00270             max = (Random::value() % MAX_FOOD_CAPACITY) + 1;
00271             getCell(i, j) = new RenewableFood(max, (Random::value() % max) + 1);
00272             break;
00273           case 2:
00274             // il doit toujours y avoir au moins un peu de nouriture
00275             getCell(i, j) = new InfiniteFood((Random::value() % MAX_FOOD_CAPACITY) + 1);
00276             break;
00277           case 3:
00278             // attention, un couloir doit toujours rester traversable...
00279             getCell(i, j) = new CorridorCell((Random::value() % CORRIDOR_SIZE_MAX) + 1);
00280             break;
00281           case 4:
00282             getCell(i, j) = new WallCell;
00283             break;
00284           default:
00285             getCell(i, j) = new EmptyCell;
00286         }
00287       }
00288     }
00289   }
00290   delete getCell(7, 7);
00291   getCell(7, 7) = new ColonyCell(_colonies[0], 7, 7);
00292   delete getCell(8, 8);
00293   getCell(8, 8) = new ColonyCell(_colonies[1], 8, 8);
00294 
00295   _fightStrategy = new UntilVictoryFight;
00296 
00297   //Chaîne de responsabilité pour les actions de déplacement
00298   _move = new NoMove(
00299             new NorthMove(
00300               new SouthMove(
00301                 new WestMove(
00302                   new EastMove(
00303                     new NorthWestMove(
00304                       new NorthEastMove(
00305                         new SouthEastMove(
00306                           new SouthWestMove(NULL)
00307                         )
00308                       )
00309                     )
00310                   )
00311                 )
00312               )
00313             )
00314           );
00315 }
00316 
00317 World::~World() {
00318   int i;
00319 
00320   for(i = 0; i < _width * _height; i++) {
00321     delete _map[i];
00322   }
00323   delete[] _map;
00324   delete _fightStrategy;
00325   delete _move;
00326 }
00327 
00328 unsigned int World::getWidth() {
00329   return _width;
00330 }
00331 
00332 unsigned int World::getHeight() {
00333   return _height;
00334 }
00335 
00336 Move* World::getMove() const {
00337   return _move;
00338 }
00339 
00340 void World::addGodsGift(unsigned int i, unsigned int j) {
00341   ReachableCell *next = NULL;
00342   GodsGiftFood *gift = NULL;
00343   Cell *cell = NULL;
00344 
00345   cell = getCell(i, j);
00346   next = dynamic_cast<ReachableCell*>(cell);
00347   //Une GodsGiftFood ne doit pas recouvrir une colonie
00348   if(next && ! dynamic_cast<ColonyCell*>(cell)) {
00349     gift = new GodsGiftFood(GODS_GIFT_FOOD_VALUE);
00350     if(gift != NULL) {
00351       // on l'empile par dessus l'ancienne case
00352       gift->push(next);
00353       // et on s'assure que la case retournée sera bien la nouvelle.
00354       getCell(i, j) = gift;
00355     }
00356   }
00357 }
00358 
00359 void World::iteration() {
00360   std::list<Worshiper*> *worshipers = NULL;
00361   std::list<Worshiper*>::iterator it, end;
00362   Cell *cell = NULL;
00363   unsigned int i, j;
00364 
00365   // Bonjour, bienvenu dans notre restaurant (création des worshipers)
00366   addNewWorshipers();
00367   // Voici la carte (prise des différentes commandes)
00368   for(i = 1; i < _height - 1; i++) {
00369     for(j = 1; j < _width - 1; j++) {
00370       cell = getCell(i, j);
00371       // pour chaque case, on itère sur les individus
00372       worshipers = cell->getPeople();
00373       if(worshipers != NULL) {
00374         end = worshipers->end();
00375         for(it = worshipers->begin(); it != end; ++it) {
00376           // Je vous laisse la consulter
00377           (*it)->think();
00378           // Quel est ce doux fumet ? (dépot de phéromones)
00379           addPheromone(cell, *it);
00380           // Et pour madame, ce sera ? (gestion des commandes)
00381           addCommand(*it, i, j);
00382         }
00383       }
00384     }
00385   }
00386   // Pour qui le steak frite ? (service)
00387   makeCommand();
00388   // Itadakimasu ! (Bon appétit !)
00389   for(i = 1; i < _height - 1; i++) {
00390     for(j = 1; j < _width - 1; j++) {
00391       cell = getCell(i, j);
00392       // on itère sur chaque individu de la case
00393       if(cell->getPeople() != NULL) {
00394         // les faibles meurent... (combats)
00395         // (point de syntaxe : plus simple que de s'embêter avec des pointeurs
00396         // de pointeurs... Il faut juste s'assurer que la variable locale
00397         // est aussi mise à jour)
00398         getCell(i, j) = cell = fight(cell);
00399         // ...et les forts se goinffrent (no comment)
00400         feed(cell);
00401         // Comment c'est passé votre repas ? (mise à jour du monde)
00402         getCell(i, j) = cell->makeActions();
00403       }
00404     }
00405   }
00406 }
00407 
00408 void World::draw() {
00409   int i, j;
00410 
00411   glMatrixMode(GL_MODELVIEW);
00412 
00413   for(i = 0; i < _height; i++) {
00414     for(j = 0; j < _width; j++) {
00415       // display each Cell the one after another
00416       getCell(i, j)->draw();
00417       glTranslatef(CELL_SIZE, 0.0, 0.0);
00418     }
00419     glTranslatef(- (GLfloat) CELL_SIZE * _width, 0.0, CELL_SIZE);
00420   }
00421 }

Generated on Sat Feb 2 22:22:54 2008 for Teapot Colony Wars by  doxygen 1.5.4