[C++] Jeu : Puissance 4

Statut
N'est pas ouverte pour d'autres réponses.

neku

Codeur roumain
Voila, un ami devait pour son cours de prog faire un puissance 4 alors les conditions suivantes :

- Utilisation des structures
- Utilisation des classes
- Utilisation de l'héritage des classes

je lui ai donc fait ce petit jeu puissance 4 en C++
Cette source est n'effectue pas le test sur les diagonales (il faut bien qu'il bosse un peu :p) mais c'est facilement faisable ;)

Code:
#include <iostream>
#include <string>
#include <ctype.h>

using namespace std;

#if defined(_MAC_) || defined(_LINUX_)
	//Idem clrscr() Borland
	#define clrscr() printf("\033[1;1H"); printf("\033[2J"); printf("\n");
#elif defined(_BORLAND_)
	#include <conio.h>
#elif defined(_VISUAL_STUDIO_)
	#include <cstdlib>
	#define clrscr() system("cls");
#else
	#error Ce logiciel ne peut etre compile avec ce compilateur
#endif

#define PRINT(String) cout << String;
#define PRINTCR(String) cout << String << endl;
#define READ(Var) cin >> Var;

#define BOARD_HEIGHT	10
#define BOARD_WIDTH		10

#define EMPTY_TOKEN " "
#define SEPARATOR "|"
#define TOKEN Token->Player->ID

//Retourne FALSE si la chaine n'est pas une chaine contenant que des chiffres
bool IsStringDigit(const string & String) {
	bool IsDigit = false;
	for (unsigned int Index = 0; Index < (unsigned int)String.size(); Index++) {
		IsDigit |= (isdigit(String.c_str()[Index]) == 0);
	}
	return !IsDigit;
}

typedef struct Player_t {
	unsigned int ID;
	string Name;
	unsigned int Color;
};

typedef struct Token_Position_t {
	unsigned int X;
	unsigned int Y;
};

typedef struct Token_t {
	unsigned int ID;
	Player_t* Player;
	Token_Position_t Position;
};

class GameBoard {
	public:
		GameBoard();
		~GameBoard();
		
		void InitBoard(void);
		void ResetToken(Token_t* Token);
		bool SetToken(Token_t* Token);
		const Token_t* GetToken(unsigned int ID);
		const Token_t* GetToken(const Token_Position_t* Token_Position);
	protected:
		const Token_t* GetFreeToken(void);
	private:
		Token_t* Board;
};

GameBoard::GameBoard() {
	//Lors de l'instanciation de la classe on aloue l'espace requis pour le plateur de jeu
	this->Board = new Token_t[BOARD_HEIGHT * BOARD_WIDTH];
}

GameBoard::~GameBoard() {
	//Lors de la destruction on libère l'espace utilisé
	delete []this->Board;
}

const Token_t* GameBoard::GetToken(const Token_Position_t* Token_Position) {
	for (unsigned int Index = 0; Index < (BOARD_WIDTH * BOARD_HEIGHT); Index++) {
		if ((this->Board[Index].Position.X == Token_Position->X) && (this->Board[Index].Position.Y == Token_Position->Y))
			return &this->Board[Index];
	}
	return NULL;
}

const Token_t* GameBoard::GetToken(unsigned int ID) {
	if (ID < (BOARD_WIDTH * BOARD_HEIGHT))
		return &this->Board[ID];
	return NULL;
}

bool GameBoard::SetToken(Token_t* Token) {
	if (Token->ID < (BOARD_WIDTH * BOARD_HEIGHT)) {
		this->Board[Token->ID] = *Token;
		return true;
	}
	else {
		return false;
	}
}

const Token_t* GameBoard::GetFreeToken(void) {
	for (unsigned int Index = 0; Index < (unsigned int)(BOARD_WIDTH * BOARD_HEIGHT); Index++) {
		if (this->Board[Index].Position.X == BOARD_WIDTH && this->Board[Index].Position.Y == BOARD_WIDTH)
			return &this->Board[Index];
	}
	return NULL;
}

void GameBoard::InitBoard(void) {
	Token_t* Empty_Token = new Token_t;
	
	this->ResetToken(Empty_Token);
	for (unsigned int Index = 0; Index < (unsigned int)(BOARD_HEIGHT * BOARD_WIDTH); Index++) {
		Empty_Token->ID = Index;
		//this->Board[Index] = *Empty_Token;
		this->SetToken(Empty_Token);
	}
	
	delete Empty_Token;
}

void GameBoard::ResetToken(Token_t* Token) {
	Token->Player = NULL;
	//On sort les jeton en dehors du plateau de jeu
	Token->Position.X = BOARD_WIDTH;
	Token->Position.Y = BOARD_HEIGHT;
}

class Puissance4 : GameBoard {
	public:
		Puissance4();
		~Puissance4();
		
		void NewGame(unsigned int nPlayers);
		void EndGame(void);
		bool IsGameRunning(void);
		void Redraw(void);
		void Draw();

		bool IsWinner(const Token_t* Token);
		const Token_t* PlayerAction(const Player_t* Player);
		const Player_t* GetPlayerInfos(unsigned int nPlayer);
		
	protected:
		unsigned int GetY(unsigned int X);
		void SetPlayersInfos(void);
		
		unsigned int nPlayers;
		Player_t* Players;
		bool InGame;
};

Puissance4::Puissance4(void) {
	this->nPlayers = 0;
	this->InGame = false;
}

Puissance4::~Puissance4(void) {
}

void Puissance4::NewGame(unsigned int nPlayers) {
	this->InitBoard();	
	//On reserve l'espace mémoire nécessaire au stockage des données joueur
	this->nPlayers = nPlayers;
	this->Players = new Player_t[nPlayers];
	this->SetPlayersInfos();
	this->InGame = true;
}

void Puissance4::EndGame(void) {
	//On libère l'espace utilisé par les données joueurs
	if (this->Players)
		delete []this->Players;
}

void Puissance4::SetPlayersInfos(void) {
	for (unsigned int Index = 0; Index < this->nPlayers; Index++) {
		this->Players[Index].ID = (Index + 1);
		PRINT("Entrez le nom du joueur " << (Index + 1) << " : ");
		READ(this->Players[Index].Name);
	}
}

const Player_t* Puissance4::GetPlayerInfos(unsigned int nPlayer) {
	return (const Player_t*)&this->Players[nPlayer];
}

bool Puissance4::IsGameRunning(void) {
	return this->InGame;
}

void Puissance4::Redraw(void) {
	//On nettoie l'écran
	clrscr();
	
	PRINTCR(" 0 1 2 3 4 5 6 7 8 9 ");
	PRINTCR("---------------------");
	
	for (int Y = (BOARD_HEIGHT - 1); Y >= 0; Y--) {
		PRINT("|")
		for (unsigned int X = 0; X < BOARD_WIDTH; X++) {
			bool Found = false;
			for (unsigned int Index = 0; Index < (unsigned int)(BOARD_WIDTH * BOARD_HEIGHT); Index++) {
				const Token_t* Token = this->GetToken(Index);
				if (Token->Player) {
					if ((Token->Position.X == X) && (Token->Position.Y == Y)) {
						PRINT(TOKEN << SEPARATOR);
						Found = true;
						break;
					}	
				}
			}
			if (!Found) PRINT(EMPTY_TOKEN << SEPARATOR);
		}
		cout << endl;
	}
}

//La partie n'a pas connu de vainqueur :/
void Puissance4::Draw() {
	this->InGame = false;
	PRINTCR("La partie n'a pas connue de vanqueur !");
}

unsigned int Puissance4::GetY(unsigned int X) {
	unsigned int nextY = 0;
	//On recherche la prochaine place en Y d'une colonne libre
	for (unsigned int Index = 0; Index < (unsigned int)(BOARD_WIDTH * BOARD_HEIGHT); Index++) {
		const Token_t* Token = this->GetToken(Index);
		//Si il s'agit d'un jeton joueur et que le X correspond à celui demandé
		if (Token->Player && Token->Position.X == X) {
			//Si la position du jeton trouvé + 1 est plus grand que le prochain Y deja trouvé ou 0
			if ((Token->Position.Y + 1) > nextY)
				nextY = Token->Position.Y + 1;
		}
	}
	return nextY;
}

