C++: Correction Il ne peut en rester qu'un

 

Cette correction est une quasi-transcription de la solution proposée en Java. Un certain nombre de choix ont été faits, parmi lesquels :
 
  • On peut choisir les dimensions de la grille
  • On souhaite pouvoir représenter plusieurs personnages sur une même case
  • Pour représenter facilement les parsonnages sur la grille, on n'affiche que des numéros de case sur la grille, et on affiche séparément la liste des personnages présents sur chaque case occupée.
  • Chaque étape du jeu se déroule en deux phases :
    • Tout le monde se déplace
    • Tous ceux qui ne sont pas seuls sur une case se battent 

Interface

#ifndef __MASSACRE_H__
#define __MASSACRE_H__
 
#include <stdlib.h>
#include <math.h>
#include <vector>
#include <iostream>
#include <sstream>
 
using namespace std;
 
/**** Combattant ****/
 
class Combattant {
  friend ostream & operator << (ostream &, const Combattant &);
 
public:
 
  Combattant(string _nom, int _force);
  Combattant(string _nom);
  Combattant();  
  ~Combattant();
 
  const string nom();
  const int force();
  void setForce(int _force);
  void setForce();
  
protected:
 
  string nom_;
  int force_;
  
};
 
/**** Ring ****/
 
class Ring {
  friend ostream & operator << (ostream &, const Ring &);
 
public:
 
  Ring();
  ~Ring();
 
  void add(Combattant * c);
  void reset();
  int nbCombattants();
  Combattant * numCombattant(int i);
  int battle();
 
protected:
 
  vector<Combattant *> combattants;
 
};
 
/**** Battle ****/
 
class Battle {
  friend ostream & operator << (ostream &, const Battle &);
 
public:
  
  Battle(int x = 8, int y = 8);
  ~Battle();
 
  int iterate(bool detail);
 
private:
  
  void deplace();
  int baston();
 
protected:
 
  Ring ** field1;
  Ring ** field2;
  int _nl;
  int _nc;
  // ATTENTION !!
  // ce dernier attribut permet de garder la liste des combattants alloués par
  // Battle. Sans cette mémoire, les combattants disparus dans Ring::battle()
  // ne pourraient pas être libérés et seraient condamnés à hanter votre
  // système pour l'eternité !...
  vector<Combattant *> participants;
 
};
 
/**** utilitaires de chaines  ****/
 
// Cette fonction calcule une chaine correspondant à src
// répétée repeat fois
string repeat(string src, int repeat);
 
// Cette fonction calcule une chaine de taille constante
// contenant i, sachant que i contient au maximum x chiffres
string chCase(int i, int x);
 
#endif // _MASSACRE_H_
 
 
 

Mise en oeuvre

#include "Massacre.h"
 
/**** Combattant ****/
 
Combattant::Combattant(string _nom, int _force) : nom_ (_nom), force_ (_force) {}
 
Combattant::Combattant(string _nom) : nom_ (_nom) {
  force_ = 1 + rand() % 100;
}
 
Combattant::Combattant() {
  force_ = 1 + rand() % 100;
  nom_ = "";
  
  int taille = 6;
  string consonnes = "bbccddfghjklllmmnnppqrrssttvvwxz";
  string voyelles = "aaéèeiouy";
  
  nom_ += toupper(consonnes[rand() % consonnes.size()]);
  nom_ += voyelles[rand() % voyelles.size()];
  for (int i=2; i<taille; i+=2) {
    nom_ += consonnes[rand() % consonnes.size()];
    nom_ += voyelles[rand() % voyelles.size()];
  }
}
 
Combattant::~Combattant() {}
 
const string Combattant::nom() {
  return nom_;
}
 
const int Combattant::force() {
  return force_;
}
  
void Combattant::setForce(int _force) {
  force_ = _force;
}
 
void Combattant::setForce() {
  force_ = 1 + rand() % 100;
}
 
/**** Ring ****/
 
Ring::Ring() {}
 
Ring::~Ring() {}
 
