Go Fish/Nim: Difference between revisions

From Rosetta Code
Content added Content deleted
(Created page with "<lang Nim>import strutils, strformat import playing_cards const Human = 0 Computer = 1 type Player = range[Human..Computer] SortedHand = array[Rank, set[Suit]] #...")
(No difference)

Revision as of 21:43, 8 January 2021

<lang Nim>import strutils, strformat import playing_cards

const

 Human = 0
 Computer = 1

type

 Player = range[Human..Computer]
 SortedHand = array[Rank, set[Suit]]   # Map card ranks to sets of suits in hand.
 Game = object
   deck: Deck                          # Current content of deck.
   hands: array[Player, SortedHand]    # Player hands.
   bookCounts: array[Player, Natural]  # Number of books.
   prevChoice: Rank                    # Previous choice done by computer.

const Quit = -1 # Special value used to indicate to quit.

  1. ---------------------------------------------------------------------------------------------------

template other(player: Player): Player = 1 - player

  1. ---------------------------------------------------------------------------------------------------

proc toSortedHand(hand: Hand): SortedHand =

 ## Convert a hand (which is as a sequence of cards) to a "SortedHand".
 for card in hand:
   result[card.rank].incl(card.suit)
  1. ---------------------------------------------------------------------------------------------------

proc `$`(hand: SortedHand): string =

 ## Return the representation of a "SortedHand".
 for rank, suits in hand:
   for suit in suits:
     result.addSep(" ")
     result.add $(rank: rank, suit: suit)
  1. ---------------------------------------------------------------------------------------------------

proc askQuestion(question: string; answers: openArray[string]): int =

 ## Ask a question and return the index in the possible answers.
 ## Rteurn the value "Quit" if an end of file is encountered.
 try:
   while true:
     stdout.write question, ' '
     let input = stdin.readLine()
     result = answers.find(input)
     if result >= 0: return
     echo "I don’t understand your answer"
 except EOFError:
   result = Quit
  1. ---------------------------------------------------------------------------------------------------

proc pronoun(player: Player): string {.inline.} =

 ## Return the pronoun to use according to the player.
 if player == Human: "You" else: "I"
  1. ---------------------------------------------------------------------------------------------------

proc checkBookCompleted(game: var Game; player: Player; rank: Rank) =

 ## Check if a book is completed.
 if game.hands[player][rank].len == 4:
   game.hands[player][rank] = {}
   inc game.bookCounts[player]
   echo &"{player.pronoun} completed the book for rank: {rank}"
  1. ---------------------------------------------------------------------------------------------------

proc findChoices(hand: SortedHand): seq[string] =

 ## Find the possible choices for ranks according to the "SortedHand".
 for rank, suits in hand:
   if suits.len != 0: result.add $rank
  1. ---------------------------------------------------------------------------------------------------

proc updateHands(game: var Game; player: Player; rank: Rank): Player =

 ## Update the hands after a player has asked for a rank.
 let otherPlayer = player.other
 # Search in other player hand.
 if game.hands[otherPlayer][rank].len != 0:
   # Transfer cards to player.
   var cards: seq[Card]
   for suit in game.hands[otherPlayer][rank]: cards.add (rank, suit)
   echo &"{player.pronoun} get: {cards}"
   game.hands[player][rank] = game.hands[player][rank] + game.hands[otherPlayer][rank]
   game.hands[otherPlayer][rank] = {}
   # Check if a book is completed.
   game.checkBookCompleted(player, rank)
   result = player
 else:
   # No cards available. Draw a card from deck.
   echo "No luck."
   let card = game.deck.draw()
   if player == Human: echo "You draw the card: ", card
   else: echo "I draw a card"
   game.hands[player][card.rank].incl(card.suit)
   game.checkBookCompleted(player, card.rank)
   result = otherPlayer
  1. ---------------------------------------------------------------------------------------------------

proc humanPlay(game: var Game): Player =

 ## Process human turn.
 echo "Your hand: ", game.hands[Human]
 var choices = game.hands[Human].findChoices()
 if choices.len == 0:
   # Special case if we have no cards in our hand.
   if game.deck.len == 0: return Computer  # Nothing to do.
   let card = game.deck.draw()
   echo "You draw the card: ", card
   game.hands[Human][card.rank].incl(card.suit)
   choices = game.hands[Human].findChoices()
 let choiceString = choices.join(", ")
 let answer = askQuestion(&"What is your choice ({choiceString})?", choices)
 if answer == Quit:
   echo ""
   quit("Quitting")
 let choice = parseEnum[Rank](choices[answer])
 result = game.updateHands(Human, choice)
  1. ---------------------------------------------------------------------------------------------------

proc computerPlay(game: var Game): Player =

 ## Process computer turn.
 # The simple (but not so bad) strategy used consists to choose the rank
 # immediately succeeding to the previous used rank (wrapping around if needed).
 var choice = game.prevChoice
 while true:
   choice = if choice == King: Ace else: succ(choice)
   if game.hands[Computer][choice].len != 0 or choice == game.prevChoice: break
 if game.hands[Computer][choice].len == 0:
   # Special case if the computer has no cards in hand.
   if game.deck.len == 0: return Human  # Nothing to do.
   let card = game.deck.draw()
   echo "I draw a card"
   game.hands[Computer][card.rank].incl(card.suit)
   choice = card.rank
 game.prevChoice = choice
 echo "I want cards of rank: ", choice
 result = game.updateHands(Computer, choice)
  1. ---------------------------------------------------------------------------------------------------

proc playGame(firstPlayer: Player) =

 ## Play a game.
 var game: Game
 game.deck = initDeck()
 game.deck.shuffle()
 let handSeqs = game.deck.deal(2, 9)
 game.hands = [handSeqs[Human].toSortedHand(), handSeqs[Computer].toSortedHand()]
 game.prevChoice = Ace
 echo &"{firstPlayer.pronoun} play first.\n"
 # Search for books before starting.
 for rank in Rank:
   game.checkBookCompleted(Human, rank)
   game.checkBookCompleted(Computer, rank)
 # Play.
 var player = firstPlayer
 while game.bookCounts[Human] + game.bookCounts[Computer] != 13:
   echo &"Your books: {game.bookCounts[Human]}     My books: {game.bookCounts[Computer]}"
   if player == Human:
     player = game.humanPlay()
   else:
     player = game.computerPlay()
   echo ""
 if game.bookCounts[Human] > game.bookCounts[Computer]:
   echo &"You win with {game.bookCounts[Human]} books against {game.bookCounts[Computer]}"
 else:
   echo &"I win with {game.bookCounts[Computer]} books against {game.bookCounts[Human]}"
  1. ———————————————————————————————————————————————————————————————————————————————————————————————————

var firstPlayer: Player let answer = askQuestion("Who will play first ([h]uman/[c]omputer)?", ["h", "c"]) if answer == Quit:

 echo ""
 quit("Quitting")

firstPlayer = Player(answer)

while true:

 playGame(firstPlayer)
 let answer = askQuestion("Do you want to play another game ([y]es/[n]o)? ", ["y", "n"])
 if answer == 1: break
 firstPlayer = firstPlayer.other()</lang>
Output: