⚠️ Warning: This is a draft ⚠️
This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.
If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.
{{collection|Go Fish}}
OOP
{{works with|Python|2.5}}
import random import sys from collections import defaultdict class HumanPlayer(object): def __init__(self,deck): self.hand = defaultdict(int) self.book = [] self.deck = deck #making a copy of deck, all changes within #this class should affect the global deck self.score = 0 self.name = raw_input('Name yourself: ') def Draw(self): #assuming that deck is a global cardDrawn = self.deck.pop() #removes the last card from deck self.hand[cardDrawn] += 1 #adds card to hand print '%s drew %s.' % (self.name,cardDrawn) self.checkForBooks() def checkForBooks(self): # Removes all items of which are 4. for key,val in self.hand.items(): #can't use iteritems() because we are modifying hand in loop if val == 4: #completed a book self.book.append(key) print '%s completed the book of %s\'s.' % (self.name,key) self.score += 1 del self.hand[key] self.emptyCheck() def emptyCheck(self): if len(self.deck)!=0 and len(self.hand)==0: #checks if deck/hand is empty self.Draw() def displayHand(self): #Displays current hand, cards separated by spaces return ' '.join(key for key,val in self.hand.iteritems() for i in range(val)) #meh, make it prettier def makeTurn(self): print '%s\'s hand: %s' % (self.name,self.displayHand()) chooseCard = raw_input('What card do you ask for? ').strip() if chooseCard == 'quit': sys.exit(0) if chooseCard not in self.hand: print 'You don\'t have that card. Try again! (or enter quit to exit)' chooseCard = self.makeTurn() return chooseCard def fishFor(self,card): if card in self.hand: # if card in hand, returns count and removes the card from hand val = self.hand.pop(card) self.emptyCheck() return val else: return False def gotCard(self,card,amount): self.hand[card] += amount self.checkForBooks() class Computer(HumanPlayer): def __init__(self,deck): self.name = 'Computer' self.hand = defaultdict(int) self.book = [] self.deck = deck self.opponentHas = set() self.score = 0 def Draw(self): #assuming that deck is a global cardDrawn = self.deck.pop() #removes the last card from deck self.hand[cardDrawn] += 1 #adds card to hand print '%s drew a card.' % (self.name) self.checkForBooks() ##AI: guesses cards that knows you have, then tries cards he has at random. ##Improvements: remember if the card was rejected before, guess probabilities def makeTurn(self): # print self.displayHand(),self.opponentHas candidates = list(self.opponentHas & set(self.hand.keys())) #checks for cards in hand that computer knows you have if not candidates: candidates = self.hand.keys() #if no intersection between those two, random guess move = random.choice(candidates) print '%s fishes for %s.' % (self.name,move) return move def fishFor(self,card): #Same as for humans players, but adds the card fished for to opponentHas list. self.opponentHas.add(card) if card in self.hand: # if card in hand, returns count and removes the card from hand val = self.hand.pop(card) self.emptyCheck() return val else: return False def gotCard(self,card,amount): self.hand[card] += amount self.opponentHas.discard(card) self.checkForBooks() class PlayGoFish(object): def __init__(self): self.deck = ('2 3 4 5 6 7 8 9 10 J Q K A '*4).split(' ') self.deck.remove('') self.player = [HumanPlayer(self.deck),Computer(self.deck)] #makes counting turns easier def endOfPlayCheck(self):#checks if hands/decks are empty using the any method return self.deck or self.player[0].hand or self.player[1].hand def play(self): random.shuffle(self.deck) for i in xrange(9): # Deal the first cards self.player[0].Draw() self.player[1].Draw() turn = 0 while self.endOfPlayCheck(): print '\nTurn %d (%s:%d %s:%d) %d cards remaining.' % (turn,self.player[0].name, self.player[0].score,self.player[1].name,self.player[1].score,len(self.deck)) whoseTurn = turn%2 otherPlayer = (turn+1)%2 while True: #loop until player finishes turn cardFished = self.player[whoseTurn].makeTurn() result = self.player[otherPlayer].fishFor(cardFished) if not result: #Draws and ends turn self.player[whoseTurn].Draw() break print '%s got %d more %s.' % (self.player[whoseTurn].name,result, cardFished) self.player[whoseTurn].gotCard(cardFished,result) if not self.endOfPlayCheck(): break turn+=1 print '\nScores: \n%s: %d\n%s: %d\n' % (self.player[0].name,self.player[0].score, self.player[1].name,self.player[1].score) if self.player[0].score>self.player[1].score: print self.player[0].name,'won!' elif self.player[0].score==self.player[1].score: print 'Draw!' else: print self.player[1].name,'won!' if __name__=="__main__": game = PlayGoFish() game.play()
Procedural
{{works with|Python|3.6}}
""" Plays a Go Fish game between a user and computer following the rules specified at http://www.rosettacode.org/wiki/Go_Fish """ import itertools import random from copy import deepcopy from operator import attrgetter from typing import (Counter, Iterable, Iterator, List, NamedTuple, NewType, Sequence, Set, Tuple, TypeVar) Card = NewType('Card', str) Hand = Counter[Card] Deck = Tuple[Card, ...] Watchlist = Set[Card] T = TypeVar('T') SUITS_COUNT = 4 RANKS = ('2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A') DECK = RANKS * SUITS_COUNT EMPTY_HAND_MESSAGE = "{} is without cards. They go fishing." CARD_REQUEST_MESSAGE = "{player_name} asks for {card}" NO_CARD_MESSAGE = "{player_name} doesn't have {card}" BOOK_COMPLETED_MESSAGE = "{player_name} completed the book of {card}'s." FISHING_RESULT_MESSAGE = "{player_name} fished a {card}" class Player(NamedTuple): name: str hand: Hand is_human: bool score: int = 0 is_lucky: bool = True # if False, turn goes to another player watchlist: Watchlist = set() # used by AI to track enemy's cards def play(deck: Deck = DECK, *, first_hand_count: int = 9, lucky_fishing: bool = False) -> None: """ Plays the Go Fish game according to http://www.rosettacode.org/wiki/Go_Fish :param deck: deck from where the cards are drawn :param first_hand_count: starting amount of cards for a player :param lucky_fishing: if True, continue playing after fishing the card from the deck that was asked from an opponent """ deck = shuffle(deck) deck, (hand, other_hand) = deal_first_hands(deck, count=first_hand_count) human = Player(name=input('Name yourself: '), hand=hand, is_human=True) ai = Player(name='Computer', hand=other_hand, is_human=False) human = check_hand_for_books(human) ai = check_hand_for_books(ai) player, opponent = ai, human for turn in itertools.count(1): player, opponent = opponent, player print_turn_stats(turn=turn, players=(player, opponent), cards_left=len(deck)) while cards_in_game(deck, hands=(player.hand, opponent.hand)): player, opponent, deck = play_turn(player, opponent, deck, lucky_fishing=lucky_fishing) if not player.is_lucky: break else: print_final_stats((player, opponent)) return def shuffle(deck: Deck) -> Deck: """Returns a shuffled deck""" deck = list(deck) random.shuffle(deck) return tuple(deck) def deal_first_hands(deck: Deck, count: int) -> Tuple[Deck, Tuple[Hand, Hand]]: """Gives count cards to each player""" hands = (Hand(), Hand()) # type: Tuple[Hand, Hand] for hand in ncycles(hands, count): card, deck = fish(deck) hand[card] += 1 return deck, hands def fish(deck: Deck) -> Tuple[Card, Deck]: """Returns a fished card from a deck, and a new deck""" return deck[-1], deck[:-1] def ncycles(iterable: Iterable[T], n: int) -> Iterator[T]: """Returns the sequence elements n times""" repeat = itertools.repeat(tuple(iterable), n) return itertools.chain.from_iterable(repeat) def check_hand_for_books(player: Player) -> Player: """ Walks through cards in the hand, removes books and adds corresponding score """ def is_book(card_and_count: Tuple[Card, int]) -> bool: """ For a pair of card-count checks if count equals the number of suits """ return card_and_count[1] == SUITS_COUNT player = deepcopy(player) cards_counts = player.hand.items() books = list(filter(is_book, cards_counts)) # type: List[Tuple[Card, int]] for card, _ in books: player.hand.pop(card) return player._replace(score=player.score + len(books)) def print_turn_stats(*, turn: int, players: Sequence[Player], cards_left: int) -> None: """Prints stats of the current turn""" name_score = name_score_pairs(players, sep=' ') print(f'\nTurn {turn} ({name_score}) {cards_left} cards remaining.') def name_score_pairs(players: Sequence[Player], *, sep: str) -> str: """Returns a string of pairs of 'name: score'; human goes first""" players = sorted(players, key=attrgetter('is_human'), reverse=True) name_score_generator = map('{0.name}: {0.score}'.format, players) return sep.join(name_score_generator) def cards_in_game(deck: Deck, hands: Iterable[Hand]) -> bool: """Checks if hands or the deck have any cards""" return deck or any(hands) def play_turn(player: Player, opponent: Player, deck: Deck, *, lucky_fishing: bool) -> Tuple[Player, Player, Deck]: """Plays one turn""" player, deck = replenish_card(player, deck=deck) opponent, deck = replenish_card(opponent, deck=deck) if player.is_human: requested_card, watchlist = human_asks_card( hand=player.hand, watchlist=opponent.watchlist) opponent = opponent._replace(watchlist=watchlist) else: requested_card = ai_asks_card(player) if requested_card in opponent.hand: player, opponent = correct_guess_actions(player=player, opponent=opponent, card=requested_card) else: player, deck = wrong_guess_actions( player=player, opponent=opponent, requested_card=requested_card, deck=deck, lucky_fishing=lucky_fishing) return player, opponent, deck def print_final_stats(players: Sequence[Player]) -> None: """Prints final stats""" name_score = name_score_pairs(players, sep='\n') print(f'\nScores: \n{name_score}\n') scores = list(map(attrgetter('score'), players)) if all(score == scores[0] for score in scores): print('Draw!') else: winning_player = max(players, key=attrgetter('score')) print(winning_player.name, 'won!') def replenish_card(player: Player, *, deck: Deck) -> Tuple[Player, Deck]: """Returns a player with a card drawn from a deck""" player = deepcopy(player) if not player.hand: print(EMPTY_HAND_MESSAGE.format(player.name)) card, deck = fish(deck) player.hand[card] += 1 print_fishing_result(player=player, card=card) return player, deck def human_asks_card(*, hand: Hand, watchlist: Watchlist) -> Tuple[Card, Watchlist]: """Set of actions for when human asks a card from computer""" watchlist = watchlist.copy() print_hand(hand) requested_card = ask_for_card(hand=hand) watchlist.add(requested_card) return requested_card, watchlist def ai_asks_card(player: Player) -> Card: """Set of actions for when computer asks a card from human""" requested_card = request_card(player.hand, watchlist=player.watchlist) print_message(CARD_REQUEST_MESSAGE, player_name=player.name, card=requested_card) return requested_card def correct_guess_actions(player: Player, opponent: Player, card: Card): """ Set of actions for when player guesses correctly card in a hand of an opponent """ player = deepcopy(player) opponent = deepcopy(opponent) card_count = opponent.hand.pop(card) print(f'{player.name} got {card_count} more {card} ' f'from {opponent.name}.') player.hand[card] += card_count player = check_book(player, card) if not player.is_human: player.watchlist.discard(card) player = player._replace(is_lucky=True) return player, opponent def wrong_guess_actions(*, player: Player, opponent: Player, requested_card: Card, deck: Deck, lucky_fishing: bool) -> Tuple[Player, Deck]: """ Set of actions for when player guesses incorrectly card in a hand of an opponent. `lucky_fishing` determines if player can continue playing after fishing the wanted card """ player = deepcopy(player) print_message(NO_CARD_MESSAGE, player_name=opponent.name, card=requested_card) card, deck = fish(deck) player.hand[card] += 1 print_fishing_result(player=player, card=card) player = check_book(player, card) if lucky_fishing: player = player._replace(is_lucky=card == requested_card) else: player = player._replace(is_lucky=False) if player.is_lucky: print("What a luck! " "The fished card was the same as requested!") return player, deck def print_hand(hand: Hand) -> None: """Prints a hand in the following form: 'Q Q 7 7 7 3'""" ranks = itertools.starmap(itertools.repeat, hand.items()) print(*itertools.chain.from_iterable(ranks)) def ask_for_card(hand: Hand) -> Card: """Asks user for a card, and checks if it is in their hand""" while True: asked_card = Card(input('What card do you ask for? ')) if asked_card in hand: return asked_card print("You don't have that card. Try again!") def request_card(hand: Hand, watchlist: Watchlist) -> Card: """AI-like choice for a card that will be asked from a human""" candidates = list(watchlist & set(hand.keys())) if not candidates: candidates = list(hand.keys()) return random.choice(candidates) def print_message(message: str, *, player_name: str, card: str) -> None: """Prints a template message""" print(message.format(player_name=player_name, card=card)) def check_book(player: Player, card: Card) -> Player: """ Checks if player has all cards of specified type, removes them if true, and adds +1 to score """ player = deepcopy(player) if player.hand[card] == SUITS_COUNT: print_message(BOOK_COMPLETED_MESSAGE, player_name=player.name, card=card) player.hand.pop(card) player = player._replace(score=player.score + 1) return player def print_fishing_result(*, player: Player, card: Card) -> None: """ Prints result of fishing. Showing card or not depends on if a player is a human. """ card_stub = str(card) if player.is_human else 'card' print_message(FISHING_RESULT_MESSAGE, player_name=player.name, card=card_stub) if __name__ == "__main__": play()