void Ring::add(Combattant * c) {
  combattants.push_back(c);
}
 
void Ring::reset() {
  combattants.clear();
}
    
int Ring:: nbCombattants() {
  return combattants.size();
}
 
Combattant * Ring::numCombattant(int i) {
  if (i>=(int)combattants.size()||i<0) return 0;
  return combattants[i];
}
    
// Sur le ring à la fin de la bataille : le ou les plus forts.
// S'il y a plusieurs survivants : on réaffecte leur force
// aléatoirement (il ne reste plus qu'à les déplacer)
// Le résultat correspond au nombre de survivants
int Ring::battle() {
  if (combattants.size() > 1) {
    vector<Combattant *> result;
    Combattant * champion = combattants[0];
    result.push_back(champion);
    for (size_t no = 1; no < combattants.size(); no++) {
      Combattant * challenger = combattants[no];
      if (challenger->force() > champion->force()) {
result.clear();
champion = challenger;
result.push_back(champion);
      } else if (challenger->force() == champion->force()) {
result.push_back(challenger);
      }
    }
    // Modification de la force des survivants
    if (result.size()>1)
      for (size_t no = 0; no < result.size(); no++)
combattants[no]->setForce();
    combattants = result;
  }
  return combattants.size();
}
 
/**** Battle ****/
 
Battle::Battle(int x, int y) : _nl(x), _nc(y) {
  field1 = new (Ring *) [_nl]; // on alloue un tableau de pointeurs
  field2 = new (Ring *) [_nl];
  for (int i = 0; i < _nl; i++) {
    field1[i] =  new Ring [_nc]; // ATTENTION : on alloue un tableau d'objets !
    // Rappel : les cases sont initialisées avec le constructeur par défaut de Ring
    field2[i] =  new Ring [_nc];
    for (int j = 0; j < _nc; j++) {
      Combattant * rambo = new Combattant();
      field1[i][j].add(rambo);
      participants.push_back(rambo);
    }
  }
}
 
Battle::~Battle(){
  for (int i = 0; i < _nl; i++) {
    delete [] field1[i]; // ATTENTION : on libère un tableau d'objets :
                         // delete [] obligatoire
    delete [] field2[i];
  }
  delete field1; // on libre un tableau de pointeurs
  delete field2;
  for (size_t no = 0; no < participants.size(); no++)
    delete participants[no]; // on libère les participants, qu'ils apparaissent
                             // sur le champs de bataille ou non
}
 
// résultat : nombre de tours pour arriver au dernier
int Battle::iterate(bool detail) {
  int surv = baston();
  int tour = 0;
  char repOuiNon;
  cout << "******** Grille initiale" << endl << *this << endl;
  while (surv != 1) {
    deplace();
    if (detail) {
      cout << "******** APRES DEPLACEMENT" << endl << *this << endl;
      cout << "Continuer le dtail [o/n] ? ";
      cin >> repOuiNon; detail = (repOuiNon=='o'?true:false);
    }
    surv = baston();
    if (detail) {
      cout << "******** APRES BATAILLE" << endl << *this << endl;
      cout << "Continuer le détail ? ";
      cin >> repOuiNon; detail = (repOuiNon=='o'?true:false);
    }
    tour++;
    if (!detail)
      cout << "\r******** Tour : " << tour << " -- survivants : " << surv 
  << "                  ";
  }
  cout << "\n******** Etat final atteint en " << tour << " tours" << endl
       << *this << endl;
  return tour;
}
 
void Battle::deplace(){
  for (int i = 0; i < _nl; i++) {
    for (int j = 0; j < _nc; j++) {
      for (int n=0; n<field1[i][j].nbCombattants(); n++) {
int direction = rand() % 4;
int nexti=i;
int nextj=j;
switch (direction) {
case 0 : //nord
 nexti=(i-1+_nl)%_nl;
 break;
case 1 : //sud
 nexti=(i+1)%_nl;
 break;
case 2 : //ouest
 nextj=(j-1+_nc)%_nc;
 break;
case 3 : //est
 nextj=(j+1)%_nc;
 break;
}
field2[nexti][nextj].add(field1[i][j].numCombattant(n));
      }
    }
  }
  Ring ** old_field1 = field1;
  field1 = field2;
  field2 = old_field1; // et maintenant, on nettoie :
  for (int i = 0; i < _nl; i++)
    for (int j = 0; j < _nc; j++)
      field2[i][j].reset();
}
 
// rsultat : nombre de survivants
int Battle::baston() {
  int s = 0;
  for (int i = 0; i < _nl; i++)
    for (int j = 0; j < _nc; j++)
      s+=field1[i][j].battle();
  return s;
}
 
/**** fonctions amies  ****/
 
ostream & operator << (ostream & o, const Combattant & c) {
  return o << c.nom_ << " (" << c.force_ << ")";
}
 
ostream & operator << (ostream & o, const Ring & r) {
  for (size_t no = 0; no < r.combattants.size(); no++)
    o << * r.combattants[no] << " ";
  return o;
}
 
ostream & operator << (ostream & o, const Battle & b) {
  string res1="";
  int numRing=1;
  int tailleMaxNumRing = (int)(log((float)b._nl*b._nc)/log((float)10))+1;
  for (int i = 0; i < b._nl; i++) {
    res1+=(repeat("-",(tailleMaxNumRing+1)*b._nc+1) + "\n|");
    for (int j = 0; j < b._nc; j++) {
      if (b.field1[i][j].nbCombattants()!=0) {
  res1+=chCase(numRing,tailleMaxNumRing)+"|";
      }
      else res1+=(repeat(" ",tailleMaxNumRing)+"|");
      numRing++;
    }
    res1+="\n";
  }
  res1+=(repeat("-",(tailleMaxNumRing+1)*b._nc+1)+"\n");
  o << res1;
  numRing=1;
  for (int i = 0; i < b._nl; i++) {
    for (int j = 0; j < b._nc; j++) {
      if (b.field1[i][j].nbCombattants()!=0)
o << "Ring " << numRing << " : " << b.field1[i][j] << endl;
      numRing++;
    }
  }
  return o;
}
 
/**** utilitaires de chaines  ****/
 
string repeat(string src, int repeat) {
  ostringstream buf;
  for (int i=0;i<repeat;i++)
    buf << src;
  return buf.str();
}
 
string chCase(int i, int x) {
  int tailleReste = x-((int)(log((float)i)/log((float)10))+1);
  ostringstream res;
  res << repeat(" ", tailleReste/2) << i
      << repeat(" ", tailleReste- tailleReste/2);
  return res.str();
}
 
/*****************************************************************************/
 
 
#ifdef _TEST_
 
 
int main(void) {
  // on réinitialise le générateur de nombres alatoires à chaque excution
  srand (time(NULL));
  
  int l, c;
  bool detail;
  char repOuiNon;
  cout << "Nombre de lignes : ";
  cin >> l;
  cout << "Nombre de colonnes : ";
  cin >> c;
  cout << "Détail des tapes [o/n] : ";
  cin >> repOuiNon; detail = (repOuiNon=='o'?true:false);
 
  Battle battle(l,c);
  battle.iterate(detail);
 
  return 0;
 
}
 
 
#endif // _TEST_
 
 

Question subsidiaire

Si vous testez ce programme avec une grille comportant un nombre pair de lignes et un nombre pair de colonnes, il ne pourra en rester que... deux !
Dans tous les autres cas, même en se poursuivant pendant un bon moment, les deux derniers combattants finiront par se rencontrer. Le cas où ils se croisent éternellement est infiniment peu probable.
 
D'où vient le problème ?
  • Est-ce un problème de mise en oeuvre ? (bug dans le code)
  • Est-ce un problème de conception ? (la structure, le comportement et/ou l'organisation des classes sont mal adaptés au problème posé)
  • Est-ce un problème d'analyse ? (le problème est mal posé)