Go Fish/D

From Rosetta Code
Revision as of 01:33, 31 August 2013 by rosettacode>Bearophile (D version of the Go Fish game)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Go Fish/D is part of Go Fish. You may find other members of Go Fish at Category:Go Fish.

Translation of: Python <lang d>import std.stdio, std.random, std.range, std.string, std.array,

      std.algorithm, std.exception;

alias replicate = std.array.replicate; //

class EndGameException : Exception {

   this() pure nothrow { super(""); }

}


class Deck {

   string[] d;
   this(string[] deck_) { d = deck_; }
   alias d this;

}


abstract class Player {

   string name;
   Deck deck;
   int[string] hand;
   int score = 0;
   string[] book;
   this(Deck deck_)
   in {
       assert(deck_ !is null);
   } body {
       deck = deck_;
   }
   void draw()
   in {
       assert(deck !is null);
   } body {
       auto cardDrawn = deck.back;
       deck.popBack; // Removes the last card from deck.
       hand[cardDrawn]++; // Adds card to hand.
       writefln("%s drew %s.", name, cardDrawn);
       checkForBooks;
   }
   string makeTurn();
   /// Removes all items of which are 4.
   void checkForBooks() {
       // We are modifying 'hand' in loop:
       foreach (key, val; zip(hand.keys, hand.values))
           if (val == 4) { // completed a book
               book ~= key;
               writefln("%s completed the book of %s's.", name, key);
               score++;
               hand.remove(key);
           }
       emptyCheck;
   }
   /// Checks if deck/hand is empty.
   void emptyCheck() {
       //if (!deck.empty && hand.empty)
       if (!deck.empty && hand.length == 0)
           draw();
   }
   /// If card in hand, returns count and removes the card from hand.
   int fishFor(string card) {
       if (card in hand) {
           // If card in hand, returns count and removes the
           // card from hand.
           auto val = hand[card];
           hand.remove(card);
           emptyCheck;
           return val;
       } else
           return 0;
   }
   void gotCard(string card, int amount) {
       hand[card] += amount;
       checkForBooks;
   }

}


class Human: Player {

   this(Deck deck_) {
       super(deck_);
       "Name yourself: ".write;
       name = readln.strip;
   }
   /// Displays current hand, cards separated by spaces.
   string displayHand() {
       //return " ".join(key for key, val in hand.iteritems()
       //                for i in xrange(val))
       string[] result;
       foreach (key, val; hand)
           result ~= [key].replicate(val);
       return result.join(" ");
   }
   override string makeTurn() {
       writefln("%s's hand: %s", name, displayHand);
       "What card do you ask for? ".write;
       auto chooseCard = readln.strip;
       if (chooseCard == "quit")
           throw new EndGameException();
       if (chooseCard !in hand) {
           writeln("You don't have that card. Try again!" ~
                   " (or enter quit to exit)");
           chooseCard = makeTurn;
       }
       return chooseCard;
   }

}


class Computer: Player {

   bool[string] opponentHas; // a Set.
   this(Deck deck_) {
       super(deck_);
       name = "Computer";
   }
   // AI: guesses cards that knows you have, then tries cards he has
   // at random. Improvements: remember if the card was rejected
   // before, guess probabilities.
   override string makeTurn() {
       // print displayHand(), opponentHas
       // Checks for cards in hand that computer knows you have.
       auto candidates = opponentHas.keys.sort()
                         .setIntersection(hand.keys.sort()).array;
       if (candidates.empty)
           // If no intersection between those two, random guess.
           candidates = hand.keys;
       auto move = candidates[uniform(0, $)];
       writefln("%s fishes for %s.", name, move);
       return move;
   }
   /// Same as for humans players, but adds the card fished
   // for to opponentHas list.
   override int fishFor(string card) {
       opponentHas[card] = true;
       return super.fishFor(card);
   }
   override void gotCard(string card, int amount) {
       opponentHas.remove(card);
       super.gotCard(card, amount);
   }

}


class GoFish {

   Deck deck;
   Player[] players;
   this() {
       deck=new Deck("2 3 4 5 6 7 8 9 10 J Q K A".split.replicate(4));
       // Makes counting turns easier.
       //players = [new Human(deck), new Computer(deck)];
       players ~= new Human(deck);
       players ~= new Computer(deck);
   }
   /// Checks if hands/decks are empty.
   bool endOfPlayCheck() {
       return !deck.empty || players[0].hand.length ||
              players[1].hand.length;
   }
   void play() {
       deck.d.randomShuffle;
       foreach (_; 0 .. 9) { // Deal the first cards
           players[0].draw;
           players[1].draw;
       }
       auto turn = 0;
       while (endOfPlayCheck) {
           writefln("\nTurn %d (%s: %d, %s: %d) %d cards remaining.",
                    turn, players[0].name,
                    players[0].score, players[1].name,
                    players[1].score, deck.length);
           auto whoseTurn = turn % 2;
           auto otherPlayer = (turn + 1) % 2;
           while (true) { // Loop until player finishes turn.
               auto cardFished = players[whoseTurn].makeTurn;
               auto result = players[otherPlayer].fishFor(cardFished);
               if (!result) { // Draws and ends turn.
                   players[whoseTurn].draw;
                   break;
               }
               writefln("%s got %d more %s.",
                        players[whoseTurn].name, result, cardFished);
               players[whoseTurn].gotCard(cardFished, result);
               if (!endOfPlayCheck)
                   break;
           }
           turn++;
       }
       writefln("\nScores: \n%s: %d\n%s: %d\n",
                players[0].name, players[0].score,
                players[1].name, players[1].score);
       if (players[0].score > players[1].score)
           writeln(players[0].name, " won!");
       else if (players[0].score == players[1].score)
           writeln("draw!");
       else
           writefln(players[1].name, " won!");
   }

}

void main() {

   try {
       auto game = new GoFish;
       game.play;
   } catch (EndGameException)
       writeln("Game finished.");

}</lang>

The gameplay is the same as the Python entry.