Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Exercices] Venez vous entraîner !

(v2)

23 août 2011 à 18:39:03

Je suis aussi du même avis.

Pour l'autre exercice, je le placerai plutôt dans réalisation d'un jeu.
  • Partager sur Facebook
  • Partager sur Twitter
23 août 2011 à 19:13:15

germino: ajout des exercices jeu de foot + En lettres ! (+solution)
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
24 août 2011 à 11:41:04

Bon ma petite contribution au Tic-Tac-Toe, si vous avez des idées d'améliorations etc ... n'hésitez pas :p

main.cpp
#include <iostream>
#include <iostream>

#include "include/grille.h"

using namespace std;

int main() {
    Grille grille;
    grille.show();
    int playing(1), X(0), Y(0), playerID(1), played(0);
    char playersToken[2] = {'X', 'O'};

    while (playing && played < 9) {
        cin >> X >> Y;
        if (grille.place(X, Y, playersToken[playerID])) {
            if (grille.isWon(X, Y, playersToken[playerID])) {
                cout << "player " << playerID << "(" << playersToken[playerID] << ") won !" << endl;
                playing = 0;
            } else {
                playerID = (playerID + 1) % 2;
                played++;
            }
        } else {
            cout << "this spot is not available. retry";
        }
        grille.show();
    }

    if (played == 9)
        cout << "no winner";

    return 0;
}

grille.h
#ifndef GRILLE_H
#define GRILLE_H

#include <iostream>

class Grille
{
    public:
        Grille();

        void show();
        int place(int X, int Y, char token);
        int isWon(int X, int Y, char token);

    private:
        char content[3][3];
};

#endif // GRILLE_H

grille.cpp
#include "../include/grille.h"

using namespace std;

Grille::Grille() {
    int i(0), j(0);

    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            content[i][j] = ' ';
        }
    }
}

void Grille::show() {
    int i(0), j(0);

    cout << "-------" << endl;
    for (i = 0; i < 3; i++) {
        cout << "|";
        for (j = 0; j < 3; j++) {
            cout << content[i][j] << "|";
        }
        cout << endl << "-------" << endl;
    }
}

int Grille::place(int X, int Y, char token) {
    if (!isInside(X) || !isInside(Y))
        return 0;

    if (content[X][Y] == ' ') {
        content[X][Y] = token;
        return 1;
    }

    return 0;
}

int Grille::isWon(int X, int Y, char token) {
    int directions[8][2] = {{0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}};
    int i(0);

    for (i = 0; i < 8; i++) {
        if (content[X + directions[i][0]][Y + directions[i][1]] == token && isInside(X + directions[i][0]) && isInside(Y + directions[i][1])) {
            if (content[X + 2*directions[i][0]][Y + 2*directions[i][1]] == token && isInside(X + 2*directions[i][0]) && isInside(Y + 2*directions[i][1])) {
                return 1;
            }
        }
    }

    return 0;
}

int Grille::isInside(int number) {
    if (number > 2 || number < 0)
        return 0;

    return 1;
}
  • Partager sur Facebook
  • Partager sur Twitter
24 août 2011 à 11:44:30

a- bien pour les boucles
b- Tu peux rendre const-correctes les fonctions membres.
c- le type booléen, c'est bool. Pas int.
d- Plutôt que de jouer avec ../include, utilise -I si tu as un compilo du monde *nix, /I avec VC.
  • Partager sur Facebook
  • Partager sur Twitter
C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
Anonyme
24 août 2011 à 12:14:54

Citation : lmghs

a- bien pour les boucles


merci :p

Citation : lmghs

b- Tu peux rendre const-correctes les fonctions membres.


J'en profite pour poser une petite question : Outre le fait de passer les méthodes qui ne modifie pas l'objet en méthode constantes, je suppose que tu parles aussi d'envoyer des arguments constants aux méthodes qui ne modifient pas ses arguments ? En toute logique ce type d'argument doit forcément être passé en référence ou sous forme de pointeur nan ? (puisqu'on gagnerait rien en terme de fonctionnalités en l'envoyant par copie ... ou mon raisonnement est totalement foireux ? :p )

Citation : lmghs

d- Plutôt que de jouer avec ../include, utilise -I si tu as un compilo du monde *nix, /I avec VC.


je fais ma feignasse, je suis sous windows avec codeblocks. J'ai essayé d'inclure automatiquement le dossier "include" du projet en cochant "Explicitly add currently compiling file's directory to compiler search's dir" mais il a pas l'air de vouloir le comprendre comme je l'ai compris :-° .
  • Partager sur Facebook
  • Partager sur Twitter
24 août 2011 à 12:37:29

Voici le code que je propose actuellement pour le jeu de foot.

#include <iostream>
#include <cstdio>

#include "Field.h"
#include "ConsolePlayer.h"
#include "IAPlayer.h"

#include "color.h"

using namespace std;

int main()
{
    GlobalColor('0', 'f');
    cout << "Welcome to the game !" << endl;

    Field field;
    Player *players[2]{new ConsolePlayer(1), new IAPlayer(2)};

    int pav(0);
    int currentPlayer(1);

    field.show();

    do
    {
        pav = players[currentPlayer - 1]->play(field);
        field.setchar(pav, (currentPlayer == 1 ? '1' : '2'));

        field.show();

        if(field.getNumberOfFree() == 7)
        {
            currentPlayer = (currentPlayer == 1 ? 2 : 1);//changement de joueur
        }
    }while(!field.getWinner());

    std::cout << std::endl << "Player " << field.getWinner() << " win the game !" << std::endl;

    getchar();
    getchar();

    return 0;
}


#ifndef COLOR_H_INCLUDED
#define COLOR_H_INCLUDED

void Color(const int t , const int f);
void GlobalColor(const char t,const char f);

#endif // COLOR_H_INCLUDED

#include "color.h"
//TODO adapter à linux
#include <cstdlib>
#include <Windows.h>

void Color(const int t, const int f)
{
	HANDLE H=GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(H,f*16+t);
}

void GlobalColor(const char t,const char f)
{
    char cmd[] = {'c','o','l','o','r',' ',t,f};
    system(cmd);
}


#ifndef FIELD_H
#define FIELD_H

