Problème avec un programme en C++ et avec SFML

leo3108

Membre enregistré
4 Mars 2010
5
0
31
Bonjour. Je suis nouveau sur le forum même si je passais souvent en visiteur avant.
Mais malgré mes recherches sur le site, je n'ai pas trouvé de solution à mon problème.

Je programmais avant en ruby, et j'avais codé un programme pour jouer au puissance 4. Il marchait en console, et je faisais un affichage assez laid, en affichant ligne par ligne des X et des O. Le programme fonctionnait parfaitement.

Puis, j'ai voulu apprendre le C++. Comme exercice, j'ai essayé de transcrire mon code de ruby en C++. Après quelques difficultés, j'ai réussi. Puis, je me suis dit que ça pourrait être mieux si j'utilisais une interface graphique. En regardant sur des forums, j'ai vu que la bibliothèque SFML était assez conseillée en C++.

J'ai installé la SFML exactement comme indiqué sur le tuto du site officiel, j'ai fait quelques test, et tout fonctionne parfaitement.

Enfin, j'ai utilisé le SFML, et la, le programme ne fonctionne pas, sans que je n'arrive à comprendre mon erreur.

Je code sur Xcode 3.1.4, et suis sous Mac OSX 10.5 "Leopard", sur un portable PowerBook G4 pas intel. Je vous envoie mon code. Je tiens à dire que c'est une première version, et que j'ai déjà des idée pour l'accélérer, mais j'aimerai déjà qu'il fonctionne dans cet état.

Remarque 1 : Ne faites pas attention à la méthode display. J'avais commencé par utiliser SDL, mais j'ai préféré SFML, et je n'ai pas encore rectifié cette fonction, j'ai juste changé son paramètre pour que elle ne gène pas le programme ou elle n'est pas encore appelée (j'ai mit tous ses appels en commentaire, pour que vous voyez ce qu'elle est supposée faire)

Remarque 2 : Le jeu en ruby permettait de faire jouer l'ordinateur. J'ai donc laissé ici dans mes fonctions des paramètres pour pouvoir, quand mon programme marchera, intégrer à nouveau le jeu contre l'ordinateur. Ne faites donc pas attention aux variables bool computer par exemple

fichier main.cpp
Bloc de code:
#include <iostream>
#include <SFML/Graphics.hpp>
#include "Game.h"
#include "fonctions.h"

using namespace std;

int main () {
    Game game;
	sf::RenderWindow fenetre(sf::VideoMode(800, 600, 32), "SFML Graphics"); 
	fenetre.Clear(sf::Color(0, 128, 255));
	
	sf::RenderWindow &ref_fenetre = fenetre;
	
	//game.display();
	game.a_game(false, false ,true, fenetre);
	//game.display();
	game.~Game();
	fenetre.Display();
	return 0;
}


Game.hpp

Bloc de code:
# define Height  6
# define Width  7
# define Power  4
# define Depth  4
# define Nb_Case ( Width*Height )

#ifndef DEF_GAME 
#define DEF_GAME

class Game
{
	public:
		Game(); // Constructeur
		bool pcheck(); // Vérifie les position gagnantes
		void play (bool computer);// Fait jouer
		int  play_player();// Fait jouer le joueur
		void possibilities(int *possibilities);// Donne les possibilités de jeu
		int  next_player();// Donne le joueur suivant
		void cplay(int column);//Place le pion dans le board et dans l'historique
		void a_game(bool first_robot, bool second_robot, bool display, sf::RenderWindow &fenetre);
		void display(sf::RenderWindow &fenetre);
	
	private: 
		int m_history[Nb_Case]; // L'historique des coups joués
		int m_board[Width][Height]; // Le tableau de jeu
		int m_winner ; // Le vainqueur
};

#endif

Game.cpp
Bloc de code:
#include <iostream>

#include <string>
#include <SFML/Graphics.hpp>

#include "Game.h"

# define Height  6
# define Width  7
# define Power  4
# define Depth  4
# define Nb_Case (Width*Height)

using namespace std ;

Game::Game() : m_winner(0){//Constructeur
	for (int i=0; i<Nb_Case; i++) {
		m_history[i]=0;
	}
	
	
	for (int i=0; i<=Width; i++) {
		for (int j=0; j<=Height; j++) {
			m_board[i][j] = 0;
		}
	}
}