const Token_t* Puissance4::PlayerAction(const Player_t* Player) {
	string Colonne;
	const Token_t* Token = this->GetFreeToken();
	
	if (!Token)
		return NULL;
		
	Token_t* Temp_Token = new Token_t;
	Temp_Token->ID = Token->ID;

	do {
		do {
			PRINT(Player->Name << ", Entrez la colonne ou placer votre jeton (0 - " << (BOARD_WIDTH - 1) << ")");
			READ(Colonne);
		
			Temp_Token->Position.X = atoi(Colonne.c_str());
		} while ((Temp_Token->Position.X > (BOARD_WIDTH - 1)) || !IsStringDigit(Colonne));
	
		//On recherche si il reste une place dans la colonne
		Temp_Token->Position.Y = this->GetY(Temp_Token->Position.X);
	} while (Temp_Token->Position.Y > (BOARD_HEIGHT - 1));
	
	Temp_Token->Player = (Player_t*)Player;
	this->SetToken(Temp_Token);
	
	delete Temp_Token;
	
	return (const Token_t*)Token;
}

//On est pas fou ... on ne test pas tout les jetons ^^
//On test uniquement le jeton qui vient d'être posé en chercher si d'autre du même joueur sont voisins ou pas ...
bool Puissance4::IsWinner(const Token_t* Token) {
	//Faire ici les test sur les token pour savoir si le joueur en a alligné 4 ...
	
	unsigned int H_Aligned = 0;
	unsigned int V_Aligned = 0;
	unsigned int D1_Aligned = 0;
	unsigned int D2_Aligned = 0;
	
	Token_Position_t* Temp_Token_Position = new Token_Position_t;
	
	//Gauche
	Temp_Token_Position->Y = Token->Position.Y;
	for (int X = Token->Position.X - 1; (X >= 0) && (X >= ((int)Token->Position.X - 3)); X--) {
		Temp_Token_Position->X = X;
		const Token_t* GToken = this->GetToken((const Token_Position_t*)Temp_Token_Position);
		//Si il y a un jeton ...
		if (GToken) {
			//Si le jeton appartient au même joueur et est différent de celui de départ
			if ((GToken->Player == Token->Player) && (GToken->ID != Token->ID))
				H_Aligned++;
			//Sinon on arrête la boucle car il n'y a plus de contact avec une autre jeton
			else
				break;
		}
		//Sinon on arrête la boucle car il n'y a plus de contact avec un autre jeton
		else
			break;
	}
	
	//Droite
	for (int X = (int)Token->Position.X + 1; (X <= (BOARD_WIDTH - 1)) && (X <= ((int)Token->Position.X + 3)); X++) {
		Temp_Token_Position->X = X;
		const Token_t* GToken = this->GetToken((const Token_Position_t*)Temp_Token_Position);
		if (GToken) {
			if ((GToken->Player == Token->Player) && (GToken->ID != Token->ID))
				H_Aligned++;
			else
				break;
		}
		else
			break;
	}
	
	//Bas
	Temp_Token_Position->X = Token->Position.X;
	for (int Y = (int)Token->Position.Y - 1; (Y >= 0) && (Y >= ((int)Token->Position.Y - 3)); Y--) {
		Temp_Token_Position->Y = Y;
		const Token_t* GToken = this->GetToken((const Token_Position_t*)Temp_Token_Position);
		if (GToken) {
			if ((GToken->Player == Token->Player) && (GToken->ID != Token->ID))
				V_Aligned++;
			else
				break;
		}
		else
			break;
	}
	
	//Haut
	for (int Y = (int)Token->Position.Y + 1; (Y <= (BOARD_HEIGHT - 1)) && (Y <= ((int)Token->Position.Y + 3)); Y++) {
		Temp_Token_Position->Y = Y;
		const Token_t* GToken = this->GetToken((const Token_Position_t*)Temp_Token_Position);
		if (GToken) {
			if ((GToken->Player == Token->Player) && (GToken->ID != Token->ID))
				V_Aligned++;
			else
				break;
		}
		else
			break;
	}
	
	//TODO : les 4 diagonales ;)
	
	delete Temp_Token_Position;
	
	if (H_Aligned >= 3 || V_Aligned >= 3 || D1_Aligned >= 3 || D2_Aligned >= 3) {
		this->InGame = false;
		return true;
	}
	
	return false;
}