#include <string>

struct Coord
{
    int l;
    int c;
};

class Field
{
    public:
        Field();
        virtual ~Field();

        void show();

        const Coord &getCurrentPoint() const {return m_currentPoint;}
        const int getWinner() const;
        const int getNumberOfFree() const;

        void setchar(const int pav, const char val);

        const bool isBlocked() const;
        const bool isFree(const int pav) const;
    protected:
    private:
        Coord deplacer(const int pav) const;
        const bool isInside(const Coord &coo) const;

        std::string m_cars[12*3+1];
        Coord m_currentPoint;
};

#endif // FIELD_H

#include "Field.h"

#include <iostream>
#include "color.h"

Field::Field(): m_currentPoint({18,0})
{
    m_cars[0]  = "         *--*--*--*         ";
    m_cars[1]  = "         |00000000|         ";
    m_cars[2]  = "         |00000000|         ";
    m_cars[3]  = "*--*--*--*00*00*00*--*--*--*";
    m_cars[4]  = "|00000000000000000000000000|";
    m_cars[5]  = "|00000000000000000000000000|";
    m_cars[6]  = "*00*00*00*00*00*00*00*00*00*";
    m_cars[7]  = "|00000000000000000000000000|";
    m_cars[8]  = "|00000000000000000000000000|";
    m_cars[9]  = "*00*00*00*00*00*00*00*00*00*";
    m_cars[10] = "|00000000000000000000000000|";
    m_cars[11] = "|00000000000000000000000000|";
    m_cars[12] = "*00*00*00*00*00*00*00*00*00*";
    m_cars[13] = "|00000000000000000000000000|";
    m_cars[14] = "|00000000000000000000000000|";
    m_cars[15] = "*00*00*00*00*00*00*00*00*00*";
    m_cars[16] = "|00000000000000000000000000|";
    m_cars[17] = "|00000000000000000000000000|";
    m_cars[18] = "O00*00*00*00*00*00*00*00*00*";
    m_cars[19] = "|00000000000000000000000000|";
    m_cars[20] = "|00000000000000000000000000|";
    m_cars[21] = "*00*00*00*00*00*00*00*00*00*";
    m_cars[22] = "|00000000000000000000000000|";
    m_cars[23] = "|00000000000000000000000000|";
    m_cars[24] = "*00*00*00*00*00*00*00*00*00*";
    m_cars[25] = "|00000000000000000000000000|";
    m_cars[26] = "|00000000000000000000000000|";
    m_cars[27] = "*00*00*00*00*00*00*00*00*00*";
    m_cars[28] = "|00000000000000000000000000|";
    m_cars[29] = "|00000000000000000000000000|";
    m_cars[30] = "*00*00*00*00*00*00*00*00*00*";
    m_cars[31] = "|00000000000000000000000000|";
    m_cars[32] = "|00000000000000000000000000|";
    m_cars[33] = "*--*--*--*00*00*00*--*--*--*";
    m_cars[34] = "         |00000000|         ";
    m_cars[35] = "         |00000000|         ";
    m_cars[36] = "         *--*--*--*         ";
}

Field::~Field()
{
    //dtor
}

void Field::show()
{
    Color(9, 0);
    std::cout << "          Player 2          " << std::endl;
    Color(15, 0);

    for(unsigned char l = 0; l < 12*3; ++l)
    {
        for(unsigned char c = 0; c <= 9*3+1; ++c)
        {
            switch(m_cars[l][c])
            {
                case 'O':
                    Color(10, 0);
                    std::cout << m_cars[l][c];
                    m_currentPoint = {l, c};
                    Color(15, 0);
                    break;

                case '0':
                    std::cout << ' ';
                    break;

                case '1':
                    Color(12, 0);
                    std::cout << '-';
                    Color(15, 0);
                    break;

                case '2':
                    Color(9, 0);
                    std::cout << '-';
                    Color(15, 0);
                    break;

                default:
                    std::cout << m_cars[l][c];
                    break;
            }
        }

        std::cout << std::endl;

        ++l;

        for(unsigned char c = 0; c <= 9*3+1; ++c)
        {
            switch(m_cars[l][c])
            {
                case 'O':
                    Color(10, 0);
                    std::cout << m_cars[l][c];
                    m_currentPoint = {l, c};
                    Color(15, 0);
                    break;

                case '0':
                    std::cout << ' ';
                    break;

                case '1':
                    Color(12, 0);
                    std::cout << (c % 3 == 0 ? '|' : (c % 3 == 1 ? '\\' : '/'));
                    Color(15, 0);
                    break;

                case '2':
                    Color(9, 0);
                    std::cout << (c % 3 == 0 ? '|' : (c % 3 == 1 ? '\\' : '/'));
                    Color(15, 0);
                    break;

                default:
                    std::cout << m_cars[l][c];
                    break;
            }
        }

        std::cout << std::endl;

        ++l;

        for(unsigned char c = 0; c <= 9*3+1; ++c)
        {
            switch(m_cars[l][c])
            {
                case 'O':
                    Color(10, 0);
                    std::cout << m_cars[l][c];
                    m_currentPoint = {l, c};
                    Color(15, 0);
                    break;

                case '0':
                    std::cout << ' ';
                    break;

                case '1':
                    Color(12, 0);
                    std::cout << (c % 3 == 0 ? '|' : (c % 3 == 1 ? '/' : '\\'));
                    Color(15, 0);
                    break;

                case '2':
                    Color(9, 0);
                    std::cout << (c % 3 == 0 ? '|' : (c % 3 == 1 ? '/' : '\\'));
                    Color(15, 0);
                    break;

                default:
                    std::cout << m_cars[l][c];
                    break;
            }
        }

        std::cout << std::endl;
    }

    std::cout << m_cars[36] << std::endl;

    Color(12, 0);
    std::cout << "          Player 1          " << std::endl;
    Color(15, 0);
}

const int Field::getWinner() const
{
    if(m_cars[0].find('O') != std::string::npos)
    {
        return 1;
    }

    if(m_cars[36].find('O') != std::string::npos)
    {
        return 2;
    }

    if(isBlocked())
        return 3;

    return 0;
}