bool Game::pcheck()
{
	// res est la variable qui donnc true s'il y a une position gagnante et false s'il n'y en a pas.
	bool res = false;
	// On définit x, la valeur de la dernière position jouée.
	int x = m_board[derniere_valeur(m_history,Nb_Case)-1][taille_reelle(m_board[derniere_valeur(m_history,Nb_Case) - 1],Width) - 1];
	// On définit quatre vecteurs, qui sont les quatres directions dans lesquelles on peut faire un puissance quatre.
	// Pour chacun de ces vecteurs ...
	int vecteurs[4][2] = {1,0,   0,1,   1,1,   1,-1};
	for (int i=0; i<4; i++) {
		// On définit ii comme la valeur en abscisses des coordonnées du vecteur
		// On définit jj comme la valeur en ordonée des coordonnées du vecteur
		// On définit k qui est le "comteur de puissance"
		// On définit n qui est le compteur de cases étudiées dans un des deux sens du vecteur étudié
		int ii = vecteurs[i][0], jj = vecteurs[i][1], k=1, n=1;
		// Tant que : 
		// - On est pas passés sous la ligne 0
		// - On a pas dépassé la colonne maximale ou minimale
		// - La case que l'on étudie est bien du même signe que la dernière jouée
		// - Alors ...
		while (taille_reelle(m_board[derniere_valeur(m_history, Nb_Case) - 1], Width) - 1 + n * jj >= 0 &&  derniere_valeur(m_history, Nb_Case)-1 + n * ii < Width  && m_board[derniere_valeur(m_history, Nb_Case)-1 + n * ii][taille_reelle(m_board[derniere_valeur(m_history, Nb_Case) - 1],Height) - 1 + n * jj] == x){
			// On incrémente le compteur de puissance
			k++ ;
			// On incrémente le compteur de case étudiées dans ce sens
			n++ ;
		}
		// Comme on change de sens, on remet le compteur de cases étudiées dans un des deux sens du vecteur étudié à 0
		n = 1;
		// Tant que : 
		// - On est pas passés derrière la colonne 0
		// - On est pas passés sous la ligne 0
		// - La case que l'on étudie est bien du même signe que la dernière jouée
		while (taille_reelle(m_board[derniere_valeur(m_history, Nb_Case) - 1], Width) - 1 - n * jj >= 0 && derniere_valeur(m_history, Nb_Case)-1 - n * ii >= 0 && m_board[derniere_valeur(m_history, Nb_Case)-1 - n * ii][taille_reelle(m_board[derniere_valeur(m_history, Nb_Case) - 1],Height) - 1 - n * jj] == x)
		{
			// On incrémente le compteur de puissance
			k++ ;
			// On incrémente le compteur de case étudiées dans ce sens
			n++ ;
		}
		if (k>=Power){
			res = true ;
		}
	}
// while @board[@history[-1] - 1].length - 1 + n * jj >= 0 and @board[@history[-1]-1 + n * ii] and @board[@history[-1]-1 + n * ii][@board[@history[-1] - 1].length - 1 + n * jj] == x
// while @board[@history[-1] - 1].length - 1 - n * jj >= 0 and @history[-1]-1 - n * ii >= 0 and    @board[@history[-1]-1 - n * ii][@board[@history[-1] - 1].length - 1 - n * jj] == x	
	return res;
}







// On définit la méthode qui gère le jeu et fait jouer l'ordinateur et le joueur à partir d'une colonne qu'on lui donne
// On rentre comme paramètre le joueur qui doit jouer et la présence ou non d'un ordinateur
void Game::play (bool computer)
{
	int column;
	// Si computer = true, alors on fait appel à la méthode play_computer
	//if (computer){
	//	column = (*this).beta(Depth,computer)[1];
	//}
	// Si computer = false, alors on fait appel à la méthode play_player
	//else{
		column = (*this).play_player();
	//}
	// Si la colonne n'existe pas, alors on rejoue
	if (column > Width){
		(*this).play(computer) ;
	}
	// Si la colonne est pleine, alors on rejoue
	else if (taille_reelle(m_board[column -1],Width)+1 > Height){
		(*this).play(computer);
	}
	else{
		(*this).cplay(column);
	}
	if (m_winner != 0){
		// Si il y a un vainqueur, alors m_winner = le numéro du joueur
	}
}


int Game::play_player()
{
	int x, column;
	/*
	 sf::Event Event;
	 while (App.GetEvent(Event))
	 {
	 // Traitement de l'évènement
	 }
	 
	 App.Display();
	*/
	while (continuer)
	{
		sf::Event Event;
		while (App.GetEvent(Event))
		{
			switch(Event.type)
			{
				case sf::Event::Closed :
					continuer = false;
					break;
				case sf::Event::MouseButtonReleased
					x = Event.MouseMove.X;
					if (x > 0 && x <= 45) {column = 1;}
					else if (x > 45 && x <= 85) {column = 2;}
					else if (x > 85 && x < 125) {column = 3;}
					else if (x > 125 && x < 165) {column = 4;}
					else if (x > 165 && x < 205) {column = 5;}
					else if (x > 205 && x < 245) {column = 6;}
					else if (x > 245 && x < 285) {column = 7;}
					continuer = false;
					break;
			}
		}
	}
	return column;
}




void Game::possibilities(int *possibilities){
	// Parmi tous les nombres de 1 à @width, on choisit ceux tels que, soit un nombre x, la colonne x ne soit pas remplie
	for (int i=0; i<Width; i++) {
		if (taille_reelle(m_board[i],Height) < Height){
			possibilities[taille_reelle(possibilities, Width)] = i+1;
		}
	}
}







int Game::next_player(){
	int player ;
	// Si le nombre de coups joués est pair, alors le joueur est 1
	if (taille_reelle(m_history, Nb_Case) % 2 == 0){
		player = 1;
	}
	// Sinon, le joueur est -1
	else{
		player = -1;
	}
	return player;
}


void Game::cplay(int column){
	// On rajoute le numéro du joueur à la colonne jouée dans m_board
	m_board[column-1][taille_reelle(m_board[column-1], Height)] = (*this).next_player();
	// On rajoute le numéro de la colonne à m_history
	m_history[taille_reelle(m_history, Nb_Case)] = column;
	if ((*this).pcheck()){
		// Si il y a un vainqueur, alors m_winner = le numéro du joueur
		m_winner = (*this).next_player() * -1;
	}
}

void Game::a_game(bool first_robot = false, bool second_robot = false, bool display = true, sf::RenderWindow &fenetre){
	int nb_tour = Nb_Case/2;
	if (display){
		sf::Shape Cercle = sf::Shape::Circle(0, 0, 10, 0,0,0);
		for (int i=0; i < Width; i++) {
			for (int j=0; j<Height ; j++) {
				Cercle.SetPosition(5+10.0*i, 5+10.0*j);

			}
		}
	}
	for (int i=0; i<= nb_tour; i++) {
 		if (m_winner==0){(*this).play(first_robot);}
		//if (m_winner==0 && display){(*this).display(fenetre);}
		if (m_winner==0){(*this).play(second_robot);}
		//if (m_winner==0 && display){(*this).display(fenetre);}
	}
}
	
	

void Game::display(sf::RenderWindow &fenetre)
{
	for (int i=0; i< Width; i++) {
		int x = m_board[derniere_valeur(m_history, Nb_Case)-1][taille_reelle(m_board[derniere_valeur(m_history, Nb_Case)-1], Height)-1];
		if (x==-1){
			pion = SDL_LoadBMP("pion_jaune.bmp");
			SDL_BlitSurface(pion, NULL, ecran, &position_pion);
		}
		else {
			pion = SDL_LoadBMP("pion_rouge.bmp");
			SDL_BlitSurface(pion, NULL, ecran, &position_pion);
		}
	}
}


fonctions.hpp

Bloc de code:
#ifndef DEF_fonction
#define DEF_fonction

int taille_reelle(int *tableau, int longueur_tableau);
int derniere_valeur(int *tableau, int longueur_tableau);

#endif





fonctions.cpp
Bloc de code:
#include <iostream>
#include "Game.hpp"
#include "fonctions.hpp"

using namespace std;

int taille_reelle(int *tableau, int longueur_tableau)
{		
	int i=-1 , x=0;
	do {
		i++;
		x = tableau[i] ;
	} while (x != 0 && i<longueur_tableau);
	return i;
}


int derniere_valeur(int *tableau, int longueur_tableau)
{
	return tableau[taille_reelle(tableau, longueur_tableau)-1];
}


Et le message d'erreur est le suivant :

Ld /Users/leonard/Desktop/C++/p4_SFML/build/Debug/p4_SFML.app/Contents/MacOS/p4_SFML normal ppc
cd /Users/leonard/Desktop/C++/p4_SFML
/Developer/usr/bin/g++-4.0 -arch ppc -L/Users/leonard/Desktop/C++/p4_SFML/build/Debug -F/Users/leonard/Desktop/C++/p4_SFML/build/Debug -filelist /Users/leonard/Desktop/C++/p4_SFML/build/p4_SFML.build/Debug/p4_SFML.build/Objects-normal/ppc/p4_SFML.LinkFileList -framework sfml-graphics -framework sfml-system -framework sfml-window -o /Users/leonard/Desktop/C++/p4_SFML/build/Debug/p4_SFML.app/Contents/MacOS/p4_SFML
Undefined symbols:
"Game::Game()", referenced from:
_main in main.o
"Game::a_game(bool, bool, bool, sf::RenderWindow&)", referenced from:
_main in main.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

