Go Fish/C++

From Rosetta Code
Revision as of 15:35, 18 January 2020 by Thundergnat (talk | contribs) (Sigh)
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++


<lang cpp>

  1. include <time.h>
  2. include <map>
  3. include <vector>
  4. include <algorithm>
  5. include <string>
  6. 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;

} </lang>