Go Fish/Ruby
< Go Fish
Go Fish/Ruby is part of Go Fish. You may find other members of Go Fish at Category:Go Fish.
<lang ruby>class Card
RANKS = %w(2 3 4 5 6 7 8 9 10 J Q K A) SUITS = %w(C D H S)
def initialize(rank, suit) @rank = rank @suit = suit end attr_reader :rank, :suit
def <=>(other) # this ordering sorts first by rank, then by suit (RANKS.find_index(self.rank) <=> RANKS.find_index(other.rank)).nonzero? || (SUITS.find_index(self.suit) <=> SUITS.find_index(other.suit)) end
def to_s @rank + @suit end
end
class Deck
def initialize @deck = [] Card::SUITS.each do |suit| Card::RANKS.each do |rank| @deck << Card.new(rank, suit) end end @deck.shuffle! end attr_reader :deck
# returns an array of cards, even for dealing just 1 card def deal(n=1) @deck.pop(n) end
def empty? @deck.empty? end
def cards_remaining @deck.length end
end
class Player
def initialize(game) @hand = {} @books = [] @game = game @opponents_hand = { :known_to_have => [], :known_not_to_have => [], } end attr_reader :name
def take_cards(cards) my_cards = @hand.values.flatten.concat(cards) @hand = my_cards.group_by {|card| card.rank}
# look for, and remove, any books @hand.each do |rank, cards| if cards.length == 4 puts "#@name made a book of #{rank}" @books << rank @hand.delete(rank) end end if @hand.length == 0 and not @game.deck.empty? @game.deal(self, 1) end end
def num_books @books.length end
# return true if the next turn is still mine # return false if the next turn is my opponent's def query(opponent) wanted = wanted_card puts "#@name: Do you have a #{wanted}?" received = opponent.answer(wanted) @opponents_hand[:known_to_have].delete(wanted) if received.empty? @game.deal(self, 1) # by my next turn, opponent will have been dealt a card # so I cannot know what he does not have. @opponents_hand[:known_not_to_have] = [] false else take_cards(received) @opponents_hand[:known_not_to_have].push(wanted).uniq! true end end
def answer(rank) cards = [] @opponents_hand[:known_to_have].push(rank).uniq! if not @hand[rank] puts "#@name: Go Fish!" else cards = @hand[rank] @hand.delete(rank) puts "#@name: Here you go -- #{cards.join(', ')}" @game.deal(self, 1) if @hand.empty? end cards end
def print_hand puts "hand for #@name:" puts " hand: "+ @hand.values.flatten.sort.join(', ') puts " books: "+ @books.join(', ') puts "opponent is known to have: " + @opponents_hand[:known_to_have].sort.join(', ') end
end
class ComputerPlayer < Player
def initialize(game) super @name = 'Computer' end
def wanted_card known = @hand.keys & @opponents_hand[:known_to_have] if not known.empty? sort_cards_by_most(known).first else possibilities = @hand.keys - @opponents_hand[:known_not_to_have] if not possibilities.empty? possibilities.shuffle.first else #sort_cards_by_most(@hand.keys).first @hand.keys.shuffle.first end end end
# sort ranks by ones with most cards in my hand. better chance to make a book def sort_cards_by_most(array_of_ranks) array_of_ranks.sort_by {|rank| 4 - @hand[rank].length} end
end
class HumanPlayer < Player
def initialize(game) super @name = 'Human' end
def take_cards(cards) puts "#@name received: #{cards.join(', ')}" super end
def wanted_card print_hand wanted = nil loop do print "\nWhat rank to ask for? " wanted = $stdin.gets wanted.strip!.upcase! if not Card::RANKS.include?(wanted) puts "not a valid rank: #{wanted} -- try again." elsif not @hand.keys.include?(wanted) puts "you don't have a #{wanted} -- try again" else break end end wanted end
end
class GoFishGame
def initialize @deck = Deck.new @players = [HumanPlayer.new(self), ComputerPlayer.new(self)] rotate_players if rand(2) == 1 @players.each {|p| deal(p, 9)} end attr_reader :deck
def start loop do p1, p2 = @players # p1.query(p2) method returns true if p1 keeps his turn # and returns false otherwise p1.query(p2) or rotate_players break if p1.num_books + p2.num_books == 13 end puts "==============================" # add a separator between turns puts "Game over" @players.each {|p| puts "#{p.name} has #{p.num_books} books"} nil end
def rotate_players @players.push(@players.shift) puts "------------------------------" # add a separator between turns end
def deal(player, n=1) if n > @deck.cards_remaining n = @deck.cards_remaining end puts "Dealer: #{n} card(s) to #{player.name}" player.take_cards(@deck.deal(n)) end
end
- main
srand GoFishGame.new.start</lang>