Si vous avez une idée, répondez-moi s'il vous plaît. Je vous remercie beaucoup.
 
Erf, trop tard, bon comme déjà dit par mpergand

dans le code (par endroits, pas dans tous les cas):
#include "Game.h"
#include "fonctions.h"

noms de fichiers:
fonctions.hpp
Game.hpp

Le compilateur fait le lien tout seul entre .h et .hpp?
 
Oui, merci, j'avais pas vu ça. Mais ça ne change rien au problème. Xcode m'indique toujours le même message d'erreur.
 
Quand on installe SFML, on installe avec des templates, qui font qu'on peut directement créer des projets SFML. Il y a donc directement quatre .framework intégrés au projet. Mais de toute façon, j'ai fait des test avant avec d'autres projet (afficher une fenêtre, une forme), et tout fonctionne très bien, ce qui signifie que le lien est fait.
 
"Symbol not found" est une erreur de link pas de compilation. Ta ligne de commande est mal foutue, la librairie qui contient ton symbole manquant n'a pas liée et il n'y a pas d'autre cause possible à ce genre d'erreur. Donc cherche pourquoi ça ne marche pas dans ce cas.

Pour inspecter les symboles contenus dans un exécutable ou une librairie, tu as la commande nm.
 
Ta ligne de commande est mal foutue, la librairie qui contient ton symbole manquant n'a pas liée et il n'y a pas d'autre cause possible à ce genre d'erreur.

Si, il se peut qu'il l'ait lié mais pas au bon moment. Lors du link il y a un sens pour lier les librairies, si on ne le fait pas comme il le faut, des symboles ne sont pas liés entre eux dans les tables. Maintenant il existe des IDE assez intelligent pour le faire à ta place mais dans un Makefile créé à la main, il faut bien faire attention à ça. D'abord les librairies des couches hautes puis les couches les plus basses à la fin. En fait le mieux c'est d'utiliser des librairies dynamiques où se problème ne se pose pas, ça permet de modifier son application plus aisément. Le désavantage c'est qu'il faut faire attention aux versions d'API. :p :D :D
 
C'est bon, mon problème est réglé. En fait, j'avais importé tous mes .cpp et mes .h d'un autre projet. Je me suis dit que l'IDE était nécessairement moins intelligent si il ne comprenait pas dès la création quels fichiers allaient avec quels autres fichiers. Je trouvais cette expliquation pas très rationnelle, mais l'informatique n'est pas toujours une science exacte.
J'ai donc créé un nouveau projet, et créé des nouveaux fichiers (sans importer les anciens), et fait des copier/coller de mon code dans mon nouveau projet.
Là, Xcode m'a indiqué 74 erreurs (ce qui me paraissait beaucoup). J'ai fait jouer les #include , en les changeant de place, et rien qu'en faisant ça, j'ai réduit à moins de 10 erreurs, ce qui est parfaitement vraissemblable.
Voila, je vous remercie beaucoup de m'avoir donné des idées.
 
mais l'informatique n'est pas toujours une science exacte.
Ben si justement, c'est un domaine où il n'y a rien de magique mais toujours une bonne explication à un problème rencontré, plus ou moins facile à trouver. Mais il n'y a rien de plus prévisible que le fonctionnement d'un circuit électronique numérique donc d'un programme : pour un état initial donné et une traitement donné, le résultat est toujours le même ... tant que tu n'introduis pas de variables aléatoires. Les anomalies vraiment aléatoires sont très très rares.
Par contre il arrive souvent qu'on ne sache pas comment fonctionne exactement les outils ou librairies utilisées faute d'une documentation insuffisante ou incomplète, c'est là qu'on rame.
 
Le problème qui rend le développement dit "aléatoire" sont les différentes couches entre le hardware et le software. Certaines ne sont pas libre d'accès, et s'il y a un soucis dans l'une d'elle aussi infime soit-elle, elle peut se répercuter dans la couche la plus haute. Et lors du debug tu te rend compte que ce n'est pas ton développement qui est en cause mais des couches plus basses dont tu n'as les sources. Et ça c'est le meilleur des cas, parce que si le problème vient du hardware c'est encore plus lourd à gérer. Bref avoir les sources sur l'ensemble des couches software (os inclus) devient de plus en plus courant dans les sociétés dont le moindre problème peut devenir très vite critique. :p :D :D