const bool Field::isBlocked() const
{
    if(!isFree(1) &&
       !isFree(2) &&
       !isFree(3) &&
       !isFree(6) &&
       !isFree(9) &&
       !isFree(8) &&
       !isFree(7) &&
       !isFree(4))
       return true;

    return false;
}

const bool Field::isFree(const int pav) const
{
    Coord coo = deplacer(pav);

    if(!isInside(coo))
        return false;

    if(m_cars[m_currentPoint.l + coo.l][m_currentPoint.c + coo.c] != '0')
        return false;

    return true;
}

Coord Field::deplacer(const int pav) const
{
    switch(pav)
    {
        case 1:
            return {+1,-1};
            break;
        case 2:
            return {+1, 0};
            break;
        case 3:
            return {+1,+1};
            break;
        case 6:
            return { 0,+1};
            break;
        case 9:
            return {-1,+1};
            break;
        case 8:
            return {-1, 0};
            break;
        case 7:
            return {-1,-1};
            break;
        case 4:
            return { 0,-1};
            break;
        default:
            //TODO lever exception pavé
            return { 0, 0};
            break;
    }
}

void Field::setchar(const int pav, const char val)
{
    if(isFree(pav))
    {
        Coord coo = deplacer(pav);
        m_cars[m_currentPoint.l + 0 * coo.l][m_currentPoint.c + 0 * coo.c] = '*';
        m_cars[m_currentPoint.l + 1 * coo.l][m_currentPoint.c + 1 * coo.c] = val;
        m_cars[m_currentPoint.l + 2 * coo.l][m_currentPoint.c + 2 * coo.c] = val;
        m_cars[m_currentPoint.l + 3 * coo.l][m_currentPoint.c + 3 * coo.c] = 'O';
    }
}

const bool Field::isInside(const Coord &coo) const
{
    if(m_currentPoint.l + coo.l < 0 || m_currentPoint.c + coo.c < 0 ||
       m_currentPoint.l + coo.l > 12*3+1 || m_currentPoint.c + coo.c > 9*3+1)
        return false;

    return true;
}

const int Field::getNumberOfFree() const
{
    int ret = 0;

    if(isFree(1))
        ++ret;
    if(isFree(2))
        ++ret;
    if(isFree(3))
        ++ret;
    if(isFree(6))
        ++ret;
    if(isFree(9))
        ++ret;
    if(isFree(8))
        ++ret;
    if(isFree(7))
        ++ret;
    if(isFree(4))
        ++ret;

    return ret;
}


#ifndef PLAYER_H
#define PLAYER_H

class Field;

class Player
{
    public:
        Player(const int no);
        virtual ~Player();

        virtual int play(const Field &field) = 0;
    protected:
        int playno;
    private:
};

#endif // PLAYER_H

#include "Player.h"

#include "Field.h"

Player::Player(const int no): playno(no)
{
    //ctor
}

Player::~Player()
{
    //dtor
}


#ifndef CONSOLEPLAYER_H
#define CONSOLEPLAYER_H

#include "Player.h"


class ConsolePlayer : public Player
{
    public:
        ConsolePlayer(const int no);
        virtual ~ConsolePlayer();

        virtual int play(const Field &field);
    protected:
    private:
};

#endif // CONSOLEPLAYER_H

#include "ConsolePlayer.h"

#include "Field.h"

#include <iostream>
#include <limits>

ConsolePlayer::ConsolePlayer(const int no): Player(no)
{
    //ctor
}

ConsolePlayer::~ConsolePlayer()
{
    //dtor
}

int ConsolePlayer::play(const Field &field)
{
    int pav(0);

    while((pav != 1 && pav != 2 && pav != 3 && pav != 6 && pav != 9 && pav != 8 && pav != 7 && pav != 4)
          || !field.isFree(pav))
    {
        std::cout << std::endl << "Player " << playno << ":";
        std::cin >> pav;//TODO sécuriser la saisie
    }

    return pav;
}


#ifndef IAPLAYER_H
#define IAPLAYER_H

#include "Player.h"


class IAPlayer : public Player
{
    public:
        IAPlayer(const int no);
        virtual ~IAPlayer();
        virtual int play(const Field &field);
    protected:
    private:
        int play1(const Field &field);
        int play2(const Field &field);
};

#endif // IAPLAYER_H

#include "IAPlayer.h"

#include "Field.h"

IAPlayer::IAPlayer(const int no): Player(no)
{
    //ctor
}

IAPlayer::~IAPlayer()
{
    //dtor
}

int IAPlayer::play(const Field &field)
{
    return playno == 1 ? play1(field) : play2(field);
}

int IAPlayer::play1(const Field &field)
{
    if(field.isFree(9))
        return 9;
    if(field.isFree(7))
        return 7;
    if(field.isFree(8))
        return 8;
    if(field.isFree(6))
        return 6;
    if(field.isFree(4))
        return 4;
    if(field.isFree(3))
        return 3;
    if(field.isFree(1))
        return 1;
    if(field.isFree(2))
        return 2;
    else
        return 0;
}

int IAPlayer::play2(const Field &field)
{
    if(field.isFree(3))
        return 3;
    if(field.isFree(1))
        return 1;
    if(field.isFree(2))
        return 2;
    if(field.isFree(6))
        return 6;
    if(field.isFree(4))
        return 4;
    if(field.isFree(9))
        return 9;
    if(field.isFree(7))
        return 7;
    if(field.isFree(8))
        return 8;
    else
        return 0;
}


N'hésitez pas à commenter ;)
  • Partager sur Facebook
  • Partager sur Twitter
24 août 2011 à 13:03:58

DDR2-b-
Argument constant, c'est possible, mais c'est uniquement pertinent sur les références et les pointeurs. Il n'y a rien de tel dans ton code.
Maintenant, si tu pousses la logique de mettre constant toute variable (locale) que tu ne modifies pas, alors mettre les arguments constant serait dans la continuité.

DDR2-c-, ah. Désolé, je suis la mauvaise personne pour les problèmes sur C::B.
  • Partager sur Facebook
  • Partager sur Twitter
C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
24 août 2011 à 13:12:40

sur c::b, va dans les options de build, va dans "search directories"->"compiler" et ajoute tel quel :

Citation : chemin

include


comme ça, le compilo ira chercher tout seul les .h
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
24 août 2011 à 13:33:35

Citation : germino

sur c::b, va dans les options de build, va dans "search directories"->"compiler" et ajoute tel quel :

Citation : chemin

include


comme ça, le compilo ira chercher tout seul les .h



yep, j'y ai pensé mais y'a pas une "technique" un peu plus clean (plus automatique quoi ... fait trop chaud pour faire des efforts :p ) ? Il détecte bien que c'est des headers quand on ajoute des fichiers au projet, il doit bien pouvoir nous les ajouter automatiquement dans les path de recherche du compilateur nan ?

Bon, V2 de mon tic-tac-toe (merci à vous deux :p ) :


#include <iostream>

#include "grille.h"

using namespace std;

int const nbLine  = 5;
int const nbCol   = 5;
int const nbAlign = 4;

int main() {
    Grille grille(nbLine, nbCol, nbAlign);
    grille.show();
    bool playing(true);
    int X(0), Y(0), playerID(1), played(0);
    char playersToken[2] = {'X', 'O'};

    while (playing && played < (nbLine * nbCol)) {
        cin >> X >> Y;
        if (grille.place(&X, &Y, &playersToken[playerID])) {
            if (grille.isWon(&X, &Y, &playersToken[playerID])) {
                cout << "player " << playerID << "(" << playersToken[playerID] << ") won !" << endl;
                playing = false;
            } else {
                playerID = (playerID + 1) % 2;
                played++;
            }
        } else {
            cout << "this spot is not available. retry";
        }
        grille.show();
    }

    if (played == 9)
        cout << "no winner";

    return 0;
}

#ifndef GRILLE_H
#define GRILLE_H

#include <iostream>

class Grille
{
    public:
        Grille(int nbX, int nbY, int nbAlign);

        void show() const;
        bool place(int const *X, int const *Y, char const *token);
        bool isWon(int const *X, int const *Y, char const *token) const;
        bool isInside(int const X, int const Y) const;

    private:
        char content[10][10];
        int directions[8][2];
        int nbX, nbY, nbAlign;
};

#endif // GRILLE_H

#include "grille.h"

using namespace std;

Grille::Grille(int p_nbX, int p_nbY, int p_nbAlign) {
    int i(0), j(0);

    nbX = p_nbX;
    nbY = p_nbY;
    nbAlign = p_nbAlign;
    directions = {{0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}};

    for (i = 0; i < nbX; i++) {
        for (j = 0; j < nbY; j++) {
            content[i][j] = ' ';
        }
    }
}

void Grille::show() const {
    int i(0), j(0), k(0);

    for (k = 0; k < 2*nbY+1; k++)
        cout << "-";
    cout << endl;

    for (i = 0; i < nbX; i++) {
        cout << "|";
        for (j = 0; j < nbY; j++) {
            cout << content[i][j] << "|";
        }
        cout << endl;
        for (k = 0; k < 2*nbY+1; k++)
            cout << "-";
        cout << endl;
    }
}

bool Grille::place(int const *X, int const *Y, char const *token) {
    if (!isInside(*X, *Y))
        return false;

    if (content[*X][*Y] == ' ') {
        content[*X][*Y] = *token;
        return true;
    }

    return false;
}

bool Grille::isWon(int const *X, int const *Y, char const *token) const {
    int i(0), j(0), newX(0), newY(0);
    bool noError(true);

    for (i = 0; i < 8; i++) {
        noError = true;
        for (j = 0; j < nbAlign && noError; j++) {
            newX = *X + j*directions[i][0];
            newY = *Y + j*directions[i][1];

            if (isInside(newX, newY) && content[newX][newY] == *token)
                noError = true;
            else
                noError = false;
        }

        if (noError == true)
            return true;
    }

    return false;
}

bool Grille::isInside(int const X, int const Y) const {
    if (X > nbX || Y > nbY || X < 0 || Y < 0)
        return false;

    return true;
}

  • Partager sur Facebook
  • Partager sur Twitter
24 août 2011 à 13:44:45

Ben, la solution qui reste pour que ce soit automatique, tu peux éditer le script du type de projet Console (clic droit dessus). Par contre ne compte pas sur moi pour te donner la doc ;) Bon bidouillage.
  • Partager sur Facebook
  • Partager sur Twitter
24 août 2011 à 19:54:34

Citation : germino

Voici le code que je propose actuellement pour le jeu de foot.


Nan, j'allais quand même pas citer tout le code !!! :p



N'hésitez pas à commenter ;)



Tu en es à quel niveau ???
  • Partager sur Facebook
  • Partager sur Twitter
25 août 2011 à 9:43:59

J'ai oublié de préciser : le niveau 3 je crois.
Au fait, les niveaux les plus difficiles sont les niveaux 1 et 5...
  • Partager sur Facebook
  • Partager sur Twitter
25 août 2011 à 10:06:39

Pour le niveau 5, il suffit d'adapter le negamax (*) que j'avais donné pour le tic-tac-toe. Et peut-être lui donner une véritable heuristique, je ne sais pas encore.

(*) qui ne suit pas l'algo de wikipédia vu qu'il est buggué.
  • Partager sur Facebook
  • Partager sur Twitter
C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
1 septembre 2011 à 16:22:41

Puissance 4


Catégorie : Réalisation d'un jeu


Énoncé :



Vous connaissez probablement le jeu du Puissance 4. Deux joueurs doivent placer un jeton dans une grille au dimension de 6 lignes et 7 colonnes et celui-ci coulisse jusqu'à la position la plus basse possible. Le but du jeu est d'avoir 4 de ces pions alignés de n'importe qu'elle façon (verticale, horizontale ou diagonale). Pour plus de détails : Wikipédia
Je conseille vivement d'utiliser la <acronym title="Programmation Orientée objet">POO</acronym> bien qu'elle ne soit pas obligatoire. Vous êtes libre de présenter la grille comme vous le souhaitez mais il est préférable que le code reste portable.

Niveau 1 :


Vous réaliserez le jeu de manière basique où 2 joueurs jouent chacun leur tour. Vous devez penser à gérer tous les cas d'alignement ainsi que le cas d'égalité.

Niveau 2 :


Vous instaurerez un système de score entre les 2 joueurs qui se sauvegarde dans un fichier. Le joueur peut alors consulter les scores depuis le programme.

Niveau 3 :


Le jeu disposera en plus d'un mode 1 joueur où vous affrontez une <acronym title="Intelligence Artificielle">IA</acronym>.

Et encore plus :


- Soyons fous, pourquoi pas un mode réseau !
- Une <acronym title="Intelligence Artificielle">IA</acronym> a plusieurs niveaux de difficultés.
  • Partager sur Facebook
  • Partager sur Twitter
1 septembre 2011 à 16:39:00

Par expérience, régler le niveau de profondeur du négamax suffit amplement pour changer la difficulté du jeu.
Et on peut rajouter une petite heuristique pour faire semblant au début du jeu que l'IA sait jouer -- même si en un rien de temps elle va exploser n'importe quel joueur humain.
  • Partager sur Facebook
  • Partager sur Twitter
C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
1 septembre 2011 à 18:35:19

Puissance 4 - Niveau 1



Ah voilà un sujet intéressant. Il y a quelques années je m'étais intéressé au sujet et j'avais fait une IA pas si mal. Jusqu'au jour où je suis tombé sur un article qui présentait une stratégie de jeu qui fait qu'on ne perd jamais.

Je suis pas très content de mon IA, donc je l'ai supprimée, mais je la posterai quand elle sera plus présentable.

En attendant, voici le code pour le niveau 1:

#include "Game.hpp"

int main()
{
    Game g;
    g.play();
    return 0;
}


#ifndef GAME_H_INCLUDED
#define GAME_H_INCLUDED

#include <cassert>
#include <iostream>
#include <iomanip>

template<size_t sizeX, size_t sizeY>
class GameBase
{
public:

    enum State {EMPTY, CROSS, CIRCLE, BORDER};

    GameBase()
        :m_turn(0),m_currentPlayer(CROSS)
    {
        assert(sizeX > 3 && sizeY > 3);
        reset();
    }

    /**
     * \brief Vide la grille
     */
    void reset()
    {
        for(size_t i(0); i<sizeX+2; ++i)
            for(size_t j(0); j<sizeY+2; ++j)
                m_grid[i][j] = BORDER;

        for(size_t i(1); i<=sizeX; ++i)
            for(size_t j(1); j<=sizeY; ++j)
                m_grid[i][j] = EMPTY;

        for(size_t i(0); i<sizeX+2; ++i)
            m_height[i] = 0;

        m_turn = 0;
    }

    /**
     * \brief Fait tourner le jeu
     */
    void play()
    {
        bool victory(false);
        while(!victory)
        {
            m_currentPlayer = (m_currentPlayer == CROSS ? CIRCLE : CROSS);
            show();
            humanPlay();
            victory = checkVictory();
            if(victory)
            {
                std::cout << (m_currentPlayer == CROSS ? "X": "O") << " has won !!" << std::endl;
            }
            else if(m_turn == sizeX * sizeY)
            {
                std::cout << "Grid is full. It's a draw !" << std::endl;
                break;
            }
        }
        show();
    }

    /**
     * \brief Affiche l'état actuel du jeu
     */
    void show() const
    {
        std::cout << std::endl;
        for(size_t j(0); j<sizeY+2; ++j)
        {
            for(size_t i(0); i<sizeX+2; ++i)
            {
                switch(m_grid[i][sizeY+1 - j])
                {
                case BORDER:
                    std::cout << " # ";
                    break;
                case EMPTY:
                    std::cout << "   ";
                    break;
                case CROSS:
                    std::cout << " X ";
                    break;
                case CIRCLE:
                    std::cout << " O ";
                    break;
                default:
                    break;
                }
            }
            std::cout << std::endl;
        }
        std::cout << "   ";
        for(size_t i(1); i<sizeX+1; ++i)
        {
            std::cout << std::setw(2) << i << ' ';
        }

        std::cout << "\n\nNumber of turns played: " << m_turn << std::endl;
    }

    /**
     * \brief Ajoute une piece au plateau
     */
    void setPiece(size_t column)
    {
        ++m_turn;
        ++m_height[column];
        m_grid[column][m_height[column]] = m_currentPlayer;
    }

    /**
     * \brief Fait jouer un humain
     */
    void humanPlay()
    {
        size_t column;
        do
        {
            std::cout << (m_currentPlayer == CROSS ? "X": "O") <<"'s turn. ";
            std::cout << "Column ?: ";
            std::cin >> column;
            if(m_height[column] == sizeY)
                std::cout << "Column is full. Play elsewhere." << std::endl;
        }
        while(column <1 ||column > sizeX || m_height[column] == sizeY);

        setPiece(column);
    }

    bool checkVictory()
    {
        //Test alignements horizontaux
        for(size_t j(1); j<sizeY+1; ++j)
        {
            for(size_t i(1); i<sizeX-2; ++i)
            {
                if(m_grid[i][j] == m_grid[i+1][j] && m_grid[i][j] == m_grid[i+2][j] && m_grid[i][j] == m_grid[i+3][j] && m_grid[i][j] == m_currentPlayer)
                {
                    return true;
                }
            }
        }
        //Test alignements verticaux
        for(size_t j(1); j<sizeY-2; ++j)
        {
            for(size_t i(1); i<sizeX+1; ++i)
            {
                if(m_grid[i][j] == m_grid[i][j+1] &&m_grid[i][j]== m_grid[i][j+2] &&m_grid[i][j]== m_grid[i][j+3] && m_grid[i][j] == m_currentPlayer)
                {
                    return true;
                }
            }
        }
        //Test alignements diagonale montante
        for(size_t j(1); j<sizeY-2; ++j)
        {
            for(size_t i(1); i<sizeX-2; ++i)
            {
                if(m_grid[i][j] == m_grid[i+1][j+1] && m_grid[i][j] == m_grid[i+2][j+2] && m_grid[i][j] == m_grid[i+3][j+3] && m_grid[i][j] == m_currentPlayer)
                {
                    return true;
                }
            }
        }
        //Test alignements diagonale descendante
        for(size_t j(4); j<sizeY+1; ++j)
        {
            for(size_t i(1); i<sizeX-2; ++i)
            {
                if(m_grid[i][j] == m_grid[i+1][j-1] && m_grid[i][j] == m_grid[i+2][j-2] && m_grid[i][j] == m_grid[i+3][j-3] && m_grid[i][j] == m_currentPlayer)
                {
                    return true;
                }
            }
        }
        return false;
    }


private:

    //Pas de copie
    GameBase(const GameBase&);
    GameBase& operator=(const GameBase&);

    State m_grid[sizeX+2][sizeY+2];         ///< La grille de jeu + ses bords
    int m_height[sizeX+2];                  ///< La hauteur de chaque colonne
    unsigned int m_turn;                    ///< Le nombre de coups joués
    State m_currentPlayer;                  ///< Le joueur courant

};


/**
 * \brief Type plus simple pour le plateau usuel
 */
typedef GameBase<7,6> Game;

#endif // GAME_H_INCLUDED


Il y a des paramètres templates sur la taille de la grille.
J'utilise un tableau annexe qui contient le nombre de pièce jouée dans chaque colonne. Ca permet des tests rapides pour la position des futurs coups.
J'utilise aussi une grille agrandie pour ne pas avoir de problème de dépassement. Mais ça. c'est pour l'IA. Ici, je pourrais faire sans.

N'hésitez pas à commenter ou à poser des questions.
  • Partager sur Facebook
  • Partager sur Twitter
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
1 septembre 2011 à 21:29:30

@ Nanoc : Presque rien à dire sauf qu'avec la norme C++11, tu peux te passer des boucles "for" pour initialiser ta grille. Je procède comme ceci :
m_grid = {{' ',' ',' ',' ', ' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '}}
  • Partager sur Facebook
  • Partager sur Twitter
1 septembre 2011 à 21:55:51

Citation : Metallica-z80

@ Nanoc : Presque rien à dire sauf qu'avec la norme C++11, tu peux te passer des boucles "for" pour initialiser ta grille. Je procède comme ceci :

m_grid = {{' ',' ',' ',' ', ' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '}}

Ouai mais c'est pas très pratique. Quelques boucles et ça suffit ^^
  • Partager sur Facebook
  • Partager sur Twitter
1 septembre 2011 à 21:58:52

Certes mais dans ce cas, tu ne peux pas faire de liste d'initialisation dans le constructeur est c'est pour cela que c'est pratique.
  • Partager sur Facebook
  • Partager sur Twitter
1 septembre 2011 à 23:57:52

Bonjour
J'ai en tête de bosser sur plusieurs des exercices proposés. Est ce que ça vous dérange de commenter mes codes, même si des solutions ont été proposé ?
  • Partager sur Facebook
  • Partager sur Twitter
2 septembre 2011 à 0:22:27

Citation : Metallica-z80

@ Nanoc : Presque rien à dire sauf qu'avec la norme C++11, tu peux te passer des boucles "for" pour initialiser ta grille. Je procède comme ceci :

m_grid = {{' ',' ',' ',' ', ' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '},{' ',' ',' ',' ',' ',' ',' '}}


Sauf qu'en faisant ça, tu perds la généricité. Tu fixes en dur le nombre de lignes et colonnes. Même si, il est vrai, en général on ne change pas trop la taille de la grille.
Je l'avais fait pour tester la dépendance de la taille de la grille dans le temps d'exécution du min-max.

Sinon, il y a un truc critiquable au niveau du test de victoire. Il est inutile de tout tester. Seul les "combinaisons" comprenant la dernière pièce jouée sont à tester.
  • Partager sur Facebook
  • Partager sur Twitter
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
2 septembre 2011 à 9:03:44

Citation : Hellish

Bonjour
J'ai en tête de bosser sur plusieurs des exercices proposés. Est ce que ça vous dérange de commenter mes codes, même si des solutions ont été proposé ?


On est là pour ça :)
  • Partager sur Facebook
  • Partager sur Twitter
2 septembre 2011 à 10:29:59

Citation : Nanoc

il y a un truc critiquable au niveau du test de victoire. Il est inutile de tout tester. Seul les "combinaisons" comprenant la dernière pièce jouée sont à tester.


Effectivement, c'est une bonne idée.

Et puis j'ai une question : La fonction "reset" est-elle prévue pour de futures versions avec d'autres fonctionnalités ? Puisque pour l'instant elle pourrait avoir sa place dans le constructeur.
  • Partager sur Facebook
  • Partager sur Twitter
2 septembre 2011 à 10:45:51

Citation : Metallica-z80

Et puis j'ai une question : La fonction "reset" est-elle prévue pour de futures versions avec d'autres fonctionnalités ? Puisque pour l'instant elle pourrait avoir sa place dans le constructeur.



Elle te permet de refaire une partie sans détruire l'objet.
Tu fais
g.reset();
g.play();

Cela remet l'objet dans un état prêt pour une nouvelle partie.
  • Partager sur Facebook
  • Partager sur Twitter
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
2 septembre 2011 à 11:41:09

OK mais dans ce cas, tu initialise à la fois m-turn dans la liste d'initialisation du constructeur et dans la fonction reset. C'est un peu répétitif. Autant l'enlever du constructeur.
  • Partager sur Facebook
  • Partager sur Twitter
2 septembre 2011 à 13:49:23

Nanoc > En changeant m_grid pour un vector de vector (par exemple), tu peux aussi faire en moins de lignes : m_grid = std::vector<std::vector<State>>(Dim1, std::vector<State>(Dim2, Value));
En C++98, passer par un swap permet d'avoir les mêmes performances que le move-assignment de C++11.
Par contre, ça rend plus difficile la mise en place d'une série de BORDER autour de la carte, mais il suffit dans ce cas d'adapter un poil l'IA.
  • Partager sur Facebook
  • Partager sur Twitter
2 septembre 2011 à 14:02:17

Citation : Metallica-z80

OK mais dans ce cas, tu initialise à la fois m-turn dans la liste d'initialisation du constructeur et dans la fonction reset. C'est un peu répétitif. Autant l'enlever du constructeur.



A mon avis, c'est du micro-pinaillage. Mais je préfère initialiser tous mes attributs dans la liste d'initialisation.

Citation : Equinoxe

Nanoc > En changeant m_grid pour un vector de vector (par exemple), tu peux aussi faire en moins de lignes : m_grid = std::vector<std::vector<State>>(Dim1, std::vector<State>(Dim2, Value));



Tout à fait. (Surtout que dans la version actuelle je n'utilise plus les BORDER...)

Mais j'ai quand même deux arguments en faveur de mon choix de tableau "à la C":
1) Mon tableau ne change jamais de taille durant l'exécution. Donc inutile de sortir un vector.
2) le vector<vector>> est moins performant que le tableau [][]. Et cela peut avoir un impact sur les performances de l'IA si on lui demande d'analyser profondément le jeu.

Par contre, en C++11, j'aurais utilisé des std::array. Même si, ça aurais juste été plus sexy en fait. :)
  • Partager sur Facebook
  • Partager sur Twitter
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
2 septembre 2011 à 15:05:23

Je pense que j'aurai fait exactement comme pour le morpion. Surtout que ... c'est exactement la même chose hormis les règles pour positionner des nouveaux pions.
Ma première version utilisait un tableau statique 2D templatisé sur les dimensions. MAis suite à une volonté d'adaptation dynamique, j'ai sorti un simple vecteur enrichi d'un opérateur d'accès à deux dimensions (pour appliquer le l*C+c)
  • Partager sur Facebook
  • Partager sur Twitter
C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
2 septembre 2011 à 15:08:33

Puissance 4 - Niveau 3



Bon, comme je m'ennuyais au boulot, j'ai fait une petite IA de démonstration avec l'aglorithme min-max.
Ce n'est pas très efficace en terme de performance, mais j'ai essayé de coller le plus possible à l'algorithme tel que présenté dans les bouquins ou sur Wikipédia. C'est vraiment à but pédagogique.

Je peux détailler le principe si nécessaire. Sachez simplement que ma fonction d'évaluation des positions se résume à ça:
  • Si la position est gagnante : 1
  • Si la position est perdante : -1
  • Sinon: 0

La seule "fantaisie" est l'ajout d'un générateur aléatoire pour jouer aléatoirement dans le cas où plusieurs cases sont équivalentes.


#include "Game.hpp"

int main()
{
    std::srand(std::time(0));

    Game g(5);
    g.play();
    return 0;
}



#ifndef GAME_HPP_INCLUDED
#define GAME_HPP_INCLUDED

#include <cassert>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <ctime>
#include <cstdlib>

template<size_t sizeX, size_t sizeY>
class GameBase
{
public:

    /**
     * \brief Les différentes cases possibles
     */
    enum State {EMPTY, CROSS, CIRCLE, BORDER};

    /**
     * \brief Construit une grille vide
     */
    explicit GameBase(int deepness)
        :m_turn(0),m_currentPlayer(CROSS),m_deepness(deepness), m_positionsTested(0)
    {
        assert(sizeX > 3 && sizeY > 3);
        reset();
    }

    /**
     * \brief Vide la grille
     */
    void reset()
    {
        for(size_t i(0); i<sizeX+2; ++i)
            for(size_t j(0); j<sizeY+2; ++j)
                m_grid[i][j] = BORDER;

        for(size_t i(1); i<=sizeX; ++i)
            for(size_t j(1); j<=sizeY; ++j)
                m_grid[i][j] = EMPTY;

        for(size_t i(0); i<sizeX+2; ++i)
            m_height[i] = 0;

        m_turn = 0;
    }

    /**
     * \brief Change le niveau de l'IA
     */
    void setLevel(int level)
    {
        if(level > 0)
            m_deepness = level;
    }

    /**
     * \brief Fait tourner le jeu
     */
    void play()
    {
        bool victory(false);
        while(!victory)
        {
            m_currentPlayer = (m_currentPlayer == CROSS ? CIRCLE : CROSS);
            show();
            (m_currentPlayer == CROSS ? computerPlay() : humanPlay());
            victory = checkVictory(m_currentPlayer);
            if(victory)
            {
                std::cout << (m_currentPlayer == CROSS ? "X": "O") << " has won !!" << std::endl;
            }
            else if(m_turn == sizeX * sizeY)
            {
                std::cout << "Grid is full. It's a draw !" << std::endl;
                break;
            }
        }
        show();
    }

    /**
     * \brief Affiche l'état actuel du jeu
     */
    void show() const
    {
        std::cout << std::endl;
        for(size_t j(0); j<sizeY+2; ++j)
        {
            for(size_t i(0); i<sizeX+2; ++i)
            {
                switch(m_grid[i][sizeY+1 - j])
                {
                case BORDER:
                    std::cout << " # ";
                    break;
                case EMPTY:
                    std::cout << "   ";
                    break;
                case CROSS:
                    std::cout << " X ";
                    break;
                case CIRCLE:
                    std::cout << " O ";
                    break;
                default: //Cannot happen
                    break;
                }
            }
            std::cout << std::endl;
        }
        std::cout << "   ";
        for(size_t i(1); i<sizeX+1; ++i)
        {
            std::cout << std::setw(2) << i << ' ';
        }

        std::cout << "\n\nNumber of turns played: " << m_turn << std::endl;
    }

    /**
     * \brief Fait jouer un humain et renvoie la colonne jouée
     */
    size_t humanPlay()
    {
        size_t column;
        do
        {
            std::cout << (m_currentPlayer == CROSS ? "X": "O") <<"'s turn. ";
            std::cout << "Column ?: ";
            std::cin >> column;
            if(m_height[column] == sizeY)
                std::cout << "Column is full. Play elsewhere." << std::endl;
        }
        while(column <1 ||column > sizeX || m_height[column] == sizeY);

        setPiece(column, m_currentPlayer);
        ++m_turn;

        return column;
    }

    /**
     * \brief Fait jouer l'ordinateur et renvoie la colonne jouée
     */
    size_t computerPlay()
    {
        size_t column;

        m_positionsTested = 0;
        column = launchIA();
        setPiece(column, m_currentPlayer);
        ++m_turn;
        std::cout << "Computer has evaluated " << m_positionsTested << " different positions." << std::endl;

        return column;
    }

    /**
     * \brief Teste si le dernier coup joué est victorieux
     *
     * \param player Couleur joué
     */
    bool checkVictory(State player) const
    {
        //Test alignements horizontaux
        for(size_t j(1); j<sizeY+1; ++j)
        {
            for(size_t i(1); i<sizeX-2; ++i)
            {
                if(m_grid[i][j] == m_grid[i+1][j] && m_grid[i][j] == m_grid[i+2][j] && m_grid[i][j] == m_grid[i+3][j] && m_grid[i][j] == player)
                {
                    return true;
                }
            }
        }
        //Test alignements verticaux
        for(size_t j(1); j<sizeY-2; ++j)
        {
            for(size_t i(1); i<sizeX+1; ++i)
            {
                if(m_grid[i][j] == m_grid[i][j+1] &&m_grid[i][j]== m_grid[i][j+2] &&m_grid[i][j]== m_grid[i][j+3] && m_grid[i][j] == player)
                {
                    return true;
                }
            }
        }
        //Test alignements diagonale montante
        for(size_t j(1); j<sizeY-2; ++j)
        {
            for(size_t i(1); i<sizeX-2; ++i)
            {
                if(m_grid[i][j] == m_grid[i+1][j+1] && m_grid[i][j] == m_grid[i+2][j+2] && m_grid[i][j] == m_grid[i+3][j+3] && m_grid[i][j] == player)
                {
                    return true;
                }
            }
        }
        //Test alignements diagonale descendante
        for(size_t j(4); j<sizeY+1; ++j)
        {
            for(size_t i(1); i<sizeX-2; ++i)
            {
                if(m_grid[i][j] == m_grid[i+1][j-1] && m_grid[i][j] == m_grid[i+2][j-2] && m_grid[i][j] == m_grid[i+3][j-3] && m_grid[i][j] == player)
                {
                    return true;
                }
            }
        }
        return false;
    }


private:

    /**
     * \brief Ajoute une piece au plateau
     *
     * \param column Colonne dans laquelle jouer
     * \param player Couleur de la pièce à ajouter
     * \param computer Si 'true' alors le compteur de coups testés est incrémenté
     */
    void setPiece(size_t column, State player, bool computer = false)
    {
        ++m_height[column];
        m_grid[column][m_height[column]] = player;
        if(computer)
            ++m_positionsTested;
    }

    /**
     * \brief Supprime une pièce du plateau
     *
     * \param column Colonne de laquelle enlever la pièce
     */
    void removePiece(size_t column)
    {
        m_grid[column][m_height[column]]  = EMPTY;
        --m_height[column];
    }

    /**
     * \brief Lance l'algorithme min-max
     */
    size_t launchIA()
    {
        size_t columnPlayed(0);
        int evaluation = -2;

        for(size_t col(1); col<=sizeX; ++col)   //On parcourt les colonnes
        {
            if(m_height[col] == sizeY)          //Teste si la colonne est jouable
                continue;

            setPiece(col, m_currentPlayer, true);

            if(checkVictory(m_currentPlayer))  //Si c'est gagnant
            {
                columnPlayed = col;                 //On garde cette colonne
                removePiece(col);
                break;                              //Et on sort
            }

            const int ret = -recursiveIA(otherPlayer(m_currentPlayer), m_deepness-1);

            if(ret > evaluation)        //Si le coup est meilleur que ce qu'on avait
            {
                evaluation = ret;       //On prend celui-la en compte
                columnPlayed = col;
            }
            else if(ret == evaluation) //Si c'est une position equivalente
            {
                if(static_cast<double>(std::rand()) / RAND_MAX > 0.5)    //On choisit au hasard si on joue là ou pas
                    columnPlayed = col;
            }
            removePiece(col);
        }
        return columnPlayed;
    }


    /**
     * \brief Partie récursive de l'algorithme min-max
     */
    int recursiveIA(State player, int deepness)
    {
        int evaluation = -2;

        for(size_t col(1); col<=sizeX; ++col)  //On parcourt les colonnes
        {
            if(m_height[col] == sizeY)          //Teste si la colonne est jouable
                continue;

            setPiece(col, player, true);             //On joue
            if(checkVictory(player))      //Si c'est gagnant
            {
                evaluation = 1;                 //On montre que ce coup est gagnant
                removePiece(col);               //Et on sort
                break;
            }
            else if(checkVictory(otherPlayer(player)))      //Si c'est perdant
            {
                evaluation = -1;                 //On montre que ce coup est perdant
                removePiece(col);               //Et on sort
                break;
            }
            else if(deepness >= 0)             //On plonge plus loin dans la recursion
            {
                const int ret = -recursiveIA(otherPlayer(player), deepness-1);

                if(ret > evaluation)        //Si le coup est meilleur que ce qu'on avait
                    evaluation = ret;       //On prend celui-la en compte
            }
            else
            {
                evaluation = 0;
            }
            removePiece(col);
        }
        return evaluation;
    }

    /**
     * \brief Renvoie le joueur opposé
     */
    State otherPlayer(State player)
    {
        return player == CROSS ? CIRCLE: CROSS;
    }

    //Pas de copie
    GameBase(const GameBase&);
    GameBase& operator=(const GameBase&);

    State m_grid[sizeX+2][sizeY+2];         ///< La grille de jeu + ses bords
    int m_height[sizeX+2];                  ///< La hauteur de chaque colonne
    unsigned int m_turn;                    ///< Le nombre de coups joués
    State m_currentPlayer;                  ///< Le joueur courant
    int m_deepness;                         ///< La profondeur de récursion maximale
    int m_positionsTested;                  ///< Le nombre de positions évaluées par l'IA
};

/**
 * \brief Type plus simple pour le plateau usuel
 */
typedef GameBase<7,6> Game;

#endif // GAME_HPP_INCLUDED


En jouant au niveau 5, on a déjà un adversaire correct. On peut bien sûr en élaguant l'arbre diminuer énormément le temps de calcul, ce qui permet alors d'aller plus loin encore dans la profondeur de récursion.
  • Partager sur Facebook
  • Partager sur Twitter
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.

[Exercices] Venez vous entraîner !

× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
  • Editeur
  • Markdown