int main (int argc, char const* argv[]) {
	
	Puissance4* Game = new Puissance4;
	unsigned int nPlayers = 2;
	
	Game->NewGame(nPlayers);
	
	//On actualise le plateau de jeu
	Game->Redraw();
	while (Game->IsGameRunning()) {
		for (unsigned int Index = 0; (Index < nPlayers) && Game->IsGameRunning(); Index++) {			
			//On demande au joueur d'entrer le numéro de colonne dans laquelle il désire placer son Token
			const Token_t* Token = Game->PlayerAction(Game->GetPlayerInfos(Index));
			Game->Redraw();
			//Si la fonction n'a pas retourné de Token valide cela signifie qu'il n'y a plus de place sur le plateau
			//Et donc la partie n'a pas connue de vanqueur !!!
			if (!Token) {
				Game->Draw();
			}
			else {
				if (Game->IsWinner(Token))
					PRINTCR("Bravo " << Token->Player->Name << ", Tu as gagne la partie !");
			}
		}
	}
	
	Game->EndGame();
	
	delete Game;
	return 0;
}
Voila j'espère que ce sera utile à quelqu'un d'autre ;)
 

tqz_

Elite
39 erreurs avec visual c++^^
 

ailless

Asimov, Sagan, Carlin, Hitchens
C'est quoi les options de compilation de ton compilateurs neku ?
 
1er
OP
neku

neku

Codeur roumain
Je suis sous OS x et j'utilise g++:
g++ -D_MAC_ -o puissance4 puissance4.cpp
$

rien de vraiment particulier

tqz_ a dit:
39 erreurs avec visual c++^^
Déjà avec visual studio tu n'aura pas de fonction clrscr() ...

Code:
void clrscr(){
system("cls");
}
peux-tu me copier la liste des erreurs ?
 

ailless

Asimov, Sagan, Carlin, Hitchens
neku a dit:
Je suis sous OS x et j'utilise g++:
$

rien de vraiment particulier


Déjà avec visual studio tu n'aura pas de fonction clrscr() ...

Code:
void clrscr(){
system("cls");
}
peux-tu me copier la liste des erreurs ?
Quand je serai sur linux, je testerai la compilation pour comparer si tu veux, là je fais du COBOL >.< :)
 

ailless

Asimov, Sagan, Carlin, Hitchens
J'ai beaucoup d'erreurs de compilations (sur linux) aussi mais faut que je corrige les premières pour avoir les autres donc je peux pas te montrer toutes les erreurs mais beaucoup d'erreurs viennent de mots séparés => v oid, unsig ned
je sais pas comment ça peut compiler chez toi héhé :)

ensuite #include <cctype> et pas ctype.h
 
1er
OP
neku

neku

Codeur roumain
les espaces dans les noms de types ou autres ce n'est pas ma faute, c'est le balise CODE du forum qui génère se bug, après ca, moi je l'ai compilé sous Windows avec VS hier soir et aucuns soucis ...
 

ailless

Asimov, Sagan, Carlin, Hitchens
neku a dit:
les espaces dans les noms de types ou autres ce n'est pas ma faute, c'est le balise CODE du forum qui génère se bug, après ca, moi je l'ai compilé sous Windows avec VS hier soir et aucuns soucis ...
wep mais faut voir tes options de compilations aussi, ça joue beaucoup

en C :

gcc -o test test.c passera sûrement alors que
gcc -W -Wall -pedantic -o test test.c laissera rien passer mais bon t'es meilleur programmeur que moi donc tu le sais sûrement

maintenant je vais demander à un ami de le compiler chez lui aussi on verra bien :)
 
Statut
N'est pas ouverte pour d'autres réponses.
Haut