Go Fish/Go

From Rosetta Code
Revision as of 00:45, 1 June 2015 by rosettacode>Joshua.Snider (Less idiosyncratic and gofmt'ed.)

The AI selects cards randomly from its hand.


<lang go> package main

import ( "fmt" "math/rand" "sort" "time" )

var cards = [13]string{"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"}

//GoFishGame Stores the game state. type GoFishGame struct { hands [][]string deck []string turn int scores []int }

// checkForBooks looks for fours of a kind // and scores them if we have them. // If we run out of cards, we draw more. func (gm *GoFishGame) checkForBooks() { sort.Strings(gm.hands[gm.turn]) prev := "" count := 1 for _, card := range gm.hands[gm.turn] { if card == prev { count++ if count == 4 { fmt.Printf("Book of %s.\n", card) gm.stealCards(card, gm.turn) gm.scores[gm.turn]++ if gm.isHandEmpty() { gm.drawCard() } } } else { count = 1 } prev = card } }

// drawCard takes a card from the deck // adding it to the current player's hand. func (gm *GoFishGame) drawCard() { if !gm.isDeckEmpty() { card := gm.deck[0] gm.deck = gm.deck[1:] if gm.turn == 0 { fmt.Printf("You drew a %s.\n", card) } gm.hands[gm.turn] = append(gm.hands[gm.turn], card) //Check for books gm.checkForBooks() } }

// endPly ends the current person's turn. // It then either calls the next person's // turn or prints a game over message. func (gm *GoFishGame) endPly() { gameOver := gm.isGameOver() if gameOver { gm.printGameOverMessage() } else if gm.turn == 1 { gm.playerTurn(getPickComputer) } else { gm.playerTurn(getPickUser) } }

// getPickComputer handles the computer's card choices. // We do the moderately smart thing of pick a random // card from our hand func getPickComputer(gm *GoFishGame) string { hand := gm.hands[1] choice := "A" if len(hand) > 0 { choice = hand[rand.Intn(len(hand))] } fmt.Printf("Computer picks %s.\n", choice) return choice }

// getPickUser gets the user's move. // If it's not valid, then the user just wastes // their turn. func getPickUser(gm *GoFishGame) string { fmt.Println("What card do you want?") var card string fmt.Scanf("%s\n", &card) return card }

// isDeckEmpty returns if the deck is empty. func (gm *GoFishGame) isDeckEmpty() bool { return len(gm.deck) == 0 }

// isHandEmpty returns if the current player's hand is empty. func (gm *GoFishGame) isHandEmpty() bool { return len(gm.hands[gm.turn]) == 0 }

// isGameOver returns if the game is over. // This happens when all 13 pips have been made into sets. func (gm *GoFishGame) isGameOver() bool { return gm.scores[0]+gm.scores[1] == 13 }

// makeDeck makes a deck. // The deck is 52 cards with 4 of each pip. func makeDeck() []string { rand.Seed(time.Now().UTC().UnixNano()) deck := make([]string, 52) perm := rand.Perm(52) for indx := range perm { tVal := perm[indx] card := cards[tVal/4] deck[indx] = card } return deck }

// opponentHas returns if the opponent's hand has a card. func (gm *GoFishGame) opponentHas(find string) bool { for _, card := range gm.hands[(gm.turn+1)%2] { if card == find { return true } } return false }

// playerTurn handles the major game logic. // It's used for both the player's and computer's turns, // with the different behavior handled by the getPick param. func (gm *GoFishGame) playerTurn(getPick func(*GoFishGame) string) { opponent := (gm.turn + 1) % 2 gm.checkForBooks() if opponent == 1 { gm.printHand() } if gm.isHandEmpty() { gm.drawCard() } gameOver := gm.isGameOver() if !gameOver { card := getPick(gm) if gm.opponentHas(card) { count := gm.stealCards(card, opponent) for indx := 0; indx < count; indx++ { gm.hands[gm.turn] = append(gm.hands[gm.turn], card) } gm.checkForBooks() } else { fmt.Println("GO FISH!") gm.drawCard() gm.turn = opponent } } gm.endPly() }

// printGameOverMessage prints the appropriate end message. func (gm *GoFishGame) printGameOverMessage() { fmt.Printf("Final score is %d to %d.\n", gm.scores[0], gm.scores[1]) if gm.scores[0] > gm.scores[1] { fmt.Println("Player wins!") } else if gm.scores[0] == gm.scores[1] { fmt.Println("It's a tie.") } else { fmt.Println("Computer wins!") } }

// printHand print's the player's hand and current score. func (gm *GoFishGame) printHand() { sort.Strings(gm.hands[0]) fmt.Printf("You have: %s.\n", gm.hands[0]) fmt.Printf("Score is %d to %d.\n", gm.scores[0], gm.scores[1]) }

// stealCards removes all instances of a card from side's hand. func (gm *GoFishGame) stealCards(purge string, side int) int { count := 0 tList := gm.hands[side] var filtered []string for _, card := range tList { if purge == card { count++ } else { filtered = append(filtered, card) } } gm.hands[side] = filtered return count }

// main creates the deck and initial hands. func main() { deck := makeDeck() playerHand := deck[0:9] compHand := deck[9:18] deck = deck[18:] hands := make([][]string, 2, 2) hands[0] = playerHand hands[1] = compHand scores := make([]int, 2, 2) scores[0] = 0 scores[1] = 0 game := GoFishGame{hands, deck, 0, scores} game.playerTurn(getPickUser) } </lang>