Go Fish/V (Vlang)

From Rosetta Code

AI selects card randomly

Translation of: go
import rand
import rand.seed
import os
const cards = ["2", "3", "4", "5", "6", "7", "8", "9",
	"10", "J", "Q", "K", "A"]
 
//GoFishGame Stores the game state.
struct GoFishGame {
mut:
	hands  [][]string
	deck   []string
	turn   int
	scores []int
}
 
// check_for_books looks for fours of a kind
// and scores them if we have them.
// If we run out of cards, we draw more.
fn (mut gm GoFishGame) check_for_books() {
	gm.hands[gm.turn].sort()
	mut prev := ""
	mut count := 1
	for card in gm.hands[gm.turn] {
		if card == prev {
			count++
			if count == 4 {
				println("Book of ${card}.")
				gm.steal_cards(card, gm.turn)
				gm.scores[gm.turn]++
				if gm.is_hand_empty() {
					gm.draw_card()
				}
			}
		} else {
			count = 1
		}
		prev = card
	}
}
 
// draw_card takes a card from the deck
// adding it to the current player's hand.
fn (mut gm GoFishGame) draw_card() {
	if !gm.is_deck_empty() {
		card := gm.deck[0]
		gm.deck = gm.deck[1..]
		if gm.is_player_turn() {
			println("You drew a ${card}.")
		}
		gm.hands[gm.turn] << card
		//Check for books
		gm.check_for_books()
	}
}
 
// get_pick_computer handles the computer's card choices.
// We do the moderately smart thing of pick a random
// card from our hand
fn get_pick_computer(gm GoFishGame) string {
	hand := gm.hands[1]
	mut choice := "A"
	if hand.len > 0 {
		choice = hand[rand.intn(hand.len) or {0}]
	}
	println("Computer picks ${choice}.")
	return choice
}
 
// get_pick_user gets the user's move.
// If it's not valid, then the user just wastes
// their turn.
fn get_pick_user(gm GoFishGame) string {
	card := os.input("What card do you want?")
	return card
}
 
// is_deck_empty returns if the deck is empty.
fn (gm GoFishGame) is_deck_empty() bool {
	return gm.deck.len == 0
}
 
// is_hand_empty returns if the current player's hand is empty.
fn (gm GoFishGame) is_hand_empty() bool {
	return gm.hands[gm.turn].len == 0
}
 
// is_game_over returns if the game is over.
// This happens when all 13 pips have been made into sets.
fn (gm GoFishGame) is_game_over() bool {
	return gm.scores[0]+gm.scores[1] == 13
}
 
// is_player_turn returns if its the player's turn to move.
fn (gm GoFishGame) is_player_turn() bool {
	return gm.turn == 0
}
 
// make_deck makes a deck.
// The deck is 52 cards with 4 of each pip.
fn make_deck() []string {
	rand.seed(seed.time_seed_array(2))
	mut deck := []string{len: 52}
	mut perm := []int{len:52, init:it}//rand.Perm(52)
    rand.shuffle<int>(mut perm) or {panic('ERROR shuffling')}
	for indx in perm {
		t_val := perm[indx]
		card := cards[t_val/4]
		deck[indx] = card
	}
	return deck
}
 
// opponent_has returns if the opponent's hand has a card.
fn (gm GoFishGame) opponent_has(find string) bool {
	for card in gm.hands[(gm.turn+1)%2] {
		if card == find {
			return true
		}
	}
	return false
}
 
// player_turn handles the major game logic.
// It's used for both the player's and computer's turns,
// with the different behavior handled by the get_pick param.
fn (mut gm GoFishGame) player_turn(get_pick fn(GoFishGame) string) {
	opponent := (gm.turn + 1) % 2
	gm.check_for_books()
	if opponent == 1 {
		gm.print_hand()
	}
	if gm.is_hand_empty() {
		gm.draw_card()
	}
	game_over := gm.is_game_over()
	if !game_over {
		card := get_pick(gm)
		if gm.opponent_has(card) {
			count := gm.steal_cards(card, opponent)
			for _ in 0..count {
				gm.hands[gm.turn] << card
			}
			gm.check_for_books()
		} else {
			println("GO FISH!")
			gm.draw_card()
			gm.turn = opponent
		}
	}
}
 
// print_game_over_message prints the appropriate end message.
fn (gm GoFishGame) print_game_over_message() {
	println("Final score is ${gm.scores[0]} to ${gm.scores[1]}.")
	if gm.scores[0] > gm.scores[1] {
		println("Player wins!")
	} else if gm.scores[0] == gm.scores[1] {
		println("It's a tie.")
	} else {
		println("Computer wins!")
	}
}
 
// print_hand print's the player's hand and current score.
fn (mut gm GoFishGame) print_hand() {
	gm.hands[0].sort()
	println("You have: ${gm.hands[0]}.")
	println("Score is ${gm.scores[0]} to ${gm.scores[1]}.")
}
 
// steal_cards removes all instances of a card from side's hand.
fn (mut gm GoFishGame) steal_cards(purge string, side int) int {
	mut count := 0
	mut filtered := []string{}
	for card in gm.hands[side] {
		if purge == card {
			count++
		} else {
			filtered << card
		}
	}
	gm.hands[side] = filtered
	return count
}
 
// main creates the deck and initial hands.
fn main() {
	mut deck := make_deck()
	player_hand := deck[0..9]
	comp_hand := deck[9..18]
	deck = deck[18..]
	mut hands := [][]string{len: 2, cap:2}
	hands[0] = player_hand
	hands[1] = comp_hand
	mut scores := []int{len: 2, cap:2}
	scores[0] = 0
	scores[1] = 0
	mut game := GoFishGame{hands, deck, 0, scores}
	for {
		if game.is_player_turn() {
			game.player_turn(get_pick_user)
		} else {
			game.player_turn(get_pick_computer)
		}
		if game.is_game_over() {
			break
		}
	}
	game.print_game_over_message()
 
}