Go Fish/C++

From Rosetta Code
Go Fish/C++ is part of Go_Fish. You may find other members of Go_Fish at Category:Go_Fish.

AI is really not that clever but it gets its job done (well, pretty much!).

It follows three simple rules:

  • 75% the times it remembers cards asked by its opponent, and asks for the first one it also has.
  • ask for a card it fished in the last round, if its different from all its other cards
  • cycles thru all its cards, asking for them.

As I said, simple...


C++
#include <time.h>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
#include <iostream>

const std::string s = "CDHS", v = "A23456789TJQK";
const int handCards = 9, drawCards = 3;

class card {
public:
    friend std::ostream& operator<< (std::ostream& os, const card& c ) { 
        os << v[c.val] << s[c.suit]; 
        return os;
    }
    bool isValid()                       { return val > -1; }
    void set( char s, char v )           { suit = s; val = v; }
    char getRank()                       { return v[val]; }
    bool operator == ( const char o )    { return v[val] == o; }
    bool operator < ( const card& a )    { if( val == a.val ) return suit < a.suit; return val < a.val; }
private:
    char                                 suit, val;
};
class deck {
public:
    static deck* instance() {
        if( !inst ) inst = new deck();
        return inst;
    }
    void destroy() {
        delete inst;
        inst = 0;
    }
    card draw() {
        card c;
        if( cards.size() > 0 ) { 
            c = cards.back();
            cards.pop_back();
            return c; 
        }
        c.set( -1, -1 );
        return c;
    }
private:
    deck() { 
        newDeck(); 
    }
    void newDeck() {
        card c; 
        for( char s = 0; s < 4; s++ ) {
            for( char v = 0; v < 13; v++ ) {
                c.set( s, v ); 
                cards.push_back( c ); 
            }
        }
        random_shuffle( cards.begin(), cards.end() );
        random_shuffle( cards.begin(), cards.end() );
    }
    static deck* inst;
    std::vector<card> cards;
};
class player {
public:
    player( std::string n ) : nm( n ) { 
        for( int x = 0; x < handCards; x++ )
            hand.push_back( deck::instance()->draw() );
        sort( hand.begin(), hand.end() );  
    }
    void outputHand() { 
        for( std::vector<card>::iterator x = hand.begin(); x != hand.end(); x++ ) 
            std::cout << ( *x ) << " ";
        std::cout << "\n"; 
    }
    bool addCard( card c ) { 
        hand.push_back( c );
        return checkForBook();
    }
    std::string name() { 
        return nm; 
    }
    bool holds( char c ) { 
        return( hand.end() != find( hand.begin(), hand.end(), c ) ); 
    }
    card takeCard( char c ) {
        std::vector<card>::iterator it = find( hand.begin(), hand.end(), c );
        std::swap( ( *it ), hand.back() );
        card d = hand.back();
        hand.pop_back();
        hasCards();
        sort( hand.begin(), hand.end() ); 
        return d;
    }
    size_t getBooksCount() {
        return books.size();
    }
    void listBooks() {
        for( std::vector<char>::iterator it = books.begin(); it != books.end(); it++ )
            std::cout << ( *it ) << "'s ";
        std::cout << "\n";
    }
    bool checkForBook() {
        bool ret = false;
        std::map<char, int> countMap;
        for( std::vector<card>::iterator it = hand.begin(); it != hand.end(); it++ )
            countMap[( *it ).getRank()]++;
        for( std::map<char, int>::iterator it = countMap.begin(); it != countMap.end(); it++ ) {
            if( ( *it ).second == 4 ) {
                do {
                    takeCard( ( *it ).first );
                } while( holds( ( *it ).first ) );
                books.push_back( ( *it ).first );
                ( *it ).second = 0;
                ret = true;
            }
        }
        sort( hand.begin(), hand.end() );
        return ret;
    }
    bool hasCards() {
        if( hand.size() < 1 ) {
            card c;
            for( int x = 0; x < drawCards; x++ ) {
                c = deck::instance()->draw();
                if( c.isValid() ) addCard( c );
                else break;
            }
        }
        return( hand.size() > 0 );
    }
protected:
    std::string nm; 
    std::vector<card> hand;
    std::vector<char> books;
};
class aiPlayer : public player {
public:
    aiPlayer( std::string n ) : player( n ), askedIdx( -1 ), lastAsked( 0 ), nextToAsk( -1 ) { }
    void rememberCard( char c ) {
        if( asked.end() != find( asked.begin(), asked.end(), c ) || !asked.size() )
            asked.push_back( c );  
    }
    char makeMove() {
        if( askedIdx < 0 || askedIdx >= static_cast<int>( hand.size() ) ) {
            askedIdx = rand() % static_cast<int>( hand.size() );
        }

        char c;
        if( nextToAsk > -1 ) {
            c = nextToAsk;
            nextToAsk = -1;
        } else {
            while( hand[askedIdx].getRank() == lastAsked ) {
                if( ++askedIdx == hand.size() ) {
                    askedIdx = 0;
                    break;
                }
            }
            c = hand[askedIdx].getRank();
            if( rand() % 100 > 25 && asked.size() ) {
                for( std::vector<char>::iterator it = asked.begin(); it != asked.end(); it++ ) {
		    if( holds( *it ) ) {
			c = ( *it );
			break;
		    }
		}
            }
        }
        lastAsked = c;
        return c;
    }
    void clearMemory( char c ) {
        std::vector<char>::iterator it = find( asked.begin(), asked.end(), c );
        if( asked.end() != it ) {
            std::swap( ( *it ), asked.back() );
            asked.pop_back();
        }
    }
    bool addCard( card c ) {
        if( !holds( c.getRank() ) )
            nextToAsk = c.getRank();
        return player::addCard( c );
    }
private:
    std::vector<char> asked;
    char nextToAsk, lastAsked;
    int askedIdx;
};
class goFish {
public:
    goFish() {
        plr = true; 
        std::string n; 
        std::cout << "Hi there, enter your name: "; std::cin >> n; 
        p1 = new player( n ); 
        p2 = new aiPlayer( "JJ" );
    }
    ~goFish() { 
        if( p1 ) delete p1; 
        if( p2 ) delete p2;
        deck::instance()->destroy();
    }
    void play() {
        while( true ) {
            if( process( getInput() ) ) break;
        }
        std::cout << "\n\n";
        showBooks();
        if( p1->getBooksCount() > p2->getBooksCount() ) {
            std::cout << "\n\n\t*** !!! CONGRATULATIONS !!! ***\n\n\n";
        } else {
            std::cout << "\n\n\t*** !!! YOU LOSE - HA HA HA !!! ***\n\n\n";
        }
    }
private:
    void showBooks() {
        if( p1->getBooksCount() > 0 ) {
            std::cout << "\nYour Book(s): ";
            p1->listBooks();
        }
        if( p2->getBooksCount() > 0 ) {
            std::cout << "\nMy Book(s): ";
            p2->listBooks();
        }
    }
    void showPlayerCards() {
        std::cout << "\n\n" << p1->name() << ", these are your cards:\n";
        p1->outputHand();
        showBooks();
    }
    char getInput() {
        char c;
        if( plr ) {
            if( !p1->hasCards() ) return -1;
            showPlayerCards();
            std::string w;
            while( true ) {
                std::cout << "\nWhat card(rank) do you want? "; std::cin >> w;
                c = toupper( w[0] );
                if( p1->holds( c ) ) break; 
                std::cout << p1->name() << ", you can't ask for a card you don't have!\n\n"; 
            }
        } else {
            if( !p2->hasCards() ) return -1;
            c = p2->makeMove();
            showPlayerCards();
            std::string r;
            std::cout << "\nDo you have any " << c << "'s? (Y)es / (G)o Fish ";
            do {
                std::getline( std::cin, r );
                r = toupper( r[0] );
            }
            while( r[0] != 'Y' && r[0] != 'G' );
            bool hasIt = p1->holds( c );
            if( hasIt && r[0] == 'G' )
                std::cout << "Are you trying to cheat me?! I know you do...\n";
            if( !hasIt && r[0] == 'Y' )
                std::cout << "Nooooo, you don't have it!!!\n";
        }
        return c;
    }
    bool process( char c ) {
        if( c < 0 ) return true;
        if( plr ) p2->rememberCard( c );

        player *a, *b;
        a = plr ? p2 : p1;
        b = plr ? p1 : p2;
        bool r;
        if( a->holds( c ) ) {
            while( a->holds( c ) ) {
                r = b->addCard( a->takeCard( c ) );
            }
            if( plr && r )p2->clearMemory( c );
        } else {
            fish();
            plr = !plr;
        }
        return false;
    }
    void fish() {
        std::cout << "\n\n\t  *** GO FISH! ***\n\n";
        card c = deck::instance()->draw();
        if( plr ) {
            std::cout << "Your new card: " << c << ".\n\n******** Your turn is over! ********\n" << std::string( 36, '-' ) << "\n\n";
            if( p1->addCard( c ) ) p2->clearMemory( c.getRank() );
        } else {
            std::cout << "\n********* My turn is over! *********\n" << std::string( 36, '-' ) << "\n\n";
            p2->addCard( c );
        }
    }

    player        *p1;
    aiPlayer    *p2;
    bool        plr;
};
deck* deck::inst = 0;
int main( int argc, char* argv[] ) {
    srand( static_cast<unsigned>( time( NULL ) ) ); 
    goFish f;  f.play(); 
    return 0;
}